diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-24 00:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-24 00:12:35 +0000 |
commit | 482368b1a8e45430672c58c9a42e7d2004367126 (patch) | |
tree | ce2a1a567d4d62dee7c2e71a46a99cf72cf1d606 /drivers/isdn | |
parent | e4d0251c6f56ab2e191afb70f80f382793e23f74 (diff) |
Merge with 2.3.47. Guys, this is buggy as shit. You've been warned.
Diffstat (limited to 'drivers/isdn')
87 files changed, 11836 insertions, 2826 deletions
diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index 2081f12fe..a73c26766 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -12,68 +12,99 @@ 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' CONFIG_ISDN_X25 fi -dep_tristate ' ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN -dep_tristate ' isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN -dep_tristate ' PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN -dep_tristate ' HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN + +mainmenu_option next_comment +comment 'ISDN feature submodules' + dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN + bool 'Support isdn diversion services' CONFIG_ISDN_DIVERSION +endmenu + +comment 'low-level hardware drivers' + +mainmenu_option next_comment +comment 'Passive ISDN cards' +dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then - bool ' HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO + comment ' D-channel protocol features' + bool ' HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO if [ "$CONFIG_HISAX_EURO" != "n" ]; then - bool ' Support for german chargeinfo' CONFIG_DE_AOC - bool ' Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE - bool ' Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC + bool ' Support for german chargeinfo' CONFIG_DE_AOC + bool ' Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE + bool ' Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC + bool ' Disable keypad protocol option' CONFIG_HISAX_NO_KEYPAD fi - 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 PCI' CONFIG_HISAX_TELESPCI - bool ' HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX - bool ' HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 - bool ' HiSax Support for AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI - bool ' HiSax Support for AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA - bool ' HiSax Support for Elsa cards' CONFIG_HISAX_ELSA - bool ' HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 - 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 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 - bool ' HiSax Support for Winbond W6692 based cards' CONFIG_HISAX_W6692 + bool ' HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 + comment ' HiSax supported cards' + bool ' Teles 16.0/8.0' CONFIG_HISAX_16_0 + bool ' Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 + bool ' Teles PCI' CONFIG_HISAX_TELESPCI + bool ' Teles S0Box' CONFIG_HISAX_S0BOX + bool ' AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 + bool ' AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI + bool ' AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA + bool ' Elsa cards' CONFIG_HISAX_ELSA + bool ' ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 + bool ' Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA + bool ' ASUSCOM ISA cards' CONFIG_HISAX_ASUSCOM + bool ' TELEINT cards' CONFIG_HISAX_TELEINT + bool ' HFC-S based cards' CONFIG_HISAX_HFCS + bool ' Sedlbauer cards' CONFIG_HISAX_SEDLBAUER + bool ' USR Sportster internal TA' CONFIG_HISAX_SPORTSTER + bool ' MIC card' CONFIG_HISAX_MIC + bool ' NETjet card' CONFIG_HISAX_NETJET + bool ' Niccy PnP/PCI card' CONFIG_HISAX_NICCY + bool ' Siemens I-Surf card' CONFIG_HISAX_ISURF + bool ' HST Saphir card' CONFIG_HISAX_HSTSAPHIR + bool ' Telekom A4T card' CONFIG_HISAX_BKM_A4T + bool ' Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO + bool ' Gazel cards' CONFIG_HISAX_GAZEL + bool ' HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI + bool ' Winbond W6692 based cards' CONFIG_HISAX_W6692 if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then -# bool ' HiSax Support for TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU + bool ' HFC-S+, HFC-SP, HFC-PCMCIA cards' CONFIG_HISAX_HFC_SX +# bool ' TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then - bool ' HiSax Support for Am7930' CONFIG_HISAX_AMD7930 + bool ' Am7930' CONFIG_HISAX_AMD7930 fi fi fi +endmenu + +mainmenu_option next_comment +comment 'Active ISDN cards' +dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN +dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN 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 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN + dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN fi -dep_tristate ' Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN +dep_tristate 'Eicon 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 + 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 +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/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA - bool ' AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA - bool ' AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI - bool ' Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON + bool ' AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA + bool ' AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI + if [ "$CONFIG_ISDN_DRV_AVMB1_B1PCI" != "n" ]; then + if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then + bool ' AVM B1 PCI V4 support' CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + fi + fi + bool ' AVM T1/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA + bool ' AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + bool ' AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI + if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then + bool ' AVM C4 support' CONFIG_ISDN_DRV_AVMB1_C4 + fi + bool ' Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON +fi +if [ "$CONFIG_PROC_FS" != "n" ]; then +if [ "$CONFIG_MODULES" != "n" ]; then + bool 'Hypercope HYSDN cards (Champ, Ergo, Metro) support (module)' CONFIG_HYSDN +fi fi +endmenu diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index 04be19f9c..c94551716 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 divert +ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon divert hysdn L_OBJS := LX_OBJS := @@ -137,5 +137,9 @@ else endif endif +ifeq ($(CONFIG_HYSDN),y) + MOD_SUB_DIRS += hysdn +endif + include $(TOPDIR)/Rules.make diff --git a/drivers/isdn/avmb1/Makefile b/drivers/isdn/avmb1/Makefile index 111c39466..bfeb81939 100644 --- a/drivers/isdn/avmb1/Makefile +++ b/drivers/isdn/avmb1/Makefile @@ -1,5 +1,5 @@ # -# $Id: Makefile,v 1.7 1999/09/15 08:16:03 calle Exp $ +# $Id: Makefile,v 1.8 2000/01/25 14:33:38 calle Exp $ # # Makefile for the CAPI and AVM-B1 device drivers. # @@ -11,6 +11,11 @@ # parent makes.. # # $Log: Makefile,v $ +# Revision 1.8 2000/01/25 14:33:38 calle +# - Added Support AVM B1 PCI V4.0 (tested with prototype) +# - splitted up t1pci.c into b1dma.c for common function with b1pciv4 +# - support for revision register +# # Revision 1.7 1999/09/15 08:16:03 calle # Implementation of 64Bit extention complete. # @@ -99,7 +104,7 @@ ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) ifdef CONFIG_ISDN_DRV_AVMB1_C4 O_OBJS += c4.o endif - OX_OBJS += capiutil.o capidrv.o b1.o + OX_OBJS += capiutil.o capidrv.o b1.o b1dma.o else ifeq ($(CONFIG_ISDN_DRV_AVMB1),m) O_TARGET += kernelcapi.o @@ -123,7 +128,7 @@ else ifdef CONFIG_ISDN_DRV_AVMB1_C4 M_OBJS += c4.o endif - MX_OBJS += capiutil.o capidrv.o b1.o + MX_OBJS += capiutil.o capidrv.o b1.o b1dma.o endif endif diff --git a/drivers/isdn/avmb1/avmcard.h b/drivers/isdn/avmb1/avmcard.h index f4b5df689..56fa0fba6 100644 --- a/drivers/isdn/avmb1/avmcard.h +++ b/drivers/isdn/avmb1/avmcard.h @@ -1,9 +1,14 @@ /* - * $Id: avmcard.h,v 1.6 1999/11/05 16:38:01 calle Exp $ + * $Id: avmcard.h,v 1.7 2000/01/25 14:33:38 calle Exp $ * * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: avmcard.h,v $ + * Revision 1.7 2000/01/25 14:33:38 calle + * - Added Support AVM B1 PCI V4.0 (tested with prototype) + * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 + * - support for revision register + * * Revision 1.6 1999/11/05 16:38:01 calle * Cleanups before kernel 2.4: * - Changed all messages to use card->name or driver->name instead of @@ -92,6 +97,8 @@ typedef struct avmcard { unsigned irq; unsigned long membase; enum avmcardtype cardtype; + unsigned char revision; + unsigned char class; int cardnr; /* for t1isa */ char msgbuf[128]; /* capimsg msg part */ @@ -228,8 +235,9 @@ extern int b1_irq_table[16]; #define B1_WRITE 0x01 #define B1_INSTAT 0x02 #define B1_OUTSTAT 0x03 -#define B1_RESET 0x10 #define B1_ANALYSE 0x04 +#define B1_REVISION 0x05 +#define B1_RESET 0x10 #define B1_STAT0(cardtype) ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l) @@ -561,10 +569,13 @@ static inline void b1_setinterrupt(unsigned int base, unsigned irq, } } +/* b1.c */ int b1_detect(unsigned int base, enum avmcardtype cardtype); +void b1_getrevision(avmcard *card); int b1_load_t4file(avmcard *card, capiloaddatapart * t4file); int b1_load_config(avmcard *card, capiloaddatapart * config); int b1_loaded(avmcard *card); + 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, @@ -577,5 +588,21 @@ 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); +/* b1dma.c */ +int b1pciv4_detect(avmcard *card); +int t1pci_detect(avmcard *card); +void b1dma_reset(avmcard *card); +void b1dma_interrupt(int interrupt, void *devptr, struct pt_regs *regs); + +int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data); +void b1dma_reset_ctr(struct capi_ctr *ctrl); +void b1dma_remove_ctr(struct capi_ctr *ctrl); +void b1dma_register_appl(struct capi_ctr *ctrl, + __u16 appl, + capi_register_params *rp); +void b1dma_release_appl(struct capi_ctr *ctrl, __u16 appl); +void b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +int b1dmactl_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 index 900b31c8c..65c4368cd 100644 --- a/drivers/isdn/avmb1/b1.c +++ b/drivers/isdn/avmb1/b1.c @@ -1,11 +1,16 @@ /* - * $Id: b1.c,v 1.12 1999/11/05 16:38:01 calle Exp $ + * $Id: b1.c,v 1.13 2000/01/25 14:33:38 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.13 2000/01/25 14:33:38 calle + * - Added Support AVM B1 PCI V4.0 (tested with prototype) + * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 + * - support for revision register + * * Revision 1.12 1999/11/05 16:38:01 calle * Cleanups before kernel 2.4: * - Changed all messages to use card->name or driver->name instead of @@ -86,7 +91,7 @@ #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.12 $"; +static char *revision = "$Revision: 1.13 $"; /* ------------------------------------------------------------- */ @@ -158,6 +163,12 @@ int b1_detect(unsigned int base, enum avmcardtype cardtype) return 0; } +void b1_getrevision(avmcard *card) +{ + card->class = inb(card->port + B1_ANALYSE); + card->revision = inb(card->port + B1_REVISION); +} + int b1_load_t4file(avmcard *card, capiloaddatapart * t4file) { unsigned char buf[256]; @@ -688,6 +699,7 @@ int b1ctl_read_proc(char *page, char **start, off_t off, EXPORT_SYMBOL(b1_irq_table); EXPORT_SYMBOL(b1_detect); +EXPORT_SYMBOL(b1_getrevision); EXPORT_SYMBOL(b1_load_t4file); EXPORT_SYMBOL(b1_load_config); EXPORT_SYMBOL(b1_loaded); diff --git a/drivers/isdn/avmb1/b1dma.c b/drivers/isdn/avmb1/b1dma.c new file mode 100644 index 000000000..8bf595282 --- /dev/null +++ b/drivers/isdn/avmb1/b1dma.c @@ -0,0 +1,984 @@ +/* + * $Id: b1dma.c,v 1.2 2000/01/25 14:44:47 calle Exp $ + * + * Common module for AVM B1 cards that support dma with AMCC + * + * (c) Copyright 2000 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: b1dma.c,v $ + * Revision 1.2 2000/01/25 14:44:47 calle + * typo in b1pciv4_detect(). + * + * Revision 1.1 2000/01/25 14:36:43 calle + * common function for T1 PCI and B1 PCI V4. + * + * + */ + +#include <linux/config.h> +#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 <asm/uaccess.h> +#include "capilli.h" +#include "avmcard.h" +#include "capicmd.h" +#include "capiutil.h" + +static char *revision = "$Revision: 1.2 $"; + +/* ------------------------------------------------------------- */ + +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); + +int suppress_pollack = 0; +MODULE_PARM(suppress_pollack, "0-1i"); + +/* ------------------------------------------------------------- */ + +static void b1dma_dispatch_tx(avmcard *card); + +/* ------------------------------------------------------------- */ + +/* S5933 */ + +#define AMCC_RXPTR 0x24 +#define AMCC_RXLEN 0x28 +#define AMCC_TXPTR 0x2c +#define AMCC_TXLEN 0x30 + +#define AMCC_INTCSR 0x38 +# define EN_READ_TC_INT 0x00008000L +# define EN_WRITE_TC_INT 0x00004000L +# define EN_TX_TC_INT EN_READ_TC_INT +# define EN_RX_TC_INT EN_WRITE_TC_INT +# define AVM_FLAG 0x30000000L + +# define ANY_S5933_INT 0x00800000L +# define READ_TC_INT 0x00080000L +# define WRITE_TC_INT 0x00040000L +# define TX_TC_INT READ_TC_INT +# define RX_TC_INT WRITE_TC_INT +# define MASTER_ABORT_INT 0x00100000L +# define TARGET_ABORT_INT 0x00200000L +# define BUS_MASTER_INT 0x00200000L +# define ALL_INT 0x000C0000L + +#define AMCC_MCSR 0x3c +# define A2P_HI_PRIORITY 0x00000100L +# define EN_A2P_TRANSFERS 0x00000400L +# define P2A_HI_PRIORITY 0x00001000L +# define EN_P2A_TRANSFERS 0x00004000L +# define RESET_A2P_FLAGS 0x04000000L +# define RESET_P2A_FLAGS 0x02000000L + +/* ------------------------------------------------------------- */ + +#define b1dmaoutmeml(addr, value) writel(value, addr) +#define b1dmainmeml(addr) readl(addr) +#define b1dmaoutmemw(addr, value) writew(value, addr) +#define b1dmainmemw(addr) readw(addr) +#define b1dmaoutmemb(addr, value) writeb(value, addr) +#define b1dmainmemb(addr) readb(addr) + +/* ------------------------------------------------------------- */ + +static inline int b1dma_tx_empty(unsigned int port) +{ + return inb(port + 0x03) & 0x1; +} + +static inline int b1dma_rx_full(unsigned int port) +{ + return inb(port + 0x02) & 0x1; +} + +static int b1dma_tolink(avmcard *card, void *buf, unsigned int len) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + unsigned char *s = (unsigned char *)buf; + while (len--) { + while ( !b1dma_tx_empty(card->port) + && time_before(jiffies, stop)); + if (!b1dma_tx_empty(card->port)) + return -1; + t1outp(card->port, 0x01, *s++); + } + return 0; +} + +static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + unsigned char *s = (unsigned char *)buf; + while (len--) { + while ( !b1dma_rx_full(card->port) + && time_before(jiffies, stop)); + if (!b1dma_rx_full(card->port)) + return -1; + *s++ = t1inp(card->port, 0x00); + } + return 0; +} + +static int WriteReg(avmcard *card, __u32 reg, __u8 val) +{ + __u8 cmd = 0x00; + if ( b1dma_tolink(card, &cmd, 1) == 0 + && b1dma_tolink(card, ®, 4) == 0) { + __u32 tmp = val; + return b1dma_tolink(card, &tmp, 4); + } + return -1; +} + +static __u8 ReadReg(avmcard *card, __u32 reg) +{ + __u8 cmd = 0x01; + if ( b1dma_tolink(card, &cmd, 1) == 0 + && b1dma_tolink(card, ®, 4) == 0) { + __u32 tmp; + if (b1dma_fromlink(card, &tmp, 4) == 0) + return (__u8)tmp; + } + return 0xff; +} + +/* ------------------------------------------------------------- */ + +static inline void _put_byte(void **pp, __u8 val) +{ + __u8 *s = *pp; + *s++ = val; + *pp = s; +} + +static inline void _put_word(void **pp, __u32 val) +{ + __u8 *s = *pp; + *s++ = val & 0xff; + *s++ = (val >> 8) & 0xff; + *s++ = (val >> 16) & 0xff; + *s++ = (val >> 24) & 0xff; + *pp = s; +} + +static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) +{ + unsigned i = len; + _put_word(pp, i); + while (i-- > 0) + _put_byte(pp, *dp++); +} + +static inline __u8 _get_byte(void **pp) +{ + __u8 *s = *pp; + __u8 val; + val = *s++; + *pp = s; + return val; +} + +static inline __u32 _get_word(void **pp) +{ + __u8 *s = *pp; + __u32 val; + val = *s++; + val |= (*s++ << 8); + val |= (*s++ << 16); + val |= (*s++ << 24); + *pp = s; + return val; +} + +static inline __u32 _get_slice(void **pp, unsigned char *dp) +{ + unsigned int len, i; + + len = i = _get_word(pp); + while (i-- > 0) *dp++ = _get_byte(pp); + return len; +} + +/* ------------------------------------------------------------- */ + +void b1dma_reset(avmcard *card) +{ + unsigned long flags; + + save_flags(flags); + cli(); + card->csr = 0x0; + b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr); + b1dmaoutmeml(card->mbase+AMCC_MCSR, 0); + b1dmaoutmeml(card->mbase+AMCC_RXLEN, 0); + b1dmaoutmeml(card->mbase+AMCC_TXLEN, 0); + + t1outp(card->port, 0x10, 0x00); + t1outp(card->port, 0x07, 0x00); + + restore_flags(flags); + + b1dmaoutmeml(card->mbase+AMCC_MCSR, 0); + udelay(10 * 1000); + b1dmaoutmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */ + udelay(10 * 1000); + b1dmaoutmeml(card->mbase+AMCC_MCSR, 0); + if (card->cardtype == avm_t1pci) + udelay(42 * 1000); + else + udelay(10 * 1000); +} + +/* ------------------------------------------------------------- */ + +int b1dma_detect(avmcard *card) +{ + b1dmaoutmeml(card->mbase+AMCC_MCSR, 0); + udelay(10 * 1000); + b1dmaoutmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */ + udelay(10 * 1000); + b1dmaoutmeml(card->mbase+AMCC_MCSR, 0); + udelay(42 * 1000); + + b1dmaoutmeml(card->mbase+AMCC_RXLEN, 0); + b1dmaoutmeml(card->mbase+AMCC_TXLEN, 0); + card->csr = 0x0; + b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr); + + if (b1dmainmeml(card->mbase+AMCC_MCSR) != 0x000000E6) + return 1; + + b1dmaoutmeml(card->mbase+AMCC_RXPTR, 0xffffffff); + b1dmaoutmeml(card->mbase+AMCC_TXPTR, 0xffffffff); + if ( b1dmainmeml(card->mbase+AMCC_RXPTR) != 0xfffffffc + || b1dmainmeml(card->mbase+AMCC_TXPTR) != 0xfffffffc) + return 2; + + b1dmaoutmeml(card->mbase+AMCC_RXPTR, 0x0); + b1dmaoutmeml(card->mbase+AMCC_TXPTR, 0x0); + if ( b1dmainmeml(card->mbase+AMCC_RXPTR) != 0x0 + || b1dmainmeml(card->mbase+AMCC_TXPTR) != 0x0) + return 3; + + t1outp(card->port, 0x10, 0x00); + t1outp(card->port, 0x07, 0x00); + + t1outp(card->port, 0x02, 0x02); + t1outp(card->port, 0x03, 0x02); + + if ( (t1inp(card->port, 0x02) & 0xFE) != 0x02 + || t1inp(card->port, 0x3) != 0x03) + return 4; + + t1outp(card->port, 0x02, 0x00); + t1outp(card->port, 0x03, 0x00); + + if ( (t1inp(card->port, 0x02) & 0xFE) != 0x00 + || t1inp(card->port, 0x3) != 0x01) + return 5; + + return 0; +} + +int t1pci_detect(avmcard *card) +{ + int ret; + + if ((ret = b1dma_detect(card)) != 0) + return ret; + + /* Transputer test */ + + if ( WriteReg(card, 0x80001000, 0x11) != 0 + || WriteReg(card, 0x80101000, 0x22) != 0 + || WriteReg(card, 0x80201000, 0x33) != 0 + || WriteReg(card, 0x80301000, 0x44) != 0) + return 6; + + if ( ReadReg(card, 0x80001000) != 0x11 + || ReadReg(card, 0x80101000) != 0x22 + || ReadReg(card, 0x80201000) != 0x33 + || ReadReg(card, 0x80301000) != 0x44) + return 7; + + if ( WriteReg(card, 0x80001000, 0x55) != 0 + || WriteReg(card, 0x80101000, 0x66) != 0 + || WriteReg(card, 0x80201000, 0x77) != 0 + || WriteReg(card, 0x80301000, 0x88) != 0) + return 8; + + if ( ReadReg(card, 0x80001000) != 0x55 + || ReadReg(card, 0x80101000) != 0x66 + || ReadReg(card, 0x80201000) != 0x77 + || ReadReg(card, 0x80301000) != 0x88) + return 9; + + return 0; +} + +int b1pciv4_detect(avmcard *card) +{ + int ret, i; + + if ((ret = b1dma_detect(card)) != 0) + return ret; + + for (i=0; i < 5 ; i++) { + if (WriteReg(card, 0x80A00000, 0x21) != 0) + return 6; + if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01) + return 7; + } + for (i=0; i < 5 ; i++) { + if (WriteReg(card, 0x80A00000, 0x20) != 0) + return 8; + if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00) + return 9; + } + + return 0; +} + +/* ------------------------------------------------------------- */ + +static void b1dma_dispatch_tx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + unsigned long flags; + struct sk_buff *skb; + __u8 cmd, subcmd; + __u16 len; + __u32 txlen; + int inint; + void *p; + + save_flags(flags); + cli(); + + inint = card->interrupt; + + if (card->csr & EN_TX_TC_INT) { /* tx busy */ + restore_flags(flags); + return; + } + + skb = skb_dequeue(&dma->send_queue); + if (!skb) { +#ifdef CONFIG_B1DMA_DEBUG + printk(KERN_DEBUG "tx(%d): underrun\n", inint); +#endif + restore_flags(flags); + return; + } + + len = CAPIMSG_LEN(skb->data); + + if (len) { + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + p = dma->sendbuf; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + _put_byte(&p, SEND_DATA_B3_REQ); + _put_slice(&p, skb->data, len); + _put_slice(&p, skb->data + len, dlen); + } else { + _put_byte(&p, SEND_MESSAGE); + _put_slice(&p, skb->data, len); + } + txlen = (__u8 *)p - (__u8 *)dma->sendbuf; +#ifdef CONFIG_B1DMA_DEBUG + printk(KERN_DEBUG "tx(%d): put msg len=%d\n", + inint, txlen); +#endif + } else { + txlen = skb->len-2; +#ifdef CONFIG_B1DMA_POLLDEBUG + if (skb->data[2] == SEND_POLLACK) + printk(KERN_INFO "%s: send ack\n", card->name); +#endif +#ifdef CONFIG_B1DMA_DEBUG + printk(KERN_DEBUG "tx(%d): put 0x%x len=%d\n", + inint, skb->data[2], txlen); +#endif + memcpy(dma->sendbuf, skb->data+2, skb->len-2); + } + txlen = (txlen + 3) & ~3; + + b1dmaoutmeml(card->mbase+AMCC_TXPTR, virt_to_phys(dma->sendbuf)); + b1dmaoutmeml(card->mbase+AMCC_TXLEN, txlen); + + card->csr |= EN_TX_TC_INT; + + if (!inint) + b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr); + + restore_flags(flags); + dev_kfree_skb(skb); +} + +/* ------------------------------------------------------------- */ + +static void queue_pollack(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost poll ack\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_POLLACK); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + b1dma_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +static void b1dma_handle_rx(avmcard *card) +{ + avmctrl_info *cinfo = &card->ctrlinfo[0]; + avmcard_dmainfo *dma = card->dma; + struct capi_ctr *ctrl = cinfo->capi_ctrl; + struct sk_buff *skb; + void *p = dma->recvbuf+4; + __u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; + __u8 b1cmd = _get_byte(&p); + +#ifdef CONFIG_B1DMA_DEBUG + printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen); +#endif + + switch (b1cmd) { + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + DataB3Len = _get_slice(&p, card->databuf); + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf+MsgLen, 0, 30-MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + 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); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, 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 = _get_word(&p); + NCCI = _get_word(&p); + WindowSize = _get_word(&p); + + ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + + if (NCCI != 0xffffffff) + ctrl->free_ncci(ctrl, ApplId, NCCI); + else ctrl->appl_released(ctrl, ApplId); + break; + + case RECEIVE_START: +#ifdef CONFIG_B1DMA_POLLDEBUG + printk(KERN_INFO "%s: receive poll\n", card->name); +#endif + if (!suppress_pollack) + queue_pollack(card); + ctrl->resume_output(ctrl); + break; + + case RECEIVE_STOP: + ctrl->suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + ctrl->ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, 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 = _get_slice(&p, 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; + + default: + printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ + +static void b1dma_handle_interrupt(avmcard *card) +{ + __u32 status = b1dmainmeml(card->mbase+AMCC_INTCSR); + __u32 newcsr; + + if ((status & ANY_S5933_INT) == 0) + return; + + newcsr = card->csr | (status & ALL_INT); + if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT; + if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT; + b1dmaoutmeml(card->mbase+AMCC_INTCSR, newcsr); + + if ((status & RX_TC_INT) != 0) { + __u8 *recvbuf = card->dma->recvbuf; + __u32 rxlen; + if (card->dma->recvlen == 0) { + card->dma->recvlen = *((__u32 *)recvbuf); + rxlen = (card->dma->recvlen + 3) & ~3; + b1dmaoutmeml(card->mbase+AMCC_RXPTR, + virt_to_phys(recvbuf+4)); + b1dmaoutmeml(card->mbase+AMCC_RXLEN, rxlen); + } else { + b1dma_handle_rx(card); + card->dma->recvlen = 0; + b1dmaoutmeml(card->mbase+AMCC_RXPTR, virt_to_phys(recvbuf)); + b1dmaoutmeml(card->mbase+AMCC_RXLEN, 4); + } + } + + if ((status & TX_TC_INT) != 0) { + card->csr &= ~EN_TX_TC_INT; + b1dma_dispatch_tx(card); + } else if (card->csr & EN_TX_TC_INT) { + if (b1dmainmeml(card->mbase+AMCC_TXLEN) == 0) { + card->csr &= ~EN_TX_TC_INT; + b1dma_dispatch_tx(card); + } + } + b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr); +} + +void b1dma_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "b1dma: interrupt: wrong device\n"); + return; + } + if (card->interrupt) { + printk(KERN_ERR "%s: reentering interrupt hander\n", card->name); + return; + } + + card->interrupt = 1; + + b1dma_handle_interrupt(card); + + card->interrupt = 0; +} + +/* ------------------------------------------------------------- */ + +static int b1dma_loaded(avmcard *card) +{ + unsigned long stop; + unsigned char ans; + unsigned long tout = 2; + unsigned int base = card->port; + + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_tx_empty(base)) + break; + } + if (!b1_tx_empty(base)) { + printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n", + card->name); + return 0; + } + b1_put_byte(base, SEND_POLLACK); + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_rx_full(base)) { + if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) { + return 1; + } + printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans); + return 0; + } + } + printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name); + return 0; +} + +/* ------------------------------------------------------------- */ + +static void b1dma_send_init(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(15, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_INIT); + _put_word(&p, AVM_NAPPS); + _put_word(&p, AVM_NCCI_PER_CHANNEL*30); + _put_word(&p, card->cardnr - 1); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + b1dma_dispatch_tx(card); +} + +int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + int retval; + + b1dma_reset(card); + + if ((retval = b1_load_t4file(card, &data->firmware))) { + b1dma_reset(card); + 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(card, &data->configuration))) { + b1dma_reset(card); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1dma_loaded(card)) { + b1dma_reset(card); + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + save_flags(flags); + cli(); + + card->csr = AVM_FLAG; + b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr); + b1dmaoutmeml(card->mbase+AMCC_MCSR, + EN_A2P_TRANSFERS|EN_P2A_TRANSFERS + |A2P_HI_PRIORITY|P2A_HI_PRIORITY + |RESET_A2P_FLAGS|RESET_P2A_FLAGS); + t1outp(card->port, 0x07, 0x30); + t1outp(card->port, 0x10, 0xF0); + + card->dma->recvlen = 0; + b1dmaoutmeml(card->mbase+AMCC_RXPTR, virt_to_phys(card->dma->recvbuf)); + b1dmaoutmeml(card->mbase+AMCC_RXLEN, 4); + card->csr |= EN_RX_TC_INT; + b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr); + restore_flags(flags); + + b1dma_send_init(card); + + return 0; +} + +void b1dma_reset_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + + b1dma_reset(card); + + memset(cinfo->version, 0, sizeof(cinfo->version)); + ctrl->reseted(ctrl); +} + + +/* ------------------------------------------------------------- */ + + +void b1dma_register_appl(struct capi_ctr *ctrl, + __u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + int want = rp->level3cnt; + int nconn; + void *p; + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel; + + skb = alloc_skb(23, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_REGISTER); + _put_word(&p, appl); + _put_word(&p, 1024 * (nconn+1)); + _put_word(&p, nconn); + _put_word(&p, rp->datablkcnt); + _put_word(&p, rp->datablklen); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + b1dma_dispatch_tx(card); + + ctrl->appl_registered(ctrl, appl); +} + +/* ------------------------------------------------------------- */ + +void b1dma_release_appl(struct capi_ctr *ctrl, __u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + void *p; + + skb = alloc_skb(7, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost release appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_RELEASE); + _put_word(&p, appl); + + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + skb_queue_tail(&card->dma->send_queue, skb); + b1dma_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +void b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + skb_queue_tail(&card->dma->send_queue, skb); + b1dma_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +int b1dmactl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + __u8 flag; + int len = 0; + char *s; + __u32 txaddr, txlen, rxaddr, rxlen, csr; + + 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); + len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase); + 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 ((s = cinfo->version[VER_DRIVER]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->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", cinfo->cardname); + + save_flags(flags); + cli(); + + txaddr = (__u32)phys_to_virt(b1dmainmeml(card->mbase+0x2c)); + txaddr -= (__u32)card->dma->sendbuf; + txlen = b1dmainmeml(card->mbase+0x30); + + rxaddr = (__u32)phys_to_virt(b1dmainmeml(card->mbase+0x24)); + rxaddr -= (__u32)card->dma->recvbuf; + rxlen = b1dmainmeml(card->mbase+0x28); + + csr = b1dmainmeml(card->mbase+AMCC_INTCSR); + + restore_flags(flags); + + len += sprintf(page+len, "%-16s 0x%lx\n", + "csr (cached)", (unsigned long)card->csr); + len += sprintf(page+len, "%-16s 0x%lx\n", + "csr", (unsigned long)csr); + len += sprintf(page+len, "%-16s %lu\n", + "txoff", (unsigned long)txaddr); + len += sprintf(page+len, "%-16s %lu\n", + "txlen", (unsigned long)txlen); + len += sprintf(page+len, "%-16s %lu\n", + "rxoff", (unsigned long)rxaddr); + len += sprintf(page+len, "%-16s %lu\n", + "rxlen", (unsigned long)rxlen); + + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +EXPORT_SYMBOL(b1dma_reset); +EXPORT_SYMBOL(t1pci_detect); +EXPORT_SYMBOL(b1pciv4_detect); +EXPORT_SYMBOL(b1dma_interrupt); + +EXPORT_SYMBOL(b1dma_load_firmware); +EXPORT_SYMBOL(b1dma_reset_ctr); +EXPORT_SYMBOL(b1dma_register_appl); +EXPORT_SYMBOL(b1dma_release_appl); +EXPORT_SYMBOL(b1dma_send_message); +EXPORT_SYMBOL(b1dmactl_read_proc); + +#ifdef MODULE +#define b1dma_init init_module +void cleanup_module(void); +#endif + +int b1dma_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 "b1dma: 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 index 01972b2d2..590e825b6 100644 --- a/drivers/isdn/avmb1/b1isa.c +++ b/drivers/isdn/avmb1/b1isa.c @@ -1,11 +1,19 @@ /* - * $Id: b1isa.c,v 1.5 1999/11/05 16:38:01 calle Exp $ + * $Id: b1isa.c,v 1.7 2000/02/02 18:36:03 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.7 2000/02/02 18:36:03 calle + * - Modules are now locked while init_module is running + * - fixed problem with memory mapping if address is not aligned + * + * Revision 1.6 2000/01/25 14:37:39 calle + * new message after successfull detection including card revision and + * used resources. + * * Revision 1.5 1999/11/05 16:38:01 calle * Cleanups before kernel 2.4: * - Changed all messages to use card->name or driver->name instead of @@ -61,7 +69,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.5 $"; +static char *revision = "$Revision: 1.7 $"; /* ------------------------------------------------------------- */ @@ -69,10 +77,6 @@ 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; @@ -96,6 +100,10 @@ static void b1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs) } /* ------------------------------------------------------------- */ +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + static void b1isa_remove_ctr(struct capi_ctr *ctrl) { avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); @@ -122,10 +130,13 @@ static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) avmcard *card; int retval; + MOD_INC_USE_COUNT; + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); if (!card) { printk(KERN_WARNING "b1isa: no memory.\n"); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(card, 0, sizeof(avmcard)); @@ -133,6 +144,7 @@ static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) if (!cinfo) { printk(KERN_WARNING "b1isa: no memory.\n"); kfree(card); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(cinfo, 0, sizeof(avmctrl_info)); @@ -149,12 +161,14 @@ static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) card->port, card->port + AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } if (b1_irq_table[card->irq & 0xf] == 0) { printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EINVAL; } if ( card->port != 0x150 && card->port != 0x250 @@ -162,6 +176,7 @@ static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) printk(KERN_WARNING "b1isa: illegal port 0x%x.\n", card->port); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EINVAL; } b1_reset(card->port); @@ -170,9 +185,11 @@ static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) card->port, retval); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EIO; } b1_reset(card->port); + b1_getrevision(card); request_region(p->port, AVMB1_PORTLEN, card->name); @@ -182,6 +199,7 @@ static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) release_region(card->port, AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } @@ -192,10 +210,14 @@ static int b1isa_add_card(struct capi_driver *driver, struct capicardparams *p) release_region(card->port, AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } - MOD_INC_USE_COUNT; + printk(KERN_INFO + "%s: AVM B1 ISA at i/o %#x, irq %d, revision %d\n", + driver->name, card->port, card->irq, card->revision); + return 0; } @@ -205,11 +227,12 @@ static char *b1isa_procinfo(struct capi_ctr *ctrl) if (!cinfo) return ""; - sprintf(cinfo->infobuf, "%s %s 0x%x %d", + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", cinfo->cardname[0] ? cinfo->cardname : "-", cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", cinfo->card ? cinfo->card->port : 0x0, - cinfo->card ? cinfo->card->irq : 0 + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 ); return cinfo->infobuf; } diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index f4e87b12f..f7affea0d 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,11 +1,20 @@ /* - * $Id: b1pci.c,v 1.18 1999/11/05 16:38:01 calle Exp $ + * $Id: b1pci.c,v 1.20 2000/02/02 18:36:03 calle Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ + * Revision 1.20 2000/02/02 18:36:03 calle + * - Modules are now locked while init_module is running + * - fixed problem with memory mapping if address is not aligned + * + * Revision 1.19 2000/01/25 14:33:38 calle + * - Added Support AVM B1 PCI V4.0 (tested with prototype) + * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 + * - support for revision register + * * Revision 1.18 1999/11/05 16:38:01 calle * Cleanups before kernel 2.4: * - Changed all messages to use card->name or driver->name instead of @@ -66,7 +75,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.18 $"; +static char *revision = "$Revision: 1.20 $"; /* ------------------------------------------------------------- */ @@ -138,11 +147,12 @@ static char *b1pci_procinfo(struct capi_ctr *ctrl) if (!cinfo) return ""; - sprintf(cinfo->infobuf, "%s %s 0x%x %d", + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", cinfo->cardname[0] ? cinfo->cardname : "-", cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", cinfo->card ? cinfo->card->port : 0x0, - cinfo->card ? cinfo->card->irq : 0 + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 ); return cinfo->infobuf; } @@ -155,10 +165,13 @@ static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) avmctrl_info *cinfo; int retval; + MOD_INC_USE_COUNT; + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); if (!card) { printk(KERN_WARNING "%s: no memory.\n", driver->name); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(card, 0, sizeof(avmcard)); @@ -166,6 +179,7 @@ static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) if (!cinfo) { printk(KERN_WARNING "%s: no memory.\n", driver->name); kfree(card); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(cinfo, 0, sizeof(avmctrl_info)); @@ -182,6 +196,7 @@ static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) driver->name, card->port, card->port + AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } b1_reset(card->port); @@ -190,9 +205,11 @@ static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) driver->name, card->port, retval); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EIO; } b1_reset(card->port); + b1_getrevision(card); request_region(p->port, AVMB1_PORTLEN, card->name); @@ -203,6 +220,7 @@ static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) release_region(card->port, AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } @@ -214,10 +232,19 @@ static int b1pci_add_card(struct capi_driver *driver, struct capicardparams *p) release_region(card->port, AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } - MOD_INC_USE_COUNT; + if (card->revision >= 4) { + printk(KERN_INFO + "%s: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n", + driver->name, card->port, card->irq, card->revision); + } else { + printk(KERN_INFO + "%s: AVM B1 PCI at i/o %#x, irq %d, revision %d\n", + driver->name, card->port, card->irq, card->revision); + } return 0; } @@ -241,6 +268,187 @@ static struct capi_driver b1pci_driver = { 0, /* no add_card function */ }; +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *div4; + +/* ------------------------------------------------------------- */ + +static void b1pciv4_remove_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + + b1dma_reset(card); + + div4->detach_ctr(ctrl); + free_irq(card->irq, card); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + release_region(card->port, AVMB1_PORTLEN); + ctrl->driverdata = 0; + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + + MOD_DEC_USE_COUNT; +} + +static char *b1pciv4_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +static int b1pciv4_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + unsigned long base, page_offset; + avmcard *card; + avmctrl_info *cinfo; + int retval; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + card->dma = (avmcard_dmainfo *) kmalloc(sizeof(avmcard_dmainfo), GFP_ATOMIC); + if (!card->dma) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card); + return -ENOMEM; + } + memset(card->dma, 0, sizeof(avmcard_dmainfo)); + cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info), GFP_ATOMIC); + if (!cinfo) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card->dma); + kfree(card); + return -ENOMEM; + } + memset(cinfo, 0, sizeof(avmctrl_info)); + card->ctrlinfo = cinfo; + cinfo->card = card; + sprintf(card->name, "b1pciv4-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = avm_b1pci; + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "%s: ports 0x%03x-0x%03x in use.\n", + driver->name, card->port, card->port + AVMB1_PORTLEN); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EBUSY; + } + + base = card->membase & PAGE_MASK; + page_offset = card->membase - base; + card->mbase = ioremap_nocache(base, page_offset + 64); + if (card->mbase) { + card->mbase += page_offset; + } else { + printk(KERN_NOTICE "%s: can't remap memory at 0x%lx\n", + driver->name, card->membase); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EIO; + } + + b1dma_reset(card); + + if ((retval = b1pciv4_detect(card)) != 0) { + printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", + driver->name, card->port, retval); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EIO; + } + b1dma_reset(card); + b1_getrevision(card); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, b1dma_interrupt, SA_SHIRQ, card->name, card); + if (retval) { + printk(KERN_ERR "%s: unable to get IRQ %d.\n", + driver->name, card->irq); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EBUSY; + } + + cinfo->capi_ctrl = div4->attach_ctr(driver, card->name, cinfo); + if (!cinfo->capi_ctrl) { + printk(KERN_ERR "%s: attach controller failed.\n", driver->name); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + return -EBUSY; + } + card->cardnr = cinfo->capi_ctrl->cnr; + + skb_queue_head_init(&card->dma->send_queue); + + printk(KERN_INFO + "%s: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n", + driver->name, card->port, card->irq, + card->membase, card->revision); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* ------------------------------------------------------------- */ + + +static struct capi_driver b1pciv4_driver = { + "b1pciv4", + "0.0", + b1dma_load_firmware, + b1dma_reset_ctr, + b1pciv4_remove_ctr, + b1dma_register_appl, + b1dma_release_appl, + b1dma_send_message, + + b1pciv4_procinfo, + b1dmactl_read_proc, + 0, /* use standard driver_read_proc */ + + 0, /* no add_card function */ +}; + +#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */ + #ifdef MODULE #define b1pci_init init_module void cleanup_module(void); @@ -248,9 +456,55 @@ void cleanup_module(void); static int ncards = 0; +static int add_card(struct pci_dev *dev) +{ + struct capi_driver *driver = &b1pci_driver; + struct capicardparams param; + int retval; + + if (dev->resource[ 2].start & PCI_BASE_ADDRESS_IO_MASK) { /* B1 PCI V4 */ +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + driver = &b1pciv4_driver; +#endif + param.membase = dev->resource[ 0].start & PCI_BASE_ADDRESS_MEM_MASK; + param.port = dev->resource[ 2].start & PCI_BASE_ADDRESS_IO_MASK; + param.irq = dev->irq; + printk(KERN_INFO + "%s: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n", + driver->name, param.port, param.irq, param.membase); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + retval = b1pciv4_add_card(driver, ¶m); +#else + retval = b1pci_add_card(driver, ¶m); +#endif + if (retval != 0) { + printk(KERN_ERR + "%s: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n", + driver->name, param.port, param.irq, param.membase); + } + } else { + param.membase = 0; + param.port = dev->resource[ 1].start & PCI_BASE_ADDRESS_IO_MASK; + param.irq = dev->irq; + printk(KERN_INFO + "%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 + "%s: no AVM-B1 at i/o %#x, irq %d detected\n", + driver->name, param.port, param.irq); + } + } + return retval; +} + int b1pci_init(void) { struct capi_driver *driver = &b1pci_driver; +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + struct capi_driver *driverv4 = &b1pciv4_driver; +#endif struct pci_dev *dev = NULL; char *p; int retval; @@ -271,26 +525,32 @@ int b1pci_init(void) return -EIO; } +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + printk(KERN_INFO "%s: revision %s\n", driverv4->name, driverv4->revision); + + div4 = attach_capi_driver(driverv4); + + if (!div4) { + detach_capi_driver(driver); + printk(KERN_ERR "%s: failed to attach capi_driver\n", + driverv4->name); + return -EIO; + } +#endif + #ifdef CONFIG_PCI if (!pci_present()) { printk(KERN_ERR "%s: no PCI bus present\n", driver->name); detach_capi_driver(driver); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + detach_capi_driver(driverv4); +#endif return -EIO; } while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev))) { - struct capicardparams param; - - param.port = dev->resource[ 1].start & PCI_BASE_ADDRESS_IO_MASK; - param.irq = dev->irq; - printk(KERN_INFO - "%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); + retval = add_card(dev); if (retval != 0) { - printk(KERN_ERR - "%s: no AVM-B1 at i/o %#x, irq %d detected\n", - driver->name, param.port, param.irq); #ifdef MODULE cleanup_module(); #endif @@ -315,5 +575,8 @@ int b1pci_init(void) void cleanup_module(void) { detach_capi_driver(&b1pci_driver); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + detach_capi_driver(&b1pciv4_driver); +#endif } #endif diff --git a/drivers/isdn/avmb1/b1pcmcia.c b/drivers/isdn/avmb1/b1pcmcia.c index 79e343164..6e39c43d2 100644 --- a/drivers/isdn/avmb1/b1pcmcia.c +++ b/drivers/isdn/avmb1/b1pcmcia.c @@ -1,11 +1,19 @@ /* - * $Id: b1pcmcia.c,v 1.5 1999/11/05 16:38:01 calle Exp $ + * $Id: b1pcmcia.c,v 1.7 2000/02/02 18:36:03 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.7 2000/02/02 18:36:03 calle + * - Modules are now locked while init_module is running + * - fixed problem with memory mapping if address is not aligned + * + * Revision 1.6 2000/01/25 14:37:39 calle + * new message after successfull detection including card revision and + * used resources. + * * Revision 1.5 1999/11/05 16:38:01 calle * Cleanups before kernel 2.4: * - Changed all messages to use card->name or driver->name instead of @@ -62,7 +70,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.5 $"; +static char *revision = "$Revision: 1.7 $"; /* ------------------------------------------------------------- */ @@ -126,12 +134,16 @@ static int b1pcmcia_add_card(struct capi_driver *driver, { avmctrl_info *cinfo; avmcard *card; + char *cardname; int retval; + MOD_INC_USE_COUNT; + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); if (!card) { printk(KERN_WARNING "%s: no memory.\n", driver->name); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(card, 0, sizeof(avmcard)); @@ -139,6 +151,7 @@ static int b1pcmcia_add_card(struct capi_driver *driver, if (!cinfo) { printk(KERN_WARNING "%s: no memory.\n", driver->name); kfree(card); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(cinfo, 0, sizeof(avmctrl_info)); @@ -159,9 +172,11 @@ static int b1pcmcia_add_card(struct capi_driver *driver, driver->name, card->port, retval); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EIO; } b1_reset(card->port); + b1_getrevision(card); retval = request_irq(card->irq, b1pcmcia_interrupt, 0, card->name, card); if (retval) { @@ -169,6 +184,7 @@ static int b1pcmcia_add_card(struct capi_driver *driver, driver->name, card->irq); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } @@ -179,10 +195,19 @@ static int b1pcmcia_add_card(struct capi_driver *driver, free_irq(card->irq, card); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } + switch (cardtype) { + case avm_m1: cardname = "M1"; break; + case avm_m2: cardname = "M2"; break; + default : cardname = "B1 PCMCIA"; break; + } + + printk(KERN_INFO + "%s: AVM %s at i/o %#x, irq %d, revision %d\n", + driver->name, cardname, card->port, card->irq, card->revision); - MOD_INC_USE_COUNT; return cinfo->capi_ctrl->cnr; } @@ -194,11 +219,12 @@ static char *b1pcmcia_procinfo(struct capi_ctr *ctrl) if (!cinfo) return ""; - sprintf(cinfo->infobuf, "%s %s 0x%x %d", + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", cinfo->cardname[0] ? cinfo->cardname : "-", cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", cinfo->card ? cinfo->card->port : 0x0, - cinfo->card ? cinfo->card->irq : 0 + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 ); return cinfo->infobuf; } diff --git a/drivers/isdn/avmb1/c4.c b/drivers/isdn/avmb1/c4.c new file mode 100644 index 000000000..7483370cf --- /dev/null +++ b/drivers/isdn/avmb1/c4.c @@ -0,0 +1,1326 @@ +/* + * $Id: c4.c,v 1.4 2000/02/02 18:36:03 calle Exp $ + * + * Module for AVM C4 card. + * + * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: c4.c,v $ + * Revision 1.4 2000/02/02 18:36:03 calle + * - Modules are now locked while init_module is running + * - fixed problem with memory mapping if address is not aligned + * + * Revision 1.3 2000/01/25 14:37:39 calle + * new message after successfull detection including card revision and + * used resources. + * + * Revision 1.2 2000/01/21 20:52:58 keil + * pci_find_subsys as local function for 2.2.X kernel + * + * Revision 1.1 2000/01/20 10:51:37 calle + * Added driver for C4. + * + * + */ + +#include <linux/config.h> +#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/pci.h> +#include <linux/capi.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include "capicmd.h" +#include "capiutil.h" +#include "capilli.h" +#include "avmcard.h" + +static char *revision = "$Revision: 1.4 $"; + +#undef CONFIG_C4_DEBUG +#undef CONFIG_C4_POLLDEBUG + +/* ------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_DEC +#define PCI_VENDOR_ID_DEC 0x1011 +#endif + +#ifndef PCI_DEVICE_ID_DEC_21285 +#define PCI_DEVICE_ID_DEC_21285 0x1065 +#endif + +#ifndef PCI_VENDOR_ID_AVM +#define PCI_VENDOR_ID_AVM 0x1244 +#endif + +#ifndef PCI_DEVICE_ID_AVM_C4 +#define PCI_DEVICE_ID_AVM_C4 0x0800 +#endif + +/* ------------------------------------------------------------- */ + +int suppress_pollack = 0; + +MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); + +MODULE_PARM(suppress_pollack, "0-1i"); + +/* ------------------------------------------------------------- */ + +static struct capi_driver_interface *di; + +/* ------------------------------------------------------------- */ + +static void c4_dispatch_tx(avmcard *card); + +/* ------------------------------------------------------------- */ + +#define DC21285_DRAM_A0MR 0x40000000 +#define DC21285_DRAM_A1MR 0x40004000 +#define DC21285_DRAM_A2MR 0x40008000 +#define DC21285_DRAM_A3MR 0x4000C000 + +#define CAS_OFFSET 0x88 + +#define DC21285_ARMCSR_BASE 0x42000000 + +#define PCI_OUT_INT_STATUS 0x30 +#define PCI_OUT_INT_MASK 0x34 +#define MAILBOX_0 0x50 +#define MAILBOX_1 0x54 +#define MAILBOX_2 0x58 +#define MAILBOX_3 0x5C +#define DOORBELL 0x60 +#define DOORBELL_SETUP 0x64 + +#define CHAN_1_CONTROL 0x90 +#define CHAN_2_CONTROL 0xB0 +#define DRAM_TIMING 0x10C +#define DRAM_ADDR_SIZE_0 0x110 +#define DRAM_ADDR_SIZE_1 0x114 +#define DRAM_ADDR_SIZE_2 0x118 +#define DRAM_ADDR_SIZE_3 0x11C +#define SA_CONTROL 0x13C +#define XBUS_CYCLE 0x148 +#define XBUS_STROBE 0x14C +#define DBELL_PCI_MASK 0x150 +#define DBELL_SA_MASK 0x154 + +#define SDRAM_SIZE 0x1000000 + +/* ------------------------------------------------------------- */ + +#define MBOX_PEEK_POKE MAILBOX_0 + +#define DBELL_ADDR 0x01 +#define DBELL_DATA 0x02 +#define DBELL_RNWR 0x40 +#define DBELL_INIT 0x80 + +/* ------------------------------------------------------------- */ + +#define MBOX_UP_ADDR MAILBOX_0 +#define MBOX_UP_LEN MAILBOX_1 +#define MBOX_DOWN_ADDR MAILBOX_2 +#define MBOX_DOWN_LEN MAILBOX_3 + +#define DBELL_UP_HOST 0x00000100 +#define DBELL_UP_ARM 0x00000200 +#define DBELL_DOWN_HOST 0x00000400 +#define DBELL_DOWN_ARM 0x00000800 +#define DBELL_RESET_HOST 0x40000000 +#define DBELL_RESET_ARM 0x80000000 + +/* ------------------------------------------------------------- */ + +#define DRAM_TIMING_DEF 0x001A01A5 +#define DRAM_AD_SZ_DEF0 0x00000045 +#define DRAM_AD_SZ_NULL 0x00000000 + +#define SA_CTL_ALLRIGHT 0x64AA0271 + +#define INIT_XBUS_CYCLE 0x100016DB +#define INIT_XBUS_STROBE 0xF1F1F1F1 + +/* ------------------------------------------------------------- */ + +#define RESET_TIMEOUT (15*HZ) /* 15 sec */ +#define PEEK_POKE_TIMEOUT (HZ/10) /* 0.1 sec */ + +/* ------------------------------------------------------------- */ + +#define c4outmeml(addr, value) writel(value, addr) +#define c4inmeml(addr) readl(addr) +#define c4outmemw(addr, value) writew(value, addr) +#define c4inmemw(addr) readw(addr) +#define c4outmemb(addr, value) writeb(value, addr) +#define c4inmemb(addr) readb(addr) + +/* ------------------------------------------------------------- */ + +static inline int wait_for_doorbell(avmcard *card, unsigned long t) +{ + unsigned long stop; + + stop = jiffies + t; + while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return -1; + } + return 0; +} + +static int c4_poke(avmcard *card, unsigned long off, unsigned long value) +{ + + if (wait_for_doorbell(card, HZ/10) < 0) + return -1; + + c4outmeml(card->mbase+MBOX_PEEK_POKE, off); + c4outmeml(card->mbase+DOORBELL, DBELL_ADDR); + + if (wait_for_doorbell(card, HZ/10) < 0) + return -1; + + c4outmeml(card->mbase+MBOX_PEEK_POKE, value); + c4outmeml(card->mbase+DOORBELL, DBELL_DATA | DBELL_ADDR); + + return 0; +} + +static int c4_peek(avmcard *card, unsigned long off, unsigned long *valuep) +{ + if (wait_for_doorbell(card, HZ/10) < 0) + return -1; + + c4outmeml(card->mbase+MBOX_PEEK_POKE, off); + c4outmeml(card->mbase+DOORBELL, DBELL_RNWR | DBELL_ADDR); + + if (wait_for_doorbell(card, HZ/10) < 0) + return -1; + + *valuep = c4inmeml(card->mbase+MBOX_PEEK_POKE); + + return 0; +} + +/* ------------------------------------------------------------- */ + +static int c4_load_t4file(avmcard *card, capiloaddatapart * t4file) +{ + __u32 val; + unsigned char *dp; + int left, retval; + __u32 loadoff = 0; + + dp = t4file->data; + left = t4file->len; + while (left >= sizeof(__u32)) { + if (t4file->user) { + retval = copy_from_user(&val, dp, sizeof(val)); + if (retval) + return -EFAULT; + } else { + memcpy(&val, dp, sizeof(val)); + } + if (c4_poke(card, loadoff, val)) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + left -= sizeof(__u32); + dp += sizeof(__u32); + loadoff += sizeof(__u32); + } + if (left) { + val = 0; + if (t4file->user) { + retval = copy_from_user(&val, dp, left); + if (retval) + return -EFAULT; + } else { + memcpy(&val, dp, left); + } + if (c4_poke(card, loadoff, val)) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + } + return 0; +} + +/* ------------------------------------------------------------- */ + +static inline void _put_byte(void **pp, __u8 val) +{ + __u8 *s = *pp; + *s++ = val; + *pp = s; +} + +static inline void _put_word(void **pp, __u32 val) +{ + __u8 *s = *pp; + *s++ = val & 0xff; + *s++ = (val >> 8) & 0xff; + *s++ = (val >> 16) & 0xff; + *s++ = (val >> 24) & 0xff; + *pp = s; +} + +static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) +{ + unsigned i = len; + _put_word(pp, i); + while (i-- > 0) + _put_byte(pp, *dp++); +} + +static inline __u8 _get_byte(void **pp) +{ + __u8 *s = *pp; + __u8 val; + val = *s++; + *pp = s; + return val; +} + +static inline __u32 _get_word(void **pp) +{ + __u8 *s = *pp; + __u32 val; + val = *s++; + val |= (*s++ << 8); + val |= (*s++ << 16); + val |= (*s++ << 24); + *pp = s; + return val; +} + +static inline __u32 _get_slice(void **pp, unsigned char *dp) +{ + unsigned int len, i; + + len = i = _get_word(pp); + while (i-- > 0) *dp++ = _get_byte(pp); + return len; +} + +/* ------------------------------------------------------------- */ + +static void c4_reset(avmcard *card) +{ + unsigned long stop; + + c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM); + + stop = jiffies + HZ*10; + while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return; + c4outmeml(card->mbase+DOORBELL, DBELL_ADDR); + } + + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0); + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0); +} + +/* ------------------------------------------------------------- */ + +static int c4_detect(avmcard *card) +{ + unsigned long stop, dummy; + + c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c); + if (c4inmeml(card->mbase+PCI_OUT_INT_MASK) != 0x0c) + return 1; + + c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM); + + stop = jiffies + HZ*10; + while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return 2; + c4outmeml(card->mbase+DOORBELL, DBELL_ADDR); + } + + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0); + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0); + + c4outmeml(card->mbase+MAILBOX_0, 0x55aa55aa); + if (c4inmeml(card->mbase+MAILBOX_0) != 0x55aa55aa) return 3; + + c4outmeml(card->mbase+MAILBOX_0, 0xaa55aa55); + if (c4inmeml(card->mbase+MAILBOX_0) != 0xaa55aa55) return 4; + + if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_SA_MASK, 0)) return 5; + if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_PCI_MASK, 0)) return 6; + if (c4_poke(card, DC21285_ARMCSR_BASE+SA_CONTROL, SA_CTL_ALLRIGHT)) + return 7; + if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_CYCLE, INIT_XBUS_CYCLE)) + return 8; + if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_STROBE, INIT_XBUS_STROBE)) + return 8; + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, 0)) return 9; + + udelay(1000); + + if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10; + if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11; + if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12; + if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13; + + if (c4_poke(card, DC21285_DRAM_A0MR+CAS_OFFSET, 0)) return 14; + if (c4_poke(card, DC21285_DRAM_A1MR+CAS_OFFSET, 0)) return 15; + if (c4_poke(card, DC21285_DRAM_A2MR+CAS_OFFSET, 0)) return 16; + if (c4_poke(card, DC21285_DRAM_A3MR+CAS_OFFSET, 0)) return 17; + + udelay(1000); + + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, DRAM_TIMING_DEF)) + return 18; + + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_0,DRAM_AD_SZ_DEF0)) + return 19; + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_1,DRAM_AD_SZ_NULL)) + return 20; + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_2,DRAM_AD_SZ_NULL)) + return 21; + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_3,DRAM_AD_SZ_NULL)) + return 22; + + /* Transputer test */ + + if ( c4_poke(card, 0x000000, 0x11111111) + || c4_poke(card, 0x400000, 0x22222222) + || c4_poke(card, 0x800000, 0x33333333) + || c4_poke(card, 0xC00000, 0x44444444)) + return 23; + + if ( c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111 + || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222 + || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333 + || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444) + return 24; + + if ( c4_poke(card, 0x000000, 0x55555555) + || c4_poke(card, 0x400000, 0x66666666) + || c4_poke(card, 0x800000, 0x77777777) + || c4_poke(card, 0xC00000, 0x88888888)) + return 25; + + if ( c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555 + || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666 + || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777 + || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888) + return 26; + + return 0; +} + +/* ------------------------------------------------------------- */ + +static void c4_dispatch_tx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + unsigned long flags; + struct sk_buff *skb; + __u8 cmd, subcmd; + __u16 len; + __u32 txlen; + void *p; + + save_flags(flags); + cli(); + + if (card->csr & DBELL_DOWN_ARM) { /* tx busy */ + restore_flags(flags); + return; + } + + skb = skb_dequeue(&dma->send_queue); + if (!skb) { +#ifdef CONFIG_C4_DEBUG + printk(KERN_DEBUG "%s: tx underrun\n", card->name); +#endif + restore_flags(flags); + return; + } + + len = CAPIMSG_LEN(skb->data); + + if (len) { + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + p = dma->sendbuf; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + __u16 dlen = CAPIMSG_DATALEN(skb->data); + _put_byte(&p, SEND_DATA_B3_REQ); + _put_slice(&p, skb->data, len); + _put_slice(&p, skb->data + len, dlen); + } else { + _put_byte(&p, SEND_MESSAGE); + _put_slice(&p, skb->data, len); + } + txlen = (__u8 *)p - (__u8 *)dma->sendbuf; +#ifdef CONFIG_C4_DEBUG + printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen); +#endif + } else { + txlen = skb->len-2; +#ifdef CONFIG_C4_POLLDEBUG + if (skb->data[2] == SEND_POLLACK) + printk(KERN_INFO "%s: ack to c4\n", card->name); +#endif +#ifdef CONFIG_C4_DEBUG + printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n", + card->name, skb->data[2], txlen); +#endif + memcpy(dma->sendbuf, skb->data+2, skb->len-2); + } + txlen = (txlen + 3) & ~3; + + c4outmeml(card->mbase+MBOX_DOWN_ADDR, virt_to_phys(dma->sendbuf)); + c4outmeml(card->mbase+MBOX_DOWN_LEN, txlen); + + card->csr |= DBELL_DOWN_ARM; + + c4outmeml(card->mbase+DOORBELL, DBELL_DOWN_ARM); + + restore_flags(flags); + dev_kfree_skb(skb); +} + +/* ------------------------------------------------------------- */ + +static void queue_pollack(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost poll ack\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_POLLACK); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +static void c4_handle_rx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + struct capi_ctr *ctrl; + avmctrl_info *cinfo; + struct sk_buff *skb; + void *p = dma->recvbuf; + __u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; + __u8 b1cmd = _get_byte(&p); + __u32 cidx; + + +#ifdef CONFIG_C4_DEBUG + printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name, + b1cmd, (unsigned long)dma->recvlen); +#endif + + switch (b1cmd) { + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + DataB3Len = _get_slice(&p, card->databuf); + cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr; + if (cidx > 3) cidx = 0; + ctrl = card->ctrlinfo[cidx].capi_ctrl; + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf+MsgLen, 0, 30-MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + 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); + ctrl->handle_capimsg(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr; + if (cidx > 3) cidx = 0; + ctrl = card->ctrlinfo[cidx].capi_ctrl; + + 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 = _get_word(&p); + NCCI = _get_word(&p); + WindowSize = _get_word(&p); + cidx = (NCCI&0x7f) - card->cardnr; + if (cidx > 3) cidx = 0; + ctrl = card->ctrlinfo[cidx].capi_ctrl; + + ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + + if (NCCI != 0xffffffff) { + cidx = (NCCI&0x7f) - card->cardnr; + if (cidx > 3) cidx = 0; + ctrl = card->ctrlinfo[cidx].capi_ctrl; + ctrl->free_ncci(ctrl, ApplId, NCCI); + } else { + for (cidx=0; cidx < 4; cidx++) { + ctrl = card->ctrlinfo[cidx].capi_ctrl; + ctrl->appl_released(ctrl, ApplId); + } + } + break; + + case RECEIVE_START: +#ifdef CONFIG_C4_POLLDEBUG + printk(KERN_INFO "%s: poll from c4\n", card->name); +#endif + if (!suppress_pollack) + queue_pollack(card); + for (cidx=0; cidx < 4; cidx++) { + ctrl = card->ctrlinfo[cidx].capi_ctrl; + ctrl->resume_output(ctrl); + } + break; + + case RECEIVE_STOP: + for (cidx=0; cidx < 4; cidx++) { + ctrl = card->ctrlinfo[cidx].capi_ctrl; + ctrl->suspend_output(ctrl); + } + break; + + case RECEIVE_INIT: + + cidx = card->nlogcontr++; + cinfo = &card->ctrlinfo[cidx]; + ctrl = cinfo->capi_ctrl; + cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + ctrl->ready(cinfo->capi_ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, 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 = _get_slice(&p, 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; + + default: + printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ + +static void c4_handle_interrupt(avmcard *card) +{ + __u32 status = c4inmeml(card->mbase+DOORBELL); + + if (status & DBELL_RESET_HOST) { + int i; + c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c); + printk(KERN_ERR "%s: unexpected reset\n", card->name); + for (i=0; i < 4; i++) { + avmctrl_info *cinfo = &card->ctrlinfo[i]; + memset(cinfo->version, 0, sizeof(cinfo->version)); + if (cinfo->capi_ctrl) + cinfo->capi_ctrl->reseted(cinfo->capi_ctrl); + } + return; + } + + status &= (DBELL_UP_HOST | DBELL_DOWN_HOST); + if (!status) + return; + c4outmeml(card->mbase+DOORBELL, status); + + if ((status & DBELL_UP_HOST) != 0) { + card->dma->recvlen = c4inmeml(card->mbase+MBOX_UP_LEN); + c4outmeml(card->mbase+MBOX_UP_LEN, 0); + c4_handle_rx(card); + card->dma->recvlen = 0; + c4outmeml(card->mbase+MBOX_UP_LEN, sizeof(card->dma->recvbuf)); + c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM); + } + + if ((status & DBELL_DOWN_HOST) != 0) { + card->csr &= ~DBELL_DOWN_ARM; + c4_dispatch_tx(card); + } else if (card->csr & DBELL_DOWN_HOST) { + if (c4inmeml(card->mbase+MBOX_DOWN_LEN) == 0) { + card->csr &= ~DBELL_DOWN_ARM; + c4_dispatch_tx(card); + } + } +} + +static void c4_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card; + + card = (avmcard *) devptr; + + if (!card) { + printk(KERN_WARNING "%s: interrupt: wrong device\n", card->name); + return; + } + if (card->interrupt) { + printk(KERN_ERR "%s: reentering interrupt hander\n", + card->name); + return; + } + + card->interrupt = 1; + + c4_handle_interrupt(card); + + card->interrupt = 0; +} + +/* ------------------------------------------------------------- */ + +static void c4_send_init(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(15, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_INIT); + _put_word(&p, AVM_NAPPS); + _put_word(&p, AVM_NCCI_PER_CHANNEL*30); + _put_word(&p, card->cardnr - 1); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); +} + +static int c4_send_config(avmcard *card, capiloaddatapart * config) +{ + struct sk_buff *skb; + __u8 val[sizeof(__u32)]; + void *p; + unsigned char *dp; + int left, retval; + + skb = alloc_skb(12 + ((config->len+3)/4)*5, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, can't send config.\n", + card->name); + return -ENOMEM; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_CONFIG); + _put_word(&p, 1); + _put_byte(&p, SEND_CONFIG); + _put_word(&p, config->len); /* 12 */ + + dp = config->data; + left = config->len; + while (left >= sizeof(__u32)) { + if (config->user) { + retval = copy_from_user(val, dp, sizeof(val)); + if (retval) { + dev_kfree_skb(skb); + return -EFAULT; + } + } else { + memcpy(val, dp, sizeof(val)); + } + _put_byte(&p, SEND_CONFIG); + _put_byte(&p, val[0]); + _put_byte(&p, val[1]); + _put_byte(&p, val[2]); + _put_byte(&p, val[3]); + left -= sizeof(val); + dp += sizeof(val); + } + if (left) { + memset(val, 0, sizeof(val)); + if (config->user) { + retval = copy_from_user(&val, dp, left); + if (retval) { + dev_kfree_skb(skb); + return -EFAULT; + } + } else { + memcpy(&val, dp, left); + } + _put_byte(&p, SEND_CONFIG); + _put_byte(&p, val[0]); + _put_byte(&p, val[1]); + _put_byte(&p, val[2]); + _put_byte(&p, val[3]); + } + + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); + + return 0; +} + +static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + int retval; + + if ((retval = c4_load_t4file(card, &data->firmware))) { + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + c4_reset(card); + return retval; + } + + save_flags(flags); + cli(); + + card->csr = 0; + c4outmeml(card->mbase+MBOX_UP_LEN, 0); + c4outmeml(card->mbase+MBOX_DOWN_LEN, 0); + c4outmeml(card->mbase+DOORBELL, DBELL_INIT); + udelay(1000); + c4outmeml(card->mbase+DOORBELL, + DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST); + + c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x08); + + card->dma->recvlen = 0; + c4outmeml(card->mbase+MBOX_UP_ADDR, virt_to_phys(card->dma->recvbuf)); + c4outmeml(card->mbase+MBOX_UP_LEN, sizeof(card->dma->recvbuf)); + c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM); + restore_flags(flags); + + if (data->configuration.len > 0 && data->configuration.data) + c4_send_config(card, &data->configuration); + + c4_send_init(card); + + return 0; +} + + +void c4_reset_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card; + avmctrl_info *cinfo; + int i; + + c4_reset(card); + + for (i=0; i < 4; i++) { + cinfo = &card->ctrlinfo[i]; + memset(cinfo->version, 0, sizeof(cinfo->version)); + if (cinfo->capi_ctrl) + cinfo->capi_ctrl->reseted(cinfo->capi_ctrl); + } +} + +static void c4_remove_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card; + avmctrl_info *cinfo; + int i; + + c4_reset(card); + + for (i=0; i <= 4; i++) { + cinfo = &card->ctrlinfo[i]; + if (cinfo->capi_ctrl) + di->detach_ctr(cinfo->capi_ctrl); + } + + free_irq(card->irq, card); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + release_region(card->port, AVMB1_PORTLEN); + ctrl->driverdata = 0; + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------- */ + + +void c4_register_appl(struct capi_ctr *ctrl, + __u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + int want = rp->level3cnt; + int nconn; + void *p; + + if (ctrl->cnr == card->cardnr) { + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * 4 * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel * 4; + + skb = alloc_skb(23, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_REGISTER); + _put_word(&p, appl); + _put_word(&p, 1024 * (nconn+1)); + _put_word(&p, nconn); + _put_word(&p, rp->datablkcnt); + _put_word(&p, rp->datablklen); + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); + } + + ctrl->appl_registered(ctrl, appl); +} + +/* ------------------------------------------------------------- */ + +void c4_release_appl(struct capi_ctr *ctrl, __u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + void *p; + + if (ctrl->cnr == card->cardnr) { + skb = alloc_skb(7, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost release appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_RELEASE); + _put_word(&p, appl); + + skb_put(skb, (__u8 *)p - (__u8 *)skb->data); + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); + } +} + +/* ------------------------------------------------------------- */ + + +static void c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +static char *c4_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0 + ); + return cinfo->infobuf; +} + +static int c4_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + __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); + len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase); + 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 ((s = cinfo->version[VER_DRIVER]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->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", cinfo->cardname); + + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +static int c4_add_card(struct capi_driver *driver, struct capicardparams *p) +{ + unsigned long base, page_offset; + avmctrl_info *cinfo; + avmcard *card; + int retval; + int i; + + MOD_INC_USE_COUNT; + + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); + + if (!card) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + memset(card, 0, sizeof(avmcard)); + card->dma = (avmcard_dmainfo *) kmalloc(sizeof(avmcard_dmainfo), GFP_ATOMIC); + if (!card->dma) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card); + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + memset(card->dma, 0, sizeof(avmcard_dmainfo)); + cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info)*4, GFP_ATOMIC); + if (!cinfo) { + printk(KERN_WARNING "%s: no memory.\n", driver->name); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + memset(cinfo, 0, sizeof(avmctrl_info)*4); + card->ctrlinfo = cinfo; + for (i=0; i < 4; i++) { + cinfo = &card->ctrlinfo[i]; + cinfo->card = card; + } + sprintf(card->name, "c4-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = avm_c4; + + if (check_region(card->port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "%s: ports 0x%03x-0x%03x in use.\n", + driver->name, card->port, card->port + AVMB1_PORTLEN); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + MOD_DEC_USE_COUNT; + return -EBUSY; + } + + base = card->membase & PAGE_MASK; + page_offset = card->membase - base; + card->mbase = ioremap_nocache(base, page_offset + 128); + if (card->mbase) { + card->mbase += page_offset; + } else { + printk(KERN_NOTICE "%s: can't remap memory at 0x%lx\n", + driver->name, card->membase); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + MOD_DEC_USE_COUNT; + return -EIO; + } + + if ((retval = c4_detect(card)) != 0) { + printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", + driver->name, card->port, retval); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + MOD_DEC_USE_COUNT; + return -EIO; + } + c4_reset(card); + + request_region(p->port, AVMB1_PORTLEN, card->name); + + retval = request_irq(card->irq, c4_interrupt, SA_SHIRQ, card->name, card); + if (retval) { + printk(KERN_ERR "%s: unable to get IRQ %d.\n", + driver->name, card->irq); + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + release_region(card->port, AVMB1_PORTLEN); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + MOD_DEC_USE_COUNT; + return -EBUSY; + } + + for (i=0; i < 4; i++) { + cinfo = &card->ctrlinfo[i]; + cinfo->card = card; + cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo); + if (!cinfo->capi_ctrl) { + printk(KERN_ERR "%s: attach controller failed (%d).\n", + driver->name, i); + for (i--; i >= 0; i--) { + cinfo = &card->ctrlinfo[i]; + di->detach_ctr(cinfo->capi_ctrl); + } + iounmap((void *) (((unsigned long) card->mbase) & PAGE_MASK)); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + kfree(card->dma); + kfree(card->ctrlinfo); + kfree(card); + MOD_DEC_USE_COUNT; + return -EBUSY; + } + if (i == 0) + card->cardnr = cinfo->capi_ctrl->cnr; + } + + skb_queue_head_init(&card->dma->send_queue); + + printk(KERN_INFO + "%s: AVM C4 at i/o %#x, irq %d, mem %#lx\n", + driver->name, card->port, card->irq, card->membase); + + return 0; +} + +/* ------------------------------------------------------------- */ + +static struct capi_driver c4_driver = { + "c4", + "0.0", + c4_load_firmware, + c4_reset_ctr, + c4_remove_ctr, + c4_register_appl, + c4_release_appl, + c4_send_message, + + c4_procinfo, + c4_read_proc, + 0, /* use standard driver_read_proc */ + + 0, /* no add_card function */ +}; + +#ifdef MODULE +#define c4_init init_module +void cleanup_module(void); +#endif + + +static int ncards = 0; + +int c4_init(void) +{ + struct capi_driver *driver = &c4_driver; + struct pci_dev *dev = NULL; + char *p; + int retval; + + 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; + } + +#ifdef CONFIG_PCI + if (!pci_present()) { + printk(KERN_ERR "%s: no PCI bus present\n", driver->name); + detach_capi_driver(driver); + return -EIO; + } + + while ((dev = pci_find_subsys( + PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, + PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, dev))) { + struct capicardparams param; + + param.port = dev->resource[ 1].start & PCI_BASE_ADDRESS_IO_MASK; + param.irq = dev->irq; + param.membase = dev->resource[ 0].start & PCI_BASE_ADDRESS_MEM_MASK; + + printk(KERN_INFO + "%s: PCI BIOS reports AVM-C4 at i/o %#x, irq %d, mem %#x\n", + driver->name, param.port, param.irq, param.membase); + retval = c4_add_card(driver, ¶m); + if (retval != 0) { + printk(KERN_ERR + "%s: no AVM-C4 at i/o %#x, irq %d detected, mem %#x\n", + driver->name, param.port, param.irq, param.membase); +#ifdef MODULE + cleanup_module(); +#endif + return retval; + } + ncards++; + } + if (ncards) { + printk(KERN_INFO "%s: %d C4 card(s) detected\n", + driver->name, ncards); + return 0; + } + printk(KERN_ERR "%s: NO C4 card detected\n", driver->name); + return -ESRCH; +#else + printk(KERN_ERR "%s: kernel not compiled with PCI.\n", driver->name); + return -EIO; +#endif +} + +#ifdef MODULE +void cleanup_module(void) +{ + detach_capi_driver(&c4_driver); +} +#endif diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 6214a7c75..62f246407 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -128,6 +128,7 @@ #include <linux/poll.h> #include <linux/capi.h> #include <linux/kernelcapi.h> +#include <linux/devfs_fs_kernel.h> #include "capiutil.h" #include "capicmd.h" @@ -511,13 +512,18 @@ capi_release(struct inode *inode, struct file *file) static struct file_operations capi_fops = { - llseek: capi_llseek, - read: capi_read, - write: capi_write, - poll: capi_poll, - ioctl: capi_ioctl, - open: capi_open, - release: capi_release, + capi_llseek, + capi_read, + capi_write, + NULL, /* capi_readdir */ + capi_poll, + capi_ioctl, + NULL, /* capi_mmap */ + capi_open, + NULL, /* capi_flush */ + capi_release, + NULL, /* capi_fsync */ + NULL, /* capi_fasync */ }; /* -------- /proc functions ----------------------------------- */ @@ -616,14 +622,36 @@ int capi_init(void) init_waitqueue_head(&capidevs[j].recv_wait); } - if (register_chrdev(capi_major, "capi20", &capi_fops)) { + if (devfs_register_chrdev(capi_major, "capi20", &capi_fops)) { printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); return -EIO; } + devfs_register (NULL, "isdn/capi20", 0, DEVFS_FL_DEFAULT, + capi_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &capi_fops, NULL); + devfs_register_series (NULL, "isdn/capi20.0%u", 10, DEVFS_FL_DEFAULT, + capi_major, 1, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &capi_fops, NULL); + devfs_register_series (NULL, "isdn/capi20.1%u", 10, DEVFS_FL_DEFAULT, + capi_major, 11, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &capi_fops, NULL); printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major); if ((capifuncs = attach_capi_interface(&cuser)) == 0) { - unregister_chrdev(capi_major, "capi20"); + devfs_unregister_chrdev(capi_major, "capi20"); + devfs_unregister(devfs_find_handle(NULL, "capi20", 0, + capi_major, 0, + DEVFS_SPECIAL_CHR, 0)); + for (j = 0; j < 10; j++) { + char devname[32]; + + sprintf(devname, "isdn/capi20.0%i", j); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, j + 1, DEVFS_SPECIAL_CHR, 0)); + sprintf (devname, "isdn/capi20.1%i", j); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, j + 11, DEVFS_SPECIAL_CHR, 0)); + } return -EIO; } (void)proc_init(); @@ -633,8 +661,18 @@ int capi_init(void) #ifdef MODULE void cleanup_module(void) { + int i; + char devname[32]; + (void)proc_exit(); - unregister_chrdev(capi_major, "capi20"); + devfs_unregister_chrdev(capi_major, "capi20"); + devfs_unregister(devfs_find_handle(NULL, "isdn/capi20", 0, capi_major, 0, DEVFS_SPECIAL_CHR, 0)); + for (i = 0; i < 10; i++) { + sprintf (devname, "isdn/capi20.0%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, i + 1, DEVFS_SPECIAL_CHR, 0)); + sprintf (devname, "isdn/capi20.1%i", i); + devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_major, i + 11, DEVFS_SPECIAL_CHR, 0)); + } (void) detach_capi_interface(&cuser); } diff --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c index 6db170e42..79bb370d1 100644 --- a/drivers/isdn/avmb1/capidrv.c +++ b/drivers/isdn/avmb1/capidrv.c @@ -1,11 +1,14 @@ /* - * $Id: capidrv.c,v 1.28 1999/11/05 16:22:37 calle Exp $ + * $Id: capidrv.c,v 1.29 1999/12/06 17:13:06 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.29 1999/12/06 17:13:06 calle + * Added controller watchdog. + * * Revision 1.28 1999/11/05 16:22:37 calle * Bugfix: Missing break in switch on ISDN_CMD_HANGUP. * @@ -168,7 +171,7 @@ #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.28 $"; +static char *revision = "$Revision: 1.29 $"; int debugmode = 0; MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); @@ -196,6 +199,7 @@ struct capidrv_contr { int state; __u32 cipmask; __u32 cipmask2; + struct timer_list listentimer; /* * ID of capi message sent @@ -2188,6 +2192,29 @@ static void disable_dchannel_trace(capidrv_contr *card) send_message(card, &cmdcmsg); } +static void send_listen(capidrv_contr *card) +{ + capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, + card->msgid++, + card->contrnr, /* controller */ + 1 << 6, /* Infomask */ + card->cipmask, + card->cipmask2, + 0, 0); + send_message(card, &cmdcmsg); + listen_change_state(card, EV_LISTEN_REQ); +} + +static void listentimerfunc(unsigned long x) +{ + capidrv_contr *card = (capidrv_contr *)x; + if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE) + printk(KERN_ERR "%s: controller dead ??\n", card->name); + send_listen(card); + mod_timer(&card->listentimer, jiffies + 60*HZ); +} + + static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) { capidrv_contr *card; @@ -2202,6 +2229,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) return -1; } memset(card, 0, sizeof(capidrv_contr)); + init_timer(&card->listentimer); strcpy(card->name, id); card->contrnr = contr; card->nbchan = profp->nbchannel; @@ -2258,15 +2286,11 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) card->cipmask = 0x1FFF03FF; /* any */ card->cipmask2 = 0; - capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, - card->msgid++, - contr, /* controller */ - 1 << 6, /* Infomask */ - card->cipmask, - card->cipmask2, - 0, 0); - send_message(card, &cmdcmsg); - listen_change_state(card, EV_LISTEN_REQ); + send_listen(card); + + card->listentimer.data = (unsigned long)card; + card->listentimer.function = listentimerfunc; + mod_timer(&card->listentimer, jiffies + 60*HZ); printk(KERN_INFO "%s: now up (%d B channels)\n", card->name, card->nbchan); @@ -2312,6 +2336,7 @@ static int capidrv_delcontr(__u16 contr) printk(KERN_ERR "capidrv: bug in free_plci()\n"); } kfree(card->bchans); + del_timer(&card->listentimer); printk(KERN_INFO "%s: now down.\n", card->name); diff --git a/drivers/isdn/avmb1/kcapi.c b/drivers/isdn/avmb1/kcapi.c index 84fb0d2f7..d82fbc496 100644 --- a/drivers/isdn/avmb1/kcapi.c +++ b/drivers/isdn/avmb1/kcapi.c @@ -1,11 +1,18 @@ /* - * $Id: kcapi.c,v 1.10 1999/10/26 15:30:32 calle Exp $ + * $Id: kcapi.c,v 1.12 2000/01/28 16:45:39 calle Exp $ * * Kernel CAPI 2.0 Module * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: kcapi.c,v $ + * Revision 1.12 2000/01/28 16:45:39 calle + * new manufacturer command KCAPI_CMD_ADDCARD (generic addcard), + * will search named driver and call the add_card function if one exist. + * + * Revision 1.11 1999/11/23 13:29:29 calle + * Bugfix: incoming capi message were never traced. + * * Revision 1.10 1999/10/26 15:30:32 calle * Generate error message if user want to add card, but driver module is * not loaded. @@ -79,7 +86,7 @@ #include <linux/b1lli.h> #endif -static char *revision = "$Revision: 1.10 $"; +static char *revision = "$Revision: 1.12 $"; /* ------------------------------------------------------------- */ @@ -154,10 +161,6 @@ 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; @@ -703,9 +706,9 @@ static void controllercb_handle_capimsg(struct capi_ctr * card, if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { card->nrecvdatapkt++; if (card->traceflag > 2) showctl |= 2; - if (card->traceflag) showctl |= 2; } else { card->nrecvctlpkt++; + if (card->traceflag) showctl |= 2; } showctl |= (card->traceflag & 1); if (showctl & 2) { @@ -877,8 +880,14 @@ drivercb_attach_ctr(struct capi_driver *driver, char *name, void *driverdata) *pp = card; driver->ncontroller++; sprintf(card->procfn, "capi/controllers/%d", card->cnr); - card->procent = create_proc_read_entry(card->procfn, 0, 0, - driver->ctr_read_proc, card); + 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); @@ -947,18 +956,18 @@ struct capi_driver_interface *attach_capi_driver(struct capi_driver *driver) 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_read_entry(driver->procfn, 0, 0, - driver->driver_read_proc - ? driver->driver_read_proc - : driver_read_proc, - driver); + 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; } @@ -968,10 +977,6 @@ void detach_capi_driver(struct capi_driver *driver) 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); @@ -1186,6 +1191,15 @@ static __u16 capi_get_profile(__u32 contr, struct capi_profile *profp) return CAPI_NOERROR; } +static struct capi_driver *find_driver(char *name) +{ + struct capi_driver *dp; + for (dp = drivers; dp; dp = dp->next) + if (strcmp(dp->name, name) == 0) + return dp; + return 0; +} + #ifdef CONFIG_AVMB1_COMPAT static int old_capi_manufacturer(unsigned int cmd, void *data) { @@ -1217,9 +1231,15 @@ static int old_capi_manufacturer(unsigned int cmd, void *data) 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; + case AVM_CARDTYPE_B1: + driver = find_driver("b1isa"); + break; + case AVM_CARDTYPE_T1: + driver = find_driver("t1isa"); + break; + default: + driver = 0; + break; } if (!driver) { printk(KERN_ERR "kcapi: driver not loaded.\n"); @@ -1331,9 +1351,7 @@ static int old_capi_manufacturer(unsigned int cmd, void *data) return -ESRCH; gdef.cardstate = card->cardstate; - if (card->driver == b1isa_driver) - gdef.cardtype = AVM_CARDTYPE_B1; - else if (card->driver == t1isa_driver) + if (card->driver == find_driver("t1isa")) gdef.cardtype = AVM_CARDTYPE_T1; else gdef.cardtype = AVM_CARDTYPE_B1; @@ -1377,7 +1395,6 @@ static int old_capi_manufacturer(unsigned int cmd, void *data) static int capi_manufacturer(unsigned int cmd, void *data) { struct capi_ctr *card; - kcapi_flagdef fdef; int retval; switch (cmd) { @@ -1392,6 +1409,9 @@ static int capi_manufacturer(unsigned int cmd, void *data) return old_capi_manufacturer(cmd, data); #endif case KCAPI_CMD_TRACE: + { + kcapi_flagdef fdef; + if ((retval = copy_from_user((void *) &fdef, data, sizeof(kcapi_flagdef)))) return retval; @@ -1406,6 +1426,44 @@ static int capi_manufacturer(unsigned int cmd, void *data) card->cnr, card->traceflag); return 0; } + + case KCAPI_CMD_ADDCARD: + { + struct capi_driver *driver; + capicardparams cparams; + kcapi_carddef cdef; + + if ((retval = copy_from_user((void *) &cdef, data, + sizeof(cdef)))) + return retval; + + cparams.port = cdef.port; + cparams.irq = cdef.irq; + cparams.membase = cdef.membase; + cparams.cardnr = cdef.cardnr; + cparams.cardtype = 0; + cdef.driver[sizeof(cdef.driver)-1] = 0; + + if ((driver = find_driver(cdef.driver)) == 0) { + printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n", + cdef.driver); + return -ESRCH; + } + + if (!driver->add_card) { + printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver); + return -EIO; + } + + return driver->add_card(driver, &cparams); + } + + default: + printk(KERN_ERR "kcapi: manufacturer command %d unknown.\n", + cmd); + break; + + } return -EINVAL; } diff --git a/drivers/isdn/avmb1/t1isa.c b/drivers/isdn/avmb1/t1isa.c index 22a07f2ad..e1efd3939 100644 --- a/drivers/isdn/avmb1/t1isa.c +++ b/drivers/isdn/avmb1/t1isa.c @@ -1,11 +1,19 @@ /* - * $Id: t1isa.c,v 1.8 1999/11/05 16:38:01 calle Exp $ + * $Id: t1isa.c,v 1.10 2000/02/02 18:36:04 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.10 2000/02/02 18:36:04 calle + * - Modules are now locked while init_module is running + * - fixed problem with memory mapping if address is not aligned + * + * Revision 1.9 2000/01/25 14:37:39 calle + * new message after successfull detection including card revision and + * used resources. + * * Revision 1.8 1999/11/05 16:38:01 calle * Cleanups before kernel 2.4: * - Changed all messages to use card->name or driver->name instead of @@ -73,7 +81,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.8 $"; +static char *revision = "$Revision: 1.10 $"; /* ------------------------------------------------------------- */ @@ -413,10 +421,13 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) avmcard *card; int retval; + MOD_INC_USE_COUNT; + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); if (!card) { printk(KERN_WARNING "%s: no memory.\n", driver->name); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(card, 0, sizeof(avmcard)); @@ -424,6 +435,7 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) if (!cinfo) { printk(KERN_WARNING "%s: no memory.\n", driver->name); kfree(card); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(cinfo, 0, sizeof(avmctrl_info)); @@ -440,6 +452,7 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) driver->name, card->port); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EINVAL; } @@ -449,6 +462,7 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) driver->name, card->port, card->port + AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } if (hema_irq_table[card->irq & 0xf] == 0) { @@ -456,6 +470,7 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) driver->name, card->irq); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EINVAL; } for (ctrl = driver->controller; ctrl; ctrl = ctrl->next) { @@ -465,6 +480,7 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) driver->name, card->cardnr, cardp->port); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } } @@ -473,6 +489,7 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) driver->name, card->port, retval); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EIO; } t1_disable_irq(card->port); @@ -487,6 +504,7 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) release_region(card->port, AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } @@ -498,10 +516,14 @@ static int t1isa_add_card(struct capi_driver *driver, struct capicardparams *p) release_region(card->port, AVMB1_PORTLEN); kfree(card->ctrlinfo); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } - MOD_INC_USE_COUNT; + printk(KERN_INFO + "%s: AVM T1 ISA at i/o %#x, irq %d, card %d\n", + driver->name, card->port, card->irq, card->cardnr); + return 0; } diff --git a/drivers/isdn/avmb1/t1pci.c b/drivers/isdn/avmb1/t1pci.c index d77340bff..d24894d9b 100644 --- a/drivers/isdn/avmb1/t1pci.c +++ b/drivers/isdn/avmb1/t1pci.c @@ -1,11 +1,20 @@ /* - * $Id: t1pci.c,v 1.3 1999/11/13 21:27:16 keil Exp $ + * $Id: t1pci.c,v 1.5 2000/02/02 18:36:04 calle Exp $ * * Module for AVM T1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: t1pci.c,v $ + * Revision 1.5 2000/02/02 18:36:04 calle + * - Modules are now locked while init_module is running + * - fixed problem with memory mapping if address is not aligned + * + * Revision 1.4 2000/01/25 14:33:38 calle + * - Added Support AVM B1 PCI V4.0 (tested with prototype) + * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 + * - support for revision register + * * Revision 1.3 1999/11/13 21:27:16 keil * remove KERNELVERSION * @@ -38,7 +47,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.3 $"; +static char *revision = "$Revision: 1.5 $"; #undef CONFIG_T1PCI_DEBUG #undef CONFIG_T1PCI_POLLDEBUG @@ -55,712 +64,20 @@ static char *revision = "$Revision: 1.3 $"; /* ------------------------------------------------------------- */ -int suppress_pollack = 0; - MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); -MODULE_PARM(suppress_pollack, "0-1i"); - - /* ------------------------------------------------------------- */ static struct capi_driver_interface *di; /* ------------------------------------------------------------- */ -static void t1pci_dispatch_tx(avmcard *card); - -/* ------------------------------------------------------------- */ - -/* S5933 */ - -#define AMCC_RXPTR 0x24 -#define AMCC_RXLEN 0x28 -#define AMCC_TXPTR 0x2c -#define AMCC_TXLEN 0x30 - -#define AMCC_INTCSR 0x38 -# define EN_READ_TC_INT 0x00008000L -# define EN_WRITE_TC_INT 0x00004000L -# define EN_TX_TC_INT EN_READ_TC_INT -# define EN_RX_TC_INT EN_WRITE_TC_INT -# define AVM_FLAG 0x30000000L - -# define ANY_S5933_INT 0x00800000L -# define READ_TC_INT 0x00080000L -# define WRITE_TC_INT 0x00040000L -# define TX_TC_INT READ_TC_INT -# define RX_TC_INT WRITE_TC_INT -# define MASTER_ABORT_INT 0x00100000L -# define TARGET_ABORT_INT 0x00200000L -# define BUS_MASTER_INT 0x00200000L -# define ALL_INT 0x000C0000L - -#define AMCC_MCSR 0x3c -# define A2P_HI_PRIORITY 0x00000100L -# define EN_A2P_TRANSFERS 0x00000400L -# define P2A_HI_PRIORITY 0x00001000L -# define EN_P2A_TRANSFERS 0x00004000L -# define RESET_A2P_FLAGS 0x04000000L -# define RESET_P2A_FLAGS 0x02000000L - -/* ------------------------------------------------------------- */ - -#define t1outmeml(addr, value) writel(value, addr) -#define t1inmeml(addr) readl(addr) -#define t1outmemw(addr, value) writew(value, addr) -#define t1inmemw(addr) readw(addr) -#define t1outmemb(addr, value) writeb(value, addr) -#define t1inmemb(addr) readb(addr) - -/* ------------------------------------------------------------- */ - -static inline int t1pci_tx_empty(unsigned int port) -{ - return inb(port + 0x03) & 0x1; -} - -static inline int t1pci_rx_full(unsigned int port) -{ - return inb(port + 0x02) & 0x1; -} - -static int t1pci_tolink(avmcard *card, void *buf, unsigned int len) -{ - unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ - unsigned char *s = (unsigned char *)buf; - while (len--) { - while ( !t1pci_tx_empty(card->port) - && time_before(jiffies, stop)); - if (!t1pci_tx_empty(card->port)) - return -1; - t1outp(card->port, 0x01, *s++); - } - return 0; -} - -static int t1pci_fromlink(avmcard *card, void *buf, unsigned int len) -{ - unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ - unsigned char *s = (unsigned char *)buf; - while (len--) { - while ( !t1pci_rx_full(card->port) - && time_before(jiffies, stop)); - if (!t1pci_rx_full(card->port)) - return -1; - *s++ = t1inp(card->port, 0x00); - } - return 0; -} - -static int WriteReg(avmcard *card, __u32 reg, __u8 val) -{ - __u8 cmd = 0x00; - if ( t1pci_tolink(card, &cmd, 1) == 0 - && t1pci_tolink(card, ®, 4) == 0) { - __u32 tmp = val; - return t1pci_tolink(card, &tmp, 4); - } - return -1; -} - -static __u8 ReadReg(avmcard *card, __u32 reg) -{ - __u8 cmd = 0x01; - if ( t1pci_tolink(card, &cmd, 1) == 0 - && t1pci_tolink(card, ®, 4) == 0) { - __u32 tmp; - if (t1pci_fromlink(card, &tmp, 4) == 0) - return (__u8)tmp; - } - return 0xff; -} - -/* ------------------------------------------------------------- */ - -static inline void _put_byte(void **pp, __u8 val) -{ - __u8 *s = *pp; - *s++ = val; - *pp = s; -} - -static inline void _put_word(void **pp, __u32 val) -{ - __u8 *s = *pp; - *s++ = val & 0xff; - *s++ = (val >> 8) & 0xff; - *s++ = (val >> 16) & 0xff; - *s++ = (val >> 24) & 0xff; - *pp = s; -} - -static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) -{ - unsigned i = len; - _put_word(pp, i); - while (i-- > 0) - _put_byte(pp, *dp++); -} - -static inline __u8 _get_byte(void **pp) -{ - __u8 *s = *pp; - __u8 val; - val = *s++; - *pp = s; - return val; -} - -static inline __u32 _get_word(void **pp) -{ - __u8 *s = *pp; - __u32 val; - val = *s++; - val |= (*s++ << 8); - val |= (*s++ << 16); - val |= (*s++ << 24); - *pp = s; - return val; -} - -static inline __u32 _get_slice(void **pp, unsigned char *dp) -{ - unsigned int len, i; - - len = i = _get_word(pp); - while (i-- > 0) *dp++ = _get_byte(pp); - return len; -} - -/* ------------------------------------------------------------- */ - -static void t1pci_reset(avmcard *card) -{ - unsigned long flags; - - save_flags(flags); - cli(); - card->csr = 0x0; - t1outmeml(card->mbase+AMCC_INTCSR, card->csr); - t1outmeml(card->mbase+AMCC_MCSR, 0); - t1outmeml(card->mbase+AMCC_RXLEN, 0); - t1outmeml(card->mbase+AMCC_TXLEN, 0); - - t1outp(card->port, T1_RESETLINK, 0x00); - t1outp(card->port, 0x07, 0x00); - - restore_flags(flags); - - t1outmeml(card->mbase+AMCC_MCSR, 0); - udelay(10 * 1000); - t1outmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */ - udelay(10 * 1000); - t1outmeml(card->mbase+AMCC_MCSR, 0); - udelay(42 * 1000); - -} - -/* ------------------------------------------------------------- */ - -static int t1pci_detect(avmcard *card) -{ - t1outmeml(card->mbase+AMCC_MCSR, 0); - udelay(10 * 1000); - t1outmeml(card->mbase+AMCC_MCSR, 0x0f000000); /* reset all */ - udelay(10 * 1000); - t1outmeml(card->mbase+AMCC_MCSR, 0); - udelay(42 * 1000); - - t1outmeml(card->mbase+AMCC_RXLEN, 0); - t1outmeml(card->mbase+AMCC_TXLEN, 0); - card->csr = 0x0; - t1outmeml(card->mbase+AMCC_INTCSR, card->csr); - - if (t1inmeml(card->mbase+AMCC_MCSR) != 0x000000E6) - return 1; - - t1outmeml(card->mbase+AMCC_RXPTR, 0xffffffff); - t1outmeml(card->mbase+AMCC_TXPTR, 0xffffffff); - if ( t1inmeml(card->mbase+AMCC_RXPTR) != 0xfffffffc - || t1inmeml(card->mbase+AMCC_TXPTR) != 0xfffffffc) - return 2; - - t1outmeml(card->mbase+AMCC_RXPTR, 0x0); - t1outmeml(card->mbase+AMCC_TXPTR, 0x0); - if ( t1inmeml(card->mbase+AMCC_RXPTR) != 0x0 - || t1inmeml(card->mbase+AMCC_TXPTR) != 0x0) - return 3; - - t1outp(card->port, T1_RESETLINK, 0x00); - t1outp(card->port, 0x07, 0x00); - - t1outp(card->port, 0x02, 0x02); - t1outp(card->port, 0x03, 0x02); - - if ( (t1inp(card->port, 0x02) & 0xFE) != 0x02 - || t1inp(card->port, 0x3) != 0x03) - return 4; - - t1outp(card->port, 0x02, 0x00); - t1outp(card->port, 0x03, 0x00); - - if ( (t1inp(card->port, 0x02) & 0xFE) != 0x00 - || t1inp(card->port, 0x3) != 0x01) - return 5; - - /* Transputer test */ - - if ( WriteReg(card, 0x80001000, 0x11) != 0 - || WriteReg(card, 0x80101000, 0x22) != 0 - || WriteReg(card, 0x80201000, 0x33) != 0 - || WriteReg(card, 0x80301000, 0x44) != 0) - return 6; - - if ( ReadReg(card, 0x80001000) != 0x11 - || ReadReg(card, 0x80101000) != 0x22 - || ReadReg(card, 0x80201000) != 0x33 - || ReadReg(card, 0x80301000) != 0x44) - return 7; - - if ( WriteReg(card, 0x80001000, 0x55) != 0 - || WriteReg(card, 0x80101000, 0x66) != 0 - || WriteReg(card, 0x80201000, 0x77) != 0 - || WriteReg(card, 0x80301000, 0x88) != 0) - return 8; - - if ( ReadReg(card, 0x80001000) != 0x55 - || ReadReg(card, 0x80101000) != 0x66 - || ReadReg(card, 0x80201000) != 0x77 - || ReadReg(card, 0x80301000) != 0x88) - return 9; - - return 0; -} - -/* ------------------------------------------------------------- */ - -static void t1pci_dispatch_tx(avmcard *card) -{ - avmcard_dmainfo *dma = card->dma; - unsigned long flags; - struct sk_buff *skb; - __u8 cmd, subcmd; - __u16 len; - __u32 txlen; - int inint; - void *p; - - save_flags(flags); - cli(); - - inint = card->interrupt; - - if (card->csr & EN_TX_TC_INT) { /* tx busy */ - restore_flags(flags); - return; - } - - skb = skb_dequeue(&dma->send_queue); - if (!skb) { -#ifdef CONFIG_T1PCI_DEBUG - printk(KERN_DEBUG "tx(%d): underrun\n", inint); -#endif - restore_flags(flags); - return; - } - - len = CAPIMSG_LEN(skb->data); - - if (len) { - cmd = CAPIMSG_COMMAND(skb->data); - subcmd = CAPIMSG_SUBCOMMAND(skb->data); - - p = dma->sendbuf; - - if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { - __u16 dlen = CAPIMSG_DATALEN(skb->data); - _put_byte(&p, SEND_DATA_B3_REQ); - _put_slice(&p, skb->data, len); - _put_slice(&p, skb->data + len, dlen); - } else { - _put_byte(&p, SEND_MESSAGE); - _put_slice(&p, skb->data, len); - } - txlen = (__u8 *)p - (__u8 *)dma->sendbuf; -#ifdef CONFIG_T1PCI_DEBUG - printk(KERN_DEBUG "tx(%d): put msg len=%d\n", - inint, txlen); -#endif - } else { - txlen = skb->len-2; -#ifdef CONFIG_T1PCI_POLLDEBUG - if (skb->data[2] == SEND_POLLACK) - printk(KERN_INFO "%s: ack to t1\n", card->name); -#endif -#ifdef CONFIG_T1PCI_DEBUG - printk(KERN_DEBUG "tx(%d): put 0x%x len=%d\n", - inint, skb->data[2], txlen); -#endif - memcpy(dma->sendbuf, skb->data+2, skb->len-2); - } - txlen = (txlen + 3) & ~3; - - t1outmeml(card->mbase+AMCC_TXPTR, virt_to_phys(dma->sendbuf)); - t1outmeml(card->mbase+AMCC_TXLEN, txlen); - - card->csr |= EN_TX_TC_INT; - - if (!inint) - t1outmeml(card->mbase+AMCC_INTCSR, card->csr); - - restore_flags(flags); - dev_kfree_skb(skb); -} - -/* ------------------------------------------------------------- */ - -static void queue_pollack(avmcard *card) -{ - struct sk_buff *skb; - void *p; - - skb = alloc_skb(3, GFP_ATOMIC); - if (!skb) { - printk(KERN_CRIT "%s: no memory, lost poll ack\n", - card->name); - return; - } - p = skb->data; - _put_byte(&p, 0); - _put_byte(&p, 0); - _put_byte(&p, SEND_POLLACK); - skb_put(skb, (__u8 *)p - (__u8 *)skb->data); - - skb_queue_tail(&card->dma->send_queue, skb); - t1pci_dispatch_tx(card); -} - -/* ------------------------------------------------------------- */ - -static void t1pci_handle_rx(avmcard *card) -{ - avmctrl_info *cinfo = &card->ctrlinfo[0]; - avmcard_dmainfo *dma = card->dma; - struct capi_ctr *ctrl = cinfo->capi_ctrl; - struct sk_buff *skb; - void *p = dma->recvbuf+4; - __u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; - __u8 b1cmd = _get_byte(&p); - -#ifdef CONFIG_T1PCI_DEBUG - printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen); -#endif - - switch (b1cmd) { - case RECEIVE_DATA_B3_IND: - - ApplId = (unsigned) _get_word(&p); - MsgLen = _get_slice(&p, card->msgbuf); - DataB3Len = _get_slice(&p, card->databuf); - - if (MsgLen < 30) { /* not CAPI 64Bit */ - memset(card->msgbuf+MsgLen, 0, 30-MsgLen); - MsgLen = 30; - CAPIMSG_SETLEN(card->msgbuf, 30); - } - 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); - ctrl->handle_capimsg(ctrl, ApplId, skb); - } - break; - - case RECEIVE_MESSAGE: - - ApplId = (unsigned) _get_word(&p); - MsgLen = _get_slice(&p, 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 = _get_word(&p); - NCCI = _get_word(&p); - WindowSize = _get_word(&p); - - ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize); - - break; - - case RECEIVE_FREE_NCCI: - - ApplId = _get_word(&p); - NCCI = _get_word(&p); - - if (NCCI != 0xffffffff) - ctrl->free_ncci(ctrl, ApplId, NCCI); - else ctrl->appl_released(ctrl, ApplId); - break; - - case RECEIVE_START: -#ifdef CONFIG_T1PCI_POLLDEBUG - printk(KERN_INFO "%s: poll from t1\n", card->name); -#endif - if (!suppress_pollack) - queue_pollack(card); - ctrl->resume_output(ctrl); - break; - - case RECEIVE_STOP: - ctrl->suspend_output(ctrl); - break; - - case RECEIVE_INIT: - - cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); - b1_parse_version(cinfo); - printk(KERN_INFO "%s: %s-card (%s) now active\n", - card->name, - cinfo->version[VER_CARDTYPE], - cinfo->version[VER_DRIVER]); - ctrl->ready(ctrl); - break; - - case RECEIVE_TASK_READY: - ApplId = (unsigned) _get_word(&p); - MsgLen = _get_slice(&p, 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 = _get_slice(&p, 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; - - default: - printk(KERN_ERR "%s: t1pci_interrupt: 0x%x ???\n", - card->name, b1cmd); - return; - } -} - -/* ------------------------------------------------------------- */ - -static void t1pci_handle_interrupt(avmcard *card) -{ - __u32 status = t1inmeml(card->mbase+AMCC_INTCSR); - __u32 newcsr; - - if ((status & ANY_S5933_INT) == 0) - return; - - newcsr = card->csr | (status & ALL_INT); - if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT; - if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT; - t1outmeml(card->mbase+AMCC_INTCSR, newcsr); - - if ((status & RX_TC_INT) != 0) { - __u8 *recvbuf = card->dma->recvbuf; - __u32 rxlen; - if (card->dma->recvlen == 0) { - card->dma->recvlen = *((__u32 *)recvbuf); - rxlen = (card->dma->recvlen + 3) & ~3; - t1outmeml(card->mbase+AMCC_RXPTR, - virt_to_phys(recvbuf+4)); - t1outmeml(card->mbase+AMCC_RXLEN, rxlen); - } else { - t1pci_handle_rx(card); - card->dma->recvlen = 0; - t1outmeml(card->mbase+AMCC_RXPTR, virt_to_phys(recvbuf)); - t1outmeml(card->mbase+AMCC_RXLEN, 4); - } - } - - if ((status & TX_TC_INT) != 0) { - card->csr &= ~EN_TX_TC_INT; - t1pci_dispatch_tx(card); - } else if (card->csr & EN_TX_TC_INT) { - if (t1inmeml(card->mbase+AMCC_TXLEN) == 0) { - card->csr &= ~EN_TX_TC_INT; - t1pci_dispatch_tx(card); - } - } - t1outmeml(card->mbase+AMCC_INTCSR, card->csr); -} - -static void t1pci_interrupt(int interrupt, void *devptr, struct pt_regs *regs) -{ - avmcard *card; - - card = (avmcard *) devptr; - - if (!card) { - printk(KERN_WARNING "t1pci: interrupt: wrong device\n"); - return; - } - if (card->interrupt) { - printk(KERN_ERR "%s: reentering interrupt hander\n", card->name); - return; - } - - card->interrupt = 1; - - t1pci_handle_interrupt(card); - - card->interrupt = 0; -} - -/* ------------------------------------------------------------- */ - -static int t1pci_loaded(avmcard *card) -{ - unsigned long stop; - unsigned char ans; - unsigned long tout = 2; - unsigned int base = card->port; - - for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { - if (b1_tx_empty(base)) - break; - } - if (!b1_tx_empty(base)) { - printk(KERN_ERR "%s: t1pci_loaded: tx err, corrupted t4 file ?\n", - card->name); - return 0; - } - b1_put_byte(base, SEND_POLLACK); - for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { - if (b1_rx_full(base)) { - if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) { - return 1; - } - printk(KERN_ERR "%s: t1pci_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans); - return 0; - } - } - printk(KERN_ERR "%s: t1pci_loaded: firmware not running\n", card->name); - return 0; -} - -/* ------------------------------------------------------------- */ - -static void t1pci_send_init(avmcard *card) -{ - struct sk_buff *skb; - void *p; - - skb = alloc_skb(15, GFP_ATOMIC); - if (!skb) { - printk(KERN_CRIT "%s: no memory, lost register appl.\n", - card->name); - return; - } - p = skb->data; - _put_byte(&p, 0); - _put_byte(&p, 0); - _put_byte(&p, SEND_INIT); - _put_word(&p, AVM_NAPPS); - _put_word(&p, AVM_NCCI_PER_CHANNEL*30); - _put_word(&p, card->cardnr - 1); - skb_put(skb, (__u8 *)p - (__u8 *)skb->data); - - skb_queue_tail(&card->dma->send_queue, skb); - t1pci_dispatch_tx(card); -} - -static int t1pci_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) -{ - avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); - avmcard *card = cinfo->card; - unsigned long flags; - int retval; - - t1pci_reset(card); - - if ((retval = b1_load_t4file(card, &data->firmware))) { - t1pci_reset(card); - 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(card, &data->configuration))) { - t1pci_reset(card); - printk(KERN_ERR "%s: failed to load config!!\n", - card->name); - return retval; - } - } - - if (!t1pci_loaded(card)) { - t1pci_reset(card); - printk(KERN_ERR "%s: failed to load t4file.\n", card->name); - return -EIO; - } - - save_flags(flags); - cli(); - - card->csr = AVM_FLAG; - t1outmeml(card->mbase+AMCC_INTCSR, card->csr); - t1outmeml(card->mbase+AMCC_MCSR, - EN_A2P_TRANSFERS|EN_P2A_TRANSFERS - |A2P_HI_PRIORITY|P2A_HI_PRIORITY - |RESET_A2P_FLAGS|RESET_P2A_FLAGS); - t1outp(card->port, 0x07, 0x30); - t1outp(card->port, 0x10, 0xF0); - - card->dma->recvlen = 0; - t1outmeml(card->mbase+AMCC_RXPTR, virt_to_phys(card->dma->recvbuf)); - t1outmeml(card->mbase+AMCC_RXLEN, 4); - card->csr |= EN_RX_TC_INT; - t1outmeml(card->mbase+AMCC_INTCSR, card->csr); - restore_flags(flags); - - t1pci_send_init(card); - - return 0; -} - -void t1pci_reset_ctr(struct capi_ctr *ctrl) -{ - avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); - avmcard *card = cinfo->card; - - t1pci_reset(card); - - memset(cinfo->version, 0, sizeof(cinfo->version)); - ctrl->reseted(ctrl); -} - static void t1pci_remove_ctr(struct capi_ctr *ctrl) { avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); avmcard *card = cinfo->card; - t1pci_reset(card); + b1dma_reset(card); di->detach_ctr(ctrl); free_irq(card->irq, card); @@ -776,210 +93,20 @@ static void t1pci_remove_ctr(struct capi_ctr *ctrl) /* ------------------------------------------------------------- */ - -void t1pci_register_appl(struct capi_ctr *ctrl, - __u16 appl, - capi_register_params *rp) -{ - avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); - avmcard *card = cinfo->card; - struct sk_buff *skb; - int want = rp->level3cnt; - int nconn; - void *p; - - if (want > 0) nconn = want; - else nconn = ctrl->profile.nbchannel * -want; - if (nconn == 0) nconn = ctrl->profile.nbchannel; - - skb = alloc_skb(23, GFP_ATOMIC); - if (!skb) { - printk(KERN_CRIT "%s: no memory, lost register appl.\n", - card->name); - return; - } - p = skb->data; - _put_byte(&p, 0); - _put_byte(&p, 0); - _put_byte(&p, SEND_REGISTER); - _put_word(&p, appl); - _put_word(&p, 1024 * (nconn+1)); - _put_word(&p, nconn); - _put_word(&p, rp->datablkcnt); - _put_word(&p, rp->datablklen); - skb_put(skb, (__u8 *)p - (__u8 *)skb->data); - - skb_queue_tail(&card->dma->send_queue, skb); - t1pci_dispatch_tx(card); - - ctrl->appl_registered(ctrl, appl); -} - -/* ------------------------------------------------------------- */ - -void t1pci_release_appl(struct capi_ctr *ctrl, __u16 appl) -{ - avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); - avmcard *card = cinfo->card; - struct sk_buff *skb; - void *p; - - skb = alloc_skb(7, GFP_ATOMIC); - if (!skb) { - printk(KERN_CRIT "%s: no memory, lost release appl.\n", - card->name); - return; - } - p = skb->data; - _put_byte(&p, 0); - _put_byte(&p, 0); - _put_byte(&p, SEND_RELEASE); - _put_word(&p, appl); - - skb_put(skb, (__u8 *)p - (__u8 *)skb->data); - skb_queue_tail(&card->dma->send_queue, skb); - t1pci_dispatch_tx(card); -} - -/* ------------------------------------------------------------- */ - - -static void t1pci_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) -{ - avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); - avmcard *card = cinfo->card; - skb_queue_tail(&card->dma->send_queue, skb); - t1pci_dispatch_tx(card); -} - -/* ------------------------------------------------------------- */ - -static char *t1pci_procinfo(struct capi_ctr *ctrl) -{ - avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); - - if (!cinfo) - return ""; - sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", - cinfo->cardname[0] ? cinfo->cardname : "-", - cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", - cinfo->card ? cinfo->card->port : 0x0, - cinfo->card ? cinfo->card->irq : 0, - cinfo->card ? cinfo->card->membase : 0 - ); - return cinfo->infobuf; -} - -static int t1pci_read_proc(char *page, char **start, off_t off, - int count, int *eof, struct capi_ctr *ctrl) -{ - avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); - avmcard *card = cinfo->card; - unsigned long flags; - __u8 flag; - int len = 0; - char *s; - __u32 txaddr, txlen, rxaddr, rxlen, csr; - - 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); - len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase); - 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 ((s = cinfo->version[VER_DRIVER]) != 0) - len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); - if ((s = cinfo->version[VER_CARDTYPE]) != 0) - len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); - if ((s = cinfo->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", cinfo->cardname); - - save_flags(flags); - cli(); - - txaddr = (__u32)phys_to_virt(t1inmeml(card->mbase+0x2c)); - txaddr -= (__u32)card->dma->sendbuf; - txlen = t1inmeml(card->mbase+0x30); - - rxaddr = (__u32)phys_to_virt(t1inmeml(card->mbase+0x24)); - rxaddr -= (__u32)card->dma->recvbuf; - rxlen = t1inmeml(card->mbase+0x28); - - csr = t1inmeml(card->mbase+AMCC_INTCSR); - - restore_flags(flags); - - len += sprintf(page+len, "%-16s 0x%lx\n", - "csr (cached)", (unsigned long)card->csr); - len += sprintf(page+len, "%-16s 0x%lx\n", - "csr", (unsigned long)csr); - len += sprintf(page+len, "%-16s %lu\n", - "txoff", (unsigned long)txaddr); - len += sprintf(page+len, "%-16s %lu\n", - "txlen", (unsigned long)txlen); - len += sprintf(page+len, "%-16s %lu\n", - "rxoff", (unsigned long)rxaddr); - len += sprintf(page+len, "%-16s %lu\n", - "rxlen", (unsigned long)rxlen); - - if (off+count >= len) - *eof = 1; - if (len < off) - return 0; - *start = page + off; - return ((count < len-off) ? count : len-off); -} - -/* ------------------------------------------------------------- */ - static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) { - unsigned long page_offset, base; + unsigned long base, page_offset; avmcard *card; avmctrl_info *cinfo; int retval; + MOD_INC_USE_COUNT; + card = (avmcard *) kmalloc(sizeof(avmcard), GFP_ATOMIC); if (!card) { printk(KERN_WARNING "%s: no memory.\n", driver->name); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(card, 0, sizeof(avmcard)); @@ -987,6 +114,7 @@ static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) if (!card->dma) { printk(KERN_WARNING "%s: no memory.\n", driver->name); kfree(card); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(card->dma, 0, sizeof(avmcard_dmainfo)); @@ -995,6 +123,7 @@ static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) printk(KERN_WARNING "%s: no memory.\n", driver->name); kfree(card->dma); kfree(card); + MOD_DEC_USE_COUNT; return -ENOMEM; } memset(cinfo, 0, sizeof(avmctrl_info)); @@ -1013,14 +142,26 @@ static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) kfree(card->ctrlinfo); kfree(card->dma); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } - base = card->membase & PAGE_MASK; + base = card->membase & PAGE_MASK; page_offset = card->membase - base; card->mbase = ioremap_nocache(base, page_offset + 64); + if (card->mbase) { + card->mbase += page_offset; + } else { + printk(KERN_NOTICE "%s: can't remap memory at 0x%lx\n", + driver->name, card->membase); + kfree(card->ctrlinfo); + kfree(card->dma); + kfree(card); + MOD_DEC_USE_COUNT; + return -EIO; + } - t1pci_reset(card); + b1dma_reset(card); if ((retval = t1pci_detect(card)) != 0) { printk(KERN_NOTICE "%s: NO card at 0x%x (%d)\n", @@ -1029,13 +170,14 @@ static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) kfree(card->ctrlinfo); kfree(card->dma); kfree(card); + MOD_DEC_USE_COUNT; return -EIO; } - t1pci_reset(card); + b1dma_reset(card); request_region(p->port, AVMB1_PORTLEN, card->name); - retval = request_irq(card->irq, t1pci_interrupt, SA_SHIRQ, card->name, card); + retval = request_irq(card->irq, b1dma_interrupt, SA_SHIRQ, card->name, card); if (retval) { printk(KERN_ERR "%s: unable to get IRQ %d.\n", driver->name, card->irq); @@ -1044,6 +186,7 @@ static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) kfree(card->ctrlinfo); kfree(card->dma); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } @@ -1056,31 +199,52 @@ static int t1pci_add_card(struct capi_driver *driver, struct capicardparams *p) kfree(card->ctrlinfo); kfree(card->dma); kfree(card); + MOD_DEC_USE_COUNT; return -EBUSY; } card->cardnr = cinfo->capi_ctrl->cnr; skb_queue_head_init(&card->dma->send_queue); - MOD_INC_USE_COUNT; + printk(KERN_INFO + "%s: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n", + driver->name, card->port, card->irq, card->membase); return 0; } /* ------------------------------------------------------------- */ +static char *t1pci_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + static struct capi_driver t1pci_driver = { "t1pci", "0.0", - t1pci_load_firmware, - t1pci_reset_ctr, + b1dma_load_firmware, + b1dma_reset_ctr, t1pci_remove_ctr, - t1pci_register_appl, - t1pci_release_appl, - t1pci_send_message, + b1dma_register_appl, + b1dma_release_appl, + b1dma_send_message, t1pci_procinfo, - t1pci_read_proc, + b1dmactl_read_proc, 0, /* use standard driver_read_proc */ 0, /* no add_card function */ diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c index 38b2944f7..110b5a172 100644 --- a/drivers/isdn/divert/divert_procfs.c +++ b/drivers/isdn/divert/divert_procfs.c @@ -1,10 +1,10 @@ -/* - * $Id: divert_procfs.c,v 1.5 1999/09/14 20:31:01 werner Exp $ +/* + * $Id: divert_procfs.c,v 1.6 2000/02/14 19:23:03 werner 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) @@ -17,9 +17,13 @@ * * 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. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: divert_procfs.c,v $ + * Revision 1.6 2000/02/14 19:23:03 werner + * + * Changed handling of proc filesystem tables to a more portable version + * * Revision 1.5 1999/09/14 20:31:01 werner * * Removed obsoleted functions for proc fs and synced with new ones. @@ -44,9 +48,9 @@ #include <linux/version.h> #include <linux/poll.h> #ifdef CONFIG_PROC_FS - #include <linux/proc_fs.h> +#include <linux/proc_fs.h> #else - #include <linux/fs.h> +#include <linux/fs.h> #endif #include <linux/isdnif.h> #include "isdn_divert.h" @@ -54,220 +58,239 @@ /*********************************/ /* 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 */ +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 */ static wait_queue_head_t rd_queue; /*********************************/ /* 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 */ +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 */ /**********************************/ -static ssize_t isdn_divert_read(struct file *file, char *buf, size_t count, loff_t *off) -{ 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 */ +static ssize_t +isdn_divert_read(struct file *file, char *buf, size_t count, loff_t * off) +{ + 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 */ /**********************************/ -static ssize_t isdn_divert_write(struct file *file, const char *buf, size_t count, loff_t *off) +static ssize_t +isdn_divert_write(struct file *file, const char *buf, size_t count, loff_t * off) { - return(-ENODEV); -} /* isdn_divert_write */ + return (-ENODEV); +} /* isdn_divert_write */ /***************************************/ /* select routines for various kernels */ /***************************************/ -static unsigned int isdn_divert_poll(struct file *file, poll_table * wait) -{ unsigned int mask = 0; +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 */ + poll_wait(file, &(rd_queue), wait); + /* mask = POLLOUT | POLLWRNORM; */ + if (*((struct divert_info **) file->private_data)) { + mask |= POLLIN | POLLRDNORM; + } + return mask; +} /* isdn_divert_poll */ /****************/ /* 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 */ +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 */ /*******************/ -static int isdn_divert_close(struct inode *ino, struct file *filep) -{ 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; - return(0); -} /* isdn_divert_close */ +static int +isdn_divert_close(struct inode *ino, struct file *filep) +{ + 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; + return (0); +} /* 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 */ +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 @@ -279,63 +302,66 @@ isdn_divert_lseek(struct file *file, loff_t offset, int orig) static struct file_operations isdn_fops = { - llseek: isdn_divert_lseek, - read: isdn_divert_read, - write: isdn_divert_write, - poll: isdn_divert_poll, - ioctl: isdn_divert_ioctl, - open: isdn_divert_open, - release: isdn_divert_close, -}; - -struct inode_operations divert_file_inode_operations = { - &isdn_fops, /* default proc file-ops */ + 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 */ }; +struct inode_operations divert_file_inode_operations; /****************************/ /* isdn subdir in /proc/net */ /****************************/ static struct proc_dir_entry *isdn_proc_entry = NULL; static struct proc_dir_entry *isdn_divert_entry = NULL; -#endif CONFIG_PROC_FS +#endif /* CONFIG_PROC_FS */ /***************************************************************************/ /* divert_dev_init must be called before the proc filesystem may be used */ /***************************************************************************/ -int divert_dev_init(void) -{ int i; +int +divert_dev_init(void) +{ init_waitqueue_head(&rd_queue); #ifdef CONFIG_PROC_FS - isdn_proc_entry = proc_mkdir("isdn", proc_net); - if (!isdn_proc_entry) - return(-1); - isdn_divert_entry = create_proc_entry("divert",0,isdn_proc_entry); - if (!isdn_divert_entry) - { - remove_proc_entry("isdn",proc_net); - return(-1); - } - isdn_divert_entry->ops = &divert_file_inode_operations; -#endif CONFIG_PROC_FS - - return(0); -} /* divert_dev_init */ + isdn_proc_entry = create_proc_entry("isdn", S_IFDIR | S_IRUGO | S_IXUGO, proc_net); + if (!isdn_proc_entry) + return (-1); + isdn_divert_entry = create_proc_entry("divert", S_IFREG | S_IRUGO, isdn_proc_entry); + if (!isdn_divert_entry) { + remove_proc_entry("isdn", proc_net); + return (-1); + } + memset(&divert_file_inode_operations, 0, sizeof(struct inode_operations)); + divert_file_inode_operations.default_file_ops = &isdn_fops; + isdn_divert_entry->ops = &divert_file_inode_operations; +#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; +int +divert_dev_deinit(void) +{ #ifdef CONFIG_PROC_FS - remove_proc_entry("divert",isdn_proc_entry); - remove_proc_entry("isdn",proc_net); -#endif CONFIG_PROC_FS - - return(0); -} /* divert_dev_deinit */ + remove_proc_entry("divert", isdn_proc_entry); + remove_proc_entry("isdn", proc_net); +#endif /* CONFIG_PROC_FS */ + return (0); +} /* divert_dev_deinit */ diff --git a/drivers/isdn/eicon/eicon.h b/drivers/isdn/eicon/eicon.h index 88af10416..5ad164518 100644 --- a/drivers/isdn/eicon/eicon.h +++ b/drivers/isdn/eicon/eicon.h @@ -1,10 +1,10 @@ -/* $Id: eicon.h,v 1.17 1999/10/26 21:15:33 armin Exp $ +/* $Id: eicon.h,v 1.19 2000/01/23 21:21:23 armin Exp $ * - * ISDN low-level module for Eicon.Diehl active ISDN-Cards. + * ISDN low-level module for Eicon active ISDN-Cards. * * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de) - * Copyright 1998,99 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1998-2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.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 @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon.h,v $ + * Revision 1.19 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * + * Revision 1.18 1999/11/25 11:43:27 armin + * Fixed statectrl and connect message. + * X.75 fix and HDLC/transparent with autoconnect. + * Minor cleanup. + * * Revision 1.17 1999/10/26 21:15:33 armin * using define for checking phone number len to avoid buffer overflow. * @@ -258,7 +267,7 @@ typedef struct { /* Macro for delay via schedule() */ #define SLEEP(j) { \ - set_current_state(TASK_INTERRUPTIBLE); \ + set_current_state(TASK_UNINTERRUPTIBLE); \ schedule_timeout(j); \ } @@ -277,6 +286,8 @@ typedef struct { #define XLOG_ERR_UNKNOWN (18) #define XLOG_OK (0) +#define TRACE_OK (1) + typedef struct { __u8 Id __attribute__ ((packed)); __u8 uX __attribute__ ((packed)); @@ -494,12 +505,13 @@ typedef struct { typedef struct { int No; /* Channel Number */ unsigned short fsm_state; /* Current D-Channel state */ + unsigned short statectrl; /* State controling bits */ unsigned short eazmask; /* EAZ-Mask for this Channel */ 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; + struct sk_buff *tskb1; /* temp skb 1 */ + struct sk_buff *tskb2; /* temp skb 2 */ unsigned char l2prot; /* Layer 2 protocol */ unsigned char l3prot; /* Layer 3 protocol */ #ifdef CONFIG_ISDN_TTY_FAX @@ -600,21 +612,16 @@ typedef struct eicon_card { struct eicon_card *next; /* Pointer to next device struct */ int myid; /* Driver-Nr. assigned by linklevel */ unsigned long flags; /* Statusflags */ - unsigned long ilock; /* Semaphores for IRQ-Routines */ struct sk_buff_head rcvq; /* Receive-Message queue */ struct sk_buff_head sndq; /* Send-Message queue */ struct sk_buff_head rackq; /* Req-Ack-Message queue */ struct sk_buff_head sackq; /* Data-Ack-Message queue */ struct sk_buff_head statq; /* Status-Message queue */ int statq_entries; - u_char *ack_msg; /* Ptr to User Data in User skb */ - __u16 need_b3ack; /* Flag: Need ACK for current skb */ - struct sk_buff *sbuf; /* skb which is currently sent */ struct tq_struct snd_tq; /* Task struct for xmit bh */ struct tq_struct rcv_tq; /* Task struct for rcv bh */ struct tq_struct ack_tq; /* Task struct for ack bh */ msn_entry *msn_list; - unsigned short msgnum; /* Message number for sending */ eicon_chan* IdTable[256]; /* Table to find entity */ __u16 ref_in; __u16 ref_out; @@ -696,6 +703,7 @@ extern int eicon_info(char *, int , void *); extern ulong DebugVar; extern void eicon_log(eicon_card * card, int level, const char *fmt, ...); +extern void eicon_putstatus(eicon_card * card, char * buf); #endif /* __KERNEL__ */ diff --git a/drivers/isdn/eicon/eicon_dsp.h b/drivers/isdn/eicon/eicon_dsp.h index 9ffbd9bdb..420d73f6e 100644 --- a/drivers/isdn/eicon/eicon_dsp.h +++ b/drivers/isdn/eicon/eicon_dsp.h @@ -1,10 +1,10 @@ -/* $Id: eicon_dsp.h,v 1.4 1999/07/25 15:12:02 armin Exp $ +/* $Id: eicon_dsp.h,v 1.5 2000/01/23 21:21:23 armin Exp $ * - * ISDN lowlevel-module for Eicon.Diehl active cards. + * ISDN lowlevel-module for Eicon active cards. * DSP definitions * - * Copyright 1999 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1999,2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.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 @@ -21,6 +21,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_dsp.h,v $ + * Revision 1.5 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * * Revision 1.4 1999/07/25 15:12:02 armin * fix of some debug logs. * enabled ISA-cards option. diff --git a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c index d8634bdbe..e53469070 100644 --- a/drivers/isdn/eicon/eicon_idi.c +++ b/drivers/isdn/eicon/eicon_idi.c @@ -1,10 +1,10 @@ -/* $Id: eicon_idi.c,v 1.24 1999/10/26 21:15:33 armin Exp $ +/* $Id: eicon_idi.c,v 1.29 2000/01/23 21:21:23 armin Exp $ * - * ISDN lowlevel-module for Eicon.Diehl active cards. + * ISDN lowlevel-module for Eicon active cards. * IDI interface * - * Copyright 1998,99 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1998-2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.de) * * Thanks to Deutsche Mailbox Saar-Lor-Lux GmbH * for sponsoring and testing fax @@ -26,6 +26,25 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.c,v $ + * Revision 1.29 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * + * Revision 1.28 2000/01/20 19:55:34 keil + * Add FAX Class 1 support + * + * Revision 1.27 1999/11/29 13:12:03 armin + * Autoconnect on L2_TRANS doesn't work with link_level correctly, + * changed back to former mode. + * + * Revision 1.26 1999/11/25 11:43:27 armin + * Fixed statectrl and connect message. + * X.75 fix and HDLC/transparent with autoconnect. + * Minor cleanup. + * + * Revision 1.25 1999/11/18 20:30:55 armin + * removed old workaround for ISA cards. + * * Revision 1.24 1999/10/26 21:15:33 armin * using define for checking phone number len to avoid buffer overflow. * @@ -130,7 +149,7 @@ #undef EICON_FULL_SERVICE_OKTETT -char *eicon_idi_revision = "$Revision: 1.24 $"; +char *eicon_idi_revision = "$Revision: 1.29 $"; eicon_manifbuf *manbuf; @@ -187,16 +206,13 @@ idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) reqbuf->XBuffer.P[l++] = LLC; reqbuf->XBuffer.P[l++] = 2; switch(chan->l2prot) { - case ISDN_PROTO_L2_HDLC: - reqbuf->XBuffer.P[l++] = 2; + case ISDN_PROTO_L2_TRANS: + reqbuf->XBuffer.P[l++] = 2; /* transparent */ break; case ISDN_PROTO_L2_X75I: case ISDN_PROTO_L2_X75UI: case ISDN_PROTO_L2_X75BUI: - reqbuf->XBuffer.P[l++] = 5; - break; - case ISDN_PROTO_L2_TRANS: - reqbuf->XBuffer.P[l++] = 2; + reqbuf->XBuffer.P[l++] = 5; /* X.75 */ break; case ISDN_PROTO_L2_MODEM: if (chan->fsm_state == EICON_STATE_IWAIT) @@ -204,17 +220,18 @@ idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) else reqbuf->XBuffer.P[l++] = 10; /* V.42 */ break; + case ISDN_PROTO_L2_HDLC: 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; + reqbuf->XBuffer.P[l++] = 2; /* transparent */ break; default: reqbuf->XBuffer.P[l++] = 1; } switch(chan->l3prot) { - case ISDN_PROTO_L3_FAX: + case ISDN_PROTO_L3_FCLASS2: #ifdef CONFIG_ISDN_TTY_FAX reqbuf->XBuffer.P[l++] = 6; reqbuf->XBuffer.P[l++] = NLC; @@ -404,8 +421,10 @@ eicon_idi_listen_req(eicon_card *card, eicon_chan *chan) idi_do_req(card, chan, ASSIGN, 0); } if (chan->fsm_state == EICON_STATE_NULL) { - idi_do_req(card, chan, INDICATE_REQ, 0); - chan->fsm_state = EICON_STATE_LISTEN; + if (!(chan->statectrl & HAVE_CONN_REQ)) { + idi_do_req(card, chan, INDICATE_REQ, 0); + chan->fsm_state = EICON_STATE_LISTEN; + } } return(0); } @@ -462,6 +481,7 @@ idi_hangup(eicon_card *card, eicon_chan *chan) } if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1); if (chan->fsm_state != EICON_STATE_NULL) { + chan->statectrl |= WAITING_FOR_HANGUP; idi_do_req(card, chan, HANGUP, 0); chan->fsm_state = EICON_STATE_NULL; } @@ -479,7 +499,6 @@ idi_connect_res(eicon_card *card, eicon_chan *chan) return 1; chan->fsm_state = EICON_STATE_IWAIT; - idi_do_req(card, chan, CALL_RES, 0); /* check if old NetID has been removed */ if (chan->e.B2Id) { @@ -489,6 +508,7 @@ idi_connect_res(eicon_card *card, eicon_chan *chan) } idi_do_req(card, chan, ASSIGN, 1); + idi_do_req(card, chan, CALL_RES, 0); return(0); } @@ -656,9 +676,18 @@ idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone, reqbuf->XBuffer.length = l; reqbuf->Reference = 0; /* Sig Entity */ - skb_queue_tail(&chan->e.X, skb); - skb_queue_tail(&card->sndq, skb2); - eicon_schedule_tx(card); + if (chan->statectrl & WAITING_FOR_HANGUP) { + /* If the line did not disconnect yet, + we have to delay this command */ + eicon_log(card, 32, "idi_req: Ch%d: delaying conn_req\n", chan->No); + chan->statectrl |= HAVE_CONN_REQ; + chan->tskb1 = skb; + chan->tskb2 = skb2; + } else { + skb_queue_tail(&chan->e.X, skb); + skb_queue_tail(&card->sndq, skb2); + eicon_schedule_tx(card); + } eicon_log(card, 8, "idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone); return(0); @@ -1433,6 +1462,7 @@ idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len) cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; cmd.arg = chan->No; + strcpy(cmd.parm.num, ""); ccard->interface.statcallb(&cmd); cmd.driver = ccard->myid; @@ -1489,6 +1519,8 @@ idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len) break; case 2: /* session end */ default: + /* send_edata produces error on some */ + /* fax-machines here, so we don't */ /* idi_send_edata(ccard, chan); */ break; } @@ -1505,6 +1537,7 @@ idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len) cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; cmd.arg = chan->No; + strcpy(cmd.parm.num, ""); ccard->interface.statcallb(&cmd); cmd.driver = ccard->myid; @@ -2277,6 +2310,51 @@ idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int } void +eicon_parse_trace(eicon_card *ccard, unsigned char *buffer, int len) +{ + int i,j,n; + int buflen = len * 3 + 30; + char *p; + struct trace_s { + unsigned long time; + unsigned short size; + unsigned short code; + unsigned char data[1]; + } *q; + + if (!(p = kmalloc(buflen, GFP_KERNEL))) { + eicon_log(ccard, 1, "idi_err: Ch??: could not allocate trace buffer\n"); + return; + } + memset(p, 0, buflen); + q = (struct trace_s *)buffer; + + if (DebugVar & 512) { + if ((q->code == 3) || (q->code == 4)) { + n = (short) *(q->data); + if (n) { + j = sprintf(p, "DTRC:"); + for (i = 0; i < n; i++) { + j += sprintf(p + j, "%02x ", q->data[i+2]); + } + j += sprintf(p + j, "\n"); + } + } + } else { + j = sprintf(p, "XLOG: %lx %04x %04x ", + q->time, q->size, q->code); + + for (i = 0; i < q->size; i++) { + j += sprintf(p + j, "%02x ", q->data[i]); + } + j += sprintf(p + j, "\n"); + } + if (strlen(p)) + eicon_putstatus(ccard, p); + kfree(p); +} + +void idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) { int tmp; @@ -2307,7 +2385,7 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) else dlev = 128; - eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, + eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%x Id=%x Ch=%x MInd=%x MLen=%x Len=%x\n", chan->No, ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); free_buff = 1; @@ -2347,12 +2425,25 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) } else { if (chan->e.B2Id) idi_do_req(ccard, chan, REMOVE, 1); - chan->fsm_state = EICON_STATE_NULL; - cmd.driver = ccard->myid; - cmd.arg = chan->No; - cmd.command = ISDN_STAT_DHUP; - ccard->interface.statcallb(&cmd); - eicon_idi_listen_req(ccard, chan); + chan->statectrl &= ~WAITING_FOR_HANGUP; + if (chan->statectrl & HAVE_CONN_REQ) { + eicon_log(ccard, 32, "idi_req: Ch%d: queueing delayed conn_req\n", chan->No); + chan->statectrl &= ~HAVE_CONN_REQ; + if ((chan->tskb1) && (chan->tskb2)) { + skb_queue_tail(&chan->e.X, chan->tskb1); + skb_queue_tail(&ccard->sndq, chan->tskb2); + eicon_schedule_tx(ccard); + } + chan->tskb1 = NULL; + chan->tskb2 = NULL; + } else { + chan->fsm_state = EICON_STATE_NULL; + cmd.driver = ccard->myid; + cmd.arg = chan->No; + cmd.command = ISDN_STAT_DHUP; + ccard->interface.statcallb(&cmd); + eicon_idi_listen_req(ccard, chan); + } } break; case INDICATE_IND: @@ -2450,8 +2541,12 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) case ISDN_PROTO_L2_MODEM: /* do nothing, wait for connect */ break; - default: + case ISDN_PROTO_L2_TRANS: idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + break; + default: + /* On most incoming calls we use automatic connect */ + /* idi_do_req(ccard, chan, IDI_N_CONNECT, 1); */ } } else idi_hangup(ccard, chan); @@ -2495,8 +2590,12 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) if (chan->No == ccard->nchannels) { /* Management Indication */ - idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length); - chan->fsm_state = 1; + if (ind->Ind == 0x04) { /* Trace_Ind */ + eicon_parse_trace(ccard, ind->RBuffer.P, ind->RBuffer.length); + } else { + idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length); + chan->fsm_state = 1; + } } else switch(ind->Ind) { @@ -2530,6 +2629,7 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; cmd.arg = chan->No; + strcpy(cmd.parm.num, "64000"); ccard->interface.statcallb(&cmd); break; case IDI_N_CONNECT: @@ -2546,6 +2646,7 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; cmd.arg = chan->No; + strcpy(cmd.parm.num, "64000"); ccard->interface.statcallb(&cmd); break; case IDI_N_DISC: @@ -2576,6 +2677,7 @@ idi_handle_ind(eicon_card *ccard, struct sk_buff *skb) cmd.arg = chan->No; ccard->interface.statcallb(&cmd); chan->fsm_state = EICON_STATE_NULL; + chan->statectrl |= WAITING_FOR_HANGUP; } #ifdef CONFIG_ISDN_TTY_FAX chan->fax = 0; @@ -2631,7 +2733,8 @@ idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack) isdn_ctrl cmd; if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) { - /* I dont know why this happens, just ignoring this RC */ + /* I dont know why this happens, should not ! */ + /* just ignoring this RC */ eicon_log(ccard, 16, "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; @@ -2640,16 +2743,16 @@ idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack) /* Management Interface */ if (chan->No == ccard->nchannels) { /* Managementinterface: changing state */ - if (chan->e.Req == 0x04) + if (chan->e.Req != 0x02) chan->fsm_state = 1; } /* Remove an Id */ if (chan->e.Req == REMOVE) { if (ack->Reference != chan->e.ref) { + /* This should not happen anymore */ eicon_log(ccard, 16, "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, ack->Reference, chan->e.ref); - return 0; } save_flags(flags); cli(); @@ -2807,11 +2910,14 @@ idi_handle_ack(eicon_card *ccard, struct sk_buff *skb) dCh, ack->Rc, ack->RcId, ack->RcCh); break; default: - eicon_log(ccard, 1, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n", - dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req); + if (dCh != ccard->nchannels) + eicon_log(ccard, 1, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n", + dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req); } if (dCh == ccard->nchannels) { /* Management */ chan->fsm_state = 2; + eicon_log(ccard, 8, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n", + dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req); } else if (dCh >= 0) { /* any other channel */ /* card reports error: we hangup */ @@ -3011,38 +3117,36 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) chan = &(card->bch[card->nchannels]); - if (chan->e.D3Id) - return -EBUSY; - chan->e.D3Id = 1; - while((skb2 = skb_dequeue(&chan->e.X))) - dev_kfree_skb(skb2); - chan->e.busy = 0; + if (!(chan->e.D3Id)) { + chan->e.D3Id = 1; + while((skb2 = skb_dequeue(&chan->e.X))) + dev_kfree_skb(skb2); + chan->e.busy = 0; - if ((ret = eicon_idi_manage_assign(card))) { - chan->e.D3Id = 0; - return(ret); - } + if ((ret = eicon_idi_manage_assign(card))) { + chan->e.D3Id = 0; + return(ret); + } - timeout = jiffies + 50; - while (timeout > jiffies) { - if (chan->e.B2Id) break; - SLEEP(10); - } - if (!chan->e.B2Id) { - chan->e.D3Id = 0; - return -EIO; + timeout = jiffies + 50; + while (timeout > jiffies) { + if (chan->e.B2Id) break; + SLEEP(10); + } + if (!chan->e.B2Id) { + chan->e.D3Id = 0; + return -EIO; + } } chan->fsm_state = 0; if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) { eicon_log(card, 1, "idi_err: alloc_manifbuf failed\n"); - chan->e.D3Id = 0; return -ENOMEM; } if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) { kfree(manbuf); - chan->e.D3Id = 0; return -EFAULT; } @@ -3056,7 +3160,6 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) if (skb2) dev_kfree_skb(skb2); kfree(manbuf); - chan->e.D3Id = 0; return -ENOMEM; } @@ -3093,25 +3196,14 @@ eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb) SLEEP(10); } if ((!chan->fsm_state) || (chan->fsm_state == 2)) { - eicon_idi_manage_remove(card); kfree(manbuf); - chan->e.D3Id = 0; return -EIO; } - - 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; } kfree(manbuf); - chan->e.D3Id = 0; return(0); } diff --git a/drivers/isdn/eicon/eicon_idi.h b/drivers/isdn/eicon/eicon_idi.h index e09c1954d..2fe6167a4 100644 --- a/drivers/isdn/eicon/eicon_idi.h +++ b/drivers/isdn/eicon/eicon_idi.h @@ -1,10 +1,10 @@ -/* $Id: eicon_idi.h,v 1.7 1999/08/22 20:26:46 calle Exp $ +/* $Id: eicon_idi.h,v 1.9 2000/01/23 21:21:23 armin Exp $ * - * ISDN lowlevel-module for the Eicon.Diehl active cards. + * ISDN lowlevel-module for the Eicon active cards. * IDI-Interface * - * Copyright 1998,99 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1998-2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.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 @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.h,v $ + * Revision 1.9 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * + * Revision 1.8 1999/11/25 11:43:27 armin + * Fixed statectrl and connect message. + * X.75 fix and HDLC/transparent with autoconnect. + * Minor cleanup. + * * Revision 1.7 1999/08/22 20:26:46 calle * backported changes from kernel 2.3.14: * - several #include "config.h" gone, others come. @@ -170,6 +179,10 @@ /*------------------------------------------------------------------*/ +/* defines for statectrl */ +#define WAITING_FOR_HANGUP 0x01 +#define HAVE_CONN_REQ 0x02 + typedef struct { char cpn[32]; char oad[32]; diff --git a/drivers/isdn/eicon/eicon_io.c b/drivers/isdn/eicon/eicon_io.c index 779f241e0..4f4180ed6 100644 --- a/drivers/isdn/eicon/eicon_io.c +++ b/drivers/isdn/eicon/eicon_io.c @@ -1,12 +1,12 @@ -/* $Id: eicon_io.c,v 1.8 1999/10/08 22:09:34 armin Exp $ +/* $Id: eicon_io.c,v 1.10 2000/01/23 21:21:23 armin Exp $ * - * ISDN low-level module for Eicon.Diehl active ISDN-Cards. + * ISDN low-level module for Eicon active ISDN-Cards. * Code for communicating with hardware. * - * Copyright 1999 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1999,2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.de) * - * Thanks to Eicon Technology Diehl GmbH & Co. oHG for + * Thanks to Eicon Technology GmbH & Co. oHG for * documents, informations and hardware. * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_io.c,v $ + * Revision 1.10 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * + * Revision 1.9 1999/11/18 20:55:25 armin + * Ready_Int fix of ISA cards. + * * Revision 1.8 1999/10/08 22:09:34 armin * Some fixes of cards interface handling. * Bugfix of NULL pointer occurence. @@ -470,7 +477,7 @@ eicon_io_transmit(eicon_card *ccard) { save_flags(flags); cli(); if (scom) { - if (ram_inb(ccard, &com->Req)) { + if ((ram_inb(ccard, &com->Req)) || (ccard->ReadyInt)) { if (!ccard->ReadyInt) { tmp = ram_inb(ccard, &com->ReadyInt) + 1; ram_outb(ccard, &com->ReadyInt, tmp); @@ -566,7 +573,8 @@ eicon_io_transmit(eicon_card *ccard) { chan->e.busy = 1; eicon_log(ccard, dlev, "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", reqbuf->Req, - ram_inb(ccard, &ReqOut->ReqId), + (scom) ? ram_inb(ccard, &com->ReqId) : + ram_inb(ccard, &ReqOut->ReqId), reqbuf->ReqCh, reqbuf->XBuffer.length, chan->e.ref); } @@ -754,6 +762,7 @@ eicon_irq(int irq, void *dev_id, struct pt_regs *regs) { if (ccard->ReadyInt) { ccard->ReadyInt--; ram_outb(ccard, &com->Rc, 0); + eicon_schedule_tx(ccard); } } else { skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC); diff --git a/drivers/isdn/eicon/eicon_isa.c b/drivers/isdn/eicon/eicon_isa.c index a89d18b12..86e6c0ef7 100644 --- a/drivers/isdn/eicon/eicon_isa.c +++ b/drivers/isdn/eicon/eicon_isa.c @@ -1,11 +1,11 @@ -/* $Id: eicon_isa.c,v 1.9 1999/09/08 20:17:31 armin Exp $ +/* $Id: eicon_isa.c,v 1.13 2000/01/23 21:21:23 armin Exp $ * - * ISDN low-level module for Eicon.Diehl active ISDN-Cards. + * ISDN low-level module for Eicon active ISDN-Cards. * Hardware-specific code for old ISA cards. * - * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de) - * Copyright 1998,99 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de) + * Copyright 1998-2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.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 @@ -22,8 +22,21 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_isa.c,v $ + * Revision 1.13 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * + * Revision 1.12 1999/11/27 12:56:19 armin + * Forgot some iomem changes for last ioremap compat. + * + * Revision 1.11 1999/11/25 11:33:09 armin + * Microchannel fix from Erik Weber (exrz73@ibm.net). + * + * Revision 1.10 1999/11/18 21:14:30 armin + * New ISA memory mapped IO + * * Revision 1.9 1999/09/08 20:17:31 armin - * Added microchannel patch from Erik Weber. + * Added microchannel patch from Erik Weber (exrz73@ibm.net). * * Revision 1.8 1999/09/06 07:29:35 fritz * Changed my mail-address. @@ -70,7 +83,7 @@ #define release_shmem release_region #define request_shmem request_region -char *eicon_isa_revision = "$Revision: 1.9 $"; +char *eicon_isa_revision = "$Revision: 1.13 $"; #undef EICON_MCA_DEBUG @@ -87,8 +100,10 @@ static int eicon_isa_valid_irq[] = { static void eicon_isa_release_shmem(eicon_isa_card *card) { - if (card->mvalid) - release_shmem((unsigned long)card->shmem, card->ramsize); + if (card->mvalid) { + iounmap(card->shmem); + release_mem_region(card->physmem, card->ramsize); + } card->mvalid = 0; } @@ -117,7 +132,7 @@ eicon_isa_printpar(eicon_isa_card *card) { case EICON_CTYPE_S2M: printk(KERN_INFO "Eicon %s at 0x%lx, irq %d.\n", eicon_ctype_name[card->type], - (unsigned long)card->shmem, + card->physmem, card->irq); } } @@ -126,6 +141,7 @@ int eicon_isa_find_card(int Mem, int Irq, char * Id) { int primary = 1; + unsigned long amem; if (!strlen(Id)) return -1; @@ -138,24 +154,27 @@ eicon_isa_find_card(int Mem, int Irq, char * Id) Mem, Id); return -1; } - if (check_shmem(Mem, RAMSIZE)) { + if (check_mem_region(Mem, RAMSIZE)) { printk(KERN_WARNING "eicon_isa_boot: memory at 0x%x already in use.\n", Mem); return -1; } - writew(0x55aa, Mem + 0x402); - if (readw(Mem + 0x402) != 0x55aa) primary = 0; - writew(0, Mem + 0x402); - if (readw(Mem + 0x402) != 0) primary = 0; + amem = (unsigned long) ioremap(Mem, RAMSIZE); + writew(0x55aa, amem + 0x402); + if (readw(amem + 0x402) != 0x55aa) primary = 0; + writew(0, amem + 0x402); + if (readw(amem + 0x402) != 0) primary = 0; printk(KERN_INFO "Eicon: Driver-ID: %s\n", Id); if (primary) { printk(KERN_INFO "Eicon: assuming pri card at 0x%x\n", Mem); - writeb(0, Mem + 0x3ffe); + writeb(0, amem + 0x3ffe); + iounmap((unsigned char *)amem); return EICON_CTYPE_ISAPRI; } else { printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem); - writeb(0, Mem + 0x400); + writeb(0, amem + 0x400); + iounmap((unsigned char *)amem); return EICON_CTYPE_ISABRI; } return -1; @@ -187,57 +206,65 @@ eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb) { return -EFAULT; } + if (card->type == EICON_CTYPE_ISAPRI) + card->ramsize = RAMSIZE_P; + else + card->ramsize = RAMSIZE; + + if (check_mem_region(card->physmem, card->ramsize)) { + printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n", + card->physmem); + kfree(code); + return -EBUSY; + } + request_mem_region(card->physmem, card->ramsize, "Eicon ISA ISDN"); + card->shmem = (eicon_isa_shmem *) ioremap(card->physmem, card->ramsize); +#ifdef EICON_MCA_DEBUG + printk(KERN_INFO "eicon_isa_boot: card->ramsize = %d.\n", card->ramsize); +#endif + card->mvalid = 1; + switch(card->type) { case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: case EICON_CTYPE_QUADRO: case EICON_CTYPE_ISABRI: - card->ramsize = RAMSIZE; card->intack = (__u8 *)card->shmem + INTACK; card->startcpu = (__u8 *)card->shmem + STARTCPU; card->stopcpu = (__u8 *)card->shmem + STOPCPU; break; case EICON_CTYPE_S2M: case EICON_CTYPE_ISAPRI: - card->ramsize = RAMSIZE_P; card->intack = (__u8 *)card->shmem + INTACK_P; card->startcpu = (__u8 *)card->shmem + STARTCPU_P; card->stopcpu = (__u8 *)card->shmem + STOPCPU_P; break; default: printk(KERN_WARNING "eicon_isa_boot: Invalid card type %d\n", card->type); + eicon_isa_release_shmem(card); + kfree(code); return -EINVAL; } - /* Register shmem */ - if (check_shmem((unsigned long)card->shmem, card->ramsize)) { - printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n", - (unsigned long)card->shmem); - kfree(code); - return -EBUSY; - } - request_shmem((unsigned long)card->shmem, card->ramsize, "Eicon ISA ISDN"); -#ifdef EICON_MCA_DEBUG - printk(KERN_INFO "eicon_isa_boot: card->ramsize = %d.\n", card->ramsize); -#endif - card->mvalid = 1; - /* clear any pending irq's */ readb(card->intack); #ifdef CONFIG_MCA - if (card->type == EICON_CTYPE_SCOM) { - outb_p(0,card->io+1); - } - else { - printk(KERN_WARNING "eicon_isa_boot: Card type yet not supported.\n"); - return -EINVAL; - }; + if (MCA_bus) { + if (card->type == EICON_CTYPE_SCOM) { + outb_p(0,card->io+1); + } + else { + printk(KERN_WARNING "eicon_isa_boot: Card type not supported yet.\n"); + eicon_isa_release_shmem(card); + return -EINVAL; + }; #ifdef EICON_MCA_DEBUG printk(KERN_INFO "eicon_isa_boot: card->io = %x.\n", card->io); printk(KERN_INFO "eicon_isa_boot: card->irq = %d.\n", (int)card->irq); #endif + } #else /* set reset-line active */ writeb(0, card->stopcpu); @@ -269,7 +296,9 @@ eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb) { /* Start CPU */ writeb(cbuf.boot_opt, &boot->ctrl); #ifdef CONFIG_MCA - outb_p(0, card->io); + if (MCA_bus) { + outb_p(0, card->io); + } #else writeb(0, card->startcpu); #endif /* CONFIG_MCA */ @@ -320,7 +349,7 @@ eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb) { } printk(KERN_INFO "%s: startup-code loaded\n", eicon_ctype_name[card->type]); if ((card->type == EICON_CTYPE_QUADRO) && (card->master)) { - tmp = eicon_addcard(card->type, (unsigned long)card->shmem, card->irq, + tmp = eicon_addcard(card->type, card->physmem, card->irq, ((eicon_card *)card->card)->regname); printk(KERN_INFO "Eicon: %d adapters added\n", tmp); } diff --git a/drivers/isdn/eicon/eicon_isa.h b/drivers/isdn/eicon/eicon_isa.h index b0d0b0eb9..b53adfcbf 100644 --- a/drivers/isdn/eicon/eicon_isa.h +++ b/drivers/isdn/eicon/eicon_isa.h @@ -1,10 +1,10 @@ -/* $Id: eicon_isa.h,v 1.6 1999/11/15 19:37:04 keil Exp $ +/* $Id: eicon_isa.h,v 1.8 2000/01/23 21:21:23 armin Exp $ * - * ISDN low-level module for Eicon.Diehl active ISDN-Cards. + * ISDN low-level module for Eicon active ISDN-Cards. * - * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de) - * Copyright 1998,99 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de) + * Copyright 1998-2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.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 @@ -21,6 +21,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_isa.h,v $ + * Revision 1.8 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * + * Revision 1.7 1999/11/18 21:14:30 armin + * New ISA memory mapped IO + * * Revision 1.6 1999/11/15 19:37:04 keil * need config.h * @@ -116,6 +123,7 @@ typedef union { typedef struct { int ramsize; int irq; /* IRQ */ + unsigned long physmem; /* physical memory address */ #ifdef CONFIG_MCA int io; /* IO-port for MCA brand */ #endif /* CONFIG_MCA */ diff --git a/drivers/isdn/eicon/eicon_mod.c b/drivers/isdn/eicon/eicon_mod.c index 8797e6aed..688d74de3 100644 --- a/drivers/isdn/eicon/eicon_mod.c +++ b/drivers/isdn/eicon/eicon_mod.c @@ -1,12 +1,12 @@ -/* $Id: eicon_mod.c,v 1.19 1999/11/12 13:21:44 armin Exp $ +/* $Id: eicon_mod.c,v 1.24 2000/01/23 21:21:23 armin Exp $ * - * ISDN lowlevel-module for Eicon.Diehl active cards. + * ISDN lowlevel-module for Eicon active cards. * - * Copyright 1997 by Fritz Elfert (fritz@isdn4linux.de) - * Copyright 1998,99 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1997 by Fritz Elfert (fritz@isdn4linux.de) + * Copyright 1998-2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.de) * - * Thanks to Eicon Technology Diehl GmbH & Co. oHG for + * Thanks to Eicon Technology GmbH & Co. oHG for * documents, informations and hardware. * * Deutsche Telekom AG for S2M support. @@ -31,6 +31,23 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_mod.c,v $ + * Revision 1.24 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * + * Revision 1.23 2000/01/20 19:55:34 keil + * Add FAX Class 1 support + * + * Revision 1.22 1999/11/27 12:56:19 armin + * Forgot some iomem changes for last ioremap compat. + * + * Revision 1.21 1999/11/25 11:35:10 armin + * Microchannel fix from Erik Weber (exrz73@ibm.net). + * Minor cleanup. + * + * Revision 1.20 1999/11/18 21:14:30 armin + * New ISA memory mapped IO + * * Revision 1.19 1999/11/12 13:21:44 armin * Bugfix of undefined reference with CONFIG_MCA * @@ -46,7 +63,7 @@ * Improved debug and log via readstat() * * Revision 1.15 1999/09/08 20:17:31 armin - * Added microchannel patch from Erik Weber. + * Added microchannel patch from Erik Weber (exrz73@ibm.net). * * Revision 1.14 1999/09/06 07:29:35 fritz * Changed my mail-address. @@ -123,7 +140,7 @@ static eicon_card *cards = (eicon_card *) NULL; /* glob. var , contains start of card-list */ -static char *eicon_revision = "$Revision: 1.19 $"; +static char *eicon_revision = "$Revision: 1.24 $"; extern char *eicon_pci_revision; extern char *eicon_isa_revision; @@ -144,7 +161,7 @@ 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_DESCRIPTION( "Driver for Eicon active ISDN cards"); MODULE_AUTHOR( "Armin Schindler"); MODULE_SUPPORTED_DEVICE( "ISDN subsystem"); MODULE_PARM_DESC(id, "ID-String of first card"); @@ -659,7 +676,7 @@ eicon_command(eicon_card * card, isdn_ctrl * c) break; chan->l3prot = (c->arg >> 8); #ifdef CONFIG_ISDN_TTY_FAX - if (chan->l3prot == ISDN_PROTO_L3_FAX) + if (chan->l3prot == ISDN_PROTO_L3_FCLASS2) chan->fax = c->parm.fax; #endif return 0; @@ -839,8 +856,7 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) } /* jiftime() copied from HiSax */ -inline int -jiftime(char *s, long mark) +static inline int jiftime(char *s, long mark) { s += 8; @@ -1000,19 +1016,28 @@ eicon_alloccard(int Type, int membase, int irq, char *id) 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; + if (MCA_bus) { + 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.physmem = (unsigned long)membase; + card->hwif.isa.master = 1; + + card->hwif.isa.irq = irq; + card->hwif.isa.type = Type; + card->nchannels = 2; + card->interface.channels = 1; + } else { + printk(KERN_WARNING + "eicon (%s): no MCA bus detected.\n", + card->interface.id); + kfree(card); + return; + } break; #endif /* CONFIG_MCA */ case EICON_CTYPE_QUADRO: @@ -1023,6 +1048,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) 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.physmem = (unsigned long)(membase + (i+1) * EICON_ISA_QOFFSET); card->hwif.isa.master = 0; strcpy(card->interface.id, id); if (id[strlen(id) - 1] == 'a') { @@ -1067,7 +1093,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) ISDN_FEATURE_L2_MODEM | ISDN_FEATURE_L2_FAX | ISDN_FEATURE_L3_TRANSDSP | - ISDN_FEATURE_L3_FAX; + ISDN_FEATURE_L3_FCLASS2; card->hwif.pci.card = (void *)card; card->hwif.pci.PCIreg = pcic->PCIreg; card->hwif.pci.PCIcfg = pcic->PCIcfg; @@ -1091,7 +1117,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) ISDN_FEATURE_L2_MODEM | ISDN_FEATURE_L2_FAX | ISDN_FEATURE_L3_TRANSDSP | - ISDN_FEATURE_L3_FAX; + ISDN_FEATURE_L3_FCLASS2; card->hwif.pci.card = (void *)card; card->hwif.pci.shmem = (eicon_pci_shmem *)pcic->shmem; card->hwif.pci.PCIreg = pcic->PCIreg; @@ -1116,6 +1142,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)membase; + card->hwif.isa.physmem = (unsigned long)membase; card->hwif.isa.master = 1; card->hwif.isa.irq = irq; card->hwif.isa.type = Type; @@ -1130,6 +1157,7 @@ eicon_alloccard(int Type, int membase, int irq, char *id) card->bus = EICON_BUS_ISA; card->hwif.isa.card = (void *)card; card->hwif.isa.shmem = (eicon_isa_shmem *)membase; + card->hwif.isa.physmem = (unsigned long)membase; card->hwif.isa.master = 1; card->hwif.isa.irq = irq; card->hwif.isa.type = Type; @@ -1151,14 +1179,15 @@ eicon_alloccard(int Type, int membase, int irq, char *id) } for (j=0; j< (card->nchannels + 1); j++) { memset((char *)&card->bch[j], 0, sizeof(eicon_chan)); - card->bch[j].plci = 0x8000; - card->bch[j].ncci = 0x8000; + card->bch[j].statectrl = 0; card->bch[j].l2prot = ISDN_PROTO_L2_X75I; card->bch[j].l3prot = ISDN_PROTO_L3_TRANS; card->bch[j].e.D3Id = 0; card->bch[j].e.B2Id = 0; card->bch[j].e.Req = 0; card->bch[j].No = j; + card->bch[j].tskb1 = NULL; + card->bch[j].tskb2 = NULL; skb_queue_head_init(&card->bch[j].e.X); skb_queue_head_init(&card->bch[j].e.R); } diff --git a/drivers/isdn/eicon/eicon_pci.c b/drivers/isdn/eicon/eicon_pci.c index 5c96302cb..47196f953 100644 --- a/drivers/isdn/eicon/eicon_pci.c +++ b/drivers/isdn/eicon/eicon_pci.c @@ -1,12 +1,12 @@ -/* $Id: eicon_pci.c,v 1.10 1999/08/22 20:26:49 calle Exp $ +/* $Id: eicon_pci.c,v 1.11 2000/01/23 21:21:23 armin Exp $ * - * ISDN low-level module for Eicon.Diehl active ISDN-Cards. + * ISDN low-level module for Eicon active ISDN-Cards. * Hardware-specific code for PCI cards. * - * Copyright 1998,99 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1998-2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.de) * - * Thanks to Eicon Technology Diehl GmbH & Co. oHG for + * Thanks to Eicon Technology GmbH & Co. oHG for * documents, informations and hardware. * * Deutsche Telekom AG for S2M support. @@ -26,6 +26,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_pci.c,v $ + * Revision 1.11 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * * Revision 1.10 1999/08/22 20:26:49 calle * backported changes from kernel 2.3.14: * - several #include "config.h" gone, others come. @@ -77,7 +81,7 @@ #include "eicon_pci.h" -char *eicon_pci_revision = "$Revision: 1.10 $"; +char *eicon_pci_revision = "$Revision: 1.11 $"; #if CONFIG_PCI /* intire stuff is only for PCI */ diff --git a/drivers/isdn/eicon/eicon_pci.h b/drivers/isdn/eicon/eicon_pci.h index a23faade2..384cc422c 100644 --- a/drivers/isdn/eicon/eicon_pci.h +++ b/drivers/isdn/eicon/eicon_pci.h @@ -1,9 +1,9 @@ -/* $Id: eicon_pci.h,v 1.3 1999/03/29 11:19:51 armin Exp $ +/* $Id: eicon_pci.h,v 1.4 2000/01/23 21:21:23 armin Exp $ * - * ISDN low-level module for Eicon.Diehl active ISDN-Cards (PCI part). + * ISDN low-level module for Eicon active ISDN-Cards (PCI part). * - * Copyright 1998,99 by Armin Schindler (mac@melware.de) - * Copyright 1999 Cytronics & Melware (info@melware.de) + * Copyright 1998-2000 by Armin Schindler (mac@melware.de) + * Copyright 1999,2000 Cytronics & Melware (info@melware.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 @@ -20,6 +20,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_pci.h,v $ + * Revision 1.4 2000/01/23 21:21:23 armin + * Added new trace capability and some updates. + * DIVA Server BRI now supports data for ISDNLOG. + * * Revision 1.3 1999/03/29 11:19:51 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index e37d9c404..7bc340de9 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -137,6 +137,10 @@ ifeq ($(CONFIG_HISAX_HFC_PCI),y) HFC_2BDS0 += hfc_pci.o endif +ifeq ($(CONFIG_HISAX_HFC_SX),y) + HFC_2BDS0 += hfc_sx.o +endif + ifeq ($(CONFIG_HISAX_NICCY),y) O_OBJS += niccy.o ISAC_OBJ := isac.o diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c index e8abc9de2..5b3f3aadb 100644 --- a/drivers/isdn/hisax/arcofi.c +++ b/drivers/isdn/hisax/arcofi.c @@ -1,12 +1,19 @@ -/* $Id: arcofi.c,v 1.8 1999/08/25 16:50:51 keil Exp $ +/* $Id: arcofi.c,v 1.10 1999/12/23 15:09:32 keil Exp $ * arcofi.c Ansteuerung ARCOFI 2165 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * * $Log: arcofi.c,v $ + * Revision 1.10 1999/12/23 15:09:32 keil + * change email + * + * Revision 1.9 1999/12/19 13:09:41 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.8 1999/08/25 16:50:51 keil * Fix bugs which cause 2.3.14 hangs (waitqueue init) * @@ -83,7 +90,7 @@ arcofi_fsm(struct IsdnCardState *cs, int event, void *data) { 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); + wake_up(&cs->dc.isac.arcofi_wait); return(1); } switch (cs->dc.isac.arcofi_state) { @@ -109,7 +116,7 @@ arcofi_fsm(struct IsdnCardState *cs, int event, void *data) { del_timer(&cs->dc.isac.arcofitimer); } cs->dc.isac.arcofi_state = ARCOFI_NOP; - wake_up_interruptible(&cs->dc.isac.arcofi_wait); + wake_up(&cs->dc.isac.arcofi_wait); } } } @@ -126,7 +133,7 @@ arcofi_fsm(struct IsdnCardState *cs, int event, void *data) { del_timer(&cs->dc.isac.arcofitimer); } cs->dc.isac.arcofi_state = ARCOFI_NOP; - wake_up_interruptible(&cs->dc.isac.arcofi_wait); + wake_up(&cs->dc.isac.arcofi_wait); } } break; diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h index 86617d6a1..7d1c445e5 100644 --- a/drivers/isdn/hisax/arcofi.h +++ b/drivers/isdn/hisax/arcofi.h @@ -1,12 +1,15 @@ -/* $Id: arcofi.h,v 1.4 1999/07/01 08:11:18 keil Exp $ +/* $Id: arcofi.h,v 1.5 1999/12/23 15:09:32 keil Exp $ * arcofi.h Ansteuerung ARCOFI 2165 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * * $Log: arcofi.h,v $ + * Revision 1.5 1999/12/23 15:09:32 keil + * change email + * * Revision 1.4 1999/07/01 08:11:18 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c index 10657ad63..98c9736b6 100644 --- a/drivers/isdn/hisax/asuscom.c +++ b/drivers/isdn/hisax/asuscom.c @@ -1,4 +1,4 @@ -/* $Id: asuscom.c,v 1.8 1999/09/04 06:20:05 keil Exp $ +/* $Id: asuscom.c,v 1.9 1999/12/19 13:09:41 keil Exp $ * asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards * @@ -8,6 +8,10 @@ * * * $Log: asuscom.c,v $ + * Revision 1.9 1999/12/19 13:09:41 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.8 1999/09/04 06:20:05 keil * Changes from kernel set_current_state() * @@ -42,7 +46,7 @@ extern const char *CardType[]; -const char *Asuscom_revision = "$Revision: 1.8 $"; +const char *Asuscom_revision = "$Revision: 1.9 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -291,13 +295,13 @@ reset_asuscom(struct IsdnCardState *cs) byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */ save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); 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 */ - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); if (cs->subtyp == ASUS_IPAC) { writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0); diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index f343bfb5a..319e0b264 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.12 1999/09/04 06:20:05 keil Exp $ +/* $Id: avm_pci.c,v 1.14 1999/12/19 13:09:41 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,13 @@ * * * $Log: avm_pci.c,v $ + * Revision 1.14 1999/12/19 13:09:41 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * + * Revision 1.13 1999/12/03 12:10:14 keil + * Bugfix: Wrong channel use on hangup of channel 2 + * * Revision 1.12 1999/09/04 06:20:05 keil * Changes from kernel set_current_state() * @@ -56,7 +63,7 @@ #include <linux/interrupt.h> extern const char *CardType[]; -static const char *avm_pci_rev = "$Revision: 1.12 $"; +static const char *avm_pci_rev = "$Revision: 1.14 $"; #define AVM_FRITZ_PCI 1 #define AVM_FRITZ_PNP 2 @@ -269,18 +276,26 @@ modehdlc(struct BCState *bcs, int mode, int bc) int hdlc = bcs->channel; if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hdlc %c mode %d ichan %d", - 'A' + hdlc, mode, bc); - bcs->mode = mode; - bcs->channel = bc; + debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d", + 'A' + hdlc, bcs->mode, mode, hdlc, bc); bcs->hw.hdlc.ctrl.ctrl = 0; switch (mode) { + case (-1): /* used for init */ + bcs->mode = 1; + bcs->channel = bc; + bc = 0; case (L1_MODE_NULL): + if (bcs->mode == L1_MODE_NULL) + return; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; write_ctrl(bcs, 5); + bcs->mode = L1_MODE_NULL; + bcs->channel = bc; break; case (L1_MODE_TRANS): + bcs->mode = mode; + bcs->channel = bc; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; write_ctrl(bcs, 5); @@ -290,6 +305,8 @@ modehdlc(struct BCState *bcs, int mode, int bc) hdlc_sched_event(bcs, B_XMTBUFREADY); break; case (L1_MODE_HDLC): + bcs->mode = mode; + bcs->channel = bc; bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG; write_ctrl(bcs, 5); @@ -695,8 +712,8 @@ inithdlc(struct IsdnCardState *cs)) cs->bcs[1].BC_SetStack = setstack_hdlc; cs->bcs[0].BC_Close = close_hdlcstate; cs->bcs[1].BC_Close = close_hdlcstate; - modehdlc(cs->bcs, 0, 0); - modehdlc(cs->bcs + 1, 0, 0); + modehdlc(cs->bcs, -1, 0); + modehdlc(cs->bcs + 1, -1, 1); } static void @@ -734,11 +751,11 @@ reset_avmpcipnp(struct IsdnCardState *cs) save_flags(flags); sti(); outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2); outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3)); } diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c index cb999972c..ce02a1bc6 100644 --- a/drivers/isdn/hisax/bkm_a4t.c +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -1,4 +1,4 @@ -/* $Id: bkm_a4t.c,v 1.8 1999/09/04 06:20:05 keil Exp $ +/* $Id: bkm_a4t.c,v 1.9 1999/12/19 13:09:41 keil 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 @@ -7,6 +7,10 @@ * Author Roland Klabunde (R.Klabunde@Berkom.de) * * $Log: bkm_a4t.c,v $ + * Revision 1.9 1999/12/19 13:09:41 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.8 1999/09/04 06:20:05 keil * Changes from kernel set_current_state() * @@ -48,7 +52,7 @@ extern const char *CardType[]; -const char *bkm_a4t_revision = "$Revision: 1.8 $"; +const char *bkm_a4t_revision = "$Revision: 1.9 $"; static inline u_char @@ -231,11 +235,11 @@ reset_bkm(struct IsdnCardState *cs) sti(); /* Issue the I20 soft reset */ pI20_Regs->i20SysControl = 0xFF; /* all in */ - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10 * HZ) / 1000); /* Remove the soft reset */ pI20_Regs->i20SysControl = sysRESET | 0xFF; - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10 * HZ) / 1000); /* Set our configuration */ pI20_Regs->i20SysControl = sysRESET | sysCFG; @@ -246,14 +250,14 @@ reset_bkm(struct IsdnCardState *cs) g_A4T_ISAC_RES | g_A4T_JADE_BOOTR | g_A4T_ISAR_BOOTR; - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); 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); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10 * HZ) / 1000); restore_flags(flags); } diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c index fda6c213c..8ec2e9a08 100644 --- a/drivers/isdn/hisax/bkm_a8.c +++ b/drivers/isdn/hisax/bkm_a8.c @@ -1,4 +1,4 @@ -/* $Id: bkm_a8.c,v 1.8 1999/09/04 06:20:05 keil Exp $ +/* $Id: bkm_a8.c,v 1.9 1999/12/19 13:09:41 keil 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 @@ -7,6 +7,10 @@ * Author Roland Klabunde (R.Klabunde@Berkom.de) * * $Log: bkm_a8.c,v $ + * Revision 1.9 1999/12/19 13:09:41 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.8 1999/09/04 06:20:05 keil * Changes from kernel set_current_state() * @@ -49,7 +53,7 @@ extern const char *CardType[]; -const char sct_quadro_revision[] = "$Revision: 1.8 $"; +const char sct_quadro_revision[] = "$Revision: 1.9 $"; /* To survive the startup phase */ typedef struct { @@ -298,13 +302,13 @@ reset_bkm(struct IsdnCardState *cs) save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10 * HZ) / 1000); /* Remove the soft reset */ wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4)); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10 * HZ) / 1000); restore_flags(flags); } diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index e4c26d9de..4a01218c3 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 2.39 1999/10/14 20:25:28 keil Exp $ +/* $Id: callc.c,v 2.40 1999/12/19 12:59:56 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,10 @@ * Fritz Elfert * * $Log: callc.c,v $ + * Revision 2.40 1999/12/19 12:59:56 keil + * fix leased line handling + * and cosmetics + * * Revision 2.39 1999/10/14 20:25:28 keil * add a statistic for error monitoring * @@ -163,7 +167,7 @@ #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module)) #endif /* MODULE */ -const char *lli_revision = "$Revision: 2.39 $"; +const char *lli_revision = "$Revision: 2.40 $"; extern struct IsdnCard cards[]; extern int nrcards; @@ -199,8 +203,7 @@ static int chancount = 0; /* * Find card with given driverId */ -static inline struct IsdnCardState -* +static inline struct IsdnCardState * hisax_findcard(int driverid) { int i; @@ -239,39 +242,39 @@ link_debug(struct Channel *chanp, int direction, char *fmt, ...) } enum { - 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 */ + 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_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", +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_IN_PROCEED_SEND", + "ST_IN_PROCEED_SEND", }; enum { @@ -333,19 +336,19 @@ static char *strEvent[] = static inline void HL_LL(struct Channel *chanp, int command) { - isdn_ctrl ic; + isdn_ctrl ic; - ic.driver = chanp->cs->myid; - ic.command = command; - ic.arg = chanp->chan; - chanp->cs->iif.statcallb(&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; - + isdn_ctrl ic; + if (chanp->proc->para.cause == NO_CAUSE) return; ic.driver = chanp->cs->myid; @@ -363,42 +366,42 @@ lli_deliver_cause(struct Channel *chanp) static inline void lli_close(struct FsmInst *fi) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_NULL); - chanp->Flags = 0; - chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_NULL); + chanp->Flags = 0; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } - static void +static void lli_leased_in(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; + isdn_ctrl ic; + int ret; - 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; + if (!chanp->leased) + return; + 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); - } + 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); + } } @@ -408,14 +411,14 @@ lli_leased_in(struct FsmInst *fi, int event, void *arg) 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); + 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 @@ -427,14 +430,13 @@ lli_prep_dialout(struct FsmInst *fi, int event, void *arg) 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_SETUP | REQUEST, chanp); - } + 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 @@ -442,18 +444,17 @@ lli_resume(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - 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); - } + 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 @@ -521,15 +522,15 @@ lli_deliver_call(struct FsmInst *fi, int event, void *arg) 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 */ + 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); - } + 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 */ break; @@ -590,17 +591,17 @@ lli_init_bchan_in(struct FsmInst *fi, int event, void *arg) 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); + 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); + 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); - } + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + } } /* Call suspend */ @@ -616,51 +617,84 @@ lli_suspend(struct FsmInst *fi, int event, void *arg) /* Call clearing */ static void +lli_leased_hup(struct FsmInst *fi, struct Channel *chanp) +{ + isdn_ctrl ic; + + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L0010"); + chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + HL_LL(chanp, ISDN_STAT_DHUP); + lli_close(fi); +} + +static void lli_disconnect_req(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); + if (chanp->leased) { + lli_leased_hup(fi, chanp); + } else { + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, + chanp->proc); + } } static void lli_disconnect_reject(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - 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); + if (chanp->leased) { + lli_leased_hup(fi, chanp); + } else { + 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_dhup_close(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + 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); + if (chanp->leased) { + lli_leased_hup(fi, chanp); + } else { + 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_reject_req(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; + if (chanp->leased) { + lli_leased_hup(fi, chanp); + return; + } #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); + 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); + 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 } @@ -678,54 +712,45 @@ static void lli_start_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - isdn_ctrl ic; if (chanp->leased) { - ic.command = ISDN_STAT_CAUSE; - ic.arg = chanp->chan; - sprintf(ic.parm.num, "L0010"); - chanp->cs->iif.statcallb(&ic); - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_DHUP"); - HL_LL(chanp, ISDN_STAT_DHUP); - lli_close(fi); + lli_leased_hup(fi, chanp); } else { - lli_disconnect_req(fi, event, arg); + lli_disconnect_req(fi, event, arg); } } static void lli_rel_b_disc(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - - release_b_st(chanp); - lli_start_disc(fi, event, arg); + struct Channel *chanp = fi->userdata; + + release_b_st(chanp); + lli_start_disc(fi, event, arg); } 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); + struct Channel *chanp = fi->userdata; - lli_rel_b_disc(fi, event, arg); + 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; + 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); + 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 @@ -742,63 +767,65 @@ lli_release_bchan(struct FsmInst *fi, int event, void *arg) static void lli_rel_b_dhup(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - release_b_st(chanp); - lli_dhup_close(fi, event, arg); + release_b_st(chanp); + lli_dhup_close(fi, event, arg); } static void lli_bhup_dhup(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - HL_LL(chanp, ISDN_STAT_BHUP); - - lli_rel_b_dhup(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_abort(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); - - lli_bhup_dhup(fi, event, arg); + 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); + struct Channel *chanp = fi->userdata; + + if (chanp->leased) { + lli_leased_hup(fi, chanp); + } else { + 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); + 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); + struct Channel *chanp = fi->userdata; - lli_rel_b_release_req(fi, event, arg); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_BHUP"); + HL_LL(chanp, ISDN_STAT_BHUP); + lli_rel_b_release_req(fi, event, arg); } @@ -825,7 +852,7 @@ lli_dchan_not_ready(struct FsmInst *fi, int event, void *arg) if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DHUP"); - HL_LL(chanp, ISDN_STAT_DHUP); + HL_LL(chanp, ISDN_STAT_DHUP); } static void @@ -836,67 +863,65 @@ lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg) if (chanp->debug & 1) link_debug(chanp, 0, "STAT_DHUP"); HL_LL(chanp, ISDN_STAT_DHUP); - lli_close(fi); + lli_close(fi); } static void lli_error(struct FsmInst *fi, int event, void *arg) { - FsmChangeState(fi, ST_WAIT_DRELEASE); + FsmChangeState(fi, ST_WAIT_DRELEASE); } static void lli_failure_l(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; - isdn_ctrl ic; + 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); + 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_rel_b_fail(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - release_b_st(chanp); - lli_failure_l(fi, event, arg); + release_b_st(chanp); + lli_failure_l(fi, event, arg); } static void lli_bhup_fail(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BHUP"); - HL_LL(chanp, ISDN_STAT_BHUP); - - lli_rel_b_fail(fi, event, arg); + 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_failure_a(struct FsmInst *fi, int event, void *arg) { - struct Channel *chanp = fi->userdata; + struct Channel *chanp = fi->userdata; - chanp->data_open = 0; - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); - - lli_bhup_fail(fi, event, arg); + 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 = - { +/* *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}, @@ -959,10 +984,9 @@ lli_failure_a(struct FsmInst *fi, int event, void *arg) {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)) @@ -985,9 +1009,9 @@ release_b_st(struct Channel *chanp) { struct PStack *st = chanp->b_st; - if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) { - chanp->bcs->BC_Close(chanp->bcs); - switch (chanp->l2_active_protocol) { + 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; @@ -995,10 +1019,10 @@ release_b_st(struct Channel *chanp) case (ISDN_PROTO_L2_TRANS): case (ISDN_PROTO_L2_MODEM): case (ISDN_PROTO_L2_FAX): - releasestack_transl2(st); - break; - } - } + releasestack_transl2(st); + break; + } + } } struct Channel @@ -1013,10 +1037,10 @@ struct Channel else i=0; - if (!bch) - { i = 2; /* virtual channel */ - chanp += 2; - } + if (!bch) { + i = 2; /* virtual channel */ + chanp += 2; + } while (i < ((bch) ? cs->chanlimit : (2 + MAX_WAITING_CALLS))) { if (chanp->fi.state == ST_NULL) @@ -1025,17 +1049,17 @@ struct Channel i++; } - 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++; - } - } + 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 (NULL); } @@ -1053,19 +1077,19 @@ 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; - 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; + 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; @@ -1121,8 +1145,8 @@ dchan_l3l4(struct PStack *st, int pr, void *arg) break; case (CC_REDIR | INDICATION): stat_redir_result(cs, chanp->chan, pc->redir_result); - break; - default: + break; + default: if (chanp->debug & 0x800) { HiSax_putstatus(chanp->cs, "Ch", "%d L3->L4 unknown primitiv %#x", @@ -1147,7 +1171,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.l3ml3 = dummy_pstack; (*stp)->l3.l3l4 = dummy_pstack; (*stp)->lli.l4l3 = dummy_pstack; (*stp)->ma.layer = dummy_pstack; @@ -1230,8 +1254,8 @@ init_chan(int chan, struct IsdnCardState *csta) } int -CallcNewChan(struct IsdnCardState *csta) -{ int i; +CallcNewChan(struct IsdnCardState *csta) { + int i; chancount += 2; init_chan(0, csta); @@ -1240,8 +1264,7 @@ CallcNewChan(struct IsdnCardState *csta) for (i = 0; i < MAX_WAITING_CALLS; i++) init_chan(i+2,csta); - printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n"); - + 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"); csta->channel->d_st->lli.l4l3(csta->channel->d_st, @@ -1272,13 +1295,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) { - 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); @@ -1311,7 +1334,7 @@ lldata_handler(struct PStack *st, int pr, void *arg) break; default: printk(KERN_WARNING "lldata_handler unknown primitive %#x\n", - pr); + pr); break; } } @@ -1341,7 +1364,7 @@ lltrans_handler(struct PStack *st, int pr, void *arg) break; default: printk(KERN_WARNING "lltrans_handler unknown primitive %#x\n", - pr); + pr); break; } } @@ -1444,7 +1467,7 @@ leased_l4l3(struct PStack *st, int pr, void *arg) break; default: printk(KERN_WARNING "transd_l4l3 unknown primitive %#x\n", - pr); + pr); break; } } @@ -1598,7 +1621,7 @@ HiSax_command(isdn_ctrl * ic) if (!csta) { printk(KERN_ERR "HiSax: if_command %d called with invalid driverId %d!\n", - ic->command, ic->driver); + ic->command, ic->driver); return -ENODEV; } switch (ic->command) { @@ -1771,11 +1794,18 @@ HiSax_command(isdn_ctrl * ic) num = *(unsigned int *) ic->parm.num; chanp = csta->channel + (num & 1); num = num >>1; - test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag); - chanp->d_st->l2.tei = num; - HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num); - printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", - num); + if (num == 127) { + test_and_clear_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag); + chanp->d_st->l2.tei = -1; + HiSax_putstatus(csta, "set card ", "in VAR TEI mode"); + printk(KERN_DEBUG "HiSax: set card in VAR TEI mode\n"); + } else { + test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag); + chanp->d_st->l2.tei = num; + HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num); + printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", + num); + } chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); break; @@ -1811,7 +1841,7 @@ HiSax_command(isdn_ctrl * ic) if (csta->auxcmd) return(csta->auxcmd(csta, ic)); printk(KERN_DEBUG "HiSax: invalid ioclt %d\n", - (int) ic->arg); + (int) ic->arg); return (-EINVAL); } break; @@ -1839,11 +1869,11 @@ HiSax_command(isdn_ctrl * ic) break; /* protocol specific io commands */ - case (ISDN_CMD_PROT_IO): + 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); + return(-EINVAL); break; default: if (csta->auxcmd) @@ -1865,7 +1895,7 @@ HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb) if (!csta) { printk(KERN_ERR - "HiSax: if_sendbuf called with invalid driverId!\n"); + "HiSax: if_sendbuf called with invalid driverId!\n"); return -ENODEV; } chanp = csta->channel + chan; diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index a75810f02..8d389c623 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,10 +1,21 @@ -/* $Id: config.c,v 2.40 1999/10/30 13:09:45 keil Exp $ +/* $Id: config.c,v 2.43 2000/01/20 19:49:36 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * * * $Log: config.c,v $ + * Revision 2.43 2000/01/20 19:49:36 keil + * Support teles 13.3c vendor version 2.1 + * + * Revision 2.42 1999/12/19 13:09:41 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * + * Revision 2.41 1999/11/18 00:00:43 werner + * + * Added support for HFC-S+ and HFC-SP cards + * * Revision 2.40 1999/10/30 13:09:45 keil * Version 3.3c * @@ -202,6 +213,7 @@ * 34 Gazel ISDN cards * 35 HFC 2BDS0 PCI none * 36 Winbond 6692 PCI none + * 37 HFC 2BDS0 S+/SP p0=irq p1=iobase * * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 * @@ -217,6 +229,7 @@ const char *CardType[] = "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI", "Sedlbauer Speed Fax +", "Siemens I-Surf", "Acer P10", "HST Saphir", "Telekom A4T", "Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", "Winbond 6692", + "HFC 2BDS0 SX", }; void HiSax_closecard(int cardnr); @@ -352,6 +365,13 @@ EXPORT_SYMBOL(sedl_init_pcmcia); #define DEFAULT_CFG {0,0,0,0} #endif +#ifdef CONFIG_HISAX_HFC_SX +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_HFC_SX +#define DEFAULT_CFG {5,0x2E0,0,0} +#endif + #ifdef CONFIG_HISAX_AMD7930 #undef DEFAULT_CARD @@ -529,9 +549,9 @@ HiSaxVersion(void)) printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n"); #ifdef MODULE - printk(KERN_INFO "HiSax: Version 3.3c (module)\n"); + printk(KERN_INFO "HiSax: Version 3.3d (module)\n"); #else - printk(KERN_INFO "HiSax: Version 3.3c (kernel)\n"); + printk(KERN_INFO "HiSax: Version 3.3d (kernel)\n"); #endif strcpy(tmp, l1_revision); printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp)); @@ -687,6 +707,10 @@ extern int setup_hfcs(struct IsdnCard *card); extern int setup_hfcpci(struct IsdnCard *card); #endif +#if CARD_HFC_SX +extern int setup_hfcsx(struct IsdnCard *card); +#endif + #if CARD_AMD7930 extern int setup_amd7930(struct IsdnCard *card); #endif @@ -994,7 +1018,7 @@ HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) while (cnt) { cs->cardmsg(cs, CARD_INIT, NULL); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); /* Timeout 10ms */ schedule_timeout((10*HZ)/1000); restore_flags(flags); @@ -1208,6 +1232,11 @@ checkcard(int cardnr, char *id, int *busy_flag)) ret = setup_hfcpci(card); break; #endif +#if CARD_HFC_SX + case ISDN_CTYPE_HFC_SX: + ret = setup_hfcsx(card); + break; +#endif #if CARD_NICCY case ISDN_CTYPE_NICCY: ret = setup_niccy(card); @@ -1515,6 +1544,7 @@ HiSax_init(void)) case ISDN_CTYPE_FRITZPCI: case ISDN_CTYPE_HSTSAPHIR: case ISDN_CTYPE_GAZEL: + case ISDN_CTYPE_HFC_SX: cards[i].para[0] = irq[i]; cards[i].para[1] = io[i]; break; diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index f1161e63d..5dc868942 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -1,4 +1,4 @@ -/* $Id: diva.c,v 1.17 1999/09/04 06:20:06 keil Exp $ +/* $Id: diva.c,v 1.18 1999/12/19 13:09:41 keil Exp $ * diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards * @@ -12,6 +12,10 @@ * * * $Log: diva.c,v $ + * Revision 1.18 1999/12/19 13:09:41 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.17 1999/09/04 06:20:06 keil * Changes from kernel set_current_state() * @@ -80,7 +84,7 @@ extern const char *CardType[]; -const char *Diva_revision = "$Revision: 1.17 $"; +const char *Diva_revision = "$Revision: 1.18 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -750,30 +754,30 @@ reset_diva(struct IsdnCardState *cs) sti(); if (cs->subtyp == DIVA_IPAC_ISA) { writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0); } 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; - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); *ireg = PITA_PARA_MPX_MODE; - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); 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); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); if (cs->subtyp == DIVA_ISA) cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index 4dbe40c38..1e425ccc5 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1,4 +1,4 @@ -/* $Id: elsa.c,v 2.19 1999/09/04 06:20:06 keil Exp $ +/* $Id: elsa.c,v 2.20 1999/12/19 13:09:42 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * @@ -14,6 +14,10 @@ * for ELSA PCMCIA support * * $Log: elsa.c,v $ + * Revision 2.20 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 2.19 1999/09/04 06:20:06 keil * Changes from kernel set_current_state() * @@ -99,7 +103,7 @@ extern const char *CardType[]; -const char *Elsa_revision = "$Revision: 2.19 $"; +const char *Elsa_revision = "$Revision: 2.20 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI", @@ -578,10 +582,10 @@ reset_elsa(struct IsdnCardState *cs) save_flags(flags); sti(); writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ restore_flags(flags); @@ -785,7 +789,7 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) cs->hw.elsa.status |= ELSA_TIMER_AKTIV; byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); byteout(cs->hw.elsa.timer, 0); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((110*HZ)/1000); restore_flags(flags); cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index aa0ff4adb..665fa2c74 100644 --- a/drivers/isdn/hisax/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -1,12 +1,15 @@ -/* $Id: fsm.c,v 1.10 1998/11/15 23:54:39 keil Exp $ +/* $Id: fsm.c,v 1.11 1999/12/23 15:09:32 keil Exp $ - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * * Thanks to Jan den Ouden * Fritz Elfert * * $Log: fsm.c,v $ + * Revision 1.11 1999/12/23 15:09:32 keil + * change email + * * Revision 1.10 1998/11/15 23:54:39 keil * changes from 2.0 * diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c index bb388c5c2..2b22cdfea 100644 --- a/drivers/isdn/hisax/hfc_2bds0.c +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -1,11 +1,14 @@ -/* $Id: hfc_2bds0.c,v 1.10 1999/10/14 20:25:28 keil Exp $ +/* $Id: hfc_2bds0.c,v 1.11 1999/12/23 15:09:32 keil Exp $ * * specific routines for CCD's HFC 2BDS0 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: hfc_2bds0.c,v $ + * Revision 1.11 1999/12/23 15:09:32 keil + * change email + * * Revision 1.10 1999/10/14 20:25:28 keil * add a statistic for error monitoring * diff --git a/drivers/isdn/hisax/hfc_2bds0.h b/drivers/isdn/hisax/hfc_2bds0.h index d11e8b503..32f703662 100644 --- a/drivers/isdn/hisax/hfc_2bds0.h +++ b/drivers/isdn/hisax/hfc_2bds0.h @@ -1,11 +1,14 @@ -/* $Id: hfc_2bds0.h,v 1.2 1998/02/02 13:26:15 keil Exp $ +/* $Id: hfc_2bds0.h,v 1.3 1999/12/23 15:09:32 keil Exp $ * specific defines for CCD's HFC 2BDS0 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: hfc_2bds0.h,v $ + * Revision 1.3 1999/12/23 15:09:32 keil + * change email + * * Revision 1.2 1998/02/02 13:26:15 keil * New * diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c index 6620b90ec..f3edefa3e 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.10 1999/10/14 20:25:28 keil Exp $ +/* $Id: hfc_2bs0.c,v 1.12 1999/12/19 14:17:12 keil Exp $ * specific routines for CCD's HFC 2BS0 * @@ -6,6 +6,13 @@ * * * $Log: hfc_2bs0.c,v $ + * Revision 1.12 1999/12/19 14:17:12 keil + * fix compiler warning + * + * Revision 1.11 1999/11/21 12:41:18 werner + * + * Implemented full audio support + * * Revision 1.10 1999/10/14 20:25:28 keil * add a statistic for error monitoring * @@ -210,7 +217,7 @@ hfc_empty_fifo(struct BCState *bcs, int count) WaitForBusy(cs); return (NULL); } - if (count < 4) { + if ((count < 4) && (bcs->mode != L1_MODE_TRANS)) { if (cs->debug & L1_DEB_WARN) debugl1(cs, "hfc_empty_fifo: incoming packet too small"); cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); @@ -225,47 +232,55 @@ hfc_empty_fifo(struct BCState *bcs, int count) #endif return (NULL); } - if (!(skb = dev_alloc_skb(count - 3))) + if (bcs->mode == L1_MODE_TRANS) + count -= 1; + else + count -= 3; + if (!(skb = dev_alloc_skb(count))) printk(KERN_WARNING "HFC: receive out of memory\n"); else { - ptr = skb_put(skb, count - 3); + ptr = skb_put(skb, count); idx = 0; cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); while ((idx < count - 3) && WaitNoBusy(cs)) { *ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); idx++; } - if (idx != count - 3) { + if (idx != count) { debugl1(cs, "RFIFO BUSY error"); printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); dev_kfree_skb(skb); - WaitNoBusy(cs); - stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | - HFC_CHANNEL(bcs->channel)); - WaitForBusy(cs); + if (bcs->mode != L1_MODE_TRANS) { + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + } return (NULL); } - WaitNoBusy(cs); - chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8); - WaitNoBusy(cs); - chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip); - WaitNoBusy(cs); - stat = cs->BC_Read_Reg(cs, HFC_DATA, cip); - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x", - bcs->channel, chksum, stat); - if (stat) { - debugl1(cs, "FIFO CRC error"); - dev_kfree_skb(skb); - skb = NULL; + if (bcs->mode != L1_MODE_TRANS) { + WaitNoBusy(cs); + chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x", + bcs->channel, chksum, stat); + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb); + skb = NULL; #ifdef ERROR_STATISTIC - bcs->err_crc++; + bcs->err_crc++; #endif + } + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); } - WaitNoBusy(cs); - stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | - HFC_CHANNEL(bcs->channel)); - WaitForBusy(cs); } return (skb); } @@ -277,6 +292,7 @@ hfc_fill_fifo(struct BCState *bcs) long flags; int idx, fcnt; int count; + int z1, z2; u_char cip; if (!bcs->tx_skb) @@ -288,29 +304,39 @@ hfc_fill_fifo(struct BCState *bcs) cli(); cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel); if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { - cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); - WaitForBusy(cs); + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + WaitForBusy(cs); } WaitNoBusy(cs); - bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); - cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel); - WaitNoBusy(cs); - bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); - bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel)); - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)", - bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2, - bcs->hw.hfc.send[bcs->hw.hfc.f1]); - fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2; - if (fcnt < 0) - fcnt += 32; - if (fcnt > 30) { - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc_fill_fifo more as 30 frames"); - restore_flags(flags); - return; - } - count = GetFreeFifoBytes(bcs); + if (bcs->mode != L1_MODE_TRANS) { + bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel)); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)", + bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2, + bcs->hw.hfc.send[bcs->hw.hfc.f1]); + fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2; + if (fcnt < 0) + fcnt += 32; + if (fcnt > 30) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo more as 30 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes(bcs); + } + else { + WaitForBusy(cs); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + count = z1 - z2; + if (count < 0) + count += cs->hw.hfc.fifosize; + } /* L1_MODE_TRANS */ if (cs->debug & L1_DEB_HSCX) debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)", bcs->channel, bcs->tx_skb->len, @@ -335,9 +361,11 @@ hfc_fill_fifo(struct BCState *bcs) count = -1; dev_kfree_skb(bcs->tx_skb); bcs->tx_skb = NULL; - WaitForBusy(cs); - WaitNoBusy(cs); - cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel)); + if (bcs->mode != L1_MODE_TRANS) { + WaitForBusy(cs); + WaitNoBusy(cs); + cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel)); + } if (bcs->st->lli.l1writewakeup && (count >= 0)) bcs->st->lli.l1writewakeup(bcs->st, count); test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); @@ -366,32 +394,39 @@ main_irq_hfc(struct BCState *bcs) WaitForBusy(cs); } WaitNoBusy(cs); - f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); - cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); - WaitNoBusy(cs); - f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); - if (f1 != f2) { - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc rec %d f1(%d) f2(%d)", - bcs->channel, f1, f2); + receive = 0; + if (bcs->mode == L1_MODE_HDLC) { + f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (f1 != f2) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc rec %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + receive = 1; + } + } + if (receive || (bcs->mode == L1_MODE_TRANS)) { WaitForBusy(cs); z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); rcnt = z1 - z2; if (rcnt < 0) rcnt += cs->hw.hfc.fifosize; - rcnt++; - if (cs->debug & L1_DEB_HSCX) - debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)", - bcs->channel, z1, z2, rcnt); -/* sti(); */ - if ((skb = hfc_empty_fifo(bcs, rcnt))) { - skb_queue_tail(&bcs->rqueue, skb); - hfc_sched_event(bcs, B_RCVBUFREADY); + if ((bcs->mode == L1_MODE_HDLC) || (rcnt)) { + rcnt++; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + /* sti(); */ + if ((skb = hfc_empty_fifo(bcs, rcnt))) { + skb_queue_tail(&bcs->rqueue, skb); + hfc_sched_event(bcs, B_RCVBUFREADY); + } } receive = 1; - } else - receive = 0; + } restore_flags(flags); udelay(1); cli(); @@ -432,12 +467,19 @@ mode_hfc(struct BCState *bcs, int mode, int bc) switch (mode) { case (L1_MODE_NULL): - if (bc) + if (bc) { + cs->hw.hfc.ctmt &= ~1; cs->hw.hfc.isac_spcr &= ~0x03; - else + } + else { + cs->hw.hfc.ctmt &= ~2; cs->hw.hfc.isac_spcr &= ~0x0c; + } break; case (L1_MODE_TRANS): + cs->hw.hfc.ctmt &= ~(1 << bc); /* set HDLC mode */ + cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt); + hfc_clear_fifo(bcs); /* complete fifo clear */ if (bc) { cs->hw.hfc.ctmt |= 1; cs->hw.hfc.isac_spcr &= ~0x03; @@ -462,7 +504,7 @@ mode_hfc(struct BCState *bcs, int mode, int bc) } cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt); cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr); - if (mode) + if (mode == L1_MODE_HDLC) hfc_clear_fifo(bcs); } diff --git a/drivers/isdn/hisax/hfc_2bs0.h b/drivers/isdn/hisax/hfc_2bs0.h index cce8e4a35..981ae7cb3 100644 --- a/drivers/isdn/hisax/hfc_2bs0.h +++ b/drivers/isdn/hisax/hfc_2bs0.h @@ -1,11 +1,14 @@ -/* $Id: hfc_2bs0.h,v 1.1 1997/09/11 17:31:34 keil Exp $ +/* $Id: hfc_2bs0.h,v 1.2 1999/12/23 15:09:32 keil Exp $ * specific defines for CCD's HFC 2BS0 * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: hfc_2bs0.h,v $ + * Revision 1.2 1999/12/23 15:09:32 keil + * change email + * * Revision 1.1 1997/09/11 17:31:34 keil * Common part for HFC 2BS0 based cards * diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 518c1670a..76f353861 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -1,4 +1,4 @@ -/* $Id: hfc_pci.c,v 1.23 1999/11/07 17:01:55 keil Exp $ +/* $Id: hfc_pci.c,v 1.26 2000/02/09 20:22:55 werner Exp $ * hfc_pci.c low level driver for CCD´s hfc-pci based cards * @@ -23,6 +23,18 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: hfc_pci.c,v $ + * Revision 1.26 2000/02/09 20:22:55 werner + * + * Updated PCI-ID table + * + * Revision 1.25 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * + * Revision 1.24 1999/11/17 23:59:55 werner + * + * removed unneeded data + * * Revision 1.23 1999/11/07 17:01:55 keil * fix for 2.3 pci structs * @@ -114,7 +126,7 @@ extern const char *CardType[]; -static const char *hfcpci_revision = "$Revision: 1.23 $"; +static const char *hfcpci_revision = "$Revision: 1.26 $"; /* table entry in the PCI devices list */ typedef struct { @@ -143,26 +155,12 @@ static const PCI_ENTRY id_list[] = {0x1051, 0x0100, "Motorola MC145575", "MC145575"}, {0x1397, 0xB100, "Seyeon", "B100"}, {0x15B0, 0x2BD0, "Zoltrix", "2BD0"}, + {0x114f, 0x71, "Digi intl.","Digicom"}, {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 */ @@ -179,13 +177,12 @@ release_io_hfcpci(struct IsdnCardState *cs) restore_flags(flags); Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ #if CONFIG_PCI pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0); /* disable 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; @@ -211,10 +208,10 @@ reset_hfcpci(struct IsdnCardState *cs) 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 */ sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ if (Read_hfc(cs, HFCPCI_STATUS) & 2) printk(KERN_WARNING "HFC-PCI init bit busy\n"); @@ -1647,24 +1644,6 @@ hfcpci_bh(struct IsdnCardState *cs) } -/*************************************/ -/* Alloc memory send data for queues */ -/*************************************/ -__initfunc(unsigned int - *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 */ /********************************/ @@ -1676,10 +1655,6 @@ __initfunc(void cs->dbusytimer.data = (long) cs; init_timer(&cs->dbusytimer); cs->tqueue.routine = (void *) (void *) hfcpci_bh; - 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; @@ -1712,7 +1687,7 @@ hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) inithfcpci(cs); save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ /* now switch timer interrupt off */ cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; @@ -1746,8 +1721,6 @@ __initfunc(int printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); #if CONFIG_PCI cs->hw.hfcpci.int_s1 = 0; - 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) { diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c new file mode 100644 index 000000000..462de9d91 --- /dev/null +++ b/drivers/isdn/hisax/hfc_sx.c @@ -0,0 +1,1583 @@ +/* $Id: hfc_sx.c,v 1.3 2000/01/20 19:49:36 keil Exp $ + + * hfc_sx.c low level driver for CCD´s hfc-s+/sp based cards + * + * Author Werner Cornelius (werner@isdn4linux.de) + * based on existing driver for CCD HFC PCI cards + * + * 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_sx.c,v $ + * Revision 1.3 2000/01/20 19:49:36 keil + * Support teles 13.3c vendor version 2.1 + * + * Revision 1.2 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * + * Revision 1.1 1999/11/18 00:09:18 werner + * + * Initial release of files for HFC-S+ and HFC-SP cards with 32K-RAM. + * Audio and Echo are supported. + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_sx.h" +#include "isdnl1.h" +#include <linux/interrupt.h> + +extern const char *CardType[]; + +static const char *hfcsx_revision = "$Revision: 1.3 $"; + +/***************************************/ +/* IRQ-table for CCDs demo board */ +/* IRQs 6,5,10,11,12,15 are supported */ +/***************************************/ + +/* Teles 16.3c Vendor Id TAG2620, Version 1.0, Vendor version 2.1 + * + * Thanks to Uwe Wisniewski + * + * ISA-SLOT Signal PIN + * B25 IRQ3 92 IRQ_G + * B23 IRQ5 94 IRQ_A + * B4 IRQ2/9 95 IRQ_B + * D3 IRQ10 96 IRQ_C + * D4 IRQ11 97 IRQ_D + * D5 IRQ12 98 IRQ_E + * D6 IRQ15 99 IRQ_F + */ + +#undef CCD_DEMO_BOARD +#ifdef CCD_DEMO_BOARD +static u_char ccd_sp_irqtab[16] = { + 0,0,0,0,0,2,1,0,0,0,3,4,5,0,0,6 +}; +#else /* Teles 16.3c */ +static u_char ccd_sp_irqtab[16] = { + 0,0,0,7,0,1,0,0,0,2,3,4,5,0,0,6 +}; +#endif +#define NT_T1_COUNT 20 /* number of 3.125ms interrupts for G2 timeout */ + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +/******************************/ +/* In/Out access to registers */ +/******************************/ +static inline void +Write_hfc(struct IsdnCardState *cs, u_char regnum, u_char val) +{ register int flags; + + save_flags(flags); + cli(); + byteout(cs->hw.hfcsx.base+1, regnum); + byteout(cs->hw.hfcsx.base, val); + restore_flags(flags); +} + +static inline u_char +Read_hfc(struct IsdnCardState *cs, u_char regnum) +{ register int flags; + register u_char ret; + + save_flags(flags); + cli(); + byteout(cs->hw.hfcsx.base+1, regnum); + ret = bytein(cs->hw.hfcsx.base); + restore_flags(flags); + return(ret); +} + + +/**************************************************/ +/* select a fifo and remember which one for reuse */ +/**************************************************/ +static void +fifo_select(struct IsdnCardState *cs, u_char fifo) +{ int flags; + + if (fifo == cs->hw.hfcsx.last_fifo) + return; /* still valid */ + + save_flags(flags); + cli(); + byteout(cs->hw.hfcsx.base+1, HFCSX_FIF_SEL); + byteout(cs->hw.hfcsx.base, fifo); + while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */ + udelay(4); + byteout(cs->hw.hfcsx.base, fifo); + while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */ + restore_flags(flags); +} + +/******************************************/ +/* reset the specified fifo to defaults. */ +/* If its a send fifo init needed markers */ +/******************************************/ +static void +reset_fifo(struct IsdnCardState *cs, u_char fifo) +{ int flags; + + save_flags(flags); + cli(); + fifo_select(cs, fifo); /* first select the fifo */ + byteout(cs->hw.hfcsx.base+1, HFCSX_CIRM); + byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.cirm | 0x80); /* reset cmd */ + udelay(1); + while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */ + restore_flags(flags); +} + + +/*************************************************************/ +/* write_fifo writes the skb contents to the desired fifo */ +/* if no space is available or an error occurs 0 is returned */ +/* the skb is not released in any way. */ +/*************************************************************/ +static int +write_fifo(struct IsdnCardState *cs, struct sk_buff *skb, u_char fifo, int trans_max) +{ unsigned short *msp; + int fifo_size, count, z1, z2; + u_char f_msk, f1, f2, *src; + + if (skb->len <= 0) return(0); + if (fifo & 1) return(0); /* no write fifo */ + + fifo_select(cs, fifo); + if (fifo & 4) { + fifo_size = D_FIFO_SIZE; /* D-channel */ + f_msk = MAX_D_FRAMES; + if (trans_max) return(0); /* only HDLC */ + } + else { + fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */ + f_msk = MAX_B_FRAMES; + } + + z1 = Read_hfc(cs, HFCSX_FIF_Z1H); + z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L)); + + /* Check for transparent mode */ + if (trans_max) { + z2 = Read_hfc(cs, HFCSX_FIF_Z2H); + z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L)); + count = z2 - z1; + if (count <= 0) + count += fifo_size; /* free bytes */ + if (count < skb->len+1) return(0); /* no room */ + count = fifo_size - count; /* bytes still not send */ + if (count > 2 * trans_max) return(0); /* delay to long */ + count = skb->len; + src = skb->data; + while (count--) + Write_hfc(cs, HFCSX_FIF_DWR, *src++); + return(1); /* success */ + } + + msp = ((struct hfcsx_extra *)(cs->hw.hfcsx.extra))->marker; + msp += (((fifo >> 1) & 3) * (MAX_B_FRAMES+1)); + f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk; + f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk; + + count = f1 - f2; /* frame count actually buffered */ + if (count < 0) + count += (f_msk + 1); /* if wrap around */ + if (count > f_msk-1) { + if (cs->debug & L1_DEB_ISAC_FIFO) + debugl1(cs, "hfcsx_write_fifo %d more as %d frames",fifo,f_msk-1); + return(0); + } + + *(msp + f1) = z1; /* remember marker */ + + if (cs->debug & L1_DEB_ISAC_FIFO) + debugl1(cs, "hfcsx_write_fifo %d f1(%x) f2(%x) z1(f1)(%x)", + fifo, f1, f2, z1); + /* now determine free bytes in FIFO buffer */ + count = *(msp + f2) - z1; + if (count <= 0) + count += fifo_size; /* count now contains available bytes */ + + if (cs->debug & L1_DEB_ISAC_FIFO) + debugl1(cs, "hfcsx_write_fifo %d count(%ld/%d)", + fifo, skb->len, count); + if (count < skb->len) { + if (cs->debug & L1_DEB_ISAC_FIFO) + debugl1(cs, "hfcsx_write_fifo %d no fifo mem", fifo); + return(0); + } + + count = skb->len; /* get frame len */ + src = skb->data; /* source pointer */ + while (count--) + Write_hfc(cs, HFCSX_FIF_DWR, *src++); + + Read_hfc(cs, HFCSX_FIF_INCF1); /* increment F1 */ + udelay(1); + while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */ + return(1); +} + +/***************************************************************/ +/* read_fifo reads data to an skb from the desired fifo */ +/* if no data is available or an error occurs NULL is returned */ +/* the skb is not released in any way. */ +/***************************************************************/ +static struct sk_buff * +read_fifo(struct IsdnCardState *cs, u_char fifo, int trans_max) +{ int fifo_size, count, z1, z2; + u_char f_msk, f1, f2, *dst; + struct sk_buff *skb; + + if (!(fifo & 1)) return(NULL); /* no read fifo */ + fifo_select(cs, fifo); + if (fifo & 4) { + fifo_size = D_FIFO_SIZE; /* D-channel */ + f_msk = MAX_D_FRAMES; + if (trans_max) return(NULL); /* only hdlc */ + } + else { + fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */ + f_msk = MAX_B_FRAMES; + } + + /* transparent mode */ + if (trans_max) { + z1 = Read_hfc(cs, HFCSX_FIF_Z1H); + z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L)); + z2 = Read_hfc(cs, HFCSX_FIF_Z2H); + z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L)); + /* now determine bytes in actual FIFO buffer */ + count = z1 - z2; + if (count <= 0) + count += fifo_size; /* count now contains buffered bytes */ + count++; + if (count > trans_max) + count = trans_max; /* limit length */ + if ((skb = dev_alloc_skb(count))) { + dst = skb_put(skb, count); + while (count--) + *dst++ = Read_hfc(cs, HFCSX_FIF_DRD); + return(skb); + } + else return(NULL); /* no memory */ + } + + do { + f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk; + f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk; + + if (f1 == f2) return(NULL); /* no frame available */ + + z1 = Read_hfc(cs, HFCSX_FIF_Z1H); + z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L)); + z2 = Read_hfc(cs, HFCSX_FIF_Z2H); + z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L)); + + if (cs->debug & L1_DEB_ISAC_FIFO) + debugl1(cs, "hfcsx_read_fifo %d f1(%x) f2(%x) z1(f2)(%x) z2(f2)(%x)", + fifo, f1, f2, z1, z2); + /* now determine bytes in actual FIFO buffer */ + count = z1 - z2; + if (count <= 0) + count += fifo_size; /* count now contains buffered bytes */ + count++; + + if (cs->debug & L1_DEB_ISAC_FIFO) + debugl1(cs, "hfcsx_read_fifo %d count %ld)", + fifo, count); + + if ((count > fifo_size) || (count < 4)) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcsx_read_fifo %d paket inv. len %d ", fifo , count); + while (count) { + count--; /* empty fifo */ + Read_hfc(cs, HFCSX_FIF_DRD); + } + skb = NULL; + } else + if ((skb = dev_alloc_skb(count - 3))) { + count -= 3; + dst = skb_put(skb, count); + + while (count--) + *dst++ = Read_hfc(cs, HFCSX_FIF_DRD); + + Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 1 */ + Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 2 */ + if (Read_hfc(cs, HFCSX_FIF_DRD)) { + dev_kfree_skb(skb); + if (cs->debug & L1_DEB_ISAC_FIFO) + debugl1(cs, "hfcsx_read_fifo %d crc error", fifo); + skb = NULL; + } + } else { + printk(KERN_WARNING "HFC-SX: receive out of memory\n"); + return(NULL); + } + + Read_hfc(cs, HFCSX_FIF_INCF2); /* increment F2 */ + udelay(1); + while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */ + udelay(1); + } while (!skb); /* retry in case of crc error */ + return(skb); +} + +/******************************************/ +/* free hardware resources used by driver */ +/******************************************/ +void +release_io_hfcsx(struct IsdnCardState *cs) +{ + int flags; + + save_flags(flags); + cli(); + cs->hw.hfcsx.int_m2 = 0; /* interrupt output off ! */ + Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2); + restore_flags(flags); + Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET); /* Reset On */ + sti(); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ + Write_hfc(cs, HFCSX_CIRM, 0); /* Reset Off */ + del_timer(&cs->hw.hfcsx.timer); + release_region(cs->hw.hfcsx.base, 2); /* release IO-Block */ + kfree(cs->hw.hfcsx.extra); + cs->hw.hfcsx.extra = NULL; +} + +/**********************************************************/ +/* set_fifo_size determines the size of the RAM and FIFOs */ +/* returning 0 -> need to reset the chip again. */ +/**********************************************************/ +static int set_fifo_size(struct IsdnCardState *cs) +{ + + if (cs->hw.hfcsx.b_fifo_size) return(1); /* already determined */ + + if ((cs->hw.hfcsx.chip >> 4) == 9) { + cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_32K; + return(1); + } + + cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_8K; + cs->hw.hfcsx.cirm |= 0x10; /* only 8K of ram */ + return(0); + +} + +/********************************************************************************/ +/* function called to reset the HFC SX chip. A complete software reset of chip */ +/* and fifos is done. */ +/********************************************************************************/ +static void +reset_hfcsx(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + cli(); + cs->hw.hfcsx.int_m2 = 0; /* interrupt output off ! */ + Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2); + + printk(KERN_INFO "HFC_SX: resetting card\n"); + while (1) { + Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET | cs->hw.hfcsx.cirm ); /* Reset */ + sti(); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ + Write_hfc(cs, HFCSX_CIRM, cs->hw.hfcsx.cirm); /* Reset Off */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + if (Read_hfc(cs, HFCSX_STATUS) & 2) + printk(KERN_WARNING "HFC-SX init bit busy\n"); + cs->hw.hfcsx.last_fifo = 0xff; /* invalidate */ + if (!set_fifo_size(cs)) continue; + break; + } + + cs->hw.hfcsx.trm = 0 + HFCSX_BTRANS_THRESMASK; /* no echo connect , threshold */ + Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm); + + Write_hfc(cs, HFCSX_CLKDEL, 0x0e); /* ST-Bit delay for TE-Mode */ + cs->hw.hfcsx.sctrl_e = HFCSX_AUTO_AWAKE; + Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e); /* S/T Auto awake */ + cs->hw.hfcsx.bswapped = 0; /* no exchange */ + cs->hw.hfcsx.nt_mode = 0; /* we are in TE mode */ + cs->hw.hfcsx.ctmt = HFCSX_TIM3_125 | HFCSX_AUTO_TIMER; + Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt); + + cs->hw.hfcsx.int_m1 = HFCSX_INTS_DTRANS | HFCSX_INTS_DREC | + HFCSX_INTS_L1STATE | HFCSX_INTS_TIMER; + Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1); + + /* Clear already pending ints */ + if (Read_hfc(cs, HFCSX_INT_S1)); + + Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 2); /* HFC ST 2 */ + udelay(10); + Write_hfc(cs, HFCSX_STATES, 2); /* HFC ST 2 */ + cs->hw.hfcsx.mst_m = HFCSX_MASTER; /* HFC Master Mode */ + + Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m); + cs->hw.hfcsx.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ + Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl); + cs->hw.hfcsx.sctrl_r = 0; + Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.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.hfcsx.conn = 0x36; /* set data flow directions */ + Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn); + Write_hfc(cs, HFCSX_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */ + Write_hfc(cs, HFCSX_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */ + Write_hfc(cs, HFCSX_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */ + Write_hfc(cs, HFCSX_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */ + + /* Finally enable IRQ output */ + cs->hw.hfcsx.int_m2 = HFCSX_IRQ_ENABLE; + Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2); + if (Read_hfc(cs, HFCSX_INT_S2)); + restore_flags(flags); +} + +/***************************************************/ +/* Timer function called when kernel timer expires */ +/***************************************************/ +static void +hfcsx_Timer(struct IsdnCardState *cs) +{ + cs->hw.hfcsx.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcsx.ctmt | 0x80); + add_timer(&cs->hw.hfcsx.timer); + */ +} + + +/*********************************/ +/* schedule a new D-channel task */ +/*********************************/ +static void +sched_event_D_sx(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 +hfcsx_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); +} + +/*******************************/ +/* D-channel receive procedure */ +/*******************************/ +static +int +receive_dmsg(struct IsdnCardState *cs) +{ + struct sk_buff *skb; + int count = 5; + + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_dmsg blocked"); + return (1); + } + + do { + skb = read_fifo(cs, HFCSX_SEL_D_RX, 0); + if (skb) { + skb_queue_tail(&cs->rq, skb); + sched_event_D_sx(cs, D_RCVBUFREADY); + } + } while (--count && skb); + + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + return (1); +} + +/**********************************/ +/* B-channel main receive routine */ +/**********************************/ +void +main_rec_hfcsx(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int count = 5; + struct sk_buff *skb; + + save_flags(flags); + + 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(); + skb = read_fifo(cs, ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ? + HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX, + (bcs->mode == L1_MODE_TRANS) ? + HFCSX_BTRANS_THRESHOLD : 0); + + if (skb) { + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfcsx_sched_event(bcs, B_RCVBUFREADY); + } + + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + if (count && skb) + goto Begin; + restore_flags(flags); + return; +} + +/**************************/ +/* D-channel send routine */ +/**************************/ +static void +hfcsx_fill_dfifo(struct IsdnCardState *cs) +{ + if (!cs->tx_skb) + return; + if (cs->tx_skb->len <= 0) + return; + + if (write_fifo(cs, cs->tx_skb, HFCSX_SEL_D_TX, 0)) { + dev_kfree_skb(cs->tx_skb); + cs->tx_skb = NULL; + } + return; +} + +/**************************/ +/* B-channel send routine */ +/**************************/ +static void +hfcsx_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int flags; + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + save_flags(flags); + sti(); + + if (write_fifo(cs, bcs->tx_skb, + ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ? + HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX, + (bcs->mode == L1_MODE_TRANS) ? + HFCSX_BTRANS_THRESHOLD : 0)) { + + 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); + dev_kfree_skb(bcs->tx_skb); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + + cli(); + restore_flags(flags); + return; +} + +/**********************************************/ +/* D-channel l1 state call for leased NT-mode */ +/**********************************************/ +static void +dch_nt_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + + switch (pr) { + case (PH_DATA | REQUEST): + case (PH_PULL | REQUEST): + case (PH_PULL | INDICATION): + st->l1.l1hw(st, pr, arg); + break; + case (PH_ACTIVATE | REQUEST): + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + break; + case (PH_TESTLOOP | REQUEST): + if (1 & (long) arg) + debugl1(cs, "PH_TEST_LOOP B1"); + if (2 & (long) arg) + debugl1(cs, "PH_TEST_LOOP B2"); + if (!(3 & (long) arg)) + debugl1(cs, "PH_TEST_LOOP DISABLED"); + st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg); + break; + default: + if (cs->debug) + debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr); + break; + } +} + + + +/***********************/ +/* set/reset echo mode */ +/***********************/ +static int +hfcsx_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic) +{ + int flags; + int i = *(unsigned int *) ic->parm.num; + + if ((ic->arg == 98) && + (!(cs->hw.hfcsx.int_m1 & (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC + HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC)))) { + save_flags(flags); + cli(); + Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 0); /* HFC ST G0 */ + udelay(10); + cs->hw.hfcsx.sctrl |= SCTRL_MODE_NT; + Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl); /* set NT-mode */ + udelay(10); + Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 1); /* HFC ST G1 */ + udelay(10); + Write_hfc(cs, HFCSX_STATES, 1 | HFCSX_ACTIVATE | HFCSX_DO_ACTION); + cs->dc.hfcsx.ph_state = 1; + cs->hw.hfcsx.nt_mode = 1; + cs->hw.hfcsx.nt_timer = 0; + cs->stlist->l2.l2l1 = dch_nt_l2l1; + restore_flags(flags); + debugl1(cs, "NT mode activated"); + return (0); + } + if ((cs->chanlimit > 1) || (cs->hw.hfcsx.bswapped) || + (cs->hw.hfcsx.nt_mode) || (ic->arg != 12)) + return (-EINVAL); + + save_flags(flags); + cli(); + if (i) { + cs->logecho = 1; + cs->hw.hfcsx.trm |= 0x20; /* enable echo chan */ + cs->hw.hfcsx.int_m1 |= HFCSX_INTS_B2REC; + /* reset Channel !!!!! */ + } else { + cs->logecho = 0; + cs->hw.hfcsx.trm &= ~0x20; /* disable echo chan */ + cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_B2REC; + } + cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA; + cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA; + cs->hw.hfcsx.conn |= 0x10; /* B2-IOM -> B2-ST */ + cs->hw.hfcsx.ctmt &= ~2; + Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt); + Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r); + Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl); + Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn); + Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm); + Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1); + restore_flags(flags); + return (0); +} /* hfcsx_auxcmd */ + +/*****************************/ +/* E-channel receive routine */ +/*****************************/ +static void +receive_emsg(struct IsdnCardState *cs) +{ + int flags; + int count = 5; + u_char *ptr; + struct sk_buff *skb; + + + save_flags(flags); + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "echo_rec_data blocked"); + restore_flags(flags); + return; + } + sti(); + + do { + skb = read_fifo(cs, HFCSX_SEL_B2_RX, 0); + if (skb) { + if (cs->debug & DEB_DLOG_HEX) { + ptr = cs->dlog; + if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) { + *ptr++ = 'E'; + *ptr++ = 'C'; + *ptr++ = 'H'; + *ptr++ = 'O'; + *ptr++ = ':'; + ptr += QuickHex(ptr, skb->data, skb->len); + ptr--; + *ptr++ = '\n'; + *ptr = 0; + HiSax_putstatus(cs, NULL, cs->dlog); + } else + HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", skb->len); + } + dev_kfree_skb(skb); + } + } while (--count && skb); + + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + restore_flags(flags); + return; +} /* receive_emsg */ + + +/*********************/ +/* Interrupt handler */ +/*********************/ +static void +hfcsx_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-SX: Spurious interrupt!\n"); + return; + } + if (!(cs->hw.hfcsx.int_m2 & 0x08)) + return; /* not initialised */ + + if (HFCSX_ANYINT & (stat = Read_hfc(cs, HFCSX_STATUS))) { + val = Read_hfc(cs, HFCSX_INT_S1); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-SX: stat(%02x) s1(%02x)", stat, val); + } else + return; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-SX irq %x %s", val, + test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ? + "locked" : "unlocked"); + val &= cs->hw.hfcsx.int_m1; + if (val & 0x40) { /* state machine irq */ + exval = Read_hfc(cs, HFCSX_STATES) & 0xf; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcsx.ph_state, + exval); + cs->dc.hfcsx.ph_state = exval; + sched_event_D_sx(cs, D_L1STATECHANGE); + val &= ~0x40; + } + if (val & 0x80) { /* timer irq */ + if (cs->hw.hfcsx.nt_mode) { + if ((--cs->hw.hfcsx.nt_timer) < 0) + sched_event_D_sx(cs, D_L1STATECHANGE); + } + val &= ~0x80; + Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER); + } + while (val) { + save_flags(flags); + cli(); + if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + cs->hw.hfcsx.int_s1 |= val; + restore_flags(flags); + return; + } + if (cs->hw.hfcsx.int_s1 & 0x18) { + exval = val; + val = cs->hw.hfcsx.int_s1; + cs->hw.hfcsx.int_s1 = exval; + } + if (val & 0x08) { + if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) { + if (cs->debug) + debugl1(cs, "hfcsx spurious 0x08 IRQ"); + } else + main_rec_hfcsx(bcs); + } + if (val & 0x10) { + if (cs->logecho) + receive_emsg(cs); + else if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcsx spurious 0x10 IRQ"); + } else + main_rec_hfcsx(bcs); + } + if (val & 0x01) { + if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) { + if (cs->debug) + debugl1(cs, "hfcsx spurious 0x01 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcsx_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)) { + hfcsx_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + hfcsx_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x02) { + if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcsx spurious 0x02 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcsx_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)) { + hfcsx_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + hfcsx_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_sx(cs, D_CLEARBUSY); + if (cs->tx_skb) { + if (cs->tx_skb->len) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcsx_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfcsx_fill_dfifo irq blocked"); + } + goto afterXPR; + } else { + dev_kfree_skb(cs->tx_skb); + 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)) { + hfcsx_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfcsx_fill_dfifo irq blocked"); + } + } else + sched_event_D_sx(cs, D_XMTBUFREADY); + } + afterXPR: + if (cs->hw.hfcsx.int_s1 && count--) { + val = cs->hw.hfcsx.int_s1; + cs->hw.hfcsx.int_s1 = 0; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-SX 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 +hfcsx_dbusy_timer(struct IsdnCardState *cs) +{ +} + +/*************************************/ +/* Layer 1 D-channel hardware access */ +/*************************************/ +static void +HFCSX_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + int flags; + + 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)) { + hfcsx_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfcsx_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)) { + hfcsx_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfcsx_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, HFCSX_STATES, HFCSX_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + Write_hfc(cs, HFCSX_STATES, 3); /* HFC ST 2 */ + cs->hw.hfcsx.mst_m |= HFCSX_MASTER; + Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m); + Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION); + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (HW_ENABLE | REQUEST): + Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION); + break; + case (HW_DEACTIVATE | REQUEST): + cs->hw.hfcsx.mst_m &= ~HFCSX_MASTER; + Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m); + break; + case (HW_INFO3 | REQUEST): + cs->hw.hfcsx.mst_m |= HFCSX_MASTER; + Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m); + break; + case (HW_TESTLOOP | REQUEST): + switch ((int) arg) { + case (1): + Write_hfc(cs, HFCSX_B1_SSL, 0x80); /* tx slot */ + Write_hfc(cs, HFCSX_B1_RSL, 0x80); /* rx slot */ + save_flags(flags); + cli(); + cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~7) | 1; + Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn); + restore_flags(flags); + break; + + case (2): + Write_hfc(cs, HFCSX_B2_SSL, 0x81); /* tx slot */ + Write_hfc(cs, HFCSX_B2_RSL, 0x81); /* rx slot */ + save_flags(flags); + cli(); + cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~0x38) | 0x08; + Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn); + restore_flags(flags); + break; + + default: + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcsx_l1hw loop invalid %4x", (int) arg); + return; + } + save_flags(flags); + cli(); + cs->hw.hfcsx.trm |= 0x80; /* enable IOM-loop */ + Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm); + restore_flags(flags); + break; + default: + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcsx_l1hw unknown pr %4x", pr); + break; + } +} + +/***********************************************/ +/* called during init setting l1 stack pointer */ +/***********************************************/ +void +setstack_hfcsx(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = HFCSX_l1hw; +} + +/**************************************/ +/* send B-channel data if not blocked */ +/**************************************/ +static void +hfcsx_send_data(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcsx_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_hfcsx(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int flags, fifo2; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HFCSX bchannel mode %d bchan %d/%d", + mode, bc, bcs->channel); + bcs->mode = mode; + bcs->channel = bc; + fifo2 = bc; + save_flags(flags); + cli(); + if (cs->chanlimit > 1) { + cs->hw.hfcsx.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcsx.sctrl_e &= ~0x80; + } else { + if (bc) { + if (mode != L1_MODE_NULL) { + cs->hw.hfcsx.bswapped = 1; /* B1 and B2 exchanged */ + cs->hw.hfcsx.sctrl_e |= 0x80; + } else { + cs->hw.hfcsx.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcsx.sctrl_e &= ~0x80; + } + fifo2 = 0; + } else { + cs->hw.hfcsx.bswapped = 0; /* B1 and B2 normal mode */ + cs->hw.hfcsx.sctrl_e &= ~0x80; + } + } + switch (mode) { + case (L1_MODE_NULL): + if (bc) { + cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA; + cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA; + } else { + cs->hw.hfcsx.sctrl &= ~SCTRL_B1_ENA; + cs->hw.hfcsx.sctrl_r &= ~SCTRL_B1_ENA; + } + if (fifo2) { + cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC); + } else { + cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC); + } + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA; + } else { + cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA; + } + if (fifo2) { + cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC); + cs->hw.hfcsx.ctmt |= 2; + cs->hw.hfcsx.conn &= ~0x18; + } else { + cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC); + cs->hw.hfcsx.ctmt |= 1; + cs->hw.hfcsx.conn &= ~0x03; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA; + } else { + cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA; + } + if (fifo2) { + cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC); + cs->hw.hfcsx.ctmt &= ~2; + cs->hw.hfcsx.conn &= ~0x18; + } else { + cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC); + cs->hw.hfcsx.ctmt &= ~1; + cs->hw.hfcsx.conn &= ~0x03; + } + break; + case (L1_MODE_EXTRN): + if (bc) { + cs->hw.hfcsx.conn |= 0x10; + cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA; + cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC); + } else { + cs->hw.hfcsx.conn |= 0x02; + cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA; + cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC); + } + break; + } + Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e); + Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1); + Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl); + Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r); + Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt); + Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn); + if (mode != L1_MODE_EXTRN) { + reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX); + reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX); + } + restore_flags(flags); +} + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ +static void +hfcsx_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_hfcsx(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_hfcsx(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_hfcsx(struct BCState *bcs) +{ + mode_hfcsx(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) { + dev_kfree_skb(bcs->tx_skb); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +/*************************************/ +/* init B-channel queues and control */ +/*************************************/ +static int +open_hfcsxstate(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_hfcsxstate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfcsx_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +/***************************/ +/* handle L1 state changes */ +/***************************/ +static void +hfcsx_bh(struct IsdnCardState *cs) +{ + int flags; +/* struct PStack *stptr; + */ + if (!cs) + return; + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + if (!cs->hw.hfcsx.nt_mode) + switch (cs->dc.hfcsx.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; + } else { + switch (cs->dc.hfcsx.ph_state) { + case (2): + save_flags(flags); + cli(); + if (cs->hw.hfcsx.nt_timer < 0) { + cs->hw.hfcsx.nt_timer = 0; + cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER; + Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1); + /* Clear already pending ints */ + if (Read_hfc(cs, HFCSX_INT_S1)); + + Write_hfc(cs, HFCSX_STATES, 4 | HFCSX_LOAD_STATE); + udelay(10); + Write_hfc(cs, HFCSX_STATES, 4); + cs->dc.hfcsx.ph_state = 4; + } else { + cs->hw.hfcsx.int_m1 |= HFCSX_INTS_TIMER; + Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1); + cs->hw.hfcsx.ctmt &= ~HFCSX_AUTO_TIMER; + cs->hw.hfcsx.ctmt |= HFCSX_TIM3_125; + Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER); + Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER); + cs->hw.hfcsx.nt_timer = NT_T1_COUNT; + Write_hfc(cs, HFCSX_STATES, 2 | HFCSX_NT_G2_G3); /* allow G2 -> G3 transition */ + } + restore_flags(flags); + break; + case (1): + case (3): + case (4): + save_flags(flags); + cli(); + cs->hw.hfcsx.nt_timer = 0; + cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER; + Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1); + restore_flags(flags); + 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); +} + + +/********************************/ +/* called for card init message */ +/********************************/ +__initfunc(void + inithfcsx(struct IsdnCardState *cs)) +{ + cs->setstack_d = setstack_hfcsx; + cs->dbusytimer.function = (void *) hfcsx_dbusy_timer; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->tqueue.routine = (void *) (void *) hfcsx_bh; + cs->BC_Send_Data = &hfcsx_send_data; + cs->bcs[0].BC_SetStack = setstack_2b; + cs->bcs[1].BC_SetStack = setstack_2b; + cs->bcs[0].BC_Close = close_hfcsx; + cs->bcs[1].BC_Close = close_hfcsx; + mode_hfcsx(cs->bcs, 0, 0); + mode_hfcsx(cs->bcs + 1, 0, 1); +} + + + +/*******************************************/ +/* handle card messages from control layer */ +/*******************************************/ +static int +hfcsx_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCSX: card_msg %x", mt); + switch (mt) { + case CARD_RESET: + reset_hfcsx(cs); + return (0); + case CARD_RELEASE: + release_io_hfcsx(cs); + return (0); + case CARD_INIT: + inithfcsx(cs); + save_flags(flags); + sti(); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ + /* now switch timer interrupt off */ + cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER; + Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1); + /* reinit mode reg */ + Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m); + restore_flags(flags); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + + + +__initfunc(int + setup_hfcsx(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + int flags; + + strcpy(tmp, hfcsx_revision); + printk(KERN_INFO "HiSax: HFC-SX driver Rev. %s\n", HiSax_getrev(tmp)); + cs->hw.hfcsx.base = card->para[1] & 0xfffe; + cs->irq = card->para[0]; + cs->hw.hfcsx.int_s1 = 0; + cs->dc.hfcsx.ph_state = 0; + cs->hw.hfcsx.fifo = 255; + if (cs->typ == ISDN_CTYPE_HFC_SX) { + if ((!cs->hw.hfcsx.base) || + check_region((cs->hw.hfcsx.base), 2)) { + printk(KERN_WARNING + "HiSax: HFC-SX io-base 0x%x already in use\n", + cs->hw.hfcsx.base); + return(0); + } else { + request_region(cs->hw.hfcsx.base, 2, "HFCSX isdn"); + } + byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.base & 0xFF); + byteout(cs->hw.hfcsx.base + 1, + ((cs->hw.hfcsx.base >> 8) & 3) | 0x54); + udelay(10); + cs->hw.hfcsx.chip = Read_hfc(cs,HFCSX_CHIP_ID); + switch (cs->hw.hfcsx.chip >> 4) { + case 1: + tmp[0] ='+'; + break; + case 9: + tmp[0] ='P'; + break; + default: + printk(KERN_WARNING + "HFC-SX: invalid chip id 0x%x\n", + cs->hw.hfcsx.chip >> 4); + release_region(cs->hw.hfcsx.base, 2); + return(0); + } + if (!ccd_sp_irqtab[cs->irq & 0xF]) { + printk(KERN_WARNING + "HFC_SX: invalid irq %d specified\n",cs->irq & 0xF); + release_region(cs->hw.hfcsx.base, 2); + return(0); + } + save_flags(flags); + cli(); + if (!(cs->hw.hfcsx.extra = (void *) + kmalloc(sizeof(struct hfcsx_extra), GFP_ATOMIC))) { + restore_flags(flags); + release_region(cs->hw.hfcsx.base, 2); + printk(KERN_WARNING "HFC-SX: unable to allocate memory\n"); + return(0); + } + restore_flags(flags); + + printk(KERN_INFO + "HFC-S%c chip detected at base 0x%x IRQ %d HZ %d\n", + tmp[0], (u_int) cs->hw.hfcsx.base, + cs->irq, HZ); + cs->hw.hfcsx.int_m2 = 0; /* disable alle interrupts */ + cs->hw.hfcsx.int_m1 = 0; + Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1); + Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2); + } 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 = &hfcsx_interrupt; + + cs->hw.hfcsx.timer.function = (void *) hfcsx_Timer; + cs->hw.hfcsx.timer.data = (long) cs; + cs->hw.hfcsx.b_fifo_size = 0; /* fifo size still unknown */ + cs->hw.hfcsx.cirm = ccd_sp_irqtab[cs->irq & 0xF]; /* RAM not evaluated */ + init_timer(&cs->hw.hfcsx.timer); + + reset_hfcsx(cs); + cs->cardmsg = &hfcsx_card_msg; + cs->auxcmd = &hfcsx_auxcmd; + return (1); +} + + + + diff --git a/drivers/isdn/hisax/hfc_sx.h b/drivers/isdn/hisax/hfc_sx.h new file mode 100644 index 000000000..ebb8d3e7d --- /dev/null +++ b/drivers/isdn/hisax/hfc_sx.h @@ -0,0 +1,216 @@ +/* $Id: hfc_sx.h,v 1.1 1999/11/18 00:09:18 werner Exp $ + + * specific defines for CCD's HFC 2BDS0 S+,SP 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_sx.h,v $ + * Revision 1.1 1999/11/18 00:09:18 werner + * + * Initial release of files for HFC-S+ and HFC-SP cards with 32K-RAM. + * Audio and Echo are supported. + * + * + * + */ + +/*********************************************/ +/* thresholds for transparent B-channel mode */ +/* change mask and threshold simultaneously */ +/*********************************************/ +#define HFCSX_BTRANS_THRESHOLD 128 +#define HFCSX_BTRANS_THRESMASK 0x00 + +/* GCI/IOM bus monitor registers */ + +#define HFCSX_C_I 0x02 +#define HFCSX_TRxR 0x03 +#define HFCSX_MON1_D 0x0A +#define HFCSX_MON2_D 0x0B + + +/* GCI/IOM bus timeslot registers */ + +#define HFCSX_B1_SSL 0x20 +#define HFCSX_B2_SSL 0x21 +#define HFCSX_AUX1_SSL 0x22 +#define HFCSX_AUX2_SSL 0x23 +#define HFCSX_B1_RSL 0x24 +#define HFCSX_B2_RSL 0x25 +#define HFCSX_AUX1_RSL 0x26 +#define HFCSX_AUX2_RSL 0x27 + +/* GCI/IOM bus data registers */ + +#define HFCSX_B1_D 0x28 +#define HFCSX_B2_D 0x29 +#define HFCSX_AUX1_D 0x2A +#define HFCSX_AUX2_D 0x2B + +/* GCI/IOM bus configuration registers */ + +#define HFCSX_MST_EMOD 0x2D +#define HFCSX_MST_MODE 0x2E +#define HFCSX_CONNECT 0x2F + + +/* Interrupt and status registers */ + +#define HFCSX_TRM 0x12 +#define HFCSX_B_MODE 0x13 +#define HFCSX_CHIP_ID 0x16 +#define HFCSX_CIRM 0x18 +#define HFCSX_CTMT 0x19 +#define HFCSX_INT_M1 0x1A +#define HFCSX_INT_M2 0x1B +#define HFCSX_INT_S1 0x1E +#define HFCSX_INT_S2 0x1F +#define HFCSX_STATUS 0x1C + +/* S/T section registers */ + +#define HFCSX_STATES 0x30 +#define HFCSX_SCTRL 0x31 +#define HFCSX_SCTRL_E 0x32 +#define HFCSX_SCTRL_R 0x33 +#define HFCSX_SQ 0x34 +#define HFCSX_CLKDEL 0x37 +#define HFCSX_B1_REC 0x3C +#define HFCSX_B1_SEND 0x3C +#define HFCSX_B2_REC 0x3D +#define HFCSX_B2_SEND 0x3D +#define HFCSX_D_REC 0x3E +#define HFCSX_D_SEND 0x3E +#define HFCSX_E_REC 0x3F + +/****************/ +/* FIFO section */ +/****************/ +#define HFCSX_FIF_SEL 0x10 +#define HFCSX_FIF_Z1L 0x80 +#define HFCSX_FIF_Z1H 0x84 +#define HFCSX_FIF_Z2L 0x88 +#define HFCSX_FIF_Z2H 0x8C +#define HFCSX_FIF_INCF1 0xA8 +#define HFCSX_FIF_DWR 0xAC +#define HFCSX_FIF_F1 0xB0 +#define HFCSX_FIF_F2 0xB4 +#define HFCSX_FIF_INCF2 0xB8 +#define HFCSX_FIF_DRD 0xBC + +/* bits in status register (READ) */ +#define HFCSX_SX_PROC 0x02 +#define HFCSX_NBUSY 0x04 +#define HFCSX_TIMER_ELAP 0x10 +#define HFCSX_STATINT 0x20 +#define HFCSX_FRAMEINT 0x40 +#define HFCSX_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCSX_CLTIMER 0x80 +#define HFCSX_TIM3_125 0x04 +#define HFCSX_TIM25 0x10 +#define HFCSX_TIM50 0x14 +#define HFCSX_TIM400 0x18 +#define HFCSX_TIM800 0x1C +#define HFCSX_AUTO_TIMER 0x20 +#define HFCSX_TRANSB2 0x02 +#define HFCSX_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCSX_IRQ_SELMSK 0x07 +#define HFCSX_IRQ_SELDIS 0x00 +#define HFCSX_RESET 0x08 +#define HFCSX_FIFO_RESET 0x80 + + +/* bits in INT_M1 and INT_S1 */ +#define HFCSX_INTS_B1TRANS 0x01 +#define HFCSX_INTS_B2TRANS 0x02 +#define HFCSX_INTS_DTRANS 0x04 +#define HFCSX_INTS_B1REC 0x08 +#define HFCSX_INTS_B2REC 0x10 +#define HFCSX_INTS_DREC 0x20 +#define HFCSX_INTS_L1STATE 0x40 +#define HFCSX_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCSX_PROC_TRANS 0x01 +#define HFCSX_GCI_I_CHG 0x02 +#define HFCSX_GCI_MON_REC 0x04 +#define HFCSX_IRQ_ENABLE 0x08 + +/* bits in STATES */ +#define HFCSX_STATE_MSK 0x0F +#define HFCSX_LOAD_STATE 0x10 +#define HFCSX_ACTIVATE 0x20 +#define HFCSX_DO_ACTION 0x40 +#define HFCSX_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCSX_MASTER 0x01 +#define HFCSX_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 HFCSX_AUTO_AWAKE 0x01 +#define HFCSX_DBIT_1 0x04 +#define HFCSX_IGNORE_COL 0x08 +#define HFCSX_CHG_B1_B2 0x80 + +/**********************************/ +/* definitions for FIFO selection */ +/**********************************/ +#define HFCSX_SEL_D_RX 5 +#define HFCSX_SEL_D_TX 4 +#define HFCSX_SEL_B1_RX 1 +#define HFCSX_SEL_B1_TX 0 +#define HFCSX_SEL_B2_RX 3 +#define HFCSX_SEL_B2_TX 2 + +#define MAX_D_FRAMES 15 +#define MAX_B_FRAMES 31 +#define B_SUB_VAL_32K 0x0200 +#define B_FIFO_SIZE_32K (0x2000 - B_SUB_VAL_32K) +#define B_SUB_VAL_8K 0x1A00 +#define B_FIFO_SIZE_8K (0x2000 - B_SUB_VAL_8K) +#define D_FIFO_SIZE 512 +#define D_FREG_MASK 0xF + +/************************************************************/ +/* structure holding additional dynamic data -> send marker */ +/************************************************************/ +struct hfcsx_extra { + unsigned short marker[2*(MAX_B_FRAMES+1) + (MAX_D_FRAMES+1)]; +}; + +extern void main_irq_hfcsx(struct BCState *bcs); +extern void inithfcsx(struct IsdnCardState *cs); +extern void releasehfcsx(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c index 8654857fd..7015f4bb5 100644 --- a/drivers/isdn/hisax/hfcscard.c +++ b/drivers/isdn/hisax/hfcscard.c @@ -1,4 +1,4 @@ -/* $Id: hfcscard.c,v 1.5 1999/09/04 06:20:06 keil Exp $ +/* $Id: hfcscard.c,v 1.6 1999/12/19 13:09:42 keil Exp $ * hfcscard.c low level stuff for hfcs based cards (Teles3c, ACER P10) * @@ -6,6 +6,10 @@ * * * $Log: hfcscard.c,v $ + * Revision 1.6 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.5 1999/09/04 06:20:06 keil * Changes from kernel set_current_state() * @@ -30,7 +34,7 @@ extern const char *CardType[]; -static const char *hfcs_revision = "$Revision: 1.5 $"; +static const char *hfcs_revision = "$Revision: 1.6 $"; static void hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs) @@ -85,13 +89,13 @@ reset_hfcs(struct IsdnCardState *cs) cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */ save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); 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 */ - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); if (cs->typ == ISDN_CTYPE_TELES3C) cs->hw.hfcD.cirm |= HFCD_INTB; @@ -138,7 +142,7 @@ hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg) init2bds0(cs); save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((80*HZ)/1000); cs->hw.hfcD.ctmt |= HFCD_TIM800; cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index 0c67d5c4a..6f5b5615c 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,8 +1,16 @@ -/* $Id: hisax.h,v 2.38 1999/11/14 23:37:03 keil Exp $ +/* $Id: hisax.h,v 2.40 2000/01/20 19:51:46 keil Exp $ * Basic declarations, defines and prototypes * * $Log: hisax.h,v $ + * Revision 2.40 2000/01/20 19:51:46 keil + * Fix AddTimer message + * Change CONFIG defines + * + * Revision 2.39 1999/11/18 00:00:43 werner + * + * Added support for HFC-S+ and HFC-SP cards + * * Revision 2.38 1999/11/14 23:37:03 keil * new ISA memory mapped IO * @@ -784,6 +792,31 @@ struct hfcPCI_hw { struct timer_list timer; }; +struct hfcSX_hw { + unsigned int base; + 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 bswapped; + unsigned char nt_mode; + unsigned char chip; + int b_fifo_size; + unsigned char last_fifo; + void *extra; + int nt_timer; + struct timer_list timer; +}; + struct hfcD_hw { unsigned int addr; unsigned int bfifosize; @@ -894,6 +927,10 @@ struct hfcpci_chip { int ph_state; }; +struct hfcsx_chip { + int ph_state; +}; + struct w6692_chip { int ph_state; }; @@ -934,6 +971,7 @@ struct IsdnCardState { struct njet_hw njet; struct hfcD_hw hfcD; struct hfcPCI_hw hfcpci; + struct hfcSX_hw hfcsx; struct ix1_hw niccy; struct isurf_hw isurf; struct saphir_hw saphir; @@ -973,6 +1011,7 @@ struct IsdnCardState { struct isac_chip isac; struct hfcd_chip hfcd; struct hfcpci_chip hfcpci; + struct hfcsx_chip hfcsx; struct w6692_chip w6692; } dc; u_char *rcvbuf; @@ -1032,7 +1071,8 @@ struct IsdnCardState { #define ISDN_CTYPE_GAZEL 34 #define ISDN_CTYPE_HFC_PCI 35 #define ISDN_CTYPE_W6692 36 -#define ISDN_CTYPE_COUNT 36 +#define ISDN_CTYPE_HFC_SX 37 +#define ISDN_CTYPE_COUNT 37 #ifdef ISDN_CHIP_ISAC @@ -1201,6 +1241,12 @@ struct IsdnCardState { #define CARD_HFC_PCI 0 #endif +#ifdef CONFIG_HISAX_HFC_SX +#define CARD_HFC_SX 1 +#else +#define CARD_HFC_SX 0 +#endif + #ifdef CONFIG_HISAX_AMD7930 #define CARD_AMD7930 1 #else @@ -1298,19 +1344,6 @@ struct IsdnCardState { #ifdef CONFIG_HISAX_EURO #undef TEI_PER_CARD #define TEI_PER_CARD 1 -#define HISAX_EURO_SENDCOMPLETE 1 -#define EXT_BEARER_CAPS 1 -#define HISAX_SEND_STD_LLC_IE 1 -#ifdef CONFIG_HISAX_NO_SENDCOMPLETE -#undef HISAX_EURO_SENDCOMPLETE -#endif -#ifdef CONFIG_HISAX_NO_LLC -#undef HISAX_SEND_STD_LLC_IE -#endif -#undef HISAX_DE_AOC -#ifdef CONFIG_DE_AOC -#define HISAX_DE_AOC 1 -#endif #endif /* L1 Debug */ diff --git a/drivers/isdn/hisax/hscx.h b/drivers/isdn/hisax/hscx.h index 08801bc73..00b566a44 100644 --- a/drivers/isdn/hisax/hscx.h +++ b/drivers/isdn/hisax/hscx.h @@ -1,11 +1,14 @@ -/* $Id: hscx.h,v 1.4 1998/04/15 16:45:34 keil Exp $ +/* $Id: hscx.h,v 1.5 1999/12/23 15:09:32 keil Exp $ * hscx.h HSCX specific defines * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: hscx.h,v $ + * Revision 1.5 1999/12/23 15:09:32 keil + * change email + * * Revision 1.4 1998/04/15 16:45:34 keil * new init code * diff --git a/drivers/isdn/hisax/ipac.h b/drivers/isdn/hisax/ipac.h index 82c5fa8f8..34a85dd6b 100644 --- a/drivers/isdn/hisax/ipac.h +++ b/drivers/isdn/hisax/ipac.h @@ -1,11 +1,14 @@ -/* $Id: ipac.h,v 1.3 1998/04/15 16:48:09 keil Exp $ +/* $Id: ipac.h,v 1.4 1999/12/23 15:09:32 keil Exp $ * ipac.h IPAC specific defines * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: ipac.h,v $ + * Revision 1.4 1999/12/23 15:09:32 keil + * change email + * * Revision 1.3 1998/04/15 16:48:09 keil * IPAC_ATX added * diff --git a/drivers/isdn/hisax/isac.h b/drivers/isdn/hisax/isac.h index bed887d4f..a8a46e135 100644 --- a/drivers/isdn/hisax/isac.h +++ b/drivers/isdn/hisax/isac.h @@ -1,11 +1,14 @@ -/* $Id: isac.h,v 1.5 1998/05/25 12:58:03 keil Exp $ +/* $Id: isac.h,v 1.6 1999/12/23 15:09:32 keil Exp $ * isac.h ISAC specific defines * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: isac.h,v $ + * Revision 1.6 1999/12/23 15:09:32 keil + * change email + * * Revision 1.5 1998/05/25 12:58:03 keil * HiSax golden code from certification, Don't use !!! * No leased lines, no X75, but many changes. diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c index 417f2157d..bfff86707 100644 --- a/drivers/isdn/hisax/isar.c +++ b/drivers/isdn/hisax/isar.c @@ -1,4 +1,4 @@ -/* $Id: isar.c,v 1.7 1999/10/14 20:25:29 keil Exp $ +/* $Id: isar.c,v 1.9 2000/01/20 19:47:45 keil Exp $ * isar.c ISAR (Siemens PSB 7110) specific routines * @@ -6,6 +6,12 @@ * * * $Log: isar.c,v $ + * Revision 1.9 2000/01/20 19:47:45 keil + * Add Fax Class 1 support + * + * Revision 1.8 1999/12/19 13:00:56 keil + * Fix races in setting a new mode + * * Revision 1.7 1999/10/14 20:25:29 keil * add a statistic for error monitoring * @@ -42,7 +48,17 @@ #define MIN(a,b) ((a<b)?a:b) +#define DLE 0x10 +#define ETX 0x03 + + +const u_char faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146"; +const u_char faxmodulation[] = {3,24,48,72,73,74,96,97,98,121,122,145,146}; +#define FAXMODCNT 13 + void isar_setup(struct IsdnCardState *cs); +static void isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para); +static inline void ll_deliver_faxstat(struct BCState *bcs, u_char status); static inline int waitforHIA(struct IsdnCardState *cs, int timeout) @@ -432,6 +448,12 @@ static void isar_bh(struct BCState *bcs) { BChannel_bh(bcs); + if (test_and_clear_bit(B_LL_NOCARRIER, &bcs->event)) + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_NOCARR); + if (test_and_clear_bit(B_LL_CONNECT, &bcs->event)) + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + if (test_and_clear_bit(B_LL_OK, &bcs->event)) + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_OK); } static void @@ -443,6 +465,42 @@ isar_sched_event(struct BCState *bcs, int event) } static inline void +send_DLE_ETX(struct BCState *bcs) +{ + u_char dleetx[2] = {DLE,ETX}; + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(2))) { + memcpy(skb_put(skb, 2), dleetx, 2); + skb_queue_tail(&bcs->rqueue, skb); + isar_sched_event(bcs, B_RCVBUFREADY); + } else { + printk(KERN_WARNING "HiSax: skb out of memory\n"); + } +} + +static inline int +dle_count(unsigned char *buf, int len) +{ + int count = 0; + + while (len--) + if (*buf++ == DLE) + count++; + return count; +} + +static inline void +insert_dle(unsigned char *dest, unsigned char *src, int count) { + /* <DLE> in input stream have to be flagged as <DLE><DLE> */ + while (count--) { + *dest++ = *src; + if (*src++ == DLE) + *dest++ = DLE; + } +} + +static inline void isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) { u_char *ptr; @@ -512,6 +570,88 @@ isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) } } break; + case L1_MODE_FAX: + if (bcs->hw.isar.state != STFAX_ACTIV) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: not ACTIV"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + break; + } + if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) { + rcv_mbox(cs, ireg, bcs->hw.isar.rcvbuf); + bcs->hw.isar.rcvidx = ireg->clsb + + dle_count(bcs->hw.isar.rcvbuf, ireg->clsb); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar_rcv_frame: raw(%d) dle(%d)", + ireg->clsb, bcs->hw.isar.rcvidx); + if ((skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) { + insert_dle((u_char *)skb_put(skb, bcs->hw.isar.rcvidx), + bcs->hw.isar.rcvbuf, ireg->clsb); + skb_queue_tail(&bcs->rqueue, skb); + isar_sched_event(bcs, B_RCVBUFREADY); + if (ireg->cmsb & SART_NMD) { /* ABORT */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: no more data"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + send_DLE_ETX(bcs); + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | + ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, + 0, NULL); + bcs->hw.isar.state = STFAX_ESCAPE; + isar_sched_event(bcs, B_LL_NOCARRIER); + } + } else { + printk(KERN_WARNING "HiSax: skb out of memory\n"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + } + if (bcs->hw.isar.cmd != PCTRL_CMD_FRH) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: unknown fax mode %x", + bcs->hw.isar.cmd); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + break; + } + /* PCTRL_CMD_FRH */ + if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: incoming packet too large"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + } else if (ireg->cmsb & HDLC_ERROR) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar frame error %x len %d", + ireg->cmsb, ireg->clsb); + bcs->hw.isar.rcvidx = 0; + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } else { + if (ireg->cmsb & HDLC_FSD) + bcs->hw.isar.rcvidx = 0; + ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx; + bcs->hw.isar.rcvidx += ireg->clsb; + rcv_mbox(cs, ireg, ptr); + if (ireg->cmsb & HDLC_FED) { + 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))) { + printk(KERN_WARNING "ISAR: receive out of memory\n"); + } else { + memcpy(skb_put(skb, bcs->hw.isar.rcvidx), + bcs->hw.isar.rcvbuf, + bcs->hw.isar.rcvidx); + skb_queue_tail(&bcs->rqueue, skb); + isar_sched_event(bcs, B_RCVBUFREADY); + send_DLE_ETX(bcs); + isar_sched_event(bcs, B_LL_OK); + } + } + } + break; default: printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode); cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); @@ -544,38 +684,57 @@ isar_fill_fifo(struct BCState *bcs) count = bcs->tx_skb->len; msb = HDLC_FED; } - if (!bcs->hw.isar.txcnt) - msb |= HDLC_FST; save_flags(flags); cli(); ptr = bcs->tx_skb->data; + if (!bcs->hw.isar.txcnt) { + msb |= HDLC_FST; + if ((bcs->mode == L1_MODE_FAX) && + (bcs->hw.isar.cmd == PCTRL_CMD_FTH)) { + if (bcs->tx_skb->len > 1) { + if ((ptr[0]== 0xff) && (ptr[1] == 0x13)) + /* last frame */ + test_and_set_bit(BC_FLG_LASTDATA, + &bcs->Flag); + } + } + } skb_pull(bcs->tx_skb, count); bcs->tx_cnt -= count; bcs->hw.isar.txcnt += count; switch (bcs->mode) { - case L1_MODE_NULL: - 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) - debugl1(cs, "isar bin data send dp%d failed", - bcs->hw.isar.dpath); - } - break; - case L1_MODE_HDLC: - if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, - msb, count, ptr)) { + case L1_MODE_NULL: + printk(KERN_ERR"isar_fill_fifo wrong mode 0\n"); + break; + case L1_MODE_TRANS: + case L1_MODE_V32: + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + 0, count, ptr); + break; + case L1_MODE_HDLC: + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + msb, count, ptr); + break; + case L1_MODE_FAX: + if (bcs->hw.isar.state != STFAX_ACTIV) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_fill_fifo: not ACTIV"); + } else if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + msb, count, ptr); + } else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) { + sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + 0, count, ptr); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_fill_fifo: not FTH/FTM"); + } + break; + default: if (cs->debug) - debugl1(cs, "isar hdlc data send dp%d failed", - bcs->hw.isar.dpath); - } - break; - default: - printk(KERN_ERR"isar_fill_fifo mode (%x)error\n", bcs->mode); - break; + debugl1(cs, "isar_fill_fifo mode(%x) error", bcs->mode); + printk(KERN_ERR"isar_fill_fifo mode(%x) error\n", bcs->mode); + break; } restore_flags(flags); } @@ -603,6 +762,18 @@ 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); + if (bcs->mode == L1_MODE_FAX) { + if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { + if (test_bit(BC_FLG_LASTDATA, &bcs->Flag)) { + test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag); + } + } else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) { + if (test_bit(BC_FLG_DLEETX, &bcs->Flag)) { + test_and_set_bit(BC_FLG_LASTDATA, &bcs->Flag); + test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag); + } + } + } dev_kfree_skb(bcs->tx_skb); bcs->hw.isar.txcnt = 0; bcs->tx_skb = NULL; @@ -613,6 +784,18 @@ send_frames(struct BCState *bcs) test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); isar_fill_fifo(bcs); } else { + if (test_and_clear_bit(BC_FLG_DLEETX, &bcs->Flag)) { + if (test_and_clear_bit(BC_FLG_LASTDATA, &bcs->Flag)) { + if (test_and_clear_bit(BC_FLG_NMD_DATA, &bcs->Flag)) { + u_char dummy = 0; + sendmsg(bcs->cs, SET_DPS(bcs->hw.isar.dpath) | + ISAR_HIS_SDATA, 0x01, 1, &dummy); + } + test_and_set_bit(BC_FLG_LL_OK, &bcs->Flag); + } else { + isar_sched_event(bcs, B_LL_CONNECT); + } + } test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); isar_sched_event(bcs, B_XMTBUFREADY); } @@ -639,6 +822,7 @@ 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"}; @@ -700,7 +884,7 @@ isar_pump_status_rsp(struct BCState *bcs, struct isar_reg *ireg) { } static void -isar_pump_status_ev(struct BCState *bcs, u_char devt) { +isar_pump_statev_modem(struct BCState *bcs, u_char devt) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); @@ -769,6 +953,192 @@ isar_pump_status_ev(struct BCState *bcs, u_char devt) { } } +static inline void +ll_deliver_faxstat(struct BCState *bcs, u_char status) +{ + isdn_ctrl ic; + struct Channel *chanp = (struct Channel *) bcs->st->lli.userdata; + + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "HL->LL FAXIND %x", status); + ic.driver = bcs->cs->myid; + ic.command = ISDN_STAT_FAXIND; + ic.arg = chanp->chan; + ic.parm.aux.cmd = status; + bcs->cs->iif.statcallb(&ic); +} + +static void +isar_pump_statev_fax(struct BCState *bcs, u_char devt) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char p1; + + switch(devt) { + case PSEV_10MS_TIMER: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev TIMER"); + break; + case PSEV_RSP_READY: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_READY"); + bcs->hw.isar.state = STFAX_READY; + l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL); + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FRH, 3); + } else { + isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FTH, 3); + } + break; + case PSEV_LINE_TX_H: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev LINE_TX_H"); + bcs->hw.isar.state = STFAX_CONT; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev LINE_TX_H wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_LINE_RX_H: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev LINE_RX_H"); + bcs->hw.isar.state = STFAX_CONT; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev LINE_RX_H wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_LINE_TX_B: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev LINE_TX_B"); + bcs->hw.isar.state = STFAX_CONT; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev LINE_TX_B wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_LINE_RX_B: + if (bcs->hw.isar.state == STFAX_LINE) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev LINE_RX_B"); + bcs->hw.isar.state = STFAX_CONT; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev LINE_RX_B wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_RSP_CONN: + if (bcs->hw.isar.state == STFAX_CONT) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_CONN"); + bcs->hw.isar.state = STFAX_ACTIV; + test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { + /* 1s Flags before data */ + if (test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag)) + del_timer(&bcs->hw.isar.ftimer); + /* 1000 ms */ + bcs->hw.isar.ftimer.expires = + jiffies + ((1000 * HZ)/1000); + test_and_set_bit(BC_FLG_LL_CONN, + &bcs->Flag); + add_timer(&bcs->hw.isar.ftimer); + } else { + isar_sched_event(bcs, B_LL_CONNECT); + } + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "pump stev RSP_CONN wrong st %x", + bcs->hw.isar.state); + } + break; + case PSEV_FLAGS_DET: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev FLAGS_DET"); + break; + case PSEV_RSP_DISC: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_DISC"); + if (bcs->hw.isar.state == STFAX_ESCAPE) { + switch(bcs->hw.isar.newcmd) { + case PCTRL_CMD_FTH: + case PCTRL_CMD_FTM: + p1 = 10; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, + PCTRL_CMD_SILON, 1, &p1); + bcs->hw.isar.state = STFAX_SILDET; + break; + case PCTRL_CMD_FRH: + case PCTRL_CMD_FRM: + p1 = bcs->hw.isar.newmod; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.cmd = bcs->hw.isar.newcmd; + bcs->hw.isar.newcmd = 0; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, + bcs->hw.isar.cmd, 1, &p1); + bcs->hw.isar.state = STFAX_LINE; + break; + default: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "RSP_DISC unknown newcmd %x", bcs->hw.isar.newcmd); + break; + } + } else if (bcs->hw.isar.state == STFAX_ACTIV) { + if (test_and_clear_bit(BC_FLG_LL_OK, &bcs->Flag)) { + isar_sched_event(bcs, B_LL_OK); + } else if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) { + send_DLE_ETX(bcs); + isar_sched_event(bcs, B_LL_NOCARRIER); + } else { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR); + } + bcs->hw.isar.state = STFAX_READY; + } else { + bcs->hw.isar.state = STFAX_READY; + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR); + } + break; + case PSEV_RSP_SILDET: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_SILDET"); + if (bcs->hw.isar.state == STFAX_SILDET) { + p1 = bcs->hw.isar.newmod; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.cmd = bcs->hw.isar.newcmd; + bcs->hw.isar.newcmd = 0; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, + bcs->hw.isar.cmd, 1, &p1); + bcs->hw.isar.state = STFAX_LINE; + } + break; + case PSEV_RSP_SILOFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_SILOFF"); + break; + case PSEV_RSP_FCERR: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev RSP_FCERR"); + bcs->hw.isar.state = STFAX_ESCAPE; + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR); + break; + default: + break; + } +} + static char debbuf[128]; void @@ -788,8 +1158,6 @@ isar_int_main(struct IsdnCardState *cs) } else { debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x", ireg->iis, ireg->cmsb, ireg->clsb); - printk(KERN_WARNING"isar spurious IIS_RDATA %x/%x/%x\n", - ireg->iis, ireg->cmsb, ireg->clsb); cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); } break; @@ -815,12 +1183,18 @@ isar_int_main(struct IsdnCardState *cs) 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); + if (bcs->mode == L1_MODE_V32) { + isar_pump_statev_modem(bcs, ireg->cmsb); + } else if (bcs->mode == L1_MODE_FAX) { + isar_pump_statev_fax(bcs, ireg->cmsb); + } else { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar IIS_PSTEV pmode %d stat %x", + bcs->mode, 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; @@ -831,8 +1205,6 @@ isar_int_main(struct IsdnCardState *cs) } 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; @@ -867,6 +1239,17 @@ isar_int_main(struct IsdnCardState *cs) } static void +ftimer_handler(struct BCState *bcs) { + if (bcs->cs->debug) + debugl1(bcs->cs, "ftimer flags %04x", + bcs->Flag); + test_and_clear_bit(BC_FLG_FTI_RUN, &bcs->Flag); + if (test_and_clear_bit(BC_FLG_LL_CONN, &bcs->Flag)) { + isar_sched_event(bcs, B_LL_CONNECT); + } +} + +static void setup_pump(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); @@ -876,11 +1259,7 @@ setup_pump(struct BCState *bcs) { case L1_MODE_NULL: case L1_MODE_TRANS: case L1_MODE_HDLC: - if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL)) { - if (cs->debug) - debugl1(cs, "isar pump bypass cfg dp%d failed", - bcs->hw.isar.dpath); - } + sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL); break; case L1_MODE_V32: ctrl = PMOD_DATAMODEM; @@ -896,11 +1275,7 @@ setup_pump(struct BCState *bcs) { 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); - } + sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param); break; case L1_MODE_FAX: ctrl = PMOD_FAX; @@ -911,18 +1286,16 @@ setup_pump(struct BCState *bcs) { 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); - } + sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param); + bcs->hw.isar.state = STFAX_NULL; + bcs->hw.isar.newcmd = 0; + bcs->hw.isar.newmod = 0; + test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag); break; } - if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) { - if (cs->debug) - debugl1(cs, "isar pump status req dp%d failed", - bcs->hw.isar.dpath); - } + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + udelay(1000); } static void @@ -933,44 +1306,30 @@ setup_sart(struct BCState *bcs) { switch (bcs->mode) { case L1_MODE_NULL: - if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL)) { - if (cs->debug) - debugl1(cs, "isar sart disable dp%d failed", - bcs->hw.isar.dpath); - } + sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, + NULL); break; case L1_MODE_TRANS: - if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0")) { - if (cs->debug) - debugl1(cs, "isar sart binary dp%d failed", - bcs->hw.isar.dpath); - } + sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, + "\0\0"); break; case L1_MODE_HDLC: 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 hdlc dp%d failed", - bcs->hw.isar.dpath); - } + sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, + param); 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); - } + sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2, + param); break; } - if (!sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL)) { - if (cs->debug) - debugl1(cs, "isar buf stat req dp%d failed", - bcs->hw.isar.dpath); - } + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL); + udelay(1000); } static void @@ -995,15 +1354,10 @@ setup_iom2(struct BCState *bcs) { cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV; break; } - if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) { - if (cs->debug) - debugl1(cs, "isar iom2 dp%d failed", bcs->hw.isar.dpath); - } - if (!sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL)) { - if (cs->debug) - debugl1(cs, "isar IOM2 cfg req dp%d failed", - bcs->hw.isar.dpath); - } + sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg); + udelay(1000); + sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL); + udelay(1000); } int @@ -1041,8 +1395,8 @@ modeisar(struct BCState *bcs, int mode, int bc) &bcs->hw.isar.reg->Flags)) bcs->hw.isar.dpath = 1; else { - printk(KERN_WARNING"isar modeisar analog works only with DP1\n"); - debugl1(cs, "isar modeisar analog works only with DP1"); + printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n"); + debugl1(cs, "isar modeisar analog funktions only with DP1"); return(1); } break; @@ -1066,6 +1420,107 @@ modeisar(struct BCState *bcs, int mode, int bc) return(0); } +static void +isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) +{ + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char ctrl = 0, nom = 0, p1 = 0; + + switch(cmd) { + case ISDN_FAX_CLASS1_FTM: + if (bcs->hw.isar.state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FTM; + nom = 1; + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.cmd = ctrl; + bcs->hw.isar.mod = para; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.newcmd = 0; + } else if ((bcs->hw.isar.state == STFAX_ACTIV) && + (bcs->hw.isar.cmd == PCTRL_CMD_FTM) && + (bcs->hw.isar.mod == para)) { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + } else { + bcs->hw.isar.newmod = para; + bcs->hw.isar.newcmd = PCTRL_CMD_FTM; + nom = 0; + ctrl = PCTRL_CMD_ESC; + bcs->hw.isar.state = STFAX_ESCAPE; + } + break; + case ISDN_FAX_CLASS1_FTH: + if (bcs->hw.isar.state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FTH; + nom = 1; + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.cmd = ctrl; + bcs->hw.isar.mod = para; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.newcmd = 0; + } else if ((bcs->hw.isar.state == STFAX_ACTIV) && + (bcs->hw.isar.cmd == PCTRL_CMD_FTH) && + (bcs->hw.isar.mod == para)) { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + } else { + bcs->hw.isar.newmod = para; + bcs->hw.isar.newcmd = PCTRL_CMD_FTH; + nom = 0; + ctrl = PCTRL_CMD_ESC; + bcs->hw.isar.state = STFAX_ESCAPE; + } + break; + case ISDN_FAX_CLASS1_FRM: + if (bcs->hw.isar.state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FRM; + nom = 1; + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.cmd = ctrl; + bcs->hw.isar.mod = para; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.newcmd = 0; + } else if ((bcs->hw.isar.state == STFAX_ACTIV) && + (bcs->hw.isar.cmd == PCTRL_CMD_FRM) && + (bcs->hw.isar.mod == para)) { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + } else { + bcs->hw.isar.newmod = para; + bcs->hw.isar.newcmd = PCTRL_CMD_FRM; + nom = 0; + ctrl = PCTRL_CMD_ESC; + bcs->hw.isar.state = STFAX_ESCAPE; + } + break; + case ISDN_FAX_CLASS1_FRH: + if (bcs->hw.isar.state == STFAX_READY) { + p1 = para; + ctrl = PCTRL_CMD_FRH; + nom = 1; + bcs->hw.isar.state = STFAX_LINE; + bcs->hw.isar.cmd = ctrl; + bcs->hw.isar.mod = para; + bcs->hw.isar.newmod = 0; + bcs->hw.isar.newcmd = 0; + } else if ((bcs->hw.isar.state == STFAX_ACTIV) && + (bcs->hw.isar.cmd == PCTRL_CMD_FRH) && + (bcs->hw.isar.mod == para)) { + ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT); + } else { + bcs->hw.isar.newmod = para; + bcs->hw.isar.newcmd = PCTRL_CMD_FRH; + nom = 0; + ctrl = PCTRL_CMD_ESC; + bcs->hw.isar.state = STFAX_ESCAPE; + } + break; + } + if (ctrl) + sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1); +} + void isar_setup(struct IsdnCardState *cs) { @@ -1076,11 +1531,8 @@ isar_setup(struct IsdnCardState *cs) msg = 61; for (i=0; i<2; i++) { /* Buffer Config */ - if (!sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | - ISAR_HIS_P12CFG, 4, 1, &msg)) { - if (cs->debug) - debugl1(cs, "isar P%dCFG failed", i+1); - } + sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | + ISAR_HIS_P12CFG, 4, 1, &msg); cs->bcs[i].hw.isar.mml = msg; cs->bcs[i].mode = 0; cs->bcs[i].hw.isar.dpath = i + 1; @@ -1147,6 +1599,7 @@ isar_l2l1(struct PStack *st, int pr, void *arg) l1_msg_b(st, PH_ACTIVATE | REQUEST, arg); break; case L1_MODE_V32: + case L1_MODE_FAX: if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc)) l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); break; @@ -1185,6 +1638,7 @@ close_isarstate(struct BCState *bcs) debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY"); } } + del_timer(&bcs->hw.isar.ftimer); } int @@ -1206,6 +1660,9 @@ open_isarstate(struct IsdnCardState *cs, struct BCState *bcs) bcs->event = 0; bcs->hw.isar.rcvidx = 0; bcs->tx_cnt = 0; + bcs->hw.isar.ftimer.function = (void *) ftimer_handler; + bcs->hw.isar.ftimer.data = (long) bcs; + init_timer(&bcs->hw.isar.ftimer); return (0); } @@ -1226,15 +1683,66 @@ setstack_isar(struct PStack *st, struct BCState *bcs) int isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) { u_long adr; - int features; + int features, i; + struct BCState *bcs; if (cs->debug & L1_DEB_HSCX) debugl1(cs, "isar_auxcmd cmd/ch %x/%d", ic->command, ic->arg); switch (ic->command) { + case (ISDN_CMD_FAXCMD): + bcs = cs->channel[ic->arg].bcs; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar_auxcmd cmd/subcmd %d/%d", + ic->parm.aux.cmd, ic->parm.aux.subcmd); + switch(ic->parm.aux.cmd) { + case ISDN_FAX_CLASS1_CTRL: + if (ic->parm.aux.subcmd == ETX) + test_and_set_bit(BC_FLG_DLEETX, + &bcs->Flag); + break; + case ISDN_FAX_CLASS1_FRM: + case ISDN_FAX_CLASS1_FRH: + case ISDN_FAX_CLASS1_FTM: + case ISDN_FAX_CLASS1_FTH: + if (ic->parm.aux.subcmd == AT_QUERY) { + sprintf(ic->parm.aux.para, + "%d", bcs->hw.isar.mod); + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY; + cs->iif.statcallb(ic); + return(0); + } else if (ic->parm.aux.subcmd == AT_EQ_QUERY) { + strcpy(ic->parm.aux.para, faxmodulation_s); + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY; + cs->iif.statcallb(ic); + return(0); + } else if (ic->parm.aux.subcmd == AT_EQ_VALUE) { + for(i=0;i<FAXMODCNT;i++) + if (faxmodulation[i]==ic->parm.aux.para[0]) + break; + if ((FAXMODCNT > i) && + test_bit(BC_FLG_INIT, &bcs->Flag)) { + isar_pump_cmd(bcs, + ic->parm.aux.cmd, + ic->parm.aux.para[0]); + return(0); + } + } + /* wrong modulation or not activ */ + /* fall through */ + default: + ic->command = ISDN_STAT_FAXIND; + ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR; + cs->iif.statcallb(ic); + } + break; case (ISDN_CMD_IOCTL): switch (ic->arg) { case (9): /* load firmware */ - features = ISDN_FEATURE_L2_MODEM; + features = ISDN_FEATURE_L2_MODEM | + ISDN_FEATURE_L2_FAX | + ISDN_FEATURE_L3_FCLASS1; memcpy(&adr, ic->parm.num, sizeof(ulong)); if (isar_load_firmware(cs, (u_char *)adr)) return(1); diff --git a/drivers/isdn/hisax/isar.h b/drivers/isdn/hisax/isar.h index f2bc4820a..ec3bff89e 100644 --- a/drivers/isdn/hisax/isar.h +++ b/drivers/isdn/hisax/isar.h @@ -1,10 +1,13 @@ -/* $Id: isar.h,v 1.6 1999/10/14 20:25:29 keil Exp $ +/* $Id: isar.h,v 1.7 2000/01/20 19:47:45 keil Exp $ * isar.h ISAR (Siemens PSB 7110) specific defines * * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: isar.h,v $ + * Revision 1.7 2000/01/20 19:47:45 keil + * Add Fax Class 1 support + * * Revision 1.6 1999/10/14 20:25:29 keil * add a statistic for error monitoring * @@ -145,6 +148,28 @@ #define PSEV_REM_REN 0xcd #define PSEV_GSTN_CLR 0xd4 +#define PSEV_RSP_READY 0xbc +#define PSEV_LINE_TX_H 0xb3 +#define PSEV_LINE_TX_B 0xb2 +#define PSEV_LINE_RX_H 0xb1 +#define PSEV_LINE_RX_B 0xb0 +#define PSEV_RSP_CONN 0xb5 +#define PSEV_RSP_DISC 0xb7 +#define PSEV_RSP_FCERR 0xb9 +#define PSEV_RSP_SILDET 0xbe +#define PSEV_RSP_SILOFF 0xab +#define PSEV_FLAGS_DET 0xba + +#define PCTRL_CMD_FTH 0xa7 +#define PCTRL_CMD_FRH 0xa5 +#define PCTRL_CMD_FTM 0xa8 +#define PCTRL_CMD_FRM 0xa6 +#define PCTRL_CMD_SILON 0xac +#define PCTRL_CMD_CONT 0xa2 +#define PCTRL_CMD_ESC 0xa4 +#define PCTRL_CMD_SILOFF 0xab +#define PCTRL_CMD_HALT 0xa9 + #define PCTRL_LOC_RET 0xcf #define PCTRL_LOC_REN 0xce @@ -193,6 +218,15 @@ #define BSTEV_TBO 0x1f #define BSTEV_RBO 0x2f +/* FAX State Machine */ +#define STFAX_NULL 0 +#define STFAX_READY 1 +#define STFAX_LINE 2 +#define STFAX_CONT 3 +#define STFAX_ACTIV 4 +#define STFAX_ESCAPE 5 +#define STFAX_SILDET 6 + extern int ISARVersion(struct IsdnCardState *cs, char *s); extern void isar_int_main(struct IsdnCardState *cs); extern void initisar(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index 768217025..7715991cb 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -1,4 +1,4 @@ -/* $Id: isdnl1.c,v 2.36 1999/08/25 16:50:57 keil Exp $ +/* $Id: isdnl1.c,v 2.37 2000/01/20 19:51:46 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,10 @@ * * * $Log: isdnl1.c,v $ + * Revision 2.37 2000/01/20 19:51:46 keil + * Fix AddTimer message + * Change CONFIG defines + * * Revision 2.36 1999/08/25 16:50:57 keil * Fix bugs which cause 2.3.14 hangs (waitqueue init) * @@ -138,7 +142,7 @@ * */ -const char *l1_revision = "$Revision: 2.36 $"; +const char *l1_revision = "$Revision: 2.37 $"; #define __NO_VERSION__ #include "hisax.h" @@ -362,7 +366,8 @@ DChannel_proc_rcv(struct IsdnCardState *cs) stptr = stptr->next; if (!found) dev_kfree_skb(skb); - } + } else + dev_kfree_skb(skb); } } @@ -559,11 +564,8 @@ l1_deact_req(struct FsmInst *fi, int event, void *arg) struct PStack *st = fi->userdata; FsmChangeState(fi, ST_L1_F3); -// if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) { - FsmDelTimer(&st->l1.timer, 1); - FsmAddTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2); - test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); -// } + FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); } static void @@ -574,8 +576,7 @@ l1_power_up(struct FsmInst *fi, int event, void *arg) if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) { FsmChangeState(fi, ST_L1_F4); st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); - FsmDelTimer(&st->l1.timer, 1); - FsmAddTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags); } else FsmChangeState(fi, ST_L1_F3); @@ -614,7 +615,7 @@ l1_info4_ind(struct FsmInst *fi, int event, void *arg) if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) { if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags)) FsmDelTimer(&st->l1.timer, 3); - FsmAddTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2); + FsmRestartTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2); test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags); } } @@ -729,7 +730,7 @@ l1b_activate(struct FsmInst *fi, int event, void *arg) struct PStack *st = fi->userdata; FsmChangeState(fi, ST_L1_WAIT_ACT); - FsmAddTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2); + FsmRestartTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2); } static void @@ -738,7 +739,7 @@ l1b_deactivate(struct FsmInst *fi, int event, void *arg) struct PStack *st = fi->userdata; FsmChangeState(fi, ST_L1_WAIT_DEACT); - FsmAddTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2); + FsmRestartTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2); } static void diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c index 4ee5831fe..18a7cfc58 100644 --- a/drivers/isdn/hisax/isurf.c +++ b/drivers/isdn/hisax/isurf.c @@ -1,10 +1,14 @@ -/* $Id: isurf.c,v 1.7 1999/11/14 23:37:03 keil Exp $ +/* $Id: isurf.c,v 1.8 1999/12/19 13:09:42 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.8 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.7 1999/11/14 23:37:03 keil * new ISA memory mapped IO * @@ -40,7 +44,7 @@ extern const char *CardType[]; -static const char *ISurf_revision = "$Revision: 1.7 $"; +static const char *ISurf_revision = "$Revision: 1.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -162,10 +166,10 @@ reset_isurf(struct IsdnCardState *cs, u_char chips) byteout(cs->hw.isurf.reset, chips); /* Reset On */ save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */ - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); restore_flags(flags); } diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c index 31b25acdf..e6297bd94 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.9 1999/07/01 08:11:55 keil Exp $ +/* $Id: l3_1tr6.c,v 2.10 2000/01/20 19:42:01 keil Exp $ * German 1TR6 D-channel protocol * @@ -10,6 +10,9 @@ * * * $Log: l3_1tr6.c,v $ + * Revision 2.10 2000/01/20 19:42:01 keil + * Fixed uninitialiesed location + * * Revision 2.9 1999/07/01 08:11:55 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * @@ -59,7 +62,7 @@ #include <linux/ctype.h> extern char *HiSax_getrev(const char *revision); -const char *l3_1tr6_revision = "$Revision: 2.9 $"; +const char *l3_1tr6_revision = "$Revision: 2.10 $"; #define MsgHead(ptr, cref, mty, dis) \ *ptr++ = dis; \ @@ -699,6 +702,7 @@ 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->para.loc = 0; pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); release_l3_process(pc); } diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c index 035ee54ca..9fa10e326 100644 --- a/drivers/isdn/hisax/l3dss1.c +++ b/drivers/isdn/hisax/l3dss1.c @@ -1,4 +1,4 @@ -/* $Id: l3dss1.c,v 2.20 1999/10/11 22:16:27 keil Exp $ +/* $Id: l3dss1.c,v 2.22 2000/01/20 19:44:20 keil Exp $ * EURO/DSS1 D-channel protocol * @@ -13,6 +13,16 @@ * Fritz Elfert * * $Log: l3dss1.c,v $ + * Revision 2.22 2000/01/20 19:44:20 keil + * Fixed uninitialiesed location + * Fixed redirecting number IE in Setup + * Changes from certification + * option for disabling use of KEYPAD protocol + * + * Revision 2.21 1999/12/19 20:25:17 keil + * fixed LLC for outgoing analog calls + * IE Signal is valid on older local switches + * * Revision 2.20 1999/10/11 22:16:27 keil * Suspend/Resume is possible without explicit ID too * @@ -91,9 +101,10 @@ #include "isdnl3.h" #include "l3dss1.h" #include <linux/ctype.h> +#include <linux/config.h> extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 2.20 $"; +const char *dss1_revision = "$Revision: 2.22 $"; #define EXT_BEARER_CAPS 1 @@ -668,34 +679,36 @@ l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) } static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, - IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, 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}; + IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL, + IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; +static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -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_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; +static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, 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}; +static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, + IE_SIGNAL, 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_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_SIGNAL, 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}; + IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN, + IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR, + 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}; + IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -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}; @@ -1272,6 +1285,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; @@ -1283,14 +1297,18 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, MsgHead(p, pc->callref, MT_SETUP); teln = pc->para.setup.phone; +#ifndef CONFIG_HISAX_NO_KEYPAD send_keypad = (strchr(teln,'*') || strchr(teln,'#')) ? 1 : 0; +#else + send_keypad = 0; +#endif +#ifndef CONFIG_HISAX_NO_SENDCOMPLETE + if (!send_keypad) + *p++ = 0xa1; /* complete indicator */ +#endif /* * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 */ -#if HISAX_EURO_SENDCOMPLETE - if (!send_keypad) - *p++ = 0xa1; /* complete indicator */ -#endif if (!send_keypad) switch (pc->para.setup.si1) { case 1: /* Telephony */ @@ -1452,12 +1470,25 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr, *p++ = 0x90; *p++ = 0x21; p = EncodeASyncParams(p, pc->para.setup.si2 - 192); -#if HISAX_SEND_STD_LLC_IE +#ifndef CONFIG_HISAX_NO_LLC } else { - *p++ = 0x7c; - *p++ = 0x02; - *p++ = 0x88; - *p++ = 0x90; + switch (pc->para.setup.si1) { + case 1: /* Telephony */ + *p++ = 0x7c; /* BC-IE-code */ + *p++ = 0x3; /* Length */ + *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + *p++ = 0xa3; /* A-Law Audio */ + break; + case 5: /* Datatransmission 64k, BTX */ + case 7: /* Datatransmission 64k */ + default: + *p++ = 0x7c; /* BC-IE-code */ + *p++ = 0x2; /* Length */ + *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */ + *p++ = 0x90; /* Circuit-Mode 64kbps */ + break; + } #endif } #endif @@ -2738,6 +2769,7 @@ static void l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg) { pc->para.cause = 0x29; /* Temporary failure */ + pc->para.loc = 0; l3dss1_disconnect_req(pc, pr, NULL); pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); } @@ -2747,6 +2779,7 @@ l3dss1_dl_release(struct l3_process *pc, u_char pr, void *arg) { newl3state(pc, 0); pc->para.cause = 0x1b; /* Destination out of order */ + pc->para.loc = 0; pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); release_l3_process(pc); } diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h index 268b5376f..2acb95c01 100644 --- a/drivers/isdn/hisax/l3dss1.h +++ b/drivers/isdn/hisax/l3dss1.h @@ -1,8 +1,11 @@ -/* $Id: l3dss1.h,v 1.7 1999/07/01 08:12:02 keil Exp $ +/* $Id: l3dss1.h,v 1.8 2000/01/20 19:46:15 keil Exp $ * * DSS1 (Euro) D-channel protocol defines * * $Log: l3dss1.h,v $ + * Revision 1.8 2000/01/20 19:46:15 keil + * Changes from certification + * * Revision 1.7 1999/07/01 08:12:02 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * @@ -35,6 +38,8 @@ #define T304 30000 #define T305 30000 #define T308 4000 +/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */ +/* This makes some tests easier and quicker */ #define T309 40000 #define T310 30000 #define T313 4000 diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c index 3b502f7db..3855625bd 100644 --- a/drivers/isdn/hisax/netjet.c +++ b/drivers/isdn/hisax/netjet.c @@ -1,4 +1,4 @@ -/* $Id: netjet.c,v 1.16 1999/10/14 20:25:29 keil Exp $ +/* $Id: netjet.c,v 1.17 1999/12/19 13:09:42 keil Exp $ * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards * @@ -7,6 +7,10 @@ * Thanks to Traverse Technologie Australia for documents and informations * * $Log: netjet.c,v $ + * Revision 1.17 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.16 1999/10/14 20:25:29 keil * add a statistic for error monitoring * @@ -81,7 +85,7 @@ extern const char *CardType[]; -const char *NETjet_revision = "$Revision: 1.16 $"; +const char *NETjet_revision = "$Revision: 1.17 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -1054,11 +1058,11 @@ reset_netjet(struct IsdnCardState *cs) sti(); cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ restore_flags(flags); cs->hw.njet.auxd = 0; diff --git a/drivers/isdn/hisax/rawhdlc.c b/drivers/isdn/hisax/rawhdlc.c index 17ac5c602..5a8b0591e 100644 --- a/drivers/isdn/hisax/rawhdlc.c +++ b/drivers/isdn/hisax/rawhdlc.c @@ -1,8 +1,8 @@ -/* $Id: rawhdlc.c,v 1.3 1998/06/17 19:51:21 he Exp $ +/* $Id: rawhdlc.c,v 1.4 1999/12/23 15:09:32 keil Exp $ * rawhdlc.c support routines for cards that don't support HDLC * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * Brent Baccala <baccala@FreeSoft.org> * * diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c index 955a9a4de..b642d759c 100644 --- a/drivers/isdn/hisax/saphir.c +++ b/drivers/isdn/hisax/saphir.c @@ -1,4 +1,4 @@ -/* $Id: saphir.c,v 1.4 1999/09/04 06:20:06 keil Exp $ +/* $Id: saphir.c,v 1.5 1999/12/19 13:09:42 keil Exp $ * saphir.c low level stuff for HST Saphir 1 * @@ -8,6 +8,10 @@ * * * $Log: saphir.c,v $ + * Revision 1.5 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.4 1999/09/04 06:20:06 keil * Changes from kernel set_current_state() * @@ -29,7 +33,7 @@ #include "isdnl1.h" extern const char *CardType[]; -static char *saphir_rev = "$Revision: 1.4 $"; +static char *saphir_rev = "$Revision: 1.5 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -237,10 +241,10 @@ saphir_reset(struct IsdnCardState *cs) save_flags(flags); sti(); byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ restore_flags(flags); byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val); diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c index b93895ea1..dd1c53d04 100644 --- a/drivers/isdn/hisax/sedlbauer.c +++ b/drivers/isdn/hisax/sedlbauer.c @@ -1,4 +1,4 @@ -/* $Id: sedlbauer.c,v 1.18 1999/11/13 21:25:03 keil Exp $ +/* $Id: sedlbauer.c,v 1.20 2000/01/20 19:47:45 keil Exp $ * sedlbauer.c low level stuff for Sedlbauer cards * includes support for the Sedlbauer speed star (speed star II), @@ -17,6 +17,13 @@ * Edgar Toernig * * $Log: sedlbauer.c,v $ + * Revision 1.20 2000/01/20 19:47:45 keil + * Add Fax Class 1 support + * + * Revision 1.19 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.18 1999/11/13 21:25:03 keil * Support for Speedfax+ PCI * @@ -110,7 +117,7 @@ extern const char *CardType[]; -const char *Sedlbauer_revision = "$Revision: 1.18 $"; +const char *Sedlbauer_revision = "$Revision: 1.20 $"; const char *Sedlbauer_Types[] = {"None", "speed card/win", "speed star", "speed fax+", @@ -490,10 +497,10 @@ reset_sedlbauer(struct IsdnCardState *cs) writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20); save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); 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); @@ -506,20 +513,20 @@ reset_sedlbauer(struct IsdnCardState *cs) byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on); save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((20*HZ)/1000); byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((20*HZ)/1000); restore_flags(flags); } else { byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */ save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */ - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); restore_flags(flags); } @@ -659,15 +666,13 @@ setup_sedlbauer(struct IsdnCard *card)) (sub_id == PCI_SUB_ID_SPEEDFAXP)) { cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; cs->subtyp = SEDL_SPEEDFAX_PCI; - cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + - SEDL_ISAR_PCI_ISAR_RESET_ON; - cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + - SEDL_ISAR_PCI_ISAR_RESET_OFF; } else { cs->hw.sedl.chip = SEDL_CHIP_IPAC; cs->subtyp = SEDL_SPEED_PCI; } bytecnt = 256; + cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON; + cs->hw.sedl.reset_off = SEDL_ISAR_PCI_ISAR_RESET_OFF; byteout(cs->hw.sedl.cfg_reg, 0xff); byteout(cs->hw.sedl.cfg_reg, 0x00); byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); @@ -675,7 +680,7 @@ setup_sedlbauer(struct IsdnCard *card)) byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on); save_flags(flags); sti(); - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off); restore_flags(flags); diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c index 91a6a6ae1..03ecedb93 100644 --- a/drivers/isdn/hisax/sportster.c +++ b/drivers/isdn/hisax/sportster.c @@ -1,12 +1,19 @@ -/* $Id: sportster.c,v 1.10 1999/09/04 06:20:06 keil Exp $ +/* $Id: sportster.c,v 1.12 1999/12/23 15:09:32 keil Exp $ * sportster.c low level stuff for USR Sportster internal TA * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation * * $Log: sportster.c,v $ + * Revision 1.12 1999/12/23 15:09:32 keil + * change email + * + * Revision 1.11 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.10 1999/09/04 06:20:06 keil * Changes from kernel set_current_state() * @@ -46,7 +53,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *sportster_revision = "$Revision: 1.10 $"; +const char *sportster_revision = "$Revision: 1.12 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -180,11 +187,11 @@ reset_sportster(struct IsdnCardState *cs) byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); 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); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); restore_flags(flags); } diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c index c34dc5613..bbf59316d 100644 --- a/drivers/isdn/hisax/teleint.c +++ b/drivers/isdn/hisax/teleint.c @@ -1,4 +1,4 @@ -/* $Id: teleint.c,v 1.11 1999/09/04 06:20:06 keil Exp $ +/* $Id: teleint.c,v 1.12 1999/12/19 13:09:42 keil Exp $ * teleint.c low level stuff for TeleInt isdn cards * @@ -6,6 +6,10 @@ * * * $Log: teleint.c,v $ + * Revision 1.12 1999/12/19 13:09:42 keil + * changed TASK_INTERRUPTIBLE into TASK_UNINTERRUPTIBLE for + * signal proof delays + * * Revision 1.11 1999/09/04 06:20:06 keil * Changes from kernel set_current_state() * @@ -51,7 +55,7 @@ extern const char *CardType[]; -const char *TeleInt_revision = "$Revision: 1.11 $"; +const char *TeleInt_revision = "$Revision: 1.12 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -260,11 +264,11 @@ reset_TeleInt(struct IsdnCardState *cs) byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset On */ save_flags(flags); sti(); - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((30*HZ)/1000); cs->hw.hfc.cirm &= ~HFC_RESET; byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset Off */ - set_current_state(TASK_INTERRUPTIBLE); + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((10*HZ)/1000); restore_flags(flags); } diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c index 8e8017bbb..c3a4a05eb 100644 --- a/drivers/isdn/hisax/teles0.c +++ b/drivers/isdn/hisax/teles0.c @@ -1,15 +1,18 @@ -/* $Id: teles0.c,v 2.10 1999/11/14 23:37:03 keil Exp $ +/* $Id: teles0.c,v 2.11 1999/12/23 15:09:32 keil Exp $ * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to Jan den Ouden * Fritz Elfert * Beat Doebeli * * $Log: teles0.c,v $ + * Revision 2.11 1999/12/23 15:09:32 keil + * change email + * * Revision 2.10 1999/11/14 23:37:03 keil * new ISA memory mapped IO * @@ -61,7 +64,7 @@ extern const char *CardType[]; -const char *teles0_revision = "$Revision: 2.10 $"; +const char *teles0_revision = "$Revision: 2.11 $"; #define TELES_IOMEM_SIZE 0x400 #define byteout(addr,val) outb(val,addr) diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c index 0db245abc..171aeb07d 100644 --- a/drivers/isdn/hisax/teles3.c +++ b/drivers/isdn/hisax/teles3.c @@ -1,16 +1,22 @@ -/* $Id: teles3.c,v 2.13 1999/08/30 12:01:28 keil Exp $ +/* $Id: teles3.c,v 2.15 2000/02/03 16:40:10 keil Exp $ * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * * based on the teles driver from Jan den Ouden * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to Jan den Ouden * Fritz Elfert * Beat Doebeli * * $Log: teles3.c,v $ + * Revision 2.15 2000/02/03 16:40:10 keil + * Fix teles pcmcia + * + * Revision 2.14 1999/12/23 15:09:32 keil + * change email + * * Revision 2.13 1999/08/30 12:01:28 keil * HW version v1.3 support * @@ -88,7 +94,7 @@ #include "isdnl1.h" extern const char *CardType[]; -const char *teles3_revision = "$Revision: 2.13 $"; +const char *teles3_revision = "$Revision: 2.15 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -225,7 +231,7 @@ void release_io_teles3(struct IsdnCardState *cs) { if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { - release_region(cs->hw.teles3.hscx[0], 97); + release_region(cs->hw.teles3.hscx[1], 96); } else { if (cs->hw.teles3.cfg_reg) { if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { @@ -367,15 +373,15 @@ setup_teles3(struct IsdnCard *card)) cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e; cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e; if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { - if (check_region((cs->hw.teles3.hscx[0]), 97)) { + if (check_region((cs->hw.teles3.hscx[1]), 96 )) { printk(KERN_WARNING "HiSax: %s ports %x-%x already in use\n", CardType[cs->typ], - cs->hw.teles3.hscx[0], - cs->hw.teles3.hscx[0] + 96); + cs->hw.teles3.hscx[1], + cs->hw.teles3.hscx[1] + 96); return (0); } else - request_region(cs->hw.teles3.hscx[0], 97, "HiSax Teles PCMCIA"); + request_region(cs->hw.teles3.hscx[1], 96, "HiSax Teles PCMCIA"); } else { if (cs->hw.teles3.cfg_reg) { if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c index 3486b470a..e90510b35 100644 --- a/drivers/isdn/hisax/telespci.c +++ b/drivers/isdn/hisax/telespci.c @@ -1,12 +1,15 @@ -/* $Id: telespci.c,v 2.10 1999/11/15 14:20:05 keil Exp $ +/* $Id: telespci.c,v 2.11 1999/12/23 15:09:32 keil Exp $ * telespci.c low level stuff for Teles PCI isdn cards * * Author Ton van Rosmalen - * Karsten Keil (keil@temic-ech.spacenet.de) + * Karsten Keil (keil@isdn4linux.de) * * * $Log: telespci.c,v $ + * Revision 2.11 1999/12/23 15:09:32 keil + * change email + * * Revision 2.10 1999/11/15 14:20:05 keil * 64Bit compatibility * @@ -47,7 +50,7 @@ #include <linux/pci.h> extern const char *CardType[]; -const char *telespci_revision = "$Revision: 2.10 $"; +const char *telespci_revision = "$Revision: 2.11 $"; #define ZORAN_PO_RQ_PEN 0x02000000 #define ZORAN_PO_WR 0x00800000 diff --git a/drivers/isdn/hysdn/.cvsignore b/drivers/isdn/hysdn/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/isdn/hysdn/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/isdn/hysdn/Makefile b/drivers/isdn/hysdn/Makefile new file mode 100644 index 000000000..626a6deaa --- /dev/null +++ b/drivers/isdn/hysdn/Makefile @@ -0,0 +1,24 @@ +SUB_DIRS := +MOD_SUB_DIRS := +ALL_SUB_DIRS := + +L_OBJS := +LX_OBJS := +M_OBJS := +MX_OBJS := +O_OBJS := +OX_OBJS := +L_TARGET := +O_TARGET := + +ifeq ($(CONFIG_PROC_FS),y) + ifeq ($(CONFIG_HYSDN),y) + M_OBJS += hysdn.o + O_TARGET += hysdn.o + O_OBJS += hysdn_procconf.o hysdn_proclog.o boardergo.o hysdn_boot.o hysdn_sched.o hysdn_net.o + OX_OBJS += hysdn_init.o + endif +endif + +include $(TOPDIR)/Rules.make + diff --git a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c new file mode 100644 index 000000000..578e4a14d --- /dev/null +++ b/drivers/isdn/hysdn/boardergo.c @@ -0,0 +1,467 @@ +/* $Id: boardergo.c,v 1.1 2000/02/10 19:45:18 werner Exp $ + + * Linux driver for HYSDN cards, specific routines for ergo type boards. + * + * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same + * DPRAM interface and layout with only minor differences all related + * stuff is done here, not in separate modules. + * + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: boardergo.c,v $ + * Revision 1.1 2000/02/10 19:45:18 werner + * + * Initial release + * + * + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <asm/io.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> + +#include "hysdn_defs.h" +#include "boardergo.h" + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +/***************************************************/ +/* The cards interrupt handler. Called from system */ +/***************************************************/ +static void +ergo_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + hysdn_card *card = dev_id; /* parameter from irq */ + tErgDpram *dpr; + ulong flags; + uchar volatile b; + + if (!card) + return; /* error -> spurious interrupt */ + if (!card->irq_enabled) + return; /* other device interrupting or irq switched off */ + + save_flags(flags); + cli(); /* no further irqs allowed */ + + if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) { + restore_flags(flags); /* restore old state */ + return; /* no interrupt requested by E1 */ + } + /* clear any pending ints on the board */ + dpr = card->dpram; + b = dpr->ToPcInt; /* clear for ergo */ + b |= dpr->ToPcIntMetro; /* same for metro */ + b |= dpr->ToHyInt; /* and for champ */ + + /* start kernel task immediately after leaving all interrupts */ + if (!card->hw_lock) { + queue_task(&card->irq_queue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + restore_flags(flags); +} /* ergo_interrupt */ + +/******************************************************************************/ +/* ergo_irq_bh is the function called by the immediate kernel task list after */ +/* being activated with queue_task and no interrupts active. This task is the */ +/* only one handling data transfer from or to the card after booting. The task */ +/* may be queued from everywhere (interrupts included). */ +/******************************************************************************/ +static void +ergo_irq_bh(hysdn_card * card) +{ + tErgDpram *dpr; + int again; + ulong flags; + + if (card->state != CARD_STATE_RUN) + return; /* invalid call */ + + dpr = card->dpram; /* point to DPRAM */ + + save_flags(flags); + cli(); + if (card->hw_lock) { + restore_flags(flags); /* hardware currently unavailable */ + return; + } + card->hw_lock = 1; /* we now lock the hardware */ + + do { + sti(); /* reenable other ints */ + again = 0; /* assume loop not to be repeated */ + + if (!dpr->ToHyFlag) { + /* we are able to send a buffer */ + + if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel, + ERG_TO_HY_BUF_SIZE)) { + dpr->ToHyFlag = 1; /* enable tx */ + again = 1; /* restart loop */ + } + } /* we are able to send a buffer */ + if (dpr->ToPcFlag) { + /* a message has arrived for us, handle it */ + + if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) { + dpr->ToPcFlag = 0; /* we worked the data */ + again = 1; /* restart loop */ + } + } /* a message has arrived for us */ + cli(); /* no further ints */ + if (again) { + dpr->ToHyInt = 1; + dpr->ToPcInt = 1; /* interrupt to E1 for all cards */ + } else + card->hw_lock = 0; /* free hardware again */ + } while (again); /* until nothing more to do */ + + restore_flags(flags); +} /* ergo_irq_bh */ + + +/*********************************************************/ +/* stop the card (hardware reset) and disable interrupts */ +/*********************************************************/ +static void +ergo_stopcard(hysdn_card * card) +{ + ulong flags; + uchar val; + + hysdn_net_release(card); /* first release the net device if existing */ + save_flags(flags); + cli(); + val = bytein(card->iobase + PCI9050_INTR_REG); /* get actual value */ + val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1); /* mask irq */ + byteout(card->iobase + PCI9050_INTR_REG, val); + card->irq_enabled = 0; + byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET); /* reset E1 processor */ + card->state = CARD_STATE_UNUSED; + card->err_log_state = ERRLOG_STATE_OFF; /* currently no log active */ + + restore_flags(flags); +} /* ergo_stopcard */ + +/**************************************************************************/ +/* enable or disable the cards error log. The event is queued if possible */ +/**************************************************************************/ +static void +ergo_set_errlog_state(hysdn_card * card, int on) +{ + ulong flags; + + if (card->state != CARD_STATE_RUN) { + card->err_log_state = ERRLOG_STATE_OFF; /* must be off */ + return; + } + save_flags(flags); + cli(); + + if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) || + ((card->err_log_state == ERRLOG_STATE_ON) && on)) { + restore_flags(flags); + return; /* nothing to do */ + } + if (on) + card->err_log_state = ERRLOG_STATE_START; /* request start */ + else + card->err_log_state = ERRLOG_STATE_STOP; /* request stop */ + + restore_flags(flags); + queue_task(&card->irq_queue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} /* ergo_set_errlog_state */ + +/******************************************/ +/* test the cards RAM and return 0 if ok. */ +/******************************************/ +static const char TestText[36] = "This Message is filler, why read it"; + +static int +ergo_testram(hysdn_card * card) +{ + tErgDpram *dpr = card->dpram; + + memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable)); /* clear all Traps */ + dpr->ToHyInt = 1; /* E1 INTR state forced */ + + memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText, + sizeof(TestText)); + if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText, + sizeof(TestText))) + return (-1); + + memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText, + sizeof(TestText)); + if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText, + sizeof(TestText))) + return (-1); + + return (0); +} /* ergo_testram */ + +/*****************************************************************************/ +/* this function is intended to write stage 1 boot image to the cards buffer */ +/* this is done in two steps. First the 1024 hi-words are written (offs=0), */ +/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the */ +/* PCI-write-buffers flushed and the card is taken out of reset. */ +/* The function then waits for a reaction of the E1 processor or a timeout. */ +/* Negative return values are interpreted as errors. */ +/*****************************************************************************/ +static int +ergo_writebootimg(struct HYSDN_CARD *card, uchar * buf, ulong offs) +{ + uchar *dst; + tErgDpram *dpram; + int cnt = (BOOT_IMG_SIZE >> 2); /* number of words to move and swap (byte order!) */ + + if (card->debug_flags & LOG_POF_CARD) + hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs); + + dst = card->dpram; /* pointer to start of DPRAM */ + dst += (offs + ERG_DPRAM_FILL_SIZE); /* offset in the DPRAM */ + while (cnt--) { + *dst++ = *(buf + 1); /* high byte */ + *dst++ = *buf; /* low byte */ + dst += 2; /* point to next longword */ + buf += 2; /* buffer only filled with words */ + } + + /* if low words (offs = 2) have been written, clear the rest of the DPRAM, */ + /* flush the PCI-write-buffer and take the E1 out of reset */ + if (offs) { + memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE); /* fill the DPRAM still not cleared */ + dpram = card->dpram; /* get pointer to dpram structure */ + dpram->ToHyNoDpramErrLog = 0xFF; /* write a dpram register */ + while (!dpram->ToHyNoDpramErrLog); /* reread volatile register to flush PCI */ + + byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN); /* start E1 processor */ + /* the interrupts are still masked */ + + sti(); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + + if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) { + if (card->debug_flags & LOG_POF_CARD) + hysdn_addlog(card, "ERGO: write bootldr no answer"); + return (-ERR_BOOTIMG_FAIL); + } + } /* start_boot_img */ + return (0); /* successfull */ +} /* ergo_writebootimg */ + +/********************************************************************************/ +/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */ +/* using the boot spool mechanism. If everything works fine 0 is returned. In */ +/* case of errors a negative error value is returned. */ +/********************************************************************************/ +static int +ergo_writebootseq(struct HYSDN_CARD *card, uchar * buf, int len) +{ + tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram; + uchar *dst; + uchar buflen; + int nr_write; + uchar tmp_rdptr; + uchar wr_mirror; + int i; + + if (card->debug_flags & LOG_POF_CARD) + hysdn_addlog(card, "ERGO: write boot seq len=%d ", len); + + dst = sp->Data; /* point to data in spool structure */ + buflen = sp->Len; /* maximum len of spooled data */ + wr_mirror = sp->WrPtr; /* only once read */ + sti(); + + /* try until all bytes written or error */ + i = 0x1000; /* timeout value */ + while (len) { + + /* first determine the number of bytes that may be buffered */ + do { + tmp_rdptr = sp->RdPtr; /* first read the pointer */ + i--; /* decrement timeout */ + } while (i && (tmp_rdptr != sp->RdPtr)); /* wait for stable pointer */ + + if (!i) { + if (card->debug_flags & LOG_POF_CARD) + hysdn_addlog(card, "ERGO: write boot seq timeout"); + return (-ERR_BOOTSEQ_FAIL); /* value not stable -> timeout */ + } + if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0) + nr_write += buflen; /* now we got number of free bytes - 1 in buffer */ + + if (!nr_write) + continue; /* no free bytes in buffer */ + + if (nr_write > len) + nr_write = len; /* limit if last few bytes */ + i = 0x1000; /* reset timeout value */ + + /* now we know how much bytes we may put in the puffer */ + len -= nr_write; /* we savely could adjust len before output */ + while (nr_write--) { + *(dst + wr_mirror) = *buf++; /* output one byte */ + if (++wr_mirror >= buflen) + wr_mirror = 0; + sp->WrPtr = wr_mirror; /* announce the next byte to E1 */ + } /* while (nr_write) */ + + } /* while (len) */ + + return (0); +} /* ergo_writebootseq */ + +/***********************************************************************************/ +/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */ +/* boot process. If the process has been successfull 0 is returned otherwise a */ +/* negative error code is returned. */ +/***********************************************************************************/ +static int +ergo_waitpofready(struct HYSDN_CARD *card) +{ + tErgDpram *dpr = card->dpram; /* pointer to DPRAM structure */ + int timecnt = 10000 / 50; /* timeout is 10 secs max. */ + ulong flags; + int msg_size; + int i; + + if (card->debug_flags & LOG_POF_CARD) + hysdn_addlog(card, "ERGO: waiting for pof ready"); + + while (timecnt--) { + /* wait until timeout */ + + if (dpr->ToPcFlag) { + /* data has arrived */ + + if ((dpr->ToPcChannel != CHAN_SYSTEM) || + (dpr->ToPcSize < MIN_RDY_MSG_SIZE) || + (dpr->ToPcSize > MAX_RDY_MSG_SIZE) || + ((*(ulong *) dpr->ToPcBuf) != RDY_MAGIC)) + break; /* an error occured */ + + /* Check for additional data delivered during SysReady */ + msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE; + if (msg_size > 0) + if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size)) + break; + + if (card->debug_flags & LOG_POF_RECORD) + hysdn_addlog(card, "ERGO: pof boot success"); + + save_flags(flags); + cli(); + + card->state = CARD_STATE_RUN; /* now card is running */ + /* enable the cards interrupt */ + byteout(card->iobase + PCI9050_INTR_REG, + bytein(card->iobase + PCI9050_INTR_REG) | + (PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1)); + card->irq_enabled = 1; /* we are ready to receive interrupts */ + + dpr->ToPcFlag = 0; /* reset data indicator */ + dpr->ToHyInt = 1; + dpr->ToPcInt = 1; /* interrupt to E1 for all cards */ + + restore_flags(flags); + if ((i = hysdn_net_create(card))) { + ergo_stopcard(card); + card->state = CARD_STATE_BOOTERR; + return (i); + } + return (0); /* success */ + } /* data has arrived */ + sti(); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((50 * HZ) / 1000); /* Timeout 50ms */ + } /* wait until timeout */ + + if (card->debug_flags & LOG_POF_CARD) + hysdn_addlog(card, "ERGO: pof boot ready timeout"); + return (-ERR_POF_TIMEOUT); +} /* ergo_waitpofready */ + + + +/************************************************************************************/ +/* release the cards hardware. Before releasing do a interrupt disable and hardware */ +/* reset. Also unmap dpram. */ +/* Use only during module release. */ +/************************************************************************************/ +static void +ergo_releasehardware(hysdn_card * card) +{ + ergo_stopcard(card); /* first stop the card if not already done */ + free_irq(card->irq, card); /* release interrupt */ + release_region(card->iobase + PCI9050_INTR_REG, 1); /* release all io ports */ + release_region(card->iobase + PCI9050_USER_IO, 1); + vfree(card->dpram); + card->dpram = NULL; /* release shared mem */ +} /* ergo_releasehardware */ + + +/*********************************************************************************/ +/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */ +/* value is returned. */ +/* Use only during module init. */ +/*********************************************************************************/ +int +ergo_inithardware(hysdn_card * card) +{ + if (check_region(card->iobase + PCI9050_INTR_REG, 1) || + check_region(card->iobase + PCI9050_USER_IO, 1)) + return (-1); /* ports already in use */ + + card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1; + if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) + return (-1); + + request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN"); + request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN"); + ergo_stopcard(card); /* disable interrupts */ + if (request_irq(card->irq, ergo_interrupt, SA_SHIRQ, "HYSDN", card)) { + ergo_releasehardware(card); /* return the aquired hardware */ + return (-1); + } + /* success, now setup the function pointers */ + card->stopcard = ergo_stopcard; + card->releasehardware = ergo_releasehardware; + card->testram = ergo_testram; + card->writebootimg = ergo_writebootimg; + card->writebootseq = ergo_writebootseq; + card->waitpofready = ergo_waitpofready; + card->set_errlog_state = ergo_set_errlog_state; + card->irq_queue.next = 0; + card->irq_queue.sync = 0; + card->irq_queue.data = card; /* init task queue for interrupt */ + card->irq_queue.routine = (void *) (void *) ergo_irq_bh; + + return (0); +} /* ergo_inithardware */ diff --git a/drivers/isdn/hysdn/boardergo.h b/drivers/isdn/hysdn/boardergo.h new file mode 100644 index 000000000..0e2c3f678 --- /dev/null +++ b/drivers/isdn/hysdn/boardergo.h @@ -0,0 +1,117 @@ +/* $Id: boardergo.h,v 1.1 2000/02/10 19:44:30 werner Exp $ + + * Linux driver for HYSDN cards, definitions for ergo type boards (buffers..). + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: boardergo.h,v $ + * Revision 1.1 2000/02/10 19:44:30 werner + * + * Initial release + * + * + */ + + +/************************************************/ +/* defines for the dual port memory of the card */ +/************************************************/ +#define ERG_DPRAM_PAGE_SIZE 0x2000 /* DPRAM occupies a 8K page */ +#define BOOT_IMG_SIZE 4096 +#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE) + +#define ERG_TO_HY_BUF_SIZE 0x0E00 /* 3072 bytes buffer size to card */ +#define ERG_TO_PC_BUF_SIZE 0x0E00 /* 3072 bytes to PC, too */ + +/* following DPRAM layout copied from OS2-driver boarderg.h */ +typedef struct ErgDpram_tag { +/*0000 */ uchar ToHyBuf[ERG_TO_HY_BUF_SIZE]; +/*0E00 */ uchar ToPcBuf[ERG_TO_PC_BUF_SIZE]; + + /*1C00 */ uchar bSoftUart[SIZE_RSV_SOFT_UART]; + /* size 0x1B0 */ + + /*1DB0 *//* tErrLogEntry */ uchar volatile ErrLogMsg[64]; + /* size 64 bytes */ + /*1DB0 ulong ulErrType; */ + /*1DB4 ulong ulErrSubtype; */ + /*1DB8 ulong ucTextSize; */ + /*1DB9 ulong ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */ + /*1DF0 */ + +/*1DF0 */ word volatile ToHyChannel; +/*1DF2 */ word volatile ToHySize; + /*1DF4 */ uchar volatile ToHyFlag; + /* !=0: msg for Hy waiting */ + /*1DF5 */ uchar volatile ToPcFlag; + /* !=0: msg for PC waiting */ +/*1DF6 */ word volatile ToPcChannel; +/*1DF8 */ word volatile ToPcSize; + /*1DFA */ uchar bRes1DBA[0x1E00 - 0x1DFA]; + /* 6 bytes */ + +/*1E00 */ uchar bRestOfEntryTbl[0x1F00 - 0x1E00]; +/*1F00 */ ulong TrapTable[62]; + /*1FF8 */ uchar bRes1FF8[0x1FFB - 0x1FF8]; + /* low part of reset vetor */ +/*1FFB */ uchar ToPcIntMetro; + /* notes: + * - metro has 32-bit boot ram - accessing + * ToPcInt and ToHyInt would be the same; + * so we moved ToPcInt to 1FFB. + * Because on the PC side both vars are + * readonly (reseting on int from E1 to PC), + * we can read both vars on both cards + * without destroying anything. + * - 1FFB is the high byte of the reset vector, + * so E1 side should NOT change this byte + * when writing! + */ +/*1FFC */ uchar volatile ToHyNoDpramErrLog; + /* note: ToHyNoDpramErrLog is used to inform + * boot loader, not to use DPRAM based + * ErrLog; when DOS driver is rewritten + * this becomes obsolete + */ +/*1FFD */ uchar bRes1FFD; + /*1FFE */ uchar ToPcInt; + /* E1_intclear; on CHAMP2: E1_intset */ + /*1FFF */ uchar ToHyInt; + /* E1_intset; on CHAMP2: E1_intclear */ +} tErgDpram; + +/**********************************************/ +/* PCI9050 controller local register offsets: */ +/* copied from boarderg.c */ +/**********************************************/ +#define PCI9050_INTR_REG 0x4C /* Interrupt register */ +#define PCI9050_USER_IO 0x51 /* User I/O register */ + + /* bitmask for PCI9050_INTR_REG: */ +#define PCI9050_INTR_REG_EN1 0x01 /* 1= enable (def.), 0= disable */ +#define PCI9050_INTR_REG_POL1 0x02 /* 1= active high (def.), 0= active low */ +#define PCI9050_INTR_REG_STAT1 0x04 /* 1= intr. active, 0= intr. not active (def.) */ +#define PCI9050_INTR_REG_ENPCI 0x40 /* 1= PCI interrupts enable (def.) */ + + /* bitmask for PCI9050_USER_IO: */ +#define PCI9050_USER_IO_EN3 0x02 /* 1= disable , 0= enable (def.) */ +#define PCI9050_USER_IO_DIR3 0x04 /* 1= output (def.), 0= input */ +#define PCI9050_USER_IO_DAT3 0x08 /* 1= high (def.) , 0= low */ + +#define PCI9050_E1_RESET ( PCI9050_USER_IO_DIR3) /* 0x04 */ +#define PCI9050_E1_RUN (PCI9050_USER_IO_DAT3|PCI9050_USER_IO_DIR3) /* 0x0C */ diff --git a/drivers/isdn/hysdn/hysdn_boot.c b/drivers/isdn/hysdn/hysdn_boot.c new file mode 100644 index 000000000..030c40de6 --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_boot.c @@ -0,0 +1,420 @@ +/* $Id: hysdn_boot.c,v 1.1 2000/02/10 19:45:18 werner Exp $ + + * Linux driver for HYSDN cards, specific routines for booting and pof handling. + * + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_boot.c,v $ + * Revision 1.1 2000/02/10 19:45:18 werner + * + * Initial release + * + * + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/vmalloc.h> +#include <linux/malloc.h> +#include <asm/uaccess.h> + +#include "hysdn_defs.h" +#include "hysdn_pof.h" + +/********************************/ +/* defines for pof read handler */ +/********************************/ +#define POF_READ_FILE_HEAD 0 +#define POF_READ_TAG_HEAD 1 +#define POF_READ_TAG_DATA 2 + +/************************************************************/ +/* definition of boot specific data area. This data is only */ +/* needed during boot and so allocated dynamically. */ +/************************************************************/ +struct boot_data { + word Cryptor; /* for use with Decrypt function */ + word Nrecs; /* records remaining in file */ + uchar pof_state; /* actual state of read handler */ + uchar is_crypted; /* card data is crypted */ + int BufSize; /* actual number of bytes bufferd */ + int last_error; /* last occured error */ + word pof_recid; /* actual pof recid */ + ulong pof_reclen; /* total length of pof record data */ + ulong pof_recoffset; /* actual offset inside pof record */ + union { + uchar BootBuf[BOOT_BUF_SIZE]; /* buffer as byte count */ + tPofRecHdr PofRecHdr; /* header for actual record/chunk */ + tPofFileHdr PofFileHdr; /* header from POF file */ + tPofTimeStamp PofTime; /* time information */ + } buf; +}; + +/*****************************************************/ +/* start decryption of sucessive POF file chuncks. */ +/* */ +/* to be called at start of POF file reading, */ +/* before starting any decryption on any POF record. */ +/*****************************************************/ +void +StartDecryption(struct boot_data *boot) +{ + boot->Cryptor = CRYPT_STARTTERM; +} /* StartDecryption */ + + +/***************************************************************/ +/* decrypt complete BootBuf */ +/* NOTE: decryption must be applied to all or none boot tags - */ +/* to HI and LO boot loader and (all) seq tags, because */ +/* global Cryptor is started for whole POF. */ +/***************************************************************/ +void +DecryptBuf(struct boot_data *boot, int cnt) +{ + uchar *bufp = boot->buf.BootBuf; + + while (cnt--) { + boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0); + *bufp++ ^= (uchar) boot->Cryptor; + } +} /* DecryptBuf */ + +/********************************************************************************/ +/* pof_handle_data executes the required actions dependant on the active record */ +/* id. If successfull 0 is returned, a negative value shows an error. */ +/********************************************************************************/ +static int +pof_handle_data(hysdn_card * card, int datlen) +{ + struct boot_data *boot = card->boot; /* pointer to boot specific data */ + long l; + uchar *imgp; + int img_len; + + /* handle the different record types */ + switch (boot->pof_recid) { + + case TAG_TIMESTMP: + if (card->debug_flags & LOG_POF_RECORD) + hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText); + break; + + case TAG_CBOOTDTA: + DecryptBuf(boot, datlen); /* we need to encrypt the buffer */ + case TAG_BOOTDTA: + if (card->debug_flags & LOG_POF_RECORD) + hysdn_addlog(card, "POF got %s len=%d offs=0x%lx", + (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA", + datlen, boot->pof_recoffset); + + if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) { + boot->last_error = EPOF_BAD_IMG_SIZE; /* invalid length */ + return (boot->last_error); + } + imgp = boot->buf.BootBuf; /* start of buffer */ + img_len = datlen; /* maximum length to transfer */ + + l = POF_BOOT_LOADER_OFF_IN_PAGE - + (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1)); + if (l > 0) { + /* buffer needs to be truncated */ + imgp += l; /* advance pointer */ + img_len -= l; /* adjust len */ + } + /* at this point no special handling for data wrapping over buffer */ + /* is necessary, because the boot image always will be adjusted to */ + /* match a page boundary inside the buffer. */ + /* The buffer for the boot image on the card is filled in 2 cycles */ + /* first the 1024 hi-words are put in the buffer, then the low 1024 */ + /* word are handled in the same way with different offset. */ + + if (img_len > 0) { + /* data available for copy */ + if ((boot->last_error = + card->writebootimg(card, imgp, + (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0) + return (boot->last_error); + } + break; /* end of case boot image hi/lo */ + + case TAG_CABSDATA: + DecryptBuf(boot, datlen); /* we need to encrypt the buffer */ + case TAG_ABSDATA: + if (card->debug_flags & LOG_POF_RECORD) + hysdn_addlog(card, "POF got %s len=%d offs=0x%lx", + (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA", + datlen, boot->pof_recoffset); + + if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0)) + return (boot->last_error); /* error writing data */ + + if (boot->pof_recoffset + datlen >= boot->pof_reclen) + return (card->waitpofready(card)); /* data completely spooled, wait for ready */ + + break; /* end of case boot seq data */ + + default: + if (card->debug_flags & LOG_POF_RECORD) + hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid, + datlen, boot->pof_recoffset); + + break; /* simply skip record */ + } /* switch boot->pof_recid */ + + return (0); +} /* pof_handle_data */ + + +/******************************************************************************/ +/* pof_write_buffer is called when the buffer has been filled with the needed */ +/* number of data bytes. The number delivered is additionally supplied for */ +/* verification. The functions handles the data and returns the needed number */ +/* of bytes for the next action. If the returned value is 0 or less an error */ +/* occured and booting must be aborted. */ +/******************************************************************************/ +int +pof_write_buffer(hysdn_card * card, int datlen) +{ + struct boot_data *boot = card->boot; /* pointer to boot specific data */ + + if (!boot) + return (-EFAULT); /* invalid call */ + if (boot->last_error < 0) + return (boot->last_error); /* repeated error */ + + if (card->debug_flags & LOG_POF_WRITE) + hysdn_addlog(card, "POF write: got %d bytes ", datlen); + + switch (boot->pof_state) { + case POF_READ_FILE_HEAD: + if (card->debug_flags & LOG_POF_WRITE) + hysdn_addlog(card, "POF write: checking file header"); + + if (datlen != sizeof(tPofFileHdr)) { + boot->last_error = -EPOF_INTERNAL; + break; + } + if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) { + boot->last_error = -EPOF_BAD_MAGIC; + break; + } + /* Setup the new state and vars */ + boot->Nrecs = (word) (boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */ + boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */ + boot->last_error = sizeof(tPofRecHdr); /* new length */ + break; + + case POF_READ_TAG_HEAD: + if (card->debug_flags & LOG_POF_WRITE) + hysdn_addlog(card, "POF write: checking tag header"); + + if (datlen != sizeof(tPofRecHdr)) { + boot->last_error = -EPOF_INTERNAL; + break; + } + boot->pof_recid = boot->buf.PofRecHdr.PofRecId; /* actual pof recid */ + boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */ + boot->pof_recoffset = 0; /* no starting offset */ + + if (card->debug_flags & LOG_POF_RECORD) + hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ", + boot->pof_recid, boot->pof_reclen); + + boot->pof_state = POF_READ_TAG_DATA; /* now start with tag data */ + if (boot->pof_reclen < BOOT_BUF_SIZE) + boot->last_error = boot->pof_reclen; /* limit size */ + else + boot->last_error = BOOT_BUF_SIZE; /* maximum */ + + if (!boot->last_error) { /* no data inside record */ + boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */ + boot->last_error = sizeof(tPofRecHdr); /* new length */ + } + break; + + case POF_READ_TAG_DATA: + if (card->debug_flags & LOG_POF_WRITE) + hysdn_addlog(card, "POF write: getting tag data"); + + if (datlen != boot->last_error) { + boot->last_error = -EPOF_INTERNAL; + break; + } + if ((boot->last_error = pof_handle_data(card, datlen)) < 0) + return (boot->last_error); /* an error occured */ + + boot->pof_recoffset += datlen; + if (boot->pof_recoffset >= boot->pof_reclen) { + boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */ + boot->last_error = sizeof(tPofRecHdr); /* new length */ + } else { + if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE) + boot->last_error = boot->pof_reclen - boot->pof_recoffset; /* limit size */ + else + boot->last_error = BOOT_BUF_SIZE; /* maximum */ + } + break; + + default: + boot->last_error = -EPOF_INTERNAL; /* unknown state */ + break; + } /* switch (boot->pof_state) */ + + return (boot->last_error); +} /* pof_write_buffer */ + + +/*******************************************************************************/ +/* pof_write_open is called when an open for boot on the cardlog device occurs. */ +/* The function returns the needed number of bytes for the next operation. If */ +/* the returned number is less or equal 0 an error specified by this code */ +/* occurred. Additionally the pointer to the buffer data area is set on success */ +/*******************************************************************************/ +int +pof_write_open(hysdn_card * card, uchar ** bufp) +{ + struct boot_data *boot; /* pointer to boot specific data */ + + if (card->boot) { + if (card->debug_flags & LOG_POF_OPEN) + hysdn_addlog(card, "POF open: already opened for boot"); + return (-ERR_ALREADY_BOOT); /* boot already active */ + } + /* error no mem available */ + if (!(boot = kmalloc(sizeof(struct boot_data), GFP_KERNEL))) { + if (card->debug_flags & LOG_MEM_ERR) + hysdn_addlog(card, "POF open: unable to allocate mem"); + return (-EFAULT); + } + card->boot = boot; + card->state = CARD_STATE_BOOTING; + memset(boot, 0, sizeof(struct boot_data)); + + card->stopcard(card); /* first stop the card */ + if (card->testram(card)) { + if (card->debug_flags & LOG_POF_OPEN) + hysdn_addlog(card, "POF open: DPRAM test failure"); + boot->last_error = -ERR_BOARD_DPRAM; + card->state = CARD_STATE_BOOTERR; /* show boot error */ + return (boot->last_error); + } + boot->BufSize = 0; /* Buffer is empty */ + boot->pof_state = POF_READ_FILE_HEAD; /* read file header */ + StartDecryption(boot); /* if POF File should be encrypted */ + + if (card->debug_flags & LOG_POF_OPEN) + hysdn_addlog(card, "POF open: success"); + + *bufp = boot->buf.BootBuf; /* point to buffer */ + return (sizeof(tPofFileHdr)); +} /* pof_write_open */ + +/********************************************************************************/ +/* pof_write_close is called when an close of boot on the cardlog device occurs. */ +/* The return value must be 0 if everything has happened as desired. */ +/********************************************************************************/ +int +pof_write_close(hysdn_card * card) +{ + struct boot_data *boot = card->boot; /* pointer to boot specific data */ + + if (!boot) + return (-EFAULT); /* invalid call */ + + card->boot = NULL; /* no boot active */ + kfree(boot); + + if (card->state == CARD_STATE_RUN) + card->set_errlog_state(card, 1); /* activate error log */ + + if (card->debug_flags & LOG_POF_OPEN) + hysdn_addlog(card, "POF close: success"); + + return (0); +} /* pof_write_close */ + +/*********************************************************************************/ +/* EvalSysrTokData checks additional records delivered with the Sysready Message */ +/* when POF has been booted. A return value of 0 is used if no error occured. */ +/*********************************************************************************/ +int +EvalSysrTokData(hysdn_card * card, uchar * cp, int len) +{ + u_char *p; + u_char crc; + + if (card->debug_flags & LOG_POF_RECORD) + hysdn_addlog(card, "SysReady Token data length %d", len); + + if (len < 2) { + hysdn_addlog(card, "SysReady Token Data to short"); + return (1); + } + for (p = cp, crc = 0; p < (cp + len - 2); p++) + if ((crc & 0x80)) + crc = (((u_char) (crc << 1)) + 1) + *p; + else + crc = ((u_char) (crc << 1)) + *p; + crc = ~crc; + if (crc != *(cp + len - 1)) { + hysdn_addlog(card, "SysReady Token Data invalid CRC"); + return (1); + } + len--; /* dont check CRC byte */ + while (len > 0) { + + if (*cp == SYSR_TOK_END) + return (0); /* End of Token stream */ + + if (len < (*(cp + 1) + 2)) { + hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1)); + return (1); + } + switch (*cp) { + case SYSR_TOK_B_CHAN: /* 1 */ + if (*(cp + 1) != 1) + return (1); /* length invalid */ + card->bchans = *(cp + 2); + break; + + case SYSR_TOK_FAX_CHAN: /* 2 */ + if (*(cp + 1) != 1) + return (1); /* length invalid */ + card->faxchans = *(cp + 2); + break; + + case SYSR_TOK_MAC_ADDR: /* 3 */ + if (*(cp + 1) != 6) + return (1); /* length invalid */ + memcpy(card->mac_addr, cp + 2, 6); + break; + + default: + hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1)); + break; + } + len -= (*(cp + 1) + 2); /* adjust len */ + cp += (*(cp + 1) + 2); /* and pointer */ + } + + hysdn_addlog(card, "no end token found"); + return (1); +} /* EvalSysrTokData */ diff --git a/drivers/isdn/hysdn/hysdn_defs.h b/drivers/isdn/hysdn/hysdn_defs.h new file mode 100644 index 000000000..cac76cb26 --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_defs.h @@ -0,0 +1,229 @@ +/* $Id: hysdn_defs.h,v 1.1 2000/02/10 19:44:30 werner Exp $ + + * Linux driver for HYSDN cards, global definitions and exported vars and functions. + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_defs.h,v $ + * Revision 1.1 2000/02/10 19:44:30 werner + * + * Initial release + * + * + */ + +#include <linux/hysdn_if.h> +#include <linux/interrupt.h> +#include <linux/tqueue.h> +#include <linux/skbuff.h> + +/****************************/ +/* storage type definitions */ +/****************************/ +#define uchar unsigned char +#define uint unsigned int +#define ulong unsigned long +#define word unsigned short + +#include "ince1pc.h" + +/************************************************/ +/* constants and bits for debugging/log outputs */ +/************************************************/ +#define LOG_MAX_LINELEN 120 +#define DEB_OUT_SYSLOG 0x80000000 /* output to syslog instead of proc fs */ +#define LOG_MEM_ERR 0x00000001 /* log memory errors like kmalloc failure */ +#define LOG_POF_OPEN 0x00000010 /* log pof open and close activities */ +#define LOG_POF_RECORD 0x00000020 /* log pof record parser */ +#define LOG_POF_WRITE 0x00000040 /* log detailed pof write operation */ +#define LOG_POF_CARD 0x00000080 /* log pof related card functions */ +#define LOG_CNF_LINE 0x00000100 /* all conf lines are put to procfs */ +#define LOG_CNF_DATA 0x00000200 /* non comment conf lines are shown with channel */ +#define LOG_CNF_MISC 0x00000400 /* additional conf line debug outputs */ +#define LOG_SCHED_ASYN 0x00001000 /* debug schedulers async tx routines */ +#define LOG_PROC_OPEN 0x00100000 /* open and close from procfs are logged */ +#define LOG_PROC_ALL 0x00200000 /* all actions from procfs are logged */ +#define LOG_NET_INIT 0x00010000 /* network init and deinit logging */ + +#define DEF_DEB_FLAGS 0x7fff000f /* everything is logged to procfs */ + +/**********************************/ +/* proc filesystem name constants */ +/**********************************/ +#define PROC_SUBDIR_NAME "hysdn" +#define PROC_CONF_BASENAME "cardconf" +#define PROC_LOG_BASENAME "cardlog" + +/************************/ +/* PCI constant defines */ +/************************/ +#define PCI_VENDOR_ID_HYPERCOPE 0x1365 +#define PCI_DEVICE_ID_PLX 0x9050 /* all DPRAM cards use the same id */ + +/*****************************/ +/* sub ids determining cards */ +/*****************************/ +#define PCI_SUB_ID_OLD_ERGO 0x0104 +#define PCI_SUB_ID_ERGO 0x0106 +#define PCI_SUB_ID_METRO 0x0107 +#define PCI_SUB_ID_CHAMP2 0x0108 +#define PCI_SUB_ID_PLEXUS 0x0109 + +/***********************************/ +/* PCI 32 bit parms for IO and MEM */ +/***********************************/ +#define PCI_REG_PLX_MEM_BASE 0 +#define PCI_REG_PLX_IO_BASE 1 +#define PCI_REG_MEMORY_BASE 3 + +/**************/ +/* card types */ +/**************/ +#define BD_NONE 0U +#define BD_PERFORMANCE 1U +#define BD_VALUE 2U +#define BD_PCCARD 3U +#define BD_ERGO 4U +#define BD_METRO 5U +#define BD_CHAMP2 6U +#define BD_PLEXUS 7U + +/******************************************************/ +/* defined states for cards shown by reading cardconf */ +/******************************************************/ +#define CARD_STATE_UNUSED 0 /* never been used or booted */ +#define CARD_STATE_BOOTING 1 /* booting is in progress */ +#define CARD_STATE_BOOTERR 2 /* a previous boot was aborted */ +#define CARD_STATE_RUN 3 /* card is active */ + +/*******************************/ +/* defines for error_log_state */ +/*******************************/ +#define ERRLOG_STATE_OFF 0 /* error log is switched off, nothing to do */ +#define ERRLOG_STATE_ON 1 /* error log is switched on, wait for data */ +#define ERRLOG_STATE_START 2 /* start error logging */ +#define ERRLOG_STATE_STOP 3 /* stop error logging */ + +/*******************************/ +/* data structure for one card */ +/*******************************/ +typedef struct HYSDN_CARD { + + /* general variables for the cards */ + int myid; /* own driver card id */ + uchar bus; /* pci bus the card is connected to */ + uchar devfn; /* slot+function bit encoded */ + word subsysid; /* PCI subsystem id */ + uchar brdtype; /* type of card */ + uint bchans; /* number of available B-channels */ + uint faxchans; /* number of available fax-channels */ + uchar mac_addr[6]; /* MAC Address read from card */ + uint irq; /* interrupt number */ + uint iobase; /* IO-port base address */ + ulong plxbase; /* PLX memory base */ + ulong membase; /* DPRAM memory base */ + ulong memend; /* DPRAM memory end */ + void *dpram; /* mapped dpram */ + int state; /* actual state of card -> CARD_STATE_** */ + struct HYSDN_CARD *next; /* pointer to next card */ + + /* data areas for the /proc file system */ + void *proclog; /* pointer to proclog filesystem specific data */ + void *procconf; /* pointer to procconf filesystem specific data */ + + /* debugging and logging */ + uchar err_log_state; /* actual error log state of the card */ + ulong debug_flags; /* tells what should be debugged and where */ + void (*set_errlog_state) (struct HYSDN_CARD *, int); + + /* interrupt handler + interrupt synchronisation */ + struct tq_struct irq_queue; /* interrupt task queue */ + uchar volatile irq_enabled; /* interrupt enabled if != 0 */ + uchar volatile hw_lock; /* hardware is currently locked -> no access */ + + /* boot process */ + void *boot; /* pointer to boot private data */ + int (*writebootimg) (struct HYSDN_CARD *, uchar *, ulong); + int (*writebootseq) (struct HYSDN_CARD *, uchar *, int); + int (*waitpofready) (struct HYSDN_CARD *); + int (*testram) (struct HYSDN_CARD *); + + /* scheduler for data transfer (only async parts) */ + uchar async_data[256]; /* async data to be sent (normally for config) */ + word volatile async_len; /* length of data to sent */ + word volatile async_channel; /* channel number for async transfer */ + int volatile async_busy; /* flag != 0 sending in progress */ + int volatile net_tx_busy; /* a network packet tx is in progress */ + + /* network interface */ + void *netif; /* pointer to network structure */ + + /* init and deinit stopcard for booting, too */ + void (*stopcard) (struct HYSDN_CARD *); + void (*releasehardware) (struct HYSDN_CARD *); +} hysdn_card; + + +/*****************/ +/* exported vars */ +/*****************/ +extern int cardmax; /* number of found cards */ +extern hysdn_card *card_root; /* pointer to first card */ + + + +/*************************/ +/* im/exported functions */ +/*************************/ +extern int printk(const char *fmt,...); +extern char *hysdn_getrev(const char *); + +/* hysdn_procconf.c */ +extern int hysdn_procconf_init(void); /* init proc config filesys */ +extern void hysdn_procconf_release(void); /* deinit proc config filesys */ + +/* hysdn_proclog.c */ +extern int hysdn_proclog_init(hysdn_card *); /* init proc log entry */ +extern void hysdn_proclog_release(hysdn_card *); /* deinit proc log entry */ +extern void put_log_buffer(hysdn_card *, char *); /* output log data */ +extern void hysdn_addlog(hysdn_card *, char *,...); /* output data to log */ +extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int); /* output card log */ + +/* boardergo.c */ +extern int ergo_inithardware(hysdn_card * card); /* get hardware -> module init */ + +/* hysdn_boot.c */ +extern int pof_write_close(hysdn_card *); /* close proc file after writing pof */ +extern int pof_write_open(hysdn_card *, uchar **); /* open proc file for writing pof */ +extern int pof_write_buffer(hysdn_card *, int); /* write boot data to card */ +extern int EvalSysrTokData(hysdn_card *, uchar *, int); /* Check Sysready Token Data */ + +/* hysdn_sched.c */ +extern int hysdn_sched_tx(hysdn_card *, uchar *, word volatile *, word volatile *, + word); +extern int hysdn_sched_rx(hysdn_card *, uchar *, word, word); +extern int hysdn_tx_cfgline(hysdn_card *, uchar *, word); /* send one cfg line */ + +/* hysdn_net.c */ +extern char *hysdn_net_revision; +extern int hysdn_net_create(hysdn_card *); /* create a new net device */ +extern int hysdn_net_release(hysdn_card *); /* delete the device */ +extern char *hysdn_net_getname(hysdn_card *); /* get name of net interface */ +extern void hysdn_tx_netack(hysdn_card *); /* acknowledge a packet tx */ +extern struct sk_buff *hysdn_tx_netget(hysdn_card *); /* get next network packet */ +extern void hysdn_rx_netpkt(hysdn_card *, uchar *, word); /* rxed packet from network */ diff --git a/drivers/isdn/hysdn/hysdn_init.c b/drivers/isdn/hysdn/hysdn_init.c new file mode 100644 index 000000000..eb3fed4d3 --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_init.c @@ -0,0 +1,243 @@ +/* $Id: hysdn_init.c,v 1.1 2000/02/10 19:45:18 werner Exp $ + + * Linux driver for HYSDN cards, init functions. + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_init.c,v $ + * Revision 1.1 2000/02/10 19:45:18 werner + * + * Initial release + * + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/poll.h> +#include <linux/vmalloc.h> +#include <linux/malloc.h> +#include <linux/pci.h> + +#include "hysdn_defs.h" + +static char *hysdn_init_revision = "$Revision: 1.1 $"; +int cardmax; /* number of found cards */ +hysdn_card *card_root = NULL; /* pointer to first card */ + +/**********************************************/ +/* table assigning PCI-sub ids to board types */ +/* the last entry contains all 0 */ +/**********************************************/ +static struct { + word subid; /* PCI sub id */ + uchar cardtyp; /* card type assigned */ +} pci_subid_map[] = { + + { + PCI_SUB_ID_METRO, BD_METRO + }, + { + PCI_SUB_ID_CHAMP2, BD_CHAMP2 + }, + { + PCI_SUB_ID_ERGO, BD_ERGO + }, + { + PCI_SUB_ID_OLD_ERGO, BD_ERGO + }, + { + 0, 0 + } /* terminating entry */ +}; + +/*********************************************************************/ +/* search_cards searches for available cards in the pci config data. */ +/* If a card is found, the card structure is allocated and the cards */ +/* ressources are reserved. cardmax is incremented. */ +/*********************************************************************/ +static void +search_cards(void) +{ + struct pci_dev *akt_pcidev = NULL; + hysdn_card *card, *card_last; + uchar irq; + int i; + + card_root = NULL; + card_last = NULL; + while ((akt_pcidev = pci_find_device(PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_PLX, + akt_pcidev)) != NULL) { + + if (!(card = kmalloc(sizeof(hysdn_card), GFP_KERNEL))) { + printk(KERN_ERR "HYSDN: unable to alloc device mem \n"); + return; + } + memset(card, 0, sizeof(hysdn_card)); + card->myid = cardmax; /* set own id */ + card->bus = akt_pcidev->bus->number; + card->devfn = akt_pcidev->devfn; /* slot + function */ + pcibios_read_config_word(card->bus, card->devfn, PCI_SUBSYSTEM_ID, &card->subsysid); + pcibios_read_config_byte(card->bus, card->devfn, PCI_INTERRUPT_LINE, &irq); + card->irq = irq; + card->iobase = akt_pcidev->resource[ PCI_REG_PLX_IO_BASE].start & PCI_BASE_ADDRESS_IO_MASK; + card->plxbase = akt_pcidev->resource[ PCI_REG_PLX_MEM_BASE].start; + card->membase = akt_pcidev->resource[ PCI_REG_MEMORY_BASE].start; + card->brdtype = BD_NONE; /* unknown */ + card->debug_flags = DEF_DEB_FLAGS; /* set default debug */ + card->faxchans = 0; /* default no fax channels */ + card->bchans = 2; /* and 2 b-channels */ + for (i = 0; pci_subid_map[i].subid; i++) + if (pci_subid_map[i].subid == card->subsysid) { + card->brdtype = pci_subid_map[i].cardtyp; + break; + } + if (card->brdtype != BD_NONE) { + if (ergo_inithardware(card)) { + printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase); + kfree(card); + continue; + } + } else { + printk(KERN_WARNING "HYSDN: unknown card id 0x%04x\n", card->subsysid); + kfree(card); /* release mem */ + continue; + } + cardmax++; + card->next = NULL; /*end of chain */ + if (card_last) + card_last->next = card; /* pointer to next card */ + else + card_root = card; + card_last = card; /* new chain end */ + } /* device found */ +} /* search_cards */ + +/************************************************************************************/ +/* free_resources frees the acquired PCI resources and returns the allocated memory */ +/************************************************************************************/ +static void +free_resources(void) +{ + hysdn_card *card; + + while (card_root) { + card = card_root; + if (card->releasehardware) + card->releasehardware(card); /* free all hardware resources */ + card_root = card_root->next; /* remove card from chain */ + kfree(card); /* return mem */ + + } /* while card_root */ +} /* free_resources */ + +/**************************************************************************/ +/* stop_cards disables (hardware resets) all cards and disables interrupt */ +/**************************************************************************/ +static void +stop_cards(void) +{ + hysdn_card *card; + + card = card_root; /* first in chain */ + while (card) { + if (card->stopcard) + card->stopcard(card); + card = card->next; /* remove card from chain */ + } /* while card */ +} /* stop_cards */ + + +/****************************************************************************/ +/* The module startup and shutdown code. Only compiled when used as module. */ +/* Using the driver as module is always advisable, because the booting */ +/* image becomes smaller and the driver code is only loaded when needed. */ +/* Additionally newer versions may be activated without rebooting. */ +/****************************************************************************/ +#ifdef CONFIG_MODULES + +/******************************************************/ +/* extract revision number from string for log output */ +/******************************************************/ +char * +hysdn_getrev(const char *revision) +{ + char *rev; + char *p; + + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "???"; + return rev; +} + + +/****************************************************************************/ +/* init_module is called once when the module is loaded to do all necessary */ +/* things like autodetect... */ +/* If the return value of this function is 0 the init has been successfull */ +/* and the module is added to the list in /proc/modules, otherwise an error */ +/* is assumed and the module will not be kept in memory. */ +/****************************************************************************/ +int +init_module(void) +{ + char tmp[50]; + + strcpy(tmp, hysdn_init_revision); + printk(KERN_NOTICE "HYSDN: module Rev: %s loaded\n", hysdn_getrev(tmp)); + strcpy(tmp, hysdn_net_revision); + printk(KERN_NOTICE "HYSDN: network interface Rev: %s \n", hysdn_getrev(tmp)); + if (!pci_present()) { + printk(KERN_ERR "HYSDN: no PCI bus present, module not loaded\n"); + return (-1); + } + search_cards(); + printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax); + + if (hysdn_procconf_init()) { + free_resources(); /* proc file_sys not created */ + return (-1); + } + return (0); /* no error */ +} /* init_module */ + + +/***********************************************************************/ +/* cleanup_module is called when the module is released by the kernel. */ +/* The routine is only called if init_module has been successfull and */ +/* the module counter has a value of 0. Otherwise this function will */ +/* not be called. This function must release all resources still allo- */ +/* cated as after the return from this function the module code will */ +/* be removed from memory. */ +/***********************************************************************/ +void +cleanup_module(void) +{ + + stop_cards(); + hysdn_procconf_release(); + free_resources(); + printk(KERN_NOTICE "HYSDN: module unloaded\n"); +} /* cleanup_module */ + +#endif /* CONFIG_MODULES */ diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c new file mode 100644 index 000000000..1a3d21012 --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_net.c @@ -0,0 +1,378 @@ +/* $Id: hysdn_net.c,v 1.3 2000/02/14 19:24:12 werner Exp $ + + * Linux driver for HYSDN cards, net (ethernet type) handling routines. + * + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This net module has been inspired by the skeleton driver from + * Donald Becker (becker@CESDIS.gsfc.nasa.gov) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_net.c,v $ + * Revision 1.3 2000/02/14 19:24:12 werner + * + * Removed superflous file + * + * Revision 1.2 2000/02/13 17:32:19 werner + * + * Added support for new network layer of 2.3.43 and 44 kernels and tested driver. + * + * Revision 1.1 2000/02/10 19:45:18 werner + * + * Initial release + * + * + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/inetdevice.h> + +#include "hysdn_defs.h" + +/* store the actual version for log reporting */ +char *hysdn_net_revision = "$Revision: 1.3 $"; + +#define MAX_SKB_BUFFERS 20 /* number of buffers for keeping TX-data */ + +/****************************************************************************/ +/* structure containing the complete network data. The structure is aligned */ +/* in a way that both, the device and statistics are kept inside it. */ +/* for proper access, the device structure MUST be the first var/struct */ +/* inside the definition. */ +/****************************************************************************/ +struct net_local { + struct net_device netdev; /* the network device */ + struct net_device_stats stats; + /* additional vars may be added here */ + char dev_name[9]; /* our own device name */ + + /* Tx control lock. This protects the transmit buffer ring + * state along with the "tx full" state of the driver. This + * means all netif_queue flow control actions are protected + * by this lock as well. + */ + spinlock_t lock; + struct sk_buff *skbs[MAX_SKB_BUFFERS]; /* pointers to tx-skbs */ + int in_idx, out_idx; /* indexes to buffer ring */ + int sk_count; /* number of buffers currently in ring */ + + int is_open; /* flag controlling module locking */ +}; /* net_local */ + + +/*****************************************************/ +/* Get the current statistics for this card. */ +/* This may be called with the card open or closed ! */ +/*****************************************************/ +static struct net_device_stats * +net_get_stats(struct net_device *dev) +{ + return (&((struct net_local *) dev)->stats); +} /* net_device_stats */ + +/*********************************************************************/ +/* Open/initialize the board. This is called (in the current kernel) */ +/* sometime after booting when the 'ifconfig' program is run. */ +/* This routine should set everything up anew at each open, even */ +/* registers that "should" only need to be set once at boot, so that */ +/* there is non-reboot way to recover if something goes wrong. */ +/*********************************************************************/ +static int +net_open(struct net_device *dev) +{ + struct in_device *in_dev; + hysdn_card *card = dev->priv; + int i; + + if (!((struct net_local *) dev)->is_open) + MOD_INC_USE_COUNT; /* increment only if interface is actually down */ + ((struct net_local *) dev)->is_open = 1; /* device actually open */ + + netif_start_queue(dev); /* start tx-queueing */ + + /* Fill in the MAC-level header (if not already set) */ + if (!card->mac_addr[0]) { + for (i = 0; i < ETH_ALEN - sizeof(ulong); i++) + dev->dev_addr[i] = 0xfc; + if ((in_dev = dev->ip_ptr) != NULL) { + struct in_ifaddr *ifa = in_dev->ifa_list; + if (ifa != NULL) + memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ulong)), &ifa->ifa_local, sizeof(ulong)); + } + } else + memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN); + + return (0); +} /* net_open */ + +/*******************************************/ +/* flush the currently occupied tx-buffers */ +/* must only be called when device closed */ +/*******************************************/ +static void +flush_tx_buffers(struct net_local *nl) +{ + + while (nl->sk_count) { + dev_kfree_skb(nl->skbs[nl->out_idx++]); /* free skb */ + if (nl->out_idx >= MAX_SKB_BUFFERS) + nl->out_idx = 0; /* wrap around */ + nl->sk_count--; + } +} /* flush_tx_buffers */ + + +/*********************************************************************/ +/* close/decativate the device. The device is not removed, but only */ +/* deactivated. */ +/*********************************************************************/ +static int +net_close(struct net_device *dev) +{ + + netif_stop_queue(dev); /* disable queueing */ + + if (((struct net_local *) dev)->is_open) + MOD_DEC_USE_COUNT; /* adjust module counter */ + ((struct net_local *) dev)->is_open = 0; + flush_tx_buffers((struct net_local *) dev); + + return (0); /* success */ +} /* net_close */ + +/************************************/ +/* send a packet on this interface. */ +/* new style for kernel >= 2.3.33 */ +/************************************/ +static int +net_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) dev; + + spin_lock_irq(&lp->lock); + + lp->skbs[lp->in_idx++] = skb; /* add to buffer list */ + if (lp->in_idx >= MAX_SKB_BUFFERS) + lp->in_idx = 0; /* wrap around */ + lp->sk_count++; /* adjust counter */ + dev->trans_start = jiffies; + + /* If we just used up the very last entry in the + * TX ring on this device, tell the queueing + * layer to send no more. + */ + if (lp->sk_count >= MAX_SKB_BUFFERS) + netif_stop_queue(dev); + + /* When the TX completion hw interrupt arrives, this + * is when the transmit statistics are updated. + */ + + spin_unlock_irq(&lp->lock); + + if (lp->sk_count <= 3) { + queue_task(&((hysdn_card *) dev->priv)->irq_queue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + return (0); /* success */ +} /* net_send_packet */ + + + +/***********************************************************************/ +/* acknowlegde a packet send. The network layer will be informed about */ +/* completion */ +/***********************************************************************/ +void +hysdn_tx_netack(hysdn_card * card) +{ + struct net_local *lp = card->netif; + + if (!lp) + return; /* non existing device */ + + + if (!lp->sk_count) + return; /* error condition */ + + lp->stats.tx_packets++; + lp->stats.tx_bytes += lp->skbs[lp->out_idx]->len; + + dev_kfree_skb(lp->skbs[lp->out_idx++]); /* free skb */ + if (lp->out_idx >= MAX_SKB_BUFFERS) + lp->out_idx = 0; /* wrap around */ + + if (lp->sk_count-- == MAX_SKB_BUFFERS) /* dec usage count */ + netif_start_queue((struct net_device *) lp); +} /* hysdn_tx_netack */ + +/*****************************************************/ +/* we got a packet from the network, go and queue it */ +/*****************************************************/ +void +hysdn_rx_netpkt(hysdn_card * card, uchar * buf, word len) +{ + struct net_local *lp = card->netif; + struct sk_buff *skb; + + if (!lp) + return; /* non existing device */ + + lp->stats.rx_bytes += len; + + skb = dev_alloc_skb(len); + if (skb == NULL) { + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", + lp->netdev.name); + lp->stats.rx_dropped++; + return; + } + skb->dev = &lp->netdev; + + /* copy the data */ + memcpy(skb_put(skb, len), buf, len); + + /* determine the used protocol */ + skb->protocol = eth_type_trans(skb, &lp->netdev); + + netif_rx(skb); + lp->stats.rx_packets++; /* adjust packet count */ + +} /* hysdn_rx_netpkt */ + +/*****************************************************/ +/* return the pointer to a network packet to be send */ +/*****************************************************/ +struct sk_buff * +hysdn_tx_netget(hysdn_card * card) +{ + struct net_local *lp = card->netif; + + if (!lp) + return (NULL); /* non existing device */ + + if (!lp->sk_count) + return (NULL); /* nothing available */ + + return (lp->skbs[lp->out_idx]); /* next packet to send */ +} /* hysdn_tx_netget */ + + +/*******************************************/ +/* init function called by register device */ +/*******************************************/ +static int +net_init(struct net_device *dev) +{ + /* setup the function table */ + dev->open = net_open; + dev->stop = net_close; + dev->hard_start_xmit = net_send_packet; + dev->get_stats = net_get_stats; + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + return (0); /* success */ +} /* net_init */ + +/*****************************************************************************/ +/* hysdn_net_create creates a new net device for the given card. If a device */ +/* already exists, it will be deleted and created a new one. The return value */ +/* 0 announces success, else a negative error code will be returned. */ +/*****************************************************************************/ +int +hysdn_net_create(hysdn_card * card) +{ + struct net_device *dev; + int i; + + hysdn_net_release(card); /* release an existing net device */ + if ((dev = kmalloc(sizeof(struct net_local), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING "HYSDN: unable to allocate mem\n"); + if (card->debug_flags & LOG_NET_INIT) + return (-ENOMEM); + } + memset(dev, 0, sizeof(struct net_local)); /* clean the structure */ + + spin_lock_init(&((struct net_local *) dev)->lock); + + /* initialise necessary or informing fields */ + dev->base_addr = card->iobase; /* IO address */ + dev->irq = card->irq; /* irq */ + dev->init = net_init; /* the init function of the device */ + dev->name = ((struct net_local *) dev)->dev_name; /* device name */ + if ((i = register_netdev(dev))) { + printk(KERN_WARNING "HYSDN: unable to create network device\n"); + kfree(dev); + return (i); + } + dev->priv = card; /* remember pointer to own data structure */ + card->netif = dev; /* setup the local pointer */ + + if (card->debug_flags & LOG_NET_INIT) + hysdn_addlog(card, "network device created"); + return (0); /* and return success */ +} /* hysdn_net_create */ + +/***************************************************************************/ +/* hysdn_net_release deletes the net device for the given card. The return */ +/* value 0 announces success, else a negative error code will be returned. */ +/***************************************************************************/ +int +hysdn_net_release(hysdn_card * card) +{ + struct net_device *dev = card->netif; + + if (!dev) + return (0); /* non existing */ + + card->netif = NULL; /* clear out pointer */ + dev->stop(dev); /* close the device */ + + flush_tx_buffers((struct net_local *) dev); /* empty buffers */ + + unregister_netdev(dev); /* release the device */ + kfree(dev); /* release the memory allocated */ + if (card->debug_flags & LOG_NET_INIT) + hysdn_addlog(card, "network device deleted"); + + return (0); /* always successfull */ +} /* hysdn_net_release */ + +/*****************************************************************************/ +/* hysdn_net_getname returns a pointer to the name of the network interface. */ +/* if the interface is not existing, a "-" is returned. */ +/*****************************************************************************/ +char * +hysdn_net_getname(hysdn_card * card) +{ + struct net_device *dev = card->netif; + + if (!dev) + return ("-"); /* non existing */ + + return (dev->name); +} /* hysdn_net_getname */ diff --git a/drivers/isdn/hysdn/hysdn_pof.h b/drivers/isdn/hysdn/hysdn_pof.h new file mode 100644 index 000000000..619c07897 --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_pof.h @@ -0,0 +1,95 @@ +/* $Id: hysdn_pof.h,v 1.1 2000/02/10 19:44:30 werner Exp $ + + * Linux driver for HYSDN cards, definitions used for handling pof-files. + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_pof.h,v $ + * Revision 1.1 2000/02/10 19:44:30 werner + * + * Initial release + * + * + */ + +/************************/ +/* POF specific defines */ +/************************/ +#define BOOT_BUF_SIZE 0x1000 /* =4096, maybe moved to other h file */ +#define CRYPT_FEEDTERM 0x8142 +#define CRYPT_STARTTERM 0x81a5 + /* max. timeout time in seconds + * from end of booting to POF is ready + */ +#define POF_READY_TIME_OUT_SEC 10 + +/**********************************/ +/* defines for 1.stage boot image */ +/**********************************/ + +/* the POF file record containing the boot loader image + * has 2 pages a 16KB: + * 1. page contains the high 16-bit part of the 32-bit E1 words + * 2. page contains the low 16-bit part of the 32-bit E1 words + * + * In each 16KB page we assume the start of the boot loader code + * in the highest 2KB part (at offset 0x3800); + * the rest (0x0000..0x37FF) is assumed to contain 0 bytes. + */ + +#define POF_BOOT_LOADER_PAGE_SIZE 0x4000 /* =16384U */ +#define POF_BOOT_LOADER_TOTAL_SIZE (2U*POF_BOOT_LOADER_PAGE_SIZE) + +#define POF_BOOT_LOADER_CODE_SIZE 0x0800 /* =2KB =2048U */ + + /* offset in boot page, where loader code may start */ + /* =0x3800= 14336U */ +#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE) + + +/*--------------------------------------POF file record structs------------*/ +typedef struct PofFileHdr_tag { /* Pof file header */ +/*00 */ ulong Magic __attribute__((packed)); +/*04 */ ulong N_PofRecs __attribute__((packed)); +/*08 */ +} tPofFileHdr; + +typedef struct PofRecHdr_tag { /* Pof record header */ +/*00 */ word PofRecId __attribute__((packed)); +/*02 */ ulong PofRecDataLen __attribute__((packed)); +/*06 */ +} tPofRecHdr; + +typedef struct PofTimeStamp_tag { +/*00 */ ulong UnixTime __attribute__((packed)); + /*04 */ uchar DateTimeText[0x28] __attribute__((packed)); + /* =40 */ +/*2C */ +} tPofTimeStamp; + + /* tPofFileHdr.Magic value: */ +#define TAGFILEMAGIC 0x464F501AUL + /* tPofRecHdr.PofRecId values: */ +#define TAG_ABSDATA 0x1000 /* abs. data */ +#define TAG_BOOTDTA 0x1001 /* boot data */ +#define TAG_COMMENT 0x0020 +#define TAG_SYSCALL 0x0021 +#define TAG_FLOWCTRL 0x0022 +#define TAG_TIMESTMP 0x0010 /* date/time stamp of version */ +#define TAG_CABSDATA 0x1100 /* crypted abs. data */ +#define TAG_CBOOTDTA 0x1101 /* crypted boot data */ diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c new file mode 100644 index 000000000..81e1b84f0 --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_procconf.c @@ -0,0 +1,485 @@ +/* $Id: hysdn_procconf.c,v 1.2 2000/02/14 19:23:03 werner Exp $ + + * Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions. + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_procconf.c,v $ + * Revision 1.2 2000/02/14 19:23:03 werner + * + * Changed handling of proc filesystem tables to a more portable version + * + * Revision 1.1 2000/02/10 19:45:18 werner + * + * Initial release + * + * + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/pci.h> + +#include "hysdn_defs.h" + +static char *hysdn_procconf_revision = "$Revision: 1.2 $"; + +#define INFO_OUT_LEN 80 /* length of info line including lf */ + +/********************************************************/ +/* defines and data structure for conf write operations */ +/********************************************************/ +#define CONF_STATE_DETECT 0 /* waiting for detect */ +#define CONF_STATE_CONF 1 /* writing config data */ +#define CONF_STATE_POF 2 /* writing pof data */ +#define CONF_LINE_LEN 80 /* 80 chars max */ + +struct conf_writedata { + hysdn_card *card; /* card the device is connected to */ + int buf_size; /* actual number of bytes in the buffer */ + int needed_size; /* needed size when reading pof */ + int state; /* actual interface states from above constants */ + uchar conf_line[CONF_LINE_LEN]; /* buffered conf line */ + word channel; /* active channel number */ + uchar *pof_buffer; /* buffer when writing pof */ +}; + +/***********************************************************************/ +/* process_line parses one config line and transfers it to the card if */ +/* necessary. */ +/* if the return value is negative an error occured. */ +/***********************************************************************/ +static int +process_line(struct conf_writedata *cnf) +{ + uchar *cp = cnf->conf_line; + int i; + + if (cnf->card->debug_flags & LOG_CNF_LINE) + hysdn_addlog(cnf->card, "conf line: %s", cp); + + if (*cp == '-') { /* option */ + cp++; /* point to option char */ + + if (*cp++ != 'c') + return (0); /* option unknown or used */ + i = 0; /* start value for channel */ + while ((*cp <= '9') && (*cp >= '0')) + i = i * 10 + *cp++ - '0'; /* get decimal number */ + if (i > 65535) { + if (cnf->card->debug_flags & LOG_CNF_MISC) + hysdn_addlog(cnf->card, "conf channel invalid %d", i); + return (-ERR_INV_CHAN); /* invalid channel */ + } + cnf->channel = i & 0xFFFF; /* set new channel number */ + return (0); /* success */ + } /* option */ + if (*cp == '*') { /* line to send */ + if (cnf->card->debug_flags & LOG_CNF_DATA) + hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp); + return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1, + cnf->channel)); /* send the line without * */ + } /* line to send */ + return (0); +} /* process_line */ + +/*************************/ +/* dummy file operations */ +/*************************/ +static loff_t +hysdn_dummy_lseek(struct file *file, loff_t offset, int orig) +{ + return -ESPIPE; +} /* hysdn_dummy_lseek */ + +/***********************************/ +/* conf file operations and tables */ +/***********************************/ + +/****************************************************/ +/* write conf file -> boot or send cfg line to card */ +/****************************************************/ +static ssize_t +hysdn_conf_write(struct file *file, const char *buf, size_t count, loff_t * off) +{ + struct conf_writedata *cnf; + int i; + uchar ch, *cp; + + if (&file->f_pos != off) /* fs error check */ + return (-ESPIPE); + if (!count) + return (0); /* nothing to handle */ + + if (!(cnf = file->private_data)) + return (-EFAULT); /* should never happen */ + + if (cnf->state == CONF_STATE_DETECT) { /* auto detect cnf or pof data */ + if (copy_from_user(&ch, buf, 1)) /* get first char for detect */ + return (-EFAULT); + + if (ch == 0x1A) { + /* we detected a pof file */ + if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0) + return (cnf->needed_size); /* an error occured -> exit */ + cnf->buf_size = 0; /* buffer is empty */ + cnf->state = CONF_STATE_POF; /* new state */ + } else { + /* conf data has been detected */ + cnf->buf_size = 0; /* buffer is empty */ + cnf->state = CONF_STATE_CONF; /* requested conf data write */ + if (cnf->card->state != CARD_STATE_RUN) + return (-ERR_NOT_BOOTED); + cnf->conf_line[CONF_LINE_LEN - 1] = 0; /* limit string length */ + cnf->channel = 4098; /* default channel for output */ + } + } /* state was auto detect */ + if (cnf->state == CONF_STATE_POF) { /* pof write active */ + i = cnf->needed_size - cnf->buf_size; /* bytes still missing for write */ + if (i <= 0) + return (-EINVAL); /* size error handling pof */ + + if (i < count) + count = i; /* limit requested number of bytes */ + if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count)) + return (-EFAULT); /* error while copying */ + cnf->buf_size += count; + + if (cnf->needed_size == cnf->buf_size) { + cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size); /* write data */ + if (cnf->needed_size <= 0) { + cnf->card->state = CARD_STATE_BOOTERR; /* show boot error */ + return (cnf->needed_size); /* an error occured */ + } + cnf->buf_size = 0; /* buffer is empty again */ + } + } + /* pof write active */ + else { /* conf write active */ + + if (cnf->card->state != CARD_STATE_RUN) { + if (cnf->card->debug_flags & LOG_CNF_MISC) + hysdn_addlog(cnf->card, "cnf write denied -> not booted"); + return (-ERR_NOT_BOOTED); + } + i = (CONF_LINE_LEN - 1) - cnf->buf_size; /* bytes available in buffer */ + if (i > 0) { + /* copy remaining bytes into buffer */ + + if (count > i) + count = i; /* limit transfer */ + if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count)) + return (-EFAULT); /* error while copying */ + + i = count; /* number of chars in buffer */ + cp = cnf->conf_line + cnf->buf_size; + while (i) { + /* search for end of line */ + if ((*cp < ' ') && (*cp != 9)) + break; /* end of line found */ + cp++; + i--; + } /* search for end of line */ + + if (i) { + /* delimiter found */ + *cp++ = 0; /* string termination */ + count -= (i - 1); /* subtract remaining bytes from count */ + while ((i) && (*cp < ' ') && (*cp != 9)) { + i--; /* discard next char */ + count++; /* mark as read */ + cp++; /* next char */ + } + cnf->buf_size = 0; /* buffer is empty after transfer */ + if ((i = process_line(cnf)) < 0) /* handle the line */ + count = i; /* return the error */ + } + /* delimiter found */ + else { + cnf->buf_size += count; /* add chars to string */ + if (cnf->buf_size >= CONF_LINE_LEN - 1) { + if (cnf->card->debug_flags & LOG_CNF_MISC) + hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count); + return (-ERR_CONF_LONG); + } + } /* not delimited */ + + } + /* copy remaining bytes into buffer */ + else { + if (cnf->card->debug_flags & LOG_CNF_MISC) + hysdn_addlog(cnf->card, "cnf line too long"); + return (-ERR_CONF_LONG); + } + } /* conf write active */ + + return (count); +} /* hysdn_conf_write */ + +/*******************************************/ +/* read conf file -> output card info data */ +/*******************************************/ +static ssize_t +hysdn_conf_read(struct file *file, char *buf, size_t count, loff_t * off) +{ + char *cp; + int i; + + if (off != &file->f_pos) /* fs error check */ + return -ESPIPE; + + if (file->f_mode & FMODE_READ) { + if (!(cp = file->private_data)) + return (-EFAULT); /* should never happen */ + i = strlen(cp); /* get total string length */ + if (*off < i) { + /* still bytes to transfer */ + cp += *off; /* point to desired data offset */ + i -= *off; /* remaining length */ + if (i > count) + i = count; /* limit length to transfer */ + if (copy_to_user(buf, cp, i)) + return (-EFAULT); /* copy error */ + *off += i; /* adjust offset */ + } else + return (0); + } else + return (-EPERM); /* no permission to read */ + + return (i); +} /* hysdn_conf_read */ + +/******************/ +/* open conf file */ +/******************/ +static int +hysdn_conf_open(struct inode *ino, struct file *filep) +{ + hysdn_card *card; + struct proc_dir_entry *pd; + struct conf_writedata *cnf; + char *cp, *tmp; + + MOD_INC_USE_COUNT; /* lock module */ + + /* now search the addressed card */ + card = card_root; + while (card) { + pd = card->procconf; + if (pd->low_ino == (ino->i_ino & 0xFFFF)) + break; + card = card->next; /* search next entry */ + } + if (!card) { + MOD_DEC_USE_COUNT; /* unlock module */ + return (-ENODEV); /* device is unknown/invalid */ + } + if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL)) + hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x", + filep->f_uid, filep->f_gid, filep->f_mode); + + if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) { + /* write only access -> write boot file or conf line */ + + if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) { + MOD_DEC_USE_COUNT; + return (-EFAULT); + } + cnf->card = card; + cnf->buf_size = 0; /* nothing buffered */ + cnf->state = CONF_STATE_DETECT; /* start auto detect */ + filep->private_data = cnf; + + } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { + /* read access -> output card info data */ + + if (!(tmp = (char *) kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) { + MOD_DEC_USE_COUNT; + return (-EFAULT); /* out of memory */ + } + filep->private_data = tmp; /* start of string */ + + /* first output a headline */ + sprintf(tmp, "id bus slot type irq iobase dp-mem b-chans fax-chans state device"); + cp = tmp; /* start of string */ + while (*cp) + cp++; + while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN) + *cp++ = ' '; + *cp++ = '\n'; + + /* and now the data */ + sprintf(cp, "%d %3d %4d %4d %3d 0x%04x 0x%08x %7d %9d %3d %s", + card->myid, + card->bus, + PCI_SLOT(card->devfn), + card->brdtype, + card->irq, + card->iobase, + card->membase, + card->bchans, + card->faxchans, + card->state, + hysdn_net_getname(card)); + while (*cp) + cp++; + while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN) + *cp++ = ' '; + *cp++ = '\n'; + *cp = 0; /* end of string */ + } else { /* simultaneous read/write access forbidden ! */ + MOD_DEC_USE_COUNT; /* unlock module */ + return (-EPERM); /* no permission this time */ + } + return (0); +} /* hysdn_conf_open */ + +/***************************/ +/* close a config file. */ +/***************************/ +static int +hysdn_conf_close(struct inode *ino, struct file *filep) +{ + hysdn_card *card; + struct conf_writedata *cnf; + int retval = 0; + struct proc_dir_entry *pd; + + /* search the addressed card */ + card = card_root; + while (card) { + pd = card->procconf; + if (pd->low_ino == (ino->i_ino & 0xFFFF)) + break; + card = card->next; /* search next entry */ + } + if (!card) { + return (-ENODEV); /* device is unknown/invalid */ + } + if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL)) + hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x", + filep->f_uid, filep->f_gid, filep->f_mode); + + if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) { + /* write only access -> write boot file or conf line */ + if (filep->private_data) { + cnf = filep->private_data; + + if (cnf->state == CONF_STATE_POF) + retval = pof_write_close(cnf->card); /* close the pof write */ + kfree(filep->private_data); /* free allocated memory for buffer */ + + } /* handle write private data */ + } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { + /* read access -> output card info data */ + + if (filep->private_data) + kfree(filep->private_data); /* release memory */ + } + MOD_DEC_USE_COUNT; /* reduce usage count */ + return (retval); +} /* hysdn_conf_close */ + +/******************************************************/ +/* table for conf filesystem functions defined above. */ +/******************************************************/ +static struct file_operations conf_fops = +{ + hysdn_dummy_lseek, + hysdn_conf_read, + hysdn_conf_write, + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + hysdn_conf_open, + NULL, /* flush */ + hysdn_conf_close, + NULL /* fsync */ +}; + +static struct inode_operations conf_inode_operations; + +/*****************************/ +/* hysdn subdir in /proc/net */ +/*****************************/ +struct proc_dir_entry *hysdn_proc_entry = NULL; + +/*******************************************************************************/ +/* hysdn_procconf_init is called when the module is loaded and after the cards */ +/* have been detected. The needed proc dir and card config files are created. */ +/* The log init is called at last. */ +/*******************************************************************************/ +int +hysdn_procconf_init(void) +{ + hysdn_card *card; + uchar conf_name[20]; + + hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, proc_net); + if (!hysdn_proc_entry) { + printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n"); + return (-1); + } + card = card_root; /* point to first card */ + while (card) { + + sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid); + if ((card->procconf = (void *) create_proc_entry(conf_name, + S_IFREG | S_IRUGO | S_IWUSR, + hysdn_proc_entry)) != NULL) { + memset(&conf_inode_operations, 0, sizeof(struct inode_operations)); + conf_inode_operations.default_file_ops = &conf_fops; + + ((struct proc_dir_entry *) card->procconf)->ops = &conf_inode_operations; + hysdn_proclog_init(card); /* init the log file entry */ + } + card = card->next; /* next entry */ + } + + printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procconf_revision)); + return (0); +} /* hysdn_procconf_init */ + +/*************************************************************************************/ +/* hysdn_procconf_release is called when the module is unloaded and before the cards */ +/* resources are released. The module counter is assumed to be 0 ! */ +/*************************************************************************************/ +void +hysdn_procconf_release(void) +{ + hysdn_card *card; + uchar conf_name[20]; + + card = card_root; /* start with first card */ + while (card) { + + sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid); + if (card->procconf) + remove_proc_entry(conf_name, hysdn_proc_entry); + + hysdn_proclog_release(card); /* init the log file entry */ + + card = card->next; /* point to next card */ + } + + remove_proc_entry(PROC_SUBDIR_NAME, proc_net); +} /* hysdn_procfs_release */ diff --git a/drivers/isdn/hysdn/hysdn_procfs.c b/drivers/isdn/hysdn/hysdn_procfs.c new file mode 100644 index 000000000..d70d350e9 --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_procfs.c @@ -0,0 +1,502 @@ +/* $Id: hysdn_procfs.c,v 1.1 2000/02/10 19:45:18 werner Exp $ + + * Linux driver for HYSDN cards, /proc/net filesystem log functions. + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_procfs.c,v $ + * Revision 1.1 2000/02/10 19:45:18 werner + * + * Initial release + * + * + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/pci.h> + +#include "hysdn_defs.h" + +static char *hysdn_procfs_revision = "$Revision: 1.1 $"; + +#define INFO_OUT_LEN 80 /* length of info line including lf */ + +/*************************************************/ +/* structure keeping ascii log for device output */ +/*************************************************/ +struct log_data { + struct log_data *next; + ulong usage_cnt; /* number of files still to work */ + void *proc_ctrl; /* pointer to own control procdata structure */ + char log_start[2]; /* log string start (final len aligned by size) */ +}; + +/**********************************************/ +/* structure holding proc entrys for one card */ +/**********************************************/ +struct procdata { + struct proc_dir_entry *log; /* log entry */ + char log_name[15]; /* log filename */ + struct log_data *log_head, *log_tail; /* head and tail for queue */ + int if_used; /* open count for interface */ + wait_queue_head_t rd_queue; +}; + +/********************************************/ +/* put an log buffer into the log queue. */ +/* This buffer will be kept until all files */ +/* opened for read got the contents. */ +/* Flushes buffers not longer in use. */ +/********************************************/ +void +put_log_buffer(hysdn_card * card, char *cp) +{ + struct log_data *ib; + struct procdata *pd = card->procfs; + int flags; + + if (!pd) + return; + if (!cp) + return; + if (!*cp) + return; + if (pd->if_used <= 0) + return; /* no open file for read */ + + if (!(ib = (struct log_data *) kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC))) + return; /* no memory */ + strcpy(ib->log_start, cp); /* set output string */ + ib->next = NULL; + ib->proc_ctrl = pd; /* point to own control structure */ + save_flags(flags); + cli(); + ib->usage_cnt = pd->if_used; + if (!pd->log_head) + pd->log_head = ib; /* new head */ + else + pd->log_tail->next = ib; /* follows existing messages */ + pd->log_tail = ib; /* new tail */ + restore_flags(flags); + + /* delete old entrys */ + while (pd->log_head->next) { + if ((pd->log_head->usage_cnt <= 0) && + (pd->log_head->next->usage_cnt <= 0)) { + ib = pd->log_head; + pd->log_head = pd->log_head->next; + kfree(ib); + } else + break; + } /* pd->log_head->next */ + wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */ +} /* put_log_buffer */ + + +/*************************/ +/* dummy file operations */ +/*************************/ +static loff_t +hysdn_dummy_lseek(struct file *file, loff_t offset, int orig) +{ + return -ESPIPE; +} /* hysdn_dummy_lseek */ + +/**********************************/ +/* log file operations and tables */ +/**********************************/ + +/****************************************/ +/* write log file -> set log level bits */ +/****************************************/ +static ssize_t +hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off) +{ + int retval; + hysdn_card *card = (hysdn_card *) file->private_data; + + if (&file->f_pos != off) /* fs error check */ + return (-ESPIPE); + + if ((retval = pof_boot_write(card, buf, count)) < 0) + retval = -EFAULT; /* an error occured */ + + return (retval); +} /* hysdn_log_write */ + +/******************/ +/* read log file */ +/******************/ +static ssize_t +hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off) +{ + struct log_data *inf; + int len; + word ino; + struct procdata *pd; + hysdn_card *card; + + if (!*((struct log_data **) file->private_data)) { + if (file->f_flags & O_NONBLOCK) + return (-EAGAIN); + + /* sorry, but we need to search the card */ + ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */ + card = card_root; + while (card) { + pd = card->procfs; + if (pd->log->low_ino == ino) + break; + card = card->next; /* search next entry */ + } + if (card) + interruptible_sleep_on(&(pd->rd_queue)); + else + return (-EAGAIN); + + } + if (!(inf = *((struct log_data **) file->private_data))) + return (0); + + inf->usage_cnt--; /* new usage count */ + (struct log_data **) file->private_data = &inf->next; /* next structure */ + if ((len = strlen(inf->log_start)) <= count) { + if (copy_to_user(buf, inf->log_start, len)) + return -EFAULT; + file->f_pos += len; + return (len); + } + return (0); +} /* hysdn_log_read */ + +/******************/ +/* open log file */ +/******************/ +static int +hysdn_log_open(struct inode *ino, struct file *filep) +{ + hysdn_card *card; + struct procdata *pd; + ulong flags; + + MOD_INC_USE_COUNT; /* lock module */ + card = card_root; + while (card) { + pd = card->procfs; + if (pd->log->low_ino == (ino->i_ino & 0xFFFF)) + break; + card = card->next; /* search next entry */ + } + if (!card) { + MOD_DEC_USE_COUNT; /* unlock module */ + return (-ENODEV); /* device is unknown/invalid */ + } + filep->private_data = card; /* remember our own card */ + + if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) { + /* write only access -> boot pof data */ + if (pof_boot_open(card)) { + MOD_DEC_USE_COUNT; /* unlock module */ + return (-EPERM); /* no permission this time */ + } + } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { + + /* read access -> log/debug read */ + save_flags(flags); + cli(); + pd->if_used++; + if (pd->log_head) + (struct log_data **) filep->private_data = &(pd->log_tail->next); + else + (struct log_data **) filep->private_data = &(pd->log_head); + restore_flags(flags); + + } else { /* simultaneous read/write access forbidden ! */ + MOD_DEC_USE_COUNT; /* unlock module */ + return (-EPERM); /* no permission this time */ + } + return (0); +} /* hysdn_log_open */ + +/*******************************************************************************/ +/* close a cardlog file. If the file has been opened for exclusive write it is */ +/* assumed as pof data input and the pof loader is noticed about. */ +/* Otherwise file is handled as log output. In this case the interface usage */ +/* count is decremented and all buffers are noticed of closing. If this file */ +/* was the last one to be closed, all buffers are freed. */ +/*******************************************************************************/ +static int +hysdn_log_close(struct inode *ino, struct file *filep) +{ + struct log_data *inf; + struct procdata *pd; + hysdn_card *card; + int flags, retval = 0; + + + if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) { + /* write only access -> write debug completely written */ + retval = 0; /* success */ + } else { + /* read access -> log/debug read, mark one further file as closed */ + + pd = NULL; + save_flags(flags); + cli(); + inf = *((struct log_data **) filep->private_data); /* get first log entry */ + if (inf) + pd = (struct procdata *) inf->proc_ctrl; /* still entries there */ + else { + /* no info available -> search card */ + card = card_root; + while (card) { + pd = card->procfs; + if (pd->log->low_ino == (ino->i_ino & 0xFFFF)) + break; + card = card->next; /* search next entry */ + } + if (card) + pd = card->procfs; /* pointer to procfs ctrl */ + } + if (pd) + pd->if_used--; /* decrement interface usage count by one */ + + while (inf) { + inf->usage_cnt--; /* decrement usage count for buffers */ + inf = inf->next; + } + restore_flags(flags); + + if (pd) + if (pd->if_used <= 0) /* delete buffers if last file closed */ + while (pd->log_head) { + inf = pd->log_head; + pd->log_head = pd->log_head->next; + kfree(inf); + } + } /* read access */ + + MOD_DEC_USE_COUNT; + return (retval); +} /* hysdn_log_close */ + +/*************************************************/ +/* select/poll routine to be able using select() */ +/*************************************************/ +static unsigned int +hysdn_log_poll(struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + word ino; + hysdn_card *card; + struct procdata *pd; + + if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) + return (mask); /* no polling for write supported */ + + /* we need to search the card */ + ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */ + card = card_root; + while (card) { + pd = card->procfs; + if (pd->log->low_ino == ino) + break; + card = card->next; /* search next entry */ + } + if (!card) + return (mask); /* card not found */ + + poll_wait(file, &(pd->rd_queue), wait); + + if (*((struct log_data **) file->private_data)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} /* hysdn_log_poll */ + +/**************************************************/ +/* table for log filesystem functions defined above. */ +/**************************************************/ +static struct file_operations log_fops = +{ + hysdn_dummy_lseek, + hysdn_log_read, + hysdn_log_write, + NULL, /* readdir */ + hysdn_log_poll, /* poll */ + NULL, /*hysdn_log_ioctl, *//* ioctl */ + NULL, /* mmap */ + hysdn_log_open, + NULL, /* flush */ + hysdn_log_close, + NULL /* fsync */ +}; + +struct inode_operations log_inode_operations = +{ + &log_fops, /* log proc file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +/*****************************************/ +/* Output info data to the cardinfo file */ +/*****************************************/ +static int +info_read(char *buffer, char **start, off_t offset, int length, int *eof, void *data) +{ + char tmp[INFO_OUT_LEN * 11 + 2]; + int i; + char *cp; + hysdn_card *card; + + sprintf(tmp, "id bus slot type irq iobase plx-mem dp-mem boot device"); + cp = tmp; /* start of string */ + while (*cp) + cp++; + while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN) + *cp++ = ' '; + *cp++ = '\n'; + + card = card_root; /* start of list */ + while (card) { + sprintf(cp, "%d %3d %4d %4d %3d 0x%04x 0x%08x 0x%08x", + card->myid, + card->bus, + PCI_SLOT(card->devfn), + card->brdtype, + card->irq, + card->iobase, + card->plxbase, + card->membase); + card = card->next; + while (*cp) + cp++; + while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN) + *cp++ = ' '; + *cp++ = '\n'; + } + + i = cp - tmp; + *start = buffer; + if (offset + length > i) { + length = i - offset; + *eof = 1; + } else if (offset > i) { + length = 0; + *eof = 1; + } + cp = tmp + offset; + + if (length > 0) { + /* start_bh_atomic(); */ + memcpy(buffer, cp, length); + /* end_bh_atomic(); */ + return length; + } + return 0; +} /* info_read */ + +/*****************************/ +/* hysdn subdir in /proc/net */ +/*****************************/ +static struct proc_dir_entry *hysdn_proc_entry = NULL; +static struct proc_dir_entry *hysdn_info_entry = NULL; + +/***************************************************************************************/ +/* hysdn_procfs_init is called when the module is loaded and after the cards have been */ +/* detected. The needed proc dir and card entries are created. */ +/***************************************************************************************/ +int +hysdn_procfs_init(void) +{ + struct procdata *pd; + hysdn_card *card; + + hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, proc_net); + if (!hysdn_proc_entry) { + printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n"); + return (-1); + } + hysdn_info_entry = create_proc_entry("cardinfo", 0, hysdn_proc_entry); + if (hysdn_info_entry) + hysdn_info_entry->read_proc = info_read; /* read info function */ + + /* create all cardlog proc entries */ + + card = card_root; /* start with first card */ + while (card) { + if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) { + memset(pd, 0, sizeof(struct procdata)); + + sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid); + if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL) + pd->log->ops = &log_inode_operations; /* set new operations table */ + + init_waitqueue_head(&(pd->rd_queue)); + + card->procfs = (void *) pd; /* remember procfs structure */ + } + card = card->next; /* point to next card */ + } + + printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procfs_revision)); + return (0); +} /* hysdn_procfs_init */ + +/***************************************************************************************/ +/* hysdn_procfs_release is called when the module is unloaded and before the cards */ +/* resources are released. The module counter is assumed to be 0 ! */ +/***************************************************************************************/ +void +hysdn_procfs_release(void) +{ + struct procdata *pd; + hysdn_card *card; + + card = card_root; /* start with first card */ + while (card) { + if ((pd = (struct procdata *) card->procfs) != NULL) { + if (pd->log) + remove_proc_entry(pd->log_name, hysdn_proc_entry); + kfree(pd); /* release memory */ + } + card = card->next; /* point to next card */ + } + + remove_proc_entry("cardinfo", hysdn_proc_entry); + remove_proc_entry(PROC_SUBDIR_NAME, proc_net); +} /* hysdn_procfs_release */ diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c new file mode 100644 index 000000000..aea6a96ad --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_proclog.c @@ -0,0 +1,481 @@ +/* $Id: hysdn_proclog.c,v 1.2 2000/02/14 19:23:03 werner Exp $ + + * Linux driver for HYSDN cards, /proc/net filesystem log functions. + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_proclog.c,v $ + * Revision 1.2 2000/02/14 19:23:03 werner + * + * Changed handling of proc filesystem tables to a more portable version + * + * Revision 1.1 2000/02/10 19:45:18 werner + * + * Initial release + * + * + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/pci.h> + +#include "hysdn_defs.h" + +static char *hysdn_proclog_revision = "$Revision: 1.2 $"; + +/* the proc subdir for the interface is defined in the procconf module */ +extern struct proc_dir_entry *hysdn_proc_entry; + +/*************************************************/ +/* structure keeping ascii log for device output */ +/*************************************************/ +struct log_data { + struct log_data *next; + ulong usage_cnt; /* number of files still to work */ + void *proc_ctrl; /* pointer to own control procdata structure */ + char log_start[2]; /* log string start (final len aligned by size) */ +}; + +/**********************************************/ +/* structure holding proc entrys for one card */ +/**********************************************/ +struct procdata { + struct proc_dir_entry *log; /* log entry */ + char log_name[15]; /* log filename */ + struct log_data *log_head, *log_tail; /* head and tail for queue */ + int if_used; /* open count for interface */ + int volatile del_lock; /* lock for delete operations */ + uchar logtmp[LOG_MAX_LINELEN]; + wait_queue_head_t rd_queue; +}; + + +/**********************************************/ +/* log function for cards error log interface */ +/**********************************************/ +void +hysdn_card_errlog(hysdn_card * card, tErrLogEntry * logp, int maxsize) +{ + char buf[ERRLOG_TEXT_SIZE + 40]; + + sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText); + put_log_buffer(card, buf); /* output the string */ +} /* hysdn_card_errlog */ + +/***************************************************/ +/* Log function using format specifiers for output */ +/***************************************************/ +void +hysdn_addlog(hysdn_card * card, char *fmt,...) +{ + struct procdata *pd = card->proclog; + char *cp; + va_list args; + + if (!pd) + return; /* log structure non existent */ + + cp = pd->logtmp; + cp += sprintf(cp, "HYSDN: card %d ", card->myid); + + va_start(args, fmt); + cp += vsprintf(cp, fmt, args); + va_end(args); + *cp++ = '\n'; + *cp = 0; + + if (card->debug_flags & DEB_OUT_SYSLOG) + printk(KERN_INFO "%s", pd->logtmp); + else + put_log_buffer(card, pd->logtmp); + +} /* hysdn_addlog */ + +/********************************************/ +/* put an log buffer into the log queue. */ +/* This buffer will be kept until all files */ +/* opened for read got the contents. */ +/* Flushes buffers not longer in use. */ +/********************************************/ +void +put_log_buffer(hysdn_card * card, char *cp) +{ + struct log_data *ib; + struct procdata *pd = card->proclog; + int i, flags; + + if (!pd) + return; + if (!cp) + return; + if (!*cp) + return; + if (pd->if_used <= 0) + return; /* no open file for read */ + + if (!(ib = (struct log_data *) kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC))) + return; /* no memory */ + strcpy(ib->log_start, cp); /* set output string */ + ib->next = NULL; + ib->proc_ctrl = pd; /* point to own control structure */ + save_flags(flags); + cli(); + ib->usage_cnt = pd->if_used; + if (!pd->log_head) + pd->log_head = ib; /* new head */ + else + pd->log_tail->next = ib; /* follows existing messages */ + pd->log_tail = ib; /* new tail */ + i = pd->del_lock++; /* get lock state */ + restore_flags(flags); + + /* delete old entrys */ + if (!i) + while (pd->log_head->next) { + if ((pd->log_head->usage_cnt <= 0) && + (pd->log_head->next->usage_cnt <= 0)) { + ib = pd->log_head; + pd->log_head = pd->log_head->next; + kfree(ib); + } else + break; + } /* pd->log_head->next */ + pd->del_lock--; /* release lock level */ + wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */ +} /* put_log_buffer */ + + +/*************************/ +/* dummy file operations */ +/*************************/ +static loff_t +hysdn_dummy_lseek(struct file *file, loff_t offset, int orig) +{ + return -ESPIPE; +} /* hysdn_dummy_lseek */ + +/******************************/ +/* file operations and tables */ +/******************************/ + +/****************************************/ +/* write log file -> set log level bits */ +/****************************************/ +static ssize_t +hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off) +{ + ulong u = 0; + int found = 0; + uchar *cp, valbuf[128]; + long base = 10; + hysdn_card *card = (hysdn_card *) file->private_data; + + if (&file->f_pos != off) /* fs error check */ + return (-ESPIPE); + + if (count > (sizeof(valbuf) - 1)) + count = sizeof(valbuf) - 1; /* limit length */ + if (copy_from_user(valbuf, buf, count)) + return (-EFAULT); /* copy failed */ + + valbuf[count] = 0; /* terminating 0 */ + cp = valbuf; + if ((count > 2) && (valbuf[0] == '0') && (valbuf[1] == 'x')) { + cp += 2; /* pointer after hex modifier */ + base = 16; + } + /* scan the input for debug flags */ + while (*cp) { + if ((*cp >= '0') && (*cp <= '9')) { + found = 1; + u *= base; /* adjust to next digit */ + u += *cp++ - '0'; + continue; + } + if (base != 16) + break; /* end of number */ + + if ((*cp >= 'a') && (*cp <= 'f')) { + found = 1; + u *= base; /* adjust to next digit */ + u += *cp++ - 'a' + 10; + continue; + } + break; /* terminated */ + } + + if (found) { + card->debug_flags = u; /* remember debug flags */ + hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags); + } + return (count); +} /* hysdn_log_write */ + +/******************/ +/* read log file */ +/******************/ +static ssize_t +hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off) +{ + struct log_data *inf; + int len; + word ino; + struct procdata *pd; + hysdn_card *card; + + if (!*((struct log_data **) file->private_data)) { + if (file->f_flags & O_NONBLOCK) + return (-EAGAIN); + + /* sorry, but we need to search the card */ + ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */ + card = card_root; + while (card) { + pd = card->proclog; + if (pd->log->low_ino == ino) + break; + card = card->next; /* search next entry */ + } + if (card) + interruptible_sleep_on(&(pd->rd_queue)); + else + return (-EAGAIN); + + } + if (!(inf = *((struct log_data **) file->private_data))) + return (0); + + inf->usage_cnt--; /* new usage count */ + (struct log_data **) file->private_data = &inf->next; /* next structure */ + if ((len = strlen(inf->log_start)) <= count) { + if (copy_to_user(buf, inf->log_start, len)) + return -EFAULT; + file->f_pos += len; + return (len); + } + return (0); +} /* hysdn_log_read */ + +/******************/ +/* open log file */ +/******************/ +static int +hysdn_log_open(struct inode *ino, struct file *filep) +{ + hysdn_card *card; + struct procdata *pd; + ulong flags; + + MOD_INC_USE_COUNT; /* lock module */ + card = card_root; + while (card) { + pd = card->proclog; + if (pd->log->low_ino == (ino->i_ino & 0xFFFF)) + break; + card = card->next; /* search next entry */ + } + if (!card) { + MOD_DEC_USE_COUNT; /* unlock module */ + return (-ENODEV); /* device is unknown/invalid */ + } + filep->private_data = card; /* remember our own card */ + + if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) { + /* write only access -> write log level only */ + } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { + + /* read access -> log/debug read */ + save_flags(flags); + cli(); + pd->if_used++; + if (pd->log_head) + (struct log_data **) filep->private_data = &(pd->log_tail->next); + else + (struct log_data **) filep->private_data = &(pd->log_head); + restore_flags(flags); + } else { /* simultaneous read/write access forbidden ! */ + MOD_DEC_USE_COUNT; /* unlock module */ + return (-EPERM); /* no permission this time */ + } + return (0); +} /* hysdn_log_open */ + +/*******************************************************************************/ +/* close a cardlog file. If the file has been opened for exclusive write it is */ +/* assumed as pof data input and the pof loader is noticed about. */ +/* Otherwise file is handled as log output. In this case the interface usage */ +/* count is decremented and all buffers are noticed of closing. If this file */ +/* was the last one to be closed, all buffers are freed. */ +/*******************************************************************************/ +static int +hysdn_log_close(struct inode *ino, struct file *filep) +{ + struct log_data *inf; + struct procdata *pd; + hysdn_card *card; + int flags, retval = 0; + + + if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) { + /* write only access -> write debug level written */ + retval = 0; /* success */ + } else { + /* read access -> log/debug read, mark one further file as closed */ + + pd = NULL; + save_flags(flags); + cli(); + inf = *((struct log_data **) filep->private_data); /* get first log entry */ + if (inf) + pd = (struct procdata *) inf->proc_ctrl; /* still entries there */ + else { + /* no info available -> search card */ + card = card_root; + while (card) { + pd = card->proclog; + if (pd->log->low_ino == (ino->i_ino & 0xFFFF)) + break; + card = card->next; /* search next entry */ + } + if (card) + pd = card->proclog; /* pointer to procfs log */ + } + if (pd) + pd->if_used--; /* decrement interface usage count by one */ + + while (inf) { + inf->usage_cnt--; /* decrement usage count for buffers */ + inf = inf->next; + } + restore_flags(flags); + + if (pd) + if (pd->if_used <= 0) /* delete buffers if last file closed */ + while (pd->log_head) { + inf = pd->log_head; + pd->log_head = pd->log_head->next; + kfree(inf); + } + } /* read access */ + + MOD_DEC_USE_COUNT; + return (retval); +} /* hysdn_log_close */ + +/*************************************************/ +/* select/poll routine to be able using select() */ +/*************************************************/ +static unsigned int +hysdn_log_poll(struct file *file, poll_table * wait) +{ + unsigned int mask = 0; + word ino; + hysdn_card *card; + struct procdata *pd; + + if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) + return (mask); /* no polling for write supported */ + + /* we need to search the card */ + ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */ + card = card_root; + while (card) { + pd = card->proclog; + if (pd->log->low_ino == ino) + break; + card = card->next; /* search next entry */ + } + if (!card) + return (mask); /* card not found */ + + poll_wait(file, &(pd->rd_queue), wait); + + if (*((struct log_data **) file->private_data)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} /* hysdn_log_poll */ + +/**************************************************/ +/* table for log filesystem functions defined above. */ +/**************************************************/ +static struct file_operations log_fops = +{ + hysdn_dummy_lseek, + hysdn_log_read, + hysdn_log_write, + NULL, /* readdir */ + hysdn_log_poll, /* poll */ + NULL, + NULL, /* mmap */ + hysdn_log_open, + NULL, /* flush */ + hysdn_log_close, + NULL /* fsync */ +}; + +struct inode_operations log_inode_operations; + +/***********************************************************************************/ +/* hysdn_proclog_init is called when the module is loaded after creating the cards */ +/* conf files. */ +/***********************************************************************************/ +int +hysdn_proclog_init(hysdn_card * card) +{ + struct procdata *pd; + + /* create a cardlog proc entry */ + + if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) { + memset(pd, 0, sizeof(struct procdata)); + memset(&log_inode_operations, 0, sizeof(struct inode_operations)); + log_inode_operations.default_file_ops = &log_fops; + + sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid); + if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL) + pd->log->ops = &log_inode_operations; /* set new operations table */ + + init_waitqueue_head(&(pd->rd_queue)); + + card->proclog = (void *) pd; /* remember procfs structure */ + } + return (0); +} /* hysdn_proclog_init */ + +/************************************************************************************/ +/* hysdn_proclog_release is called when the module is unloaded and before the cards */ +/* conf file is released */ +/* The module counter is assumed to be 0 ! */ +/************************************************************************************/ +void +hysdn_proclog_release(hysdn_card * card) +{ + struct procdata *pd; + + if ((pd = (struct procdata *) card->proclog) != NULL) { + if (pd->log) + remove_proc_entry(pd->log_name, hysdn_proc_entry); + kfree(pd); /* release memory */ + card->proclog = NULL; + } +} /* hysdn_proclog_release */ diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c new file mode 100644 index 000000000..0e5f9e7ba --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_sched.c @@ -0,0 +1,202 @@ +/* $Id: hysdn_sched.c,v 1.1 2000/02/10 19:45:18 werner Exp $ + + * Linux driver for HYSDN cards, scheduler routines for handling exchange card <-> pc. + * + * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH + * + * Copyright 1999 by Werner Cornelius (werner@titro.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: hysdn_sched.c,v $ + * Revision 1.1 2000/02/10 19:45:18 werner + * + * Initial release + * + * + */ + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/version.h> +#include <asm/io.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> + +#include "hysdn_defs.h" + +/*****************************************************************************/ +/* hysdn_sched_rx is called from the cards handler to announce new data is */ +/* available from the card. The routine has to handle the data and return */ +/* with a nonzero code if the data could be worked (or even thrown away), if */ +/* no room to buffer the data is available a zero return tells the card */ +/* to keep the data until later. */ +/*****************************************************************************/ +int +hysdn_sched_rx(hysdn_card * card, uchar * buf, word len, word chan) +{ + + switch (chan) { + case CHAN_NDIS_DATA: + hysdn_rx_netpkt(card, buf, len); /* give packet to network handler */ + break; + + case CHAN_ERRLOG: + hysdn_card_errlog(card, (tErrLogEntry *) buf, len); + if (card->err_log_state == ERRLOG_STATE_ON) + card->err_log_state = ERRLOG_STATE_START; /* start new fetch */ + break; + + default: + printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len); + break; + + } /* switch rx channel */ + + return (1); /* always handled */ +} /* hysdn_sched_rx */ + +/*****************************************************************************/ +/* hysdn_sched_tx is called from the cards handler to announce that there is */ +/* room in the tx-buffer to the card and data may be sent if needed. */ +/* If the routine wants to send data it must fill buf, len and chan with the */ +/* appropriate data and return a nonzero value. With a zero return no new */ +/* data to send is assumed. maxlen specifies the buffer size available for */ +/* sending. */ +/*****************************************************************************/ +int +hysdn_sched_tx(hysdn_card * card, uchar * buf, word volatile *len, word volatile *chan, word maxlen) +{ + struct sk_buff *skb; + + if (card->net_tx_busy) { + card->net_tx_busy = 0; /* reset flag */ + hysdn_tx_netack(card); /* acknowledge packet send */ + } /* a network packet has completely been transferred */ + /* first of all async requests are handled */ + if (card->async_busy) { + if (card->async_len <= maxlen) { + memcpy(buf, card->async_data, card->async_len); + *len = card->async_len; + *chan = card->async_channel; + card->async_busy = 0; /* reset request */ + return (1); + } + card->async_busy = 0; /* in case of length error */ + } /* async request */ + if ((card->err_log_state == ERRLOG_STATE_START) && + (maxlen >= ERRLOG_CMD_REQ_SIZE)) { + strcpy(buf, ERRLOG_CMD_REQ); /* copy the command */ + *len = ERRLOG_CMD_REQ_SIZE; /* buffer length */ + *chan = CHAN_ERRLOG; /* and channel */ + card->err_log_state = ERRLOG_STATE_ON; /* new state is on */ + return (1); /* tell that data should be send */ + } /* error log start and able to send */ + if ((card->err_log_state == ERRLOG_STATE_STOP) && + (maxlen >= ERRLOG_CMD_STOP_SIZE)) { + strcpy(buf, ERRLOG_CMD_STOP); /* copy the command */ + *len = ERRLOG_CMD_STOP_SIZE; /* buffer length */ + *chan = CHAN_ERRLOG; /* and channel */ + card->err_log_state = ERRLOG_STATE_OFF; /* new state is off */ + return (1); /* tell that data should be send */ + } /* error log start and able to send */ + /* now handle network interface packets */ + if ((skb = hysdn_tx_netget(card)) != NULL) { + if (skb->len <= maxlen) { + memcpy(buf, skb->data, skb->len); /* copy the packet to the buffer */ + *len = skb->len; + *chan = CHAN_NDIS_DATA; + card->net_tx_busy = 1; /* we are busy sending network data */ + return (1); /* go and send the data */ + } else + hysdn_tx_netack(card); /* aknowledge packet -> throw away */ + } /* send a network packet if available */ + return (0); /* nothing to send */ +} /* hysdn_sched_tx */ + + +/*****************************************************************************/ +/* send one config line to the card and return 0 if successfull, otherwise a */ +/* negative error code. */ +/* The function works with timeouts perhaps not giving the greatest speed */ +/* sending the line, but this should be meaningless beacuse only some lines */ +/* are to be sent and this happens very seldom. */ +/*****************************************************************************/ +int +hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan) +{ + int cnt = 50; /* timeout intervalls */ + ulong flags; + + if (card->debug_flags & LOG_SCHED_ASYN) + hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1); + + save_flags(flags); + cli(); + while (card->async_busy) { + sti(); + + if (card->debug_flags & LOG_SCHED_ASYN) + hysdn_addlog(card, "async tx-cfg delayed"); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + if (!--cnt) { + restore_flags(flags); + return (-ERR_ASYNC_TIME); /* timed out */ + } + cli(); + } /* wait for buffer to become free */ + + strcpy(card->async_data, line); + card->async_len = strlen(line) + 1; + card->async_channel = chan; + card->async_busy = 1; /* request transfer */ + + /* now queue the task */ + queue_task(&card->irq_queue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + sti(); + + if (card->debug_flags & LOG_SCHED_ASYN) + hysdn_addlog(card, "async tx-cfg data queued"); + + cnt++; /* short delay */ + cli(); + + while (card->async_busy) { + sti(); + + if (card->debug_flags & LOG_SCHED_ASYN) + hysdn_addlog(card, "async tx-cfg waiting for tx-ready"); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + if (!--cnt) { + restore_flags(flags); + return (-ERR_ASYNC_TIME); /* timed out */ + } + cli(); + } /* wait for buffer to become free again */ + + restore_flags(flags); + + if (card->debug_flags & LOG_SCHED_ASYN) + hysdn_addlog(card, "async tx-cfg data send"); + + return (0); /* line send correctly */ +} /* hysdn_tx_cfgline */ diff --git a/drivers/isdn/hysdn/ince1pc.h b/drivers/isdn/hysdn/ince1pc.h new file mode 100644 index 000000000..c22974d85 --- /dev/null +++ b/drivers/isdn/hysdn/ince1pc.h @@ -0,0 +1,132 @@ +#ifndef __INCE1PC_H__
+#define __INCE1PC_H__
+
+/****************************************************************************
+
+ FILE: ince1pc.h
+
+ AUTHOR: M.Steinkopf
+
+ PURPOSE: common definitions for both sides of the bus:
+ - conventions both spoolers must know
+ - channel numbers agreed upon
+
+*****************************************************************************/
+
+/* basic scalar definitions have same meanning,
+ * but their declaration location depends on environment
+ */
+
+/*--------------------------------------channel numbers---------------------*/
+#define CHAN_SYSTEM 0x0001 /* system channel (spooler to spooler) */
+#define CHAN_ERRLOG 0x0005 /* error logger */
+#define CHAN_CAPI 0x0064 /* CAPI interface */
+#define CHAN_NDIS_DATA 0x1001 /* NDIS data transfer */
+
+/*--------------------------------------POF ready msg-----------------------*/
+ /* NOTE: after booting POF sends system ready message to PC: */
+#define RDY_MAGIC 0x52535953UL /* 'SYSR' reversed */
+#define RDY_MAGIC_SIZE 4 /* size in bytes */
+
+#define MAX_N_TOK_BYTES 255
+
+#define MIN_RDY_MSG_SIZE RDY_MAGIC_SIZE
+#define MAX_RDY_MSG_SIZE (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+
+#define SYSR_TOK_END 0
+#define SYSR_TOK_B_CHAN 1 /* nr. of B-Channels; DataLen=1; def: 2 */
+#define SYSR_TOK_FAX_CHAN 2 /* nr. of FAX Channels; DataLen=1; def: 0 */
+#define SYSR_TOK_MAC_ADDR 3 /* MAC-Address; DataLen=6; def: auto */
+#define SYSR_TOK_ESC 255 /* undefined data size yet */
+ /* default values, if not corrected by token: */
+#define SYSR_TOK_B_CHAN_DEF 2 /* assume 2 B-Channels */
+#define SYSR_TOK_FAX_CHAN_DEF 1 /* assume 1 FAX Channel */
+
+/* syntax of new SYSR token stream:
+ * channel: CHAN_SYSTEM
+ * msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
+ * RDY_MAGIC_SIZE <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+ * msg : 0 1 2 3 {4 5 6 ..}
+ * S Y S R MAX_N_TOK_BYTES bytes of TokenStream
+ *
+ * TokenStream := empty
+ * | {NonEndTokenChunk} EndToken RotlCRC
+ * NonEndTokenChunk:= NonEndTokenId DataLen [Data]
+ * NonEndTokenId := 0x01 .. 0xFE 1 BYTE
+ * DataLen := 0x00 .. 0xFF 1 BYTE
+ * Data := DataLen bytes
+ * EndToken := 0x00
+ * RotlCRC := special 1 byte CRC over all NonEndTokenChunk bytes
+ * s. RotlCRC algorithm
+ *
+ * RotlCRC algorithm:
+ * ucSum= 0 1 uchar
+ * for all NonEndTokenChunk bytes:
+ * ROTL(ucSum,1) rotate left by 1
+ * ucSum += Char; add current byte with swap around
+ * RotlCRC= ~ucSum; invert all bits for result
+ *
+ * note:
+ * - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
+ */
+
+/*--------------------------------------error logger------------------------*/
+ /* note: pof needs final 0 ! */
+#define ERRLOG_CMD_REQ "ERRLOG ON"
+#define ERRLOG_CMD_REQ_SIZE 10 /* with final 0 byte ! */
+#define ERRLOG_CMD_STOP "ERRLOG OFF"
+#define ERRLOG_CMD_STOP_SIZE 11 /* with final 0 byte ! */
+
+#define ERRLOG_ENTRY_SIZE 64 /* sizeof(tErrLogEntry) */
+ /* remaining text size = 55 */
+#define ERRLOG_TEXT_SIZE (ERRLOG_ENTRY_SIZE-2*4-1)
+
+typedef struct ErrLogEntry_tag { +
+/*00 */ ulong ulErrType; +
+/*04 */ ulong ulErrSubtype; +
+/*08 */ uchar ucTextSize; +
+ /*09 */ uchar ucText[ERRLOG_TEXT_SIZE]; + /* ASCIIZ of len ucTextSize-1 */ +
+/*40 */
+} tErrLogEntry; + +
+#if defined(__TURBOC__)
+#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
+#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
+#endif /*
*/ +#endif /*
*/ +
+/*--------------------------------------DPRAM boot spooler------------------*/
+ /* this is the struture used between pc and
+ * hyperstone to exchange boot data
+ */
+#define DPRAM_SPOOLER_DATA_SIZE 0x20
+typedef struct DpramBootSpooler_tag { +
+/*00 */ uchar Len; +
+/*01 */ volatile uchar RdPtr; +
+/*02 */ uchar WrPtr; +
+/*03 */ uchar Data[DPRAM_SPOOLER_DATA_SIZE]; +
+/*23 */
+} tDpramBootSpooler; + +
+#define DPRAM_SPOOLER_MIN_SIZE 5 /* Len+RdPtr+Wrptr+2*data */
+#define DPRAM_SPOOLER_DEF_SIZE 0x23 /* current default size */
+
+/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/
+ /* at DPRAM offset 0x1C00: */
+#define SIZE_RSV_SOFT_UART 0x1B0 /* 432 bytes reserved for SoftUart */
+
+
+#endif /* __INCE1PC_H__ */
diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index 1626fa4e9..f1df11f55 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -1,4 +1,4 @@ -/* $Id: isdn_common.c,v 1.93 1999/11/04 13:11:36 keil Exp $ +/* $Id: isdn_common.c,v 1.97 2000/01/23 18:45:37 keil Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -21,6 +21,32 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.97 2000/01/23 18:45:37 keil + * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN) + * + * Revision 1.96 2000/01/20 19:55:33 keil + * Add FAX Class 1 support + * + * Revision 1.95 2000/01/09 20:43:13 detabc + * exand logical bind-group's for both call's (in and out). + * add first part of kernel-config-help for abc-extension. + * + * Revision 1.94 1999/11/20 22:14:13 detabc + * added channel dial-skip in case of external use + * (isdn phone or another isdn device) on the same NTBA. + * usefull with two or more card's connected the different NTBA's. + * global switchable in kernel-config and also per netinterface. + * + * add auto disable of netinterface's in case of: + * to many connection's in short time. + * config mistakes (wrong encapsulation, B2-protokoll or so on) on local + * or remote side. + * wrong password's or something else to a ISP (syncppp). + * + * possible encapsulations for this future are: + * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP, + * and ISDN_NET_ENCAP_CISCOHDLCK. + * * Revision 1.93 1999/11/04 13:11:36 keil * Reinit of v110 structs * @@ -410,13 +436,14 @@ #endif CONFIG_ISDN_DIVERSION #include "isdn_v110.h" #include "isdn_cards.h" +#include <linux/devfs_fs_kernel.h> /* Debugflags */ #undef ISDN_DEBUG_STATCALLB isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.93 $"; +static char *isdn_revision = "$Revision: 1.97 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -438,6 +465,9 @@ isdn_divert_if *divert_if = NULL; /* interface to diversion module */ static int isdn_writebuf_stub(int, int, const u_char *, int, int); +static void set_global_features(void); +static void isdn_register_devfs(int); +static void isdn_unregister_devfs(int); void isdn_MOD_INC_USE_COUNT(void) @@ -720,29 +750,33 @@ isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb) int isdn_command(isdn_ctrl *cmd) { + if (cmd->driver == -1) { + printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command); + return(1); + } if (cmd->command == ISDN_CMD_SETL2) { - int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255); - unsigned long l2prot = (cmd->arg >> 8) & 255; - unsigned long features = (dev->drv[cmd->driver]->interface->features - >> ISDN_FEATURE_L2_SHIFT) & - ISDN_FEATURE_L2_MASK; - unsigned long l2_feature = (1 << l2prot); - - switch (l2prot) { - case ISDN_PROTO_L2_V11096: - case ISDN_PROTO_L2_V11019: - case ISDN_PROTO_L2_V11038: - /* If V.110 requested, but not supported by - * HL-driver, set emulator-flag and change - * Layer-2 to transparent - */ - if (!(features & l2_feature)) { - dev->v110emu[idx] = l2prot; - cmd->arg = (cmd->arg & 255) | - (ISDN_PROTO_L2_TRANS << 8); - } else - dev->v110emu[idx] = 0; - } + int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255); + unsigned long l2prot = (cmd->arg >> 8) & 255; + unsigned long features = (dev->drv[cmd->driver]->interface->features + >> ISDN_FEATURE_L2_SHIFT) & + ISDN_FEATURE_L2_MASK; + unsigned long l2_feature = (1 << l2prot); + + switch (l2prot) { + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: + /* If V.110 requested, but not supported by + * HL-driver, set emulator-flag and change + * Layer-2 to transparent + */ + if (!(features & l2_feature)) { + dev->v110emu[idx] = l2prot; + cmd->arg = (cmd->arg & 255) | + (ISDN_PROTO_L2_TRANS << 8); + } else + dev->v110emu[idx] = 0; + } } return dev->drv[cmd->driver]->interface->command(cmd); } @@ -822,6 +856,7 @@ isdn_status_callback(isdn_ctrl * c) for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (dev->drvmap[i] == di) isdn_all_eaz(di, dev->chanmap[i]); + set_global_features(); break; case ISDN_STAT_STOP: dev->drv[di]->flags &= ~DRV_FLAG_RUNNING; @@ -1065,6 +1100,7 @@ isdn_status_callback(isdn_ctrl * c) dev->drvmap[i] = -1; dev->chanmap[i] = -1; dev->usage[i] &= ~ISDN_USAGE_DISABLED; + isdn_unregister_devfs(i); } dev->drivers--; dev->channels -= dev->drv[di]->channels; @@ -1078,6 +1114,7 @@ isdn_status_callback(isdn_ctrl * c) dev->drv[di] = NULL; dev->drvid[di][0] = '\0'; isdn_info_update(); + set_global_features(); restore_flags(flags); return 0; case ISDN_STAT_L1ERR: @@ -1563,6 +1600,9 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) * are serialized by means of a semaphore. */ switch (cmd) { + case IIOCNETDWRSET: + printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n"); + return(-EINVAL); case IIOCNETLCR: printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n"); return -ENODEV; @@ -1854,7 +1894,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) for (i = 0; i < 10; i++) { sprintf(bname, "%s%s", strlen(dev->drv[drvidx]->msn2eaz[i]) ? - dev->drv[drvidx]->msn2eaz[i] : "-", + dev->drv[drvidx]->msn2eaz[i] : "_", (i < 9) ? "," : "\0"); if (copy_to_user(p, bname, strlen(bname) + 1)) return -EFAULT; @@ -2024,13 +2064,17 @@ isdn_close(struct inode *ino, struct file *filep) static struct file_operations isdn_fops = { - llseek: isdn_lseek, - read: isdn_read, - write: isdn_write, - poll: isdn_poll, - ioctl: isdn_ioctl, - open: isdn_open, - release: isdn_close, + isdn_lseek, + isdn_read, + isdn_write, + NULL, /* isdn_readdir */ + isdn_poll, /* isdn_poll */ + isdn_ioctl, /* isdn_ioctl */ + NULL, /* isdn_mmap */ + isdn_open, + NULL, /* flush */ + isdn_close, + NULL /* fsync */ }; char * @@ -2056,7 +2100,7 @@ isdn_map_eaz2msn(char *msn, int di) int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev - ,int pre_chan) + ,int pre_chan, char *msn) { int i; ulong flags; @@ -2079,6 +2123,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 (!strcmp(isdn_map_eaz2msn(msn, d), "-")) + continue; if (dev->usage[i] & ISDN_USAGE_DISABLED) continue; /* usage not allowed */ if (dev->drv[d]->flags & DRV_FLAG_RUNNING) { @@ -2349,6 +2395,7 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) if (dev->chanmap[k] < 0) { dev->chanmap[k] = j; dev->drvmap[k] = drvidx; + isdn_register_devfs(k); break; } restore_flags(flags); @@ -2360,6 +2407,19 @@ isdn_add_channels(driver *d, int drvidx, int n, int adding) * Low-level-driver registration */ +static void +set_global_features(void) +{ + int drvidx; + + dev->global_features = 0; + for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) { + if (!dev->drv[drvidx]) + continue; + if (dev->drv[drvidx]->interface) + dev->global_features |= dev->drv[drvidx]->interface->features; + } +} #ifdef CONFIG_ISDN_DIVERSION extern isdn_divert_if *divert_if; @@ -2473,6 +2533,7 @@ register_isdn(isdn_if * i) strcpy(dev->drvid[drvidx], i->id); isdn_info_update(); dev->drivers++; + set_global_features(); restore_flags(flags); return 1; } @@ -2504,6 +2565,96 @@ isdn_getrev(const char *revision) return rev; } +#ifdef CONFIG_DEVFS_FS + +static devfs_handle_t devfs_handle = NULL; + +static void isdn_register_devfs(int k) +{ + char buf[11]; + + sprintf (buf, "isdn%d", k); + dev->devfs_handle_isdnX[k] = + devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR, 0, 0, + &isdn_fops, NULL); + sprintf (buf, "isdnctrl%d", k); + dev->devfs_handle_isdnctrlX[k] = + devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_CTRL + k, 0600 | S_IFCHR, + 0, 0, &isdn_fops, NULL); +} + +static void isdn_unregister_devfs(int k) +{ + devfs_unregister (dev->devfs_handle_isdnX[k]); + devfs_unregister (dev->devfs_handle_isdnctrlX[k]); +} + +static void isdn_init_devfs(void) +{ +# ifdef CONFIG_ISDN_PPP + int i; +# endif + + devfs_handle = devfs_mk_dir (NULL, "isdn", 4, NULL); +# ifdef CONFIG_ISDN_PPP + for (i = 0; i < ISDN_MAX_CHANNELS; i++) { + char buf[8]; + + sprintf (buf, "ippp%d", i); + dev->devfs_handle_ipppX[i] = + devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_PPP + i, + 0600 | S_IFCHR, 0, 0, &isdn_fops, NULL); + } +# endif + + dev->devfs_handle_isdninfo = + devfs_register (devfs_handle, "isdninfo", 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_STATUS, 0600 | S_IFCHR, + 0, 0, &isdn_fops, NULL); + dev->devfs_handle_isdnctrl = + devfs_register (devfs_handle, "isdnctrl", 0, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, 0, 0, + &isdn_fops, NULL); +} + +static void isdn_cleanup_devfs(void) +{ +# ifdef CONFIG_ISDN_PPP + int i; + for (i = 0; i < ISDN_MAX_CHANNELS; i++) + devfs_unregister (dev->devfs_handle_ipppX[i]); +# endif + devfs_unregister (dev->devfs_handle_isdninfo); + devfs_unregister (dev->devfs_handle_isdnctrl); + devfs_unregister (devfs_handle); +} + +#else /* CONFIG_DEVFS_FS */ +static void isdn_register_devfs(int dummy) +{ + return; +} + +static void isdn_unregister_devfs(int dummy) +{ + return; +} + +static void isdn_init_devfs(void) +{ + return; +} + +static void isdn_cleanup_devfs(void) +{ + return; +} + +#endif /* CONFIG_DEVFS_FS */ + /* * Allocate and initialize all data, register modem-devices */ @@ -2530,11 +2681,12 @@ isdn_init(void) init_waitqueue_head(&dev->mdm.info[i].open_wait); init_waitqueue_head(&dev->mdm.info[i].close_wait); } - if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { + if (devfs_register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { printk(KERN_WARNING "isdn: Could not register control devices\n"); vfree(dev); return -EIO; } + isdn_init_devfs(); if ((i = isdn_tty_modem_init()) < 0) { printk(KERN_WARNING "isdn: Could not register tty devices\n"); if (i == -3) @@ -2542,7 +2694,8 @@ isdn_init(void) if (i <= -2) tty_unregister_driver(&dev->mdm.tty_modem); vfree(dev); - unregister_chrdev(ISDN_MAJOR, "isdn"); + isdn_cleanup_devfs(); + devfs_unregister_chrdev(ISDN_MAJOR, "isdn"); return -EIO; } #ifdef CONFIG_ISDN_PPP @@ -2552,7 +2705,8 @@ isdn_init(void) tty_unregister_driver(&dev->mdm.cua_modem); for (i = 0; i < ISDN_MAX_CHANNELS; i++) kfree(dev->mdm.info[i].xmit_buf - 4); - unregister_chrdev(ISDN_MAJOR, "isdn"); + isdn_cleanup_devfs(); + devfs_unregister_chrdev(ISDN_MAJOR, "isdn"); vfree(dev); return -EIO; } @@ -2618,9 +2772,10 @@ cleanup_module(void) kfree(dev->mdm.info[i].fax); #endif } - if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { + if (devfs_unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n"); } else { + isdn_cleanup_devfs(); del_timer(&dev->timer); vfree(dev); printk(KERN_NOTICE "ISDN-subsystem unloaded\n"); diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h index 3c60d7c80..c45dd5202 100644 --- a/drivers/isdn/isdn_common.h +++ b/drivers/isdn/isdn_common.h @@ -1,4 +1,4 @@ -/* $Id: isdn_common.h,v 1.17 1999/10/27 21:21:17 detabc Exp $ +/* $Id: isdn_common.h,v 1.18 2000/01/23 18:45:37 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.18 2000/01/23 18:45:37 keil + * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN) + * * Revision 1.17 1999/10/27 21:21:17 detabc * Added support for building logically-bind-group's per interface. * usefull for outgoing call's with more then one isdn-card. @@ -128,7 +131,7 @@ extern void isdn_timer_ctrl(int tf, int onoff); extern void isdn_unexclusive_channel(int di, int ch); extern int isdn_getnum(char **); extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *); -extern int isdn_get_free_channel(int, int, int, int, int); +extern int isdn_get_free_channel(int, int, int, int, int, char *); extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *); extern int register_isdn(isdn_if * i); extern int isdn_wildmat(char *, char *); diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index e1c7cb75d..592d20ed9 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.95 1999/10/27 21:21:17 detabc Exp $ +/* $Id: isdn_net.c,v 1.107 2000/02/13 09:52:05 kai Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -21,6 +21,66 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.107 2000/02/13 09:52:05 kai + * increased TX_TIMEOUT to 20sec + * + * Revision 1.106 2000/02/12 19:26:55 kai + * adopted to latest 2.3 softnet changes. + * + * tested with PPP and MPPP, it works here. + * can somebody check raw-ip? + * + * also changed std2kern, stddiff for bash-1 compatibility, + * hope this doesn't break anything. + * + * Revision 1.105 2000/02/12 11:43:26 he + * SOFTNET related changes, first try. Compatible with linux 2.2.x, but + * not tested for kernels with softnet (>= 2.3.43) yet. + * + * Revision 1.104 2000/02/06 21:49:59 detabc + * add rewriting of socket's and frame's saddr for udp-ipv4 dynip-connections. + * Include checksum-recompute of ip- and udp-header's. + * + * Revision 1.103 2000/01/23 18:45:37 keil + * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN) + * + * Revision 1.102 2000/01/09 20:43:14 detabc + * exand logical bind-group's for both call's (in and out). + * add first part of kernel-config-help for abc-extension. + * + * Revision 1.101 1999/12/05 16:06:08 detabc + * add resethandling for rawip-compression. + * at now all B2-Protocols are usable with rawip-compression + * + * Revision 1.100 1999/12/04 15:05:25 detabc + * bugfix abc-rawip-bsdcompress with channel-bundeling + * + * Revision 1.99 1999/11/30 11:29:06 detabc + * add a on the fly frame-counter and limit + * + * Revision 1.98 1999/11/28 14:49:07 detabc + * In case of rawip-compress adjust dev[x]->ibytes/obytes to reflect the + * uncompressed size. + * + * Revision 1.97 1999/11/26 15:54:59 detabc + * added compression (isdn_bsdcompress) for rawip interfaces with x75i B2-protocol. + * + * Revision 1.96 1999/11/20 22:14:13 detabc + * added channel dial-skip in case of external use + * (isdn phone or another isdn device) on the same NTBA. + * usefull with two or more card's connected the different NTBA's. + * global switchable in kernel-config and also per netinterface. + * + * add auto disable of netinterface's in case of: + * to many connection's in short time. + * config mistakes (wrong encapsulation, B2-protokoll or so on) on local + * or remote side. + * wrong password's or something else to a ISP (syncppp). + * + * possible encapsulations for this future are: + * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP, + * and ISDN_NET_ENCAP_CISCOHDLCK. + * * Revision 1.95 1999/10/27 21:21:17 detabc * Added support for building logically-bind-group's per interface. * usefull for outgoing call's with more then one isdn-card. @@ -393,10 +453,6 @@ #endif -#ifndef ISDN_NEW_TBUSY -#define ISDN_NEW_TBUSY -#endif -#ifdef ISDN_NEW_TBUSY /* * Outline of new tbusy handling: * @@ -413,29 +469,60 @@ */ /* - * Tell upper layers that the network device is ready to xmit more frames. + * About SOFTNET: + * Most of the changes were pretty obvious and basically done by HE already. + * + * One problem of the isdn net device code is that is uses struct net_device + * for masters and slaves. However, only master interface are registered to + * the network layer, and therefore, it only makes sense to call netif_* + * functions on them. + * + * The old code abused the slaves dev->start to remember the corresponding + * master's interface state (ifup'ed or not). This does not work with SOFTNET + * any more, because there's now dev->start anymore. + * Instead I chose to add isdn_net_started() which gives the state of the + * master in case of slaves. + * I'm still not sure if this is how it's supposed to be done this way + * because it uses netif_running(dev) which might be + * considered private to the network layer. However, it works for now. + * Alternative: set a flag in _open() and clear it in _close() + * + * I left some dead code around in #if 0 which I'm not absolutely sure about. + * If no problems turn up, it should be removed later + * + * --KG */ -static void __inline__ isdn_net_dev_xon(struct net_device * dev) -{ - dev->tbusy = 0; - mark_bh(NET_BH); -} -static void __inline__ isdn_net_lp_xon(isdn_net_local * lp) +/* + * Find out if the netdevice has been ifup-ed yet. + * For slaves, look at the corresponding master. + */ +static int __inline__ isdn_net_started(isdn_net_dev *n) { - lp->netdev->dev.tbusy = 0; - if(lp->master) lp->master->tbusy = 0; - mark_bh(NET_BH); + isdn_net_local *lp = n->local; + struct net_device *dev; + + if (lp->master) + dev = lp->master; + else + dev = &n->dev; + return netif_running(dev); } /* - * Ask upper layers to temporarily cease passing us more xmit frames. + * wake up the network -> net_device queue. + * For slaves, wake the corresponding master interface. */ -static void __inline__ isdn_net_dev_xoff(struct net_device * dev) +static void __inline__ isdn_net_lp_xon(isdn_net_local * lp) { - dev->tbusy = 1; + if (lp->master) + netif_wake_queue(lp->master); + else + netif_wake_queue(&lp->netdev->dev); } -#endif + + +#define ISDN_NET_TX_TIMEOUT (20*HZ) /* Prototypes */ @@ -443,7 +530,7 @@ int isdn_net_force_dial_lp(isdn_net_local *); 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.95 $"; +char *isdn_net_revision = "$Revision: 1.107 $"; /* * Code for raw-networking over ISDN @@ -482,14 +569,9 @@ isdn_net_reset(struct net_device *dev) #endif ulong flags; + /* not sure if the cli() is needed at all --KG */ save_flags(flags); cli(); /* Avoid glitch on writes to CMD regs */ - dev->interrupt = 0; -#ifdef ISDN_NEW_TBUSY - isdn_net_dev_xon(dev); -#else - dev->tbusy = 0; -#endif #ifdef CONFIG_ISDN_X25 if( cprot && cprot -> pops && dops ) cprot -> pops -> restart ( cprot, dev, dops ); @@ -505,8 +587,12 @@ isdn_net_open(struct net_device *dev) struct net_device *p; struct in_device *in_dev; + /* moved here from isdn_net_reset, because only the master has an + interface associated which is supposed to be started. BTW: + we need to call netif_start_queue, not netif_wake_queue here */ + netif_start_queue(dev); + isdn_net_reset(dev); - dev->start = 1; /* Fill in the MAC-level header (not needed, but for compatibility... */ for (i = 0; i < ETH_ALEN - sizeof(u32); i++) dev->dev_addr[i] = 0xfc; @@ -524,7 +610,6 @@ isdn_net_open(struct net_device *dev) if ((p = (((isdn_net_local *) dev->priv)->slave))) { while (p) { isdn_net_reset(p); - p->start = 1; p = (((isdn_net_local *) p->priv)->slave); } } @@ -703,19 +788,11 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) mdev = &lp->netdev->dev; if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) { lp->sav_skb = NULL; -#ifndef ISDN_NEW_TBUSY - mark_bh(NET_BH); -#endif } else { return 1; } } -#ifdef ISDN_NEW_TBUSY isdn_net_lp_xon(lp); -#else - if (test_and_clear_bit(0, (void *) &(p->dev.tbusy))) - mark_bh(NET_BH); -#endif } return 1; case ISDN_STAT_DCONN: @@ -814,18 +891,7 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c) if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb))) lp->first_skb = NULL; } -#ifdef ISDN_NEW_TBUSY if(! lp->first_skb) isdn_net_lp_xon(lp); -#else - else { - /* - * dev.tbusy is usually cleared implicitly by isdn_net_xmit(,,lp->first_skb). - * With an empty lp->first_skb, we need to do this ourselves - */ - lp->netdev->dev.tbusy = 0; - mark_bh(NET_BH); - } -#endif /* ISDN_NEW_TBUSY */ return 1; } break; @@ -1259,14 +1325,16 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) strcpy(addinfo, " IDP"); break; } - printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n", + printk(KERN_INFO + "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n", p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19], addinfo); break; case ETH_P_ARP: - printk(KERN_INFO "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n", + printk(KERN_INFO + "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n", p[14], p[15], p[16], p[17], p[24], p[25], p[26], p[27]); break; @@ -1280,14 +1348,8 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) * * Return: 0 on success, !0 on failure. */ -#ifndef ISDN_NEW_TBUSY -/* - * Side-effects: ndev->tbusy is cleared on success. - */ -#endif -int -isdn_net_send_skb(struct net_device *ndev, isdn_net_local * lp, - struct sk_buff *skb) +int isdn_net_send_skb + (struct net_device *ndev, isdn_net_local * lp,struct sk_buff *skb) { int ret; int len = skb->len; /* save len */ @@ -1295,17 +1357,11 @@ isdn_net_send_skb(struct net_device *ndev, isdn_net_local * lp, ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); if (ret == len) { lp->transcount += len; -#ifndef ISDN_NEW_TBUSY - clear_bit(0, (void *) &(ndev->tbusy)); -#endif return 0; } if (ret < 0) { dev_kfree_skb(skb); lp->stats.tx_errors++; -#ifndef ISDN_NEW_TBUSY - clear_bit(0, (void *) &(ndev->tbusy)); -#endif return 0; } return 1; @@ -1351,11 +1407,7 @@ isdn_net_xmit(struct net_device *ndev, isdn_net_local * lp, struct sk_buff *skb) if (lp->srobin == ndev) ret = isdn_net_send_skb(ndev, lp, skb); else -#ifdef ISDN_NEW_TBUSY ret = isdn_net_start_xmit(skb, lp->srobin); -#else - ret = ndev->tbusy = isdn_net_start_xmit(skb, lp->srobin); -#endif lp->srobin = (slp->slave) ? slp->slave : ndev; slp = (isdn_net_local *) (lp->srobin->priv); if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0))) @@ -1397,6 +1449,35 @@ isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev) } } + +void isdn_net_tx_timeout(struct net_device * ndev) +{ + isdn_net_local *lp = (isdn_net_local *) ndev->priv; + + printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate); + if (!lp->dialstate){ + lp->stats.tx_errors++; + /* + * There is a certain probability that this currently + * works at all because if we always wake up the interface, + * then upper layer will try to send the next packet + * immediately. And then, the old clean_up logic in the + * driver will hopefully continue to work as it used to do. + * + * This is rather primitive right know, we better should + * clean internal queues here, in particular for multilink and + * ppp, and reset HL driver's channel, too. --HE + * + * actually, this may not matter at all, because ISDN hardware + * should not see transmitter hangs at all IMO + * changed KERN_DEBUG to KERN_WARNING to find out if this is + * ever called + */ + } + ndev->trans_start = jiffies; + netif_wake_queue(ndev); +} + /* * Try sending a packet. * If this interface isn't connected to a ISDN-Channel, find a free channel, @@ -1409,19 +1490,6 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) #ifdef CONFIG_ISDN_X25 struct concap_proto * cprot = lp -> netdev -> cprot; #endif - if (ndev->tbusy) { - if (jiffies - ndev->trans_start < (2 * HZ)) - return 1; - if (!lp->dialstate) - lp->stats.tx_errors++; - ndev->trans_start = jiffies; -#ifdef ISDN_NEW_TBUSY - isdn_net_dev_xon(ndev); -#endif - } -#ifndef ISDN_NEW_TBUSY - ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */ -#endif #ifdef CONFIG_ISDN_X25 /* At this point hard_start_xmit() passes control to the encapsulation protocol (if present). @@ -1436,9 +1504,7 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) */ if( cprot ) { int ret = cprot -> pops -> encap_and_xmit ( cprot , skb); -#ifdef ISDN_NEW_TBUSY - if(ret) isdn_net_dev_xoff(ndev); -#endif + if(ret) netif_stop_queue(ndev); return ret; } else #endif @@ -1458,9 +1524,6 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) { isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'"); dev_kfree_skb(skb); -#ifndef ISDN_NEW_TBUSY - ndev->tbusy = 0; -#endif return 0; } if (lp->phone[1]) { @@ -1476,15 +1539,11 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) if(jiffies < lp->dialwait_timer) { isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached"); dev_kfree_skb(skb); -#ifndef ISDN_NEW_TBUSY - ndev->tbusy = 0; -#endif restore_flags(flags); return 0; } else lp->dialwait_timer = 0; } - /* Grab a free ISDN-Channel */ if (((chi = isdn_get_free_channel( @@ -1492,7 +1551,8 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) lp->l2_proto, lp->l3_proto, lp->pre_device, - lp->pre_channel) + lp->pre_channel, + lp->msn) ) < 0) && ((chi = isdn_get_free_channel( @@ -1500,15 +1560,13 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) lp->l2_proto, lp->l3_proto, lp->pre_device, - lp->pre_channel^1) + lp->pre_channel^1, + lp->msn) ) < 0)) { restore_flags(flags); isdn_net_unreachable(ndev, skb, "No channel"); dev_kfree_skb(skb); -#ifndef ISDN_NEW_TBUSY - ndev->tbusy = 0; -#endif return 0; } /* Log packet, which triggered dialing */ @@ -1528,9 +1586,7 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) } restore_flags(flags); isdn_net_dial(); /* Initiate dialing */ -#ifdef ISDN_NEW_TBUSY - isdn_net_dev_xoff(ndev); -#endif + netif_stop_queue(ndev); return 1; /* let upper layer requeue skb packet */ } #endif @@ -1544,9 +1600,6 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) } lp->first_skb = skb; /* Initiate dialing */ -#ifndef ISDN_NEW_TBUSY - ndev->tbusy = 0; -#endif restore_flags(flags); isdn_net_dial(); return 0; @@ -1554,9 +1607,6 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) isdn_net_unreachable(ndev, skb, "No phone number"); dev_kfree_skb(skb); -#ifndef ISDN_NEW_TBUSY - ndev->tbusy = 0; -#endif return 0; } } else { @@ -1567,24 +1617,16 @@ isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) int ret; if (lp->first_skb) { if (isdn_net_xmit(ndev, lp, lp->first_skb)){ -#ifdef ISDN_NEW_TBUSY - isdn_net_dev_xoff(ndev); -#endif + netif_stop_queue(ndev); return 1; } lp->first_skb = NULL; } ret = (isdn_net_xmit(ndev, lp, skb)); -#ifdef ISDN_NEW_TBUSY - if(ret) isdn_net_dev_xoff(ndev); -#endif + if(ret) netif_stop_queue(ndev); return ret; } else -#ifdef ISDN_NEW_TBUSY - isdn_net_dev_xoff(ndev); -#else - ndev->tbusy = 1; -#endif + netif_stop_queue(ndev); } } return 1; @@ -1606,8 +1648,7 @@ isdn_net_close(struct net_device *dev) #ifdef CONFIG_ISDN_X25 if( cprot && cprot -> pops ) cprot -> pops -> close( cprot ); #endif - dev->tbusy = 1; - dev->start = 0; + netif_stop_queue(dev); if ((p = (((isdn_net_local *) dev->priv)->slave))) { /* If this interface has slaves, stop them also */ while (p) { @@ -1618,8 +1659,6 @@ isdn_net_close(struct net_device *dev) cprot -> pops -> close( cprot ); #endif isdn_net_hangup(p); - p->tbusy = 1; - p->start = 0; p = (((isdn_net_local *) p->priv)->slave); } } @@ -2360,7 +2399,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) * Is the interface up? * If not, reject the call actively. */ - if (!p->dev.start) { + if (!isdn_net_started(p)) { restore_flags(flags); printk(KERN_INFO "%s: incoming call, interface down -> rejected\n", lp->name); @@ -2389,7 +2428,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) p = (isdn_net_dev *) p->next; continue; } - } + } if (lp->flags & ISDN_NET_CALLBACK) { int chi; /* @@ -2411,9 +2450,10 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) isdn_get_free_channel( ISDN_USAGE_NET, lp->l2_proto, - lp->l3_proto, + lp->l3_proto, lp->pre_device, - lp->pre_channel) + lp->pre_channel, + lp->msn) ) < 0) { printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name); @@ -2528,7 +2568,8 @@ isdn_net_force_dial_lp(isdn_net_local * lp) lp->l2_proto, lp->l3_proto, lp->pre_device, - lp->pre_channel) + lp->pre_channel, + lp->msn) ) < 0) { printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name); restore_flags(flags); @@ -2626,11 +2667,13 @@ isdn_net_new(char *name, struct net_device *master) p = (((isdn_net_local *) p->priv)->slave); } ((isdn_net_local *) q->priv)->slave = &(netdev->dev); - q->interrupt = 0; - q->tbusy = 0; - q->start = master->start; } else { /* Device shall be a master */ + /* + * Watchdog timer (currently) for master only. + */ + netdev->dev.tx_timeout = isdn_net_tx_timeout; + netdev->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT; if (register_netdev(&netdev->dev) != 0) { printk(KERN_WARNING "isdn_net: Could not register net-device\n"); kfree(netdev->local); @@ -2701,7 +2744,7 @@ isdn_net_newslave(char *parm) if (n->local->master) return NULL; /* Master must not be started yet */ - if (n->dev.start) + if (isdn_net_started(n)) return NULL; return (isdn_net_new(newname, &(n->dev))); } @@ -2744,9 +2787,8 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) #ifdef CONFIG_ISDN_X25 struct concap_proto * cprot = p -> cprot; #endif - if (p->dev.start) { - printk(KERN_WARNING - "%s: cannot change encap when if is up\n", + if (isdn_net_started(p)) { + printk(KERN_WARNING "%s: cannot change encap when if is up\n", lp->name); return -EBUSY; } @@ -2837,10 +2879,9 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) /* If binding is exclusive, try to grab the channel */ save_flags(flags); - if ((i = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto, - lp->l3_proto, - drvidx, - chidx)) < 0) { + if ((i = isdn_get_free_channel(ISDN_USAGE_NET, + lp->l2_proto, lp->l3_proto, drvidx, + chidx, lp->msn)) < 0) { /* Grab failed, because desired channel is in use */ lp->exclusive = -1; restore_flags(flags); @@ -3181,14 +3222,7 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) save_flags(flags); cli(); - if (p->local->master) { - /* If it's a slave, it may be removed even if it is busy. However - * it has to be hung up first. - */ - isdn_net_hangup(&p->dev); - p->dev.start = 0; - } - if (p->dev.start) { + if (isdn_net_started(p)) { restore_flags(flags); return -EBUSY; } diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index 06c2d83ad..2551dc344 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.60 1999/11/04 20:29:55 he Exp $ +/* $Id: isdn_ppp.c,v 1.62 2000/02/12 19:26:55 kai Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,31 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.62 2000/02/12 19:26:55 kai + * adopted to latest 2.3 softnet changes. + * + * tested with PPP and MPPP, it works here. + * can somebody check raw-ip? + * + * also changed std2kern, stddiff for bash-1 compatibility, + * hope this doesn't break anything. + * + * Revision 1.61 1999/11/20 22:14:14 detabc + * added channel dial-skip in case of external use + * (isdn phone or another isdn device) on the same NTBA. + * usefull with two or more card's connected the different NTBA's. + * global switchable in kernel-config and also per netinterface. + * + * add auto disable of netinterface's in case of: + * to many connection's in short time. + * config mistakes (wrong encapsulation, B2-protokoll or so on) on local + * or remote side. + * wrong password's or something else to a ISP (syncppp). + * + * possible encapsulations for this future are: + * ISDN_NET_ENCAP_SYNCPPP, ISDN_NET_ENCAP_UIHDLC, ISDN_NET_ENCAP_RAWIP, + * and ISDN_NET_ENCAP_CISCOHDLCK. + * * Revision 1.60 1999/11/04 20:29:55 he * applied Andre Beck's reset_free fix * @@ -306,7 +331,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.60 $"; +char *isdn_ppp_revision = "$Revision: 1.62 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; @@ -699,7 +724,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) case PPPIOCGIFNAME: if(!lp) return -EINVAL; - if ((r = set_arg((void *) arg, lp->name,strlen(lp->name)))) + if ((r = set_arg((void *) arg, lp->name, strlen(lp->name)))) return r; break; case PPPIOCGMPFLAGS: /* get configuration flags */ @@ -721,8 +746,8 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) } if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) { if (lp) { - lp->netdev->dev.tbusy = 0; - mark_bh(NET_BH); /* OK .. we are ready to send buffers */ + /* OK .. we are ready to send buffers */ + netif_wake_queue(&lp->netdev->dev); } } is->pppcfg = val; diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index 5b301adb9..0503c67a2 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.c,v 1.80 1999/11/07 13:34:30 armin Exp $ +/* $Id: isdn_tty.c,v 1.82 2000/01/23 18:45:37 keil Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -20,6 +20,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.82 2000/01/23 18:45:37 keil + * Change EAZ mapping to forbit the use of cards (insert a "-" for the MSN) + * + * Revision 1.81 2000/01/20 19:55:33 keil + * Add FAX Class 1 support + * * Revision 1.80 1999/11/07 13:34:30 armin * Fixed AT command line editor * @@ -348,6 +354,7 @@ #endif #define FIX_FILE_TRANSFER +#define DUMMY_HAYES_AT /* Prototypes */ @@ -372,7 +379,7 @@ static int bit2si[8] = static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.80 $"; +char *isdn_tty_revision = "$Revision: 1.82 $"; /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() @@ -976,7 +983,7 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) m->mdmreg[REG_SI1I] = si2bit[si]; save_flags(flags); cli(); - i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1); + i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); if (i < 0) { restore_flags(flags); isdn_tty_modem_result(6, info); @@ -1187,7 +1194,7 @@ isdn_tty_resume(char *id, modem_info * info, atemu * m) m->mdmreg[REG_SI1I] = si2bit[si]; save_flags(flags); cli(); - i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1); + i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); if (i < 0) { restore_flags(flags); isdn_tty_modem_result(6, info); @@ -1281,7 +1288,7 @@ isdn_tty_send_msg(modem_info * info, atemu * m, char *msg) m->mdmreg[REG_SI1I] = si2bit[si]; save_flags(flags); cli(); - i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1); + i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); if (i < 0) { restore_flags(flags); isdn_tty_modem_result(6, info); @@ -1555,6 +1562,23 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co } } } else + if (TTY_IS_FCLASS1(info)) { + int cc = isdn_tty_handleDLEdown(info, m, c); + + if (info->vonline & 4) { /* ETX seen */ + isdn_ctrl c; + + c.command = ISDN_CMD_FAXCMD; + c.driver = info->isdn_driver; + c.arg = info->isdn_channel; + c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL; + c.parm.aux.subcmd = ETX; + isdn_command(&c); + } + info->vonline = 0; + printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc,c); + info->xmit_count += cc; + } else #endif info->xmit_count += c; } else { @@ -2567,7 +2591,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) (info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) int -isdn_tty_stat_callback(int i, isdn_ctrl * c) +isdn_tty_stat_callback(int i, isdn_ctrl *c) { int mi; modem_info *info; @@ -2668,8 +2692,7 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) { strcpy(info->emu.connmsg, c->parm.num); isdn_tty_modem_result(1, info); - } - else + } else isdn_tty_modem_result(5, info); } if (USG_VOICE(dev->usage[i])) @@ -2720,7 +2743,7 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) #ifdef CONFIG_ISDN_TTY_FAX case ISDN_STAT_FAXIND: if (TTY_IS_ACTIVE(info)) { - isdn_tty_fax_command(info); + isdn_tty_fax_command(info, c); } break; #endif @@ -3247,8 +3270,22 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) info->xmit_size /= 10; } break; + case 'C': + /* &C - DCD Status */ + p[0]++; + switch (isdn_getnum(p)) { + case 0: + m->mdmreg[REG_DCD] &= ~BIT_DCD; + break; + case 1: + m->mdmreg[REG_DCD] |= BIT_DCD; + break; + default: + PARSE_ERROR1 + } + break; case 'D': - /* &D - Set DCD-Low-behavior */ + /* &D - Set DTR-Low-behavior */ p[0]++; switch (isdn_getnum(p)) { case 0: @@ -3280,6 +3317,14 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) isdn_tty_reset_profile(m); isdn_tty_modem_reset_regs(info, 1); break; +#ifdef DUMMY_HAYES_AT + case 'K': + /* only for be compilant with common scripts */ + /* &K Flowcontrol - no function */ + p[0]++; + isdn_getnum(p); + break; +#endif case 'L': /* &L -Set Numbers to listen on */ p[0]++; @@ -3565,8 +3610,10 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) 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"); + if (TTY_IS_FCLASS2(info)) + sprintf(rs, "\r\n2"); + else if (TTY_IS_FCLASS1(info)) + sprintf(rs, "\r\n1"); #endif isdn_tty_at_cout(rs, info); break; @@ -3582,11 +3629,25 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) m->mdmreg[REG_PSIZE] * 16; break; #ifdef CONFIG_ISDN_TTY_FAX + case '1': + p[0]++; + if (!(dev->global_features & + ISDN_FEATURE_L3_FCLASS1)) + PARSE_ERROR1; + m->mdmreg[REG_SI1] = 1; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1; + info->xmit_size = + m->mdmreg[REG_PSIZE] * 16; + break; case '2': p[0]++; + if (!(dev->global_features & + ISDN_FEATURE_L3_FCLASS2)) + PARSE_ERROR1; m->mdmreg[REG_SI1] = 1; m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; - m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FAX; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2; info->xmit_size = m->mdmreg[REG_PSIZE] * 16; break; @@ -3601,11 +3662,17 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) break; case '?': p[0]++; + strcpy(rs, "\r\n0,"); #ifdef CONFIG_ISDN_TTY_FAX - isdn_tty_at_cout("\r\n0,2,8", info); -#else - isdn_tty_at_cout("\r\n0,8", info); + if (dev->global_features & + ISDN_FEATURE_L3_FCLASS1) + strcat(rs, "1,"); + if (dev->global_features & + ISDN_FEATURE_L3_FCLASS2) + strcat(rs, "2,"); #endif + strcat(rs, "8"); + isdn_tty_at_cout(rs, info); break; default: PARSE_ERROR1; @@ -3995,6 +4062,15 @@ isdn_tty_parse_at(modem_info * info) default: } break; +#ifdef DUMMY_HAYES_AT + case 'L': + case 'M': + /* only for be compilant with common scripts */ + /* no function */ + p++; + isdn_getnum(&p); + break; +#endif case 'O': /* O - Go online */ p++; diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h index 1c27b8300..ff7e479f0 100644 --- a/drivers/isdn/isdn_tty.h +++ b/drivers/isdn/isdn_tty.h @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.h,v 1.17 1999/09/21 19:00:35 armin Exp $ +/* $Id: isdn_tty.h,v 1.18 2000/01/20 19:55:33 keil Exp $ * header for Linux ISDN subsystem, tty related functions (linklevel). * @@ -20,6 +20,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.h,v $ + * Revision 1.18 2000/01/20 19:55:33 keil + * Add FAX Class 1 support + * * Revision 1.17 1999/09/21 19:00:35 armin * Extended FCON message with added CPN * can now be activated with Bit 1 of Reg 23. @@ -160,6 +163,13 @@ #define BIT_CPN 1 #define BIT_CPNFCON 2 +#define TTY_IS_FCLASS1(info) \ + ((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \ + (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS1)) +#define TTY_IS_FCLASS2(info) \ + ((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \ + (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS2)) + extern void isdn_tty_modem_escape(void); extern void isdn_tty_modem_ring(void); extern void isdn_tty_carrier_timeout(void); @@ -175,6 +185,6 @@ 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 int isdn_tty_fax_command(modem_info *, isdn_ctrl *); 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 index 9b7268b32..33f67ff4b 100644 --- a/drivers/isdn/isdn_ttyfax.c +++ b/drivers/isdn/isdn_ttyfax.c @@ -1,9 +1,9 @@ -/* $Id: isdn_ttyfax.c,v 1.4 1999/09/21 19:00:35 armin Exp $ +/* $Id: isdn_ttyfax.c,v 1.6 2000/01/26 00:41:13 keil 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 + * 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 @@ -20,6 +20,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ttyfax.c,v $ + * Revision 1.6 2000/01/26 00:41:13 keil + * add "00" as dummy msn in isdn_get_free_channel call + * + * Revision 1.5 2000/01/20 19:55:33 keil + * Add FAX Class 1 support + * * Revision 1.4 1999/09/21 19:00:35 armin * Extended FCON message with added CPN * can now be activated with Bit 1 of Reg 23. @@ -50,31 +56,32 @@ #include "isdn_ttyfax.h" -static char *isdn_tty_fax_revision = "$Revision: 1.4 $"; +static char *isdn_tty_fax_revision = "$Revision: 1.6 $"; #define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; } static char * isdn_getrev(const char *revision) { - char *rev; - char *p; + char *rev; + char *p; - if ((p = strchr(revision, ':'))) { - rev = p + 2; - p = strchr(rev, '$'); - *--p = 0; - } else - rev = "???"; - return rev; + 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) + +static void +isdn_tty_fax_modem_result(int code, modem_info * info) { atemu *m = &info->emu; T30_s *f = info->fax; @@ -85,7 +92,7 @@ static void isdn_tty_fax_modem_result(int code, modem_info * info) static char *msg[] = {"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:", "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:", - "+FCFR", "+FPTS:", "+FET:" }; + "+FCFR", "+FPTS:", "+FET:"}; isdn_tty_at_cout("\r\n", info); @@ -115,12 +122,12 @@ static void isdn_tty_fax_modem_result(int code, modem_info * info) case 3: /* +FCSI */ case 8: /* +FTSI */ sprintf(rs, "\"%s\"", f->r_id); - isdn_tty_at_cout(rs, info); + isdn_tty_at_cout(rs, info); break; case 4: /* +FDIS */ rs[0] = 0; rp = &f->r_resolution; - for(i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) { sprintf(rss, "%c%s", rp[i] + 48, (i < 7) ? "," : ""); strcat(rs, rss); @@ -128,18 +135,18 @@ static void isdn_tty_fax_modem_result(int code, modem_info * info) 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); + rs, info->line); #endif break; case 5: /* +FHNG */ sprintf(rs, "%d", f->code); - isdn_tty_at_cout(rs, info); + 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++) { + for (i = 0; i < 8; i++) { sprintf(rss, "%c%s", rp[i] + 48, (i < 7) ? "," : ""); strcat(rs, rss); @@ -147,127 +154,169 @@ static void isdn_tty_fax_modem_result(int code, modem_info * info) 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); + rs, info->line); #endif break; case 7: /* CONNECT */ info->faxonline |= 2; break; - case 9: /* FCFR */ + case 9: /* FCFR */ break; - case 10: /* FPTS */ + case 10: /* FPTS */ isdn_tty_at_cout("1", info); break; - case 11: /* FET */ + case 11: /* FET */ sprintf(rs, "%d", f->fet); - isdn_tty_at_cout(rs, info); + isdn_tty_at_cout(rs, info); break; } isdn_tty_at_cout("\r\n", info); switch (code) { - case 7: /* CONNECT */ + case 7: /* CONNECT */ info->online = 2; if (info->faxonline & 1) { sprintf(rs, "%c", XON); - isdn_tty_at_cout(rs, info); + isdn_tty_at_cout(rs, info); + } + break; + } +} + +int +isdn_tty_fax_command1(modem_info * info, isdn_ctrl * c) +{ + static char *msg[] = + {"OK", "CONNECT", "NO CARRIER", "ERROR", "FCERROR"}; + +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: FCLASS1 cmd(%d)\n", c->parm.aux.cmd); +#endif + if (c->parm.aux.cmd < ISDN_FAX_CLASS1_QUERY) { + if (info->online) + info->online = 1; + isdn_tty_at_cout("\r\n", info); + isdn_tty_at_cout(msg[c->parm.aux.cmd], info); + isdn_tty_at_cout("\r\n", info); + } + switch (c->parm.aux.cmd) { + case ISDN_FAX_CLASS1_CONNECT: + info->online = 2; + break; + case ISDN_FAX_CLASS1_OK: + case ISDN_FAX_CLASS1_FCERROR: + case ISDN_FAX_CLASS1_ERROR: + case ISDN_FAX_CLASS1_NOCARR: + break; + case ISDN_FAX_CLASS1_QUERY: + isdn_tty_at_cout("\r\n", info); + if (!c->parm.aux.para[0]) { + isdn_tty_at_cout(msg[ISDN_FAX_CLASS1_ERROR], info); + isdn_tty_at_cout("\r\n", info); + } else { + isdn_tty_at_cout(c->parm.aux.para, info); + isdn_tty_at_cout("\r\nOK\r\n", info); } break; } + return (0); } int -isdn_tty_fax_command(modem_info * info) +isdn_tty_fax_command(modem_info * info, isdn_ctrl * c) { T30_s *f = info->fax; char rs[10]; + if (TTY_IS_FCLASS1(info)) + return (isdn_tty_fax_command1(info, c)); + #ifdef ISDN_TTY_FAX_CMD_DEBUG printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n", - f->r_code, info->line); + f->r_code, info->line); #endif - switch(f->r_code) { + switch (f->r_code) { case ISDN_TTY_FAX_FCON: info->faxonline = 1; - isdn_tty_fax_modem_result(2, info); /* +FCON */ - return(0); + 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); + 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 */ + isdn_tty_fax_modem_result(3, info); /* +FCSI */ if (info->faxonline & 16) - isdn_tty_fax_modem_result(8, info); /* +FTSI */ - return(0); + 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); + 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); + isdn_tty_at_cout(rs, info); } else { sprintf(rs, "%c", 0x18); - isdn_tty_at_cout(rs, info); + isdn_tty_at_cout(rs, info); } - info->faxonline &= ~2; /* leave data mode */ + 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); + 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 */ + isdn_tty_fax_modem_result(6, info); /* +FDCS */ + isdn_tty_fax_modem_result(7, info); /* CONNECT */ f->phase = ISDN_FAX_PHASE_C; - return(0); + 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); + 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); + 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); + 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 */ + 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); + return (0); case ISDN_TTY_FAX_PTS: - isdn_tty_fax_modem_result(10, info); /* +FPTS */ + 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 */ + isdn_tty_fax_modem_result(0, info); /* OK */ } - return(0); + return (0); case ISDN_TTY_FAX_EOP: - info->faxonline &= ~2; /* leave data mode */ + info->faxonline &= ~2; /* leave data mode */ info->online = 1; f->phase = ISDN_FAX_PHASE_D; - return(0); + return (0); } - return(-1); + return (-1); } void -isdn_tty_fax_bitorder(modem_info *info, struct sk_buff *skb) +isdn_tty_fax_bitorder(modem_info * info, struct sk_buff *skb) { __u8 LeftMask; __u8 RightMask; @@ -276,13 +325,13 @@ isdn_tty_fax_bitorder(modem_info *info, struct sk_buff *skb) int i; if (!info->fax->bor) { - for(i = 0; i < skb->len; i++) { + for (i = 0; i < skb->len; i++) { Data = skb->data[i]; for ( - LeftMask = 0x80, RightMask = 0x01; - LeftMask > RightMask; - LeftMask >>= 1, RightMask <<= 1 - ) { + LeftMask = 0x80, RightMask = 0x01; + LeftMask > RightMask; + LeftMask >>= 1, RightMask <<= 1 + ) { fBit = (Data & LeftMask); if (Data & RightMask) Data |= LeftMask; @@ -300,10 +349,103 @@ isdn_tty_fax_bitorder(modem_info *info, struct sk_buff *skb) } /* + * Parse AT+F.. FAX class 1 commands + */ + +int +isdn_tty_cmd_FCLASS1(char **p, modem_info * info) +{ + static char *cmd[] = + {"AE", "TS", "RS", "TM", "RM", "TH", "RH"}; + isdn_ctrl c; + int par, i; + long flags; + + for (c.parm.aux.cmd = 0; c.parm.aux.cmd < 7; c.parm.aux.cmd++) + if (!strncmp(p[0], cmd[c.parm.aux.cmd], 2)) + break; + +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 (%s,%d)\n", p[0], c.parm.aux.cmd); +#endif + if (c.parm.aux.cmd == 7) + PARSE_ERROR1; + + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + c.parm.aux.subcmd = AT_QUERY; + break; + case '=': + p[0]++; + if (*p[0] == '?') { + p[0]++; + c.parm.aux.subcmd = AT_EQ_QUERY; + } else { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + c.parm.aux.subcmd = AT_EQ_VALUE; + c.parm.aux.para[0] = par; + } + break; + case 0: + c.parm.aux.subcmd = AT_COMMAND; + break; + default: + PARSE_ERROR1; + } + c.command = ISDN_CMD_FAXCMD; +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 %d/%d/%d)\n", + c.parm.aux.cmd, c.parm.aux.subcmd, c.parm.aux.para[0]); +#endif + if (info->isdn_driver < 0) { + save_flags(flags); + cli(); + if ((c.parm.aux.subcmd == AT_EQ_VALUE) || + (c.parm.aux.subcmd == AT_COMMAND)) { + restore_flags(flags); + PARSE_ERROR1; + } + /* get a temporary connection to the first free fax driver */ + i = isdn_get_free_channel(ISDN_USAGE_FAX, ISDN_PROTO_L2_FAX, + ISDN_PROTO_L3_FCLASS1, -1, -1, "00"); + if (i < 0) { + restore_flags(flags); + PARSE_ERROR1; + } + info->isdn_driver = dev->drvmap[i]; + info->isdn_channel = dev->chanmap[i]; + info->drv_index = i; + dev->m_idx[i] = info->line; + c.driver = info->isdn_driver; + c.arg = info->isdn_channel; + isdn_command(&c); + isdn_free_channel(info->isdn_driver, info->isdn_channel, + ISDN_USAGE_FAX); + info->isdn_driver = -1; + info->isdn_channel = -1; + if (info->drv_index >= 0) { + dev->m_idx[info->drv_index] = -1; + info->drv_index = -1; + } + restore_flags(flags); + } else { + c.driver = info->isdn_driver; + c.arg = info->isdn_channel; + isdn_command(&c); + } + return 1; +} + +/* * Parse AT+F.. FAX class 2 commands */ -int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) +int +isdn_tty_cmd_FCLASS2(char **p, modem_info * info) { atemu *m = &info->emu; T30_s *f = info->fax; @@ -311,10 +453,11 @@ int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) int par; char rs[50]; char rss[50]; - int maxdccval[]={1,5,2,2,3,2,0,7}; + int maxdccval[] = + {1, 5, 2, 2, 3, 2, 0, 7}; /* FAA still unchanged */ - if (!strncmp(p[0], "AA", 2)) { /* TODO */ + if (!strncmp(p[0], "AA", 2)) { /* TODO */ p[0] += 2; switch (*p[0]) { case '?': @@ -332,399 +475,363 @@ int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) 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; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par); #endif - } - break; + } + break; default: - PARSE_ERROR1; + PARSE_ERROR1; } - return 0; - } - + return 0; + } /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */ - if (!strncmp(p[0], "BADMUL", 6)){ - p[0] +=6; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + return 0; + } /* BOR=n - Phase C bit order, 0=direct, 1=reverse */ - if (!strncmp(p[0], "BOR", 3)){ - p[0] +=3; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par); #endif - } - break; - default: - PARSE_ERROR1; - } - return 0; - } - + } + break; + default: + PARSE_ERROR1; + } + return 0; + } /* NBC=n - No Best Capabilities */ - if (!strncmp(p[0], "NBC", 3)){ - p[0] +=3; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par); #endif - } - break; - default: - PARSE_ERROR1; - } - return 0; - } - + } + break; + default: + PARSE_ERROR1; + } + return 0; + } /* BUF? - Readonly buffersize readout */ - if (!strncmp(p[0], "BUF?", 4)) { - p[0] += 4; + 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])); + 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; - } - + 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)) { + if (!strncmp(p[0], "CIG", 3)) { int i, r; - p[0] += 3; + p[0] += 3; switch (*p[0]) { case '?': p[0]++; sprintf(rs, "\r\n\"%s\"", f->pollid); - isdn_tty_at_cout(rs, info); - break; + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + 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; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + 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; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + return 0; + } /* CTCRTY=value - ECM retry count */ - if (!strncmp(p[0], "CTCRTY", 6)){ - p[0] +=6; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + return 0; + } /* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */ - if (!strncmp(p[0], "DCC", 3)) { + if (!strncmp(p[0], "DCC", 3)) { char *rp = &f->resolution; int i; - p[0] += 3; - switch(*p[0]) { + 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); + 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++) { + 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] == ',') + if (*p[0] == ',') p[0]++; - } else 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]); + 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; - } - + return 0; + } /* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */ - if (!strncmp(p[0], "DIS", 3)) { + if (!strncmp(p[0], "DIS", 3)) { char *rp = &f->resolution; int i; - p[0] += 3; - switch(*p[0]) { + 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); + 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++) { + 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] == ',') + if (*p[0] == ',') p[0]++; - } else 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]); + 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; + 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))) { + ((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 @@ -735,11 +842,11 @@ int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) 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) { + } 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 */ + isdn_tty_fax_modem_result(7, info); /* CONNECT */ break; case 1: /* next doc will be received */ f->phase = ISDN_FAX_PHASE_B; @@ -747,35 +854,36 @@ int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) case 2: /* fax session is terminating */ f->phase = ISDN_FAX_PHASE_E; break; - default: + default: PARSE_ERROR1; } } } else { PARSE_ERROR1; } - return 1; + 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}; + 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 */ + 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++) { + 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] == ',') + if (*p[0] == ',') p[0]++; - } else 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", @@ -789,48 +897,43 @@ int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) isdn_command(&cmd); if (f->phase == ISDN_FAX_PHASE_D) { f->phase = ISDN_FAX_PHASE_C; - isdn_tty_fax_modem_result(7, info); /* CONNECT */ + isdn_tty_fax_modem_result(7, info); /* CONNECT */ } } else { PARSE_ERROR1; } - return 1; - } - + 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; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + return 0; + } /* ET=n - End of page or document */ if (!strncmp(p[0], "ET=", 3)) { p[0] += 3; @@ -857,7 +960,6 @@ int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) } return 0; } - /* K - terminate */ if (!strncmp(p[0], "K", 1)) { p[0] += 1; @@ -866,205 +968,191 @@ int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) isdn_tty_modem_hup(info, 1); return 1; } - /* LID=string - local fax ID */ - if (!strncmp(p[0], "LID", 3)) { + if (!strncmp(p[0], "LID", 3)) { int i, r; - p[0] += 3; + p[0] += 3; switch (*p[0]) { case '?': p[0]++; sprintf(rs, "\r\n\"%s\"", f->id); - isdn_tty_at_cout(rs, info); - break; + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + return 0; + } /* MDL? - DCE Model */ - if (!strncmp(p[0], "MDL?", 4)) { - p[0] += 4; + if (!strncmp(p[0], "MDL?", 4)) { + p[0] += 4; #ifdef ISDN_TTY_FAX_STAT_DEBUG - printk(KERN_DEBUG "isdn_tty: FMDL?\n"); + printk(KERN_DEBUG "isdn_tty: FMDL?\n"); #endif - isdn_tty_at_cout("\r\nisdn4linux", info); - return 0; - } - + isdn_tty_at_cout("\r\nisdn4linux", info); + return 0; + } /* MFR? - DCE Manufacturer */ - if (!strncmp(p[0], "MFR?", 4)) { - p[0] += 4; + if (!strncmp(p[0], "MFR?", 4)) { + p[0] += 4; #ifdef ISDN_TTY_FAX_STAT_DEBUG - printk(KERN_DEBUG "isdn_tty: FMFR?\n"); + printk(KERN_DEBUG "isdn_tty: FMFR?\n"); #endif - isdn_tty_at_cout("\r\nisdn4linux", info); - return 0; - } - + 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; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + return 0; + } /* PHCTO=value - DTE phase C timeout */ - if (!strncmp(p[0], "PHCTO", 5)){ - p[0] +=5; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + return 0; + } /* REL=n - Phase C received EOL alignment */ - if (!strncmp(p[0], "REL", 3)) { - p[0] += 3; + 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); + 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; + 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); + printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par); #endif - } - break; + } + break; default: PARSE_ERROR1; } - return 0; - } - + return 0; + } /* REV? - DCE Revision */ - if (!strncmp(p[0], "REV?", 4)) { - p[0] += 4; + if (!strncmp(p[0], "REV?", 4)) { + p[0] += 4; #ifdef ISDN_TTY_FAX_STAT_DEBUG - printk(KERN_DEBUG "isdn_tty: FREV?\n"); + 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; - } - + isdn_tty_at_cout(rs, info); + return 0; + } /* Phase C Transmit Data Block Size */ - if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */ + 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]); + 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; + 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]); + printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); PARSE_ERROR1; } +int +isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) +{ + if (TTY_IS_FCLASS2(info)) + return (isdn_tty_cmd_FCLASS2(p, info)); + else if (TTY_IS_FCLASS1(info)) + return (isdn_tty_cmd_FCLASS1(p, info)); + PARSE_ERROR1; +} |