diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
commit | d6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch) | |
tree | e2be02f33984c48ec019c654051d27964e42c441 /drivers/isdn | |
parent | 609d1e803baf519487233b765eb487f9ec227a18 (diff) |
Merge with 2.3.19.
Diffstat (limited to 'drivers/isdn')
112 files changed, 22438 insertions, 4159 deletions
diff --git a/drivers/isdn/.cvsignore b/drivers/isdn/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index 52e3acbdb..ec27982bb 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -9,6 +9,10 @@ if [ "$CONFIG_INET" != "n" ]; then fi fi bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO +if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then + bool 'Support AT-Fax Class 2 commands' CONFIG_ISDN_TTY_FAX +fi +bool 'Support isdn diversion services' CONFIG_ISDN_DIVERSION if [ "$CONFIG_X25" != "n" ]; then bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' CONFIG_ISDN_X25 fi @@ -26,7 +30,6 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0 bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 - bool 'HiSax Support for Teles 16.3c' CONFIG_HISAX_TELES3C bool 'HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI bool 'HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 @@ -37,24 +40,39 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT - bool 'HiSax Support for Sedlbauer speed card/win/star/fax' CONFIG_HISAX_SEDLBAUER + bool 'HiSax Support for HFC-S based cards' CONFIG_HISAX_HFCS + bool 'HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY + bool 'HiSax Support for Siemens I-Surf card' CONFIG_HISAX_ISURF + bool 'HiSax Support for HST Saphir card' CONFIG_HISAX_HSTSAPHIR + bool 'HiSax Support for Telekom A4T card' CONFIG_HISAX_BKM_A4T + bool 'HiSax Support for Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO + bool 'HiSax Support for Gazel cards' CONFIG_HISAX_GAZEL + bool 'HiSax Support for HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then - if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930 - fi +# bool 'HiSax Support for TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930 + fi fi fi if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN - dep_tristate 'Eicon.Diehl active card support (EXPERIMENTAL)' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN fi -dep_tristate 'AVM-B1 with CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN +dep_tristate 'Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN +if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then + bool 'Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA +fi +dep_tristate 'AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then + bool 'AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA + bool 'AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI + bool 'AVM T1/T1B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA + bool 'AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA bool 'Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON fi diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index 36a1f5bb7..04be19f9c 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile @@ -1,6 +1,6 @@ SUB_DIRS := MOD_SUB_DIRS := -ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon +ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon divert L_OBJS := LX_OBJS := @@ -24,6 +24,9 @@ ifeq ($(CONFIG_ISDN),y) endif ifdef CONFIG_ISDN_AUDIO L_OBJS += isdn_audio.o + ifdef CONFIG_ISDN_TTY_FAX + L_OBJS += isdn_ttyfax.o + endif endif else ifeq ($(CONFIG_ISDN),m) @@ -41,10 +44,19 @@ else endif ifdef CONFIG_ISDN_AUDIO O_OBJS += isdn_audio.o + ifdef CONFIG_ISDN_TTY_FAX + O_OBJS += isdn_ttyfax.o + endif endif endif endif +ifeq ($(CONFIG_ISDN_DIVERSION),y) + ifeq ($(CONFIG_MODULES),y) + MOD_SUB_DIRS += divert + endif +endif + ifeq ($(CONFIG_ISDN_DRV_HISAX),y) L_OBJS += hisax/hisax.o SUB_DIRS += hisax diff --git a/drivers/isdn/act2000/.cvsignore b/drivers/isdn/act2000/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/act2000/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags diff --git a/drivers/isdn/avmb1/.cvsignore b/drivers/isdn/avmb1/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/avmb1/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags diff --git a/drivers/isdn/avmb1/Makefile b/drivers/isdn/avmb1/Makefile index cce4af131..9f73ea2e3 100644 --- a/drivers/isdn/avmb1/Makefile +++ b/drivers/isdn/avmb1/Makefile @@ -1,5 +1,5 @@ # -# $Id: Makefile,v 1.4 1997/03/30 17:10:40 calle Exp $ +# $Id: Makefile,v 1.6 1999/07/20 06:41:44 calle Exp $ # # Makefile for the CAPI and AVM-B1 device drivers. # @@ -11,6 +11,26 @@ # parent makes.. # # $Log: Makefile,v $ +# Revision 1.6 1999/07/20 06:41:44 calle +# Bugfix: After the redesign of the AVM B1 driver, the driver didn't even +# compile, if not selected as modules. +# +# Revision 1.5 1999/07/01 15:26:20 calle +# complete new version (I love it): +# + new hardware independed "capi_driver" interface that will make it easy to: +# - support other controllers with CAPI-2.0 (i.e. USB Controller) +# - write a CAPI-2.0 for the passive cards +# - support serial link CAPI-2.0 boxes. +# + wrote "capi_driver" for all supported cards. +# + "capi_driver" (supported cards) now have to be configured with +# make menuconfig, in the past all supported cards where included +# at once. +# + new and better informations in /proc/capi/ +# + new ioctl to switch trace of capi messages per controller +# using "avmcapictrl trace [contr] on|off|...." +# + complete testcircle with all supported cards and also the +# PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. +# # Revision 1.4 1997/03/30 17:10:40 calle # added support for AVM-B1-PCI card. # @@ -56,20 +76,38 @@ L_TARGET := # used for .a targets (from L and LX objects) ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) O_TARGET += avmb1.o - O_OBJS += capi.o b1lli.o - OX_OBJS += capiutil.o b1capi.o capidrv.o - ifdef CONFIG_PCI - OX_OBJS += b1pci.o + OX_OBJS += kcapi.o + O_OBJS += capi.o + ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA + O_OBJS += b1isa.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI + O_OBJS += b1pci.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA + O_OBJS += t1isa.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + OX_OBJS += b1pcmcia.o endif + OX_OBJS += capiutil.o capidrv.o b1.o else ifeq ($(CONFIG_ISDN_DRV_AVMB1),m) O_TARGET += kernelcapi.o - O_OBJS += b1lli.o - OX_OBJS += b1capi.o + OX_OBJS += kcapi.o M_OBJS += capi.o kernelcapi.o - MX_OBJS += capiutil.o capidrv.o - ifdef CONFIG_PCI - MX_OBJS += b1pci.o + ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA + M_OBJS += b1isa.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI + M_OBJS += b1pci.o + endif + ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA + M_OBJS += t1isa.o + endif + MX_OBJS += capiutil.o capidrv.o b1.o + ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + MX_OBJS += b1pcmcia.o endif endif endif diff --git a/drivers/isdn/avmb1/avmcard.h b/drivers/isdn/avmb1/avmcard.h new file mode 100644 index 000000000..e94c5637c --- /dev/null +++ b/drivers/isdn/avmb1/avmcard.h @@ -0,0 +1,559 @@ +/* + * $Id: avmcard.h,v 1.4 1999/08/04 10:10:08 calle Exp $ + * + * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: avmcard.h,v $ + * Revision 1.4 1999/08/04 10:10:08 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.3 1999/07/23 08:41:47 calle + * prepared for new AVM cards. + * + * Revision 1.2 1999/07/05 15:09:45 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:22 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + */ + +#ifndef _AVMCARD_H_ +#define _AVMCARD_H_ + +#define AVMB1_PORTLEN 0x1f +#define AVM_MAXVERSION 8 +#define AVM_NAPPS 30 +#define AVM_NCCI_PER_CHANNEL 4 + +/* + * Versions + */ + +#define VER_DRIVER 0 +#define VER_CARDTYPE 1 +#define VER_HWID 2 +#define VER_SERIAL 3 +#define VER_OPTION 4 +#define VER_PROTO 5 +#define VER_PROFILE 6 +#define VER_CAPI 7 + +enum avmcardtype { + avm_b1isa, + avm_b1pci, + avm_b1pcmcia, + avm_m1, + avm_m2, + avm_t1isa, + avm_t1pci, + avm_c4 +}; + +typedef struct avmcard_dmainfo { + __u32 recvlen; + __u8 recvbuf[128+2048]; + struct sk_buff_head send_queue; + __u8 sendbuf[128+2048]; +} avmcard_dmainfo; + +typedef struct avmcard { + char name[32]; + unsigned int port; + unsigned irq; + unsigned long membase; + enum avmcardtype cardtype; + int cardnr; /* for t1isa */ + + int versionlen; + char versionbuf[1024]; + char *version[AVM_MAXVERSION]; + + char cardname[32]; + + char infobuf[128]; /* for function procinfo */ + char msgbuf[128]; /* capimsg msg part */ + char databuf[2048]; /* capimsg data part */ + + int interrupt; + + void *mbase; + __u32 csr; + avmcard_dmainfo *dma; + + struct capi_ctr *ctrl; +} avmcard; + +extern int b1_irq_table[16]; + +/* + * LLI Messages to the ISDN-ControllerISDN Controller + */ + +#define SEND_POLL 0x72 /* + * after load <- RECEIVE_POLL + */ +#define SEND_INIT 0x11 /* + * first message <- RECEIVE_INIT + * int32 NumApplications int32 + * NumNCCIs int32 BoardNumber + */ +#define SEND_REGISTER 0x12 /* + * register an application int32 + * ApplIDId int32 NumMessages + * int32 NumB3Connections int32 + * NumB3Blocks int32 B3Size + * + * AnzB3Connection != 0 && + * AnzB3Blocks >= 1 && B3Size >= 1 + */ +#define SEND_RELEASE 0x14 /* + * deregister an application int32 + * ApplID + */ +#define SEND_MESSAGE 0x15 /* + * send capi-message int32 length + * capi-data ... + */ +#define SEND_DATA_B3_REQ 0x13 /* + * send capi-data-message int32 + * MsgLength capi-data ... int32 + * B3Length data .... + */ + +#define SEND_CONFIG 0x21 /* + */ + +#define SEND_POLLACK 0x73 /* T1 Watchdog */ + +/* + * LLI Messages from the ISDN-ControllerISDN Controller + */ + +#define RECEIVE_POLL 0x32 /* + * <- after SEND_POLL + */ +#define RECEIVE_INIT 0x27 /* + * <- after SEND_INIT int32 length + * byte total length b1struct board + * driver revision b1struct card + * type b1struct reserved b1struct + * serial number b1struct driver + * capability b1struct d-channel + * protocol b1struct CAPI-2.0 + * profile b1struct capi version + */ +#define RECEIVE_MESSAGE 0x21 /* + * <- after SEND_MESSAGE int32 + * AppllID int32 Length capi-data + * .... + */ +#define RECEIVE_DATA_B3_IND 0x22 /* + * received data int32 AppllID + * int32 Length capi-data ... + * int32 B3Length data ... + */ +#define RECEIVE_START 0x23 /* + * Handshake + */ +#define RECEIVE_STOP 0x24 /* + * Handshake + */ +#define RECEIVE_NEW_NCCI 0x25 /* + * int32 AppllID int32 NCCI int32 + * WindowSize + */ +#define RECEIVE_FREE_NCCI 0x26 /* + * int32 AppllID int32 NCCI + */ +#define RECEIVE_RELEASE 0x26 /* + * int32 AppllID int32 0xffffffff + */ +#define RECEIVE_TASK_READY 0x31 /* + * int32 tasknr + * int32 Length Taskname ... + */ +#define RECEIVE_DEBUGMSG 0x71 /* + * int32 Length message + * + */ +#define RECEIVE_POLLDWORD 0x75 /* t1pci in dword mode */ + +#define WRITE_REGISTER 0x00 +#define READ_REGISTER 0x01 + +/* + * port offsets + */ + +#define B1_READ 0x00 +#define B1_WRITE 0x01 +#define B1_INSTAT 0x02 +#define B1_OUTSTAT 0x03 +#define B1_RESET 0x10 +#define B1_ANALYSE 0x04 + + +#define B1_STAT0(cardtype) ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l) +#define B1_STAT1(cardtype) (0x80E00000l) + +/* ---------------------------------------------------------------- */ + +static inline unsigned char b1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); + return inb(base + B1_ANALYSE); +} + + +static inline int b1_rx_full(unsigned int base) +{ + return inb(base + B1_INSTAT) & 0x1; +} + +static inline unsigned char b1_get_byte(unsigned int base) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + while (!b1_rx_full(base) && time_before(jiffies, stop)); + if (b1_rx_full(base)) + return inb(base + B1_READ); + printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base); + return 0; +} + +static inline unsigned int b1_get_word(unsigned int base) +{ + unsigned int val = 0; + val |= b1_get_byte(base); + val |= (b1_get_byte(base) << 8); + val |= (b1_get_byte(base) << 16); + val |= (b1_get_byte(base) << 24); + return val; +} + +static inline int b1_tx_empty(unsigned int base) +{ + return inb(base + B1_OUTSTAT) & 0x1; +} + +static inline void b1_put_byte(unsigned int base, unsigned char val) +{ + while (!b1_tx_empty(base)); + b1outp(base, B1_WRITE, val); +} + +static inline int b1_save_put_byte(unsigned int base, unsigned char val) +{ + unsigned long stop = jiffies + 2 * HZ; + while (!b1_tx_empty(base) && time_before(jiffies,stop)); + if (!b1_tx_empty(base)) return -1; + b1outp(base, B1_WRITE, val); + return 0; +} + +static inline void b1_put_word(unsigned int base, unsigned int val) +{ + b1_put_byte(base, val & 0xff); + b1_put_byte(base, (val >> 8) & 0xff); + b1_put_byte(base, (val >> 16) & 0xff); + b1_put_byte(base, (val >> 24) & 0xff); +} + +static inline unsigned int b1_get_slice(unsigned int base, + unsigned char *dp) +{ + unsigned int len, i; + + len = i = b1_get_word(base); + while (i-- > 0) *dp++ = b1_get_byte(base); + return len; +} + +static inline void b1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + b1_put_word(base, i); + while (i-- > 0) + b1_put_byte(base, *dp++); +} + +static void b1_wr_reg(unsigned int base, + unsigned int reg, + unsigned int value) +{ + b1_put_byte(base, WRITE_REGISTER); + b1_put_word(base, reg); + b1_put_word(base, value); +} + +static inline unsigned int b1_rd_reg(unsigned int base, + unsigned int reg) +{ + b1_put_byte(base, READ_REGISTER); + b1_put_word(base, reg); + return b1_get_word(base); + +} + +static inline void b1_reset(unsigned int base) +{ + b1outp(base, B1_RESET, 0); + udelay(55 * 2 * 1000); /* 2 TIC's */ + + b1outp(base, B1_RESET, 1); + udelay(55 * 2 * 1000); /* 2 TIC's */ + + b1outp(base, B1_RESET, 0); + udelay(55 * 2 * 1000); /* 2 TIC's */ +} + +static inline unsigned char b1_disable_irq(unsigned int base) +{ + return b1outp(base, B1_INSTAT, 0x00); +} + +/* ---------------------------------------------------------------- */ + +static inline void b1_set_test_bit(unsigned int base, + enum avmcardtype cardtype, + int onoff) +{ + b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20); +} + +static inline int b1_get_test_bit(unsigned int base, + enum avmcardtype cardtype) +{ + return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0; +} + +/* ---------------------------------------------------------------- */ + +#define T1_FASTLINK 0x00 +#define T1_SLOWLINK 0x08 + +#define T1_READ B1_READ +#define T1_WRITE B1_WRITE +#define T1_INSTAT B1_INSTAT +#define T1_OUTSTAT B1_OUTSTAT +#define T1_IRQENABLE 0x05 +#define T1_FIFOSTAT 0x06 +#define T1_RESETLINK 0x10 +#define T1_ANALYSE 0x11 +#define T1_IRQMASTER 0x12 +#define T1_IDENT 0x17 +#define T1_RESETBOARD 0x1f + +#define T1F_IREADY 0x01 +#define T1F_IHALF 0x02 +#define T1F_IFULL 0x04 +#define T1F_IEMPTY 0x08 +#define T1F_IFLAGS 0xF0 + +#define T1F_OREADY 0x10 +#define T1F_OHALF 0x20 +#define T1F_OEMPTY 0x40 +#define T1F_OFULL 0x80 +#define T1F_OFLAGS 0xF0 + +/* there are HEMA cards with 1k and 4k FIFO out */ +#define FIFO_OUTBSIZE 256 +#define FIFO_INPBSIZE 512 + +#define HEMA_VERSION_ID 0 +#define HEMA_PAL_ID 0 + +static inline void t1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); +} + +static inline unsigned char t1inp(unsigned int base, + unsigned short offset) +{ + return inb(base + offset); +} + +static inline int t1_isfastlink(unsigned int base) +{ + return (inb(base + T1_IDENT) & ~0x82) == 1; +} + +static inline unsigned char t1_fifostatus(unsigned int base) +{ + return inb(base + T1_FIFOSTAT); +} + +static inline unsigned int t1_get_slice(unsigned int base, + unsigned char *dp) +{ + unsigned int len, i; +#ifdef FASTLINK_DEBUG + unsigned wcnt = 0, bcnt = 0; +#endif + + len = i = b1_get_word(base); + if (t1_isfastlink(base)) { + int status; + while (i > 0) { + status = t1_fifostatus(base) & (T1F_IREADY|T1F_IHALF); + if (i >= FIFO_INPBSIZE) status |= T1F_IFULL; + + switch (status) { + case T1F_IREADY|T1F_IHALF|T1F_IFULL: + insb(base+B1_READ, dp, FIFO_INPBSIZE); + dp += FIFO_INPBSIZE; + i -= FIFO_INPBSIZE; +#ifdef FASTLINK_DEBUG + wcnt += FIFO_INPBSIZE; +#endif + break; + case T1F_IREADY|T1F_IHALF: + insb(base+B1_READ,dp, i); +#ifdef FASTLINK_DEBUG + wcnt += i; +#endif + dp += i; + i = 0; + if (i == 0) + break; + /* fall through */ + default: + *dp++ = b1_get_byte(base); + i--; +#ifdef FASTLINK_DEBUG + bcnt++; +#endif + break; + } + } +#ifdef FASTLINK_DEBUG + if (wcnt) + printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n", + base, len, wcnt, bcnt); +#endif + } else { + while (i-- > 0) + *dp++ = b1_get_byte(base); + } + return len; +} + +static inline void t1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + b1_put_word(base, i); + if (t1_isfastlink(base)) { + int status; + while (i > 0) { + status = t1_fifostatus(base) & (T1F_OREADY|T1F_OHALF); + if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY; + switch (status) { + case T1F_OREADY|T1F_OHALF|T1F_OEMPTY: + outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE); + dp += FIFO_OUTBSIZE; + i -= FIFO_OUTBSIZE; + break; + case T1F_OREADY|T1F_OHALF: + outsb(base+B1_WRITE, dp, i); + dp += i; + i = 0; + break; + default: + b1_put_byte(base, *dp++); + i--; + break; + } + } + } else { + while (i-- > 0) + b1_put_byte(base, *dp++); + } +} + +static inline void t1_disable_irq(unsigned int base) +{ + t1outp(base, T1_IRQMASTER, 0x00); +} + +static inline void t1_reset(unsigned int base) +{ + /* reset T1 Controller */ + b1_reset(base); + /* disable irq on HEMA */ + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_OUTSTAT, 0x00); + t1outp(base, T1_IRQMASTER, 0x00); + /* reset HEMA board configuration */ + t1outp(base, T1_RESETBOARD, 0xf); +} + +static inline void b1_setinterrupt(unsigned int base, unsigned irq, + enum avmcardtype cardtype) +{ + switch (cardtype) { + case avm_t1isa: + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_INSTAT, 0x02); + t1outp(base, T1_IRQMASTER, 0x08); + break; + case avm_b1isa: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, b1_irq_table[irq]); + b1outp(base, B1_INSTAT, 0x02); + break; + default: + case avm_m1: + case avm_m2: + case avm_b1pci: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, 0xf0); + b1outp(base, B1_INSTAT, 0x02); + break; + case avm_c4: + case avm_t1pci: + b1outp(base, B1_RESET, 0xf0); + break; + } +} + +int b1_detect(unsigned int base, enum avmcardtype cardtype); +int b1_load_t4file(unsigned int base, capiloaddatapart * t4file); +int b1_load_config(unsigned int base, capiloaddatapart * config); +int b1_loaded(unsigned int base); +int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data); +void b1_reset_ctr(struct capi_ctr *ctrl); +void b1_register_appl(struct capi_ctr *ctrl, __u16 appl, + capi_register_params *rp); +void b1_release_appl(struct capi_ctr *ctrl, __u16 appl); +void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +void b1_parse_version(avmcard *card); +void b1_handle_interrupt(avmcard * card); + +int b1ctl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl); + + +#endif /* _AVMCARD_H_ */ diff --git a/drivers/isdn/avmb1/b1.c b/drivers/isdn/avmb1/b1.c new file mode 100644 index 000000000..c4dffdacb --- /dev/null +++ b/drivers/isdn/avmb1/b1.c @@ -0,0 +1,693 @@ +/* + * $Id: b1.c,v 1.8 1999/08/22 20:26:22 calle Exp $ + * + * Common module for AVM B1 cards. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1.c,v $ + * Revision 1.8 1999/08/22 20:26:22 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.7 1999/08/04 10:10:09 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.6 1999/07/23 08:51:04 calle + * small fix and typo in checkin before. + * + * Revision 1.5 1999/07/23 08:41:48 calle + * prepared for new AVM cards. + * + * Revision 1.4 1999/07/09 15:05:38 keil + * compat.h is now isdn_compat.h + * + * Revision 1.3 1999/07/06 07:41:59 calle + * - changes in /proc interface + * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. + * + * Revision 1.2 1999/07/05 15:09:47 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:23 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <asm/io.h> +#include <linux/isdn_compat.h> +#include "capilli.h" +#include "avmcard.h" +#include "capicmd.h" +#include "capiutil.h" + +static char *revision = "$Revision: 1.8 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); + +/* ------------------------------------------------------------- */ + +int b1_irq_table[16] = +{0, + 0, + 0, + 192, /* irq 3 */ + 32, /* irq 4 */ + 160, /* irq 5 */ + 96, /* irq 6 */ + 224, /* irq 7 */ + 0, + 64, /* irq 9 */ + 80, /* irq 10 */ + 208, /* irq 11 */ + 48, /* irq 12 */ + 0, + 0, + 112, /* irq 15 */ +}; + +/* ------------------------------------------------------------- */ + +int b1_detect(unsigned int base, enum avmcardtype cardtype) +{ + int onoff, i; + + /* + * Statusregister 0000 00xx + */ + if ((inb(base + B1_INSTAT) & 0xfc) + || (inb(base + B1_OUTSTAT) & 0xfc)) + return 1; + /* + * Statusregister 0000 001x + */ + b1outp(base, B1_INSTAT, 0x2); /* enable irq */ + /* b1outp(base, B1_OUTSTAT, 0x2); */ + if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 + /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */) + return 2; + /* + * Statusregister 0000 000x + */ + b1outp(base, B1_INSTAT, 0x0); /* disable irq */ + b1outp(base, B1_OUTSTAT, 0x0); + if ((inb(base + B1_INSTAT) & 0xfe) + || (inb(base + B1_OUTSTAT) & 0xfe)) + return 3; + + for (onoff = !0, i= 0; i < 10 ; i++) { + b1_set_test_bit(base, cardtype, onoff); + if (b1_get_test_bit(base, cardtype) != onoff) + return 4; + onoff = !onoff; + } + + if (cardtype == avm_m1) + return 0; + + if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01) + return 5; + + return 0; +} + +int b1_load_t4file(unsigned int base, capiloaddatapart * t4file) +{ + unsigned char buf[256]; + unsigned char *dp; + int i, left, retval; + + dp = t4file->data; + left = t4file->len; + while (left > sizeof(buf)) { + if (t4file->user) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + } else { + memcpy(buf, dp, sizeof(buf)); + } + for (i = 0; i < sizeof(buf); i++) + if (b1_save_put_byte(base, buf[i]) < 0) { + printk(KERN_ERR "b1_load_t4file: corrupted t4 file ?\n"); + return -EIO; + } + left -= sizeof(buf); + dp += sizeof(buf); + } + if (left) { + if (t4file->user) { + retval = copy_from_user(buf, dp, left); + if (retval) + return -EFAULT; + } else { + memcpy(buf, dp, left); + } + for (i = 0; i < left; i++) + if (b1_save_put_byte(base, buf[i]) < 0) { + printk(KERN_ERR "b1_load_t4file: corrupted t4 file ?\n"); + return -EIO; + } + } + return 0; +} + +int b1_load_config(unsigned int base, capiloaddatapart * config) +{ + unsigned char buf[256]; + unsigned char *dp; + int i, j, left, retval; + + dp = config->data; + left = config->len; + if (left) { + b1_put_byte(base, SEND_CONFIG); + b1_put_word(base, 1); + b1_put_byte(base, SEND_CONFIG); + b1_put_word(base, left); + } + while (left > sizeof(buf)) { + if (config->user) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + } else { + memcpy(buf, dp, sizeof(buf)); + } + for (i = 0; i < sizeof(buf); ) { + b1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + b1_put_byte(base, buf[i++]); + } + } + left -= sizeof(buf); + dp += sizeof(buf); + } + if (left) { + if (config->user) { + retval = copy_from_user(buf, dp, left); + if (retval) + return -EFAULT; + } else { + memcpy(buf, dp, left); + } + for (i = 0; i < left; ) { + b1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + if (i < left) + b1_put_byte(base, buf[i++]); + else + b1_put_byte(base, 0); + } + } + } + return 0; +} + +int b1_loaded(unsigned int base) +{ + unsigned long stop; + unsigned char ans; + unsigned long tout = 2; + + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_tx_empty(base)) + break; + } + if (!b1_tx_empty(base)) { + printk(KERN_ERR "b1_loaded: tx err, corrupted t4 file ?\n"); + return 0; + } + b1_put_byte(base, SEND_POLL); + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_rx_full(base)) { + if ((ans = b1_get_byte(base)) == RECEIVE_POLL) { + return 1; + } + printk(KERN_ERR "b1_loaded: got 0x%x, firmware not running\n", ans); + return 0; + } + } + printk(KERN_ERR "b1_loaded: firmware not running\n"); + return 0; +} + +/* ------------------------------------------------------------- */ + +int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + int retval; + + b1_reset(port); + + if ((retval = b1_load_t4file(port, &data->firmware))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + b1_disable_irq(port); + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(port, &data->configuration))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1_loaded(port)) { + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + save_flags(flags); + cli(); + b1_setinterrupt(port, card->irq, card->cardtype); + b1_put_byte(port, SEND_INIT); + b1_put_word(port, AVM_NAPPS); + b1_put_word(port, AVM_NCCI_PER_CHANNEL*2); + b1_put_word(port, ctrl->cnr - 1); + restore_flags(flags); + + return 0; +} + +void b1_reset_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + memset(card->version, 0, sizeof(card->version)); + ctrl->reseted(ctrl); +} + +void b1_register_appl(struct capi_ctr *ctrl, + __u16 appl, + capi_register_params *rp) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + int nconn, want = rp->level3cnt; + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel; + + save_flags(flags); + cli(); + b1_put_byte(port, SEND_REGISTER); + b1_put_word(port, appl); + b1_put_word(port, 1024 * (nconn+1)); + b1_put_word(port, nconn); + b1_put_word(port, rp->datablkcnt); + b1_put_word(port, rp->datablklen); + restore_flags(flags); + + ctrl->appl_registered(ctrl, appl); +} + +void b1_release_appl(struct capi_ctr *ctrl, __u16 appl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + + save_flags(flags); + cli(); + b1_put_byte(port, SEND_RELEASE); + b1_put_word(port, appl); + restore_flags(flags); +} + +void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + __u16 len = CAPIMSG_LEN(skb->data); + __u8 cmd = CAPIMSG_COMMAND(skb->data); + __u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + save_flags(flags); + cli(); + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + b1_put_byte(port, SEND_DATA_B3_REQ); + b1_put_slice(port, skb->data, len); + b1_put_slice(port, skb->data + len, dlen); + } else { + b1_put_byte(port, SEND_MESSAGE); + b1_put_slice(port, skb->data, len); + } + restore_flags(flags); + dev_kfree_skb(skb); +} + +/* ------------------------------------------------------------- */ + +void b1_parse_version(avmcard *card) +{ + struct capi_ctr *ctrl = card->ctrl; + capi_profile *profp; + __u8 *dversion; + __u8 flag; + int i, j; + + for (j = 0; j < AVM_MAXVERSION; j++) + card->version[j] = "\0\0" + 1; + for (i = 0, j = 0; + j < AVM_MAXVERSION && i < card->versionlen; + j++, i += card->versionbuf[i] + 1) + card->version[j] = &card->versionbuf[i + 1]; + + strncpy(ctrl->serial, card->version[VER_SERIAL], CAPI_SERIAL_LEN); + memcpy(&ctrl->profile, card->version[VER_PROFILE],sizeof(capi_profile)); + strncpy(ctrl->manu, "AVM GmbH", CAPI_MANUFACTURER_LEN); + dversion = card->version[VER_DRIVER]; + ctrl->version.majorversion = 2; + ctrl->version.minorversion = 0; + ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); + ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf); + ctrl->version.minormanuversion = (dversion[3] - '0') << 4; + ctrl->version.minormanuversion |= + (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); + + profp = &ctrl->profile; + + flag = ((__u8 *)(profp->manu))[1]; + switch (flag) { + case 0: if (card->version[VER_CARDTYPE]) + strcpy(card->cardname, card->version[VER_CARDTYPE]); + else strcpy(card->cardname, "B1"); + break; + case 3: strcpy(card->cardname,"PCMCIA B"); break; + case 4: strcpy(card->cardname,"PCMCIA M1"); break; + case 5: strcpy(card->cardname,"PCMCIA M2"); break; + case 6: strcpy(card->cardname,"B1 V3.0"); break; + case 7: strcpy(card->cardname,"B1 PCI"); break; + default: sprintf(card->cardname, "AVM?%u", (unsigned int)flag); break; + } + printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n", + card->name, ctrl->cnr, card->cardname); + + flag = ((__u8 *)(profp->manu))[3]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n", + ctrl->cnr, + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + + flag = ((__u8 *)(profp->manu))[5]; + if (flag) + printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n", + card->name, + ctrl->cnr, + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); +} + +/* ------------------------------------------------------------- */ + +void b1_handle_interrupt(avmcard * card) +{ + struct capi_ctr *ctrl = card->ctrl; + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + + if (!b1_rx_full(card->port)) + return; + + b1cmd = b1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + DataB3Len = b1_get_slice(card->port, card->databuf); + + if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + CAPIMSG_SETDATA(skb->data, skb->data + MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + WindowSize = b1_get_word(card->port); + + ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + + if (NCCI != 0xffffffff) + ctrl->free_ncci(ctrl, ApplId, NCCI); + else ctrl->appl_released(ctrl, ApplId); + break; + + case RECEIVE_START: + /* b1_put_byte(card->port, SEND_POLLACK); */ + ctrl->resume_output(ctrl); + break; + + case RECEIVE_STOP: + ctrl->suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + card->versionlen = b1_get_slice(card->port, card->versionbuf); + b1_parse_version(card); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + card->version[VER_CARDTYPE], + card->version[VER_DRIVER]); + ctrl->ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = b1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + case 0xff: + printk(KERN_ERR "%s: card removed ?\n", card->name); + return; + default: + printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ +int b1ctl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + __u8 flag; + int len = 0; + char *s; + + len += sprintf(page+len, "%-16s %s\n", "name", card->name); + len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port); + len += sprintf(page+len, "%-16s %d\n", "irq", card->irq); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + default: s = "???"; break; + } + len += sprintf(page+len, "%-16s %s\n", "type", s); + if (card->cardtype == avm_t1isa) + len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr); + if ((s = card->version[VER_DRIVER]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); + if ((s = card->version[VER_CARDTYPE]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); + if ((s = card->version[VER_SERIAL]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((__u8 *)(ctrl->profile.manu))[3]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((__u8 *)(ctrl->profile.manu))[5]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + len += sprintf(page+len, "%-16s %s\n", "cardname", card->cardname); + + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +EXPORT_SYMBOL(b1_irq_table); + +EXPORT_SYMBOL(b1_detect); +EXPORT_SYMBOL(b1_load_t4file); +EXPORT_SYMBOL(b1_load_config); +EXPORT_SYMBOL(b1_loaded); +EXPORT_SYMBOL(b1_load_firmware); +EXPORT_SYMBOL(b1_reset_ctr); +EXPORT_SYMBOL(b1_register_appl); +EXPORT_SYMBOL(b1_release_appl); +EXPORT_SYMBOL(b1_send_message); + +EXPORT_SYMBOL(b1_parse_version); +EXPORT_SYMBOL(b1_handle_interrupt); + +EXPORT_SYMBOL(b1ctl_read_proc); + +#ifdef MODULE +#define b1_init init_module +void cleanup_module(void); +#endif + +int b1_init(void) +{ + char *p; + char rev[10]; + + if ((p = strchr(revision, ':'))) { + strncpy(rev, p + 1, sizeof(rev)); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, "1.0"); + + printk(KERN_INFO "b1: revision %s\n", rev); + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ +} +#endif diff --git a/drivers/isdn/avmb1/b1isa.c b/drivers/isdn/avmb1/b1isa.c new file mode 100644 index 000000000..9ab198432 --- /dev/null +++ b/drivers/isdn/avmb1/b1isa.c @@ -0,0 +1,243 @@ +/* + * $Id: b1isa.c,v 1.4 1999/08/22 20:26:24 calle Exp $ + * + * Module for AVM B1 ISA-card. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1isa.c,v $ + * Revision 1.4 1999/08/22 20:26:24 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.3 1999/07/09 15:05:40 keil + * compat.h is now isdn_compat.h + * + * Revision 1.2 1999/07/05 15:09:49 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:27 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <asm/io.h> +#include <linux/isdn_compat.h> +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.4 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static void b1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "b1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name); + return; + } + + card->interrupt = 1; + + b1_handle_interrupt(card); + + card->interrupt = 0; +} +/* ------------------------------------------------------------- */ + +static void b1isa_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------- */ + +static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + avmcard *card; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "b1isa: no memory.\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + sprintf(card->name, "b1isa-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->cardtype = avm_b1isa; + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1isa: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + if (b1_irq_table[card->irq & 0xf] == 0) { + printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq); + kfree(card); + return -EINVAL; + } + if ( card->port != 0x150 && card->port != 0x250 + && card->port != 0x300 && card->port != 0x340) { + printk(KERN_WARNING "b1isa: illegal port 0x%x.\n", card->port); + kfree(card); + return -EINVAL; + } + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n", + card->port, retval); + kfree(card); + return -EIO; + } + b1_reset(card->port); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, b1isa_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + card->ctrl = di->attach_ctr(driver, card->name, card); + if (!card->ctrl) { + printk(KERN_ERR "b1isa: attach controller failed.\n"); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + MOD_INC_USE_COUNT; + return 0; +} + +static char *b1isa_procinfo(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + if (!card) + return ""; + sprintf(card->infobuf, "%s %s 0x%x %d", + card->cardname[0] ? card->cardname : "-", + card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", + card->port, card->irq + ); + return card->infobuf; +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver b1isa_driver = { + "b1isa", + "0.0", + b1_load_firmware, + b1_reset_ctr, + b1isa_remove_ctr, + b1_register_appl, + b1_release_appl, + b1_send_message, + + b1isa_procinfo, + b1ctl_read_proc, + 0, /* use standard driver_read_proc */ + + b1isa_add_card, +}; + +#ifdef MODULE +#define b1isa_init init_module +void cleanup_module(void); +#endif + +int b1isa_init(void) +{ + struct capi_driver *driver = &b1isa_driver; + char *p; + + if ((p = strchr(revision, ':'))) { + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); + *p = 0; + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + detach_capi_driver(&b1isa_driver); +} +#endif diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index 112ddbb81..8c0006a78 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,59 +1,65 @@ /* - * $Id: b1pci.c,v 1.9 1999/04/15 19:49:32 calle Exp $ + * $Id: b1pci.c,v 1.16 1999/08/11 21:01:07 keil Exp $ * * Module for AVM B1 PCI-card. * - * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ - * Revision 1.9 1999/04/15 19:49:32 calle - * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ... + * Revision 1.16 1999/08/11 21:01:07 keil + * new PCI codefix * - * Revision 1.8 1998/06/17 19:51:16 he - * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay()) - * brute force fix to avoid Ugh's in isdn_tty_write() - * cleaned up some dead code + * Revision 1.15 1999/08/10 16:02:27 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. * - * Revision 1.7 1998/03/29 16:06:02 calle - * changes from 2.0 tree merged. + * Revision 1.14 1999/07/09 15:05:41 keil + * compat.h is now isdn_compat.h * - * Revision 1.2.2.2 1998/01/23 16:49:30 calle - * added functions for pcmcia cards, - * avmb1_addcard returns now the controller number. + * Revision 1.13 1999/07/05 15:09:50 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. * - * Revision 1.6 1998/02/25 09:15:36 fritz - * apply Martin's pci driver patch to isdn drivers (vgerCVS) + * Revision 1.12 1999/07/01 15:26:29 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. * - * Revision 1.5 1998/01/31 11:14:43 calle - * merged changes to 2.0 tree, prepare 2.1.82 to work. - * - * Revision 1.4 1997/12/10 20:00:50 calle - * get changes from 2.0 version - * - * Revision 1.3 1997/10/01 09:21:14 fritz - * Removed old compatibility stuff for 2.0.X kernels. - * From now on, this code is for 2.1.X ONLY! - * Old stuff is still in the separate branch. - * - * Revision 1.2 1997/05/18 09:24:13 calle - * added verbose disconnect reason reporting to avmb1. - * some fixes in capi20 interface. - * changed info messages for B1-PCI - * - * Revision 1.1 1997/03/30 17:10:42 calle - * added support for AVM-B1-PCI card. * */ #include <linux/config.h> -#include <linux/string.h> #include <linux/module.h> #include <linux/kernel.h> -#include <linux/pci.h> #include <linux/skbuff.h> -#include "compat.h" +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/pci.h> #include <linux/capi.h> -#include <linux/b1lli.h> +#include <asm/io.h> +#include <linux/isdn_compat.h> +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.16 $"; + +/* ------------------------------------------------------------- */ #ifndef PCI_VENDOR_ID_AVM #define PCI_VENDOR_ID_AVM 0x1244 @@ -63,65 +69,213 @@ #define PCI_DEVICE_ID_AVM_B1 0x700 #endif -static char *revision = "$Revision: 1.9 $"; - /* ------------------------------------------------------------- */ MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); /* ------------------------------------------------------------- */ +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static void b1pci_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "b1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name); + return; + } + + card->interrupt = 1; + + b1_handle_interrupt(card); + + card->interrupt = 0; +} +/* ------------------------------------------------------------- */ + +static void b1pci_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + ctrl->driverdata = 0; + kfree(card); + + MOD_DEC_USE_COUNT; +} + /* ------------------------------------------------------------- */ -/* -------- Init & Cleanup ------------------------------------- */ + +static char *b1pci_procinfo(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + if (!card) + return ""; + sprintf(card->infobuf, "%s %s 0x%x %d", + card->cardname[0] ? card->cardname : "-", + card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", + card->port, card->irq + ); + return card->infobuf; +} + /* ------------------------------------------------------------- */ -/* - * init / exit functions - */ +static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + avmcard *card; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "b1pci: no memory.\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + sprintf(card->name, "b1pci-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->cardtype = avm_b1pci; + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1pci: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n", + card->port, retval); + kfree(card); + return -EIO; + } + b1_reset(card->port); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, b1pci_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + card->ctrl = di->attach_ctr(driver, card->name, card); + if (!card->ctrl) { + printk(KERN_ERR "b1pci: attach controller failed.\n"); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + MOD_INC_USE_COUNT; + + return 0; +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver b1pci_driver = { + "b1pci", + "0.0", + b1_load_firmware, + b1_reset_ctr, + b1pci_remove_ctr, + b1_register_appl, + b1_release_appl, + b1_send_message, + + b1pci_procinfo, + b1ctl_read_proc, + 0, /* use standard driver_read_proc */ + + 0, /* no add_card function */ +}; #ifdef MODULE #define b1pci_init init_module +void cleanup_module(void); #endif +static int ncards = 0; + int b1pci_init(void) { - char *p; - char rev[10]; - int rc; + struct capi_driver *driver = &b1pci_driver; struct pci_dev *dev = NULL; + char *p; + int retval; if ((p = strchr(revision, ':'))) { - strcpy(rev, p + 1); - p = strchr(rev, '$'); + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); *p = 0; - } else - strcpy(rev, " ??? "); + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } #ifdef CONFIG_PCI if (!pci_present()) { - printk(KERN_ERR "b1pci: no PCI bus present\n"); + printk(KERN_ERR "%s: no PCI bus present\n", driver->name); + detach_capi_driver(driver); return -EIO; } - printk(KERN_INFO "b1pci: revision %s\n", rev); - while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev))) { - unsigned int ioaddr = dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; - unsigned int irq = dev->irq; + struct capicardparams param; + + param.port = get_pcibase(dev, 1) & PCI_BASE_ADDRESS_IO_MASK; + param.irq = dev->irq; printk(KERN_INFO - "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", - ioaddr, irq); - if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1PCI)) != 0) { + "%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", + driver->name, param.port, param.irq); + retval = b1pci_add_card(driver, ¶m); + if (retval != 0) { printk(KERN_ERR - "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n", - ioaddr, irq); - return rc; + "%s: no AVM-B1 at i/o %#x, irq %d detected\n", + driver->name, param.port, param.irq); +#ifdef MODULE + cleanup_module(); +#endif + return retval; } - if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1PCI)) < 0) - return rc; + ncards++; } - return 0; + if (ncards) { + printk(KERN_INFO "%s: %d B1-PCI card(s) detected\n", + driver->name, ncards); + return 0; + } + printk(KERN_ERR "%s: NO B1-PCI card detected\n", driver->name); + return -ESRCH; #else printk(KERN_ERR "b1pci: kernel not compiled with PCI.\n"); return -EIO; @@ -131,5 +285,6 @@ int b1pci_init(void) #ifdef MODULE void cleanup_module(void) { + detach_capi_driver(&b1pci_driver); } #endif diff --git a/drivers/isdn/avmb1/b1pcmcia.c b/drivers/isdn/avmb1/b1pcmcia.c new file mode 100644 index 000000000..d6acda9d9 --- /dev/null +++ b/drivers/isdn/avmb1/b1pcmcia.c @@ -0,0 +1,272 @@ +/* + * $Id: b1pcmcia.c,v 1.4 1999/08/22 20:26:26 calle Exp $ + * + * Module for AVM B1/M1/M2 PCMCIA-card. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1pcmcia.c,v $ + * Revision 1.4 1999/08/22 20:26:26 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.3 1999/07/09 15:05:41 keil + * compat.h is now isdn_compat.h + * + * Revision 1.2 1999/07/05 15:09:51 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:30 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <linux/capi.h> +#include <linux/b1pcmcia.h> +#include <linux/isdn_compat.h> +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.4 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static void b1pcmcia_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "b1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "b1_interrupt: reentering interrupt hander (%s)\n", card->name); + return; + } + + card->interrupt = 1; + + b1_handle_interrupt(card); + + card->interrupt = 0; +} +/* ------------------------------------------------------------- */ + +static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + /* io addrsses managent by CardServices + * release_region(card->port, AVMB1_PORTLEN); + */ + kfree(card); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------- */ + +static int b1pcmcia_add_card(struct capi_driver *driver, + unsigned int port, + unsigned irq, + enum avmcardtype cardtype) +{ + avmcard *card; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "b1pcmcia: no memory.\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + switch (cardtype) { + case avm_m1: sprintf(card->name, "m1-%x", port); break; + case avm_m2: sprintf(card->name, "m2-%x", port); break; + default: sprintf(card->name, "b1pcmcia-%x", port); break; + } + card->port = port; + card->irq = irq; + card->cardtype = cardtype; + + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n", + card->port, retval); + kfree(card); + return -EIO; + } + b1_reset(card->port); + + retval = request_irq(card->irq, b1pcmcia_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n", card->irq); + kfree(card); + return -EBUSY; + } + + card->ctrl = di->attach_ctr(driver, card->name, card); + if (!card->ctrl) { + printk(KERN_ERR "b1pcmcia: attach controller failed.\n"); + free_irq(card->irq, card); + kfree(card); + return -EBUSY; + } + + MOD_INC_USE_COUNT; + return card->ctrl->cnr; +} + +/* ------------------------------------------------------------- */ + +static char *b1pcmcia_procinfo(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + if (!card) + return ""; + sprintf(card->infobuf, "%s %s 0x%x %d", + card->cardname[0] ? card->cardname : "-", + card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", + card->port, card->irq + ); + return card->infobuf; +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver b1pcmcia_driver = { + "b1pcmcia", + "0.0", + b1_load_firmware, + b1_reset_ctr, + b1pcmcia_remove_ctr, + b1_register_appl, + b1_release_appl, + b1_send_message, + + b1pcmcia_procinfo, + b1ctl_read_proc, + 0, /* use standard driver_read_proc */ + + 0, +}; + +/* ------------------------------------------------------------- */ + +int b1pcmcia_addcard_b1(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(&b1pcmcia_driver, port, irq, avm_b1pcmcia); +} + +int b1pcmcia_addcard_m1(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(&b1pcmcia_driver, port, irq, avm_m1); +} + +int b1pcmcia_addcard_m2(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(&b1pcmcia_driver, port, irq, avm_m2); +} + +int b1pcmcia_delcard(unsigned int port, unsigned irq) +{ + struct capi_ctr *ctrl; + avmcard *card; + + for (ctrl = b1pcmcia_driver.controller; ctrl; ctrl = ctrl->next) { + card = (avmcard *)(ctrl->driverdata); + if (card->port == port && card->irq == irq) { + b1pcmcia_remove_ctr(ctrl); + return 0; + } + } + return -ESRCH; +} + +EXPORT_SYMBOL(b1pcmcia_addcard_b1); +EXPORT_SYMBOL(b1pcmcia_addcard_m1); +EXPORT_SYMBOL(b1pcmcia_addcard_m2); +EXPORT_SYMBOL(b1pcmcia_delcard); + +/* ------------------------------------------------------------- */ + +#ifdef MODULE +#define b1pcmcia_init init_module +void cleanup_module(void); +#endif + +int b1pcmcia_init(void) +{ + struct capi_driver *driver = &b1pcmcia_driver; + char *p; + + if ((p = strchr(revision, ':'))) { + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); + *p = 0; + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + detach_capi_driver(&b1pcmcia_driver); +} +#endif diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 69ed317f3..c8cab6919 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -1,11 +1,43 @@ /* - * $Id: capi.c,v 1.13 1998/08/28 04:32:25 calle Exp $ + * $Id: capi.c,v 1.19 1999/07/09 15:05:42 keil Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capi.c,v $ + * Revision 1.19 1999/07/09 15:05:42 keil + * compat.h is now isdn_compat.h + * + * Revision 1.18 1999/07/06 07:42:01 calle + * - changes in /proc interface + * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. + * + * Revision 1.17 1999/07/01 15:26:30 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * Revision 1.16 1999/07/01 08:22:57 keil + * compatibility macros now in <linux/isdn_compat.h> + * + * Revision 1.15 1999/06/21 15:24:11 calle + * extend information in /proc. + * + * Revision 1.14 1999/06/10 16:51:03 calle + * Bugfix: open/release of control device was not handled correct. + * * Revision 1.13 1998/08/28 04:32:25 calle * Added patch send by Michael.Mueller4@post.rwth-aachen.de, to get AVM B1 * driver running with 2.1.118. @@ -80,11 +112,12 @@ #include <linux/timer.h> #include <linux/wait.h> #include <linux/skbuff.h> +#include <linux/proc_fs.h> #include <linux/poll.h> #include <linux/capi.h> #include <linux/kernelcapi.h> -#include "compat.h" +#include <linux/isdn_compat.h> #include "capiutil.h" #include "capicmd.h" #include "capidev.h" @@ -178,7 +211,10 @@ static ssize_t capi_read(struct file *file, char *buf, } copied = skb->len; - + if (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) + cdev->nrecvdatapkt++; + else cdev->nrecvctlpkt++; kfree_skb(skb); return copied; @@ -207,7 +243,7 @@ static ssize_t capi_write(struct file *file, const char *buf, skb = alloc_skb(count, GFP_USER); if ((retval = copy_from_user(skb_put(skb, count), buf, count))) { - dev_kfree_skb(skb); + kfree_skb(skb); return retval; } cmd = CAPIMSG_COMMAND(skb->data); @@ -216,11 +252,11 @@ static ssize_t capi_write(struct file *file, const char *buf, if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { __u16 dlen = CAPIMSG_DATALEN(skb->data); if (mlen + dlen != count) { - dev_kfree_skb(skb); + kfree_skb(skb); return -EINVAL; } } else if (mlen != count) { - dev_kfree_skb(skb); + kfree_skb(skb); return -EINVAL; } CAPIMSG_SETAPPID(skb->data, cdev->applid); @@ -228,9 +264,12 @@ static ssize_t capi_write(struct file *file, const char *buf, cdev->errcode = (*capifuncs->capi_put_message) (cdev->applid, skb); if (cdev->errcode) { - dev_kfree_skb(skb); + kfree_skb(skb); return -EIO; } + if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) + cdev->nsentdatapkt++; + else cdev->nsentctlpkt++; return count; } @@ -426,16 +465,13 @@ static int capi_open(struct inode *inode, struct file *file) capidevs[minor].is_open = 1; skb_queue_head_init(&capidevs[minor].recv_queue); MOD_INC_USE_COUNT; + capidevs[minor].nopen++; } else { - - if (!capidevs[minor].is_open) { - capidevs[minor].is_open = 1; - MOD_INC_USE_COUNT; - } + capidevs[minor].is_open++; + MOD_INC_USE_COUNT; } - return 0; } @@ -460,10 +496,13 @@ capi_release(struct inode *inode, struct file *file) cdev->is_registered = 0; cdev->applid = 0; - while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) + while ((skb = skb_dequeue(&cdev->recv_queue)) != 0) { kfree_skb(skb); + } + cdev->is_open = 0; + } else { + cdev->is_open--; } - cdev->is_open = 0; MOD_DEC_USE_COUNT; return 0; @@ -487,7 +526,82 @@ static struct file_operations capi_fops = NULL, /* capi_fasync */ }; +/* -------- /proc functions ----------------------------------- */ +/* + * /proc/capi/capi20: + * minor opencount nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt + */ +static int proc_capidev_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capidev *cp; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXMINOR; i++) { + cp = &capidevs[i+1]; + if (cp->nopen == 0) continue; + len += sprintf(page+len, "%d %lu %lu %lu %lu %lu\n", + i+1, + cp->nopen, + cp->nrecvctlpkt, + cp->nrecvdatapkt, + cp->nsentctlpkt, + cp->nsentdatapkt); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXMINOR) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +static struct procfsentries { + char *name; + mode_t mode; + int (*read_proc)(char *page, char **start, off_t off, + int count, int *eof, void *data); + struct proc_dir_entry *procent; +} procfsentries[] = { + /* { "capi", S_IFDIR, 0 }, */ + { "capi/capi20", 0 , proc_capidev_read_proc }, +}; + +static void proc_init(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=0; i < nelem; i++) { + struct procfsentries *p = procfsentries + i; + p->procent = create_proc_entry(p->name, p->mode, 0); + if (p->procent) p->procent->read_proc = p->read_proc; + } +} + +static void proc_exit(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=nelem-1; i >= 0; i--) { + struct procfsentries *p = procfsentries + i; + if (p->procent) { + remove_proc_entry(p->name, 0); + p->procent = 0; + } + } +} /* -------- init function and module interface ---------------------- */ #ifdef MODULE @@ -501,12 +615,12 @@ static struct capi_interface_user cuser = { int capi_init(void) { -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ int j; #endif memset(capidevs, 0, sizeof(capidevs)); -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ for ( j = 0; j < CAPI_MAXMINOR+1; j++ ) { init_waitqueue_head(&capidevs[j].recv_wait); } @@ -522,13 +636,14 @@ int capi_init(void) unregister_chrdev(capi_major, "capi20"); return -EIO; } - + (void)proc_init(); return 0; } #ifdef MODULE void cleanup_module(void) { + (void)proc_exit(); unregister_chrdev(capi_major, "capi20"); (void) detach_capi_interface(&cuser); } diff --git a/drivers/isdn/avmb1/capidev.h b/drivers/isdn/avmb1/capidev.h index bd57255b6..c66554a29 100644 --- a/drivers/isdn/avmb1/capidev.h +++ b/drivers/isdn/avmb1/capidev.h @@ -1,11 +1,33 @@ /* - * $Id: capidev.h,v 1.1 1997/03/04 21:50:30 calle Exp $ + * $Id: capidev.h,v 1.4 1999/07/01 15:26:32 calle Exp $ * * CAPI 2.0 Interface for Linux * * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidev.h,v $ + * Revision 1.4 1999/07/01 15:26:32 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * Revision 1.3 1999/07/01 08:22:58 keil + * compatibility macros now in <linux/isdn_compat.h> + * + * Revision 1.2 1999/06/21 15:24:13 calle + * extend information in /proc. + * * Revision 1.1 1997/03/04 21:50:30 calle * Frirst version in isdn4linux * @@ -22,12 +44,18 @@ struct capidev { int is_registered; __u16 applid; struct sk_buff_head recv_queue; -#if LINUX_VERSION_CODE < 131841 - struct wait_queue *recv_wait; -#else +#ifdef COMPAT_HAS_NEW_WAITQ wait_queue_head_t recv_wait; +#else + struct wait_queue *recv_wait; #endif __u16 errcode; + /* Statistic */ + unsigned long nopen; + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; }; #define CAPI_MAXMINOR CAPI_MAXAPPL diff --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c index 80a2eccdb..fb83e52a6 100644 --- a/drivers/isdn/avmb1/capidrv.c +++ b/drivers/isdn/avmb1/capidrv.c @@ -1,11 +1,67 @@ /* - * $Id: capidrv.c,v 1.13 1998/06/26 15:12:55 fritz Exp $ + * $Id: capidrv.c,v 1.26 1999/08/06 07:41:16 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.26 1999/08/06 07:41:16 calle + * Added the "vbox patch". if (si1 == 1) si2 = 0; + * + * Revision 1.25 1999/08/04 10:10:11 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.24 1999/07/20 06:48:02 calle + * Bugfix: firmware version check for D2 trace was too restrictiv. + * + * Revision 1.23 1999/07/09 15:05:44 keil + * compat.h is now isdn_compat.h + * + * Revision 1.22 1999/07/06 07:24:14 calle + * Bugfix: call to kfree_skb in capidrv_signal was too early, + * thanks to Lars Heete <hel@admin.de>. + * + * Revision 1.21 1999/07/01 15:26:34 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * Revision 1.20 1999/07/01 08:22:59 keil + * compatibility macros now in <linux/isdn_compat.h> + * + * Revision 1.19 1999/06/29 16:16:54 calle + * Let ISDN_CMD_UNLOAD work with open isdn devices without crash again. + * Also right unlocking (ISDN_CMD_UNLOCK) is done now. + * isdnlog should check returncode of read(2) calls. + * + * Revision 1.18 1999/06/21 15:24:15 calle + * extend information in /proc. + * + * Revision 1.17 1999/06/10 16:53:55 calle + * Removing of module b1pci will now remove card from lower level. + * + * Revision 1.16 1999/05/31 11:50:33 calle + * Bugfix: In if_sendbuf, skb_push'ed DATA_B3 header was not skb_pull'ed + * on failure, result in data block with DATA_B3 header transmitted + * + * Revision 1.15 1999/05/25 21:26:16 calle + * Include CAPI-Channelallocation (leased lines) from the 2.0 tree. + * + * Revision 1.14 1999/05/22 07:55:06 calle + * Added *V110* to AVM B1 driver. + * * Revision 1.13 1998/06/26 15:12:55 fritz * Added handling of STAT_ICALL with incomplete CPN. * Added AT&L for ttyI emulator. @@ -96,15 +152,18 @@ #include <linux/skbuff.h> #include <linux/isdn.h> #include <linux/isdnif.h> +#include <linux/proc_fs.h> #include <linux/capi.h> #include <linux/kernelcapi.h> +#include <linux/ctype.h> +#include <asm/segment.h> -#include "compat.h" +#include <linux/isdn_compat.h> #include "capiutil.h" #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.13 $"; +static char *revision = "$Revision: 1.26 $"; int debugmode = 0; MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); @@ -160,6 +219,7 @@ struct capidrv_contr { __u16 msgid; /* to identfy CONNECT_CONF */ int chan; int state; + int leasedline; struct capidrv_ncci { struct capidrv_ncci *next; struct capidrv_plci *plcip; @@ -194,6 +254,12 @@ struct capidrv_data { __u16 appid; int ncontr; struct capidrv_contr *contr_list; + + /* statistic */ + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; }; typedef struct capidrv_plci capidrv_plci; @@ -224,6 +290,12 @@ static inline __u32 b1prot(int l2, int l3) return 0; case ISDN_PROTO_L2_TRANS: return 1; + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: + return 2; + case ISDN_PROTO_L2_FAX: + return 4; } } @@ -237,7 +309,12 @@ static inline __u32 b2prot(int l2, int l3) return 0; case ISDN_PROTO_L2_HDLC: case ISDN_PROTO_L2_TRANS: + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: return 1; + case ISDN_PROTO_L2_FAX: + return 4; } } @@ -249,8 +326,45 @@ static inline __u32 b3prot(int l2, int l3) case ISDN_PROTO_L2_X75BUI: case ISDN_PROTO_L2_HDLC: case ISDN_PROTO_L2_TRANS: + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: default: return 0; + case ISDN_PROTO_L2_FAX: + return 4; + } +} + +static _cstruct b1config_sync_v110(__u16 rate) +{ + /* CAPI-Spec "B1 Configuration" */ + static unsigned char buf[9]; + buf[0] = 8; /* len */ + /* maximum bitrate */ + buf[1] = rate & 0xff; buf[2] = (rate >> 8) & 0xff; + buf[3] = buf[4] = 0; /* reserved, bits per character */ + buf[5] = buf[6] = 0; /* reserved, parity */ + buf[7] = buf[9] = 0; /* reserved, stop bits */ + return buf; +} + +static _cstruct b1config(int l2, int l3) +{ + switch (l2) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + case ISDN_PROTO_L2_HDLC: + case ISDN_PROTO_L2_TRANS: + default: + return 0; + case ISDN_PROTO_L2_V11096: + return b1config_sync_v110(9600); + case ISDN_PROTO_L2_V11019: + return b1config_sync_v110(19200); + case ISDN_PROTO_L2_V11038: + return b1config_sync_v110(38400); } } @@ -522,9 +636,10 @@ static void send_message(capidrv_contr * card, _cmsg * cmsg) size_t len; capi_cmsg2message(cmsg, cmsg->buf); len = CAPIMSG_LEN(cmsg->buf); - skb = dev_alloc_skb(len); + skb = alloc_skb(len, GFP_ATOMIC); memcpy(skb_put(skb, len), cmsg->buf, len); (*capifuncs->capi_put_message) (global.appid, skb); + global.nsentctlpkt++; } /* -------- state machine -------------------------------------------- */ @@ -917,6 +1032,13 @@ static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) cmd.parm.setup.si2, cmd.parm.setup.eazmsn); + if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) { + printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n", + card->contrnr, + cmd.parm.setup.si2); + cmd.parm.setup.si2 = 0; + } + switch (card->interface.statcallb(&cmd)) { case 0: case 3: @@ -1353,6 +1475,7 @@ static void handle_data(_cmsg * cmsg, struct sk_buff *skb) printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController & 0x7f); + kfree_skb(skb); return; } if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { @@ -1377,13 +1500,14 @@ static void capidrv_signal(__u16 applid, __u32 dummy) while ((*capifuncs->capi_get_message) (global.appid, &skb) == CAPI_NOERROR) { capi_message2cmsg(&s_cmsg, skb->data); - if (debugmode > 1) + if (debugmode > 2) printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n", applid, capi_cmsg2str(&s_cmsg)); if (s_cmsg.Command == CAPI_DATA_B3 && s_cmsg.Subcommand == CAPI_IND) { handle_data(&s_cmsg, skb); + global.nrecvdatapkt++; continue; } if ((s_cmsg.adr.adrController & 0xffffff00) == 0) @@ -1392,8 +1516,13 @@ static void capidrv_signal(__u16 applid, __u32 dummy) handle_plci(&s_cmsg); else handle_ncci(&s_cmsg); - + /* + * data of skb used in s_cmsg, + * free data when s_cmsg is not used again + * thanks to Lars Heete <hel@admin.de> + */ kfree_skb(skb); + global.nrecvctlpkt++; } } @@ -1459,6 +1588,11 @@ static _cmsg cmdcmsg; static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) { switch (c->arg) { + case 1: + debugmode = (int)(*((unsigned int *)c->parm.num)); + printk(KERN_DEBUG "capidrv-%d: debugmode=%d\n", + card->contrnr, debugmode); + return 0; default: printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n", card->contrnr, c->arg); @@ -1467,11 +1601,103 @@ static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) return -EINVAL; } +/* + * Handle leased lines (CAPI-Bundling) + */ + +struct internal_bchannelinfo { + unsigned short channelalloc; + unsigned short operation; + unsigned char cmask[31]; +}; + +static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep) +{ + unsigned long bmask = 0; + int active = !0; + char *s; + int i; + + if (strncmp(teln, "FV:", 3) != 0) + return 1; + s = teln + 3; + while (*s && *s == ' ') s++; + if (!*s) return -2; + if (*s == 'p' || *s == 'P') { + active = 0; + s++; + } + if (*s == 'a' || *s == 'A') { + active = !0; + s++; + } + while (*s) { + int digit1 = 0; + int digit2 = 0; + if (!isdigit(*s)) return -3; + while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; } + if (digit1 <= 0 && digit1 > 30) return -4; + if (*s == 0 || *s == ',' || *s == ' ') { + bmask |= (1 << digit1); + digit1 = 0; + if (*s) s++; + continue; + } + if (*s != '-') return -5; + s++; + if (!isdigit(*s)) return -3; + while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; } + if (digit2 <= 0 && digit2 > 30) return -4; + if (*s == 0 || *s == ',' || *s == ' ') { + if (digit1 > digit2) + for (i = digit2; i <= digit1 ; i++) + bmask |= (1 << i); + else + for (i = digit1; i <= digit2 ; i++) + bmask |= (1 << i); + digit1 = digit2 = 0; + if (*s) s++; + continue; + } + return -6; + } + if (activep) *activep = active; + if (bmaskp) *bmaskp = bmask; + return 0; +} + +static int FVteln2capi20(char *teln, __u8 AdditionalInfo[1+2+2+31]) +{ + unsigned long bmask; + int active; + int rc, i; + + rc = decodeFVteln(teln, &bmask, &active); + if (rc) return rc; + /* Length */ + AdditionalInfo[0] = 2+2+31; + /* Channel: 3 => use channel allocation */ + AdditionalInfo[1] = 3; AdditionalInfo[2] = 0; + /* Operation: 0 => DTE mode, 1 => DCE mode */ + if (active) { + AdditionalInfo[3] = 0; AdditionalInfo[4] = 0; + } else { + AdditionalInfo[3] = 1; AdditionalInfo[4] = 0; + } + /* Channel mask array */ + AdditionalInfo[5] = 0; /* no D-Channel */ + for (i=1; i <= 30; i++) + AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0; + return 0; +} + static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) { isdn_ctrl cmd; struct capidrv_bchan *bchan; struct capidrv_plci *plcip; + __u8 AdditionalInfo[1+2+2+31]; + int rc, isleasedline = 0; if (c->command == ISDN_CMD_IOCTL) return capidrv_ioctl(c, card); @@ -1508,15 +1734,25 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num)); strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum)); - - calling[0] = strlen(bchan->mynum) + 2; - calling[1] = 0; - calling[2] = 0x80; - strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN); - - called[0] = strlen(bchan->num) + 1; - called[1] = 0x80; - strncpy(called + 2, bchan->num, ISDN_MSNLEN); + rc = FVteln2capi20(bchan->num, AdditionalInfo); + isleasedline = (rc == 0); + if (rc < 0) + printk(KERN_ERR "capidrv-%d: WARNING: illegal leased linedefinition \"%s\"\n", card->contrnr, bchan->num); + + if (isleasedline) { + calling[0] = 0; + called[0] = 0; + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr); + } else { + calling[0] = strlen(bchan->mynum) + 2; + calling[1] = 0; + calling[2] = 0x80; + strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN); + called[0] = strlen(bchan->num) + 1; + called[1] = 0x80; + strncpy(called + 2, bchan->num, ISDN_MSNLEN); + } capi_fill_CONNECT_REQ(&cmdcmsg, global.appid, @@ -1530,13 +1766,14 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) b1prot(bchan->l2, bchan->l3), /* B1protocol */ b2prot(bchan->l2, bchan->l3), /* B2protocol */ b3prot(bchan->l2, bchan->l3), /* B3protocol */ - 0, /* B1configuration */ + b1config(bchan->l2, bchan->l3), /* B1configuration */ 0, /* B2configuration */ 0, /* B3configuration */ 0, /* BC */ 0, /* LLC */ 0, /* HLC */ - 0, /* BChannelinformation */ + /* BChannelinformation */ + isleasedline ? AdditionalInfo : 0, 0, /* Keypadfacility */ 0, /* Useruserdata */ 0 /* Facilitydataarray */ @@ -1549,6 +1786,7 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) return -1; } plcip->msgid = cmdcmsg.Messagenumber; + plcip->leasedline = isleasedline; plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ); send_message(card, &cmdcmsg); return 0; @@ -1570,7 +1808,7 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) b1prot(bchan->l2, bchan->l3), /* B1protocol */ b2prot(bchan->l2, bchan->l3), /* B2protocol */ b3prot(bchan->l2, bchan->l3), /* B3protocol */ - 0, /* B1configuration */ + b1config(bchan->l2, bchan->l3), /* B1configuration */ 0, /* B2configuration */ 0, /* B3configuration */ 0, /* ConnectedNumber */ @@ -1752,6 +1990,9 @@ static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) card->contrnr, id); return 0; } + if (debugmode > 1) + printk(KERN_DEBUG "capidrv-%d: sendbuf len=%d skb=%p doack=%d\n", + card->contrnr, len, skb, doack); bchan = &card->bchans[channel % card->nbchan]; nccip = bchan->nccip; if (!nccip || nccip->state != ST_NCCI_ACTIVE) { @@ -1774,23 +2015,23 @@ static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) capi_cmsg2message(&sendcmsg, sendcmsg.buf); msglen = CAPIMSG_LEN(sendcmsg.buf); if (skb_headroom(skb) < msglen) { - struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len); + struct sk_buff *nskb = skb_realloc_headroom(skb, msglen); if (!nskb) { printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n", card->contrnr); (void)capidrv_del_ack(nccip, datahandle); return 0; } -#if 0 - printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom\n", - card->contrnr, skb_headroom(skb)); +#if 1 + printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom, need %d\n", + card->contrnr, skb_headroom(skb), msglen); #endif - memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen); - memcpy(skb_put(nskb, skb->len), skb->data, skb->len); + memcpy(skb_push(nskb, msglen), sendcmsg.buf, msglen); errcode = (*capifuncs->capi_put_message) (global.appid, nskb); if (errcode == CAPI_NOERROR) { dev_kfree_skb(skb); nccip->datahandle++; + global.nsentdatapkt++; return len; } (void)capidrv_del_ack(nccip, datahandle); @@ -1801,8 +2042,10 @@ static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) errcode = (*capifuncs->capi_put_message) (global.appid, skb); if (errcode == CAPI_NOERROR) { nccip->datahandle++; + global.nsentdatapkt++; return len; } + skb_pull(skb, msglen); (void)capidrv_del_ack(nccip, datahandle); return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; } @@ -1847,8 +2090,8 @@ static void enable_dchannel_trace(capidrv_contr *card) return; } if (strstr(manufacturer, "AVM") == 0) { - printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n", - card->name); + printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n", + card->name, manufacturer); return; } errcode = (*capifuncs->capi_get_version)(contr, &version); @@ -1862,7 +2105,7 @@ static void enable_dchannel_trace(capidrv_contr *card) avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; avmversion[2] |= version.minormanuversion & 0x0f; - if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 6)) { + if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { printk(KERN_INFO "%s: D2 trace enabled\n", card->name); capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, card->msgid++, @@ -1884,6 +2127,51 @@ static void enable_dchannel_trace(capidrv_contr *card) send_message(card, &cmdcmsg); } +static void disable_dchannel_trace(capidrv_contr *card) +{ + __u8 manufacturer[CAPI_MANUFACTURER_LEN]; + capi_version version; + __u16 contr = card->contrnr; + __u16 errcode; + __u16 avmversion[3]; + + errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n", + card->name, errcode); + return; + } + if (strstr(manufacturer, "AVM") == 0) { + printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n", + card->name, manufacturer); + return; + } + errcode = (*capifuncs->capi_get_version)(contr, &version); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get version (0x%x)\n", + card->name, errcode); + return; + } + avmversion[0] = (version.majormanuversion >> 4) & 0x0f; + avmversion[1] = (version.majormanuversion << 4) & 0xf0; + avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; + avmversion[2] |= version.minormanuversion & 0x0f; + + if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { + printk(KERN_INFO "%s: D2 trace disabled\n", card->name); + } else { + printk(KERN_INFO "%s: D3 trace disabled\n", card->name); + } + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\000\000\000\000"); + send_message(card, &cmdcmsg); +} + static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) { capidrv_contr *card; @@ -1920,6 +2208,13 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS | ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_L2_V11096 | + ISDN_FEATURE_L2_V11019 | + ISDN_FEATURE_L2_V11038 | +#if 0 + ISDN_FEATURE_L2_FAX | + ISDN_FEATURE_L3_FAX | +#endif ISDN_FEATURE_P_UNKNOWN; card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); @@ -1964,8 +2259,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) printk(KERN_INFO "%s: now up (%d B channels)\n", card->name, card->nbchan); - if (card->nbchan == 2) /* no T1 */ - enable_dchannel_trace(card); + enable_dchannel_trace(card); return 0; } @@ -1985,6 +2279,15 @@ static int capidrv_delcontr(__u16 contr) return -1; } card = *pp; + + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n", + card->contrnr, card->myid); + + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + *pp = (*pp)->next; global.ncontr--; @@ -1998,10 +2301,6 @@ static int capidrv_delcontr(__u16 contr) } kfree(card->bchans); - cmd.command = ISDN_STAT_UNLOAD; - cmd.driver = card->myid; - card->interface.statcallb(&cmd); - printk(KERN_INFO "%s: now down.\n", card->name); kfree(card); @@ -2012,16 +2311,78 @@ static int capidrv_delcontr(__u16 contr) static void lower_callback(unsigned int cmd, __u16 contr, void *data) { + switch (cmd) { case KCI_CONTRUP: + printk(KERN_INFO "capidrv: controller %hu up\n", contr); (void) capidrv_addcontr(contr, (capi_profile *) data); break; case KCI_CONTRDOWN: + printk(KERN_INFO "capidrv: controller %hu down\n", contr); (void) capidrv_delcontr(contr); break; } } +/* + * /proc/capi/capidrv: + * nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt + */ +static int proc_capidrv_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len += sprintf(page+len, "%lu %lu %lu %lu\n", + global.nrecvctlpkt, + global.nrecvdatapkt, + global.nsentctlpkt, + global.nsentdatapkt); + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +static struct procfsentries { + char *name; + mode_t mode; + int (*read_proc)(char *page, char **start, off_t off, + int count, int *eof, void *data); + struct proc_dir_entry *procent; +} procfsentries[] = { + /* { "capi", S_IFDIR, 0 }, */ + { "capi/capidrv", 0 , proc_capidrv_read_proc }, +}; + +static void proc_init(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=0; i < nelem; i++) { + struct procfsentries *p = procfsentries + i; + p->procent = create_proc_entry(p->name, p->mode, 0); + if (p->procent) p->procent->read_proc = p->read_proc; + } +} + +static void proc_exit(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=nelem-1; i >= 0; i--) { + struct procfsentries *p = procfsentries + i; + if (p->procent) { + remove_proc_entry(p->name, 0); + p->procent = 0; + } + } +} + static struct capi_interface_user cuser = { "capidrv", lower_callback @@ -2077,6 +2438,7 @@ int capidrv_init(void) continue; (void) capidrv_addcontr(contr, &profile); } + proc_init(); return 0; } @@ -2098,11 +2460,13 @@ void cleanup_module(void) for (card = global.contr_list; card; card = next) { next = card->next; + disable_dchannel_trace(card); capidrv_delcontr(card->contrnr); } (void) (*capifuncs->capi_release) (global.appid); detach_capi_interface(&cuser); + proc_exit(); printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev); } diff --git a/drivers/isdn/avmb1/capilli.h b/drivers/isdn/avmb1/capilli.h new file mode 100644 index 000000000..da64a183b --- /dev/null +++ b/drivers/isdn/avmb1/capilli.h @@ -0,0 +1,110 @@ +/* + * $Id: capilli.h,v 1.4 1999/07/23 08:51:05 calle Exp $ + * + * Kernel CAPI 2.0 Driver Interface for Linux + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + */ +#ifndef __CAPILLI_H__ +#define __CAPILLI_H__ + +typedef struct capiloaddatapart { + int user; /* data in userspace ? */ + int len; + unsigned char *data; +} capiloaddatapart; + +typedef struct capiloaddata { + capiloaddatapart firmware; + capiloaddatapart configuration; +} capiloaddata; + +typedef struct capicardparams { + unsigned int port; + unsigned irq; + int cardtype; + int cardnr; + unsigned int membase; +} capicardparams; + +struct capi_driver; + +struct capi_ctr { + struct capi_ctr *next; /* next ctr of same driver */ + struct capi_driver *driver; + int cnr; /* controller number */ + char name[32]; /* name of controller */ + volatile unsigned short cardstate; /* controller state */ + volatile int blocked; /* output blocked */ + int traceflag; /* capi trace */ + + void *driverdata; /* driver specific */ + + /* filled before calling ready callback */ + __u8 manu[CAPI_MANUFACTURER_LEN]; /* CAPI_GET_MANUFACTURER */ + capi_version version; /* CAPI_GET_VERSION */ + capi_profile profile; /* CAPI_GET_PROFILE */ + __u8 serial[CAPI_SERIAL_LEN]; /* CAPI_GET_SERIAL */ + + /* functions */ + void (*ready)(struct capi_ctr * card); + void (*reseted)(struct capi_ctr * card); + void (*suspend_output)(struct capi_ctr * card); + void (*resume_output)(struct capi_ctr * card); + void (*handle_capimsg)(struct capi_ctr * card, + __u16 appl, struct sk_buff *skb); + void (*appl_registered)(struct capi_ctr * card, __u16 appl); + void (*appl_released)(struct capi_ctr * card, __u16 appl); + + void (*new_ncci)(struct capi_ctr * card, + __u16 appl, __u32 ncci, __u32 winsize); + void (*free_ncci)(struct capi_ctr * card, __u16 appl, __u32 ncci); + + /* management information for kcapi */ + + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; + + struct proc_dir_entry *procent; + char procfn[128]; +}; + +struct capi_driver_interface { + struct capi_ctr *(*attach_ctr)(struct capi_driver *driver, char *name, void *data); + int (*detach_ctr)(struct capi_ctr *); +}; + +struct capi_driver { + char name[32]; /* driver name */ + char revision[32]; + int (*load_firmware)(struct capi_ctr *, capiloaddata *); + void (*reset_ctr)(struct capi_ctr *); + void (*remove_ctr)(struct capi_ctr *); + void (*register_appl)(struct capi_ctr *, __u16 appl, + capi_register_params *); + void (*release_appl)(struct capi_ctr *, __u16 appl); + void (*send_message)(struct capi_ctr *, struct sk_buff *skb); + + char *(*procinfo)(struct capi_ctr *); + int (*ctr_read_proc)(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *card); + int (*driver_read_proc)(char *page, char **start, off_t off, + int count, int *eof, struct capi_driver *driver); + + int (*add_card)(struct capi_driver *driver, capicardparams *data); + + /* intitialized by kcapi */ + struct capi_ctr *controller; /* list of controllers */ + struct capi_driver *next; + int ncontroller; + struct proc_dir_entry *procent; + char procfn[128]; +}; + +struct capi_driver_interface *attach_capi_driver(struct capi_driver *driver); +void detach_capi_driver(struct capi_driver *driver); + +#endif /* __CAPILLI_H__ */ diff --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c index 8eb7d3ae1..7f1a9f1ad 100644 --- a/drivers/isdn/avmb1/capiutil.c +++ b/drivers/isdn/avmb1/capiutil.c @@ -1,5 +1,5 @@ /* - * $Id: capiutil.c,v 1.6 1997/11/04 06:12:12 calle Exp $ + * $Id: capiutil.c,v 1.9 1999/07/09 15:05:46 keil Exp $ * * CAPI 2.0 convert capi message to capi message struct * @@ -7,6 +7,28 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.c,v $ + * Revision 1.9 1999/07/09 15:05:46 keil + * compat.h is now isdn_compat.h + * + * Revision 1.8 1999/07/01 15:26:37 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * Revision 1.7 1999/07/01 08:23:01 keil + * compatibility macros now in <linux/isdn_compat.h> + * * Revision 1.6 1997/11/04 06:12:12 calle * capi.c: new read/write in file_ops since 2.1.60 * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. @@ -48,7 +70,7 @@ #include <asm/segment.h> #include <linux/config.h> -#include "compat.h" +#include <linux/isdn_compat.h> #include "capiutil.h" /* from CAPI2.0 DDK AVM Berlin GmbH */ diff --git a/drivers/isdn/avmb1/kcapi.c b/drivers/isdn/avmb1/kcapi.c new file mode 100644 index 000000000..fc58f1241 --- /dev/null +++ b/drivers/isdn/avmb1/kcapi.c @@ -0,0 +1,1553 @@ +/* + * $Id: kcapi.c,v 1.6 1999/07/20 06:41:49 calle Exp $ + * + * Kernel CAPI 2.0 Module + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: kcapi.c,v $ + * Revision 1.6 1999/07/20 06:41:49 calle + * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even + * compile, if not selected as modules. + * + * Revision 1.5 1999/07/09 15:05:48 keil + * compat.h is now isdn_compat.h + * + * Revision 1.4 1999/07/08 14:15:17 calle + * Forgot to count down ncards in drivercb_detach_ctr. + * + * Revision 1.3 1999/07/06 07:42:02 calle + * - changes in /proc interface + * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. + * + * Revision 1.2 1999/07/05 15:09:52 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:42 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + */ +#define CONFIG_AVMB1_COMPAT + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <asm/segment.h> +#include <linux/proc_fs.h> +#include <linux/skbuff.h> +#include <linux/tqueue.h> +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <linux/isdn_compat.h> +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#ifdef CONFIG_AVMB1_COMPAT +#include <linux/b1lli.h> +#endif + +static char *revision = "$Revision: 1.6 $"; + +/* ------------------------------------------------------------- */ + +#define CARD_FREE 0 +#define CARD_DETECTED 1 +#define CARD_LOADING 2 +#define CARD_RUNNING 3 + +/* ------------------------------------------------------------- */ + +int showcapimsgs = 0; + +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); +MODULE_PARM(showcapimsgs, "0-4i"); + +/* ------------------------------------------------------------- */ + +struct msgidqueue { + struct msgidqueue *next; + __u16 msgid; +}; + +struct capi_ncci { + struct capi_ncci *next; + __u16 applid; + __u32 ncci; + __u32 winsize; + int nmsg; + struct msgidqueue *msgidqueue; + struct msgidqueue *msgidlast; + struct msgidqueue *msgidfree; + struct msgidqueue msgidpool[CAPI_MAXDATAWINDOW]; +}; + +struct capi_appl { + __u16 applid; + capi_register_params rparam; + int releasing; + __u32 param; + void (*signal) (__u16 applid, __u32 param); + struct sk_buff_head recv_queue; + int nncci; + struct capi_ncci *nccilist; + + unsigned long nrecvctlpkt; + unsigned long nrecvdatapkt; + unsigned long nsentctlpkt; + unsigned long nsentdatapkt; +}; + +/* ------------------------------------------------------------- */ + +static struct capi_version driver_version = {2, 0, 1, 1<<4}; +static char driver_serial[CAPI_SERIAL_LEN] = "4711"; +static char capi_manufakturer[64] = "AVM Berlin"; + +#define APPL(a) (&applications[(a)-1]) +#define VALID_APPLID(a) ((a) && (a) <= CAPI_MAXAPPL && APPL(a)->applid == a) +#define APPL_IS_FREE(a) (APPL(a)->applid == 0) +#define APPL_MARK_FREE(a) do{ APPL(a)->applid=0; MOD_DEC_USE_COUNT; }while(0); +#define APPL_MARK_USED(a) do{ APPL(a)->applid=(a); MOD_INC_USE_COUNT; }while(0); + +#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) + +#define VALID_CARD(c) ((c) > 0 && (c) <= CAPI_MAXCONTR) +#define CARD(c) (&cards[(c)-1]) +#define CARDNR(cp) (((cp)-cards)+1) + +static struct capi_appl applications[CAPI_MAXAPPL]; +static struct capi_ctr cards[CAPI_MAXCONTR]; +static int ncards = 0; +static struct sk_buff_head recv_queue; +static struct capi_interface_user *capi_users = 0; +static struct capi_driver *drivers; +#ifdef CONFIG_AVMB1_COMPAT +static struct capi_driver *b1isa_driver; +static struct capi_driver *t1isa_driver; +#endif +static long notify_up_set = 0; +static long notify_down_set = 0; + +static struct tq_struct tq_state_notify; +static struct tq_struct tq_recv_notify; + +/* -------- util functions ------------------------------------ */ + +static char *cardstate2str(unsigned short cardstate) +{ + switch (cardstate) { + default: + case CARD_FREE: return "free"; + case CARD_DETECTED: return "detected"; + case CARD_LOADING: return "loading"; + case CARD_RUNNING: return "running"; + } +} + +static inline int capi_cmd_valid(__u8 cmd) +{ + switch (cmd) { + case CAPI_ALERT: + case CAPI_CONNECT: + case CAPI_CONNECT_ACTIVE: + case CAPI_CONNECT_B3_ACTIVE: + case CAPI_CONNECT_B3: + case CAPI_CONNECT_B3_T90_ACTIVE: + case CAPI_DATA_B3: + case CAPI_DISCONNECT_B3: + case CAPI_DISCONNECT: + case CAPI_FACILITY: + case CAPI_INFO: + case CAPI_LISTEN: + case CAPI_MANUFACTURER: + case CAPI_RESET_B3: + case CAPI_SELECT_B_PROTOCOL: + return 1; + } + return 0; +} + +static inline int capi_subcmd_valid(__u8 subcmd) +{ + switch (subcmd) { + case CAPI_REQ: + case CAPI_CONF: + case CAPI_IND: + case CAPI_RESP: + return 1; + } + return 0; +} + +/* -------- /proc functions ----------------------------------- */ +/* + * /proc/capi/applications: + * applid l3cnt dblkcnt dblklen #ncci recvqueuelen + */ +static int proc_applications_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_appl *ap; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXAPPL; i++) { + ap = &applications[i]; + if (ap->applid == 0) continue; + len += sprintf(page+len, "%u %d %d %d %d %d\n", + ap->applid, + ap->rparam.level3cnt, + ap->rparam.datablkcnt, + ap->rparam.datablklen, + ap->nncci, + skb_queue_len(&ap->recv_queue)); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXAPPL) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/ncci: + * applid ncci winsize nblk + */ +static int proc_ncci_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_appl *ap; + struct capi_ncci *np; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXAPPL; i++) { + ap = &applications[i]; + if (ap->applid == 0) continue; + for (np = ap->nccilist; np; np = np->next) { + len += sprintf(page+len, "%d 0x%x %d %d\n", + np->applid, + np->ncci, + np->winsize, + np->nmsg); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } + } +endloop: + if (i >= CAPI_MAXAPPL) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/driver: + * driver ncontroller + */ +static int proc_driver_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_driver *driver; + int len = 0; + off_t begin = 0; + + for (driver = drivers; driver; driver = driver->next) { + len += sprintf(page+len, "%-32s %d %s\n", + driver->name, + driver->ncontroller, + driver->revision); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (!driver) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/users: + * name + */ +static int proc_users_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_interface_user *cp; + int len = 0; + off_t begin = 0; + + for (cp = capi_users; cp ; cp = cp->next) { + len += sprintf(page+len, "%s\n", cp->name); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (cp == 0) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/controller: + * cnr driver cardstate name driverinfo + */ +static int proc_controller_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_ctr *cp; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXCONTR; i++) { + cp = &cards[i]; + if (cp->cardstate == CARD_FREE) continue; + len += sprintf(page+len, "%d %-10s %-8s %-16s %s\n", + cp->cnr, cp->driver->name, + cardstate2str(cp->cardstate), + cp->name, + cp->driver->procinfo ? cp->driver->procinfo(cp) : "" + ); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXCONTR) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/applstats: + * applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt + */ +static int proc_applstats_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_appl *ap; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXAPPL; i++) { + ap = &applications[i]; + if (ap->applid == 0) continue; + len += sprintf(page+len, "%u %lu %lu %lu %lu\n", + ap->applid, + ap->nrecvctlpkt, + ap->nrecvdatapkt, + ap->nsentctlpkt, + ap->nsentdatapkt); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXAPPL) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * /proc/capi/contrstats: + * cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt + */ +static int proc_contrstats_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_ctr *cp; + int i; + int len = 0; + off_t begin = 0; + + for (i=0; i < CAPI_MAXCONTR; i++) { + cp = &cards[i]; + if (cp->cardstate == CARD_FREE) continue; + len += sprintf(page+len, "%d %lu %lu %lu %lu\n", + cp->cnr, + cp->nrecvctlpkt, + cp->nrecvdatapkt, + cp->nsentctlpkt, + cp->nsentdatapkt); + if (len+begin > off+count) + goto endloop; + if (len+begin < off) { + begin += len; + len = 0; + } + } +endloop: + if (i >= CAPI_MAXCONTR) + *eof = 1; + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +static struct procfsentries { + char *name; + mode_t mode; + int (*read_proc)(char *page, char **start, off_t off, + int count, int *eof, void *data); + struct proc_dir_entry *procent; +} procfsentries[] = { + { "capi", S_IFDIR, 0 }, + { "capi/applications", 0 , proc_applications_read_proc }, + { "capi/ncci", 0 , proc_ncci_read_proc }, + { "capi/driver", 0 , proc_driver_read_proc }, + { "capi/users", 0 , proc_users_read_proc }, + { "capi/controller", 0 , proc_controller_read_proc }, + { "capi/applstats", 0 , proc_applstats_read_proc }, + { "capi/contrstats", 0 , proc_contrstats_read_proc }, + { "capi/drivers", S_IFDIR, 0 }, + { "capi/controllers", S_IFDIR, 0 }, +}; + +static void proc_capi_init(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=0; i < nelem; i++) { + struct procfsentries *p = procfsentries + i; + p->procent = create_proc_entry(p->name, p->mode, 0); + if (p->procent) p->procent->read_proc = p->read_proc; + } +} + +static void proc_capi_exit(void) +{ + int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]); + int i; + + for (i=nelem-1; i >= 0; i--) { + struct procfsentries *p = procfsentries + i; + if (p->procent) { + remove_proc_entry(p->name, 0); + p->procent = 0; + } + } +} + +/* -------- NCCI Handling ------------------------------------- */ + +static inline void mq_init(struct capi_ncci * np) +{ + int i; + np->msgidqueue = 0; + np->msgidlast = 0; + np->nmsg = 0; + memset(np->msgidpool, 0, sizeof(np->msgidpool)); + np->msgidfree = &np->msgidpool[0]; + for (i = 1; i < np->winsize; i++) { + np->msgidpool[i].next = np->msgidfree; + np->msgidfree = &np->msgidpool[i]; + } +} + +static inline int mq_enqueue(struct capi_ncci * np, __u16 msgid) +{ + struct msgidqueue *mq; + if ((mq = np->msgidfree) == 0) + return 0; + np->msgidfree = mq->next; + mq->msgid = msgid; + mq->next = 0; + if (np->msgidlast) + np->msgidlast->next = mq; + np->msgidlast = mq; + if (!np->msgidqueue) + np->msgidqueue = mq; + np->nmsg++; + return 1; +} + +static inline int mq_dequeue(struct capi_ncci * np, __u16 msgid) +{ + struct msgidqueue **pp; + for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) { + if ((*pp)->msgid == msgid) { + struct msgidqueue *mq = *pp; + *pp = mq->next; + if (mq == np->msgidlast) + np->msgidlast = 0; + mq->next = np->msgidfree; + np->msgidfree = mq; + np->nmsg--; + return 1; + } + } + return 0; +} + +static void controllercb_appl_registered(struct capi_ctr * card, __u16 appl) +{ +} + +static void controllercb_appl_released(struct capi_ctr * card, __u16 appl) +{ + struct capi_ncci **pp, **nextpp; + for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) { + if (NCCI2CTRL((*pp)->ncci) == card->cnr) { + struct capi_ncci *np = *pp; + *pp = np->next; + printk(KERN_INFO "kcapi: appl %d ncci 0x%x down!\n", appl, np->ncci); + kfree(np); + APPL(appl)->nncci--; + nextpp = pp; + } else { + nextpp = &(*pp)->next; + } + } + APPL(appl)->releasing--; + if (APPL(appl)->releasing <= 0) { + APPL(appl)->signal = 0; + APPL_MARK_FREE(appl); + printk(KERN_INFO "kcapi: appl %d down\n", appl); + } +} +/* + * ncci managment + */ + +static void controllercb_new_ncci(struct capi_ctr * card, + __u16 appl, __u32 ncci, __u32 winsize) +{ + struct capi_ncci *np; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "avmb1_handle_new_ncci: illegal appl %d\n", appl); + return; + } + if ((np = (struct capi_ncci *) kmalloc(sizeof(struct capi_ncci), GFP_ATOMIC)) == 0) { + printk(KERN_ERR "capi_new_ncci: alloc failed ncci 0x%x\n", ncci); + return; + } + if (winsize > CAPI_MAXDATAWINDOW) { + printk(KERN_ERR "capi_new_ncci: winsize %d too big, set to %d\n", + winsize, CAPI_MAXDATAWINDOW); + winsize = CAPI_MAXDATAWINDOW; + } + np->applid = appl; + np->ncci = ncci; + np->winsize = winsize; + mq_init(np); + np->next = APPL(appl)->nccilist; + APPL(appl)->nccilist = np; + APPL(appl)->nncci++; + printk(KERN_INFO "kcapi: appl %d ncci 0x%x up\n", appl, ncci); + +} + +static void controllercb_free_ncci(struct capi_ctr * card, + __u16 appl, __u32 ncci) +{ + struct capi_ncci **pp; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "free_ncci: illegal appl %d\n", appl); + return; + } + for (pp = &APPL(appl)->nccilist; *pp; pp = &(*pp)->next) { + if ((*pp)->ncci == ncci) { + struct capi_ncci *np = *pp; + *pp = np->next; + kfree(np); + APPL(appl)->nncci--; + printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", appl, ncci); + return; + } + } + printk(KERN_ERR "free_ncci: ncci 0x%x not found\n", ncci); +} + + +static struct capi_ncci *find_ncci(struct capi_appl * app, __u32 ncci) +{ + struct capi_ncci *np; + for (np = app->nccilist; np; np = np->next) { + if (np->ncci == ncci) + return np; + } + return 0; +} + +/* -------- Receiver ------------------------------------------ */ + +static void recv_handler(void *dummy) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&recv_queue)) != 0) { + __u16 appl = CAPIMSG_APPID(skb->data); + struct capi_ncci *np; + if (!VALID_APPLID(appl)) { + printk(KERN_ERR "kcapi: recv_handler: applid %d ? (%s)\n", + appl, capi_message2str(skb->data)); + kfree_skb(skb); + continue; + } + if (APPL(appl)->signal == 0) { + printk(KERN_ERR "kcapi: recv_handler: applid %d has no signal function\n", + appl); + kfree_skb(skb); + continue; + } + if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF + && (np = find_ncci(APPL(appl), CAPIMSG_NCCI(skb->data))) != 0 + && mq_dequeue(np, CAPIMSG_MSGID(skb->data)) == 0) { + printk(KERN_ERR "kcapi: msgid %hu ncci 0x%x not on queue\n", + CAPIMSG_MSGID(skb->data), np->ncci); + } + if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3 + && CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { + APPL(appl)->nrecvdatapkt++; + } else { + APPL(appl)->nrecvctlpkt++; + } + skb_queue_tail(&APPL(appl)->recv_queue, skb); + (APPL(appl)->signal) (APPL(appl)->applid, APPL(appl)->param); + } +} + +static void controllercb_handle_capimsg(struct capi_ctr * card, + __u16 appl, struct sk_buff *skb) +{ + int showctl = 0; + __u8 cmd, subcmd; + + if (card->cardstate != CARD_RUNNING) { + printk(KERN_INFO "kcapi: controller %d not active, got: %s", + card->cnr, capi_message2str(skb->data)); + goto error; + } + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { + card->nrecvdatapkt++; + if (card->traceflag > 2) showctl |= 2; + if (card->traceflag) showctl |= 2; + } else { + card->nrecvctlpkt++; + } + showctl |= (card->traceflag & 1); + if (showctl & 2) { + if (showctl & 1) { + printk(KERN_DEBUG "kcapi: got [0x%lx] id#%d %s len=%u\n", + (unsigned long) card->cnr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), + CAPIMSG_LEN(skb->data)); + } else { + printk(KERN_DEBUG "kcapi: got [0x%lx] %s\n", + (unsigned long) card->cnr, + capi_message2str(skb->data)); + } + + } + skb_queue_tail(&recv_queue, skb); + queue_task(&tq_recv_notify, &tq_immediate); + mark_bh(IMMEDIATE_BH); + return; + +error: + kfree_skb(skb); +} + +/* -------- Notifier ------------------------------------------ */ + +static void notify_up(__u16 contr) +{ + struct capi_interface_user *p; + + printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr); + for (p = capi_users; p; p = p->next) { + if (!p->callback) continue; + (*p->callback) (KCI_CONTRUP, contr, &CARD(contr)->profile); + } +} + +static void notify_down(__u16 contr) +{ + struct capi_interface_user *p; + printk(KERN_NOTICE "kcapi: notify down contr %d\n", contr); + for (p = capi_users; p; p = p->next) { + if (!p->callback) continue; + (*p->callback) (KCI_CONTRDOWN, contr, 0); + } +} + +static void notify_handler(void *dummy) +{ + __u16 contr; + + for (contr=1; VALID_CARD(contr); contr++) + if (test_and_clear_bit(contr, ¬ify_up_set)) + notify_up(contr); + for (contr=1; VALID_CARD(contr); contr++) + if (test_and_clear_bit(contr, ¬ify_down_set)) + notify_down(contr); +} + + +static void controllercb_ready(struct capi_ctr * card) +{ + __u16 appl; + + card->cardstate = CARD_RUNNING; + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + if (!VALID_APPLID(appl)) continue; + if (APPL(appl)->releasing) continue; + card->driver->register_appl(card, appl, &APPL(appl)->rparam); + } + + set_bit(CARDNR(card), ¬ify_up_set); + queue_task(&tq_state_notify, &tq_scheduler); + + printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n", + CARDNR(card), card->name); +} + +static void controllercb_reseted(struct capi_ctr * card) +{ + __u16 appl; + + if (card->cardstate == CARD_FREE) + return; + if (card->cardstate == CARD_DETECTED) + return; + + card->cardstate = CARD_DETECTED; + + memset(card->manu, 0, sizeof(card->manu)); + memset(&card->version, 0, sizeof(card->version)); + memset(&card->profile, 0, sizeof(card->profile)); + memset(card->serial, 0, sizeof(card->serial)); + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + struct capi_ncci **pp, **nextpp; + for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) { + if (NCCI2CTRL((*pp)->ncci) == card->cnr) { + struct capi_ncci *np = *pp; + *pp = np->next; + printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down!\n", appl, np->ncci); + kfree(np); + nextpp = pp; + } else { + nextpp = &(*pp)->next; + } + } + } + set_bit(CARDNR(card), ¬ify_down_set); + queue_task(&tq_state_notify, &tq_scheduler); + printk(KERN_NOTICE "kcapi: card %d down.\n", CARDNR(card)); +} + +static void controllercb_suspend_output(struct capi_ctr *card) +{ + if (!card->blocked) { + printk(KERN_DEBUG "kcapi: card %d suspend\n", CARDNR(card)); + card->blocked = 1; + } +} + +static void controllercb_resume_output(struct capi_ctr *card) +{ + if (card->blocked) { + printk(KERN_DEBUG "kcapi: card %d resume\n", CARDNR(card)); + card->blocked = 0; + } +} + +/* ------------------------------------------------------------- */ + + +struct capi_ctr * +drivercb_attach_ctr(struct capi_driver *driver, char *name, void *driverdata) +{ + struct capi_ctr *card, **pp; + int i; + + for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ; + + if (i == CAPI_MAXCONTR) { + printk(KERN_ERR "kcapi: out of controller slots\n"); + return 0; + } + card = &cards[i]; + memset(card, 0, sizeof(struct capi_ctr)); + card->driver = driver; + card->cnr = CARDNR(card); + strncpy(card->name, name, sizeof(card->name)); + card->cardstate = CARD_DETECTED; + card->blocked = 0; + card->driverdata = driverdata; + card->traceflag = showcapimsgs; + + card->ready = controllercb_ready; + card->reseted = controllercb_reseted; + card->suspend_output = controllercb_suspend_output; + card->resume_output = controllercb_resume_output; + card->handle_capimsg = controllercb_handle_capimsg; + card->appl_registered = controllercb_appl_registered; + card->appl_released = controllercb_appl_released; + card->new_ncci = controllercb_new_ncci; + card->free_ncci = controllercb_free_ncci; + + for (pp = &driver->controller; *pp; pp = &(*pp)->next) ; + card->next = 0; + *pp = card; + driver->ncontroller++; + sprintf(card->procfn, "capi/controllers/%d", card->cnr); + card->procent = create_proc_entry(card->procfn, 0, 0); + if (card->procent) { + card->procent->read_proc = + (int (*)(char *,char **,off_t,int,int *,void *)) + driver->ctr_read_proc; + card->procent->data = card; + } + + ncards++; + printk(KERN_NOTICE "kcapi: Controller %d: %s attached\n", + card->cnr, card->name); + return card; +} + +static int drivercb_detach_ctr(struct capi_ctr *card) +{ + struct capi_driver *driver = card->driver; + struct capi_ctr **pp; + + if (card->cardstate == CARD_FREE) + return 0; + if (card->cardstate != CARD_DETECTED) + controllercb_reseted(card); + for (pp = &driver->controller; *pp ; pp = &(*pp)->next) { + if (*pp == card) { + *pp = card->next; + driver->ncontroller--; + ncards--; + break; + } + } + if (card->procent) { + remove_proc_entry(card->procfn, 0); + card->procent = 0; + } + card->cardstate = CARD_FREE; + printk(KERN_NOTICE "kcapi: Controller %d: %s unregistered\n", + card->cnr, card->name); + return 0; +} + +/* ------------------------------------------------------------- */ + +/* fallback if no driver read_proc function defined by driver */ + +static int driver_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct capi_driver *driver = (struct capi_driver *)data; + int len = 0; + + len += sprintf(page+len, "%-16s %s\n", "name", driver->name); + len += sprintf(page+len, "%-16s %s\n", "revision", driver->revision); + + if (len < off) + return 0; + *eof = 1; + *start = page - off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface di = { + drivercb_attach_ctr, + drivercb_detach_ctr, +}; + +struct capi_driver_interface *attach_capi_driver(struct capi_driver *driver) +{ + struct capi_driver **pp; + + for (pp = &drivers; *pp; pp = &(*pp)->next) ; + driver->next = 0; + *pp = driver; + printk(KERN_NOTICE "kcapi: driver %s attached\n", driver->name); +#ifdef CONFIG_AVMB1_COMPAT + if (strcmp(driver->name, "b1isa") == 0 && driver->add_card) + b1isa_driver = driver; + if (strcmp(driver->name, "t1isa") == 0 && driver->add_card) + t1isa_driver = driver; +#endif + sprintf(driver->procfn, "capi/drivers/%s", driver->name); + driver->procent = create_proc_entry(driver->procfn, 0, 0); + if (driver->procent) { + if (driver->driver_read_proc) { + driver->procent->read_proc = + (int (*)(char *,char **,off_t,int,int *,void *)) + driver->driver_read_proc; + } else { + driver->procent->read_proc = driver_read_proc; + } + driver->procent->data = driver; + } + return &di; +} + +void detach_capi_driver(struct capi_driver *driver) +{ + struct capi_driver **pp; + for (pp = &drivers; *pp && *pp != driver; pp = &(*pp)->next) ; + if (*pp) { + *pp = (*pp)->next; +#ifdef CONFIG_AVMB1_COMPAT + if (driver == b1isa_driver) b1isa_driver = 0; + if (driver == t1isa_driver) t1isa_driver = 0; +#endif + printk(KERN_NOTICE "kcapi: driver %s detached\n", driver->name); + } else { + printk(KERN_ERR "kcapi: driver %s double detach ?\n", driver->name); + } + if (driver->procent) { + remove_proc_entry(driver->procfn, 0); + driver->procent = 0; + } +} + +/* ------------------------------------------------------------- */ +/* -------- CAPI2.0 Interface ---------------------------------- */ +/* ------------------------------------------------------------- */ + +static int capi_installed(void) +{ + int i; + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate == CARD_RUNNING) + return 1; + } + return 0; +} + +static __u16 capi_register(capi_register_params * rparam, __u16 * applidp) +{ + int appl; + int i; + + if (rparam->datablklen < 128) + return CAPI_LOGBLKSIZETOSMALL; + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + if (APPL_IS_FREE(appl)) + break; + } + if (appl > CAPI_MAXAPPL) + return CAPI_TOOMANYAPPLS; + + APPL_MARK_USED(appl); + skb_queue_head_init(&APPL(appl)->recv_queue); + APPL(appl)->nncci = 0; + + memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params)); + + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_RUNNING) + continue; + cards[i].driver->register_appl(&cards[i], appl, + &APPL(appl)->rparam); + } + *applidp = appl; + printk(KERN_INFO "kcapi: appl %d up\n", appl); + + return CAPI_NOERROR; +} + +static __u16 capi_release(__u16 applid) +{ + struct sk_buff *skb; + int i; + + if (!VALID_APPLID(applid) || APPL(applid)->releasing) + return CAPI_ILLAPPNR; + while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0) + kfree_skb(skb); + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_RUNNING) + continue; + APPL(applid)->releasing++; + cards[i].driver->release_appl(&cards[i], applid); + } + if (APPL(applid)->releasing <= 0) { + APPL(applid)->signal = 0; + APPL_MARK_FREE(applid); + printk(KERN_INFO "kcapi: appl %d down\n", applid); + } + return CAPI_NOERROR; +} + +static __u16 capi_put_message(__u16 applid, struct sk_buff *skb) +{ + struct capi_ncci *np; + int contr; + int showctl = 0; + __u8 cmd, subcmd; + + if (ncards == 0) + return CAPI_REGNOTINSTALLED; + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + if (skb->len < 12 + || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) + || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) + return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; + contr = CAPIMSG_CONTROLLER(skb->data); + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) { + contr = 1; + if (CARD(contr)->cardstate != CARD_RUNNING) + return CAPI_REGNOTINSTALLED; + } + if (CARD(contr)->blocked) + return CAPI_SENDQUEUEFULL; + + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) { + if ((np = find_ncci(APPL(applid), CAPIMSG_NCCI(skb->data))) != 0 + && mq_enqueue(np, CAPIMSG_MSGID(skb->data)) == 0) + return CAPI_SENDQUEUEFULL; + CARD(contr)->nsentdatapkt++; + APPL(applid)->nsentdatapkt++; + if (CARD(contr)->traceflag > 2) showctl |= 2; + } else { + CARD(contr)->nsentctlpkt++; + APPL(applid)->nsentctlpkt++; + if (CARD(contr)->traceflag) showctl |= 2; + } + showctl |= (CARD(contr)->traceflag & 1); + if (showctl & 2) { + if (showctl & 1) { + printk(KERN_DEBUG "kcapi: put [0x%lx] id#%d %s len=%u\n", + (unsigned long) contr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), + CAPIMSG_LEN(skb->data)); + } else { + printk(KERN_DEBUG "kcapi: put [0x%lx] %s\n", + (unsigned long) contr, + capi_message2str(skb->data)); + } + + } + CARD(contr)->driver->send_message(CARD(contr), skb); + return CAPI_NOERROR; +} + +static __u16 capi_get_message(__u16 applid, struct sk_buff **msgp) +{ + struct sk_buff *skb; + + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + if ((skb = skb_dequeue(&APPL(applid)->recv_queue)) == 0) + return CAPI_RECEIVEQUEUEEMPTY; + *msgp = skb; + return CAPI_NOERROR; +} + +static __u16 capi_set_signal(__u16 applid, + void (*signal) (__u16 applid, __u32 param), + __u32 param) +{ + if (!VALID_APPLID(applid)) + return CAPI_ILLAPPNR; + APPL(applid)->signal = signal; + APPL(applid)->param = param; + return CAPI_NOERROR; +} + +static __u16 capi_get_manufacturer(__u16 contr, __u8 buf[CAPI_MANUFACTURER_LEN]) +{ + if (contr == 0) { + strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + strncpy(buf, CARD(contr)->manu, CAPI_MANUFACTURER_LEN); + return CAPI_NOERROR; +} + +static __u16 capi_get_version(__u16 contr, struct capi_version *verp) +{ + if (contr == 0) { + *verp = driver_version; + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + memcpy((void *) verp, &CARD(contr)->version, sizeof(capi_version)); + return CAPI_NOERROR; +} + +static __u16 capi_get_serial(__u16 contr, __u8 serial[CAPI_SERIAL_LEN]) +{ + if (contr == 0) { + strncpy(serial, driver_serial, CAPI_SERIAL_LEN); + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + strncpy((void *) serial, CARD(contr)->serial, CAPI_SERIAL_LEN); + return CAPI_NOERROR; +} + +static __u16 capi_get_profile(__u16 contr, struct capi_profile *profp) +{ + if (contr == 0) { + profp->ncontroller = ncards; + return CAPI_NOERROR; + } + if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) + return 0x2002; + + memcpy((void *) profp, &CARD(contr)->profile, + sizeof(struct capi_profile)); + return CAPI_NOERROR; +} + +#ifdef CONFIG_AVMB1_COMPAT +static int old_capi_manufacturer(unsigned int cmd, void *data) +{ + avmb1_loadandconfigdef ldef; + avmb1_extcarddef cdef; + avmb1_resetdef rdef; + avmb1_getdef gdef; + struct capi_driver *driver; + struct capi_ctr *card; + capicardparams cparams; + capiloaddata ldata; + int retval; + + switch (cmd) { + case AVMB1_ADDCARD: + case AVMB1_ADDCARD_WITH_TYPE: + if (cmd == AVMB1_ADDCARD) { + if ((retval = copy_from_user((void *) &cdef, data, + sizeof(avmb1_carddef)))) + return retval; + cdef.cardtype = AVM_CARDTYPE_B1; + } else { + if ((retval = copy_from_user((void *) &cdef, data, + sizeof(avmb1_extcarddef)))) + return retval; + } + cparams.port = cdef.port; + cparams.irq = cdef.irq; + cparams.cardnr = cdef.cardnr; + + switch (cdef.cardtype) { + case AVM_CARDTYPE_B1: driver = b1isa_driver; break; + case AVM_CARDTYPE_T1: driver = t1isa_driver; break; + default: driver = 0; + } + if (!driver || !driver->add_card) { + return -EIO; + } + + return driver->add_card(driver, &cparams); + + case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + + if (cmd == AVMB1_LOAD) { + if ((retval = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loaddef)))) + return retval; + ldef.t4config.len = 0; + ldef.t4config.data = 0; + } else { + if ((retval = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loadandconfigdef)))) + return retval; + } + if (!VALID_CARD(ldef.contr)) + return -ESRCH; + + card = CARD(ldef.contr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + + if (ldef.t4file.len <= 0) { + printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len); + return -EINVAL; + } + if (ldef.t4file.data == 0) { + printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n"); + return -EINVAL; + } + + ldata.firmware.user = 1; + ldata.firmware.data = ldef.t4file.data; + ldata.firmware.len = ldef.t4file.len; + ldata.configuration.user = 1; + ldata.configuration.data = ldef.t4config.data; + ldata.configuration.len = ldef.t4config.len; + + if (card->cardstate != CARD_DETECTED) { + printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr); + return -EBUSY; + } + card->cardstate = CARD_LOADING; + + retval = card->driver->load_firmware(card, &ldata); + + if (retval) { + card->cardstate = CARD_DETECTED; + return retval; + } + + while (card->cardstate != CARD_RUNNING) { + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); /* 0.1 sec */ + + if (signal_pending(current)) + return -EINTR; + } + return 0; + + case AVMB1_RESETCARD: + if ((retval = copy_from_user((void *) &rdef, data, + sizeof(avmb1_resetdef)))) + return retval; + if (!VALID_CARD(rdef.contr)) + return -ESRCH; + card = CARD(rdef.contr); + + if (card->cardstate == CARD_FREE) + return -ESRCH; + if (card->cardstate == CARD_DETECTED) + return 0; + + card->driver->reset_ctr(card); + + while (card->cardstate > CARD_DETECTED) { + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); /* 0.1 sec */ + + if (signal_pending(current)) + return -EINTR; + } + return 0; + + case AVMB1_GET_CARDINFO: + if ((retval = copy_from_user((void *) &gdef, data, + sizeof(avmb1_getdef)))) + return retval; + + if (!VALID_CARD(gdef.contr)) + return -ESRCH; + + card = CARD(gdef.contr); + + if (card->cardstate == CARD_FREE) + return -ESRCH; + + gdef.cardstate = card->cardstate; + if (card->driver == b1isa_driver) + gdef.cardtype = AVM_CARDTYPE_B1; + else if (card->driver == t1isa_driver) + gdef.cardtype = AVM_CARDTYPE_T1; + else gdef.cardtype = AVM_CARDTYPE_B1; + + if ((retval = copy_to_user(data, (void *) &gdef, + sizeof(avmb1_getdef)))) + return retval; + + return 0; + + case AVMB1_REMOVECARD: + if ((retval = copy_from_user((void *) &rdef, data, + sizeof(avmb1_resetdef)))) + return retval; + + if (!VALID_CARD(rdef.contr)) + return -ESRCH; + card = CARD(rdef.contr); + + if (card->cardstate == CARD_FREE) + return -ESRCH; + + if (card->cardstate != CARD_DETECTED) + return -EBUSY; + + card->driver->remove_ctr(card); + + while (card->cardstate != CARD_FREE) { + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); /* 0.1 sec */ + + if (signal_pending(current)) + return -EINTR; + } + return 0; + } + return -EINVAL; +} +#endif + +static int capi_manufacturer(unsigned int cmd, void *data) +{ + struct capi_ctr *card; + kcapi_flagdef fdef; + int retval; + + switch (cmd) { +#ifdef CONFIG_AVMB1_COMPAT + case AVMB1_ADDCARD: + case AVMB1_ADDCARD_WITH_TYPE: + case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + case AVMB1_RESETCARD: + case AVMB1_GET_CARDINFO: + case AVMB1_REMOVECARD: + return old_capi_manufacturer(cmd, data); +#endif + case KCAPI_CMD_TRACE: + if ((retval = copy_from_user((void *) &fdef, data, + sizeof(kcapi_flagdef)))) + return retval; + + if (!VALID_CARD(fdef.contr)) + return -ESRCH; + card = CARD(fdef.contr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + card->traceflag = fdef.flag; + printk(KERN_INFO "kcapi: contr %d set trace=%d\n", + card->cnr, card->traceflag); + return 0; + } + return -EINVAL; +} + +struct capi_interface avmb1_interface = +{ + capi_installed, + capi_register, + capi_release, + capi_put_message, + capi_get_message, + capi_set_signal, + capi_get_manufacturer, + capi_get_version, + capi_get_serial, + capi_get_profile, + capi_manufacturer +}; + +/* ------------------------------------------------------------- */ +/* -------- Exported Functions --------------------------------- */ +/* ------------------------------------------------------------- */ + +struct capi_interface *attach_capi_interface(struct capi_interface_user *userp) +{ + struct capi_interface_user *p; + + for (p = capi_users; p; p = p->next) { + if (p == userp) { + printk(KERN_ERR "kcapi: double attach from %s\n", + userp->name); + return 0; + } + } + userp->next = capi_users; + capi_users = userp; + MOD_INC_USE_COUNT; + printk(KERN_NOTICE "kcapi: %s attached\n", userp->name); + + return &avmb1_interface; +} + +int detach_capi_interface(struct capi_interface_user *userp) +{ + struct capi_interface_user **pp; + + for (pp = &capi_users; *pp; pp = &(*pp)->next) { + if (*pp == userp) { + *pp = userp->next; + userp->next = 0; + MOD_DEC_USE_COUNT; + printk(KERN_NOTICE "kcapi: %s detached\n", userp->name); + return 0; + } + } + printk(KERN_ERR "kcapi: double detach from %s\n", userp->name); + return -1; +} + +/* ------------------------------------------------------------- */ +/* -------- Init & Cleanup ------------------------------------- */ +/* ------------------------------------------------------------- */ + +EXPORT_SYMBOL(attach_capi_interface); +EXPORT_SYMBOL(detach_capi_interface); +EXPORT_SYMBOL(attach_capi_driver); +EXPORT_SYMBOL(detach_capi_driver); + +#ifndef MODULE +#ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA +extern int b1isa_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI +extern int b1pci_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA +extern int t1isa_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA +extern int b1pcmcia_init(void); +#endif +#endif + +/* + * init / exit functions + */ + +#ifdef MODULE +#define kcapi_init init_module +#endif + +int kcapi_init(void) +{ + char *p; + char rev[10]; + + skb_queue_head_init(&recv_queue); + /* init_bh(CAPI_BH, do_capi_bh); */ + + tq_state_notify.routine = notify_handler; + tq_state_notify.data = 0; + + tq_recv_notify.routine = recv_handler; + tq_recv_notify.data = 0; + + proc_capi_init(); + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, "1.0"); + +#ifdef MODULE + printk(KERN_NOTICE "CAPI-driver Rev%s: loaded\n", rev); +#else + printk(KERN_NOTICE "CAPI-driver Rev%s: started\n", rev); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA + (void)b1isa_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI + (void)b1pci_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA + (void)t1isa_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + (void)b1pcmcia_init(); +#endif +#endif + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + char rev[10]; + char *p; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else { + strcpy(rev, "1.0"); + } + + schedule(); /* execute queued tasks .... */ + proc_capi_exit(); + printk(KERN_NOTICE "CAPI-driver Rev%s: unloaded\n", rev); +} +#endif diff --git a/drivers/isdn/avmb1/t1isa.c b/drivers/isdn/avmb1/t1isa.c new file mode 100644 index 000000000..3546e1061 --- /dev/null +++ b/drivers/isdn/avmb1/t1isa.c @@ -0,0 +1,550 @@ +/* + * $Id: t1isa.c,v 1.5 1999/08/22 20:26:28 calle Exp $ + * + * Module for AVM T1 HEMA-card. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: t1isa.c,v $ + * Revision 1.5 1999/08/22 20:26:28 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.4 1999/07/09 15:05:50 keil + * compat.h is now isdn_compat.h + * + * Revision 1.3 1999/07/06 07:42:04 calle + * - changes in /proc interface + * - check and changed calls to [dev_]kfree_skb and [dev_]alloc_skb. + * + * Revision 1.2 1999/07/05 15:09:54 calle + * - renamed "appl_release" to "appl_released". + * - version und profile data now cleared on controller reset + * - extended /proc interface, to allow driver and controller specific + * informations to include by driver hackers. + * + * Revision 1.1 1999/07/01 15:26:44 calle + * complete new version (I love it): + * + new hardware independed "capi_driver" interface that will make it easy to: + * - support other controllers with CAPI-2.0 (i.e. USB Controller) + * - write a CAPI-2.0 for the passive cards + * - support serial link CAPI-2.0 boxes. + * + wrote "capi_driver" for all supported cards. + * + "capi_driver" (supported cards) now have to be configured with + * make menuconfig, in the past all supported cards where included + * at once. + * + new and better informations in /proc/capi/ + * + new ioctl to switch trace of capi messages per controller + * using "avmcapictrl trace [contr] on|off|...." + * + complete testcircle with all supported cards and also the + * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done. + * + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <asm/io.h> +#include <linux/isdn_compat.h> +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.5 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static int hema_irq_table[16] = +{0, + 0, + 0, + 0x80, /* irq 3 */ + 0, + 0x90, /* irq 5 */ + 0, + 0xA0, /* irq 7 */ + 0, + 0xB0, /* irq 9 */ + 0xC0, /* irq 10 */ + 0xD0, /* irq 11 */ + 0xE0, /* irq 12 */ + 0, + 0, + 0xF0, /* irq 15 */ +}; + +static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr) +{ + unsigned char cregs[8]; + unsigned char reverse_cardnr; + unsigned long flags; + unsigned char dummy; + int i; + + reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1) + | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3); + cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf); + cregs[1] = 0x00; /* fast & slow link connected to CON1 */ + cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */ + cregs[3] = 0; + cregs[4] = 0x11; /* zero wait state */ + cregs[5] = hema_irq_table[irq & 0xf]; + cregs[6] = 0; + cregs[7] = 0; + + save_flags(flags); + cli(); + /* board reset */ + t1outp(base, T1_RESETBOARD, 0xf); + udelay(100 * 1000); + dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */ + + /* write config */ + dummy = (base >> 4) & 0xff; + for (i=1;i<=0xf;i++) t1outp(base, i, dummy); + t1outp(base, HEMA_PAL_ID & 0xf, dummy); + t1outp(base, HEMA_PAL_ID >> 4, cregs[0]); + for(i=1;i<7;i++) t1outp(base, 0, cregs[i]); + t1outp(base, ((base >> 4)) & 0x3, cregs[7]); + restore_flags(flags); + + udelay(100 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); + udelay(10 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 1); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1); + udelay(100 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); + udelay(10 * 1000); + t1outp(base, T1_FASTLINK+T1_ANALYSE, 0); + udelay(5 * 1000); + t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0); + + if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */ + return 1; + if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */ + return 2; + if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0) + return 3; + if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70) + return 4; + if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0) + return 5; + if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1) + return 6; + if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */ + return 7; + if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0) + return 8; + if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0) + return 9; + return 0; +} + +static void t1_handle_interrupt(avmcard * card) +{ + struct capi_ctr *ctrl = card->ctrl; + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + + while (b1_rx_full(card->port)) { + + b1cmd = b1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + DataB3Len = t1_get_slice(card->port, card->databuf); + + if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "t1isa: incoming packet dropped\n"); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + CAPIMSG_SETDATA(skb->data, skb->data + MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "t1isa: incoming packet dropped\n"); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + WindowSize = b1_get_word(card->port); + + ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + + if (NCCI != 0xffffffff) + ctrl->free_ncci(ctrl, ApplId, NCCI); + else ctrl->appl_released(ctrl, ApplId); + break; + + case RECEIVE_START: + b1_put_byte(card->port, SEND_POLLACK); + ctrl->resume_output(ctrl); + break; + + case RECEIVE_STOP: + ctrl->suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + card->versionlen = t1_get_slice(card->port, card->versionbuf); + b1_parse_version(card); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + card->version[VER_CARDTYPE], + card->version[VER_DRIVER]); + ctrl->ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = t1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen--] = 0; + while ( MsgLen >= 0 + && ( card->msgbuf[MsgLen] == '\n' + || card->msgbuf[MsgLen] == '\r')) + card->msgbuf[MsgLen--] = 0; + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + case 0xff: + printk(KERN_ERR "%s: card reseted ?\n", card->name); + return; + default: + printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } + } +} + +/* ------------------------------------------------------------- */ + +static void t1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "t1_interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "t1_interrupt: reentering interrupt hander (%s)\n", card->name); + return; + } + + card->interrupt = 1; + + t1_handle_interrupt(card); + + card->interrupt = 0; +} +/* ------------------------------------------------------------- */ + +static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + int retval; + + t1_disable_irq(port); + b1_reset(port); + + if ((retval = b1_load_t4file(port, &data->firmware))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(port, &data->configuration))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1_loaded(port)) { + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + save_flags(flags); + cli(); + b1_setinterrupt(port, card->irq, card->cardtype); + b1_put_byte(port, SEND_INIT); + b1_put_word(port, AVM_NAPPS); + b1_put_word(port, AVM_NCCI_PER_CHANNEL*30); + b1_put_word(port, ctrl->cnr - 1); + restore_flags(flags); + + return 0; +} + +void t1isa_reset_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + t1_disable_irq(port); + b1_reset(port); + b1_reset(port); + + memset(card->version, 0, sizeof(card->version)); + ctrl->reseted(ctrl); +} + +static void t1isa_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + + t1_disable_irq(port); + b1_reset(port); + b1_reset(port); + t1_reset(port); + + di->detach_ctr(ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------- */ + +static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + struct capi_ctr *ctrl; + avmcard *card; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "t1isa: no memory.\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + sprintf(card->name, "t1isa-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->cardtype = avm_t1isa; + card->cardnr = p->cardnr; + + if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) { + printk(KERN_WARNING "t1isa: illegal port 0x%x.\n", card->port); + kfree(card); + return -EINVAL; + } + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "t1isa: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + if (hema_irq_table[card->irq & 0xf] == 0) { + printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq); + kfree(card); + return -EINVAL; + } + for (ctrl = driver->controller; ctrl; ctrl = ctrl->next) { + if (((avmcard *)(ctrl->driverdata))->cardnr == card->cardnr) { + printk(KERN_WARNING "t1isa: card with number %d already installed.\n", card->cardnr); + kfree(card); + return -EBUSY; + } + } + if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) { + printk(KERN_NOTICE "t1isa: NO card at 0x%x (%d)\n", + card->port, retval); + kfree(card); + return -EIO; + } + t1_disable_irq(card->port); + b1_reset(card->port); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "t1isa: unable to get IRQ %d.\n", card->irq); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + card->ctrl = di->attach_ctr(driver, card->name, card); + if (!card->ctrl) { + printk(KERN_ERR "t1isa: attach controller failed.\n"); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card); + return -EBUSY; + } + + MOD_INC_USE_COUNT; + return 0; +} + +static void t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + unsigned int port = card->port; + unsigned long flags; + __u16 len = CAPIMSG_LEN(skb->data); + __u8 cmd = CAPIMSG_COMMAND(skb->data); + __u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + save_flags(flags); + cli(); + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + b1_put_byte(port, SEND_DATA_B3_REQ); + t1_put_slice(port, skb->data, len); + t1_put_slice(port, skb->data + len, dlen); + } else { + b1_put_byte(port, SEND_MESSAGE); + t1_put_slice(port, skb->data, len); + } + restore_flags(flags); + dev_kfree_skb(skb); +} +/* ------------------------------------------------------------- */ + +static char *t1isa_procinfo(struct capi_ctr *ctrl) +{ + avmcard *card = (avmcard *)(ctrl->driverdata); + if (!card) + return ""; + sprintf(card->infobuf, "%s %s 0x%x %d %d", + card->cardname[0] ? card->cardname : "-", + card->version[VER_DRIVER] ? card->version[VER_DRIVER] : "-", + card->port, card->irq, card->cardnr + ); + return card->infobuf; +} + + +/* ------------------------------------------------------------- */ + +static struct capi_driver t1isa_driver = { + "t1isa", + "0.0", + t1isa_load_firmware, + t1isa_reset_ctr, + t1isa_remove_ctr, + b1_register_appl, + b1_release_appl, + t1isa_send_message, + + t1isa_procinfo, + b1ctl_read_proc, + 0, /* use standard driver_read_proc */ + + t1isa_add_card, +}; + +#ifdef MODULE +#define t1isa_init init_module +void cleanup_module(void); +#endif + +int t1isa_init(void) +{ + struct capi_driver *driver = &t1isa_driver; + char *p; + + if ((p = strchr(revision, ':'))) { + strncpy(driver->revision, p + 1, sizeof(driver->revision)); + p = strchr(driver->revision, '$'); + *p = 0; + } + + printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); + + di = attach_capi_driver(driver); + + if (!di) { + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driver->name); + return -EIO; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + detach_capi_driver(&t1isa_driver); +} +#endif diff --git a/drivers/isdn/divert/Makefile b/drivers/isdn/divert/Makefile new file mode 100644 index 000000000..109c900d8 --- /dev/null +++ b/drivers/isdn/divert/Makefile @@ -0,0 +1,21 @@ +L_OBJS := +LX_OBJS := +M_OBJS := +MX_OBJS := +O_OBJS := +OX_OBJS := +L_TARGET := +O_TARGET := + +O_OBJS += isdn_divert.o divert_procfs.o +O_TARGET := dss1_divert.o +M_OBJS += dss1_divert.o +OX_OBJS += divert_init.o + +include $(TOPDIR)/Rules.make + + + + + + diff --git a/drivers/isdn/divert/divert_init.c b/drivers/isdn/divert/divert_init.c new file mode 100644 index 000000000..ce44cfa19 --- /dev/null +++ b/drivers/isdn/divert/divert_init.c @@ -0,0 +1,110 @@ +/* + * $Id: divert_init.c,v 1.4 1999/08/22 20:26:32 calle Exp $ + * + * Module init for DSS1 diversion services for i4l. + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.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: divert_init.c,v $ + * Revision 1.4 1999/08/22 20:26:32 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.3 1999/07/05 20:21:39 werner + * changes to use diversion sources for all kernel versions. + * removed static device, only proc filesystem used + * + * Revision 1.2 1999/07/04 21:37:30 werner + * Ported from kernel version 2.0 + * + * + * + */ + +#include <linux/module.h> +#include <linux/version.h> +#include "isdn_divert.h" + +/********************/ +/* needed externals */ +/********************/ +extern int printk(const char *fmt,...); + +/****************************************/ +/* structure containing interface to hl */ +/****************************************/ +isdn_divert_if divert_if = + { DIVERT_IF_MAGIC, /* magic value */ + DIVERT_CMD_REG, /* register cmd */ + ll_callback, /* callback routine from ll */ + NULL, /* command still not specified */ + NULL, /* drv_to_name */ + NULL, /* name_to_drv */ + }; + +/*************************/ +/* Module interface code */ +/* no cmd line parms */ +/*************************/ +int init_module(void) +{ int i; + + if (divert_dev_init()) + { printk(KERN_WARNING "dss1_divert: cannot install device, not loaded\n"); + return(-EIO); + } + if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) + { divert_dev_deinit(); + printk(KERN_WARNING "dss1_divert: error %d registering module, not loaded\n",i); + return(-EIO); + } +#if (LINUX_VERSION_CODE < 0x020111) + register_symtab(0); +#endif + printk(KERN_INFO "dss1_divert module successfully installed\n"); + return(0); +} /* init_module */ + +/**********************/ +/* Module deinit code */ +/**********************/ +void cleanup_module(void) +{ int flags; + int i; + + save_flags(flags); + cli(); + divert_if.cmd = DIVERT_CMD_REL; /* release */ + if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) + { printk(KERN_WARNING "dss1_divert: error %d releasing module\n",i); + restore_flags(flags); + return; + } + if (divert_dev_deinit()) + { printk(KERN_WARNING "dss1_divert: device busy, remove cancelled\n"); + restore_flags(flags); + return; + } + restore_flags(flags); + deleterule(-1); /* delete all rules and free mem */ + deleteprocs(); + printk(KERN_INFO "dss1_divert module successfully removed \n"); +} /* cleanup_module */ + + diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c new file mode 100644 index 000000000..035aa3636 --- /dev/null +++ b/drivers/isdn/divert/divert_procfs.c @@ -0,0 +1,456 @@ +/* + * $Id: divert_procfs.c,v 1.4 1999/08/06 07:42:48 calle Exp $ + * + * Filesystem handling for the diversion supplementary services. + * + * Copyright 1998 by Werner Cornelius (werner@isdn4linux.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: divert_procfs.c,v $ + * Revision 1.4 1999/08/06 07:42:48 calle + * Added COMPAT_HAS_NEW_WAITQ for rd_queue for newer kernels. + * + * Revision 1.3 1999/07/05 20:21:41 werner + * changes to use diversion sources for all kernel versions. + * removed static device, only proc filesystem used + * + * Revision 1.2 1999/07/04 21:37:31 werner + * Ported from kernel version 2.0 + * + * + * + */ + +#include <linux/config.h> +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#if (LINUX_VERSION_CODE >= 0x020117) +#include <linux/poll.h> +#endif +#ifdef CONFIG_PROC_FS + #include <linux/proc_fs.h> +#else + #include <linux/fs.h> +#endif +#include <linux/isdnif.h> +#include <linux/isdn_compat.h> +#include "isdn_divert.h" + +/*********************************/ +/* Variables for interface queue */ +/*********************************/ +ulong if_used = 0; /* number of interface users */ +static struct divert_info *divert_info_head = NULL; /* head of queue */ +static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */ +#ifdef COMPAT_HAS_NEW_WAITQ +static wait_queue_head_t rd_queue; +#else +static struct wait_queue *rd_queue = 0; /* Queue IO */ +#endif + +/*********************************/ +/* put an info buffer into queue */ +/*********************************/ +void put_info_buffer(char *cp) +{ struct divert_info *ib; + int flags; + + if (if_used <= 0) return; + if (!cp) return; + if (!*cp) return; + if (!(ib = (struct divert_info *) kmalloc(sizeof(struct divert_info)+strlen(cp), GFP_ATOMIC))) return; /* no memory */ + strcpy(ib->info_start,cp); /* set output string */ + ib->next = NULL; + save_flags(flags); + cli(); + ib->usage_cnt = if_used; + if (!divert_info_head) + divert_info_head = ib; /* new head */ + else + divert_info_tail->next = ib; /* follows existing messages */ + divert_info_tail = ib; /* new tail */ + restore_flags(flags); + + /* delete old entrys */ + while (divert_info_head->next) + { if ((divert_info_head->usage_cnt <= 0) && + (divert_info_head->next->usage_cnt <= 0)) + { ib = divert_info_head; + divert_info_head = divert_info_head->next; + kfree(ib); + } + else break; + } /* divert_info_head->next */ + wake_up_interruptible(&(rd_queue)); +} /* put_info_buffer */ + +/**********************************/ +/* deflection device read routine */ +/**********************************/ +#if (LINUX_VERSION_CODE < 0x020117) +static int isdn_divert_read(struct inode *inode, struct file *file, char *buf, RWARG count) +#else +static ssize_t isdn_divert_read(struct file *file, char *buf, size_t count, loff_t *off) +#endif +{ struct divert_info *inf; + int len; + + if (!*((struct divert_info **)file->private_data)) + { if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + interruptible_sleep_on(&(rd_queue)); + } + if (!(inf = *((struct divert_info **)file->private_data))) return(0); + + inf->usage_cnt--; /* new usage count */ + (struct divert_info **)file->private_data = &inf->next; /* next structure */ + if ((len = strlen(inf->info_start)) <= count) + { if (copy_to_user(buf, inf->info_start, len)) + return -EFAULT; + file->f_pos += len; + return(len); + } + return(0); +} /* isdn_divert_read */ + +/**********************************/ +/* deflection device write routine */ +/**********************************/ +#if (LINUX_VERSION_CODE < 0x020117) +static int isdn_divert_write(struct inode *inode, struct file *file, const char *buf, RWARG count) +#else +static ssize_t isdn_divert_write(struct file *file, const char *buf, size_t count, loff_t *off) +#endif +{ + return(-ENODEV); +} /* isdn_divert_write */ + + +/***************************************/ +/* select routines for various kernels */ +/***************************************/ +#if (LINUX_VERSION_CODE < 0x020117) +static int isdn_divert_select(struct inode *inode, struct file *file, int type, select_table * st) +{ + if (*((struct divert_info **)file->private_data)) + return 1; + else + { if (st) select_wait(&(rd_queue), st); + return 0; + } +} /* isdn_divert_select */ +#else +static unsigned int isdn_divert_poll(struct file *file, poll_table * wait) +{ unsigned int mask = 0; + + poll_wait(file, &(rd_queue), wait); + /* mask = POLLOUT | POLLWRNORM; */ + if (*((struct divert_info **)file->private_data)) + { mask |= POLLIN | POLLRDNORM; + } + return mask; +} /* isdn_divert_poll */ +#endif + +/****************/ +/* Open routine */ +/****************/ +static int isdn_divert_open(struct inode *ino, struct file *filep) +{ int flags; + + MOD_INC_USE_COUNT; + save_flags(flags); + cli(); + if_used++; + if (divert_info_head) + (struct divert_info **)filep->private_data = &(divert_info_tail->next); + else + (struct divert_info **)filep->private_data = &divert_info_head; + restore_flags(flags); + /* start_divert(); */ + return(0); +} /* isdn_divert_open */ + +/*******************/ +/* close routine */ +/*******************/ +#if (LINUX_VERSION_CODE < 0x020117) +static void isdn_divert_close(struct inode *ino, struct file *filep) +#else +static int isdn_divert_close(struct inode *ino, struct file *filep) +#endif +{ struct divert_info *inf; + int flags; + + save_flags(flags); + cli(); + if_used--; + inf = *((struct divert_info **)filep->private_data); + while (inf) + { inf->usage_cnt--; + inf = inf->next; + } + restore_flags(flags); + if (if_used <= 0) + while (divert_info_head) + { inf = divert_info_head; + divert_info_head = divert_info_head->next; + kfree(inf); + } + MOD_DEC_USE_COUNT; +#if (LINUX_VERSION_CODE < 0x020117) +#else + return(0); +#endif +} /* isdn_divert_close */ + +/*********/ +/* IOCTL */ +/*********/ +static int isdn_divert_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ divert_ioctl dioctl; + int i, flags; + divert_rule *rulep; + char *cp; + + if ((i = copy_from_user(&dioctl,(char *) arg, sizeof(dioctl)))) + return(i); + + switch (cmd) + { + case IIOCGETVER: + dioctl.drv_version = DIVERT_IIOC_VERSION ; /* set version */ + break; + + case IIOCGETDRV: + if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0) + return(-EINVAL); + break; + + case IIOCGETNAM: + cp = divert_if.drv_to_name(dioctl.getid.drvid); + if (!cp) return(-EINVAL); + if (!*cp) return(-EINVAL); + strcpy(dioctl.getid.drvnam,cp); + break; + + case IIOCGETRULE: + if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) + return(-EINVAL); + dioctl.getsetrule.rule = *rulep; /* copy data */ + break; + + case IIOCMODRULE: + if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) + return(-EINVAL); + save_flags(flags); + cli(); + *rulep = dioctl.getsetrule.rule; /* copy data */ + restore_flags(flags); + return(0); /* no copy required */ + break; + + case IIOCINSRULE: + return(insertrule(dioctl.getsetrule.ruleidx,&dioctl.getsetrule.rule)); + break; + + case IIOCDELRULE: + return(deleterule(dioctl.getsetrule.ruleidx)); + break; + + case IIOCDODFACT: + return(deflect_extern_action(dioctl.fwd_ctrl.subcmd, + dioctl.fwd_ctrl.callid, + dioctl.fwd_ctrl.to_nr)); + + case IIOCDOCFACT: + case IIOCDOCFDIS: + case IIOCDOCFINT: + if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid)) + return(-EINVAL); /* invalid driver */ + if ((i = cf_command(dioctl.cf_ctrl.drvid, + (cmd == IIOCDOCFACT) ? 1: (cmd == IIOCDOCFDIS) ? 0:2, + dioctl.cf_ctrl.cfproc, + dioctl.cf_ctrl.msn, + dioctl.cf_ctrl.service, + dioctl.cf_ctrl.fwd_nr, + &dioctl.cf_ctrl.procid))) + return(i); + break; + + default: + return(-EINVAL); + } /* switch cmd */ + return(copy_to_user((char *) arg, &dioctl, sizeof(dioctl))); /* success */ +} /* isdn_divert_ioctl */ + + +#ifdef CONFIG_PROC_FS +#if (LINUX_VERSION_CODE < 0x020117) +static LSTYPE +isdn_divert_lseek(struct inode *inode, struct file *file, LSARG offset, int orig) +#else +static loff_t +isdn_divert_lseek(struct file *file, loff_t offset, int orig) +#endif +{ + return -ESPIPE; +} + +#if (LINUX_VERSION_CODE < 0x020117) +static struct file_operations isdn_fops = +{ + isdn_divert_lseek, + isdn_divert_read, + isdn_divert_write, + NULL, /* isdn_readdir */ + isdn_divert_select, /* isdn_select */ + isdn_divert_ioctl, /* isdn_ioctl */ + NULL, /* isdn_mmap */ + isdn_divert_open, + isdn_divert_close, + NULL /* fsync */ +}; + +#else + +static struct file_operations isdn_fops = +{ + isdn_divert_lseek, + isdn_divert_read, + isdn_divert_write, + NULL, /* isdn_readdir */ + isdn_divert_poll, /* isdn_poll */ + isdn_divert_ioctl, /* isdn_ioctl */ + NULL, /* isdn_mmap */ + isdn_divert_open, + NULL, /* flush */ + isdn_divert_close, + NULL /* fsync */ +}; +#endif /* kernel >= 2.1 */ + + +/* + * proc directories can do almost nothing.. + */ +struct inode_operations proc_isdn_inode_ops = { + &isdn_fops, /* isdn divert special 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 */ +}; + +/****************************/ +/* isdn subdir in /proc/net */ +/****************************/ +static struct proc_dir_entry isdn_proc_entry = + { 0, 4, "isdn", S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, 0, + &proc_dir_inode_operations,NULL,NULL,NULL,NULL,NULL + }; + +static struct proc_dir_entry isdn_divert_entry = +{ 0, 6, "divert",S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_isdn_inode_ops, + NULL + }; + +/*****************************************************************/ +/* variables used for automatic determining existence of proc fs */ +/*****************************************************************/ +static int (*proc_reg_dynamic)(struct proc_dir_entry *, struct proc_dir_entry *) = NULL; +static int (*proc_unreg)(struct proc_dir_entry *, int) = NULL; + +#endif CONFIG_PROC_FS + +/***************************************************************************/ +/* divert_dev_init must be called before the proc filesystem may be used */ +/***************************************************************************/ +int divert_dev_init(void) +{ int i; + +#ifdef COMPAT_HAS_NEW_WAITQ + init_waitqueue_head(&rd_queue); +#endif + +#ifdef CONFIG_PROC_FS +#if (LINUX_VERSION_CODE < 0x020117) + (void *) proc_reg_dynamic = get_module_symbol("","proc_register_dynamic"); + (void *) proc_unreg = get_module_symbol("","proc_unregister"); + if (proc_unreg) + { i = proc_reg_dynamic(&proc_net,&isdn_proc_entry); + if (i) return(i); + i = proc_reg_dynamic(&isdn_proc_entry,&isdn_divert_entry); + if (i) + { proc_unreg(&proc_net,isdn_proc_entry.low_ino); + return(i); + } + } /* proc exists */ +#else + (void *) proc_reg_dynamic = get_module_symbol("","proc_register"); + (void *) proc_unreg = get_module_symbol("","proc_unregister"); + if (proc_unreg) + { i = proc_reg_dynamic(proc_net,&isdn_proc_entry); + if (i) return(i); + i = proc_reg_dynamic(&isdn_proc_entry,&isdn_divert_entry); + if (i) + { proc_unreg(proc_net,isdn_proc_entry.low_ino); + return(i); + } + } /* proc exists */ +#endif +#endif CONFIG_PROC_FS + + return(0); +} /* divert_dev_init */ + +/***************************************************************************/ +/* divert_dev_deinit must be called before leaving isdn when included as */ +/* a module. */ +/***************************************************************************/ +int divert_dev_deinit(void) +{ int i; + +#ifdef CONFIG_PROC_FS + if (proc_unreg) + { i = proc_unreg(&isdn_proc_entry,isdn_divert_entry.low_ino); + if (i) return(i); +#if (LINUX_VERSION_CODE < 0x020117) + i = proc_unreg(&proc_net,isdn_proc_entry.low_ino); +#else + i = proc_unreg(proc_net,isdn_proc_entry.low_ino); +#endif + if (i) return(i); + } /* proc exists */ +#endif CONFIG_PROC_FS + + return(0); +} /* divert_dev_deinit */ + diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c new file mode 100644 index 000000000..f10ea18b2 --- /dev/null +++ b/drivers/isdn/divert/isdn_divert.c @@ -0,0 +1,911 @@ +/* + * $Id: isdn_divert.c,v 1.4 1999/08/25 20:02:21 werner Exp $ + * + * DSS1 main diversion supplementary handling for i4l. + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.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: isdn_divert.c,v $ + * Revision 1.4 1999/08/25 20:02:21 werner + * Changed return values for stat_icall(w) from 3->4 and 4->5 because of conflicts + * with existing software definitions. (PtP incomplete called party number) + * + * Revision 1.3 1999/08/22 20:26:35 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.2 1999/07/04 21:37:32 werner + * Ported from kernel version 2.0 + * + * + * + */ + + + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/proc_fs.h> +#include "isdn_divert.h" + +/**********************************/ +/* structure keeping calling info */ +/**********************************/ +struct call_struc + { isdn_ctrl ics; /* delivered setup + driver parameters */ + ulong divert_id; /* Id delivered to user */ + unsigned char akt_state; /* actual state */ + char deflect_dest[35]; /* deflection destination */ + struct timer_list timer; /* timer control structure */ + char info[90]; /* device info output */ + struct call_struc *next; /* pointer to next entry */ + struct call_struc *prev; + }; + + +/********************************************/ +/* structure keeping deflection table entry */ +/********************************************/ +struct deflect_struc + { struct deflect_struc *next,*prev; + divert_rule rule; /* used rule */ + }; + + +/*****************************************/ +/* variables for main diversion services */ +/*****************************************/ +/* diversion/deflection processes */ +static struct call_struc *divert_head = NULL; /* head of remembered entrys */ +static ulong next_id = 1; /* next info id */ +static struct deflect_struc *table_head = NULL; +static struct deflect_struc *table_tail = NULL; +static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */ + +/***************************/ +/* timer callback function */ +/***************************/ +static void deflect_timer_expire(ulong arg) +{ int flags; + struct call_struc *cs = (struct call_struc *) arg; + + save_flags(flags); + cli(); + del_timer(&cs->timer); /* delete active timer */ + restore_flags(flags); + + switch(cs->akt_state) + { case DEFLECT_PROCEED: + cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */ + divert_if.ll_cmd(&cs->ics); + save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + break; + + case DEFLECT_ALERT: + cs->ics.command = ISDN_CMD_REDIR; /* protocol */ + strcpy(cs->ics.parm.setup.phone,cs->deflect_dest); + strcpy(cs->ics.parm.setup.eazmsn,"Testtext delayed"); + divert_if.ll_cmd(&cs->ics); + save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + break; + + case DEFLECT_AUTODEL: + default: + save_flags(flags); + cli(); + if (cs->prev) + cs->prev->next = cs->next; /* forward link */ + else + divert_head = cs->next; + if (cs->next) + cs->next->prev = cs->prev; /* back link */ + restore_flags(flags); + kfree(cs); + return; + + } /* switch */ +} /* deflect_timer_func */ + + +/*****************************************/ +/* handle call forwarding de/activations */ +/* 0 = deact, 1 = act, 2 = interrogate */ +/*****************************************/ +int cf_command(int drvid, int mode, + u_char proc, char *msn, + u_char service, char *fwd_nr, ulong *procid) +{ int retval,msnlen,flags; + int fwd_len; + char *p,*ielenp,tmp[60]; + struct call_struc *cs; + + if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */ + if ((proc & 0x7F) > 2) return(-EINVAL); + proc &= 3; + p = tmp; + *p++ = 0x30; /* enumeration */ + ielenp = p++; /* remember total length position */ + *p++ = 0xa; /* proc tag */ + *p++ = 1; /* length */ + *p++ = proc & 0x7F; /* procedure to de/activate/interrogate */ + *p++ = 0xa; /* service tag */ + *p++ = 1; /* length */ + *p++ = service; /* service to handle */ + + if (mode == 1) + { if (!*fwd_nr) return(-EINVAL); /* destination missing */ + if (strchr(fwd_nr,'.')) return(-EINVAL); /* subaddress not allowed */ + fwd_len = strlen(fwd_nr); + *p++ = 0x30; /* number enumeration */ + *p++ = fwd_len + 2; /* complete forward to len */ + *p++ = 0x80; /* fwd to nr */ + *p++ = fwd_len; /* length of number */ + strcpy(p,fwd_nr); /* copy number */ + p += fwd_len; /* pointer beyond fwd */ + } /* activate */ + + msnlen = strlen(msn); + *p++ = 0x80; /* msn number */ + if (msnlen > 1) + { *p++ = msnlen; /* length */ + strcpy(p,msn); + p += msnlen; + } + else *p++ = 0; + + *ielenp = p - ielenp - 1; /* set total IE length */ + + /* allocate mem for information struct */ + if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) + return(-ENOMEM); /* no memory */ + init_timer(&cs->timer); + cs->info[0] = '\0'; + cs->timer.function = deflect_timer_expire; + cs->timer.data = (ulong) cs; /* pointer to own structure */ + cs->ics.driver = drvid; + cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */ + cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */ + cs->ics.parm.dss1_io.proc = (mode == 1) ? 7: (mode == 2) ? 11:8; /* operation */ + cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */ + cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */ + cs->ics.parm.dss1_io.data = tmp; /* start of buffer */ + + save_flags(flags); + cli(); + cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */ + restore_flags(flags); + *procid = cs->ics.parm.dss1_io.ll_id; + + sprintf(cs->info,"%d 0x%lx %s%s 0 %s %0x %d%s%s\n", + (!mode ) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT, + cs->ics.parm.dss1_io.ll_id, + (mode != 2) ? "" : "0 ", + divert_if.drv_to_name(cs->ics.driver), + msn, + service & 0xFF, + proc, + (mode != 1) ? "" : " 0 ", + (mode != 1) ? "" : fwd_nr); + + retval = divert_if.ll_cmd(&cs->ics); /* excute command */ + + if (!retval) + { cs->prev = NULL; + save_flags(flags); + cli(); + cs->next = divert_head; + divert_head = cs; + restore_flags(flags); + } + else + kfree(cs); + return(retval); +} /* cf_command */ + + +/****************************************/ +/* handle a external deflection command */ +/****************************************/ +int deflect_extern_action(u_char cmd, ulong callid, char *to_nr) +{ struct call_struc *cs; + isdn_ctrl ic; + int flags; + int i; + + if ((cmd & 0x7F) > 2) return(-EINVAL); /* invalid command */ + cs = divert_head; /* start of parameter list */ + while (cs) + { if (cs->divert_id == callid) break; /* found */ + cs = cs->next; + } /* search entry */ + if (!cs) return(-EINVAL); /* invalid callid */ + + ic.driver = cs->ics.driver; + ic.arg = cs->ics.arg; + i = -EINVAL; + if (cs->akt_state == DEFLECT_AUTODEL) return(i); /* no valid call */ + switch (cmd & 0x7F) + { case 0: /* hangup */ + del_timer(&cs->timer); + ic.command = ISDN_CMD_HANGUP; + i = divert_if.ll_cmd(&ic); + save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + break; + + case 1: /* alert */ + if (cs->akt_state == DEFLECT_ALERT) return(0); + cmd &= 0x7F; /* never wait */ + del_timer(&cs->timer); + ic.command = ISDN_CMD_ALERT; + if ((i = divert_if.ll_cmd(&ic))) + { save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + } + else + cs->akt_state = DEFLECT_ALERT; + break; + + case 2: /* redir */ + del_timer(&cs->timer); + strcpy(cs->ics.parm.setup.phone, to_nr); + strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual"); + ic.command = ISDN_CMD_REDIR; + if ((i = divert_if.ll_cmd(&ic))) + { save_flags(flags); + cli(); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + add_timer(&cs->timer); + restore_flags(flags); + } + else + cs->akt_state = DEFLECT_ALERT; + break; + + } /* switch */ + return(i); +} /* deflect_extern_action */ + +/********************************/ +/* insert a new rule before idx */ +/********************************/ +int insertrule(int idx, divert_rule *newrule) +{ struct deflect_struc *ds,*ds1; + int flags; + + if (!(ds = (struct deflect_struc *) kmalloc(sizeof(struct deflect_struc), + GFP_KERNEL))) + return(-ENOMEM); /* no memory */ + + ds->rule = *newrule; /* set rule */ + + save_flags(flags); + cli(); + + if (idx >= 0) + { ds1 = table_head; + while ((ds1) && (idx > 0)) + { idx--; + ds1 = ds1->next; + } + if (!ds1) idx = -1; + } + + if (idx < 0) + { ds->prev = table_tail; /* previous entry */ + ds->next = NULL; /* end of chain */ + if (ds->prev) + ds->prev->next = ds; /* last forward */ + else + table_head = ds; /* is first entry */ + table_tail = ds; /* end of queue */ + } + else + { ds->next = ds1; /* next entry */ + ds->prev = ds1->prev; /* prev entry */ + ds1->prev = ds; /* backward chain old element */ + if (!ds->prev) + table_head = ds; /* first element */ + } + + restore_flags(flags); + return(0); +} /* insertrule */ + +/***********************************/ +/* delete the rule at position idx */ +/***********************************/ +int deleterule(int idx) +{ struct deflect_struc *ds,*ds1; + int flags; + + if (idx < 0) + { save_flags(flags); + cli(); + ds = table_head; + table_head = NULL; + table_tail = NULL; + restore_flags(flags); + while (ds) + { ds1 = ds; + ds = ds->next; + kfree(ds1); + } + return(0); + } + + save_flags(flags); + cli(); + ds = table_head; + + while ((ds) && (idx > 0)) + { idx--; + ds = ds->next; + } + + if (!ds) + { restore_flags(flags); + return(-EINVAL); + } + + if (ds->next) + ds->next->prev = ds->prev; /* backward chain */ + else + table_tail = ds->prev; /* end of chain */ + + if (ds->prev) + ds->prev->next = ds->next; /* forward chain */ + else + table_head = ds->next; /* start of chain */ + + restore_flags(flags); + kfree(ds); + return(0); +} /* deleterule */ + +/*******************************************/ +/* get a pointer to a specific rule number */ +/*******************************************/ +divert_rule *getruleptr(int idx) +{ struct deflect_struc *ds = table_head; + + if (idx < 0) return(NULL); + while ((ds) && (idx >= 0)) + { if (!(idx--)) + { return(&ds->rule); + break; + } + ds = ds->next; + } + return(NULL); +} /* getruleptr */ + +/*************************************************/ +/* called from common module on an incoming call */ +/*************************************************/ +int isdn_divert_icall(isdn_ctrl *ic) +{ int retval = 0; + int flags; + struct call_struc *cs = NULL; + struct deflect_struc *dv; + char *p,*p1; + u_char accept; + + /* first check the internal deflection table */ + for (dv = table_head; dv ; dv = dv->next ) + { /* scan table */ + if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) || + ((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL))) + continue; /* call option check */ + if (!(dv->rule.drvid & (1L << ic->driver))) + continue; /* driver not matching */ + if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1)) + continue; /* si1 not matching */ + if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2)) + continue; /* si2 not matching */ + + p = dv->rule.my_msn; + p1 = ic->parm.setup.eazmsn; + accept = 0; + while (*p) + { /* complete compare */ + if (*p == '-') + { accept = 1; /* call accepted */ + break; + } + if (*p++ != *p1++) + break; /* not accepted */ + if ((!*p) && (!*p1)) + accept = 1; + } /* complete compare */ + if (!accept) continue; /* not accepted */ + + if ((strcmp(dv->rule.caller,"0")) || (ic->parm.setup.phone[0])) + { p = dv->rule.caller; + p1 = ic->parm.setup.phone; + accept = 0; + while (*p) + { /* complete compare */ + if (*p == '-') + { accept = 1; /* call accepted */ + break; + } + if (*p++ != *p1++) + break; /* not accepted */ + if ((!*p) && (!*p1)) + accept = 1; + } /* complete compare */ + if (!accept) continue; /* not accepted */ + } + + switch (dv->rule.action) + { case DEFLECT_IGNORE: + return(0); + break; + + case DEFLECT_ALERT: + case DEFLECT_PROCEED: + case DEFLECT_REPORT: + case DEFLECT_REJECT: + if (dv->rule.action == DEFLECT_PROCEED) + if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime))) + return(0); /* no external deflection needed */ + if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) + return(0); /* no memory */ + init_timer(&cs->timer); + cs->info[0] = '\0'; + cs->timer.function = deflect_timer_expire; + cs->timer.data = (ulong) cs; /* pointer to own structure */ + + cs->ics = *ic; /* copy incoming data */ + if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone,"0"); + if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn,"0"); + cs->ics.parm.setup.screen = dv->rule.screen; + if (dv->rule.waittime) + cs->timer.expires = jiffies + (HZ * dv->rule.waittime); + else + if (dv->rule.action == DEFLECT_PROCEED) + cs->timer.expires = jiffies + (HZ * extern_wait_max); + else + cs->timer.expires = 0; + cs->akt_state = dv->rule.action; + save_flags(flags); + cli(); + cs->divert_id = next_id++; /* new sequence number */ + restore_flags(flags); + cs->prev = NULL; + if (cs->akt_state == DEFLECT_ALERT) + { strcpy(cs->deflect_dest,dv->rule.to_nr); + if (!cs->timer.expires) + { strcpy(ic->parm.setup.eazmsn,"Testtext direkt"); + ic->parm.setup.screen = dv->rule.screen; + strcpy(ic->parm.setup.phone,dv->rule.to_nr); + cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */ + cs->timer.expires = jiffies + (HZ * AUTODEL_TIME); + retval = 5; + } + else + retval = 1; /* alerting */ + } + else + { cs->deflect_dest[0] = '\0'; + retval = 4; /* only proceed */ + } + sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n", + cs->akt_state, + cs->divert_id, + divert_if.drv_to_name(cs->ics.driver), + (ic->command == ISDN_STAT_ICALLW) ? "1":"0", + cs->ics.parm.setup.phone, + cs->ics.parm.setup.eazmsn, + cs->ics.parm.setup.si1, + cs->ics.parm.setup.si2, + cs->ics.parm.setup.screen, + dv->rule.waittime, + cs->deflect_dest); + if ((dv->rule.action == DEFLECT_REPORT) || + (dv->rule.action == DEFLECT_REJECT)) + { put_info_buffer(cs->info); + kfree(cs); /* remove */ + return((dv->rule.action == DEFLECT_REPORT) ? 0:2); /* nothing to do */ + } + break; + + default: + return(0); /* ignore call */ + break; + } /* switch action */ + break; + } /* scan_table */ + + if (cs) + { cs->prev = NULL; + save_flags(flags); + cli(); + cs->next = divert_head; + divert_head = cs; + if (cs->timer.expires) add_timer(&cs->timer); + restore_flags(flags); + + put_info_buffer(cs->info); + return(retval); + } + else + return(0); +} /* isdn_divert_icall */ + + +void deleteprocs(void) +{ struct call_struc *cs, *cs1; + int flags; + + save_flags(flags); + cli(); + cs = divert_head; + divert_head = NULL; + while (cs) + { del_timer(&cs->timer); + cs1 = cs; + cs = cs->next; + kfree(cs1); + } + restore_flags(flags); +} /* deleteprocs */ + +/****************************************************/ +/* put a address including address type into buffer */ +/****************************************************/ +int put_address(char *st, u_char *p, int len) +{ u_char retval = 0; + u_char adr_typ = 0; /* network standard */ + + if (len < 2) return(retval); + if (*p == 0xA1) + { retval = *(++p) + 2; /* total length */ + if (retval > len) return(0); /* too short */ + len = retval - 2; /* remaining length */ + if (len < 3) return(0); + if ((*(++p) != 0x0A) || (*(++p) != 1)) return(0); + adr_typ = *(++p); + len -= 3; + p++; + if (len < 2) return(0); + if (*p++ != 0x12) return(0); + if (*p > len) return(0); /* check number length */ + len = *p++; + } + else + if (*p == 0x80) + { retval = *(++p) + 2; /* total length */ + if (retval > len) return(0); + len = retval - 2; + p++; + } + else + return(0); /* invalid address information */ + + sprintf(st,"%d ",adr_typ); + st += strlen(st); + if (!len) + *st++ = '-'; + else + while (len--) + *st++ = *p++; + *st = '\0'; + return(retval); +} /* put_address */ + +/*************************************/ +/* report a succesfull interrogation */ +/*************************************/ +int interrogate_success(isdn_ctrl *ic, struct call_struc *cs) +{ char *src = ic->parm.dss1_io.data; + int restlen = ic->parm.dss1_io.datalen; + int cnt = 1; + u_char n,n1; + char st[90], *p, *stp; + + if (restlen < 2) return(-100); /* frame too short */ + if (*src++ != 0x30) return(-101); + if ((n = *src++) > 0x81) return(-102); /* invalid length field */ + restlen -= 2; /* remaining bytes */ + if (n == 0x80) + { if (restlen < 2) return(-103); + if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104); + restlen -= 2; + } + else + if ( n == 0x81) + { n = *src++; + restlen--; + if (n > restlen) return(-105); + restlen = n; + } + else + if (n > restlen) return(-106); + else + restlen = n; /* standard format */ + if (restlen < 3) return(-107); /* no procedure */ + if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108); + restlen -= 3; + if (restlen < 2) return(-109); /* list missing */ + if (*src == 0x31) + { src++; + if ((n = *src++) > 0x81) return(-110); /* invalid length field */ + restlen -= 2; /* remaining bytes */ + if (n == 0x80) + { if (restlen < 2) return(-111); + if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112); + restlen -= 2; + } + else + if ( n == 0x81) + { n = *src++; + restlen--; + if (n > restlen) return(-113); + restlen = n; + } + else + if (n > restlen) return(-114); + else + restlen = n; /* standard format */ + } /* result list header */ + + while (restlen >= 2) + { stp = st; + sprintf(stp,"%d 0x%lx %d %s ",DIVERT_REPORT, ic->parm.dss1_io.ll_id, + cnt++,divert_if.drv_to_name(ic->driver)); + stp += strlen(stp); + if (*src++ != 0x30) return(-115); /* invalid enum */ + n = *src++; + restlen -= 2; + if (n > restlen) return(-116); /* enum length wrong */ + restlen -= n; + p = src; /* one entry */ + src += n; + if (!(n1 = put_address(stp,p,n & 0xFF))) continue; + stp += strlen(stp); + p += n1; + n -= n1; + if (n < 6) continue; /* no service and proc */ + if ((*p++ != 0x0A) || (*p++ != 1)) continue; + sprintf(stp," 0x%02x ",(*p++) & 0xFF); + stp += strlen(stp); + if ((*p++ != 0x0A) || (*p++ != 1)) continue; + sprintf(stp,"%d ",(*p++) & 0xFF); + stp += strlen(stp); + n -= 6; + if (n > 2) + { if (*p++ != 0x30) continue; + if (*p > (n-2)) continue; + n = *p++; + if (!(n1 = put_address(stp,p,n & 0xFF))) continue; + stp += strlen(stp); + } + sprintf(stp,"\n"); + put_info_buffer(st); + } /* while restlen */ + if (restlen) return(-117); + return(0); +} /* interrogate_success */ + +/*********************************************/ +/* callback for protocol specific extensions */ +/*********************************************/ +int prot_stat_callback(isdn_ctrl *ic) +{ struct call_struc *cs, *cs1; + int i,flags; + + cs = divert_head; /* start of list */ + cs1 = NULL; + while (cs) + { if (ic->driver == cs->ics.driver) + { switch (cs->ics.arg) + { case DSS1_CMD_INVOKE: + if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) && + (cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id)) + { switch (ic->arg) + { case DSS1_STAT_INVOKE_ERR: + sprintf(cs->info,"128 0x%lx 0x%x\n", + ic->parm.dss1_io.ll_id, + ic->parm.dss1_io.timeout); + put_info_buffer(cs->info); + break; + + case DSS1_STAT_INVOKE_RES: + switch (cs->ics.parm.dss1_io.proc) + { case 7: + case 8: + put_info_buffer(cs->info); + break; + + case 11: + i = interrogate_success(ic,cs); + if (i) + sprintf(cs->info,"%d 0x%lx %d\n",DIVERT_REPORT, + ic->parm.dss1_io.ll_id,i); + put_info_buffer(cs->info); + break; + + default: + printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc); + break; + } + + +#if 0 + sprintf(st, "0x%lx 0x%lx",ic->arg, ic->parm.dss1_io.ll_id); + p = st + strlen(st); + p1 = ic->parm.dss1_io.data; + i = ic->parm.dss1_io.datalen; + while ((i > 0) && (p - st < 530)) + { p += sprintf(p," %02x",(*p1++) & 0xFF); + i--; + } + sprintf(p, "\n"); + put_info_buffer(st); +#endif + break; + + default: + printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg); + break; + } + cs1 = cs; /* remember structure */ + cs = NULL; + continue; /* abort search */ + } /* id found */ + break; + + case DSS1_CMD_INVOKE_ABORT: + printk(KERN_WARNING "dss1_divert unhandled invoke abort\n"); + break; + + default: + printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg); + break; + } /* switch ics.arg */ + cs = cs->next; + } /* driver ok */ + } + + if (!cs1) + { printk(KERN_WARNING "dss1_divert unhandled process\n"); + return(0); + } + + if (cs1->ics.driver == -1) + { save_flags(flags); + cli(); + del_timer(&cs1->timer); + if (cs1->prev) + cs1->prev->next = cs1->next; /* forward link */ + else + divert_head = cs1->next; + if (cs1->next) + cs1->next->prev = cs1->prev; /* back link */ + restore_flags(flags); + kfree(cs1); + } + + return(0); +} /* prot_stat_callback */ + + +/***************************/ +/* status callback from HL */ +/***************************/ +int isdn_divert_stat_callback(isdn_ctrl *ic) +{ struct call_struc *cs, *cs1; + int flags, retval; + + retval = -1; + cs = divert_head; /* start of list */ + while (cs) + { if ((ic->driver == cs->ics.driver) && (ic->arg == cs->ics.arg)) + { switch (ic->command) + { case ISDN_STAT_DHUP: + sprintf(cs->info,"129 0x%lx\n",cs->divert_id); + del_timer(&cs->timer); + cs->ics.driver = -1; + break; + + case ISDN_STAT_CAUSE: + sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num); + break; + + case ISDN_STAT_REDIR: + sprintf(cs->info,"131 0x%lx\n",cs->divert_id); + del_timer(&cs->timer); + cs->ics.driver = -1; + break; + + default: + sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command)); + break; + } + put_info_buffer(cs->info); + retval = 0; + } + cs1 = cs; + cs = cs->next; + if (cs1->ics.driver == -1) + { + save_flags(flags); + cli(); + if (cs1->prev) + cs1->prev->next = cs1->next; /* forward link */ + else + divert_head = cs1->next; + if (cs1->next) + cs1->next->prev = cs1->prev; /* back link */ + restore_flags(flags); + kfree(cs1); + } + } + return(retval); /* not found */ +} /* isdn_divert_stat_callback */ + + +/********************/ +/* callback from ll */ +/********************/ +int ll_callback(isdn_ctrl *ic) +{ + switch (ic->command) + { case ISDN_STAT_ICALL: + case ISDN_STAT_ICALLW: + return(isdn_divert_icall(ic)); + break; + + case ISDN_STAT_PROT: + if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO) + { if (ic->arg != DSS1_STAT_INVOKE_BRD) + return(prot_stat_callback(ic)); + else + return(0); /* DSS1 invoke broadcast */ + } + else + return(-1); /* protocol not euro */ + + default: + return(isdn_divert_stat_callback(ic)); + } +} /* ll_callback */ + diff --git a/drivers/isdn/divert/isdn_divert.h b/drivers/isdn/divert/isdn_divert.h new file mode 100644 index 000000000..1d329c0fb --- /dev/null +++ b/drivers/isdn/divert/isdn_divert.h @@ -0,0 +1,164 @@ +/* + * $Id: isdn_divert.h,v 1.3 1999/08/22 20:26:37 calle Exp $ + * + * Header for the diversion supplementary ioctl interface. + * + * Copyright 1998 by Werner Cornelius (werner@ikt.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: isdn_divert.h,v $ + * Revision 1.3 1999/08/22 20:26:37 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.2 1999/07/04 21:37:33 werner + * Ported from kernel version 2.0 + * + * + * + */ + + + +#include <linux/ioctl.h> +#include <linux/types.h> + +/******************************************/ +/* IOCTL codes for interface to user prog */ +/******************************************/ +#define DIVERT_IIOC_VERSION 0x01 /* actual version */ +#define IIOCGETVER _IO('I', 1) /* get version of interface */ +#define IIOCGETDRV _IO('I', 2) /* get driver number */ +#define IIOCGETNAM _IO('I', 3) /* get driver name */ +#define IIOCGETRULE _IO('I', 4) /* read one rule */ +#define IIOCMODRULE _IO('I', 5) /* modify/replace a rule */ +#define IIOCINSRULE _IO('I', 6) /* insert/append one rule */ +#define IIOCDELRULE _IO('I', 7) /* delete a rule */ +#define IIOCDODFACT _IO('I', 8) /* hangup/reject/alert/immediately deflect a call */ +#define IIOCDOCFACT _IO('I', 9) /* activate control forwarding in PBX */ +#define IIOCDOCFDIS _IO('I',10) /* deactivate control forwarding in PBX */ +#define IIOCDOCFINT _IO('I',11) /* interrogate control forwarding in PBX */ + +/*************************************/ +/* states reported through interface */ +/*************************************/ +#define DEFLECT_IGNORE 0 /* ignore incoming call */ +#define DEFLECT_REPORT 1 /* only report */ +#define DEFLECT_PROCEED 2 /* deflect when externally triggered */ +#define DEFLECT_ALERT 3 /* alert and deflect after delay */ +#define DEFLECT_REJECT 4 /* reject immediately */ +#define DIVERT_ACTIVATE 5 /* diversion activate */ +#define DIVERT_DEACTIVATE 6 /* diversion deactivate */ +#define DIVERT_REPORT 7 /* interrogation result */ +#define DEFLECT_AUTODEL 255 /* only for internal use */ + +#define DEFLECT_ALL_IDS 0xFFFFFFFF /* all drivers selected */ + +typedef struct + { ulong drvid; /* driver ids, bit mapped */ + char my_msn[35]; /* desired msn, subaddr allowed */ + char caller[35]; /* caller id, partial string with * + subaddr allowed */ + char to_nr[35]; /* deflected to number incl. subaddress */ + u_char si1,si2; /* service indicators, si1=bitmask, si1+2 0 = all */ + u_char screen; /* screening: 0 = no info, 1 = info, 2 = nfo with nr */ + u_char callopt; /* option for call handling: + 0 = all calls + 1 = only non waiting calls + 2 = only waiting calls */ + u_char action; /* desired action: + 0 = don't report call -> ignore + 1 = report call, do not allow/proceed for deflection + 2 = report call, send proceed, wait max waittime secs + 3 = report call, alert and deflect after waittime + 4 = report call, reject immediately + actions 1-2 only take place if interface is opened + */ + u_char waittime; /* maximum wait time for proceeding */ + } divert_rule; + +typedef union + { int drv_version; /* return of driver version */ + struct + { int drvid; /* id of driver */ + char drvnam[30]; /* name of driver */ + } getid; + struct + { int ruleidx; /* index of rule */ + divert_rule rule; /* rule parms */ + } getsetrule; + struct + { u_char subcmd; /* 0 = hangup/reject, + 1 = alert, + 2 = deflect */ + ulong callid; /* id of call delivered by ascii output */ + char to_nr[35]; /* destination when deflect, + else uus1 string (maxlen 31), + data from rule used if empty */ + } fwd_ctrl; + struct + { int drvid; /* id of driver */ + u_char cfproc; /* cfu = 0, cfb = 1, cfnr = 2 */ + ulong procid; /* process id returned when no error */ + u_char service; /* basically coded service, 0 = all */ + char msn[25]; /* desired msn, empty = all */ + char fwd_nr[35];/* forwarded to number + subaddress */ + } cf_ctrl; + } divert_ioctl; + +#ifdef __KERNEL__ + +#include <linux/isdnif.h> +#include <linux/isdn_divertif.h> + +#define AUTODEL_TIME 30 /* timeout in s to delete internal entrys */ + +/**************************************************/ +/* structure keeping ascii info for device output */ +/**************************************************/ +struct divert_info + { struct divert_info *next; + ulong usage_cnt; /* number of files still to work */ + char info_start[2]; /* info string start */ + }; + + +/**************/ +/* Prototypes */ +/**************/ +extern ulong if_used; /* number of interface users */ +extern int divert_dev_deinit(void); +extern int divert_dev_init(void); +extern void put_info_buffer(char *); +extern int ll_callback(isdn_ctrl *); +extern isdn_divert_if divert_if; +extern divert_rule *getruleptr(int); +extern int insertrule(int, divert_rule *); +extern int deleterule(int); +extern void deleteprocs(void); +extern int deflect_extern_action(u_char, ulong, char *); +extern int cf_command(int, int, u_char, char *, u_char, char *, ulong *); + +#endif __KERNEL__ + + + + + + + + diff --git a/drivers/isdn/eicon/eicon.h b/drivers/isdn/eicon/eicon.h index 9552d2b54..beee023dc 100644 --- a/drivers/isdn/eicon/eicon.h +++ b/drivers/isdn/eicon/eicon.h @@ -1,4 +1,4 @@ -/* $Id: eicon.h,v 1.5 1999/03/29 11:19:41 armin Exp $ +/* $Id: eicon.h,v 1.11 1999/08/29 17:23:44 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * @@ -21,6 +21,33 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon.h,v $ + * Revision 1.11 1999/08/29 17:23:44 armin + * New setup compat. + * Bugfix if compile as not module. + * + * Revision 1.10 1999/08/22 20:26:41 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.9 1999/08/18 20:16:57 armin + * Added XLOG function for all cards. + * Bugfix of alloc_skb NULL pointer. + * + * Revision 1.8 1999/07/25 15:12:01 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.7 1999/07/11 17:16:23 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * + * Revision 1.6 1999/06/09 19:31:24 armin + * Wrong PLX size for request_region() corrected. + * Added first MCA code from Erik Weber. + * * Revision 1.5 1999/03/29 11:19:41 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -60,6 +87,7 @@ #define EICON_IOCTL_LOADPCI 7 #define EICON_IOCTL_LOADISA 8 #define EICON_IOCTL_GETVER 9 +#define EICON_IOCTL_GETXLOG 10 #define EICON_IOCTL_MANIF 90 @@ -89,6 +117,7 @@ #define MAX_HEADER_LEN 10 + /* Struct for adding new cards */ typedef struct eicon_cdef { int membase; @@ -100,6 +129,7 @@ typedef struct eicon_cdef { #define EICON_ISA_BOOT_NORMAL 2 /* Struct for downloading protocol via ioctl for ISA cards */ +/* same struct for downloading protocol via ioctl for MCA cards */ typedef struct { /* start-up parameters */ unsigned char tei; @@ -156,6 +186,7 @@ typedef struct { /* Data for downloading protocol via ioctl */ typedef union { eicon_isa_codebuf isa; + eicon_isa_codebuf mca; eicon_pci_codebuf pci; } eicon_codebuf; @@ -171,6 +202,7 @@ typedef struct { #ifdef __KERNEL__ /* Kernel includes */ +#include <linux/config.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/tqueue.h> @@ -194,6 +226,8 @@ typedef struct { #include <linux/isdnif.h> +#include <linux/isdn_compat.h> + typedef struct { __u16 length __attribute__ ((packed)); /* length of data/parameter field */ __u8 P[1]; /* data/parameter field */ @@ -209,6 +243,92 @@ typedef struct { #endif /* KERNEL */ +#define DIVAS_SHARED_OFFSET (0x1000) + +#define MIPS_BUFFER_SZ 128 +#define MIPS_MAINT_OFFS 0xff00 + +#define XLOG_ERR_CARD_NUM (13) +#define XLOG_ERR_DONE (14) +#define XLOG_ERR_CMD (15) +#define XLOG_ERR_TIMEOUT (16) +#define XLOG_ERR_CARD_STATE (17) +#define XLOG_ERR_UNKNOWN (18) +#define XLOG_OK (0) + +typedef struct { + __u8 Id __attribute__ ((packed)); + __u8 uX __attribute__ ((packed)); + __u8 listen __attribute__ ((packed)); + __u8 active __attribute__ ((packed)); + __u8 sin[3] __attribute__ ((packed)); + __u8 bc[6] __attribute__ ((packed)); + __u8 llc[6] __attribute__ ((packed)); + __u8 hlc[6] __attribute__ ((packed)); + __u8 oad[20] __attribute__ ((packed)); +}DSigStruc; + +typedef struct { + __u32 cx_b1 __attribute__ ((packed)); + __u32 cx_b2 __attribute__ ((packed)); + __u32 cr_b1 __attribute__ ((packed)); + __u32 cr_b2 __attribute__ ((packed)); + __u32 px_b1 __attribute__ ((packed)); + __u32 px_b2 __attribute__ ((packed)); + __u32 pr_b1 __attribute__ ((packed)); + __u32 pr_b2 __attribute__ ((packed)); + __u16 er_b1 __attribute__ ((packed)); + __u16 er_b2 __attribute__ ((packed)); +}BL1Struc; + +typedef struct { + __u32 XTotal __attribute__ ((packed)); + __u32 RTotal __attribute__ ((packed)); + __u16 XError __attribute__ ((packed)); + __u16 RError __attribute__ ((packed)); +}L2Struc; + +typedef struct { + __u16 free_n; +}OSStruc; + +typedef union +{ + DSigStruc DSigStats; + BL1Struc BL1Stats; + L2Struc L2Stats; + OSStruc OSStats; + __u8 b[MIPS_BUFFER_SZ]; + __u16 w[MIPS_BUFFER_SZ>>1]; + __u16 l[MIPS_BUFFER_SZ>>2]; /* word is wrong, do not use! Use 'd' instead. */ + __u32 d[MIPS_BUFFER_SZ>>2]; +} MIPS_BUFFER; + +typedef struct +{ + __u8 req __attribute__ ((packed)); + __u8 rc __attribute__ ((packed)); + __u8 reserved[2] __attribute__ ((packed)); /* R3000 alignment ... */ + __u8 *mem __attribute__ ((packed)); + __u16 length __attribute__ ((packed)); /* used to be short */ + __u16 port __attribute__ ((packed)); + __u8 fill[4] __attribute__ ((packed)); /* data at offset 16 */ + MIPS_BUFFER data __attribute__ ((packed)); +} mi_pc_maint_t; + +typedef struct +{ + __u16 command; + mi_pc_maint_t pcm; +}xlogreq_t; + +typedef struct{ + __u16 code __attribute__ ((packed)); /* used to be short */ + __u16 timeh __attribute__ ((packed)); + __u16 timel __attribute__ ((packed)); + char buffer[MIPS_BUFFER_SZ - 6]; +}xlog_entry_t; + #define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48 #define DSP_COMBIFILE_FORMAT_VERSION_BCD 0x0100 @@ -333,19 +453,39 @@ typedef struct { __u16 ref; /* saved reference */ } entity; +#define FAX_MAX_SCANLINE 256 + +typedef struct { + __u8 PrevObject; + __u8 NextObject; + __u8 abLine[FAX_MAX_SCANLINE]; + __u8 abFrame[FAX_MAX_SCANLINE]; + unsigned int LineLen; + unsigned int LineDataLen; + __u32 LineData; + unsigned int NullBytesPos; + __u8 NullByteExist; + int PageCount; + __u8 Dle; + __u8 Eop; +} eicon_ch_fax_buf; typedef struct { int No; /* Channel Number */ unsigned short callref; /* Call Reference */ unsigned short fsm_state; /* Current D-Channel state */ unsigned short eazmask; /* EAZ-Mask for this Channel */ - unsigned int queued; /* User-Data Bytes in TX queue */ - unsigned int waitq; /* User-Data Bytes in wait queue */ - unsigned int waitpq; /* User-Data Bytes in packet queue */ + int queued; /* User-Data Bytes in TX queue */ + int waitq; /* User-Data Bytes in wait queue */ + int waitpq; /* User-Data Bytes in packet queue */ unsigned short plci; unsigned short ncci; unsigned char l2prot; /* Layer 2 protocol */ unsigned char l3prot; /* Layer 3 protocol */ +#ifdef CONFIG_ISDN_TTY_FAX + T30_s *fax; /* pointer to fax data in LL */ + eicon_ch_fax_buf fax2; /* fax related struct */ +#endif entity e; /* Entity */ char cpn[32]; /* remember cpn */ char oad[32]; /* remember oad */ @@ -392,14 +532,10 @@ typedef struct { #define EICON_LOCK_TX 0 #define EICON_LOCK_RX 1 -typedef struct { - int dummy; -} eicon_mca_card; - typedef union { eicon_isa_card isa; eicon_pci_card pci; - eicon_mca_card mca; + eicon_isa_card mca; } eicon_hwif; typedef struct { @@ -465,6 +601,9 @@ typedef struct eicon_card { char *status_buf_end; isdn_if interface; /* Interface to upper layer */ char regname[35]; /* Name used for request_region */ +#ifdef CONFIG_MCA + int mca_slot; /* # of cards MCA slot */ +#endif } eicon_card; /* -----------------------------------------------------------** @@ -521,6 +660,13 @@ extern void eicon_io_transmit(eicon_card *card); extern void eicon_irq(int irq, void *dev_id, struct pt_regs *regs); extern void eicon_io_rcv_dispatch(eicon_card *ccard); extern void eicon_io_ack_dispatch(eicon_card *ccard); +extern int eicon_get_xlog(eicon_card *card, xlogreq_t *xlogreq); +#ifdef CONFIG_MCA +extern int eicon_mca_find_card(int, int, int, char *); +extern int eicon_mca_probe(int, int, int, int, char *); +extern int eicon_info(char *, int , void *); +#endif /* CONFIG_MCA */ + extern ulong DebugVar; #endif /* __KERNEL__ */ diff --git a/drivers/isdn/eicon/eicon_dsp.h b/drivers/isdn/eicon/eicon_dsp.h index 94a4595c8..9ffbd9bdb 100644 --- a/drivers/isdn/eicon/eicon_dsp.h +++ b/drivers/isdn/eicon/eicon_dsp.h @@ -1,4 +1,4 @@ -/* $Id: eicon_dsp.h,v 1.2 1999/03/29 11:19:42 armin Exp $ +/* $Id: eicon_dsp.h,v 1.4 1999/07/25 15:12:02 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * DSP definitions @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_dsp.h,v $ + * Revision 1.4 1999/07/25 15:12:02 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.3 1999/07/11 17:16:24 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * * Revision 1.2 1999/03/29 11:19:42 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -31,7 +40,7 @@ * */ -#ifndef DSP_H +#ifndef DSP_H #define DSP_H #define DSP_UDATA_REQUEST_RECONFIGURE 0 @@ -264,6 +273,10 @@ parameters: <word> tone duration (ms) <word> gap duration (ms) */ +typedef struct enable_dtmf_s { + __u16 tone; + __u16 gap; +} enable_dtmf_s; #define DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER 18 /* @@ -300,5 +313,107 @@ returns: - none - */ +/* ============= FAX ================ */ + +#define EICON_FAXID_LEN 20 + +typedef struct eicon_t30_s { + __u8 code; + __u8 rate; + __u8 resolution; + __u8 format; + __u8 pages_low; + __u8 pages_high; + __u8 atf; + __u8 control_bits_low; + __u8 control_bits_high; + __u8 feature_bits_low; + __u8 feature_bits_high; + __u8 universal_5; + __u8 universal_6; + __u8 universal_7; + __u8 station_id_len; + __u8 head_line_len; + __u8 station_id[EICON_FAXID_LEN]; +/* __u8 head_line[]; */ +} eicon_t30_s; + + /* EDATA transmit messages */ +#define EDATA_T30_DIS 0x01 +#define EDATA_T30_FTT 0x02 +#define EDATA_T30_MCF 0x03 + + /* EDATA receive messages */ +#define EDATA_T30_DCS 0x81 +#define EDATA_T30_TRAIN_OK 0x82 +#define EDATA_T30_EOP 0x83 +#define EDATA_T30_MPS 0x84 +#define EDATA_T30_EOM 0x85 +#define EDATA_T30_DTC 0x86 + +#define T30_FORMAT_SFF 0 +#define T30_FORMAT_ASCII 1 +#define T30_FORMAT_COUNT 2 + +#define T30_CONTROL_BIT_DISABLE_FINE 0x0001 +#define T30_CONTROL_BIT_ENABLE_ECM 0x0002 +#define T30_CONTROL_BIT_ECM_64_BYTES 0x0004 +#define T30_CONTROL_BIT_ENABLE_2D_CODING 0x0008 +#define T30_CONTROL_BIT_ENABLE_T6_CODING 0x0010 +#define T30_CONTROL_BIT_ENABLE_UNCOMPR 0x0020 +#define T30_CONTROL_BIT_ACCEPT_POLLING 0x0040 +#define T30_CONTROL_BIT_REQUEST_POLLING 0x0080 +#define T30_CONTROL_BIT_MORE_DOCUMENTS 0x0100 + +#define T30_CONTROL_BIT_ALL_FEATURES\ + (T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING |\ + T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR) + +#define T30_FEATURE_BIT_FINE 0x0001 +#define T30_FEATURE_BIT_ECM 0x0002 +#define T30_FEATURE_BIT_ECM_64_BYTES 0x0004 +#define T30_FEATURE_BIT_2D_CODING 0x0008 +#define T30_FEATURE_BIT_T6_CODING 0x0010 +#define T30_FEATURE_BIT_UNCOMPR_ENABLED 0x0020 +#define T30_FEATURE_BIT_POLLING 0x0040 + +#define FAX_OBJECT_DOCU 1 +#define FAX_OBJECT_PAGE 2 +#define FAX_OBJECT_LINE 3 + +#define T4_EOL 0x800 +#define T4_EOL_BITSIZE 12 +#define T4_EOL_DWORD (T4_EOL << (32 - T4_EOL_BITSIZE)) +#define T4_EOL_MASK_DWORD ((__u32) -1 << (32 - T4_EOL_BITSIZE)) + +#define SFF_LEN_FLD_SIZE 3 + +#define _DLE_ 0x10 +#define _ETX_ 0x03 + +typedef struct eicon_sff_dochead { + __u32 id __attribute__ ((packed)); + __u8 version __attribute__ ((packed)); + __u8 reserved1 __attribute__ ((packed)); + __u16 userinfo __attribute__ ((packed)); + __u16 pagecount __attribute__ ((packed)); + __u16 off1pagehead __attribute__ ((packed)); + __u32 offnpagehead __attribute__ ((packed)); + __u32 offdocend __attribute__ ((packed)); +} eicon_sff_dochead; + +typedef struct eicon_sff_pagehead { + __u8 pageheadid __attribute__ ((packed)); + __u8 pageheadlen __attribute__ ((packed)); + __u8 resvert __attribute__ ((packed)); + __u8 reshoriz __attribute__ ((packed)); + __u8 coding __attribute__ ((packed)); + __u8 reserved2 __attribute__ ((packed)); + __u16 linelength __attribute__ ((packed)); + __u16 pagelength __attribute__ ((packed)); + __u32 offprevpage __attribute__ ((packed)); + __u32 offnextpage __attribute__ ((packed)); +} eicon_sff_pagehead; + #endif /* DSP_H */ diff --git a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c index a28f316c1..af9c30483 100644 --- a/drivers/isdn/eicon/eicon_idi.c +++ b/drivers/isdn/eicon/eicon_idi.c @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.c,v 1.9 1999/03/29 11:19:42 armin Exp $ +/* $Id: eicon_idi.c,v 1.15 1999/08/28 21:32:50 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * IDI interface @@ -21,6 +21,33 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.c,v $ + * Revision 1.15 1999/08/28 21:32:50 armin + * Prepared for fax related functions. + * Now compilable without errors/warnings. + * + * Revision 1.14 1999/08/28 20:24:40 armin + * Corrected octet 3/3a in CPN/OAD information element. + * Thanks to John Simpson <xfl23@dial.pipex.com> + * + * Revision 1.13 1999/08/22 20:26:44 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.12 1999/08/18 20:16:59 armin + * Added XLOG function for all cards. + * Bugfix of alloc_skb NULL pointer. + * + * Revision 1.11 1999/07/25 15:12:03 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.10 1999/07/11 17:16:24 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * * Revision 1.9 1999/03/29 11:19:42 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -61,6 +88,7 @@ * */ +#include <linux/config.h> #define __NO_VERSION__ #include "eicon.h" #include "eicon_idi.h" @@ -68,7 +96,7 @@ #undef EICON_FULL_SERVICE_OKTETT -char *eicon_idi_revision = "$Revision: 1.9 $"; +char *eicon_idi_revision = "$Revision: 1.15 $"; eicon_manifbuf *manbuf; @@ -86,11 +114,15 @@ static char HLC_faxg3[2] = { 0x91, 0x84 }; int eicon_idi_manage_assign(eicon_card *card); int eicon_idi_manage_remove(eicon_card *card); +int idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer); int idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) { int l = 0; + int tmp; + + tmp = 0; if (!signet) { /* Signal Layer */ reqbuf->XBuffer.P[l++] = CAI; @@ -133,10 +165,25 @@ idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) case ISDN_PROTO_L2_MODEM: reqbuf->XBuffer.P[l++] = 2; break; + case ISDN_PROTO_L2_FAX: + if (chan->fsm_state == EICON_STATE_IWAIT) + reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */ + else + reqbuf->XBuffer.P[l++] = 2; + break; default: reqbuf->XBuffer.P[l++] = 1; } switch(chan->l3prot) { + case ISDN_PROTO_L3_FAX: +#ifdef CONFIG_ISDN_TTY_FAX + reqbuf->XBuffer.P[l++] = 6; + reqbuf->XBuffer.P[l++] = NLC; + tmp = idi_fill_in_T30(chan, &reqbuf->XBuffer.P[l+1]); + reqbuf->XBuffer.P[l++] = tmp; + l += tmp; + break; +#endif case ISDN_PROTO_L3_TRANS: default: reqbuf->XBuffer.P[l++] = 4; @@ -210,6 +257,20 @@ idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan) reqbuf->XBuffer.P[6] = 128; reqbuf->XBuffer.P[7] = 0; break; + case ISDN_PROTO_L2_FAX: + reqbuf->XBuffer.P[2] = 0x10; + reqbuf->XBuffer.P[3] = 0; + reqbuf->XBuffer.P[4] = 0; + reqbuf->XBuffer.P[5] = 0; + reqbuf->XBuffer.P[6] = 128; + reqbuf->XBuffer.P[7] = 0; + break; + case ISDN_PROTO_L2_TRANS: + switch(chan->l3prot) { + case ISDN_PROTO_L3_TRANSDSP: + reqbuf->XBuffer.P[2] = 22; /* DTMF, audio events on */ + } + break; } reqbuf->XBuffer.P[8] = 0; reqbuf->XBuffer.length = l; @@ -232,7 +293,11 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -241,7 +306,7 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); if (DebugVar & 8) - printk(KERN_DEBUG "idi_req: Ch%d: 0x%02x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); + printk(KERN_DEBUG "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); if (layer) cmd |= 0x700; switch(cmd) { case ASSIGN: @@ -282,6 +347,8 @@ idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) default: if (DebugVar & 1) printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No); + dev_kfree_skb(skb); + dev_kfree_skb(skb2); return(-1); } @@ -358,6 +425,9 @@ idi_hangup(eicon_card *card, eicon_chan *chan) chan->fsm_state = EICON_STATE_NULL; if (DebugVar & 8) printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No); +#ifdef CONFIG_ISDN_TTY_FAX + chan->fax = 0; +#endif return(0); } @@ -389,7 +459,11 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -403,14 +477,14 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, reqbuf->XBuffer.P[l++] = CPN; reqbuf->XBuffer.P[l++] = strlen(phone) + 1; - reqbuf->XBuffer.P[l++] = 0xc1; + reqbuf->XBuffer.P[l++] = 0x81; for(i=0; i<strlen(phone);i++) reqbuf->XBuffer.P[l++] = phone[i]; reqbuf->XBuffer.P[l++] = OAD; reqbuf->XBuffer.P[l++] = strlen(eazmsn) + 2; reqbuf->XBuffer.P[l++] = 0x01; - reqbuf->XBuffer.P[l++] = 0x81; + reqbuf->XBuffer.P[l++] = 0x80; for(i=0; i<strlen(eazmsn);i++) reqbuf->XBuffer.P[l++] = eazmsn[i]; @@ -472,6 +546,20 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, reqbuf->XBuffer.P[l-2] = 128; reqbuf->XBuffer.P[l-1] = 0; break; + case ISDN_PROTO_L2_FAX: + reqbuf->XBuffer.P[l-6] = 0x10; + reqbuf->XBuffer.P[l-5] = 0; + reqbuf->XBuffer.P[l-4] = 0; + reqbuf->XBuffer.P[l-3] = 0; + reqbuf->XBuffer.P[l-2] = 128; + reqbuf->XBuffer.P[l-1] = 0; + break; + case ISDN_PROTO_L2_TRANS: + switch(chan->l3prot) { + case ISDN_PROTO_L3_TRANSDSP: + reqbuf->XBuffer.P[l-6] = 22; /* DTMF, audio events on */ + } + break; } reqbuf->XBuffer.P[l++] = 0; /* end */ @@ -813,6 +901,195 @@ idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned ch } } +/********************* FAX stuff ***************************/ + +#ifdef CONFIG_ISDN_TTY_FAX + +int +idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer) +{ + + /* TODO , code follows */ + + return(0); +} + +/* send fax struct */ +int +idi_send_edata(eicon_card *card, eicon_chan *chan) +{ + + /* TODO , code follows */ + + return (0); +} + +void +idi_parse_edata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len) +{ + + /* TODO , code follows */ + +} + +void +idi_fax_send_header(eicon_card *card, eicon_chan *chan, int header) +{ + + /* TODO , code follows */ + +} + +void +idi_fax_cmd(eicon_card *card, eicon_chan *chan) +{ + + /* TODO , code follows */ + +} + +void +idi_edata_rcveop(eicon_card *card, eicon_chan *chan) +{ + + /* TODO , code follows */ + +} + +void +idi_reset_fax_stat(eicon_chan *chan) +{ + + /* TODO , code follows */ + +} + +void +idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len) +{ + + /* TODO , code follows */ + +} + +void +fax_put_rcv(eicon_card *ccard, eicon_chan *chan, u_char *Data, int len) +{ + + /* TODO , code follows */ + +} + +void +idi_faxdata_rcv(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb) +{ + + /* TODO , code follows */ + +} + +int +idi_fax_send_outbuf(eicon_card *ccard, eicon_chan *chan, eicon_OBJBUFFER *OutBuf) +{ + + /* TODO , code follows */ + + return(0); +} + +int +idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb) +{ + + /* TODO , code follows */ + + return(0); +} + +void +idi_fax_hangup(eicon_card *ccard, eicon_chan *chan) +{ + + /* TODO , code follows */ + +} + +#endif /******** FAX ********/ + +int +idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int len) +{ + struct sk_buff *skb; + struct sk_buff *skb2; + eicon_REQ *reqbuf; + eicon_chan_ptr *chan2; + + if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) { + if (DebugVar & 1) + printk(KERN_DEBUG"idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state); + return -ENODEV; + } + if (DebugVar & 8) + printk(KERN_DEBUG"idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No, + UReq, buffer[0], buffer[1], buffer[2], buffer[3]); + + skb = alloc_skb(sizeof(eicon_REQ) + len + 1, GFP_ATOMIC); + skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); + + if ((!skb) || (!skb2)) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); + return -ENOMEM; + } + + chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); + chan2->ptr = chan; + + reqbuf = (eicon_REQ *)skb_put(skb, 1 + len + sizeof(eicon_REQ)); + + reqbuf->Req = IDI_N_UDATA; + reqbuf->ReqCh = 0; + reqbuf->ReqId = 1; + + reqbuf->XBuffer.length = len + 1; + reqbuf->XBuffer.P[0] = UReq; + memcpy(&reqbuf->XBuffer.P[1], buffer, len); + reqbuf->Reference = 1; /* Net Entity */ + + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); + return (0); +} + +void +idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value) +{ + u_char buf[6]; + struct enable_dtmf_s *dtmf_buf = (struct enable_dtmf_s *)buf; + + memset(buf, 0, 6); + switch(cmd) { + case ISDN_AUDIO_SETDD: + if (value[0]) { + dtmf_buf->tone = (__u16) (value[1] * 5); + dtmf_buf->gap = (__u16) (value[1] * 5); + idi_send_udata(ccard, chan, + DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER, + buf, 4); + } else { + idi_send_udata(ccard, chan, + DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER, + buf, 0); + } + break; + } +} + void idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len) { @@ -822,6 +1099,9 @@ idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int {"", "V.21", "V.23", "V.22", "V.22bis", "V.32bis", "V.34", "V.8", "Bell 212A", "Bell 103", "V.29 Leased", "V.33 Leased", "V.90", "V.21 CH2", "V.27ter", "V.29", "V.33", "V.17"}; + static u_char dtmf_code[] = { + '1','4','7','*','2','5','8','0','3','6','9','#','A','B','C','D' + }; switch (buffer[0]) { case DSP_UDATA_INDICATION_SYNC: @@ -863,6 +1143,17 @@ idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int if (DebugVar & 8) printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]); break; + case DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED: + if (DebugVar & 8) + printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No, + dtmf_code[buffer[1]]); + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_AUDIO; + cmd.parm.num[0] = ISDN_AUDIO_DTMF; + cmd.parm.num[1] = dtmf_code[buffer[1]]; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + break; default: if (DebugVar & 8) printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]); @@ -887,7 +1178,7 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) if ((DebugVar & 128) || ((DebugVar & 16) && (ind->Ind != 8))) { - printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, + printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); } @@ -921,6 +1212,10 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.command = ISDN_STAT_DHUP; ccard->interface.statcallb(&cmd); eicon_idi_listen_req(ccard, chan); +#ifdef CONFIG_ISDN_TTY_FAX + if (!chan->e.B2Id) + chan->fax = 0; +#endif break; case INDICATE_IND: if (DebugVar & 8) @@ -994,9 +1289,17 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.command = ISDN_STAT_DCONN; cmd.arg = chan->No; ccard->interface.statcallb(&cmd); - idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + if (chan->l2prot != ISDN_PROTO_L2_FAX) { + idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + } +#ifdef CONFIG_ISDN_TTY_FAX + else { + if (chan->fax) + chan->fax->phase = ISDN_FAX_PHASE_A; + } +#endif } else - idi_hangup(ccard, chan); + idi_hangup(ccard, chan); break; case CALL_CON: if (DebugVar & 8) @@ -1009,6 +1312,12 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) ccard->interface.statcallb(&cmd); idi_do_req(ccard, chan, ASSIGN, 1); idi_do_req(ccard, chan, IDI_N_CONNECT, 1); +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + if (chan->fax) + chan->fax->phase = ISDN_FAX_PHASE_A; + } +#endif } else idi_hangup(ccard, chan); break; @@ -1038,6 +1347,27 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) chan->fsm_state = EICON_STATE_WMCONN; break; } + if (chan->l2prot == ISDN_PROTO_L2_FAX) { +#ifdef CONFIG_ISDN_TTY_FAX + chan->fsm_state = EICON_STATE_ACTIVE; + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + if (chan->fax) { + if (chan->fax->phase == ISDN_FAX_PHASE_B) { + idi_fax_send_header(ccard, chan, 2); + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_DCS; + ccard->interface.statcallb(&cmd); + } + } + else { + if (DebugVar & 1) + printk(KERN_DEBUG "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n"); + } +#endif + break; + } chan->fsm_state = EICON_STATE_ACTIVE; cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; @@ -1048,6 +1378,9 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) if (DebugVar & 16) printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No); if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1); + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + break; + } if (chan->l2prot == ISDN_PROTO_L2_MODEM) { chan->fsm_state = EICON_STATE_WMCONN; break; @@ -1065,19 +1398,35 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1); idi_do_req(ccard, chan, REMOVE, 1); } +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + idi_fax_hangup(ccard, chan); + } +#endif chan->queued = 0; chan->waitq = 0; chan->waitpq = 0; + idi_do_req(ccard, chan, HANGUP, 0); if (chan->fsm_state == EICON_STATE_ACTIVE) { cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BHUP; cmd.arg = chan->No; ccard->interface.statcallb(&cmd); } +#ifdef CONFIG_ISDN_TTY_FAX + chan->fax = 0; +#endif break; case IDI_N_DISC_ACK: if (DebugVar & 16) printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No); +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + idi_fax_hangup(ccard, chan); + } +#endif break; case IDI_N_DATA_ACK: if (DebugVar & 16) @@ -1087,12 +1436,23 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) skb_pull(skb, sizeof(eicon_IND) - 1); if (DebugVar & 128) printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len); - ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb); - free_buff = 0; + if (chan->l2prot == ISDN_PROTO_L2_FAX) { +#ifdef CONFIG_ISDN_TTY_FAX + idi_faxdata_rcv(ccard, chan, skb); +#endif + } else { + ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb); + free_buff = 0; + } break; case IDI_N_UDATA: idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); break; +#ifdef CONFIG_ISDN_TTY_FAX + case IDI_N_EDATA: + idi_edata_action(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + break; +#endif default: if (DebugVar & 8) printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind); @@ -1105,90 +1465,138 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) if (free_buff) dev_kfree_skb(skb); } -void -idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) +int +idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack) { - int j; - eicon_RC *ack = (eicon_RC *)skb->data; - eicon_chan *chan; isdn_ctrl cmd; - if ((ack->Rc != ASSIGN_OK) && (ack->Rc != OK)) { - if ((chan = ccard->IdTable[ack->RcId]) != NULL) { - chan->e.busy = 0; - if (DebugVar & 24) - printk(KERN_ERR "eicon_ack: Ch%d: Not OK: Rc=%d Id=%d Ch=%d\n", chan->No, - ack->Rc, ack->RcId, ack->RcCh); - if (chan->No == ccard->nchannels) { /* Management */ - chan->fsm_state = 2; - } else { /* any other channel */ - /* card reports error: we hangup */ - idi_hangup(ccard, chan); - cmd.driver = ccard->myid; - cmd.command = ISDN_STAT_DHUP; - cmd.arg = chan->No; - ccard->interface.statcallb(&cmd); - } + if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { + /* I dont know why this happens, just ignoring this RC */ + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, + ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id); + return 1; + } + + /* Management Interface */ + if (chan->No == ccard->nchannels) { + /* Managementinterface: changing state */ + if (chan->e.Req == 0x04) + chan->fsm_state = 1; + } + + /* Remove an Id */ + if (chan->e.Req == REMOVE) { + if (ack->Reference != chan->e.ref) { + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, + ack->Reference, chan->e.ref); + return 0; } - } - else { - if ((chan = ccard->IdTable[ack->RcId]) != NULL) { - if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, - ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id); - } else { - if (chan->No == ccard->nchannels) { /* Management */ - if (chan->e.Req == 0x04) chan->fsm_state = 1; - } - if (chan->e.ReqCh) { - switch(chan->e.Req & 0x0f) { - case IDI_N_MDATA: - case IDI_N_DATA: - chan->queued -= chan->waitq; - if (chan->queued < 0) chan->queued = 0; - if ((chan->e.Req & 0x0f) == IDI_N_DATA) { + ccard->IdTable[ack->RcId] = NULL; + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%x Ch=%d (%s)\n", chan->No, + ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); + if (!chan->e.ReqCh) + chan->e.D3Id = 0; + else + chan->e.B2Id = 0; + return 1; + } + + /* Signal layer */ + if (!chan->e.ReqCh) { + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); + } else { + /* Network layer */ + switch(chan->e.Req & 0x0f) { + case IDI_N_MDATA: + case IDI_N_DATA: + if ((chan->e.Req & 0x0f) == IDI_N_DATA) { + if (chan->queued) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_BSENT; + cmd.arg = chan->No; + cmd.parm.length = chan->waitpq; + ccard->interface.statcallb(&cmd); + } + chan->waitpq = 0; +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + if (((chan->queued - chan->waitq) < 1) && + (chan->fax2.Eop)) { + chan->fax2.Eop = 0; + if (chan->fax) { cmd.driver = ccard->myid; - cmd.command = ISDN_STAT_BSENT; + cmd.command = ISDN_STAT_FAXIND; cmd.arg = chan->No; - cmd.parm.length = chan->waitpq; - chan->waitpq = 0; + chan->fax->r_code = ISDN_TTY_FAX_SENT; ccard->interface.statcallb(&cmd); } - break; - default: - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No, - ack->RcId, ack->RcCh, ack->Reference); + else { + if (DebugVar & 1) + printk(KERN_DEBUG "idi_ack: Sent with NULL fax struct, ERROR\n"); + } + } } - } - else { - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No, - ack->RcId, ack->RcCh, ack->Reference); +#endif } + chan->queued -= chan->waitq; + if (chan->queued < 0) chan->queued = 0; + break; + default: + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No, + ack->RcId, ack->RcCh, ack->Reference); + } + } + return 1; +} - if (chan->e.Req == REMOVE) { - if (ack->Reference == chan->e.ref) { - ccard->IdTable[ack->RcId] = NULL; - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No, - ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); - if (!chan->e.ReqCh) - chan->e.D3Id = 0; - else - chan->e.B2Id = 0; - } - else { - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, - ack->Reference, chan->e.ref); - } - } - chan->e.busy = 0; +void +idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) +{ + int j; + eicon_RC *ack = (eicon_RC *)skb->data; + eicon_chan *chan; + isdn_ctrl cmd; + int dCh = -1; + + if ((chan = ccard->IdTable[ack->RcId]) != NULL) + dCh = chan->No; + + + switch (ack->Rc) { + case OK_FC: + case N_FLOW_CONTROL: + case ASSIGN_RC: + if (DebugVar & 1) + printk(KERN_ERR "idi_ack: Ch%d: unhandled RC 0x%x\n", + dCh, ack->Rc); + break; + case READY_INT: + case TIMER_INT: + /* we do nothing here */ + break; + + case OK: + if (!chan) { + if (DebugVar & 1) + printk(KERN_ERR "idi_ack: Ch%d: OK on chan without Id\n", dCh); + break; + } + if (!idi_handle_ack_ok(ccard, chan, ack)) + chan = NULL; + break; + + case ASSIGN_OK: + if (chan) { + if (DebugVar & 1) + printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n", + chan->No, chan->e.D3Id, chan->e.B2Id); } - } - else { for(j = 0; j < ccard->nchannels + 1; j++) { if (ccard->bch[j].e.ref == ack->Reference) { if (!ccard->bch[j].e.ReqCh) @@ -1199,7 +1607,7 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) ccard->bch[j].e.busy = 0; ccard->bch[j].e.ref = 0; if (DebugVar & 16) - printk(KERN_DEBUG"idi_ack: Ch%d: Id %d assigned (%s)\n", j, + printk(KERN_DEBUG"idi_ack: Ch%d: Id %x assigned (%s)\n", j, ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig"); break; } @@ -1209,14 +1617,39 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) printk(KERN_DEBUG"idi_ack: Ch??: ref %d not found for Id %d\n", ack->Reference, ack->RcId); } - } + break; + + case OUT_OF_RESOURCES: + case UNKNOWN_COMMAND: + case WRONG_COMMAND: + case WRONG_ID: + case WRONG_CH: + case UNKNOWN_IE: + case WRONG_IE: + default: + if (DebugVar & 1) + printk(KERN_ERR "eicon_ack: Ch%d: Not OK !!: Rc=%d Id=%x Ch=%d\n", dCh, + ack->Rc, ack->RcId, ack->RcCh); + if (dCh == ccard->nchannels) { /* Management */ + chan->fsm_state = 2; + } else if (dCh >= 0) { + /* any other channel */ + /* card reports error: we hangup */ + idi_hangup(ccard, chan); + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan->No; + ccard->interface.statcallb(&cmd); + } } - dev_kfree_skb(skb); - eicon_schedule_tx(ccard); + if (chan) + chan->e.busy = 0; + dev_kfree_skb(skb); + eicon_schedule_tx(ccard); } int -idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) +idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que) { struct sk_buff *xmit_skb; struct sk_buff *skb2; @@ -1240,6 +1673,7 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) return 0; if (DebugVar & 128) printk(KERN_DEBUG"idi_snd: Ch%d: %d bytes\n", chan->No, len); + save_flags(flags); cli(); while(offset < len) { @@ -1249,10 +1683,14 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC); skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); - if ((!skb) || (!skb2)) { + if ((!xmit_skb) || (!skb2)) { restore_flags(flags); if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No); + if (xmit_skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1278,7 +1716,8 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) offset += plen; } - chan->queued += len; + if (que) + chan->queued += len; restore_flags(flags); eicon_schedule_tx(card); dev_kfree_skb(skb); @@ -1286,7 +1725,6 @@ idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb) } - int eicon_idi_manage_assign(eicon_card *card) { @@ -1303,7 +1741,11 @@ eicon_idi_manage_assign(eicon_card *card) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err: alloc_skb failed in manage_assign()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1342,7 +1784,11 @@ eicon_idi_manage_remove(eicon_card *card) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err: alloc_skb failed in manage_remove()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1379,7 +1825,8 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) chan = &(card->bch[card->nchannels]); - if (chan->e.D3Id) return -EBUSY; + if (chan->e.D3Id) + return -EBUSY; chan->e.D3Id = 1; while((skb2 = skb_dequeue(&chan->e.X))) dev_kfree_skb(skb2); @@ -1409,6 +1856,7 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) return -ENOMEM; } if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) { + kfree(manbuf); chan->e.D3Id = 0; return -EFAULT; } @@ -1418,7 +1866,11 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err_manif: alloc_skb failed in manage()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); kfree(manbuf); chan->e.D3Id = 0; return -ENOMEM; @@ -1464,11 +1916,13 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) } if ((ret = eicon_idi_manage_remove(card))) { + kfree(manbuf); chan->e.D3Id = 0; return(ret); } if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) { + kfree(manbuf); chan->e.D3Id = 0; return -EFAULT; } diff --git a/drivers/isdn/eicon/eicon_idi.h b/drivers/isdn/eicon/eicon_idi.h index a0605cdef..e09c1954d 100644 --- a/drivers/isdn/eicon/eicon_idi.h +++ b/drivers/isdn/eicon/eicon_idi.h @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.h,v 1.4 1999/03/29 11:19:44 armin Exp $ +/* $Id: eicon_idi.h,v 1.7 1999/08/22 20:26:46 calle Exp $ * * ISDN lowlevel-module for the Eicon.Diehl active cards. * IDI-Interface @@ -21,6 +21,21 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.h,v $ + * Revision 1.7 1999/08/22 20:26:46 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.6 1999/07/25 15:12:04 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.5 1999/07/11 17:16:26 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * * Revision 1.4 1999/03/29 11:19:44 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -45,6 +60,7 @@ #ifndef IDI_H #define IDI_H +#include <linux/config.h> #define ASSIGN 0x01 #define REMOVE 0xff @@ -232,6 +248,12 @@ typedef struct { __u8 B[1]; /* buffer space for Req,Ind and Rc */ } eicon_pr_ram; +typedef struct { + __u8 *Data; + unsigned int Size; + unsigned int Len; + __u8 *Next; +} eicon_OBJBUFFER; extern int idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer); extern int idi_hangup(eicon_card *card, eicon_chan *chan); @@ -243,6 +265,11 @@ extern int idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, extern void idi_handle_ack(eicon_card *card, struct sk_buff *skb); extern void idi_handle_ind(eicon_card *card, struct sk_buff *skb); extern int eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb); -extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb); +extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que); +extern void idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value); +#ifdef CONFIG_ISDN_TTY_FAX +extern void idi_fax_cmd(eicon_card *card, eicon_chan *chan); +extern int idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb); +#endif #endif diff --git a/drivers/isdn/eicon/eicon_io.c b/drivers/isdn/eicon/eicon_io.c index 1c69d37cd..60b5b4818 100644 --- a/drivers/isdn/eicon/eicon_io.c +++ b/drivers/isdn/eicon/eicon_io.c @@ -1,4 +1,4 @@ -/* $Id: eicon_io.c,v 1.1 1999/03/29 11:19:45 armin Exp $ +/* $Id: eicon_io.c,v 1.4 1999/08/22 20:26:47 calle Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Code for communicating with hardware. @@ -24,6 +24,20 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_io.c,v $ + * Revision 1.4 1999/08/22 20:26:47 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.3 1999/08/18 20:17:01 armin + * Added XLOG function for all cards. + * Bugfix of alloc_skb NULL pointer. + * + * Revision 1.2 1999/07/25 15:12:05 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.1 1999/03/29 11:19:45 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -32,6 +46,7 @@ */ +#include <linux/config.h> #include "eicon.h" void @@ -56,8 +71,8 @@ eicon_io_rcv_dispatch(eicon_card *ccard) { /* doesn't matter if this happens */ break; default: - printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%d\n", ind->Ind, ind->IndId); - printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", + printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%x\n", ind->Ind, ind->IndId); + printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); } } @@ -84,12 +99,18 @@ eicon_io_rcv_dispatch(eicon_card *ccard) { if (DebugVar & 1) printk(KERN_ERR "eicon: buffer incomplete, but 0 in queue\n"); dev_kfree_skb(skb); - dev_kfree_skb(skb2); continue; } ind2 = (eicon_IND *)skb2->data; skb_new = alloc_skb(((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length), GFP_ATOMIC); + if (!skb_new) { + if (DebugVar & 1) + printk(KERN_ERR "eicon_io: skb_alloc failed in rcv_dispatch()\n"); + dev_kfree_skb(skb); + dev_kfree_skb(skb2); + continue; + } ind_new = (eicon_IND *)skb_put(skb_new, ((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length)); ind_new->Ind = ind2->Ind; @@ -272,6 +293,92 @@ void ram_copytocard(eicon_card *card, void *adrto, void *adr, int len) { } /* + * XLOG + */ +int +eicon_get_xlog(eicon_card *card, xlogreq_t *xlogreq) +{ + int timeout, i; + int divas_shared_offset = 0; + int len = 0; + int stype = 0; + __u32 time = 0; + mi_pc_maint_t *pcm = &xlogreq->pcm; + eicon_pci_card *pci_card = &card->hwif.pci; + eicon_isa_card *isa_card = &card->hwif.isa; + eicon_pr_ram *prram = 0; + char *ram; + + switch(card->type) { + case EICON_CTYPE_MAESTRAP: + ram = (char *)pci_card->PCIram; + prram = (eicon_pr_ram *)ram; + divas_shared_offset = DIVAS_SHARED_OFFSET; + len = sizeof(mi_pc_maint_t); + break; + case EICON_CTYPE_MAESTRA: + prram = 0; + divas_shared_offset = 0; + len = sizeof(mi_pc_maint_t); + break; + case EICON_CTYPE_S: + case EICON_CTYPE_SX: + case EICON_CTYPE_SCOM: + case EICON_CTYPE_QUADRO: + case EICON_CTYPE_S2M: + prram = (eicon_pr_ram *)isa_card->shmem; + divas_shared_offset = 0xfb80; + len = sizeof(mi_pc_maint_t) - 78; + stype = 1; + break; + default: + return -ENODEV; + } + + memset(&(xlogreq->pcm), 0, sizeof(mi_pc_maint_t)); + + xlogreq->pcm.rc = 0; + xlogreq->pcm.req = 1; /* DO_LOG */ + + ram = ((char *)prram) + MIPS_MAINT_OFFS - divas_shared_offset; + + ram_outb(card, ram+1, pcm->rc); + ram_outb(card, ram+0, pcm->req); + + timeout = jiffies + 50; + while (timeout > jiffies) { + pcm->rc = ram_inb(card, ram+1); + pcm->req = ram_inb(card, ram+0); + if (!pcm->req) break; + SLEEP(10); + } + + if (pcm->req) { + return XLOG_ERR_TIMEOUT; + } + + if (pcm->rc != OK) { + return XLOG_ERR_DONE; + } + + ram_copyfromcard(card, pcm, ram, len); + + if (stype) { + for (i=0; i<8; i++) + ((__u8 *)pcm)[11-i] = ((__u8 *)pcm)[9-i]; + time = (__u32)pcm->data.w[2] * 3600 * 1000 + + (__u32)pcm->data.w[1] * 1000 + + (__u32)pcm->data.b[1] * 20 + + (__u32)pcm->data.b[0] ; + pcm->data.w[1] = (__u16) (time >> 16); + pcm->data.w[2] = (__u16) (time & 0x0000ffff); + pcm->data.w[0] = 2; + } + + return XLOG_OK; +} + +/* * Transmit-Function */ void @@ -303,6 +410,7 @@ eicon_io_transmit(eicon_card *ccard) { } switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -314,6 +422,7 @@ eicon_io_transmit(eicon_card *ccard) { scom = 0; prram = (eicon_pr_ram *)isa_card->shmem; break; +#endif case EICON_CTYPE_MAESTRAP: scom = 0; ram = (char *)pci_card->PCIram; @@ -366,9 +475,13 @@ eicon_io_transmit(eicon_card *ccard) { chan = chan2->ptr; if (!chan->e.busy) { if((skb = skb_dequeue(&chan->e.X))) { - save_flags(flags); - cli(); - reqbuf = (eicon_REQ *)skb->data; + save_flags(flags); + cli(); + reqbuf = (eicon_REQ *)skb->data; + if ((reqbuf->Reference) && (chan->e.B2Id == 0) && (reqbuf->ReqId & 0x1f)) { + if (DebugVar & 16) + printk(KERN_WARNING "eicon: transmit: error Id=0 on %d (Net)\n", chan->No); + } else { if (scom) { ram_outw(ccard, &com->XBuffer.length, reqbuf->XBuffer.length); ram_copytocard(ccard, &com->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length); @@ -383,7 +496,7 @@ eicon_io_transmit(eicon_card *ccard) { ram_outb(ccard, &ReqOut->Req, reqbuf->Req); } - if (reqbuf->ReqId &0x1f) { /* if this is no ASSIGN */ + if (reqbuf->ReqId & 0x1f) { /* if this is no ASSIGN */ if (!reqbuf->Reference) { /* Signal Layer */ if (scom) @@ -432,14 +545,15 @@ eicon_io_transmit(eicon_card *ccard) { ram_outw(ccard, &prram->NextReq, ram_inw(ccard, &ReqOut->next)); chan->e.busy = 1; - restore_flags(flags); if (DebugVar & 32) - printk(KERN_DEBUG "eicon: Req=%x Id=%x Ch=%x Len=%x Ref=%d\n", + printk(KERN_DEBUG "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", reqbuf->Req, ram_inb(ccard, &ReqOut->ReqId), reqbuf->ReqCh, reqbuf->XBuffer.length, chan->e.ref); - dev_kfree_skb(skb); + } + restore_flags(flags); + dev_kfree_skb(skb); } dev_kfree_skb(skb2); } @@ -510,6 +624,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { isa_card = &ccard->hwif.isa; switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -523,6 +638,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { prram = (eicon_pr_ram *)isa_card->shmem; irqprobe = &isa_card->irqprobe; break; +#endif case EICON_CTYPE_MAESTRAP: scom = 0; ram = (char *)pci_card->PCIram; @@ -546,6 +662,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { if (*irqprobe) { switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -563,6 +680,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { } (*irqprobe)++; break; +#endif case EICON_CTYPE_MAESTRAP: if (readb(&ram[0x3fe])) { writeb(0, &prram->RcOutput); @@ -581,6 +699,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { } switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -592,6 +711,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { return; } break; +#endif case EICON_CTYPE_MAESTRAP: if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */ if (DebugVar & 1) @@ -623,16 +743,21 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { } } else { skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC); - ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC)); - ack->Rc = tmp; - ack->RcId = ram_inb(ccard, &com->RcId); - ack->RcCh = ram_inb(ccard, &com->RcCh); - ack->Reference = ccard->ref_in++; - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n", - tmp,ack->RcId,ack->RcCh,ack->Reference); - skb_queue_tail(&ccard->rackq, skb); - eicon_schedule_ack(ccard); + if (!skb) { + if (DebugVar & 1) + printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n"); + } else { + ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC)); + ack->Rc = tmp; + ack->RcId = ram_inb(ccard, &com->RcId); + ack->RcCh = ram_inb(ccard, &com->RcCh); + ack->Reference = ccard->ref_in++; + if (DebugVar & 64) + printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", + tmp,ack->RcId,ack->RcCh,ack->Reference); + skb_queue_tail(&ccard->rackq, skb); + eicon_schedule_ack(ccard); + } ram_outb(ccard, &com->Req, 0); ram_outb(ccard, &com->Rc, 0); } @@ -644,19 +769,24 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { eicon_IND *ind; int len = ram_inw(ccard, &com->RBuffer.length); skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC); - ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1)); - ind->Ind = tmp; - ind->IndId = ram_inb(ccard, &com->IndId); - ind->IndCh = ram_inb(ccard, &com->IndCh); - ind->MInd = ram_inb(ccard, &com->MInd); - ind->MLength = ram_inw(ccard, &com->MLength); - ind->RBuffer.length = len; - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", - tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); - ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len); - skb_queue_tail(&ccard->rcvq, skb); - eicon_schedule_rx(ccard); + if (!skb) { + if (DebugVar & 1) + printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n"); + } else { + ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1)); + ind->Ind = tmp; + ind->IndId = ram_inb(ccard, &com->IndId); + ind->IndCh = ram_inb(ccard, &com->IndCh); + ind->MInd = ram_inb(ccard, &com->MInd); + ind->MLength = ram_inw(ccard, &com->MLength); + ind->RBuffer.length = len; + if (DebugVar & 64) + printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", + tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); + ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len); + skb_queue_tail(&ccard->rcvq, skb); + eicon_schedule_rx(ccard); + } ram_outb(ccard, &com->Ind, 0); } } @@ -673,17 +803,22 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { if((Rc=ram_inb(ccard, &RcIn->Rc))) { skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC); - ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC)); - ack->Rc = Rc; - ack->RcId = ram_inb(ccard, &RcIn->RcId); - ack->RcCh = ram_inb(ccard, &RcIn->RcCh); - ack->Reference = ram_inw(ccard, &RcIn->Reference); - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n", - Rc,ack->RcId,ack->RcCh,ack->Reference); - ram_outb(ccard, &RcIn->Rc, 0); - skb_queue_tail(&ccard->rackq, skb); - eicon_schedule_ack(ccard); + if (!skb) { + if (DebugVar & 1) + printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n"); + } else { + ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC)); + ack->Rc = Rc; + ack->RcId = ram_inb(ccard, &RcIn->RcId); + ack->RcCh = ram_inb(ccard, &RcIn->RcCh); + ack->Reference = ram_inw(ccard, &RcIn->Reference); + if (DebugVar & 64) + printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", + Rc,ack->RcId,ack->RcCh,ack->Reference); + skb_queue_tail(&ccard->rackq, skb); + eicon_schedule_ack(ccard); + } + ram_outb(ccard, &RcIn->Rc, 0); } /* get buffer address of next return code */ RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &RcIn->next)]; @@ -703,19 +838,24 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { if(Ind) { int len = ram_inw(ccard, &IndIn->RBuffer.length); skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC); - ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1)); - ind->Ind = Ind; - ind->IndId = ram_inb(ccard, &IndIn->IndId); - ind->IndCh = ram_inb(ccard, &IndIn->IndCh); - ind->MInd = ram_inb(ccard, &IndIn->MInd); - ind->MLength = ram_inw(ccard, &IndIn->MLength); - ind->RBuffer.length = len; - if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", - Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); - ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len); - skb_queue_tail(&ccard->rcvq, skb); - eicon_schedule_rx(ccard); + if (!skb) { + if (DebugVar & 1) + printk(KERN_ERR "eicon_io: skb_alloc failed in _irq()\n"); + } else { + ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1)); + ind->Ind = Ind; + ind->IndId = ram_inb(ccard, &IndIn->IndId); + ind->IndCh = ram_inb(ccard, &IndIn->IndCh); + ind->MInd = ram_inb(ccard, &IndIn->MInd); + ind->MLength = ram_inw(ccard, &IndIn->MLength); + ind->RBuffer.length = len; + if (DebugVar & 64) + printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", + Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); + ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len); + skb_queue_tail(&ccard->rcvq, skb); + eicon_schedule_rx(ccard); + } ram_outb(ccard, &IndIn->Ind, 0); } /* get buffer address of next indication */ @@ -728,6 +868,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { /* clear interrupt */ switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_QUADRO: writeb(0, isa_card->intack); writeb(0, &com[0x401]); @@ -738,6 +879,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { case EICON_CTYPE_S2M: writeb(0, isa_card->intack); break; +#endif case EICON_CTYPE_MAESTRAP: writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]); writew(0, &cfg[MP_IRQ_RESET + 2]); diff --git a/drivers/isdn/eicon/eicon_isa.c b/drivers/isdn/eicon/eicon_isa.c index 184f1c394..75122559c 100644 --- a/drivers/isdn/eicon/eicon_isa.c +++ b/drivers/isdn/eicon/eicon_isa.c @@ -1,4 +1,4 @@ -/* $Id: eicon_isa.c,v 1.5 1999/04/01 12:48:33 armin Exp $ +/* $Id: eicon_isa.c,v 1.7 1999/08/22 20:26:48 calle Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Hardware-specific code for old ISA cards. @@ -22,6 +22,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_isa.c,v $ + * Revision 1.7 1999/08/22 20:26:48 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.6 1999/07/25 15:12:06 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.5 1999/04/01 12:48:33 armin * Changed some log outputs. * @@ -46,6 +56,7 @@ * */ +#include <linux/config.h> #include "eicon.h" #include "eicon_isa.h" @@ -53,7 +64,9 @@ #define release_shmem release_region #define request_shmem request_region -char *eicon_isa_revision = "$Revision: 1.5 $"; +char *eicon_isa_revision = "$Revision: 1.7 $"; + +#ifdef CONFIG_ISDN_DRV_EICON_ISA /* Mask for detecting invalid IRQ parameter */ static int eicon_isa_valid_irq[] = { @@ -430,3 +443,5 @@ eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb) { card->irqprobe = 0; return 0; } + +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ diff --git a/drivers/isdn/eicon/eicon_mod.c b/drivers/isdn/eicon/eicon_mod.c index c14d91d7e..1ac22aa80 100644 --- a/drivers/isdn/eicon/eicon_mod.c +++ b/drivers/isdn/eicon/eicon_mod.c @@ -1,4 +1,4 @@ -/* $Id: eicon_mod.c,v 1.5 1999/04/01 12:48:35 armin Exp $ +/* $Id: eicon_mod.c,v 1.11 1999/08/29 17:23:45 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * @@ -26,6 +26,31 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_mod.c,v $ + * Revision 1.11 1999/08/29 17:23:45 armin + * New setup compat. + * Bugfix if compile as not module. + * + * Revision 1.10 1999/08/28 21:32:53 armin + * Prepared for fax related functions. + * Now compilable without errors/warnings. + * + * Revision 1.9 1999/08/18 20:17:02 armin + * Added XLOG function for all cards. + * Bugfix of alloc_skb NULL pointer. + * + * Revision 1.8 1999/07/25 15:12:08 armin + * fix of some debug logs. + * enabled ISA-cards option. + * + * Revision 1.7 1999/07/11 17:16:27 armin + * Bugfixes in queue handling. + * Added DSP-DTMF decoder functions. + * Reorganized ack_handler. + * + * Revision 1.6 1999/06/09 19:31:26 armin + * Wrong PLX size for request_region() corrected. + * Added first MCA code from Erik Weber. + * * Revision 1.5 1999/04/01 12:48:35 armin * Changed some log outputs. * @@ -50,17 +75,23 @@ * */ +#define DRIVERPATCH "" + #include <linux/config.h> #include <linux/module.h> #include <linux/init.h> +#ifdef CONFIG_MCA +#include <linux/mca.h> +#endif #include "eicon.h" #define INCLUDE_INLINE_FUNCS -static eicon_card *cards = (eicon_card *) NULL; +static eicon_card *cards = (eicon_card *) NULL; /* glob. var , contains + start of card-list */ -static char *eicon_revision = "$Revision: 1.5 $"; +static char *eicon_revision = "$Revision: 1.11 $"; extern char *eicon_pci_revision; extern char *eicon_isa_revision; @@ -70,24 +101,28 @@ extern char *eicon_idi_revision; #define MOD_USE_COUNT (GET_USE_COUNT (&__this_module)) #endif -#define EICON_CTRL_VERSION 1 +#define EICON_CTRL_VERSION 2 ulong DebugVar; /* Parameters to be set by insmod */ +#ifdef CONFIG_ISDN_DRV_EICON_ISA static int membase = -1; static int irq = -1; +#endif static char *id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; MODULE_DESCRIPTION( "Driver for Eicon.Diehl active ISDN cards"); MODULE_AUTHOR( "Armin Schindler"); MODULE_SUPPORTED_DEVICE( "ISDN subsystem"); +MODULE_PARM_DESC(id, "ID-String of first card"); +MODULE_PARM(id, "s"); +#ifdef CONFIG_ISDN_DRV_EICON_ISA MODULE_PARM_DESC(membase, "Base address of first ISA card"); MODULE_PARM_DESC(irq, "IRQ of first card"); -MODULE_PARM_DESC(id, "ID-String of first card"); MODULE_PARM(membase, "i"); MODULE_PARM(irq, "i"); -MODULE_PARM(id, "s"); +#endif char *eicon_ctype_name[] = { "ISDN-S", @@ -291,10 +326,10 @@ eicon_rcv_dispatch(struct eicon_card *card) { switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: case EICON_BUS_PCI: eicon_io_rcv_dispatch(card); break; - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -307,10 +342,10 @@ eicon_ack_dispatch(struct eicon_card *card) { switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: case EICON_BUS_PCI: eicon_io_ack_dispatch(card); break; - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -323,10 +358,10 @@ eicon_transmit(struct eicon_card *card) { switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: case EICON_BUS_PCI: eicon_io_transmit(card); break; - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -334,6 +369,32 @@ eicon_transmit(struct eicon_card *card) } } +static int eicon_xlog(eicon_card *card, xlogreq_t *xlogreq) +{ + xlogreq_t *xlr; + int ret_val; + + if (!(xlr = kmalloc(sizeof(xlogreq_t), GFP_KERNEL))) { + if (DebugVar & 1) + printk(KERN_WARNING "idi_err: alloc_xlogreq_t failed\n"); + return -ENOMEM; + } + if (copy_from_user(xlr, xlogreq, sizeof(xlogreq_t))) { + kfree(xlr); + return -EFAULT; + } + + ret_val = eicon_get_xlog(card, xlr); + + if (copy_to_user(xlogreq, xlr, sizeof(xlogreq_t))) { + kfree(xlr); + return -EFAULT; + } + kfree(xlr); + + return ret_val; +} + static int eicon_command(eicon_card * card, isdn_ctrl * c) { @@ -345,6 +406,10 @@ eicon_command(eicon_card * card, isdn_ctrl * c) int ret = 0; unsigned long flags; + if (DebugVar & 16) + printk(KERN_WARNING "eicon_cmd 0x%x with arg 0x%lx (0x%lx)\n", + c->command, c->arg, (ulong) *c->parm.num); + switch (c->command) { case ISDN_CMD_IOCTL: memcpy(&a, c->parm.num, sizeof(ulong)); @@ -356,6 +421,7 @@ eicon_command(eicon_card * card, isdn_ctrl * c) case EICON_IOCTL_GETMMIO: switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: return (int)card->hwif.isa.shmem; #if CONFIG_PCI case EICON_BUS_PCI: @@ -368,11 +434,13 @@ eicon_command(eicon_card * card, isdn_ctrl * c) card->bus); ret = -ENODEV; } +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_SETMMIO: if (card->flags & EICON_FLAGS_LOADED) return -EBUSY; switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: if (eicon_isa_find_card(a, card->hwif.isa.irq, card->regname) < 0) @@ -386,9 +454,11 @@ eicon_command(eicon_card * card, isdn_ctrl * c) card->bus); ret = -ENODEV; } +#endif case EICON_IOCTL_GETIRQ: switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: return card->hwif.isa.irq; #if CONFIG_PCI case EICON_BUS_PCI: @@ -408,6 +478,7 @@ eicon_command(eicon_card * card, isdn_ctrl * c) return -EFAULT; switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: card->hwif.isa.irq = a; return 0; default: @@ -417,11 +488,13 @@ eicon_command(eicon_card * card, isdn_ctrl * c) card->bus); ret = -ENODEV; } +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_LOADBOOT: if (card->flags & EICON_FLAGS_RUNNING) return -EBUSY; switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: ret = eicon_isa_bootload( &(card->hwif.isa), &(((eicon_codebuf *)a)->isa)); @@ -434,11 +507,14 @@ eicon_command(eicon_card * card, isdn_ctrl * c) ret = -ENODEV; } return ret; +#endif +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_LOADISA: if (card->flags & EICON_FLAGS_RUNNING) return -EBUSY; switch (card->bus) { case EICON_BUS_ISA: + case EICON_BUS_MCA: ret = eicon_isa_load( &(card->hwif.isa), &(((eicon_codebuf *)a)->isa)); @@ -465,7 +541,7 @@ eicon_command(eicon_card * card, isdn_ctrl * c) ret = -ENODEV; } return ret; - +#endif case EICON_IOCTL_MANIF: if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; @@ -475,6 +551,12 @@ eicon_command(eicon_card * card, isdn_ctrl * c) card, (eicon_manifbuf *)a); return ret; + + case EICON_IOCTL_GETXLOG: + if (!card->flags & EICON_FLAGS_RUNNING) + return XLOG_ERR_CARD_STATE; + ret = eicon_xlog(card, (xlogreq_t *)a); + return ret; #if CONFIG_PCI case EICON_IOCTL_LOADPCI: if (card->flags & EICON_FLAGS_RUNNING) @@ -639,14 +721,13 @@ eicon_command(eicon_card * card, isdn_ctrl * c) case ISDN_CMD_SETL3: if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; - if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) { - if (DebugVar & 1) - printk(KERN_WARNING "L3 protocol unknown\n"); - return -1; - } if (!(chan = find_channel(card, c->arg & 0x1f))) break; chan->l3prot = (c->arg >> 8); +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l3prot == ISDN_PROTO_L3_FAX) + chan->fax = c->parm.fax; +#endif return 0; case ISDN_CMD_GETL3: if (!card->flags & EICON_FLAGS_RUNNING) @@ -678,6 +759,24 @@ eicon_command(eicon_card * card, isdn_ctrl * c) case ISDN_CMD_UNLOCK: MOD_DEC_USE_COUNT; return 0; +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_CMD_FAXCMD: + if (!card->flags & EICON_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x1f))) + break; + if (!chan->fax) + break; + idi_fax_cmd(card, chan); + return 0; +#endif + case ISDN_CMD_AUDIO: + if (!card->flags & EICON_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x1f))) + break; + idi_audio_cmd(card, chan, c->arg >> 8, c->parm.num); + return 0; } return -EINVAL; @@ -718,16 +817,19 @@ if_command(isdn_ctrl * c) static int if_writecmd(const u_char * buf, int len, int user, int id, int channel) { +#if 0 + /* Not yet used */ eicon_card *card = eicon_findcard(id); if (card) { if (!card->flags & EICON_FLAGS_RUNNING) - return -ENODEV; + return (len); return (len); } printk(KERN_ERR "eicon: if_writecmd called with invalid driverId!\n"); - return -ENODEV; +#endif + return (len); } static int @@ -745,7 +847,7 @@ if_readstatus(u_char * buf, int len, int user, int id, int channel) printk(KERN_ERR "eicon: if_readstatus called with invalid driverId!\n"); #endif - return -ENODEV; + return 0; } static int @@ -753,6 +855,10 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) { eicon_card *card = eicon_findcard(id); eicon_chan *chan; + int ret = 0; + int len; + + len = skb->len; if (card) { if (!card->flags & EICON_FLAGS_RUNNING) { @@ -763,9 +869,17 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) dev_kfree_skb(skb); return -ENODEV; } - if (chan->fsm_state == EICON_STATE_ACTIVE) - return (idi_send_data(card, chan, ack, skb)); - else { + if (chan->fsm_state == EICON_STATE_ACTIVE) { +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + if ((ret = idi_faxdata_send(card, chan, skb)) > 0) + ret = len; + } + else +#endif + ret = idi_send_data(card, chan, ack, skb, 1); + return (ret); + } else { dev_kfree_skb(skb); return -ENODEV; } @@ -787,7 +901,9 @@ eicon_alloccard(int Type, int membase, int irq, char *id) int i; int j; int qloop; +#ifdef CONFIG_ISDN_DRV_EICON_ISA char qid[5]; +#endif eicon_card *card; #if CONFIG_PCI eicon_pci_card *pcic; @@ -828,12 +944,32 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->myid = -1; card->type = Type; switch (Type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA +#if CONFIG_MCA /* only needed for MCA */ + case EICON_CTYPE_S: + case EICON_CTYPE_SX: + case EICON_CTYPE_SCOM: + if (membase == -1) + membase = EICON_ISA_MEMBASE; + if (irq == -1) + irq = EICON_ISA_IRQ; + card->bus = EICON_BUS_MCA; + card->hwif.isa.card = (void *)card; + card->hwif.isa.shmem = (eicon_isa_shmem *)membase; + card->hwif.isa.master = 1; + + card->hwif.isa.irq = irq; + card->hwif.isa.type = Type; + card->nchannels = 2; + card->interface.channels = 1; + break; +#endif /* CONFIG_MCA */ case EICON_CTYPE_QUADRO: if (membase == -1) membase = EICON_ISA_MEMBASE; if (irq == -1) irq = EICON_ISA_IRQ; - card->bus = EICON_BUS_ISA; + card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET); card->hwif.isa.master = 0; @@ -868,6 +1004,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->nchannels = 2; card->interface.channels = 1; break; +#endif #if CONFIG_PCI case EICON_CTYPE_MAESTRA: (eicon_pci_card *)pcic = (eicon_pci_card *)membase; @@ -876,7 +1013,10 @@ eicon_alloccard(int Type, int membase, int irq, char *id) ISDN_FEATURE_L2_V11096 | ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038 | - ISDN_FEATURE_L2_MODEM; + ISDN_FEATURE_L2_MODEM | + /* ISDN_FEATURE_L2_FAX | */ + ISDN_FEATURE_L3_TRANSDSP | + ISDN_FEATURE_L3_FAX; card->hwif.pci.card = (void *)card; card->hwif.pci.PCIreg = pcic->PCIreg; card->hwif.pci.PCIcfg = pcic->PCIcfg; @@ -897,7 +1037,10 @@ eicon_alloccard(int Type, int membase, int irq, char *id) ISDN_FEATURE_L2_V11096 | ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038 | - ISDN_FEATURE_L2_MODEM; + ISDN_FEATURE_L2_MODEM | + /* ISDN_FEATURE_L2_FAX | */ + ISDN_FEATURE_L3_TRANSDSP | + ISDN_FEATURE_L3_FAX; card->hwif.pci.card = (void *)card; card->hwif.pci.shmem = (eicon_pci_shmem *)pcic->shmem; card->hwif.pci.PCIreg = pcic->PCIreg; @@ -913,6 +1056,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->interface.channels = 1; break; #endif +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_ISABRI: if (membase == -1) membase = EICON_ISA_MEMBASE; @@ -932,7 +1076,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) membase = EICON_ISA_MEMBASE; if (irq == -1) irq = EICON_ISA_IRQ; - card->bus = EICON_BUS_ISA; + card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)membase; card->hwif.isa.master = 1; @@ -941,6 +1085,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->nchannels = 30; card->interface.channels = 1; break; +#endif default: printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type); kfree(card); @@ -978,15 +1123,21 @@ static int eicon_registercard(eicon_card * card) { switch (card->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: /* TODO something to print */ break; +#ifdef CONFIG_MCA + case EICON_BUS_MCA: + eicon_isa_printpar(&card->hwif.isa); + break; +#endif +#endif case EICON_BUS_PCI: #if CONFIG_PCI eicon_pci_printpar(&card->hwif.pci); break; #endif - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -1015,15 +1166,19 @@ unregister_card(eicon_card * card) cmd.driver = card->myid; card->interface.statcallb(&cmd); switch (card->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: +#ifdef CONFIG_MCA + case EICON_BUS_MCA: +#endif eicon_isa_release(&card->hwif.isa); break; +#endif case EICON_BUS_PCI: #if CONFIG_PCI eicon_pci_release(&card->hwif.pci); break; #endif - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -1050,9 +1205,11 @@ eicon_addcard(int Type, int membase, int irq, char *id) int added = 0; int failed = 0; +#ifdef CONFIG_ISDN_DRV_EICON_ISA if (!Type) /* ISA */ if ((Type = eicon_isa_find_card(membase, irq, id)) < 0) return 0; +#endif eicon_alloccard(Type, membase, irq, id); p = cards; while (p) { @@ -1063,11 +1220,14 @@ eicon_addcard(int Type, int membase, int irq, char *id) */ added++; switch (p->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: + case EICON_BUS_MCA: if (eicon_registercard(p)) break; registered = 1; break; +#endif case EICON_BUS_PCI: #if CONFIG_PCI if (eicon_registercard(p)) @@ -1075,7 +1235,6 @@ eicon_addcard(int Type, int membase, int irq, char *id) registered = 1; break; #endif - case EICON_BUS_MCA: default: if (DebugVar & 1) printk(KERN_WARNING @@ -1116,10 +1275,10 @@ eicon_addcard(int Type, int membase, int irq, char *id) #define eicon_init init_module #endif -__initfunc(int -eicon_init(void)) +int __init +eicon_init(void) { - int tmp = 0; + int card_count = 0; int release = 0; char tmprev[50]; @@ -1130,30 +1289,66 @@ eicon_init(void)) printk("%s/", eicon_getrev(tmprev)); release += getrel(tmprev); strcpy(tmprev, eicon_pci_revision); +#if CONFIG_PCI printk("%s/", eicon_getrev(tmprev)); +#else + printk("---/"); +#endif release += getrel(tmprev); strcpy(tmprev, eicon_isa_revision); +#ifdef CONFIG_ISDN_DRV_EICON_ISA printk("%s/", eicon_getrev(tmprev)); +#else + printk("---/"); +#endif release += getrel(tmprev); strcpy(tmprev, eicon_idi_revision); printk("%s\n", eicon_getrev(tmprev)); release += getrel(tmprev); sprintf(tmprev,"%d", release); - printk(KERN_INFO "%s Release: %s.%s\n", DRIVERNAME, - DRIVERRELEASE, tmprev); + printk(KERN_INFO "%s Release: %s.%s%s\n", DRIVERNAME, + DRIVERRELEASE, tmprev, DRIVERPATCH); - tmp = eicon_addcard(0, membase, irq, id); +#ifdef CONFIG_ISDN_DRV_EICON_ISA +#ifdef CONFIG_MCA + /* Check if we have MCA-bus */ + if (!MCA_bus) + { + printk(KERN_INFO + "eicon: No MCA bus, ISDN-interfaces not probed.\n"); + } else { + if (DebugVar & 8) + printk(KERN_DEBUG + "eicon_mca_find_card, irq=%d.\n", + irq); + if (!eicon_mca_find_card(0, membase, irq, id)) + card_count++; + }; +#else + card_count = eicon_addcard(0, membase, irq, id); +#endif /* CONFIG_MCA */ +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ + #if CONFIG_PCI - tmp += eicon_pci_find_card(id); + card_count += eicon_pci_find_card(id); #endif if (!cards) { #ifdef MODULE +#ifndef CONFIG_PCI +#ifndef CONFIG_ISDN_DRV_EICON_ISA + printk(KERN_INFO "Eicon: Driver is neither ISA nor PCI compiled !\n"); +#else printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n"); #endif +#else + printk(KERN_INFO "Eicon: No PCI-cards found, driver not loaded !\n"); +#endif +#endif /* MODULE */ return -ENODEV; } else - printk(KERN_INFO "Eicon: %d card%s added\n", tmp, (tmp>1)?"s":""); + printk(KERN_INFO "Eicon: %d card%s added\n", card_count, + (card_count>1)?"s":""); /* No symbols to export, hide all symbols */ EXPORT_NO_SYMBOLS; return 0; @@ -1166,6 +1361,13 @@ cleanup_module(void) eicon_card *card = cards; eicon_card *last; while (card) { +#ifdef CONFIG_MCA + if (MCA_bus) + { + mca_mark_as_unused (card->mca_slot); + mca_set_adapter_procfn(card->mca_slot, NULL, NULL); + }; +#endif unregister_card(card); card = card->next; } @@ -1178,14 +1380,27 @@ cleanup_module(void) printk(KERN_INFO "%s unloaded\n", DRIVERNAME); } +#else /* no module */ + +#ifdef COMPAT_HAS_NEW_SETUP +static int __init +eicon_setup(char *line) +{ + int i, argc; + int ints[5]; + char *str; + + str = get_options(line, 4, ints); #else -__initfunc(void -eicon_setup(char *str, int *ints)) +void __init +eicon_setup(char *str, int *ints) { int i, argc; +#endif argc = ints[0]; i = 1; +#ifdef CONFIG_ISDN_DRV_EICON_ISA if (argc) { membase = irq = -1; if (argc) { @@ -1203,8 +1418,187 @@ eicon_setup(char *str, int *ints)) } else { strcpy(id, "eicon"); } - /* eicon_addcard(0, membase, irq, id); */ - printk(KERN_INFO "eicon: membase=0x%x irq=%d id=%s\n", membase, irq, id); + printk(KERN_INFO "Eicon ISDN active driver setup (id=%s membase=0x%x irq=%d)\n", + id, membase, irq); } +#else + printk(KERN_INFO "Eicon ISDN active driver setup\n"); +#endif +#ifdef COMPAT_HAS_NEW_SETUP + return(1); +} +__setup("eicon=", eicon_setup); +#else } #endif + +#endif /* MODULE */ + +#ifdef CONFIG_ISDN_DRV_EICON_ISA +#ifdef CONFIG_MCA + +struct eicon_mca_adapters_struct { + char * name; + int adf_id; +}; +/* possible MCA-brands of eicon cards */ +struct eicon_mca_adapters_struct eicon_mca_adapters[] = { + { "ISDN-P/2 Adapter", 0x6abb }, + { "ISDN-[S|SX|SCOM]/2 Adapter", 0x6a93 }, + { "DIVA /MCA", 0x6336 }, + { NULL, 0 }, +}; + +int eicon_mca_find_card(int type, /* type-idx of eicon-card */ + int membase, + int irq, + char * id) /* name of eicon-isdn-dev */ +{ + int j, curr_slot = 0; + + if (DebugVar & 8) + printk(KERN_DEBUG + "eicon_mca_find_card type: %d, membase: %#x, irq %d \n", + type, membase, irq); + /* find a no-driver-assigned eicon card */ + for (j=0; eicon_mca_adapters[j].adf_id != 0; j++) + { + for ( curr_slot=0; curr_slot<=MCA_MAX_SLOT_NR; curr_slot++) + { + curr_slot = mca_find_unused_adapter( + eicon_mca_adapters[j].adf_id, curr_slot); + if (curr_slot != MCA_NOTFOUND) + { + /* check if pre-set parameters match + these of the card, check cards memory */ + if (!(int) eicon_mca_probe(curr_slot, + j, + membase, + irq, + id)) + { + return 0; + /* means: adapter parms did match */ + }; + }; + break; + /* MCA_NOTFOUND-branch: no matching adapter of + THIS flavor found, next flavor */ + + }; + }; + /* all adapter flavors checked without match, finito with: */ + return ENODEV; +}; + + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * stolen from 3c523.c/elmc_getinfo, ewe, 10.5.1999 + */ +int eicon_info(char * buf, int slot, void *d) +{ + int len = 0; + struct eicon_card *dev; + + dev = (struct eicon_card *) d; + + if (dev == NULL) + return len; + len += sprintf(buf+len, "eicon ISDN adapter, type %d.\n",dev->type); + len += sprintf(buf+len, "IRQ: %d\n", dev->hwif.isa.irq); + len += sprintf(buf+len, "MEMBASE: %#lx\n", (unsigned long)dev->hwif.isa.shmem); + + return len; +}; + +int eicon_mca_probe(int slot, /* slot-nr where the card was detected */ + int a_idx, /* idx-nr of probed card in eicon_mca_adapters */ + int membase, + int irq, + char * id) /* name of eicon-isdn-dev */ +{ + unsigned char adf_pos0; + int cards_irq, cards_membase, cards_io; + int type = EICON_CTYPE_S; + int irq_array[]={0,3,4,2}; + int irq_array1[]={3,4,0,0,2,10,11,12}; + + adf_pos0 = mca_read_stored_pos(slot,2); + if (DebugVar & 8) + printk(KERN_DEBUG + "eicon_mca_probe irq=%d, membase=%d\n", + irq, + membase); + switch (a_idx) { + case 0: /* P/2-Adapter (== PRI/S2M ? ) */ + cards_membase= 0xC0000+((adf_pos0>>4)*0x4000); + if (membase == -1) { + membase = cards_membase; + } else { + if (membase != cards_membase) + return ENODEV; + }; + cards_irq=irq_array[((adf_pos0 & 0xC)>>2)]; + if (irq == -1) { + irq = cards_irq; + } else { + if (irq != irq) + return ENODEV; + }; + cards_io= 0xC00 + ((adf_pos0>>4)*0x10); + type = EICON_CTYPE_ISAPRI; + break; + + case 1: /* [S|SX|SCOM]/2 */ + cards_membase= 0xC0000+((adf_pos0>>4)*0x2000); + if (membase == -1) { + membase = cards_membase; + } else { + if (membase != cards_membase) + return ENODEV; + }; + cards_irq=irq_array[((adf_pos0 & 0xC)>>2)]; + if (irq == -1) { + irq = cards_irq; + } else { + if (irq != cards_irq) + return ENODEV; + }; + + cards_io= 0xC00 + ((adf_pos0>>4)*0x10); + type = EICON_CTYPE_SCOM; + break; + + case 2: /* DIVA/MCA */ + cards_io = 0x200+ ((adf_pos0>>4)* 0x20); + cards_irq = irq_array1[(adf_pos0 & 0x7)]; + if (irq == -1) { + irq = cards_irq; + } else { + if (irq != irq) + return ENODEV; + }; + type = 0; + break; + default: + return ENODEV; + }; + /* Uebereinstimmung vorgegebener membase & irq */ + if ( 1 == eicon_addcard(type, membase, irq, id)) { + mca_set_adapter_name(slot, eicon_mca_adapters[a_idx].name); + mca_set_adapter_procfn(slot, (MCA_ProcFn) eicon_info, cards); + + mca_mark_as_used(slot); + cards->mca_slot = slot; + /* card->io noch setzen oder ?? */ + if (DebugVar & 8) + printk("eicon_addcard: erfolgreich fuer slot: %d.\n", + cards->mca_slot+1); + return 0 ; /* eicon_addcard hat eine Karte zugefuegt */ + } else { + return ENODEV; + }; +}; +#endif /* CONFIG_MCA */ +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ + diff --git a/drivers/isdn/eicon/eicon_pci.c b/drivers/isdn/eicon/eicon_pci.c index 3d07167ce..c1919e9f8 100644 --- a/drivers/isdn/eicon/eicon_pci.c +++ b/drivers/isdn/eicon/eicon_pci.c @@ -1,4 +1,4 @@ -/* $Id: eicon_pci.c,v 1.6 1999/04/01 12:48:37 armin Exp $ +/* $Id: eicon_pci.c,v 1.10 1999/08/22 20:26:49 calle Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Hardware-specific code for PCI cards. @@ -26,6 +26,22 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_pci.c,v $ + * Revision 1.10 1999/08/22 20:26:49 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.9 1999/08/11 21:01:11 keil + * new PCI codefix + * + * Revision 1.8 1999/08/10 16:02:20 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.7 1999/06/09 19:31:29 armin + * Wrong PLX size for request_region() corrected. + * Added first MCA code from Erik Weber. + * * Revision 1.6 1999/04/01 12:48:37 armin * Changed some log outputs. * @@ -61,7 +77,7 @@ #include "eicon_pci.h" -char *eicon_pci_revision = "$Revision: 1.6 $"; +char *eicon_pci_revision = "$Revision: 1.10 $"; #if CONFIG_PCI /* intire stuff is only for PCI */ @@ -136,8 +152,8 @@ int eicon_pci_find_card(char *ID) aparms->type = EICON_CTYPE_MAESTRA; aparms->irq = pdev->irq; - preg = pdev->base_address[2] & 0xfffffffc; - pcfg = pdev->base_address[1] & 0xffffff80; + preg = get_pcibase(pdev, 2) & 0xfffffffc; + pcfg = get_pcibase(pdev, 1) & 0xffffff80; #ifdef EICON_PCI_DEBUG printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq); @@ -158,9 +174,9 @@ int eicon_pci_find_card(char *ID) printk(KERN_INFO "Eicon: DIVA Server PRI/PCI detected !\n"); aparms->type = EICON_CTYPE_MAESTRAP; /*includes 9M,30M*/ aparms->irq = pdev->irq; - pram = pdev->base_address[0] & 0xfffff000; - preg = pdev->base_address[2] & 0xfffff000; - pcfg = pdev->base_address[4] & 0xfffff000; + pram = get_pcibase(pdev, 0) & 0xfffff000; + preg = get_pcibase(pdev, 2) & 0xfffff000; + pcfg = get_pcibase(pdev, 4) & 0xfffff000; #ifdef EICON_PCI_DEBUG printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq); @@ -194,12 +210,13 @@ int eicon_pci_find_card(char *ID) } else { request_region(aparms->PCIreg, 0x20, "eicon reg"); } - if (check_region((aparms->PCIcfg), 0x100)) { + if (check_region((aparms->PCIcfg), 0x80)) { printk(KERN_WARNING "eicon_pci: cfg port already in use !\n"); aparms->PCIcfg = 0; + release_region(aparms->PCIreg, 0x20); break; } else { - request_region(aparms->PCIcfg, 0x100, "eicon cfg"); + request_region(aparms->PCIcfg, 0x80, "eicon cfg"); } break; case PCI_MAESTRAQ: @@ -327,7 +344,7 @@ eicon_pci_release_shmem(eicon_pci_card *card) { outw(0, card->PCIreg + M_DATA); release_region(card->PCIreg, 0x20); - release_region(card->PCIcfg, 0x100); + release_region(card->PCIcfg, 0x80); break; case EICON_CTYPE_MAESTRAQ: case EICON_CTYPE_MAESTRAQ_U: diff --git a/drivers/isdn/hisax/.cvsignore b/drivers/isdn/hisax/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/hisax/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index 9e7cb7773..051df6182 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -25,13 +25,11 @@ ifeq ($(CONFIG_HISAX_1TR6),y) endif ISAC_OBJ := -ARCOFI_OBJ := HSCX_OBJ := ISAR_OBJ := HFC_OBJ := HFC_2BDS0 := -RAWHDLC_OBJ := - +JADE_OBJ := ifeq ($(CONFIG_HISAX_16_0),y) O_OBJS += teles0.o ISAC_OBJ := isac.o @@ -78,7 +76,6 @@ ifeq ($(CONFIG_HISAX_ELSA),y) O_OBJS += elsa.o ISAC_OBJ := isac.o HSCX_OBJ := hscx.o - ARCOFI_OBJ := arcofi.o endif ifeq ($(CONFIG_HISAX_IX1MICROR2),y) @@ -127,16 +124,15 @@ endif ifeq ($(CONFIG_HISAX_NETJET),y) O_OBJS += netjet.o ISAC_OBJ := isac.o -# RAWHDLC_OBJ := rawhdlc.o endif -ifeq ($(CONFIG_HISAX_TELES3C),y) - O_OBJS += teles3c.o +ifeq ($(CONFIG_HISAX_HFCS),y) + O_OBJS += hfcscard.o HFC_2BDS0 := hfc_2bds0.o endif -ifeq ($(CONFIG_HISAX_AMD7930),y) - O_OBJS += amd7930.o - RAWHDLC_OBJ := rawhdlc.o + +ifeq ($(CONFIG_HISAX_HFC_PCI),y) + HFC_2BDS0 += hfc_pci.o endif ifeq ($(CONFIG_HISAX_NICCY),y) @@ -145,8 +141,45 @@ ifeq ($(CONFIG_HISAX_NICCY),y) HSCX_OBJ := hscx.o endif -O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(ARCOFI_OBJ) -O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ) +ifeq ($(CONFIG_HISAX_ISURF),y) + O_OBJS += isurf.o + ISAC_OBJ := isac.o + ISAR_OBJ := isar.o +endif + +ifeq ($(CONFIG_HISAX_HSTSAPHIR),y) + O_OBJS += saphir.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_BKM_A4T),y) + O_OBJS += bkm_a4t.o + ISAC_OBJ := isac.o + JADE_OBJ := jade.o +endif +ifeq ($(CONFIG_HISAX_SCT_QUADRO),y) + O_OBJS += bkm_a8.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_GAZEL),y) + O_OBJS += gazel.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +# ifeq ($(CONFIG_HISAX_TESTEMU),y) +# O_OBJS += testemu.o +# endif + +ifeq ($(ISAC_OBJ), isac.o) + ISAC_OBJ += arcofi.o +endif + +O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(JADE_OBJ) +O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) OX_OBJS += config.o O_TARGET := @@ -164,7 +197,8 @@ endif include $(TOPDIR)/Rules.make MD5FILES += isac.c isdnl1.c isdnl2.c isdnl3.c \ - tei.c callc.c cert.c l3dss1.c l3_1tr6.c elsa.c + tei.c callc.c cert.c l3dss1.c l3_1tr6.c \ + elsa.c diva.c CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) diff --git a/drivers/isdn/hisax/amd7930.c b/drivers/isdn/hisax/amd7930.c index 1cf2307b3..7bcc8827c 100644 --- a/drivers/isdn/hisax/amd7930.c +++ b/drivers/isdn/hisax/amd7930.c @@ -1,4 +1,4 @@ -/* $Id: amd7930.c,v 1.2 1998/02/12 23:07:10 keil Exp $ +/* $Id: amd7930.c,v 1.3 1999/07/12 21:04:52 keil Exp $ * * HiSax ISDN driver - chip specific routines for AMD 7930 * @@ -7,6 +7,10 @@ * * * $Log: amd7930.c,v $ + * Revision 1.3 1999/07/12 21:04:52 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 1.2 1998/02/12 23:07:10 keil * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * @@ -105,7 +109,7 @@ #include "rawhdlc.h" #include <linux/interrupt.h> -static const char *amd7930_revision = "$Revision: 1.2 $"; +static const char *amd7930_revision = "$Revision: 1.3 $"; #define RCV_BUFSIZE 1024 /* Size of raw receive buffer in bytes */ #define RCV_BUFBLKS 4 /* Number of blocks to divide buffer into @@ -734,8 +738,6 @@ amd7930_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_amd7930(cs); return(0); - case CARD_SETIRQ: - return(0); case CARD_INIT: cs->l1cmd = amd7930_l1cmd; amd7930_liu_init(0, &amd7930_liu_callback, (void *)cs); @@ -747,7 +749,7 @@ amd7930_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __init setup_amd7930(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c index a7c091321..9e582641d 100644 --- a/drivers/isdn/hisax/arcofi.c +++ b/drivers/isdn/hisax/arcofi.c @@ -1,4 +1,4 @@ -/* $Id: arcofi.c,v 1.6 1998/09/30 22:21:56 keil Exp $ +/* $Id: arcofi.c,v 1.8 1999/08/25 16:50:51 keil Exp $ * arcofi.c Ansteuerung ARCOFI 2165 * @@ -7,6 +7,12 @@ * * * $Log: arcofi.c,v $ + * Revision 1.8 1999/08/25 16:50:51 keil + * Fix bugs which cause 2.3.14 hangs (waitqueue init) + * + * Revision 1.7 1999/07/01 08:11:17 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.6 1998/09/30 22:21:56 keil * cosmetics * @@ -32,48 +38,124 @@ #include "hisax.h" #include "isdnl1.h" #include "isac.h" +#include "arcofi.h" -int -send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive) { +#define ARCOFI_TIMER_VALUE 20 + +static void +add_arcofi_timer(struct IsdnCardState *cs) { + if (test_and_set_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) { + del_timer(&cs->dc.isac.arcofitimer); + } + init_timer(&cs->dc.isac.arcofitimer); + cs->dc.isac.arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ)/1000); + add_timer(&cs->dc.isac.arcofitimer); +} + +static void +send_arcofi(struct IsdnCardState *cs) { u_char val; - long flags; - int cnt=30; - cs->mon_txp = 0; - cs->mon_txc = msg[0]; - memcpy(cs->mon_tx, &msg[1], cs->mon_txc); - switch(bc) { + add_arcofi_timer(cs); + cs->dc.isac.mon_txp = 0; + cs->dc.isac.mon_txc = cs->dc.isac.arcofi_list->len; + memcpy(cs->dc.isac.mon_tx, cs->dc.isac.arcofi_list->msg, cs->dc.isac.mon_txc); + switch(cs->dc.isac.arcofi_bc) { case 0: break; - case 1: cs->mon_tx[1] |= 0x40; + case 1: cs->dc.isac.mon_tx[1] |= 0x40; break; default: break; } - cs->mocr &= 0x0f; - cs->mocr |= 0xa0; - test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags); - if (receive) - test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags); - cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->dc.isac.mocr &= 0x0f; + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); val = cs->readisac(cs, ISAC_MOSR); - cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]); - cs->mocr |= 0x10; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - save_flags(flags); - sti(); - while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) { - cnt--; - udelay(500); + cs->writeisac(cs, ISAC_MOX1, cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]); + cs->dc.isac.mocr |= 0x10; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); +} + +int +arcofi_fsm(struct IsdnCardState *cs, int event, void *data) { + if (cs->debug & L1_DEB_MONITOR) { + debugl1(cs, "arcofi state %d event %d", cs->dc.isac.arcofi_state, event); } - if (receive) { - while (cnt && !test_bit(HW_MON1_RX_END, &cs->HW_Flags)) { - cnt--; - udelay(500); - } + if (event == ARCOFI_TIMEOUT) { + cs->dc.isac.arcofi_state = ARCOFI_NOP; + test_and_set_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags); + wake_up_interruptible(&cs->dc.isac.arcofi_wait); + return(1); } - restore_flags(flags); - if (cnt <= 0) { - printk(KERN_WARNING"HiSax arcofi monitor timed out\n"); - debugl1(cs, "HiSax arcofi monitor timed out"); + switch (cs->dc.isac.arcofi_state) { + case ARCOFI_NOP: + if (event == ARCOFI_START) { + cs->dc.isac.arcofi_list = data; + cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT; + send_arcofi(cs); + } + break; + case ARCOFI_TRANSMIT: + if (event == ARCOFI_TX_END) { + if (cs->dc.isac.arcofi_list->receive) { + add_arcofi_timer(cs); + cs->dc.isac.arcofi_state = ARCOFI_RECEIVE; + } else { + if (cs->dc.isac.arcofi_list->next) { + cs->dc.isac.arcofi_list = + cs->dc.isac.arcofi_list->next; + send_arcofi(cs); + } else { + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) { + del_timer(&cs->dc.isac.arcofitimer); + } + cs->dc.isac.arcofi_state = ARCOFI_NOP; + wake_up_interruptible(&cs->dc.isac.arcofi_wait); + } + } + } + break; + case ARCOFI_RECEIVE: + if (event == ARCOFI_RX_END) { + if (cs->dc.isac.arcofi_list->next) { + cs->dc.isac.arcofi_list = + cs->dc.isac.arcofi_list->next; + cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT; + send_arcofi(cs); + } else { + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) { + del_timer(&cs->dc.isac.arcofitimer); + } + cs->dc.isac.arcofi_state = ARCOFI_NOP; + wake_up_interruptible(&cs->dc.isac.arcofi_wait); + } + } + break; + default: + debugl1(cs, "Arcofi unknown state %x", cs->dc.isac.arcofi_state); + return(2); } - return(cnt); + return(0); +} + +static void +arcofi_timer(struct IsdnCardState *cs) { + arcofi_fsm(cs, ARCOFI_TIMEOUT, NULL); +} + +void +clear_arcofi(struct IsdnCardState *cs) { + if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) { + del_timer(&cs->dc.isac.arcofitimer); + } +} + +void +init_arcofi(struct IsdnCardState *cs) { + cs->dc.isac.arcofitimer.function = (void *) arcofi_timer; + cs->dc.isac.arcofitimer.data = (long) cs; + init_timer(&cs->dc.isac.arcofitimer); +#ifdef COMPAT_HAS_NEW_WAITQ + init_waitqueue_head(&cs->dc.isac.arcofi_wait); +#endif + test_and_set_bit(HW_ARCOFI, &cs->HW_Flags); } diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h index be1097d15..86617d6a1 100644 --- a/drivers/isdn/hisax/arcofi.h +++ b/drivers/isdn/hisax/arcofi.h @@ -1,4 +1,4 @@ -/* $Id: arcofi.h,v 1.3 1998/05/25 12:57:39 keil Exp $ +/* $Id: arcofi.h,v 1.4 1999/07/01 08:11:18 keil Exp $ * arcofi.h Ansteuerung ARCOFI 2165 * @@ -7,6 +7,9 @@ * * * $Log: arcofi.h,v $ + * Revision 1.4 1999/07/01 08:11:18 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.3 1998/05/25 12:57:39 keil * HiSax golden code from certification, Don't use !!! * No leased lines, no X75, but many changes. @@ -21,4 +24,16 @@ #define ARCOFI_USE 1 -extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive); +/* states */ +#define ARCOFI_NOP 0 +#define ARCOFI_TRANSMIT 1 +#define ARCOFI_RECEIVE 2 +/* events */ +#define ARCOFI_START 1 +#define ARCOFI_TX_END 2 +#define ARCOFI_RX_END 3 +#define ARCOFI_TIMEOUT 4 + +extern int arcofi_fsm(struct IsdnCardState *cs, int event, void *data); +extern void init_arcofi(struct IsdnCardState *cs); +extern void clear_arcofi(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c index eeaad83f5..06d8b59d0 100644 --- a/drivers/isdn/hisax/asuscom.c +++ b/drivers/isdn/hisax/asuscom.c @@ -1,4 +1,4 @@ -/* $Id: asuscom.c,v 1.5 1998/11/15 23:54:19 keil Exp $ +/* $Id: asuscom.c,v 1.7 1999/07/12 21:04:53 keil Exp $ * asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards * @@ -8,6 +8,13 @@ * * * $Log: asuscom.c,v $ + * Revision 1.7 1999/07/12 21:04:53 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.6 1999/07/01 08:11:18 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.5 1998/11/15 23:54:19 keil * changes from 2.0 * @@ -32,7 +39,7 @@ extern const char *CardType[]; -const char *Asuscom_revision = "$Revision: 1.5 $"; +const char *Asuscom_revision = "$Revision: 1.7 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -181,7 +188,7 @@ static void asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); @@ -189,16 +196,12 @@ asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); if (val) { if (cs->debug & L1_DEB_HSCX) @@ -211,23 +214,19 @@ asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - if (stat & 1) { - writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF); - writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF); - writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0); - writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0); - } - if (stat & 2) { - writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0); } static void asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char ista, val, icnt = 20; + u_char ista, val, icnt = 5; if (!cs) { printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); @@ -290,13 +289,13 @@ reset_asuscom(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); if (cs->subtyp == ASUS_IPAC) writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0); else byteout(cs->hw.asus.adr, 0); /* Reset Off */ current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); if (cs->subtyp == ASUS_IPAC) { writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0); writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff); @@ -317,13 +316,6 @@ Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_asuscom(cs); return(0); - case CARD_SETIRQ: - if (cs->subtyp == ASUS_IPAC) - return(request_irq(cs->irq, &asuscom_interrupt_ipac, - I4L_IRQ_FLAG, "HiSax", cs)); - else - return(request_irq(cs->irq, &asuscom_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: cs->debug |= L1_DEB_IPAC; inithscxisac(cs, 3); @@ -334,7 +326,7 @@ Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __init setup_asuscom(struct IsdnCard *card) { int bytecnt; @@ -378,6 +370,7 @@ setup_asuscom(struct IsdnCard *card) cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; cs->writeisacfifo = &WriteISACfifo_IPAC; + cs->irq_func = &asuscom_interrupt_ipac; printk(KERN_INFO "Asus: IPAC version %x\n", val); } else { cs->subtyp = ASUS_ISACHSCX; @@ -390,6 +383,7 @@ setup_asuscom(struct IsdnCard *card) cs->writeisac = &WriteISAC; cs->readisacfifo = &ReadISACfifo; cs->writeisacfifo = &WriteISACfifo; + cs->irq_func = &asuscom_interrupt; ISACVersion(cs, "ISDNLink:"); if (HscxVersion(cs, "ISDNLink:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c index e782708fb..f96db3675 100644 --- a/drivers/isdn/hisax/avm_a1.c +++ b/drivers/isdn/hisax/avm_a1.c @@ -1,4 +1,4 @@ -/* $Id: avm_a1.c,v 2.10 1998/11/15 23:54:21 keil Exp $ +/* $Id: avm_a1.c,v 2.11 1999/07/12 21:04:54 keil Exp $ * avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards * @@ -6,6 +6,10 @@ * * * $Log: avm_a1.c,v $ + * Revision 2.11 1999/07/12 21:04:54 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 2.10 1998/11/15 23:54:21 keil * changes from 2.0 * @@ -66,7 +70,7 @@ #include "isdnl1.h" extern const char *CardType[]; -static const char *avm_revision = "$Revision: 2.10 $"; +static const char *avm_revision = "$Revision: 2.11 $"; #define AVM_A1_STAT_ISAC 0x01 #define AVM_A1_STAT_HSCX 0x02 @@ -153,7 +157,7 @@ static void avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, sval, stat = 0; + u_char val, sval; if (!cs) { printk(KERN_WARNING "AVM A1: Spurious interrupt!\n"); @@ -167,29 +171,21 @@ avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "avm IntStatus %x", sval); if (!(sval & AVM_A1_STAT_HSCX)) { val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA); - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } } if (!(sval & AVM_A1_STAT_ISAC)) { val = readreg(cs->hw.avm.isac, ISAC_ISTA); - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } } } - if (stat & 1) { - writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF); - writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF); - writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0); - writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0); - } - if (stat & 2) { - writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.avm.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.avm.isac, ISAC_MASK, 0x0); + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0); } inline static void @@ -219,9 +215,6 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_ioregs(cs, 0x3f); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &avm_a1_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: inithscxisac(cs, 1); byteout(cs->hw.avm.cfg_reg, 0x16); @@ -234,7 +227,7 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __init setup_avm_a1(struct IsdnCard *card) { u_char val; @@ -378,6 +371,7 @@ setup_avm_a1(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &AVM_card_msg; + cs->irq_func = &avm_a1_interrupt; ISACVersion(cs, "AVM A1:"); if (HscxVersion(cs, "AVM A1:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/avm_a1p.c b/drivers/isdn/hisax/avm_a1p.c index c11ac41e4..862829ab3 100644 --- a/drivers/isdn/hisax/avm_a1p.c +++ b/drivers/isdn/hisax/avm_a1p.c @@ -1,4 +1,4 @@ -/* $Id: avm_a1p.c,v 2.3 1998/11/15 23:54:22 keil Exp $ +/* $Id: avm_a1p.c,v 2.4 1999/07/12 21:04:55 keil Exp $ * * avm_a1p.c low level stuff for the following AVM cards: * A1 PCMCIA @@ -8,6 +8,10 @@ * Author Carsten Paeth (calle@calle.in-berlin.de) * * $Log: avm_a1p.c,v $ + * Revision 2.4 1999/07/12 21:04:55 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 2.3 1998/11/15 23:54:22 keil * changes from 2.0 * @@ -67,7 +71,7 @@ #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) -static const char *avm_revision = "$Revision: 2.3 $"; +static const char *avm_revision = "$Revision: 2.4 $"; static inline u_char ReadISAC(struct IsdnCardState *cs, u_char offset) @@ -195,7 +199,7 @@ static void avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, sval, stat = 0; + u_char val, sval; if (!cs) { printk(KERN_WARNING "AVM A1 PCMCIA: Spurious interrupt!\n"); @@ -206,35 +210,26 @@ avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "avm IntStatus %x", sval); if (sval & ASL0_R_HSCX) { val = ReadHSCX(cs, 1, HSCX_ISTA); - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } } if (sval & ASL0_R_ISAC) { val = ReadISAC(cs, ISAC_ISTA); - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } } } - if (stat & 1) { - WriteHSCX(cs, 0, HSCX_MASK, 0xff); - WriteHSCX(cs, 1, HSCX_MASK, 0xff); - WriteHSCX(cs, 0, HSCX_MASK, 0x00); - WriteHSCX(cs, 1, HSCX_MASK, 0x00); - } - if (stat & 2) { - WriteISAC(cs, ISAC_MASK, 0xff); - WriteISAC(cs, ISAC_MASK, 0x00); - } + WriteHSCX(cs, 0, HSCX_MASK, 0xff); + WriteHSCX(cs, 1, HSCX_MASK, 0xff); + WriteISAC(cs, ISAC_MASK, 0xff); + WriteISAC(cs, ISAC_MASK, 0x00); + WriteHSCX(cs, 0, HSCX_MASK, 0x00); + WriteHSCX(cs, 1, HSCX_MASK, 0x00); } static int AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret; switch (mt) { case CARD_RESET: byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); @@ -249,15 +244,6 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) /* free_irq(cs->irq, cs); */ return 0; - case CARD_SETIRQ: - ret = request_irq(cs->irq, &avm_a1p_interrupt, - I4L_IRQ_FLAG, "HiSax", cs); - if (ret) - return ret; - byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, - ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE); - return 0; - case CARD_INIT: clear_pending_isac_ints(cs); clear_pending_hscx_ints(cs); @@ -276,8 +262,8 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return 0; } -__initfunc(int -setup_avm_a1_pcmcia(struct IsdnCard *card)) +int __init +setup_avm_a1_pcmcia(struct IsdnCard *card) { u_char model, vers; struct IsdnCardState *cs = card->cs; @@ -323,6 +309,7 @@ setup_avm_a1_pcmcia(struct IsdnCard *card)) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &AVM_card_msg; + cs->irq_func = &avm_a1p_interrupt; ISACVersion(cs, "AVM A1 PCMCIA:"); if (HscxVersion(cs, "AVM A1 PCMCIA:")) { diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index c0f04f91c..cd293f147 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -1,4 +1,4 @@ -/* $Id: avm_pci.c,v 1.7 1999/02/22 18:26:30 keil Exp $ +/* $Id: avm_pci.c,v 1.11 1999/08/11 21:01:18 keil Exp $ * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards * Thanks to AVM, Berlin for informations @@ -7,6 +7,19 @@ * * * $Log: avm_pci.c,v $ + * Revision 1.11 1999/08/11 21:01:18 keil + * new PCI codefix + * + * Revision 1.10 1999/08/10 16:01:44 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.9 1999/07/12 21:04:57 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.8 1999/07/01 08:11:19 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.7 1999/02/22 18:26:30 keil * Argh ! ISAC address was only set with PCI * @@ -37,10 +50,13 @@ #include "isac.h" #include "isdnl1.h" #include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif #include <linux/interrupt.h> extern const char *CardType[]; -static const char *avm_pci_rev = "$Revision: 1.7 $"; +static const char *avm_pci_rev = "$Revision: 1.11 $"; #define AVM_FRITZ_PCI 1 #define AVM_FRITZ_PNP 2 @@ -466,7 +482,7 @@ HDLC_irq(struct BCState *bcs, u_int stat) { if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hdlc.count); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->hw.hdlc.count = 0; bcs->tx_skb = NULL; } @@ -593,7 +609,7 @@ close_hdlcstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -687,7 +703,7 @@ static void avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; u_char sval; if (!cs) { @@ -701,15 +717,12 @@ avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs) if (!(sval & AVM_STATUS0_IRQ_ISAC)) { val = ReadISAC(cs, ISAC_ISTA); isac_interrupt(cs, val); - stat |= 2; } if (!(sval & AVM_STATUS0_IRQ_HDLC)) { HDLC_irq_main(cs); } - if (stat & 2) { - WriteISAC(cs, ISAC_MASK, 0xFF); - WriteISAC(cs, ISAC_MASK, 0x0); - } + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); } static void @@ -733,8 +746,6 @@ reset_avmpcipnp(struct IsdnCardState *cs) static int AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - u_int irq_flag; - switch (mt) { case CARD_RESET: reset_avmpcipnp(cs); @@ -743,13 +754,6 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) outb(0, cs->hw.avm.cfg_reg + 2); release_region(cs->hw.avm.cfg_reg, 32); return(0); - case CARD_SETIRQ: - if (cs->subtyp == AVM_FRITZ_PCI) - irq_flag = I4L_IRQ_FLAG | SA_SHIRQ; - else - irq_flag = I4L_IRQ_FLAG; - return(request_irq(cs->irq, &avm_pcipnp_interrupt, - irq_flag, "HiSax", cs)); case CARD_INIT: clear_pending_isac_ints(cs); initisac(cs); @@ -769,10 +773,14 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_avm __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif -__initfunc(int -setup_avm_pcipnp(struct IsdnCard *card)) +int __init +setup_avm_pcipnp(struct IsdnCard *card) { u_int val, ver; struct IsdnCardState *cs = card->cs; @@ -788,6 +796,7 @@ setup_avm_pcipnp(struct IsdnCard *card)) cs->subtyp = AVM_FRITZ_PNP; } else { #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "FritzPCI: no PCI bus present\n"); return(0); @@ -799,7 +808,7 @@ setup_avm_pcipnp(struct IsdnCard *card)) printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n"); return(0); } - cs->hw.avm.cfg_reg = dev_avm->base_address[1] & + cs->hw.avm.cfg_reg = get_pcibase(dev_avm, 1) & PCI_BASE_ADDRESS_IO_MASK; if (!cs->hw.avm.cfg_reg) { printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n"); @@ -811,6 +820,37 @@ setup_avm_pcipnp(struct IsdnCard *card)) return(0); } #else + for (; pci_index < 255; pci_index++) { + unsigned char pci_bus, pci_device_fn; + unsigned int ioaddr; + unsigned char irq; + + if (pcibios_find_device (PCI_VENDOR_AVM, + PCI_FRITZPCI_ID, pci_index, + &pci_bus, &pci_device_fn) != 0) { + continue; + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &ioaddr); + cs->irq = irq; + cs->hw.avm.cfg_reg = ioaddr & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.avm.cfg_reg) { + printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n"); + return(0); + } + cs->subtyp = AVM_FRITZ_PCI; + break; + } + if (pci_index == 255) { + printk(KERN_WARNING "FritzPCI: No PCI card found\n"); + return(0); + } + pci_index++; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->irq_flags |= SA_SHIRQ; +#else printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); return (0); #endif /* CONFIG_PCI */ @@ -846,8 +886,6 @@ setup_avm_pcipnp(struct IsdnCard *card)) break; default: printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp); - outb(0, cs->hw.avm.cfg_reg + 2); - release_region(cs->hw.avm.cfg_reg, 32); return(0); } printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n", @@ -860,6 +898,7 @@ setup_avm_pcipnp(struct IsdnCard *card)) cs->writeisacfifo = &WriteISACfifo; cs->BC_Send_Data = &fill_hdlc; cs->cardmsg = &AVM_card_msg; + cs->irq_func = &avm_pcipnp_interrupt; ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:"); return (1); } diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c new file mode 100644 index 000000000..57d99b436 --- /dev/null +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -0,0 +1,410 @@ +/* $Id: bkm_a4t.c,v 1.7 1999/08/22 20:26:55 calle Exp $ + * bkm_a4t.c low level stuff for T-Berkom A4T + * derived from the original file sedlbauer.c + * derived from the original file niccy.c + * derived from the original file netjet.c + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: bkm_a4t.c,v $ + * Revision 1.7 1999/08/22 20:26:55 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.6 1999/08/11 21:01:22 keil + * new PCI codefix + * + * Revision 1.5 1999/08/10 16:01:46 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.4 1999/07/14 11:43:14 keil + * correct PCI_SUBSYSTEM_VENDOR_ID + * + * Revision 1.3 1999/07/12 21:04:58 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:07:53 keil + * Initial version + * + * + */ + +#define __NO_VERSION__ + +#include <linux/config.h> +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "jade.h" +#include "isdnl1.h" +#include "bkm_ax.h" +#include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif + +extern const char *CardType[]; + +const char *bkm_a4t_revision = "$Revision: 1.7 $"; + + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_int ret; + long flags; + unsigned int *po = (unsigned int *) adr; /* Postoffice */ + save_flags(flags); + cli(); + *po = (GCS_2 | PO_WRITE | off); + __WAITI20__(po); + *po = (ale | PO_READ); + __WAITI20__(po); + ret = *po; + restore_flags(flags); + return ((unsigned char) ret); +} + + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + int i; + for (i = 0; i < size; i++) + *data++ = readreg(ale, adr, off); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + unsigned int *po = (unsigned int *) adr; /* Postoffice */ + save_flags(flags); + cli(); + *po = (GCS_2 | PO_WRITE | off); + __WAITI20__(po); + *po = (ale | PO_WRITE | data); + __WAITI20__(po); + restore_flags(flags); +} + + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + int i; + + for (i = 0; i < size; i++) + writereg(ale, adr, off, *data++); +} + + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size); +} + +static u_char +ReadJADE(struct IsdnCardState *cs, int jade, u_char offset) +{ + return (readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)))); +} + +static void +WriteJADE(struct IsdnCardState *cs, int jade, u_char offset, u_char value) +{ + writereg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)), value); +} + +/* + * fast interrupt JADE stuff goes here + */ + +#define READJADE(cs, nr, reg) readreg(cs->hw.ax.jade_ale,\ + cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80))) +#define WRITEJADE(cs, nr, reg, data) writereg(cs->hw.ax.jade_ale,\ + cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), data) + +#define READJADEFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.jade_ale,\ + cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt) +#define WRITEJADEFIFO(cs, nr, ptr, cnt) writefifo( cs->hw.ax.jade_ale,\ + cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt) + +#include "jade_irq.c" + +static void +bkm_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val = 0; + I20_REGISTER_FILE *pI20_Regs; + + if (!cs) { + printk(KERN_WARNING "HiSax: Telekom A4T: Spurious interrupt!\n"); + return; + } + pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + + /* ISDN interrupt pending? */ + if (pI20_Regs->i20IntStatus & intISDN) { + /* Reset the ISDN interrupt */ + pI20_Regs->i20IntStatus = intISDN; + /* Disable ISDN interrupt */ + pI20_Regs->i20IntCtrl &= ~intISDN; + /* Channel A first */ + val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0x80); + if (val) { + jade_int_main(cs, val, 0); + } + /* Channel B */ + val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0xC0); + if (val) { + jade_int_main(cs, val, 1); + } + /* D-Channel */ + val = readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + } + /* Reenable ISDN interrupt */ + pI20_Regs->i20IntCtrl |= intISDN; + } +} + +void +release_io_bkm(struct IsdnCardState *cs) +{ + if (cs->hw.ax.base) { + iounmap((void *) cs->hw.ax.base); + cs->hw.ax.base = 0; + } +} + +static void +enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable) +{ + if (cs->typ == ISDN_CTYPE_BKM_A4T) { + I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + if (bEnable) + pI20_Regs->i20IntCtrl |= (intISDN | intPCI); + else + /* CAUTION: This disables the video capture driver too */ + pI20_Regs->i20IntCtrl &= ~(intISDN | intPCI); + } +} + +static void +reset_bkm(struct IsdnCardState *cs) +{ + long flags; + + if (cs->typ == ISDN_CTYPE_BKM_A4T) { + I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + save_flags(flags); + sti(); + /* Issue the I20 soft reset */ + pI20_Regs->i20SysControl = 0xFF; /* all in */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + /* Remove the soft reset */ + pI20_Regs->i20SysControl = sysRESET | 0xFF; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + /* Set our configuration */ + pI20_Regs->i20SysControl = sysRESET | sysCFG; + /* Issue ISDN reset */ + pI20_Regs->i20GuestControl = guestWAIT_CFG | + g_A4T_JADE_RES | + g_A4T_ISAR_RES | + g_A4T_ISAC_RES | + g_A4T_JADE_BOOTR | + g_A4T_ISAR_BOOTR; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + + /* Remove RESET state from ISDN */ + pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES | + g_A4T_JADE_RES | + g_A4T_ISAR_RES); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + restore_flags(flags); + } +} + +static int +BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + /* Disable ints */ + enable_bkm_int(cs, 0); + reset_bkm(cs); + return (0); + case CARD_RELEASE: + /* Sanity */ + enable_bkm_int(cs, 0); + reset_bkm(cs); + release_io_bkm(cs); + return (0); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_jade_ints(cs); + initisac(cs); + initjade(cs); + /* Enable ints */ + enable_bkm_int(cs, 1); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_a4t __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +int __init + setup_bkm_a4t(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + u_int pci_memaddr = 0, found = 0; + I20_REGISTER_FILE *pI20_Regs; +#if CONFIG_PCI +#ifndef COMPAT_HAS_NEW_PCI + u_char pci_bus, pci_device_fn, pci_irq = 0; +#endif +#endif + + strcpy(tmp, bkm_a4t_revision); + printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ == ISDN_CTYPE_BKM_A4T) { + cs->subtyp = BKM_A4T; + } else + return (0); + +#if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "bkm_a4t: no PCI bus present\n"); + return (0); + } + if ((dev_a4t = pci_find_device(I20_VENDOR_ID, I20_DEVICE_ID, dev_a4t))) { + u_int sub_sys_id = 0; + + pci_read_config_dword(dev_a4t, PCI_SUBSYSTEM_VENDOR_ID, + &sub_sys_id); + if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) { + found = 1; + pci_memaddr = get_pcibase(dev_a4t, 0); + cs->irq = dev_a4t->irq; + } + } +#else + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(I20_VENDOR_ID, + I20_DEVICE_ID, + pci_index, + &pci_bus, + &pci_device_fn) == PCIBIOS_SUCCESSFUL) { + u_int sub_sys_id = 0; + + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_SUBSYSTEM_VENDOR_ID, &sub_sys_id); + if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) { + found = 1; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + cs->irq = pci_irq; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_memaddr); + break; + } + } + } +#endif /* COMPAT_HAS_NEW_PCI */ + if (!found) { + printk(KERN_WARNING "HiSax: %s: Card not found\n", CardType[card->typ]); + return (0); + } +#ifndef COMPAT_HAS_NEW_PCI + pci_index++; +#endif + if (!cs->irq) { /* IRQ range check ?? */ + printk(KERN_WARNING "HiSax: %s: No IRQ\n", CardType[card->typ]); + return (0); + } + if (!pci_memaddr) { + printk(KERN_WARNING "HiSax: %s: No Memory base address\n", CardType[card->typ]); + return (0); + } + pci_memaddr &= PCI_BASE_ADDRESS_MEM_MASK; + cs->hw.ax.base = (u_int) ioremap(pci_memaddr, 4096); + /* Check suspecious address */ + pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + if ((pI20_Regs->i20IntStatus & 0x8EFFFFFF) != 0) { + printk(KERN_WARNING "HiSax: %s address %x-%x suspecious\n", + CardType[card->typ], cs->hw.ax.base, cs->hw.ax.base + 4096); + iounmap((void *) cs->hw.ax.base); + cs->hw.ax.base = 0; + return (0); + } + cs->hw.ax.isac_adr = cs->hw.ax.base + PO_OFFSET; + cs->hw.ax.jade_adr = cs->hw.ax.base + PO_OFFSET; + cs->hw.ax.isac_ale = GCS_1; + cs->hw.ax.jade_ale = GCS_3; +#else + printk(KERN_WARNING "HiSax: %s: NO_PCI_BIOS\n", CardType[card->typ]); + printk(KERN_WARNING "HiSax: %s: unable to configure\n", CardType[card->typ]); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO "HiSax: %s: Card configured at 0x%X IRQ %d\n", + CardType[card->typ], cs->hw.ax.base, cs->irq); + + reset_bkm(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadJADE; + cs->BC_Write_Reg = &WriteJADE; + cs->BC_Send_Data = &jade_fill_fifo; + cs->cardmsg = &BKM_card_msg; + cs->irq_func = &bkm_interrupt; + cs->irq_flags |= SA_SHIRQ; + ISACVersion(cs, "Telekom A4T:"); + /* Jade version */ + JadeVersion(cs, "Telekom A4T:"); + return (1); +} diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c new file mode 100644 index 000000000..e32c4544a --- /dev/null +++ b/drivers/isdn/hisax/bkm_a8.c @@ -0,0 +1,536 @@ +/* $Id: bkm_a8.c,v 1.7 1999/08/22 20:26:58 calle Exp $ + * bkm_a8.c low level stuff for Scitel Quadro (4*S0, passive) + * derived from the original file sedlbauer.c + * derived from the original file niccy.c + * derived from the original file netjet.c + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: bkm_a8.c,v $ + * Revision 1.7 1999/08/22 20:26:58 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.6 1999/08/11 21:01:24 keil + * new PCI codefix + * + * Revision 1.5 1999/08/10 16:01:48 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.4 1999/07/14 11:43:15 keil + * correct PCI_SUBSYSTEM_VENDOR_ID + * + * Revision 1.3 1999/07/12 21:04:59 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:07:54 keil + * Initial version + * + * + */ +#define __NO_VERSION__ + +#include <linux/config.h> +#include "hisax.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" +#include "isdnl1.h" +#include "bkm_ax.h" +#include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif + +#define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */ + +extern const char *CardType[]; + +const char sct_quadro_revision[] = "$Revision: 1.7 $"; + +/* To survive the startup phase */ +typedef struct { + u_int active; /* true/false */ + u_int base; /* ipac base address */ +} IPAC_STATE; + +static IPAC_STATE ipac_state[4 + 1] __initdata = +{ + {0, 0}, /* dummy */ + {0, 0}, /* SCT_1 */ + {0, 0}, /* SCT_2 */ + {0, 0}, /* SCT_3 */ + {0, 0} /* SCT_4 */ +}; + +static const char *sct_quadro_subtypes[] = +{ + "", + "#1", + "#2", + "#3", + "#4" +}; + + +#define wordout(addr,val) outw(val,addr) +#define wordin(addr) inw(addr) + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + save_flags(flags); + cli(); + wordout(ale, off); + ret = wordin(adr) & 0xFF; + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + int i; + wordout(ale, off); + for (i = 0; i < size; i++) + data[i] = wordin(adr) & 0xFF; +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + save_flags(flags); + cli(); + wordout(ale, off); + wordout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + int i; + wordout(ale, off); + for (i = 0; i < size; i++) + wordout(adr, data[i]); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size); +} + + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value); +} + +/* Check whether the specified ipac is already active or not */ +static int +is_ipac_active(u_int ipac_nr) +{ + return (ipac_state[ipac_nr].active); +} + +/* Set the specific ipac to active */ +static void +set_ipac_active(u_int ipac_nr, u_int active) +{ + /* set activation state */ + ipac_state[ipac_nr].active = active; +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.ax.base, \ + cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ax.base, \ + cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0), data) +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.base, \ + cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.base, \ + cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +bkm_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista, val, icnt = 5; + int i; + if (!cs) { + printk(KERN_WARNING "HiSax: Scitel Quadro: Spurious interrupt!\n"); + return; + } + ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA); + + Start_IPAC: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) { + hscx_int_main(cs, val); + } + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.ax.base, cs->hw.ax.data_adr, ISAC_ISTA | 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "HiSax: %s (%s) IRQ LOOP\n", + CardType[cs->typ], + sct_quadro_subtypes[cs->subtyp]); + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF); + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0); + + /* Read out all interrupt sources from currently not active ipacs */ + /* "Handle" all interrupts from currently not active ipac by reading the regs */ + for (i = SCT_1; i <= SCT_4; i++) + if (!is_ipac_active(i)) { + u_int base = ipac_state[i].base; + if (readreg(base, base + 4, 0xC1)) { + readreg(base, base + 4, 0xA0); + readreg(base, base + 4, 0xA4); + readreg(base, base + 4, 0x20); + readreg(base, base + 4, 0x24); + readreg(base, base + 4, 0x60); + readreg(base, base + 4, 0x64); + readreg(base, base + 4, 0xC1); + readreg(base, base + 4, ISAC_CIR0 + 0x80); + } + } +} + + +void +release_io_sct_quadro(struct IsdnCardState *cs) +{ + /* ?? */ +} + +static void +enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable) +{ + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + if (bEnable) + wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41)); + else + /* Issue general di only if no ipac is active */ + if (!is_ipac_active(SCT_1) && + !is_ipac_active(SCT_2) && + !is_ipac_active(SCT_3) && + !is_ipac_active(SCT_4)) + wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41)); + } +} + +static void +reset_bkm(struct IsdnCardState *cs) +{ + long flags; + + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + if (!is_ipac_active(SCT_1) && + !is_ipac_active(SCT_2) && + !is_ipac_active(SCT_3) && + !is_ipac_active(SCT_4)) { + /* Issue total reset only if no ipac is active */ + wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4)); + + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + + /* Remove the soft reset */ + wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4)); + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + restore_flags(flags); + } + } +} + +static int +BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + /* Disable ints */ + set_ipac_active(cs->subtyp, 0); + enable_bkm_int(cs, 0); + reset_bkm(cs); + return (0); + case CARD_RELEASE: + /* Sanity */ + set_ipac_active(cs->subtyp, 0); + enable_bkm_int(cs, 0); + reset_bkm(cs); + release_io_sct_quadro(cs); + return (0); + case CARD_INIT: + cs->debug |= L1_DEB_IPAC; + set_ipac_active(cs->subtyp, 1); + inithscxisac(cs, 3); + /* Enable ints */ + enable_bkm_int(cs, 1); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_a8 __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +int __init + setup_sct_quadro(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; +#if CONFIG_PCI + u_char pci_bus = 0, pci_device_fn = 0, pci_irq = 0, pci_rev_id; + u_int found = 0; + u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5; +#endif + + strcpy(tmp, sct_quadro_revision); + printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + cs->subtyp = SCT_1; /* Preset */ + } else + return (0); + + /* Identify subtype by para[0] */ + if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4) + cs->subtyp = card->para[0]; + else + printk(KERN_WARNING "HiSax: %s: Invalid subcontroller in configuration, default to 1\n", + CardType[card->typ]); +#if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "bkm_a4t: no PCI bus present\n"); + return (0); + } + if ((dev_a8 = pci_find_device(PLX_VENDOR_ID, PLX_DEVICE_ID, dev_a8))) { + u_int sub_sys_id = 0; + + pci_read_config_dword(dev_a8, PCI_SUBSYSTEM_VENDOR_ID, + &sub_sys_id); + if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) { + found = 1; + pci_ioaddr1 = get_pcibase(dev_a8, 1); + pci_irq = dev_a8->irq; + pci_bus = dev_a8->bus->number; + pci_device_fn = dev_a8->devfn; + } + } +#else + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device( + PLX_VENDOR_ID, + PLX_DEVICE_ID, + pci_index, + &pci_bus, + &pci_device_fn) == PCIBIOS_SUCCESSFUL) { + + u_int sub_sys_id = 0; + + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_SUBSYSTEM_VENDOR_ID, &sub_sys_id); + if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) { + found = 1; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr1); + cs->irq = pci_irq; + break; + } + } + } +#endif /* COMPAT_HAS_NEW_PCI */ + if (!found) { + printk(KERN_WARNING "HiSax: %s (%s): Card not found\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } +#ifndef COMPAT_HAS_NEW_PCI + pci_index++; /* need for more as one card */ +#endif + if (!pci_irq) { /* IRQ range check ?? */ + printk(KERN_WARNING "HiSax: %s (%s): No IRQ\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } +#ifdef ATTEMPT_PCI_REMAPPING +/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */ + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_REVISION_ID, &pci_rev_id); + if ((pci_ioaddr1 & 0x80) && (pci_rev_id == 1)) { + printk(KERN_WARNING "HiSax: %s (%s): PLX rev 1, remapping required!\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + /* Restart PCI negotiation */ + pcibios_write_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, (u_int) - 1); + /* Move up by 0x80 byte */ + pci_ioaddr1 += 0x80; + pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_write_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, pci_ioaddr1); +#ifdef COMPAT_HAS_NEW_PCI + get_pcibase(dev_a8, 1) = pci_ioaddr1; +#endif /* COMPAT_HAS_NEW_PCI */ + } +/* End HACK */ +#endif + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_ioaddr1); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_ioaddr2); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_ioaddr3); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_4, &pci_ioaddr4); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_5, &pci_ioaddr5); + if (!pci_ioaddr1 || !pci_ioaddr2 || !pci_ioaddr3 || !pci_ioaddr4 || !pci_ioaddr5) { + printk(KERN_WARNING "HiSax: %s (%s): No IO base address(es)\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } + pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr2 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr3 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr4 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr5 &= PCI_BASE_ADDRESS_IO_MASK; + /* Take over */ + cs->irq = pci_irq; + cs->irq_flags |= SA_SHIRQ; + /* pci_ioaddr1 is unique to all subdevices */ + /* pci_ioaddr2 is for the fourth subdevice only */ + /* pci_ioaddr3 is for the third subdevice only */ + /* pci_ioaddr4 is for the second subdevice only */ + /* pci_ioaddr5 is for the first subdevice only */ + cs->hw.ax.plx_adr = pci_ioaddr1; + /* Enter all ipac_base addresses */ + ipac_state[SCT_1].base = pci_ioaddr5 + 0x00; + ipac_state[SCT_2].base = pci_ioaddr4 + 0x08; + ipac_state[SCT_3].base = pci_ioaddr3 + 0x10; + ipac_state[SCT_4].base = pci_ioaddr2 + 0x20; + /* For isac and hscx control path */ + cs->hw.ax.base = ipac_state[cs->subtyp].base; + /* For isac and hscx data path */ + cs->hw.ax.data_adr = cs->hw.ax.base + 4; +#else + printk(KERN_WARNING "HiSax: %s (%s): NO_PCI_BIOS\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + printk(KERN_WARNING "HiSax: %s (%s): Unable to configure\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO "HiSax: %s (%s) configured at 0x%.4X, 0x%.4X, 0x%.4X and IRQ %d\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp], + cs->hw.ax.plx_adr, + cs->hw.ax.base, + cs->hw.ax.data_adr, + cs->irq); + + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + + /* Disable all currently not active ipacs */ + if (!is_ipac_active(SCT_1)) + set_ipac_active(SCT_1, 0); + if (!is_ipac_active(SCT_2)) + set_ipac_active(SCT_2, 0); + if (!is_ipac_active(SCT_3)) + set_ipac_active(SCT_3, 0); + if (!is_ipac_active(SCT_4)) + set_ipac_active(SCT_4, 0); + + /* Perfom general reset (if possible) */ + reset_bkm(cs); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &BKM_card_msg; + cs->irq_func = &bkm_interrupt_ipac; + + printk(KERN_INFO "HiSax: %s (%s): IPAC Version %d\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp], + readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID)); + return (1); +} diff --git a/drivers/isdn/hisax/bkm_ax.h b/drivers/isdn/hisax/bkm_ax.h new file mode 100644 index 000000000..e2d5fa70e --- /dev/null +++ b/drivers/isdn/hisax/bkm_ax.h @@ -0,0 +1,133 @@ +/* $Id: bkm_ax.h,v 1.2 1999/07/01 08:07:55 keil Exp $ + * bkm_ax.h low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: bkm_ax.h,v $ + * Revision 1.2 1999/07/01 08:07:55 keil + * Initial version + * + * + * + */ + +#ifndef __BKM_AX_H__ +#define __BKM_AX_H__ + +/* Supported boards (subtypes) */ +#define SCT_1 1 +#define SCT_2 2 +#define SCT_3 3 +#define SCT_4 4 +#define BKM_A4T 5 + + +/* A4T */ +#define I20_DEVICE_ID 0x6120 /* I20 PCI device ID */ +#define I20_VENDOR_ID 0x11DE /* I20 PCI vendor ID */ +#define A4T_SUBVEN_ID 0x0871 +#define A4T_SUBSYS_ID 0xFFA4 +/* Scitel Quadro */ +#define PLX_DEVICE_ID 0x9050 /* Scitel Quadro PLX */ +#define PLX_VENDOR_ID 0x10B5 +#define SCT_SUBVEN_ID 0x0871 +#define SCT_SUBSYS_ID 0xFFA8 + + +#define PLX_ADDR_PLX 0x14 /* Addr PLX configuration */ +#define PLX_ADDR_ISAC 0x18 /* Addr ISAC */ +#define PLX_ADDR_HSCX 0x1C /* Addr HSCX */ +#define PLX_ADDR_ALE 0x20 /* Addr ALE */ +#define PLX_ADDR_ALEPLUS 0x24 /* Next Addr behind ALE */ + +#define PLX_SUBVEN 0x2C /* Offset SubVendor */ +#define PLX_SUBSYS 0x2E /* Offset SubSystem */ + + +/* Application specific registers I20 (Siemens SZB6120H) */ +typedef struct { + /* Video front end horizontal configuration register */ + volatile u_int i20VFEHorzCfg; /* Offset 00 */ + /* Video front end vertical configuration register */ + volatile u_int i20VFEVertCfg; /* Offset 04 */ + /* Video front end scaler and pixel format register */ + volatile u_int i20VFEScaler; /* Offset 08 */ + /* Video display top register */ + volatile u_int i20VDispTop; /* Offset 0C */ + /* Video display bottom register */ + volatile u_int i20VDispBottom; /* Offset 10 */ + /* Video stride, status and frame grab register */ + volatile u_int i20VidFrameGrab;/* Offset 14 */ + /* Video display configuration register */ + volatile u_int i20VDispCfg; /* Offset 18 */ + /* Video masking map top */ + volatile u_int i20VMaskTop; /* Offset 1C */ + /* Video masking map bottom */ + volatile u_int i20VMaskBottom; /* Offset 20 */ + /* Overlay control register */ + volatile u_int i20OvlyControl; /* Offset 24 */ + /* System, PCI and general purpose pins control register */ + volatile u_int i20SysControl; /* Offset 28 */ +#define sysRESET 0x01000000 /* bit 24:Softreset (Low) */ + /* GPIO 4...0: Output fixed for our cfg! */ +#define sysCFG 0x000000E0 /* GPIO 7,6,5: Input */ + /* General purpose pins and guest bus control register */ + volatile u_int i20GuestControl;/* Offset 2C */ +#define guestWAIT_CFG 0x00005555 /* 4 PCI waits for all */ +#define guestISDN_INT_E 0x01000000 /* ISDN Int en (low) */ +#define guestVID_INT_E 0x02000000 /* Video interrupt en (low) */ +#define guestADI1_INT_R 0x04000000 /* ADI #1 int req (low) */ +#define guestADI2_INT_R 0x08000000 /* ADI #2 int req (low) */ +#define guestISDN_RES 0x10000000 /* ISDN reset bit (high) */ +#define guestADI1_INT_S 0x20000000 /* ADI #1 int pending (low) */ +#define guestADI2_INT_S 0x40000000 /* ADI #2 int pending (low) */ +#define guestISDN_INT_S 0x80000000 /* ISAC int pending (low) */ + +#define g_A4T_JADE_RES 0x01000000 /* JADE Reset (High) */ +#define g_A4T_ISAR_RES 0x02000000 /* ISAR Reset (High) */ +#define g_A4T_ISAC_RES 0x04000000 /* ISAC Reset (High) */ +#define g_A4T_JADE_BOOTR 0x08000000 /* JADE enable boot SRAM (Low) NOT USED */ +#define g_A4T_ISAR_BOOTR 0x10000000 /* ISAR enable boot SRAM (Low) NOT USED */ +#define g_A4T_JADE_INT_S 0x20000000 /* JADE interrupt pnd (Low) */ +#define g_A4T_ISAR_INT_S 0x40000000 /* ISAR interrupt pnd (Low) */ +#define g_A4T_ISAC_INT_S 0x80000000 /* ISAC interrupt pnd (Low) */ + + volatile u_int i20CodeSource; /* Offset 30 */ + volatile u_int i20CodeXferCtrl;/* Offset 34 */ + volatile u_int i20CodeMemPtr; /* Offset 38 */ + + volatile u_int i20IntStatus; /* Offset 3C */ + volatile u_int i20IntCtrl; /* Offset 40 */ +#define intISDN 0x40000000 /* GIRQ1En (ISAC/ADI) (High) */ +#define intVID 0x20000000 /* GIRQ0En (VSYNC) (High) */ +#define intCOD 0x10000000 /* CodRepIrqEn (High) */ +#define intPCI 0x01000000 /* PCI IntA enable (High) */ + + volatile u_int i20I2CCtrl; /* Offset 44 */ +} I20_REGISTER_FILE, *PI20_REGISTER_FILE; + +/* + * Postoffice structure for A4T + * + */ +#define PO_OFFSET 0x00000200 /* Postoffice offset from base */ + +#define GCS_0 0x00000000 /* Guest bus chip selects */ +#define GCS_1 0x00100000 +#define GCS_2 0x00200000 +#define GCS_3 0x00300000 + +#define PO_READ 0x00000000 /* R/W from/to guest bus */ +#define PO_WRITE 0x00800000 + +#define PO_PEND 0x02000000 + +#define POSTOFFICE(postoffice) *(volatile unsigned int*)(postoffice) + +/* Wait unlimited (don't worry) */ +#define __WAITI20__(postoffice) \ +do { \ + while ((POSTOFFICE(postoffice) & PO_PEND)) ; \ +} while (0) + +#endif /* __BKM_AX_H__ */ diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index bf09acd20..ce0428a46 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 2.25 1999/01/02 11:17:20 keil Exp $ +/* $Id: callc.c,v 2.34 1999/08/25 20:02:34 werner Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,39 @@ * Fritz Elfert * * $Log: callc.c,v $ + * Revision 2.34 1999/08/25 20:02:34 werner + * Changed return values for stat_icall(w) from 3->4 and 4->5 because of conflicts + * with existing software definitions. (PtP incomplete called party number) + * + * Revision 2.33 1999/08/25 17:00:09 keil + * Make ISAR V32bis modem running + * Make LL->HL interface open for additional commands + * + * Revision 2.32 1999/08/22 20:27:01 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 2.31 1999/08/05 20:43:10 keil + * ISAR analog modem support + * + * Revision 2.30 1999/07/25 16:24:04 keil + * Fixed TEI now working again + * + * Revision 2.29 1999/07/13 21:05:41 werner + * Modified set_channel_limit to use new callback ISDN_STAT_DISCH. + * + * Revision 2.28 1999/07/09 08:30:02 keil + * cosmetics + * + * Revision 2.27 1999/07/05 23:51:38 werner + * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl + * hisaxctrl id 10 <nr. of chans 0-2> + * + * Revision 2.26 1999/07/01 08:11:21 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.25 1999/01/02 11:17:20 keil * Changes for 2.2 * @@ -112,10 +145,15 @@ #include "../avmb1/capicmd.h" /* this should be moved in a common place */ #ifdef MODULE +#ifdef COMPAT_HAS_NEW_SYMTAB #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module)) -#endif /* MODULE */ +#else +extern long mod_use_count_; +#define MOD_USE_COUNT mod_use_count_ +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* MODULE */ -const char *lli_revision = "$Revision: 2.25 $"; +const char *lli_revision = "$Revision: 2.34 $"; extern struct IsdnCard cards[]; extern int nrcards; @@ -146,28 +184,7 @@ static int chancount = 0; /* Flags for remembering action done in lli */ -#define FLG_START_D 0 -#define FLG_ESTAB_D 1 -#define FLG_CALL_SEND 2 -#define FLG_CALL_REC 3 -#define FLG_CALL_ALERT 4 -#define FLG_START_B 5 -#define FLG_CONNECT_B 6 -#define FLG_LL_DCONN 7 -#define FLG_LL_BCONN 8 -#define FLG_DISC_SEND 9 -#define FLG_DISC_REC 10 -#define FLG_REL_REC 11 -#define FLG_DO_ALERT 12 -#define FLG_DO_HANGUP 13 -#define FLG_DO_CONNECT 14 -#define FLG_DO_ESTAB 15 -#define FLG_RESUME 16 - -/* - * Because of callback it's a good idea to delay the shutdown of the d-channel - */ -#define DREL_TIMER_VALUE 40000 +#define FLG_START_B 0 /* * Find card with given driverId @@ -192,7 +209,7 @@ discard_queue(struct sk_buff_head *q) int ret=0; while ((skb = skb_dequeue(q))) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); ret++; } return(ret); @@ -211,44 +228,40 @@ link_debug(struct Channel *chanp, int direction, char *fmt, ...) va_end(args); } - enum { - ST_NULL, /* 0 inactive */ - ST_OUT_WAIT_D, /* 1 outgoing, awaiting d-channel establishment */ - ST_IN_WAIT_D, /* 2 incoming, awaiting d-channel establishment */ - ST_OUT_DIAL, /* 3 outgoing, SETUP send; awaiting confirm */ - ST_IN_WAIT_LL, /* 4 incoming call received; wait for LL confirm */ - ST_IN_ALERT_SEND, /* 5 incoming call received; ALERT send */ - ST_IN_WAIT_CONN_ACK, /* 6 incoming CONNECT send; awaiting CONN_ACK */ - ST_WAIT_BCONN, /* 7 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */ - ST_ACTIVE, /* 8 active, b channel prot. established */ - ST_WAIT_BRELEASE, /* 9 call clear. (initiator), awaiting b channel prot. rel. */ - ST_WAIT_BREL_DISC, /* 10 call clear. (receiver), DISCONNECT req. received */ - ST_WAIT_DCOMMAND, /* 11 call clear. (receiver), awaiting DCHANNEL message */ - ST_WAIT_DRELEASE, /* 12 DISCONNECT sent, awaiting RELEASE */ - ST_WAIT_D_REL_CNF, /* 13 RELEASE sent, awaiting RELEASE confirm */ - ST_WAIT_DSHUTDOWN, /* 14 awaiting d-channel shutdown */ + ST_NULL, /* 0 inactive */ + ST_OUT_DIAL, /* 1 outgoing, SETUP send; awaiting confirm */ + ST_IN_WAIT_LL, /* 2 incoming call received; wait for LL confirm */ + ST_IN_ALERT_SENT, /* 3 incoming call received; ALERT send */ + ST_IN_WAIT_CONN_ACK, /* 4 incoming CONNECT send; awaiting CONN_ACK */ + ST_WAIT_BCONN, /* 5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */ + ST_ACTIVE, /* 6 active, b channel prot. established */ + ST_WAIT_BRELEASE, /* 7 call clear. (initiator), awaiting b channel prot. rel. */ + ST_WAIT_BREL_DISC, /* 8 call clear. (receiver), DISCONNECT req. received */ + ST_WAIT_DCOMMAND, /* 9 call clear. (receiver), awaiting DCHANNEL message */ + ST_WAIT_DRELEASE, /* 10 DISCONNECT sent, awaiting RELEASE */ + ST_WAIT_D_REL_CNF, /* 11 RELEASE sent, awaiting RELEASE confirm */ + ST_IN_PROCEED_SEND, /* 12 incoming call, proceeding send */ }; - -#define STATE_COUNT (ST_WAIT_DSHUTDOWN +1) - -static char *strState[] = -{ - "ST_NULL", - "ST_OUT_WAIT_D", - "ST_IN_WAIT_D", - "ST_OUT_DIAL", - "ST_IN_WAIT_LL", - "ST_IN_ALERT_SEND", - "ST_IN_WAIT_CONN_ACK", - "ST_WAIT_BCONN", - "ST_ACTIVE", + + +#define STATE_COUNT (ST_IN_PROCEED_SEND + 1) + + static char *strState[] = + { + "ST_NULL", + "ST_OUT_DIAL", + "ST_IN_WAIT_LL", + "ST_IN_ALERT_SENT", + "ST_IN_WAIT_CONN_ACK", + "ST_WAIT_BCONN", + "ST_ACTIVE", "ST_WAIT_BRELEASE", "ST_WAIT_BREL_DISC", "ST_WAIT_DCOMMAND", "ST_WAIT_DRELEASE", "ST_WAIT_D_REL_CNF", - "ST_WAIT_DSHUTDOWN", + "ST_IN_PROCEED_SEND", }; enum { @@ -256,29 +269,28 @@ enum { EV_SETUP_CNF, /* 1 */ EV_ACCEPTB, /* 2 */ EV_DISCONNECT_IND, /* 3 */ - EV_RELEASE_CNF, /* 4 */ - EV_DLEST, /* 5 */ - EV_DLRL, /* 6 */ + EV_RELEASE, /* 4 */ + EV_LEASED, /* 5 */ + EV_LEASED_REL, /* 6 */ EV_SETUP_IND, /* 7 */ - EV_RELEASE_IND, /* 8 */ - EV_ACCEPTD, /* 9 */ - EV_SETUP_CMPL_IND, /* 10 */ - EV_BC_EST, /* 11 */ - EV_WRITEBUF, /* 12 */ - EV_ESTABLISH, /* 13 */ - EV_HANGUP, /* 14 */ - EV_BC_REL, /* 15 */ - EV_CINF, /* 16 */ - EV_SUSPEND, /* 17 */ - EV_RESUME, /* 18 */ - EV_SHUTDOWN_D, /* 19 */ - EV_NOSETUP_RSP, /* 20 */ - EV_SETUP_ERR, /* 21 */ - EV_CONNECT_ERR, /* 22 */ - EV_RELEASE_ERR, /* 23 */ + EV_ACCEPTD, /* 8 */ + EV_SETUP_CMPL_IND, /* 9 */ + EV_BC_EST, /* 10 */ + EV_WRITEBUF, /* 11 */ + EV_HANGUP, /* 12 */ + EV_BC_REL, /* 13 */ + EV_CINF, /* 14 */ + EV_SUSPEND, /* 15 */ + EV_RESUME, /* 16 */ + EV_NOSETUP_RSP, /* 17 */ + EV_SETUP_ERR, /* 18 */ + EV_CONNECT_ERR, /* 19 */ + EV_PROCEED, /* 20 */ + EV_ALERT, /* 21 */ + EV_REDIR, /* 22 */ }; -#define EVENT_COUNT (EV_RELEASE_ERR +1) +#define EVENT_COUNT (EV_REDIR + 1) static char *strEvent[] = { @@ -286,150 +298,152 @@ static char *strEvent[] = "EV_SETUP_CNF", "EV_ACCEPTB", "EV_DISCONNECT_IND", - "EV_RELEASE_CNF", - "EV_DLEST", - "EV_DLRL", + "EV_RELEASE", + "EV_LEASED", + "EV_LEASED_REL", "EV_SETUP_IND", - "EV_RELEASE_IND", "EV_ACCEPTD", "EV_SETUP_CMPL_IND", "EV_BC_EST", "EV_WRITEBUF", - "EV_ESTABLISH", "EV_HANGUP", "EV_BC_REL", "EV_CINF", "EV_SUSPEND", "EV_RESUME", - "EV_SHUTDOWN_D", "EV_NOSETUP_RSP", "EV_SETUP_ERR", "EV_CONNECT_ERR", - "EV_RELEASE_ERR", + "EV_PROCEED", + "EV_ALERT", + "EV_REDIR", }; + +static inline void +HL_LL(struct Channel *chanp, int command) +{ + isdn_ctrl ic; + + ic.driver = chanp->cs->myid; + ic.command = command; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); +} + static inline void -lli_deliver_cause(struct Channel *chanp, isdn_ctrl *ic) +lli_deliver_cause(struct Channel *chanp) { - if (chanp->proc->para.cause < 0) + isdn_ctrl ic; + + if (chanp->proc->para.cause == NO_CAUSE) return; - ic->driver = chanp->cs->myid; - ic->command = ISDN_STAT_CAUSE; - ic->arg = chanp->chan; + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; if (chanp->cs->protocol == ISDN_PTYPE_EURO) - sprintf(ic->parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f, + sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f, chanp->proc->para.cause & 0x7f); else - sprintf(ic->parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f, + sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f, chanp->proc->para.cause & 0x7f); - chanp->cs->iif.statcallb(ic); + chanp->cs->iif.statcallb(&ic); } -static void -lli_d_established(struct FsmInst *fi, int event, void *arg) +static inline void +lli_close(struct FsmInst *fi) { - struct Channel *chanp = fi->userdata; - - test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); - if (chanp->leased) { - isdn_ctrl ic; - int ret; + struct Channel *chanp = fi->userdata; - chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); - FsmChangeState(fi, ST_IN_WAIT_LL); - test_and_set_bit(FLG_CALL_REC, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_ICALL_LEASED"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_ICALL; - ic.arg = chanp->chan; - ic.parm.setup.si1 = 7; - ic.parm.setup.si2 = 0; - ic.parm.setup.plan = 0; - ic.parm.setup.screen = 0; - sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); - sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid); - ret = chanp->cs->iif.statcallb(&ic); - if (chanp->debug & 1) - link_debug(chanp, 1, "statcallb ret=%d", ret); - if (!ret) { - chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); - FsmChangeState(fi, ST_NULL); - } - } else if (fi->state == ST_WAIT_DSHUTDOWN) - FsmChangeState(fi, ST_NULL); + FsmChangeState(fi, ST_NULL); + chanp->Flags = 0; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } -static void -lli_d_released(struct FsmInst *fi, int event, void *arg) + static void +lli_leased_in(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - test_and_clear_bit(FLG_START_D, &chanp->Flags); + struct Channel *chanp = fi->userdata; + + isdn_ctrl ic; + int ret; + + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_IN_WAIT_LL); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_ICALL_LEASED"); + ic.driver = chanp->cs->myid; + ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW); + ic.arg = chanp->chan; + ic.parm.setup.si1 = 7; + ic.parm.setup.si2 = 0; + ic.parm.setup.plan = 0; + ic.parm.setup.screen = 0; + sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); + sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid); + ret = chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, 1, "statcallb ret=%d", ret); + + if (!ret) { + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_NULL); + } } + /* * Dial out */ static void +lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + FsmChangeState(fi, ST_WAIT_BCONN); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DCONN"); + HL_LL(chanp, ISDN_STAT_DCONN); + init_b_st(chanp, 0); + chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); +} + +static void lli_prep_dialout(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_OUT_WAIT_D); FsmDelTimer(&chanp->drel_timer, 60); FsmDelTimer(&chanp->dial_timer, 73); chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = 0; - if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { - FsmEvent(fi, EV_DLEST, NULL); - } else { - chanp->Flags = 0; - if (EV_RESUME == event) - test_and_set_bit(FLG_RESUME, &chanp->Flags); - test_and_set_bit(FLG_START_D, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); - } -} -static void -lli_do_dialout(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - int ev; - - FsmChangeState(fi, ST_OUT_DIAL); - chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); - if (test_and_clear_bit(FLG_RESUME, &chanp->Flags)) - ev = CC_RESUME | REQUEST; - else - ev = CC_SETUP | REQUEST; - if (chanp->leased) { - FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); - } else { - test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, ev, chanp); - test_and_set_bit(FLG_CALL_SEND, &chanp->Flags); - } + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + if (chanp->leased) { + lli_init_bchan_out(fi, event, arg); + } else { + FsmChangeState(fi, ST_OUT_DIAL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp); + } } static void -lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) +lli_resume(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - FsmChangeState(fi, ST_WAIT_BCONN); - test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DCONN"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DCONN; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - init_b_st(chanp, 0); - test_and_set_bit(FLG_START_B, &chanp->Flags); - chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); + FsmDelTimer(&chanp->drel_timer, 60); + FsmDelTimer(&chanp->dial_timer, 73); + chanp->l2_active_protocol = chanp->l2_protocol; + chanp->incoming = 0; + + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + if (chanp->leased) { + lli_init_bchan_out(fi, event, arg); + } else { + FsmChangeState(fi, ST_OUT_DIAL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp); + } } static void @@ -438,12 +452,15 @@ lli_go_active(struct FsmInst *fi, int event, void *arg) struct Channel *chanp = fi->userdata; isdn_ctrl ic; + FsmChangeState(fi, ST_ACTIVE); chanp->data_open = !0; - test_and_set_bit(FLG_CONNECT_B, &chanp->Flags); + if (chanp->bcs->conmsg) + strcpy(ic.parm.num, chanp->bcs->conmsg); + else + ic.parm.num[0] = 0; if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BCONN"); - test_and_set_bit(FLG_LL_BCONN, &chanp->Flags); + link_debug(chanp, 0, "STAT_BCONN %s", ic.parm.num); ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BCONN; ic.arg = chanp->chan; @@ -451,6 +468,7 @@ lli_go_active(struct FsmInst *fi, int event, void *arg) chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan); } + /* * RESUME */ @@ -458,27 +476,6 @@ lli_go_active(struct FsmInst *fi, int event, void *arg) /* incomming call */ static void -lli_start_dchan(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_IN_WAIT_D); - FsmDelTimer(&chanp->drel_timer, 61); - if (event == EV_ACCEPTD) - test_and_set_bit(FLG_DO_CONNECT, &chanp->Flags); - else if (event == EV_HANGUP) { - test_and_set_bit(FLG_DO_HANGUP, &chanp->Flags); -#ifdef ALERT_REJECT - test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); -#endif - } - if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { - FsmEvent(fi, EV_DLEST, NULL); - } else if (!test_and_set_bit(FLG_START_D, &chanp->Flags)) - chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); -} - -static void lli_deliver_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; @@ -493,11 +490,11 @@ lli_deliver_call(struct FsmInst *fi, int event, void *arg) */ if (1) { /* for only one TEI */ FsmChangeState(fi, ST_IN_WAIT_LL); - test_and_set_bit(FLG_CALL_REC, &chanp->Flags); if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_ICALL"); + link_debug(chanp, 0, (chanp->chan < 2) ? "STAT_ICALL" : "STAT_ICALLW"); ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_ICALL; + ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW); + ic.arg = chanp->chan; /* * No need to return "unknown" for calls without OAD, @@ -507,118 +504,95 @@ lli_deliver_call(struct FsmInst *fi, int event, void *arg) ret = chanp->cs->iif.statcallb(&ic); if (chanp->debug & 1) link_debug(chanp, 1, "statcallb ret=%d", ret); + switch (ret) { - case 1: /* OK, anybody likes this call */ + case 1: /* OK, someone likes this call */ FsmDelTimer(&chanp->drel_timer, 61); - if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { - FsmChangeState(fi, ST_IN_ALERT_SEND); - test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); - } else { - test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); - FsmChangeState(fi, ST_IN_WAIT_D); - test_and_set_bit(FLG_START_D, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, - DL_ESTABLISH | REQUEST, NULL); - } + FsmChangeState(fi, ST_IN_ALERT_SENT); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + break; + case 5: /* direct redirect */ + case 4: /* Proceeding desired */ + FsmDelTimer(&chanp->drel_timer, 61); + FsmChangeState(fi, ST_IN_PROCEED_SEND); + chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc); + if (ret == 5) + { chanp->setup = ic.parm.setup; + chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); + } break; case 2: /* Rejecting Call */ - test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); break; case 0: /* OK, nobody likes this call */ default: /* statcallb problems */ chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); FsmChangeState(fi, ST_NULL); - if (test_bit(FLG_ESTAB_D, &chanp->Flags) && - !test_bit(FLG_PTP, &chanp->d_st->l2.flag)) - FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); break; } } else { chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); - FsmChangeState(fi, ST_NULL); - if (test_bit(FLG_ESTAB_D, &chanp->Flags) && - !test_bit(FLG_PTP, &chanp->d_st->l2.flag)) - FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); } } static void -lli_establish_d(struct FsmInst *fi, int event, void *arg) +lli_send_dconnect(struct FsmInst *fi, int event, void *arg) { - /* This establish the D-channel for pending L3 messages - * without blocking the channel - */ struct Channel *chanp = fi->userdata; - test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags); - FsmChangeState(fi, ST_IN_WAIT_D); - test_and_set_bit(FLG_START_D, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); } static void -lli_do_action(struct FsmInst *fi, int event, void *arg) +lli_send_alert(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); - if (chanp->leased) { - FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); - test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); - test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags); - FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); - } else if (test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags) && - !test_bit(FLG_DO_HANGUP, &chanp->Flags)) { - FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); - test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); - } else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) { - if (test_bit(FLG_DO_HANGUP, &chanp->Flags)) - FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); - FsmChangeState(fi, ST_IN_ALERT_SEND); - test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); - } else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) { - FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->proc->para.cause = 0x15; /* Call Rejected */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); - test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); - } + FsmChangeState(fi, ST_IN_ALERT_SENT); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); } static void -lli_send_dconnect(struct FsmInst *fi, int event, void *arg) +lli_send_redir(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); - chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); } static void lli_init_bchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DCONN"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DCONN; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); + HL_LL(chanp, ISDN_STAT_DCONN); chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = !0; init_b_st(chanp, !0); - test_and_set_bit(FLG_START_B, &chanp->Flags); chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); } +static void +lli_setup_rsp(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + if (chanp->leased) { + lli_init_bchan_in(fi, event, arg); + } else { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); +#ifdef WANT_ALERT + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); +#endif + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + } +} + /* Call suspend */ static void @@ -632,82 +606,52 @@ lli_suspend(struct FsmInst *fi, int event, void *arg) /* Call clearing */ static void -lli_cancel_call(struct FsmInst *fi, int event, void *arg) +lli_disconnect_req(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); - test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } static void -lli_shutdown_d(struct FsmInst *fi, int event, void *arg) +lli_disconnect_reject(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - FsmDelTimer(&chanp->drel_timer, 62); - if (test_bit(FLG_PTP, &chanp->d_st->l2.flag)) { - FsmChangeState(fi, ST_NULL); - } else { - if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) { - if (chanp->chan) { - if (chanp->cs->channel[0].fi.state != ST_NULL) - return; - } else { - if (chanp->cs->channel[1].fi.state != ST_NULL) - return; - } - } - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); - } + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); } static void -lli_timeout_d(struct FsmInst *fi, int event, void *arg) +lli_dhup_close(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - FsmChangeState(fi, ST_NULL); - chanp->Flags = 0; - test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); - if (!test_bit(FLG_PTP, &chanp->d_st->l2.flag)) - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); - chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + struct Channel *chanp = fi->userdata; + + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp); + HL_LL(chanp, ISDN_STAT_DHUP); + + lli_close(fi); } static void -lli_go_null(struct FsmInst *fi, int event, void *arg) +lli_reject_req(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_NULL); - chanp->Flags = 0; - FsmDelTimer(&chanp->drel_timer, 63); - chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + struct Channel *chanp = fi->userdata; + +#ifndef ALERT_REJECT + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); + lli_dhup_close(fi, event, arg); +#else + FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); + FsmChangeState(fi, ST_IN_ALERT_SENT); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); +#endif } static void @@ -717,30 +661,15 @@ lli_disconn_bchan(struct FsmInst *fi, int event, void *arg) chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_BRELEASE); - test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } static void -lli_send_d_disc(struct FsmInst *fi, int event, void *arg) +lli_start_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - if (test_bit(FLG_DISC_REC, &chanp->Flags) || - test_bit(FLG_REL_REC, &chanp->Flags)) - return; - FsmChangeState(fi, ST_WAIT_DRELEASE); - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); if (chanp->leased) { ic.command = ISDN_STAT_CAUSE; ic.arg = chanp->chan; @@ -748,43 +677,46 @@ lli_send_d_disc(struct FsmInst *fi, int event, void *arg) chanp->cs->iif.statcallb(&ic); if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + HL_LL(chanp, ISDN_STAT_DHUP); + lli_close(fi); } else { - if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) - chanp->proc->para.cause = 0x15; /* Call Reject */ - else - chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); - test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); + lli_disconnect_req(fi, event, arg); } } static void -lli_released_bchan(struct FsmInst *fi, int event, void *arg) +lli_rel_b_disc(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; + + release_b_st(chanp); + lli_start_disc(fi, event, arg); +} - FsmChangeState(fi, ST_WAIT_DCOMMAND); - chanp->data_open = 0; - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - release_b_st(chanp); - test_and_clear_bit(FLG_START_B, &chanp->Flags); +static void +lli_bhup_disc(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + + lli_rel_b_disc(fi, event, arg); } +static void +lli_bhup_rel_b(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + FsmChangeState(fi, ST_WAIT_DCOMMAND); + chanp->data_open = 0; + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + release_b_st(chanp); +} static void lli_release_bchan(struct FsmInst *fi, int event, void *arg) @@ -792,117 +724,74 @@ lli_release_bchan(struct FsmInst *fi, int event, void *arg) struct Channel *chanp = fi->userdata; chanp->data_open = 0; - test_and_set_bit(FLG_DISC_REC, &chanp->Flags); FsmChangeState(fi, ST_WAIT_BREL_DISC); - test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } + static void -lli_received_d_rel(struct FsmInst *fi, int event, void *arg) +lli_rel_b_dhup(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - FsmChangeState(fi, ST_NULL); - test_and_set_bit(FLG_REL_REC, &chanp->Flags); - if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); - } - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags); - test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); - test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); - lli_timeout_d(fi, event, arg); + release_b_st(chanp); + lli_dhup_close(fi, event, arg); } static void -lli_received_d_relcnf(struct FsmInst *fi, int event, void *arg) +lli_bhup_dhup(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - FsmChangeState(fi, ST_NULL); - if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); - } - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags); - test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); - test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); - lli_timeout_d(fi, event, arg); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + + lli_rel_b_dhup(fi, event, arg); } static void -lli_received_d_disc(struct FsmInst *fi, int event, void *arg) +lli_abort(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_D_REL_CNF); - test_and_set_bit(FLG_DISC_REC, &chanp->Flags); - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); - chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc); + chanp->data_open = 0; + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + + lli_bhup_dhup(fi, event, arg); +} + +static void +lli_release_req(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + FsmChangeState(fi, ST_WAIT_D_REL_CNF); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc); +} + +static void +lli_rel_b_release_req(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + release_b_st(chanp); + lli_release_req(fi, event, arg); +} + +static void +lli_bhup_release_req(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + + lli_rel_b_release_req(fi, event, arg); } + /* processing charge info */ static void lli_charge_info(struct FsmInst *fi, int event, void *arg) @@ -920,259 +809,150 @@ lli_charge_info(struct FsmInst *fi, int event, void *arg) /* error procedures */ static void -lli_no_dchan(struct FsmInst *fi, int event, void *arg) +lli_dchan_not_ready(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_NODCH"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_NODCH; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - chanp->Flags = 0; - FsmChangeState(fi, ST_NULL); - chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + link_debug(chanp, 0, "STAT_DHUP"); + HL_LL(chanp, ISDN_STAT_DHUP); } static void -lli_no_dchan_ready(struct FsmInst *fi, int event, void *arg) +lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); + HL_LL(chanp, ISDN_STAT_DHUP); + lli_close(fi); } static void -lli_no_dchan_in(struct FsmInst *fi, int event, void *arg) +lli_error(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc); - chanp->Flags = 0; - FsmChangeState(fi, ST_NULL); - chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + FsmChangeState(fi, ST_WAIT_DRELEASE); } static void -lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg) +lli_failure_l(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; - - FsmChangeState(fi, ST_NULL); - test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - lli_shutdown_d(fi, event, arg); + struct Channel *chanp = fi->userdata; + isdn_ctrl ic; + + FsmChangeState(fi, ST_NULL); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f); + chanp->cs->iif.statcallb(&ic); + HL_LL(chanp, ISDN_STAT_DHUP); + chanp->Flags = 0; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } static void -lli_setup_err(struct FsmInst *fi, int event, void *arg) +lli_rel_b_fail(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ + release_b_st(chanp); + lli_failure_l(fi, event, arg); } static void -lli_connect_err(struct FsmInst *fi, int event, void *arg) +lli_bhup_fail(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + + lli_rel_b_fail(fi, event, arg); } static void -lli_got_dlrl(struct FsmInst *fi, int event, void *arg) +lli_failure_a(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - FsmChangeState(fi, ST_NULL); - if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); - } - if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_BHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - } - if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) - release_b_st(chanp); - if (chanp->leased) { - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_CAUSE; - ic.arg = chanp->chan; - sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f); - chanp->cs->iif.statcallb(&ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - chanp->Flags = 0; - } else { - test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - if (chanp->cs->protocol == ISDN_PTYPE_EURO) { - chanp->proc->para.cause = 0x2f; - chanp->proc->para.loc = 0; - } else { - chanp->proc->para.cause = 0x70; - chanp->proc->para.loc = 0; - } - lli_deliver_cause(chanp, &ic); - ic.driver = chanp->cs->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&ic); - chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc); - chanp->Flags = 0; - chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); - } - chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); -} - -/* *INDENT-OFF* */ -static struct FsmNode fnlist[] HISAX_INITDATA = -{ - {ST_NULL, EV_DIAL, lli_prep_dialout}, - {ST_NULL, EV_RESUME, lli_prep_dialout}, - {ST_NULL, EV_SETUP_IND, lli_deliver_call}, - {ST_NULL, EV_SHUTDOWN_D, lli_shutdown_d}, - {ST_NULL, EV_DLRL, lli_go_null}, - {ST_NULL, EV_DLEST, lli_d_established}, - {ST_NULL, EV_ESTABLISH, lli_establish_d}, - {ST_OUT_WAIT_D, EV_DLEST, lli_do_dialout}, - {ST_OUT_WAIT_D, EV_DLRL, lli_no_dchan}, - {ST_OUT_WAIT_D, EV_HANGUP, lli_no_dchan}, - {ST_IN_WAIT_D, EV_DLEST, lli_do_action}, - {ST_IN_WAIT_D, EV_DLRL, lli_no_dchan_in}, - {ST_IN_WAIT_D, EV_ACCEPTD, lli_start_dchan}, - {ST_IN_WAIT_D, EV_HANGUP, lli_start_dchan}, - {ST_OUT_DIAL, EV_SETUP_CNF, lli_init_bchan_out}, - {ST_OUT_DIAL, EV_HANGUP, lli_cancel_call}, - {ST_OUT_DIAL, EV_DISCONNECT_IND, lli_received_d_disc}, - {ST_OUT_DIAL, EV_RELEASE_IND, lli_received_d_rel}, - {ST_OUT_DIAL, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_OUT_DIAL, EV_NOSETUP_RSP, lli_no_setup_rsp}, - {ST_OUT_DIAL, EV_SETUP_ERR, lli_setup_err}, - {ST_OUT_DIAL, EV_DLRL, lli_got_dlrl}, - {ST_IN_WAIT_LL, EV_DLEST, lli_d_established}, - {ST_IN_WAIT_LL, EV_DLRL, lli_d_released}, - {ST_IN_WAIT_LL, EV_ACCEPTD, lli_start_dchan}, - {ST_IN_WAIT_LL, EV_HANGUP, lli_start_dchan}, - {ST_IN_WAIT_LL, EV_DISCONNECT_IND, lli_received_d_disc}, - {ST_IN_WAIT_LL, EV_RELEASE_IND, lli_received_d_rel}, - {ST_IN_WAIT_LL, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_IN_ALERT_SEND, EV_SETUP_CMPL_IND, lli_init_bchan_in}, - {ST_IN_ALERT_SEND, EV_ACCEPTD, lli_send_dconnect}, - {ST_IN_ALERT_SEND, EV_HANGUP, lli_send_d_disc}, - {ST_IN_ALERT_SEND, EV_DISCONNECT_IND, lli_received_d_disc}, - {ST_IN_ALERT_SEND, EV_RELEASE_IND, lli_received_d_rel}, - {ST_IN_ALERT_SEND, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_IN_ALERT_SEND, EV_DLRL, lli_got_dlrl}, - {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, lli_init_bchan_in}, - {ST_IN_WAIT_CONN_ACK, EV_HANGUP, lli_send_d_disc}, - {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, lli_received_d_disc}, - {ST_IN_WAIT_CONN_ACK, EV_RELEASE_IND, lli_received_d_rel}, - {ST_IN_WAIT_CONN_ACK, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, lli_connect_err}, - {ST_IN_WAIT_CONN_ACK, EV_DLRL, lli_got_dlrl}, - {ST_WAIT_BCONN, EV_BC_EST, lli_go_active}, - {ST_WAIT_BCONN, EV_BC_REL, lli_send_d_disc}, - {ST_WAIT_BCONN, EV_HANGUP, lli_send_d_disc}, - {ST_WAIT_BCONN, EV_DISCONNECT_IND, lli_received_d_disc}, - {ST_WAIT_BCONN, EV_RELEASE_IND, lli_received_d_rel}, - {ST_WAIT_BCONN, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_WAIT_BCONN, EV_DLRL, lli_got_dlrl}, - {ST_WAIT_BCONN, EV_CINF, lli_charge_info}, - {ST_ACTIVE, EV_CINF, lli_charge_info}, - {ST_ACTIVE, EV_BC_REL, lli_released_bchan}, - {ST_ACTIVE, EV_SUSPEND, lli_suspend}, - {ST_ACTIVE, EV_HANGUP, lli_disconn_bchan}, - {ST_ACTIVE, EV_DISCONNECT_IND, lli_release_bchan}, - {ST_ACTIVE, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_ACTIVE, EV_RELEASE_IND, lli_received_d_rel}, - {ST_ACTIVE, EV_DLRL, lli_got_dlrl}, - {ST_WAIT_BRELEASE, EV_BC_REL, lli_send_d_disc}, - {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, lli_received_d_disc}, - {ST_WAIT_BRELEASE, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_WAIT_BRELEASE, EV_RELEASE_IND, lli_received_d_rel}, - {ST_WAIT_BRELEASE, EV_DLRL, lli_got_dlrl}, - {ST_WAIT_BREL_DISC, EV_BC_REL, lli_received_d_disc}, - {ST_WAIT_BREL_DISC, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_WAIT_BREL_DISC, EV_RELEASE_IND, lli_received_d_rel}, - {ST_WAIT_BREL_DISC, EV_DLRL, lli_got_dlrl}, - {ST_WAIT_DCOMMAND, EV_HANGUP, lli_send_d_disc}, - {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, lli_received_d_disc}, - {ST_WAIT_DCOMMAND, EV_RELEASE_CNF, lli_received_d_relcnf}, - {ST_WAIT_DCOMMAND, EV_RELEASE_IND, lli_received_d_rel}, - {ST_WAIT_DCOMMAND, EV_DLRL, lli_got_dlrl}, - {ST_WAIT_DRELEASE, EV_RELEASE_IND, lli_timeout_d}, - {ST_WAIT_DRELEASE, EV_RELEASE_CNF, lli_timeout_d}, - {ST_WAIT_DRELEASE, EV_RELEASE_ERR, lli_timeout_d}, - {ST_WAIT_DRELEASE, EV_DIAL, lli_no_dchan_ready}, - {ST_WAIT_DRELEASE, EV_DLRL, lli_got_dlrl}, - {ST_WAIT_D_REL_CNF, EV_RELEASE_CNF, lli_timeout_d}, - {ST_WAIT_D_REL_CNF, EV_RELEASE_ERR, lli_timeout_d}, -/* ETS 300-104 16.1 */ - {ST_WAIT_D_REL_CNF, EV_RELEASE_IND, lli_timeout_d}, - {ST_WAIT_D_REL_CNF, EV_DIAL, lli_no_dchan_ready}, - {ST_WAIT_D_REL_CNF, EV_DLRL, lli_got_dlrl}, - {ST_WAIT_DSHUTDOWN, EV_DLRL, lli_go_null}, - {ST_WAIT_DSHUTDOWN, EV_DLEST, lli_d_established}, - {ST_WAIT_DSHUTDOWN, EV_DIAL, lli_prep_dialout}, - {ST_WAIT_DSHUTDOWN, EV_RESUME, lli_prep_dialout}, - {ST_WAIT_DSHUTDOWN, EV_SETUP_IND, lli_deliver_call}, + chanp->data_open = 0; + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + + lli_bhup_fail(fi, event, arg); +} + + /* *INDENT-OFF* */ + static struct FsmNode fnlist[] HISAX_INITDATA = + { + {ST_NULL, EV_DIAL, lli_prep_dialout}, + {ST_NULL, EV_RESUME, lli_resume}, + {ST_NULL, EV_SETUP_IND, lli_deliver_call}, + {ST_NULL, EV_LEASED, lli_leased_in}, + {ST_OUT_DIAL, EV_SETUP_CNF, lli_init_bchan_out}, + {ST_OUT_DIAL, EV_HANGUP, lli_disconnect_req}, + {ST_OUT_DIAL, EV_DISCONNECT_IND, lli_release_req}, + {ST_OUT_DIAL, EV_RELEASE, lli_dhup_close}, + {ST_OUT_DIAL, EV_NOSETUP_RSP, lli_no_setup_rsp}, + {ST_OUT_DIAL, EV_SETUP_ERR, lli_error}, + {ST_IN_WAIT_LL, EV_LEASED_REL, lli_failure_l}, + {ST_IN_WAIT_LL, EV_ACCEPTD, lli_setup_rsp}, + {ST_IN_WAIT_LL, EV_HANGUP, lli_reject_req}, + {ST_IN_WAIT_LL, EV_DISCONNECT_IND, lli_release_req}, + {ST_IN_WAIT_LL, EV_RELEASE, lli_dhup_close}, + {ST_IN_ALERT_SENT, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_ALERT_SENT, EV_ACCEPTD, lli_send_dconnect}, + {ST_IN_ALERT_SENT, EV_HANGUP, lli_disconnect_reject}, + {ST_IN_ALERT_SENT, EV_DISCONNECT_IND, lli_release_req}, + {ST_IN_ALERT_SENT, EV_RELEASE, lli_dhup_close}, + {ST_IN_ALERT_SENT, EV_REDIR, lli_send_redir}, + {ST_IN_PROCEED_SEND, EV_REDIR, lli_send_redir}, + {ST_IN_PROCEED_SEND, EV_ALERT, lli_send_alert}, + {ST_IN_PROCEED_SEND, EV_ACCEPTD, lli_send_dconnect}, + {ST_IN_PROCEED_SEND, EV_HANGUP, lli_disconnect_reject}, + {ST_IN_PROCEED_SEND, EV_DISCONNECT_IND, lli_dhup_close}, + {ST_IN_ALERT_SENT, EV_RELEASE, lli_dhup_close}, + {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_WAIT_CONN_ACK, EV_HANGUP, lli_disconnect_req}, + {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, lli_release_req}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE, lli_dhup_close}, + {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, lli_error}, + {ST_WAIT_BCONN, EV_BC_EST, lli_go_active}, + {ST_WAIT_BCONN, EV_BC_REL, lli_rel_b_disc}, + {ST_WAIT_BCONN, EV_HANGUP, lli_rel_b_disc}, + {ST_WAIT_BCONN, EV_DISCONNECT_IND, lli_rel_b_release_req}, + {ST_WAIT_BCONN, EV_RELEASE, lli_rel_b_dhup}, + {ST_WAIT_BCONN, EV_LEASED_REL, lli_rel_b_fail}, + {ST_WAIT_BCONN, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_BC_REL, lli_bhup_rel_b}, + {ST_ACTIVE, EV_SUSPEND, lli_suspend}, + {ST_ACTIVE, EV_HANGUP, lli_disconn_bchan}, + {ST_ACTIVE, EV_DISCONNECT_IND, lli_release_bchan}, + {ST_ACTIVE, EV_RELEASE, lli_abort}, + {ST_ACTIVE, EV_LEASED_REL, lli_failure_a}, + {ST_WAIT_BRELEASE, EV_BC_REL, lli_bhup_disc}, + {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, lli_bhup_release_req}, + {ST_WAIT_BRELEASE, EV_RELEASE, lli_bhup_dhup}, + {ST_WAIT_BRELEASE, EV_LEASED_REL, lli_bhup_fail}, + {ST_WAIT_BREL_DISC, EV_BC_REL, lli_bhup_release_req}, + {ST_WAIT_BREL_DISC, EV_RELEASE, lli_bhup_dhup}, + {ST_WAIT_DCOMMAND, EV_HANGUP, lli_start_disc}, + {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, lli_release_req}, + {ST_WAIT_DCOMMAND, EV_RELEASE, lli_dhup_close}, + {ST_WAIT_DCOMMAND, EV_LEASED_REL, lli_failure_l}, + {ST_WAIT_DRELEASE, EV_RELEASE, lli_dhup_close}, + {ST_WAIT_DRELEASE, EV_DIAL, lli_dchan_not_ready}, + /* ETS 300-104 16.1 */ + {ST_WAIT_D_REL_CNF, EV_RELEASE, lli_dhup_close}, + {ST_WAIT_D_REL_CNF, EV_DIAL, lli_dchan_not_ready}, }; -/* *INDENT-ON* */ + /* *INDENT-ON* */ -#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) + #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) HISAX_INITFUNC(void CallcNew(void)) @@ -1195,21 +975,24 @@ release_b_st(struct Channel *chanp) { struct PStack *st = chanp->b_st; - chanp->bcs->BC_Close(chanp->bcs); - switch (chanp->l2_active_protocol) { - case (ISDN_PROTO_L2_X75I): - releasestack_isdnl2(st); - break; - case (ISDN_PROTO_L2_HDLC): - case (ISDN_PROTO_L2_TRANS): -// case (ISDN_PROTO_L2_MODEM): - releasestack_transl2(st); - break; - } + if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) { + chanp->bcs->BC_Close(chanp->bcs); + switch (chanp->l2_active_protocol) { + case (ISDN_PROTO_L2_X75I): + releasestack_isdnl2(st); + break; + case (ISDN_PROTO_L2_HDLC): + case (ISDN_PROTO_L2_TRANS): + case (ISDN_PROTO_L2_MODEM): + case (ISDN_PROTO_L2_FAX): + releasestack_transl2(st); + break; + } + } } struct Channel -*selectfreechannel(struct PStack *st) +*selectfreechannel(struct PStack *st, int bch) { struct IsdnCardState *cs = st->l1.hardware; struct Channel *chanp = st->lli.userdata; @@ -1219,74 +1002,60 @@ struct Channel i=1; else i=0; - while (i<2) { + + if (!bch) + { i = 2; /* virtual channel */ + chanp += 2; + } + + while (i < ((bch) ? cs->chanlimit : (2 + MAX_WAITING_CALLS))) { if (chanp->fi.state == ST_NULL) return (chanp); chanp++; i++; } - return (NULL); -} - -int -is_activ(struct PStack *st) -{ - struct IsdnCardState *cs = st->l1.hardware; - struct Channel *chanp = st->lli.userdata; - int i; - if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) - i=1; - else - i=0; - while (i<2) { - if (test_bit(FLG_ESTAB_D, &chanp->Flags)) - return (1); + if (bch) /* number of channels is limited */ + { i = 2; /* virtual channel */ + chanp = st->lli.userdata; + chanp += i; + while (i < (2 + MAX_WAITING_CALLS)) { + if (chanp->fi.state == ST_NULL) + return (chanp); chanp++; i++; - } - return (0); + } + } + return (NULL); } +static void stat_redir_result(struct IsdnCardState *cs, int chan, ulong result) +{ isdn_ctrl ic; + + ic.driver = cs->myid; + ic.command = ISDN_STAT_REDIR; + ic.arg = chan; + (ulong)(ic.parm.num[0]) = result; + cs->iif.statcallb(&ic); +} /* stat_redir_result */ + static void dchan_l3l4(struct PStack *st, int pr, void *arg) { struct l3_process *pc = arg; - struct IsdnCardState *cs = st->l1.hardware; + struct IsdnCardState *cs = st->l1.hardware; struct Channel *chanp; - int event; - switch (pr) { - case (DL_ESTABLISH | INDICATION): - event = EV_DLEST; - break; - case (DL_RELEASE | INDICATION): - event = EV_DLRL; - break; - default: - event = -1; - break; - } - if (event >= 0) { - int i; - - chanp = st->lli.userdata; - if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) - i = 1; - else - i = 0; - while (i < 2) { - FsmEvent(&chanp->fi, event, NULL); - chanp++; - i++; - } - return; - } else if (pr == (CC_SETUP | INDICATION)) { - if (!(chanp = selectfreechannel(pc->st))) { - pc->st->lli.l4l3(pc->st, CC_DLRL | REQUEST, pc); - } else { - chanp->proc = pc; - pc->chan = chanp; + if(!pc) + return; + + if (pr == (CC_SETUP | INDICATION)) { + if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) { + pc->para.cause = 0x11; /* User busy */ + pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc); + } else { + chanp->proc = pc; + pc->chan = chanp; FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); } return; @@ -1299,19 +1068,19 @@ dchan_l3l4(struct PStack *st, int pr, void *arg) FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); break; case (CC_RELEASE | CONFIRM): - FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; case (CC_SUSPEND | CONFIRM): - FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; case (CC_RESUME | CONFIRM): FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); break; case (CC_RESUME_ERR): - FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; case (CC_RELEASE | INDICATION): - FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; case (CC_SETUP_COMPL | INDICATION): FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); @@ -1332,15 +1101,21 @@ dchan_l3l4(struct PStack *st, int pr, void *arg) FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL); break; case (CC_RELEASE_ERR): - FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL); + FsmEvent(&chanp->fi, EV_RELEASE, NULL); break; + case (CC_PROCEED_SEND | INDICATION): case (CC_PROCEEDING | INDICATION): case (CC_ALERTING | INDICATION): + case (CC_PROGRESS | INDICATION): + case (CC_NOTIFY | INDICATION): break; + case (CC_REDIR | INDICATION): + stat_redir_result(cs, chanp->chan, pc->redir_result); + break; default: if (chanp->debug & 0x800) { HiSax_putstatus(chanp->cs, "Ch", - "%d L3->L4 unknown primitiv %x", + "%d L3->L4 unknown primitiv %#x", chanp->chan, pr); } } @@ -1406,6 +1181,7 @@ init_PStack(struct PStack **stp) { (*stp)->l2.l2l1 = dummy_pstack; (*stp)->l2.l2l3 = dummy_pstack; (*stp)->l3.l3l2 = dummy_pstack; + (*stp)->l3.l3ml3 = dummy_pstack; (*stp)->l3.l3l4 = dummy_pstack; (*stp)->lli.l4l3 = dummy_pstack; (*stp)->ma.layer = dummy_pstack; @@ -1446,14 +1222,19 @@ init_chan(int chan, struct IsdnCardState *csta) int CallcNewChan(struct IsdnCardState *csta) -{ +{ int i; + chancount += 2; init_chan(0, csta); init_chan(1, csta); printk(KERN_INFO "HiSax: 2 channels added\n"); + + for (i = 0; i < MAX_WAITING_CALLS; i++) + init_chan(i+2,csta); + printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n"); + if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) { printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); - test_and_set_bit(FLG_START_D, &csta->channel->Flags); csta->channel->d_st->lli.l4l3(csta->channel->d_st, DL_ESTABLISH | REQUEST, NULL); } @@ -1482,14 +1263,13 @@ CallcFreeChan(struct IsdnCardState *csta) for (i = 0; i < 2; i++) { FsmDelTimer(&csta->channel[i].drel_timer, 74); FsmDelTimer(&csta->channel[i].dial_timer, 75); - if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) - release_d_st(csta->channel + i); - if (csta->channel[i].b_st) { - if (test_and_clear_bit(FLG_START_B, &csta->channel[i].Flags)) - release_b_st(csta->channel + i); - kfree(csta->channel[i].b_st); - csta->channel[i].b_st = NULL; - } else + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) + release_d_st(csta->channel + i); + if (csta->channel[i].b_st) { + release_b_st(csta->channel + i); + kfree(csta->channel[i].b_st); + csta->channel[i].b_st = NULL; + } else printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i); if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { release_d_st(csta->channel + i); @@ -1509,7 +1289,7 @@ lldata_handler(struct PStack *st, int pr, void *arg) if (chanp->data_open) chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } break; case (DL_ESTABLISH | INDICATION): @@ -1521,7 +1301,7 @@ lldata_handler(struct PStack *st, int pr, void *arg) FsmEvent(&chanp->fi, EV_BC_REL, NULL); break; default: - printk(KERN_WARNING "lldata_handler unknown primitive %x\n", + printk(KERN_WARNING "lldata_handler unknown primitive %#x\n", pr); break; } @@ -1539,7 +1319,7 @@ lltrans_handler(struct PStack *st, int pr, void *arg) chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { link_debug(chanp, 0, "channel not open"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } break; case (PH_ACTIVATE | INDICATION): @@ -1551,7 +1331,7 @@ lltrans_handler(struct PStack *st, int pr, void *arg) FsmEvent(&chanp->fi, EV_BC_REL, NULL); break; default: - printk(KERN_WARNING "lltrans_handler unknown primitive %x\n", + printk(KERN_WARNING "lltrans_handler unknown primitive %#x\n", pr); break; } @@ -1566,7 +1346,7 @@ ll_writewakeup(struct PStack *st, int len) ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BSENT; ic.arg = chanp->chan; -// ic.parm.length = len; + ic.parm.length = len; chanp->cs->iif.statcallb(&ic); } @@ -1590,12 +1370,14 @@ init_b_st(struct Channel *chanp, int incoming) case (ISDN_PROTO_L2_TRANS): st->l1.mode = L1_MODE_TRANS; break; -#if 0 case (ISDN_PROTO_L2_MODEM): - st->l1.mode = L1_MODE_MODEM; + st->l1.mode = L1_MODE_V32; + break; + case (ISDN_PROTO_L2_FAX): + st->l1.mode = L1_MODE_FAX; break; -#endif } + chanp->bcs->conmsg = NULL; if (chanp->bcs->BC_SetStack(st, chanp->bcs)) return (-1); st->l2.flag = 0; @@ -1622,7 +1404,8 @@ init_b_st(struct Channel *chanp, int incoming) break; case (ISDN_PROTO_L2_HDLC): case (ISDN_PROTO_L2_TRANS): -// case (ISDN_PROTO_L2_MODEM): + case (ISDN_PROTO_L2_MODEM): + case (ISDN_PROTO_L2_FAX): st->l1.l1l2 = lltrans_handler; st->lli.userdata = chanp; st->lli.l1writewakeup = ll_writewakeup; @@ -1630,6 +1413,7 @@ init_b_st(struct Channel *chanp, int incoming) setstack_l3bc(st, chanp); break; } + test_and_set_bit(FLG_START_B, &chanp->Flags); return (0); } @@ -1642,7 +1426,7 @@ leased_l4l3(struct PStack *st, int pr, void *arg) switch (pr) { case (DL_DATA | REQUEST): link_debug(chanp, 0, "leased line d-channel DATA"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); break; case (DL_ESTABLISH | REQUEST): st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL); @@ -1650,7 +1434,7 @@ leased_l4l3(struct PStack *st, int pr, void *arg) case (DL_RELEASE | REQUEST): break; default: - printk(KERN_WARNING "transd_l4l3 unknown primitive %x\n", + printk(KERN_WARNING "transd_l4l3 unknown primitive %#x\n", pr); break; } @@ -1661,16 +1445,16 @@ leased_l1l2(struct PStack *st, int pr, void *arg) { struct Channel *chanp = (struct Channel *) st->lli.userdata; struct sk_buff *skb = arg; - int i,event = EV_DLRL; + int i,event = EV_LEASED_REL; switch (pr) { case (PH_DATA | INDICATION): link_debug(chanp, 0, "leased line d-channel DATA"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); break; case (PH_ACTIVATE | INDICATION): case (PH_ACTIVATE | CONFIRM): - event = EV_DLEST; + event = EV_LEASED; case (PH_DEACTIVATE | INDICATION): case (PH_DEACTIVATE | CONFIRM): if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) @@ -1685,7 +1469,7 @@ leased_l1l2(struct PStack *st, int pr, void *arg) break; default: printk(KERN_WARNING - "transd_l1l2 unknown primitive %x\n", pr); + "transd_l1l2 unknown primitive %#x\n", pr); break; } } @@ -1701,7 +1485,7 @@ distr_debug(struct IsdnCardState *csta, int debugflags) int i; struct Channel *chanp = csta->channel; - for (i = 0; i < 2; i++) { + for (i = 0; i < (2 + MAX_WAITING_CALLS) ; i++) { chanp[i].debug = debugflags; chanp[i].fi.debug = debugflags & 2; chanp[i].d_st->l2.l2m.debug = debugflags & 8; @@ -1774,14 +1558,43 @@ lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg * } } + +/***************************************************************/ +/* Limit the available number of channels for the current card */ +/***************************************************************/ +static int +set_channel_limit(struct IsdnCardState *cs, int chanmax) +{ + isdn_ctrl ic; + int i, ii; + + if ((chanmax < 0) || (chanmax > 2)) + return(-EINVAL); + cs->chanlimit = 0; + for (ii = 0; ii < 2; ii++) { + ic.driver = cs->myid; + ic.command = ISDN_STAT_DISCH; + ic.arg = ii; + if (ii >= chanmax) + ic.parm.num[0] = 0; /* disabled */ + else + ic.parm.num[0] = 1; /* enabled */ + i = cs->iif.statcallb(&ic); + if (i) return(-EINVAL); + if (ii < chanmax) + cs->chanlimit++; + } + return(0); +} /* set_channel_limit */ + int HiSax_command(isdn_ctrl * ic) { struct IsdnCardState *csta = hisax_findcard(ic->driver); + struct PStack *st; struct Channel *chanp; int i; u_int num; - u_long adr; if (!csta) { printk(KERN_ERR @@ -1789,12 +1602,10 @@ HiSax_command(isdn_ctrl * ic) ic->command, ic->driver); return -ENODEV; } - switch (ic->command) { case (ISDN_CMD_SETEAZ): chanp = csta->channel + ic->arg; break; - case (ISDN_CMD_SETL2): chanp = csta->channel + (ic->arg & 0xff); if (chanp->debug & 1) @@ -1868,7 +1679,7 @@ HiSax_command(isdn_ctrl * ic) HiSax_mod_inc_use_count(); #ifdef MODULE if (csta->channel[0].debug & 0x400) - HiSax_putstatus(csta, " LOCK ", "modcnt %x", + HiSax_putstatus(csta, " LOCK ", "modcnt %lx", MOD_USE_COUNT); #endif /* MODULE */ break; @@ -1876,7 +1687,7 @@ HiSax_command(isdn_ctrl * ic) HiSax_mod_dec_use_count(); #ifdef MODULE if (csta->channel[0].debug & 0x400) - HiSax_putstatus(csta, " UNLOCK ", "modcnt %x", + HiSax_putstatus(csta, " UNLOCK ", "modcnt %lx", MOD_USE_COUNT); #endif /* MODULE */ break; @@ -1949,8 +1760,6 @@ HiSax_command(isdn_ctrl * ic) HiSax_putstatus(csta, "set card ", "in PTP mode"); printk(KERN_DEBUG "HiSax: set card in PTP mode\n"); printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); - test_and_set_bit(FLG_START_D, &csta->channel[0].Flags); - test_and_set_bit(FLG_START_D, &csta->channel[1].Flags); csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st, DL_ESTABLISH | REQUEST, NULL); } else { @@ -1969,16 +1778,12 @@ HiSax_command(isdn_ctrl * ic) HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num); printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", num); - break; - case (9): /* load firmware */ - memcpy(&adr, ic->parm.num, sizeof(ulong)); - csta->cardmsg(csta, CARD_LOAD_FIRM, - (void *) adr); + chanp->d_st->lli.l4l3(chanp->d_st, + DL_ESTABLISH | REQUEST, NULL); break; #ifdef MODULE case (55): - while ( MOD_USE_COUNT > 0) - MOD_DEC_USE_COUNT; + MOD_USE_COUNT = 0; HiSax_mod_inc_use_count(); break; #endif /* MODULE */ @@ -2001,16 +1806,52 @@ HiSax_command(isdn_ctrl * ic) printk(KERN_DEBUG "HiSax: l3 debugging flags card %d set to %x\n", csta->cardnr + 1, *(unsigned int *) ic->parm.num); break; + case (10): + i = *(unsigned int *) ic->parm.num; + return(set_channel_limit(csta, i)); default: + if (csta->auxcmd) + return(csta->auxcmd(csta, ic)); printk(KERN_DEBUG "HiSax: invalid ioclt %d\n", (int) ic->arg); return (-EINVAL); } break; - default: + + case (ISDN_CMD_PROCEED): + chanp = csta->channel + ic->arg; + if (chanp->debug & 1) + link_debug(chanp, 1, "PROCEED"); + FsmEvent(&chanp->fi, EV_PROCEED, NULL); + break; + + case (ISDN_CMD_ALERT): + chanp = csta->channel + ic->arg; + if (chanp->debug & 1) + link_debug(chanp, 1, "ALERT"); + FsmEvent(&chanp->fi, EV_ALERT, NULL); break; - } + case (ISDN_CMD_REDIR): + chanp = csta->channel + ic->arg; + if (chanp->debug & 1) + link_debug(chanp, 1, "REDIR"); + chanp->setup = ic->parm.setup; + FsmEvent(&chanp->fi, EV_REDIR, NULL); + break; + + /* protocol specific io commands */ + case (ISDN_CMD_PROT_IO): + for (st = csta->stlist; st; st = st->next) + if (st->protocol == (ic->arg & 0xFF)) + return(st->lli.l4l3_proto(st, ic)); + return(-EINVAL); + break; + default: + if (csta->auxcmd) + return(csta->auxcmd(csta, ic)); + return(-EINVAL); + } return (0); } @@ -2049,7 +1890,8 @@ HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb) if (chanp->debug & 0x800) link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len); return 0; - } + } else if (chanp->debug & 0x800) + link_debug(chanp, 1, "writebuf %d/%d/%d", len, chanp->bcs->tx_cnt,MAX_DATA_MEM); save_flags(flags); cli(); nskb = skb_clone(skb, GFP_ATOMIC); @@ -2062,7 +1904,7 @@ HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb) chanp->bcs->tx_cnt += len; st->l2.l2l1(st, PH_DATA | REQUEST, nskb); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_WRITE); } else len = 0; restore_flags(flags); diff --git a/drivers/isdn/hisax/cert.c b/drivers/isdn/hisax/cert.c index a76736b60..03f416d5b 100644 --- a/drivers/isdn/hisax/cert.c +++ b/drivers/isdn/hisax/cert.c @@ -1,4 +1,4 @@ -/* $Id: cert.c,v 2.1 1998/11/15 23:51:15 keil Exp $ +/* $Id: cert.c,v 2.2 1999/08/07 17:35:05 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * @@ -7,6 +7,9 @@ * ../../../Documentation/isdn/HiSax.cert * * $Log: cert.c,v $ + * Revision 2.2 1999/08/07 17:35:05 keil + * approval for Eicon Technology Diva 2.01 PCI + * * Revision 2.1 1998/11/15 23:51:15 keil * certification stuff * @@ -29,6 +32,7 @@ certification_check(int output) { printk(KERN_INFO "HiSax: Approval registration numbers:\n"); printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n"); printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n"); + printk(KERN_INFO "HiSax: Approved with Eicon Technology Diva 2.01 PCI cards\n"); } return(0); #endif diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index d3bf31d90..dc0fa46a0 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,10 +1,42 @@ -/* $Id: config.c,v 2.23 1999/02/17 10:53:02 cpetig Exp $ +/* $Id: config.c,v 2.33 1999/08/30 11:57:52 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * * * $Log: config.c,v $ + * Revision 2.33 1999/08/30 11:57:52 keil + * Fix broken avm pcmcia + * + * Revision 2.32 1999/08/28 22:11:10 keil + * __setup function should be static + * + * Revision 2.31 1999/08/25 16:47:43 keil + * Support new __setup; allow to add FEATURES after register_isdn + * + * Revision 2.30 1999/08/05 20:43:14 keil + * ISAR analog modem support + * + * Revision 2.29 1999/07/21 14:46:00 keil + * changes from EICON certification + * + * Revision 2.28 1999/07/14 12:38:36 werner + * Added changes for echo channel handling + * + * Revision 2.27 1999/07/12 21:05:00 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.26 1999/07/08 21:27:17 keil + * version 3.2 + * + * Revision 2.25 1999/07/05 23:51:44 werner + * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl + * hisaxctrl id 10 <nr. of chans 0-2> + * + * Revision 2.24 1999/07/01 08:11:26 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.23 1999/02/17 10:53:02 cpetig * Added Hisax_closecard to exported symbols. * As indicated by Oliver Schoett <os@sdm.de>. @@ -94,6 +126,10 @@ #include <linux/stddef.h> #include <linux/timer.h> #include <linux/config.h> +#include <linux/isdn_compat.h> +#ifdef COMPAT_HAS_NEW_SETUP +#include <linux/init.h> +#endif #include "hisax.h" #include <linux/module.h> #include <linux/kernel_stat.h> @@ -139,7 +175,13 @@ * 26 AVM A1 PCMCIA (Fritz) p0=irq p1=iobase * 27 AVM PnP/PCI p0=irq p1=iobase (PCI no parameter) * 28 Sedlbauer Speed Fax+ p0=irq p1=iobase (from isapnp setup) - * + * 29 Siemens I-Surf p0=irq p1=iobase p2=memory (from isapnp setup) + * 30 ACER P10 p0=irq p1=iobase (from isapnp setup) + * 31 HST Saphir p0=irq p1=iobase + * 32 Telekom A4T none + * 33 Scitel Quadro p0=subcontroller (4*S0, subctrl 1...4) + * 34 Gazel ISDN cards + * 35 HFC 2BDS0 PCI none * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 * * @@ -152,15 +194,27 @@ const char *CardType[] = "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI", "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)", "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI", - "Sedlbauer Speed Fax +" + "Sedlbauer Speed Fax +", "Siemens I-Surf", "Acer P10", "HST Saphir", + "Telekom A4T", "Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", }; +void HiSax_closecard(int cardnr); + #ifdef CONFIG_HISAX_ELSA #define DEFAULT_CARD ISDN_CTYPE_ELSA #define DEFAULT_CFG {0,0,0,0} int elsa_init_pcmcia(void*, int, int*, int); +#ifdef COMPAT_HAS_NEW_SYMTAB EXPORT_SYMBOL(elsa_init_pcmcia); -#endif +#else +static struct symbol_table hisax_syms_elsa = { +#include <linux/symtab_begin.h> + X(elsa_init_pcmcia), +#include <linux/symtab_end.h> +}; +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* CONFIG_HISAX_ELSA */ + #ifdef CONFIG_HISAX_AVM_A1 #undef DEFAULT_CARD #undef DEFAULT_CFG @@ -174,8 +228,18 @@ EXPORT_SYMBOL(elsa_init_pcmcia); #define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA #define DEFAULT_CFG {11,0x170,0,0} int avm_a1_init_pcmcia(void*, int, int*, int); +#ifdef COMPAT_HAS_NEW_SYMTAB EXPORT_SYMBOL(avm_a1_init_pcmcia); -#endif +EXPORT_SYMBOL(HiSax_closecard); +#else +static struct symbol_table hisax_syms_avm_a1= { +#include <linux/symtab_begin.h> + X(avm_a1_init_pcmcia), + X(HiSax_closecard), +#include <linux/symtab_end.h> +}; +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* CONFIG_HISAX_AVM_A1_PCMCIA */ #ifdef CONFIG_HISAX_FRITZPCI #undef DEFAULT_CARD @@ -190,12 +254,14 @@ EXPORT_SYMBOL(avm_a1_init_pcmcia); #define DEFAULT_CARD ISDN_CTYPE_16_3 #define DEFAULT_CFG {15,0x180,0,0} #endif + #ifdef CONFIG_HISAX_S0BOX #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_S0BOX #define DEFAULT_CFG {7,0x378,0,0} #endif + #ifdef CONFIG_HISAX_16_0 #undef DEFAULT_CARD #undef DEFAULT_CFG @@ -244,8 +310,16 @@ EXPORT_SYMBOL(avm_a1_init_pcmcia); #define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER #define DEFAULT_CFG {11,0x270,0,0} int sedl_init_pcmcia(void*, int, int*, int); +#ifdef COMPAT_HAS_NEW_SYMTAB EXPORT_SYMBOL(sedl_init_pcmcia); -#endif +#else +static struct symbol_table hisax_syms_sedl= { +#include <linux/symtab_begin.h> + X(sedl_init_pcmcia), +#include <linux/symtab_end.h> +}; +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* CONFIG_HISAX_SEDLBAUER */ #ifdef CONFIG_HISAX_SPORTSTER #undef DEFAULT_CARD @@ -268,13 +342,21 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #define DEFAULT_CFG {0,0,0,0} #endif -#ifdef CONFIG_HISAX_TELES3C +#ifdef CONFIG_HISAX_HFCS #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_TELES3C #define DEFAULT_CFG {5,0x500,0,0} #endif +#ifdef CONFIG_HISAX_HFC_PCI +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_HFC_PCI +#define DEFAULT_CFG {0,0,0,0} +#endif + + #ifdef CONFIG_HISAX_AMD7930 #undef DEFAULT_CARD #undef DEFAULT_CFG @@ -289,6 +371,41 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #define DEFAULT_CFG {0,0x0,0,0} #endif +#ifdef CONFIG_HISAX_ISURF +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_ISURF +#define DEFAULT_CFG {5,0x100,0xc8000,0} +#endif + +#ifdef CONFIG_HISAX_HSTSAPHIR +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_HSTSAPHIR +#define DEFAULT_CFG {5,0x250,0,0} +#endif + +#ifdef CONFIG_HISAX_BKM_A4T +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_BKM_A4T +#define DEFAULT_CFG {0,0x0,0,0} +#endif + +#ifdef CONFIG_HISAX_SCT_QUADRO +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SCT_QUADRO +#define DEFAULT_CFG {1,0x0,0,0} +#endif + +#ifdef CONFIG_HISAX_GAZEL +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_GAZEL +#define DEFAULT_CFG {15,0x180,0,0} +#endif + #ifdef CONFIG_HISAX_1TR6 #define DEFAULT_PROTO ISDN_PTYPE_1TR6 #define DEFAULT_PROTO_NAME "1TR6" @@ -366,6 +483,7 @@ static int mem[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0}; static char *id HISAX_INITDATA = HiSaxID; +#ifdef COMPAT_HAS_NEW_SYMTAB MODULE_AUTHOR("Karsten Keil"); MODULE_PARM(type, "1-8i"); MODULE_PARM(protocol, "1-8i"); @@ -377,7 +495,7 @@ MODULE_PARM(id, "s"); MODULE_PARM(io0, "1-8i"); MODULE_PARM(io1, "1-8i"); #endif /* CONFIG_HISAX_16_3 */ - +#endif /* COMPAT_HAS_NEW_SYMTAB */ #endif /* MODULE */ int nrcards; @@ -410,9 +528,9 @@ HiSaxVersion(void)) printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n"); #ifdef MODULE - printk(KERN_INFO "HiSax: Version 3.1a (module)\n"); + printk(KERN_INFO "HiSax: Version 3.3a (module)\n"); #else - printk(KERN_INFO "HiSax: Version 3.1a (kernel)\n"); + printk(KERN_INFO "HiSax: Version 3.3a (kernel)\n"); #endif strcpy(tmp, l1_revision); printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp)); @@ -442,12 +560,24 @@ HiSax_mod_inc_use_count(void) #ifdef MODULE #define HiSax_init init_module #else -void __init -HiSax_setup(char *str, int *ints) +#ifdef COMPAT_HAS_NEW_SETUP +#define MAX_ARG (HISAX_MAX_CARDS*5) +static int __init +HiSax_setup(char *line) { int i, j, argc; + int ints[MAX_ARG + 1]; + char *str; + str = get_options(line, MAX_ARG, ints); +#else +void __init +HiSax_setup(char *str, int *ints) +{ + int i, j, argc; +#endif argc = ints[0]; + printk(KERN_DEBUG"HiSax_setup: argc(%d) str(%s)\n", argc, str); i = 0; j = 1; while (argc && (i < HISAX_MAX_CARDS)) { @@ -485,8 +615,15 @@ HiSax_setup(char *str, int *ints) strcpy(HiSaxID, "HiSax"); HiSax_id = HiSaxID; } +#ifdef COMPAT_HAS_NEW_SETUP + return(1); } -#endif + +__setup("hisax=", HiSax_setup); +#else +} +#endif /* COMPAT_HAS_NEW_SETUP */ +#endif /* MODULES */ #if CARD_TELES0 extern int setup_teles0(struct IsdnCard *card); @@ -552,8 +689,12 @@ extern int setup_mic(struct IsdnCard *card); extern int setup_netjet(struct IsdnCard *card); #endif -#if CARD_TELES3C -extern int setup_t163c(struct IsdnCard *card); +#if CARD_HFCS +extern int setup_hfcs(struct IsdnCard *card); +#endif + +#if CARD_HFC_PCI +extern int setup_hfcpci(struct IsdnCard *card); #endif #if CARD_AMD7930 @@ -564,6 +705,30 @@ extern int setup_amd7930(struct IsdnCard *card); extern int setup_niccy(struct IsdnCard *card); #endif +#if CARD_ISURF +extern int setup_isurf(struct IsdnCard *card); +#endif + +#if CARD_HSTSAPHIR +extern int setup_saphir(struct IsdnCard *card); +#endif + +#if CARD_TESTEMU +extern int setup_testemu(struct IsdnCard *card); +#endif + +#if CARD_BKM_A4T +extern int setup_bkm_a4t(struct IsdnCard *card); +#endif + +#if CARD_SCT_QUADRO +extern int setup_sct_quadro(struct IsdnCard *card); +#endif + +#if CARD_GAZEL +extern int setup_gazel(struct IsdnCard *card); +#endif + /* * Find card with given driverId */ @@ -579,6 +744,18 @@ static inline struct IsdnCardState return (NULL); } +/* + * Find card with given card number + */ +struct IsdnCardState +*hisax_get_card(int cardnr) +{ + if ((cardnr <= nrcards) && (cardnr>0)) + if (cards[cardnr-1].cs) + return (cards[cardnr-1].cs); + return (NULL); +} + int HiSax_readstatus(u_char * buf, int len, int user, int id, int channel) { @@ -731,7 +908,7 @@ HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...) } int -ll_run(struct IsdnCardState *cs) +ll_run(struct IsdnCardState *cs, int addfeatures) { long flags; isdn_ctrl ic; @@ -740,6 +917,7 @@ ll_run(struct IsdnCardState *cs) cli(); ic.driver = cs->myid; ic.command = ISDN_STAT_RUN; + cs->iif.features |= addfeatures; cs->iif.statcallb(&ic); restore_flags(flags); return 0; @@ -782,23 +960,18 @@ closecard(int cardnr) csta->bcs->BC_Close(csta->bcs); } + discard_queue(&csta->rq); + discard_queue(&csta->sq); if (csta->rcvbuf) { kfree(csta->rcvbuf); csta->rcvbuf = NULL; } - discard_queue(&csta->rq); - discard_queue(&csta->sq); if (csta->tx_skb) { - dev_kfree_skb(csta->tx_skb); + idev_kfree_skb(csta->tx_skb, FREE_WRITE); csta->tx_skb = NULL; } - if (csta->mon_rx) { - kfree(csta->mon_rx); - csta->mon_rx = NULL; - } - if (csta->mon_tx) { - kfree(csta->mon_tx); - csta->mon_tx = NULL; + if (csta->DC_Close != NULL) { + csta->DC_Close(csta); } csta->cardmsg(csta, CARD_RELEASE, NULL); if (csta->dbusytimer.function != NULL) @@ -811,12 +984,14 @@ HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) int irq_cnt, cnt = 3; long flags; + if (!cs->irq) + return(cs->cardmsg(cs, CARD_INIT, NULL)); save_flags(flags); cli(); irq_cnt = kstat_irqs(cs->irq); printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq, irq_cnt); - if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) { + if (request_irq(cs->irq, cs->irq_func, cs->irq_flags, "HiSax", cs)) { printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n", cs->irq); restore_flags(flags); @@ -827,7 +1002,7 @@ HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) sti(); current->state = TASK_INTERRUPTIBLE; /* Timeout 10ms */ - schedule_timeout((10 * HZ) / 1000); + schedule_timeout((10*HZ)/1000); restore_flags(flags); printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq, kstat_irqs(cs->irq)); @@ -871,198 +1046,228 @@ checkcard(int cardnr, char *id, int *busy_flag)) } memset(cs, 0, sizeof(struct IsdnCardState)); card->cs = cs; + cs->chanlimit = 2; /* maximum B-channel number */ + cs->logecho = 0; /* No echo logging */ cs->cardnr = cardnr; cs->debug = L1_DEB_WARN; cs->HW_Flags = 0; cs->busy_flag = busy_flag; + cs->irq_flags = I4L_IRQ_FLAG; #if TEI_PER_CARD #else test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags); #endif cs->protocol = card->protocol; - if ((card->typ > 0) && (card->typ < 31)) { - if (!((1 << card->typ) & SUPORTED_CARDS)) { + if ((card->typ > 0) && (card->typ <= ISDN_CTYPE_COUNT)) { + if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) { printk(KERN_WARNING - "HiSax: Support for %s Card not selected\n", - CardType[card->typ]); + "HiSax: No memory for dlog(card %d)\n", + cardnr + 1); restore_flags(flags); return (0); } - } else { - printk(KERN_WARNING - "HiSax: Card Type %d out of range\n", - card->typ); - restore_flags(flags); - return (0); - } - if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for dlog(card %d)\n", - cardnr + 1); - restore_flags(flags); - return (0); - } - if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for status_buf(card %d)\n", - cardnr + 1); - kfree(cs->dlog); - restore_flags(flags); - return (0); - } - cs->stlist = NULL; - cs->mon_tx = NULL; - cs->mon_rx = NULL; - cs->status_read = cs->status_buf; - cs->status_write = cs->status_buf; - cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1; - cs->typ = card->typ; - strcpy(cs->iif.id, id); - cs->iif.channels = 2; - cs->iif.maxbufsize = MAX_DATA_SIZE; - cs->iif.hl_hdrlen = MAX_HEADER_LEN; - cs->iif.features = - ISDN_FEATURE_L2_X75I | - ISDN_FEATURE_L2_HDLC | - ISDN_FEATURE_L2_MODEM | - ISDN_FEATURE_L2_TRANS | - ISDN_FEATURE_L3_TRANS | + if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for status_buf(card %d)\n", + cardnr + 1); + kfree(cs->dlog); + restore_flags(flags); + return (0); + } + cs->stlist = NULL; + cs->status_read = cs->status_buf; + cs->status_write = cs->status_buf; + cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1; + cs->typ = card->typ; + strcpy(cs->iif.id, id); + cs->iif.channels = 2; + cs->iif.maxbufsize = MAX_DATA_SIZE; + cs->iif.hl_hdrlen = MAX_HEADER_LEN; + cs->iif.features = + ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_HDLC | + ISDN_FEATURE_L2_TRANS | + ISDN_FEATURE_L3_TRANS | #ifdef CONFIG_HISAX_1TR6 - ISDN_FEATURE_P_1TR6 | + ISDN_FEATURE_P_1TR6 | #endif #ifdef CONFIG_HISAX_EURO - ISDN_FEATURE_P_EURO | -#endif -#ifdef CONFIG_HISAX_NI1 - ISDN_FEATURE_P_NI1 | -#endif - 0; - - cs->iif.command = HiSax_command; - cs->iif.writecmd = NULL; - cs->iif.writebuf_skb = HiSax_writebuf_skb; - cs->iif.readstat = HiSax_readstatus; - register_isdn(&cs->iif); - cs->myid = cs->iif.channels; - printk(KERN_INFO - "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1, - (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" : - (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" : - (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" : - (card->protocol == ISDN_PTYPE_NI1) ? "NI1" : - "NONE", cs->iif.id, cs->myid); - switch (card->typ) { + ISDN_FEATURE_P_EURO | +#endif +#ifdef CONFIG_HISAX_NI1 + ISDN_FEATURE_P_NI1 | +#endif + 0; + + cs->iif.command = HiSax_command; + cs->iif.writecmd = NULL; + cs->iif.writebuf_skb = HiSax_writebuf_skb; + cs->iif.readstat = HiSax_readstatus; + register_isdn(&cs->iif); + cs->myid = cs->iif.channels; + printk(KERN_INFO + "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1, + (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" : + (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" : + (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" : + (card->protocol == ISDN_PTYPE_NI1) ? "NI1" : + "NONE", cs->iif.id, cs->myid); + switch (card->typ) { #if CARD_TELES0 - case ISDN_CTYPE_16_0: - case ISDN_CTYPE_8_0: - ret = setup_teles0(card); - break; + case ISDN_CTYPE_16_0: + case ISDN_CTYPE_8_0: + ret = setup_teles0(card); + break; #endif #if CARD_TELES3 - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_TELESPCMCIA: - case ISDN_CTYPE_COMPAQ_ISA: - ret = setup_teles3(card); - break; + case ISDN_CTYPE_16_3: + case ISDN_CTYPE_PNP: + case ISDN_CTYPE_TELESPCMCIA: + case ISDN_CTYPE_COMPAQ_ISA: + ret = setup_teles3(card); + break; #endif #if CARD_S0BOX - case ISDN_CTYPE_S0BOX: - ret = setup_s0box(card); - break; + case ISDN_CTYPE_S0BOX: + ret = setup_s0box(card); + break; #endif #if CARD_TELESPCI - case ISDN_CTYPE_TELESPCI: - ret = setup_telespci(card); - break; + case ISDN_CTYPE_TELESPCI: + ret = setup_telespci(card); + break; #endif #if CARD_AVM_A1 - case ISDN_CTYPE_A1: - ret = setup_avm_a1(card); - break; + case ISDN_CTYPE_A1: + ret = setup_avm_a1(card); + break; #endif #if CARD_AVM_A1_PCMCIA - case ISDN_CTYPE_A1_PCMCIA: - ret = setup_avm_a1_pcmcia(card); - break; + case ISDN_CTYPE_A1_PCMCIA: + ret = setup_avm_a1_pcmcia(card); + break; #endif #if CARD_FRITZPCI - case ISDN_CTYPE_FRITZPCI: - ret = setup_avm_pcipnp(card); - break; + case ISDN_CTYPE_FRITZPCI: + ret = setup_avm_pcipnp(card); + break; #endif #if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_PNP: - case ISDN_CTYPE_ELSA_PCMCIA: - case ISDN_CTYPE_ELSA_PCI: - ret = setup_elsa(card); - break; + case ISDN_CTYPE_ELSA: + case ISDN_CTYPE_ELSA_PNP: + case ISDN_CTYPE_ELSA_PCMCIA: + case ISDN_CTYPE_ELSA_PCI: + ret = setup_elsa(card); + break; #endif #if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - ret = setup_ix1micro(card); - break; + case ISDN_CTYPE_IX1MICROR2: + ret = setup_ix1micro(card); + break; #endif #if CARD_DIEHLDIVA - case ISDN_CTYPE_DIEHLDIVA: - ret = setup_diva(card); - break; + case ISDN_CTYPE_DIEHLDIVA: + ret = setup_diva(card); + break; #endif #if CARD_ASUSCOM - case ISDN_CTYPE_ASUSCOM: - ret = setup_asuscom(card); - break; + case ISDN_CTYPE_ASUSCOM: + ret = setup_asuscom(card); + break; #endif #if CARD_TELEINT - case ISDN_CTYPE_TELEINT: - ret = setup_TeleInt(card); - break; + case ISDN_CTYPE_TELEINT: + ret = setup_TeleInt(card); + break; #endif #if CARD_SEDLBAUER - case ISDN_CTYPE_SEDLBAUER: - case ISDN_CTYPE_SEDLBAUER_PCMCIA: - case ISDN_CTYPE_SEDLBAUER_FAX: - ret = setup_sedlbauer(card); - break; + case ISDN_CTYPE_SEDLBAUER: + case ISDN_CTYPE_SEDLBAUER_PCMCIA: + case ISDN_CTYPE_SEDLBAUER_FAX: + ret = setup_sedlbauer(card); + break; #endif #if CARD_SPORTSTER - case ISDN_CTYPE_SPORTSTER: - ret = setup_sportster(card); - break; + case ISDN_CTYPE_SPORTSTER: + ret = setup_sportster(card); + break; #endif #if CARD_MIC - case ISDN_CTYPE_MIC: - ret = setup_mic(card); - break; + case ISDN_CTYPE_MIC: + ret = setup_mic(card); + break; #endif #if CARD_NETJET - case ISDN_CTYPE_NETJET: - ret = setup_netjet(card); - break; + case ISDN_CTYPE_NETJET: + ret = setup_netjet(card); + break; #endif -#if CARD_TELES3C - case ISDN_CTYPE_TELES3C: - ret = setup_t163c(card); - break; +#if CARD_HFCS + case ISDN_CTYPE_TELES3C: + case ISDN_CTYPE_ACERP10: + ret = setup_hfcs(card); + break; +#endif +#if CARD_HFC_PCI + case ISDN_CTYPE_HFC_PCI: + ret = setup_hfcpci(card); + break; #endif #if CARD_NICCY - case ISDN_CTYPE_NICCY: - ret = setup_niccy(card); - break; + case ISDN_CTYPE_NICCY: + ret = setup_niccy(card); + break; #endif #if CARD_AMD7930 - case ISDN_CTYPE_AMD7930: - ret = setup_amd7930(card); + case ISDN_CTYPE_AMD7930: + ret = setup_amd7930(card); + break; +#endif +#if CARD_ISURF + case ISDN_CTYPE_ISURF: + ret = setup_isurf(card); + break; +#endif +#if CARD_HSTSAPHIR + case ISDN_CTYPE_HSTSAPHIR: + ret = setup_saphir(card); + break; +#endif +#if CARD_TESTEMU + case ISDN_CTYPE_TESTEMU: + ret = setup_testemu(card); + break; +#endif +#if CARD_BKM_A4T + case ISDN_CTYPE_BKM_A4T: + ret = setup_bkm_a4t(card); + break; +#endif +#if CARD_SCT_QUADRO + case ISDN_CTYPE_SCT_QUADRO: + ret = setup_sct_quadro(card); break; #endif +#if CARD_GAZEL + case ISDN_CTYPE_GAZEL: + ret = setup_gazel(card); + break; +#endif default: - printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n", - card->typ); + printk(KERN_WARNING + "HiSax: Support for %s Card not selected\n", + CardType[card->typ]); ll_unload(cs); restore_flags(flags); return (0); + } + } else { + printk(KERN_WARNING + "HiSax: Card Type %d out of range\n", + card->typ); + restore_flags(flags); + return (0); } if (!ret) { ll_unload(cs); @@ -1097,7 +1302,7 @@ checkcard(int cardnr, char *id, int *busy_flag)) CallcNewChan(cs); /* ISAR needs firmware download first */ if (!test_bit(HW_ISAR, &cs->HW_Flags)) - ll_run(cs); + ll_run(cs, 0); restore_flags(flags); return (1); } @@ -1168,7 +1373,8 @@ HiSax_closecard(int cardnr) ll_stop(cards[cardnr].cs); release_tei(cards[cardnr].cs); closecard(cardnr); - free_irq(cards[cardnr].cs->irq, cards[cardnr].cs); + if (cards[cardnr].cs->irq) + free_irq(cards[cardnr].cs->irq, cards[cardnr].cs); kfree((void *) cards[cardnr].cs); cards[cardnr].cs = NULL; } @@ -1180,8 +1386,6 @@ HiSax_closecard(int cardnr) nrcards--; } -EXPORT_SYMBOL(HiSax_closecard); - void HiSax_reportcard(int cardnr) { @@ -1202,11 +1406,13 @@ HiSax_reportcard(int cardnr) cs->bcs[0].mode, cs->bcs[0].channel); printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n", cs->bcs[1].mode, cs->bcs[1].channel); + printk(KERN_DEBUG "HiSax: cs setstack_d 0x%lX\n", (ulong) cs->setstack_d); printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist)); stptr = cs->stlist; while (stptr != NULL) { printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr); printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp); + printk(KERN_DEBUG "HiSax: dst%d l1.l1hw 0x%lX\n", i, (ulong) stptr->l1.l1hw); printk(KERN_DEBUG "HiSax: tei %d sapi %d\n", stptr->l2.tei, stptr->l2.sap); printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); @@ -1236,7 +1442,7 @@ HiSax_reportcard(int cardnr) } -int __init +int __init HiSax_init(void) { int i; @@ -1246,18 +1452,27 @@ HiSax_init(void) #ifdef CONFIG_HISAX_ELSA if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) { /* we have exported and return in this case */ +#ifndef COMPAT_HAS_NEW_SYMTAB + register_symtab(&hisax_syms_elsa); +#endif return 0; } #endif #ifdef CONFIG_HISAX_SEDLBAUER if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) { /* we have to export and return in this case */ +#ifndef COMPAT_HAS_NEW_SYMTAB + register_symtab(&hisax_syms_sedl); +#endif return 0; } #endif #ifdef CONFIG_HISAX_AVM_A1_PCMCIA if (type[0] == ISDN_CTYPE_A1_PCMCIA) { /* we have to export and return in this case */ +#ifndef COMPAT_HAS_NEW_SYMTAB + register_symtab(&hisax_syms_avm_a1); +#endif return 0; } #endif @@ -1318,16 +1533,29 @@ HiSax_init(void) case ISDN_CTYPE_SPORTSTER: case ISDN_CTYPE_MIC: case ISDN_CTYPE_TELES3C: + case ISDN_CTYPE_ACERP10: case ISDN_CTYPE_S0BOX: case ISDN_CTYPE_FRITZPCI: + case ISDN_CTYPE_HSTSAPHIR: + case ISDN_CTYPE_GAZEL: + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + break; + case ISDN_CTYPE_ISURF: cards[i].para[0] = irq[i]; cards[i].para[1] = io[i]; + cards[i].para[2] = mem[i]; break; case ISDN_CTYPE_ELSA_PCI: case ISDN_CTYPE_NETJET: case ISDN_CTYPE_AMD7930: case ISDN_CTYPE_TELESPCI: break; + case ISDN_CTYPE_BKM_A4T: + break; + case ISDN_CTYPE_SCT_QUADRO: + cards[i].para[0] = irq[i]; + break; } } if (!nzproto) { @@ -1353,11 +1581,13 @@ HiSax_init(void) Isdnl1New(); if (HiSax_inithardware(NULL)) { /* Install only, if at least one card found */ - /* No symbols to export, hide all symbols */ - #ifdef MODULE +#ifndef COMPAT_HAS_NEW_SYMTAB + /* No symbols to export, hide all symbols */ + register_symtab(NULL); printk(KERN_INFO "HiSax: module installed\n"); -#endif +#endif /* COMPAT_HAS_NEW_SYMTAB */ +#endif /* MODULE */ return (0); } else { Isdnl1Free(); diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 9774b2218..8ec1a536c 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -1,13 +1,36 @@ -/* $Id: diva.c,v 1.10 1998/11/15 23:54:31 keil Exp $ +/* $Id: diva.c,v 1.16 1999/08/11 21:01:25 keil Exp $ * diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards * * Author Karsten Keil (keil@isdn4linux.de) * - * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * + * Thanks to Eicon Technology for documents and informations * * * $Log: diva.c,v $ + * Revision 1.16 1999/08/11 21:01:25 keil + * new PCI codefix + * + * Revision 1.15 1999/08/10 16:01:49 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.14 1999/08/07 17:35:08 keil + * approval for Eicon Technology Diva 2.01 PCI + * + * Revision 1.13 1999/07/21 14:46:07 keil + * changes from EICON certification + * + * Revision 1.12 1999/07/12 21:05:04 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.11 1999/07/01 08:11:29 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.10 1998/11/15 23:54:31 keil * changes from 2.0 * @@ -51,10 +74,13 @@ #include "ipac.h" #include "isdnl1.h" #include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif extern const char *CardType[]; -const char *Diva_revision = "$Revision: 1.10 $"; +const char *Diva_revision = "$Revision: 1.16 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -75,6 +101,7 @@ const char *Diva_revision = "$Revision: 1.10 $"; #define DIVA_ISA 1 #define DIVA_PCI 2 #define DIVA_IPAC_ISA 3 +#define DIVA_IPAC_PCI 4 /* PCI stuff */ #define PCI_VENDOR_EICON_DIEHL 0x1133 @@ -82,10 +109,12 @@ const char *Diva_revision = "$Revision: 1.10 $"; #define PCI_DIVA20_ID 0xe002 #define PCI_DIVA20PRO_U_ID 0xe003 #define PCI_DIVA20_U_ID 0xe004 +#define PCI_DIVA_201 0xe005 /* CTRL (Read) */ #define DIVA_IRQ_STAT 0x01 #define DIVA_EEPROM_SDA 0x02 + /* CTRL (Write) */ #define DIVA_IRQ_REQ 0x01 #define DIVA_RESET 0x08 @@ -96,6 +125,13 @@ const char *Diva_revision = "$Revision: 1.10 $"; #define DIVA_ISA_LED_B 0x40 #define DIVA_IRQ_CLR 0x80 +/* Siemens PITA */ +#define PITA_MISC_REG 0x1c +#define PITA_PARA_SOFTRESET 0x01000000 +#define PITA_PARA_MPX_MODE 0x04000000 +#define PITA_INT0_ENABLE 0x00020000 +#define PITA_INT0_STATUS 0x00000002 + static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off) { @@ -140,6 +176,22 @@ writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size outsb(adr, data, size); } +static inline u_char +memreadreg(unsigned long adr, u_char off) +{ + return(0xff & *((unsigned int *) + (((unsigned int *)adr) + off))); +} + +static inline void +memwritereg(unsigned long adr, u_char off, u_char data) +{ + register u_char *p; + + p = (unsigned char *)(((unsigned int *)adr) + off); + *p = data; +} + /* Interface functions */ static u_char @@ -204,6 +256,44 @@ WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value); } +static u_char +MemReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) +{ + return (memreadreg(cs->hw.diva.cfg_reg, offset+0x80)); +} + +static void +MemWriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + memwritereg(cs->hw.diva.cfg_reg, offset|0x80, value); +} + +static void +MemReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + while(size--) + *data++ = memreadreg(cs->hw.diva.cfg_reg, 0x80); +} + +static void +MemWriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + while(size--) + memwritereg(cs->hw.diva.cfg_reg, 0x80, *data++); +} + +static u_char +MemReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return(memreadreg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0))); +} + +static void +MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value); +} + /* * fast interrupt HSCX stuff goes here */ @@ -225,8 +315,8 @@ static void diva_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, sval, stat = 0; - int cnt=8; + u_char val, sval; + int cnt=5; if (!cs) { printk(KERN_WARNING "Diva: Spurious interrupt!\n"); @@ -234,44 +324,36 @@ diva_interrupt(int intno, void *dev_id, struct pt_regs *regs) } while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) { val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40); - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA); - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } cnt--; } if (!cnt) printk(KERN_WARNING "Diva: IRQ LOOP\n"); - if (stat & 1) { - writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF); - writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF); - writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0); - writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0); - } - if (stat & 2) { - writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0); } static void -diva_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +diva_irq_ipac_isa(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char ista,val; - int icnt=20; + int icnt=5; if (!cs) { printk(KERN_WARNING "Diva: Spurious interrupt!\n"); return; } ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); -Start_IPAC: +Start_IPACISA: if (cs->debug & L1_DEB_IPAC) debugl1(cs, "IPAC ISTA %02X", ista); if (ista & 0x0f) { @@ -298,7 +380,7 @@ Start_IPAC: ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); if ((ista & 0x3f) && icnt) { icnt--; - goto Start_IPAC; + goto Start_IPACISA; } if (!icnt) printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n"); @@ -306,13 +388,348 @@ Start_IPAC: writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0); } +static inline void +MemwaitforCEC(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((MemReadHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC timeout\n"); +} + + +static inline void +MemwaitforXFW(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((!(MemReadHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); +} + +static inline void +MemWriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + MemwaitforCEC(cs, hscx); + MemWriteHSCX(cs, hscx, HSCX_CMDR, data); + restore_flags(flags); +} + +static void +Memhscx_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct IsdnCardState *cs = bcs->cs; + long flags; + int cnt; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_empty_fifo"); + + if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hscx_empty_fifo: incoming packet too large"); + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); + bcs->hw.hscx.rcvidx = 0; + return; + } + save_flags(flags); + cli(); + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + cnt = count; + while (cnt--) + *ptr++ = memreadreg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0); + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + bcs->hw.hscx.rcvidx += count; + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "hscx_empty_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static void +Memhscx_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int more, count, cnt; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + u_char *ptr,*p; + long flags; + + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_fill_fifo"); + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; + if (bcs->tx_skb->len > fifo_size) { + more = !0; + count = fifo_size; + } else + count = bcs->tx_skb->len; + cnt = count; + MemwaitforXFW(cs, bcs->hw.hscx.hscx); + save_flags(flags); + cli(); + p = ptr = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hscx.count += count; + while(cnt--) + memwritereg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0, + *p++); + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "hscx_fill_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static inline void +Memhscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) +{ + u_char r; + struct BCState *bcs = cs->bcs + hscx; + struct sk_buff *skb; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + int count; + + if (!test_bit(BC_FLG_INIT, &bcs->Flag)) + return; + + if (val & 0x80) { /* RME */ + r = MemReadHSCX(cs, hscx, HSCX_RSTA); + if ((r & 0xf0) != 0xa0) { + if (!(r & 0x80)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX invalid frame"); + if ((r & 0x40) && bcs->mode) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX RDO mode=%d", + bcs->mode); + if (!(r & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX CRC error"); + MemWriteHSCXCMDR(cs, hscx, 0x80); + } else { + count = MemReadHSCX(cs, hscx, HSCX_RBCL) & ( + test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f); + if (count == 0) + count = fifo_size; + Memhscx_empty_fifo(bcs, count); + if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { + if (cs->debug & L1_DEB_HSCX_FIFO) + debugl1(cs, "HX Frame %d", count); + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "HSCX: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + } + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + Memhscx_empty_fifo(bcs, fifo_size); + if (bcs->mode == L1_MODE_TRANS) { + /* receive audio data */ + if (!(skb = dev_alloc_skb(fifo_size))) + printk(KERN_WARNING "HiSax: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + } + if (val & 0x10) { /* XPR */ + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + Memhscx_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->hw.hscx.count = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + Memhscx_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +static inline void +Memhscx_int_main(struct IsdnCardState *cs, u_char val) +{ + + u_char exval; + struct BCState *bcs; + + if (val & 0x01) { + bcs = cs->bcs + 1; + exval = MemReadHSCX(cs, 1, HSCX_EXIR); + if (exval & 0x40) { + if (bcs->mode == 1) + Memhscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX B EXIR %x Lost TX", exval); + } + } else if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX B EXIR %x", exval); + } + if (val & 0xf8) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX B interrupt %x", val); + Memhscx_interrupt(cs, val, 1); + } + if (val & 0x02) { + bcs = cs->bcs; + exval = MemReadHSCX(cs, 0, HSCX_EXIR); + if (exval & 0x40) { + if (bcs->mode == L1_MODE_TRANS) + Memhscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX A EXIR %x Lost TX", exval); + } + } else if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX A EXIR %x", exval); + } + if (val & 0x04) { + exval = MemReadHSCX(cs, 0, HSCX_ISTA); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX A interrupt %x", exval); + Memhscx_interrupt(cs, exval, 0); + } +} + +static void +diva_irq_ipac_pci(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista,val; + int icnt=5; + u_char *cfg; + + if (!cs) { + printk(KERN_WARNING "Diva: Spurious interrupt!\n"); + return; + } + cfg = (u_char *) cs->hw.diva.pci_cfg; + val = *cfg; + if (!(val & PITA_INT0_STATUS)) + return; /* other shared IRQ */ + *cfg = PITA_INT0_STATUS; /* Reset pending INT0 */ + ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA); +Start_IPACPCI: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = memreadreg(cs->hw.diva.cfg_reg, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + Memhscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & memreadreg(cs->hw.diva.cfg_reg, ISAC_ISTA + 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPACPCI; + } + if (!icnt) + printk(KERN_WARNING "DIVA IPAC PCI IRQ LOOP\n"); + memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xFF); + memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0); +} void release_io_diva(struct IsdnCardState *cs) { int bytecnt; - if (cs->subtyp != DIVA_IPAC_ISA) { + if (cs->subtyp == DIVA_IPAC_PCI) { + u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg; + + *cfg = 0; /* disable INT0/1 */ + *cfg = 2; /* reset pending INT0 */ + iounmap((void *)cs->hw.diva.cfg_reg); + iounmap((void *)cs->hw.diva.pci_cfg); + return; + } else if (cs->subtyp != DIVA_IPAC_ISA) { del_timer(&cs->hw.diva.tl); if (cs->hw.diva.cfg_reg) byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */ @@ -336,24 +753,37 @@ reset_diva(struct IsdnCardState *cs) if (cs->subtyp == DIVA_IPAC_ISA) { writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0); - } else { + } else if (cs->subtyp == DIVA_IPAC_PCI) { + unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg + + PITA_MISC_REG); + *ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + *ireg = PITA_PARA_MPX_MODE; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0); + } else { /* DIVA 2.0 */ cs->hw.diva.ctrl_reg = 0; /* Reset On */ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); if (cs->subtyp == DIVA_ISA) cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; - else + else { + /* Workaround PCI9060 */ + byteout(cs->hw.diva.pci_cfg + 0x69, 9); cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A; + } byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); } restore_flags(flags); @@ -366,7 +796,7 @@ diva_led_handler(struct IsdnCardState *cs) { int blink = 0; - if (cs->subtyp == DIVA_IPAC_ISA) + if ((cs->subtyp == DIVA_IPAC_ISA) || (cs->subtyp == DIVA_IPAC_PCI)) return; del_timer(&cs->hw.diva.tl); if (cs->hw.diva.status & DIVA_ASSIGN) @@ -399,7 +829,7 @@ diva_led_handler(struct IsdnCardState *cs) static int Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - u_int irq_flag = I4L_IRQ_FLAG; + u_int *ireg; switch (mt) { case CARD_RESET: @@ -408,17 +838,11 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_diva(cs); return(0); - case CARD_SETIRQ: - if (cs->subtyp == DIVA_PCI) - irq_flag |= SA_SHIRQ; - if (cs->subtyp == DIVA_IPAC_ISA) { - return(request_irq(cs->irq, &diva_interrupt_ipac, - irq_flag, "HiSax", cs)); - } else { - return(request_irq(cs->irq, &diva_interrupt, - irq_flag, "HiSax", cs)); - } case CARD_INIT: + if (cs->subtyp == DIVA_IPAC_PCI) { + ireg = (unsigned int *)cs->hw.diva.pci_cfg; + *ireg = PITA_INT0_ENABLE; + } inithscxisac(cs, 3); return(0); case CARD_TEST: @@ -451,15 +875,20 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) } break; } - if (cs->subtyp != DIVA_IPAC_ISA) + if ((cs->subtyp != DIVA_IPAC_ISA) && (cs->subtyp != DIVA_IPAC_PCI)) diva_led_handler(cs); return(0); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_diva __initdata = NULL; static struct pci_dev *dev_diva_u __initdata = NULL; +static struct pci_dev *dev_diva201 __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif -int __init +int __init setup_diva(struct IsdnCard *card) { int bytecnt; @@ -478,7 +907,7 @@ setup_diva(struct IsdnCard *card) val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR, cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID); printk(KERN_INFO "Diva: IPAC version %x\n", val); - if (val == 1) { + if ((val == 1) || (val==2)) { cs->subtyp = DIVA_IPAC_ISA; cs->hw.diva.ctrl = 0; cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA; @@ -498,6 +927,7 @@ setup_diva(struct IsdnCard *card) bytecnt = 8; } else { #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "Diva: no PCI bus present\n"); return(0); @@ -506,20 +936,26 @@ setup_diva(struct IsdnCard *card) cs->subtyp = 0; if ((dev_diva = pci_find_device(PCI_VENDOR_EICON_DIEHL, PCI_DIVA20_ID, dev_diva))) { - cs->subtyp = DIVA_PCI; - /* get IRQ */ + cs->subtyp = DIVA_PCI; cs->irq = dev_diva->irq; - /* get IO address */ - cs->hw.diva.cfg_reg = dev_diva->base_address[2] + cs->hw.diva.cfg_reg = get_pcibase(dev_diva, 2) & PCI_BASE_ADDRESS_IO_MASK; } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_EICON_DIEHL, PCI_DIVA20_U_ID, dev_diva_u))) { - cs->subtyp = DIVA_PCI; - /* get IRQ */ + cs->subtyp = DIVA_PCI; cs->irq = dev_diva_u->irq; - /* get IO address */ - cs->hw.diva.cfg_reg = dev_diva_u->base_address[2] + cs->hw.diva.cfg_reg = get_pcibase(dev_diva_u, 2) & PCI_BASE_ADDRESS_IO_MASK; + } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA_201, dev_diva201))) { + cs->subtyp = DIVA_IPAC_PCI; + cs->irq = dev_diva201->irq; + cs->hw.diva.pci_cfg = + (ulong) ioremap((get_pcibase(dev_diva201, 0) + & PCI_BASE_ADDRESS_IO_MASK), 4096); + cs->hw.diva.cfg_reg = + (ulong) ioremap((get_pcibase(dev_diva201, 1) + & PCI_BASE_ADDRESS_IO_MASK), 4096); } else { printk(KERN_WARNING "Diva: No PCI card found\n"); return(0); @@ -534,35 +970,112 @@ setup_diva(struct IsdnCard *card) printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); return(0); } - cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL; - cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA; - cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA; - cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR; - cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR; - bytecnt = 32; +#else + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_U_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA_201, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_IPAC_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + if (cs->subtyp == DIVA_IPAC_PCI) { + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + cs->hw.diva.pci_cfg = (ulong) ioremap(pci_ioaddr, + 4096); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + cs->hw.diva.cfg_reg = (ulong) ioremap(pci_ioaddr, + 4096); + } else { + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + cs->hw.diva.pci_cfg = pci_ioaddr & ~3; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr); + cs->hw.diva.cfg_reg = pci_ioaddr & ~3; + } + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Diva: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Diva: No IRQ for PCI card found\n"); + return(0); + } + if (!pci_ioaddr) { + printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); + return(0); + } + cs->irq = pci_irq; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->irq_flags |= SA_SHIRQ; #else printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n"); printk(KERN_WARNING "Diva: unable to config DIVA PCI\n"); return (0); #endif /* CONFIG_PCI */ + if (cs->subtyp == DIVA_IPAC_PCI) { + cs->hw.diva.ctrl = 0; + cs->hw.diva.isac = 0; + cs->hw.diva.hscx = 0; + cs->hw.diva.isac_adr = 0; + cs->hw.diva.hscx_adr = 0; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + bytecnt = 0; + } else { + cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL; + cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA; + cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR; + cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR; + bytecnt = 32; + } } printk(KERN_INFO - "Diva: %s card configured at 0x%x IRQ %d\n", + "Diva: %s card configured at %#lx IRQ %d\n", (cs->subtyp == DIVA_PCI) ? "PCI" : - (cs->subtyp == DIVA_ISA) ? "ISA" : "IPAC", + (cs->subtyp == DIVA_ISA) ? "ISA" : + (cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" : "IPAC PCI", cs->hw.diva.cfg_reg, cs->irq); - if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { - printk(KERN_WARNING - "HiSax: %s config port %x-%x already in use\n", - CardType[card->typ], - cs->hw.diva.cfg_reg, - cs->hw.diva.cfg_reg + bytecnt); - return (0); - } else { - request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn"); + if ((cs->subtyp == DIVA_IPAC_PCI) || (cs->subtyp == DIVA_PCI)) + printk(KERN_INFO "Diva: %s PCI space at %#lx\n", + (cs->subtyp == DIVA_PCI) ? "PCI" : "IPAC PCI", + cs->hw.diva.pci_cfg); + if (cs->subtyp != DIVA_IPAC_PCI) { + if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %lx-%lx already in use\n", + CardType[card->typ], + cs->hw.diva.cfg_reg, + cs->hw.diva.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn"); + } } - reset_diva(cs); cs->BC_Read_Reg = &ReadHSCX; cs->BC_Write_Reg = &WriteHSCX; @@ -573,9 +1086,21 @@ setup_diva(struct IsdnCard *card) cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; cs->writeisacfifo = &WriteISACfifo_IPAC; + cs->irq_func = &diva_irq_ipac_isa; val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID); printk(KERN_INFO "Diva: IPAC version %x\n", val); - } else { + } else if (cs->subtyp == DIVA_IPAC_PCI) { + cs->readisac = &MemReadISAC_IPAC; + cs->writeisac = &MemWriteISAC_IPAC; + cs->readisacfifo = &MemReadISACfifo_IPAC; + cs->writeisacfifo = &MemWriteISACfifo_IPAC; + cs->BC_Read_Reg = &MemReadHSCX; + cs->BC_Write_Reg = &MemWriteHSCX; + cs->BC_Send_Data = &Memhscx_fill_fifo; + cs->irq_func = &diva_irq_ipac_pci; + val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID); + printk(KERN_INFO "Diva: IPAC version %x\n", val); + } else { /* DIVA 2.0 */ cs->hw.diva.tl.function = (void *) diva_led_handler; cs->hw.diva.tl.data = (long) cs; init_timer(&cs->hw.diva.tl); @@ -583,6 +1108,7 @@ setup_diva(struct IsdnCard *card) cs->writeisac = &WriteISAC; cs->readisacfifo = &ReadISACfifo; cs->writeisacfifo = &WriteISACfifo; + cs->irq_func = &diva_interrupt; ISACVersion(cs, "Diva:"); if (HscxVersion(cs, "Diva:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index 50f9df9c0..c2d6cf2d3 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1,4 +1,4 @@ -/* $Id: elsa.c,v 2.12 1998/11/15 23:54:35 keil Exp $ +/* $Id: elsa.c,v 2.18 1999/08/25 16:50:54 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * @@ -13,8 +13,29 @@ * Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE) * for ELSA PCMCIA support * - * * $Log: elsa.c,v $ + * Revision 2.18 1999/08/25 16:50:54 keil + * Fix bugs which cause 2.3.14 hangs (waitqueue init) + * + * Revision 2.17 1999/08/11 20:57:40 keil + * bugfix IPAC version 1.1 + * new PCI codefix + * + * Revision 2.16 1999/08/10 16:01:51 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 2.15 1999/08/09 19:25:21 keil + * Support (alpha version) for the '98 model of ELSA Microlink ISDN/MC + * by Christer Weinigel, Cendio Systems AB <wingel@cendio.se> + * Add support for IPAC 1.2 + * + * Revision 2.14 1999/07/12 21:05:07 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.13 1999/07/01 08:11:31 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.12 1998/11/15 23:54:35 keil * changes from 2.0 * @@ -70,16 +91,19 @@ #include "hscx.h" #include "isdnl1.h" #include <linux/pci.h> - -//#define KDEBUG_DEF -//#include "../kdebug.h" +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif +#include <linux/serial.h> +#include <linux/serial_reg.h> extern const char *CardType[]; -static const char *Elsa_revision = "$Revision: 2.12 $"; +const char *Elsa_revision = "$Revision: 2.18 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", - "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"}; + "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI", + "PCMCIA-IPAC" }; const char *ITACVer[] = {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2", @@ -109,6 +133,7 @@ const char *ITACVer[] = #define ELSA_QS3000 8 #define ELSA_QS1000PCI 9 #define ELSA_QS3000PCI 10 +#define ELSA_PCMCIA_IPAC 11 /* PCI stuff */ #define PCI_VENDOR_ELSA 0x1048 @@ -158,22 +183,32 @@ const char *ITACVer[] = #define ELSA_IPAC_LINE_LED 0x40 /* Bit 6 Gelbe LED */ #define ELSA_IPAC_STAT_LED 0x80 /* Bit 7 Gruene LED */ -const u_char ARCOFI_VERSION[] = {2,0xa0,0}; -const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */ -const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */ -const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */ -const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */ -const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}; /* RX */ -const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */ -const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR UP */ -const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* Normal OP */ -const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}; +#if ARCOFI_USE +static struct arcofi_msg ARCOFI_XOP_F = + {NULL,0,2,{0xa1,0x3f,0,0,0,0,0,0,0,0}}; /* Normal OP */ +static struct arcofi_msg ARCOFI_XOP_1 = + {&ARCOFI_XOP_F,0,2,{0xa1,0x31,0,0,0,0,0,0,0,0}}; /* PWR UP */ +static struct arcofi_msg ARCOFI_SOP_F = + {&ARCOFI_XOP_1,0,10,{0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}}; +static struct arcofi_msg ARCOFI_COP_9 = + {&ARCOFI_SOP_F,0,10,{0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}}; /* RX */ +static struct arcofi_msg ARCOFI_COP_8 = + {&ARCOFI_COP_9,0,10,{0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}}; /* TX */ +static struct arcofi_msg ARCOFI_COP_7 = + {&ARCOFI_COP_8,0,4,{0xa1,0x27,0x80,0x80,0,0,0,0,0,0}}; /* GZ */ +static struct arcofi_msg ARCOFI_COP_6 = + {&ARCOFI_COP_7,0,6,{0xa1,0x26,0,0,0x82,0x7c,0,0,0,0}}; /* GRL GRH */ +static struct arcofi_msg ARCOFI_COP_5 = + {&ARCOFI_COP_6,0,4,{0xa1,0x25,0xbb,0x4a,0,0,0,0,0,0}}; /* GTX */ +static struct arcofi_msg ARCOFI_VERSION = + {NULL,1,2,{0xa0,0,0,0,0,0,0,0,0,0}}; +static struct arcofi_msg ARCOFI_XOP_0 = + {NULL,0,2,{0xa1,0x30,0,0,0,0,0,0,0,0}}; /* PWR Down */ static void set_arcofi(struct IsdnCardState *cs, int bc); -#if ARCOFI_USE #include "elsa_ser.c" -#endif +#endif /* ARCOFI_USE */ static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off) @@ -343,7 +378,7 @@ elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char val; - int icnt=20; + int icnt=5; if (!cs) { printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); @@ -400,6 +435,7 @@ elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) cs->hw.elsa.counter++; } } +#if ARCOFI_USE if (cs->hw.elsa.MFlag) { val = serial_inp(cs, UART_MCR); val ^= 0x8; @@ -408,6 +444,7 @@ elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) val ^= 0x8; serial_outp(cs, UART_MCR, val); } +#endif if (cs->hw.elsa.trig) byteout(cs->hw.elsa.trig, 0x00); writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0); @@ -420,15 +457,17 @@ elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char ista,val; - int icnt=20; + int icnt=5; if (!cs) { printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); return; } - val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */ - if (!(val & ELSA_PCI_IRQ_MASK)) - return; + if (cs->subtyp == ELSA_QS1000PCI || cs->subtyp == ELSA_QS3000PCI) { + val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */ + if (!(val & ELSA_PCI_IRQ_MASK)) + return; + } #if ARCOFI_USE if (cs->hw.elsa.MFlag) { val = serial_inp(cs, UART_IIR); @@ -480,6 +519,9 @@ release_io_elsa(struct IsdnCardState *cs) int bytecnt = 8; del_timer(&cs->hw.elsa.tl); +#if ARCOFI_USE + clear_arcofi(cs); +#endif if (cs->hw.elsa.ctrl) byteout(cs->hw.elsa.ctrl, 0); /* LEDs Out */ if (cs->subtyp == ELSA_QS1000PCI) { @@ -493,12 +535,17 @@ release_io_elsa(struct IsdnCardState *cs) writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); release_region(cs->hw.elsa.cfg, 0x80); } + if (cs->subtyp == ELSA_PCMCIA_IPAC) { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); + } if ((cs->subtyp == ELSA_PCFPRO) || (cs->subtyp == ELSA_QS3000) || (cs->subtyp == ELSA_PCF) || (cs->subtyp == ELSA_QS3000PCI)) { bytecnt = 16; +#if ARCOFI_USE release_modem(cs); +#endif } if (cs->hw.elsa.base) release_region(cs->hw.elsa.base, bytecnt); @@ -527,19 +574,25 @@ reset_elsa(struct IsdnCardState *cs) if (cs->hw.elsa.trig) byteout(cs->hw.elsa.trig, 0xff); } - if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) { save_flags(flags); sti(); writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00); current->state = TASK_INTERRUPTIBLE; - schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); + schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ restore_flags(flags); - writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0); - writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c); + if (cs->subtyp != ELSA_PCMCIA_IPAC) { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c); + } else { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_PCFG, 0x10); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x4); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0xf8); + } writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); if (cs->subtyp == ELSA_QS1000PCI) byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */ @@ -548,69 +601,41 @@ reset_elsa(struct IsdnCardState *cs) } } -static void -init_arcofi(struct IsdnCardState *cs) { - send_arcofi(cs, ARCOFI_XOP_0, 1, 0); -/* send_arcofi(cs, ARCOFI_XOP_F, 1); -*/ -} - -#define ARCDEL 500 +#if ARCOFI_USE static void set_arcofi(struct IsdnCardState *cs, int bc) { - long flags; - - debugl1(cs,"set_arcofi bc=%d", bc); - save_flags(flags); - sti(); - send_arcofi(cs, ARCOFI_XOP_0, bc, 0); - udelay(ARCDEL); - send_arcofi(cs, ARCOFI_COP_5, bc, 0); - udelay(ARCDEL); - send_arcofi(cs, ARCOFI_COP_6, bc, 0); - udelay(ARCDEL); - send_arcofi(cs, ARCOFI_COP_7, bc, 0); - udelay(ARCDEL); - send_arcofi(cs, ARCOFI_COP_8, bc, 0); - udelay(ARCDEL); - send_arcofi(cs, ARCOFI_COP_9, bc, 0); - udelay(ARCDEL); - send_arcofi(cs, ARCOFI_SOP_F, bc, 0); - udelay(ARCDEL); - send_arcofi(cs, ARCOFI_XOP_1, bc, 0); - udelay(ARCDEL); - send_arcofi(cs, ARCOFI_XOP_F, bc, 0); - restore_flags(flags); - debugl1(cs,"end set_arcofi bc=%d", bc); + cs->dc.isac.arcofi_bc = bc; + arcofi_fsm(cs, ARCOFI_START, &ARCOFI_COP_5); + interruptible_sleep_on(&cs->dc.isac.arcofi_wait); } static int check_arcofi(struct IsdnCardState *cs) { -#if ARCOFI_USE int arcofi_present = 0; char tmp[40]; char *t; u_char *p; - if (!cs->mon_tx) - if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (!cs->dc.isac.mon_tx) + if (!(cs->dc.isac.mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC MON TX out of buffers!"); return(0); } - send_arcofi(cs, ARCOFI_VERSION, 0, 1); - if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) { - if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) { - debugl1(cs, "Arcofi response received %d bytes", cs->mon_rxp); - p = cs->mon_rx; + cs->dc.isac.arcofi_bc = 0; + arcofi_fsm(cs, ARCOFI_START, &ARCOFI_VERSION); + interruptible_sleep_on(&cs->dc.isac.arcofi_wait); + if (!test_and_clear_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags)) { + debugl1(cs, "Arcofi response received %d bytes", cs->dc.isac.mon_rxp); + p = cs->dc.isac.mon_rx; t = tmp; t += sprintf(tmp, "Arcofi data"); - QuickHex(t, p, cs->mon_rxp); + QuickHex(t, p, cs->dc.isac.mon_rxp); debugl1(cs, tmp); - if ((cs->mon_rxp == 2) && (cs->mon_rx[0] == 0xa0)) { - switch(cs->mon_rx[1]) { + if ((cs->dc.isac.mon_rxp == 2) && (cs->dc.isac.mon_rx[0] == 0xa0)) { + switch(cs->dc.isac.mon_rx[1]) { case 0x80: debugl1(cs, "Arcofi 2160 detected"); arcofi_present = 1; @@ -629,9 +654,8 @@ check_arcofi(struct IsdnCardState *cs) } } else debugl1(cs, "undefined Monitor response"); - cs->mon_rxp = 0; - } - } else if (cs->mon_tx) { + cs->dc.isac.mon_rxp = 0; + } else if (cs->dc.isac.mon_tx) { debugl1(cs, "Arcofi not detected"); } if (arcofi_present) { @@ -672,19 +696,20 @@ check_arcofi(struct IsdnCardState *cs) "Elsa: %s detected modem at 0x%x\n", Elsa_Types[cs->subtyp], cs->hw.elsa.base+8); - init_arcofi(cs); + arcofi_fsm(cs, ARCOFI_START, &ARCOFI_XOP_0); + interruptible_sleep_on(&cs->dc.isac.arcofi_wait); return(1); } -#endif return(0); } +#endif /* ARCOFI_USE */ static void elsa_led_handler(struct IsdnCardState *cs) { int blink = 0; - if (cs->subtyp == ELSA_PCMCIA) + if (cs->subtyp == ELSA_PCMCIA || cs->subtyp == ELSA_PCMCIA_IPAC) return; del_timer(&cs->hw.elsa.tl); if (cs->hw.elsa.status & ELSA_ASSIGN) @@ -723,8 +748,7 @@ elsa_led_handler(struct IsdnCardState *cs) static int Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int len, ret = 0; - u_char *msg; + int ret = 0; long flags; switch (mt) { @@ -734,15 +758,6 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_elsa(cs); return(0); - case CARD_SETIRQ: - if ((cs->subtyp == ELSA_QS1000PCI) || - (cs->subtyp == ELSA_QS3000PCI)) - ret = request_irq(cs->irq, &elsa_interrupt_ipac, - I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs); - else - ret = request_irq(cs->irq, &elsa_interrupt, - I4L_IRQ_FLAG, "HiSax", cs); - return(ret); case CARD_INIT: cs->debug |= L1_DEB_IPAC; inithscxisac(cs, 1); @@ -757,6 +772,7 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); case CARD_TEST: if ((cs->subtyp == ELSA_PCMCIA) || + (cs->subtyp == ELSA_PCMCIA_IPAC) || (cs->subtyp == ELSA_QS1000PCI)) { return(0); } else if (cs->subtyp == ELSA_QS3000PCI) { @@ -770,7 +786,6 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); byteout(cs->hw.elsa.timer, 0); current->state = TASK_INTERRUPTIBLE; - /* Timeout 110ms */ schedule_timeout((110*HZ)/1000); restore_flags(flags); cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; @@ -822,8 +837,12 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) cs->hw.elsa.status &= ~0x0100; } break; +#if ARCOFI_USE case CARD_AUX_IND: if (cs->hw.elsa.MFlag) { + int len; + u_char *msg; + if (!arg) return(0); msg = arg; @@ -832,6 +851,7 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) modem_write_cmd(cs, msg, len); } break; +#endif } if (cs->typ == ISDN_CTYPE_ELSA) { int pwr = bytein(cs->hw.elsa.ale); @@ -908,8 +928,12 @@ probe_elsa(struct IsdnCardState *cs) return (CARD_portlist[i]); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_qs1000 __initdata = NULL; static struct pci_dev *dev_qs3000 __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif int setup_elsa(struct IsdnCard *card) @@ -1004,10 +1028,19 @@ setup_elsa(struct IsdnCard *card) } else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) { cs->hw.elsa.base = card->para[1]; cs->irq = card->para[0]; - cs->subtyp = ELSA_PCMCIA; - cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; - cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; - cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + val = readreg(cs->hw.elsa.base + 0, cs->hw.elsa.base + 2, IPAC_ID); + if ((val == 1) || (val == 2)) { /* IPAC version 1.1/1.2 */ + cs->subtyp = ELSA_PCMCIA_IPAC; + cs->hw.elsa.ale = cs->hw.elsa.base + 0; + cs->hw.elsa.isac = cs->hw.elsa.base + 2; + cs->hw.elsa.hscx = cs->hw.elsa.base + 2; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + cs->subtyp = ELSA_PCMCIA; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + } cs->hw.elsa.timer = 0; cs->hw.elsa.trig = 0; cs->hw.elsa.ctrl = 0; @@ -1018,6 +1051,7 @@ setup_elsa(struct IsdnCard *card) cs->irq); } else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "Elsa: no PCI bus present\n"); return(0); @@ -1027,17 +1061,17 @@ setup_elsa(struct IsdnCard *card) dev_qs1000))) { cs->subtyp = ELSA_QS1000PCI; cs->irq = dev_qs1000->irq; - cs->hw.elsa.cfg = dev_qs1000->base_address[1] & + cs->hw.elsa.cfg = get_pcibase(dev_qs1000, 1) & PCI_BASE_ADDRESS_IO_MASK; - cs->hw.elsa.base = dev_qs1000->base_address[3] & + cs->hw.elsa.base = get_pcibase(dev_qs1000, 3) & PCI_BASE_ADDRESS_IO_MASK; } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ELSA, PCI_QS3000_ID, dev_qs3000))) { cs->subtyp = ELSA_QS3000PCI; cs->irq = dev_qs3000->irq; - cs->hw.elsa.cfg = dev_qs3000->base_address[1] & + cs->hw.elsa.cfg = get_pcibase(dev_qs3000, 1) & PCI_BASE_ADDRESS_IO_MASK; - cs->hw.elsa.base = dev_qs3000->base_address[3] & + cs->hw.elsa.base = get_pcibase(dev_qs3000, 3) & PCI_BASE_ADDRESS_IO_MASK; } else { printk(KERN_WARNING "Elsa: No PCI card found\n"); @@ -1052,18 +1086,6 @@ setup_elsa(struct IsdnCard *card) printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); return(0); } - cs->hw.elsa.ale = cs->hw.elsa.base; - cs->hw.elsa.isac = cs->hw.elsa.base +1; - cs->hw.elsa.hscx = cs->hw.elsa.base +1; - test_and_set_bit(HW_IPAC, &cs->HW_Flags); - cs->hw.elsa.timer = 0; - cs->hw.elsa.trig = 0; - printk(KERN_INFO - "Elsa: %s defined at 0x%x/0x%x IRQ %d\n", - Elsa_Types[cs->subtyp], - cs->hw.elsa.base, - cs->hw.elsa.cfg, - cs->irq); if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) { printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n"); printk(KERN_WARNING "Elsa: If your system hangs now, read\n"); @@ -1075,6 +1097,67 @@ setup_elsa(struct IsdnCard *card) restore_flags(flags); } #else + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_ELSA, + PCI_QS1000_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = ELSA_QS1000PCI; + else if (pcibios_find_device(PCI_VENDOR_ELSA, + PCI_QS3000_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = ELSA_QS3000PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.cfg = pci_ioaddr; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_3, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Elsa: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.base = pci_ioaddr; + cs->irq = pci_irq; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->hw.elsa.ale = cs->hw.elsa.base; + cs->hw.elsa.isac = cs->hw.elsa.base +1; + cs->hw.elsa.hscx = cs->hw.elsa.base +1; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + cs->irq_flags |= SA_SHIRQ; + printk(KERN_INFO + "Elsa: %s defined at 0x%x/0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->hw.elsa.cfg, + cs->irq); +#else printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n"); printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n"); return (0); @@ -1088,6 +1171,7 @@ setup_elsa(struct IsdnCard *card) case ELSA_PCC16: case ELSA_QS1000: case ELSA_PCMCIA: + case ELSA_PCMCIA_IPAC: bytecnt = 8; break; case ELSA_PCFPRO: @@ -1129,6 +1213,9 @@ setup_elsa(struct IsdnCard *card) request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci"); } } +#if ARCOFI_USE + init_arcofi(cs); +#endif cs->hw.elsa.tl.function = (void *) elsa_led_handler; cs->hw.elsa.tl.data = (long) cs; init_timer(&cs->hw.elsa.tl); @@ -1161,11 +1248,12 @@ setup_elsa(struct IsdnCard *card) cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Elsa_card_msg; reset_elsa(cs); - if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) { cs->readisac = &ReadISAC_IPAC; cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; cs->writeisacfifo = &WriteISACfifo_IPAC; + cs->irq_func = &elsa_interrupt_ipac; val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID); printk(KERN_INFO "Elsa: IPAC version %x\n", val); } else { @@ -1173,6 +1261,7 @@ setup_elsa(struct IsdnCard *card) cs->writeisac = &WriteISAC; cs->readisacfifo = &ReadISACfifo; cs->writeisacfifo = &WriteISACfifo; + cs->irq_func = &elsa_interrupt; ISACVersion(cs, "Elsa:"); if (HscxVersion(cs, "Elsa:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index f5ec29839..e4b35e502 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -298,7 +298,7 @@ modem_fill(struct BCState *bcs) { (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; } } @@ -510,13 +510,13 @@ close_elsastate(struct BCState *bcs) bcs->hw.hscx.rcvbuf = NULL; } while ((skb = skb_dequeue(&bcs->rqueue))) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } while ((skb = skb_dequeue(&bcs->squeue))) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_WRITE); } if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -664,7 +664,9 @@ modem_l2l1(struct PStack *st, int pr, void *arg) st->l1.bcs->cs->hw.elsa.MFlag=2; } else if (pr == (PH_DEACTIVATE | REQUEST)) { test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); - send_arcofi(st->l1.bcs->cs, ARCOFI_XOP_0, st->l1.bc, 0); + st->l1.bcs->cs->dc.isac.arcofi_bc = st->l1.bc; + arcofi_fsm(st->l1.bcs->cs, ARCOFI_START, &ARCOFI_XOP_0); + interruptible_sleep_on(&st->l1.bcs->cs->dc.isac.arcofi_wait); st->l1.bcs->cs->hw.elsa.MFlag=1; } else { printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr); diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c new file mode 100644 index 000000000..ef64a4702 --- /dev/null +++ b/drivers/isdn/hisax/gazel.c @@ -0,0 +1,758 @@ +/* $Id: gazel.c,v 2.6 1999/08/22 20:27:03 calle Exp $ + + * gazel.c low level stuff for Gazel isdn cards + * + * Author BeWan Systems + * based on source code from Karsten Keil + * + * $Log: gazel.c,v $ + * Revision 2.6 1999/08/22 20:27:03 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 2.5 1999/08/11 21:01:26 keil + * new PCI codefix + * + * Revision 2.4 1999/08/10 16:01:54 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 2.3 1999/07/12 21:05:09 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.1 1999/07/08 21:26:17 keil + * new card + * + * Revision 1.0 1999/28/06 + * Initial revision + * + */ +#include <linux/config.h> +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include "ipac.h" +#include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif + +extern const char *CardType[]; +const char *gazel_revision = "$Revision: 2.6 $"; + +#define R647 1 +#define R685 2 +#define R753 3 +#define R742 4 + +/* Gazel R685 stuff */ +#define GAZEL_MANUFACTURER 0x10b5 +#define GAZEL_R685 0x1030 +#define GAZEL_R753 0x1152 +#define GAZEL_DJINN_ITOO 0x1151 + +#define PLX_CNTRL 0x50 /* registre de controle PLX */ +#define RESET_GAZEL 0x4 +#define RESET_9050 0x40000000 +#define PLX_INCSR 0x4C /* registre d'IT du 9050 */ +#define INT_ISAC_EN 0x8 /* 1 = enable IT isac */ +#define INT_ISAC 0x20 /* 1 = IT isac en cours */ +#define INT_HSCX_EN 0x1 /* 1 = enable IT hscx */ +#define INT_HSCX 0x4 /* 1 = IT hscx en cours */ +#define INT_PCI_EN 0x40 /* 1 = enable IT PCI */ +#define INT_IPAC_EN 0x3 /* enable IT ipac */ + + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static inline u_char +readreg(unsigned int adr, u_short off) +{ + return bytein(adr + off); +} + +static inline void +writereg(unsigned int adr, u_short off, u_char data) +{ + byteout(adr + off, data); +} + + +static inline void +read_fifo(unsigned int adr, u_char * data, int size) +{ + insb(adr, data, size); +} + +static void +write_fifo(unsigned int adr, u_char * data, int size) +{ + outsb(adr, data, size); +} + +static inline u_char +readreg_ipac(unsigned int adr, u_short off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(adr, off); + ret = bytein(adr + 4); + restore_flags(flags); + return ret; +} + +static inline void +writereg_ipac(unsigned int adr, u_short off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(adr, off); + byteout(adr + 4, data); + restore_flags(flags); +} + + +static inline void +read_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size) +{ + byteout(adr, off); + insb(adr + 4, data, size); +} + +static void +write_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size) +{ + byteout(adr, off); + outsb(adr + 4, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + u_short off2 = offset; + + switch (cs->subtyp) { + case R647: + off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf)); + case R685: + return (readreg(cs->hw.gazel.isac, off2)); + case R753: + case R742: + return (readreg_ipac(cs->hw.gazel.ipac, 0x80 + off2)); + } + return 0; +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + u_short off2 = offset; + + switch (cs->subtyp) { + case R647: + off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf)); + case R685: + writereg(cs->hw.gazel.isac, off2, value); + break; + case R753: + case R742: + writereg_ipac(cs->hw.gazel.ipac, 0x80 + off2, value); + break; + } +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + switch (cs->subtyp) { + case R647: + case R685: + read_fifo(cs->hw.gazel.isacfifo, data, size); + break; + case R753: + case R742: + read_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size); + break; + } +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + switch (cs->subtyp) { + case R647: + case R685: + write_fifo(cs->hw.gazel.isacfifo, data, size); + break; + case R753: + case R742: + write_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size); + break; + } +} + +static void +ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size) +{ + switch (cs->subtyp) { + case R647: + case R685: + read_fifo(cs->hw.gazel.hscxfifo[hscx], data, size); + break; + case R753: + case R742: + read_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size); + break; + } +} + +static void +WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size) +{ + switch (cs->subtyp) { + case R647: + case R685: + write_fifo(cs->hw.gazel.hscxfifo[hscx], data, size); + break; + case R753: + case R742: + write_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size); + break; + } +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + u_short off2 = offset; + + switch (cs->subtyp) { + case R647: + off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf)); + case R685: + return (readreg(cs->hw.gazel.hscx[hscx], off2)); + case R753: + case R742: + return (readreg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2)); + } + return 0; +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + u_short off2 = offset; + + switch (cs->subtyp) { + case R647: + off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf)); + case R685: + writereg(cs->hw.gazel.hscx[hscx], off2, value); + break; + case R753: + case R742: + writereg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2, value); + break; + } +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt) + +#include "hscx_irq.c" + +static void +gazel_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ +#define MAXCOUNT 5 + struct IsdnCardState *cs = dev_id; + u_char valisac, valhscx; + int count = 0; + + if (!cs) { + printk(KERN_WARNING "Gazel: Spurious interrupt!\n"); + return; + } + do { + valhscx = ReadHSCX(cs, 1, HSCX_ISTA); + if (valhscx) + hscx_int_main(cs, valhscx); + valisac = ReadISAC(cs, ISAC_ISTA); + if (valisac) + isac_interrupt(cs, valisac); + count++; + } while ((valhscx || valisac) && (count < MAXCOUNT)); + + WriteHSCX(cs, 0, HSCX_MASK, 0xFF); + WriteHSCX(cs, 1, HSCX_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); + WriteHSCX(cs, 0, HSCX_MASK, 0x0); + WriteHSCX(cs, 1, HSCX_MASK, 0x0); +} + + +static void +gazel_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista, val; + int count = 0; + + if (!cs) { + printk(KERN_WARNING "Gazel: Spurious interrupt!\n"); + return; + } + ista = ReadISAC(cs, IPAC_ISTA - 0x80); + do { + if (ista & 0x0f) { + val = ReadHSCX(cs, 1, HSCX_ISTA); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) { + hscx_int_main(cs, val); + } + } + if (ista & 0x20) { + val = 0xfe & ReadISAC(cs, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = ReadISAC(cs, IPAC_ISTA - 0x80); + count++; + } + while ((ista & 0x3f) && (count < MAXCOUNT)); + + WriteISAC(cs, IPAC_MASK - 0x80, 0xFF); + WriteISAC(cs, IPAC_MASK - 0x80, 0xC0); +} +void +release_io_gazel(struct IsdnCardState *cs) +{ + unsigned int i; + + switch (cs->subtyp) { + case R647: + for (i = 0x0000; i < 0xC000; i += 0x1000) + release_region(i + cs->hw.gazel.hscx[0], 16); + release_region(0xC000 + cs->hw.gazel.hscx[0], 1); + break; + + case R685: + release_region(cs->hw.gazel.hscx[0], 0x100); + release_region(cs->hw.gazel.cfg_reg, 0x80); + break; + + case R753: + release_region(cs->hw.gazel.ipac, 0x8); + release_region(cs->hw.gazel.cfg_reg, 0x80); + break; + + case R742: + release_region(cs->hw.gazel.ipac, 8); + break; + } +} + +static int +reset_gazel(struct IsdnCardState *cs) +{ + long flags; + unsigned long plxcntrl, addr = cs->hw.gazel.cfg_reg; + + switch (cs->subtyp) { + case R647: + save_flags(flags); + cli(); + writereg(addr, 0, 0); + HZDELAY(10); + writereg(addr, 0, 1); + HZDELAY(2); + restore_flags(flags); + break; + case R685: + plxcntrl = inl(addr + PLX_CNTRL); + plxcntrl |= (RESET_9050 + RESET_GAZEL); + outl(plxcntrl, addr + PLX_CNTRL); + plxcntrl &= ~(RESET_9050 + RESET_GAZEL); + HZDELAY(4); + outl(plxcntrl, addr + PLX_CNTRL); + HZDELAY(10); + outb(INT_ISAC_EN + INT_HSCX_EN + INT_PCI_EN, addr + PLX_INCSR); + break; + case R753: + plxcntrl = inl(addr + PLX_CNTRL); + plxcntrl |= (RESET_9050 + RESET_GAZEL); + outl(plxcntrl, addr + PLX_CNTRL); + plxcntrl &= ~(RESET_9050 + RESET_GAZEL); + WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20); + HZDELAY(4); + outl(plxcntrl, addr + PLX_CNTRL); + HZDELAY(10); + WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00); + WriteISAC(cs, IPAC_ACFG - 0x80, 0xff); + WriteISAC(cs, IPAC_AOE - 0x80, 0x0); + WriteISAC(cs, IPAC_MASK - 0x80, 0xff); + WriteISAC(cs, IPAC_CONF - 0x80, 0x1); + outb(INT_IPAC_EN + INT_PCI_EN, addr + PLX_INCSR); + WriteISAC(cs, IPAC_MASK - 0x80, 0xc0); + break; + case R742: + WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20); + HZDELAY(4); + WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00); + WriteISAC(cs, IPAC_ACFG - 0x80, 0xff); + WriteISAC(cs, IPAC_AOE - 0x80, 0x0); + WriteISAC(cs, IPAC_MASK - 0x80, 0xff); + WriteISAC(cs, IPAC_CONF - 0x80, 0x1); + WriteISAC(cs, IPAC_MASK - 0x80, 0xc0); + break; + } + return (0); +} + +static int +Gazel_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_gazel(cs); + return (0); + case CARD_RELEASE: + release_io_gazel(cs); + return (0); + case CARD_INIT: + inithscxisac(cs, 1); + if ((cs->subtyp==R647)||(cs->subtyp==R685)) { + int i; + for (i=0;i<(2+MAX_WAITING_CALLS);i++) { + cs->bcs[i].hw.hscx.tsaxr0 = 0x1f; + cs->bcs[i].hw.hscx.tsaxr1 = 0x23; + } + } + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +static int +reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs) +{ + unsigned int i, base = 0, adr = 0, len = 0; + long flags; + + save_flags(flags); + cli(); + + switch (cs->subtyp) { + case R647: + base = cs->hw.gazel.hscx[0]; + for (i = 0x0000; i < 0xC000; i += 0x1000) { + if (check_region(adr = (i + base), len = 16)) + goto error; + } + if (check_region(adr = (0xC000 + base), len = 1)) + goto error; + + for (i = 0x0000; i < 0xC000; i += 0x1000) + request_region(i + base, 16, "gazel"); + request_region(0xC000 + base, 1, "gazel"); + + break; + + case R685: + if (check_region(adr = cs->hw.gazel.hscx[0], len = 0x100)) + goto error; + if (check_region(adr = cs->hw.gazel.cfg_reg, len = 0x80)) + goto error; + + request_region(cs->hw.gazel.hscx[0], 0x100, "gazel"); + request_region(cs->hw.gazel.cfg_reg, 0x80, "gazel"); + break; + + case R753: + if (check_region(adr = cs->hw.gazel.ipac, len = 0x8)) + goto error; + if (check_region(adr = cs->hw.gazel.cfg_reg, len = 0x80)) + goto error; + + request_region(cs->hw.gazel.ipac, 0x8, "gazel"); + request_region(cs->hw.gazel.cfg_reg, 0x80, "gazel"); + break; + + case R742: + if (check_region(adr = cs->hw.gazel.ipac, len = 0x8)) + goto error; + request_region(cs->hw.gazel.ipac, 0x8, "gazel"); + break; + } + + restore_flags(flags); + return 0; + + error: + restore_flags(flags); + printk(KERN_WARNING "Gazel: %s io ports 0x%x-0x%x already in use\n", + CardType[cs->typ], adr, adr + len); + return 1; +} + +static int +setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs) +{ + printk(KERN_INFO "Gazel: ISA PnP card automatic recognition\n"); + // we got an irq parameter, assume it is an ISA card + // R742 decodes address even in not started... + // R647 returns FF if not present or not started + // eventually needs improvment + if (readreg_ipac(card->para[1], IPAC_ID) == 1) + cs->subtyp = R742; + else + cs->subtyp = R647; + + cs->hw.gazel.cfg_reg = card->para[1] + 0xC000; + cs->hw.gazel.ipac = card->para[1]; + cs->hw.gazel.isac = card->para[1] + 0x8000; + cs->hw.gazel.hscx[0] = card->para[1]; + cs->hw.gazel.hscx[1] = card->para[1] + 0x4000; + cs->irq = card->para[0]; + cs->hw.gazel.isacfifo = cs->hw.gazel.isac; + cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0]; + cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1]; + + switch (cs->subtyp) { + case R647: + printk(KERN_INFO "Gazel: Card ISA R647/R648 found\n"); + cs->dc.isac.adf2 = 0x87; + printk(KERN_INFO + "Gazel: config irq:%d isac:0x%X cfg:0x%X\n", + cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg); + printk(KERN_INFO + "Gazel: hscx A:0x%X hscx B:0x%X\n", + cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]); + + break; + case R742: + printk(KERN_INFO "Gazel: Card ISA R742 found\n"); + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + printk(KERN_INFO + "Gazel: config irq:%d ipac:0x%X\n", + cs->irq, cs->hw.gazel.ipac); + break; + } + + return (0); +} + +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_tel __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +static int +setup_gazelpci(struct IsdnCardState *cs) +{ + u_int pci_ioaddr0 = 0, pci_ioaddr1 = 0; + u_char pci_irq = 0, found; + u_int nbseek, seekcard; + + printk(KERN_WARNING "Gazel: PCI card automatic recognition\n"); + + found = 0; +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_WARNING "Gazel: No PCI bus present\n"); + return 1; + } +#endif + seekcard = GAZEL_R685; + for (nbseek = 0; nbseek < 3; nbseek++) { +#ifdef COMPAT_HAS_NEW_PCI + if ((dev_tel = pci_find_device(GAZEL_MANUFACTURER, seekcard, dev_tel))) { + + pci_irq = dev_tel->irq; + pci_ioaddr0 = get_pcibase(dev_tel, 1); + pci_ioaddr1 = get_pcibase(dev_tel, 2); + found = 1; + } +#else + for (; pci_index < 0xff; pci_index++) { + u_char pci_bus, pci_device_fn; + + if (pcibios_find_device(GAZEL_MANUFACTURER, seekcard, + pci_index, &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr0); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr1); + found = 1; + break; + } +#endif /* COMPAT_HAS_NEW_PCI */ + if (found) + break; + else { + switch (seekcard) { + case GAZEL_R685: + seekcard = GAZEL_R753; + break; + case GAZEL_R753: + seekcard = GAZEL_DJINN_ITOO; + break; + } +#ifndef COMPAT_HAS_NEW_PCI + pci_index = 0; +#endif + } + } + if (!found) { + printk(KERN_WARNING "Gazel: No PCI card found\n"); + return (1); + } + if (!pci_irq) { + printk(KERN_WARNING "Gazel: No IRQ for PCI card found\n"); + return 1; + } + cs->hw.gazel.pciaddr[0] = pci_ioaddr0; + cs->hw.gazel.pciaddr[1] = pci_ioaddr1; + + pci_ioaddr1 &= 0xfffe; + cs->hw.gazel.cfg_reg = pci_ioaddr0 & 0xfffe; + cs->hw.gazel.ipac = pci_ioaddr1; + cs->hw.gazel.isac = pci_ioaddr1 + 0x80; + cs->hw.gazel.hscx[0] = pci_ioaddr1; + cs->hw.gazel.hscx[1] = pci_ioaddr1 + 0x40; + cs->hw.gazel.isacfifo = cs->hw.gazel.isac; + cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0]; + cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1]; + cs->irq = pci_irq; + cs->irq_flags |= SA_SHIRQ; + + switch (seekcard) { + case GAZEL_R685: + printk(KERN_INFO "Gazel: Card PCI R685 found\n"); + cs->subtyp = R685; + cs->dc.isac.adf2 = 0x87; + printk(KERN_INFO + "Gazel: config irq:%d isac:0x%X cfg:0x%X\n", + cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg); + printk(KERN_INFO + "Gazel: hscx A:0x%X hscx B:0x%X\n", + cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]); + break; + case GAZEL_R753: + case GAZEL_DJINN_ITOO: + printk(KERN_INFO "Gazel: Card PCI R753 found\n"); + cs->subtyp = R753; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + printk(KERN_INFO + "Gazel: config irq:%d ipac:0x%X cfg:0x%X\n", + cs->irq, cs->hw.gazel.ipac, cs->hw.gazel.cfg_reg); + break; + } + + return (0); +} + +int __init + setup_gazel(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + u_char val; + + strcpy(tmp, gazel_revision); + printk(KERN_INFO "Gazel: Driver Revision %s\n", HiSax_getrev(tmp)); + + if (cs->typ != ISDN_CTYPE_GAZEL) + return (0); + + if (card->para[0]) { + if (setup_gazelisa(card, cs)) + return (0); + } else { + +#if CONFIG_PCI + if (setup_gazelpci(cs)) + return (0); +#else + printk(KERN_WARNING "Gazel: Card PCI requested and NO_PCI_BIOS, unable to config\n"); + return (0); +#endif /* CONFIG_PCI */ + } + + if (reserve_regions(card, cs)) { + return (0); + } + if (reset_gazel(cs)) { + printk(KERN_WARNING "Gazel: wrong IRQ\n"); + release_io_gazel(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Gazel_card_msg; + + switch (cs->subtyp) { + case R647: + case R685: + cs->irq_func = &gazel_interrupt; + ISACVersion(cs, "Gazel:"); + if (HscxVersion(cs, "Gazel:")) { + printk(KERN_WARNING + "Gazel: wrong HSCX versions check IO address\n"); + release_io_gazel(cs); + return (0); + } + break; + case R742: + case R753: + cs->irq_func = &gazel_interrupt_ipac; + val = ReadISAC(cs, IPAC_ID - 0x80); + printk(KERN_INFO "Gazel: IPAC version %x\n", val); + break; + } + + return (1); +} diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c index 4e734b2c6..266916c39 100644 --- a/drivers/isdn/hisax/hfc_2bds0.c +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -1,4 +1,4 @@ -/* $Id: hfc_2bds0.c,v 1.8 1998/11/15 23:54:40 keil Exp $ +/* $Id: hfc_2bds0.c,v 1.9 1999/07/01 08:11:35 keil Exp $ * * specific routines for CCD's HFC 2BDS0 * @@ -6,6 +6,9 @@ * * * $Log: hfc_2bds0.c,v $ + * Revision 1.9 1999/07/01 08:11:35 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.8 1998/11/15 23:54:40 keil * changes from 2.0 * @@ -264,6 +267,7 @@ static struct sk_buff } else if (!(skb = dev_alloc_skb(count - 3))) printk(KERN_WARNING "HFC: receive out of memory\n"); else { + SET_SKB_FREE(skb); ptr = skb_put(skb, count - 3); idx = 0; cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); @@ -281,7 +285,7 @@ static struct sk_buff sti(); debugl1(cs, "RFIFO BUSY error"); printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } else { cli(); @@ -297,7 +301,7 @@ static struct sk_buff bcs->channel, chksum, stat); if (stat) { debugl1(cs, "FIFO CRC error"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } } @@ -386,7 +390,7 @@ hfc_fill_fifo(struct BCState *bcs) if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; } WaitForBusy(cs); @@ -588,7 +592,7 @@ close_2bs0(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -642,7 +646,7 @@ hfcd_bh(struct IsdnCardState *cs) } #endif if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { - switch (cs->ph_state) { + switch (cs->dc.hfcd.ph_state) { case (0): l1_msg(cs, HW_RESET | INDICATION, NULL); break; @@ -733,6 +737,7 @@ int receive_dmsg(struct IsdnCardState *cs) while ((idx++ < rcnt) && WaitNoBusy(cs)) ReadReg(cs, HFCD_DATA_NODEB, cip); } else if ((skb = dev_alloc_skb(rcnt - 3))) { + SET_SKB_FREE(skb); ptr = skb_put(skb, rcnt - 3); while (idx < (rcnt - 3)) { cli(); @@ -747,7 +752,7 @@ int receive_dmsg(struct IsdnCardState *cs) sti(); debugl1(cs, "RFIFO D BUSY error"); printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } else { cli(); @@ -763,7 +768,7 @@ int receive_dmsg(struct IsdnCardState *cs) chksum, stat); if (stat) { debugl1(cs, "FIFO CRC error"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } else { skb_queue_tail(&cs->rq, skb); @@ -860,7 +865,7 @@ hfc_fill_dfifo(struct IsdnCardState *cs) cli(); WaitNoBusy(cs); ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND); - dev_kfree_skb(cs->tx_skb); + idev_kfree_skb(cs->tx_skb, FREE_WRITE); cs->tx_skb = NULL; sti(); WaitForBusy(cs); @@ -895,9 +900,9 @@ hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) if (val & 0x40) { /* TE state machine irq */ exval = cs->readisac(cs, HFCD_STATES) & 0xf; if (cs->debug & L1_DEB_ISAC) - debugl1(cs, "ph_state chg %d->%d", cs->ph_state, + debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcd.ph_state, exval); - cs->ph_state = exval; + cs->dc.hfcd.ph_state = exval; sched_event_D(cs, D_L1STATECHANGE); val &= ~0x40; } @@ -984,7 +989,7 @@ hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) del_timer(&cs->dbusytimer); if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) sched_event_D(cs, D_CLEARBUSY); - if (cs->tx_skb) + if (cs->tx_skb) { if (cs->tx_skb->len) { if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { hfc_fill_dfifo(cs); @@ -994,10 +999,11 @@ hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) } goto afterXPR; } else { - dev_kfree_skb(cs->tx_skb); + idev_kfree_skb(cs->tx_skb, FREE_WRITE); cs->tx_cnt = 0; cs->tx_skb = NULL; } + } if ((cs->tx_skb = skb_dequeue(&cs->sq))) { cs->tx_cnt = 0; if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { @@ -1166,8 +1172,8 @@ hfc_dbusy_timer(struct IsdnCardState *cs) #endif } -unsigned int __init -*init_send_hfcd(int cnt) +unsigned int * __init +init_send_hfcd(int cnt) { int i, *send; @@ -1181,7 +1187,7 @@ unsigned int __init return(send); } -void __init +void __init init2bds0(struct IsdnCardState *cs) { cs->setstack_d = setstack_hfcd; diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c index 13225cb7a..71767c888 100644 --- a/drivers/isdn/hisax/hfc_2bs0.c +++ b/drivers/isdn/hisax/hfc_2bs0.c @@ -1,4 +1,4 @@ -/* $Id: hfc_2bs0.c,v 1.8 1998/11/15 23:54:43 keil Exp $ +/* $Id: hfc_2bs0.c,v 1.9 1999/07/01 08:11:36 keil Exp $ * specific routines for CCD's HFC 2BS0 * @@ -6,6 +6,9 @@ * * * $Log: hfc_2bs0.c,v $ + * Revision 1.9 1999/07/01 08:11:36 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.8 1998/11/15 23:54:43 keil * changes from 2.0 * @@ -219,6 +222,7 @@ hfc_empty_fifo(struct BCState *bcs, int count) if (!(skb = dev_alloc_skb(count - 3))) printk(KERN_WARNING "HFC: receive out of memory\n"); else { + SET_SKB_FREE(skb); ptr = skb_put(skb, count - 3); idx = 0; cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); @@ -229,7 +233,7 @@ hfc_empty_fifo(struct BCState *bcs, int count) if (idx != count - 3) { debugl1(cs, "RFIFO BUSY error"); printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); WaitNoBusy(cs); stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | HFC_CHANNEL(bcs->channel)); @@ -247,7 +251,7 @@ hfc_empty_fifo(struct BCState *bcs, int count) bcs->channel, chksum, stat); if (stat) { debugl1(cs, "FIFO CRC error"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); skb = NULL; } WaitNoBusy(cs); @@ -321,7 +325,7 @@ hfc_fill_fifo(struct BCState *bcs) bcs->tx_cnt -= count; if (PACKET_NOACK == bcs->tx_skb->pkt_type) count = -1; - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; WaitForBusy(cs); WaitNoBusy(cs); @@ -519,7 +523,7 @@ close_hfcstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -555,7 +559,7 @@ setstack_hfc(struct PStack *st, struct BCState *bcs) return (0); } -void __init +void __init init_send(struct BCState *bcs) { int i; @@ -569,7 +573,7 @@ init_send(struct BCState *bcs) bcs->hw.hfc.send[i] = 0x1fff; } -void __init +void __init inithfc(struct IsdnCardState *cs) { init_send(&cs->bcs[0]); diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c new file mode 100644 index 000000000..9d8968e98 --- /dev/null +++ b/drivers/isdn/hisax/hfc_pci.c @@ -0,0 +1,1744 @@ +/* $Id: hfc_pci.c,v 1.18 1999/08/29 17:05:44 werner Exp $ + + * hfc_pci.c low level driver for CCD´s hfc-pci based cards + * + * Author Werner Cornelius (werner@isdn4linux.de) + * based on existing driver for CCD hfc ISA cards + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) + * Copyright 1999 by Karsten Keil (keil@isdn4linux.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: hfc_pci.c,v $ + * Revision 1.18 1999/08/29 17:05:44 werner + * corrected tx_lo line setup. Datasheet is not correct. + * + * Revision 1.17 1999/08/28 21:04:27 werner + * Implemented full audio support (transparent mode) + * + * Revision 1.16 1999/08/25 17:01:27 keil + * Use new LL->HL auxcmd call + * + * Revision 1.15 1999/08/22 20:27:05 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.14 1999/08/12 18:59:45 werner + * Added further manufacturer and device ids to PCI list + * + * Revision 1.13 1999/08/11 21:01:28 keil + * new PCI codefix + * + * Revision 1.12 1999/08/10 16:01:58 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.11 1999/08/09 19:13:32 werner + * moved constant pci ids to pci id table + * + * Revision 1.10 1999/08/08 10:17:34 werner + * added new PCI vendor and card ids for Manufacturer 0x1043 + * + * Revision 1.9 1999/08/07 21:09:10 werner + * Fixed another memcpy problem in fifo handling. + * Thanks for debugging aid by Olaf Kordwittenborg. + * + * Revision 1.8 1999/07/23 14:25:15 werner + * Some smaller bug fixes and prepared support for GCI/IOM bus + * + * Revision 1.7 1999/07/14 21:24:20 werner + * fixed memcpy problem when using E-channel feature + * + * Revision 1.6 1999/07/13 21:08:08 werner + * added echo channel logging feature. + * + * Revision 1.5 1999/07/12 21:05:10 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.4 1999/07/04 21:51:39 werner + * Changes to solve problems with irq sharing and smp machines + * Thanks to Karsten Keil and Alex Holden for giving aid with + * testing and debugging + * + * Revision 1.3 1999/07/01 09:43:19 keil + * removed additional schedules in timeouts + * + * Revision 1.2 1999/07/01 08:07:51 keil + * Initial version + * + * + * + */ + +#include <linux/config.h> +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_pci.h" +#include "isdnl1.h" +#include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif +#include <linux/interrupt.h> + +extern const char *CardType[]; + +static const char *hfcpci_revision = "$Revision: 1.18 $"; + +/* table entry in the PCI devices list */ +typedef struct { + int vendor_id; + int device_id; + char *vendor_name; + char *card_name; + } PCI_ENTRY; + +static const PCI_ENTRY id_list[] = { + {0x1397,0x2BD0,"CCD/Billion/Asuscom","2BD0"}, + {0x1397,0xB000,"Billion","B000"}, + {0x1397,0xB006,"Billion","B006"}, + {0x1397,0xB007,"Billion","B007"}, + {0x1397,0xB008,"Billion","B008"}, + {0x1397,0xB009,"Billion","B009"}, + {0x1397,0xB00A,"Billion","B00A"}, + {0x1397,0xB00B,"Billion","B00B"}, + {0x1397,0xB00C,"Billion","B00C"}, + {0x1043,0x0675,"Asuscom/Askey","675"}, + {0x0871,0xFFA2,"German telekom","T-Concept"}, + {0x0871,0xFFA1,"German telekom","A1T"}, + {0x1051,0x0100,"Motorola MC145575","MC145575"}, + {0x1397,0xB100,"Seyeon","B100"}, + {0x15B0,0x2BD0,"Zoltrix","2BD0"}, + {0,0,NULL,NULL}, +}; + + +#if CONFIG_PCI +/*****************************/ +/* release D- and B-channels */ +/*****************************/ +void +releasehfcpci(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.hfc.send) { + kfree(cs->bcs[0].hw.hfc.send); + cs->bcs[0].hw.hfc.send = NULL; + } + if (cs->bcs[1].hw.hfc.send) { + kfree(cs->bcs[1].hw.hfc.send); + cs->bcs[1].hw.hfc.send = NULL; + } +} + +/******************************************/ +/* free hardware resources used by driver */ +/******************************************/ +void +release_io_hfcpci(struct IsdnCardState *cs) +{ +#if CONFIG_PCI + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0); /* disabe memory mapped ports + busmaster */ +#endif /* CONFIG_PCI */ + releasehfcpci(cs); + del_timer(&cs->hw.hfcpci.timer); + kfree(cs->hw.hfcpci.share_start); + cs->hw.hfcpci.share_start = NULL; + vfree(cs->hw.hfcpci.pci_io); +} + +/********************************************************************************/ +/* function called to reset the HFC PCI chip. A complete software reset of chip */ +/* and fifos is done. */ +/********************************************************************************/ +static void +reset_hfcpci(struct IsdnCardState *cs) +{ + long flags; + + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ + printk(KERN_INFO "HFC_PCI: resetting card\n"); + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */ + Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ + Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + if (Read_hfc(cs, HFCPCI_STATUS) & 2) + printk(KERN_WARNING "HFC-PCI init bit busy\n"); + + cs->hw.hfcpci.fifo_en = 0x30; /* only D fifos enabled */ + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + + cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ + Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); + + Write_hfc(cs, HFCPCI_CLKDEL, 0x0e); /* ST-Bit delay for TE-Mode */ + cs->hw.hfcpci.sctrl_e = HFCPCI_AUTO_AWAKE; + Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e); /* S/T Auto awake */ + cs->hw.hfcpci.bswapped = 0; /* no exchange */ + cs->hw.hfcpci.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + + cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE; + cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | + HFCPCI_INTS_L1STATE | HFCPCI_CLTIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); + + /* Clear already pending ints */ + if (Read_hfc(cs, HFCPCI_INT_S1)); + if (Read_hfc(cs, HFCPCI_INT_S2)); + + Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 2); /* HFC ST 2 */ + udelay(10); + Write_hfc(cs, HFCPCI_STATES, 2); /* HFC ST 2 */ + cs->hw.hfcpci.mst_m = HFCPCI_MASTER; /* HFC Master Mode */ + + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + cs->hw.hfcpci.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + cs->hw.hfcpci.sctrl_r = 0; + Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); + + /* Init GCI/IOM2 in master mode */ + /* Slots 0 and 1 are set for B-chan 1 and 2 */ + /* D- and monitor/CI channel are not enabled */ + /* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */ + /* STIO2 is used as data input, B1+B2 from IOM->ST */ + /* ST B-channel send disabled -> continous 1s */ + /* The IOM slots are always enabled */ + cs->hw.hfcpci.conn = 0x36; /* set data flow directions */ + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */ + Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */ + Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */ + Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */ + restore_flags(flags); +} + +/***************************************************/ +/* Timer function called when kernel timer expires */ +/***************************************************/ +static void +hfcpci_Timer(struct IsdnCardState *cs) +{ + cs->hw.hfcpci.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcpci.ctmt | 0x80); + add_timer(&cs->hw.hfcpci.timer); + */ +} + + +/*********************************/ +/* schedule a new D-channel task */ +/*********************************/ +static void +sched_event_D_pci(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/*********************************/ +/* schedule a new b_channel task */ +/*********************************/ +static void +hfcpci_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/************************************************/ +/* select a b-channel entry matching and active */ +/************************************************/ +static +struct BCState * +Sel_BCS(struct IsdnCardState *cs, int channel) +{ + if (cs->bcs[0].mode && (cs->bcs[0].channel == channel)) + return (&cs->bcs[0]); + else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel)) + return (&cs->bcs[1]); + else + return (NULL); +} + +/*********************************************/ +/* read a complete B-frame out of the buffer */ +/*********************************************/ +static struct sk_buff +* +hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type * bz, u_char * bdata, int count) +{ + u_char *ptr, *ptr1, new_f2; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int flags, total, maxlen, new_z2; + z_type *zp; + + save_flags(flags); + sti(); + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfcpci_empty_fifo"); + zp = &bz->za[bz->f2]; /* point to Z-Regs */ + new_z2 = zp->z2 + count; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((count > HSCX_BUFMAX + 3) || (count < 4) || + (*(bdata + (zp->z1 - B_SUB_VAL)))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count); + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + skb = NULL; + } else if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + total = count; + count -= 3; + ptr = skb_put(skb, count); + + if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = count; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + + ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + count -= maxlen; + + if (count) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, count); /* rest */ + } + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + + } + restore_flags(flags); + return (skb); +} + +/*******************************/ +/* D-channel receive procedure */ +/*******************************/ +static +int +receive_dmsg(struct IsdnCardState *cs) +{ + struct sk_buff *skb; + int maxlen; + int rcnt, total; + int count = 5; + u_char *ptr, *ptr1; + dfifo_type *df; + z_type *zp; + + df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_rx; + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_dmsg blocked"); + return (1); + } + while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { + zp = &df->za[df->f2 & D_FREG_MASK]; + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += D_FIFO_SIZE; + rcnt++; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", + df->f1, df->f2, zp->z1, zp->z2, rcnt); + + if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || + (df->data[zp->z1])) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[zp->z1]); + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + rcnt) & (D_FIFO_SIZE - 1); + } else if ((skb = dev_alloc_skb(rcnt - 3))) { + SET_SKB_FREE(skb); + total = rcnt; + rcnt -= 3; + ptr = skb_put(skb, rcnt); + + if (zp->z2 + rcnt <= D_FIFO_SIZE) + maxlen = rcnt; /* complete transfer */ + else + maxlen = D_FIFO_SIZE - zp->z2; /* maximum */ + + ptr1 = df->data + zp->z2; /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = df->data; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + total) & (D_FIFO_SIZE - 1); + + skb_queue_tail(&cs->rq, skb); + sched_event_D_pci(cs, D_RCVBUFREADY); + } else + printk(KERN_WARNING "HFC-PCI: D receive out of memory\n"); + } + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + return (1); +} + +/*******************************************************************************/ +/* check for transparent receive data and read max one threshold size if avail */ +/*******************************************************************************/ +int hfcpci_empty_fifo_trans(struct BCState *bcs, bzfifo_type *bz, u_char *bdata) +{ unsigned short *z1r, *z2r; + int new_z2, fcnt, maxlen; + struct sk_buff *skb; + u_char *ptr, *ptr1; + + z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ + z2r = z1r + 1; + + if (!(fcnt = *z1r - *z2r)) + return(0); /* no data avail */ + + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; /* bytes actually buffered */ + if (fcnt > HFCPCI_BTRANS_THRESHOLD) + fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ + + new_z2 = *z2r + fcnt; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + + if (!(skb = dev_alloc_skb(fcnt))) + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + ptr = skb_put(skb, fcnt); + if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = fcnt; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r; /* maximum */ + + ptr1 = bdata + (*z2r - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + fcnt -= maxlen; + + if (fcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, fcnt); /* rest */ + } + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfcpci_sched_event(bcs, B_RCVBUFREADY); + } + + *z2r = new_z2; /* new position */ + return(1); +} /* hfcpci_empty_fifo_trans */ + +/**********************************/ +/* B-channel main receive routine */ +/**********************************/ +void +main_rec_hfcpci(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int rcnt; + int receive, count = 5; + struct sk_buff *skb; + bzfifo_type *bz; + u_char *bdata; + z_type *zp; + + + save_flags(flags); + if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2; + } else { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b1; + } + Begin: + count--; + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_data %d blocked", bcs->channel); + restore_flags(flags); + return; + } + sti(); + if (bz->f1 != bz->f2) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci rec %d f1(%d) f2(%d)", + bcs->channel, bz->f1, bz->f2); + zp = &bz->za[bz->f2]; + + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, zp->z1, zp->z2, rcnt); + if ((skb = hfcpci_empty_fifo(bcs, bz, bdata, rcnt))) { + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfcpci_sched_event(bcs, B_RCVBUFREADY); + } + rcnt = bz->f1 - bz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + if (rcnt > 1) + receive = 1; + else + receive = 0; + } else + if (bcs->mode == L1_MODE_TRANS) + receive = hfcpci_empty_fifo_trans(bcs, bz, bdata); + else + receive = 0; + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + if (count && receive) + goto Begin; + restore_flags(flags); + return; +} + +/**************************/ +/* D-channel send routine */ +/**************************/ +static void +hfcpci_fill_dfifo(struct IsdnCardState *cs) +{ + long flags; + int fcnt; + int count, new_z1, maxlen; + dfifo_type *df; + u_char *src, *dst, new_f1; + + if (!cs->tx_skb) + return; + if (cs->tx_skb->len <= 0) + return; + + df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_tx; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)", + df->f1, df->f2, + df->za[df->f1 & D_FREG_MASK].z1); + fcnt = df->f1 - df->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_D_FRAMES - 1)) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo more as 14 frames"); + return; + } + /* now determine free bytes in FIFO buffer */ + count = df->za[df->f1 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1; + if (count <= 0) + count += D_FIFO_SIZE; /* count now contains available bytes */ + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo count(%ld/%d)", + cs->tx_skb->len, count); + if (count < cs->tx_skb->len) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo no fifo mem"); + return; + } + count = cs->tx_skb->len; /* get frame len */ + new_z1 = (df->za[df->f1 & D_FREG_MASK].z1 + count) & (D_FIFO_SIZE - 1); + new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); + src = cs->tx_skb->data; /* source pointer */ + dst = df->data + df->za[df->f1 & D_FREG_MASK].z1; + maxlen = D_FIFO_SIZE - df->za[df->f1 & D_FREG_MASK].z1; /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = df->data; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + save_flags(flags); + cli(); + df->za[new_f1 & D_FREG_MASK].z1 = new_z1; /* for next buffer */ + df->za[df->f1 & D_FREG_MASK].z1 = new_z1; /* new pos actual buffer */ + df->f1 = new_f1; /* next frame */ + restore_flags(flags); + + idev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_skb = NULL; + return; +} + +/**************************/ +/* B-channel send routine */ +/**************************/ +static void +hfcpci_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int flags, maxlen, fcnt; + int count, new_z1; + bzfifo_type *bz; + u_char *bdata; + u_char new_f1, *src, *dst; + unsigned short *z1t, *z2t; + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + save_flags(flags); + sti(); + + if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b2; + } else { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b1; + } + + if (bcs->mode == L1_MODE_TRANS) { + z1t = &bz->za[MAX_B_FRAMES].z1; + z2t = z1t + 1; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)", + bcs->channel, *z1t, *z2t); + fcnt = *z2t - *z1t; + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; /* fcnt contains available bytes in fifo */ + fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send */ + + while ((fcnt < 2 * HFCPCI_BTRANS_THRESHOLD) && (bcs->tx_skb)) { + if (bcs->tx_skb->len < B_FIFO_SIZE - fcnt) { + /* data is suitable for fifo */ + count = bcs->tx_skb->len; + + new_z1 = *z1t + count; /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + src = bcs->tx_skb->data; /* source pointer */ + dst = bdata + (*z1t - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t; /* end of fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bcs->tx_cnt -= bcs->tx_skb->len; + fcnt += bcs->tx_skb->len; + *z1t = new_z1; /* now send data */ + } + else + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded", + bcs->channel, bcs->tx_skb->len); + + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + cli(); + bcs->tx_skb = skb_dequeue(&bcs->squeue); /* fetch next data */ + sti(); + } + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + restore_flags(flags); + return; + } + + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo_hdlc %d f1(%d) f2(%d) z1(f1)(%x)", + bcs->channel, bz->f1, bz->f2, + bz->za[bz->f1].z1); + + fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_B_FRAMES - 1)) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_Bfifo more as 14 frames"); + restore_flags(flags); + return; + } + /* now determine free bytes in FIFO buffer */ + count = bz->za[bz->f1].z2 - bz->za[bz->f1].z1; + if (count <= 0) + count += B_FIFO_SIZE; /* count now contains available bytes */ + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo %d count(%ld/%d),%lx", + bcs->channel, bcs->tx_skb->len, + count, current->state); + + if (count < bcs->tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + count = bcs->tx_skb->len; /* get frame len */ + new_z1 = bz->za[bz->f1].z1 + count; /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + + new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); + src = bcs->tx_skb->data; /* source pointer */ + dst = bdata + (bz->za[bz->f1].z1 - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - bz->za[bz->f1].z1; /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bcs->tx_cnt -= bcs->tx_skb->len; + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); + + cli(); + bz->za[new_f1].z1 = new_z1; /* for next buffer */ + bz->f1 = new_f1; /* next frame */ + restore_flags(flags); + + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + return; +} + +/***********************/ +/* set/reset echo mode */ +/***********************/ +static int +hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) +{ + int flags; + int i = *(unsigned int *) ic->parm.num; + + if (cs->chanlimit > 1) + return(-EINVAL); + + save_flags(flags); + cli(); + if (i) { + cs->logecho = 1; + cs->hw.hfcpci.trm |= 0x20; /* enable echo chan */ + cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX; + } + else { + cs->logecho = 0; + cs->hw.hfcpci.trm &= ~0x20; /* enable echo chan */ + cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX; + } + cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.conn |= 0x10; /* B2-IOM -> B2-ST */ + cs->hw.hfcpci.ctmt &= ~2; + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + restore_flags(flags); + return(0); +} /* hfcpci_auxcmd */ + +/*****************************/ +/* E-channel receive routine */ +/*****************************/ +static void receive_emsg(struct IsdnCardState *cs) +{ + long flags; + int rcnt; + int receive, count = 5; + bzfifo_type *bz; + u_char *bdata; + z_type *zp; + u_char *ptr, *ptr1, new_f2; + int total, maxlen, new_z2; + u_char e_buffer[256]; + + save_flags(flags); + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2; + Begin: + count--; + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "echo_rec_data blocked"); + restore_flags(flags); + return; + } + sti(); + if (bz->f1 != bz->f2) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci e_rec f1(%d) f2(%d)", + bz->f1, bz->f2); + zp = &bz->za[bz->f2]; + + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci e_rec z1(%x) z2(%x) cnt(%d)", + zp->z1, zp->z2, rcnt); + new_z2 = zp->z2 + rcnt; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((rcnt > 256 + 3) || (count < 4) || + (*(bdata + (zp->z1 - B_SUB_VAL)))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt); + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + } else { + total = rcnt; + rcnt -= 3; + ptr = e_buffer; + + if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = rcnt; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + + ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + if (cs->debug & DEB_DLOG_HEX) { + ptr = cs->dlog; + if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) { + *ptr++ = 'E'; + *ptr++ = 'C'; + *ptr++ = 'H'; + *ptr++ = 'O'; + *ptr++ = ':'; + ptr += QuickHex(ptr, e_buffer, total - 3); + ptr--; + *ptr++ = '\n'; + *ptr = 0; + HiSax_putstatus(cs, NULL, cs->dlog); + } else + HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", total - 3); + } + + } + + rcnt = bz->f1 - bz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + if (rcnt > 1) + receive = 1; + else + receive = 0; + } else + receive = 0; + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + if (count && receive) + goto Begin; + restore_flags(flags); + return; +} /* receive_emsg */ + +/*********************/ +/* Interrupt handler */ +/*********************/ +static void +hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char exval; + struct BCState *bcs; + int count = 15; + long flags; + u_char val, stat; + + if (!cs) { + printk(KERN_WARNING "HFC-PCI: Spurious interrupt!\n"); + return; + } + if (HFCPCI_ANYINT & (stat = Read_hfc(cs, HFCPCI_STATUS))) { + val = Read_hfc(cs, HFCPCI_INT_S1); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI: stat(%02x) s1(%02x)", stat, val); + } else + return; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI irq %x %s", val, + test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ? + "locked" : "unlocked"); + val &= cs->hw.hfcpci.int_m1; + if (val & 0x40) { /* TE state machine irq */ + exval = Read_hfc(cs, HFCPCI_STATES) & 0xf; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcpci.ph_state, + exval); + cs->dc.hfcpci.ph_state = exval; + sched_event_D_pci(cs, D_L1STATECHANGE); + val &= ~0x40; + } + while (val) { + save_flags(flags); + cli(); + if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + cs->hw.hfcpci.int_s1 |= val; + restore_flags(flags); + return; + } + if (cs->hw.hfcpci.int_s1 & 0x18) { + exval = val; + val = cs->hw.hfcpci.int_s1; + cs->hw.hfcpci.int_s1 = exval; + } + if (val & 0x08) { + if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1:0))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x08 IRQ"); + } else + main_rec_hfcpci(bcs); + } + if (val & 0x10) { + if (cs->logecho) + receive_emsg(cs); + else + if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x10 IRQ"); + } else + main_rec_hfcpci(bcs); + } + if (val & 0x01) { + if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1:0))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x01 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + hfcpci_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x02) { + if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x02 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + hfcpci_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x20) { /* receive dframe */ + receive_dmsg(cs); + } + if (val & 0x04) { /* dframe transmitted */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + sched_event_D_pci(cs, D_CLEARBUSY); + if (cs->tx_skb) { + if (cs->tx_skb->len) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfcpci_fill_dfifo irq blocked"); + } + goto afterXPR; + } else { + idev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfcpci_fill_dfifo irq blocked"); + } + } else + sched_event_D_pci(cs, D_XMTBUFREADY); + } + afterXPR: + if (cs->hw.hfcpci.int_s1 && count--) { + val = cs->hw.hfcpci.int_s1; + cs->hw.hfcpci.int_s1 = 0; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI irq %x loop %d", val, 15 - count); + } else + val = 0; + restore_flags(flags); + } +} + +/********************************************************************/ +/* timer callback for D-chan busy resolution. Currently no function */ +/********************************************************************/ +static void +hfcpci_dbusy_timer(struct IsdnCardState *cs) +{ +#if 0 + struct PStack *stptr; + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy"); + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + stptr = stptr->next; + } + } +#endif +} + +/*************************************/ +/* Layer 1 D-channel hardware access */ +/*************************************/ +static void +HFCPCI_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + + switch (pr) { + case (PH_DATA | REQUEST): + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + if (cs->tx_skb) { + skb_queue_tail(&cs->sq, skb); +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA Queued", 0); +#endif + } else { + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 0); +#endif + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfcpci_fill_dfifo blocked"); + + } + break; + case (PH_PULL | INDICATION): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA_PULLED", 0); +#endif + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfcpci_fill_dfifo blocked"); + break; + case (PH_PULL | REQUEST): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (HW_RESET | REQUEST): + Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + Write_hfc(cs, HFCPCI_STATES, 3); /* HFC ST 2 */ + cs->hw.hfcpci.mst_m |= HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (HW_ENABLE | REQUEST): + Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); + break; + case (HW_DEACTIVATE | REQUEST): + cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + break; + case (HW_INFO3 | REQUEST): + cs->hw.hfcpci.mst_m |= HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + break; +#if 0 + case (HW_TESTLOOP | REQUEST): + u_char val = 0; + if (1 & (int) arg) + val |= 0x0c; + if (2 & (int) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; +#endif + default: + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_l1hw unknown pr %4x", pr); + break; + } +} + +/***********************************************/ +/* called during init setting l1 stack pointer */ +/***********************************************/ +void +setstack_hfcpci(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = HFCPCI_l1hw; +} + +/**************************************/ +/* send B-channel data if not blocked */ +/**************************************/ +static void +hfcpci_send_data(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "send_data %d blocked", bcs->channel); +} + +/***************************************************************/ +/* activate/deactivate hardware for selected channels and mode */ +/***************************************************************/ +void +mode_hfcpci(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + bzfifo_type *bzr, *bzt; + int flags; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HFCPCI bchannel mode %d bchan %d/%d", + mode, bc, bcs->channel); + bcs->mode = mode; + bcs->channel = bc; + if (cs->chanlimit > 1) { + cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcpci.sctrl_e &= ~0x80; + } + else { + if (bc) { + cs->hw.hfcpci.bswapped = 1; /* B1 and B2 exchanged */ + cs->hw.hfcpci.sctrl_e |= 0x80; + bc = 0; /* B1 controller used */ + } + else { + cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcpci.sctrl_e &= ~0x80; + } + } + save_flags(flags); + cli(); + switch (mode) { + case (L1_MODE_NULL): + if (bc) { + cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + } else { + cs->hw.hfcpci.sctrl &= ~SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r &= ~SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + } + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfcpci.ctmt |= 2; + cs->hw.hfcpci.conn &= ~0x18; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2; + bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2; + } else { + cs->hw.hfcpci.ctmt |= 1; + cs->hw.hfcpci.conn &= ~0x03; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1; + bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1; + } + bzr->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1; + bzr->za[MAX_B_FRAMES].z2 = bzr->za[MAX_B_FRAMES].z1; + bzr->f1 = MAX_B_FRAMES; + bzr->f2 = bzr->f1; /* init F pointers to remain constant */ + bzt->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1; + bzt->za[MAX_B_FRAMES].z2 = bzt->za[MAX_B_FRAMES].z1; + bzt->f1 = MAX_B_FRAMES; + bzt->f2 = bzt->f1; /* init F pointers to remain constant */ + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfcpci.ctmt &= ~2; + cs->hw.hfcpci.conn &= ~0x18; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + } else { + cs->hw.hfcpci.ctmt &= ~1; + cs->hw.hfcpci.conn &= ~0x3; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + } + break; + case (L1_MODE_EXTRN): + if (bc) { + cs->hw.hfcpci.conn |= 0x10; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + } else { + cs->hw.hfcpci.conn |= 0x02; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + } + break; + } + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + restore_flags(flags); + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); +} + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ +static void +hfcpci_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; +/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + */ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); +/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + */ st->l1.bcs->tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_hfcpci(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + mode_hfcpci(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +/******************************************/ +/* deactivate B-channel access and queues */ +/******************************************/ +static void +close_hfcpci(struct BCState *bcs) +{ + mode_hfcpci(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +/*************************************/ +/* init B-channel queues and control */ +/*************************************/ +static int +open_hfcpcistate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +/*********************************/ +/* inits the stack for B-channel */ +/*********************************/ +static int +setstack_2b(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_hfcpcistate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfcpci_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +/***************************/ +/* handle L1 state changes */ +/***************************/ +static void +hfcpci_bh(struct IsdnCardState *cs) +{ +/* struct PStack *stptr; + */ + if (!cs) + return; +#if 0 + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } +#endif + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + switch (cs->dc.hfcpci.ph_state) { + case (0): + l1_msg(cs, HW_RESET | INDICATION, NULL); + break; + case (3): + l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); + break; + case (8): + l1_msg(cs, HW_RSYNC | INDICATION, NULL); + break; + case (6): + l1_msg(cs, HW_INFO2 | INDICATION, NULL); + break; + case (7): + l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); + break; + default: + break; + } + } + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); +} + + +/*************************************/ +/* Alloc memory send data for queues */ +/*************************************/ +unsigned int * __init + init_send_hfcpci(int cnt) +{ + int i, *send; + + if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfcpci.send\n"); + return (NULL); + } + for (i = 0; i < cnt; i++) + send[i] = 0x1fff; + return (send); +} + +/********************************/ +/* called for card init message */ +/********************************/ +void __init + inithfcpci(struct IsdnCardState *cs) +{ + cs->setstack_d = setstack_hfcpci; + cs->dbusytimer.function = (void *) hfcpci_dbusy_timer; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->tqueue.routine = (void *) (void *) hfcpci_bh; +#if 0 + if (!cs->hw.hfcpci.send) + cs->hw.hfcpci.send = init_send_hfcpci(16); +#endif + if (!cs->bcs[0].hw.hfc.send) + cs->bcs[0].hw.hfc.send = init_send_hfcpci(32); + if (!cs->bcs[1].hw.hfc.send) + cs->bcs[1].hw.hfc.send = init_send_hfcpci(32); + cs->BC_Send_Data = &hfcpci_send_data; + cs->bcs[0].BC_SetStack = setstack_2b; + cs->bcs[1].BC_SetStack = setstack_2b; + cs->bcs[0].BC_Close = close_hfcpci; + cs->bcs[1].BC_Close = close_hfcpci; + mode_hfcpci(cs->bcs, 0, 0); + mode_hfcpci(cs->bcs + 1, 0, 1); +} + + + +/*******************************************/ +/* handle card messages from control layer */ +/*******************************************/ +static int +hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCPCI: card_msg %x", mt); + switch (mt) { + case CARD_RESET: + reset_hfcpci(cs); + return (0); + case CARD_RELEASE: + release_io_hfcpci(cs); + return (0); + case CARD_INIT: + inithfcpci(cs); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ + /* now switch timer interrupt off */ + cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + /* reinit mode reg */ + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + restore_flags(flags); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + + +/* this variable is used as card index when more than one cards are present */ +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_hfcpci __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +#endif /* CONFIG_PCI */ + +int __init + setup_hfcpci(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + int i; +#ifdef COMPAT_HAS_NEW_PCI + struct pci_dev *tmp_hfcpci = NULL; +#endif + + strcpy(tmp, hfcpci_revision); + printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); +#if CONFIG_PCI + cs->hw.hfcpci.int_s1 = 0; +#if 0 + cs->hw.hfcpci.send = NULL; +#endif + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->dc.hfcpci.ph_state = 0; + cs->hw.hfcpci.fifo = 255; + if (cs->typ == ISDN_CTYPE_HFC_PCI) { +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "HFC-PCI: no PCI bus present\n"); + return (0); + } + i = 0; + while (id_list[i].vendor_id) { + tmp_hfcpci = pci_find_device(id_list[i].vendor_id, + id_list[i].device_id, + dev_hfcpci); + if (tmp_hfcpci) break; + i++; + } + + if (tmp_hfcpci) { + dev_hfcpci = tmp_hfcpci; /* old device */ + cs->hw.hfcpci.pci_bus = dev_hfcpci->bus->number; + cs->hw.hfcpci.pci_device_fn = dev_hfcpci->devfn; + cs->irq = dev_hfcpci->irq; + if (!cs->irq) { + printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); + return (0); + } + cs->hw.hfcpci.pci_io = (char *) get_pcibase(dev_hfcpci, 1); + printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n",id_list[i].vendor_name,id_list[i].card_name); + } else { + printk(KERN_WARNING "HFC-PCI: No PCI card found\n"); + return (0); + } +#else + for (; pci_index < 255; pci_index++) { + unsigned char irq; + + i = 0; + while (id_list[i].vendor_id) { + if (pcibios_find_device(id_list[i].vendor_id, + id_list[i].device_id, pci_index, + &cs->hw.hfcpci.pci_bus, &cs->hw.hfcpci.pci_device_fn) == 0) + break; + i++; + } + if (!id_list[i].vendor_id) + continue; + + pcibios_read_config_byte(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + cs->irq = irq; + + pcibios_read_config_dword(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, PCI_BASE_ADDRESS_1, + (void *) &cs->hw.hfcpci.pci_io); + printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n",id_list[i].vendor_name,id_list[i].card_name); + break; + } + if (pci_index == 255) { + printk(KERN_WARNING "HFC-PCI: No card found\n"); + return (0); + } + pci_index++; +#endif /* COMPAT_HAS_NEW_PCI */ + if (!cs->hw.hfcpci.pci_io) { + printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); + return (0); + } + /* Allocate memory for FIFOS */ + /* Because the HFC-PCI needs a 32K physical alignment, we */ + /* need to allocate the double mem and align the address */ + if (!((void *) cs->hw.hfcpci.share_start = kmalloc(65536, GFP_KERNEL))) { + printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n"); + return 0; + } + (ulong) cs->hw.hfcpci.fifos = + (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000; + pcibios_write_config_dword(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, 0x80, + (u_int) virt_to_bus(cs->hw.hfcpci.fifos)); + cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256); + printk(KERN_INFO + "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n", + (u_int) cs->hw.hfcpci.pci_io, + (u_int) cs->hw.hfcpci.fifos, + (u_int) virt_to_bus(cs->hw.hfcpci.fifos), + cs->irq, HZ); + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ + cs->hw.hfcpci.int_m2 = 0; /* disable alle interrupts */ + cs->hw.hfcpci.int_m1 = 0; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + } else + return (0); /* no valid card type */ + + + cs->readisac = NULL; + cs->writeisac = NULL; + cs->readisacfifo = NULL; + cs->writeisacfifo = NULL; + cs->BC_Read_Reg = NULL; + cs->BC_Write_Reg = NULL; + cs->irq_func = &hfcpci_interrupt; + cs->irq_flags |= SA_SHIRQ; + + cs->hw.hfcpci.timer.function = (void *) hfcpci_Timer; + cs->hw.hfcpci.timer.data = (long) cs; + init_timer(&cs->hw.hfcpci.timer); + + reset_hfcpci(cs); + cs->cardmsg = &hfcpci_card_msg; + cs->auxcmd = &hfcpci_auxcmd; + return (1); +#else + printk(KERN_WARNING "HFC-PCI: NO_PCI_BIOS\n"); + return (0); +#endif /* CONFIG_PCI */ +} diff --git a/drivers/isdn/hisax/hfc_pci.h b/drivers/isdn/hisax/hfc_pci.h new file mode 100644 index 000000000..ae04d3d16 --- /dev/null +++ b/drivers/isdn/hisax/hfc_pci.h @@ -0,0 +1,263 @@ +/* $Id: hfc_pci.h,v 1.6 1999/08/28 21:04:29 werner Exp $ + + * specific defines for CCD's HFC 2BDS0 PCI chips + * + * Author Werner Cornelius (werner@isdn4linux.de) + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.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: hfc_pci.h,v $ + * Revision 1.6 1999/08/28 21:04:29 werner + * Implemented full audio support (transparent mode) + * + * Revision 1.5 1999/08/09 19:13:34 werner + * moved constant pci ids to pci id table + * + * Revision 1.4 1999/08/08 10:17:33 werner + * added new PCI vendor and card ids for Manufacturer 0x1043 + * + * Revision 1.3 1999/07/14 12:39:34 werner + * Added changes for echo handling. + * + * Revision 1.2 1999/07/01 08:07:52 keil + * Initial version + * + * + * + */ + +/*********************************************/ +/* thresholds for transparent B-channel mode */ +/* change mask and threshold simultaneously */ +/*********************************************/ +#define HFCPCI_BTRANS_THRESHOLD 128 +#define HFCPCI_BTRANS_THRESMASK 0x00 + + + +/* defines for PCI config */ + +#define PCI_ENA_MEMIO 0x02 +#define PCI_ENA_MASTER 0x04 + + +/* GCI/IOM bus monitor registers */ + +#define HCFPCI_C_I 0x08 +#define HFCPCI_TRxR 0x0C +#define HFCPCI_MON1_D 0x28 +#define HFCPCI_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define HFCPCI_B1_SSL 0x80 +#define HFCPCI_B2_SSL 0x84 +#define HFCPCI_AUX1_SSL 0x88 +#define HFCPCI_AUX2_SSL 0x8C +#define HFCPCI_B1_RSL 0x90 +#define HFCPCI_B2_RSL 0x94 +#define HFCPCI_AUX1_RSL 0x98 +#define HFCPCI_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define HFCPCI_B1_D 0xA0 +#define HFCPCI_B2_D 0xA4 +#define HFCPCI_AUX1_D 0xA8 +#define HFCPCI_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define HFCPCI_MST_EMOD 0xB4 +#define HFCPCI_MST_MODE 0xB8 +#define HFCPCI_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define HFCPCI_FIFO_EN 0x44 +#define HFCPCI_TRM 0x48 +#define HFCPCI_B_MODE 0x4C +#define HFCPCI_CHIP_ID 0x58 +#define HFCPCI_CIRM 0x60 +#define HFCPCI_CTMT 0x64 +#define HFCPCI_INT_M1 0x68 +#define HFCPCI_INT_M2 0x6C +#define HFCPCI_INT_S1 0x78 +#define HFCPCI_INT_S2 0x7C +#define HFCPCI_STATUS 0x70 + +/* S/T section registers */ + +#define HFCPCI_STATES 0xC0 +#define HFCPCI_SCTRL 0xC4 +#define HFCPCI_SCTRL_E 0xC8 +#define HFCPCI_SCTRL_R 0xCC +#define HFCPCI_SQ 0xD0 +#define HFCPCI_CLKDEL 0xDC +#define HFCPCI_B1_REC 0xF0 +#define HFCPCI_B1_SEND 0xF0 +#define HFCPCI_B2_REC 0xF4 +#define HFCPCI_B2_SEND 0xF4 +#define HFCPCI_D_REC 0xF8 +#define HFCPCI_D_SEND 0xF8 +#define HFCPCI_E_REC 0xFC + + +/* bits in status register (READ) */ +#define HFCPCI_PCI_PROC 0x02 +#define HFCPCI_NBUSY 0x04 +#define HFCPCI_TIMER_ELAP 0x10 +#define HFCPCI_STATINT 0x20 +#define HFCPCI_FRAMEINT 0x40 +#define HFCPCI_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCPCI_CLTIMER 0x80 +#define HFCPCI_TIM3_125 0x00 +#define HFCPCI_TIM25 0x10 +#define HFCPCI_TIM50 0x14 +#define HFCPCI_TIM400 0x18 +#define HFCPCI_TIM800 0x1C +#define HFCPCI_AUTO_TIMER 0x20 +#define HFCPCI_TRANSB2 0x02 +#define HFCPCI_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCPCI_AUX_MSK 0x07 +#define HFCPCI_RESET 0x08 +#define HFCPCI_B1_REV 0x40 +#define HFCPCI_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define HFCPCI_INTS_B1TRANS 0x01 +#define HFCPCI_INTS_B2TRANS 0x02 +#define HFCPCI_INTS_DTRANS 0x04 +#define HFCPCI_INTS_B1REC 0x08 +#define HFCPCI_INTS_B2REC 0x10 +#define HFCPCI_INTS_DREC 0x20 +#define HFCPCI_INTS_L1STATE 0x40 +#define HFCPCI_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCPCI_PROC_TRANS 0x01 +#define HFCPCI_GCI_I_CHG 0x02 +#define HFCPCI_GCI_MON_REC 0x04 +#define HFCPCI_IRQ_ENABLE 0x08 +#define HFCPCI_PMESEL 0x80 + +/* bits in STATES */ +#define HFCPCI_STATE_MSK 0x0F +#define HFCPCI_LOAD_STATE 0x10 +#define HFCPCI_ACTIVATE 0x20 +#define HFCPCI_DO_ACTION 0x40 +#define HFCPCI_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCPCI_MASTER 0x01 +#define HFCPCI_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_MODE_TE 0x00 +#define SCTRL_MODE_NT 0x04 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define HFCPCI_AUTO_AWAKE 0x01 +#define HFCPCI_DBIT_1 0x04 +#define HFCPCI_IGNORE_COL 0x08 +#define HFCPCI_CHG_B1_B2 0x80 + +/****************************/ +/* bits in FIFO_EN register */ +/****************************/ +#define HFCPCI_FIFOEN_B1 0x03 +#define HFCPCI_FIFOEN_B2 0x0C +#define HFCPCI_FIFOEN_DTX 0x10 +#define HFCPCI_FIFOEN_B2RX 0x08 + + +/***********************************/ +/* definitions of fifo memory area */ +/***********************************/ +#define MAX_D_FRAMES 15 +#define MAX_B_FRAMES 31 +#define B_SUB_VAL 0x200 +#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) +#define D_FIFO_SIZE 512 +#define D_FREG_MASK 0xF + +typedef struct { + unsigned short z1; /* Z1 pointer 16 Bit */ + unsigned short z2; /* Z2 pointer 16 Bit */ + } z_type; + +typedef struct { + u_char data[D_FIFO_SIZE]; /* FIFO data space */ + u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */ + u_char f1,f2; /* f pointers */ + u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */ + z_type za[MAX_D_FRAMES+1]; /* mask index with D_FREG_MASK for access */ + u_char fill3[0x4000-0x2100]; /* align 16K */ + } dfifo_type; + +typedef struct { + z_type za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ + u_char f1,f2; /* f pointers */ + u_char fill[0x2100-0x2082]; /* alignment */ + } bzfifo_type; + + +typedef union { + struct { + dfifo_type d_tx; /* D-send channel */ + dfifo_type d_rx; /* D-receive channel */ + } d_chan; + struct { + u_char fill1[0x200]; + u_char txdat_b1[B_FIFO_SIZE]; + bzfifo_type txbz_b1; + + bzfifo_type txbz_b2; + u_char txdat_b2[B_FIFO_SIZE]; + + u_char fill2[D_FIFO_SIZE]; + + u_char rxdat_b1[B_FIFO_SIZE]; + bzfifo_type rxbz_b1; + + bzfifo_type rxbz_b2; + u_char rxdat_b2[B_FIFO_SIZE]; + } b_chans; + u_char fill[32768]; + } fifo_area; + + +#define Write_hfc(a,b,c) (*(((u_char *)a->hw.hfcpci.pci_io)+b) = c) +#define Read_hfc(a,b) (*(((u_char *)a->hw.hfcpci.pci_io)+b)) + +extern void main_irq_hcpci(struct BCState *bcs); +extern void inithfcpci(struct IsdnCardState *cs); +extern void releasehfcpci(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c new file mode 100644 index 000000000..05735e150 --- /dev/null +++ b/drivers/isdn/hisax/hfcscard.c @@ -0,0 +1,206 @@ +/* $Id: hfcscard.c,v 1.4 1999/08/09 18:59:59 keil Exp $ + + * hfcscard.c low level stuff for hfcs based cards (Teles3c, ACER P10) + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: hfcscard.c,v $ + * Revision 1.4 1999/08/09 18:59:59 keil + * Fix S0 init - Thanks to Stefan Gybas + * + * Revision 1.3 1999/07/12 21:05:12 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:16:03 keil + * teles3c ---> hfcscard + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +static const char *hfcs_revision = "$Revision: 1.4 $"; + +static void +hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat; + + if (!cs) { + printk(KERN_WARNING "HFCS: Spurious interrupt!\n"); + return; + } + if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & + (stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) { + val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCS: stat(%02x) s1(%02x)", stat, val); + hfc2bds0_interrupt(cs, val); + } else { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCS: irq_no_irq stat(%02x)", stat); + } +} + +static void +hfcs_Timer(struct IsdnCardState *cs) +{ + cs->hw.hfcD.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80); + add_timer(&cs->hw.hfcD.timer); +*/ +} + +void +release_io_hfcs(struct IsdnCardState *cs) +{ + release2bds0(cs); + del_timer(&cs->hw.hfcD.timer); + if (cs->hw.hfcD.addr) + release_region(cs->hw.hfcD.addr, 2); +} + +static void +reset_hfcs(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "HFCS: resetting card\n"); + cs->hw.hfcD.cirm = HFCD_RESET; + if (cs->typ == ISDN_CTYPE_TELES3C) + cs->hw.hfcD.cirm |= HFCD_MEM8K; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30*HZ)/1000); + cs->hw.hfcD.cirm = 0; + if (cs->typ == ISDN_CTYPE_TELES3C) + cs->hw.hfcD.cirm |= HFCD_MEM8K; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + if (cs->typ == ISDN_CTYPE_TELES3C) + cs->hw.hfcD.cirm |= HFCD_INTB; + else if (cs->typ == ISDN_CTYPE_ACERP10) + cs->hw.hfcD.cirm |= HFCD_INTA; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */ + cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE; + cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS | + HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC | + HFCD_INTS_DREC | HFCD_INTS_L1STATE; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */ + udelay(10); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */ + cs->hw.hfcD.mst_m = HFCD_MASTER; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); /* HFC Master */ + cs->hw.hfcD.sctrl = 0; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl); + restore_flags(flags); +} + +static int +hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCS: card_msg %x", mt); + switch (mt) { + case CARD_RESET: + reset_hfcs(cs); + return(0); + case CARD_RELEASE: + release_io_hfcs(cs); + return(0); + case CARD_INIT: + cs->hw.hfcD.timer.expires = jiffies + 75; + add_timer(&cs->hw.hfcD.timer); + init2bds0(cs); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((80*HZ)/1000); + cs->hw.hfcD.ctmt |= HFCD_TIM800; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + restore_flags(flags); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +int __init +setup_hfcs(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, hfcs_revision); + printk(KERN_INFO "HiSax: HFC-S driver Rev. %s\n", HiSax_getrev(tmp)); + cs->hw.hfcD.addr = card->para[1] & 0xfffe; + cs->irq = card->para[0]; + cs->hw.hfcD.cip = 0; + cs->hw.hfcD.int_s1 = 0; + cs->hw.hfcD.send = NULL; + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->hw.hfcD.dfifosize = 512; + cs->dc.hfcd.ph_state = 0; + cs->hw.hfcD.fifo = 255; + if (cs->typ == ISDN_CTYPE_TELES3C) { + cs->hw.hfcD.bfifosize = 1024 + 512; + } else if (cs->typ == ISDN_CTYPE_ACERP10) { + cs->hw.hfcD.bfifosize = 7*1024 + 512; + } else + return (0); + if (check_region((cs->hw.hfcD.addr), 2)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.hfcD.addr, + cs->hw.hfcD.addr + 2); + return (0); + } else { + request_region(cs->hw.hfcD.addr, 2, "HFCS isdn"); + } + printk(KERN_INFO + "HFCS: defined at 0x%x IRQ %d HZ %d\n", + cs->hw.hfcD.addr, + cs->irq, HZ); + if (cs->typ == ISDN_CTYPE_TELES3C) { + /* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */ + outb(0x00, cs->hw.hfcD.addr); + outb(0x56, cs->hw.hfcD.addr | 1); + } else if (cs->typ == ISDN_CTYPE_ACERP10) { + /* Acer P10 IO ADR is 0x300 */ + outb(0x00, cs->hw.hfcD.addr); + outb(0x57, cs->hw.hfcD.addr | 1); + } + set_cs_func(cs); + cs->hw.hfcD.timer.function = (void *) hfcs_Timer; + cs->hw.hfcD.timer.data = (long) cs; + init_timer(&cs->hw.hfcD.timer); + reset_hfcs(cs); + cs->cardmsg = &hfcs_card_msg; + cs->irq_func = &hfcs_interrupt; + return (1); +} diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index 36cd82ee1..0fdf65a40 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,8 +1,32 @@ -/* $Id: hisax.h,v 2.26 1998/11/15 23:54:45 keil Exp $ +/* $Id: hisax.h,v 2.34 1999/08/25 17:00:04 keil Exp $ * Basic declarations, defines and prototypes * * $Log: hisax.h,v $ + * Revision 2.34 1999/08/25 17:00:04 keil + * Make ISAR V32bis modem running + * Make LL->HL interface open for additional commands + * + * Revision 2.33 1999/08/05 20:43:16 keil + * ISAR analog modem support + * + * Revision 2.31 1999/07/21 14:46:11 keil + * changes from EICON certification + * + * Revision 2.30 1999/07/14 12:38:38 werner + * Added changes for echo channel handling + * + * Revision 2.29 1999/07/12 21:05:14 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.28 1999/07/05 23:51:46 werner + * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl + * hisaxctrl id 10 <nr. of chans 0-2> + * + * Revision 2.27 1999/07/01 08:11:38 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.26 1998/11/15 23:54:45 keil * changes from 2.0 * @@ -111,8 +135,7 @@ #include <linux/wait.h> #include <linux/isdnif.h> #include <linux/tty.h> -#include <linux/init.h> -#include <linux/serialP.h> +#include <linux/serial_reg.h> #define REQUEST 0 #define CONFIRM 1 @@ -131,12 +154,10 @@ #define HW_RSYNC 0x0060 #define HW_TESTLOOP 0x0070 #define CARD_RESET 0x00F0 -#define CARD_SETIRQ 0x00F1 #define CARD_INIT 0x00F2 #define CARD_RELEASE 0x00F3 #define CARD_TEST 0x00F4 #define CARD_AUX_IND 0x00F5 -#define CARD_LOAD_FIRM 0x00F6 #define PH_ACTIVATE 0x0100 #define PH_DEACTIVATE 0x0110 @@ -168,16 +189,21 @@ #define CC_SETUP_COMPL 0x0330 #define CC_PROCEEDING 0x0340 #define CC_ALERTING 0x0344 +#define CC_PROGRESS 0x0348 #define CC_CONNECT 0x0350 #define CC_CHARGE 0x0354 +#define CC_NOTIFY 0x0358 #define CC_DISCONNECT 0x0360 #define CC_RELEASE 0x0368 #define CC_SUSPEND 0x0370 +#define CC_PROCEED_SEND 0x0374 +#define CC_REDIR 0x0378 #define CC_T303 0x0383 #define CC_T304 0x0384 #define CC_T305 0x0385 #define CC_T308_1 0x0388 -#define CC_T308_2 0x0389 +#define CC_T308_2 0x038A +#define CC_T309 0x0309 #define CC_T310 0x0390 #define CC_T313 0x0393 #define CC_T318 0x0398 @@ -188,12 +214,22 @@ #define CC_RESUME_ERR 0x03E3 #define CC_CONNECT_ERR 0x03E4 #define CC_RELEASE_ERR 0x03E5 -#define CC_DLRL 0x03F0 #define CC_RESTART 0x03F4 +#define CC_TDSS1_IO 0x13F4 /* DSS1 IO user timer */ + +/* define maximum number of possible waiting incoming calls */ +#define MAX_WAITING_CALLS 2 #ifdef __KERNEL__ +/* include only l3dss1 specific process structures, but no other defines */ +#ifdef CONFIG_HISAX_EURO + #define l3dss1_process + #include "l3dss1.h" + #undef l3dss1_process +#endif CONFIG_HISAX_EURO + #define MAX_DFRAME_LEN 260 #define MAX_DFRAME_LEN_L1 300 #define HSCX_BUFMAX 4096 @@ -294,16 +330,17 @@ struct Layer1 { #define FLG_ESTAB_PEND 13 #define FLG_PTP 14 #define FLG_FIXED_TEI 15 +#define FLG_L2BLOCK 16 struct Layer2 { int tei; int sap; int maxlen; unsigned int flag; - int vs, va, vr; + unsigned int vs, va, vr; int rc; - int window; - int sow; + unsigned int window; + unsigned int sow; struct sk_buff *windowar[MAX_WINDOW]; struct sk_buff_head i_queue; struct sk_buff_head ui_queue; @@ -319,8 +356,10 @@ struct Layer2 { struct Layer3 { void (*l3l4) (struct PStack *, int, void *); + void (*l3ml3) (struct PStack *, int, void *); void (*l3l2) (struct PStack *, int, void *); struct FsmInst l3m; + struct FsmTimer l3m_timer; struct sk_buff_head squeue; struct l3_process *proc; struct l3_process *global; @@ -331,6 +370,7 @@ struct Layer3 { struct LLInterface { void (*l4l3) (struct PStack *, int, void *); + int (*l4l3_proto) (struct PStack *, isdn_ctrl *); void *userdata; void (*l1writewakeup) (struct PStack *, int); void (*l2writewakeup) (struct PStack *, int); @@ -345,16 +385,17 @@ struct Management { void (*layer) (struct PStack *, int, void *); }; +#define NO_CAUSE 254 struct Param { - int cause; - int loc; + u_char cause; + u_char loc; + u_char diag[6]; int bchannel; - setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ - int chargeinfo; /* Charge Info - only for 1tr6 in - * the moment - */ + int chargeinfo; int spv; /* SPV Flag */ + setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ + u_char moderate; /* transfer mode and rate (bearer octet 4) */ }; @@ -366,6 +407,14 @@ struct PStack { struct LLInterface lli; struct Management ma; int protocol; /* EDSS1 or 1TR6 */ + + /* protocol specific data fields */ + union + { u_char uuuu; /* only as dummy */ +#ifdef CONFIG_HISAX_EURO + dss1_stk_priv dss1; /* private dss1 data */ +#endif CONFIG_HISAX_EURO + } prot; }; struct l3_process { @@ -378,6 +427,15 @@ struct l3_process { struct Channel *chan; struct PStack *st; struct l3_process *next; + ulong redir_result; + + /* protocol specific data fields */ + union + { u_char uuuu; /* only when euro not defined, avoiding empty union */ +#ifdef CONFIG_HISAX_EURO + dss1_proc_priv dss1; /* private dss1 data */ +#endif CONFIG_HISAX_EURO + } prot; }; struct hscx_hw { @@ -385,6 +443,8 @@ struct hscx_hw { int rcvidx; int count; /* Current skb sent count */ u_char *rcvbuf; /* B-Channel receive Buffer */ + u_char tsaxr0; + u_char tsaxr1; }; struct isar_reg { @@ -401,7 +461,14 @@ struct isar_hw { int rcvidx; int txcnt; int mml; + u_char state; + u_char cmd; + u_char mod; + u_char newcmd; + u_char newmod; + struct timer_list ftimer; u_char *rcvbuf; /* B-Channel receive Buffer */ + u_char conmsg[16]; struct isar_reg *reg; }; @@ -469,11 +536,23 @@ struct amd7930_hw { #define BC_FLG_NOFRAME 4 #define BC_FLG_HALF 5 #define BC_FLG_EMPTY 6 +#define BC_FLG_ORIG 7 +#define BC_FLG_DLEETX 8 +#define BC_FLG_LASTDLE 9 +#define BC_FLG_FIRST 10 +#define BC_FLG_LASTDATA 11 +#define BC_FLG_NMD_DATA 12 +#define BC_FLG_FTI_RUN 13 +#define BC_FLG_LL_OK 14 +#define BC_FLG_LL_CONN 15 #define L1_MODE_NULL 0 #define L1_MODE_TRANS 1 #define L1_MODE_HDLC 2 +#define L1_MODE_EXTRN 3 #define L1_MODE_MODEM 7 +#define L1_MODE_V32 8 +#define L1_MODE_FAX 9 struct BCState { int channel; @@ -486,6 +565,7 @@ struct BCState { struct sk_buff_head squeue; /* B-Channel send Queue */ struct PStack *st; u_char *blog; + u_char *conmsg; struct timer_list transbusy; struct tq_struct tqueue; int event; @@ -578,7 +658,8 @@ struct ix1_hw { }; struct diva_hw { - unsigned int cfg_reg; + unsigned long cfg_reg; + unsigned long pci_cfg; unsigned int ctrl; unsigned int isac_adr; unsigned int isac; @@ -647,6 +728,31 @@ struct njet_hw { unsigned char last_is0; }; +struct hfcPCI_hw { + unsigned char cirm; + unsigned char ctmt; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char int_s1; + unsigned char sctrl; + unsigned char sctrl_r; + unsigned char sctrl_e; + unsigned char trm; + unsigned char stat; + unsigned char fifo; + unsigned char fifo_en; + unsigned char bswapped; + /* unsigned int *send; */ + unsigned char pci_bus; + unsigned char pci_device_fn; + unsigned char *pci_io; /* start of PCI IO memory */ + void *share_start; /* shared memory for Fifos start */ + void *fifos; /* FIFO memory */ + struct timer_list timer; +}; + struct hfcD_hw { unsigned int addr; unsigned int bfifosize; @@ -668,25 +774,119 @@ struct hfcD_hw { struct timer_list timer; }; -#define HW_IOM1 0 -#define HW_IPAC 1 -#define HW_ISAR 2 -#define FLG_TWO_DCHAN 4 -#define FLG_L1_DBUSY 5 -#define FLG_DBUSY_TIMER 6 -#define FLG_LOCK_ATOMIC 7 -#define HW_MON0_RX_END 8 -#define HW_MON1_RX_END 9 -#define HW_MON0_TX_END 10 -#define HW_MON1_TX_END 11 +struct isurf_hw { + unsigned int reset; + unsigned int isac; + unsigned int isar; + struct isar_reg isar_r; +}; + +struct saphir_hw { + unsigned int cfg_reg; + unsigned int ale; + unsigned int isac; + unsigned int hscx; + struct timer_list timer; +}; + +struct bkm_hw { + unsigned int base; + /* A4T stuff */ + unsigned int isac_adr; + unsigned int isac_ale; + unsigned int jade_adr; + unsigned int jade_ale; + /* Scitel Quadro stuff */ + unsigned int plx_adr; + unsigned int data_adr; +}; + +struct gazel_hw { + unsigned int cfg_reg; + unsigned int pciaddr[2]; + signed int ipac; + signed int isac; + signed int hscx[2]; + signed int isacfifo; + signed int hscxfifo[2]; + unsigned char timeslot; + unsigned char iom2; +}; + +#ifdef CONFIG_HISAX_TESTEMU +struct te_hw { + unsigned char *sfifo; + unsigned char *sfifo_w; + unsigned char *sfifo_r; + unsigned char *sfifo_e; + int sfifo_cnt; + unsigned int stat; +#ifdef COMPAT_HAS_NEW_WAITQ + wait_queue_head_t rwaitq; + wait_queue_head_t swaitq; +#else + struct wait_queue *rwaitq; + struct wait_queue *swaitq; +#endif +}; +#endif + +struct arcofi_msg { + struct arcofi_msg *next; + u_char receive; + u_char len; + u_char msg[10]; +}; + +struct isac_chip { + int ph_state; + u_char *mon_tx; + u_char *mon_rx; + int mon_txp; + int mon_txc; + int mon_rxp; + struct arcofi_msg *arcofi_list; + struct timer_list arcofitimer; +#ifdef COMPAT_HAS_NEW_WAITQ + wait_queue_head_t arcofi_wait; +#else + struct wait_queue *arcofi_wait; +#endif + u_char arcofi_bc; + u_char arcofi_state; + u_char mocr; + u_char adf2; +}; + +struct hfcd_chip { + int ph_state; +}; + +struct hfcpci_chip { + int ph_state; +}; + +#define HW_IOM1 0 +#define HW_IPAC 1 +#define HW_ISAR 2 +#define HW_ARCOFI 3 +#define FLG_TWO_DCHAN 4 +#define FLG_L1_DBUSY 5 +#define FLG_DBUSY_TIMER 6 +#define FLG_LOCK_ATOMIC 7 +#define FLG_ARCOFI_TIMER 8 +#define FLG_ARCOFI_ERROR 9 struct IsdnCardState { unsigned char typ; unsigned char subtyp; int protocol; unsigned int irq; + unsigned long irq_flags; int HW_Flags; int *busy_flag; + int chanlimit; /* limited number of B-chans to use */ + int logecho; /* log echo if supported by card */ union { struct elsa_hw elsa; struct teles0_hw teles0; @@ -701,7 +901,15 @@ struct IsdnCardState { struct mic_hw mic; struct njet_hw njet; struct hfcD_hw hfcD; + struct hfcPCI_hw hfcpci; struct ix1_hw niccy; + struct isurf_hw isurf; + struct saphir_hw saphir; +#ifdef CONFIG_HISAX_TESTEMU + struct te_hw te; +#endif + struct bkm_hw ax; + struct gazel_hw gazel; } hw; int myid; isdn_if iif; @@ -717,9 +925,22 @@ struct IsdnCardState { void (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char); void (*BC_Send_Data) (struct BCState *); int (*cardmsg) (struct IsdnCardState *, int, void *); - struct Channel channel[2]; - struct BCState bcs[2]; + void (*setstack_d) (struct PStack *, struct IsdnCardState *); + void (*DC_Close) (struct IsdnCardState *); + void (*irq_func) (int, void *, struct pt_regs *); + int (*auxcmd) (struct IsdnCardState *, isdn_ctrl *); + struct Channel channel[2+MAX_WAITING_CALLS]; + struct BCState bcs[2+MAX_WAITING_CALLS]; struct PStack *stlist; + struct sk_buff_head rq, sq; /* D-channel queues */ + int cardnr; + char *dlog; + int debug; + union { + struct isac_chip isac; + struct hfcd_chip hfcd; + struct hfcpci_chip hfcpci; + } dc; u_char *rcvbuf; int rcvidx; struct sk_buff *tx_skb; @@ -727,18 +948,6 @@ struct IsdnCardState { int event; struct tq_struct tqueue; struct timer_list dbusytimer; - struct sk_buff_head rq, sq; /* D-channel queues */ - int ph_state; - int cardnr; - char *dlog; - int debug; - u_char *mon_tx; - u_char *mon_rx; - int mon_txp; - int mon_txc; - int mon_rxp; - u_char mocr; - void (*setstack_d) (struct PStack *, struct IsdnCardState *); }; #define MON0_RX 1 @@ -776,18 +985,37 @@ struct IsdnCardState { #define ISDN_CTYPE_A1_PCMCIA 26 #define ISDN_CTYPE_FRITZPCI 27 #define ISDN_CTYPE_SEDLBAUER_FAX 28 +#define ISDN_CTYPE_ISURF 29 +#define ISDN_CTYPE_ACERP10 30 +#define ISDN_CTYPE_HSTSAPHIR 31 +#define ISDN_CTYPE_BKM_A4T 32 +#define ISDN_CTYPE_SCT_QUADRO 33 +#define ISDN_CTYPE_GAZEL 34 +#define ISDN_CTYPE_HFC_PCI 35 +#define ISDN_CTYPE_COUNT 35 -#define ISDN_CTYPE_COUNT 28 #ifdef ISDN_CHIP_ISAC #undef ISDN_CHIP_ISAC #endif +#ifndef __initfunc +#define __initfunc(__arginit) __arginit +#endif + +#ifndef __initdata +#define __initdata +#endif + +#ifndef __init +#define __init +#endif + #define HISAX_INITFUNC(__arginit) __initfunc(__arginit) #define HISAX_INITDATA __initdata #ifdef CONFIG_HISAX_16_0 -#define CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0) +#define CARD_TELES0 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -796,8 +1024,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_16_3 -#define CARD_TELES3 (1<< ISDN_CTYPE_16_3) | (1<< ISDN_CTYPE_PNP) | \ - (1<< ISDN_CTYPE_TELESPCMCIA) | (1<< ISDN_CTYPE_COMPAQ_ISA) +#define CARD_TELES3 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -806,7 +1033,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_TELESPCI -#define CARD_TELESPCI (1<< ISDN_CTYPE_TELESPCI) +#define CARD_TELESPCI 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -815,7 +1042,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_AVM_A1 -#define CARD_AVM_A1 (1<< ISDN_CTYPE_A1) +#define CARD_AVM_A1 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -824,7 +1051,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_AVM_A1_PCMCIA -#define CARD_AVM_A1_PCMCIA (1<< ISDN_CTYPE_A1_PCMCIA) +#define CARD_AVM_A1_PCMCIA 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -833,7 +1060,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_FRITZPCI -#define CARD_FRITZPCI (1<< ISDN_CTYPE_FRITZPCI) +#define CARD_FRITZPCI 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -842,8 +1069,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_ELSA -#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_PNP) | \ - (1<< ISDN_CTYPE_ELSA_PCMCIA) | (1<< ISDN_CTYPE_ELSA_PCI) +#define CARD_ELSA 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -855,9 +1081,8 @@ struct IsdnCardState { #define CARD_ELSA 0 #endif - #ifdef CONFIG_HISAX_IX1MICROR2 -#define CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2) +#define CARD_IX1MICROR2 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -866,7 +1091,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_DIEHLDIVA -#define CARD_DIEHLDIVA (1 << ISDN_CTYPE_DIEHLDIVA) +#define CARD_DIEHLDIVA 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -875,7 +1100,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_ASUSCOM -#define CARD_ASUSCOM (1 << ISDN_CTYPE_ASUSCOM) +#define CARD_ASUSCOM 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -884,7 +1109,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_TELEINT -#define CARD_TELEINT (1 << ISDN_CTYPE_TELEINT) +#define CARD_TELEINT 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -893,7 +1118,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_SEDLBAUER -#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA) | ( 1 << ISDN_CTYPE_SEDLBAUER_FAX) +#define CARD_SEDLBAUER 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -902,7 +1127,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_SPORTSTER -#define CARD_SPORTSTER (1 << ISDN_CTYPE_SPORTSTER) +#define CARD_SPORTSTER 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -911,7 +1136,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_MIC -#define CARD_MIC (1 << ISDN_CTYPE_MIC) +#define CARD_MIC 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -920,7 +1145,7 @@ struct IsdnCardState { #endif #ifdef CONFIG_HISAX_NETJET -#define CARD_NETJET (1 << ISDN_CTYPE_NETJET) +#define CARD_NETJET 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -928,20 +1153,26 @@ struct IsdnCardState { #define CARD_NETJET 0 #endif -#ifdef CONFIG_HISAX_TELES3C -#define CARD_TELES3C (1<< ISDN_CTYPE_TELES3C) +#ifdef CONFIG_HISAX_HFCS +#define CARD_HFCS 1 +#else +#define CARD_HFCS 0 +#endif + +#ifdef CONFIG_HISAX_HFC_PCI +#define CARD_HFC_PCI 1 #else -#define CARD_TELES3C 0 +#define CARD_HFC_PCI 0 #endif #ifdef CONFIG_HISAX_AMD7930 -#define CARD_AMD7930 (1 << ISDN_CTYPE_AMD7930) +#define CARD_AMD7930 1 #else #define CARD_AMD7930 0 #endif #ifdef CONFIG_HISAX_NICCY -#define CARD_NICCY (1 << ISDN_CTYPE_NICCY) +#define CARD_NICCY 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -949,8 +1180,17 @@ struct IsdnCardState { #define CARD_NICCY 0 #endif +#ifdef CONFIG_HISAX_ISURF +#define CARD_ISURF 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_ISURF 0 +#endif + #ifdef CONFIG_HISAX_S0BOX -#define CARD_S0BOX (1 << ISDN_CTYPE_S0BOX) +#define CARD_S0BOX 1 #ifndef ISDN_CHIP_ISAC #define ISDN_CHIP_ISAC 1 #endif @@ -958,12 +1198,50 @@ struct IsdnCardState { #define CARD_S0BOX 0 #endif -#define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \ - | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \ - | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \ - | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \ - | CARD_AVM_A1_PCMCIA | CARD_FRITZPCI\ - | CARD_NICCY | CARD_S0BOX | CARD_TELESPCI) +#ifdef CONFIG_HISAX_HSTSAPHIR +#define CARD_HSTSAPHIR 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_HSTSAPHIR 0 +#endif + +#ifdef CONFIG_HISAX_TESTEMU +#define CARD_TESTEMU 1 +#define ISDN_CTYPE_TESTEMU 99 +#undef ISDN_CTYPE_COUNT +#define ISDN_CTYPE_COUNT ISDN_CTYPE_TESTEMU +#else +#define CARD_TESTEMU 0 +#endif + +#ifdef CONFIG_HISAX_BKM_A4T +#define CARD_BKM_A4T 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_BKM_A4T 0 +#endif + +#ifdef CONFIG_HISAX_SCT_QUADRO +#define CARD_SCT_QUADRO 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_SCT_QUADRO 0 +#endif + +#ifdef CONFIG_HISAX_GAZEL +#define CARD_GAZEL 1 +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_GAZEL 0 +#endif #define TEI_PER_CARD 0 @@ -1071,7 +1349,7 @@ void setstack_isac(struct PStack *st, struct IsdnCardState *cs); #define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);} -int ll_run(struct IsdnCardState *cs); +int ll_run(struct IsdnCardState *cs, int addfeatures); void ll_stop(struct IsdnCardState *cs); void CallcNew(void); void CallcFree(void); diff --git a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c index 980dc76e8..290c89f50 100644 --- a/drivers/isdn/hisax/hscx.c +++ b/drivers/isdn/hisax/hscx.c @@ -1,4 +1,4 @@ -/* $Id: hscx.c,v 1.16 1998/11/15 23:54:48 keil Exp $ +/* $Id: hscx.c,v 1.17 1999/07/01 08:11:41 keil Exp $ * hscx.c HSCX specific routines * @@ -6,6 +6,9 @@ * * * $Log: hscx.c,v $ + * Revision 1.17 1999/07/01 08:11:41 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.16 1998/11/15 23:54:48 keil * changes from 2.0 * @@ -110,12 +113,12 @@ modehscx(struct BCState *bcs, int mode, int bc) if (bc == 0) { cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, - test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0); cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, - test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0); } else { - cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x3); - cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x3); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, bcs->hw.hscx.tsaxr1); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, bcs->hw.hscx.tsaxr1); } switch (mode) { case (L1_MODE_NULL): @@ -216,7 +219,7 @@ close_hscxstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -301,6 +304,10 @@ inithscx(struct IsdnCardState *cs)) cs->bcs[1].BC_Close = close_hscxstate; cs->bcs[0].hw.hscx.hscx = 0; cs->bcs[1].hw.hscx.hscx = 1; + cs->bcs[0].hw.hscx.tsaxr0 = 0x2f; + cs->bcs[0].hw.hscx.tsaxr1 = 3; + cs->bcs[1].hw.hscx.tsaxr0 = 0x2f; + cs->bcs[1].hw.hscx.tsaxr1 = 3; modehscx(cs->bcs, 0, 0); modehscx(cs->bcs + 1, 0, 0); } diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c index 217d241c3..ecc1e825e 100644 --- a/drivers/isdn/hisax/hscx_irq.c +++ b/drivers/isdn/hisax/hscx_irq.c @@ -1,4 +1,4 @@ -/* $Id: hscx_irq.c,v 1.11 1998/11/15 23:54:49 keil Exp $ +/* $Id: hscx_irq.c,v 1.12 1999/07/01 08:11:42 keil Exp $ * hscx_irq.c low level b-channel stuff for Siemens HSCX * @@ -7,6 +7,9 @@ * This is an include file for fast inline IRQ stuff * * $Log: hscx_irq.c,v $ + * Revision 1.12 1999/07/01 08:11:42 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.11 1998/11/15 23:54:49 keil * changes from 2.0 * @@ -42,8 +45,6 @@ * */ -#include <linux/string.h> - static inline void waitforCEC(struct IsdnCardState *cs, int hscx) @@ -203,6 +204,7 @@ hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) if (!(skb = dev_alloc_skb(count))) printk(KERN_WARNING "HSCX: receive out of memory\n"); else { + SET_SKB_FREE(skb); memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); skb_queue_tail(&bcs->rqueue, skb); } @@ -218,6 +220,7 @@ hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) if (!(skb = dev_alloc_skb(fifo_size))) printk(KERN_WARNING "HiSax: receive out of memory\n"); else { + SET_SKB_FREE(skb); memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); skb_queue_tail(&bcs->rqueue, skb); } @@ -234,7 +237,7 @@ hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->hw.hscx.count = 0; bcs->tx_skb = NULL; } diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c index a239fdfad..46d5daef0 100644 --- a/drivers/isdn/hisax/isac.c +++ b/drivers/isdn/hisax/isac.c @@ -1,4 +1,4 @@ -/* $Id: isac.c,v 1.18 1998/11/15 23:54:51 keil Exp $ +/* $Id: isac.c,v 1.23 1999/08/25 16:50:52 keil Exp $ * isac.c ISAC specific routines * @@ -9,6 +9,22 @@ * ../../../Documentation/isdn/HiSax.cert * * $Log: isac.c,v $ + * Revision 1.23 1999/08/25 16:50:52 keil + * Fix bugs which cause 2.3.14 hangs (waitqueue init) + * + * Revision 1.22 1999/08/09 19:04:40 keil + * Fix race condition - Thanks to Christer Weinigel + * + * Revision 1.21 1999/07/12 21:05:17 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.20 1999/07/09 08:23:06 keil + * Fix ISAC lost TX IRQ handling + * + * Revision 1.19 1999/07/01 08:11:43 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.18 1998/11/15 23:54:51 keil * changes from 2.0 * @@ -69,6 +85,7 @@ #define __NO_VERSION__ #include "hisax.h" #include "isac.h" +#include "arcofi.h" #include "isdnl1.h" #include <linux/interrupt.h> @@ -100,7 +117,7 @@ ph_command(struct IsdnCardState *cs, unsigned int command) static void isac_new_ph(struct IsdnCardState *cs) { - switch (cs->ph_state) { + switch (cs->dc.isac.ph_state) { case (ISAC_IND_RS): case (ISAC_IND_EI): ph_command(cs, ISAC_CMD_DUI); @@ -154,14 +171,14 @@ isac_bh(struct IsdnCardState *cs) DChannel_proc_rcv(cs); if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) DChannel_proc_xmt(cs); - if (test_and_clear_bit(D_RX_MON0, &cs->event)) - test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags); +#if ARCOFI_USE + if (!test_bit(HW_ARCOFI, &cs->HW_Flags)) + return; if (test_and_clear_bit(D_RX_MON1, &cs->event)) - test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags); - if (test_and_clear_bit(D_TX_MON0, &cs->event)) - test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + arcofi_fsm(cs, ARCOFI_RX_END, NULL); if (test_and_clear_bit(D_TX_MON1, &cs->event)) - test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + arcofi_fsm(cs, ARCOFI_TX_END, NULL); +#endif } void @@ -226,7 +243,6 @@ isac_fill_fifo(struct IsdnCardState *cs) cs->tx_cnt += count; cs->writeisacfifo(cs, ptr, count); cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { debugl1(cs, "isac_fill_fifo dbusytimer running"); del_timer(&cs->dbusytimer); @@ -234,6 +250,7 @@ isac_fill_fifo(struct IsdnCardState *cs) init_timer(&cs->dbusytimer); cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); add_timer(&cs->dbusytimer); + restore_flags(flags); if (cs->debug & L1_DEB_ISAC_FIFO) { char *t = cs->dlog; @@ -283,6 +300,7 @@ isac_interrupt(struct IsdnCardState *cs, u_char val) if (!(skb = alloc_skb(count, GFP_ATOMIC))) printk(KERN_WARNING "HiSax: D receive out of memory\n"); else { + SET_SKB_FREE(skb); memcpy(skb_put(skb, count), cs->rcvbuf, count); skb_queue_tail(&cs->rq, skb); } @@ -310,7 +328,7 @@ isac_interrupt(struct IsdnCardState *cs, u_char val) isac_fill_fifo(cs); goto afterXPR; } else { - dev_kfree_skb(cs->tx_skb); + idev_kfree_skb(cs->tx_skb, FREE_WRITE); cs->tx_cnt = 0; cs->tx_skb = NULL; } @@ -327,9 +345,9 @@ isac_interrupt(struct IsdnCardState *cs, u_char val) if (cs->debug & L1_DEB_ISAC) debugl1(cs, "ISAC CIR0 %02X", exval ); if (exval & 2) { - cs->ph_state = (exval >> 2) & 0xf; + cs->dc.isac.ph_state = (exval >> 2) & 0xf; if (cs->debug & L1_DEB_ISAC) - debugl1(cs, "ph_state change %x", cs->ph_state); + debugl1(cs, "ph_state change %x", cs->dc.isac.ph_state); isac_sched_event(cs, D_L1STATECHANGE); } if (exval & 1) { @@ -347,127 +365,147 @@ isac_interrupt(struct IsdnCardState *cs, u_char val) exval = cs->readisac(cs, ISAC_EXIR); if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC EXIR %02x", exval); - if (exval & 0x04) { + if (exval & 0x80) { /* XMR */ + debugl1(cs, "ISAC XMR"); + printk(KERN_WARNING "HiSax: ISAC XMR\n"); + } + if (exval & 0x40) { /* XDU */ + debugl1(cs, "ISAC XDU"); + printk(KERN_WARNING "HiSax: ISAC XDU\n"); + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + isac_sched_event(cs, D_CLEARBUSY); + if (cs->tx_skb) { /* Restart frame */ + skb_push(cs->tx_skb, cs->tx_cnt); + cs->tx_cnt = 0; + isac_fill_fifo(cs); + } else { + printk(KERN_WARNING "HiSax: ISAC XDU no skb\n"); + debugl1(cs, "ISAC XDU no skb"); + } + } + if (exval & 0x04) { /* MOS */ v1 = cs->readisac(cs, ISAC_MOSR); if (cs->debug & L1_DEB_MONITOR) debugl1(cs, "ISAC MOSR %02x", v1); #if ARCOFI_USE if (v1 & 0x08) { - if (!cs->mon_rx) { - if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (!cs->dc.isac.mon_rx) { + if (!(cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC MON RX out of memory!"); - cs->mocr &= 0xf0; - cs->mocr |= 0x0a; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->dc.isac.mocr &= 0xf0; + cs->dc.isac.mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); goto afterMONR0; } else - cs->mon_rxp = 0; + cs->dc.isac.mon_rxp = 0; } - if (cs->mon_rxp >= MAX_MON_FRAME) { - cs->mocr &= 0xf0; - cs->mocr |= 0x0a; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - cs->mon_rxp = 0; + if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) { + cs->dc.isac.mocr &= 0xf0; + cs->dc.isac.mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mon_rxp = 0; if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC MON RX overflow!"); goto afterMONR0; } - cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR0); + cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR0); if (cs->debug & L1_DEB_MONITOR) - debugl1(cs, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]); - if (cs->mon_rxp == 1) { - cs->mocr |= 0x04; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); + debugl1(cs, "ISAC MOR0 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]); + if (cs->dc.isac.mon_rxp == 1) { + cs->dc.isac.mocr |= 0x04; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); } } afterMONR0: if (v1 & 0x80) { - if (!cs->mon_rx) { - if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (!cs->dc.isac.mon_rx) { + if (!(cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC MON RX out of memory!"); - cs->mocr &= 0x0f; - cs->mocr |= 0xa0; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->dc.isac.mocr &= 0x0f; + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); goto afterMONR1; } else - cs->mon_rxp = 0; + cs->dc.isac.mon_rxp = 0; } - if (cs->mon_rxp >= MAX_MON_FRAME) { - cs->mocr &= 0x0f; - cs->mocr |= 0xa0; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - cs->mon_rxp = 0; + if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) { + cs->dc.isac.mocr &= 0x0f; + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mon_rxp = 0; if (cs->debug & L1_DEB_WARN) debugl1(cs, "ISAC MON RX overflow!"); goto afterMONR1; } - cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR1); + cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR1); if (cs->debug & L1_DEB_MONITOR) - debugl1(cs, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]); - cs->mocr |= 0x40; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); + debugl1(cs, "ISAC MOR1 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]); + cs->dc.isac.mocr |= 0x40; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); } afterMONR1: if (v1 & 0x04) { - cs->mocr &= 0xf0; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - cs->mocr |= 0x0a; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags); + cs->dc.isac.mocr &= 0xf0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + isac_sched_event(cs, D_RX_MON0); } if (v1 & 0x40) { - cs->mocr &= 0x0f; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - cs->mocr |= 0xa0; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags); + cs->dc.isac.mocr &= 0x0f; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + isac_sched_event(cs, D_RX_MON1); } if (v1 & 0x02) { - if ((!cs->mon_tx) || (cs->mon_txc && - (cs->mon_txp >= cs->mon_txc) && + if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc && + (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) && !(v1 & 0x08))) { - cs->mocr &= 0xf0; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - cs->mocr |= 0x0a; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - if (cs->mon_txc && - (cs->mon_txp >= cs->mon_txc)) - test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + cs->dc.isac.mocr &= 0xf0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + if (cs->dc.isac.mon_txc && + (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) + isac_sched_event(cs, D_TX_MON0); goto AfterMOX0; } - if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) { - test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) { + isac_sched_event(cs, D_TX_MON0); goto AfterMOX0; } cs->writeisac(cs, ISAC_MOX0, - cs->mon_tx[cs->mon_txp++]); + cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]); if (cs->debug & L1_DEB_MONITOR) - debugl1(cs, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, "ISAC %02x -> MOX0", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]); } AfterMOX0: if (v1 & 0x20) { - if ((!cs->mon_tx) || (cs->mon_txc && - (cs->mon_txp >= cs->mon_txc) && + if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc && + (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) && !(v1 & 0x80))) { - cs->mocr &= 0x0f; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - cs->mocr |= 0xa0; - cs->writeisac(cs, ISAC_MOCR, cs->mocr); - if (cs->mon_txc && - (cs->mon_txp >= cs->mon_txc)) - test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + cs->dc.isac.mocr &= 0x0f; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + cs->dc.isac.mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr); + if (cs->dc.isac.mon_txc && + (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) + isac_sched_event(cs, D_TX_MON1); goto AfterMOX1; } - if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) { - test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) { + isac_sched_event(cs, D_TX_MON1); goto AfterMOX1; } cs->writeisac(cs, ISAC_MOX1, - cs->mon_tx[cs->mon_txp++]); + cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]); if (cs->debug & L1_DEB_MONITOR) - debugl1(cs, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, "ISAC %02x -> MOX1", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]); } AfterMOX1: #endif @@ -535,9 +573,9 @@ ISAC_l1hw(struct PStack *st, int pr, void *arg) test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; case (HW_RESET | REQUEST): - if ((cs->ph_state == ISAC_IND_EI) || - (cs->ph_state == ISAC_IND_DR) || - (cs->ph_state == ISAC_IND_RS)) + if ((cs->dc.isac.ph_state == ISAC_IND_EI) || + (cs->dc.isac.ph_state == ISAC_IND_DR) || + (cs->dc.isac.ph_state == ISAC_IND_RS)) ph_command(cs, ISAC_CMD_TIM); else ph_command(cs, ISAC_CMD_RS); @@ -576,7 +614,7 @@ ISAC_l1hw(struct PStack *st, int pr, void *arg) discard_queue(&cs->rq); discard_queue(&cs->sq); if (cs->tx_skb) { - dev_kfree_skb(cs->tx_skb); + idev_kfree_skb(cs->tx_skb, FREE_WRITE); cs->tx_skb = NULL; } if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) @@ -597,27 +635,50 @@ setstack_isac(struct PStack *st, struct IsdnCardState *cs) st->l1.l1hw = ISAC_l1hw; } +void +DC_Close_isac(struct IsdnCardState *cs) { + if (cs->dc.isac.mon_rx) { + kfree(cs->dc.isac.mon_rx); + cs->dc.isac.mon_rx = NULL; + } + if (cs->dc.isac.mon_tx) { + kfree(cs->dc.isac.mon_tx); + cs->dc.isac.mon_tx = NULL; + } +} + static void dbusy_timer_handler(struct IsdnCardState *cs) { struct PStack *stptr; - int val; + int rbch, star; if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { - if (cs->debug) { - debugl1(cs, "D-Channel Busy"); - val = cs->readisac(cs, ISAC_RBCH); - if (val & ISAC_RBCH_XAC) - debugl1(cs, "ISAC XAC"); - else - debugl1(cs, "ISAC No XAC"); - } - test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); - stptr = cs->stlist; - - while (stptr != NULL) { - stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); - stptr = stptr->next; + rbch = cs->readisac(cs, ISAC_RBCH); + star = cs->readisac(cs, ISAC_STAR); + if (cs->debug) + debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x", + rbch, star); + if (rbch & ISAC_RBCH_XAC) { /* D-Channel Busy */ + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + stptr = stptr->next; + } + } else { + /* discard frame; reset transceiver */ + test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags); + if (cs->tx_skb) { + idev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } else { + printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n"); + debugl1(cs, "D-Channel Busy no skb"); + } + cs->writeisac(cs, ISAC_CMDR, 0x01); /* Transmitter reset */ + cs->irq_func(cs->irq, cs, NULL); } } } @@ -627,11 +688,14 @@ initisac(struct IsdnCardState *cs)) { cs->tqueue.routine = (void *) (void *) isac_bh; cs->setstack_d = setstack_isac; + cs->DC_Close = DC_Close_isac; + cs->dc.isac.mon_tx = NULL; + cs->dc.isac.mon_rx = NULL; cs->dbusytimer.function = (void *) dbusy_timer_handler; cs->dbusytimer.data = (long) cs; init_timer(&cs->dbusytimer); cs->writeisac(cs, ISAC_MASK, 0xff); - cs->mocr = 0xaa; + cs->dc.isac.mocr = 0xaa; if (test_bit(HW_IOM1, &cs->HW_Flags)) { /* IOM 1 Mode */ cs->writeisac(cs, ISAC_ADF2, 0x0); @@ -641,7 +705,9 @@ initisac(struct IsdnCardState *cs)) cs->writeisac(cs, ISAC_MODE, 0xc9); } else { /* IOM 2 Mode */ - cs->writeisac(cs, ISAC_ADF2, 0x80); + if (!cs->dc.isac.adf2) + cs->dc.isac.adf2 = 0x80; + cs->writeisac(cs, ISAC_ADF2, cs->dc.isac.adf2); cs->writeisac(cs, ISAC_SQXR, 0x2f); cs->writeisac(cs, ISAC_SPCR, 0x00); cs->writeisac(cs, ISAC_STCR, 0x70); @@ -672,7 +738,7 @@ clear_pending_isac_ints(struct IsdnCardState *cs)) } val = cs->readisac(cs, ISAC_CIR0); debugl1(cs, "ISAC CIR0 %x", val); - cs->ph_state = (val >> 2) & 0xf; + cs->dc.isac.ph_state = (val >> 2) & 0xf; isac_sched_event(cs, D_L1STATECHANGE); /* Disable all IRQ */ cs->writeisac(cs, ISAC_MASK, 0xFF); diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c index ba9247a9f..96442ff7d 100644 --- a/drivers/isdn/hisax/isar.c +++ b/drivers/isdn/hisax/isar.c @@ -1,4 +1,4 @@ -/* $Id: isar.c,v 1.2 1998/11/15 23:54:53 keil Exp $ +/* $Id: isar.c,v 1.5 1999/08/25 16:59:55 keil Exp $ * isar.c ISAR (Siemens PSB 7110) specific routines * @@ -6,6 +6,16 @@ * * * $Log: isar.c,v $ + * Revision 1.5 1999/08/25 16:59:55 keil + * Make ISAR V32bis modem running + * Make LL->HL interface open for additional commands + * + * Revision 1.4 1999/08/05 20:43:18 keil + * ISAR analog modem support + * + * Revision 1.3 1999/07/01 08:11:45 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.2 1998/11/15 23:54:53 keil * changes from 2.0 * @@ -303,6 +313,10 @@ isar_load_firmware(struct IsdnCardState *cs, u_char *buf) printk(KERN_DEBUG"isar firmware block %5d words loaded\n", blk_head.len); } + /* 10ms delay */ + cnt = 10; + while (cnt--) + udelay(1000); msg[0] = 0xff; msg[1] = 0xfe; ireg->bstat = 0; @@ -337,20 +351,26 @@ isar_load_firmware(struct IsdnCardState *cs, u_char *buf) printk(KERN_DEBUG"isar general status event %x\n", ireg->bstat); } + /* 10ms delay */ + cnt = 10; + while (cnt--) + udelay(1000); ireg->iis = 0; if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) { printk(KERN_ERR"isar sendmsg self tst failed\n"); ret = 1;goto reterrflg; } - cnt = 1000; /* max 10 ms */ + cnt = 10000; /* max 100 ms */ while ((ireg->iis != ISAR_IIS_DIAG) && cnt) { udelay(10); cnt--; } + udelay(1000); if (!cnt) { printk(KERN_ERR"isar no self tst response\n"); ret = 1;goto reterrflg; - } else if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1) + } + if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1) && (ireg->par[0] == 0)) { printk(KERN_DEBUG"isar selftest OK\n"); } else { @@ -363,11 +383,12 @@ isar_load_firmware(struct IsdnCardState *cs, u_char *buf) printk(KERN_ERR"isar RQST SVN failed\n"); ret = 1;goto reterror; } - cnt = 10000; /* max 100 ms */ + cnt = 30000; /* max 300 ms */ while ((ireg->iis != ISAR_IIS_DIAG) && cnt) { udelay(10); cnt--; } + udelay(1000); if (!cnt) { printk(KERN_ERR"isar no SVN response\n"); ret = 1;goto reterrflg; @@ -396,7 +417,18 @@ reterror: return(ret); } -void +extern void BChannel_bh(struct BCState *); +#define B_LL_NOCARRIER 8 +#define B_LL_CONNECT 9 +#define B_LL_OK 10 + +static void +isar_bh(struct BCState *bcs) +{ + BChannel_bh(bcs); +} + +static void isar_sched_event(struct BCState *bcs, int event) { bcs->event |= 1 << event; @@ -425,7 +457,9 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); break; case L1_MODE_TRANS: + case L1_MODE_V32: if ((skb = dev_alloc_skb(ireg->clsb))) { + SET_SKB_FREE(skb); rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb)); skb_queue_tail(&bcs->rqueue, skb); isar_sched_event(bcs, B_RCVBUFREADY); @@ -456,9 +490,10 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */ printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n", bcs->hw.isar.rcvidx); - } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2))) + } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2))) { printk(KERN_WARNING "ISAR: receive out of memory\n"); - else { + } else { + SET_SKB_FREE(skb); memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2), bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2); skb_queue_tail(&bcs->rqueue, skb); @@ -512,6 +547,7 @@ isar_fill_fifo(struct BCState *bcs) printk(KERN_ERR"isar_fill_fifo wrong mode 0\n"); break; case L1_MODE_TRANS: + case L1_MODE_V32: if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, 0, count, ptr)) { if (cs->debug) @@ -557,7 +593,7 @@ send_frames(struct BCState *bcs) if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->hw.isar.txcnt = 0; bcs->tx_skb = NULL; } @@ -593,8 +629,137 @@ check_send(struct IsdnCardState *cs, u_char rdm) } } +const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", + "300", "600", "1200", "2400", "4800", "7200", + "9600nt", "9600t", "12000", "14400", "WRONG"}; +const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", + "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"}; -static char debbuf[64]; +static void +isar_pump_status_rsp(struct BCState *bcs, struct isar_reg *ireg) { + struct IsdnCardState *cs = bcs->cs; + u_char ril = ireg->par[0]; + u_char rim; + + if (!test_and_clear_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags)) + return; + if (ril > 14) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "wrong pstrsp ril=%d",ril); + ril = 15; + } + switch(ireg->par[1]) { + case 0: + rim = 0; + break; + case 0x20: + rim = 2; + break; + case 0x40: + rim = 3; + break; + case 0x41: + rim = 4; + break; + case 0x51: + rim = 5; + break; + case 0x61: + rim = 6; + break; + case 0x71: + rim = 7; + break; + case 0x82: + rim = 8; + break; + case 0x92: + rim = 9; + break; + case 0xa2: + rim = 10; + break; + default: + rim = 1; + break; + } + sprintf(bcs->hw.isar.conmsg,"%s %s", dmril[ril], dmrim[rim]); + bcs->conmsg = bcs->hw.isar.conmsg; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump strsp %s", bcs->conmsg); +} + +static void +isar_pump_status_ev(struct BCState *bcs, u_char devt) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + + switch(devt) { + case PSEV_10MS_TIMER: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev TIMER"); + break; + case PSEV_CON_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CONNECT"); + l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL); + break; + case PSEV_CON_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev NO CONNECT"); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + l1_msg_b(bcs->st, PH_DEACTIVATE | REQUEST, NULL); + break; + case PSEV_V24_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev V24 OFF"); + break; + case PSEV_CTS_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CTS ON"); + break; + case PSEV_CTS_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CTS OFF"); + break; + case PSEV_DCD_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CARRIER ON"); + test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + break; + case PSEV_DCD_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CARRIER OFF"); + break; + case PSEV_DSR_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev DSR ON"); + break; + case PSEV_DSR_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev DSR_OFF"); + break; + case PSEV_REM_RET: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev REMOTE RETRAIN"); + break; + case PSEV_REM_REN: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev REMOTE RENEGOTIATE"); + break; + case PSEV_GSTN_CLR: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev GSTN CLEAR", devt); + break; + default: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "unknown pump stev %x", devt); + break; + } +} + +static char debbuf[128]; void isar_int_main(struct IsdnCardState *cs) @@ -628,10 +793,31 @@ isar_int_main(struct IsdnCardState *cs) if (cs->debug & L1_DEB_WARN) debugl1(cs, "Buffer STEV dpath%d msb(%x)", ireg->iis>>6, ireg->cmsb); + case ISAR_IIS_PSTEV: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + rcv_mbox(cs, ireg, (u_char *)ireg->par); + isar_pump_status_ev(bcs, ireg->cmsb); + } else { + debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar spurious IIS_PSTEV %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } break; - case ISAR_IIS_DIAG: case ISAR_IIS_PSTRSP: - case ISAR_IIS_PSTEV: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + rcv_mbox(cs, ireg, (u_char *)ireg->par); + isar_pump_status_rsp(bcs, ireg); + } else { + debugl1(cs, "isar spurious IIS_PSTRSP %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar spurious IIS_PSTRSP %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_DIAG: case ISAR_IIS_BSTRSP: case ISAR_IIS_IOM2RSP: rcv_mbox(cs, ireg, (u_char *)ireg->par); @@ -645,6 +831,12 @@ isar_int_main(struct IsdnCardState *cs) debugl1(cs, debbuf); } break; + case ISAR_IIS_INVMSG: + rcv_mbox(cs, ireg, debbuf); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "invalid msg his:%x", + ireg->cmsb); + break; default: rcv_mbox(cs, ireg, debbuf); if (cs->debug & L1_DEB_WARN) @@ -655,11 +847,12 @@ isar_int_main(struct IsdnCardState *cs) restore_flags(flags); } -void +static void setup_pump(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); - + u_char ctrl, param[6]; + switch (bcs->mode) { case L1_MODE_NULL: case L1_MODE_TRANS: @@ -670,6 +863,41 @@ setup_pump(struct BCState *bcs) { bcs->hw.isar.dpath); } break; + case L1_MODE_V32: + ctrl = PMOD_DATAMODEM; + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + ctrl |= PCTRL_ORIG; + param[5] = PV32P6_CTN; + } else { + param[5] = PV32P6_ATN; + } + param[0] = 6; /* 6 db */ + param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B | + PV32P2_V22C | PV32P2_V21 | PV32P2_BEL; + param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B; + param[3] = PV32P4_UT144; + param[4] = PV32P5_UT144; + if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param)) { + if (cs->debug) + debugl1(cs, "isar pump datamodem cfg dp%d failed", + bcs->hw.isar.dpath); + } + break; + case L1_MODE_FAX: + ctrl = PMOD_FAX; + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + ctrl |= PCTRL_ORIG; + param[1] = PFAXP2_CTN; + } else { + param[1] = PFAXP2_ATN; + } + param[0] = 6; /* 6 db */ + if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param)) { + if (cs->debug) + debugl1(cs, "isar pump faxmodem cfg dp%d failed", + bcs->hw.isar.dpath); + } + break; } if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) { if (cs->debug) @@ -678,10 +906,11 @@ setup_pump(struct BCState *bcs) { } } -void +static void setup_sart(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char ctrl, param[2]; switch (bcs->mode) { case L1_MODE_NULL: @@ -699,9 +928,21 @@ setup_sart(struct BCState *bcs) { } break; case L1_MODE_HDLC: - if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, "\0")) { + case L1_MODE_FAX: + param[0] = 0; + if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, param)) { if (cs->debug) - debugl1(cs, "isar sart binary dp%d failed", + debugl1(cs, "isar sart hdlc dp%d failed", + bcs->hw.isar.dpath); + } + break; + case L1_MODE_V32: + ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; + param[0] = S_P1_CHS_8; + param[1] = S_P2_BFT_DEF; + if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2, param)) { + if (cs->debug) + debugl1(cs, "isar sart v14 dp%d failed", bcs->hw.isar.dpath); } break; @@ -713,22 +954,26 @@ setup_sart(struct BCState *bcs) { } } -void +static void setup_iom2(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); - u_char cmsb = 0, msg[5] = {0x10,0,0,0,0}; + u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD,0,0,0,0}; + if (bcs->channel) + msg[1] = msg[3] = 1; switch (bcs->mode) { case L1_MODE_NULL: + cmsb = 0; /* dummy slot */ msg[1] = msg[3] = bcs->hw.isar.dpath + 2; break; case L1_MODE_TRANS: case L1_MODE_HDLC: - cmsb = 0x80; - if (bcs->channel) - msg[1] = msg[3] = 1; + break; + case L1_MODE_V32: + case L1_MODE_FAX: + cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV; break; } if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) { @@ -752,6 +997,9 @@ modeisar(struct BCState *bcs, int mode, int bc) bcs->channel = bc; switch (mode) { case L1_MODE_NULL: /* init */ + if (!bcs->hw.isar.dpath) + /* no init for dpath 0 */ + return(0); break; case L1_MODE_TRANS: case L1_MODE_HDLC: @@ -763,7 +1011,19 @@ modeisar(struct BCState *bcs, int mode, int bc) &bcs->hw.isar.reg->Flags)) bcs->hw.isar.dpath = 1; else { - printk(KERN_ERR"isar modeisar both pathes in use\n"); + printk(KERN_WARNING"isar modeisar both pathes in use\n"); + return(1); + } + break; + case L1_MODE_V32: + case L1_MODE_FAX: + /* only datapath 1 */ + if (!test_and_set_bit(ISAR_DP1_USE, + &bcs->hw.isar.reg->Flags)) + bcs->hw.isar.dpath = 1; + else { + printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n"); + debugl1(cs, "isar modeisar analog funktions only with DP1"); return(1); } break; @@ -774,8 +1034,8 @@ modeisar(struct BCState *bcs, int mode, int bc) bcs->hw.isar.dpath, bcs->mode, mode, bc); bcs->mode = mode; setup_pump(bcs); - setup_sart(bcs); setup_iom2(bcs); + setup_sart(bcs); if (bcs->mode == L1_MODE_NULL) { /* Clear resources */ if (bcs->hw.isar.dpath == 1) @@ -806,6 +1066,7 @@ isar_setup(struct IsdnCardState *cs) cs->bcs[i].mode = 0; cs->bcs[i].hw.isar.dpath = i + 1; modeisar(&cs->bcs[i], 0, 0); + cs->bcs[i].tqueue.routine = (void *) (void *) isar_bh; } } @@ -853,8 +1114,24 @@ isar_l2l1(struct PStack *st, int pr, void *arg) break; case (PH_ACTIVATE | REQUEST): test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); - modeisar(st->l1.bcs, st->l1.mode, st->l1.bc); - l1_msg_b(st, pr, arg); + st->l1.bcs->hw.isar.conmsg[0] = 0; + if (test_bit(FLG_ORIG, &st->l2.flag)) + test_and_set_bit(BC_FLG_ORIG, &st->l1.bcs->Flag); + else + test_and_clear_bit(BC_FLG_ORIG, &st->l1.bcs->Flag); + switch(st->l1.mode) { + case L1_MODE_TRANS: + case L1_MODE_HDLC: + if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc)) + l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); + else + l1_msg_b(st, PH_ACTIVATE | REQUEST, arg); + break; + case L1_MODE_V32: + if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc)) + l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); + break; + } break; case (PH_DEACTIVATE | REQUEST): l1_msg_b(st, pr, arg); @@ -882,7 +1159,7 @@ close_isarstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); if (bcs->cs->debug & L1_DEB_HSCX) @@ -927,6 +1204,36 @@ setstack_isar(struct PStack *st, struct BCState *bcs) return (0); } +int +isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) { + u_long adr; + int features; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar_auxcmd cmd/ch %x/%d", ic->command, ic->arg); + switch (ic->command) { + case (ISDN_CMD_IOCTL): + switch (ic->arg) { + case (9): /* load firmware */ + features = ISDN_FEATURE_L2_MODEM; + memcpy(&adr, ic->parm.num, sizeof(ulong)); + if (isar_load_firmware(cs, (u_char *)adr)) + return(1); + else + ll_run(cs, features); + break; + default: + printk(KERN_DEBUG "HiSax: invalid ioclt %d\n", + (int) ic->arg); + return(-EINVAL); + } + break; + default: + return(-EINVAL); + } + return(0); +} + HISAX_INITFUNC(void initisar(struct IsdnCardState *cs)) { diff --git a/drivers/isdn/hisax/isar.h b/drivers/isdn/hisax/isar.h index de892f813..c6ac6532b 100644 --- a/drivers/isdn/hisax/isar.h +++ b/drivers/isdn/hisax/isar.h @@ -1,10 +1,20 @@ -/* $Id: isar.h,v 1.2 1998/11/15 23:54:54 keil Exp $ +/* $Id: isar.h,v 1.5 1999/08/25 16:59:59 keil Exp $ * isar.h ISAR (Siemens PSB 7110) specific defines * * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: isar.h,v $ + * Revision 1.5 1999/08/25 16:59:59 keil + * Make ISAR V32bis modem running + * Make LL->HL interface open for additional commands + * + * Revision 1.4 1999/08/05 20:43:20 keil + * ISAR analog modem support + * + * Revision 1.3 1999/07/01 08:11:46 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.2 1998/11/15 23:54:54 keil * changes from 2.0 * @@ -27,38 +37,41 @@ #define ISAR_WADR 0x4a #define ISAR_RADR 0x48 -#define ISAR_HIS_VNR 0x14 -#define ISAR_HIS_DKEY 0x02 -#define ISAR_HIS_FIRM 0x1e -#define ISAR_HIS_STDSP 0x08 -#define ISAR_HIS_DIAG 0x05 -#define ISAR_HIS_P0CFG 0x3c -#define ISAR_HIS_P12CFG 0x24 +#define ISAR_HIS_VNR 0x14 +#define ISAR_HIS_DKEY 0x02 +#define ISAR_HIS_FIRM 0x1e +#define ISAR_HIS_STDSP 0x08 +#define ISAR_HIS_DIAG 0x05 +#define ISAR_HIS_P0CFG 0x3c +#define ISAR_HIS_P12CFG 0x24 #define ISAR_HIS_SARTCFG 0x25 #define ISAR_HIS_PUMPCFG 0x26 +#define ISAR_HIS_PUMPCTRL 0x2a #define ISAR_HIS_IOM2CFG 0x27 #define ISAR_HIS_IOM2REQ 0x07 -#define ISAR_HIS_BSTREQ 0x0c -#define ISAR_HIS_PSTREQ 0x0e -#define ISAR_HIS_SDATA 0x20 -#define ISAR_HIS_DPS1 0x40 -#define ISAR_HIS_DPS2 0x80 -#define SET_DPS(x) ((x<<6) & 0xc0) - -#define ISAR_IIS_MSCMSD 0x3f -#define ISAR_IIS_VNR 0x15 -#define ISAR_IIS_DKEY 0x03 -#define ISAR_IIS_FIRM 0x1f -#define ISAR_IIS_STDSP 0x09 -#define ISAR_IIS_DIAG 0x25 -#define ISAR_IIS_GSTEV 0x0 -#define ISAR_IIS_BSTEV 0x28 -#define ISAR_IIS_BSTRSP 0x2c -#define ISAR_IIS_PSTRSP 0x2e -#define ISAR_IIS_PSTEV 0x2a +#define ISAR_HIS_IOM2CTRL 0x2b +#define ISAR_HIS_BSTREQ 0x0c +#define ISAR_HIS_PSTREQ 0x0e +#define ISAR_HIS_SDATA 0x20 +#define ISAR_HIS_DPS1 0x40 +#define ISAR_HIS_DPS2 0x80 +#define SET_DPS(x) ((x<<6) & 0xc0) + +#define ISAR_IIS_MSCMSD 0x3f +#define ISAR_IIS_VNR 0x15 +#define ISAR_IIS_DKEY 0x03 +#define ISAR_IIS_FIRM 0x1f +#define ISAR_IIS_STDSP 0x09 +#define ISAR_IIS_DIAG 0x25 +#define ISAR_IIS_GSTEV 0x00 +#define ISAR_IIS_BSTEV 0x28 +#define ISAR_IIS_BSTRSP 0x2c +#define ISAR_IIS_PSTRSP 0x2e +#define ISAR_IIS_PSTEV 0x2a #define ISAR_IIS_IOM2RSP 0x27 +#define ISAR_IIS_RDATA 0x20 +#define ISAR_IIS_INVMSG 0x3f -#define ISAR_IIS_RDATA 0x20 #define ISAR_CTRL_SWVER 0x10 #define ISAR_CTRL_STST 0x40 @@ -66,26 +79,114 @@ #define ISAR_DP1_USE 1 #define ISAR_DP2_USE 2 +#define ISAR_RATE_REQ 3 +#define PMOD_DISABLE 0 +#define PMOD_FAX 1 +#define PMOD_DATAMODEM 2 +#define PMOD_HALFDUPLEX 3 +#define PMOD_V110 4 +#define PMOD_DTMF 5 +#define PMOD_DTMF_TRANS 6 #define PMOD_BYPASS 7 +#define PCTRL_ORIG 0x80 +#define PV32P2_V23R 0x40 +#define PV32P2_V22A 0x20 +#define PV32P2_V22B 0x10 +#define PV32P2_V22C 0x08 +#define PV32P2_V21 0x02 +#define PV32P2_BEL 0x01 + +// LSB MSB in ISAR doc wrong !!! Arghhh +#define PV32P3_AMOD 0x80 +#define PV32P3_V32B 0x02 +#define PV32P3_V23B 0x01 +#define PV32P4_48 0x11 +#define PV32P5_48 0x05 +#define PV32P4_UT48 0x11 +#define PV32P5_UT48 0x0d +#define PV32P4_96 0x11 +#define PV32P5_96 0x03 +#define PV32P4_UT96 0x11 +#define PV32P5_UT96 0x0f +#define PV32P4_B96 0x91 +#define PV32P5_B96 0x0b +#define PV32P4_UTB96 0xd1 +#define PV32P5_UTB96 0x0f +#define PV32P4_120 0xb1 +#define PV32P5_120 0x09 +#define PV32P4_UT120 0xf1 +#define PV32P5_UT120 0x0f +#define PV32P4_144 0x99 +#define PV32P5_144 0x09 +#define PV32P4_UT144 0xf9 +#define PV32P5_UT144 0x0f +#define PV32P6_CTN 0x01 +#define PV32P6_ATN 0x02 + +#define PFAXP2_CTN 0x01 +#define PFAXP2_ATN 0x04 + +#define PSEV_10MS_TIMER 0x02 +#define PSEV_CON_ON 0x18 +#define PSEV_CON_OFF 0x19 +#define PSEV_V24_OFF 0x20 +#define PSEV_CTS_ON 0x21 +#define PSEV_CTS_OFF 0x22 +#define PSEV_DCD_ON 0x23 +#define PSEV_DCD_OFF 0x24 +#define PSEV_DSR_ON 0x25 +#define PSEV_DSR_OFF 0x26 +#define PSEV_REM_RET 0xcc +#define PSEV_REM_REN 0xcd +#define PSEV_GSTN_CLR 0xd4 + +#define PCTRL_LOC_RET 0xcf +#define PCTRL_LOC_REN 0xce + #define SMODE_DISABLE 0 +#define SMODE_V14 2 #define SMODE_HDLC 3 #define SMODE_BINARY 4 +#define SMODE_FSK_V14 5 + +#define SCTRL_HDMC_BOTH 0x00 +#define SCTRL_HDMC_DTX 0x80 +#define SCTRL_HDMC_DRX 0x40 +#define S_P1_OVSP 0x40 +#define S_P1_SNP 0x20 +#define S_P1_EOP 0x10 +#define S_P1_EDP 0x08 +#define S_P1_NSB 0x04 +#define S_P1_CHS_8 0x03 +#define S_P1_CHS_7 0x02 +#define S_P1_CHS_6 0x01 +#define S_P1_CHS_5 0x00 + +#define S_P2_BFT_DEF 0x10 + +#define IOM_CTRL_ENA 0x80 +#define IOM_CTRL_NOPCM 0x00 +#define IOM_CTRL_ALAW 0x02 +#define IOM_CTRL_ULAW 0x04 +#define IOM_CTRL_RCV 0x01 + +#define IOM_P1_TXD 0x10 #define HDLC_FED 0x40 #define HDLC_FSD 0x20 #define HDLC_FST 0x20 #define HDLC_ERROR 0x1c +#define SART_NMD 0x01 #define BSTAT_RDM0 0x1 #define BSTAT_RDM1 0x2 #define BSTAT_RDM2 0x4 #define BSTAT_RDM3 0x8 - extern int ISARVersion(struct IsdnCardState *cs, char *s); -extern int isar_load_firmware(struct IsdnCardState *cs, u_char *buf); extern void isar_int_main(struct IsdnCardState *cs); extern void initisar(struct IsdnCardState *cs); extern void isar_fill_fifo(struct BCState *bcs); +extern int isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic); diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index c8f88a162..c819c4811 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -1,4 +1,4 @@ -/* $Id: isdnl1.c,v 2.31 1998/11/15 23:54:56 keil Exp $ +/* $Id: isdnl1.c,v 2.36 1999/08/25 16:50:57 keil Exp $ * isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards * based on the teles driver from Jan den Ouden @@ -15,6 +15,24 @@ * * * $Log: isdnl1.c,v $ + * Revision 2.36 1999/08/25 16:50:57 keil + * Fix bugs which cause 2.3.14 hangs (waitqueue init) + * + * Revision 2.35 1999/08/22 20:27:07 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 2.34 1999/07/09 13:50:15 keil + * remove unused variable + * + * Revision 2.33 1999/07/09 13:34:33 keil + * remove debug code + * + * Revision 2.32 1999/07/01 08:11:47 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.31 1998/11/15 23:54:56 keil * changes from 2.0 * @@ -120,7 +138,7 @@ * */ -const char *l1_revision = "$Revision: 2.31 $"; +const char *l1_revision = "$Revision: 2.36 $"; #define __NO_VERSION__ #include "hisax.h" @@ -298,9 +316,18 @@ DChannel_proc_rcv(struct IsdnCardState *cs) Logl2Frame(cs, skb, "PH_DATA", 1); #endif stptr = cs->stlist; + if (skb->len<3) { + debugl1(cs, "D-channel frame too short(%d)",skb->len); + idev_kfree_skb(skb, FREE_READ); + return; + } + if ((skb->data[0] & 1) || !(skb->data[1] &1)) { + debugl1(cs, "D-channel frame wrong EA0/EA1"); + idev_kfree_skb(skb, FREE_READ); + return; + } sapi = skb->data[0] >> 2; tei = skb->data[1] >> 1; - if (cs->debug & DEB_DLOG_HEX) LogFrame(cs, skb->data, skb->len); if (cs->debug & DEB_DLOG_VERBOSE) @@ -323,7 +350,7 @@ DChannel_proc_rcv(struct IsdnCardState *cs) stptr = stptr->next; } } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } else if (sapi == CTRL_SAPI) { /* sapi 0 */ found = 0; while (stptr != NULL) @@ -334,7 +361,7 @@ DChannel_proc_rcv(struct IsdnCardState *cs) } else stptr = stptr->next; if (!found) - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } } } @@ -372,7 +399,7 @@ BChannel_proc_rcv(struct BCState *bcs) } } -static void +void BChannel_bh(struct BCState *bcs) { if (!bcs) diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index ccef2682e..ca721ea86 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1,4 +1,4 @@ -/* $Id: isdnl2.c,v 2.16 1998/11/15 23:55:01 keil Exp $ +/* $Id: isdnl2.c,v 2.20 1999/08/25 16:52:04 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,18 @@ * Fritz Elfert * * $Log: isdnl2.c,v $ + * Revision 2.20 1999/08/25 16:52:04 keil + * Make gcc on AXP happy + * + * Revision 2.19 1999/08/05 20:40:26 keil + * Fix interlayer communication + * + * Revision 2.18 1999/07/21 14:46:16 keil + * changes from EICON certification + * + * Revision 2.17 1999/07/01 08:11:50 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.16 1998/11/15 23:55:01 keil * changes from 2.0 * @@ -71,7 +83,7 @@ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 2.16 $"; +const char *l2_revision = "$Revision: 2.20 $"; static void l2m_debug(struct FsmInst *fi, char *fmt, ...); @@ -106,7 +118,7 @@ static char *strL2State[] = enum { EV_L2_UI, - EV_L2_SABMX, + EV_L2_SABME, EV_L2_DISC, EV_L2_DM, EV_L2_UA, @@ -116,22 +128,25 @@ enum { EV_L2_DL_DATA, EV_L2_ACK_PULL, EV_L2_DL_UNIT_DATA, - EV_L2_DL_ESTABLISH, - EV_L2_DL_RELEASE, + EV_L2_DL_ESTABLISH_REQ, + EV_L2_DL_RELEASE_REQ, EV_L2_MDL_ASSIGN, EV_L2_MDL_REMOVE, EV_L2_MDL_ERROR, EV_L1_DEACTIVATE, EV_L2_T200, EV_L2_T203, + EV_L2_SET_OWN_BUSY, + EV_L2_CLEAR_OWN_BUSY, + EV_L2_FRAME_ERROR, }; -#define L2_EVENT_COUNT (EV_L2_T203+1) +#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1) static char *strL2Event[] = { "EV_L2_UI", - "EV_L2_SABMX", + "EV_L2_SABME", "EV_L2_DISC", "EV_L2_DM", "EV_L2_UA", @@ -141,19 +156,35 @@ static char *strL2Event[] = "EV_L2_DL_DATA", "EV_L2_ACK_PULL", "EV_L2_DL_UNIT_DATA", - "EV_L2_DL_ESTABLISH", - "EV_L2_DL_RELEASE", + "EV_L2_DL_ESTABLISH_REQ", + "EV_L2_DL_RELEASE_REQ", "EV_L2_MDL_ASSIGN", "EV_L2_MDL_REMOVE", "EV_L2_MDL_ERROR", "EV_L1_DEACTIVATE", "EV_L2_T200", "EV_L2_T203", + "EV_L2_SET_OWN_BUSY", + "EV_L2_CLEAR_OWN_BUSY", + "EV_L2_FRAME_ERROR", }; static int l2addrsize(struct Layer2 *l2); static void +set_peer_busy(struct Layer2 *l2) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) + test_and_set_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +clear_peer_busy(struct Layer2 *l2) { + if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) + test_and_clear_bit(FLG_L2BLOCK, &l2->flag); +} + +static void InitWin(struct Layer2 *l2) { int i; @@ -162,30 +193,45 @@ InitWin(struct Layer2 *l2) l2->windowar[i] = NULL; } -static void -ReleaseWin(struct Layer2 *l2) +static int +freewin1(struct Layer2 *l2) { int i, cnt = 0; for (i = 0; i < MAX_WINDOW; i++) { if (l2->windowar[i]) { cnt++; - dev_kfree_skb(l2->windowar[i]); + idev_kfree_skb(l2->windowar[i], FREE_WRITE); l2->windowar[i] = NULL; } } - if (cnt) + return cnt; +} + +inline void +freewin(struct PStack *st) +{ + freewin1(&st->l2); +} + +static void +ReleaseWin(struct Layer2 *l2) +{ + int cnt; + + if((cnt = freewin1(l2))) printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt); } -inline int +inline unsigned int cansend(struct PStack *st) { - int p1; + unsigned int p1; - p1 = st->l2.vs - st->l2.va; - if (p1 < 0) - p1 += (test_bit(FLG_MOD128, &st->l2.flag) ? 128 : 8); + if(test_bit(FLG_MOD128, &st->l2.flag)) + p1 = (st->l2.vs - st->l2.va) % 128; + else + p1 = (st->l2.vs - st->l2.va) % 8; return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag)); } @@ -195,7 +241,7 @@ clear_exception(struct Layer2 *l2) test_and_clear_bit(FLG_ACK_PEND, &l2->flag); test_and_clear_bit(FLG_REJEXC, &l2->flag); test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); - test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + clear_peer_busy(l2); } inline int @@ -244,98 +290,197 @@ enqueue_super(struct PStack *st, #define enqueue_ui(a, b) enqueue_super(a, b) inline int -IsUI(u_char * data, int ext) +IsUI(u_char * data) { return ((data[0] & 0xef) == UI); } inline int -IsUA(u_char * data, int ext) +IsUA(u_char * data) { return ((data[0] & 0xef) == UA); } inline int -IsDM(u_char * data, int ext) +IsDM(u_char * data) { return ((data[0] & 0xef) == DM); } inline int -IsDISC(u_char * data, int ext) +IsDISC(u_char * data) { return ((data[0] & 0xef) == DISC); } inline int -IsRR(u_char * data, int ext) +IsRR(u_char * data, struct PStack *st) { - if (ext) + if (test_bit(FLG_MOD128, &st->l2.flag)) return (data[0] == RR); else return ((data[0] & 0xf) == 1); } inline int -IsSFrame(u_char * data, int ext) +IsSFrame(u_char * data, struct PStack *st) { register u_char d = *data; - if (!ext) + if (!test_bit(FLG_MOD128, &st->l2.flag)) d &= 0xf; return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c)); } inline int -IsSABMX(u_char * data, int ext) +IsSABME(u_char * data, struct PStack *st) { u_char d = data[0] & ~0x10; - return (ext ? d == SABME : d == SABM); + return (test_bit(FLG_MOD128, &st->l2.flag) ? d == SABME : d == SABM); } inline int -IsREJ(u_char * data, int ext) +IsREJ(u_char * data, struct PStack *st) { - return (ext ? data[0] == REJ : (data[0] & 0xf) == REJ); + return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == REJ : (data[0] & 0xf) == REJ); } inline int -IsFRMR(u_char * data, int ext) +IsFRMR(u_char * data) { return ((data[0] & 0xef) == FRMR); } inline int -IsRNR(u_char * data, int ext) +IsRNR(u_char * data, struct PStack *st) { - return (ext ? data[0] == RNR : (data[0] & 0xf) == RNR); + return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == RNR : (data[0] & 0xf) == RNR); } -static int -legalnr(struct PStack *st, int nr) +int +iframe_error(struct PStack *st, struct sk_buff *skb) { - struct Layer2 *l2 = &st->l2; - int lnr, lvs; + int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 1 : 0); + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (rsp) + return 'L'; + + + if (skb->len <= i) + return 'N'; + + if ((skb->len - i) > st->l2.maxlen) + return 'O'; - lvs = (l2->vs >= l2->va) ? l2->vs : - (l2->vs + (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8)); - lnr = (nr >= l2->va) ? nr : (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); - return (lnr <= lvs); + + return 0; +} + +int +super_error(struct PStack *st, struct sk_buff *skb) +{ + if (skb->len != l2addrsize(&st->l2) + + (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1)) + return 'N'; + + return 0; +} + +int +unnum_error(struct PStack *st, struct sk_buff *skb, int wantrsp) +{ + int rsp = (*skb->data & 0x2) >> 1; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (rsp != wantrsp) + return 'L'; + + if (skb->len != l2addrsize(&st->l2) + 1) + return 'N'; + + return 0; +} + +int +UI_error(struct PStack *st, struct sk_buff *skb) +{ + int rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (rsp) + return 'L'; + + if (skb->len > st->l2.maxlen + l2addrsize(&st->l2) + 1) + return 'O'; + + return 0; +} + +int +FRMR_error(struct PStack *st, struct sk_buff *skb) +{ + int headers = l2addrsize(&st->l2) + 1; + u_char *datap = skb->data + headers; + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + + if (!rsp) + return 'L'; + + if (test_bit(FLG_MOD128, &st->l2.flag)) { + if (skb->len < headers + 5) + return 'N'; + else + l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x", + datap[0], datap[1], datap[2], + datap[3], datap[4]); + } else { + if (skb->len < headers + 3) + return 'N'; + else + l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x", + datap[0], datap[1], datap[2]); + } + + return 0; +} + +static unsigned int +legalnr(struct PStack *st, unsigned int nr) +{ + struct Layer2 *l2 = &st->l2; + + if(test_bit(FLG_MOD128, &l2->flag)) + return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); + else + return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); } static void -setva(struct PStack *st, int nr) +setva(struct PStack *st, unsigned int nr) { struct Layer2 *l2 = &st->l2; int len; while (l2->va != nr) { - l2->va = (l2->va + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + (l2->va)++; + if(test_bit(FLG_MOD128, &l2->flag)) + l2->va %= 128; + else + l2->va %= 8; len = l2->windowar[l2->sow]->len; if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type) len = -1; - dev_kfree_skb(l2->windowar[l2->sow]); + idev_kfree_skb(l2->windowar[l2->sow], FREE_WRITE); l2->windowar[l2->sow] = NULL; l2->sow = (l2->sow + 1) % l2->window; if (st->lli.l2writewakeup && (len >=0)) @@ -356,6 +501,7 @@ send_uframe(struct PStack *st, u_char cmd, u_char cr) printk(KERN_WARNING "isdl2 can't alloc sbbuff for send_uframe\n"); return; } + SET_SKB_FREE(skb); memcpy(skb_put(skb, i), tmp, i); enqueue_super(st, skb); } @@ -369,7 +515,7 @@ get_PollFlag(struct PStack * st, struct sk_buff * skb) inline void FreeSkb(struct sk_buff *skb) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } @@ -383,6 +529,48 @@ get_PollFlagFree(struct PStack *st, struct sk_buff *skb) return (PF); } +inline void +start_t200(struct PStack *st, int i) +{ + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); +} + +inline void +restart_t200(struct PStack *st, int i) +{ + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); +} + +inline void +stop_t200(struct PStack *st, int i) +{ + if(test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, i); +} + +inline void +st5_dl_release_l2l3(struct PStack *st) +{ + int pr; + + if(test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) + pr = DL_RELEASE | CONFIRM; + else + pr = DL_RELEASE | INDICATION; + + st->l2.l2l3(st, pr, NULL); +} + +inline void +lapb_dl_release_l2l3(struct PStack *st, int f) +{ + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | f, NULL); +} + static void establishlink(struct FsmInst *fi) { @@ -394,56 +582,91 @@ establishlink(struct FsmInst *fi) cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10; send_uframe(st, cmd, CMD); FsmDelTimer(&st->l2.t203, 1); - FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 1); - test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + restart_t200(st, 1); + test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); + freewin(st); FsmChangeState(fi, ST_L2_5); } static void -l2_mdl_error(struct FsmInst *fi, int event, void *arg) +l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; struct PStack *st = fi->userdata; - switch (event) { - case EV_L2_UA: - if (get_PollFlagFree(st, skb)) - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C'); - else - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D'); - break; - case EV_L2_DM: - if (get_PollFlagFree(st, skb)) - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B'); - else { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E'); - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); - } - break; - } + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C'); + else + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D'); } static void -l2_dl_establish(struct FsmInst *fi, int event, void *arg) +l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) { + struct sk_buff *skb = arg; struct PStack *st = fi->userdata; - int state = fi->state; - - if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { - FsmChangeState(fi, ST_L2_4); + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B'); + else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E'); establishlink(fi); - test_and_set_bit(FLG_L3_INIT, &st->l2.flag); - } else { - FsmChangeState(fi, ST_L2_3); - if (state == ST_L2_1) - st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } +} + +static void +l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct PStack *st = fi->userdata; + + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B'); + else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E'); } + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); +} + +static void +l2_go_st3(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L2_3); } static void -l2_send_ui(struct PStack *st) +l2_mdl_assign(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L2_3); + st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); +} + +static void +l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&st->l2.ui_queue, skb); + FsmChangeState(fi, ST_L2_2); + st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); +} + +static void +l2_queue_ui(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&st->l2.ui_queue, skb); +} + +static void +tx_ui(struct PStack *st) { struct sk_buff *skb; u_char header[MAX_HEADER_LEN]; @@ -458,18 +681,13 @@ l2_send_ui(struct PStack *st) } static void -l2_put_ui(struct FsmInst *fi, int event, void *arg) +l2_send_ui(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; skb_queue_tail(&st->l2.ui_queue, skb); - if (fi->state == ST_L2_1) { - FsmChangeState(fi, ST_L2_2); - st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); - } - if (fi->state > ST_L2_3) - l2_send_ui(st); + tx_ui(st); } static void @@ -479,11 +697,12 @@ l2_got_ui(struct FsmInst *fi, int event, void *arg) struct sk_buff *skb = arg; skb_pull(skb, l2headersize(&st->l2, 1)); - if (skb->len > st->l2.maxlen) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O'); - FreeSkb(skb); - } else - st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb); + st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb); +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * in states 1-3 for broadcast + */ + + } static void @@ -491,277 +710,244 @@ l2_establish(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - if (fi->state != ST_L2_4) - discard_queue(&st->l2.i_queue); - if (fi->state != ST_L2_5) - establishlink(fi); + establishlink(fi); test_and_set_bit(FLG_L3_INIT, &st->l2.flag); } static void -l2_dl_release(struct FsmInst *fi, int event, void *arg) +l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - if (fi->state == ST_L2_4) { - st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); - return; - } else if (fi->state == ST_L2_5) { - test_and_set_bit(FLG_PEND_REL, &st->l2.flag); - return; - } discard_queue(&st->l2.i_queue); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); + test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); +} + +static void +l2_l3_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); +} + +static void +l2_release(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); +} + +static void +l2_pend_rel(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + test_and_set_bit(FLG_PEND_REL, &st->l2.flag); +} + +static void +l2_disconnect(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + freewin(st); FsmChangeState(fi, ST_L2_6); st->l2.rc = 0; send_uframe(st, DISC | 0x10, CMD); FsmDelTimer(&st->l2.t203, 1); - FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 2); - test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + restart_t200(st, 2); +} + +static void +l2_start_multi(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(st, UA | get_PollFlagFree(st, skb), RSP); + + clear_exception(&st->l2); + st->l2.vs = 0; + st->l2.va = 0; + st->l2.vr = 0; + st->l2.sow = 0; + FsmChangeState(fi, ST_L2_7); + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); + + st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL); } static void -l2_got_SABMX(struct FsmInst *fi, int event, void *arg) +l2_send_UA(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int est = 1, state, rsp; - u_char PollFlag; + + send_uframe(st, UA | get_PollFlagFree(st, skb), RSP); +} + +static void +l2_send_DM(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(st, DM | get_PollFlagFree(st, skb), RSP); +} + +static void +l2_restart_multi(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int est = 0, state; state = fi->state; - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &st->l2.flag)) - rsp = !rsp; - if (rsp) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); - FreeSkb(skb); - if ((state == ST_L2_7) || (state == ST_L2_8)) - establishlink(fi); - return; - } - if (skb->len != (l2addrsize(&st->l2) + 1)) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - FreeSkb(skb); - if ((state == ST_L2_7) || (state == ST_L2_8)) - establishlink(fi); - return; - } - PollFlag = get_PollFlagFree(st, skb); - if (ST_L2_6 == state) { - send_uframe(st, DM | PollFlag, RSP); - return; - } else - send_uframe(st, UA | PollFlag, RSP); - if (ST_L2_5 == state) - return; - if (ST_L2_4 != state) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F'); - if (st->l2.vs != st->l2.va) { - discard_queue(&st->l2.i_queue); - est = 1; - } else - est = 0; + + send_uframe(st, UA | get_PollFlagFree(st, skb), RSP); + + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F'); + + if (st->l2.vs != st->l2.va) { + discard_queue(&st->l2.i_queue); + est = 1; } + clear_exception(&st->l2); st->l2.vs = 0; st->l2.va = 0; st->l2.vr = 0; st->l2.sow = 0; FsmChangeState(fi, ST_L2_7); - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 2); + stop_t200(st, 3); FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); if (est) st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL); - if (ST_L2_8 == state) + if ((ST_L2_7==state) || (ST_L2_8 == state)) if (skb_queue_len(&st->l2.i_queue) && cansend(st)) st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } static void -l2_got_disconn(struct FsmInst *fi, int event, void *arg) +l2_stop_multi(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - u_char PollFlag, cmd = UA; - int state, rel = 1, cst = 1, rsp; - state = fi->state; - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &st->l2.flag)) - rsp = !rsp; + FsmChangeState(fi, ST_L2_4); + FsmDelTimer(&st->l2.t203, 3); + stop_t200(st, 4); - if (rsp) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); - FreeSkb(skb); - if ((state == ST_L2_7) || (state == ST_L2_8)) - establishlink(fi); - return; - } - if (skb->len != (l2addrsize(&st->l2) + 1)) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - FreeSkb(skb); - if ((state == ST_L2_7) || (state == ST_L2_8)) - establishlink(fi); - return; - } - PollFlag = get_PollFlagFree(st, skb); - if ((state == ST_L2_4) || (state == ST_L2_5)) { - rel = 0; - cst = 0; - cmd = DM; - } else if (state == ST_L2_6) { - rel = 0; - cst = 0; - } - if (cst) { - FsmChangeState(fi, ST_L2_4); - FsmDelTimer(&st->l2.t203, 3); - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 2); - } - send_uframe(st, cmd | PollFlag, RSP); - if (rel) { - if (test_bit(FLG_LAPB, &st->l2.flag)) - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); - } -} + send_uframe(st, UA | get_PollFlagFree(st, skb), RSP); + discard_queue(&st->l2.i_queue); + freewin(st); + lapb_dl_release_l2l3(st, INDICATION); +} static void -l2_got_ua(struct FsmInst *fi, int event, void *arg) +l2_connected(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; int pr=-1; - u_char PollFlag; - int state,rsp; - state = fi->state; - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &st->l2.flag)) - rsp = !rsp; - - if (!rsp) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); - FreeSkb(skb); - if ((state == ST_L2_7) || (state == ST_L2_8)) - establishlink(fi); - return; - } - if (skb->len != (l2addrsize(&st->l2) + 1)) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - FreeSkb(skb); - if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) - establishlink(fi); + if (!get_PollFlag(st, skb)) { + l2_mdl_error_ua(fi, event, arg); return; } - PollFlag = get_PollFlag(st, skb); - if (!PollFlag) { - l2_mdl_error(fi, event, arg); + FreeSkb(skb); + + if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) + l2_disconnect(fi, event, arg); + + if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) { + pr = DL_ESTABLISH | CONFIRM; + } else if (st->l2.vs != st->l2.va) { + discard_queue(&st->l2.i_queue); + pr = DL_ESTABLISH | INDICATION; + } + + stop_t200(st, 5); + + st->l2.vr = 0; + st->l2.vs = 0; + st->l2.va = 0; + st->l2.sow = 0; + FsmChangeState(fi, ST_L2_7); + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4); + + if (pr != -1) + st->l2.l2l3(st, pr, NULL); + + if (skb_queue_len(&st->l2.i_queue) && cansend(st)) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); +} + +static void +l2_released(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlag(st, skb)) { + l2_mdl_error_ua(fi, event, arg); return; } FreeSkb(skb); - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 2); - if (fi->state == ST_L2_5) { - if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) { + stop_t200(st, 6); + lapb_dl_release_l2l3(st, CONFIRM); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlagFree(st, skb)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); + } +} + +static void +l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(st, skb)) { + stop_t200(st, 7); + if (!test_bit(FLG_L3_INIT, &st->l2.flag)) discard_queue(&st->l2.i_queue); - st->l2.rc = 0; - send_uframe(st, DISC | 0x10, CMD); - FsmChangeState(fi, ST_L2_6); - FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4); - test_and_set_bit(FLG_T200_RUN, &st->l2.flag); - } else { - if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) { - pr = DL_ESTABLISH | CONFIRM; - } else if (st->l2.vs != st->l2.va) { - discard_queue(&st->l2.i_queue); - pr = DL_ESTABLISH | INDICATION; - } - st->l2.vs = 0; - st->l2.va = 0; - st->l2.vr = 0; - st->l2.sow = 0; - FsmChangeState(fi, ST_L2_7); - if (pr > -1) - st->l2.l2l3(st, pr, NULL); - } - } else { /* ST_L2_6 */ if (test_bit(FLG_LAPB, &st->l2.flag)) st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); - st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + st5_dl_release_l2l3(st); FsmChangeState(fi, ST_L2_4); } } static void -l2_got_dm(struct FsmInst *fi, int event, void *arg) +l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - u_char PollFlag; - int state,rsp; - state = fi->state; - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &st->l2.flag)) - rsp = !rsp; - - if (!rsp) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); - FreeSkb(skb); - if ((state == ST_L2_7) || (state == ST_L2_8)) - establishlink(fi); - return; - } - if (skb->len != (l2addrsize(&st->l2) + 1)) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - FreeSkb(skb); - if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) - establishlink(fi); - return; - } - PollFlag = get_PollFlagFree(st, skb); - if (!PollFlag) { - if (fi->state == ST_L2_4) { - establishlink(fi); - test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); - FsmChangeState(fi, ST_L2_5); - } else if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E'); - establishlink(fi); - } - } else { - switch (fi->state) { - case ST_L2_8: - establishlink(fi); - case ST_L2_7: - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B'); - break; - case ST_L2_4: - break; - case ST_L2_5: - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 2); - discard_queue(&st->l2.i_queue); - if (test_bit(FLG_LAPB, &st->l2.flag)) - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); - FsmChangeState(fi, ST_L2_4); - break; - case ST_L2_6: - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 2); - if (test_bit(FLG_LAPB, &st->l2.flag)) - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); - st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); - FsmChangeState(fi, ST_L2_4); - break; - } + if (get_PollFlagFree(st, skb)) { + stop_t200(st, 8); + lapb_dl_release_l2l3(st, CONFIRM); + FsmChangeState(fi, ST_L2_4); } } @@ -784,6 +970,7 @@ enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf) printk(KERN_WARNING "isdl2 can't alloc sbbuff for enquiry_cr\n"); return; } + SET_SKB_FREE(skb); memcpy(skb_put(skb, i), tmp, i); enqueue_super(st, skb); } @@ -806,8 +993,7 @@ transmit_enquiry(struct PStack *st) else enquiry_cr(st, RR, CMD, 1); test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); - FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 12); - test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + start_t200(st, 9); } @@ -818,42 +1004,42 @@ nrerrorrecovery(struct FsmInst *fi) st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J'); establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); } static void -invoke_retransmission(struct PStack *st, int nr) +invoke_retransmission(struct PStack *st, unsigned int nr) { struct Layer2 *l2 = &st->l2; - int p1; - long flags; + unsigned int p1; if (l2->vs != nr) { - save_flags(flags); - cli(); while (l2->vs != nr) { - l2->vs = l2->vs - 1; - if (l2->vs < 0) - l2->vs += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); - p1 = l2->vs - l2->va; - if (p1 < 0) - p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + (l2->vs)--; + if(test_bit(FLG_MOD128, &l2->flag)) { + l2->vs %= 128; + p1 = (l2->vs - l2->va) % 128; + } else { + l2->vs %= 8; + p1 = (l2->vs - l2->va) % 8; + } p1 = (p1 + l2->sow) % l2->window; if (test_bit(FLG_LAPB, &l2->flag)) st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0); skb_queue_head(&l2->i_queue, l2->windowar[p1]); l2->windowar[p1] = NULL; } - restore_flags(flags); st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } } static void -l2_got_st7_super(struct FsmInst *fi, int event, void *arg) +l2_st7_got_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int PollFlag, nr, rsp, typ = RR; + int PollFlag, rsp, typ = RR; + unsigned int nr; struct Layer2 *l2 = &st->l2; rsp = *skb->data & 0x2; @@ -861,72 +1047,77 @@ l2_got_st7_super(struct FsmInst *fi, int event, void *arg) rsp = !rsp; skb_pull(skb, l2addrsize(l2)); - if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { - test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (IsRNR(skb->data, st)) { + set_peer_busy(l2); typ = RNR; } else - test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); - if (IsREJ(skb->data, test_bit(FLG_MOD128, &l2->flag))) + clear_peer_busy(l2); + if (IsREJ(skb->data, st)) typ = REJ; + if (test_bit(FLG_MOD128, &l2->flag)) { - if (skb->len == 2) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - nr = skb->data[1] >> 1; - } else { - if (skb->len >2) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - establishlink(fi); - } - FreeSkb(skb); - return; - } + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; } else { - if (skb->len == 1) { - PollFlag = (skb->data[0] & 0x10); - nr = (skb->data[0] >> 5) & 0x7; - } else { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - FreeSkb(skb); - establishlink(fi); - return; - } + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; } FreeSkb(skb); - if ((!rsp) && PollFlag) - enquiry_response(st); - if (rsp && PollFlag) - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A'); + if (PollFlag) { + if (rsp) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A'); + else + enquiry_response(st); + } if (legalnr(st, nr)) { if (typ == REJ) { setva(st, nr); invoke_retransmission(st, nr); - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 9); + stop_t200(st, 10); if (FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 6)) l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ"); } else if ((nr == l2->vs) && (typ == RR)) { setva(st, nr); - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 9); + stop_t200(st, 11); FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 7); } else if ((l2->va != nr) || (typ == RNR)) { setva(st, nr); - FsmDelTimer(&st->l2.t203, 9); - FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 6); - test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + if(typ != RR) FsmDelTimer(&st->l2.t203, 9); + restart_t200(st, 12); } if (skb_queue_len(&st->l2.i_queue) && (typ == RR)) st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } else nrerrorrecovery(fi); +} - if ((fi->userint & LC_FLUSH_WAIT) && rsp && !(skb_queue_len(&st->l2.i_queue))) { - fi->userint &= ~LC_FLUSH_WAIT; - st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL); - } +static void +l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); + if (!test_bit(FLG_L3_INIT, &st->l2.flag)) + skb_queue_tail(&st->l2.i_queue, skb); + else + FreeSkb(skb); +} + +static void +l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); + skb_queue_tail(&st->l2.i_queue, skb); + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } static void @@ -937,10 +1128,7 @@ l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) if (test_bit(FLG_LAPB, &st->l2.flag)) st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); - if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag))) - skb_queue_tail(&st->l2.i_queue, skb); - if (fi->state == ST_L2_7) - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + skb_queue_tail(&st->l2.i_queue, skb); } static void @@ -949,54 +1137,30 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg) struct PStack *st = fi->userdata; struct sk_buff *skb = arg; struct Layer2 *l2 = &(st->l2); - int PollFlag, ns, nr, i, rsp; - - rsp = *skb->data & 0x2; - if (test_bit(FLG_ORIG, &l2->flag)) - rsp = !rsp; + int PollFlag, ns, i; + unsigned int nr; - if (rsp) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); - FreeSkb(skb); - establishlink(fi); - return; - } i = l2addrsize(l2); if (test_bit(FLG_MOD128, &l2->flag)) { - if (skb->len <= (i + 1)) { - FreeSkb(skb); - return; - } else if ((skb->len - i - 1) > l2->maxlen) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O'); - FreeSkb(skb); - establishlink(fi); - return; - } PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); ns = skb->data[i] >> 1; nr = (skb->data[i + 1] >> 1) & 0x7f; } else { - if (skb->len <= i) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - FreeSkb(skb); - establishlink(fi); - return; - } else if ((skb->len - i) > l2->maxlen) { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O'); - FreeSkb(skb); - establishlink(fi); - return; - } PollFlag = (skb->data[i] & 0x10); ns = (skb->data[i] >> 1) & 0x7; nr = (skb->data[i] >> 5) & 0x7; } if (test_bit(FLG_OWN_BUSY, &l2->flag)) { FreeSkb(skb); - enquiry_response(st); + if(PollFlag) enquiry_response(st); } else if (l2->vr == ns) { - l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + (l2->vr)++; + if(test_bit(FLG_MOD128, &l2->flag)) + l2->vr %= 128; + else + l2->vr %= 8; test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (PollFlag) enquiry_response(st); else @@ -1016,19 +1180,15 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg) } if (legalnr(st, nr)) { - setva(st, nr); if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) { if (nr == st->l2.vs) { - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 10); + stop_t200(st, 13); FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 7); - } else if (nr != st->l2.va) { - FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, - NULL, 8); - test_and_set_bit(FLG_T200_RUN, &st->l2.flag); - } + } else if (nr != st->l2.va) + restart_t200(st, 14); } + setva(st, nr); } else { nrerrorrecovery(fi); return; @@ -1053,7 +1213,7 @@ l2_got_tei(struct FsmInst *fi, int event, void *arg) } else FsmChangeState(fi, ST_L2_4); if (skb_queue_len(&st->l2.ui_queue)) - l2_send_ui(st); + tx_ui(st); } static void @@ -1071,7 +1231,7 @@ l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G'); if (test_bit(FLG_LAPB, &st->l2.flag)) st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + st5_dl_release_l2l3(st); } else { st->l2.rc++; FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); @@ -1090,10 +1250,9 @@ l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); } else if (st->l2.rc == st->l2.N200) { FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H'); - if (test_bit(FLG_LAPB, &st->l2.flag)) - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + lapb_dl_release_l2l3(st, CONFIRM); } else { st->l2.rc++; FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, @@ -1103,7 +1262,7 @@ l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) } static void -l2_st78_tout_200(struct FsmInst *fi, int event, void *arg) +l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; @@ -1113,12 +1272,28 @@ l2_st78_tout_200(struct FsmInst *fi, int event, void *arg) return; } test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); - if (fi->state == ST_L2_7) { - st->l2.rc = 0; - FsmChangeState(fi, ST_L2_8); + st->l2.rc = 0; + FsmChangeState(fi, ST_L2_8); + + transmit_enquiry(st); + st->l2.rc++; +} + +static void +l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + return; } + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); if (st->l2.rc == st->l2.N200) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'I'); establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); } else { transmit_enquiry(st); st->l2.rc++; @@ -1147,7 +1322,9 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) struct sk_buff *skb, *oskb; struct Layer2 *l2 = &st->l2; u_char header[MAX_HEADER_LEN]; - int p1, i; + int i; + int unsigned p1; + long flags; if (!cansend(st)) return; @@ -1156,14 +1333,17 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) if (!skb) return; - p1 = l2->vs - l2->va; - if (p1 < 0) - p1 += test_bit(FLG_MOD128, &l2->flag) ? 128 : 8; + save_flags(flags); + cli(); + if(test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; p1 = (p1 + l2->sow) % l2->window; if (l2->windowar[p1]) { printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", p1); - dev_kfree_skb(l2->windowar[p1]); + idev_kfree_skb(l2->windowar[p1], FREE_WRITE); } l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC); @@ -1177,6 +1357,8 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) header[i++] = (l2->vr << 5) | (l2->vs << 1); l2->vs = (l2->vs + 1) % 8; } + restore_flags(flags); + p1 = skb->data - skb->head; if (p1 >= i) memcpy(skb_push(skb, i), header, i); @@ -1185,6 +1367,7 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) "isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); oskb = skb; skb = alloc_skb(oskb->len + i, GFP_ATOMIC); + SET_SKB_FREE(skb); memcpy(skb_put(skb, i), header, i); memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len); FreeSkb(oskb); @@ -1200,74 +1383,58 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) } static void -l2_got_st8_super(struct FsmInst *fi, int event, void *arg) +l2_st8_got_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int PollFlag, nr, rsp, rnr = 0; + int PollFlag, rsp, rnr = 0; + unsigned int nr; struct Layer2 *l2 = &st->l2; rsp = *skb->data & 0x2; if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; + skb_pull(skb, l2addrsize(l2)); - if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { - test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (IsRNR(skb->data, st)) { + set_peer_busy(l2); rnr = 1; } else - test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + clear_peer_busy(l2); + if (test_bit(FLG_MOD128, &l2->flag)) { - if (skb->len == 2) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - nr = skb->data[1] >> 1; - } else { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - FreeSkb(skb); - establishlink(fi); - return; - } + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; } else { - if (skb->len == 1) { - PollFlag = (skb->data[0] & 0x10); - nr = (skb->data[0] >> 5) & 0x7; - } else { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - FreeSkb(skb); - establishlink(fi); - return; - } + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; } FreeSkb(skb); if (rsp && PollFlag) { if (legalnr(st, nr)) { - setva(st, nr); - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 7); - FsmDelTimer(&l2->t203, 8); if (rnr) { - FsmRestartTimer(&l2->t200, l2->T200, - EV_L2_T200, NULL, 14); - test_and_set_bit(FLG_T200_RUN, &st->l2.flag); - } else + restart_t200(st, 15); + } else { + stop_t200(st, 16); FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 5); + setva(st, nr); + } invoke_retransmission(st, nr); FsmChangeState(fi, ST_L2_7); if (skb_queue_len(&l2->i_queue) && cansend(st)) st->l2.l2l1(st, PH_PULL | REQUEST, NULL); - else if (fi->userint & LC_FLUSH_WAIT) { - fi->userint &= ~LC_FLUSH_WAIT; - st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL); - } - } + } else + nrerrorrecovery(fi); } else { if (!rsp && PollFlag) enquiry_response(st); if (legalnr(st, nr)) { setva(st, nr); - } + } else + nrerrorrecovery(fi); } } @@ -1278,22 +1445,9 @@ l2_got_FRMR(struct FsmInst *fi, int event, void *arg) struct sk_buff *skb = arg; skb_pull(skb, l2addrsize(&st->l2) + 1); - if (test_bit(FLG_MOD128, &st->l2.flag)) { - if (skb->len < 5) - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - else - l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x", - skb->data[0], skb->data[1], skb->data[2], - skb->data[3], skb->data[4]); - } else { - if (skb->len < 3) - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); - else - l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x", - skb->data[0], skb->data[1], skb->data[2]); - } + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ - (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) { + (IsUA(skb->data) && (fi->state == ST_L2_7))) { st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K'); establishlink(fi); test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); @@ -1302,115 +1456,209 @@ l2_got_FRMR(struct FsmInst *fi, int event, void *arg) } static void +l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.ui_queue); + st->l2.tei = -1; + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.ui_queue); + st->l2.tei = -1; + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + freewin(st); + st->l2.tei = -1; + stop_t200(st, 17); + st5_dl_release_l2l3(st); + FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.ui_queue); + st->l2.tei = -1; + stop_t200(st, 18); + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + FsmChangeState(fi, ST_L2_1); +} + +static void l2_tei_remove(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; discard_queue(&st->l2.i_queue); discard_queue(&st->l2.ui_queue); + freewin(st); st->l2.tei = -1; - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 18); + stop_t200(st, 17); FsmDelTimer(&st->l2.t203, 19); - if (fi->state != ST_L2_4) - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); FsmChangeState(fi, ST_L2_1); } static void -l2_persistant_da(struct FsmInst *fi, int event, void *arg) +l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - int rel = DL_RELEASE | INDICATION; - discard_queue(&st->l2.i_queue); discard_queue(&st->l2.ui_queue); - if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) - FsmDelTimer(&st->l2.t200, 18); + if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); +} + +static void +l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + freewin(st); + stop_t200(st, 19); + st5_dl_release_l2l3(st); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.ui_queue); + stop_t200(st, 20); + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + freewin(st); + stop_t200(st, 19); FsmDelTimer(&st->l2.t203, 19); - clear_exception(&st->l2); - switch (fi->state) { - case ST_L2_1: - if (!test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) - break; - case ST_L2_3: - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); - case ST_L2_2: - FsmChangeState(fi, ST_L2_1); - break; - case ST_L2_6: - rel = DL_RELEASE | CONFIRM; - case ST_L2_5: - if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) - rel = DL_RELEASE | CONFIRM; - case ST_L2_7: - case ST_L2_8: - st->l2.l2l3(st, rel, NULL); - FsmChangeState(fi, ST_L2_4); - break; - case ST_L2_4: - if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); - break; + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + FsmChangeState(fi, ST_L2_4); +} + +static void +l2_set_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if(!test_and_set_bit(FLG_OWN_BUSY, &st->l2.flag)) { + enquiry_cr(st, RNR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); } - test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); - test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag); +} + +static void +l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if(!test_and_clear_bit(FLG_OWN_BUSY, &st->l2.flag)) { + enquiry_cr(st, RR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + } +} + +static void +l2_frame_error(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->ma.layer(st, MDL_ERROR | INDICATION, arg); +} + +static void +l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + st->ma.layer(st, MDL_ERROR | INDICATION, arg); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); } static struct FsmNode L2FnList[] HISAX_INITDATA = { - {ST_L2_1, EV_L2_DL_ESTABLISH, l2_dl_establish}, - {ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish}, - {ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish}, - {ST_L2_5, EV_L2_DL_ESTABLISH, l2_establish}, - {ST_L2_7, EV_L2_DL_ESTABLISH, l2_establish}, - {ST_L2_8, EV_L2_DL_ESTABLISH, l2_establish}, - {ST_L2_4, EV_L2_DL_RELEASE, l2_dl_release}, - {ST_L2_5, EV_L2_DL_RELEASE, l2_dl_release}, - {ST_L2_7, EV_L2_DL_RELEASE, l2_dl_release}, - {ST_L2_8, EV_L2_DL_RELEASE, l2_dl_release}, - {ST_L2_5, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_7, EV_L2_DL_DATA, l2_feed_iqueue}, + {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, + {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, + {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, + {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, + {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, + {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, + {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, + {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_1, EV_L2_DL_UNIT_DATA, l2_put_ui}, - {ST_L2_2, EV_L2_DL_UNIT_DATA, l2_put_ui}, - {ST_L2_3, EV_L2_DL_UNIT_DATA, l2_put_ui}, - {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_put_ui}, - {ST_L2_5, EV_L2_DL_UNIT_DATA, l2_put_ui}, - {ST_L2_6, EV_L2_DL_UNIT_DATA, l2_put_ui}, - {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_put_ui}, - {ST_L2_8, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_1, EV_L2_DL_UNIT_DATA, l2_queue_ui_assign}, + {ST_L2_2, EV_L2_DL_UNIT_DATA, l2_queue_ui}, + {ST_L2_3, EV_L2_DL_UNIT_DATA, l2_queue_ui}, + {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_6, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_8, EV_L2_DL_UNIT_DATA, l2_send_ui}, {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_2, EV_L2_MDL_ERROR, l2_tei_remove}, - {ST_L2_3, EV_L2_MDL_ERROR, l2_tei_remove}, - {ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_4, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_5, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_6, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_7, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_8, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_4, EV_L2_DISC, l2_got_disconn}, - {ST_L2_5, EV_L2_DISC, l2_got_disconn}, - {ST_L2_6, EV_L2_DISC, l2_got_disconn}, - {ST_L2_7, EV_L2_DISC, l2_got_disconn}, - {ST_L2_8, EV_L2_DISC, l2_got_disconn}, - {ST_L2_4, EV_L2_UA, l2_mdl_error}, - {ST_L2_5, EV_L2_UA, l2_got_ua}, - {ST_L2_6, EV_L2_UA, l2_got_ua}, - {ST_L2_7, EV_L2_UA, l2_mdl_error}, - {ST_L2_8, EV_L2_UA, l2_mdl_error}, - {ST_L2_4, EV_L2_DM, l2_got_dm}, - {ST_L2_5, EV_L2_DM, l2_got_dm}, - {ST_L2_6, EV_L2_DM, l2_got_dm}, - {ST_L2_7, EV_L2_DM, l2_got_dm}, - {ST_L2_8, EV_L2_DM, l2_got_dm}, + {ST_L2_4, EV_L2_SABME, l2_start_multi}, + {ST_L2_5, EV_L2_SABME, l2_send_UA}, + {ST_L2_6, EV_L2_SABME, l2_send_DM}, + {ST_L2_7, EV_L2_SABME, l2_restart_multi}, + {ST_L2_8, EV_L2_SABME, l2_restart_multi}, + {ST_L2_4, EV_L2_DISC, l2_send_DM}, + {ST_L2_5, EV_L2_DISC, l2_send_DM}, + {ST_L2_6, EV_L2_DISC, l2_send_UA}, + {ST_L2_7, EV_L2_DISC, l2_stop_multi}, + {ST_L2_8, EV_L2_DISC, l2_stop_multi}, + {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_5, EV_L2_UA, l2_connected}, + {ST_L2_6, EV_L2_UA, l2_released}, + {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_4, EV_L2_DM, l2_reestablish}, + {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, + {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, + {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, + {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, {ST_L2_1, EV_L2_UI, l2_got_ui}, {ST_L2_2, EV_L2_UI, l2_got_ui}, {ST_L2_3, EV_L2_UI, l2_got_ui}, @@ -1421,22 +1669,31 @@ static struct FsmNode L2FnList[] HISAX_INITDATA = {ST_L2_8, EV_L2_UI, l2_got_ui}, {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_7, EV_L2_SUPER, l2_got_st7_super}, - {ST_L2_8, EV_L2_SUPER, l2_got_st8_super}, + {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, + {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, {ST_L2_7, EV_L2_I, l2_got_iframe}, {ST_L2_8, EV_L2_I, l2_got_iframe}, {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, - {ST_L2_7, EV_L2_T200, l2_st78_tout_200}, - {ST_L2_8, EV_L2_T200, l2_st78_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, - {ST_L2_1, EV_L1_DEACTIVATE, l2_persistant_da}, - {ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da}, - {ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da}, - {ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da}, - {ST_L2_5, EV_L1_DEACTIVATE, l2_persistant_da}, - {ST_L2_6, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, }; @@ -1449,6 +1706,7 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) struct sk_buff *skb = arg; u_char *datap; int ret = 1, len; + int c = 0; switch (pr) { case (PH_DATA | INDICATION): @@ -1457,36 +1715,46 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) if (skb->len > len) datap += len; else { - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'N'); FreeSkb(skb); return; } - if (!(*datap & 1)) /* I-Frame */ - ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb); - else if (IsSFrame(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb); - else if (IsUI(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb); - else if (IsSABMX(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb); - else if (IsUA(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb); - else if (IsDISC(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb); - else if (IsDM(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb); - else if (IsFRMR(datap, test_bit(FLG_MOD128, &st->l2.flag))) - ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb); - else { - ret = 1; - if ((st->l2.l2m.state == ST_L2_7) || - (st->l2.l2m.state == ST_L2_8)) - establishlink(&st->l2.l2m); - st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + if (!(*datap & 1)) { /* I-Frame */ + if(!(c = iframe_error(st, skb))) + ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb); + } else if (IsSFrame(datap, st)) { /* S-Frame */ + if(!(c = super_error(st, skb))) + ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb); + } else if (IsUI(datap)) { + if(!(c = UI_error(st, skb))) + ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb); + } else if (IsSABME(datap, st)) { + if(!(c = unnum_error(st, skb, CMD))) + ret = FsmEvent(&st->l2.l2m, EV_L2_SABME, skb); + } else if (IsUA(datap)) { + if(!(c = unnum_error(st, skb, RSP))) + ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb); + } else if (IsDISC(datap)) { + if(!(c = unnum_error(st, skb, CMD))) + ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb); + } else if (IsDM(datap)) { + if(!(c = unnum_error(st, skb, RSP))) + ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb); + } else if (IsFRMR(datap)) { + if(!(c = FRMR_error(st,skb))) + ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb); + } else { + FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'L'); + FreeSkb(skb); + ret = 0; } - if (ret) { + if(c) { FreeSkb(skb); + FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *)(long)c); + ret = 0; } + if (ret) + FreeSkb(skb); break; case (PH_PULL | CONFIRM): FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg); @@ -1501,7 +1769,7 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) case (PH_ACTIVATE | INDICATION): test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag); if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) - FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg); + FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg); break; case (PH_DEACTIVATE | INDICATION): case (PH_DEACTIVATE | CONFIRM): @@ -1520,19 +1788,19 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) switch (pr) { case (DL_DATA | REQUEST): if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) { - dev_kfree_skb((struct sk_buff *) arg); + idev_kfree_skb((struct sk_buff *) arg, FREE_READ); } break; case (DL_UNIT_DATA | REQUEST): if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) { - dev_kfree_skb((struct sk_buff *) arg); + idev_kfree_skb((struct sk_buff *) arg, FREE_READ); } break; case (DL_ESTABLISH | REQUEST): if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) { if (test_bit(FLG_LAPD, &st->l2.flag) || test_bit(FLG_ORIG, &st->l2.flag)) { - FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg); + FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg); } } else { if (test_bit(FLG_LAPD, &st->l2.flag) || @@ -1546,10 +1814,7 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) if (test_bit(FLG_LAPB, &st->l2.flag)) { st->l2.l2l1(st, PH_DEACTIVATE, NULL); } - FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg); - break; - case (DL_FLUSH | REQUEST): - (&st->l2.l2m)->userint |= LC_FLUSH_WAIT; + FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE_REQ, arg); break; case (MDL_ASSIGN | REQUEST): FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg); @@ -1566,7 +1831,7 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) void releasestack_isdnl2(struct PStack *st) { - FsmDelTimer(&st->l2.t200, 15); + FsmDelTimer(&st->l2.t200, 21); FsmDelTimer(&st->l2.t203, 16); discard_queue(&st->l2.i_queue); discard_queue(&st->l2.ui_queue); diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c index 4511251e7..458afe920 100644 --- a/drivers/isdn/hisax/isdnl3.c +++ b/drivers/isdn/hisax/isdnl3.c @@ -1,4 +1,4 @@ -/* $Id: isdnl3.c,v 2.8 1998/11/15 23:55:04 keil Exp $ +/* $Id: isdnl3.c,v 2.10 1999/07/21 14:46:19 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,12 @@ * Fritz Elfert * * $Log: isdnl3.c,v $ + * Revision 2.10 1999/07/21 14:46:19 keil + * changes from EICON certification + * + * Revision 2.9 1999/07/01 08:11:53 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.8 1998/11/15 23:55:04 keil * changes from 2.0 * @@ -62,7 +68,7 @@ #include "isdnl3.h" #include <linux/config.h> -const char *l3_revision = "$Revision: 2.8 $"; +const char *l3_revision = "$Revision: 2.10 $"; static struct Fsm l3fsm = @@ -71,6 +77,7 @@ struct Fsm l3fsm = enum { ST_L3_LC_REL, ST_L3_LC_ESTAB_WAIT, + ST_L3_LC_REL_DELAY, ST_L3_LC_REL_WAIT, ST_L3_LC_ESTAB, }; @@ -81,6 +88,7 @@ static char *strL3State[] = { "ST_L3_LC_REL", "ST_L3_LC_ESTAB_WAIT", + "ST_L3_LC_REL_DELAY", "ST_L3_LC_REL_WAIT", "ST_L3_LC_ESTAB", }; @@ -92,9 +100,10 @@ enum { EV_RELEASE_REQ, EV_RELEASE_CNF, EV_RELEASE_IND, + EV_TIMEOUT, }; -#define L3_EVENT_COUNT (EV_RELEASE_IND+1) +#define L3_EVENT_COUNT (EV_TIMEOUT+1) static char *strL3Event[] = { @@ -104,6 +113,7 @@ static char *strL3Event[] = "EV_RELEASE_REQ", "EV_RELEASE_CNF", "EV_RELEASE_IND", + "EV_TIMEOUT", }; static void @@ -142,7 +152,14 @@ findie(u_char * p, int size, u_char ie, int wanted_set) else { if (codeset == wanted_set) { if (*p == ie) - return (p); + { /* improved length check (Werner Cornelius) */ + if ((pend - p) < 2) + return(NULL); + if (*(p+1) > (pend - (p+2))) + return(NULL); + return (p); + } + if (*p > ie) return (NULL); } @@ -158,15 +175,15 @@ findie(u_char * p, int size, u_char ie, int wanted_set) int getcallref(u_char * p) { - int l, m = 1, cr = 0; + int l, cr = 0; + p++; /* prot discr */ + if (*p & 0xfe) /* wrong callref BRI only 1 octet*/ + return(-2); l = 0xf & *p++; /* callref length */ if (!l) /* dummy CallRef */ return(-1); - while (l--) { - cr += m * (*p++); - m *= 8; - } + cr = *p++; return (cr); } @@ -186,8 +203,9 @@ void newl3state(struct l3_process *pc, int state) { if (pc->debug & L3_DEB_STATE) - l3_debug(pc->st, "newstate cr %d %d --> %d", pc->callref, - pc->state, state); + l3_debug(pc->st, "newstate cr %d %d --> %d", + pc->callref & 0x7F, + pc->state, state); pc->state = state; } @@ -242,6 +260,7 @@ l3_alloc_skb(int len) printk(KERN_WARNING "HiSax: No skb for D-channel\n"); return (NULL); } + SET_SKB_FREE(skb); skb_reserve(skb, MAX_HEADER_LEN); return (skb); } @@ -253,10 +272,17 @@ no_l3_proto(struct PStack *st, int pr, void *arg) HiSax_putstatus(st->l1.hardware, "L3", "no D protocol"); if (skb) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } } +static int +no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic) +{ + printk(KERN_WARNING "HiSax: no specific protocol handler for proto %lu\n",ic->arg & 0xFF); + return(-1); +} + #ifdef CONFIG_HISAX_EURO extern void setstack_dss1(struct PStack *st); #endif @@ -300,7 +326,7 @@ struct l3_process np->next = p; } p->next = NULL; - p->debug = L3_DEB_WARN; + p->debug = st->l3.debug; p->callref = cr; p->state = 0; p->chan = NULL; @@ -323,8 +349,19 @@ release_l3_process(struct l3_process *p) StopAllL3Timer(p); if (pp) pp->next = np->next; - else - p->st->l3.proc = np->next; + else if (!(p->st->l3.proc = np->next) && + !test_bit(FLG_PTP, &p->st->l2.flag)) { + if (p->debug) + l3_debug(p->st, "release_l3_process: last process"); + if (!skb_queue_len(&p->st->l3.squeue)) { + if (p->debug) + l3_debug(p->st, "release_l3_process: release link"); + FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL); + } else { + if (p->debug) + l3_debug(p->st, "release_l3_process: not release link"); + } + } kfree(p); return; } @@ -335,6 +372,17 @@ release_l3_process(struct l3_process *p) l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref); }; +static void +l3ml3p(struct PStack *st, int pr) +{ + struct l3_process *p = st->l3.proc; + + while (p) { + st->l3.l3ml3(st, pr, p); + p = p->next; + } +} + void setstack_l3dc(struct PStack *st, struct Channel *chanp) { @@ -349,7 +397,9 @@ setstack_l3dc(struct PStack *st, struct Channel *chanp) st->l3.l3m.userdata = st; st->l3.l3m.userint = 0; st->l3.l3m.printdebug = l3m_debug; + FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer); strcpy(st->l3.debug_id, "L3DC "); + st->lli.l4l3_proto = no_l3_proto_spec; #ifdef CONFIG_HISAX_EURO if (st->protocol == ISDN_PTYPE_EURO) { @@ -369,10 +419,12 @@ setstack_l3dc(struct PStack *st, struct Channel *chanp) if (st->protocol == ISDN_PTYPE_LEASED) { st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; + st->l3.l3ml3 = no_l3_proto; printk(KERN_INFO "HiSax: Leased line mode\n"); } else { st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; + st->l3.l3ml3 = no_l3_proto; sprintf(tmp, "protocol %s not supported", (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" : (st->protocol == ISDN_PTYPE_EURO) ? "euro" : @@ -398,6 +450,7 @@ releasestack_isdnl3(struct PStack *st) kfree(st->l3.global); st->l3.global = NULL; } + FsmDelTimer(&st->l3.l3m_timer, 54); discard_queue(&st->l3.squeue); } @@ -418,6 +471,8 @@ setstack_l3bc(struct PStack *st, struct Channel *chanp) st->lli.l4l3 = isdnl3_trans; } +#define DREL_TIMER_VALUE 40000 + static void lc_activate(struct FsmInst *fi, int event, void *arg) { @@ -432,12 +487,49 @@ lc_connect(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; + int dequeued = 0; FsmChangeState(fi, ST_L3_LC_ESTAB); while ((skb = skb_dequeue(&st->l3.squeue))) { st->l3.l3l2(st, DL_DATA | REQUEST, skb); + dequeued++; } - st->l3.l3l4(st, DL_ESTABLISH | INDICATION, NULL); + if ((!st->l3.proc) && dequeued) { + if (st->l3.debug) + l3_debug(st, "lc_connect: release link"); + FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); + } else + l3ml3p(st, DL_ESTABLISH | INDICATION); +} + +static void +lc_connected(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int dequeued = 0; + + FsmDelTimer(&st->l3.l3m_timer, 51); + FsmChangeState(fi, ST_L3_LC_ESTAB); + while ((skb = skb_dequeue(&st->l3.squeue))) { + st->l3.l3l2(st, DL_DATA | REQUEST, skb); + dequeued++; + } + if ((!st->l3.proc) && dequeued) { + if (st->l3.debug) + l3_debug(st, "lc_connected: release link"); + FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); + } else + l3ml3p(st, DL_ESTABLISH | CONFIRM); +} + +static void +lc_start_delay(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L3_LC_REL_DELAY); + FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50); } static void @@ -445,11 +537,15 @@ lc_release_req(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - if (fi->state == ST_L3_LC_ESTAB_WAIT) - FsmChangeState(fi, ST_L3_LC_REL); - else + if (test_bit(FLG_L2BLOCK, &st->l2.flag)) { + if (st->l3.debug) + l3_debug(st, "lc_release_req: l2 blocked"); + /* restart release timer */ + FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51); + } else { FsmChangeState(fi, ST_L3_LC_REL_WAIT); - st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); + st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); + } } static void @@ -457,23 +553,38 @@ lc_release_ind(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; + FsmDelTimer(&st->l3.l3m_timer, 52); FsmChangeState(fi, ST_L3_LC_REL); discard_queue(&st->l3.squeue); - st->l3.l3l4(st, DL_RELEASE | INDICATION, NULL); + l3ml3p(st, DL_RELEASE | INDICATION); } +static void +lc_release_cnf(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L3_LC_REL); + discard_queue(&st->l3.squeue); + l3ml3p(st, DL_RELEASE | CONFIRM); +} + + /* *INDENT-OFF* */ static struct FsmNode L3FnList[] HISAX_INITDATA = { {ST_L3_LC_REL, EV_ESTABLISH_REQ, lc_activate}, {ST_L3_LC_REL, EV_ESTABLISH_IND, lc_connect}, {ST_L3_LC_REL, EV_ESTABLISH_CNF, lc_connect}, - {ST_L3_LC_ESTAB_WAIT, EV_ESTABLISH_CNF, lc_connect}, - {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_release_req}, + {ST_L3_LC_ESTAB_WAIT, EV_ESTABLISH_CNF, lc_connected}, + {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_start_delay}, {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_IND, lc_release_ind}, {ST_L3_LC_ESTAB, EV_RELEASE_IND, lc_release_ind}, - {ST_L3_LC_ESTAB, EV_RELEASE_REQ, lc_release_req}, - {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_ind}, + {ST_L3_LC_ESTAB, EV_RELEASE_REQ, lc_start_delay}, + {ST_L3_LC_REL_DELAY, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_REL_DELAY, EV_ESTABLISH_REQ, lc_connected}, + {ST_L3_LC_REL_DELAY, EV_TIMEOUT, lc_release_req}, + {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_cnf}, {ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate}, }; /* *INDENT-ON* */ diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h index a0e518f8c..c7050f5f1 100644 --- a/drivers/isdn/hisax/isdnl3.h +++ b/drivers/isdn/hisax/isdnl3.h @@ -1,6 +1,12 @@ -/* $Id: isdnl3.h,v 2.3 1998/11/15 23:55:06 keil Exp $ +/* $Id: isdnl3.h,v 2.5 1999/07/25 16:18:32 keil Exp $ * $Log: isdnl3.h,v $ + * Revision 2.5 1999/07/25 16:18:32 keil + * Fix Suspend/Resume + * + * Revision 2.4 1999/07/01 08:11:54 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.3 1998/11/15 23:55:06 keil * changes from 2.0 * @@ -31,14 +37,16 @@ */ #define SBIT(state) (1<<state) -#define ALL_STATES 0x00ffffff +#define ALL_STATES 0x03ffffff -#define PROTO_DIS_EURO 0x08 +#define PROTO_DIS_EURO 0x08 #define L3_DEB_WARN 0x01 -#define L3_DEB_PROTERR 0x02 -#define L3_DEB_STATE 0x04 -#define L3_DEB_CHARGE 0x08 +#define L3_DEB_PROTERR 0x02 +#define L3_DEB_STATE 0x04 +#define L3_DEB_CHARGE 0x08 +#define L3_DEB_CHECK 0x10 +#define L3_DEB_SI 0x20 struct stateentry { int state; diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c new file mode 100644 index 000000000..dd713921a --- /dev/null +++ b/drivers/isdn/hisax/isurf.c @@ -0,0 +1,270 @@ +/* $Id: isurf.c,v 1.5 1999/08/25 17:00:02 keil Exp $ + + * isurf.c low level stuff for Siemens I-Surf/I-Talk cards + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * $Log: isurf.c,v $ + * Revision 1.5 1999/08/25 17:00:02 keil + * Make ISAR V32bis modem running + * Make LL->HL interface open for additional commands + * + * Revision 1.4 1999/08/22 20:27:09 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.3 1999/07/12 21:05:18 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:07:56 keil + * Initial version + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "isar.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +static const char *ISurf_revision = "$Revision: 1.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISURF_ISAR_RESET 1 +#define ISURF_ISAC_RESET 2 +#define ISURF_ISAR_EA 4 +#define ISURF_ARCOFI_RESET 8 +#define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET) + +#define ISURF_ISAR_OFFSET 0 +#define ISURF_ISAC_OFFSET 0x100 + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readb(cs->hw.isurf.isac + offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writeb(value, cs->hw.isurf.isac + offset); mb(); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + register int i; + for (i = 0; i < size; i++) + data[i] = readb(cs->hw.isurf.isac); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + register int i; + for (i = 0; i < size; i++){ + writeb(data[i], cs->hw.isurf.isac);mb(); + } +} + +/* ISAR access routines + * mode = 0 access with IRQ on + * mode = 1 access with IRQ off + * mode = 2 access with IRQ off and using last offset + */ + +static u_char +ReadISAR(struct IsdnCardState *cs, int mode, u_char offset) +{ + return(readb(cs->hw.isurf.isar + offset)); +} + +static void +WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value) +{ + writeb(value, cs->hw.isurf.isar + offset);mb(); +} + +static void +isurf_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + int cnt = 5; + + if (!cs) { + printk(KERN_WARNING "ISurf: Spurious interrupt!\n"); + return; + } + + val = readb(cs->hw.isurf.isar + ISAR_IRQBIT); + Start_ISAR: + if (val & ISAR_IRQSTA) + isar_int_main(cs); + val = readb(cs->hw.isurf.isac + ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = readb(cs->hw.isurf.isar + ISAR_IRQBIT); + if ((val & ISAR_IRQSTA) && --cnt) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "ISAR IntStat after IntRoutine"); + goto Start_ISAR; + } + val = readb(cs->hw.isurf.isac + ISAC_ISTA); + if (val && --cnt) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (!cnt) + printk(KERN_WARNING "ISurf IRQ LOOP\n"); + + writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb(); + writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK);mb(); + writeb(0, cs->hw.isurf.isac + ISAC_MASK);mb(); + writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb(); +} + +void +release_io_isurf(struct IsdnCardState *cs) +{ + release_region(cs->hw.isurf.reset, 1); +} + +static void +reset_isurf(struct IsdnCardState *cs, u_char chips) +{ + long flags; + + printk(KERN_INFO "ISurf: resetting card\n"); + + byteout(cs->hw.isurf.reset, chips); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + restore_flags(flags); +} + +static int +ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_isurf(cs, ISURF_RESET); + return(0); + case CARD_RELEASE: + release_io_isurf(cs); + return(0); + case CARD_INIT: + clear_pending_isac_ints(cs); + writeb(0, cs->hw.isurf.isar+ISAR_IRQBIT);mb(); + initisac(cs); + initisar(cs); + /* Reenable ISAC IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + /* RESET Receiver and Transmitter */ + cs->writeisac(cs, ISAC_CMDR, 0x41); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static int +isurf_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) { + int ret; + + if ((ic->command == ISDN_CMD_IOCTL) && (ic->arg == 9)) { + ret = isar_auxcmd(cs, ic); + if (!ret) { + reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET | + ISURF_ARCOFI_RESET); + initisac(cs); + cs->writeisac(cs, ISAC_MASK, 0); + cs->writeisac(cs, ISAC_CMDR, 0x41); + } + return(ret); + } + return(isar_auxcmd(cs, ic)); +} + +int __init +setup_isurf(struct IsdnCard *card) +{ + int ver; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, ISurf_revision); + printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp)); + + if (cs->typ != ISDN_CTYPE_ISURF) + return(0); + if (card->para[1] && card->para[2]) { + cs->hw.isurf.reset = card->para[1]; + cs->hw.isurf.isar = card->para[2] + ISURF_ISAR_OFFSET; + cs->hw.isurf.isac = card->para[2] + ISURF_ISAC_OFFSET; + cs->irq = card->para[0]; + } else { + printk(KERN_WARNING "HiSax: %s port/mem not set\n", + CardType[card->typ]); + return (0); + } + if (check_region(cs->hw.isurf.reset, 1)) { + printk(KERN_WARNING + "HiSax: %s config port %x already in use\n", + CardType[card->typ], + cs->hw.isurf.reset); + return (0); + } else { + request_region(cs->hw.isurf.reset, 1, "isurf isdn"); + } + + printk(KERN_INFO + "ISurf: defined at 0x%x 0x%x IRQ %d\n", + cs->hw.isurf.reset, + cs->hw.isurf.isar, + cs->irq); + + cs->cardmsg = &ISurf_card_msg; + cs->irq_func = &isurf_interrupt; + cs->auxcmd = &isurf_auxcmd; + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r; + cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r; + reset_isurf(cs, ISURF_RESET); + test_and_set_bit(HW_ISAR, &cs->HW_Flags); + ISACVersion(cs, "ISurf:"); + cs->BC_Read_Reg = &ReadISAR; + cs->BC_Write_Reg = &WriteISAR; + cs->BC_Send_Data = &isar_fill_fifo; + ver = ISARVersion(cs, "ISurf:"); + if (ver < 0) { + printk(KERN_WARNING + "ISurf: wrong ISAR version (ret = %d)\n", ver); + release_io_isurf(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c index 732e2b8a7..389d043a3 100644 --- a/drivers/isdn/hisax/ix1_micro.c +++ b/drivers/isdn/hisax/ix1_micro.c @@ -1,4 +1,4 @@ -/* $Id: ix1_micro.c,v 2.7 1998/04/15 16:44:31 keil Exp $ +/* $Id: ix1_micro.c,v 2.8 1999/07/12 21:05:19 keil Exp $ * ix1_micro.c low level stuff for ITK ix1-micro Rev.2 isdn cards * derived from the original file teles3.c from Karsten Keil @@ -11,6 +11,10 @@ * Beat Doebeli * * $Log: ix1_micro.c,v $ + * Revision 2.8 1999/07/12 21:05:19 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 2.7 1998/04/15 16:44:31 keil * new init code * @@ -84,7 +88,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *ix1_revision = "$Revision: 2.7 $"; +const char *ix1_revision = "$Revision: 2.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -199,7 +203,7 @@ static void ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "IX1: Spurious interrupt!\n"); @@ -207,16 +211,12 @@ ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); if (val) { if (cs->debug & L1_DEB_HSCX) @@ -229,16 +229,12 @@ ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - if (stat & 1) { - writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF); - writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF); - writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0); - writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0); - } - if (stat & 2) { - writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0); - } + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0); } void @@ -276,9 +272,6 @@ ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_ix1micro(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &ix1micro_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: inithscxisac(cs, 3); return(0); @@ -289,7 +282,7 @@ ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) } -int __init +int __init setup_ix1micro(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; @@ -331,6 +324,7 @@ setup_ix1micro(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &ix1_card_msg; + cs->irq_func = &ix1micro_interrupt; ISACVersion(cs, "ix1-Micro:"); if (HscxVersion(cs, "ix1-Micro:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c new file mode 100644 index 000000000..5699f28a7 --- /dev/null +++ b/drivers/isdn/hisax/jade.c @@ -0,0 +1,326 @@ +/* $Id: jade.c,v 1.2 1999/07/01 08:07:57 keil Exp $ + * + * jade.c JADE stuff (derived from original hscx.c) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: jade.c,v $ + * Revision 1.2 1999/07/01 08:07:57 keil + * Initial version + * + * + */ + + +#define __NO_VERSION__ +#include "hisax.h" +#include "hscx.h" +#include "jade.h" +#include "isdnl1.h" +#include <linux/interrupt.h> + + +HISAX_INITFUNC(int +JadeVersion(struct IsdnCardState *cs, char *s)) +{ + int ver,i; + int to = 50; + cs->BC_Write_Reg(cs, -1, 0x50, 0x19); + i=0; + while (to) { + udelay(1); + ver = cs->BC_Read_Reg(cs, -1, 0x60); + to--; + if (ver) + break; + if (!to) { + printk(KERN_INFO "%s JADE version not obtainable\n", s); + return (0); + } + } + /* Wait for the JADE */ + udelay(10); + /* Read version */ + ver = cs->BC_Read_Reg(cs, -1, 0x60); + printk(KERN_INFO "%s JADE version: %d\n", s, ver); + return (1); +} + +/* Write to indirect accessible jade register set */ +static void +jade_write_indirect(struct IsdnCardState *cs, u_char reg, u_char value) +{ + int to = 50; + long flags; + u_char ret; + save_flags(flags); + cli(); + /* Write the data */ + cs->BC_Write_Reg(cs, -1, COMM_JADE+1, value); + /* Say JADE we wanna write indirect reg 'reg' */ + cs->BC_Write_Reg(cs, -1, COMM_JADE, reg); + to = 50; + /* Wait for RDY goes high */ + while (to) { + udelay(1); + ret = cs->BC_Read_Reg(cs, -1, COMM_JADE); + to--; + if (ret & 1) + /* Got acknowledge */ + break; + if (!to) { + restore_flags(flags); + printk(KERN_INFO "Can not see ready bit from JADE DSP (reg=0x%X, value=0x%X)\n", reg, value); + return; + } + } + restore_flags(flags); +} + + + +void +modejade(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int jade = bcs->hw.hscx.hscx; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "jade %c mode %d ichan %d", + 'A' + jade, mode, bc); + debugl1(cs, tmp); + } + bcs->mode = mode; + bcs->channel = bc; + + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (mode == L1_MODE_TRANS ? jadeMODE_TMO:0x00)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR0, (jadeCCR0_PU|jadeCCR0_ITF)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR1, 0x00); + + jade_write_indirect(cs, jade_HDLC1SERRXPATH, 0x08); + jade_write_indirect(cs, jade_HDLC2SERRXPATH, 0x08); + jade_write_indirect(cs, jade_HDLC1SERTXPATH, 0x00); + jade_write_indirect(cs, jade_HDLC2SERTXPATH, 0x00); + + cs->BC_Write_Reg(cs, jade, jade_HDLC_XCCR, 0x07); + cs->BC_Write_Reg(cs, jade, jade_HDLC_RCCR, 0x07); + + if (bc == 0) { + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x00); + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x00); + } else { + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x04); + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x04); + } + switch (mode) { + case (L1_MODE_NULL): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, jadeMODE_TMO); + break; + case (L1_MODE_TRANS): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_TMO|jadeMODE_RAC|jadeMODE_XAC)); + break; + case (L1_MODE_HDLC): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_RAC|jadeMODE_XAC)); + break; + } + if (mode) { + cs->BC_Write_Reg(cs, jade, jade_HDLC_RCMD, (jadeRCMD_RRES|jadeRCMD_RMC)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_XCMD, jadeXCMD_XRES); + /* Unmask ints */ + cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0xF8); + } + else + /* Mask ints */ + cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0x00); +} + +void +jade_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +jade_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hscx.count = 0; + restore_flags(flags); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "jade_l2l1: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->tx_skb = skb; + st->l1.bcs->hw.hscx.count = 0; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + modejade(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + modejade(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +void +close_jadestate(struct BCState *bcs) +{ + modejade(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.hscx.rcvbuf) { + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + } + if (bcs->blog) { + kfree(bcs->blog); + bcs->blog = NULL; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_jadestate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hscx.rcvbuf\n"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + return (1); + } + if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for bcs->blog\n"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + return (2); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->hw.hscx.rcvidx = 0; + bcs->tx_cnt = 0; + return (0); +} + + +int +setstack_jade(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_jadestate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = jade_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +HISAX_INITFUNC(void +clear_pending_jade_ints(struct IsdnCardState *cs)) +{ + int val; + char tmp[64]; + + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00); + + val = cs->BC_Read_Reg(cs, 1, jade_HDLC_ISR); + sprintf(tmp, "jade B ISTA %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 0, jade_HDLC_ISR); + sprintf(tmp, "jade A ISTA %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 1, jade_HDLC_STAR); + sprintf(tmp, "jade B STAR %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 0, jade_HDLC_STAR); + sprintf(tmp, "jade A STAR %x", val); + debugl1(cs, tmp); + /* Unmask ints */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0xF8); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0xF8); +} + +HISAX_INITFUNC(void +initjade(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_jade; + cs->bcs[1].BC_SetStack = setstack_jade; + cs->bcs[0].BC_Close = close_jadestate; + cs->bcs[1].BC_Close = close_jadestate; + cs->bcs[0].hw.hscx.hscx = 0; + cs->bcs[1].hw.hscx.hscx = 1; + + /* Stop DSP audio tx/rx */ + jade_write_indirect(cs, 0x11, 0x0f); + jade_write_indirect(cs, 0x17, 0x2f); + + /* Transparent Mode, RxTx inactive, No Test, No RFS/TFS */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_MODE, jadeMODE_TMO); + cs->BC_Write_Reg(cs, 1, jade_HDLC_MODE, jadeMODE_TMO); + /* Power down, 1-Idle, RxTx least significant bit first */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_CCR0, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_CCR0, 0x00); + /* Mask all interrupts */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00); + /* Setup host access to hdlc controller */ + jade_write_indirect(cs, jade_HDLCCNTRACCESS, (jadeINDIRECT_HAH1|jadeINDIRECT_HAH2)); + /* Unmask HDLC int (don´t forget DSP int later on)*/ + cs->BC_Write_Reg(cs, -1,jade_INT, (jadeINT_HDLC1|jadeINT_HDLC2)); + + /* once again TRANSPARENT */ + modejade(cs->bcs, 0, 0); + modejade(cs->bcs + 1, 0, 0); +} + diff --git a/drivers/isdn/hisax/jade.h b/drivers/isdn/hisax/jade.h new file mode 100644 index 000000000..bcbe6b291 --- /dev/null +++ b/drivers/isdn/hisax/jade.h @@ -0,0 +1,137 @@ +/* $Id: jade.h,v 1.2 1999/07/01 08:07:58 keil Exp $ + * jade.h JADE specific defines + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * + * $Log: jade.h,v $ + * Revision 1.2 1999/07/01 08:07:58 keil + * Initial version + * + * + */ + +/* All Registers original Siemens Spec */ +#ifndef __JADE_H__ +#define __JADE_H__ + +/* Special registers for access to indirect accessible JADE regs */ +#define DIRECT_IO_JADE 0x0000 /* Jade direct io access area */ +#define COMM_JADE 0x0040 /* Jade communication area */ + +/********************************************************************/ +/* JADE-HDLC registers */ +/********************************************************************/ +#define jade_HDLC_RFIFO 0x00 /* R */ +#define jade_HDLC_XFIFO 0x00 /* W */ + +#define jade_HDLC_STAR 0x20 /* R */ + #define jadeSTAR_XDOV 0x80 + #define jadeSTAR_XFW 0x40 /* Does not work*/ + #define jadeSTAR_XCEC 0x20 + #define jadeSTAR_RCEC 0x10 + #define jadeSTAR_BSY 0x08 + #define jadeSTAR_RNA 0x04 + #define jadeSTAR_STR 0x02 + #define jadeSTAR_STX 0x01 + +#define jade_HDLC_XCMD 0x20 /* W */ + #define jadeXCMD_XF 0x80 + #define jadeXCMD_XME 0x40 + #define jadeXCMD_XRES 0x20 + #define jadeXCMD_STX 0x01 + +#define jade_HDLC_RSTA 0x21 /* R */ + #define jadeRSTA_VFR 0x80 + #define jadeRSTA_RDO 0x40 + #define jadeRSTA_CRC 0x20 + #define jadeRSTA_RAB 0x10 + #define jadeRSTA_MASK 0xF0 + +#define jade_HDLC_MODE 0x22 /* RW*/ + #define jadeMODE_TMO 0x80 + #define jadeMODE_RAC 0x40 + #define jadeMODE_XAC 0x20 + #define jadeMODE_TLP 0x10 + #define jadeMODE_ERFS 0x02 + #define jadeMODE_ETFS 0x01 + +#define jade_HDLC_RBCH 0x24 /* R */ + +#define jade_HDLC_RBCL 0x25 /* R */ +#define jade_HDLC_RCMD 0x25 /* W */ + #define jadeRCMD_RMC 0x80 + #define jadeRCMD_RRES 0x40 + #define jadeRCMD_RMD 0x20 + #define jadeRCMD_STR 0x02 + +#define jade_HDLC_CCR0 0x26 /* RW*/ + #define jadeCCR0_PU 0x80 + #define jadeCCR0_ITF 0x40 + #define jadeCCR0_C32 0x20 + #define jadeCCR0_CRL 0x10 + #define jadeCCR0_RCRC 0x08 + #define jadeCCR0_XCRC 0x04 + #define jadeCCR0_RMSB 0x02 + #define jadeCCR0_XMSB 0x01 + +#define jade_HDLC_CCR1 0x27 /* RW*/ + #define jadeCCR1_RCS0 0x80 + #define jadeCCR1_RCONT 0x40 + #define jadeCCR1_RFDIS 0x20 + #define jadeCCR1_XCS0 0x10 + #define jadeCCR1_XCONT 0x08 + #define jadeCCR1_XFDIS 0x04 + +#define jade_HDLC_TSAR 0x28 /* RW*/ +#define jade_HDLC_TSAX 0x29 /* RW*/ +#define jade_HDLC_RCCR 0x2A /* RW*/ +#define jade_HDLC_XCCR 0x2B /* RW*/ + +#define jade_HDLC_ISR 0x2C /* R */ +#define jade_HDLC_IMR 0x2C /* W */ + #define jadeISR_RME 0x80 + #define jadeISR_RPF 0x40 + #define jadeISR_RFO 0x20 + #define jadeISR_XPR 0x10 + #define jadeISR_XDU 0x08 + #define jadeISR_ALLS 0x04 + +#define jade_INT 0x75 + #define jadeINT_HDLC1 0x02 + #define jadeINT_HDLC2 0x01 + #define jadeINT_DSP 0x04 +#define jade_INTR 0x70 + +/********************************************************************/ +/* Indirect accessible JADE registers of common interest */ +/********************************************************************/ +#define jade_CHIPVERSIONNR 0x00 /* Does not work*/ + +#define jade_HDLCCNTRACCESS 0x10 + #define jadeINDIRECT_HAH1 0x02 + #define jadeINDIRECT_HAH2 0x01 + +#define jade_HDLC1SERRXPATH 0x1D +#define jade_HDLC1SERTXPATH 0x1E +#define jade_HDLC2SERRXPATH 0x1F +#define jade_HDLC2SERTXPATH 0x20 + #define jadeINDIRECT_SLIN1 0x10 + #define jadeINDIRECT_SLIN0 0x08 + #define jadeINDIRECT_LMOD1 0x04 + #define jadeINDIRECT_LMOD0 0x02 + #define jadeINDIRECT_HHR 0x01 + #define jadeINDIRECT_HHX 0x01 + +#define jade_RXAUDIOCH1CFG 0x11 +#define jade_RXAUDIOCH2CFG 0x14 +#define jade_TXAUDIOCH1CFG 0x17 +#define jade_TXAUDIOCH2CFG 0x1A + +extern int JadeVersion(struct IsdnCardState *cs, char *s); +extern void jade_sched_event(struct BCState *bcs, int event); +extern void modejade(struct BCState *bcs, int mode, int bc); +extern void clear_pending_jade_ints(struct IsdnCardState *cs); +extern void initjade(struct IsdnCardState *cs); + +#endif /* __JADE_H__ */ diff --git a/drivers/isdn/hisax/jade_irq.c b/drivers/isdn/hisax/jade_irq.c new file mode 100644 index 000000000..9cfcc7d6d --- /dev/null +++ b/drivers/isdn/hisax/jade_irq.c @@ -0,0 +1,247 @@ +/* $Id: jade_irq.c,v 1.2 1999/07/01 08:07:59 keil Exp $ + * + * jade_irq.c Low level JADE IRQ stuff (derived from original hscx_irq.c) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log: jade_irq.c,v $ + * Revision 1.2 1999/07/01 08:07:59 keil + * Initial version + * + * + */ + +static inline void +waitforCEC(struct IsdnCardState *cs, int jade, int reg) +{ + int to = 50; + int mask = (reg == jade_HDLC_XCMD ? jadeSTAR_XCEC : jadeSTAR_RCEC); + while ((READJADE(cs, jade, jade_HDLC_STAR) & mask) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC (jade) timeout\n"); +} + + +static inline void +waitforXFW(struct IsdnCardState *cs, int jade) +{ + /* Does not work on older jade versions, don't care */ +} + +static inline void +WriteJADECMDR(struct IsdnCardState *cs, int jade, int reg, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(cs, jade, reg); + WRITEJADE(cs, jade, reg, data); + restore_flags(flags); +} + + + +static void +jade_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct IsdnCardState *cs = bcs->cs; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "jade_empty_fifo"); + + if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "jade_empty_fifo: incoming packet too large"); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC); + bcs->hw.hscx.rcvidx = 0; + return; + } + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + bcs->hw.hscx.rcvidx += count; + save_flags(flags); + cli(); + READJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "jade_empty_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static void +jade_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int more, count; + int fifo_size = 32; + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "jade_fill_fifo"); + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; + if (bcs->tx_skb->len > fifo_size) { + more = !0; + count = fifo_size; + } else + count = bcs->tx_skb->len; + + waitforXFW(cs, bcs->hw.hscx.hscx); + save_flags(flags); + cli(); + ptr = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hscx.count += count; + WRITEJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, more ? jadeXCMD_XF : (jadeXCMD_XF|jadeXCMD_XME)); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "jade_fill_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + + +static inline void +jade_interrupt(struct IsdnCardState *cs, u_char val, u_char jade) +{ + u_char r; + struct BCState *bcs = cs->bcs + jade; + struct sk_buff *skb; + int fifo_size = 32; + int count; + int i_jade = (int) jade; /* To satisfy the compiler */ + + if (!test_bit(BC_FLG_INIT, &bcs->Flag)) + return; + + if (val & 0x80) { /* RME */ + r = READJADE(cs, i_jade, jade_HDLC_RSTA); + if ((r & 0xf0) != 0xa0) { + if (!(r & 0x80)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %s invalid frame", (jade ? "B":"A")); + if ((r & 0x40) && bcs->mode) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c RDO mode=%d", 'A'+jade, bcs->mode); + if (!(r & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c CRC error", 'A'+jade); + WriteJADECMDR(cs, jade, jade_HDLC_RCMD, jadeRCMD_RMC); + } else { + count = READJADE(cs, i_jade, jade_HDLC_RBCL) & 0x1F; + if (count == 0) + count = fifo_size; + jade_empty_fifo(bcs, count); + if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { + if (cs->debug & L1_DEB_HSCX_FIFO) + debugl1(cs, "HX Frame %d", count); + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "JADE %s receive out of memory\n", (jade ? "B":"A")); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + } + } + bcs->hw.hscx.rcvidx = 0; + jade_sched_event(bcs, B_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + jade_empty_fifo(bcs, fifo_size); + if (bcs->mode == L1_MODE_TRANS) { + /* receive audio data */ + if (!(skb = dev_alloc_skb(fifo_size))) + printk(KERN_WARNING "HiSax: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->hw.hscx.rcvidx = 0; + jade_sched_event(bcs, B_RCVBUFREADY); + } + } + if (val & 0x10) { /* XPR */ + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + jade_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->hw.hscx.count = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + jade_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + jade_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +static inline void +jade_int_main(struct IsdnCardState *cs, u_char val, int jade) +{ + struct BCState *bcs; + bcs = cs->bcs + jade; + + if (val & jadeISR_RFO) { + /* handled with RDO */ + val &= ~jadeISR_RFO; + } + if (val & jadeISR_XDU) { + /* relevant in HDLC mode only */ + /* don't reset XPR here */ + if (bcs->mode == 1) + jade_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, jadeXCMD_XRES); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c EXIR %x Lost TX", 'A'+jade, val); + } + } + if (val & (jadeISR_RME|jadeISR_RPF|jadeISR_XPR)) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "JADE %c interrupt %x", 'A'+jade, val); + jade_interrupt(cs, val, jade); + } +} diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c index db6ceca76..ba8d95dbd 100644 --- a/drivers/isdn/hisax/l3_1tr6.c +++ b/drivers/isdn/hisax/l3_1tr6.c @@ -1,4 +1,4 @@ -/* $Id: l3_1tr6.c,v 2.8 1998/11/15 23:55:08 keil Exp $ +/* $Id: l3_1tr6.c,v 2.9 1999/07/01 08:11:55 keil Exp $ * German 1TR6 D-channel protocol * @@ -10,6 +10,9 @@ * * * $Log: l3_1tr6.c,v $ + * Revision 2.9 1999/07/01 08:11:55 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.8 1998/11/15 23:55:08 keil * changes from 2.0 * @@ -56,7 +59,7 @@ #include <linux/ctype.h> extern char *HiSax_getrev(const char *revision); -const char *l3_1tr6_revision = "$Revision: 2.8 $"; +const char *l3_1tr6_revision = "$Revision: 2.9 $"; #define MsgHead(ptr, cref, mty, dis) \ *ptr++ = dis; \ @@ -91,14 +94,14 @@ l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); l3_1tr6_release_req(pc, 0, NULL); } static void l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); if (pc->st->l3.debug & L3_DEB_WARN) l3_debug(pc->st, msg); l3_1tr6_release_req(pc, 0, NULL); @@ -248,7 +251,7 @@ l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg) if ((FAC_SPV == p[3]) || (FAC_Activate == p[3])) pc->para.spv = 1; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); /* Signal all services, linklevel takes care of Service-Indicator */ if (bcfound) { @@ -287,7 +290,7 @@ l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg) l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); L3AddTimer(&pc->timer, T304, CC_T304); pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); } @@ -318,7 +321,7 @@ l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg) l3_1tr6_error(pc, "missing call sent WE0_chanID", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); L3AddTimer(&pc->timer, T310, CC_T310); newl3state(pc, 3); pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); @@ -329,7 +332,7 @@ l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); L3DelTimer(&pc->timer); /* T304 */ newl3state(pc, 4); pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); @@ -360,7 +363,7 @@ l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg) } } else if (pc->st->l3.debug & L3_DEB_CHARGE) l3_debug(pc->st, "charging info not found"); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } @@ -369,7 +372,7 @@ l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } static void @@ -383,7 +386,7 @@ l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg) return; } newl3state(pc, 10); - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); pc->para.chargeinfo = 0; pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); } @@ -407,11 +410,11 @@ l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg) pc->para.loc = 0; } } else { - pc->para.cause = -1; + pc->para.cause = NO_CAUSE; l3_1tr6_error(pc, "missing REL cause", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); StopAllL3Timer(pc); newl3state(pc, 0); l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1); @@ -424,10 +427,10 @@ l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); StopAllL3Timer(pc); newl3state(pc, 0); - pc->para.cause = -1; + pc->para.cause = NO_CAUSE; pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); release_l3_process(pc); } @@ -475,13 +478,13 @@ l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg) } else { if (pc->st->l3.debug & L3_DEB_WARN) l3_debug(pc->st, "cause not found"); - pc->para.cause = -1; + pc->para.cause = NO_CAUSE; } if (!findie(skb->data, skb->len, WE6_date, 6)) { l3_1tr6_error(pc, "missing connack date", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); newl3state(pc, 12); pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); } @@ -496,7 +499,7 @@ l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg) l3_1tr6_error(pc, "missing connack date", skb); return; } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); newl3state(pc, 10); pc->para.chargeinfo = 0; L3DelTimer(&pc->timer); @@ -567,6 +570,9 @@ l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg) case 0x10: clen = 0; break; + case 0x11: + cause = CAUSE_UserBusy; + break; case 0x15: cause = CAUSE_CallRejected; break; @@ -620,7 +626,7 @@ l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg) u_char clen = 1; L3DelTimer(&pc->timer); - if (pc->para.cause > 0) + if (pc->para.cause != NO_CAUSE) cause = pc->para.cause; /* Map DSS1 causes */ switch (cause & 0x7f) { @@ -679,6 +685,24 @@ l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg) pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); release_l3_process(pc); } + +static void +l3_1tr6_dl_reset(struct l3_process *pc, u_char pr, void *arg) +{ + pc->para.cause = CAUSE_LocalProcErr; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3_1tr6_dl_release(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 0); + pc->para.cause = 0x1b; /* Destination out of order */ + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + release_l3_process(pc); +} + /* *INDENT-OFF* */ static struct stateentry downstl[] = { @@ -689,8 +713,6 @@ static struct stateentry downstl[] = CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req}, {SBIT(12), CC_RELEASE | REQUEST, l3_1tr6_release_req}, - {ALL_STATES, - CC_DLRL | REQUEST, l3_1tr6_reset}, {SBIT(6), CC_IGNORE | REQUEST, l3_1tr6_reset}, {SBIT(6), @@ -751,11 +773,22 @@ static struct stateentry datastln1[] = {SBIT(19), MT_N1_REL_ACK, l3_1tr6_rel_ack} }; -/* *INDENT-ON* */ #define DATASTLN1_LEN \ (sizeof(datastln1) / sizeof(struct stateentry)) +static struct stateentry manstatelist[] = +{ + {SBIT(2), + DL_ESTABLISH | INDICATION, l3_1tr6_dl_reset}, + {ALL_STATES, + DL_RELEASE | INDICATION, l3_1tr6_dl_release}, +}; + +#define MANSLLEN \ + (sizeof(manstatelist) / sizeof(struct stateentry)) +/* *INDENT-ON* */ + static void up1tr6(struct PStack *st, int pr, void *arg) { @@ -778,20 +811,20 @@ up1tr6(struct PStack *st, int pr, void *arg) } if (skb->len < 4) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "up1tr6 len only %ld", skb->len); + sprintf(tmp, "up1tr6 len only %d", skb->len); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld", + sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", skb->data[0], skb->len); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } if (skb->data[1] != 1) { @@ -799,13 +832,13 @@ up1tr6(struct PStack *st, int pr, void *arg) sprintf(tmp, "up1tr6 CR len not 1"); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } cr = skb->data[2]; mt = skb->data[3]; if (skb->data[0] == PROTO_DIS_N0) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%s N0 mt %x unhandled", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt); @@ -820,11 +853,11 @@ up1tr6(struct PStack *st, int pr, void *arg) sprintf(tmp, "up1tr6 no roc mem"); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } } else { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } } else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) || @@ -832,7 +865,7 @@ up1tr6(struct PStack *st, int pr, void *arg) (mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) || (mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) || (mt == MT_N1_INFO)) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } else { if (!(proc = new_l3_process(st, cr))) { @@ -840,7 +873,7 @@ up1tr6(struct PStack *st, int pr, void *arg) sprintf(tmp, "up1tr6 no roc mem"); l3_debug(st, tmp); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } mt = MT_N1_INVALID; @@ -851,7 +884,7 @@ up1tr6(struct PStack *st, int pr, void *arg) ((1 << proc->state) & datastln1[i].state)) break; if (i == DATASTLN1_LEN) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x unhandled", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", @@ -879,7 +912,7 @@ down1tr6(struct PStack *st, int pr, void *arg) struct Channel *chan; char tmp[80]; - if (((DL_ESTABLISH | REQUEST)== pr) || ((DL_RELEASE | REQUEST)== pr)) { + if ((DL_ESTABLISH | REQUEST)== pr) { l3_msg(st, pr, NULL); return; } else if ((CC_SETUP | REQUEST) == pr) { @@ -918,6 +951,34 @@ down1tr6(struct PStack *st, int pr, void *arg) } } +static void +man1tr6(struct PStack *st, int pr, void *arg) +{ + int i; + struct l3_process *proc = arg; + + if (!proc) { + printk(KERN_ERR "HiSax man1tr6 without proc pr=%04x\n", pr); + return; + } + for (i = 0; i < MANSLLEN; i++) + if ((pr == manstatelist[i].primitive) && + ((1 << proc->state) & manstatelist[i].state)) + break; + if (i == MANSLLEN) { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d man1tr6 state %d prim %d unhandled", + proc->callref & 0x7f, proc->state, pr); + } + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d man1tr6 state %d prim %d", + proc->callref & 0x7f, proc->state, pr); + } + manstatelist[i].rout(proc, pr, arg); + } +} + void setstack_1tr6(struct PStack *st) { @@ -925,6 +986,7 @@ setstack_1tr6(struct PStack *st) st->lli.l4l3 = down1tr6; st->l2.l2l3 = up1tr6; + st->l3.l3ml3 = man1tr6; st->l3.N303 = 0; strcpy(tmp, l3_1tr6_revision); diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c index 0808d32a0..22a5de9fa 100644 --- a/drivers/isdn/hisax/l3dss1.c +++ b/drivers/isdn/hisax/l3dss1.c @@ -1,4 +1,4 @@ -/* $Id: l3dss1.c,v 2.12 1998/11/15 23:55:10 keil Exp $ +/* $Id: l3dss1.c,v 2.19 1999/08/25 16:55:23 keil Exp $ * EURO/DSS1 D-channel protocol * @@ -13,6 +13,24 @@ * Fritz Elfert * * $Log: l3dss1.c,v $ + * Revision 2.19 1999/08/25 16:55:23 keil + * Fix for test case TC10011 + * + * Revision 2.18 1999/08/11 20:54:39 keil + * High layer compatibility is valid in SETUP + * + * Revision 2.17 1999/07/25 16:18:25 keil + * Fix Suspend/Resume + * + * Revision 2.16 1999/07/21 14:46:23 keil + * changes from EICON certification + * + * Revision 2.14 1999/07/09 08:30:08 keil + * cosmetics + * + * Revision 2.13 1999/07/01 08:11:58 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.12 1998/11/15 23:55:10 keil * changes from 2.0 * @@ -72,29 +90,227 @@ #include <linux/ctype.h> extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 2.12 $"; +const char *dss1_revision = "$Revision: 2.19 $"; + +#define EXT_BEARER_CAPS 1 #define MsgHead(ptr, cref, mty) \ *ptr++ = 0x8; \ - *ptr++ = 0x1; \ - *ptr++ = cref^0x80; \ + if (cref == -1) { \ + *ptr++ = 0x0; \ + } else { \ + *ptr++ = 0x1; \ + *ptr++ = cref^0x80; \ + } \ *ptr++ = mty -#if HISAX_DE_AOC +/**********************************************/ +/* get a new invoke id for remote operations. */ +/* Only a return value != 0 is valid */ +/**********************************************/ +static unsigned char new_invoke_id(struct PStack *p) +{ + unsigned char retval; + int flags,i; + + i = 32; /* maximum search depth */ + + save_flags(flags); + cli(); + + retval = p->prot.dss1.last_invoke_id + 1; /* try new id */ + while ((i) && (p->prot.dss1.invoke_used[retval >> 3] == 0xFF)) { + p->prot.dss1.last_invoke_id = (retval & 0xF8) + 8; + i--; + } + if (i) { + while (p->prot.dss1.invoke_used[retval >> 3] & (1 << (retval & 7))) + retval++; + } else + retval = 0; + p->prot.dss1.last_invoke_id = retval; + p->prot.dss1.invoke_used[retval >> 3] |= (1 << (retval & 7)); + restore_flags(flags); + + return(retval); +} /* new_invoke_id */ + +/*************************/ +/* free a used invoke id */ +/*************************/ +static void free_invoke_id(struct PStack *p, unsigned char id) +{ int flags; + + if (!id) return; /* 0 = invalid value */ + + save_flags(flags); + cli(); + p->prot.dss1.invoke_used[id >> 3] &= ~(1 << (id & 7)); + restore_flags(flags); +} /* free_invoke_id */ + + +/**********************************************************/ +/* create a new l3 process and fill in dss1 specific data */ +/**********************************************************/ +static struct l3_process +*dss1_new_l3_process(struct PStack *st, int cr) +{ struct l3_process *proc; + + if (!(proc = new_l3_process(st, cr))) + return(NULL); + + proc->prot.dss1.invoke_id = 0; + proc->prot.dss1.remote_operation = 0; + proc->prot.dss1.uus1_data[0] = '\0'; + + return(proc); +} /* dss1_new_l3_process */ + +/************************************************/ +/* free a l3 process and all dss1 specific data */ +/************************************************/ static void -l3dss1_parse_facility(struct l3_process *pc, u_char * p) +dss1_release_l3_process(struct l3_process *p) +{ + free_invoke_id(p->st,p->prot.dss1.invoke_id); + release_l3_process(p); +} /* dss1_release_l3_process */ + +/********************************************************/ +/* search a process with invoke id id and dummy callref */ +/********************************************************/ +static struct l3_process * +l3dss1_search_dummy_proc(struct PStack *st, int id) +{ struct l3_process *pc = st->l3.proc; /* start of processes */ + + if (!id) return(NULL); + + while (pc) + { if ((pc->callref == -1) && (pc->prot.dss1.invoke_id == id)) + return(pc); + pc = pc->next; + } + return(NULL); +} /* l3dss1_search_dummy_proc */ + +/*******************************************************************/ +/* called when a facility message with a dummy callref is received */ +/* and a return result is delivered. id specifies the invoke id. */ +/*******************************************************************/ +static void +l3dss1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen) +{ isdn_ctrl ic; + struct IsdnCardState *cs; + struct l3_process *pc = NULL; + + if ((pc = l3dss1_search_dummy_proc(st, id))) + { L3DelTimer(&pc->timer); /* remove timer */ + + cs = pc->st->l1.hardware; + ic.driver = cs->myid; + ic.command = ISDN_STAT_PROT; + ic.arg = DSS1_STAT_INVOKE_RES; + ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id; + ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id; + ic.parm.dss1_io.proc = pc->prot.dss1.proc; + ic.parm.dss1_io.timeout= 0; + ic.parm.dss1_io.datalen = nlen; + ic.parm.dss1_io.data = p; + free_invoke_id(pc->st, pc->prot.dss1.invoke_id); + pc->prot.dss1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + dss1_release_l3_process(pc); + } + else + l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen); +} /* l3dss1_dummy_return_result */ + +/*******************************************************************/ +/* called when a facility message with a dummy callref is received */ +/* and a return error is delivered. id specifies the invoke id. */ +/*******************************************************************/ +static void +l3dss1_dummy_error_return(struct PStack *st, int id, ulong error) +{ isdn_ctrl ic; + struct IsdnCardState *cs; + struct l3_process *pc = NULL; + + if ((pc = l3dss1_search_dummy_proc(st, id))) + { L3DelTimer(&pc->timer); /* remove timer */ + + cs = pc->st->l1.hardware; + ic.driver = cs->myid; + ic.command = ISDN_STAT_PROT; + ic.arg = DSS1_STAT_INVOKE_ERR; + ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id; + ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id; + ic.parm.dss1_io.proc = pc->prot.dss1.proc; + ic.parm.dss1_io.timeout= error; + ic.parm.dss1_io.datalen = 0; + ic.parm.dss1_io.data = NULL; + free_invoke_id(pc->st, pc->prot.dss1.invoke_id); + pc->prot.dss1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + dss1_release_l3_process(pc); + } + else + l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error); +} /* l3dss1_error_return */ + +/*******************************************************************/ +/* called when a facility message with a dummy callref is received */ +/* and a invoke is delivered. id specifies the invoke id. */ +/*******************************************************************/ +static void +l3dss1_dummy_invoke(struct PStack *st, int cr, int id, + int ident, u_char *p, u_char nlen) +{ isdn_ctrl ic; + struct IsdnCardState *cs; + + l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d", + (cr == -1) ? "local" : "broadcast",id,ident,nlen); + if (cr >= -1) return; /* ignore local data */ + + cs = st->l1.hardware; + ic.driver = cs->myid; + ic.command = ISDN_STAT_PROT; + ic.arg = DSS1_STAT_INVOKE_BRD; + ic.parm.dss1_io.hl_id = id; + ic.parm.dss1_io.ll_id = 0; + ic.parm.dss1_io.proc = ident; + ic.parm.dss1_io.timeout= 0; + ic.parm.dss1_io.datalen = nlen; + ic.parm.dss1_io.data = p; + + cs->iif.statcallb(&ic); +} /* l3dss1_dummy_invoke */ + +static void +l3dss1_parse_facility(struct PStack *st, struct l3_process *pc, + int cr, u_char * p) { int qd_len = 0; + unsigned char nlen = 0, ilen, cp_tag; + int ident, id; + ulong err_ret; + + if (pc) + st = pc->st; /* valid Stack */ + else + if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */ p++; qd_len = *p++; if (qd_len == 0) { - l3_debug(pc->st, "qd_len == 0"); + l3_debug(st, "qd_len == 0"); return; } if ((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */ - l3_debug(pc->st, "supplementary service != 0x11"); + l3_debug(st, "supplementary service != 0x11"); return; } while (qd_len > 0 && !(*p & 0x80)) { /* extension ? */ @@ -102,72 +318,92 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) qd_len--; } if (qd_len < 2) { - l3_debug(pc->st, "qd_len < 2"); + l3_debug(st, "qd_len < 2"); return; } p++; qd_len--; if ((*p & 0xE0) != 0xA0) { /* class and form */ - l3_debug(pc->st, "class and form != 0xA0"); + l3_debug(st, "class and form != 0xA0"); return; } - switch (*p & 0x1F) { /* component tag */ - case 1: /* invoke */ - { - unsigned char nlen = 0, ilen; - int ident; - - p++; - qd_len--; - if (qd_len < 1) { - l3_debug(pc->st, "qd_len < 1"); - break; - } - if (*p & 0x80) { /* length format */ - l3_debug(pc->st, "*p & 0x80 length format"); - break; - } - nlen = *p++; - qd_len--; - if (qd_len < nlen) { - l3_debug(pc->st, "qd_len < nlen"); - return; - } - qd_len -= nlen; - - if (nlen < 2) { - l3_debug(pc->st, "nlen < 2"); - return; - } - if (*p != 0x02) { /* invoke identifier tag */ - l3_debug(pc->st, "invoke identifier tag !=0x02"); - return; - } - p++; - nlen--; - if (*p & 0x80) { /* length format */ - l3_debug(pc->st, "*p & 0x80 length format 2"); - break; - } - ilen = *p++; - nlen--; - if (ilen > nlen || ilen == 0) { - l3_debug(pc->st, "ilen > nlen || ilen == 0"); - return; - } - nlen -= ilen; - ident = 0; - while (ilen > 0) { - ident = (ident << 8) | (*p++ & 0xFF); /* invoke identifier */ - ilen--; - } + + cp_tag = *p & 0x1F; /* remember tag value */ + p++; + qd_len--; + if (qd_len < 1) + { l3_debug(st, "qd_len < 1"); + return; + } + if (*p & 0x80) + { /* length format indefinite or limited */ + nlen = *p++ & 0x7F; /* number of len bytes or indefinite */ + if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) || + (nlen > 1)) + { l3_debug(st, "length format error or not implemented"); + return; + } + if (nlen == 1) + { nlen = *p++; /* complete length */ + qd_len--; + } + else + { qd_len -= 2; /* trailing null bytes */ + if ((*(p+qd_len)) || (*(p+qd_len+1))) + { l3_debug(st,"length format indefinite error"); + return; + } + nlen = qd_len; + } + } + else + { nlen = *p++; + qd_len--; + } + if (qd_len < nlen) + { l3_debug(st, "qd_len < nlen"); + return; + } + qd_len -= nlen; + + if (nlen < 2) + { l3_debug(st, "nlen < 2"); + return; + } + if (*p != 0x02) + { /* invoke identifier tag */ + l3_debug(st, "invoke identifier tag !=0x02"); + return; + } + p++; + nlen--; + if (*p & 0x80) + { /* length format */ + l3_debug(st, "invoke id length format 2"); + return; + } + ilen = *p++; + nlen--; + if (ilen > nlen || ilen == 0) + { l3_debug(st, "ilen > nlen || ilen == 0"); + return; + } + nlen -= ilen; + id = 0; + while (ilen > 0) + { id = (id << 8) | (*p++ & 0xFF); /* invoke identifier */ + ilen--; + } + + switch (cp_tag) { /* component tag */ + case 1: /* invoke */ if (nlen < 2) { - l3_debug(pc->st, "nlen < 2 22"); + l3_debug(st, "nlen < 2 22"); return; } if (*p != 0x02) { /* operation value */ - l3_debug(pc->st, "operation value !=0x02"); + l3_debug(st, "operation value !=0x02"); return; } p++; @@ -175,7 +411,7 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) ilen = *p++; nlen--; if (ilen > nlen || ilen == 0) { - l3_debug(pc->st, "ilen > nlen || ilen == 0 22"); + l3_debug(st, "ilen > nlen || ilen == 0 22"); return; } nlen -= ilen; @@ -185,11 +421,18 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) ilen--; } + if (!pc) + { l3dss1_dummy_invoke(st, cr, id, ident, p, nlen); + return; + } +#if HISAX_DE_AOC + { + #define FOO1(s,a,b) \ while(nlen > 1) { \ int ilen = p[1]; \ if(nlen < ilen+2) { \ - l3_debug(pc->st, "FOO1 nlen < ilen+2"); \ + l3_debug(st, "FOO1 nlen < ilen+2"); \ return; \ } \ nlen -= ilen+2; \ @@ -203,8 +446,6 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) } switch (ident) { - default: - break; case 0x22: /* during */ FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( { ident = 0; @@ -215,14 +456,14 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) } if (ident > pc->para.chargeinfo) { pc->para.chargeinfo = ident; - pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + st->l3.l3l4(st, CC_CHARGE | INDICATION, pc); } - if (pc->st->l3.debug & L3_DEB_CHARGE) { + if (st->l3.debug & L3_DEB_CHARGE) { if (*(p + 2) == 0) { - l3_debug(pc->st, "charging info during %d", pc->para.chargeinfo); + l3_debug(st, "charging info during %d", pc->para.chargeinfo); } else { - l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo); + l3_debug(st, "charging info final %d", pc->para.chargeinfo); } } } @@ -238,113 +479,548 @@ l3dss1_parse_facility(struct l3_process *pc, u_char * p) } if (ident > pc->para.chargeinfo) { pc->para.chargeinfo = ident; - pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + st->l3.l3l4(st, CC_CHARGE | INDICATION, pc); } - if (pc->st->l3.debug & L3_DEB_CHARGE) { - l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo); + if (st->l3.debug & L3_DEB_CHARGE) { + l3_debug(st, "charging info final %d", pc->para.chargeinfo); } } )))))) break; + default: + l3_debug(st, "invoke break invalid ident %02x",ident); + break; } #undef FOO1 } +#else not HISAX_DE_AOC + l3_debug(st, "invoke break"); +#endif not HISAX_DE_AOC break; case 2: /* return result */ - l3_debug(pc->st, "return result break"); + /* if no process available handle separately */ + if (!pc) + { if (cr == -1) + l3dss1_dummy_return_result(st, id, p, nlen); + return; + } + if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id)) + { /* Diversion successfull */ + free_invoke_id(st,pc->prot.dss1.invoke_id); + pc->prot.dss1.remote_result = 0; /* success */ + pc->prot.dss1.invoke_id = 0; + pc->redir_result = pc->prot.dss1.remote_result; + st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successfull */ + else + l3_debug(st,"return error unknown identifier"); break; case 3: /* return error */ - l3_debug(pc->st, "return error break"); + err_ret = 0; + if (nlen < 2) + { l3_debug(st, "return error nlen < 2"); + return; + } + if (*p != 0x02) + { /* result tag */ + l3_debug(st, "invoke error tag !=0x02"); + return; + } + p++; + nlen--; + if (*p > 4) + { /* length format */ + l3_debug(st, "invoke return errlen > 4 "); + return; + } + ilen = *p++; + nlen--; + if (ilen > nlen || ilen == 0) + { l3_debug(st, "error return ilen > nlen || ilen == 0"); + return; + } + nlen -= ilen; + while (ilen > 0) + { err_ret = (err_ret << 8) | (*p++ & 0xFF); /* error value */ + ilen--; + } + /* if no process available handle separately */ + if (!pc) + { if (cr == -1) + l3dss1_dummy_error_return(st, id, err_ret); + return; + } + if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id)) + { /* Deflection error */ + free_invoke_id(st,pc->prot.dss1.invoke_id); + pc->prot.dss1.remote_result = err_ret; /* result */ + pc->prot.dss1.invoke_id = 0; + pc->redir_result = pc->prot.dss1.remote_result; + st->l3.l3l4(st, CC_REDIR | INDICATION, pc); + } /* Deflection error */ + else + l3_debug(st,"return result unknown identifier"); break; default: - l3_debug(pc->st, "default break"); + l3_debug(st, "facility default break tag=0x%02x",cp_tag); break; } } -#endif + +static void +l3dss1_message(struct l3_process *pc, u_char mt) +{ + struct sk_buff *skb; + u_char *p; + + if (!(skb = l3_alloc_skb(4))) + return; + p = skb_put(skb, 4); + MsgHead(p, pc->callref, mt); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3dss1_message_cause(struct l3_process *pc, u_char mt, u_char cause) +{ + struct sk_buff *skb; + u_char tmp[16]; + u_char *p = tmp; + int l; + + MsgHead(p, pc->callref, mt); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = cause | 0x80; + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3dss1_status_send(struct l3_process *pc, u_char pr, void *arg) +{ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb; + + MsgHead(p, pc->callref, MT_STATUS); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = pc->para.cause | 0x80; + + *p++ = IE_CALL_STATE; + *p++ = 0x1; + *p++ = pc->state & 0x3f; + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) +{ + /* This routine is called if here was no SETUP made (checks in dss1up and in + * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code + * MT_STATUS_ENQUIRE in the NULL state is handled too + */ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb; + + switch (pc->para.cause) { + case 81: /* invalid callreference */ + case 88: /* incomp destination */ + case 96: /* mandory IE missing */ + case 100: /* invalid IE contents */ + case 101: /* incompatible Callstate */ + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = pc->para.cause | 0x80; + break; + default: + printk(KERN_ERR "HiSax l3dss1_msg_without_setup wrong cause %d\n", + pc->para.cause); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + dss1_release_l3_process(pc); +} + +static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; +static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1}; +static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_CONNECT_PN, + IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; +static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, -1}; +static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY, + IE_PROGRESS, IE_DISPLAY, IE_USER_USER, -1}; +static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, + IE_CALLED_PN, -1}; +static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS | + IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; +static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, IE_USER_USER, -1}; +/* a RELEASE_COMPLETE with errors don't require special actions +static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_USER_USER, -1}; +*/ +static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, + IE_DISPLAY, -1}; +static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER | IE_MANDATORY, + IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS, + IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_CALLING_PN, + IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_LLC, IE_HLC, + IE_USER_USER, -1}; +static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY, + IE_PROGRESS, IE_DISPLAY, -1}; +static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE | + IE_MANDATORY, IE_DISPLAY, -1}; +static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1}; +static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_DISPLAY, IE_FACILITY, -1}; +static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; +/* not used + * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY, + * IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; + * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1}; + * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND | + * IE_MANDATORY, -1}; + */ +static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1}; +static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1}; +static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1}; + +struct ie_len { + int ie; + int len; +}; + +static +struct ie_len max_ie_len[] = { + {IE_SEGMENT, 4}, + {IE_BEARER, 12}, + {IE_CAUSE, 32}, + {IE_CALL_ID, 10}, + {IE_CALL_STATE, 3}, + {IE_CHANNEL_ID, 34}, + {IE_FACILITY, 255}, + {IE_PROGRESS, 4}, + {IE_NET_FAC, 255}, + {IE_NOTIFY, 3}, + {IE_DISPLAY, 82}, + {IE_DATE, 8}, + {IE_KEYPAD, 34}, + {IE_SIGNAL, 3}, + {IE_INFORATE, 6}, + {IE_E2E_TDELAY, 11}, + {IE_TDELAY_SEL, 5}, + {IE_PACK_BINPARA, 3}, + {IE_PACK_WINSIZE, 4}, + {IE_PACK_SIZE, 4}, + {IE_CUG, 7}, + {IE_REV_CHARGE, 3}, + {IE_CALLING_PN, 24}, + {IE_CALLING_SUB, 23}, + {IE_CALLED_PN, 24}, + {IE_CALLED_SUB, 23}, + {IE_REDIR_NR, 255}, + {IE_TRANS_SEL, 255}, + {IE_RESTART_IND, 3}, + {IE_LLC, 18}, + {IE_HLC, 5}, + {IE_USER_USER, 131}, + {-1,0}, +}; + +static int +getmax_ie_len(u_char ie) { + int i = 0; + while (max_ie_len[i].ie != -1) { + if (max_ie_len[i].ie == ie) + return(max_ie_len[i].len); + i++; + } + return(255); +} + +static int +ie_in_set(struct l3_process *pc, u_char ie, int *checklist) { + int ret = 1; + + while (*checklist != -1) { + if ((*checklist & 0xff) == ie) { + if (ie & 0x80) + return(-ret); + else + return(ret); + } + ret++; + checklist++; + } + return(0); +} static int -l3dss1_check_messagetype_validity(int mt) +check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist) +{ + int *cl = checklist; + u_char mt; + u_char *p, ie; + int l, newpos, oldpos; + int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0; + + p = skb->data; + /* skip cr */ + p++; + l = (*p++) & 0xf; + p += l; + mt = *p++; + oldpos = 0; +/* shift codeset procedure not implemented in the moment */ + while ((p - skb->data) < skb->len) { + if ((newpos = ie_in_set(pc, *p, cl))) { + if (newpos > 0) { + if (newpos < oldpos) + err_seq++; + else + oldpos = newpos; + } + } else { + if (ie_in_set(pc, *p, comp_required)) + err_compr++; + else + err_ureg++; + } + ie = *p++; + if (ie & 0x80) { + l = 1; + } else { + l = *p++; + p += l; + l += 2; + } + if (l > getmax_ie_len(ie)) + err_len++; + } + if (err_compr | err_ureg | err_len | err_seq) { + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check_infoelements mt %x %d/%d/%d/%d", + mt, err_compr, err_ureg, err_len, err_seq); + if (err_compr) + return(ERR_IE_COMPREHENSION); + if (err_ureg) + return(ERR_IE_UNRECOGNIZED); + if (err_len) + return(ERR_IE_LENGTH); + if (err_seq) + return(ERR_IE_SEQUENCE); + } + return(0); +} + +/* verify if a message type exists and contain no IE error */ +static int +l3dss1_check_messagetype_validity(struct l3_process *pc, int mt, void *arg) { -/* verify if a message type exists */ switch (mt) { case MT_ALERTING: case MT_CALL_PROCEEDING: case MT_CONNECT: case MT_CONNECT_ACKNOWLEDGE: + case MT_DISCONNECT: + case MT_INFORMATION: + case MT_FACILITY: + case MT_NOTIFY: case MT_PROGRESS: + case MT_RELEASE: + case MT_RELEASE_COMPLETE: case MT_SETUP: case MT_SETUP_ACKNOWLEDGE: - case MT_RESUME: case MT_RESUME_ACKNOWLEDGE: case MT_RESUME_REJECT: - case MT_SUSPEND: case MT_SUSPEND_ACKNOWLEDGE: case MT_SUSPEND_REJECT: case MT_USER_INFORMATION: - case MT_DISCONNECT: - case MT_RELEASE: - case MT_RELEASE_COMPLETE: case MT_RESTART: case MT_RESTART_ACKNOWLEDGE: - case MT_SEGMENT: case MT_CONGESTION_CONTROL: - case MT_INFORMATION: - case MT_FACILITY: - case MT_NOTIFY: case MT_STATUS: case MT_STATUS_ENQUIRY: + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) OK", mt); + break; + case MT_RESUME: /* RESUME only in user->net */ + case MT_SUSPEND: /* SUSPEND only in user->net */ + default: + if (pc->debug & (L3_DEB_CHECK | L3_DEB_WARN)) + l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) fail", mt); + pc->para.cause = 97; + l3dss1_status_send(pc, 0, NULL); return(1); + } + return(0); +} + +static void +l3dss1_std_ie_err(struct l3_process *pc, int ret) { + + if (pc->debug & L3_DEB_CHECK) + l3_debug(pc->st, "check_infoelements ret %d", ret); + switch(ret) { + case 0: + break; + case ERR_IE_COMPREHENSION: + pc->para.cause = 96; + l3dss1_status_send(pc, 0, NULL); + break; + case ERR_IE_UNRECOGNIZED: + pc->para.cause = 99; + l3dss1_status_send(pc, 0, NULL); + break; + case ERR_IE_LENGTH: + pc->para.cause = 100; + l3dss1_status_send(pc, 0, NULL); + break; + case ERR_IE_SEQUENCE: default: - return(0); + break; } +} + +static int +l3dss1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) { + u_char *p; + + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + p++; + if (*p != 1) { /* len for BRI = 1 */ + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong chid len %d", *p); + return (-2); + } + p++; + if (*p & 0x60) { /* only base rate interface */ + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong chid %x", *p); + return (-3); + } + return(*p & 0x3); + } else + return(-1); +} + +static int +l3dss1_get_cause(struct l3_process *pc, struct sk_buff *skb) { + u_char l, i=0; + u_char *p; + + p = skb->data; + pc->para.cause = 31; + pc->para.loc = 0; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + l = *p++; + if (l>30) + return(1); + if (l) { + pc->para.loc = *p++; + l--; + } else { + return(2); + } + if (l && !(pc->para.loc & 0x80)) { + l--; + p++; /* skip recommendation */ + } + if (l) { + pc->para.cause = *p++; + l--; + if (!(pc->para.cause & 0x80)) + return(3); + } else + return(4); + while (l && (i<6)) { + pc->para.diag[i++] = *p++; + l--; + } + } else + return(-1); return(0); } static void -l3dss1_message(struct l3_process *pc, u_char mt) +l3dss1_msg_with_uus(struct l3_process *pc, u_char cmd) { struct sk_buff *skb; - u_char *p; + u_char tmp[16+40]; + u_char *p = tmp; + int l; - if (!(skb = l3_alloc_skb(4))) + MsgHead(p, pc->callref, cmd); + + if (pc->prot.dss1.uus1_data[0]) + { *p++ = IE_USER_USER; /* UUS info element */ + *p++ = strlen(pc->prot.dss1.uus1_data) + 1; + *p++ = 0x04; /* IA5 chars */ + strcpy(p,pc->prot.dss1.uus1_data); + p += strlen(pc->prot.dss1.uus1_data); + pc->prot.dss1.uus1_data[0] = '\0'; + } + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) return; - p = skb_put(skb, 4); - MsgHead(p, pc->callref, mt); + memcpy(skb_put(skb, l), tmp, l); l3_msg(pc->st, DL_DATA | REQUEST, skb); -} +} /* l3dss1_msg_with_uus */ static void l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg) { StopAllL3Timer(pc); newl3state(pc, 19); - l3dss1_message(pc, MT_RELEASE); + if (!pc->prot.dss1.uus1_data[0]) + l3dss1_message(pc, MT_RELEASE); + else + l3dss1_msg_with_uus(pc, MT_RELEASE); L3AddTimer(&pc->timer, T308, CC_T308_1); } static void l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - int cause = -1; + int ret; - p = skb->data; - pc->para.loc = 0; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - if (*p++ == 2) - pc->para.loc = *p++; - cause = *p & 0x7f; - } - dev_kfree_skb(skb); + if ((ret = l3dss1_get_cause(pc, skb))>0) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret); + } else if (ret < 0) + pc->para.cause = NO_CAUSE; StopAllL3Timer(pc); - pc->para.cause = cause; newl3state(pc, 0); pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); - release_l3_process(pc); + dss1_release_l3_process(pc); } #if EXT_BEARER_CAPS @@ -574,9 +1250,8 @@ DecodeSI2(struct sk_buff *skb) break; case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption - - return DecodeSyncParams(176, p[5]); // V.120 - + if (p[1] > 3) + return DecodeSyncParams(176, p[5]); // V.120 break; } } @@ -594,6 +1269,7 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, u_char tmp[128]; u_char *p = tmp; u_char channel = 0; + u_char send_keypad; u_char screen = 0x80; u_char *teln; u_char *msn; @@ -603,13 +1279,17 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, MsgHead(p, pc->callref, MT_SETUP); + teln = pc->para.setup.phone; + send_keypad = (strchr(teln,'*') || strchr(teln,'#')) ? 1 : 0; /* * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 */ #if HISAX_EURO_SENDCOMPLETE - *p++ = 0xa1; /* complete indicator */ + if (!send_keypad) + *p++ = 0xa1; /* complete indicator */ #endif - switch (pc->para.setup.si1) { + if (!send_keypad) + switch (pc->para.setup.si1) { case 1: /* Telephony */ *p++ = 0x4; /* BC-IE-code */ *p++ = 0x3; /* Length */ @@ -625,12 +1305,26 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ *p++ = 0x90; /* Circuit-Mode 64kbps */ break; - } + } + else { *p++ = 0x4; /* assumptions for bearer services with keypad */ + *p++ = 0x3; + *p++ = 0x80; + *p++ = 0x90; + *p++ = 0xa3; + *p++ = 0x18; /* no specific channel */ + *p++ = 0x01; + *p++ = 0x83; + *p++ = 0x2C; /* IE keypad */ + *p++ = strlen(teln); + while (*teln) + *p++ = (*teln++) & 0x7F; + } + + /* * What about info2? Mapping to High-Layer-Compatibility? */ - teln = pc->para.setup.phone; - if (*teln) { + if ((*teln) && (!send_keypad)) { /* parse number for special things */ if (!isdigit(*teln)) { switch (0x5f & *teln) { @@ -650,7 +1344,8 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, case 'D': screen = 0x80; break; - default: + + default: if (pc->debug & L3_DEB_WARN) l3_debug(pc->st, "Wrong MSN Code"); break; @@ -703,24 +1398,33 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, } else sp++; } - *p++ = 0x70; - *p++ = strlen(teln) + 1; - /* Classify as AnyPref. */ - *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ - while (*teln) - *p++ = *teln++ & 0x7f; - - if (sub) { - *sub++ = '.'; - *p++ = 0x71; /* Called party subaddress */ - *p++ = strlen(sub) + 2; - *p++ = 0x80; /* NSAP coded */ - *p++ = 0x50; /* local IDI format */ - while (*sub) - *p++ = *sub++ & 0x7f; - } + + if (!send_keypad) { + *p++ = 0x70; + *p++ = strlen(teln) + 1; + /* Classify as AnyPref. */ + *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ + while (*teln) + *p++ = *teln++ & 0x7f; + + if (sub) { + *sub++ = '.'; + *p++ = 0x71; /* Called party subaddress */ + *p++ = strlen(sub) + 2; + *p++ = 0x80; /* NSAP coded */ + *p++ = 0x50; /* local IDI format */ + while (*sub) + *p++ = *sub++ & 0x7f; + } + } #if EXT_BEARER_CAPS - if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 + if (send_keypad) { /* special handling independant of si2 */ + *p++ = 0x7c; + *p++ = 0x03; + *p++ = 0x80; + *p++ = 0x90; + *p++ = 0xa3; + } else if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 *p++ = 0x7c; *p++ = 0x04; @@ -767,74 +1471,136 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, static void l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - + int id, ret; + + if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) { + if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer with wrong chid %x", id); + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + pc->para.bchannel = id; + } else if (1 == pc->state) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer wrong chid (ret %d)", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } L3DelTimer(&pc->timer); - p = skb->data; - if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { - pc->para.bchannel = p[2] & 0x3; - if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) - l3_debug(pc->st, "setup answer without bchannel"); - } else if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup answer without bchannel"); - dev_kfree_skb(skb); newl3state(pc, 3); L3AddTimer(&pc->timer, T310, CC_T310); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - + int id, ret; + + if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) { + if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer with wrong chid %x", id); + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + pc->para.bchannel = id; + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer wrong chid (ret %d)", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } L3DelTimer(&pc->timer); - p = skb->data; - if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { - pc->para.bchannel = p[2] & 0x3; - if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) - l3_debug(pc->st, "setup answer without bchannel"); - } else if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup answer without bchannel"); - dev_kfree_skb(skb); newl3state(pc, 2); L3AddTimer(&pc->timer, T304, CC_T304); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - int cause = -1; + u_char *p; + int ret; + u_char cause = 0; StopAllL3Timer(pc); - p = skb->data; - pc->para.loc = 0; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - if (*p++ == 2) - pc->para.loc = *p++; - cause = *p & 0x7f; - } - dev_kfree_skb(skb); + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "DISC get_cause ret(%d)", ret); + if (ret < 0) + cause = 96; + else if (ret > 0) + cause = 100; + } + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) + l3dss1_parse_facility(pc->st, pc, pc->callref, p); + ret = check_infoelements(pc, skb, ie_DISCONNECT); + if (ERR_IE_COMPREHENSION == ret) + cause = 96; + else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret)) + cause = 99; + ret = pc->state; newl3state(pc, 12); - pc->para.cause = cause; - pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); + if (cause) + newl3state(pc, 19); + if (11 != ret) + pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); + else if (!cause) + l3dss1_release_req(pc, pr, NULL); + if (cause) { + l3dss1_message_cause(pc, MT_RELEASE, cause); + L3AddTimer(&pc->timer, T308, CC_T308_1); + } } static void l3dss1_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; + int ret; - dev_kfree_skb(skb); + ret = check_infoelements(pc, skb, ie_CONNECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } L3DelTimer(&pc->timer); /* T310 */ newl3state(pc, 10); pc->para.chargeinfo = 0; + /* here should inserted COLP handling KKe */ + if (ret) + l3dss1_std_ie_err(pc, ret); pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); } @@ -842,143 +1608,139 @@ static void l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; + int ret; - dev_kfree_skb(skb); + ret = check_infoelements(pc, skb, ie_ALERTING); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } L3DelTimer(&pc->timer); /* T304 */ newl3state(pc, 4); + if (ret) + l3dss1_std_ie_err(pc, ret); pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); } static void -l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) -{ - /* This routine is called if here was no SETUP made (checks in dss1up and in - * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code - * MT_STATUS_ENQUIRE in the NULL state is handled too - */ - u_char tmp[16]; - u_char *p = tmp; - int l; - struct sk_buff *skb; - - switch (pc->para.cause) { - case 81: /* 0x51 invalid callreference */ - case 88: /* 0x58 incomp destination */ - case 96: /* 0x60 mandory IE missing */ - case 101: /* 0x65 incompatible Callstate */ - MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); - *p++ = IE_CAUSE; - *p++ = 0x2; - *p++ = 0x80; - *p++ = pc->para.cause | 0x80; - break; - default: - printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n"); - return; - } - l = p - tmp; - if (!(skb = l3_alloc_skb(l))) - return; - memcpy(skb_put(skb, l), tmp, l); - l3_msg(pc->st, DL_DATA | REQUEST, skb); - release_l3_process(pc); -} - -static void l3dss1_setup(struct l3_process *pc, u_char pr, void *arg) { - u_char *p, *ptmp[8]; - int i; + u_char *p; int bcfound = 0; char tmp[80]; struct sk_buff *skb = arg; + int id; + int err = 0; - /* ETS 300-104 1.3.4 and 1.3.5 - * we need to detect unknown inform. element from 0 to 7 + /* + * Bearer Capabilities */ p = skb->data; - for (i = 0; i < 8; i++) - ptmp[i] = skb->data; - if (findie(ptmp[1], skb->len, 0x01, 0) - || findie(ptmp[2], skb->len, 0x02, 0) - || findie(ptmp[3], skb->len, 0x03, 0) - || findie(ptmp[5], skb->len, 0x05, 0) - || findie(ptmp[6], skb->len, 0x06, 0) - || findie(ptmp[7], skb->len, 0x07, 0)) { - /* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE - * cause 0x60 - */ - pc->para.cause = 0x60; - dev_kfree_skb(skb); + /* only the first occurence 'll be detected ! */ + if ((p = findie(p, skb->len, 0x04, 0))) { + if ((p[1] < 2) || (p[1] > 11)) + err = 1; + else { + pc->para.setup.si2 = 0; + switch (p[2] & 0x7f) { + case 0x00: /* Speech */ + case 0x10: /* 3.1 Khz audio */ + pc->para.setup.si1 = 1; + break; + case 0x08: /* Unrestricted digital information */ + pc->para.setup.si1 = 7; +/* JIM, 05.11.97 I wanna set service indicator 2 */ +#if EXT_BEARER_CAPS + pc->para.setup.si2 = DecodeSI2(skb); +#endif + break; + case 0x09: /* Restricted digital information */ + pc->para.setup.si1 = 2; + break; + case 0x11: + /* Unrestr. digital information with + * tones/announcements ( or 7 kHz audio + */ + pc->para.setup.si1 = 3; + break; + case 0x18: /* Video */ + pc->para.setup.si1 = 4; + break; + default: + err = 2; + break; + } + switch (p[3] & 0x7f) { + case 0x40: /* packed mode */ + pc->para.setup.si1 = 8; + break; + case 0x10: /* 64 kbit */ + case 0x11: /* 2*64 kbit */ + case 0x13: /* 384 kbit */ + case 0x15: /* 1536 kbit */ + case 0x17: /* 1920 kbit */ + pc->para.moderate = p[3] & 0x7f; + break; + default: + err = 3; + break; + } + } + if (pc->debug & L3_DEB_SI) + l3_debug(pc->st, "SI=%d, AI=%d", + pc->para.setup.si1, pc->para.setup.si2); + if (err) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)", + p[1], p[2], p[3]); + pc->para.cause = 100; + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bearer capabilities"); + /* ETS 300-104 1.3.3 */ + pc->para.cause = 96; l3dss1_msg_without_setup(pc, pr, NULL); return; } /* * Channel Identification */ - p = skb->data; - if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { - pc->para.bchannel = p[2] & 0x3; - if (pc->para.bchannel) + if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) { + if ((pc->para.bchannel = id)) { + if ((3 == id) && (0x10 == pc->para.moderate)) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup with wrong chid %x", + id); + pc->para.cause = 100; + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } bcfound++; - else if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup without bchannel"); + } else + { if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel, call waiting"); + bcfound++; + } } else { if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup without bchannel"); - pc->para.cause = 0x60; - dev_kfree_skb(skb); + l3_debug(pc->st, "setup with wrong chid ret %d", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; l3dss1_msg_without_setup(pc, pr, NULL); return; } - /* - * Bearer Capabilities - */ - p = skb->data; - if ((p = findie(p, skb->len, 0x04, 0))) { - pc->para.setup.si2 = 0; - switch (p[2] & 0x1f) { - case 0x00: - /* Speech */ - case 0x10: - /* 3.1 Khz audio */ - pc->para.setup.si1 = 1; - break; - case 0x08: - /* Unrestricted digital information */ - pc->para.setup.si1 = 7; -/* JIM, 05.11.97 I wanna set service indicator 2 */ -#if EXT_BEARER_CAPS - pc->para.setup.si2 = DecodeSI2(skb); - printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n", - pc->para.setup.si1, pc->para.setup.si2); -#endif - break; - case 0x09: - /* Restricted digital information */ - pc->para.setup.si1 = 2; - break; - case 0x11: - /* Unrestr. digital information with tones/announcements */ - pc->para.setup.si1 = 3; - break; - case 0x18: - /* Video */ - pc->para.setup.si1 = 4; - break; - default: - pc->para.setup.si1 = 0; - } - } else { - if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup without bearer capabilities"); - /* ETS 300-104 1.3.3 */ - pc->para.cause = 0x60; - dev_kfree_skb(skb); + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_SETUP); + if (ERR_IE_COMPREHENSION == err) { + pc->para.cause = 96; l3dss1_msg_without_setup(pc, pr, NULL); return; } - p = skb->data; if ((p = findie(p, skb->len, 0x70, 0))) iecpy(pc->para.setup.eazmsn, p, 1); @@ -1020,62 +1782,28 @@ l3dss1_setup(struct l3_process *pc, u_char pr, void *arg) } else if (pc->debug & L3_DEB_WARN) l3_debug(pc->st, "wrong calling subaddress"); } - dev_kfree_skb(skb); - - if (bcfound) { - if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) { - l3_debug(pc->st, "non-digital call: %s -> %s", - pc->para.setup.phone, pc->para.setup.eazmsn); - } - if ((pc->para.setup.si1 != 7) && - test_bit(FLG_PTP, &pc->st->l2.flag)) { - pc->para.cause = 0x58; - l3dss1_msg_without_setup(pc, pr, NULL); - return; - } - newl3state(pc, 6); - pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); - } else - release_l3_process(pc); + newl3state(pc, 6); + if (err) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, err); + pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); } static void l3dss1_reset(struct l3_process *pc, u_char pr, void *arg) { - release_l3_process(pc); -} - -static void -l3dss1_setup_rsp(struct l3_process *pc, u_char pr, - void *arg) -{ - newl3state(pc, 8); - l3dss1_message(pc, MT_CONNECT); - L3DelTimer(&pc->timer); - L3AddTimer(&pc->timer, T313, CC_T313); -} - -static void -l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg) -{ - struct sk_buff *skb = arg; - - dev_kfree_skb(skb); - newl3state(pc, 10); - L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); + dss1_release_l3_process(pc); } static void l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; - u_char tmp[16]; + u_char tmp[16+40]; u_char *p = tmp; int l; - u_char cause = 0x10; + u_char cause = 16; - if (pc->para.cause > 0) + if (pc->para.cause != NO_CAUSE) cause = pc->para.cause; StopAllL3Timer(pc); @@ -1087,6 +1815,15 @@ l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) *p++ = 0x80; *p++ = cause | 0x80; + if (pc->prot.dss1.uus1_data[0]) + { *p++ = IE_USER_USER; /* UUS info element */ + *p++ = strlen(pc->prot.dss1.uus1_data) + 1; + *p++ = 0x04; /* IA5 chars */ + strcpy(p,pc->prot.dss1.uus1_data); + p += strlen(pc->prot.dss1.uus1_data); + pc->prot.dss1.uus1_data[0] = '\0'; + } + l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; @@ -1097,15 +1834,49 @@ l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) } static void +l3dss1_setup_rsp(struct l3_process *pc, u_char pr, + void *arg) +{ + if (!pc->para.bchannel) + { if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "D-chan connect for waiting call"); + l3dss1_disconnect_req(pc, pr, arg); + return; + } + newl3state(pc, 8); + l3dss1_message(pc, MT_CONNECT); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); +} + +static void +l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int ret; + + ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } + newl3state(pc, 10); + L3DelTimer(&pc->timer); + if (ret) + l3dss1_std_ie_err(pc, ret); + pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); +} + +static void l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; u_char *p = tmp; int l; - u_char cause = 0x95; + u_char cause = 21; - if (pc->para.cause > 0) + if (pc->para.cause != NO_CAUSE) cause = pc->para.cause; MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); @@ -1113,7 +1884,7 @@ l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) *p++ = IE_CAUSE; *p++ = 0x2; *p++ = 0x80; - *p++ = cause; + *p++ = cause | 0x80; l = p - tmp; if (!(skb = l3_alloc_skb(l))) @@ -1122,38 +1893,41 @@ l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) l3_msg(pc->st, DL_DATA | REQUEST, skb); pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); - release_l3_process(pc); + dss1_release_l3_process(pc); } static void l3dss1_release(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - int cause = -1; + u_char *p; + int ret, cause=0; - p = skb->data; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - if (*p++ == 2) - pc->para.loc = *p++; - cause = *p & 0x7f; - } - p = skb->data; - if ((p = findie(p, skb->len, IE_FACILITY, 0))) { -#if HISAX_DE_AOC - l3dss1_parse_facility(pc, p); -#else - p = NULL; -#endif - } - dev_kfree_skb(skb); StopAllL3Timer(pc); - pc->para.cause = cause; - l3dss1_message(pc, MT_RELEASE_COMPLETE); + if ((ret = l3dss1_get_cause(pc, skb))>0) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "REL get_cause ret(%d)", ret); + } else if (ret<0) + pc->para.cause = NO_CAUSE; + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) { + l3dss1_parse_facility(pc->st, pc, pc->callref, p); + } + if ((ret<0) && (pc->state != 11)) + cause = 96; + else if (ret>0) + cause = 100; + ret = check_infoelements(pc, skb, ie_RELEASE); + if (ERR_IE_COMPREHENSION == ret) + cause = 96; + else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause)) + cause = 99; + if (cause) + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); + else + l3dss1_message(pc, MT_RELEASE_COMPLETE); pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); - release_l3_process(pc); + dss1_release_l3_process(pc); } static void @@ -1161,66 +1935,358 @@ l3dss1_alert_req(struct l3_process *pc, u_char pr, void *arg) { newl3state(pc, 7); - l3dss1_message(pc, MT_ALERTING); + if (!pc->prot.dss1.uus1_data[0]) + l3dss1_message(pc, MT_ALERTING); + else + l3dss1_msg_with_uus(pc, MT_ALERTING); } static void -l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg) +l3dss1_proceed_req(struct l3_process *pc, u_char pr, + void *arg) { - u_char tmp[16]; - u_char *p = tmp; - int l; - struct sk_buff *skb = arg; + newl3state(pc, 9); + l3dss1_message(pc, MT_CALL_PROCEEDING); + pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); +} - dev_kfree_skb(skb); +/********************************************/ +/* deliver a incoming display message to HL */ +/********************************************/ +static void +l3dss1_deliver_display(struct l3_process *pc, int pr, u_char *infp) +{ u_char len; + isdn_ctrl ic; + struct IsdnCardState *cs; + char *p; + + if (*infp++ != IE_DISPLAY) return; + if ((len = *infp++) > 80) return; /* total length <= 82 */ + if (!pc->chan) return; + + p = ic.parm.display; + while (len--) + *p++ = *infp++; + *p = '\0'; + ic.command = ISDN_STAT_DISPLAY; + cs = pc->st->l1.hardware; + ic.driver = cs->myid; + ic.arg = pc->chan->chan; + cs->iif.statcallb(&ic); +} /* l3dss1_deliver_display */ - MsgHead(p, pc->callref, MT_STATUS); - *p++ = IE_CAUSE; - *p++ = 0x2; - *p++ = 0x80; - *p++ = 0x9E; /* answer status enquire */ +static void +l3dss1_progress(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int err = 0; + u_char *p; - *p++ = 0x14; /* CallState */ - *p++ = 0x1; - *p++ = pc->state & 0x3f; + if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) { + if (p[1] != 2) { + err = 1; + pc->para.cause = 100; + } else if (p[2] & 0x60) { + switch (p[2]) { + case 0x80: + case 0x81: + case 0x82: + case 0x84: + case 0x85: + case 0x87: + case 0x8a: + switch (p[3]) { + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x88: + break; + default: + err = 2; + pc->para.cause = 100; + break; + } + break; + default: + err = 3; + pc->para.cause = 100; + break; + } + } + } else { + pc->para.cause = 96; + err = 4; + } + if (err) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "progress error %d", err); + l3dss1_status_send(pc, pr, NULL); + return; + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_PROGRESS); + if (err) + l3dss1_std_ie_err(pc, err); + if (ERR_IE_COMPREHENSION != err) + pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc); +} - l = p - tmp; - if (!(skb = l3_alloc_skb(l))) +static void +l3dss1_notify(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + int err = 0; + u_char *p; + + if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) { + if (p[1] != 1) { + err = 1; + pc->para.cause = 100; + } else { + switch (p[2]) { + case 0x80: + case 0x81: + case 0x82: + break; + default: + pc->para.cause = 100; + err = 2; + break; + } + } + } else { + pc->para.cause = 96; + err = 3; + } + if (err) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "notify error %d", err); + l3dss1_status_send(pc, pr, NULL); return; - memcpy(skb_put(skb, l), tmp, l); - l3_msg(pc->st, DL_DATA | REQUEST, skb); + } + /* Now we are on none mandatory IEs */ + err = check_infoelements(pc, skb, ie_NOTIFY); + if (err) + l3dss1_std_ie_err(pc, err); + if (ERR_IE_COMPREHENSION != err) + pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc); } static void -l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg) +l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg) { - /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1... - if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62 */ - u_char tmp[16]; - u_char *p = tmp; - int l; + int ret; struct sk_buff *skb = arg; - dev_kfree_skb(skb); + ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY); + l3dss1_std_ie_err(pc, ret); + pc->para.cause = 30; /* response to STATUS_ENQUIRY */ + l3dss1_status_send(pc, pr, NULL); +} - MsgHead(p, pc->callref, MT_STATUS); +static void +l3dss1_information(struct l3_process *pc, u_char pr, void *arg) +{ + int ret; + struct sk_buff *skb = arg; - *p++ = IE_CAUSE; - *p++ = 0x2; - *p++ = 0x80; - *p++ = 0x62 | 0x80; /* status sending */ + ret = check_infoelements(pc, skb, ie_INFORMATION); + l3dss1_std_ie_err(pc, ret); +} - *p++ = 0x14; /* CallState */ - *p++ = 0x1; - *p++ = pc->state & 0x3f; +/******************************/ +/* handle deflection requests */ +/******************************/ +static void l3dss1_redir_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[128]; + u_char *p = tmp; + u_char *subp; + u_char len_phone = 0; + u_char len_sub = 0; + int l; + + + strcpy(pc->prot.dss1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */ + if (!pc->chan->setup.phone[0]) + { pc->para.cause = -1; + l3dss1_disconnect_req(pc,pr,arg); /* disconnect immediately */ + return; + } /* only uus */ + + if (pc->prot.dss1.invoke_id) + free_invoke_id(pc->st,pc->prot.dss1.invoke_id); + + if (!(pc->prot.dss1.invoke_id = new_invoke_id(pc->st))) + return; + + MsgHead(p, pc->callref, MT_FACILITY); + + for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */ + if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subadress element */ + + *p++ = 0x1c; /* Facility info element */ + *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */ + *p++ = 0x91; /* remote operations protocol */ + *p++ = 0xa1; /* invoke component */ + + *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */ + *p++ = 0x02; /* invoke id tag, integer */ + *p++ = 0x01; /* length */ + *p++ = pc->prot.dss1.invoke_id; /* invoke id */ + *p++ = 0x02; /* operation value tag, integer */ + *p++ = 0x01; /* length */ + *p++ = 0x0D; /* Call Deflect */ + + *p++ = 0x30; /* sequence phone number */ + *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */ + + *p++ = 0x30; /* Deflected to UserNumber */ + *p++ = len_phone+2+len_sub; /* length */ + *p++ = 0x80; /* NumberDigits */ + *p++ = len_phone; /* length */ + for (l = 0; l < len_phone; l++) + *p++ = pc->chan->setup.phone[l]; + + if (len_sub) + { *p++ = 0x04; /* called party subadress */ + *p++ = len_sub - 2; + while (*subp) *p++ = *subp++; + } + + *p++ = 0x01; /* screening identifier */ + *p++ = 0x01; + *p++ = pc->chan->setup.screen; l = p - tmp; - if (!(skb = l3_alloc_skb(l))) - return; + if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - l3_msg(pc->st, DL_DATA | REQUEST, skb); -} + + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} /* l3dss1_redir_req */ + +/********************************************/ +/* handle deflection request in early state */ +/********************************************/ +static void l3dss1_redir_req_early(struct l3_process *pc, u_char pr, void *arg) +{ + l3dss1_proceed_req(pc,pr,arg); + l3dss1_redir_req(pc,pr,arg); +} /* l3dss1_redir_req_early */ + +/***********************************************/ +/* handle special commands for this protocol. */ +/* Examples are call independant services like */ +/* remote operations with dummy callref. */ +/***********************************************/ +static int l3dss1_cmd_global(struct PStack *st, isdn_ctrl *ic) +{ u_char id; + u_char temp[265]; + u_char *p = temp; + int i, l, proc_len; + struct sk_buff *skb; + struct l3_process *pc = NULL; + + switch (ic->arg) + { case DSS1_CMD_INVOKE: + if (ic->parm.dss1_io.datalen < 0) return(-2); /* invalid parameter */ + + for (proc_len = 1, i = ic->parm.dss1_io.proc >> 8; i; i++) + i = i >> 8; /* add one byte */ + l = ic->parm.dss1_io.datalen + proc_len + 8; /* length excluding ie header */ + if (l > 255) + return(-2); /* too long */ + + if (!(id = new_invoke_id(st))) + return(0); /* first get a invoke id -> return if no available */ + + i = -1; + MsgHead(p, i, MT_FACILITY); /* build message head */ + *p++ = 0x1C; /* Facility IE */ + *p++ = l; /* length of ie */ + *p++ = 0x91; /* remote operations */ + *p++ = 0xA1; /* invoke */ + *p++ = l - 3; /* length of invoke */ + *p++ = 0x02; /* invoke id tag */ + *p++ = 0x01; /* length is 1 */ + *p++ = id; /* invoke id */ + *p++ = 0x02; /* operation */ + *p++ = proc_len; /* length of operation */ + + for (i = proc_len; i; i--) + *p++ = (ic->parm.dss1_io.proc >> (i-1)) & 0xFF; + memcpy(p, ic->parm.dss1_io.data, ic->parm.dss1_io.datalen); /* copy data */ + l = (p - temp) + ic->parm.dss1_io.datalen; /* total length */ + + if (ic->parm.dss1_io.timeout > 0) + if (!(pc = dss1_new_l3_process(st, -1))) + { free_invoke_id(st, id); + return(-2); + } + pc->prot.dss1.ll_id = ic->parm.dss1_io.ll_id; /* remember id */ + pc->prot.dss1.proc = ic->parm.dss1_io.proc; /* and procedure */ + + if (!(skb = l3_alloc_skb(l))) + { free_invoke_id(st, id); + if (pc) dss1_release_l3_process(pc); + return(-2); + } + memcpy(skb_put(skb, l), temp, l); + + if (pc) + { pc->prot.dss1.invoke_id = id; /* remember id */ + L3AddTimer(&pc->timer, ic->parm.dss1_io.timeout, CC_TDSS1_IO | REQUEST); + } + + l3_msg(st, DL_DATA | REQUEST, skb); + ic->parm.dss1_io.hl_id = id; /* return id */ + return(0); + + case DSS1_CMD_INVOKE_ABORT: + if ((pc = l3dss1_search_dummy_proc(st, ic->parm.dss1_io.hl_id))) + { L3DelTimer(&pc->timer); /* remove timer */ + dss1_release_l3_process(pc); + return(0); + } + else + { l3_debug(st, "l3dss1_cmd_global abort unknown id"); + return(-2); + } + break; + + default: + l3_debug(st, "l3dss1_cmd_global unknown cmd 0x%lx", ic->arg); + return(-1); + } /* switch ic-> arg */ + return(-1); +} /* l3dss1_cmd_global */ + +static void +l3dss1_io_timer(struct l3_process *pc) +{ isdn_ctrl ic; + struct IsdnCardState *cs = pc->st->l1.hardware; + + L3DelTimer(&pc->timer); /* remove timer */ + + ic.driver = cs->myid; + ic.command = ISDN_STAT_PROT; + ic.arg = DSS1_STAT_INVOKE_ERR; + ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id; + ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id; + ic.parm.dss1_io.proc = pc->prot.dss1.proc; + ic.parm.dss1_io.timeout= -1; + ic.parm.dss1_io.datalen = 0; + ic.parm.dss1_io.data = NULL; + free_invoke_id(pc->st, pc->prot.dss1.invoke_id); + pc->prot.dss1.invoke_id = 0; /* reset id */ + + cs->iif.statcallb(&ic); + + dss1_release_l3_process(pc); +} /* l3dss1_io_timer */ static void l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg) @@ -1241,13 +2307,18 @@ l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg) */ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); - release_l3_process(pc); + dss1_release_l3_process(pc); } else { pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc); } } static void +l3dss1_dummy(struct l3_process *pc, u_char pr, void *arg) +{ +} + +static void l3dss1_t303(struct l3_process *pc, u_char pr, void *arg) { if (pc->N303 > 0) { @@ -1256,8 +2327,9 @@ l3dss1_t303(struct l3_process *pc, u_char pr, void *arg) l3dss1_setup_req(pc, pr, arg); } else { L3DelTimer(&pc->timer); + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102); pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc); - release_l3_process(pc); + dss1_release_l3_process(pc); } } @@ -1265,7 +2337,7 @@ static void l3dss1_t304(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0xE6; + pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); @@ -1278,18 +2350,18 @@ l3dss1_t305(struct l3_process *pc, u_char pr, void *arg) u_char *p = tmp; int l; struct sk_buff *skb; - u_char cause = 0x90; + u_char cause = 16; L3DelTimer(&pc->timer); - if (pc->para.cause > 0) - cause = pc->para.cause | 0x80; + if (pc->para.cause != NO_CAUSE) + cause = pc->para.cause; MsgHead(p, pc->callref, MT_RELEASE); *p++ = IE_CAUSE; *p++ = 0x2; *p++ = 0x80; - *p++ = cause; + *p++ = cause | 0x80; l = p - tmp; if (!(skb = l3_alloc_skb(l))) @@ -1304,7 +2376,7 @@ static void l3dss1_t310(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0xE6; + pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); } @@ -1313,7 +2385,7 @@ static void l3dss1_t313(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0xE6; + pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); } @@ -1332,14 +2404,14 @@ l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); - release_l3_process(pc); + dss1_release_l3_process(pc); } static void l3dss1_t318(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0x66; /* Timer expiry */ + pc->para.cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); newl3state(pc, 19); @@ -1351,7 +2423,7 @@ static void l3dss1_t319(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->para.cause = 0x66; /* Timer expiry */ + pc->para.cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); newl3state(pc, 10); @@ -1361,69 +2433,80 @@ static void l3dss1_restart(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_DLRL | INDICATION, pc); - release_l3_process(pc); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + dss1_release_l3_process(pc); } static void l3dss1_status(struct l3_process *pc, u_char pr, void *arg) { u_char *p; - char tmp[64], *t; - int l; struct sk_buff *skb = arg; - int cause, callState; - - cause = callState = -1; - p = skb->data; - t = tmp; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - l = *p++; - t += sprintf(t, "Status CR %x Cause:", pc->callref); - while (l--) { - cause = *p; - t += sprintf(t, " %2x", *p++); - } - } else - sprintf(t, "Status CR %x no Cause", pc->callref); - l3_debug(pc->st, tmp); - p = skb->data; - t = tmp; - t += sprintf(t, "Status state %x ", pc->state); - if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + int ret; + u_char cause = 0, callState = 0; + + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "STATUS get_cause ret(%d)",ret); + if (ret < 0) + cause = 96; + else if (ret > 0) + cause = 100; + } + if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) { p++; if (1 == *p++) { callState = *p; - t += sprintf(t, "peer state %x", *p); + if (!ie_in_set(pc, *p, l3_valid_states)) + cause = 100; } else - t += sprintf(t, "peer state len error"); + cause = 100; } else - sprintf(t, "no peer state"); - l3_debug(pc->st, tmp); - if (((cause & 0x7f) == 0x6f) && (callState == 0)) { + cause = 96; + if (!cause) { /* no error before */ + ret = check_infoelements(pc, skb, ie_STATUS); + if (ERR_IE_COMPREHENSION == ret) + cause = 96; + else if (ERR_IE_UNRECOGNIZED == ret) + cause = 99; + } + if (cause) { + u_char tmp; + + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause); + tmp = pc->para.cause; + pc->para.cause = cause; + l3dss1_status_send(pc, 0, NULL); + if (cause == 99) + pc->para.cause = tmp; + else + return; + } + cause = pc->para.cause; + if (((cause & 0x7f) == 111) && (callState == 0)) { /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... - * if received MT_STATUS with cause == 0x6f and call + * if received MT_STATUS with cause == 111 and call * state == 0, then we must set down layer 3 */ - l3dss1_release_ind(pc, pr, arg); - } else - dev_kfree_skb(skb); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + dss1_release_l3_process(pc); + } } static void l3dss1_facility(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - - p = skb->data; - if ((p = findie(p, skb->len, IE_FACILITY, 0))) { -#if HISAX_DE_AOC - l3dss1_parse_facility(pc, p); -#else - p = NULL; -#endif + int ret; + + ret = check_infoelements(pc, skb, ie_FACILITY); + l3dss1_std_ie_err(pc, ret); + { + u_char *p; + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) + l3dss1_parse_facility(pc->st, pc, pc->callref, p); } } @@ -1438,14 +2521,14 @@ l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg) MsgHead(p, pc->callref, MT_SUSPEND); - *p++ = IE_CALLID; + *p++ = IE_CALL_ID; l = *msg++; if (l && (l <= 10)) { /* Max length 10 octets */ *p++ = l; for (i = 0; i < l; i++) *p++ = *msg++; } else { - l3_debug(pc->st, "SUS wrong CALLID len %d", l); + l3_debug(pc->st, "SUS wrong CALL_ID len %d", l); return; } l = p - tmp; @@ -1461,34 +2544,45 @@ static void l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; + int ret; L3DelTimer(&pc->timer); newl3state(pc, 0); - dev_kfree_skb(skb); - pc->para.cause = -1; + pc->para.cause = NO_CAUSE; pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc); - release_l3_process(pc); + /* We don't handle suspend_ack for IE errors now */ + if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE))) + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "SUSPACK check ie(%d)",ret); + dss1_release_l3_process(pc); } static void l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - int cause = -1; + int ret; - L3DelTimer(&pc->timer); - p = skb->data; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - if (*p++ == 2) - pc->para.loc = *p++; - cause = *p & 0x7f; + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret); + if (ret < 0) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; } - dev_kfree_skb(skb); - pc->para.cause = cause; + ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } + L3DelTimer(&pc->timer); pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); newl3state(pc, 10); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); } static void @@ -1502,14 +2596,14 @@ l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg) MsgHead(p, pc->callref, MT_RESUME); - *p++ = IE_CALLID; + *p++ = IE_CALL_ID; l = *msg++; if (l && (l <= 10)) { /* Max length 10 octets */ *p++ = l; for (i = 0; i < l; i++) *p++ = *msg++; } else { - l3_debug(pc->st, "RES wrong CALLID len %d", l); + l3_debug(pc->st, "RES wrong CALL_ID len %d", l); return; } l = p - tmp; @@ -1518,48 +2612,70 @@ l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg) memcpy(skb_put(skb, l), tmp, l); l3_msg(pc->st, DL_DATA | REQUEST, skb); newl3state(pc, 17); - L3AddTimer(&pc->timer, T319, CC_T319); + L3AddTimer(&pc->timer, T318, CC_T318); } static void l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - + int id, ret; + + if ((id = l3dss1_get_channel_id(pc, skb)) > 0) { + if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "resume ack with wrong chid %x", id); + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; + } + pc->para.bchannel = id; + } else if (1 == pc->state) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "resume ack without chid (ret %d)", id); + pc->para.cause = 96; + l3dss1_status_send(pc, pr, NULL); + return; + } + ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } L3DelTimer(&pc->timer); - p = skb->data; - if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { - pc->para.bchannel = p[2] & 0x3; - if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) - l3_debug(pc->st, "resume ack without bchannel"); - } else if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "resume ack without bchannel"); - dev_kfree_skb(skb); pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc); newl3state(pc, 10); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); } static void l3dss1_resume_rej(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; struct sk_buff *skb = arg; - int cause = -1; + int ret; - L3DelTimer(&pc->timer); - p = skb->data; - if ((p = findie(p, skb->len, IE_CAUSE, 0))) { - p++; - if (*p++ == 2) - pc->para.loc = *p++; - cause = *p & 0x7f; + if ((ret = l3dss1_get_cause(pc, skb))) { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret); + if (ret < 0) + pc->para.cause = 96; + else + pc->para.cause = 100; + l3dss1_status_send(pc, pr, NULL); + return; } - dev_kfree_skb(skb); - pc->para.cause = cause; - newl3state(pc, 0); + ret = check_infoelements(pc, skb, ie_RESUME_REJECT); + if (ERR_IE_COMPREHENSION == ret) { + l3dss1_std_ie_err(pc, ret); + return; + } + L3DelTimer(&pc->timer); pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); - release_l3_process(pc); + newl3state(pc, 0); + if (ret) /* STATUS for none mandatory IE errors after actions are taken */ + l3dss1_std_ie_err(pc, ret); + dss1_release_l3_process(pc); } static void @@ -1589,7 +2705,6 @@ l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg) if (pc->st->l3.debug) l3_debug(pc->st, "Restart for channel %d", chan); } - dev_kfree_skb(skb); newl3state(pc, 2); up = pc->st->l3.proc; while (up) { @@ -1616,6 +2731,41 @@ l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg) newl3state(pc, 0); l3_msg(pc->st, DL_DATA | REQUEST, skb); } + +static void +l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg) +{ + pc->para.cause = 0x29; /* Temporary failure */ + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3dss1_dl_release(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 0); + pc->para.cause = 0x1b; /* Destination out of order */ + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + release_l3_process(pc); +} + +static void +l3dss1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T309, CC_T309); + l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL); +} + +static void +l3dss1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + + pc->para.cause = 0x1F; /* normal, unspecified */ + l3dss1_status_send(pc, 0, NULL); +} + /* *INDENT-OFF* */ static struct stateentry downstatelist[] = { @@ -1623,24 +2773,32 @@ static struct stateentry downstatelist[] = CC_SETUP | REQUEST, l3dss1_setup_req}, {SBIT(0), CC_RESUME | REQUEST, l3dss1_resume_req}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, {SBIT(12), CC_RELEASE | REQUEST, l3dss1_release_req}, {ALL_STATES, - CC_DLRL | REQUEST, l3dss1_reset}, - {ALL_STATES, CC_RESTART | REQUEST, l3dss1_restart}, {SBIT(6), CC_IGNORE | REQUEST, l3dss1_reset}, {SBIT(6), CC_REJECT | REQUEST, l3dss1_reject_req}, {SBIT(6), + CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req}, + {SBIT(6) | SBIT(9), CC_ALERTING | REQUEST, l3dss1_alert_req}, - {SBIT(6) | SBIT(7), + {SBIT(6) | SBIT(7) | SBIT(9), CC_SETUP | RESPONSE, l3dss1_setup_rsp}, {SBIT(10), CC_SUSPEND | REQUEST, l3dss1_suspend_req}, + {SBIT(6), + CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req}, + {SBIT(7) | SBIT(9), + CC_REDIR | REQUEST, l3dss1_redir_req}, + {SBIT(6), + CC_REDIR | REQUEST, l3dss1_redir_req_early}, + {SBIT(9) | SBIT(25), + CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, {SBIT(1), CC_T303, l3dss1_t303}, {SBIT(2), @@ -1659,6 +2817,8 @@ static struct stateentry downstatelist[] = CC_T308_1, l3dss1_t308_1}, {SBIT(19), CC_T308_2, l3dss1_t308_2}, + {SBIT(10), + CC_T309, l3dss1_dl_release}, }; #define DOWNSLLEN \ @@ -1674,37 +2834,36 @@ static struct stateentry datastatelist[] = MT_STATUS, l3dss1_release_ind}, {ALL_STATES, MT_STATUS, l3dss1_status}, - {SBIT(0) | SBIT(6), + {SBIT(0), MT_SETUP, l3dss1_setup}, + {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | + SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_SETUP, l3dss1_dummy}, {SBIT(1) | SBIT(2), MT_CALL_PROCEEDING, l3dss1_call_proc}, - {SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), - MT_CALL_PROCEEDING, l3dss1_status_req}, {SBIT(1), MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, - {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), - MT_SETUP_ACKNOWLEDGE, l3dss1_status_req}, - {SBIT(1) | SBIT(2) | SBIT(3), + {SBIT(2) | SBIT(3), MT_ALERTING, l3dss1_alerting}, - {SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), - MT_ALERTING, l3dss1_status_req}, + {SBIT(2) | SBIT(3), + MT_PROGRESS, l3dss1_progress}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_INFORMATION, l3dss1_information}, + {SBIT(10) | SBIT(11) | SBIT(15), + MT_NOTIFY, l3dss1_notify}, {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | - SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | - SBIT(11) | SBIT(12) | SBIT(15) /* | SBIT(17) | SBIT(19)*/, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25), MT_RELEASE, l3dss1_release}, {SBIT(19), MT_RELEASE, l3dss1_release_ind}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(15), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25), MT_DISCONNECT, l3dss1_disconnect}, - {SBIT(11), - MT_DISCONNECT, l3dss1_release_req}, + {SBIT(19), + MT_DISCONNECT, l3dss1_dummy}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), MT_CONNECT, l3dss1_connect}, - {SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), - MT_CONNECT, l3dss1_status_req}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(11) | SBIT(19), - MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(8), MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, {SBIT(15), @@ -1715,8 +2874,6 @@ static struct stateentry datastatelist[] = MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack}, {SBIT(17), MT_RESUME_REJECT, l3dss1_resume_rej}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(19), - MT_INVALID, l3dss1_status_req}, }; #define DATASLLEN \ @@ -1734,26 +2891,56 @@ static struct stateentry globalmes_list[] = }; #define GLOBALM_LEN \ (sizeof(globalmes_list) / sizeof(struct stateentry)) + +static struct stateentry manstatelist[] = +{ + {SBIT(2), + DL_ESTABLISH | INDICATION, l3dss1_dl_reset}, + {SBIT(10), + DL_ESTABLISH | CONFIRM, l3dss1_dl_reest_status}, + {SBIT(10), + DL_RELEASE | INDICATION, l3dss1_dl_reestablish}, + {ALL_STATES, + DL_RELEASE | INDICATION, l3dss1_dl_release}, +}; + +#define MANSLLEN \ + (sizeof(manstatelist) / sizeof(struct stateentry)) /* *INDENT-ON* */ static void global_handler(struct PStack *st, int mt, struct sk_buff *skb) { + u_char tmp[16]; + u_char *p = tmp; + int l; int i; struct l3_process *proc = st->l3.global; + proc->callref = skb->data[2]; /* cr flag */ for (i = 0; i < GLOBALM_LEN; i++) if ((mt == globalmes_list[i].primitive) && ((1 << proc->state) & globalmes_list[i].state)) break; if (i == GLOBALM_LEN) { - dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { l3_debug(st, "dss1 global state %d mt %x unhandled", proc->state, mt); } - return; + MsgHead(p, proc->callref, MT_STATUS); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = 81 |0x80; /* invalid cr */ + *p++ = 0x14; /* CallState */ + *p++ = 0x1; + *p++ = proc->state & 0x3f; + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(proc->st, DL_DATA | REQUEST, skb); } else { if (st->l3.debug & L3_DEB_STATE) { l3_debug(st, "dss1 global %d mt %x", @@ -1768,6 +2955,7 @@ dss1up(struct PStack *st, int pr, void *arg) { int i, mt, cr, cause, callState; char *ptr; + u_char *p; struct sk_buff *skb = arg; struct l3_process *proc; @@ -1782,23 +2970,57 @@ dss1up(struct PStack *st, int pr, void *arg) l3_msg(st, pr, arg); return; break; + default: + printk(KERN_ERR "HiSax dss1up unknown pr=%04x\n", pr); + return; + } + if (skb->len < 3) { + l3_debug(st, "dss1up frame too short(%d)", skb->len); + idev_kfree_skb(skb, FREE_READ); + return; } + if (skb->data[0] != PROTO_DIS_EURO) { if (st->l3.debug & L3_DEB_PROTERR) { l3_debug(st, "dss1up%sunexpected discriminator %x message len %d", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", skb->data[0], skb->len); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } cr = getcallref(skb->data); + if (skb->len < ((skb->data[1] & 0x0f) + 3)) { + l3_debug(st, "dss1up frame too short(%d)", skb->len); + idev_kfree_skb(skb, FREE_READ); + return; + } mt = skb->data[skb->data[1] + 2]; - if (!cr) { /* Global CallRef */ - global_handler(st, mt, skb); + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "dss1up cr %d", cr); + if (cr == -2) { /* wrong Callref */ + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "dss1up wrong Callref"); + idev_kfree_skb(skb, FREE_READ); return; } else if (cr == -1) { /* Dummy Callref */ - dev_kfree_skb(skb); + if (mt == MT_FACILITY) + if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) { + l3dss1_parse_facility(st, NULL, + (pr == (DL_DATA | INDICATION)) ? -1 : -2, p); + idev_kfree_skb(skb, FREE_READ); + return; + } + if (st->l3.debug & L3_DEB_WARN) + l3_debug(st, "dss1up dummy Callref (no facility msg or ie)"); + idev_kfree_skb(skb, FREE_READ); + return; + } else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) || + (((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) { /* Global CallRef */ + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "dss1up Global CallRef"); + global_handler(st, mt, skb); + idev_kfree_skb(skb, FREE_READ); return; } else if (!(proc = getl3proc(st, cr))) { /* No transaction process exist, that means no call with @@ -1806,12 +3028,19 @@ dss1up(struct PStack *st, int pr, void *arg) */ if (mt == MT_SETUP) { /* Setup creates a new transaction process */ - if (!(proc = new_l3_process(st, cr))) { + if (skb->data[2] & 0x80) { + /* Setup with wrong CREF flag */ + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "dss1up wrong CRef flag"); + idev_kfree_skb(skb, FREE_READ); + return; + } + if (!(proc = dss1_new_l3_process(st, cr))) { /* May be to answer with RELEASE_COMPLETE and * CAUSE 0x2f "Resource unavailable", but this * need a new_l3_process too ... arghh */ - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } } else if (mt == MT_STATUS) { @@ -1829,62 +3058,60 @@ dss1up(struct PStack *st, int pr, void *arg) ptr++; callState = *ptr; } - if (callState == 0) { - /* ETS 300-104 part 2.4.1 - * if setup has not been made and a message type - * MT_STATUS is received with call state == 0, - * we must send nothing - */ - dev_kfree_skb(skb); - return; - } else { + /* ETS 300-104 part 2.4.1 + * if setup has not been made and a message type + * MT_STATUS is received with call state == 0, + * we must send nothing + */ + if (callState != 0) { /* ETS 300-104 part 2.4.2 * if setup has not been made and a message type * MT_STATUS is received with call state != 0, * we must send MT_RELEASE_COMPLETE cause 101 */ - dev_kfree_skb(skb); - if ((proc = new_l3_process(st, cr))) { - proc->para.cause = 0x65; /* 101 */ + if ((proc = dss1_new_l3_process(st, cr))) { + proc->para.cause = 101; l3dss1_msg_without_setup(proc, 0, NULL); } - return; } + idev_kfree_skb(skb, FREE_READ); + return; } else if (mt == MT_RELEASE_COMPLETE) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } else { /* ETS 300-104 part 2 * if setup has not been made and a message type * (except MT_SETUP and RELEASE_COMPLETE) is received, * we must send MT_RELEASE_COMPLETE cause 81 */ - dev_kfree_skb(skb); - if ((proc = new_l3_process(st, cr))) { - proc->para.cause = 0x51; /* 81 */ + idev_kfree_skb(skb, FREE_READ); + if ((proc = dss1_new_l3_process(st, cr))) { + proc->para.cause = 81; l3dss1_msg_without_setup(proc, 0, NULL); } return; } - } else if (!l3dss1_check_messagetype_validity(mt)) { - /* ETS 300-104 7.4.2, 8.4.2, 10.3.2, 11.4.2, 12.4.2, 13.4.2, - * 14.4.2... - * if setup has been made and invalid message type is received, - * we must send MT_STATUS cause 0x62 - */ - mt = MT_INVALID; /* sorry, not clean, but do the right thing ;-) */ } + if (l3dss1_check_messagetype_validity(proc, mt, skb)) { + idev_kfree_skb(skb, FREE_READ); + return; + } + if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) + l3dss1_deliver_display(proc, pr, p); /* Display IE included */ for (i = 0; i < DATASLLEN; i++) if ((mt == datastatelist[i].primitive) && ((1 << proc->state) & datastatelist[i].state)) break; if (i == DATASLLEN) { - dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { - l3_debug(st, "dss1up%sstate %d mt %x unhandled", + l3_debug(st, "dss1up%sstate %d mt %#x unhandled", (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", proc->state, mt); } - return; + if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) { + proc->para.cause = 101; + l3dss1_status_send(proc, pr, skb); + } } else { if (st->l3.debug & L3_DEB_STATE) { l3_debug(st, "dss1up%sstate %d mt %x", @@ -1893,6 +3120,8 @@ dss1up(struct PStack *st, int pr, void *arg) } datastatelist[i].rout(proc, pr, skb); } + idev_kfree_skb(skb, FREE_READ); + return; } static void @@ -1902,14 +3131,14 @@ dss1down(struct PStack *st, int pr, void *arg) struct l3_process *proc; struct Channel *chan; - if (((DL_ESTABLISH | REQUEST) == pr) || ((DL_RELEASE | REQUEST) == pr)) { + if ((DL_ESTABLISH | REQUEST) == pr) { l3_msg(st, pr, NULL); return; } else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) { chan = arg; cr = newcallref(); cr |= 0x80; - if ((proc = new_l3_process(st, cr))) { + if ((proc = dss1_new_l3_process(st, cr))) { proc->chan = chan; chan->proc = proc; proc->para.setup = chan->setup; @@ -1922,32 +3151,75 @@ dss1down(struct PStack *st, int pr, void *arg) printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr); return; } + + if ( pr == (CC_TDSS1_IO | REQUEST)) { + l3dss1_io_timer(proc); /* timer expires */ + return; + } + for (i = 0; i < DOWNSLLEN; i++) if ((pr == downstatelist[i].primitive) && ((1 << proc->state) & downstatelist[i].state)) break; if (i == DOWNSLLEN) { if (st->l3.debug & L3_DEB_STATE) { - l3_debug(st, "dss1down state %d prim %d unhandled", + l3_debug(st, "dss1down state %d prim %#x unhandled", proc->state, pr); } } else { if (st->l3.debug & L3_DEB_STATE) { - l3_debug(st, "dss1down state %d prim %d", + l3_debug(st, "dss1down state %d prim %#x", proc->state, pr); } downstatelist[i].rout(proc, pr, arg); } } +static void +dss1man(struct PStack *st, int pr, void *arg) +{ + int i; + struct l3_process *proc = arg; + + if (!proc) { + printk(KERN_ERR "HiSax dss1man without proc pr=%04x\n", pr); + return; + } + for (i = 0; i < MANSLLEN; i++) + if ((pr == manstatelist[i].primitive) && + ((1 << proc->state) & manstatelist[i].state)) + break; + if (i == MANSLLEN) { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d dss1man state %d prim %#x unhandled", + proc->callref & 0x7f, proc->state, pr); + } + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "cr %d dss1man state %d prim %#x", + proc->callref & 0x7f, proc->state, pr); + } + manstatelist[i].rout(proc, pr, arg); + } +} + void setstack_dss1(struct PStack *st) { char tmp[64]; + int i; st->lli.l4l3 = dss1down; + st->lli.l4l3_proto = l3dss1_cmd_global; st->l2.l2l3 = dss1up; + st->l3.l3ml3 = dss1man; st->l3.N303 = 1; + st->prot.dss1.last_invoke_id = 0; + st->prot.dss1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */ + i = 1; + while (i < 32) + st->prot.dss1.invoke_used[i++] = 0; + if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n"); } else { @@ -1957,6 +3229,8 @@ setstack_dss1(struct PStack *st) st->l3.global->debug = L3_DEB_WARN; st->l3.global->st = st; st->l3.global->N303 = 1; + st->l3.global->prot.dss1.invoke_id = 0; + L3InitTimer(st->l3.global, &st->l3.global->timer); } strcpy(tmp, dss1_revision); diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h index 10e612482..268b5376f 100644 --- a/drivers/isdn/hisax/l3dss1.h +++ b/drivers/isdn/hisax/l3dss1.h @@ -1,8 +1,11 @@ -/* $Id: l3dss1.h,v 1.6 1998/03/19 13:18:50 keil Exp $ +/* $Id: l3dss1.h,v 1.7 1999/07/01 08:12:02 keil Exp $ * * DSS1 (Euro) D-channel protocol defines * * $Log: l3dss1.h,v $ + * Revision 1.7 1999/07/01 08:12:02 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.6 1998/03/19 13:18:50 keil * Start of a CAPI like interface for supplementary Service * first service: SUSPEND @@ -25,10 +28,14 @@ * * */ + +#ifndef l3dss1_process + #define T303 4000 #define T304 30000 #define T305 30000 #define T308 4000 +#define T309 40000 #define T310 30000 #define T313 4000 #define T318 4000 @@ -38,39 +45,99 @@ * Message-Types */ -#define MT_ALERTING 0x01 -#define MT_CALL_PROCEEDING 0x02 -#define MT_CONNECT 0x07 -#define MT_CONNECT_ACKNOWLEDGE 0x0f -#define MT_PROGRESS 0x03 -#define MT_SETUP 0x05 -#define MT_SETUP_ACKNOWLEDGE 0x0d -#define MT_RESUME 0x26 -#define MT_RESUME_ACKNOWLEDGE 0x2e -#define MT_RESUME_REJECT 0x22 -#define MT_SUSPEND 0x25 -#define MT_SUSPEND_ACKNOWLEDGE 0x2d -#define MT_SUSPEND_REJECT 0x21 -#define MT_USER_INFORMATION 0x20 -#define MT_DISCONNECT 0x45 -#define MT_RELEASE 0x4d -#define MT_RELEASE_COMPLETE 0x5a -#define MT_RESTART 0x46 -#define MT_RESTART_ACKNOWLEDGE 0x4e -#define MT_SEGMENT 0x60 -#define MT_CONGESTION_CONTROL 0x79 -#define MT_INFORMATION 0x7b -#define MT_FACILITY 0x62 -#define MT_NOTIFY 0x6e -#define MT_STATUS 0x7d -#define MT_STATUS_ENQUIRY 0x75 +#define MT_ALERTING 0x01 +#define MT_CALL_PROCEEDING 0x02 +#define MT_CONNECT 0x07 +#define MT_CONNECT_ACKNOWLEDGE 0x0f +#define MT_PROGRESS 0x03 +#define MT_SETUP 0x05 +#define MT_SETUP_ACKNOWLEDGE 0x0d +#define MT_RESUME 0x26 +#define MT_RESUME_ACKNOWLEDGE 0x2e +#define MT_RESUME_REJECT 0x22 +#define MT_SUSPEND 0x25 +#define MT_SUSPEND_ACKNOWLEDGE 0x2d +#define MT_SUSPEND_REJECT 0x21 +#define MT_USER_INFORMATION 0x20 +#define MT_DISCONNECT 0x45 +#define MT_RELEASE 0x4d +#define MT_RELEASE_COMPLETE 0x5a +#define MT_RESTART 0x46 +#define MT_RESTART_ACKNOWLEDGE 0x4e +#define MT_SEGMENT 0x60 +#define MT_CONGESTION_CONTROL 0x79 +#define MT_INFORMATION 0x7b +#define MT_FACILITY 0x62 +#define MT_NOTIFY 0x6e +#define MT_STATUS 0x7d +#define MT_STATUS_ENQUIRY 0x75 + +#define IE_SEGMENT 0x00 +#define IE_BEARER 0x04 +#define IE_CAUSE 0x08 +#define IE_CALL_ID 0x10 +#define IE_CALL_STATE 0x14 +#define IE_CHANNEL_ID 0x18 +#define IE_FACILITY 0x1c +#define IE_PROGRESS 0x1e +#define IE_NET_FAC 0x20 +#define IE_NOTIFY 0x27 +#define IE_DISPLAY 0x28 +#define IE_DATE 0x29 +#define IE_KEYPAD 0x2c +#define IE_SIGNAL 0x34 +#define IE_INFORATE 0x40 +#define IE_E2E_TDELAY 0x42 +#define IE_TDELAY_SEL 0x43 +#define IE_PACK_BINPARA 0x44 +#define IE_PACK_WINSIZE 0x45 +#define IE_PACK_SIZE 0x46 +#define IE_CUG 0x47 +#define IE_REV_CHARGE 0x4a +#define IE_CONNECT_PN 0x4c +#define IE_CONNECT_SUB 0x4d +#define IE_CALLING_PN 0x6c +#define IE_CALLING_SUB 0x6d +#define IE_CALLED_PN 0x70 +#define IE_CALLED_SUB 0x71 +#define IE_REDIR_NR 0x74 +#define IE_TRANS_SEL 0x78 +#define IE_RESTART_IND 0x79 +#define IE_LLC 0x7c +#define IE_HLC 0x7d +#define IE_USER_USER 0x7e +#define IE_ESCAPE 0x7f +#define IE_SHIFT 0x90 +#define IE_MORE_DATA 0xa0 +#define IE_COMPLETE 0xa1 +#define IE_CONGESTION 0xb0 +#define IE_REPEAT 0xd0 + +#define IE_MANDATORY 0x0100 +/* mandatory not in every case */ +#define IE_MANDATORY_1 0x0200 + +#define ERR_IE_COMPREHENSION 1 +#define ERR_IE_UNRECOGNIZED -1 +#define ERR_IE_LENGTH -2 +#define ERR_IE_SEQUENCE -3 + +#else /* only l3dss1_process */ + +/* l3dss1 specific data in l3 process */ +typedef struct + { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */ + ulong ll_id; /* remebered ll id */ + u_char remote_operation; /* handled remote operation, 0 = not active */ + int proc; /* rememered procedure */ + ulong remote_result; /* result of remote operation for statcallb */ + char uus1_data[35]; /* data send during alerting or disconnect */ + } dss1_proc_priv; -#define MT_INVALID 0xff +/* l3dss1 specific data in protocol stack */ +typedef struct + { unsigned char last_invoke_id; /* last used value for invoking */ + unsigned char invoke_used[32]; /* 256 bits for 256 values */ + } dss1_stk_priv; -#define IE_BEARER 0x04 -#define IE_CAUSE 0x08 -#define IE_CALLID 0x10 -#define IE_FACILITY 0x1c -#define IE_CALL_STATE 0x14 -#define IE_CHANNEL_ID 0x18 -#define IE_RESTART_IND 0x79 +#endif /* only l3dss1_process */ diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c index d6ef0b630..6097fe547 100644 --- a/drivers/isdn/hisax/lmgr.c +++ b/drivers/isdn/hisax/lmgr.c @@ -1,4 +1,4 @@ -/* $Id: lmgr.c,v 1.5 1998/11/15 23:55:12 keil Exp $ +/* $Id: lmgr.c,v 1.6 1999/07/01 08:12:04 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * @@ -6,6 +6,9 @@ * Layermanagement module * * $Log: lmgr.c,v $ + * Revision 1.6 1999/07/01 08:12:04 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.5 1998/11/15 23:55:12 keil * changes from 2.0 * @@ -50,7 +53,7 @@ hisax_manager(struct PStack *st, int pr, void *arg) case (MDL_ERROR | INDICATION): Code = (long) arg; HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR", - "%c %s\n", (char)Code, + " %c %s", (char)Code, test_bit(FLG_LAPD, &st->l2.flag) ? "D-channel" : "B-channel"); if (test_bit(FLG_LAPD, &st->l2.flag)) diff --git a/drivers/isdn/hisax/md5sums.asc b/drivers/isdn/hisax/md5sums.asc index 49888c55d..481a7dc72 100644 --- a/drivers/isdn/hisax/md5sums.asc +++ b/drivers/isdn/hisax/md5sums.asc @@ -2,28 +2,30 @@ # This are valid md5sums for certificated HiSax driver. # The certification is valid only if the md5sums of all files match. -# The certification is valid only for ELSA QuickStep cards in the moment. +# The certification is valid only for ELSA QuickStep cards and +# Eicon Technology Diva 2.01 PCI cards in the moment. # Read ../../../Documentation/isdn/HiSax.cert for more informations. # -a273c532aec063574273ee519975cd9a isac.c -27c5c5bfa2ceabf02e2e6d686b03abde isdnl1.c -8c89ac659d3188ab997fb575da22b566 isdnl2.c -d0fa912aa284b8fd19fed86b65999f6f isdnl3.c -1bce120740b615006286ad9b2d7fcdcb tei.c -8845f88dd17917d9b58badeff1605057 callc.c -f3ec2a634f06074d16167aaba02b6dc1 cert.c -71840ec8189f42b0db86fb38e5e5984c l3dss1.c -1882de8bea921b9ccd98fbe77267aa04 l3_1tr6.c -3bd7af3a11693d028300278744d0da09 elsa.c +0cc164fadd4ec0e2983ec9735e209cbd isac.c +5fe8cb5526c78c91f61b0a94a423ea5d isdnl1.c +3b9522e8bf9e1c3e7848d729fc3dc05d isdnl2.c +f4184a50e35e5b568608e6cb7a693319 isdnl3.c +ef70f4269fdc2ca15100f9b776afaa0d tei.c +65be616dd9d0e06c788d4fdd0fe5fe0a callc.c +bf9605b36429898f7be6630034e83230 cert.c +97c5e31c2739665b9c2976a30ce0b357 l3dss1.c +b674eee9314a7cc413971c84003cf1d2 l3_1tr6.c +51b2ef1efb221bb09fd08ab28bd2c565 elsa.c +24cda374da44b57f6a1bb215424267b5 diva.c # end of md5sums -----BEGIN PGP SIGNATURE----- Version: 2.6.3i Charset: noconv -iQCVAwUBNrl5JDpxHvX/mS9tAQHm8wP+Nk64UJ2abdDG/igXZSrwcYhX/Kp7cxt9 -ccYp+aaur+pALA0lxwY3xcLt9u36fCYuTLHAVmQoiC9Vbemj37yzM2rUpz9nkw/7 -D6gLqZs2jxVpAwVVJgp0JwDONKXaRX6Lt2EPD9PTW6vxRWEu0HqGhM5hrtd/o4rV -mC1W7Wj13XM= -=LdhT +iQCVAwUBN8RgFjpxHvX/mS9tAQFzFQP/dOgnppDIm5ug1hnlWjQ/0BVurKEEJ64r +DYDHwkcog+0gVE/EB1A7WUDqpFEnj52OZeoVinCfdVuVjP8IkrAJ8dCONsnXjBXz +pzM+FunP1LFxuv2TVM0f642j98JxS8rObGWH8ZwY36P2QfNp47zorO2F9WvdCkuz +sxJUtMUOlQ8= +=8uEP -----END PGP SIGNATURE----- diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c index 4f1474624..fa85bbd27 100644 --- a/drivers/isdn/hisax/mic.c +++ b/drivers/isdn/hisax/mic.c @@ -1,4 +1,4 @@ -/* $Id: mic.c,v 1.7 1998/04/15 16:44:32 keil Exp $ +/* $Id: mic.c,v 1.8 1999/07/12 21:05:20 keil Exp $ * mic.c low level stuff for mic cards * @@ -8,6 +8,10 @@ * * * $Log: mic.c,v $ + * Revision 1.8 1999/07/12 21:05:20 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 1.7 1998/04/15 16:44:32 keil * new init code * @@ -40,7 +44,7 @@ extern const char *CardType[]; -const char *mic_revision = "$Revision: 1.7 $"; +const char *mic_revision = "$Revision: 1.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -158,7 +162,7 @@ static void mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "mic: Spurious interrupt!\n"); @@ -166,16 +170,12 @@ mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); if (val) { if (cs->debug & L1_DEB_HSCX) @@ -188,16 +188,12 @@ mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - if (stat & 1) { - writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF); - writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF); - writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0); - writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0); - } - if (stat & 2) { - writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0); } void @@ -218,9 +214,6 @@ mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_mic(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &mic_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: inithscx(cs); /* /RTSA := ISAC RST */ inithscxisac(cs, 3); @@ -231,7 +224,7 @@ mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __init setup_mic(struct IsdnCard *card) { int bytecnt; @@ -273,6 +266,7 @@ setup_mic(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &mic_card_msg; + cs->irq_func = &mic_interrupt; ISACVersion(cs, "mic:"); if (HscxVersion(cs, "mic:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c index 75696ad2c..d88442e37 100644 --- a/drivers/isdn/hisax/netjet.c +++ b/drivers/isdn/hisax/netjet.c @@ -1,4 +1,4 @@ -/* $Id: netjet.c,v 1.8 1998/11/15 23:55:14 keil Exp $ +/* $Id: netjet.c,v 1.13 1999/08/11 21:01:31 keil Exp $ * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards * @@ -6,8 +6,25 @@ * * Thanks to Traverse Technologie Australia for documents and informations * - * * $Log: netjet.c,v $ + * Revision 1.13 1999/08/11 21:01:31 keil + * new PCI codefix + * + * Revision 1.12 1999/08/10 16:02:00 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.11 1999/08/07 17:32:00 keil + * Asymetric buffers for improved ping times. Interframe spacing + * fix for NJ<->NJ thoughput. Matt Henderson - www.traverse.com.au + * + * + * Revision 1.10 1999/07/12 21:05:22 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.9 1999/07/01 08:12:05 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.8 1998/11/15 23:55:14 keil * changes from 2.0 * @@ -42,12 +59,23 @@ #include "hscx.h" #include "isdnl1.h" #include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif #include <linux/interrupt.h> #include <linux/ppp_defs.h> +#ifndef bus_to_virt +#define bus_to_virt (u_int *) +#endif + +#ifndef virt_to_bus +#define virt_to_bus (u_int) +#endif + extern const char *CardType[]; -const char *NETjet_revision = "$Revision: 1.8 $"; +const char *NETjet_revision = "$Revision: 1.13 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -83,7 +111,8 @@ const char *NETjet_revision = "$Revision: 1.8 $"; #define NETJET_IRQM0_WRITE_1 0x01 #define NETJET_IRQM0_WRITE_2 0x02 -#define NETJET_DMA_SIZE 512 +#define NETJET_DMA_TXSIZE 512 +#define NETJET_DMA_RXSIZE 128 #define HDLC_ZERO_SEARCH 0 #define HDLC_FLAG_SEARCH 1 @@ -211,7 +240,7 @@ mode_tiger(struct BCState *bcs, int mode, int bc) switch (mode) { case (L1_MODE_NULL): fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, bc, 0xff); + NETJET_DMA_TXSIZE, bc, 0xff); if (cs->debug & L1_DEB_HSCX) debugl1(cs, "Tiger stat rec %d/%d send %d", bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err, @@ -228,7 +257,7 @@ mode_tiger(struct BCState *bcs, int mode, int bc) break; case (L1_MODE_HDLC): fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, bc, 0xff); + NETJET_DMA_TXSIZE, bc, 0xff); bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH; bcs->hw.tiger.r_tot = 0; bcs->hw.tiger.r_bitcnt = 0; @@ -237,14 +266,14 @@ mode_tiger(struct BCState *bcs, int mode, int bc) bcs->hw.tiger.s_tot = 0; if (! cs->hw.njet.dmactrl) { fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, !bc, 0xff); + NETJET_DMA_TXSIZE, !bc, 0xff); cs->hw.njet.dmactrl = 1; byteout(cs->hw.njet.base + NETJET_DMACTRL, cs->hw.njet.dmactrl); byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x3f); } bcs->hw.tiger.sendp = bcs->hw.tiger.send; - bcs->hw.tiger.free = NETJET_DMA_SIZE; + bcs->hw.tiger.free = NETJET_DMA_TXSIZE; test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); break; } @@ -363,6 +392,7 @@ static int make_raw_data(struct BCState *bcs) { s_val |= 0x80; } bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + bcs->hw.tiger.sendbuf[s_cnt++] = 0xff; // NJ<->NJ thoughput bug fix } bcs->hw.tiger.sendcnt = s_cnt; bcs->tx_cnt -= bcs->tx_skb->len; @@ -376,6 +406,7 @@ static void got_frame(struct BCState *bcs, int count) { if (!(skb = dev_alloc_skb(count))) printk(KERN_WARNING "TIGER: receive out of memory\n"); else { + SET_SKB_FREE(skb); memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count); skb_queue_tail(&bcs->rqueue, skb); } @@ -393,7 +424,7 @@ static void read_raw(struct BCState *bcs, u_int *buf, int cnt){ int i; register u_char j; register u_char val; - u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -1; + u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_RXSIZE -1; register u_char state = bcs->hw.tiger.r_state; register u_char r_one = bcs->hw.tiger.r_one; register u_char r_val = bcs->hw.tiger.r_val; @@ -548,7 +579,7 @@ static void read_raw(struct BCState *bcs, u_int *buf, int cnt){ static void read_tiger(struct IsdnCardState *cs) { u_int *p; - int cnt = NETJET_DMA_SIZE/2; + int cnt = NETJET_DMA_RXSIZE/2; if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) { debugl1(cs,"tiger warn read double dma %x/%x", @@ -559,7 +590,7 @@ static void read_tiger(struct IsdnCardState *cs) { cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ); } if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1) - p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1; + p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1; else p = cs->bcs[0].hw.tiger.rec + cnt - 1; if (cs->bcs[0].mode == L1_MODE_HDLC) @@ -616,12 +647,12 @@ static void fill_dma(struct BCState *bcs) cnt = bcs->hw.tiger.s_end - p; if (cnt < 2) { p = bcs->hw.tiger.send + 1; - cnt = NETJET_DMA_SIZE/2 - 2; + cnt = NETJET_DMA_TXSIZE/2 - 2; } else { p++; p++; - if (cnt <= (NETJET_DMA_SIZE/2)) - cnt += NETJET_DMA_SIZE/2; + if (cnt <= (NETJET_DMA_TXSIZE/2)) + cnt += NETJET_DMA_TXSIZE/2; cnt--; cnt--; } @@ -674,12 +705,12 @@ static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { if (bcs->st->lli.l1writewakeup && (PACKET_NOACK != bcs->tx_skb->pkt_type)) bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; } test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); bcs->hw.tiger.free = cnt - s_cnt; - if (bcs->hw.tiger.free > (NETJET_DMA_SIZE/2)) + if (bcs->hw.tiger.free > (NETJET_DMA_TXSIZE/2)) test_and_set_bit(BC_FLG_HALF, &bcs->Flag); else { test_and_clear_bit(BC_FLG_HALF, &bcs->Flag); @@ -719,7 +750,7 @@ static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { } static void write_tiger(struct IsdnCardState *cs) { - u_int *p, cnt = NETJET_DMA_SIZE/2; + u_int *p, cnt = NETJET_DMA_TXSIZE/2; if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) { debugl1(cs,"tiger warn write double dma %x/%x", @@ -730,7 +761,7 @@ static void write_tiger(struct IsdnCardState *cs) { cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE); } if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE_1) - p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + p = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1; else p = cs->bcs[0].hw.tiger.send + cnt - 1; if (cs->bcs[0].mode == L1_MODE_HDLC) @@ -811,7 +842,7 @@ close_tigerstate(struct BCState *bcs) discard_queue(&bcs->rqueue); discard_queue(&bcs->squeue); if (bcs->tx_skb) { - dev_kfree_skb(bcs->tx_skb); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); bcs->tx_skb = NULL; test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); } @@ -858,45 +889,45 @@ setstack_tiger(struct PStack *st, struct BCState *bcs) } -void __init +void __init inittiger(struct IsdnCardState *cs) { - if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_TXSIZE * sizeof(unsigned int), GFP_KERNEL | GFP_DMA))) { printk(KERN_WARNING "HiSax: No memory for tiger.send\n"); return; } - cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE/2 - 1; - cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE/2 - 1; + cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1; cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send; cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq; cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end; - memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int)); + memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_TXSIZE * sizeof(unsigned int)); debugl1(cs, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send, - (u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1)); + (u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1)); outl(virt_to_bus(cs->bcs[0].hw.tiger.send), cs->hw.njet.base + NETJET_DMA_READ_START); outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq), cs->hw.njet.base + NETJET_DMA_READ_IRQ); outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end), cs->hw.njet.base + NETJET_DMA_READ_END); - if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_RXSIZE * sizeof(unsigned int), GFP_KERNEL | GFP_DMA))) { printk(KERN_WARNING "HiSax: No memory for tiger.rec\n"); return; } debugl1(cs, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec, - (u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1)); + (u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1)); cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec; - memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int)); + memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_RXSIZE * sizeof(unsigned int)); outl(virt_to_bus(cs->bcs[0].hw.tiger.rec), cs->hw.njet.base + NETJET_DMA_WRITE_START); - outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE/2 - 1), + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE/2 - 1), cs->hw.njet.base + NETJET_DMA_WRITE_IRQ); - outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1), + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1), cs->hw.njet.base + NETJET_DMA_WRITE_END); debugl1(cs, "tiger: dmacfg %x/%x pulse=%d", inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), @@ -1030,9 +1061,6 @@ NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_netjet(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &netjet_interrupt, - I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); case CARD_INIT: inittiger(cs); clear_pending_isac_ints(cs); @@ -1046,23 +1074,31 @@ NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } - - +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_netjet __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif -int __init +int __init setup_netjet(struct IsdnCard *card) { int bytecnt; struct IsdnCardState *cs = card->cs; char tmp[64]; - +#if CONFIG_PCI +#ifndef COMPAT_HAS_NEW_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr, found; +#endif +#endif strcpy(tmp, NETjet_revision); printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_NETJET) return(0); test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "Netjet: no PCI bus present\n"); return(0); @@ -1071,22 +1107,57 @@ setup_netjet(struct IsdnCard *card) PCI_NETJET_ID, dev_netjet))) { cs->irq = dev_netjet->irq; if (!cs->irq) { + printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n"); + return(0); + } + cs->hw.njet.base = get_pcibase(dev_netjet, 0) + & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.njet.base) { + printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); + return(0); + } + } else { + printk(KERN_WARNING "NETjet: No PCI card found\n"); + return(0); + } +#else + found = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH, + PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + found = 1; + else + continue; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + if (found) + break; + } + if (!found) { + printk(KERN_WARNING "NETjet: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n"); return(0); } - cs->hw.njet.base = dev_netjet->base_address[0] & - PCI_BASE_ADDRESS_IO_MASK; - if (!cs->hw.njet.base) { + if (!pci_ioaddr) { printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); return(0); } - cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA; - cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF; + cs->hw.njet.base = pci_ioaddr & PCI_BASE_ADDRESS_IO_MASK; + cs->irq = pci_irq; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA; + cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF; bytecnt = 256; - } else { - printk(KERN_WARNING "NETjet: No PCI card found\n"); - return(0); - } #else printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n"); printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n"); @@ -1114,6 +1185,8 @@ setup_netjet(struct IsdnCard *card) cs->BC_Write_Reg = &dummywr; cs->BC_Send_Data = &fill_dma; cs->cardmsg = &NETjet_card_msg; + cs->irq_func = &netjet_interrupt; + cs->irq_flags |= SA_SHIRQ; ISACVersion(cs, "NETjet:"); return (1); } diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c index 4a4f0d7c3..7574a8e8f 100644 --- a/drivers/isdn/hisax/niccy.c +++ b/drivers/isdn/hisax/niccy.c @@ -1,4 +1,4 @@ -/* $Id: niccy.c,v 1.4 1998/04/16 19:16:48 keil Exp $ +/* $Id: niccy.c,v 1.8 1999/08/11 21:01:33 keil Exp $ * niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and * compatible (SAGEM cybermodem) @@ -8,6 +8,19 @@ * Thanks to Dr. Neuhaus and SAGEM for informations * * $Log: niccy.c,v $ + * Revision 1.8 1999/08/11 21:01:33 keil + * new PCI codefix + * + * Revision 1.7 1999/08/10 16:02:04 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.6 1999/07/12 21:05:23 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.5 1999/07/01 08:12:07 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.4 1998/04/16 19:16:48 keil * need config.h * @@ -27,9 +40,12 @@ #include "hscx.h" #include "isdnl1.h" #include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif extern const char *CardType[]; -const char *niccy_revision = "$Revision: 1.4 $"; +const char *niccy_revision = "$Revision: 1.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -154,7 +170,7 @@ static void niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "Niccy: Spurious interrupt!\n"); @@ -169,16 +185,12 @@ niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); if (val) { if (cs->debug & L1_DEB_HSCX) @@ -191,16 +203,12 @@ niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - if (stat & 1) { - writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); - writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF); - writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); - writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); - } - if (stat & 2) { - writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); - } + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); } void @@ -235,8 +243,6 @@ niccy_reset(struct IsdnCardState *cs) static int niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int imode; - switch (mt) { case CARD_RESET: niccy_reset(cs); @@ -244,14 +250,6 @@ niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_niccy(cs); return(0); - case CARD_SETIRQ: - if (cs->subtyp == NICCY_PCI) - imode = I4L_IRQ_FLAG | SA_SHIRQ; - else - imode = I4L_IRQ_FLAG; - return(request_irq(cs->irq, &niccy_interrupt, - imode, "HiSax", cs)); - break; case CARD_INIT: niccy_reset(cs); return(0); @@ -261,9 +259,13 @@ niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *niccy_dev __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif -int __init +int __init setup_niccy(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; @@ -304,40 +306,85 @@ setup_niccy(struct IsdnCard *card) } else { #if CONFIG_PCI u_int pci_ioaddr; - +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "Niccy: no PCI bus present\n"); return(0); } - cs->subtyp = 0; if ((niccy_dev = pci_find_device(PCI_VENDOR_DR_NEUHAUS, PCI_NICCY_ID, niccy_dev))) { /* get IRQ */ if (!niccy_dev->irq) { printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); + return(0); + } + cs->irq = niccy_dev->irq; + if (!get_pcibase(niccy_dev, 0)) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); + return(0); + } + cs->hw.niccy.cfg_reg = get_pcibase(niccy_dev, 0) & PCI_BASE_ADDRESS_IO_MASK; + if (!get_pcibase(niccy_dev, 1)) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr = get_pcibase(niccy_dev, 1) & PCI_BASE_ADDRESS_IO_MASK; + cs->subtyp = NICCY_PCI; + } else { + printk(KERN_WARNING "Niccy: No PCI card found\n"); return(0); } - cs->irq = niccy_dev->irq; - if (!niccy_dev->base_address[0]) { +#else + u_char pci_bus, pci_device_fn, pci_irq; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS, + PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = NICCY_PCI; + else + continue; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO pci AMCC address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + if (!pci_ioaddr) { printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); + return(0); + } + cs->hw.niccy.cfg_reg = pci_ioaddr & ~3 ; + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Niccy: No PCI card found\n"); return(0); } - cs->hw.niccy.cfg_reg = niccy_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; - if (!niccy_dev->base_address[1]) { + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); + return(0); + } + if (!pci_ioaddr) { printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); return(0); } - pci_ioaddr = niccy_dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->irq = pci_irq; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->irq_flags |= SA_SHIRQ; cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA; cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR; cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA; cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR; - cs->subtyp = NICCY_PCI; - } else { - printk(KERN_WARNING "Niccy: No PCI card found\n"); - return(0); - } if (check_region((cs->hw.niccy.isac), 4)) { printk(KERN_WARNING "HiSax: %s data port %x-%x already in use\n", @@ -376,6 +423,7 @@ setup_niccy(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &niccy_card_msg; + cs->irq_func = &niccy_interrupt; ISACVersion(cs, "Niccy:"); if (HscxVersion(cs, "Niccy:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/s0box.c b/drivers/isdn/hisax/s0box.c index 479880bb3..d8dfb2ee3 100644 --- a/drivers/isdn/hisax/s0box.c +++ b/drivers/isdn/hisax/s0box.c @@ -1,4 +1,4 @@ -/* $Id: s0box.c,v 2.1 1998/04/15 16:38:24 keil Exp $ +/* $Id: s0box.c,v 2.2 1999/07/12 21:05:25 keil Exp $ * s0box.c low level stuff for Creatix S0BOX * @@ -13,7 +13,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *s0box_revision = "$Revision: 2.1 $"; +const char *s0box_revision = "$Revision: 2.2 $"; static inline void writereg(unsigned int padr, signed int addr, u_char off, u_char val) { @@ -148,9 +148,9 @@ WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) static void s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs) { -#define MAXCOUNT 20 +#define MAXCOUNT 5 struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; int count = 0; if (!cs) { @@ -159,16 +159,12 @@ s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } count++; val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA); if (val && count < MAXCOUNT) { @@ -184,16 +180,12 @@ s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs) } if (count >= MAXCOUNT) printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count); - if (stat & 1) { - writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); - writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); - writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); - writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); - } - if (stat & 2) { - writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); } void @@ -211,9 +203,6 @@ S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_s0box(cs); break; - case CARD_SETIRQ: - return(request_irq(cs->irq, &s0box_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: inithscxisac(cs, 3); break; @@ -223,8 +212,8 @@ S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -__initfunc(int -setup_s0box(struct IsdnCard *card)) +int __init +setup_s0box(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; char tmp[64]; @@ -266,6 +255,7 @@ setup_s0box(struct IsdnCard *card)) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &S0Box_card_msg; + cs->irq_func = &s0box_interrupt; ISACVersion(cs, "S0Box:"); if (HscxVersion(cs, "S0Box:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c new file mode 100644 index 000000000..0d524faa8 --- /dev/null +++ b/drivers/isdn/hisax/saphir.c @@ -0,0 +1,326 @@ +/* $Id: saphir.c,v 1.3 1999/07/12 21:05:26 keil Exp $ + + * saphir.c low level stuff for HST Saphir 1 + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Thanks to HST High Soft Tech GmbH + * + * + * $Log: saphir.c,v $ + * Revision 1.3 1999/07/12 21:05:26 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.2 1999/07/01 08:07:55 keil + * Initial version + * + * + */ + + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +static char *saphir_rev = "$Revision: 1.3 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISAC_DATA 0 +#define HSCX_DATA 1 +#define ADDRESS_REG 2 +#define IRQ_REG 3 +#define SPARE_REG 4 +#define RESET_REG 5 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, + offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, + offset + (hscx ? 0x40 : 0), value); +} + +#define READHSCX(cs, nr, reg) readreg(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +saphir_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + + if (!cs) { + printk(KERN_WARNING "saphir: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) + hscx_int_main(cs, val); + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + /* Watchdog */ + if (cs->hw.saphir.timer.function) { + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 1*HZ; + add_timer(&cs->hw.saphir.timer); + } else + printk(KERN_WARNING "saphir: Spurious timer!\n"); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0); +} + +static void +SaphirWatchDog(struct IsdnCardState *cs) +{ + /* 5 sec WatchDog, so read at least every 4 sec */ + cs->readisac(cs, ISAC_RBCH); + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 1*HZ; + add_timer(&cs->hw.saphir.timer); +} + +void +release_io_saphir(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + cli(); + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, 0xff); + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.function = NULL; + restore_flags(flags); + if (cs->hw.saphir.cfg_reg) + release_region(cs->hw.saphir.cfg_reg, 6); +} + +static int +saphir_reset(struct IsdnCardState *cs) +{ + long flags; + u_char irq_val; + + switch(cs->irq) { + case 5: irq_val = 0; + break; + case 3: irq_val = 1; + break; + case 11: + irq_val = 2; + break; + case 12: + irq_val = 3; + break; + case 15: + irq_val = 4; + break; + default: + printk(KERN_WARNING "HiSax: saphir wrong IRQ %d\n", + cs->irq); + return (1); + } + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val); + save_flags(flags); + sti(); + byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ + byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ + restore_flags(flags); + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val); + byteout(cs->hw.saphir.cfg_reg + SPARE_REG, 0x02); + return (0); +} + +static int +saphir_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + saphir_reset(cs); + return(0); + case CARD_RELEASE: + release_io_saphir(cs); + return(0); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + + +int __init +setup_saphir(struct IsdnCard *card) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, saphir_rev); + printk(KERN_INFO "HiSax: HST Saphir driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_HSTSAPHIR) + return (0); + + /* IO-Ports */ + cs->hw.saphir.cfg_reg = card->para[1]; + cs->hw.saphir.isac = card->para[1] + ISAC_DATA; + cs->hw.saphir.hscx = card->para[1] + HSCX_DATA; + cs->hw.saphir.ale = card->para[1] + ADDRESS_REG; + cs->irq = card->para[0]; + if (check_region((cs->hw.saphir.cfg_reg), 6)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.saphir.cfg_reg, + cs->hw.saphir.cfg_reg + 5); + return (0); + } else + request_region(cs->hw.saphir.cfg_reg,6, "saphir"); + + printk(KERN_INFO + "HiSax: %s config irq:%d io:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.saphir.cfg_reg); + + cs->hw.saphir.timer.function = (void *) SaphirWatchDog; + cs->hw.saphir.timer.data = (long) cs; + init_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 4*HZ; + add_timer(&cs->hw.saphir.timer); + if (saphir_reset(cs)) { + release_io_saphir(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &saphir_card_msg; + cs->irq_func = &saphir_interrupt; + ISACVersion(cs, "saphir:"); + if (HscxVersion(cs, "saphir:")) { + printk(KERN_WARNING + "saphir: wrong HSCX versions check IO address\n"); + release_io_saphir(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c index 105c9a380..2640555b2 100644 --- a/drivers/isdn/hisax/sedlbauer.c +++ b/drivers/isdn/hisax/sedlbauer.c @@ -1,4 +1,4 @@ -/* $Id: sedlbauer.c,v 1.9 1998/11/15 23:55:20 keil Exp $ +/* $Id: sedlbauer.c,v 1.16 1999/08/29 18:23:01 niemann Exp $ * sedlbauer.c low level stuff for Sedlbauer cards * includes support for the Sedlbauer speed star (speed star II), @@ -17,6 +17,30 @@ * Edgar Toernig * * $Log: sedlbauer.c,v $ + * Revision 1.16 1999/08/29 18:23:01 niemann + * Fixed typo in errormsg + * + * Revision 1.15 1999/08/25 17:00:00 keil + * Make ISAR V32bis modem running + * Make LL->HL interface open for additional commands + * + * Revision 1.14 1999/08/11 20:59:22 keil + * new PCI codefix + * fix IRQ problem while unload + * + * Revision 1.13 1999/08/10 16:02:08 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.12 1999/08/05 20:43:22 keil + * ISAR analog modem support + * + * Revision 1.11 1999/07/12 21:05:27 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.10 1999/07/01 08:12:09 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.9 1998/11/15 23:55:20 keil * changes from 2.0 * @@ -76,10 +100,13 @@ #include "isar.h" #include "isdnl1.h" #include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif extern const char *CardType[]; -const char *Sedlbauer_revision = "$Revision: 1.9 $"; +const char *Sedlbauer_revision = "$Revision: 1.16 $"; const char *Sedlbauer_Types[] = {"None", "speed card/win", "speed star", "speed fax+", @@ -291,7 +318,7 @@ static void sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); @@ -307,16 +334,12 @@ sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); if (val) { if (cs->debug & L1_DEB_HSCX) @@ -329,23 +352,19 @@ sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - if (stat & 1) { - writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF); - writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF); - writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0); - writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0); - } - if (stat & 2) { - writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0); } static void sedlbauer_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char ista, val, icnt = 20; + u_char ista, val, icnt = 5; if (!cs) { printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); @@ -392,7 +411,7 @@ sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char val; - int cnt = 20; + int cnt = 5; if (!cs) { printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); @@ -431,9 +450,11 @@ sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs) void release_io_sedlbauer(struct IsdnCardState *cs) { - int bytecnt = (cs->subtyp == SEDL_SPEED_FAX) ? 16 : 8; + int bytecnt = 8; - if (cs->hw.sedl.bus == SEDL_BUS_PCI) { + if (cs->subtyp == SEDL_SPEED_FAX) { + bytecnt = 16; + } else if (cs->hw.sedl.bus == SEDL_BUS_PCI) { bytecnt = 256; } if (cs->hw.sedl.cfg_reg) @@ -454,10 +475,10 @@ reset_sedlbauer(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0); @@ -469,10 +490,10 @@ reset_sedlbauer(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */ current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); restore_flags(flags); } } @@ -486,19 +507,19 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) reset_sedlbauer(cs); return(0); case CARD_RELEASE: - release_io_sedlbauer(cs); - return(0); - case CARD_SETIRQ: if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { - return(request_irq(cs->irq, &sedlbauer_interrupt_isar, - I4L_IRQ_FLAG, "HiSax", cs)); - } else if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) { - return(request_irq(cs->irq, &sedlbauer_interrupt_ipac, - I4L_IRQ_FLAG, "HiSax", cs)); - } else { - return(request_irq(cs->irq, &sedlbauer_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, + ISAR_IRQBIT, 0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, + ISAC_MASK, 0xFF); + reset_sedlbauer(cs); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, + ISAR_IRQBIT, 0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, + ISAC_MASK, 0xFF); } + release_io_sedlbauer(cs); + return(0); case CARD_INIT: if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { clear_pending_isac_ints(cs); @@ -516,24 +537,19 @@ Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); case CARD_TEST: return(0); - case CARD_LOAD_FIRM: - if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { - if (isar_load_firmware(cs, arg)) - return(1); - else - ll_run(cs); - } - return(0); } return(0); } - #ifdef SEDLBAUER_PCI +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_sedl __initdata = NULL; +#else static int pci_index __initdata = 0; #endif +#endif -int __init +int __init setup_sedlbauer(struct IsdnCard *card) { int bytecnt, ver, val; @@ -569,6 +585,25 @@ setup_sedlbauer(struct IsdnCard *card) /* Probe for Sedlbauer speed pci */ #if SEDLBAUER_PCI #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "Sedlbauer: no PCI bus present\n"); + return(0); + } + if ((dev_sedl = pci_find_device(PCI_VENDOR_SEDLBAUER, + PCI_SPEEDPCI_ID, dev_sedl))) { + cs->irq = dev_sedl->irq; + if (!cs->irq) { + printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n"); + return(0); + } + cs->hw.sedl.cfg_reg = get_pcibase(dev_sedl, 0) & + PCI_BASE_ADDRESS_IO_MASK; + } else { + printk(KERN_WARNING "Sedlbauer: No PCI card found\n"); + return(0); + } +#else for (; pci_index < 255; pci_index++) { unsigned char pci_bus, pci_device_fn; unsigned int ioaddr; @@ -589,14 +624,6 @@ setup_sedlbauer(struct IsdnCard *card) printk(KERN_WARNING "Sedlbauer: No IO-Adr for PCI card found\n"); return(0); } - cs->hw.sedl.bus = SEDL_BUS_PCI; - cs->hw.sedl.chip = SEDL_CHIP_IPAC; - cs->subtyp = SEDL_SPEED_PCI; - bytecnt = 256; - byteout(cs->hw.sedl.cfg_reg, 0xff); - byteout(cs->hw.sedl.cfg_reg, 0x00); - byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); - byteout(cs->hw.sedl.cfg_reg+ 5, 0x02); break; } if (pci_index == 255) { @@ -604,6 +631,16 @@ setup_sedlbauer(struct IsdnCard *card) return(0); } pci_index++; +#endif /* COMPAT_HAS_NEW_PCI */ + cs->irq_flags |= SA_SHIRQ; + cs->hw.sedl.bus = SEDL_BUS_PCI; + cs->hw.sedl.chip = SEDL_CHIP_IPAC; + cs->subtyp = SEDL_SPEED_PCI; + bytecnt = 256; + byteout(cs->hw.sedl.cfg_reg, 0xff); + byteout(cs->hw.sedl.cfg_reg, 0x00); + byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); + byteout(cs->hw.sedl.cfg_reg+ 5, 0x02); #else printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n"); return (0); @@ -683,6 +720,7 @@ setup_sedlbauer(struct IsdnCard *card) cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; cs->writeisacfifo = &WriteISACfifo_IPAC; + cs->irq_func = &sedlbauer_interrupt_ipac; val = readreg(cs->hw.sedl.adr,cs->hw.sedl.isac, IPAC_ID); printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val); @@ -702,7 +740,8 @@ setup_sedlbauer(struct IsdnCard *card) cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar; cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar; test_and_set_bit(HW_ISAR, &cs->HW_Flags); - + cs->irq_func = &sedlbauer_interrupt_isar; + cs->auxcmd = &isar_auxcmd; ISACVersion(cs, "Sedlbauer:"); cs->BC_Read_Reg = &ReadISAR; @@ -729,6 +768,7 @@ setup_sedlbauer(struct IsdnCard *card) cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON; cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF; } + cs->irq_func = &sedlbauer_interrupt; ISACVersion(cs, "Sedlbauer:"); if (HscxVersion(cs, "Sedlbauer:")) { diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c index ec2240b19..29fae02f7 100644 --- a/drivers/isdn/hisax/sportster.c +++ b/drivers/isdn/hisax/sportster.c @@ -1,4 +1,4 @@ -/* $Id: sportster.c,v 1.7 1998/11/15 23:55:22 keil Exp $ +/* $Id: sportster.c,v 1.9 1999/07/12 21:05:29 keil Exp $ * sportster.c low level stuff for USR Sportster internal TA * @@ -7,6 +7,13 @@ * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation * * $Log: sportster.c,v $ + * Revision 1.9 1999/07/12 21:05:29 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.8 1999/07/01 08:12:10 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.7 1998/11/15 23:55:22 keil * changes from 2.0 * @@ -36,7 +43,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *sportster_revision = "$Revision: 1.7 $"; +const char *sportster_revision = "$Revision: 1.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -171,11 +178,11 @@ reset_sportster(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */ byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); restore_flags(flags); } @@ -189,9 +196,6 @@ Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_sportster(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &sportster_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: inithscxisac(cs, 1); cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */ @@ -204,7 +208,7 @@ Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __init get_io_range(struct IsdnCardState *cs) { int i, j, adr; @@ -230,7 +234,7 @@ get_io_range(struct IsdnCardState *cs) } } -int __init +int __init setup_sportster(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; @@ -282,6 +286,7 @@ setup_sportster(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Sportster_card_msg; + cs->irq_func = &sportster_interrupt; ISACVersion(cs, "Sportster:"); if (HscxVersion(cs, "Sportster:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c index c9d94fe4d..f21284bec 100644 --- a/drivers/isdn/hisax/tei.c +++ b/drivers/isdn/hisax/tei.c @@ -1,4 +1,4 @@ -/* $Id: tei.c,v 2.11 1998/11/15 23:55:24 keil Exp $ +/* $Id: tei.c,v 2.13 1999/07/21 14:46:28 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,12 @@ * Fritz Elfert * * $Log: tei.c,v $ + * Revision 2.13 1999/07/21 14:46:28 keil + * changes from EICON certification + * + * Revision 2.12 1999/07/01 08:12:11 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.11 1998/11/15 23:55:24 keil * changes from 2.0 * @@ -72,7 +78,7 @@ #include "isdnl2.h" #include <linux/random.h> -const char *tei_revision = "$Revision: 2.11 $"; +const char *tei_revision = "$Revision: 2.13 $"; #define ID_REQUEST 1 #define ID_ASSIGNED 2 @@ -163,6 +169,7 @@ put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei) printk(KERN_WARNING "HiSax: No skb for TEI manager\n"); return; } + SET_SKB_FREE(skb); bp = skb_put(skb, 3); bp[0] = (TEI_SAPI << 2); bp[1] = (GROUP_TEI << 1) | 0x1; @@ -226,6 +233,25 @@ tei_id_assign(struct FsmInst *fi, int event, void *arg) } static void +tei_id_test_dup(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *ost, *st = fi->userdata; + struct sk_buff *skb = arg; + int tei, ri; + + ri = ((unsigned int) skb->data[1] << 8) + skb->data[2]; + tei = skb->data[4] >> 1; + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "foreign identity assign ri %d tei %d", ri, tei); + if ((ost = findtei(st, tei))) { /* same tei is in use */ + st->ma.tei_m.printdebug(&st->ma.tei_m, + "possible duplicate assignment tei %d", tei); + FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL); + } +} + +static void tei_id_denied(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; @@ -345,7 +371,7 @@ tei_l1l2(struct PStack *st, int pr, void *arg) int mt; if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); return; } @@ -353,8 +379,8 @@ tei_l1l2(struct PStack *st, int pr, void *arg) if (skb->len < 3) { st->ma.tei_m.printdebug(&st->ma.tei_m, "short mgr frame %ld/3", skb->len); - } else if (((skb->data[0] >> 2) != TEI_SAPI) || - ((skb->data[1] >> 1) != GROUP_TEI)) { + } else if ((skb->data[0] != ((TEI_SAPI << 2) | 2)) || + (skb->data[1] != ((GROUP_TEI << 1) | 1))) { st->ma.tei_m.printdebug(&st->ma.tei_m, "wrong mgr sapi/tei %x/%x", skb->data[0], skb->data[1]); @@ -391,7 +417,7 @@ tei_l1l2(struct PStack *st, int pr, void *arg) st->ma.tei_m.printdebug(&st->ma.tei_m, "tei handler wrong pr %x\n", pr); } - dev_kfree_skb(skb); + idev_kfree_skb(skb, FREE_READ); } static void @@ -468,6 +494,7 @@ release_tei(struct IsdnCardState *cs) static struct FsmNode TeiFnList[] HISAX_INITDATA = { {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c index b37aded89..430ea2768 100644 --- a/drivers/isdn/hisax/teleint.c +++ b/drivers/isdn/hisax/teleint.c @@ -1,4 +1,4 @@ -/* $Id: teleint.c,v 1.7 1998/11/15 23:55:26 keil Exp $ +/* $Id: teleint.c,v 1.9 1999/07/12 21:05:30 keil Exp $ * teleint.c low level stuff for TeleInt isdn cards * @@ -6,6 +6,13 @@ * * * $Log: teleint.c,v $ + * Revision 1.9 1999/07/12 21:05:30 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 1.8 1999/07/01 08:12:12 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 1.7 1998/11/15 23:55:26 keil * changes from 2.0 * @@ -38,7 +45,7 @@ extern const char *CardType[]; -const char *TeleInt_revision = "$Revision: 1.7 $"; +const char *TeleInt_revision = "$Revision: 1.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -191,7 +198,7 @@ static void TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "TeleInt: Spurious interrupt!\n"); @@ -199,20 +206,16 @@ TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); if (val) { if (cs->debug & L1_DEB_ISAC) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - if (stat & 2) { - writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF); - writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0); - } + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF); + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0); } static void @@ -252,11 +255,11 @@ reset_TeleInt(struct IsdnCardState *cs) save_flags(flags); sti(); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(3); + schedule_timeout((30*HZ)/1000); cs->hw.hfc.cirm &= ~HFC_RESET; byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset Off */ current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout((10*HZ)/1000); restore_flags(flags); } @@ -270,9 +273,6 @@ TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_TeleInt(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &TeleInt_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: inithfc(cs); clear_pending_isac_ints(cs); @@ -289,7 +289,7 @@ TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __init setup_TeleInt(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; @@ -365,6 +365,7 @@ setup_TeleInt(struct IsdnCard *card) cs->BC_Read_Reg = &ReadHFC; cs->BC_Write_Reg = &WriteHFC; cs->cardmsg = &TeleInt_card_msg; + cs->irq_func = &TeleInt_interrupt; ISACVersion(cs, "TeleInt:"); return (1); } diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c index c562de877..4046c4357 100644 --- a/drivers/isdn/hisax/teles0.c +++ b/drivers/isdn/hisax/teles0.c @@ -1,4 +1,4 @@ -/* $Id: teles0.c,v 2.8 1998/04/15 16:44:28 keil Exp $ +/* $Id: teles0.c,v 2.9 1999/07/12 21:05:31 keil Exp $ * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden @@ -10,6 +10,10 @@ * Beat Doebeli * * $Log: teles0.c,v $ + * Revision 2.9 1999/07/12 21:05:31 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * * Revision 2.8 1998/04/15 16:44:28 keil * new init code * @@ -54,7 +58,7 @@ extern const char *CardType[]; -const char *teles0_revision = "$Revision: 2.8 $"; +const char *teles0_revision = "$Revision: 2.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -177,7 +181,7 @@ static void teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; int count = 0; if (!cs) { @@ -186,39 +190,31 @@ teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readisac(cs->hw.teles0.membase, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } count++; val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); - if (val && count < 20) { + if (val && count < 5) { if (cs->debug & L1_DEB_HSCX) debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } val = readisac(cs->hw.teles0.membase, ISAC_ISTA); - if (val && count < 20) { + if (val && count < 5) { if (cs->debug & L1_DEB_ISAC) debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } - if (stat & 1) { - writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); - writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); - writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); - writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); - } - if (stat & 2) { - writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); - writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); - } + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); } void @@ -290,9 +286,6 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_teles0(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &teles0_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: inithscxisac(cs, 3); return(0); @@ -302,7 +295,7 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __init setup_teles0(struct IsdnCard *card) { u_char val; @@ -382,6 +375,7 @@ setup_teles0(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Teles_card_msg; + cs->irq_func = &teles0_interrupt; ISACVersion(cs, "Teles0:"); if (HscxVersion(cs, "Teles0:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c index addc0dbf1..3210a6af6 100644 --- a/drivers/isdn/hisax/teles3.c +++ b/drivers/isdn/hisax/teles3.c @@ -1,4 +1,4 @@ -/* $Id: teles3.c,v 2.10 1999/02/15 14:37:15 cpetig Exp $ +/* $Id: teles3.c,v 2.13 1999/08/30 12:01:28 keil Exp $ * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * @@ -11,6 +11,16 @@ * Beat Doebeli * * $Log: teles3.c,v $ + * Revision 2.13 1999/08/30 12:01:28 keil + * HW version v1.3 support + * + * Revision 2.12 1999/07/12 21:05:32 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.11 1999/07/01 08:12:14 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.10 1999/02/15 14:37:15 cpetig * oops, missed something in last commit * @@ -78,7 +88,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *teles3_revision = "$Revision: 2.10 $"; +const char *teles3_revision = "$Revision: 2.13 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -160,9 +170,9 @@ WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) static void teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) { -#define MAXCOUNT 20 +#define MAXCOUNT 5 struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; int count = 0; if (!cs) { @@ -171,16 +181,12 @@ teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) } val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); Start_HSCX: - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readreg(cs->hw.teles3.isac, ISAC_ISTA); Start_ISAC: - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } count++; val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); if (val && count < MAXCOUNT) { @@ -196,16 +202,12 @@ teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) } if (count >= MAXCOUNT) printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count); - if (stat & 1) { - writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); - writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); - writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); - writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); - } - if (stat & 2) { - writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF); - writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0); - } + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); } inline static void @@ -222,15 +224,16 @@ release_ioregs(struct IsdnCardState *cs, int mask) void release_io_teles3(struct IsdnCardState *cs) { - if (cs->typ == ISDN_CTYPE_TELESPCMCIA) + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { release_region(cs->hw.teles3.hscx[0], 97); - else { - if (cs->hw.teles3.cfg_reg) + } else { + if (cs->hw.teles3.cfg_reg) { if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { release_region(cs->hw.teles3.cfg_reg, 1); } else { release_region(cs->hw.teles3.cfg_reg, 8); } + } release_ioregs(cs, 0x7); } } @@ -309,9 +312,6 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_teles3(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &teles3_interrupt, - I4L_IRQ_FLAG, "HiSax", cs)); case CARD_INIT: inithscxisac(cs, 3); return(0); @@ -321,7 +321,7 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __init setup_teles3(struct IsdnCard *card) { u_char val; @@ -405,12 +405,13 @@ setup_teles3(struct IsdnCard *card) CardType[cs->typ], cs->hw.teles3.isac + 32, cs->hw.teles3.isac + 64); - if (cs->hw.teles3.cfg_reg) + if (cs->hw.teles3.cfg_reg) { if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { release_region(cs->hw.teles3.cfg_reg, 1); } else { release_region(cs->hw.teles3.cfg_reg, 8); } + } return (0); } else request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac"); @@ -420,12 +421,13 @@ setup_teles3(struct IsdnCard *card) CardType[cs->typ], cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[0] + 64); - if (cs->hw.teles3.cfg_reg) + if (cs->hw.teles3.cfg_reg) { if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { release_region(cs->hw.teles3.cfg_reg, 1); } else { release_region(cs->hw.teles3.cfg_reg, 8); } + } release_ioregs(cs, 1); return (0); } else @@ -436,12 +438,13 @@ setup_teles3(struct IsdnCard *card) CardType[cs->typ], cs->hw.teles3.hscx[1] + 32, cs->hw.teles3.hscx[1] + 64); - if (cs->hw.teles3.cfg_reg) + if (cs->hw.teles3.cfg_reg) { if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { release_region(cs->hw.teles3.cfg_reg, 1); } else { release_region(cs->hw.teles3.cfg_reg, 8); } + } release_ioregs(cs, 3); return (0); } else @@ -464,9 +467,10 @@ setup_teles3(struct IsdnCard *card) * 0x1f=with AB * 0x1c 16.3 ??? * 0x39 16.3 1.1 + * 0x38 16.3 1.3 * 0x46 16.3 with AB + Video (Teles-Vision) */ - if (val != 0x46 && val != 0x39 && val != 0x1c && val != 0x1e && val != 0x1f) { + if (val != 0x46 && val != 0x39 && val != 0x38 && val != 0x1c && val != 0x1e && val != 0x1f) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", cs->hw.teles3.cfg_reg + 2, val); release_io_teles3(cs); @@ -494,6 +498,7 @@ setup_teles3(struct IsdnCard *card) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Teles_card_msg; + cs->irq_func = &teles3_interrupt; ISACVersion(cs, "Teles3:"); if (HscxVersion(cs, "Teles3:")) { printk(KERN_WARNING diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c index 88592aa8d..9ffe99918 100644 --- a/drivers/isdn/hisax/telespci.c +++ b/drivers/isdn/hisax/telespci.c @@ -1,4 +1,4 @@ -/* $Id: telespci.c,v 2.5 1998/11/15 23:55:28 keil Exp $ +/* $Id: telespci.c,v 2.9 1999/08/11 21:01:34 keil Exp $ * telespci.c low level stuff for Teles PCI isdn cards * @@ -7,6 +7,19 @@ * * * $Log: telespci.c,v $ + * Revision 2.9 1999/08/11 21:01:34 keil + * new PCI codefix + * + * Revision 2.8 1999/08/10 16:02:10 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 2.7 1999/07/12 21:05:34 keil + * fix race in IRQ handling + * added watchdog for lost IRQs + * + * Revision 2.6 1999/07/01 08:12:15 keil + * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel + * * Revision 2.5 1998/11/15 23:55:28 keil * changes from 2.0 * @@ -24,16 +37,17 @@ */ #define __NO_VERSION__ #include <linux/config.h> -#include <linux/string.h> #include "hisax.h" #include "isac.h" #include "hscx.h" #include "isdnl1.h" #include <linux/pci.h> +#ifndef COMPAT_HAS_NEW_PCI +#include <linux/bios32.h> +#endif extern const char *CardType[]; - -const char *telespci_revision = "$Revision: 2.5 $"; +const char *telespci_revision = "$Revision: 2.9 $"; #define ZORAN_PO_RQ_PEN 0x02000000 #define ZORAN_PO_WR 0x00800000 @@ -243,35 +257,27 @@ telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs) { #define MAXCOUNT 20 struct IsdnCardState *cs = dev_id; - u_char val, stat = 0; + u_char val; if (!cs) { printk(KERN_WARNING "TelesPCI: Spurious interrupt!\n"); return; } val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); - if (val) { + if (val) hscx_int_main(cs, val); - stat |= 1; - } val = readisac(cs->hw.teles0.membase, ISAC_ISTA); - if (val) { + if (val) isac_interrupt(cs, val); - stat |= 2; - } /* Clear interrupt register for Zoran PCI controller */ writel(0x70000000, cs->hw.teles0.membase + 0x3C); - if (stat & 1) { - writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); - writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); - writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); - writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); - } - if (stat & 2) { - writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); - writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); - } + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); } void @@ -289,9 +295,6 @@ TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg) case CARD_RELEASE: release_io_telespci(cs); return(0); - case CARD_SETIRQ: - return(request_irq(cs->irq, &telespci_interrupt, - I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); case CARD_INIT: inithscxisac(cs, 3); return(0); @@ -301,20 +304,29 @@ TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } +#ifdef COMPAT_HAS_NEW_PCI static struct pci_dev *dev_tel __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif -__initfunc(int -setup_telespci(struct IsdnCard *card)) +int __init +setup_telespci(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; char tmp[64]; +#ifndef COMPAT_HAS_NEW_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_memaddr; + u_char found = 0; +#endif strcpy(tmp, telespci_revision); printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_TELESPCI) return (0); - #if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI if (!pci_present()) { printk(KERN_ERR "TelesPCI: no PCI bus present\n"); return(0); @@ -325,15 +337,41 @@ setup_telespci(struct IsdnCard *card)) printk(KERN_WARNING "Teles: No IRQ for PCI card found\n"); return(0); } - cs->hw.teles0.membase = (u_int) ioremap(dev_tel->base_address[0], + cs->hw.teles0.membase = (u_int) ioremap(get_pcibase(dev_tel, 0), PAGE_SIZE); printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n", - dev_tel->base_address[0], dev_tel->irq); + get_pcibase(dev_tel, 0), dev_tel->irq); } else { printk(KERN_WARNING "TelesPCI: No PCI card found\n"); return(0); } #else + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device (0x11DE, 0x6120, + pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) { + found = 1; + } else { + break; + } + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_memaddr); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + printk(KERN_INFO "Found: Zoran, base-address: 0x%x," + " irq: 0x%x\n", pci_memaddr, pci_irq); + break; + } + if (!found) { + printk(KERN_WARNING "TelesPCI: No PCI card found\n"); + return(0); + } + pci_index++; + cs->irq = pci_irq; + cs->hw.teles0.membase = (u_int) vremap(pci_memaddr, PAGE_SIZE); +#endif /* COMPAT_HAS_NEW_PCI */ +#else printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n"); printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n"); return (0); @@ -361,6 +399,8 @@ setup_telespci(struct IsdnCard *card)) cs->BC_Write_Reg = &WriteHSCX; cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &TelesPCI_card_msg; + cs->irq_func = &telespci_interrupt; + cs->irq_flags |= SA_SHIRQ; ISACVersion(cs, "TelesPCI:"); if (HscxVersion(cs, "TelesPCI:")) { printk(KERN_WARNING diff --git a/drivers/isdn/icn/.cvsignore b/drivers/isdn/icn/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/icn/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index d08730e17..26ac3502d 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1,4 +1,4 @@ -/* $Id: icn.c,v 1.56 1999/04/12 13:15:07 fritz Exp $ +/* $Id: icn.c,v 1.59 1999/08/28 22:10:55 keil Exp $ * ISDN low-level module for the ICN active ISDN-Card. * @@ -19,6 +19,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.c,v $ + * Revision 1.59 1999/08/28 22:10:55 keil + * __setup function should be static + * + * Revision 1.58 1999/08/25 16:44:17 keil + * Support for new __setup function + * + * Revision 1.57 1999/07/06 16:15:30 detabc + * remove unused messages + * * Revision 1.56 1999/04/12 13:15:07 fritz * Fixed a cast. * @@ -39,10 +48,6 @@ * Revision 1.51 1998/03/07 22:29:55 fritz * Adapted Detlef's chenges for 2.1. * - * Revision 1.50 1998/03/07 17:41:54 detabc - * add d-channel connect and disconnect support statcallback - * from icn low-level to link->level - * * Revision 1.49 1998/02/13 11:14:15 keil * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * @@ -233,7 +238,7 @@ #undef MAP_DEBUG static char -*revision = "$Revision: 1.56 $"; +*revision = "$Revision: 1.59 $"; static int icn_addcard(int, char *, char *); @@ -1847,13 +1852,25 @@ icn_addcard(int port, char *id1, char *id2) #ifdef MODULE #define icn_init init_module #else +#ifdef COMPAT_HAS_NEW_SETUP +#include <linux/init.h> +static int __init +icn_setup(char *line) +{ + char *p, *str; + int ints[3]; + static char sid[20]; + static char sid2[20]; + + str = get_options(line, 2, ints); +#else void icn_setup(char *str, int *ints) { char *p; static char sid[20]; static char sid2[20]; - +#endif if (ints[0]) portbase = ints[1]; if (ints[0] > 1) @@ -1867,8 +1884,14 @@ icn_setup(char *str, int *ints) icn_id2 = sid2; } } +#ifdef COMPAT_HAS_NEW_SETUP + return(1); +} +__setup("icn=", icn_setup); +#else } #endif +#endif /* MODULES */ int icn_init(void) diff --git a/drivers/isdn/isdn_audio.c b/drivers/isdn/isdn_audio.c index 67307a0ec..3599d2ada 100644 --- a/drivers/isdn/isdn_audio.c +++ b/drivers/isdn/isdn_audio.c @@ -1,4 +1,4 @@ -/* $Id: isdn_audio.c,v 1.13 1999/04/12 12:33:09 fritz Exp $ +/* $Id: isdn_audio.c,v 1.17 1999/08/17 11:10:52 paul Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * @@ -21,6 +21,30 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.c,v $ + * Revision 1.17 1999/08/17 11:10:52 paul + * don't try to use x86 assembler on non-x86! + * + * Revision 1.16 1999/08/06 12:47:35 calle + * Using __GNUC__ == 2 && __GNUC_MINOR__ < 95 how to define + * ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT + * + * Revision 1.15 1999/08/06 12:02:52 calle + * egcs 2.95 complain about invalid asm statement: + * "fixed or forbidden register 2 (cx) was spilled for class CREG." + * Using ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT and not + * define it at the moment. + * + * Revision 1.14 1999/07/11 17:14:06 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * * Revision 1.13 1999/04/12 12:33:09 fritz * Changes from 2.0 tree. * @@ -71,7 +95,7 @@ #include "isdn_audio.h" #include "isdn_common.h" -char *isdn_audio_revision = "$Revision: 1.13 $"; +char *isdn_audio_revision = "$Revision: 1.17 $"; /* * Misc. lookup-tables. @@ -268,7 +292,18 @@ static char dtmf_matrix[4][4] = {'*', '0', '#', 'D'} }; -#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) + +/* + * egcs 2.95 complain about invalid asm statement: + * "fixed or forbidden register 2 (cx) was spilled for class CREG." + */ +#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) && defined(__GNUC__) +#if __GNUC__ == 2 && __GNUC_MINOR__ < 95 +#define ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT +#endif +#endif + +#ifdef ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT static inline void isdn_audio_tlookup(const void *table, void *buff, unsigned long n) { @@ -711,16 +746,51 @@ isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt) } void -isdn_audio_eval_silence(modem_info * info) +isdn_audio_put_dle_code(modem_info * info, u_char code) { - silence_state *s = info->silence_state; struct sk_buff *skb; unsigned long flags; int di; int ch; - char what; char *p; + skb = dev_alloc_skb(2); + if (!skb) { + printk(KERN_WARNING + "isdn_audio: Could not alloc skb for ttyI%d\n", + info->line); + return; + } + p = (char *) skb_put(skb, 2); + p[0] = 0x10; + p[1] = code; + if (skb_headroom(skb) < sizeof(isdn_audio_skb)) { + printk(KERN_WARNING + "isdn_audio: insufficient skb_headroom, dropping\n"); + kfree_skb(skb); + return; + } + ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; + ISDN_AUDIO_SKB_LOCK(skb) = 0; + save_flags(flags); + cli(); + di = info->isdn_driver; + ch = info->isdn_channel; + __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); + dev->drv[di]->rcvcount[ch] += 2; + restore_flags(flags); + /* Schedule dequeuing */ + if ((dev->modempoll) && (info->rcvsched)) + isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); + wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); +} + +void +isdn_audio_eval_silence(modem_info * info) +{ + silence_state *s = info->silence_state; + char what; + what = ' '; if (s->idx > (info->emu.vpar[2] * 800)) { @@ -734,28 +804,6 @@ isdn_audio_eval_silence(modem_info * info) if ((what == 's') || (what == 'q')) { printk(KERN_DEBUG "ttyI%d: %s\n", info->line, (what=='s') ? "silence":"quiet"); - skb = dev_alloc_skb(2); - p = (char *) skb_put(skb, 2); - p[0] = 0x10; - p[1] = what; - if (skb_headroom(skb) < sizeof(isdn_audio_skb)) { - printk(KERN_WARNING - "isdn_audio: insufficient skb_headroom, dropping\n"); - kfree_skb(skb); - return; - } - ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; - ISDN_AUDIO_SKB_LOCK(skb) = 0; - save_flags(flags); - cli(); - di = info->isdn_driver; - ch = info->isdn_channel; - __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); - dev->drv[di]->rcvcount[ch] += 2; - restore_flags(flags); - /* Schedule dequeuing */ - if ((dev->modempoll) && (info->rcvsched)) - isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); - wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); + isdn_audio_put_dle_code(info, what); } } diff --git a/drivers/isdn/isdn_audio.h b/drivers/isdn/isdn_audio.h index ee33ce020..54dcb253e 100644 --- a/drivers/isdn/isdn_audio.h +++ b/drivers/isdn/isdn_audio.h @@ -1,4 +1,4 @@ -/* $Id: isdn_audio.h,v 1.7 1999/04/12 12:33:11 fritz Exp $ +/* $Id: isdn_audio.h,v 1.8 1999/07/11 17:14:07 armin Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * @@ -19,6 +19,17 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.h,v $ + * Revision 1.8 1999/07/11 17:14:07 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * * Revision 1.7 1999/04/12 12:33:11 fritz * Changes from 2.0 tree. * @@ -74,3 +85,4 @@ dtmf_state *isdn_audio_dtmf_init(dtmf_state *); extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int); extern void isdn_audio_eval_silence(modem_info *); silence_state *isdn_audio_silence_init(silence_state *); +extern void isdn_audio_put_dle_code(modem_info *, u_char); diff --git a/drivers/isdn/isdn_budget.c b/drivers/isdn/isdn_budget.c deleted file mode 100644 index 985c97ba8..000000000 --- a/drivers/isdn/isdn_budget.c +++ /dev/null @@ -1,206 +0,0 @@ -/* $Id: isdn_budget.c,v 1.3 1998/10/23 10:18:39 paul Exp $ - * - * Linux ISDN subsystem, budget-accounting for network interfaces. - * - * Copyright 1997 by Christian Lademann <cal@zls.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: isdn_budget.c,v $ - * Revision 1.3 1998/10/23 10:18:39 paul - * Implementation of "dialmode" (successor of "status") - * You also need current isdnctrl for this! - * - * Revision 1.2 1998/03/07 23:17:30 fritz - * Added RCS keywords - * Bugfix: Did not compile without isdn_dumppkt beeing enabled. - * - */ - -/* -30.06.97:cal:angelegt -04.11.97:cal:budget.period: int --> time_t -*/ - -#include <linux/config.h> -#define __NO_VERSION__ -#include <linux/module.h> -#include <linux/isdn.h> -#include "isdn_common.h" -#include "isdn_net.h" - -#ifdef CONFIG_ISDN_BUDGET - -#define VERBOSE_PRINTK(v, l, p...) { \ - if(dev->net_verbose >= (v)) { \ - printk(l ## p); \ - } else { ; } \ -} - - -int -isdn_net_budget(int type, struct device *ndev) { - isdn_net_local *lp = (isdn_net_local *)ndev->priv; - int i, ret = 0; - - switch(type) { - case ISDN_BUDGET_INIT: - for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) { - lp->budget [i] .amount = -1; - lp->budget [i] .used = 0; - lp->budget [i] .period = (time_t)0; - lp->budget [i] .period_started = (time_t)0; - lp->budget [i] .last_check = CURRENT_TIME; - lp->budget [i] .notified = 0; - } - - return(0); - break; - - case ISDN_BUDGET_CHECK_DIAL: - case ISDN_BUDGET_CHECK_CHARGE: - case ISDN_BUDGET_CHECK_ONLINE: - ret = 0; - - for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) { - if(lp->budget [i] .amount < 0) - continue; - - if(lp->budget [i] .period_started + lp->budget [i] .period < CURRENT_TIME) { - lp->budget [i] .used = 0; - lp->budget [i] .period_started = CURRENT_TIME; - lp->budget [i] .notified = 0; - } - - if(lp->budget [i] .used >= lp->budget [i] .amount) - ret |= (1 << i); - } - - switch(type) { - case ISDN_BUDGET_CHECK_DIAL: - if(! ret) { - lp->budget [ISDN_BUDGET_DIAL] .used++; - lp->budget [ISDN_BUDGET_DIAL] .last_check = CURRENT_TIME; - } - break; - - case ISDN_BUDGET_CHECK_CHARGE: - lp->budget [ISDN_BUDGET_CHARGE] .used++; - lp->budget [ISDN_BUDGET_CHARGE] .last_check = CURRENT_TIME; - break; - - case ISDN_BUDGET_CHECK_ONLINE: - if(lp->budget [ISDN_BUDGET_ONLINE] .last_check) { - lp->budget [ISDN_BUDGET_ONLINE] .used += (CURRENT_TIME - lp->budget [ISDN_BUDGET_ONLINE] .last_check); - } - - lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME; - break; - } - -/* - if(ret) - lp->flags |= ISDN_NET_DM_OFF; -*/ - for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) { - if(ret & (1 << i) && ! lp->budget [i] .notified) { - switch(i) { - case ISDN_BUDGET_DIAL: - printk(KERN_WARNING "isdn_budget: dial budget used up.\n"); - break; - - case ISDN_BUDGET_CHARGE: - printk(KERN_WARNING "isdn_budget: charge budget used up.\n"); - break; - - case ISDN_BUDGET_ONLINE: - printk(KERN_WARNING "isdn_budget: online budget used up.\n"); - break; - - default: - printk(KERN_WARNING "isdn_budget: budget #%d used up.\n", i); - break; - } - - lp->budget [i] .notified = 1; - } - } - - return(ret); - break; - - case ISDN_BUDGET_START_ONLINE: - lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME; - return(0); - - break; - } - - return(-1); -} - - -int -isdn_budget_ioctl(isdn_ioctl_budget *iocmd) { - isdn_net_dev *p = isdn_net_findif(iocmd->name); - - if(p) { - switch(iocmd->command) { - case ISDN_BUDGET_SET_BUDGET: - if(! suser()) - return(-EPERM); - - if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET) - return(-EINVAL); - - if(iocmd->amount < 0) - iocmd->amount = -1; - - p->local->budget [iocmd->budget] .amount = iocmd->amount; - p->local->budget [iocmd->budget] .period = iocmd->period; - - if(iocmd->used <= 0) - p->local->budget [iocmd->budget] .used = 0; - else - p->local->budget [iocmd->budget] .used = iocmd->used; - - if(iocmd->period_started == (time_t)0) - p->local->budget [iocmd->budget] .period_started = CURRENT_TIME; - else - p->local->budget [iocmd->budget] .period_started = iocmd->period_started; - - return(0); - break; - - case ISDN_BUDGET_GET_BUDGET: - if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET) - return(-EINVAL); - - iocmd->amount = p->local->budget [iocmd->budget] .amount; - iocmd->used = p->local->budget [iocmd->budget] .used; - iocmd->period = p->local->budget [iocmd->budget] .period; - iocmd->period_started = p->local->budget [iocmd->budget] .period_started; - - return(0); - break; - - default: - return(-EINVAL); - break; - } - } - return(-ENODEV); -} -#endif diff --git a/drivers/isdn/isdn_cards.c b/drivers/isdn/isdn_cards.c index 06fa3e7d7..bdbcd4191 100644 --- a/drivers/isdn/isdn_cards.c +++ b/drivers/isdn/isdn_cards.c @@ -1,4 +1,4 @@ -/* $Id: isdn_cards.c,v 1.9 1999/04/12 12:33:11 fritz Exp $ +/* $Id: isdn_cards.c,v 1.10 1999/07/20 06:41:28 calle Exp $ * Linux ISDN subsystem, initialization for non-modularized drivers. * @@ -19,6 +19,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.c,v $ + * Revision 1.10 1999/07/20 06:41:28 calle + * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even + * compile, if not selected as modules. + * * Revision 1.9 1999/04/12 12:33:11 fritz * Changes from 2.0 tree. * @@ -67,12 +71,9 @@ extern void eicon_init(void); #endif #ifdef CONFIG_ISDN_DRV_AVMB1 -extern void avmb1_init(void); +extern void kcapi_init(void); extern void capi_init(void); extern void capidrv_init(void); -#ifdef CONFIG_PCI -extern int b1pci_init(void); -#endif #endif void @@ -88,10 +89,7 @@ isdn_cards_init(void) pcbit_init(); #endif #ifdef CONFIG_ISDN_DRV_AVMB1 - avmb1_init(); -#ifdef CONFIG_PCI - b1pci_init(); -#endif + kcapi_init(); capi_init(); capidrv_init(); #endif diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index 779a327fa..cac4be6dc 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -1,4 +1,4 @@ -/* $Id: isdn_common.c,v 1.75 1999/04/18 14:06:47 fritz Exp $ +/* $Id: isdn_common.c,v 1.86 1999/07/31 12:59:42 armin Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -21,6 +21,51 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.86 1999/07/31 12:59:42 armin + * Added tty fax capabilities. + * + * Revision 1.85 1999/07/29 16:58:35 armin + * Bugfix: DLE handling in isdn_readbchan() + * + * Revision 1.84 1999/07/25 16:21:10 keil + * fix number matching + * + * Revision 1.83 1999/07/13 21:02:05 werner + * Added limit possibilty of driver b_channel resources (ISDN_STAT_DISCH) + * + * Revision 1.82 1999/07/12 21:06:50 werner + * Fixed problem when loading more than one driver temporary + * + * Revision 1.81 1999/07/11 17:14:09 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * + * Revision 1.80 1999/07/07 10:14:00 detabc + * remove unused messages + * + * Revision 1.79 1999/07/05 23:51:30 werner + * Allow limiting of available HiSax B-chans per card. Controlled by hisaxctrl + * hisaxctrl id 10 <nr. of chans 0-2> + * + * Revision 1.78 1999/07/05 20:21:15 werner + * changes to use diversion sources for all kernel versions. + * removed static device, only proc filesystem used + * + * Revision 1.77 1999/07/01 08:29:50 keil + * compatibility to 2.3 kernel + * + * Revision 1.76 1999/06/29 16:16:44 calle + * Let ISDN_CMD_UNLOAD work with open isdn devices without crash again. + * Also right unlocking (ISDN_CMD_UNLOCK) is done now. + * isdnlog should check returncode of read(2) calls. + * * Revision 1.75 1999/04/18 14:06:47 fritz * Removed TIMRU stuff. * @@ -65,16 +110,6 @@ * brute force fix to avoid Ugh's in isdn_tty_write() * cleaned up some dead code * - * Revision 1.65 1998/06/07 00:20:00 fritz - * abc cleanup. - * - * Revision 1.64 1998/06/02 12:10:03 detabc - * wegen einer einstweiliger verfuegung gegen DW ist zur zeit - * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar - * - * Revision 1.63 1998/05/03 17:40:38 detabc - * Include abc-extension-support for >= 2.1.x Kernels in - * isdn_net.c and isdn_common.c. alpha-test OK and running ! * * Revision 1.62 1998/04/14 16:28:43 he * Fixed user space access with interrupts off and remaining @@ -335,6 +370,9 @@ #ifdef CONFIG_ISDN_AUDIO #include "isdn_audio.h" #endif +#ifdef CONFIG_ISDN_DIVERSION +#include <linux/isdn_divertif.h> +#endif CONFIG_ISDN_DIVERSION #include "isdn_v110.h" #include "isdn_cards.h" @@ -343,7 +381,7 @@ isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.75 $"; +static char *isdn_revision = "$Revision: 1.86 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -359,6 +397,11 @@ static char *isdn_audio_revision = ": none $"; #endif extern char *isdn_v110_revision; +#ifdef CONFIG_ISDN_DIVERSION +isdn_divert_if *divert_if = NULL; /* interface to diversion module */ +#endif CONFIG_ISDN_DIVERSION + + static int isdn_writebuf_stub(int, int, const u_char *, int, int); void @@ -449,6 +492,8 @@ isdn_wildmat(char *s, char *p) register int reverse; register int nostar = 1; + if (!(*s) && !(*p)) + return(1); for (; *p; s++, p++) switch (*p) { case '\\': @@ -760,7 +805,7 @@ isdn_status_callback(isdn_ctrl * c) return 0; } /* Try to find a network-interface which will accept incoming call */ - r = isdn_net_find_icall(di, c->arg, i, c->parm.setup); + r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, c->parm.setup)); switch (r) { case 0: /* No network-device replies. @@ -768,7 +813,13 @@ isdn_status_callback(isdn_ctrl * c) * These return 0 on no match, 1 on match and * 3 on eventually match, if CID is longer. */ - retval = isdn_tty_find_icall(di, c->arg, c->parm.setup); + if (c->command == ISDN_STAT_ICALL) + if ((retval = isdn_tty_find_icall(di, c->arg, c->parm.setup))) return(retval); +#ifdef CONFIG_ISDN_DIVERSION + if (divert_if) + if ((retval = divert_if->stat_callback(c))) + return(retval); /* processed */ +#endif CONFIG_ISDN_DIVERSION if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) { /* No tty responding */ cmd.driver = di; @@ -831,6 +882,20 @@ isdn_status_callback(isdn_ctrl * c) printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n", dev->drvid[di], c->arg, c->parm.num); isdn_tty_stat_callback(i, c); +#ifdef CONFIG_ISDN_DIVERSION + if (divert_if) + divert_if->stat_callback(c); +#endif CONFIG_ISDN_DIVERSION + break; + case ISDN_STAT_DISPLAY: +#ifdef ISDN_DEBUG_STATCALLB + printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display); +#endif + isdn_tty_stat_callback(i, c); +#ifdef CONFIG_ISDN_DIVERSION + if (divert_if) + divert_if->stat_callback(c); +#endif CONFIG_ISDN_DIVERSION break; case ISDN_STAT_DCONN: if (i < 0) @@ -869,6 +934,11 @@ isdn_status_callback(isdn_ctrl * c) isdn_v110_stat_callback(i, c); if (isdn_tty_stat_callback(i, c)) break; +#ifdef CONFIG_ISDN_DIVERSION + if (divert_if) + divert_if->stat_callback(c); +#endif CONFIG_ISDN_DIVERSION + break; break; case ISDN_STAT_BCONN: if (i < 0) @@ -924,7 +994,34 @@ isdn_status_callback(isdn_ctrl * c) return -1; isdn_info_update(); break; + case ISDN_STAT_DISCH: + save_flags(flags); + cli(); + for (i = 0; i < ISDN_MAX_CHANNELS; i++) + if ((dev->drvmap[i] == di) && + (dev->chanmap[i] == c->arg)) { + if (c->parm.num[0]) + dev->usage[i] &= ~ISDN_USAGE_DISABLED; + else + if (USG_NONE(dev->usage[i])) { + dev->usage[i] |= ISDN_USAGE_DISABLED; + } + else + retval = -1; + break; + } + restore_flags(flags); + isdn_info_update(); + break; case ISDN_STAT_UNLOAD: + while (dev->drv[di]->locks > 0) { + isdn_ctrl cmd; + cmd.driver = di; + cmd.arg = 0; + cmd.command = ISDN_CMD_UNLOCK; + isdn_command(&cmd); + dev->drv[di]->locks--; + } save_flags(flags); cli(); isdn_tty_stat_callback(i, c); @@ -932,6 +1029,7 @@ isdn_status_callback(isdn_ctrl * c) if (dev->drvmap[i] == di) { dev->drvmap[i] = -1; dev->chanmap[i] = -1; + dev->usage[i] &= ~ISDN_USAGE_DISABLED; } dev->drivers--; dev->channels -= dev->drv[di]->channels; @@ -941,7 +1039,7 @@ isdn_status_callback(isdn_ctrl * c) isdn_free_queue(&dev->drv[di]->rpqueue[i]); kfree(dev->drv[di]->rpqueue); kfree(dev->drv[di]->rcv_waitq); -#if LINUX_VERSION_CODE < 131841 +#ifndef COMPAT_HAS_NEW_WAITQ kfree(dev->drv[di]->snd_waitq); #endif kfree(dev->drv[di]); @@ -954,6 +1052,22 @@ isdn_status_callback(isdn_ctrl * c) break; case CAPI_PUT_MESSAGE: return(isdn_capi_rec_hl_msg(&c->parm.cmsg)); +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_STAT_FAXIND: + isdn_tty_stat_callback(i, c); + break; +#endif +#ifdef CONFIG_ISDN_AUDIO + case ISDN_STAT_AUDIO: + isdn_tty_stat_callback(i, c); + break; +#endif +#ifdef CONFIG_ISDN_DIVERSION + case ISDN_STAT_PROT: + case ISDN_STAT_REDIR: + if (divert_if) + return(divert_if->stat_callback(c)); +#endif CONFIG_ISDN_DIVERSION default: return -1; } @@ -989,10 +1103,10 @@ isdn_getnum(char **p) * of the mapping (di,ch)<->minor, happen during the sleep? --he */ int -#if LINUX_VERSION_CODE < 131841 -isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, struct wait_queue **sleep) -#else +#ifdef COMPAT_HAS_NEW_WAITQ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep) +#else +isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, struct wait_queue **sleep) #endif { int left; @@ -1021,7 +1135,7 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_que if (ISDN_AUDIO_SKB_LOCK(skb)) break; ISDN_AUDIO_SKB_LOCK(skb) = 1; - if (ISDN_AUDIO_SKB_DLECOUNT(skb)) { + if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) { char *p = skb->data; unsigned long DLEmask = (1 << channel); @@ -1320,11 +1434,11 @@ isdn_poll(struct file *file, poll_table * wait) return mask; } if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) { - poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait); if (drvidx < 0) { - printk(KERN_ERR "isdn_common: isdn_poll 1 -> what the hell\n"); - return POLLERR; + /* driver deregistered while file open */ + return POLLHUP; } + poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait); mask = POLLOUT | POLLWRNORM; if (dev->drv[drvidx]->stavail) { mask |= POLLIN | POLLRDNORM; @@ -1938,6 +2052,8 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) && ((pre_dev != d) || (pre_chan != dev->chanmap[i]))) continue; + if (dev->usage[i] & ISDN_USAGE_DISABLED) + continue; /* usage not allowed */ if (dev->drv[d]->flags & DRV_FLAG_RUNNING) { if (((dev->drv[d]->interface->features & features) == features) || (((dev->drv[d]->interface->features & vfeatures) == vfeatures) && @@ -2132,13 +2248,12 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) int j, k, m; ulong flags; -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&d->st_waitq); #endif if (d->flags & DRV_FLAG_RUNNING) return -1; - if (n < 1) - return 0; + if (n < 1) return 0; m = (adding) ? d->channels + n : n; @@ -2185,13 +2300,13 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) if ((adding) && (d->rcv_waitq)) kfree(d->rcv_waitq); -#if LINUX_VERSION_CODE < 131841 - if (!(d->rcv_waitq = (struct wait_queue **) - kmalloc(sizeof(struct wait_queue *) * m, GFP_KERNEL))) { -#else +#ifdef COMPAT_HAS_NEW_WAITQ d->rcv_waitq = (wait_queue_head_t *) kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_KERNEL); if (!d->rcv_waitq) { +#else + if (!(d->rcv_waitq = (struct wait_queue **) + kmalloc(sizeof(struct wait_queue *) * m, GFP_KERNEL))) { #endif printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n"); if (!adding) { @@ -2201,7 +2316,13 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) } return -1; } -#if LINUX_VERSION_CODE < 131841 +#ifdef COMPAT_HAS_NEW_WAITQ + d->snd_waitq = d->rcv_waitq + m; + for (j = 0; j < m; j++) { + init_waitqueue_head(&d->rcv_waitq[m]); + init_waitqueue_head(&d->snd_waitq[m]); + } +#else memset((char *) d->rcv_waitq, 0, sizeof(struct wait_queue *) * m); if ((adding) && (d->snd_waitq)) @@ -2218,12 +2339,6 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) return -1; } memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * m); -#else - d->snd_waitq = d->rcv_waitq + m; - for (j = 0; j < m; j++) { - init_waitqueue_head(&d->rcv_waitq[m]); - init_waitqueue_head(&d->snd_waitq[m]); - } #endif dev->channels += n; @@ -2245,6 +2360,60 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) * Low-level-driver registration */ + +#ifdef CONFIG_ISDN_DIVERSION +extern isdn_divert_if *divert_if; + +static char *map_drvname(int di) +{ + if ((di < 0) || (di >= ISDN_MAX_DRIVERS)) + return(NULL); + return(dev->drvid[di]); /* driver name */ +} /* map_drvname */ + +static int map_namedrv(char *id) +{ int i; + + for (i = 0; i < ISDN_MAX_DRIVERS; i++) + { if (!strcmp(dev->drvid[i],id)) + return(i); + } + return(-1); +} /* map_namedrv */ + +int DIVERT_REG_NAME(isdn_divert_if *i_div) +{ + if (i_div->if_magic != DIVERT_IF_MAGIC) + return(DIVERT_VER_ERR); + switch (i_div->cmd) + { + case DIVERT_CMD_REL: + if (divert_if != i_div) + return(DIVERT_REL_ERR); + divert_if = NULL; /* free interface */ + MOD_DEC_USE_COUNT; + return(DIVERT_NO_ERR); + + case DIVERT_CMD_REG: + if (divert_if) + return(DIVERT_REG_ERR); + i_div->ll_cmd = isdn_command; /* set command function */ + i_div->drv_to_name = map_drvname; + i_div->name_to_drv = map_namedrv; + MOD_INC_USE_COUNT; + divert_if = i_div; /* remember interface */ + return(DIVERT_NO_ERR); + + default: + return(DIVERT_CMD_ERR); + } +} /* DIVERT_REG_NAME */ + +EXPORT_SYMBOL(DIVERT_REG_NAME); + +#endif CONFIG_ISDN_DIVERSION + + EXPORT_SYMBOL(register_isdn); EXPORT_SYMBOL(register_isdn_module); EXPORT_SYMBOL(unregister_isdn_module); @@ -2352,18 +2521,18 @@ isdn_init(void) memset((char *) dev, 0, sizeof(isdn_dev)); init_timer(&dev->timer); dev->timer.function = isdn_timer_funct; -#if LINUX_VERSION_CODE < 131841 - dev->sem = MUTEX; -#else +#ifdef COMPAT_HAS_NEW_WAITQ init_MUTEX(&dev->sem); init_waitqueue_head(&dev->info_waitq); +#else + dev->sem = MUTEX; #endif for (i = 0; i < ISDN_MAX_CHANNELS; i++) { dev->drvmap[i] = -1; dev->chanmap[i] = -1; dev->m_idx[i] = -1; strcpy(dev->num[i], "???"); -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&dev->mdm.info[i].open_wait); init_waitqueue_head(&dev->mdm.info[i].close_wait); #endif @@ -2452,6 +2621,9 @@ cleanup_module(void) for (i = 0; i < ISDN_MAX_CHANNELS; i++) { isdn_tty_cleanup_xmit(&dev->mdm.info[i]); kfree(dev->mdm.info[i].xmit_buf - 4); +#ifdef CONFIG_ISDN_TTY_FAX + kfree(dev->mdm.info[i].fax); +#endif } if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n"); diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h index 3ba4855b7..e6fb122f6 100644 --- a/drivers/isdn/isdn_common.h +++ b/drivers/isdn/isdn_common.h @@ -1,4 +1,4 @@ -/* $Id: isdn_common.h,v 1.15 1999/04/18 14:06:50 fritz Exp $ +/* $Id: isdn_common.h,v 1.16 1999/07/01 08:29:54 keil Exp $ * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel). * @@ -21,6 +21,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.h,v $ + * Revision 1.16 1999/07/01 08:29:54 keil + * compatibility to 2.3 kernel + * * Revision 1.15 1999/04/18 14:06:50 fritz * Removed TIMRU stuff. * @@ -115,10 +118,10 @@ extern char *isdn_map_eaz2msn(char *msn, int di); extern void isdn_timer_ctrl(int tf, int onoff); extern void isdn_unexclusive_channel(int di, int ch); extern int isdn_getnum(char **); -#if LINUX_VERSION_CODE < 131841 -extern int isdn_readbchan(int, int, u_char *, u_char *, int, struct wait_queue**); -#else +#ifdef COMPAT_HAS_NEW_WAITQ extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *); +#else +extern int isdn_readbchan(int, int, u_char *, u_char *, int, struct wait_queue**); #endif extern int isdn_get_free_channel(int, int, int, int, int); extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *); diff --git a/drivers/isdn/isdn_concap.c b/drivers/isdn/isdn_concap.c index 1ca6c34e3..ca05f8bf3 100644 --- a/drivers/isdn/isdn_concap.c +++ b/drivers/isdn/isdn_concap.c @@ -1,10 +1,16 @@ -/* $Id: isdn_concap.c,v 1.5 1998/10/30 18:44:48 he Exp $ +/* $Id: isdn_concap.c,v 1.6 1999/08/22 20:26:01 calle Exp $ * Stuff to support the concap_proto by isdn4linux. isdn4linux - specific * stuff goes here. Stuff that depends only on the concap protocol goes to * another -- protocol specific -- source file. * * $Log: isdn_concap.c,v $ + * Revision 1.6 1999/08/22 20:26:01 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * * Revision 1.5 1998/10/30 18:44:48 he * pass return value from isdn_net_dial_req for dialmode change * @@ -53,7 +59,7 @@ int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb) { int tmp; - struct device *ndev = concap -> net_dev; + struct net_device *ndev = concap -> net_dev; isdn_net_local *lp = (isdn_net_local *) ndev->priv; IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name); @@ -66,7 +72,7 @@ int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb) int isdn_concap_dl_connect_req(struct concap_proto *concap) { - struct device *ndev = concap -> net_dev; + struct net_device *ndev = concap -> net_dev; isdn_net_local *lp = (isdn_net_local *) ndev->priv; int ret; IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name); diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index 9e4426af0..9d54f0127 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.84 1999/04/18 14:06:55 fritz Exp $ +/* $Id: isdn_net.c,v 1.89 1999/08/22 20:26:03 calle Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -21,6 +21,25 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.89 1999/08/22 20:26:03 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.88 1999/07/07 10:13:31 detabc + * remove unused messages + * + * Revision 1.87 1999/07/06 07:53:53 calle + * calls to dev_alloc_skb waste 16 bytes of memory, if we calculate the + * right header space for the lowlevel driver. using alloc_skb instead. + * + * Revision 1.86 1999/06/09 10:12:05 paul + * thinko in previous patch + * + * Revision 1.85 1999/06/07 19:42:39 paul + * isdn_net_getpeer() fixed to return correct `outgoing' flag + * * Revision 1.84 1999/04/18 14:06:55 fritz * Removed TIMRU stuff. * @@ -68,7 +87,7 @@ * Added more locking stuff in tty_write. * * Revision 1.71 1998/06/18 22:43:08 fritz - * Bugfix: Setting ndev->do_ioctl had beed accidetly removed at abc-cleanup. + * Bugfix: Setting ndev->do_ioctl had beed accidetly removed at cleanup. * * Revision 1.70 1998/06/17 19:50:49 he * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay()) @@ -80,40 +99,13 @@ * so autodial is suppressed for that device until it is switched on using * 'isdnctrl status dev-name on'. * - * Revision 1.68 1998/06/07 00:20:05 fritz - * abc cleanup. * - * Revision 1.67 1998/06/02 12:10:08 detabc - * wegen einer einstweiliger verfuegung gegen DW ist zur zeit - * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar * * Revision 1.66 1998/05/26 22:39:24 he * sync'ed with 2.1.102 where appropriate (CAPABILITY changes) * concap typo * cleared dev.tbusy in isdn_net BCONN status callback * - * Revision 1.65 1998/05/22 10:01:11 detabc - * in case of a icmp-unreach condition the tcp-keepalive-entrys - * will be dropped from the internal double-link-list (only abc-extension). - * send icmp unreach only if the skb->protocol == ETH_P_IP - * speedup abc-no-dchan redial - * - * Revision 1.64 1998/05/07 19:58:39 detabc - * bugfix in abc_delayed_hangup - * optimize keepalive-tests for abc_rawip - * - * Revision 1.63 1998/05/05 23:23:36 detabc - * change ICMP_HOST_UNREACH to ICMP_NET_UNREACH (only abc-ext.) - * set dev->tbusy to zero in isdn_net_unreachable() (only abc-ext.) - * drop all new packets and send ICMP_NET_UNREACH for - * min. dialwait to max. dialwait * 6 time. (only abc-ext.) - * change random-deliver of packets (small first) from all emcapsulation - * to only rawip with ABC-Router-Flag enabled. - * - * Revision 1.62 1998/05/03 17:40:42 detabc - * Include abc-extension-support for >= 2.1.x Kernels in - * isdn_net.c and isdn_common.c. alpha-test OK and running ! - * * Revision 1.61 1998/04/16 19:19:42 keil * Fix from vger (tx max qlength) * @@ -371,17 +363,17 @@ /* Prototypes */ int isdn_net_force_dial_lp(isdn_net_local *); -static int isdn_net_start_xmit(struct sk_buff *, struct device *); -static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *); +static int isdn_net_start_xmit(struct sk_buff *, struct net_device *); +static int isdn_net_xmit(struct net_device *, isdn_net_local *, struct sk_buff *); -char *isdn_net_revision = "$Revision: 1.84 $"; +char *isdn_net_revision = "$Revision: 1.89 $"; /* * Code for raw-networking over ISDN */ static void -isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason) +isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason) { if(skb) { @@ -403,7 +395,7 @@ isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason) } static void -isdn_net_reset(struct device *dev) +isdn_net_reset(struct net_device *dev) { #ifdef CONFIG_ISDN_X25 struct concap_device_ops * dops = @@ -426,10 +418,10 @@ isdn_net_reset(struct device *dev) /* Open/initialize the board. */ static int -isdn_net_open(struct device *dev) +isdn_net_open(struct net_device *dev) { int i; - struct device *p; + struct net_device *p; struct in_device *in_dev; isdn_net_reset(dev); @@ -616,7 +608,7 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) lp->stats.tx_packets++; lp->stats.tx_bytes += c->parm.length; if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) { - struct device *mdev; + struct net_device *mdev; if (lp->master) mdev = lp->master; else @@ -1060,7 +1052,7 @@ isdn_net_dial(void) * Perform hangup for a net-interface. */ void -isdn_net_hangup(struct device *d) +isdn_net_hangup(struct net_device *d) { isdn_net_local *lp = (isdn_net_local *) d->priv; isdn_ctrl cmd; @@ -1191,7 +1183,7 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) * Side-effects: ndev->tbusy is cleared on success. */ int -isdn_net_send_skb(struct device *ndev, isdn_net_local * lp, +isdn_net_send_skb(struct net_device *ndev, isdn_net_local * lp, struct sk_buff *skb) { int ret; @@ -1224,7 +1216,7 @@ isdn_net_send_skb(struct device *ndev, isdn_net_local * lp, */ static int -isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) +isdn_net_xmit(struct net_device *ndev, isdn_net_local * lp, struct sk_buff *skb) { int ret; @@ -1280,7 +1272,7 @@ isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) } static void -isdn_net_adjust_hdr(struct sk_buff *skb, struct device *dev) +isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev) { isdn_net_local *lp = (isdn_net_local *) dev->priv; if (!skb) @@ -1300,7 +1292,7 @@ isdn_net_adjust_hdr(struct sk_buff *skb, struct device *dev) * and start dialing. */ static int -isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) +isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) { isdn_net_local *lp = (isdn_net_local *) ndev->priv; #ifdef CONFIG_ISDN_X25 @@ -1452,9 +1444,9 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) * Shutdown a net-interface. */ static int -isdn_net_close(struct device *dev) +isdn_net_close(struct net_device *dev) { - struct device *p; + struct net_device *p; #ifdef CONFIG_ISDN_X25 struct concap_proto * cprot = ( (isdn_net_local *) dev->priv ) -> netdev -> cprot; @@ -1490,7 +1482,7 @@ isdn_net_close(struct device *dev) * Get statistics */ static struct enet_statistics * -isdn_net_get_stats(struct device *dev) +isdn_net_get_stats(struct net_device *dev) { isdn_net_local *lp = (isdn_net_local *) dev->priv; return &lp->stats; @@ -1506,7 +1498,7 @@ isdn_net_get_stats(struct device *dev) */ static unsigned short -isdn_net_type_trans(struct sk_buff *skb, struct device *dev) +isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev) { struct ethhdr *eth; unsigned char *rawp; @@ -1553,7 +1545,7 @@ static void isdn_net_slarp_send(isdn_net_local *lp, int is_reply) { unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; - struct sk_buff *skb = dev_alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp)); + struct sk_buff *skb = alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp), GFP_ATOMIC); unsigned long t = (jiffies / HZ * 1000000); int len; cisco_hdr *ch; @@ -1644,7 +1636,7 @@ isdn_net_slarp_out(void) * Got a packet from ISDN-Channel. */ static void -isdn_net_receive(struct device *ndev, struct sk_buff *skb) +isdn_net_receive(struct net_device *ndev, struct sk_buff *skb) { isdn_net_local *lp = (isdn_net_local *) ndev->priv; isdn_net_local *olp = lp; /* original 'lp' */ @@ -1793,7 +1785,7 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb) } static int -my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type, +my_eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); @@ -1838,7 +1830,7 @@ my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type, */ static int -isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, +isdn_net_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned plen) { isdn_net_local *lp = dev->priv; @@ -1894,7 +1886,7 @@ isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, static int isdn_net_rebuild_header(struct sk_buff *skb) { - struct device *dev = skb->dev; + struct net_device *dev = skb->dev; isdn_net_local *lp = dev->priv; int ret = 0; @@ -1926,7 +1918,7 @@ isdn_net_rebuild_header(struct sk_buff *skb) * Interface-setup. (called just after registering a new interface) */ static int -isdn_net_init(struct device *ndev) +isdn_net_init(struct net_device *ndev) { ushort max_hlhdr_len = 0; isdn_net_local *lp = (isdn_net_local *) ndev->priv; @@ -2437,7 +2429,7 @@ isdn_net_force_dial(char *name) * Allocate a new network-interface and initialize its data structures. */ char * -isdn_net_new(char *name, struct device *master) +isdn_net_new(char *name, struct net_device *master) { isdn_net_dev *netdev; @@ -2466,8 +2458,8 @@ isdn_net_new(char *name, struct device *master) netdev->local->p_encap = ISDN_NET_ENCAP_RAWIP; if (master) { /* Device shall be a slave */ - struct device *p = (((isdn_net_local *) master->priv)->slave); - struct device *q = master; + struct net_device *p = (((isdn_net_local *) master->priv)->slave); + struct net_device *q = master; netdev->local->master = master; /* Put device at end of slave-chain */ @@ -2928,7 +2920,7 @@ isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer) /* for pre-bound channels, we need this extra check */ if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN; strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN); - phone->outgoing=USG_OUTGOING(idx); + phone->outgoing=USG_OUTGOING(dev->usage[idx]); if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT; return 0; } @@ -3004,7 +2996,7 @@ int isdn_net_force_hangup(char *name) { isdn_net_dev *p = isdn_net_findif(name); - struct device *q; + struct net_device *q; if (p) { if (p->local->isdn_device < 0) diff --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h index 79d4ff615..bd48c592c 100644 --- a/drivers/isdn/isdn_net.h +++ b/drivers/isdn/isdn_net.h @@ -1,4 +1,4 @@ -/* $Id: isdn_net.h,v 1.9 1999/04/12 12:33:27 fritz Exp $ +/* $Id: isdn_net.h,v 1.10 1999/08/22 20:26:06 calle Exp $ * header for Linux ISDN subsystem, network related functions (linklevel). * @@ -21,6 +21,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.h,v $ + * Revision 1.10 1999/08/22 20:26:06 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * * Revision 1.9 1999/04/12 12:33:27 fritz * Changes from 2.0 tree. * @@ -100,7 +106,7 @@ typedef struct cisco_slarp { #define CISCO_SLARP_REQUEST 1 #define CISCO_SLARP_KEEPALIVE 2 -extern char *isdn_net_new(char *, struct device *); +extern char *isdn_net_new(char *, struct net_device *); extern char *isdn_net_newslave(char *); extern int isdn_net_rm(char *); extern int isdn_net_rmall(void); @@ -112,13 +118,13 @@ extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *); extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *); extern int isdn_net_delphone(isdn_net_ioctl_phone *); extern int isdn_net_find_icall(int, int, int, setup_parm); -extern void isdn_net_hangup(struct device *); +extern void isdn_net_hangup(struct net_device *); extern void isdn_net_dial(void); extern void isdn_net_autohup(void); extern int isdn_net_force_hangup(char *); extern int isdn_net_force_dial(char *); extern isdn_net_dev *isdn_net_findif(char *); -extern int isdn_net_send_skb(struct device *, isdn_net_local *, +extern int isdn_net_send_skb(struct net_device *, isdn_net_local *, struct sk_buff *); extern int isdn_net_rcv_skb(int, struct sk_buff *); extern void isdn_net_slarp_out(void); diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index 7bce66d44..78c71c7fe 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.47 1999/04/18 14:06:59 fritz Exp $ +/* $Id: isdn_ppp.c,v 1.52 1999/08/22 20:26:07 calle Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,26 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.52 1999/08/22 20:26:07 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.51 1999/08/18 16:19:17 hipp + * applied MPPP-resize-headroom patch + * + * Revision 1.50 1999/08/16 07:11:41 hipp + * Additional VJ decomp-buffer-size increased from 40 to 128 + * + * Revision 1.49 1999/07/06 07:47:11 calle + * bugfix: dev_alloc_skb only reserve 16 bytes. We need to look at the + * hdrlen the driver want. So I changed dev_alloc_skb calls + * to alloc_skb and skb_reserve. + * + * Revision 1.48 1999/07/01 08:29:56 keil + * compatibility to 2.3 kernel + * * Revision 1.47 1999/04/18 14:06:59 fritz * Removed TIMRU stuff. * @@ -261,7 +281,7 @@ static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb, static void isdn_ppp_free_mpqueue(isdn_net_dev *); #endif -char *isdn_ppp_revision = "$Revision: 1.47 $"; +char *isdn_ppp_revision = "$Revision: 1.52 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; static struct isdn_ppp_compressor *ipc_head = NULL; @@ -430,7 +450,7 @@ isdn_ppp_wakeup_daemon(isdn_net_local * lp) ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; -#if LINUX_VERSION_CODE < 131841 +#ifndef COMPAT_HAS_NEW_WAITQ if (ippp_table[lp->ppp_slot]->wq) #endif wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq); @@ -450,10 +470,10 @@ isdn_ppp_closewait(int slot) return 0; is = ippp_table[slot]; -#if LINUX_VERSION_CODE < 131841 - if (is->state && is->wq) -#else +#ifdef COMPAT_HAS_NEW_WAITQ if (is->state) +#else + if (is->state && is->wq) #endif wake_up_interruptible(&is->wq); @@ -519,10 +539,10 @@ isdn_ppp_open(int min, struct file *file) is->mru = 1524; /* MRU, default 1524 */ is->maxcid = 16; /* VJ: maxcid */ is->tk = current; -#if LINUX_VERSION_CODE < 131841 - is->wq = NULL; /* read() wait queue */ -#else +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&is->wq); +#else + is->wq = NULL; /* read() wait queue */ #endif is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ is->last = is->rq; @@ -891,7 +911,7 @@ isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot) is->last = bl->next; restore_flags(flags); -#if LINUX_VERSION_CODE < 131841 +#ifndef COMPAT_HAS_NEW_WAITQ if (is->wq) #endif wake_up_interruptible(&is->wq); @@ -983,13 +1003,21 @@ isdn_ppp_write(int min, struct file *file, const char *buf, int count) if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && lp->dialstate == 0 && (lp->flags & ISDN_NET_CONNECTED)) { + unsigned short hl; int cnt; struct sk_buff *skb; - skb = dev_alloc_skb(count); + /* + * we need to reserve enought space in front of + * sk_buff. old call to dev_alloc_skb only reserved + * 16 bytes, now we are looking what the driver want + */ + hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + skb = alloc_skb(hl+count, GFP_ATOMIC); if (!skb) { printk(KERN_WARNING "isdn_ppp_write: out of memory!\n"); return count; } + skb_reserve(skb, hl); if (copy_from_user(skb_put(skb, count), buf, count)) return -EFAULT; if (is->debug & 0x40) { @@ -1271,7 +1299,7 @@ void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buf static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto) { - struct device *dev = &net_dev->dev; + struct net_device *dev = &net_dev->dev; struct ippp_struct *is = ippp_table[lp->ppp_slot]; if (is->debug & 0x10) { @@ -1336,7 +1364,7 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff { struct sk_buff *skb_old = skb; int pkt_len; - skb = dev_alloc_skb(skb_old->len + 40); + skb = dev_alloc_skb(skb_old->len + 128); if (!skb) { printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); @@ -1345,7 +1373,7 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff return; } skb->dev = dev; - skb_put(skb, skb_old->len + 40); + skb_put(skb, skb_old->len + 128); memcpy(skb->data, skb_old->data, skb_old->len); skb->mac.raw = skb->data; pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp, @@ -1399,9 +1427,17 @@ static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) struct sk_buff *skb = *skb_p; if(skb_headroom(skb) < len) { - printk(KERN_ERR "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len); + struct sk_buff *nskb = skb_realloc_headroom(skb, len); + + if (!nskb) { + printk(KERN_ERR "isdn_ppp_skb_push: can't realloc headroom!\n"); + dev_kfree_skb(skb); + return NULL; + } + printk(KERN_DEBUG "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len); dev_kfree_skb(skb); - return NULL; + *skb_p = nskb; + return skb_push(nskb, len); } return skb_push(skb,len); } @@ -1416,9 +1452,9 @@ static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) */ int -isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) +isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev) { - struct device *mdev = ((isdn_net_local *) (dev->priv))->master; /* get master (for redundancy) */ + struct net_device *mdev = ((isdn_net_local *) (netdev->priv))->master; /* get master (for redundancy) */ isdn_net_local *lp,*mlp; isdn_net_dev *nd; unsigned int proto = PPP_IP; /* 0x21 */ @@ -1427,8 +1463,8 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) if (mdev) mlp = (isdn_net_local *) (mdev->priv); else { - mdev = dev; - mlp = (isdn_net_local *) (dev->priv); + mdev = netdev; + mlp = (isdn_net_local *) (netdev->priv); } nd = mlp->netdev; /* get master lp */ ipts = ippp_table[mlp->ppp_slot]; @@ -1441,7 +1477,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) ipts->old_pa_dstaddr = mdev->pa_dstaddr; #endif if (ipts->debug & 0x1) - printk(KERN_INFO "%s: IP frame delayed.\n", dev->name); + printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name); return 1; } @@ -1506,12 +1542,19 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) #ifdef CONFIG_ISDN_PPP_VJ if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */ struct sk_buff *new_skb; - - new_skb = dev_alloc_skb(skb->len); + unsigned short hl; + /* + * we need to reserve enought space in front of + * sk_buff. old call to dev_alloc_skb only reserved + * 16 bytes, now we are looking what the driver want. + */ + hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC); if (new_skb) { u_char *buf; int pktlen; + skb_reserve(new_skb, hl); new_skb->dev = skb->dev; skb_put(new_skb, skb->len); buf = skb->data; @@ -1611,9 +1654,9 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot); } - if (isdn_net_send_skb(dev, lp, skb)) { + if (isdn_net_send_skb(netdev, lp, skb)) { if (lp->sav_skb) { /* whole sav_skb processing with disabled IRQs ?? */ - printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", dev->name); + printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", netdev->name); dev_kfree_skb(skb); } else lp->sav_skb = skb; @@ -1985,7 +2028,7 @@ isdn_ppp_timer_timeout(void) */ static int -isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct device *dev) +isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev) { struct ppp_stats *res, t; @@ -2025,7 +2068,7 @@ isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct device *dev) } int -isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int error=0; char *r; @@ -2088,7 +2131,7 @@ isdn_ppp_dial_slave(char *name) #ifdef CONFIG_ISDN_MPP isdn_net_dev *ndev; isdn_net_local *lp; - struct device *sdev; + struct net_device *sdev; if (!(ndev = isdn_net_findif(name))) return 1; @@ -2119,7 +2162,7 @@ isdn_ppp_hangup_slave(char *name) #ifdef CONFIG_ISDN_MPP isdn_net_dev *ndev; isdn_net_local *lp; - struct device *sdev; + struct net_device *sdev; if (!(ndev = isdn_net_findif(name))) return 1; diff --git a/drivers/isdn/isdn_ppp.h b/drivers/isdn/isdn_ppp.h index 2c60240a5..1a1dcfcfe 100644 --- a/drivers/isdn/isdn_ppp.h +++ b/drivers/isdn/isdn_ppp.h @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.h,v 1.13 1998/03/22 18:50:50 hipp Exp $ +/* $Id: isdn_ppp.h,v 1.14 1999/08/22 20:26:10 calle Exp $ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.h,v $ + * Revision 1.14 1999/08/22 20:26:10 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * * Revision 1.13 1998/03/22 18:50:50 hipp * Added BSD Compression for syncPPP .. UNTESTED at the moment * @@ -78,9 +84,9 @@ extern int isdn_ppp_init(void); extern void isdn_ppp_cleanup(void); extern int isdn_ppp_free(isdn_net_local *); extern int isdn_ppp_bind(isdn_net_local *); -extern int isdn_ppp_xmit(struct sk_buff *, struct device *); +extern int isdn_ppp_xmit(struct sk_buff *, struct net_device *); extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *); -extern int isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int); +extern int isdn_ppp_dev_ioctl(struct net_device *, struct ifreq *, int); extern unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *); extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long); extern void isdn_ppp_release(int, struct file *); diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index bfa895424..e502f90da 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.c,v 1.63 1999/04/12 12:33:39 fritz Exp $ +/* $Id: isdn_tty.c,v 1.73 1999/08/28 21:56:27 keil Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -20,6 +20,46 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.73 1999/08/28 21:56:27 keil + * misplaced #endif caused ttyI crash in 2.3.X + * + * Revision 1.72 1999/07/31 12:59:45 armin + * Added tty fax capabilities. + * + * Revision 1.71 1999/07/27 10:34:34 armin + * Fixed last change. Did not compile with AUDIO support off. + * + * Revision 1.70 1999/07/25 16:17:58 keil + * Fix Suspend/Resume + * + * Revision 1.69 1999/07/25 12:56:15 armin + * isdn_tty_at_cout() now queues the message if online and + * data is in queue or flip buffer is full. + * needed for fax connections. + * + * Revision 1.68 1999/07/11 17:51:51 armin + * Bugfix, "-" was missing for AT&L settings. + * + * Revision 1.67 1999/07/11 17:14:12 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * + * Revision 1.66 1999/07/07 10:13:46 detabc + * remove unused messages + * + * Revision 1.65 1999/07/04 21:01:59 werner + * Added support for keypad and display (ported from 2.0) + * + * Revision 1.64 1999/07/01 08:30:00 keil + * compatibility to 2.3 kernel + * * Revision 1.63 1999/04/12 12:33:39 fritz * Changes from 2.0 tree. * @@ -61,24 +101,12 @@ * brute force fix to avoid Ugh's in isdn_tty_write() * cleaned up some dead code * - * Revision 1.54 1998/06/07 00:20:13 fritz - * abc cleanup. * - * Revision 1.53 1998/06/02 12:10:16 detabc - * wegen einer einstweiliger verfuegung gegen DW ist zur zeit - * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar * * Revision 1.52 1998/03/19 13:18:21 keil * Start of a CAPI like interface for supplementary Service * first service: SUSPEND * - * Revision 1.51 1998/03/08 14:26:11 detabc - * change kfree_skb to dev_kfree_skb - * remove SET_SKB_FREE - * - * Revision 1.50 1998/03/08 13:14:28 detabc - * abc-extension support for kernels > 2.1.x - * first try (sorry experimental) * * Revision 1.49 1998/03/08 00:01:59 fritz * Bugfix: Lowlevel module usage and channel usage were not @@ -303,7 +331,6 @@ static int isdn_tty_edit_at(const char *, int, modem_info *, int); static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int); static void isdn_tty_modem_reset_regs(modem_info *, int); static void isdn_tty_cmd_ATA(modem_info *); -static void isdn_tty_at_cout(char *, modem_info *); static void isdn_tty_flush_buffer(struct tty_struct *); static void isdn_tty_modem_result(int, modem_info *); #ifdef CONFIG_ISDN_AUDIO @@ -321,64 +348,8 @@ static int bit2si[8] = static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.63 $"; - -#define DLE 0x10 -#define ETX 0x03 -#define DC4 0x14 +char *isdn_tty_revision = "$Revision: 1.73 $"; -/* - * Definition of some special Registers of AT-Emulator - */ -#define REG_RINGATA 0 -#define REG_RINGCNT 1 -#define REG_ESC 2 -#define REG_CR 3 -#define REG_LF 4 -#define REG_BS 5 - -#define REG_WAITC 7 - -#define REG_RESP 12 -#define BIT_RESP 1 -#define REG_RESPNUM 12 -#define BIT_RESPNUM 2 -#define REG_ECHO 12 -#define BIT_ECHO 4 -#define REG_DCD 12 -#define BIT_DCD 8 -#define REG_CTS 12 -#define BIT_CTS 16 -#define REG_DTRR 12 -#define BIT_DTRR 32 -#define REG_DSR 12 -#define BIT_DSR 64 -#define REG_CPPP 12 -#define BIT_CPPP 128 - -#define REG_T70 13 -#define BIT_T70 2 -#define BIT_T70_EXT 32 -#define REG_DTRHUP 13 -#define BIT_DTRHUP 4 -#define REG_RESPXT 13 -#define BIT_RESPXT 8 -#define REG_CIDONCE 13 -#define BIT_CIDONCE 16 -#define REG_RUNG 13 -#define BIT_RUNG 64 -#define REG_RESRXT 13 -#define BIT_RESRXT 128 - -#define REG_L2PROT 14 -#define REG_L3PROT 15 -#define REG_PSIZE 16 -#define REG_WSIZE 17 -#define REG_SI1 18 -#define REG_SI2 19 -#define REG_SI1I 20 -#define REG_PLAN 21 -#define REG_SCREEN 22 /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() * to stuff incoming data directly into a tty's flip-buffer. This @@ -514,7 +485,7 @@ isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) #ifdef CONFIG_ISDN_AUDIO ifmt = 1; - if (info->vonline) + if ((info->vonline) && (!info->emu.vpar[4])) isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt); if ((info->vonline & 1) && (info->emu.vpar[1])) isdn_audio_calc_silence(info, skb->data, skb->len, ifmt); @@ -580,6 +551,15 @@ isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) ISDN_AUDIO_SKB_DLECOUNT(skb) = isdn_tty_countDLE(skb->data, skb->len); } +#ifdef CONFIG_ISDN_TTY_FAX + else { + if (info->faxonline & 2) { + isdn_tty_fax_bitorder(info, skb); + ISDN_AUDIO_SKB_DLECOUNT(skb) = + isdn_tty_countDLE(skb->data, skb->len); + } + } +#endif #endif /* Try to deliver directly via tty-flip-buf if queue is empty */ save_flags(flags); @@ -909,6 +889,35 @@ isdn_tty_modem_ncarrier(modem_info * info) } } +/* + * return the usage calculated by si and layer 2 protocol + */ +int +isdn_calc_usage(int si, int l2) +{ + int usg = ISDN_USAGE_MODEM; + +#ifdef CONFIG_ISDN_AUDIO + if (si == 1) { + switch(l2) { + case ISDN_PROTO_L2_MODEM: + usg = ISDN_USAGE_MODEM; + break; +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_PROTO_L2_FAX: + usg = ISDN_USAGE_FAX; + break; +#endif + case ISDN_PROTO_L2_TRANS: + default: + usg = ISDN_USAGE_VOICE; + break; + } + } +#endif + return(usg); +} + /* isdn_tty_dial() performs dialing of a tty an the necessary * setup of the lower levels before that. */ @@ -928,8 +937,14 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -967,6 +982,12 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); +#ifdef CONFIG_ISDN_TTY_FAX + if (l2 == ISDN_PROTO_L2_FAX) { + cmd.parm.fax = info->fax; + info->fax->direction = ISDN_TTY_FAX_CONN_OUT; + } +#endif isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; @@ -989,7 +1010,7 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) * ISDN-line (hangup). The usage-status is cleared * and some cleanup is done also. */ -static void +void isdn_tty_modem_hup(modem_info * info, int local) { isdn_ctrl cmd; @@ -1010,6 +1031,12 @@ isdn_tty_modem_hup(modem_info * info, int local) } #ifdef CONFIG_ISDN_AUDIO info->vonline = 0; +#ifdef CONFIG_ISDN_TTY_FAX + info->faxonline = 0; + info->fax->phase = ISDN_FAX_PHASE_IDLE; +#endif + info->emu.vpar[4] = 0; + info->emu.vpar[5] = 8; if (info->dtmf_state) { kfree(info->dtmf_state); info->dtmf_state = NULL; @@ -1041,9 +1068,8 @@ isdn_tty_modem_hup(modem_info * info, int local) } isdn_all_eaz(info->isdn_driver, info->isdn_channel); info->emu.mdmreg[REG_RINGCNT] = 0; - usage = ((info->emu.mdmreg[REG_SI1I] != 1) || - (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? - ISDN_USAGE_MODEM : ISDN_USAGE_VOICE; + usage = isdn_calc_usage(info->emu.mdmreg[REG_SI1I], + info->emu.mdmreg[REG_L2PROT]); isdn_free_channel(info->isdn_driver, info->isdn_channel, usage); } @@ -1130,8 +1156,14 @@ isdn_tty_resume(char *id, modem_info * info, atemu * m) si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -1184,11 +1216,11 @@ isdn_tty_resume(char *id, modem_info * info, atemu * m) cmd.parm.cmsg.para[5] = l; strncpy(&cmd.parm.cmsg.para[6], id, l); cmd.command =CAPI_PUT_MESSAGE; -/* info->dialing = 1; - strcpy(dev->num[i], n); + info->dialing = 1; +// strcpy(dev->num[i], n); isdn_info_update(); -*/ isdn_command(&cmd); + isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); } } @@ -1218,8 +1250,14 @@ isdn_tty_send_msg(modem_info * info, atemu * m, char *msg) si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -1863,11 +1901,11 @@ isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios) static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info) { -#if LINUX_VERSION_CODE < 131841 +#ifdef COMPAT_HAS_NEW_WAITQ + DECLARE_WAITQUEUE(wait, NULL); +#else struct wait_queue wait = {current, NULL}; -#else - DECLARE_WAITQUEUE(wait, NULL); #endif int do_clocal = 0; unsigned long flags; @@ -1946,7 +1984,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * restore_flags(flags); info->blocked_open++; while (1) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ISDN_ASYNC_INITIALIZED)) { #ifdef MODEM_DO_RESTART @@ -2191,6 +2229,7 @@ isdn_tty_reset_profile(atemu * m) m->profile[18] = 4; m->profile[19] = 0; m->profile[20] = 0; + m->profile[23] = 0; m->pmsn[0] = '\0'; m->plmsn[0] = '\0'; } @@ -2203,6 +2242,42 @@ isdn_tty_modem_reset_vpar(atemu * m) m->vpar[1] = 0; /* Silence detection level (0 = none ) */ m->vpar[2] = 70; /* Silence interval (7 sec. ) */ m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */ + m->vpar[4] = 0; /* DTMF detection level (0 = softcode ) */ + m->vpar[5] = 8; /* DTMF interval (8 * 5 ms. ) */ +} +#endif + +#ifdef CONFIG_ISDN_TTY_FAX +static void +isdn_tty_modem_reset_faxpar(modem_info * info) +{ + T30_s *f = info->fax; + + f->code = 0; + f->phase = ISDN_FAX_PHASE_IDLE; + f->direction = 0; + f->resolution = 1; /* fine */ + f->rate = 5; /* 14400 bit/s */ + f->width = 0; + f->length = 0; + f->compression = 0; + f->ecm = 0; + f->binary = 0; + f->scantime = 0; + memset(&f->id[0], 32, FAXIDLEN - 1); + f->id[FAXIDLEN - 1] = 0; + f->badlin = 0; + f->badmul = 0; + f->bor = 0; + f->nbc = 0; + f->cq = 0; + f->cr = 0; + f->ctcrty = 0; + f->minsp = 0; + f->phcto = 30; + f->rel = 0; + memset(&f->pollid[0], 32, FAXIDLEN - 1); + f->pollid[FAXIDLEN - 1] = 0; } #endif @@ -2219,6 +2294,9 @@ isdn_tty_modem_reset_regs(modem_info * info, int force) #ifdef CONFIG_ISDN_AUDIO isdn_tty_modem_reset_vpar(m); #endif +#ifdef CONFIG_ISDN_TTY_FAX + isdn_tty_modem_reset_faxpar(info); +#endif m->mdmcmdl = 0; } @@ -2291,10 +2369,16 @@ isdn_tty_modem_init(void) } for (i = 0; i < ISDN_MAX_CHANNELS; i++) { info = &m->info[i]; -#if LINUX_VERSION_CODE < 131841 - info->write_sem = MUTEX; -#else +#ifdef CONFIG_ISDN_TTY_FAX + if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) { + printk(KERN_ERR "Could not allocate fax t30-buffer\n"); + return -3; + } +#endif +#ifdef COMPAT_HAS_NEW_WAITQ init_MUTEX(&info->write_sem); +#else + info->write_sem = MUTEX; #endif sprintf(info->last_cause, "0000"); sprintf(info->last_num, "none"); @@ -2312,12 +2396,12 @@ isdn_tty_modem_init(void) info->blocked_open = 0; info->callout_termios = m->cua_modem.init_termios; info->normal_termios = m->tty_modem.init_termios; -#if LINUX_VERSION_CODE < 131841 - info->open_wait = 0; - info->close_wait = 0; -#else +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); +#else + info->open_wait = 0; + info->close_wait = 0; #endif info->isdn_driver = -1; info->isdn_channel = -1; @@ -2371,8 +2455,15 @@ isdn_tty_match_icall(char *cid, atemu *emu, int di) break; } return ret; - } else - return isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di)); + } else { + int tmp; + tmp = isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di)); +#ifdef ISDN_DEBUG_MODEM_ICALL + printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n", + isdn_map_eaz2msn(emu->msn, di), tmp); +#endif + return tmp; + } } /* @@ -2424,7 +2515,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ idx = isdn_dc2minor(di, ch); #ifdef ISDN_DEBUG_MODEM_ICALL - printk(KERN_DEBUG "m_fi: match1\n"); + printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret); printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx, info->flags, info->isdn_driver, info->isdn_channel, dev->usage[idx]); @@ -2446,8 +2537,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) info->drv_index = idx; dev->m_idx[idx] = info->line; dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; - dev->usage[idx] |= ((si1 != 1) || (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? - ISDN_USAGE_MODEM : ISDN_USAGE_VOICE; + dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); strcpy(dev->num[idx], nr); strcpy(info->emu.cpn, eaz); info->emu.mdmreg[REG_SI1I] = si2bit[si1]; @@ -2514,6 +2604,19 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) /* Signal cause to tty-device */ strncpy(info->last_cause, c->parm.num, 5); return 1; + case ISDN_STAT_DISPLAY: +#ifdef ISDN_TTY_STAT_DEBUG + printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line); +#endif + /* Signal display to tty-device */ + if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) && + !(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) { + isdn_tty_at_cout("\r\n", info); + isdn_tty_at_cout("DISPLAY: ", info); + isdn_tty_at_cout(c->parm.display, info); + isdn_tty_at_cout("\r\n", info); + } + return 1; case ISDN_STAT_DCONN: #ifdef ISDN_TTY_STAT_DEBUG printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line); @@ -2612,6 +2715,27 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) } } return 1; +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_STAT_FAXIND: + if (TTY_IS_ACTIVE(info)) { + isdn_tty_fax_command(info); + } + break; +#endif +#ifdef CONFIG_ISDN_AUDIO + case ISDN_STAT_AUDIO: + if (TTY_IS_ACTIVE(info)) { + switch(c->parm.num[0]) { + case ISDN_AUDIO_DTMF: + if (info->vonline) { + isdn_audio_put_dle_code(info, + c->parm.num[1]); + } + break; + } + } + break; +#endif } } return 0; @@ -2621,13 +2745,13 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) Modem-Emulator-Routines *********************************************************************/ -#define cmdchar(c) ((c>' ')&&(c<=0x7f)) +#define cmdchar(c) ((c>=' ')&&(c<=0x7f)) /* * Put a message from the AT-emulator into receive-buffer of tty, * convert CR, LF, and BS to values in modem-registers 3, 4 and 5. */ -static void +void isdn_tty_at_cout(char *msg, modem_info * info) { struct tty_struct *tty; @@ -2635,6 +2759,8 @@ isdn_tty_at_cout(char *msg, modem_info * info) char *p; char c; ulong flags; + struct sk_buff *skb = 0; + char *sp = 0; if (!msg) { printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n"); @@ -2643,6 +2769,34 @@ isdn_tty_at_cout(char *msg, modem_info * info) save_flags(flags); cli(); tty = info->tty; + if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { + restore_flags(flags); + return; + } + + /* use queue instead of direct flip, if online and */ + /* data is in queue or flip buffer is full */ + if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) || + (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) { + skb = alloc_skb(strlen(msg) +#ifdef CONFIG_ISDN_AUDIO + + sizeof(isdn_audio_skb) +#endif + , GFP_ATOMIC); + if (!skb) { + restore_flags(flags); + return; + } +#ifdef CONFIG_ISDN_AUDIO + skb_reserve(skb, sizeof(isdn_audio_skb)); +#endif + sp = skb_put(skb, strlen(msg)); +#ifdef CONFIG_ISDN_AUDIO + ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; + ISDN_AUDIO_SKB_LOCK(skb) = 0; +#endif + } + for (p = msg; *p; p++) { switch (*p) { case '\r': @@ -2657,16 +2811,26 @@ isdn_tty_at_cout(char *msg, modem_info * info) default: c = *p; } - if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { - restore_flags(flags); - return; + if (skb) { + *sp++ = c; + } else { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + tty_insert_flip_char(tty, c, 0); } - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - break; - tty_insert_flip_char(tty, c, 0); } - restore_flags(flags); - queue_task(&tty->flip.tqueue, &tq_timer); + if (skb) { + __skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb); + dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len; + restore_flags(flags); + /* Schedule dequeuing */ + if ((dev->modempoll) && (info->rcvsched)) + isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); + + } else { + restore_flags(flags); + queue_task(&tty->flip.tqueue, &tq_timer); + } } /* @@ -2847,7 +3011,7 @@ isdn_tty_modem_result(int code, modem_info * info) break; case 2: /* Append CPN, if enabled */ - if ((m->mdmreg[REG_RESRXT] & BIT_RESRXT)) { + if ((m->mdmreg[REG_CPN] & BIT_CPN)) { sprintf(s, "/%s", m->cpn); isdn_tty_at_cout(s, info); } @@ -2954,8 +3118,9 @@ isdn_tty_getdial(char *p, char *q,int cnt) int limit=39; /* MUST match the size in isdn_tty_parse to avoid buffer overflow */ - while (strchr("0123456789,#.*WPTS-", *p) && *p && --cnt>0) { - if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) + while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) { + if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) || + (*p == '*') || (*p == '#')) *q++ = *p; p++; if(!--limit) @@ -3008,6 +3173,9 @@ isdn_tty_report(modem_info * info) case ISDN_PROTO_L2_MODEM: isdn_tty_at_cout("modem", info); break; + case ISDN_PROTO_L2_FAX: + isdn_tty_at_cout("fax", info); + break; default: isdn_tty_at_cout("unknown", info); break; @@ -3107,8 +3275,8 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) /* &L -Set Numbers to listen on */ p[0]++; i = 0; - while ((strchr("0123456789,*[]?;", *p[0])) && - (i < ISDN_LMSNLEN)) + while ((strchr("0123456789,-*[]?;", *p[0])) && + (i < ISDN_LMSNLEN) && *p[0]) m->lmsn[i++] = *p[0]++; m->lmsn[i] = '\0'; break; @@ -3264,7 +3432,7 @@ isdn_tty_cmd_ATS(char **p, modem_info * info) int bval; mreg = isdn_getnum(p); - if (mreg < 0 || mreg > ISDN_MODEM_ANZREG) + if (mreg < 0 || mreg >= ISDN_MODEM_ANZREG) PARSE_ERROR1; switch (*p[0]) { case '=': @@ -3338,7 +3506,7 @@ isdn_tty_cmd_ATA(modem_info * info) /* If more than one bit set in reg18, autoselect Layer2 */ if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) { if (m->mdmreg[REG_SI1I] == 1) { - if (l2 != ISDN_PROTO_L2_MODEM) + if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX)) l2 = ISDN_PROTO_L2_TRANS; } else l2 = ISDN_PROTO_L2_X75I; @@ -3352,6 +3520,12 @@ isdn_tty_cmd_ATA(modem_info * info) cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); +#ifdef CONFIG_ISDN_TTY_FAX + if (l2 == ISDN_PROTO_L2_FAX) { + cmd.parm.fax = info->fax; + info->fax->direction = ISDN_TTY_FAX_CONN_IN; + } +#endif isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; @@ -3372,7 +3546,6 @@ static int isdn_tty_cmd_PLUSF(char **p, modem_info * info) { atemu *m = &info->emu; - int par; char rs[20]; if (!strncmp(p[0], "CLASS", 5)) { @@ -3382,6 +3555,10 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) p[0]++; sprintf(rs, "\r\n%d", (m->mdmreg[REG_SI1] & 1) ? 8 : 0); +#ifdef CONFIG_ISDN_TTY_FAX + if (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) + sprintf(rs, "\r\n2"); +#endif isdn_tty_at_cout(rs, info); break; case '=': @@ -3389,23 +3566,37 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) switch (*p[0]) { case '0': p[0]++; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; m->mdmreg[REG_SI1] = 4; info->xmit_size = m->mdmreg[REG_PSIZE] * 16; break; +#ifdef CONFIG_ISDN_TTY_FAX case '2': - printk(KERN_DEBUG "isdn_tty: FCLASS=2\n"); p[0]++; + m->mdmreg[REG_SI1] = 1; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FAX; + info->xmit_size = + m->mdmreg[REG_PSIZE] * 16; break; +#endif case '8': p[0]++; + /* L2 will change on dialout with si=1 */ + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; m->mdmreg[REG_SI1] = 5; info->xmit_size = VBUF; break; case '?': p[0]++; - isdn_tty_at_cout("\r\n0,2,8", - info); +#ifdef CONFIG_ISDN_TTY_FAX + isdn_tty_at_cout("\r\n0,2,8", info); +#else + isdn_tty_at_cout("\r\n0,8", info); +#endif break; default: PARSE_ERROR1; @@ -3416,115 +3607,11 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) } return 0; } - if (!strncmp(p[0], "AA", 2)) { - p[0] += 2; - switch (*p[0]) { - case '?': - p[0]++; - sprintf(rs, "\r\n%d", - m->mdmreg[REG_RINGATA]); - isdn_tty_at_cout(rs, info); - break; - case '=': - p[0]++; - par = isdn_getnum(p); - if ((par < 0) || (par > 255)) - PARSE_ERROR1; - m->mdmreg[REG_RINGATA] = par; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "TBC=", 4)) { /* UNKLAR !! */ - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); - switch (*p[0]) { - case '0': - p[0]++; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "BOR=", 4)) { /* UNKLAR !! */ - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: Fax FBOR=%c\n", *p[0]); - switch (*p[0]) { - case '0': - p[0]++; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "DCC=", 4)) { /* SETUP irgendwie !! */ - int i, val[]={0,0,0,0,0,0,0,0}; - - p[0] += 4; - if (*p[0] == '?') { - isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0,1),(0),(0),(0-7)",info); - p[0]++; - } else { - for (i=0; (*p[0]>='0') && (*p[0]<='9'); i++) { - val[i] = *p[0] - 48; - p[0]++; - if (*p[0] == ',') - p[0]++; - } - printk(KERN_DEBUG "isdn_tty: Fax Setup values=%d,%d,%d,%d,%d,%d,%d,%d\n", - val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); - } - return 0; - } - if (!strncmp(p[0], "LID=", 4)) { /* set sender ID !! */ - char senderID[80]; - int i; - - p[0] += 4; - if (*p[0] =='"') - p[0]++; - for(i=0; (*p[0]) && (*p[0] != '"'); i++) - senderID[i] = *p[0]++; - senderID[i] = 0; - if (*p[0] =='"') - p[0]++; - printk(KERN_DEBUG "isdn_tty: Fax sender=>%s<\n", senderID); - return 0; - } - if (!strncmp(p[0], "MFR?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FMFR?\n"); - isdn_tty_at_cout("\r\nISDNfax", info); - return 0; - } - if (!strncmp(p[0], "MDL?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FMDL?\n"); - isdn_tty_at_cout("\r\nAVM-B1", info); - return 0; - } - if (!strncmp(p[0], "AP=?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FAP=?\n"); - return 0; - } - if (!strncmp(p[0], "PHCTO=", 6)) { - /* beim trace mit dem zyxel folgt der wert 30 ;*/ - p[0] += 6; - printk(KERN_DEBUG "isdn_tty: FPHCTO=%s\n", p[0]); - return 0; - } - if (!strncmp(p[0], "CR=", 3)) { - p[0] += 3; - printk(KERN_DEBUG "isdn_tty: FCR=%s\n", p[0]); - return 0; - } - printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); +#ifdef CONFIG_ISDN_TTY_FAX + return (isdn_tty_cmd_PLUSF_FAX(p, info)); +#else PARSE_ERROR1; +#endif } /* @@ -3534,8 +3621,9 @@ static int isdn_tty_cmd_PLUSV(char **p, modem_info * info) { atemu *m = &info->emu; + isdn_ctrl cmd; static char *vcmd[] = - {"NH", "IP", "LS", "RX", "SD", "SM", "TX", NULL}; + {"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL}; int i; int par1; int par2; @@ -3711,9 +3799,9 @@ isdn_tty_cmd_PLUSV(char **p, modem_info * info) info); isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n", info); - isdn_tty_at_cout("5;ALAW;8;0;(8000)", + isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n", info); - isdn_tty_at_cout("6;ULAW;8;0;(8000)", + isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n", info); break; default: @@ -3752,6 +3840,52 @@ isdn_tty_cmd_PLUSV(char **p, modem_info * info) isdn_tty_modem_result(1, info); return 0; break; + case 7: + /* AT+VDD - DTMF detection */ + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n<%d>,<%d>", + m->vpar[4], + m->vpar[5]); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if ((*p[0]>='0') && (*p[0]<='9')) { + if (info->online != 1) + PARSE_ERROR1; + par1 = isdn_getnum(p); + if ((par1 < 0) || (par1 > 15)) + PARSE_ERROR1; + if (*p[0] != ',') + PARSE_ERROR1; + p[0]++; + par2 = isdn_getnum(p); + if ((par2 < 0) || (par2 > 255)) + PARSE_ERROR1; + m->vpar[4] = par1; + m->vpar[5] = par2; + cmd.driver = info->isdn_driver; + cmd.command = ISDN_CMD_AUDIO; + cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8); + cmd.parm.num[0] = par1; + cmd.parm.num[1] = par2; + isdn_command(&cmd); + break; + } else + if (*p[0] == '?') { + p[0]++; + isdn_tty_at_cout("\r\n<0-15>,<0-255>", + info); + break; + } else + PARSE_ERROR1; + break; + default: + PARSE_ERROR1; + } + break; default: PARSE_ERROR1; } @@ -3774,6 +3908,9 @@ isdn_tty_parse_at(modem_info * info) #endif for (p = &m->mdmcmd[2]; *p;) { switch (*p) { + case ' ': + p++; + break; case 'A': /* A - Accept incoming call */ p++; @@ -3919,8 +4056,10 @@ isdn_tty_parse_at(modem_info * info) isdn_tty_suspend(ds, info, m); break; case 'R': /* RESUME */ + p++; isdn_tty_get_msnstr(ds, &p); isdn_tty_resume(ds, info, m); + break; case 'M': /* MESSAGE */ p++; isdn_tty_send_msg(info, m, p); @@ -3981,7 +4120,7 @@ isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) eb[1] = 0; isdn_tty_at_cout(eb, info); } - if (m->mdmcmdl >= 2) + if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2)))) isdn_tty_parse_at(info); m->mdmcmdl = 0; continue; @@ -4007,15 +4146,16 @@ isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) switch (m->mdmcmdl) { case 0: if (c == 'A') - m->mdmcmd[m->mdmcmdl++] = c; + m->mdmcmd[m->mdmcmdl] = c; break; case 1: if (c == 'T') - m->mdmcmd[m->mdmcmdl++] = c; + m->mdmcmd[m->mdmcmdl] = c; break; default: - m->mdmcmd[m->mdmcmdl++] = c; + m->mdmcmd[m->mdmcmdl] = c; } + m->mdmcmd[++m->mdmcmdl] = 0; } } } diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h index 663648e58..87acd7be2 100644 --- a/drivers/isdn/isdn_tty.h +++ b/drivers/isdn/isdn_tty.h @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.h,v 1.13 1999/04/12 12:33:46 fritz Exp $ +/* $Id: isdn_tty.h,v 1.16 1999/08/22 20:26:10 calle Exp $ * header for Linux ISDN subsystem, tty related functions (linklevel). * @@ -20,6 +20,26 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.h,v $ + * Revision 1.16 1999/08/22 20:26:10 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.15 1999/07/31 12:59:48 armin + * Added tty fax capabilities. + * + * Revision 1.14 1999/07/11 17:14:15 armin + * Added new layer 2 and 3 protocols for Fax and DSP functions. + * Moved "Add CPN to RING message" to new register S23, + * "Display message" is now correct on register S13 bit 7. + * New audio command AT+VDD implemented (deactivate DTMF decoder and + * activate possible existing hardware/DSP decoder). + * Moved some tty defines to .h file. + * Made whitespace possible in AT command line. + * Some AT-emulator output bugfixes. + * First Fax G3 implementations. + * * Revision 1.13 1999/04/12 12:33:46 fritz * Changes from 2.0 tree. * @@ -72,6 +92,69 @@ * */ +#include <linux/config.h> + +#define DLE 0x10 +#define ETX 0x03 +#define DC4 0x14 + + +/* + * Definition of some special Registers of AT-Emulator + */ +#define REG_RINGATA 0 +#define REG_RINGCNT 1 +#define REG_ESC 2 +#define REG_CR 3 +#define REG_LF 4 +#define REG_BS 5 + +#define REG_WAITC 7 + +#define REG_RESP 12 +#define BIT_RESP 1 +#define REG_RESPNUM 12 +#define BIT_RESPNUM 2 +#define REG_ECHO 12 +#define BIT_ECHO 4 +#define REG_DCD 12 +#define BIT_DCD 8 +#define REG_CTS 12 +#define BIT_CTS 16 +#define REG_DTRR 12 +#define BIT_DTRR 32 +#define REG_DSR 12 +#define BIT_DSR 64 +#define REG_CPPP 12 +#define BIT_CPPP 128 + +#define REG_T70 13 +#define BIT_T70 2 +#define BIT_T70_EXT 32 +#define REG_DTRHUP 13 +#define BIT_DTRHUP 4 +#define REG_RESPXT 13 +#define BIT_RESPXT 8 +#define REG_CIDONCE 13 +#define BIT_CIDONCE 16 +#define REG_RUNG 13 +#define BIT_RUNG 64 +#define REG_DISPLAY 13 +#define BIT_DISPLAY 128 + +#define REG_L2PROT 14 +#define REG_L3PROT 15 +#define REG_PSIZE 16 +#define REG_WSIZE 17 +#define REG_SI1 18 +#define REG_SI2 19 +#define REG_SI1I 20 +#define REG_PLAN 21 +#define REG_SCREEN 22 + +#define REG_CPN 23 +#define BIT_CPN 1 + extern void isdn_tty_modem_escape(void); extern void isdn_tty_modem_ring(void); extern void isdn_tty_carrier_timeout(void); @@ -83,3 +166,10 @@ extern void isdn_tty_cleanup_xmit(modem_info *); extern int isdn_tty_stat_callback(int, isdn_ctrl *); extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *); extern int isdn_tty_capi_facility(capi_msg *cm); +extern void isdn_tty_at_cout(char *, modem_info *); +extern void isdn_tty_modem_hup(modem_info *, int); +#ifdef CONFIG_ISDN_TTY_FAX +extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *); +extern int isdn_tty_fax_command(modem_info *); +extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *); +#endif diff --git a/drivers/isdn/isdn_ttyfax.c b/drivers/isdn/isdn_ttyfax.c new file mode 100644 index 000000000..7665aa812 --- /dev/null +++ b/drivers/isdn/isdn_ttyfax.c @@ -0,0 +1,1206 @@ +/* $Id: isdn_ttyfax.c,v 1.3 1999/08/22 20:26:12 calle Exp $ + * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel). + * + * Copyright 1999 by Armin Schindler (mac@melware.de) + * Copyright 1999 by Ralf Spachmann (mel@melware.de) + * Copyright 1999 by Cytronics & Melware + * + * 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: isdn_ttyfax.c,v $ + * Revision 1.3 1999/08/22 20:26:12 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * + * Revision 1.2 1999/08/05 10:36:10 armin + * Bugfix: kernel oops on getting revision. + * + * Revision 1.1 1999/07/31 12:59:50 armin + * Added tty fax capabilities. + * + * + */ + +#undef ISDN_TTY_FAX_STAT_DEBUG +#undef ISDN_TTY_FAX_CMD_DEBUG + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/isdn.h> +#include "isdn_common.h" +#include "isdn_tty.h" +#include "isdn_ttyfax.h" + + +static char *isdn_tty_fax_revision = "$Revision: 1.3 $"; + +#define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; } + +static char * +isdn_getrev(const char *revision) +{ + char *rev; + char *p; + + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "???"; + return rev; +} + + +/* + * Fax Class 2 Modem results + * + */ +static void isdn_tty_fax_modem_result(int code, modem_info * info) +{ + atemu *m = &info->emu; + T30_s *f = info->fax; + char rs[50]; + char rss[50]; + char *rp; + int i; + static char *msg[] = + {"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:", + "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:", + "+FCFR", "+FPTS:", "+FET:" }; + + + isdn_tty_at_cout("\r\n", info); + isdn_tty_at_cout(msg[code], info); + +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n", + msg[code], info->line); +#endif + switch (code) { + case 0: /* OK */ + break; + case 1: /* ERROR */ + break; + case 2: /* +FCON */ + /* Append CPN, if enabled */ + if ((m->mdmreg[REG_CPN] & BIT_CPN) && + (!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) { + sprintf(rs, "/%s", m->cpn); + isdn_tty_at_cout(rs, info); + } + info->online = 1; + f->fet = 0; + if (f->phase == ISDN_FAX_PHASE_A) + f->phase = ISDN_FAX_PHASE_B; + break; + case 3: /* +FCSI */ + case 8: /* +FTSI */ + sprintf(rs, "\"%s\"", f->r_id); + isdn_tty_at_cout(rs, info); + break; + case 4: /* +FDIS */ + rs[0] = 0; + rp = &f->r_resolution; + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n", + rs, info->line); +#endif + break; + case 5: /* +FHNG */ + sprintf(rs, "%d", f->code); + isdn_tty_at_cout(rs, info); + info->faxonline = 0; + break; + case 6: /* +FDCS */ + rs[0] = 0; + rp = &f->r_resolution; + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n", + rs, info->line); +#endif + break; + case 7: /* CONNECT */ + info->faxonline |= 2; + break; + case 9: /* FCFR */ + break; + case 10: /* FPTS */ + isdn_tty_at_cout("1", info); + break; + case 11: /* FET */ + sprintf(rs, "%d", f->fet); + isdn_tty_at_cout(rs, info); + break; + } + + isdn_tty_at_cout("\r\n", info); + + switch (code) { + case 7: /* CONNECT */ + info->online = 2; + if (info->faxonline & 1) { + sprintf(rs, "%c", XON); + isdn_tty_at_cout(rs, info); + } + break; + } +} + +int +isdn_tty_fax_command(modem_info * info) +{ + T30_s *f = info->fax; + char rs[10]; + +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n", + f->r_code, info->line); +#endif + switch(f->r_code) { + case ISDN_TTY_FAX_FCON: + info->faxonline = 1; + isdn_tty_fax_modem_result(2, info); /* +FCON */ + return(0); + case ISDN_TTY_FAX_FCON_I: + info->faxonline = 16; + isdn_tty_fax_modem_result(2, info); /* +FCON */ + return(0); + case ISDN_TTY_FAX_RID: + if (info->faxonline & 1) + isdn_tty_fax_modem_result(3, info); /* +FCSI */ + if (info->faxonline & 16) + isdn_tty_fax_modem_result(8, info); /* +FTSI */ + return(0); + case ISDN_TTY_FAX_DIS: + isdn_tty_fax_modem_result(4, info); /* +FDIS */ + return(0); + case ISDN_TTY_FAX_HNG: + if (f->phase == ISDN_FAX_PHASE_C) { + if (f->direction == ISDN_TTY_FAX_CONN_IN) { + sprintf(rs, "%c%c", DLE, ETX); + isdn_tty_at_cout(rs, info); + } else { + sprintf(rs, "%c", 0x18); + isdn_tty_at_cout(rs, info); + } + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + } + f->phase = ISDN_FAX_PHASE_E; + isdn_tty_fax_modem_result(5, info); /* +FHNG */ + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_DCS: + isdn_tty_fax_modem_result(6, info); /* +FDCS */ + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + f->phase = ISDN_FAX_PHASE_C; + return(0); + case ISDN_TTY_FAX_TRAIN_OK: + isdn_tty_fax_modem_result(6, info); /* +FDCS */ + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_SENT: + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_CFR: + isdn_tty_fax_modem_result(9, info); /* +FCFR */ + return(0); + case ISDN_TTY_FAX_ET: + sprintf(rs, "%c%c", DLE, ETX); + isdn_tty_at_cout(rs, info); + isdn_tty_fax_modem_result(10, info); /* +FPTS */ + isdn_tty_fax_modem_result(11, info); /* +FET */ + isdn_tty_fax_modem_result(0, info); /* OK */ + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + f->phase = ISDN_FAX_PHASE_D; + return(0); + case ISDN_TTY_FAX_PTS: + isdn_tty_fax_modem_result(10, info); /* +FPTS */ + if (f->direction == ISDN_TTY_FAX_CONN_OUT) { + if (f->fet == 1) + f->phase = ISDN_FAX_PHASE_B; + if (f->fet == 0) + isdn_tty_fax_modem_result(0, info); /* OK */ + } + return(0); + case ISDN_TTY_FAX_EOP: + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + f->phase = ISDN_FAX_PHASE_D; + return(0); + + } + return(-1); +} + + +void +isdn_tty_fax_bitorder(modem_info *info, struct sk_buff *skb) +{ + __u8 LeftMask; + __u8 RightMask; + __u8 fBit; + __u8 Data; + int i; + + if (!info->fax->bor) { + for(i = 0; i < skb->len; i++) { + Data = skb->data[i]; + for ( + LeftMask = 0x80, RightMask = 0x01; + LeftMask > RightMask; + LeftMask >>= 1, RightMask <<= 1 + ) { + fBit = (Data & LeftMask); + if (Data & RightMask) + Data |= LeftMask; + else + Data &= ~LeftMask; + if (fBit) + Data |= RightMask; + else + Data &= ~RightMask; + + } + skb->data[i] = Data; + } + } +} + +/* + * Parse AT+F.. FAX class 2 commands + */ + +int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) +{ + atemu *m = &info->emu; + T30_s *f = info->fax; + isdn_ctrl cmd; + int par; + char rs[50]; + char rss[50]; + int maxdccval[]={1,5,2,2,3,2,0,7}; + + /* FAA still unchanged */ + if (!strncmp(p[0], "AA", 2)) { /* TODO */ + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", 0); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */ + if (!strncmp(p[0], "BADLIN", 6)) { + p[0] += 6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->badlin); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->badlin = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */ + if (!strncmp(p[0], "BADMUL", 6)){ + p[0] +=6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->badmul); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->badmul = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BOR=n - Phase C bit order, 0=direct, 1=reverse */ + if (!strncmp(p[0], "BOR", 3)){ + p[0] +=3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->bor); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->bor = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* NBC=n - No Best Capabilities */ + if (!strncmp(p[0], "NBC", 3)){ + p[0] +=3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->nbc); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->nbc = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BUF? - Readonly buffersize readout */ + if (!strncmp(p[0], "BUF?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE])); +#endif + p[0]++; + sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE])); + isdn_tty_at_cout(rs, info); + return 0; + } + + /* CIG=string - local fax station id string for polling rx */ + if (!strncmp(p[0], "CIG", 3)) { + int i, r; + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n\"%s\"", f->pollid); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n\"STRING\""); + isdn_tty_at_cout(rs, info); + } + else + { + if (*p[0] =='"') + p[0]++; + for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++) + { + f->pollid[i] = *p[0]++; + } + if (*p[0] =='"') + p[0]++; + for(r=i; r < FAXIDLEN; r++) + { + f->pollid[r] = 32; + } + f->pollid[FAXIDLEN-1] = 0; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */ + if (!strncmp(p[0], "CQ", 2)) { + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->cq); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->cq = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */ + if (!strncmp(p[0], "CR", 2)) { + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->cr); /* read actual value from struct and print */ + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); /* display online help */ + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->cr = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CTCRTY=value - ECM retry count */ + if (!strncmp(p[0], "CTCRTY", 6)){ + p[0] +=6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->ctcrty); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->ctcrty = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */ + if (!strncmp(p[0], "DCC", 3)) { + char *rp = &f->resolution; + int i; + + p[0] += 3; + switch(*p[0]) { + case '?': + p[0]++; + strcpy(rs, "\r\n"); + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') { + isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info); + p[0]++; + } else { + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[i]) { + PARSE_ERROR1; + } + rp[i] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n", + rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */ + if (!strncmp(p[0], "DIS", 3)) { + char *rp = &f->resolution; + int i; + + p[0] += 3; + switch(*p[0]) { + case '?': + p[0]++; + strcpy(rs, "\r\n"); + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') { + isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info); + p[0]++; + } else { + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[i]) { + PARSE_ERROR1; + } + rp[i] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n", + rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DR - Receive Phase C data command, initiates document reception */ + if (!strncmp(p[0], "DR", 2)) { + p[0] += 2; + if ((info->faxonline & 16) && /* incoming connection */ + ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) { +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDR\n"); +#endif + f->code = ISDN_TTY_FAX_DR; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); + if (f->phase == ISDN_FAX_PHASE_B) { + f->phase = ISDN_FAX_PHASE_C; + } else if (f->phase == ISDN_FAX_PHASE_D) { + switch(f->fet) { + case 0: /* next page will be received */ + f->phase = ISDN_FAX_PHASE_C; + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + break; + case 1: /* next doc will be received */ + f->phase = ISDN_FAX_PHASE_B; + break; + case 2: /* fax session is terminating */ + f->phase = ISDN_FAX_PHASE_E; + break; + default: + PARSE_ERROR1; + } + } + } else { + PARSE_ERROR1; + } + return 1; + } + + /* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */ + if (!strncmp(p[0], "DT", 2)) { + int i, val[]={4,0,2,3}; + char *rp = &f->resolution; + + p[0] += 2; + if (!info->faxonline & 1) /* not outgoing connection */ + PARSE_ERROR1; + + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<4); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[val[i]]) { + PARSE_ERROR1; + } + rp[val[i]] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n", + rp[4], rp[0], rp[2], rp[3]); +#endif + if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) { + f->code = ISDN_TTY_FAX_DT; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); + if (f->phase == ISDN_FAX_PHASE_D) { + f->phase = ISDN_FAX_PHASE_C; + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + } + } else { + PARSE_ERROR1; + } + return 1; + } + + /* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */ + if (!strncmp(p[0], "ECM", 3)) { + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->ecm); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par != 0) && (par != 2)) + PARSE_ERROR1; + f->ecm = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* ET=n - End of page or document */ + if (!strncmp(p[0], "ET=", 3)) { + p[0] += 3; + if (*p[0] == '?') { + p[0]++; + sprintf(rs, "\r\n0-2"); + isdn_tty_at_cout(rs, info); + } else { + if ((f->phase != ISDN_FAX_PHASE_D) || (!info->faxonline & 1)) + PARSE_ERROR1; + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->fet = par; + f->code = ISDN_TTY_FAX_ET; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par); +#endif + return 1; + } + return 0; + } + + /* K - terminate */ + if (!strncmp(p[0], "K", 1)) { + p[0] += 1; + if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E)) + PARSE_ERROR1; + isdn_tty_modem_hup(info, 1); + return 1; + } + + /* LID=string - local fax ID */ + if (!strncmp(p[0], "LID", 3)) { + int i, r; + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n\"%s\"", f->id); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n\"STRING\""); + isdn_tty_at_cout(rs, info); + } + else + { + if (*p[0] =='"') + p[0]++; + for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++) + { + f->id[i] = *p[0]++; + } + if (*p[0] =='"') + p[0]++; + for(r=i; r < FAXIDLEN; r++) + { + f->id[r] = 32; + } + f->id[FAXIDLEN-1] = 0; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + +#if 0 + /* LO=n - Flow control opts */ + if (!strncmp(p[0], "LO", 2)) { /* TODO */ + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->lo); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->lo = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FLO=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif +#if 0 + /* LPL=n - Doc for polling cmd */ + if (!strncmp(p[0], "LPL", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->lpl); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->lpl = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FLPL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* MDL? - DCE Model */ + if (!strncmp(p[0], "MDL?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FMDL?\n"); +#endif + isdn_tty_at_cout("\r\nisdn4linux", info); + return 0; + } + + /* MFR? - DCE Manufacturer */ + if (!strncmp(p[0], "MFR?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FMFR?\n"); +#endif + isdn_tty_at_cout("\r\nisdn4linux", info); + return 0; + } + + /* MINSP=n - Minimum Speed for Phase C */ + if (!strncmp(p[0], "MINSP", 5)) { + p[0] += 5; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->minsp); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-5"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 5)) + PARSE_ERROR1; + f->minsp = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* PHCTO=value - DTE phase C timeout */ + if (!strncmp(p[0], "PHCTO", 5)){ + p[0] +=5; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->phcto); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->phcto = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + +#if 0 + /* PTS=n - Page transfer status */ + if (!strncmp(p[0], "PTS", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->pts); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-5"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 5)) + PARSE_ERROR1; + f->pts = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FPTS=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* REL=n - Phase C received EOL alignment */ + if (!strncmp(p[0], "REL", 3)) { + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->rel); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->rel = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* REV? - DCE Revision */ + if (!strncmp(p[0], "REV?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FREV?\n"); +#endif + strcpy(rss, isdn_tty_fax_revision); + sprintf(rs, "\r\nRev: %s", isdn_getrev(rss)); + isdn_tty_at_cout(rs, info); + return 0; + } + +#if 0 + /* SPL=n - Enable polling */ + if (!strncmp(p[0], "SPL", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->spl); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->spl = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FSPL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* Phase C Transmit Data Block Size */ + if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */ + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); +#endif + switch (*p[0]) { + case '0': + p[0]++; + break; + default: + PARSE_ERROR1; + } + return 0; + } + + printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); + PARSE_ERROR1; +} + diff --git a/drivers/isdn/isdn_ttyfax.h b/drivers/isdn/isdn_ttyfax.h new file mode 100644 index 000000000..1ad75d2df --- /dev/null +++ b/drivers/isdn/isdn_ttyfax.h @@ -0,0 +1,33 @@ +/* $Id: isdn_ttyfax.h,v 1.1 1999/07/31 12:59:51 armin Exp $ + * header for Linux ISDN subsystem, tty_fax related functions (linklevel). + * + * Copyright 1999 by Armin Schindler (mac@melware.de) + * Copyright 1999 by Ralf Spachmann (mel@melware.de) + * Copyright 1999 by Cytronics & Melware + * + * 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: isdn_ttyfax.h,v $ + * Revision 1.1 1999/07/31 12:59:51 armin + * Added tty fax capabilities. + * + * + */ + + +#define XON 0x11 +#define XOFF 0x13 +#define DC2 0x12 + diff --git a/drivers/isdn/isdn_x25iface.c b/drivers/isdn/isdn_x25iface.c index d0594fe34..9a5074801 100644 --- a/drivers/isdn/isdn_x25iface.c +++ b/drivers/isdn/isdn_x25iface.c @@ -1,4 +1,4 @@ -/* $Id: isdn_x25iface.c,v 1.6 1999/01/27 22:53:19 he Exp $ +/* $Id: isdn_x25iface.c,v 1.7 1999/08/22 20:26:13 calle Exp $ * stuff needed to support the Linux X.25 PLP code on top of devices that * can provide a lab_b service using the concap_proto mechanism. * This module supports a network interface wich provides lapb_sematics @@ -10,6 +10,12 @@ * goes to another -- device related -- concap_proto support source file. * * $Log: isdn_x25iface.c,v $ + * Revision 1.7 1999/08/22 20:26:13 calle + * backported changes from kernel 2.3.14: + * - several #include "config.h" gone, others come. + * - "struct device" changed to "struct net_device" in 2.3.14, added a + * define in isdn_compat.h for older kernel versions. + * * Revision 1.6 1999/01/27 22:53:19 he * minor updates (spellings, jiffies wrap around in isdn_tty) * @@ -57,7 +63,7 @@ typedef struct isdn_x25iface_proto_data { void isdn_x25iface_proto_del( struct concap_proto * ); int isdn_x25iface_proto_close( struct concap_proto * ); int isdn_x25iface_proto_restart( struct concap_proto *, - struct device *, + struct net_device *, struct concap_device_ops *); int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * ); int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * ); @@ -175,7 +181,7 @@ void isdn_x25iface_proto_del(struct concap_proto *cprot){ /* (re-)initialize the data structures for x25iface encapsulation */ int isdn_x25iface_proto_restart(struct concap_proto *cprot, - struct device *ndev, + struct net_device *ndev, struct concap_device_ops *dops) { ix25_pdata_t * pda = cprot -> proto_data ; diff --git a/drivers/isdn/isdnloop/.cvsignore b/drivers/isdn/isdnloop/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/isdnloop/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags diff --git a/drivers/isdn/pcbit/.cvsignore b/drivers/isdn/pcbit/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/pcbit/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c index a3186ac11..77f0ed45f 100644 --- a/drivers/isdn/pcbit/drv.c +++ b/drivers/isdn/pcbit/drv.c @@ -86,7 +86,7 @@ int pcbit_init_dev(int board, int mem_base, int irq) dev_pcbit[board] = dev; memset(dev, 0, sizeof(struct pcbit_dev)); -#if LINUX_VERSION_CODE >= 131841 +#ifdef COMPAT_HAS_NEW_WAITQ init_waitqueue_head(&dev->set_running_wq); #endif diff --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c index 33aee3360..799552019 100644 --- a/drivers/isdn/pcbit/module.c +++ b/drivers/isdn/pcbit/module.c @@ -102,10 +102,21 @@ void cleanup_module(void) } #else -void pcbit_setup(char *str, int *ints) +#ifdef COMPAT_HAS_NEW_SETUP +#define MAX_PARA (MAX_PCBIT_CARDS * 2) +#include <linux/init.h> +static int __init pcbit_setup(char *line) { int i, j, argc; + char *str; + int ints[MAX_PARA+1]; + str = get_options(line, MAX_PARA, ints); +#else +void pcbit_setup(char *str, int *ints) +{ + int i, j, argc; +#endif argc = ints[0]; i = 0; j = 1; @@ -124,8 +135,14 @@ void pcbit_setup(char *str, int *ints) i++; } +#ifdef COMPAT_HAS_NEW_SETUP + return(1); +} +__setup("pcbit=", pcbit_setup); +#else } #endif +#endif diff --git a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h index 0dbdb5fd1..45c68871d 100644 --- a/drivers/isdn/pcbit/pcbit.h +++ b/drivers/isdn/pcbit/pcbit.h @@ -68,10 +68,10 @@ struct pcbit_dev { struct frame_buf *write_queue; /* Protocol start */ -#if LINUX_VERSION_CODE < 131841 - struct wait_queue *set_running_wq; -#else +#ifdef COMPAT_HAS_NEW_WAITQ wait_queue_head_t set_running_wq; +#else + struct wait_queue *set_running_wq; #endif struct timer_list set_running_timer; diff --git a/drivers/isdn/sc/.cvsignore b/drivers/isdn/sc/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/sc/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags diff --git a/drivers/isdn/teles/.cvsignore b/drivers/isdn/teles/.cvsignore deleted file mode 100644 index 857dd22e9..000000000 --- a/drivers/isdn/teles/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.depend -.*.flags |