diff options
Diffstat (limited to 'drivers/isdn')
96 files changed, 25827 insertions, 9151 deletions
diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index 758e30d2b..3787c735f 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -9,24 +9,44 @@ if [ "$CONFIG_INET" != "n" ]; then fi fi bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO +if [ "$CONFIG_X25" != "n" ]; then + bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' 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 if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then + bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO + if [ "$CONFIG_HISAX_EURO" != "n" ]; then + bool 'Support for german tarifinfo' CONFIG_DE_AOC + bool 'Support for australian Microlink service (not for std. EURO)' CONFIG_HISAX_ML + 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 16.3c' CONFIG_HISAX_TELES3C bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 - bool 'HiSax Support for Elsa ISA cards' CONFIG_HISAX_ELSA_PCC - bool 'HiSax Support for Elsa PCMCIA card' CONFIG_HISAX_ELSA_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 EURO/DSS1' CONFIG_HISAX_EURO - bool 'HiSax Support for US/NI-1' CONFIG_HISAX_NI1 - bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 + bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA + bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM + bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT + bool 'HiSax Support for Sedlbauer speed card/win/star' 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 + if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then + bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930 + fi fi if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN + dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN fi dep_tristate 'AVM-B1 with CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then bool 'Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON fi + diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index c5f508217..35d56d142 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 +ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 L_OBJS := LX_OBJS := @@ -13,11 +13,15 @@ O_TARGET := ifeq ($(CONFIG_ISDN),y) L_TARGET := isdn.a - L_OBJS += isdn_common.o isdn_net.o isdn_tty.o isdn_cards.o - LX_OBJS += isdn_syms.o + L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o isdn_v110.o + LX_OBJS += isdn_common.o ifdef CONFIG_ISDN_PPP L_OBJS += isdn_ppp.o endif + ifdef CONFIG_ISDN_X25 + L_OBJS += isdn_x25iface.o + L_OBJS += isdn_concap.o + endif ifdef CONFIG_ISDN_AUDIO L_OBJS += isdn_audio.o endif @@ -25,11 +29,15 @@ else ifeq ($(CONFIG_ISDN),m) M_OBJS += isdn.o O_TARGET += isdn.o - O_OBJS += isdn_common.o isdn_net.o isdn_tty.o - OX_OBJS += isdn_syms.o + O_OBJS += isdn_net.o isdn_tty.o isdn_v110.o + OX_OBJS += isdn_common.o ifdef CONFIG_ISDN_PPP O_OBJS += isdn_ppp.o endif + ifdef CONFIG_ISDN_X25 + O_OBJS += isdn_x25iface.o + O_OBJS += isdn_concap.o + endif ifdef CONFIG_ISDN_AUDIO O_OBJS += isdn_audio.o endif @@ -96,5 +104,15 @@ else endif endif +ifeq ($(CONFIG_ISDN_DRV_ACT2000),y) + L_OBJS += act2000/act2000.o + SUB_DIRS += act2000 + MOD_SUB_DIRS += act2000 +else + ifeq ($(CONFIG_ISDN_DRV_ACT2000),m) + MOD_SUB_DIRS += act2000 + endif +endif + include $(TOPDIR)/Rules.make diff --git a/drivers/isdn/act2000/.cvsignore b/drivers/isdn/act2000/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/isdn/act2000/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/isdn/act2000/Makefile b/drivers/isdn/act2000/Makefile new file mode 100644 index 000000000..0e3c4a7e1 --- /dev/null +++ b/drivers/isdn/act2000/Makefile @@ -0,0 +1,15 @@ +L_OBJS := +M_OBJS := +O_OBJS := module.o capi.o act2000_isa.o + +O_TARGET := +ifeq ($(CONFIG_ISDN_DRV_ACT2000),y) + O_TARGET += act2000.o +else + ifeq ($(CONFIG_ISDN_DRV_ACT2000),m) + O_TARGET += act2000.o + M_OBJS = act2000.o + endif +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/isdn/act2000/act2000.h b/drivers/isdn/act2000/act2000.h new file mode 100644 index 000000000..534cc42f9 --- /dev/null +++ b/drivers/isdn/act2000/act2000.h @@ -0,0 +1,239 @@ +/* $Id: act2000.h,v 1.5 1997/10/09 22:22:59 fritz Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: act2000.h,v $ + * Revision 1.5 1997/10/09 22:22:59 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.4 1997/09/25 17:25:37 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.3 1997/09/24 23:11:43 fritz + * Optimized IRQ load and polling-mode. + * + * Revision 1.2 1997/09/24 19:44:12 fritz + * Added MSN mapping support, some cleanup. + * + * Revision 1.1 1997/09/23 18:00:05 fritz + * New driver for IBM Active 2000. + * + */ + +#ifndef act2000_h +#define act2000_h + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include <linux/module.h> +#include <linux/version.h> +#endif + +#define ACT2000_IOCTL_SETPORT 1 +#define ACT2000_IOCTL_GETPORT 2 +#define ACT2000_IOCTL_SETIRQ 3 +#define ACT2000_IOCTL_GETIRQ 4 +#define ACT2000_IOCTL_SETBUS 5 +#define ACT2000_IOCTL_GETBUS 6 +#define ACT2000_IOCTL_SETPROTO 7 +#define ACT2000_IOCTL_GETPROTO 8 +#define ACT2000_IOCTL_SETMSN 9 +#define ACT2000_IOCTL_GETMSN 10 +#define ACT2000_IOCTL_LOADBOOT 11 +#define ACT2000_IOCTL_ADDCARD 12 + +#define ACT2000_IOCTL_TEST 98 +#define ACT2000_IOCTL_DEBUGVAR 99 + +#define ACT2000_BUS_ISA 1 +#define ACT2000_BUS_MCA 2 +#define ACT2000_BUS_PCMCIA 3 + +/* Struct for adding new cards */ +typedef struct act2000_cdef { + int bus; + int port; + int irq; + char id[10]; +} act2000_cdef; + +/* Struct for downloading firmware */ +typedef struct act2000_ddef { + int length; /* Length of code */ + char *buffer; /* Ptr. to code */ +} act2000_ddef; + +typedef struct act2000_fwid { + char isdn[4]; + char revlen[2]; + char revision[504]; +} act2000_fwid; + +#if defined(__KERNEL__) || defined(__DEBUGVAR__) + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/tqueue.h> +#include <linux/interrupt.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/major.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/malloc.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/ioport.h> +#include <linux/timer.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/isdnif.h> + +#endif /* __KERNEL__ */ + +#define ACT2000_PORTLEN 8 + +#define ACT2000_FLAGS_RUNNING 1 /* Cards driver activated */ +#define ACT2000_FLAGS_PVALID 2 /* Cards port is valid */ +#define ACT2000_FLAGS_IVALID 4 /* Cards irq is valid */ +#define ACT2000_FLAGS_LOADED 8 /* Firmware loaded */ + +#define ACT2000_BCH 2 /* # of channels per card */ + +/* D-Channel states */ +#define ACT2000_STATE_NULL 0 +#define ACT2000_STATE_ICALL 1 +#define ACT2000_STATE_OCALL 2 +#define ACT2000_STATE_IWAIT 3 +#define ACT2000_STATE_OWAIT 4 +#define ACT2000_STATE_IBWAIT 5 +#define ACT2000_STATE_OBWAIT 6 +#define ACT2000_STATE_BWAIT 7 +#define ACT2000_STATE_BHWAIT 8 +#define ACT2000_STATE_BHWAIT2 9 +#define ACT2000_STATE_DHWAIT 10 +#define ACT2000_STATE_DHWAIT2 11 +#define ACT2000_STATE_BSETUP 12 +#define ACT2000_STATE_ACTIVE 13 + +#define ACT2000_MAX_QUEUED 8000 /* 2 * maxbuff */ + +#define ACT2000_LOCK_TX 0 +#define ACT2000_LOCK_RX 1 + +typedef struct act2000_chan { + unsigned short callref; /* Call Reference */ + unsigned short fsm_state; /* Current D-Channel state */ + unsigned short eazmask; /* EAZ-Mask for this Channel */ + short queued; /* User-Data Bytes in TX queue */ + unsigned short plci; + unsigned short ncci; + unsigned char l2prot; /* Layer 2 protocol */ + unsigned char l3prot; /* Layer 3 protocol */ +} act2000_chan; + +typedef struct msn_entry { + char eaz; + char msn[16]; + struct msn_entry * next; +} msn_entry; + +typedef struct irq_data_isa { + __u8 *rcvptr; + __u16 rcvidx; + __u16 rcvlen; + struct sk_buff *rcvskb; + __u8 rcvignore; + __u8 rcvhdr[8]; +} irq_data_isa; + +typedef union irq_data { + irq_data_isa isa; +} irq_data; + +/* + * Per card driver data + */ +typedef struct act2000_card { + unsigned short port; /* Base-port-address */ + unsigned short irq; /* Interrupt */ + u_char ptype; /* Protocol type (1TR6 or Euro) */ + u_char bus; /* Cardtype (ISA, MCA, PCMCIA) */ + struct act2000_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 ackq; /* Data-Ack-Message queue */ + 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 timer_list ptimer; /* Poll timer */ + struct tq_struct snd_tq; /* Task struct for xmit bh */ + struct tq_struct rcv_tq; /* Task struct for rcv bh */ + struct tq_struct poll_tq; /* Task struct for polled rcv bh */ + msn_entry *msn_list; + unsigned short msgnum; /* Message number fur sending */ + act2000_chan bch[ACT2000_BCH]; /* B-Channel status/control */ + char status_buf[256]; /* Buffer for status messages */ + char *status_buf_read; + char *status_buf_write; + char *status_buf_end; + irq_data idat; /* Data used for IRQ handler */ + isdn_if interface; /* Interface to upper layer */ + char regname[35]; /* Name used for request_region */ +} act2000_card; + +extern act2000_card *cards; + +extern __inline__ void act2000_schedule_tx(act2000_card *card) +{ + queue_task(&card->snd_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +extern __inline__ void act2000_schedule_rx(act2000_card *card) +{ + queue_task(&card->rcv_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +extern __inline__ void act2000_schedule_poll(act2000_card *card) +{ + queue_task(&card->poll_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +extern char *act2000_find_eaz(act2000_card *, char); + +#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ +#endif /* act2000_h */ diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c new file mode 100644 index 000000000..078760a0c --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.c @@ -0,0 +1,525 @@ +/* $Id: act2000_isa.c,v 1.5 1998/02/12 23:06:47 keil Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: act2000_isa.c,v $ + * Revision 1.5 1998/02/12 23:06:47 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.4 1997/10/09 22:23:00 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.3 1997/09/25 17:25:38 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.2 1997/09/24 23:11:44 fritz + * Optimized IRQ load and polling-mode. + * + * Revision 1.1 1997/09/23 18:00:05 fritz + * New driver for IBM Active 2000. + * + */ + +#define __NO_VERSION__ +#include "act2000.h" +#include "act2000_isa.h" +#include "capi.h" + +static act2000_card *irq2card_map[16] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static int isa_irqs[] = +{ + 3, 5, 7, 10, 11, 12, 15 +}; +#define ISA_NRIRQS (sizeof(isa_irqs)/sizeof(int)) + +static void +isa_delay(long t) +{ + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + t; + schedule(); + sti(); +} + +/* + * Reset Controller, then try to read the Card's signature. + + Return: + * 1 = Signature found. + * 0 = Signature not found. + */ +static int +isa_reset(unsigned short portbase) +{ + unsigned char reg; + int i; + int found; + int serial = 0; + + found = 0; + if ((reg = inb(portbase + ISA_COR)) != 0xff) { + outb(reg | ISA_COR_RESET, portbase + ISA_COR); + udelay(10000); + outb(reg, portbase + ISA_COR); + udelay(10000); + + for (i = 0; i < 16; i++) { + if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL) + serial |= 0x10000; + serial >>= 1; + } + if (serial == ISA_SER_ID) + found++; + } + return found; +} + +int +isa_detect(unsigned short portbase) +{ + int ret = 0; + unsigned long flags; + + save_flags(flags); + cli(); + if (!check_region(portbase, ISA_REGION)) + ret = isa_reset(portbase); + restore_flags(flags); + return ret; +} + +static void +isa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + act2000_card *card = irq2card_map[irq]; + u_char istatus; + + if (!card) { + printk(KERN_WARNING + "act2000: Spurious interrupt!\n"); + return; + } + istatus = (inb(ISA_PORT_ISR) & 0x07); + if (istatus & ISA_ISR_OUT) { + /* RX fifo has data */ + istatus &= ISA_ISR_OUT_MASK; + outb(0, ISA_PORT_SIS); + isa_receive(card); + outb(ISA_SIS_INT, ISA_PORT_SIS); + } + if (istatus & ISA_ISR_ERR) { + /* Error Interrupt */ + istatus &= ISA_ISR_ERR_MASK; + printk(KERN_WARNING "act2000: errIRQ\n"); + } + if (istatus) + printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", irq, istatus); +} + +static void +isa_select_irq(act2000_card * card) +{ + unsigned char reg; + + reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR; + switch (card->irq) { + case 3: + reg = ISA_COR_IRQ03; + break; + case 5: + reg = ISA_COR_IRQ05; + break; + case 7: + reg = ISA_COR_IRQ07; + break; + case 10: + reg = ISA_COR_IRQ10; + break; + case 11: + reg = ISA_COR_IRQ11; + break; + case 12: + reg = ISA_COR_IRQ12; + break; + case 15: + reg = ISA_COR_IRQ15; + break; + } + outb(reg, ISA_PORT_COR); +} + +static void +isa_enable_irq(act2000_card * card) +{ + isa_select_irq(card); + /* Enable READ irq */ + outb(ISA_SIS_INT, ISA_PORT_SIS); +} + +/* + * Install interrupt handler, enable irq on card. + * If irq is -1, choose next free irq, else irq is given explicitely. + */ +int +isa_config_irq(act2000_card * card, short irq) +{ + int i; + unsigned long flags; + + if (card->flags & ACT2000_FLAGS_IVALID) { + free_irq(card->irq, NULL); + irq2card_map[card->irq] = NULL; + } + card->flags &= ~ACT2000_FLAGS_IVALID; + outb(ISA_COR_IRQOFF, ISA_PORT_COR); + if (!irq) + return 0; + save_flags(flags); + cli(); + if (irq == -1) { + /* Auto select */ + for (i = 0; i < ISA_NRIRQS; i++) { + if (!request_irq(isa_irqs[i], &isa_interrupt, 0, card->regname, NULL)) { + card->irq = isa_irqs[i]; + irq2card_map[card->irq] = card; + card->flags |= ACT2000_FLAGS_IVALID; + break; + } + } + } else { + /* Fixed irq */ + if (!request_irq(irq, &isa_interrupt, 0, card->regname, NULL)) { + card->irq = irq; + irq2card_map[card->irq] = card; + card->flags |= ACT2000_FLAGS_IVALID; + } + } + restore_flags(flags); + if (!card->flags & ACT2000_FLAGS_IVALID) { + printk(KERN_WARNING + "act2000: Could not request irq\n"); + return -EBUSY; + } else { + isa_select_irq(card); + /* Disable READ and WRITE irq */ + outb(0, ISA_PORT_SIS); + outb(0, ISA_PORT_SOS); + } + return 0; +} + +int +isa_config_port(act2000_card * card, unsigned short portbase) +{ + if (card->flags & ACT2000_FLAGS_PVALID) { + release_region(card->port, ISA_REGION); + card->flags &= ~ACT2000_FLAGS_PVALID; + } + if (!check_region(portbase, ISA_REGION)) { + request_region(portbase, ACT2000_PORTLEN, card->regname); + card->port = portbase; + card->flags |= ACT2000_FLAGS_PVALID; + return 0; + } + return -EBUSY; +} + +/* + * Release ressources, used by an adaptor. + */ +void +isa_release(act2000_card * card) +{ + unsigned long flags; + + save_flags(flags); + cli(); + if (card->flags & ACT2000_FLAGS_IVALID) { + free_irq(card->irq, NULL); + irq2card_map[card->irq] = NULL; + } + card->flags &= ~ACT2000_FLAGS_IVALID; + if (card->flags & ACT2000_FLAGS_PVALID) + release_region(card->port, ISA_REGION); + card->flags &= ~ACT2000_FLAGS_PVALID; + restore_flags(flags); +} + +static int +isa_writeb(act2000_card * card, u_char data) +{ + u_char timeout = 40; + + while (timeout) { + if (inb(ISA_PORT_SOS) & ISA_SOS_READY) { + outb(data, ISA_PORT_SDO); + return 0; + } else { + timeout--; + udelay(10); + } + } + return 1; +} + +static int +isa_readb(act2000_card * card, u_char * data) +{ + u_char timeout = 40; + + while (timeout) { + if (inb(ISA_PORT_SIS) & ISA_SIS_READY) { + *data = inb(ISA_PORT_SDI); + return 0; + } else { + timeout--; + udelay(10); + } + } + return 1; +} + +void +isa_receive(act2000_card *card) +{ + u_char c; + + if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0) + return; + while (!isa_readb(card, &c)) { + if (card->idat.isa.rcvidx < 8) { + card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c; + if (card->idat.isa.rcvidx == 8) { + int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr); + + if (valid) { + card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len; + card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen); + if (card->idat.isa.rcvskb == NULL) { + card->idat.isa.rcvignore = 1; + printk(KERN_WARNING + "isa_receive: no memory\n"); + test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); + return; + } + memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8); + card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8); + } else { + card->idat.isa.rcvidx = 0; + printk(KERN_WARNING + "isa_receive: Invalid CAPI msg\n"); + { + int i; __u8 *p; __u8 *c; __u8 tmp[30]; + for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++) + c += sprintf(c, "%02x ", *(p++)); + printk(KERN_WARNING "isa_receive: %s\n", tmp); + } + } + } + } else { + if (!card->idat.isa.rcvignore) + *card->idat.isa.rcvptr++ = c; + if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) { + if (!card->idat.isa.rcvignore) { + skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb); + act2000_schedule_rx(card); + } + card->idat.isa.rcvidx = 0; + card->idat.isa.rcvlen = 8; + card->idat.isa.rcvignore = 0; + card->idat.isa.rcvskb = NULL; + card->idat.isa.rcvptr = card->idat.isa.rcvhdr; + } + } + } + if (!(card->flags & ACT2000_FLAGS_IVALID)) { + /* In polling mode, schedule myself */ + if ((card->idat.isa.rcvidx) && + (card->idat.isa.rcvignore || + (card->idat.isa.rcvidx < card->idat.isa.rcvlen))) + act2000_schedule_poll(card); + } + test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); +} + +void +isa_send(act2000_card * card) +{ + unsigned long flags; + struct sk_buff *skb; + actcapi_msg *msg; + int l; + + if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0) + return; + while (1) { + save_flags(flags); + cli(); + if (!(card->sbuf)) { + if ((card->sbuf = skb_dequeue(&card->sndq))) { + card->ack_msg = card->sbuf->data; + msg = (actcapi_msg *)card->sbuf->data; + if ((msg->hdr.cmd.cmd == 0x86) && + (msg->hdr.cmd.subcmd == 0) ) { + /* Save flags in message */ + card->need_b3ack = msg->msg.data_b3_req.flags; + msg->msg.data_b3_req.flags = 0; + } + } + } + restore_flags(flags); + if (!(card->sbuf)) { + /* No more data to send */ + test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); + return; + } + skb = card->sbuf; + l = 0; + while (skb->len) { + if (isa_writeb(card, *(skb->data))) { + /* Fifo is full, but more data to send */ +#if 0 + printk(KERN_DEBUG "isa_send: %d bytes\n", l); +#endif + test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); + /* Schedule myself */ + act2000_schedule_tx(card); + return; + } + skb_pull(skb, 1); + l++; + } + msg = (actcapi_msg *)card->ack_msg; + if ((msg->hdr.cmd.cmd == 0x86) && + (msg->hdr.cmd.subcmd == 0) ) { + /* + * If it's user data, reset data-ptr + * and put skb into ackq. + */ + skb->data = card->ack_msg; + /* Restore flags in message */ + msg->msg.data_b3_req.flags = card->need_b3ack; + skb_queue_tail(&card->ackq, skb); + } else + dev_kfree_skb(skb); + card->sbuf = NULL; +#if 0 + printk(KERN_DEBUG "isa_send: %d bytes\n", l); +#endif + } +} + +/* + * Get firmware ID, check for 'ISDN' signature. + */ +static int +isa_getid(act2000_card * card) +{ + + act2000_fwid fid; + u_char *p = (u_char *) & fid; + int count = 0; + + while (1) { + if (count > 510) + return -EPROTO; + if (isa_readb(card, p++)) + break; + count++; + } + if (count <= 20) { + printk(KERN_WARNING "act2000: No Firmware-ID!\n"); + return -ETIME; + } + *p = '\0'; + fid.revlen[0] = '\0'; + if (strcmp(fid.isdn, "ISDN")) { + printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n"); + return -EPROTO; + } + if ((p = strchr(fid.revision, '\n'))) + *p = '\0'; + printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision); + if (card->flags & ACT2000_FLAGS_IVALID) { + printk(KERN_DEBUG "Enabling Interrupts ...\n"); + isa_enable_irq(card); + } + return 0; +} + +/* + * Download microcode into card, check Firmware signature. + */ +int +isa_download(act2000_card * card, act2000_ddef * cb) +{ + int length; + int ret; + int l; + int c; + long timeout; + u_char *b; + u_char *p; + u_char *buf; + act2000_ddef cblock; + + if (!isa_reset(card->port)) + return -ENXIO; + isa_delay(HZ / 2); + if ((ret = verify_area(VERIFY_READ, (void *) cb, sizeof(cblock)))) + return ret; + copy_from_user(&cblock, (char *) cb, sizeof(cblock)); + length = cblock.length; + p = cblock.buffer; + if ((ret = verify_area(VERIFY_READ, (void *) p, length))) + return ret; + buf = (u_char *) kmalloc(1024, GFP_KERNEL); + if (!buf) + return -ENOMEM; + timeout = 0; + while (length) { + l = (length > 1024) ? 1024 : length; + c = 0; + b = buf; + copy_from_user(buf, p, l); + while (c < l) { + if (isa_writeb(card, *b++)) { + printk(KERN_WARNING + "act2000: loader timed out" + " len=%d c=%d\n", length, c); + kfree(buf); + return -ETIME; + } + c++; + } + length -= l; + p += l; + } + kfree(buf); + isa_delay(HZ / 2); + return (isa_getid(card)); +} diff --git a/drivers/isdn/act2000/act2000_isa.h b/drivers/isdn/act2000/act2000_isa.h new file mode 100644 index 000000000..b7c01ee2b --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.h @@ -0,0 +1,149 @@ +/* $Id: act2000_isa.h,v 1.1 1997/09/23 18:00:07 fritz Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: act2000_isa.h,v $ + * Revision 1.1 1997/09/23 18:00:07 fritz + * New driver for IBM Active 2000. + * + */ + +#ifndef act2000_isa_h +#define act2000_isa_h + +#define ISA_POLL_LOOP 40 /* Try to read-write before give up */ + +typedef enum { + INT_NO_CHANGE = 0, /* Do not change the Mask */ + INT_ON = 1, /* Set to Enable */ + INT_OFF = 2, /* Set to Disable */ +} ISA_INT_T; + +/**************************************************************************/ +/* Configuration Register COR (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* Soft Res| IRQM | IRQ Select | N/A | WAIT |Proc err */ +/**************************************************************************/ +#define ISA_COR 0 /* Offset for ISA config register */ +#define ISA_COR_PERR 0x01 /* Processor Error Enabled */ +#define ISA_COR_WS 0x02 /* Insert Wait State if 1 */ +#define ISA_COR_IRQOFF 0x38 /* No Interrupt */ +#define ISA_COR_IRQ07 0x30 /* IRQ 7 Enable */ +#define ISA_COR_IRQ05 0x28 /* IRQ 5 Enable */ +#define ISA_COR_IRQ03 0x20 /* IRQ 3 Enable */ +#define ISA_COR_IRQ10 0x18 /* IRQ 10 Enable */ +#define ISA_COR_IRQ11 0x10 /* IRQ 11 Enable */ +#define ISA_COR_IRQ12 0x08 /* IRQ 12 Enable */ +#define ISA_COR_IRQ15 0x00 /* IRQ 15 Enable */ +#define ISA_COR_IRQPULSE 0x40 /* 0 = Level 1 = Pulse Interrupt */ +#define ISA_COR_RESET 0x80 /* Soft Reset for Transputer */ + +/**************************************************************************/ +/* Interrupt Source Register ISR (RO) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A |Err sig |Ser ID |IN Intr |Out Intr| Error */ +/**************************************************************************/ +#define ISA_ISR 1 /* Offset for Interrupt Register */ +#define ISA_ISR_ERR 0x01 /* Error Interrupt */ +#define ISA_ISR_OUT 0x02 /* Output Interrupt */ +#define ISA_ISR_INP 0x04 /* Input Interrupt */ +#define ISA_ISR_SERIAL 0x08 /* Read out Serial ID after Reset */ +#define ISA_ISR_ERRSIG 0x10 /* Error Signal Input */ +#define ISA_ISR_ERR_MASK 0xfe /* Mask Error Interrupt */ +#define ISA_ISR_OUT_MASK 0xfd /* Mask Output Interrupt */ +#define ISA_ISR_INP_MASK 0xfb /* Mask Input Interrupt */ + +/* Signature delivered after Reset at ISA_ISR_SERIAL (LSB first) */ +#define ISA_SER_ID 0x0201 /* ID for ISA Card */ + +/**************************************************************************/ +/* EEPROM Register EPR (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A |ROM Hold| ROM CS |ROM CLK | ROM IN |ROM Out */ +/**************************************************************************/ +#define ISA_EPR 2 /* Offset for this Register */ +#define ISA_EPR_OUT 0x01 /* Rome Register Out (RO) */ +#define ISA_EPR_IN 0x02 /* Rom Register In (WR) */ +#define ISA_EPR_CLK 0x04 /* Rom Clock (WR) */ +#define ISA_EPR_CS 0x08 /* Rom Cip Select (WR) */ +#define ISA_EPR_HOLD 0x10 /* Rom Hold Signal (WR) */ + +/**************************************************************************/ +/* EEPROM enable Register EER (unused) */ +/**************************************************************************/ +#define ISA_EER 3 /* Offset for this Register */ + +/**************************************************************************/ +/* SLC Data Input SDI (RO) */ +/**************************************************************************/ +#define ISA_SDI 4 /* Offset for this Register */ + +/**************************************************************************/ +/* SLC Data Output SDO (WO) */ +/**************************************************************************/ +#define ISA_SDO 5 /* Offset for this Register */ + +/**************************************************************************/ +/* IMS C011 Mode 2 Input Status Register for INMOS CPU SIS (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Data Pre */ +/**************************************************************************/ +#define ISA_SIS 6 /* Offset for this Register */ +#define ISA_SIS_READY 0x01 /* If 1 : data is available */ +#define ISA_SIS_INT 0x02 /* Enable Interrupt for READ */ + +/**************************************************************************/ +/* IMS C011 Mode 2 Output Status Register from INMOS CPU SOS (RW) */ +/**************************************************************************/ +/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ +/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Out Rdy */ +/**************************************************************************/ +#define ISA_SOS 7 /* Offset for this Register */ +#define ISA_SOS_READY 0x01 /* If 1 : we can write Data */ +#define ISA_SOS_INT 0x02 /* Enable Interrupt for WRITE */ + +#define ISA_REGION 8 /* Number of Registers */ + + +/* Macros for accessing ports */ +#define ISA_PORT_COR (card->port+ISA_COR) +#define ISA_PORT_ISR (card->port+ISA_ISR) +#define ISA_PORT_EPR (card->port+ISA_EPR) +#define ISA_PORT_EER (card->port+ISA_EER) +#define ISA_PORT_SDI (card->port+ISA_SDI) +#define ISA_PORT_SDO (card->port+ISA_SDO) +#define ISA_PORT_SIS (card->port+ISA_SIS) +#define ISA_PORT_SOS (card->port+ISA_SOS) + +/* Prototypes */ + +extern int isa_detect(unsigned short portbase); +extern int isa_config_irq(act2000_card * card, short irq); +extern int isa_config_port(act2000_card * card, unsigned short portbase); +extern int isa_download(act2000_card * card, act2000_ddef * cb); +extern void isa_release(act2000_card * card); +extern void isa_receive(act2000_card *card); +extern void isa_send(act2000_card *card); + +#endif /* act2000_isa_h */ diff --git a/drivers/isdn/act2000/capi.c b/drivers/isdn/act2000/capi.c new file mode 100644 index 000000000..d0310bcc0 --- /dev/null +++ b/drivers/isdn/act2000/capi.c @@ -0,0 +1,1218 @@ +/* $Id: capi.c,v 1.7 1998/02/23 23:35:41 fritz Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * CAPI encoder/decoder + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: capi.c,v $ + * Revision 1.7 1998/02/23 23:35:41 fritz + * Eliminated some compiler warnings. + * + * Revision 1.6 1998/02/12 23:06:50 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.5 1997/10/09 22:23:02 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.4 1997/09/25 17:25:39 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.3 1997/09/24 19:44:14 fritz + * Added MSN mapping support, some cleanup. + * + * Revision 1.2 1997/09/23 19:41:24 fritz + * Disabled Logging of DATA_B3_IND/RESP/REQ/CONF Messages. + * + * Revision 1.1 1997/09/23 18:00:08 fritz + * New driver for IBM Active 2000. + * + */ + +#define __NO_VERSION__ +#include "act2000.h" +#include "capi.h" + +static actcapi_msgdsc valid_msg[] = { + {{ 0x86, 0x02}, "DATA_B3_IND"}, /* DATA_B3_IND/CONF must be first because of speed!!! */ + {{ 0x86, 0x01}, "DATA_B3_CONF"}, + {{ 0x02, 0x01}, "CONNECT_CONF"}, + {{ 0x02, 0x02}, "CONNECT_IND"}, + {{ 0x09, 0x01}, "CONNECT_INFO_CONF"}, + {{ 0x03, 0x02}, "CONNECT_ACTIVE_IND"}, + {{ 0x04, 0x01}, "DISCONNECT_CONF"}, + {{ 0x04, 0x02}, "DISCONNECT_IND"}, + {{ 0x05, 0x01}, "LISTEN_CONF"}, + {{ 0x06, 0x01}, "GET_PARAMS_CONF"}, + {{ 0x07, 0x01}, "INFO_CONF"}, + {{ 0x07, 0x02}, "INFO_IND"}, + {{ 0x08, 0x01}, "DATA_CONF"}, + {{ 0x08, 0x02}, "DATA_IND"}, + {{ 0x40, 0x01}, "SELECT_B2_PROTOCOL_CONF"}, + {{ 0x80, 0x01}, "SELECT_B3_PROTOCOL_CONF"}, + {{ 0x81, 0x01}, "LISTEN_B3_CONF"}, + {{ 0x82, 0x01}, "CONNECT_B3_CONF"}, + {{ 0x82, 0x02}, "CONNECT_B3_IND"}, + {{ 0x83, 0x02}, "CONNECT_B3_ACTIVE_IND"}, + {{ 0x84, 0x01}, "DISCONNECT_B3_CONF"}, + {{ 0x84, 0x02}, "DISCONNECT_B3_IND"}, + {{ 0x85, 0x01}, "GET_B3_PARAMS_CONF"}, + {{ 0x01, 0x01}, "RESET_B3_CONF"}, + {{ 0x01, 0x02}, "RESET_B3_IND"}, + /* {{ 0x87, 0x02, "HANDSET_IND"}, not implemented */ + {{ 0xff, 0x01}, "MANUFACTURER_CONF"}, + {{ 0xff, 0x02}, "MANUFACTURER_IND"}, +#ifdef DEBUG_MSG + /* Requests */ + {{ 0x01, 0x00}, "RESET_B3_REQ"}, + {{ 0x02, 0x00}, "CONNECT_REQ"}, + {{ 0x04, 0x00}, "DISCONNECT_REQ"}, + {{ 0x05, 0x00}, "LISTEN_REQ"}, + {{ 0x06, 0x00}, "GET_PARAMS_REQ"}, + {{ 0x07, 0x00}, "INFO_REQ"}, + {{ 0x08, 0x00}, "DATA_REQ"}, + {{ 0x09, 0x00}, "CONNECT_INFO_REQ"}, + {{ 0x40, 0x00}, "SELECT_B2_PROTOCOL_REQ"}, + {{ 0x80, 0x00}, "SELECT_B3_PROTOCOL_REQ"}, + {{ 0x81, 0x00}, "LISTEN_B3_REQ"}, + {{ 0x82, 0x00}, "CONNECT_B3_REQ"}, + {{ 0x84, 0x00}, "DISCONNECT_B3_REQ"}, + {{ 0x85, 0x00}, "GET_B3_PARAMS_REQ"}, + {{ 0x86, 0x00}, "DATA_B3_REQ"}, + {{ 0xff, 0x00}, "MANUFACTURER_REQ"}, + /* Responses */ + {{ 0x01, 0x03}, "RESET_B3_RESP"}, + {{ 0x02, 0x03}, "CONNECT_RESP"}, + {{ 0x03, 0x03}, "CONNECT_ACTIVE_RESP"}, + {{ 0x04, 0x03}, "DISCONNECT_RESP"}, + {{ 0x07, 0x03}, "INFO_RESP"}, + {{ 0x08, 0x03}, "DATA_RESP"}, + {{ 0x82, 0x03}, "CONNECT_B3_RESP"}, + {{ 0x83, 0x03}, "CONNECT_B3_ACTIVE_RESP"}, + {{ 0x84, 0x03}, "DISCONNECT_B3_RESP"}, + {{ 0x86, 0x03}, "DATA_B3_RESP"}, + {{ 0xff, 0x03}, "MANUFACTURER_RESP"}, +#if 0 +/* CAPI 2.0 */ + {{ 0x05, 0x80}, "LISTEN_REQ (CAPI 2.0)"}, +#endif +#endif + {{ 0x00, 0x00}, NULL}, +}; +#define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc)) +#define num_valid_imsg 27 /* MANUFACTURER_IND */ + +/* + * Check for a valid incoming CAPI message. + * Return: + * 0 = Invalid message + * 1 = Valid message, no B-Channel-data + * 2 = Valid message, B-Channel-data + */ +int +actcapi_chkhdr(act2000_card * card, actcapi_msghdr *hdr) +{ + int i; + + if (hdr->applicationID != 1) + return 0; + if (hdr->len < 9) + return 0; + for (i = 0; i < num_valid_imsg; i++) + if ((hdr->cmd.cmd == valid_msg[i].cmd.cmd) && + (hdr->cmd.subcmd == valid_msg[i].cmd.subcmd)) { + return (i?1:2); + } + return 0; +} + +#define ACTCAPI_MKHDR(l, c, s) { \ + skb = alloc_skb(l + 8, GFP_ATOMIC); \ + if (skb) { \ + m = (actcapi_msg *)skb_put(skb, l + 8); \ + m->hdr.len = l + 8; \ + m->hdr.applicationID = 1; \ + m->hdr.cmd.cmd = c; \ + m->hdr.cmd.subcmd = s; \ + m->hdr.msgnum = actcapi_nextsmsg(card); \ + } \ +} + +#define ACTCAPI_CHKSKB if (!skb) { \ + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); \ + return; \ +} + +#define ACTCAPI_QUEUE_TX { \ + actcapi_debug_msg(skb, 1); \ + skb_queue_tail(&card->sndq, skb); \ + act2000_schedule_tx(card); \ +} + +int +actcapi_listen_req(act2000_card *card) +{ + __u16 eazmask = 0; + int i; + actcapi_msg *m; + struct sk_buff *skb; + + for (i = 0; i < ACT2000_BCH; i++) + eazmask |= card->bch[i].eazmask; + ACTCAPI_MKHDR(9, 0x05, 0x00); + if (!skb) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.listen_req.controller = 0; + m->msg.listen_req.infomask = 0x3f; /* All information */ + m->msg.listen_req.eazmask = eazmask; + m->msg.listen_req.simask = (eazmask)?0x86:0; /* All SI's */ + ACTCAPI_QUEUE_TX; + return 0; +} + +int +actcapi_connect_req(act2000_card *card, act2000_chan *chan, char *phone, + char eaz, int si1, int si2) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR((11 + strlen(phone)), 0x02, 0x00); + if (!skb) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + chan->fsm_state = ACT2000_STATE_NULL; + return -ENOMEM; + } + m->msg.connect_req.controller = 0; + m->msg.connect_req.bchan = 0x83; + m->msg.connect_req.infomask = 0x3f; + m->msg.connect_req.si1 = si1; + m->msg.connect_req.si2 = si2; + m->msg.connect_req.eaz = eaz?eaz:'0'; + m->msg.connect_req.addr.len = strlen(phone) + 1; + m->msg.connect_req.addr.tnp = 0x81; + memcpy(m->msg.connect_req.addr.num, phone, strlen(phone)); + chan->callref = m->hdr.msgnum; + ACTCAPI_QUEUE_TX; + return 0; +} + +static void +actcapi_connect_b3_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(17, 0x82, 0x00); + ACTCAPI_CHKSKB; + m->msg.connect_b3_req.plci = chan->plci; + memset(&m->msg.connect_b3_req.ncpi, 0, + sizeof(m->msg.connect_b3_req.ncpi)); + m->msg.connect_b3_req.ncpi.len = 13; + m->msg.connect_b3_req.ncpi.modulo = 8; + ACTCAPI_QUEUE_TX; +} + +/* + * Set net type (1TR6) or (EDSS1) + */ +int +actcapi_manufacturer_req_net(act2000_card *card) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(5, 0xff, 0x00); + if (!skb) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.manufacturer_req_net.manuf_msg = 0x11; + m->msg.manufacturer_req_net.controller = 1; + m->msg.manufacturer_req_net.nettype = (card->ptype == ISDN_PTYPE_EURO)?1:0; + ACTCAPI_QUEUE_TX; + printk(KERN_INFO "act2000 %s: D-channel protocol now %s\n", + card->interface.id, (card->ptype == ISDN_PTYPE_EURO)?"euro":"1tr6"); + card->interface.features &= + ~(ISDN_FEATURE_P_UNKNOWN | ISDN_FEATURE_P_EURO | ISDN_FEATURE_P_1TR6); + card->interface.features |= + ((card->ptype == ISDN_PTYPE_EURO)?ISDN_FEATURE_P_EURO:ISDN_FEATURE_P_1TR6); + return 0; +} + +/* + * Switch V.42 on or off + */ +int +actcapi_manufacturer_req_v42(act2000_card *card, ulong arg) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(8, 0xff, 0x00); + if (!skb) { + + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.manufacturer_req_v42.manuf_msg = 0x10; + m->msg.manufacturer_req_v42.controller = 0; + m->msg.manufacturer_req_v42.v42control = (arg?1:0); + ACTCAPI_QUEUE_TX; + return 0; +} + +/* + * Set error-handler + */ +int +actcapi_manufacturer_req_errh(act2000_card *card) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(4, 0xff, 0x00); + if (!skb) { + + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.manufacturer_req_err.manuf_msg = 0x03; + m->msg.manufacturer_req_err.controller = 0; + ACTCAPI_QUEUE_TX; + return 0; +} + +/* + * Set MSN-Mapping. + */ +int +actcapi_manufacturer_req_msn(act2000_card *card) +{ + msn_entry *p = card->msn_list; + actcapi_msg *m; + struct sk_buff *skb; + int len; + + while (p) { + int i; + + len = strlen(p->msn); + for (i = 0; i < 2; i++) { + ACTCAPI_MKHDR(6 + len, 0xff, 0x00); + if (!skb) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return -ENOMEM; + } + m->msg.manufacturer_req_msn.manuf_msg = 0x13 + i; + m->msg.manufacturer_req_msn.controller = 0; + m->msg.manufacturer_req_msn.msnmap.eaz = p->eaz; + m->msg.manufacturer_req_msn.msnmap.len = len; + memcpy(m->msg.manufacturer_req_msn.msnmap.msn, p->msn, len); + ACTCAPI_QUEUE_TX; + } + p = p->next; + } + return 0; +} + +void +actcapi_select_b2_protocol_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(10, 0x40, 0x00); + ACTCAPI_CHKSKB; + m->msg.select_b2_protocol_req.plci = chan->plci; + memset(&m->msg.select_b2_protocol_req.dlpd, 0, + sizeof(m->msg.select_b2_protocol_req.dlpd)); + m->msg.select_b2_protocol_req.dlpd.len = 6; + switch (chan->l2prot) { + case ISDN_PROTO_L2_TRANS: + m->msg.select_b2_protocol_req.protocol = 0x03; + m->msg.select_b2_protocol_req.dlpd.dlen = 4000; + break; + case ISDN_PROTO_L2_HDLC: + m->msg.select_b2_protocol_req.protocol = 0x02; + m->msg.select_b2_protocol_req.dlpd.dlen = 4000; + break; + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + m->msg.select_b2_protocol_req.protocol = 0x01; + m->msg.select_b2_protocol_req.dlpd.dlen = 4000; + m->msg.select_b2_protocol_req.dlpd.laa = 3; + m->msg.select_b2_protocol_req.dlpd.lab = 1; + m->msg.select_b2_protocol_req.dlpd.win = 7; + m->msg.select_b2_protocol_req.dlpd.modulo = 8; + break; + } + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_select_b3_protocol_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(17, 0x80, 0x00); + ACTCAPI_CHKSKB; + m->msg.select_b3_protocol_req.plci = chan->plci; + memset(&m->msg.select_b3_protocol_req.ncpd, 0, + sizeof(m->msg.select_b3_protocol_req.ncpd)); + switch (chan->l3prot) { + case ISDN_PROTO_L3_TRANS: + m->msg.select_b3_protocol_req.protocol = 0x04; + m->msg.select_b3_protocol_req.ncpd.len = 13; + m->msg.select_b3_protocol_req.ncpd.modulo = 8; + break; + } + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_listen_b3_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x81, 0x00); + ACTCAPI_CHKSKB; + m->msg.listen_b3_req.plci = chan->plci; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_disconnect_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(3, 0x04, 0x00); + ACTCAPI_CHKSKB; + m->msg.disconnect_req.plci = chan->plci; + m->msg.disconnect_req.cause = 0; + ACTCAPI_QUEUE_TX; +} + +void +actcapi_disconnect_b3_req(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(17, 0x84, 0x00); + ACTCAPI_CHKSKB; + m->msg.disconnect_b3_req.ncci = chan->ncci; + memset(&m->msg.disconnect_b3_req.ncpi, 0, + sizeof(m->msg.disconnect_b3_req.ncpi)); + m->msg.disconnect_b3_req.ncpi.len = 13; + m->msg.disconnect_b3_req.ncpi.modulo = 8; + chan->fsm_state = ACT2000_STATE_BHWAIT; + ACTCAPI_QUEUE_TX; +} + +void +actcapi_connect_resp(act2000_card *card, act2000_chan *chan, __u8 cause) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(3, 0x02, 0x03); + ACTCAPI_CHKSKB; + m->msg.connect_resp.plci = chan->plci; + m->msg.connect_resp.rejectcause = cause; + if (cause) { + chan->fsm_state = ACT2000_STATE_NULL; + chan->plci = 0x8000; + } else + chan->fsm_state = ACT2000_STATE_IWAIT; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_connect_active_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x03, 0x03); + ACTCAPI_CHKSKB; + m->msg.connect_resp.plci = chan->plci; + if (chan->fsm_state == ACT2000_STATE_IWAIT) + chan->fsm_state = ACT2000_STATE_IBWAIT; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_connect_b3_resp(act2000_card *card, act2000_chan *chan, __u8 rejectcause) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR((rejectcause?3:17), 0x82, 0x03); + ACTCAPI_CHKSKB; + m->msg.connect_b3_resp.ncci = chan->ncci; + m->msg.connect_b3_resp.rejectcause = rejectcause; + if (!rejectcause) { + memset(&m->msg.connect_b3_resp.ncpi, 0, + sizeof(m->msg.connect_b3_resp.ncpi)); + m->msg.connect_b3_resp.ncpi.len = 13; + m->msg.connect_b3_resp.ncpi.modulo = 8; + chan->fsm_state = ACT2000_STATE_BWAIT; + } + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_connect_b3_active_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x83, 0x03); + ACTCAPI_CHKSKB; + m->msg.connect_b3_active_resp.ncci = chan->ncci; + chan->fsm_state = ACT2000_STATE_ACTIVE; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_info_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x07, 0x03); + ACTCAPI_CHKSKB; + m->msg.info_resp.plci = chan->plci; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_disconnect_b3_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x84, 0x03); + ACTCAPI_CHKSKB; + m->msg.disconnect_b3_resp.ncci = chan->ncci; + chan->ncci = 0x8000; + chan->queued = 0; + ACTCAPI_QUEUE_TX; +} + +static void +actcapi_disconnect_resp(act2000_card *card, act2000_chan *chan) +{ + actcapi_msg *m; + struct sk_buff *skb; + + ACTCAPI_MKHDR(2, 0x04, 0x03); + ACTCAPI_CHKSKB; + m->msg.disconnect_resp.plci = chan->plci; + chan->plci = 0x8000; + ACTCAPI_QUEUE_TX; +} + +static int +new_plci(act2000_card *card, __u16 plci) +{ + int i; + for (i = 0; i < ACT2000_BCH; i++) + if (card->bch[i].plci == 0x8000) { + card->bch[i].plci = plci; + return i; + } + return -1; +} + +static int +find_plci(act2000_card *card, __u16 plci) +{ + int i; + for (i = 0; i < ACT2000_BCH; i++) + if (card->bch[i].plci == plci) + return i; + return -1; +} + +static int +find_ncci(act2000_card *card, __u16 ncci) +{ + int i; + for (i = 0; i < ACT2000_BCH; i++) + if (card->bch[i].ncci == ncci) + return i; + return -1; +} + +static int +find_dialing(act2000_card *card, __u16 callref) +{ + int i; + for (i = 0; i < ACT2000_BCH; i++) + if ((card->bch[i].callref == callref) && + (card->bch[i].fsm_state == ACT2000_STATE_OCALL)) + return i; + return -1; +} + +static int +actcapi_data_b3_ind(act2000_card *card, struct sk_buff *skb) { + __u16 plci; + __u16 ncci; + __u16 controller; + __u8 blocknr; + int chan; + actcapi_msg *msg = (actcapi_msg *)skb->data; + + EVAL_NCCI(msg->msg.data_b3_ind.fakencci, plci, controller, ncci); + chan = find_ncci(card, ncci); + if (chan < 0) + return 0; + if (card->bch[chan].fsm_state != ACT2000_STATE_ACTIVE) + return 0; + if (card->bch[chan].plci != plci) + return 0; + blocknr = msg->msg.data_b3_ind.blocknr; + skb_pull(skb, 19); + card->interface.rcvcallb_skb(card->myid, chan, skb); + if (!(skb = alloc_skb(11, GFP_ATOMIC))) { + printk(KERN_WARNING "actcapi: alloc_skb failed\n"); + return 1; + } + msg = (actcapi_msg *)skb_put(skb, 11); + msg->hdr.len = 11; + msg->hdr.applicationID = 1; + msg->hdr.cmd.cmd = 0x86; + msg->hdr.cmd.subcmd = 0x03; + msg->hdr.msgnum = actcapi_nextsmsg(card); + msg->msg.data_b3_resp.ncci = ncci; + msg->msg.data_b3_resp.blocknr = blocknr; + ACTCAPI_QUEUE_TX; + return 1; +} + +/* + * Walk over ackq, unlink DATA_B3_REQ from it, if + * ncci and blocknr are matching. + * Decrement queued-bytes counter. + */ +static int +handle_ack(act2000_card *card, act2000_chan *chan, __u8 blocknr) { + unsigned long flags; + struct sk_buff *skb; + struct sk_buff *tmp; + struct actcapi_msg *m; + int ret = 0; + + save_flags(flags); + cli(); + skb = skb_peek(&card->ackq); + restore_flags(flags); + if (!skb) { + printk(KERN_WARNING "act2000: handle_ack nothing found!\n"); + return 0; + } + tmp = skb; + while (1) { + m = (actcapi_msg *)tmp->data; + if ((((m->msg.data_b3_req.fakencci >> 8) & 0xff) == chan->ncci) && + (m->msg.data_b3_req.blocknr == blocknr)) { + /* found corresponding DATA_B3_REQ */ + skb_unlink(tmp); + chan->queued -= m->msg.data_b3_req.datalen; + if (m->msg.data_b3_req.flags) + ret = m->msg.data_b3_req.datalen; + dev_kfree_skb(tmp); + if (chan->queued < 0) + chan->queued = 0; + return ret; + } + save_flags(flags); + cli(); + tmp = skb_peek((struct sk_buff_head *)tmp); + restore_flags(flags); + if ((tmp == skb) || (tmp == NULL)) { + /* reached end of queue */ + printk(KERN_WARNING "act2000: handle_ack nothing found!\n"); + return 0; + } + } +} + +void +actcapi_dispatch(act2000_card *card) +{ + struct sk_buff *skb; + actcapi_msg *msg; + __u16 ccmd; + int chan; + int len; + act2000_chan *ctmp; + isdn_ctrl cmd; + char tmp[170]; + + while ((skb = skb_dequeue(&card->rcvq))) { + actcapi_debug_msg(skb, 0); + msg = (actcapi_msg *)skb->data; + ccmd = ((msg->hdr.cmd.cmd << 8) | msg->hdr.cmd.subcmd); + switch (ccmd) { + case 0x8602: + /* DATA_B3_IND */ + if (actcapi_data_b3_ind(card, skb)) + return; + break; + case 0x8601: + /* DATA_B3_CONF */ + chan = find_ncci(card, msg->msg.data_b3_conf.ncci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_ACTIVE)) { + if (msg->msg.data_b3_conf.info != 0) + printk(KERN_WARNING "act2000: DATA_B3_CONF: %04x\n", + msg->msg.data_b3_conf.info); + len = handle_ack(card, &card->bch[chan], + msg->msg.data_b3_conf.blocknr); + if (len) { + cmd.driver = card->myid; + cmd.command = ISDN_STAT_BSENT; + cmd.arg = chan; + cmd.parm.length = len; + card->interface.statcallb(&cmd); + } + } + break; + case 0x0201: + /* CONNECT_CONF */ + chan = find_dialing(card, msg->hdr.msgnum); + if (chan >= 0) { + if (msg->msg.connect_conf.info) { + card->bch[chan].fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } else { + card->bch[chan].fsm_state = ACT2000_STATE_OWAIT; + card->bch[chan].plci = msg->msg.connect_conf.plci; + } + } + break; + case 0x0202: + /* CONNECT_IND */ + chan = new_plci(card, msg->msg.connect_ind.plci); + if (chan < 0) { + ctmp = (act2000_chan *)tmp; + ctmp->plci = msg->msg.connect_ind.plci; + actcapi_connect_resp(card, ctmp, 0x11); /* All Card-Cannels busy */ + } else { + card->bch[chan].fsm_state = ACT2000_STATE_ICALL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_ICALL; + cmd.arg = chan; + cmd.parm.setup.si1 = msg->msg.connect_ind.si1; + cmd.parm.setup.si2 = msg->msg.connect_ind.si2; + if (card->ptype == ISDN_PTYPE_EURO) + strcpy(cmd.parm.setup.eazmsn, + act2000_find_eaz(card, msg->msg.connect_ind.eaz)); + else { + cmd.parm.setup.eazmsn[0] = msg->msg.connect_ind.eaz; + cmd.parm.setup.eazmsn[1] = 0; + } + memset(cmd.parm.setup.phone, 0, sizeof(cmd.parm.setup.phone)); + memcpy(cmd.parm.setup.phone, msg->msg.connect_ind.addr.num, + msg->msg.connect_ind.addr.len - 1); + cmd.parm.setup.plan = msg->msg.connect_ind.addr.tnp; + cmd.parm.setup.screen = 0; + if (card->interface.statcallb(&cmd) == 2) + actcapi_connect_resp(card, &card->bch[chan], 0x15); /* Reject Call */ + } + break; + case 0x0302: + /* CONNECT_ACTIVE_IND */ + chan = find_plci(card, msg->msg.connect_active_ind.plci); + if (chan >= 0) + switch (card->bch[chan].fsm_state) { + case ACT2000_STATE_IWAIT: + actcapi_connect_active_resp(card, &card->bch[chan]); + break; + case ACT2000_STATE_OWAIT: + actcapi_connect_active_resp(card, &card->bch[chan]); + actcapi_select_b2_protocol_req(card, &card->bch[chan]); + break; + } + break; + case 0x8202: + /* CONNECT_B3_IND */ + chan = find_plci(card, msg->msg.connect_b3_ind.plci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_IBWAIT)) { + card->bch[chan].ncci = msg->msg.connect_b3_ind.ncci; + actcapi_connect_b3_resp(card, &card->bch[chan], 0); + } else { + ctmp = (act2000_chan *)tmp; + ctmp->ncci = msg->msg.connect_b3_ind.ncci; + actcapi_connect_b3_resp(card, ctmp, 0x11); /* All Card-Cannels busy */ + } + break; + case 0x8302: + /* CONNECT_B3_ACTIVE_IND */ + chan = find_ncci(card, msg->msg.connect_b3_active_ind.ncci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BWAIT)) { + actcapi_connect_b3_active_resp(card, &card->bch[chan]); + cmd.driver = card->myid; + cmd.command = ISDN_STAT_BCONN; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + break; + case 0x8402: + /* DISCONNECT_B3_IND */ + chan = find_ncci(card, msg->msg.disconnect_b3_ind.ncci); + if (chan >= 0) { + ctmp = &card->bch[chan]; + actcapi_disconnect_b3_resp(card, ctmp); + switch (ctmp->fsm_state) { + case ACT2000_STATE_ACTIVE: + ctmp->fsm_state = ACT2000_STATE_DHWAIT2; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_BHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + break; + case ACT2000_STATE_BHWAIT2: + actcapi_disconnect_req(card, ctmp); + ctmp->fsm_state = ACT2000_STATE_DHWAIT; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_BHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + break; + } + } + break; + case 0x0402: + /* DISCONNECT_IND */ + chan = find_plci(card, msg->msg.disconnect_ind.plci); + if (chan >= 0) { + ctmp = &card->bch[chan]; + actcapi_disconnect_resp(card, ctmp); + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } else { + ctmp = (act2000_chan *)tmp; + ctmp->plci = msg->msg.disconnect_ind.plci; + actcapi_disconnect_resp(card, ctmp); + } + break; + case 0x4001: + /* SELECT_B2_PROTOCOL_CONF */ + chan = find_plci(card, msg->msg.select_b2_protocol_conf.plci); + if (chan >= 0) + switch (card->bch[chan].fsm_state) { + case ACT2000_STATE_ICALL: + case ACT2000_STATE_OWAIT: + ctmp = &card->bch[chan]; + if (msg->msg.select_b2_protocol_conf.info == 0) + actcapi_select_b3_protocol_req(card, ctmp); + else { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + break; + } + break; + case 0x8001: + /* SELECT_B3_PROTOCOL_CONF */ + chan = find_plci(card, msg->msg.select_b3_protocol_conf.plci); + if (chan >= 0) + switch (card->bch[chan].fsm_state) { + case ACT2000_STATE_ICALL: + case ACT2000_STATE_OWAIT: + ctmp = &card->bch[chan]; + if (msg->msg.select_b3_protocol_conf.info == 0) + actcapi_listen_b3_req(card, ctmp); + else { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + } + break; + case 0x8101: + /* LISTEN_B3_CONF */ + chan = find_plci(card, msg->msg.listen_b3_conf.plci); + if (chan >= 0) + switch (card->bch[chan].fsm_state) { + case ACT2000_STATE_ICALL: + ctmp = &card->bch[chan]; + if (msg->msg.listen_b3_conf.info == 0) + actcapi_connect_resp(card, ctmp, 0); + else { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + break; + case ACT2000_STATE_OWAIT: + ctmp = &card->bch[chan]; + if (msg->msg.listen_b3_conf.info == 0) { + actcapi_connect_b3_req(card, ctmp); + ctmp->fsm_state = ACT2000_STATE_OBWAIT; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DCONN; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } else { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } + break; + } + break; + case 0x8201: + /* CONNECT_B3_CONF */ + chan = find_plci(card, msg->msg.connect_b3_conf.plci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_OBWAIT)) { + ctmp = &card->bch[chan]; + if (msg->msg.connect_b3_conf.info) { + ctmp->fsm_state = ACT2000_STATE_NULL; + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg = chan; + card->interface.statcallb(&cmd); + } else { + ctmp->ncci = msg->msg.connect_b3_conf.ncci; + ctmp->fsm_state = ACT2000_STATE_BWAIT; + } + } + break; + case 0x8401: + /* DISCONNECT_B3_CONF */ + chan = find_ncci(card, msg->msg.disconnect_b3_conf.ncci); + if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BHWAIT)) + card->bch[chan].fsm_state = ACT2000_STATE_BHWAIT2; + break; + case 0x0702: + /* INFO_IND */ + chan = find_plci(card, msg->msg.info_ind.plci); + if (chan >= 0) + /* TODO: Eval Charging info / cause */ + actcapi_info_resp(card, &card->bch[chan]); + break; + case 0x0401: + /* LISTEN_CONF */ + case 0x0501: + /* LISTEN_CONF */ + case 0xff01: + /* MANUFACTURER_CONF */ + break; + case 0xff02: + /* MANUFACTURER_IND */ + if (msg->msg.manuf_msg == 3) { + memset(tmp, 0, sizeof(tmp)); + strncpy(tmp, + &msg->msg.manufacturer_ind_err.errstring, + msg->hdr.len - 16); + if (msg->msg.manufacturer_ind_err.errcode) + printk(KERN_WARNING "act2000: %s\n", tmp); + else { + printk(KERN_DEBUG "act2000: %s\n", tmp); + if ((!strncmp(tmp, "INFO: Trace buffer con", 22)) || + (!strncmp(tmp, "INFO: Compile Date/Tim", 22))) { + card->flags |= ACT2000_FLAGS_RUNNING; + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + actcapi_manufacturer_req_net(card); + actcapi_manufacturer_req_msn(card); + actcapi_listen_req(card); + card->interface.statcallb(&cmd); + } + } + } + break; + default: + printk(KERN_WARNING "act2000: UNHANDLED Message %04x\n", ccmd); + break; + } + dev_kfree_skb(skb); + } +} + +#ifdef DEBUG_MSG +static void +actcapi_debug_caddr(actcapi_addr *addr) +{ + char tmp[30]; + + printk(KERN_DEBUG " Alen = %d\n", addr->len); + if (addr->len > 0) + printk(KERN_DEBUG " Atnp = 0x%02x\n", addr->tnp); + if (addr->len > 1) { + memset(tmp, 0, 30); + memcpy(tmp, addr->num, addr->len - 1); + printk(KERN_DEBUG " Anum = '%s'\n", tmp); + } +} + +static void +actcapi_debug_ncpi(actcapi_ncpi *ncpi) +{ + printk(KERN_DEBUG " ncpi.len = %d\n", ncpi->len); + if (ncpi->len >= 2) + printk(KERN_DEBUG " ncpi.lic = 0x%04x\n", ncpi->lic); + if (ncpi->len >= 4) + printk(KERN_DEBUG " ncpi.hic = 0x%04x\n", ncpi->hic); + if (ncpi->len >= 6) + printk(KERN_DEBUG " ncpi.ltc = 0x%04x\n", ncpi->ltc); + if (ncpi->len >= 8) + printk(KERN_DEBUG " ncpi.htc = 0x%04x\n", ncpi->htc); + if (ncpi->len >= 10) + printk(KERN_DEBUG " ncpi.loc = 0x%04x\n", ncpi->loc); + if (ncpi->len >= 12) + printk(KERN_DEBUG " ncpi.hoc = 0x%04x\n", ncpi->hoc); + if (ncpi->len >= 13) + printk(KERN_DEBUG " ncpi.mod = %d\n", ncpi->modulo); +} + +static void +actcapi_debug_dlpd(actcapi_dlpd *dlpd) +{ + printk(KERN_DEBUG " dlpd.len = %d\n", dlpd->len); + if (dlpd->len >= 2) + printk(KERN_DEBUG " dlpd.dlen = 0x%04x\n", dlpd->dlen); + if (dlpd->len >= 3) + printk(KERN_DEBUG " dlpd.laa = 0x%02x\n", dlpd->laa); + if (dlpd->len >= 4) + printk(KERN_DEBUG " dlpd.lab = 0x%02x\n", dlpd->lab); + if (dlpd->len >= 5) + printk(KERN_DEBUG " dlpd.modulo = %d\n", dlpd->modulo); + if (dlpd->len >= 6) + printk(KERN_DEBUG " dlpd.win = %d\n", dlpd->win); +} + +#ifdef DEBUG_DUMP_SKB +static void dump_skb(struct sk_buff *skb) { + char tmp[80]; + char *p = skb->data; + char *t = tmp; + int i; + + for (i = 0; i < skb->len; i++) { + t += sprintf(t, "%02x ", *p++ & 0xff); + if ((i & 0x0f) == 8) { + printk(KERN_DEBUG "dump: %s\n", tmp); + t = tmp; + } + } + if (i & 0x07) + printk(KERN_DEBUG "dump: %s\n", tmp); +} +#endif + +void +actcapi_debug_msg(struct sk_buff *skb, int direction) +{ + actcapi_msg *msg = (actcapi_msg *)skb->data; + char *descr; + int i; + char tmp[170]; + +#ifndef DEBUG_DATA_MSG + if (msg->hdr.cmd.cmd == 0x86) + return; +#endif + descr = "INVALID"; +#ifdef DEBUG_DUMP_SKB + dump_skb(skb); +#endif + for (i = 0; i < num_valid_msg; i++) + if ((msg->hdr.cmd.cmd == valid_msg[i].cmd.cmd) && + (msg->hdr.cmd.subcmd == valid_msg[i].cmd.subcmd)) { + descr = valid_msg[i].description; + break; + } + printk(KERN_DEBUG "%s %s msg\n", direction?"Outgoing":"Incoming", descr); + printk(KERN_DEBUG " ApplID = %d\n", msg->hdr.applicationID); + printk(KERN_DEBUG " Len = %d\n", msg->hdr.len); + printk(KERN_DEBUG " MsgNum = 0x%04x\n", msg->hdr.msgnum); + printk(KERN_DEBUG " Cmd = 0x%02x\n", msg->hdr.cmd.cmd); + printk(KERN_DEBUG " SubCmd = 0x%02x\n", msg->hdr.cmd.subcmd); + switch (i) { + case 0: + /* DATA B3 IND */ + printk(KERN_DEBUG " BLOCK = 0x%02x\n", + msg->msg.data_b3_ind.blocknr); + break; + case 2: + /* CONNECT CONF */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_conf.plci); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.connect_conf.info); + break; + case 3: + /* CONNECT IND */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_ind.plci); + printk(KERN_DEBUG " Contr = %d\n", + msg->msg.connect_ind.controller); + printk(KERN_DEBUG " SI1 = %d\n", + msg->msg.connect_ind.si1); + printk(KERN_DEBUG " SI2 = %d\n", + msg->msg.connect_ind.si2); + printk(KERN_DEBUG " EAZ = '%c'\n", + msg->msg.connect_ind.eaz); + actcapi_debug_caddr(&msg->msg.connect_ind.addr); + break; + case 5: + /* CONNECT ACTIVE IND */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_active_ind.plci); + actcapi_debug_caddr(&msg->msg.connect_active_ind.addr); + break; + case 8: + /* LISTEN CONF */ + printk(KERN_DEBUG " Contr = %d\n", + msg->msg.listen_conf.controller); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.listen_conf.info); + break; + case 11: + /* INFO IND */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.info_ind.plci); + printk(KERN_DEBUG " Imsk = 0x%04x\n", + msg->msg.info_ind.nr.mask); + if (msg->hdr.len > 12) { + int l = msg->hdr.len - 12; + int j; + char *p = tmp; + for (j = 0; j < l ; j++) + p += sprintf(p, "%02x ", msg->msg.info_ind.el.display[j]); + printk(KERN_DEBUG " D = '%s'\n", tmp); + } + break; + case 14: + /* SELECT B2 PROTOCOL CONF */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.select_b2_protocol_conf.plci); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.select_b2_protocol_conf.info); + break; + case 15: + /* SELECT B3 PROTOCOL CONF */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.select_b3_protocol_conf.plci); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.select_b3_protocol_conf.info); + break; + case 16: + /* LISTEN B3 CONF */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.listen_b3_conf.plci); + printk(KERN_DEBUG " Info = 0x%04x\n", + msg->msg.listen_b3_conf.info); + break; + case 18: + /* CONNECT B3 IND */ + printk(KERN_DEBUG " NCCI = 0x%04x\n", + msg->msg.connect_b3_ind.ncci); + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_b3_ind.plci); + actcapi_debug_ncpi(&msg->msg.connect_b3_ind.ncpi); + break; + case 19: + /* CONNECT B3 ACTIVE IND */ + printk(KERN_DEBUG " NCCI = 0x%04x\n", + msg->msg.connect_b3_active_ind.ncci); + actcapi_debug_ncpi(&msg->msg.connect_b3_active_ind.ncpi); + break; + case 26: + /* MANUFACTURER IND */ + printk(KERN_DEBUG " Mmsg = 0x%02x\n", + msg->msg.manufacturer_ind_err.manuf_msg); + switch (msg->msg.manufacturer_ind_err.manuf_msg) { + case 3: + printk(KERN_DEBUG " Contr = %d\n", + msg->msg.manufacturer_ind_err.controller); + printk(KERN_DEBUG " Code = 0x%08x\n", + msg->msg.manufacturer_ind_err.errcode); + memset(tmp, 0, sizeof(tmp)); + strncpy(tmp, &msg->msg.manufacturer_ind_err.errstring, + msg->hdr.len - 16); + printk(KERN_DEBUG " Emsg = '%s'\n", tmp); + break; + } + break; + case 30: + /* LISTEN REQ */ + printk(KERN_DEBUG " Imsk = 0x%08x\n", + msg->msg.listen_req.infomask); + printk(KERN_DEBUG " Emsk = 0x%04x\n", + msg->msg.listen_req.eazmask); + printk(KERN_DEBUG " Smsk = 0x%04x\n", + msg->msg.listen_req.simask); + break; + case 35: + /* SELECT_B2_PROTOCOL_REQ */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.select_b2_protocol_req.plci); + printk(KERN_DEBUG " prot = 0x%02x\n", + msg->msg.select_b2_protocol_req.protocol); + if (msg->hdr.len >= 11) + printk(KERN_DEBUG "No dlpd\n"); + else + actcapi_debug_dlpd(&msg->msg.select_b2_protocol_req.dlpd); + break; + case 44: + /* CONNECT RESP */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_resp.plci); + printk(KERN_DEBUG " CAUSE = 0x%02x\n", + msg->msg.connect_resp.rejectcause); + break; + case 45: + /* CONNECT ACTIVE RESP */ + printk(KERN_DEBUG " PLCI = 0x%04x\n", + msg->msg.connect_active_resp.plci); + break; + } +} +#endif diff --git a/drivers/isdn/act2000/capi.h b/drivers/isdn/act2000/capi.h new file mode 100644 index 000000000..901f15ed4 --- /dev/null +++ b/drivers/isdn/act2000/capi.h @@ -0,0 +1,406 @@ +/* $Id: capi.h,v 1.4 1997/10/01 09:21:04 fritz Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: capi.h,v $ + * Revision 1.4 1997/10/01 09:21:04 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.3 1997/09/25 17:25:41 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.2 1997/09/24 19:44:15 fritz + * Added MSN mapping support, some cleanup. + * + * Revision 1.1 1997/09/23 18:00:10 fritz + * New driver for IBM Active 2000. + * + */ + +#ifndef CAPI_H +#define CAPI_H + +/* Command-part of a CAPI message */ +typedef struct actcapi_msgcmd { + __u8 cmd; + __u8 subcmd; +} actcapi_msgcmd; + +/* CAPI message header */ +typedef struct actcapi_msghdr { + __u16 len; + __u16 applicationID; + actcapi_msgcmd cmd; + __u16 msgnum; +} actcapi_msghdr; + +/* CAPI message description (for debugging) */ +typedef struct actcapi_msgdsc { + actcapi_msgcmd cmd; + char *description; +} actcapi_msgdsc; + +/* CAPI Adress */ +typedef struct actcapi_addr { + __u8 len; /* Length of element */ + __u8 tnp; /* Type/Numbering Plan */ + __u8 num[20]; /* Caller ID */ +} actcapi_addr; + +/* CAPI INFO element mask */ +typedef union actcapi_infonr { /* info number */ + __u16 mask; /* info-mask field */ + struct bmask { /* bit definitions */ + unsigned codes : 3; /* code set */ + unsigned rsvd : 5; /* reserved */ + unsigned svind : 1; /* single, variable length ind. */ + unsigned wtype : 7; /* W-element type */ + } bmask; +} actcapi_infonr; + +/* CAPI INFO element */ +typedef union actcapi_infoel { /* info element */ + __u8 len; /* length of info element */ + __u8 display[40]; /* display contents */ + __u8 uuinfo[40]; /* User-user info field */ + struct cause { /* Cause information */ + unsigned ext2 : 1; /* extension */ + unsigned cod : 2; /* coding standard */ + unsigned spare : 1; /* spare */ + unsigned loc : 4; /* location */ + unsigned ext1 : 1; /* extension */ + unsigned cval : 7; /* Cause value */ + } cause; + struct charge { /* Charging information */ + __u8 toc; /* type of charging info */ + __u8 unit[10]; /* charging units */ + } charge; + __u8 date[20]; /* date fields */ + __u8 stat; /* state of remote party */ +} actcapi_infoel; + +/* Message for EAZ<->MSN Mapping */ +typedef struct actcapi_msn { + __u8 eaz; + __u8 len; /* Length of MSN */ + __u8 msn[15] __attribute__ ((packed)); +} actcapi_msn; + +typedef struct actcapi_dlpd { + __u8 len; /* Length of structure */ + __u16 dlen __attribute__ ((packed)); /* Data Length */ + __u8 laa __attribute__ ((packed)); /* Link Address A */ + __u8 lab; /* Link Address B */ + __u8 modulo; /* Modulo Mode */ + __u8 win; /* Window size */ + __u8 xid[100]; /* XID Information */ +} actcapi_dlpd; + +typedef struct actcapi_ncpd { + __u8 len; /* Length of structure */ + __u16 lic __attribute__ ((packed)); + __u16 hic __attribute__ ((packed)); + __u16 ltc __attribute__ ((packed)); + __u16 htc __attribute__ ((packed)); + __u16 loc __attribute__ ((packed)); + __u16 hoc __attribute__ ((packed)); + __u8 modulo __attribute__ ((packed)); +} actcapi_ncpd; +#define actcapi_ncpi actcapi_ncpd + +/* + * Layout of NCCI field in a B3 DATA CAPI message is different from + * standard at act2000: + * + * Bit 0-4 = PLCI + * Bit 5-7 = Controller + * Bit 8-15 = NCCI + */ +#define MAKE_NCCI(plci,contr,ncci) \ + ((plci & 0x1f) | ((contr & 0x7) << 5) | ((ncci & 0xff) << 8)) + +#define EVAL_NCCI(fakencci,plci,contr,ncci) { \ + plci = fakencci & 0x1f; \ + contr = (fakencci >> 5) & 0x7; \ + ncci = (fakencci >> 8) & 0xff; \ +} + +/* + * Layout of PLCI field in a B3 DATA CAPI message is different from + * standard at act2000: + * + * Bit 0-4 = PLCI + * Bit 5-7 = Controller + * Bit 8-15 = reserved (must be 0) + */ +#define MAKE_PLCI(plci,contr) \ + ((plci & 0x1f) | ((contr & 0x7) << 5)) + +#define EVAL_PLCI(fakeplci,plci,contr) { \ + plci = fakeplci & 0x1f; \ + contr = (fakeplci >> 5) & 0x7; \ +} + +typedef struct actcapi_msg { + actcapi_msghdr hdr; + union msg { + __u16 manuf_msg; + struct manufacturer_req_net { + __u16 manuf_msg; + __u16 controller; + __u8 nettype; + } manufacturer_req_net; + struct manufacturer_req_v42 { + __u16 manuf_msg; + __u16 controller; + __u32 v42control; + } manufacturer_req_v42; + struct manufacturer_conf_v42 { + __u16 manuf_msg; + __u16 controller; + } manufacturer_conf_v42; + struct manufacturer_req_err { + __u16 manuf_msg; + __u16 controller; + } manufacturer_req_err; + struct manufacturer_ind_err { + __u16 manuf_msg; + __u16 controller; + __u32 errcode; + __u8 errstring; /* actually up to 160 */ + } manufacturer_ind_err; + struct manufacturer_req_msn { + __u16 manuf_msg; + __u16 controller; + actcapi_msn msnmap; + } manufacturer_req_msn; + /* TODO: TraceInit-req/conf/ind/resp and + * TraceDump-req/conf/ind/resp + */ + struct connect_req { + __u8 controller; + __u8 bchan; + __u32 infomask __attribute__ ((packed)); + __u8 si1; + __u8 si2; + __u8 eaz; + actcapi_addr addr; + } connect_req; + struct connect_conf { + __u16 plci; + __u16 info; + } connect_conf; + struct connect_ind { + __u16 plci; + __u8 controller; + __u8 si1; + __u8 si2; + __u8 eaz; + actcapi_addr addr; + } connect_ind; + struct connect_resp { + __u16 plci; + __u8 rejectcause; + } connect_resp; + struct connect_active_ind { + __u16 plci; + actcapi_addr addr; + } connect_active_ind; + struct connect_active_resp { + __u16 plci; + } connect_active_resp; + struct connect_b3_req { + __u16 plci; + actcapi_ncpi ncpi; + } connect_b3_req; + struct connect_b3_conf { + __u16 plci; + __u16 ncci; + __u16 info; + } connect_b3_conf; + struct connect_b3_ind { + __u16 ncci; + __u16 plci; + actcapi_ncpi ncpi; + } connect_b3_ind; + struct connect_b3_resp { + __u16 ncci; + __u8 rejectcause; + actcapi_ncpi ncpi __attribute__ ((packed)); + } connect_b3_resp; + struct disconnect_req { + __u16 plci; + __u8 cause; + } disconnect_req; + struct disconnect_conf { + __u16 plci; + __u16 info; + } disconnect_conf; + struct disconnect_ind { + __u16 plci; + __u16 info; + } disconnect_ind; + struct disconnect_resp { + __u16 plci; + } disconnect_resp; + struct connect_b3_active_ind { + __u16 ncci; + actcapi_ncpi ncpi; + } connect_b3_active_ind; + struct connect_b3_active_resp { + __u16 ncci; + } connect_b3_active_resp; + struct disconnect_b3_req { + __u16 ncci; + actcapi_ncpi ncpi; + } disconnect_b3_req; + struct disconnect_b3_conf { + __u16 ncci; + __u16 info; + } disconnect_b3_conf; + struct disconnect_b3_ind { + __u16 ncci; + __u16 info; + actcapi_ncpi ncpi; + } disconnect_b3_ind; + struct disconnect_b3_resp { + __u16 ncci; + } disconnect_b3_resp; + struct info_ind { + __u16 plci; + actcapi_infonr nr; + actcapi_infoel el; + } info_ind; + struct info_resp { + __u16 plci; + } info_resp; + struct listen_b3_req { + __u16 plci; + } listen_b3_req; + struct listen_b3_conf { + __u16 plci; + __u16 info; + } listen_b3_conf; + struct select_b2_protocol_req { + __u16 plci; + __u8 protocol; + actcapi_dlpd dlpd __attribute__ ((packed)); + } select_b2_protocol_req; + struct select_b2_protocol_conf { + __u16 plci; + __u16 info; + } select_b2_protocol_conf; + struct select_b3_protocol_req { + __u16 plci; + __u8 protocol; + actcapi_ncpd ncpd __attribute__ ((packed)); + } select_b3_protocol_req; + struct select_b3_protocol_conf { + __u16 plci; + __u16 info; + } select_b3_protocol_conf; +#if 0 + struct listen_req { + __u32 controller; + __u32 infomask; + __u32 cipmask; + __u32 cipmask2; + __u16 dummy; /* 2 Length-bytes of 2 Structs MUST always be 0!!! */ + } listen_req; + struct listen_conf { + __u32 controller; + __u16 info; + } listen_conf; +#else + struct listen_req { + __u8 controller; + __u32 infomask __attribute__ ((packed)); + __u16 eazmask __attribute__ ((packed)); + __u16 simask __attribute__ ((packed)); + } listen_req; + struct listen_conf { + __u8 controller; + __u16 info __attribute__ ((packed)); + } listen_conf; +#endif + struct data_b3_req { + __u16 fakencci; + __u16 datalen; + __u32 unused; + __u8 blocknr; + __u16 flags __attribute__ ((packed)); + } data_b3_req; + struct data_b3_ind { + __u16 fakencci; + __u16 datalen; + __u32 unused; + __u8 blocknr; + __u16 flags __attribute__ ((packed)); + } data_b3_ind; + struct data_b3_resp { + __u16 ncci; + __u8 blocknr; + } data_b3_resp; + struct data_b3_conf { + __u16 ncci; + __u8 blocknr; + __u16 info __attribute__ ((packed)); + } data_b3_conf; + } msg; +} actcapi_msg; + +extern __inline__ unsigned short +actcapi_nextsmsg(act2000_card *card) +{ + unsigned long flags; + unsigned short n; + + save_flags(flags); + cli(); + n = card->msgnum; + card->msgnum++; + card->msgnum &= 0x7fff; + restore_flags(flags); + return n; +} +#define DEBUG_MSG +#undef DEBUG_DATA_MSG +#undef DEBUG_DUMP_SKB + +extern int actcapi_chkhdr(act2000_card *, actcapi_msghdr *); +extern int actcapi_listen_req(act2000_card *); +extern int actcapi_manufacturer_req_net(act2000_card *); +extern int actcapi_manufacturer_req_v42(act2000_card *, ulong); +extern int actcapi_manufacturer_req_errh(act2000_card *); +extern int actcapi_manufacturer_req_msn(act2000_card *); +extern int actcapi_connect_req(act2000_card *, act2000_chan *, char *, char, int, int); +extern void actcapi_select_b2_protocol_req(act2000_card *, act2000_chan *); +extern void actcapi_disconnect_b3_req(act2000_card *, act2000_chan *); +extern void actcapi_connect_resp(act2000_card *, act2000_chan *, __u8); +extern void actcapi_dispatch(act2000_card *); +#ifdef DEBUG_MSG +extern void actcapi_debug_msg(struct sk_buff *skb, int); +#else +#define actcapi_debug_msg(skb, len) +#endif +#endif diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c new file mode 100644 index 000000000..76be187f2 --- /dev/null +++ b/drivers/isdn/act2000/module.c @@ -0,0 +1,953 @@ +/* $Id: module.c,v 1.7 1998/02/12 23:06:52 keil Exp $ + * + * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de) + * Thanks to Friedemann Baitinger and IBM Germany + * + * 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: module.c,v $ + * Revision 1.7 1998/02/12 23:06:52 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.6 1998/01/31 22:10:42 keil + * changes for 2.1.82 + * + * Revision 1.5 1997/10/09 22:23:04 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.4 1997/09/25 17:25:43 fritz + * Support for adding cards at runtime. + * Support for new Firmware. + * + * Revision 1.3 1997/09/24 23:11:45 fritz + * Optimized IRQ load and polling-mode. + * + * Revision 1.2 1997/09/24 19:44:17 fritz + * Added MSN mapping support, some cleanup. + * + * Revision 1.1 1997/09/23 18:00:13 fritz + * New driver for IBM Active 2000. + * + */ + +#include "act2000.h" +#include "act2000_isa.h" +#include "capi.h" + +static unsigned short isa_ports[] = +{ + 0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380, + 0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60, +}; +#define ISA_NRPORTS (sizeof(isa_ports)/sizeof(unsigned short)) + +act2000_card *cards = (act2000_card *) NULL; + +/* Parameters to be set by insmod */ +static int act_bus = 0; +static int act_port = -1; /* -1 = Autoprobe */ +static int act_irq = -1; /* -1 = Autoselect */ +static char *act_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +MODULE_DESCRIPTION( "Driver for IBM Active 2000 ISDN card"); +MODULE_AUTHOR( "Fritz Elfert"); +MODULE_SUPPORTED_DEVICE( "ISDN subsystem"); +MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA"); +MODULE_PARM_DESC(membase, "Base port address of first card"); +MODULE_PARM_DESC(act_irq, "IRQ of first card (-1 = grab next free IRQ)"); +MODULE_PARM_DESC(act_id, "ID-String of first card"); +MODULE_PARM(act_bus, "i"); +MODULE_PARM(act_port, "i"); +MODULE_PARM(act_irq, "i"); +MODULE_PARM(act_id, "s"); + +static int act2000_addcard(int, int, int, char *); + +static act2000_chan * +find_channel(act2000_card *card, int channel) +{ + if ((channel >= 0) && (channel < ACT2000_BCH)) + return &(card->bch[channel]); + printk(KERN_WARNING "act2000: Invalid channel %d\n", channel); + return NULL; +} + +/* + * Free MSN list + */ +static void +act2000_clear_msn(act2000_card *card) +{ + struct msn_entry *p = card->msn_list; + struct msn_entry *q; + unsigned long flags; + + save_flags(flags); + cli(); + card->msn_list = NULL; + restore_flags(flags); + while (p) { + q = p->next; + kfree(p); + p = q; + } +} + +/* + * Find an MSN entry in the list. + * If ia5 != 0, return IA5-encoded EAZ, else + * return a bitmask with corresponding bit set. + */ +static __u16 +act2000_find_msn(act2000_card *card, char *msn, int ia5) +{ + struct msn_entry *p = card->msn_list; + __u8 eaz = '0'; + + while (p) { + if (!strcmp(p->msn, msn)) { + eaz = p->eaz; + break; + } + p = p->next; + } + if (!ia5) + return (1 << (eaz - '0')); + else + return eaz; +} + +/* + * Find an EAZ entry in the list. + * return a string with corresponding msn. + */ +char * +act2000_find_eaz(act2000_card *card, char eaz) +{ + struct msn_entry *p = card->msn_list; + + while (p) { + if (p->eaz == eaz) + return(p->msn); + p = p->next; + } + return("\0"); +} + +/* + * Add or delete an MSN to the MSN list + * + * First character of msneaz is EAZ, rest is MSN. + * If length of eazmsn is 1, delete that entry. + */ +static int +act2000_set_msn(act2000_card *card, char *eazmsn) +{ + struct msn_entry *p = card->msn_list; + struct msn_entry *q = NULL; + unsigned long flags; + int i; + + if (!strlen(eazmsn)) + return 0; + if (strlen(eazmsn) > 16) + return -EINVAL; + for (i = 0; i < strlen(eazmsn); i++) + if (!isdigit(eazmsn[i])) + return -EINVAL; + if (strlen(eazmsn) == 1) { + /* Delete a single MSN */ + while (p) { + if (p->eaz == eazmsn[0]) { + save_flags(flags); + cli(); + if (q) + q->next = p->next; + else + card->msn_list = p->next; + restore_flags(flags); + kfree(p); + printk(KERN_DEBUG + "Mapping for EAZ %c deleted\n", + eazmsn[0]); + return 0; + } + q = p; + p = p->next; + } + return 0; + } + /* Add a single MSN */ + while (p) { + /* Found in list, replace MSN */ + if (p->eaz == eazmsn[0]) { + save_flags(flags); + cli(); + strcpy(p->msn, &eazmsn[1]); + restore_flags(flags); + printk(KERN_DEBUG + "Mapping for EAZ %c changed to %s\n", + eazmsn[0], + &eazmsn[1]); + return 0; + } + p = p->next; + } + /* Not found in list, add new entry */ + p = kmalloc(sizeof(msn_entry), GFP_KERNEL); + if (!p) + return -ENOMEM; + p->eaz = eazmsn[0]; + strcpy(p->msn, &eazmsn[1]); + p->next = card->msn_list; + save_flags(flags); + cli(); + card->msn_list = p; + restore_flags(flags); + printk(KERN_DEBUG + "Mapping %c -> %s added\n", + eazmsn[0], + &eazmsn[1]); + return 0; +} + +static void +act2000_transmit(struct act2000_card *card) +{ + switch (card->bus) { + case ACT2000_BUS_ISA: + isa_send(card); + break; + case ACT2000_BUS_PCMCIA: + case ACT2000_BUS_MCA: + default: + printk(KERN_WARNING + "act2000_transmit: Illegal bustype %d\n", card->bus); + } +} + +static void +act2000_receive(struct act2000_card *card) +{ + switch (card->bus) { + case ACT2000_BUS_ISA: + isa_receive(card); + break; + case ACT2000_BUS_PCMCIA: + case ACT2000_BUS_MCA: + default: + printk(KERN_WARNING + "act2000_receive: Illegal bustype %d\n", card->bus); + } +} + +static void +act2000_poll(unsigned long data) +{ + act2000_card * card = (act2000_card *)data; + unsigned long flags; + + act2000_receive(card); + save_flags(flags); + cli(); + del_timer(&card->ptimer); + card->ptimer.expires = jiffies + 3; + add_timer(&card->ptimer); + restore_flags(flags); +} + +static int +act2000_command(act2000_card * card, isdn_ctrl * c) +{ + ulong a; + act2000_chan *chan; + act2000_cdef cdef; + isdn_ctrl cmd; + char tmp[17]; + int ret; + unsigned long flags; + + switch (c->command) { + case ISDN_CMD_IOCTL: + memcpy(&a, c->parm.num, sizeof(ulong)); + switch (c->arg) { + case ACT2000_IOCTL_LOADBOOT: + switch (card->bus) { + case ACT2000_BUS_ISA: + ret = isa_download(card, + (act2000_ddef *)a); + if (!ret) { + card->flags |= ACT2000_FLAGS_LOADED; + if (!(card->flags & ACT2000_FLAGS_IVALID)) { + card->ptimer.expires = jiffies + 3; + card->ptimer.function = act2000_poll; + card->ptimer.data = (unsigned long)card; + add_timer(&card->ptimer); + } + actcapi_manufacturer_req_errh(card); + } + break; + default: + printk(KERN_WARNING + "act2000: Illegal BUS type %d\n", + card->bus); + ret = -EIO; + } + return ret; + case ACT2000_IOCTL_SETPROTO: + card->ptype = a?ISDN_PTYPE_EURO:ISDN_PTYPE_1TR6; + if (!(card->flags & ACT2000_FLAGS_RUNNING)) + return 0; + actcapi_manufacturer_req_net(card); + return 0; + case ACT2000_IOCTL_SETMSN: + if ((ret = copy_from_user(tmp, (char *)a, sizeof(tmp)))) + return ret; + if ((ret = act2000_set_msn(card, tmp))) + return ret; + if (card->flags & ACT2000_FLAGS_RUNNING) + return(actcapi_manufacturer_req_msn(card)); + return 0; + case ACT2000_IOCTL_ADDCARD: + if ((ret = copy_from_user(&cdef, (char *)a, sizeof(cdef)))) + return ret; + if (act2000_addcard(cdef.bus, cdef.port, cdef.irq, cdef.id)) + return -EIO; + return 0; + case ACT2000_IOCTL_TEST: + if (!(card->flags & ACT2000_FLAGS_RUNNING)) + return -ENODEV; + return 0; + default: + return -EINVAL; + } + break; + case ISDN_CMD_DIAL: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + save_flags(flags); + cli(); + if (chan->fsm_state != ACT2000_STATE_NULL) { + restore_flags(flags); + printk(KERN_WARNING "Dial on channel with state %d\n", + chan->fsm_state); + return -EBUSY; + } + if (card->ptype == ISDN_PTYPE_EURO) + tmp[0] = act2000_find_msn(card, c->parm.setup.eazmsn, 1); + else + tmp[0] = c->parm.setup.eazmsn[0]; + chan->fsm_state = ACT2000_STATE_OCALL; + chan->callref = 0xffff; + restore_flags(flags); + ret = actcapi_connect_req(card, chan, c->parm.setup.phone, + tmp[0], c->parm.setup.si1, + c->parm.setup.si2); + if (ret) { + cmd.driver = card->myid; + cmd.command = ISDN_STAT_DHUP; + cmd.arg &= 0x0f; + card->interface.statcallb(&cmd); + } + return ret; + case ISDN_CMD_ACCEPTD: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + if (chan->fsm_state == ACT2000_STATE_ICALL) + actcapi_select_b2_protocol_req(card, chan); + return 0; + case ISDN_CMD_ACCEPTB: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + return 0; + case ISDN_CMD_HANGUP: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + switch (chan->fsm_state) { + case ACT2000_STATE_ICALL: + case ACT2000_STATE_BSETUP: + actcapi_connect_resp(card, chan, 0x15); + break; + case ACT2000_STATE_ACTIVE: + actcapi_disconnect_b3_req(card, chan); + break; + } + return 0; + case ISDN_CMD_SETEAZ: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + if (strlen(c->parm.num)) { + if (card->ptype == ISDN_PTYPE_EURO) { + chan->eazmask = act2000_find_msn(card, c->parm.num, 0); + } + if (card->ptype == ISDN_PTYPE_1TR6) { + int i; + chan->eazmask = 0; + for (i = 0; i < strlen(c->parm.num); i++) + if (isdigit(c->parm.num[i])) + chan->eazmask |= (1 << (c->parm.num[i] - '0')); + } + } else + chan->eazmask = 0x3ff; + actcapi_listen_req(card); + return 0; + case ISDN_CMD_CLREAZ: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + chan->eazmask = 0; + actcapi_listen_req(card); + return 0; + case ISDN_CMD_SETL2: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + chan->l2prot = (c->arg >> 8); + return 0; + case ISDN_CMD_GETL2: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + return chan->l2prot; + case ISDN_CMD_SETL3: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) { + printk(KERN_WARNING "L3 protocol unknown\n"); + return -1; + } + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + chan->l3prot = (c->arg >> 8); + return 0; + case ISDN_CMD_GETL3: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + if (!(chan = find_channel(card, c->arg & 0x0f))) + break; + return chan->l3prot; + case ISDN_CMD_GETEAZ: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + printk(KERN_DEBUG "act2000 CMD_GETEAZ not implemented\n"); + return 0; + case ISDN_CMD_SETSIL: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + printk(KERN_DEBUG "act2000 CMD_SETSIL not implemented\n"); + return 0; + case ISDN_CMD_GETSIL: + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + printk(KERN_DEBUG "act2000 CMD_GETSIL not implemented\n"); + return 0; + case ISDN_CMD_LOCK: + MOD_INC_USE_COUNT; + return 0; + case ISDN_CMD_UNLOCK: + MOD_DEC_USE_COUNT; + return 0; + } + + return -EINVAL; +} + +static int +act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb) +{ + struct sk_buff *xmit_skb; + int len; + act2000_chan *chan; + actcapi_msg *msg; + + if (!(chan = find_channel(card, channel))) + return -1; + if (chan->fsm_state != ACT2000_STATE_ACTIVE) + return -1; + len = skb->len; + if ((chan->queued + len) >= ACT2000_MAX_QUEUED) + return 0; + if (!len) + return 0; + if (skb_headroom(skb) < 19) { + printk(KERN_WARNING "act2000_sendbuf: Headroom only %d\n", + skb_headroom(skb)); + xmit_skb = alloc_skb(len + 19, GFP_ATOMIC); + if (!xmit_skb) { + printk(KERN_WARNING "act2000_sendbuf: Out of memory\n"); + return 0; + } + skb_reserve(xmit_skb, 19); + memcpy(skb_put(xmit_skb, len), skb->data, len); + } else { + xmit_skb = skb_clone(skb, GFP_ATOMIC); + if (!xmit_skb) { + printk(KERN_WARNING "act2000_sendbuf: Out of memory\n"); + return 0; + } + } + dev_kfree_skb(skb); + msg = (actcapi_msg *)skb_push(xmit_skb, 19); + msg->hdr.len = 19 + len; + msg->hdr.applicationID = 1; + msg->hdr.cmd.cmd = 0x86; + msg->hdr.cmd.subcmd = 0x00; + msg->hdr.msgnum = actcapi_nextsmsg(card); + msg->msg.data_b3_req.datalen = len; + msg->msg.data_b3_req.blocknr = (msg->hdr.msgnum & 0xff); + msg->msg.data_b3_req.fakencci = MAKE_NCCI(chan->plci, 0, chan->ncci); + msg->msg.data_b3_req.flags = ack; /* Will be set to 0 on actual sending */ + actcapi_debug_msg(xmit_skb, 1); + chan->queued += len; + skb_queue_tail(&card->sndq, xmit_skb); + act2000_schedule_tx(card); + return len; +} + + +/* Read the Status-replies from the Interface */ +static int +act2000_readstatus(u_char * buf, int len, int user, act2000_card * card) +{ + int count; + u_char *p; + + for (p = buf, count = 0; count < len; p++, count++) { + if (card->status_buf_read == card->status_buf_write) + return count; + if (user) + put_user(*card->status_buf_read++, p); + else + *p = *card->status_buf_read++; + if (card->status_buf_read > card->status_buf_end) + card->status_buf_read = card->status_buf; + } + return count; +} + +static void +act2000_putmsg(act2000_card *card, char c) +{ + ulong flags; + + save_flags(flags); + cli(); + *card->status_buf_write++ = c; + if (card->status_buf_write == card->status_buf_read) { + if (++card->status_buf_read > card->status_buf_end) + card->status_buf_read = card->status_buf; + } + if (card->status_buf_write > card->status_buf_end) + card->status_buf_write = card->status_buf; + restore_flags(flags); +} + +static void +act2000_logstat(struct act2000_card *card, char *str) +{ + char *p = str; + isdn_ctrl c; + + while (*p) + act2000_putmsg(card, *p++); + c.command = ISDN_STAT_STAVAIL; + c.driver = card->myid; + c.arg = strlen(str); + card->interface.statcallb(&c); +} + +/* + * Find card with given driverId + */ +static inline act2000_card * +act2000_findcard(int driverid) +{ + act2000_card *p = cards; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (act2000_card *) 0; +} + +/* + * Wrapper functions for interface to linklevel + */ +static int +if_command(isdn_ctrl * c) +{ + act2000_card *card = act2000_findcard(c->driver); + + if (card) + return (act2000_command(card, c)); + printk(KERN_ERR + "act2000: if_command %d called with invalid driverId %d!\n", + c->command, c->driver); + return -ENODEV; +} + +static int +if_writecmd(const u_char * buf, int len, int user, int id, int channel) +{ + act2000_card *card = act2000_findcard(id); + + if (card) { + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + return (len); + } + printk(KERN_ERR + "act2000: if_writecmd called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_readstatus(u_char * buf, int len, int user, int id, int channel) +{ + act2000_card *card = act2000_findcard(id); + + if (card) { + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + return (act2000_readstatus(buf, len, user, card)); + } + printk(KERN_ERR + "act2000: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) +{ + act2000_card *card = act2000_findcard(id); + + if (card) { + if (!card->flags & ACT2000_FLAGS_RUNNING) + return -ENODEV; + return (act2000_sendbuf(card, channel, ack, skb)); + } + printk(KERN_ERR + "act2000: if_sendbuf called with invalid driverId!\n"); + return -ENODEV; +} + + +/* + * Allocate a new card-struct, initialize it + * link it into cards-list. + */ +static void +act2000_alloccard(int bus, int port, int irq, char *id) +{ + int i; + act2000_card *card; + if (!(card = (act2000_card *) kmalloc(sizeof(act2000_card), GFP_KERNEL))) { + printk(KERN_WARNING + "act2000: (%s) Could not allocate card-struct.\n", id); + return; + } + memset((char *) card, 0, sizeof(act2000_card)); + skb_queue_head_init(&card->sndq); + skb_queue_head_init(&card->rcvq); + skb_queue_head_init(&card->ackq); + card->snd_tq.routine = (void *) (void *) act2000_transmit; + card->snd_tq.data = card; + card->rcv_tq.routine = (void *) (void *) actcapi_dispatch; + card->rcv_tq.data = card; + card->poll_tq.routine = (void *) (void *) act2000_receive; + card->poll_tq.data = card; + init_timer(&card->ptimer); + card->interface.channels = ACT2000_BCH; + card->interface.maxbufsize = 4000; + card->interface.command = if_command; + card->interface.writebuf_skb = if_sendbuf; + card->interface.writecmd = if_writecmd; + card->interface.readstat = if_readstatus; + card->interface.features = + ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_HDLC | +#if 0 +/* Not yet! New Firmware is on the way ... */ + ISDN_FEATURE_L2_TRANS | +#endif + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN; + card->interface.hl_hdrlen = 20; + card->ptype = ISDN_PTYPE_EURO; + strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); + for (i=0; i<ACT2000_BCH; i++) { + card->bch[i].plci = 0x8000; + card->bch[i].ncci = 0x8000; + card->bch[i].l2prot = ISDN_PROTO_L2_X75I; + card->bch[i].l3prot = ISDN_PROTO_L3_TRANS; + } + card->myid = -1; + card->bus = bus; + card->port = port; + card->irq = irq; + card->next = cards; + cards = card; +} + +/* + * register card at linklevel + */ +static int +act2000_registercard(act2000_card * card) +{ + switch (card->bus) { + case ACT2000_BUS_ISA: + break; + case ACT2000_BUS_MCA: + case ACT2000_BUS_PCMCIA: + default: + printk(KERN_WARNING + "act2000: Illegal BUS type %d\n", + card->bus); + return -1; + } + if (!register_isdn(&card->interface)) { + printk(KERN_WARNING + "act2000: Unable to register %s\n", + card->interface.id); + return -1; + } + card->myid = card->interface.channels; + sprintf(card->regname, "act2000-isdn (%s)", card->interface.id); + return 0; +} + +static void +unregister_card(act2000_card * card) +{ + isdn_ctrl cmd; + + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + switch (card->bus) { + case ACT2000_BUS_ISA: + isa_release(card); + break; + case ACT2000_BUS_MCA: + case ACT2000_BUS_PCMCIA: + default: + printk(KERN_WARNING + "act2000: Invalid BUS type %d\n", + card->bus); + break; + } +} + +static int +act2000_addcard(int bus, int port, int irq, char *id) +{ + act2000_card *p; + act2000_card *q = NULL; + int initialized; + int added = 0; + int failed = 0; + int i; + + if (!bus) + bus = ACT2000_BUS_ISA; + if (port != -1) { + /* Port defined, do fixed setup */ + act2000_alloccard(bus, port, irq, id); + } else { + /* No port defined, perform autoprobing. + * This may result in more than one card detected. + */ + switch (bus) { + case ACT2000_BUS_ISA: + for (i = 0; i < ISA_NRPORTS; i++) + if (isa_detect(isa_ports[i])) { + printk(KERN_INFO + "act2000: Detected ISA card at port 0x%x\n", + isa_ports[i]); + act2000_alloccard(bus, isa_ports[i], irq, id); + } + break; + case ACT2000_BUS_MCA: + case ACT2000_BUS_PCMCIA: + default: + printk(KERN_WARNING + "act2000: addcard: Invalid BUS type %d\n", + bus); + } + } + if (!cards) + return 1; + p = cards; + while (p) { + initialized = 0; + if (!p->interface.statcallb) { + /* Not yet registered. + * Try to register and activate it. + */ + added++; + switch (p->bus) { + case ACT2000_BUS_ISA: + if (isa_detect(p->port)) { + if (act2000_registercard(p)) + break; + if (isa_config_port(p, p->port)) { + printk(KERN_WARNING + "act2000: Could not request port 0x%04x\n", + p->port); + unregister_card(p); + p->interface.statcallb = NULL; + break; + } + if (isa_config_irq(p, p->irq)) { + printk(KERN_INFO + "act2000: No IRQ available, fallback to polling\n"); + /* Fall back to polled operation */ + p->irq = 0; + } + printk(KERN_INFO + "act2000: ISA" + "-type card at port " + "0x%04x ", + p->port); + if (p->irq) + printk("irq %d\n", p->irq); + else + printk("polled\n"); + initialized = 1; + } + break; + case ACT2000_BUS_MCA: + case ACT2000_BUS_PCMCIA: + default: + printk(KERN_WARNING + "act2000: addcard: Invalid BUS type %d\n", + p->bus); + } + } else + /* Card already initialized */ + initialized = 1; + if (initialized) { + /* Init OK, next card ... */ + q = p; + p = p->next; + } else { + /* Init failed, remove card from list, free memory */ + printk(KERN_WARNING + "act2000: Initialization of %s failed\n", + p->interface.id); + if (q) { + q->next = p->next; + kfree(p); + p = q->next; + } else { + cards = p->next; + kfree(p); + p = cards; + } + failed++; + } + } + return (added - failed); +} + +#define DRIVERNAME "IBM Active 2000 ISDN driver" + +#ifdef MODULE +#define act2000_init init_module +#endif + +int +act2000_init(void) +{ + printk(KERN_INFO "%s\n", DRIVERNAME); + if (!cards) + act2000_addcard(act_bus, act_port, act_irq, act_id); + if (!cards) + printk(KERN_INFO "act2000: No cards defined yet\n"); + /* No symbols to export, hide all symbols */ + EXPORT_NO_SYMBOLS; + return 0; +} + +#ifdef MODULE +void +cleanup_module(void) +{ + act2000_card *card = cards; + act2000_card *last; + while (card) { + unregister_card(card); + del_timer(&card->ptimer); + card = card->next; + } + card = cards; + while (card) { + last = card; + card = card->next; + act2000_clear_msn(last); + kfree(last); + } + printk(KERN_INFO "%s unloaded\n", DRIVERNAME); +} + +#else +void +act2000_setup(char *str, int *ints) +{ + int i, j, argc, port, irq, bus; + + argc = ints[0]; + i = 1; + if (argc) + while (argc) { + port = irq = -1; + bus = 0; + if (argc) { + bus = ints[i]; + i++; + argc--; + } + if (argc) { + port = ints[i]; + i++; + argc--; + } + if (argc) { + irq = ints[i]; + i++; + argc--; + } + act2000_addcard(bus, port, irq, act_id); + } +} +#endif diff --git a/drivers/isdn/avmb1/b1capi.c b/drivers/isdn/avmb1/b1capi.c index 00be6fef7..648f19fc2 100644 --- a/drivers/isdn/avmb1/b1capi.c +++ b/drivers/isdn/avmb1/b1capi.c @@ -1,11 +1,39 @@ /* - * $Id: b1capi.c,v 1.4 1997/05/27 15:17:45 fritz Exp $ + * $Id: b1capi.c,v 1.10 1998/02/13 07:09:10 calle Exp $ * * CAPI 2.0 Module for AVM B1-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1capi.c,v $ + * Revision 1.10 1998/02/13 07:09:10 calle + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.9 1998/01/31 11:14:39 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.8 1997/12/10 20:00:46 calle + * get changes from 2.0 version + * + * Revision 1.4.2.5 1997/12/07 19:59:54 calle + * more changes for M1/T1/B1 + config + * + * Revision 1.4.2.4 1997/11/26 16:57:20 calle + * more changes for B1/M1/T1. + * + * Revision 1.7 1997/10/19 14:45:40 calle + * fixed capi_get_version. + * + * Revision 1.6 1997/10/01 09:21:09 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.5 1997/07/12 08:22:26 calle + * Correct bug in CARD_NR macro, so now more than one card will work. + * Allow card reset, even if card is in running state. + * + * * Revision 1.4 1997/05/27 15:17:45 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -48,24 +76,16 @@ #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.4 $"; +static char *revision = "$Revision: 1.10 $"; /* ------------------------------------------------------------- */ int showcapimsgs = 0; /* used in lli.c */ int loaddebug = 0; -static int portbase = 0x150; -#ifdef MODULE -static int irq = 15; -#ifdef HAS_NEW_SYMTAB MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); -MODULE_PARM(portbase, "i"); -MODULE_PARM(irq, "2-15i"); MODULE_PARM(showcapimsgs, "0-3i"); MODULE_PARM(loaddebug, "0-1i"); -#endif -#endif /* ------------------------------------------------------------- */ @@ -97,7 +117,7 @@ typedef struct avmb1_appl { /* ------------------------------------------------------------- */ -static struct capi_version driver_version = {2, 0, 0, 9}; +static struct capi_version driver_version = {2, 0, 1, 1<<4}; static char driver_serial[CAPI_SERIAL_LEN] = "4711"; static char capi_manufakturer[64] = "AVM Berlin"; @@ -109,9 +129,9 @@ static char capi_manufakturer[64] = "AVM Berlin"; #define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) -#define VALID_CARD(c) ((c) > 0 && (c) <= ncards) +#define VALID_CARD(c) ((c) > 0 && (c) <= CAPI_MAXCONTR) #define CARD(c) (&cards[(c)-1]) -#define CARDNR(cp) ((cards-(cp))+1) +#define CARDNR(cp) (((cp)-cards)+1) static avmb1_appl applications[CAPI_MAXAPPL]; static avmb1_card cards[CAPI_MAXCONTR]; @@ -126,6 +146,17 @@ static struct tq_struct tq_recv_notify; /* -------- util functions ------------------------------------ */ +static char *cardtype2str(int cardtype) +{ + switch (cardtype) { + default: + case AVM_CARDTYPE_B1: return "B1"; + case AVM_CARDTYPE_M1: return "M1"; + case AVM_CARDTYPE_M2: return "M2"; + case AVM_CARDTYPE_T1: return "T1"; + } +} + static inline int capi_cmd_valid(__u8 cmd) { switch (cmd) { @@ -287,6 +318,8 @@ static avmb1_ncci *find_ncci(avmb1_appl * app, __u32 ncci) return 0; } + + /* -------- Receiver ------------------------------------------ */ @@ -367,6 +400,7 @@ static void notify_up(__u16 contr) { struct capi_interface_user *p; + printk(KERN_NOTICE "b1capi: notify up contr %d\n", contr); for (p = capi_users; p; p = p->next) { if (p->callback) (*p->callback) (KCI_CONTRUP, contr, @@ -378,6 +412,7 @@ static void notify_up(__u16 contr) static void notify_down(__u16 contr) { struct capi_interface_user *p; + printk(KERN_NOTICE "b1capi: notify down contr %d\n", contr); for (p = capi_users; p; p = p->next) { if (p->callback) (*p->callback) (KCI_CONTRDOWN, contr, 0); @@ -400,18 +435,22 @@ static void notify_handler(void *dummy) void avmb1_card_ready(avmb1_card * card) { + struct capi_profile *profp = + (struct capi_profile *)card->version[VER_PROFILE]; + char *dversion = card->version[VER_DRIVER]; __u16 appl; + char *cardname, cname[20]; + __u32 flag; card->cversion.majorversion = 2; card->cversion.minorversion = 0; - card->cversion.majormanuversion = (card->version[VER_DRIVER][0] - '0') << 4; - card->cversion.majormanuversion |= (card->version[VER_DRIVER][2] - '0'); - card->cversion.minormanuversion = (card->version[VER_DRIVER][3] - '0') << 4; - card->cversion.minormanuversion |= (card->version[VER_DRIVER][5] - '0') * 10; - card->cversion.minormanuversion |= (card->version[VER_DRIVER][6] - '0'); + card->cversion.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); + card->cversion.majormanuversion |= ((dversion[2] - '0') & 0xf); + card->cversion.minormanuversion = (dversion[3] - '0') << 4; + card->cversion.minormanuversion |= + (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); card->cardstate = CARD_RUNNING; - for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { if (VALID_APPLID(appl) && !APPL(appl)->releasing) { B1_send_register(card->port, appl, @@ -424,56 +463,190 @@ void avmb1_card_ready(avmb1_card * card) set_bit(CARDNR(card), ¬ify_up_set); queue_task(&tq_state_notify, &tq_scheduler); + + flag = ((__u8 *)(profp->manu))[1]; + switch (flag) { + case 0: cardname = cardtype2str(card->cardtype); break; + case 3: cardname = "PCMCIA B"; break; + case 4: cardname = "PCMCIA M1"; break; + case 5: cardname = "PCMCIA M2"; break; + case 6: cardname = "B1 V3.0"; break; + case 7: cardname = "B1 PCI"; break; + default: cardname = cname; break; + sprintf(cname, "AVM?%u", (unsigned int)flag); + break; + } + printk(KERN_NOTICE "b1capi: card %d \"%s\" ready.\n", + CARDNR(card), cardname); + flag = ((__u8 *)(profp->manu))[3]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n", + CARDNR(card), + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + flag = ((__u8 *)(profp->manu))[5]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Linetype:%s%s%s%s\n", + CARDNR(card), + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); +} + +static void avmb1_card_down(avmb1_card * card, int notify) +{ + __u16 appl; + + card->cardstate = CARD_DETECTED; + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + avmb1_ncci **pp, **nextpp; + for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) { + if (NCCI2CTRL((*pp)->ncci) == card->cnr) { + avmb1_ncci *np = *pp; + *pp = np->next; + printk(KERN_INFO "b1capi: appl %d ncci 0x%x forced down!\n", appl, np->ncci); + kfree(np); + nextpp = pp; + } else { + nextpp = &(*pp)->next; + } + } + } + set_bit(CARDNR(card), ¬ify_down_set); + queue_task(&tq_state_notify, &tq_scheduler); + printk(KERN_NOTICE "b1capi: card %d down.\n", CARDNR(card)); } /* ------------------------------------------------------------- */ -int avmb1_addcard(int port, int irq) + +int avmb1_registercard(int port, int irq, int cardtype, int allocio) { struct avmb1_card *card; - int irqval; + int irqval,i; + + for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ; + + if (i == CAPI_MAXCONTR) { + printk(KERN_ERR "b1capi: out of controller slots\n"); + return -ENFILE; + } - card = &cards[ncards]; + card = &cards[i]; memset(card, 0, sizeof(avmb1_card)); - sprintf(card->name, "avmb1-%d", ncards + 1); + sprintf(card->name, "avmb1-%d", CARDNR(card)); - request_region(port, AVMB1_PORTLEN, card->name); + if (allocio) + request_region(port, AVMB1_PORTLEN, card->name); - if ((irqval = request_irq(irq, avmb1_interrupt, SA_SHIRQ, card->name, card)) != 0) { + if ((irqval = request_irq(irq, avmb1_interrupt, + SA_SHIRQ, card->name, card)) != 0) { printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n", irq, irqval); release_region((unsigned short) port, AVMB1_PORTLEN); return -EIO; } + + card->cardstate = CARD_DETECTED; ncards++; - card->cnr = ncards; + card->cnr = CARDNR(card); card->port = port; card->irq = irq; - card->cardstate = CARD_DETECTED; - return 0; + card->cardtype = cardtype; + return card->cnr; +} + +int avmb1_addcard(int port, int irq, int cardtype) +{ + return avmb1_registercard(port, irq, cardtype, 1); } -int avmb1_probecard(int port, int irq) +int avmb1_detectcard(int port, int irq, int cardtype) { int rc; - if (check_region((unsigned short) port, AVMB1_PORTLEN)) { - printk(KERN_WARNING - "b1capi: ports 0x%03x-0x%03x in use.\n", - portbase, portbase + AVMB1_PORTLEN); + if (!B1_valid_irq(irq, cardtype)) { + printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n", + irq, cardtype2str(cardtype)); return -EIO; } - if (!B1_valid_irq(irq)) { - printk(KERN_WARNING "b1capi: irq %d not valid.\n", irq); + if ((rc = B1_detect(port, cardtype)) != 0) { + printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n", + cardtype2str(cardtype), port, rc); return -EIO; } - if ((rc = B1_detect(port)) != 0) { - printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc); + B1_reset(port); + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port); + break; + case AVM_CARDTYPE_T1: + printk(KERN_NOTICE "b1capi: AVM-%s-Controller may be at 0x%x\n", cardtype2str(cardtype), port); + break; + } + + return 0; +} + +int avmb1_probecard(int port, int irq, int cardtype) +{ + if (check_region((unsigned short) port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1capi: ports 0x%03x-0x%03x in use.\n", + port, port + AVMB1_PORTLEN); return -EIO; } - B1_reset(port); - printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port); + return avmb1_detectcard(port, irq, cardtype); +} + +int avmb1_unregistercard(int cnr, int freeio) +{ + avmb1_card * card; + if (!VALID_CARD(cnr)) + return -ESRCH; + card = CARD(cnr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + if (card->cardstate == CARD_RUNNING) + avmb1_card_down(card, freeio); + + free_irq(card->irq, card); + if (freeio) + release_region(card->port, AVMB1_PORTLEN); + card->cardstate = CARD_FREE; + return 0; +} + +int avmb1_resetcard(int cnr) +{ + avmb1_card * card; + + if (!VALID_CARD(cnr)) + return -ESRCH; + card = CARD(cnr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + + if (card->cardstate == CARD_RUNNING) + avmb1_card_down(card, 0); + + B1_reset(card->port); + B1_reset(card->port); + + card->cardstate = CARD_DETECTED; return 0; } @@ -485,7 +658,7 @@ int avmb1_probecard(int port, int irq) static int capi_installed(void) { int i; - for (i = 0; i < ncards; i++) { + for (i = 0; i < CAPI_MAXCONTR; i++) { if (cards[i].cardstate == CARD_RUNNING) return 1; } @@ -512,7 +685,7 @@ static __u16 capi_register(capi_register_params * rparam, __u16 * applidp) memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params)); - for (i = 0; i < ncards; i++) { + for (i = 0; i < CAPI_MAXCONTR; i++) { if (cards[i].cardstate != CARD_RUNNING) continue; B1_send_register(cards[i].port, appl, @@ -538,9 +711,10 @@ static __u16 capi_release(__u16 applid) return CAPI_ILLAPPNR; while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0) kfree_skb(skb); - for (i = 0; i < ncards; i++) { - if (cards[i].cardstate != CARD_RUNNING) + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_RUNNING) { continue; + } APPL(applid)->releasing++; B1_send_release(cards[i].port, applid); } @@ -628,7 +802,7 @@ static __u16 capi_get_version(__u16 contr, struct capi_version *verp) if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) return 0x2002; - memcpy((void *) verp, CARD(contr)->version[VER_SERIAL], + memcpy((void *) verp, &CARD(contr)->cversion, sizeof(capi_version)); return CAPI_NOERROR; } @@ -665,33 +839,52 @@ static __u16 capi_get_profile(__u16 contr, struct capi_profile *profp) static int capi_manufacturer(unsigned int cmd, void *data) { unsigned long flags; - avmb1_loaddef ldef; - avmb1_carddef cdef; + avmb1_loadandconfigdef ldef; + avmb1_extcarddef cdef; avmb1_resetdef rdef; + avmb1_getdef gdef; avmb1_card *card; int rc; switch (cmd) { case AVMB1_ADDCARD: - if ((rc = copy_from_user((void *) &cdef, data, - sizeof(avmb1_carddef)))) - return rc; - if (!B1_valid_irq(cdef.irq)) - return -EINVAL; + case AVMB1_ADDCARD_WITH_TYPE: + if (cmd == AVMB1_ADDCARD) { + if ((rc = copy_from_user((void *) &cdef, data, + sizeof(avmb1_carddef)))) + return rc; + cdef.cardtype = AVM_CARDTYPE_B1; + } else { + if ((rc = copy_from_user((void *) &cdef, data, + sizeof(avmb1_extcarddef)))) + return rc; + } - if ((rc = avmb1_probecard(cdef.port, cdef.irq)) != 0) + if ((rc = avmb1_probecard(cdef.port, cdef.irq, cdef.cardtype)) != 0) return rc; - return avmb1_addcard(cdef.port, cdef.irq); + return avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype); case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + + if (cmd == AVMB1_LOAD) { + if ((rc = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loaddef)))) + return rc; + ldef.t4config.len = 0; + ldef.t4config.data = 0; + } else { + if ((rc = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loadandconfigdef)))) + return rc; + } + if (!VALID_CARD(ldef.contr)) + return -ESRCH; - if ((rc = copy_from_user((void *) &ldef, data, - sizeof(avmb1_loaddef)))) - return rc; - if (!VALID_CARD(ldef.contr) || ldef.t4file.len <= 0) { + if (ldef.t4file.len <= 0) { if (loaddebug) - printk(KERN_DEBUG "b1capi: load: invalid parameter contr=%d len=%d\n", ldef.contr, ldef.t4file.len); + printk(KERN_DEBUG "b1capi: load: invalid parameter length of t4file is %d ?\n", ldef.t4file.len); return -EINVAL; } @@ -720,6 +913,20 @@ static int capi_manufacturer(unsigned int cmd, void *data) return rc; } B1_disable_irq(card->port); + + if (ldef.t4config.len > 0) { /* load config */ + if (loaddebug) { + printk(KERN_DEBUG "b1capi: loading config to contr %d\n", + ldef.contr); + } + if ((rc = B1_load_config(card->port, &ldef.t4config))) { + B1_reset(card->port); + printk(KERN_ERR "b1capi: failed to load config!!\n"); + card->cardstate = CARD_DETECTED; + return rc; + } + } + if (loaddebug) { printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n", ldef.contr); @@ -737,7 +944,7 @@ static int capi_manufacturer(unsigned int cmd, void *data) card->cardstate = CARD_INITSTATE; save_flags(flags); cli(); - B1_assign_irq(card->port, card->irq); + B1_assign_irq(card->port, card->irq, card->cardtype); B1_enable_irq(card->port); restore_flags(flags); @@ -766,23 +973,31 @@ static int capi_manufacturer(unsigned int cmd, void *data) return -EINTR; } return 0; + case AVMB1_RESETCARD: if ((rc = copy_from_user((void *) &rdef, data, sizeof(avmb1_resetdef)))) return rc; - if (!VALID_CARD(rdef.contr)) - return -EINVAL; + return avmb1_resetcard(rdef.contr); + + case AVMB1_GET_CARDINFO: + if ((rc = copy_from_user((void *) &gdef, data, + sizeof(avmb1_getdef)))) + return rc; - card = CARD(rdef.contr); + if (!VALID_CARD(gdef.contr)) + return -ESRCH; - if (card->cardstate == CARD_RUNNING) - return -EBUSY; + card = CARD(gdef.contr); - B1_reset(card->port); - B1_reset(card->port); + gdef.cardstate = card->cardstate; + gdef.cardtype = card->cardtype; + + if ((rc = copy_to_user(data, (void *) &gdef, + sizeof(avmb1_getdef)))) + return rc; - card->cardstate = CARD_DETECTED; return 0; } return -EINVAL; @@ -845,22 +1060,14 @@ int detach_capi_interface(struct capi_interface_user *userp) /* -------- Init & Cleanup ------------------------------------- */ /* ------------------------------------------------------------- */ -#ifdef HAS_NEW_SYMTAB EXPORT_SYMBOL(attach_capi_interface); EXPORT_SYMBOL(detach_capi_interface); EXPORT_SYMBOL(avmb1_addcard); EXPORT_SYMBOL(avmb1_probecard); -#else -static struct symbol_table capidev_syms = -{ -#include <linux/symtab_begin.h> - X(attach_capi_interface), - X(detach_capi_interface), - X(avmb1_addcard), - X(avmb1_probecard), -#include <linux/symtab_end.h> -}; -#endif +EXPORT_SYMBOL(avmb1_registercard); +EXPORT_SYMBOL(avmb1_unregistercard); +EXPORT_SYMBOL(avmb1_resetcard); +EXPORT_SYMBOL(avmb1_detectcard); /* @@ -876,11 +1083,6 @@ int avmb1_init(void) char *p; char rev[10]; - -#ifndef HAS_NEW_SYMTAB - /* No symbols to export, hide all symbols */ - register_symtab(&capidev_syms); -#endif skb_queue_head_init(&recv_queue); /* init_bh(CAPI_BH, do_capi_bh); */ @@ -899,15 +1101,7 @@ int avmb1_init(void) strcpy(rev, " ??? "); #ifdef MODULE - if (portbase) { - int rc; - if ((rc = avmb1_probecard(portbase, irq)) != 0) - return rc; - if ((rc = avmb1_addcard(portbase, irq)) != 0) - return rc; - } else { - printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev); - } + printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev); #else printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: started\n", rev); #endif @@ -929,20 +1123,20 @@ void cleanup_module(void) strcpy(rev, " ??? "); } - for (i = 0; i < ncards; i++) { - /* - * disable card - */ - B1_disable_irq(cards[i].port); - B1_reset(cards[i].port); - B1_reset(cards[i].port); - /* - * free kernel resources - */ - free_irq(cards[i].irq, &cards[i]); - release_region(cards[i].port, AVMB1_PORTLEN); - + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_FREE) { + /* + * disable card + */ + B1_disable_irq(cards[i].port); + avmb1_resetcard(i+1); + /* + * free kernel resources + */ + avmb1_unregistercard(i+1, 1); + } } + schedule(); /* execute queued tasks .... */ printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev); } #endif diff --git a/drivers/isdn/avmb1/b1lli.c b/drivers/isdn/avmb1/b1lli.c index 64292ef21..1c4c0b806 100644 --- a/drivers/isdn/avmb1/b1lli.c +++ b/drivers/isdn/avmb1/b1lli.c @@ -1,11 +1,35 @@ /* - * $Id: b1lli.c,v 1.1 1997/03/04 21:50:28 calle Exp $ + * $Id: b1lli.c,v 1.6 1998/02/13 07:09:11 calle Exp $ * * ISDN lowlevel-module for AVM B1-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1lli.c,v $ + * Revision 1.6 1998/02/13 07:09:11 calle + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.5 1998/01/31 11:14:41 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.4 1997/12/10 20:00:48 calle + * get changes from 2.0 version + * + * Revision 1.1.2.2 1997/11/26 10:46:55 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * + * Revision 1.3 1997/10/01 09:21:13 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.2 1997/07/13 12:22:42 calle + * bug fix for more than one controller in connect_req. + * debugoutput now with contrnr. + * + * * Revision 1.1 1997/03/04 21:50:28 calle * Frirst version in isdn4linux * @@ -66,6 +90,9 @@ * B3Length data .... */ +#define SEND_CONFIG 0x21 /* + */ + /* * LLI Messages from the ISDN-ControllerISDN Controller */ @@ -110,6 +137,9 @@ * int32 AppllID int32 0xffffffff */ +#define WRITE_REGISTER 0x00 +#define READ_REGISTER 0x01 + /* * port offsets */ @@ -120,7 +150,11 @@ #define B1_OUTSTAT 0x03 #define B1_RESET 0x10 #define B1_ANALYSE 0x04 +#define B1_IDENT 0x17 /* Hema card T1 */ +#define B1_IRQ_MASTER 0x12 /* Hema card T1 */ +#define B1_STAT0(cardtype) ((cardtype) == AVM_CARDTYPE_M1 ? 0x81200000l : 0x80A00000l) +#define B1_STAT1(cardtype) (0x80E00000l) static inline unsigned char b1outp(unsigned short base, @@ -131,6 +165,100 @@ static inline unsigned char b1outp(unsigned short base, return inb(base + B1_ANALYSE); } +static inline int B1_rx_full(unsigned short base) +{ + return inb(base + B1_INSTAT) & 0x1; +} + +static inline unsigned char B1_get_byte(unsigned short base) +{ + unsigned long i = jiffies + 5 * HZ; /* maximum wait time 5 sec */ + while (!B1_rx_full(base) && i > jiffies); + if (B1_rx_full(base)) + return inb(base + B1_READ); + printk(KERN_CRIT "b1lli: rx not full after 5 second\n"); + return 0; +} + +static inline unsigned int B1_get_word(unsigned short base) +{ + unsigned int val = 0; + val |= B1_get_byte(base); + val |= (B1_get_byte(base) << 8); + val |= (B1_get_byte(base) << 16); + val |= (B1_get_byte(base) << 24); + return val; +} + +static inline int B1_tx_empty(unsigned short base) +{ + return inb(base + B1_OUTSTAT) & 0x1; +} + +static inline void B1_put_byte(unsigned short base, unsigned char val) +{ + while (!B1_tx_empty(base)); + b1outp(base, B1_WRITE, val); +} + +static inline void B1_put_word(unsigned short base, unsigned int val) +{ + B1_put_byte(base, val & 0xff); + B1_put_byte(base, (val >> 8) & 0xff); + B1_put_byte(base, (val >> 16) & 0xff); + B1_put_byte(base, (val >> 24) & 0xff); +} + +static inline unsigned int B1_get_slice(unsigned short base, + unsigned char *dp) +{ + unsigned int len, i; + + len = i = B1_get_word(base); + while (i-- > 0) + *dp++ = B1_get_byte(base); + return len; +} + +static inline void B1_put_slice(unsigned short base, + unsigned char *dp, unsigned int len) +{ + B1_put_word(base, len); + while (len-- > 0) + B1_put_byte(base, *dp++); +} + +static void b1_wr_reg(unsigned short base, + unsigned int reg, + unsigned int value) +{ + B1_put_byte(base, WRITE_REGISTER); + B1_put_word(base, reg); + B1_put_word(base, value); +} + +static inline unsigned int b1_rd_reg(unsigned short base, + unsigned int reg) +{ + B1_put_byte(base, READ_REGISTER); + B1_put_word(base, reg); + return B1_get_word(base); + +} + +static inline void b1_set_test_bit(unsigned short base, + int cardtype, + int onoff) +{ + b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20); +} + +static inline int b1_get_test_bit(unsigned short base, + int cardtype) +{ + return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0; +} + static int irq_table[16] = {0, 0, @@ -150,14 +278,30 @@ static int irq_table[16] = 112, /* irq 15 */ }; -int B1_valid_irq(unsigned irq) +int B1_valid_irq(unsigned irq, int cardtype) { - return irq_table[irq] != 0; + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + return irq_table[irq] != 0; + case AVM_CARDTYPE_T1: + return irq == 5; + } } -unsigned char B1_assign_irq(unsigned short base, unsigned irq) +unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype) { - return b1outp(base, B1_RESET, irq_table[irq]); + switch (cardtype) { + case AVM_CARDTYPE_T1: + return b1outp(base, B1_IRQ_MASTER, 0x08); + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + return b1outp(base, B1_RESET, irq_table[irq]); + } } unsigned char B1_enable_irq(unsigned short base) @@ -182,24 +326,27 @@ void B1_reset(unsigned short base) udelay(55 * 2 * 1000); /* 2 TIC's */ } -int B1_detect(unsigned short base) +int B1_detect(unsigned short base, int cardtype) { + int onoff, i; + + if (cardtype == AVM_CARDTYPE_T1) + return 0; + /* * Statusregister 0000 00xx */ if ((inb(base + B1_INSTAT) & 0xfc) || (inb(base + B1_OUTSTAT) & 0xfc)) return 1; - /* * Statusregister 0000 001x */ b1outp(base, B1_INSTAT, 0x2); /* enable irq */ - b1outp(base, B1_OUTSTAT, 0x2); + /* b1outp(base, B1_OUTSTAT, 0x2); */ if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 - || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2) + /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */) return 2; - /* * Statusregister 0000 000x */ @@ -208,72 +355,23 @@ int B1_detect(unsigned short base) if ((inb(base + B1_INSTAT) & 0xfe) || (inb(base + B1_OUTSTAT) & 0xfe)) return 3; + + for (onoff = !0, i= 0; i < 10 ; i++) { + b1_set_test_bit(base, cardtype, onoff); + if (b1_get_test_bit(base, cardtype) != onoff) + return 4; + onoff = !onoff; + } - return 0; -} + if (cardtype == AVM_CARDTYPE_M1) + return 0; -static inline int B1_rx_full(unsigned short base) -{ - return inb(base + B1_INSTAT) & 0x1; -} + if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01) + return 5; -static inline unsigned char B1_get_byte(unsigned short base) -{ - unsigned long i = jiffies + 5 * HZ; /* maximum wait time 5 sec */ - while (!B1_rx_full(base) && i > jiffies); - if (B1_rx_full(base)) - return inb(base + B1_READ); - printk(KERN_CRIT "b1lli: rx not full after 5 second\n"); return 0; } -static inline unsigned int B1_get_word(unsigned short base) -{ - unsigned int val = 0; - val |= B1_get_byte(base); - val |= (B1_get_byte(base) << 8); - val |= (B1_get_byte(base) << 16); - val |= (B1_get_byte(base) << 24); - return val; -} - -static inline int B1_tx_empty(unsigned short base) -{ - return inb(base + B1_OUTSTAT) & 0x1; -} - -static inline void B1_put_byte(unsigned short base, unsigned char val) -{ - while (!B1_tx_empty(base)); - b1outp(base, B1_WRITE, val); -} - -static inline void B1_put_word(unsigned short base, unsigned int val) -{ - B1_put_byte(base, val & 0xff); - B1_put_byte(base, (val >> 8) & 0xff); - B1_put_byte(base, (val >> 16) & 0xff); - B1_put_byte(base, (val >> 24) & 0xff); -} - -static inline unsigned int B1_get_slice(unsigned short base, - unsigned char *dp) -{ - unsigned int len, i; - - len = i = B1_get_word(base); - while (i-- > 0) - *dp++ = B1_get_byte(base); - return len; -} - -static inline void B1_put_slice(unsigned short base, - unsigned char *dp, unsigned int len) -{ - B1_put_word(base, len); - while (len-- > 0) - B1_put_byte(base, *dp++); -} extern int loaddebug; @@ -316,6 +414,62 @@ int B1_load_t4file(unsigned short base, avmb1_t4file * t4file) return 0; } +int B1_load_config(unsigned short base, avmb1_t4file * config) +{ + /* + * Data is in user space !!! + */ + unsigned char buf[256]; + unsigned char *dp; + int i, j, left, retval; + + + dp = config->data; + left = config->len; + if (left) { + B1_put_byte(base, SEND_CONFIG); + B1_put_word(base, 1); + B1_put_byte(base, SEND_CONFIG); + B1_put_word(base, left); + } + while (left > sizeof(buf)) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", sizeof(buf)); + for (i = 0; i < sizeof(buf); ) { + B1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + B1_put_byte(base, buf[i++]); + } + } + if (loaddebug) + printk("ok\n"); + left -= sizeof(buf); + dp += sizeof(buf); + } + if (left) { + retval = copy_from_user(buf, dp, left); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", left); + for (i = 0; i < left; ) { + B1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + if (i < left) + B1_put_byte(base, buf[i++]); + else + B1_put_byte(base, 0); + } + } + if (loaddebug) + printk("ok\n"); + } + return 0; +} + int B1_loaded(unsigned short base) { int i; @@ -428,7 +582,9 @@ void B1_send_message(unsigned short port, struct sk_buff *skb) CAPIMSG_APPID(skb->data), capi_cmd2str(cmd, subcmd), len); } else { - printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data)); + printk(KERN_DEBUG "b1lli: Put [0x%lx] %s\n", + (unsigned long) contr, + capi_message2str(skb->data)); } } @@ -447,7 +603,7 @@ void B1_send_message(unsigned short port, struct sk_buff *skb) CAPIMSG_APPID(skb->data), capi_cmd2str(cmd, subcmd), len); } else { - printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data)); + printk(KERN_DEBUG "b1lli: Put [0x%lx] %s\n", (unsigned long)contr, capi_message2str(skb->data)); } } save_flags(flags); @@ -499,13 +655,12 @@ void B1_handle_interrupt(avmb1_card * card) capi_cmd2str(cmd, subcmd), MsgLen, DataB3Len); } else { - printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf)); + printk(KERN_DEBUG "b1lli: Got [0x%lx] %s\n", (unsigned long)contr, capi_message2str(card->msgbuf)); } } if (!(skb = dev_alloc_skb(DataB3Len + MsgLen))) { printk(KERN_ERR "b1lli: incoming packet dropped\n"); } else { - SET_SKB_FREE(skb); memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); CAPIMSG_SETDATA(skb->data, skb->data + MsgLen); @@ -528,14 +683,15 @@ void B1_handle_interrupt(avmb1_card * card) capi_cmd2str(cmd, subcmd), MsgLen); } else { - printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf)); + printk(KERN_DEBUG "b1lli: Got [0x%lx] %s\n", + (unsigned long) contr, + capi_message2str(card->msgbuf)); } } if (!(skb = dev_alloc_skb(MsgLen))) { printk(KERN_ERR "b1lli: incoming packet dropped\n"); } else { - SET_SKB_FREE(skb); memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); avmb1_handle_capimsg(card, ApplId, skb); } @@ -581,10 +737,9 @@ void B1_handle_interrupt(avmb1_card * card) card->versionlen = B1_get_slice(card->port, card->versionbuf); card->cardstate = CARD_ACTIVE; parse_version(card); - printk(KERN_INFO "b1lli: %s-card (%s) with %s now active\n", + printk(KERN_INFO "b1lli: %s-card (%s) now active\n", card->version[VER_CARDTYPE], - card->version[VER_DRIVER], - card->version[VER_PROTO]); + card->version[VER_DRIVER]); avmb1_card_ready(card); break; default: diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index af7b58407..5a31d7992 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,11 +1,22 @@ /* - * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 calle Exp $ + * $Id: b1pci.c,v 1.5 1998/01/31 11:14:43 calle Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ + * Revision 1.5 1998/01/31 11:14:43 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.4 1997/12/10 20:00:50 calle + * get changes from 2.0 version + * + * Revision 1.3 1997/10/01 09:21:14 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.2 1997/05/18 09:24:13 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -19,7 +30,6 @@ #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/skbuff.h> #include "compat.h" @@ -34,13 +44,11 @@ #define PCI_DEVICE_ID_AVM_B1 0x700 #endif -static char *revision = "$Revision: 1.2 $"; +static char *revision = "$Revision: 1.5 $"; /* ------------------------------------------------------------- */ -#ifdef HAS_NEW_SYMTAB MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); -#endif /* ------------------------------------------------------------- */ @@ -61,7 +69,7 @@ int b1pci_init(void) char *p; char rev[10]; int rc; - int pci_index; + struct pci_dev *dev = NULL; if ((p = strchr(revision, ':'))) { strcpy(rev, p + 1); @@ -72,39 +80,26 @@ int b1pci_init(void) #ifdef CONFIG_PCI - if (!pcibios_present()) { - printk(KERN_ERR "b1pci: no PCI-BIOS present\n"); + if (!pci_present()) { + printk(KERN_ERR "b1pci: no PCI bus present\n"); return -EIO; } printk(KERN_INFO "b1pci: revision %s\n", rev); - for (pci_index = 0; pci_index < 8; pci_index++) { - unsigned char pci_bus, pci_device_fn; - unsigned int ioaddr; - unsigned char irq; - - if (pcibios_find_device (PCI_VENDOR_ID_AVM, - PCI_DEVICE_ID_AVM_B1, pci_index, - &pci_bus, &pci_device_fn) != 0) { - continue; - } - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &irq); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_1, &ioaddr); - /* Strip the I/O address out of the returned value */ - ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + while (dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev)) { + unsigned int ioaddr = dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + unsigned int irq = dev->irq; printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", ioaddr, irq); - if ((rc = avmb1_probecard(ioaddr, irq)) != 0) { + if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1)) != 0) { printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n", ioaddr, irq); return rc; } - if ((rc = avmb1_addcard(ioaddr, irq)) != 0) + if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1)) < 0) return rc; } return 0; diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 14a5e7d0f..67c39c675 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -1,11 +1,34 @@ /* - * $Id: capi.c,v 1.4 1997/05/27 15:17:50 fritz Exp $ + * $Id: capi.c,v 1.10 1998/02/13 07:09:13 calle Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capi.c,v $ + * Revision 1.10 1998/02/13 07:09:13 calle + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.9 1998/01/31 11:14:44 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.8 1997/11/04 06:12:08 calle + * capi.c: new read/write in file_ops since 2.1.60 + * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. + * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON) + * compat.h: added #define LinuxVersionCode + * + * Revision 1.7 1997/10/11 10:29:34 calle + * llseek() parameters changed in 2.1.56. + * + * Revision 1.6 1997/10/01 09:21:15 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.5 1997/08/21 23:11:55 fritz + * Added changes for kernels >= 2.1.45 + * * Revision 1.4 1997/05/27 15:17:50 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -45,26 +68,22 @@ #include <linux/timer.h> #include <linux/wait.h> #include <linux/skbuff.h> +#include <linux/poll.h> #include <linux/capi.h> #include <linux/kernelcapi.h> -#include <linux/poll.h> #include "compat.h" #include "capiutil.h" #include "capicmd.h" #include "capidev.h" -#ifdef HAS_NEW_SYMTAB MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)"); -#endif /* -------- driver information -------------------------------------- */ int capi_major = 68; /* allocated */ -#ifdef HAS_NEW_SYMTAB MODULE_PARM(capi_major, "i"); -#endif /* -------- global variables ---------------------------------------- */ @@ -94,21 +113,25 @@ static void capi_signal(__u16 applid, __u32 minor) /* -------- file_operations ----------------------------------------- */ -static loff_t capi_llseek(struct file *file, loff_t offset, int origin) +static long long capi_llseek(struct file *file, + long long offset, int origin) { return -ESPIPE; } -static ssize_t capi_read(struct file *file, - char *buf, size_t count, - loff_t *off) +static ssize_t capi_read(struct file *file, char *buf, + size_t count, loff_t *ppos) { - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct inode *inode = file->f_dentry->d_inode; + unsigned int minor = MINOR(inode->i_rdev); struct capidev *cdev; struct sk_buff *skb; int retval; size_t copied; + if (ppos != &file->f_pos) + return -ESPIPE; + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) return -ENODEV; @@ -149,11 +172,11 @@ static ssize_t capi_read(struct file *file, return copied; } -static ssize_t capi_write(struct file *file, - const char *buf, size_t count, - loff_t *off) +static ssize_t capi_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) { - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct inode *inode = file->f_dentry->d_inode; + unsigned int minor = MINOR(inode->i_rdev); struct capidev *cdev; struct sk_buff *skb; int retval; @@ -161,6 +184,9 @@ static ssize_t capi_write(struct file *file, __u8 subcmd; __u16 mlen; + if (ppos != &file->f_pos) + return -ESPIPE; + if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) return -ENODEV; @@ -200,7 +226,11 @@ static unsigned int capi_poll(struct file *file, poll_table * wait) { unsigned int mask = 0; +#if (LINUX_VERSION_CODE >= 0x02012d) unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); +#else + unsigned int minor = MINOR(file->f_inode->i_rdev); +#endif struct capidev *cdev; if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered) @@ -394,7 +424,7 @@ static int capi_open(struct inode *inode, struct file *file) return 0; } -static CLOSETYPE +static int capi_release(struct inode *inode, struct file *file) { unsigned int minor = MINOR(inode->i_rdev); @@ -403,7 +433,7 @@ capi_release(struct inode *inode, struct file *file) if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) { printk(KERN_ERR "capi20: release minor %d ???\n", minor); - return CLOSEVAL; + return 0; } cdev = &capidevs[minor]; @@ -421,7 +451,7 @@ capi_release(struct inode *inode, struct file *file) cdev->is_open = 0; MOD_DEC_USE_COUNT; - return CLOSEVAL; + return 0; } static struct file_operations capi_fops = diff --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c index c1dfcdafe..1d4a7c2e8 100644 --- a/drivers/isdn/avmb1/capidrv.c +++ b/drivers/isdn/avmb1/capidrv.c @@ -1,11 +1,42 @@ /* - * $Id: capidrv.c,v 1.3 1997/05/18 09:24:15 calle Exp $ + * $Id: capidrv.c,v 1.11 1998/02/13 07:09:15 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.11 1998/02/13 07:09:15 calle + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.10 1998/02/02 19:52:23 calle + * Fixed vbox (audio) acceptb. + * + * Revision 1.9 1998/01/31 11:14:45 calle + * merged changes to 2.0 tree, prepare 2.1.82 to work. + * + * Revision 1.8 1997/11/04 06:12:09 calle + * capi.c: new read/write in file_ops since 2.1.60 + * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. + * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON) + * compat.h: added #define LinuxVersionCode + * + * Revision 1.7 1997/10/11 10:36:34 calle + * Added isdnlog support. patch to isdnlog needed. + * + * Revision 1.6 1997/10/11 10:25:55 calle + * New interface for lowlevel drivers. BSENT with nr. of bytes sent, + * allow sending without ACK. + * + * Revision 1.5 1997/10/01 09:21:16 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.4 1997/07/13 12:22:43 calle + * bug fix for more than one controller in connect_req. + * debugoutput now with contrnr. + * * Revision 1.3 1997/05/18 09:24:15 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -48,13 +79,11 @@ #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.3 $"; +static char *revision = "$Revision: 1.11 $"; int debugmode = 0; -#ifdef HAS_NEW_SYMTAB MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); MODULE_PARM(debugmode, "i"); -#endif /* -------- type definitions ----------------------------------------- */ @@ -116,14 +145,26 @@ struct capidrv_contr { int oldstate; /* */ __u16 datahandle; + struct ncci_datahandle_queue { + struct ncci_datahandle_queue *next; + __u16 datahandle; + int len; + } *ackqueue; } *ncci_list; } *plcip; struct capidrv_ncci *nccip; } *bchans; struct capidrv_plci *plci_list; + + /* for q931 data */ + __u8 q931_buf[4096]; + __u8 *q931_read; + __u8 *q931_write; + __u8 *q931_end; }; + struct capidrv_data { __u16 appid; int ncontr; @@ -141,6 +182,9 @@ typedef struct capidrv_bchan capidrv_bchan; static capidrv_data global; static struct capi_interface *capifuncs; +static void handle_dtrace_data(capidrv_contr *card, + int send, int level2, __u8 *data, __u16 len); + /* -------- convert functions ---------------------------------------- */ static inline __u32 b1prot(int l2, int l3) @@ -167,9 +211,8 @@ static inline __u32 b2prot(int l2, int l3) default: return 0; case ISDN_PROTO_L2_HDLC: - return 1; case ISDN_PROTO_L2_TRANS: - return 0; + return 1; } } @@ -410,6 +453,42 @@ static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip) kfree(nccip); } +static int capidrv_add_ack(struct capidrv_ncci *nccip, + __u16 datahandle, int len) +{ + struct ncci_datahandle_queue *n, **pp; + + n = (struct ncci_datahandle_queue *) + kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC); + if (!n) { + printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n"); + return -1; + } + n->next = 0; + n->datahandle = datahandle; + n->len = len; + for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ; + *pp = n; + return 0; +} + +static int capidrv_del_ack(struct capidrv_ncci *nccip, __u16 datahandle) +{ + struct ncci_datahandle_queue **pp, *p; + int len; + + for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) { + if ((*pp)->datahandle == datahandle) { + p = *pp; + len = p->len; + *pp = (*pp)->next; + kfree(p); + return len; + } + } + return -1; +} + /* -------- convert and send capi message ---------------------------- */ static void send_message(capidrv_contr * card, _cmsg * cmsg) @@ -419,7 +498,6 @@ static void send_message(capidrv_contr * card, _cmsg * cmsg) capi_cmsg2message(cmsg, cmsg->buf); len = CAPIMSG_LEN(cmsg->buf); skb = dev_alloc_skb(len); - SET_SKB_FREE(skb); memcpy(skb_put(skb, len), cmsg->buf, len); (*capifuncs->capi_put_message) (global.appid, skb); } @@ -686,8 +764,53 @@ static void handle_controller(_cmsg * cmsg) break; case CAPI_MANUFACTURER_IND: /* Controller */ + if ( cmsg->ManuID == 0x214D5641 + && cmsg->Class == 0 + && cmsg->Function == 1) { + __u8 *data = cmsg->ManuData+3; + __u16 len = cmsg->ManuData[0]; + __u16 layer; + int direction; + if (len == 255) { + len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8)); + data += 2; + } + len -= 2; + layer = ((*(data-1)) << 8) | *(data-2); + if (layer & 0x300) + direction = (layer & 0x200) ? 0 : 1; + else direction = (layer & 0x800) ? 0 : 1; + if (layer & 0x0C00) { + if ((layer & 0xff) == 0x80) { + handle_dtrace_data(card, direction, 1, data, len); + break; + } + } else if ((layer & 0xff) < 0x80) { + handle_dtrace_data(card, direction, 0, data, len); + break; + } + printk(KERN_INFO "capidrv: %s from controller 0x%x layer 0x%x, ignored\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController, layer); + break; + } goto ignored; case CAPI_MANUFACTURER_CONF: /* Controller */ + if (cmsg->ManuID == 0x214D5641) { + char *s = 0; + switch (cmsg->Class) { + case 0: break; + case 1: s = "unknown class"; break; + case 2: s = "unknown function"; break; + default: s = "unkown error"; break; + } + if (s) + printk(KERN_INFO "capidrv: %s from controller 0x%x function %d: %s\n", + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController, + cmsg->Function, s); + break; + } goto ignored; case CAPI_FACILITY_IND: /* Controller/plci/ncci */ goto ignored; @@ -996,6 +1119,7 @@ static void handle_ncci(_cmsg * cmsg) capidrv_plci *plcip; capidrv_ncci *nccip; isdn_ctrl cmd; + int len; if (!card) { printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", @@ -1093,11 +1217,14 @@ static void handle_ncci(_cmsg * cmsg) if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) goto notfound; - cmd.command = ISDN_STAT_BSENT; - cmd.driver = card->myid; - cmd.arg = nccip->chan; - card->interface.statcallb(&cmd); - + len = capidrv_del_ack(nccip, cmsg->DataHandle); + if (len < 0) + break; + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = nccip->chan; + cmd.parm.length = len; + card->interface.statcallb(&cmd); break; case CAPI_DISCONNECT_B3_IND: /* ncci */ @@ -1206,6 +1333,60 @@ static void capidrv_signal(__u16 applid, __u32 dummy) /* ------------------------------------------------------------------- */ +#define PUTBYTE_TO_STATUS(card, byte) \ + do { \ + *(card)->q931_write++ = (byte); \ + if ((card)->q931_write > (card)->q931_end) \ + (card)->q931_write = (card)->q931_buf; \ + } while (0) + +static void handle_dtrace_data(capidrv_contr *card, + int send, int level2, __u8 *data, __u16 len) +{ + long flags; + __u8 *p, *end; + isdn_ctrl cmd; + + if (!len) { + printk(KERN_DEBUG "avmb1_q931_data: len == %d\n", len); + return; + } + + save_flags(flags); + cli(); + + if (level2) { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '2'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } else { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '3'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } + + for (p = data, end = data+len; p < end; p++) { + __u8 w; + PUTBYTE_TO_STATUS(card, ' '); + w = (*p >> 4) & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + w = *p & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + } + PUTBYTE_TO_STATUS(card, '\n'); + + restore_flags(flags); + + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = len*3+5; + card->interface.statcallb(&cmd); +} + +/* ------------------------------------------------------------------- */ + static _cmsg cmdcmsg; static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) @@ -1270,7 +1451,7 @@ static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) capi_fill_CONNECT_REQ(&cmdcmsg, global.appid, card->msgid++, - 1, /* adr */ + card->contrnr, /* adr */ si2cip(bchan->si1, bchan->si2), /* cipvalue */ called, /* CalledPartyNumber */ calling, /* CallingPartyNumber */ @@ -1471,7 +1652,7 @@ static int if_command(isdn_ctrl * c) static _cmsg sendcmsg; -static int if_sendbuf(int id, int channel, struct sk_buff *skb) +static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) { capidrv_contr *card = findcontrbydriverid(id); capidrv_bchan *bchan; @@ -1479,6 +1660,7 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb) int len = skb->len; size_t msglen; __u16 errcode; + __u16 datahandle; if (!card) { printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n", @@ -1492,51 +1674,128 @@ static int if_sendbuf(int id, int channel, struct sk_buff *skb) card->name, channel); return 0; } + datahandle = nccip->datahandle; capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++, nccip->ncci, /* adr */ (__u32) skb->data, /* Data */ skb->len, /* DataLength */ - nccip->datahandle++, /* DataHandle */ + datahandle, /* DataHandle */ 0 /* Flags */ ); + + if (capidrv_add_ack(nccip, datahandle, doack ? skb->len : -1) < 0) + return 0; + capi_cmsg2message(&sendcmsg, sendcmsg.buf); msglen = CAPIMSG_LEN(sendcmsg.buf); if (skb_headroom(skb) < msglen) { struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len); if (!nskb) { printk(KERN_ERR "capidrv: if_sendbuf: no memory\n"); + (void)capidrv_del_ack(nccip, datahandle); return 0; } #if 0 printk(KERN_DEBUG "capidrv: only %d bytes headroom\n", skb_headroom(skb)); #endif - SET_SKB_FREE(nskb); memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen); memcpy(skb_put(nskb, skb->len), skb->data, skb->len); errcode = (*capifuncs->capi_put_message) (global.appid, nskb); - switch (errcode) { - case CAPI_NOERROR: + if (errcode == CAPI_NOERROR) { dev_kfree_skb(skb); + nccip->datahandle++; return len; - case CAPI_SENDQUEUEFULL: - dev_kfree_skb(nskb); - return 0; - default: - return -1; } + (void)capidrv_del_ack(nccip, datahandle); + dev_kfree_skb(nskb); + return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; } else { memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen); errcode = (*capifuncs->capi_put_message) (global.appid, skb); - switch (errcode) { - case CAPI_NOERROR: + if (errcode == CAPI_NOERROR) { + nccip->datahandle++; return len; - case CAPI_SENDQUEUEFULL: - return 0; - default: - return -1; } + (void)capidrv_del_ack(nccip, datahandle); + return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; + } +} + +static int if_readstat(__u8 *buf, int len, int user, int id, int channel) +{ + capidrv_contr *card = findcontrbydriverid(id); + int count; + __u8 *p; + + if (!card) { + printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n", + id); + return -ENODEV; + } + + for (p=buf, count=0; count < len; p++, count++) { + if (user) + put_user(*card->q931_read++, p); + else + *p = *card->q931_read++; + if (card->q931_read > card->q931_end) + card->q931_read = card->q931_buf; + } + return count; + +} + +static void enable_dchannel_trace(capidrv_contr *card) +{ + __u8 manufacturer[CAPI_MANUFACTURER_LEN]; + capi_version version; + __u16 contr = card->contrnr; + __u16 errcode; + __u16 avmversion[3]; + + errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n", + card->name, errcode); + return; + } + if (strstr(manufacturer, "AVM") == 0) { + printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n", + card->name); + return; + } + errcode = (*capifuncs->capi_get_version)(contr, &version); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get version (0x%x)\n", + card->name, errcode); + return; + } + avmversion[0] = (version.majormanuversion >> 4) & 0x0f; + avmversion[1] = (version.majormanuversion << 4) & 0xf0; + avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; + avmversion[2] |= version.minormanuversion & 0x0f; + + if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { + printk(KERN_INFO "%s: D2 trace enabled\n", card->name); + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\200\014\000\000"); + } else { + printk(KERN_INFO "%s: D3 trace enabled\n", card->name); + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\002\003\000\000"); } + send_message(card, &cmdcmsg); } static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) @@ -1568,7 +1827,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) card->interface.command = if_command; card->interface.writebuf_skb = if_sendbuf; card->interface.writecmd = 0; - card->interface.readstat = 0; + card->interface.readstat = if_readstat; card->interface.features = ISDN_FEATURE_L2_X75I | ISDN_FEATURE_L2_X75UI | ISDN_FEATURE_L2_X75BUI | @@ -1581,6 +1840,9 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) card->next = global.contr_list; global.contr_list = card; global.ncontr++; + card->q931_read = card->q931_buf; + card->q931_write = card->q931_buf; + card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1; if (!register_isdn(&card->interface)) { global.contr_list = global.contr_list->next; @@ -1600,7 +1862,7 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) cmd.command = ISDN_STAT_RUN; card->interface.statcallb(&cmd); - card->cipmask = 1; /* any */ + card->cipmask = 0x1FFF03FF; /* any */ card->cipmask2 = 0; capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, @@ -1616,6 +1878,8 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) printk(KERN_INFO "%s: now up (%d B channels)\n", card->name, card->nbchan); + enable_dchannel_trace(card); + return 0; } @@ -1694,11 +1958,6 @@ int capidrv_init(void) if (!capifuncs) return -EIO; -#ifndef HAS_NEW_SYMTAB - /* No symbols to export, hide all symbols */ - register_symtab(NULL); -#endif - if ((p = strchr(revision, ':'))) { strcpy(rev, p + 1); p = strchr(rev, '$'); diff --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c index 9eb60afed..8eb7d3ae1 100644 --- a/drivers/isdn/avmb1/capiutil.c +++ b/drivers/isdn/avmb1/capiutil.c @@ -1,5 +1,5 @@ /* - * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $ + * $Id: capiutil.c,v 1.6 1997/11/04 06:12:12 calle Exp $ * * CAPI 2.0 convert capi message to capi message struct * @@ -7,6 +7,20 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.c,v $ + * Revision 1.6 1997/11/04 06:12:12 calle + * capi.c: new read/write in file_ops since 2.1.60 + * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. + * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON) + * compat.h: added #define LinuxVersionCode + * + * Revision 1.5 1997/10/01 09:21:19 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.4 1997/08/10 07:43:55 calle + * forgot to export symbol capi_info2str for 2.1.x + * * Revision 1.3 1997/05/18 09:24:18 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -26,13 +40,13 @@ * */ #include <linux/module.h> -#include <linux/config.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/stddef.h> #include <linux/kernel.h> #include <linux/mm.h> #include <asm/segment.h> +#include <linux/config.h> #include "compat.h" #include "capiutil.h" @@ -936,35 +950,18 @@ char *capi_cmsg2str(_cmsg * cmsg) } -#ifdef HAS_NEW_SYMTAB EXPORT_SYMBOL(capi_cmsg2message); EXPORT_SYMBOL(capi_message2cmsg); EXPORT_SYMBOL(capi_cmsg_header); EXPORT_SYMBOL(capi_cmd2str); EXPORT_SYMBOL(capi_cmsg2str); EXPORT_SYMBOL(capi_message2str); -#else -static struct symbol_table capifunc_syms = -{ -#include <linux/symtab_begin.h> - X(capi_cmsg2message), - X(capi_message2cmsg), - X(capi_cmsg_header), - X(capi_cmd2str), - X(capi_cmsg2str), - X(capi_message2str), - X(capi_info2str), -#include <linux/symtab_end.h> -}; -#endif +EXPORT_SYMBOL(capi_info2str); #ifdef MODULE int init_module(void) { -#ifndef HAS_NEW_SYMTAB - register_symtab(&capifunc_syms); -#endif return 0; } diff --git a/drivers/isdn/avmb1/compat.h b/drivers/isdn/avmb1/compat.h index 551b20d60..41ad2b626 100644 --- a/drivers/isdn/avmb1/compat.h +++ b/drivers/isdn/avmb1/compat.h @@ -1,11 +1,22 @@ /* - * $Id: compat.h,v 1.1 1997/03/04 21:50:36 calle Exp $ + * $Id: compat.h,v 1.3 1997/11/04 06:12:15 calle Exp $ * * Headerfile for Compartibility between different kernel versions * * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: compat.h,v $ + * Revision 1.3 1997/11/04 06:12:15 calle + * capi.c: new read/write in file_ops since 2.1.60 + * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware. + * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON) + * compat.h: added #define LinuxVersionCode + * + * Revision 1.2 1997/10/01 09:21:22 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.1 1997/03/04 21:50:36 calle * Frirst version in isdn4linux * @@ -23,8 +34,8 @@ #include <linux/version.h> #include <linux/isdnif.h> -#if LINUX_VERSION_CODE >= 0x020112 /* 2.1.18 */ -#define HAS_NEW_SYMTAB +#ifndef LinuxVersionCode +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) #endif #endif /* __COMPAT_H__ */ diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index 2cc325b1f..a8e1f83ef 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -1,7 +1,14 @@ L_OBJS := M_OBJS := -O_OBJS := isdnl1.o config.o tei.o isdnl2.o isdnl3.o \ - q931.o callc.o fsm.o +LX_OBJS := +MX_OBJS := +O_OBJS := +OX_OBJS := +L_TARGET := +O_TARGET := + +O_OBJS := isdnl1.o tei.o isdnl2.o isdnl3.o \ + lmgr.o q931.o callc.o fsm.o # EXTRA_CFLAGS += -S @@ -17,31 +24,105 @@ ifeq ($(CONFIG_HISAX_1TR6),y) O_OBJS += l3_1tr6.o endif +ISAC_OBJ := +ARCOFI_OBJ := +HSCX_OBJ := +HFC_OBJ := +HFC_2BDS0 := +RAWHDLC_OBJ := + ifeq ($(CONFIG_HISAX_16_0),y) O_OBJS += teles0.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif ifeq ($(CONFIG_HISAX_16_3),y) O_OBJS += teles3.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif ifeq ($(CONFIG_HISAX_AVM_A1),y) O_OBJS += avm_a1.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif -ifeq ($(CONFIG_HISAX_ELSA_PCC),y) - O_OBJS += elsa.o -endif - -ifeq ($(CONFIG_HISAX_ELSA_PCMCIA),y) +ifeq ($(CONFIG_HISAX_ELSA),y) O_OBJS += elsa.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o + ARCOFI_OBJ := arcofi.o endif ifeq ($(CONFIG_HISAX_IX1MICROR2),y) O_OBJS += ix1_micro.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_DIEHLDIVA),y) + O_OBJS += diva.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_ASUSCOM),y) + O_OBJS += asuscom.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_TELEINT),y) + O_OBJS += teleint.o + ISAC_OBJ := isac.o + HFC_OBJ := hfc_2bs0.o +endif + +ifeq ($(CONFIG_HISAX_SEDLBAUER),y) + O_OBJS += sedlbauer.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif +ifeq ($(CONFIG_HISAX_SPORTSTER),y) + O_OBJS += sportster.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_MIC),y) + O_OBJS += mic.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_NETJET),y) + O_OBJS += netjet.o + ISAC_OBJ := isac.o +endif + +ifeq ($(CONFIG_HISAX_TELES3C),y) + O_OBJS += teles3c.o + HFC_2BDS0 := hfc_2bds0.o +endif +ifeq ($(CONFIG_HISAX_AMD7930),y) + O_OBJS += amd7930.o + RAWHDLC_OBJ := rawhdlc.o +endif + +ifeq ($(CONFIG_HISAX_NICCY),y) + O_OBJS += niccy.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(HFC_OBJ) $(ARCOFI_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ) +OX_OBJS += config.o + O_TARGET := + ifeq ($(CONFIG_ISDN_DRV_HISAX),y) O_TARGET += hisax.o else diff --git a/drivers/isdn/hisax/amd7930.c b/drivers/isdn/hisax/amd7930.c new file mode 100644 index 000000000..202599948 --- /dev/null +++ b/drivers/isdn/hisax/amd7930.c @@ -0,0 +1,768 @@ +/* $Id: amd7930.c,v 1.2 1998/02/12 23:07:10 keil Exp $ + * + * HiSax ISDN driver - chip specific routines for AMD 7930 + * + * Author Brent Baccala (baccala@FreeSoft.org) + * + * + * + * $Log: amd7930.c,v $ + * Revision 1.2 1998/02/12 23:07:10 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.1 1998/02/03 23:20:51 keil + * New files for SPARC isdn support + * + * Revision 1.1 1998/01/08 04:17:12 baccala + * ISDN comes to the Sparc. Key points: + * + * - Existing ISDN HiSax driver provides all the smarts + * - it compiles, runs, talks to an isolated phone switch, connects + * to a Cisco, pings go through + * - AMD 7930 support only (no DBRI yet) + * - no US NI-1 support (may not work on US phone system - untested) + * - periodic packet loss, apparently due to lost interrupts + * - ISDN sometimes freezes, requiring reboot before it will work again + * + * The code is unreliable enough to be consider alpha + * + * + * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the + * SparcStation 1+. The chip provides microphone and speaker interfaces + * which provide mono-channel audio at 8K samples per second via either + * 8-bit A-law or 8-bit mu-law encoding. Also, the chip features an + * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface, + * which performs basic D channel LAPD processing and provides raw + * B channel data. The digital audio channel, the two ISDN B channels, + * and two 64 Kbps channels to the microprocessor are all interconnected + * via a multiplexer. + * + * This driver interfaces to the Linux HiSax ISDN driver, which performs + * all high-level Q.921 and Q.931 ISDN functions. The file is not + * itself a hardware driver; rather it uses functions exported by + * the AMD7930 driver in the sparcaudio subsystem (drivers/sbus/audio), + * allowing the chip to be simultaneously used for both audio and ISDN data. + * The hardware driver does _no_ buffering, but provides several callbacks + * which are called during interrupt service and should therefore run quickly. + * + * D channel transmission is performed by passing the hardware driver the + * address and size of an skb's data area, then waiting for a callback + * to signal successful transmission of the packet. A task is then + * queued to notify the HiSax driver that another packet may be transmitted. + * + * D channel reception is quite simple, mainly because of: + * 1) the slow speed of the D channel - 16 kbps, and + * 2) the presence of an 8- or 32-byte (depending on chip version) FIFO + * to buffer the D channel data on the chip + * Worst case scenario of back-to-back packets with the 8 byte buffer + * at 16 kbps yields an service time of 4 ms - long enough to preclude + * the need for fancy buffering. We queue a background task that copies + * data out of the receive buffer into an skb, and the hardware driver + * simply does nothing until we're done with the receive buffer and + * reset it for a new packet. + * + * B channel processing is more complex, because of: + * 1) the faster speed - 64 kbps, + * 2) the lack of any on-chip buffering (it interrupts for every byte), and + * 3) the lack of any chip support for HDLC encapsulation + * + * The HiSax driver can put each B channel into one of three modes - + * L1_MODE_NULL (channel disabled), L1_MODE_TRANS (transparent data relay), + * and L1_MODE_HDLC (HDLC encapsulation by low-level driver). + * L1_MODE_HDLC is the most common, used for almost all "pure" digital + * data sessions. L1_MODE_TRANS is used for ISDN audio. + * + * HDLC B channel transmission is performed via a large buffer into + * which the skb is copied while performing HDLC bit-stuffing. A CRC + * is computed and attached to the end of the buffer, which is then + * passed to the low-level routines for raw transmission. Once + * transmission is complete, the hardware driver is set to enter HDLC + * idle by successive transmission of mark (all 1) bytes, waiting for + * the ISDN driver to prepare another packet for transmission and + * deliver it. + * + * HDLC B channel reception is performed via an X-byte ring buffer + * divided into N sections of X/N bytes each. Defaults: X=256 bytes, N=4. + * As the hardware driver notifies us that each section is full, we + * hand it the next section and schedule a background task to peruse + * the received section, bit-by-bit, with an HDLC decoder. As + * packets are detected, they are copied into a large buffer while + * decoding HDLC bit-stuffing. The ending CRC is verified, and if + * it is correct, we alloc a new skb of the correct length (which we + * now know), copy the packet into it, and hand it to the upper layers. + * Optimization: for large packets, we hand the buffer (which also + * happens to be an skb) directly to the upper layer after an skb_trim, + * and alloc a new large buffer for future packets, thus avoiding a copy. + * Then we return to HDLC processing; state is saved between calls. + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "../../sbus/audio/amd7930.h" +#include "isac.h" +#include "isdnl1.h" +#include "rawhdlc.h" +#include <linux/interrupt.h> + +static const char *amd7930_revision = "$Revision: 1.2 $"; + +#define RCV_BUFSIZE 1024 /* Size of raw receive buffer in bytes */ +#define RCV_BUFBLKS 4 /* Number of blocks to divide buffer into + * (must divide RCV_BUFSIZE) */ + +static void Bchan_fill_fifo(struct BCState *, struct sk_buff *); + +static void +Bchan_xmt_bh(struct BCState *bcs) +{ + struct sk_buff *skb; + + if (bcs->hw.amd7930.tx_skb != NULL) { + dev_kfree_skb(bcs->hw.amd7930.tx_skb); + bcs->hw.amd7930.tx_skb = NULL; + } + + if ((skb = skb_dequeue(&bcs->squeue))) { + Bchan_fill_fifo(bcs, skb); + } else { + clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event |= 1 << B_XMTBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } +} + +static void +Bchan_xmit_callback(struct BCState *bcs) +{ + queue_task(&bcs->hw.amd7930.tq_xmt, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/* B channel transmission: two modes (three, if you count L1_MODE_NULL) + * + * L1_MODE_HDLC - We need to do HDLC encapsulation before transmiting + * the packet (i.e. make_raw_hdlc_data). Since this can be a + * time-consuming operation, our completion callback just schedules + * a bottom half to do encapsulation for the next packet. In between, + * the link will just idle + * + * L1_MODE_TRANS - Data goes through, well, transparent. No HDLC encap, + * and we can't just let the link idle, so the "bottom half" actually + * gets called during the top half (it's our callback routine in this case), + * but it's a lot faster now since we don't call make_raw_hdlc_data + */ + +static void +Bchan_fill_fifo(struct BCState *bcs, struct sk_buff *skb) +{ + struct IsdnCardState *cs = bcs->cs; + int len; + + if ((cs->debug & L1_DEB_HSCX) || (cs->debug & L1_DEB_HSCX_FIFO)) { + char tmp[1024]; + char *t = tmp; + + t += sprintf(t, "amd7930_fill_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', skb->len); + if (cs->debug & L1_DEB_HSCX_FIFO) + QuickHex(t, skb->data, skb->len); + debugl1(cs, tmp); + } + + if (bcs->mode == L1_MODE_HDLC) { + len = make_raw_hdlc_data(skb->data, skb->len, + bcs->hw.amd7930.tx_buff, RAW_BUFMAX); + if (len > 0) + amd7930_bxmit(0, bcs->channel, + bcs->hw.amd7930.tx_buff, len, + (void *) &Bchan_xmit_callback, + (void *) bcs); + dev_kfree_skb(skb); + } else if (bcs->mode == L1_MODE_TRANS) { + amd7930_bxmit(0, bcs->channel, + bcs->hw.amd7930.tx_buff, skb->len, + (void *) &Bchan_xmt_bh, + (void *) bcs); + bcs->hw.amd7930.tx_skb = skb; + } else { + dev_kfree_skb(skb); + } +} + +static void +Bchan_mode(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "AMD 7930 mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; +} + +/* Bchan_l2l1 is the entry point for upper layer routines that want to + * transmit on the B channel. PH_DATA_REQ is a normal packet that + * we either start transmitting (if idle) or queue (if busy). + * PH_PULL_REQ can be called to request a callback message (PH_PULL_CNF) + * once the link is idle. After a "pull" callback, the upper layer + * routines can use PH_PULL_IND to send data. + */ + +static void +Bchan_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + + switch (pr) { + case (PH_DATA_REQ): + if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + } else { + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + Bchan_fill_fifo(st->l1.bcs, skb); + } + break; + case (PH_PULL_IND): + if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + printk(KERN_WARNING "amd7930: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + Bchan_fill_fifo(st->l1.bcs, skb); + break; + case (PH_PULL_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) { + clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +/* Receiver callback and bottom half - decodes HDLC at leisure (if + * L1_MODE_HDLC) and passes newly received skb on via bcs->rqueue. If + * a large packet is received, stick rv_skb (the buffer that the + * packet has been decoded into) on the receive queue and alloc a new + * (large) skb to act as buffer for future receives. If a small + * packet is received, leave rv_skb alone, alloc a new skb of the + * correct size, and copy the packet into it + */ + +static void +Bchan_recv_callback(struct BCState *bcs) +{ + struct amd7930_hw *hw = &bcs->hw.amd7930; + + hw->rv_buff_in += RCV_BUFSIZE/RCV_BUFBLKS; + hw->rv_buff_in %= RCV_BUFSIZE; + + if (hw->rv_buff_in != hw->rv_buff_out) { + amd7930_brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, + RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, (void *) bcs); + } + + queue_task(&hw->tq_rcv, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +Bchan_rcv_bh(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + struct amd7930_hw *hw = &bcs->hw.amd7930; + struct sk_buff *skb; + int len; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[1024]; + + sprintf(tmp, "amd7930_Bchan_rcv (%d/%d)", + hw->rv_buff_in, hw->rv_buff_out); + debugl1(cs, tmp); + QuickHex(tmp, hw->rv_buff + hw->rv_buff_out, + RCV_BUFSIZE/RCV_BUFBLKS); + debugl1(cs, tmp); + } + + do { + if (bcs->mode == L1_MODE_HDLC) { + while ((len = read_raw_hdlc_data(hw->hdlc_state, + hw->rv_buff + hw->rv_buff_out, RCV_BUFSIZE/RCV_BUFBLKS, + hw->rv_skb->tail, HSCX_BUFMAX))) { + if (len > 0 && (cs->debug & L1_DEB_HSCX_FIFO)) { + char tmp[1024]; + char *t = tmp; + + t += sprintf(t, "amd7930_Bchan_rcv %c cnt %d", bcs->channel ? 'B' : 'A', len); + QuickHex(t, hw->rv_skb->tail, len); + debugl1(cs, tmp); + } + + if (len > HSCX_BUFMAX/2) { + /* Large packet received */ + + if (!(skb = dev_alloc_skb(HSCX_BUFMAX))) { + printk(KERN_WARNING "amd7930: receive out of memory"); + } else { + skb_put(hw->rv_skb, len); + skb_queue_tail(&bcs->rqueue, hw->rv_skb); + hw->rv_skb = skb; + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + } + } else if (len > 0) { + /* Small packet received */ + + if (!(skb = dev_alloc_skb(len))) { + printk(KERN_WARNING "amd7930: receive out of memory\n"); + } else { + memcpy(skb_put(skb, len), hw->rv_skb->tail, len); + skb_queue_tail(&bcs->rqueue, skb); + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } else { + /* Reception Error */ + /* printk("amd7930: B channel receive error\n"); */ + } + } + } else if (bcs->mode == L1_MODE_TRANS) { + if (!(skb = dev_alloc_skb(RCV_BUFSIZE/RCV_BUFBLKS))) { + printk(KERN_WARNING "amd7930: receive out of memory\n"); + } else { + memcpy(skb_put(skb, RCV_BUFSIZE/RCV_BUFBLKS), + hw->rv_buff + hw->rv_buff_out, + RCV_BUFSIZE/RCV_BUFBLKS); + skb_queue_tail(&bcs->rqueue, skb); + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + + if (hw->rv_buff_in == hw->rv_buff_out) { + /* Buffer was filled up - need to restart receiver */ + amd7930_brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, + RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, + (void *) bcs); + } + + hw->rv_buff_out += RCV_BUFSIZE/RCV_BUFBLKS; + hw->rv_buff_out %= RCV_BUFSIZE; + + } while (hw->rv_buff_in != hw->rv_buff_out); +} + +static void +Bchan_close(struct BCState *bcs) +{ + struct sk_buff *skb; + + Bchan_mode(bcs, 0, 0); + amd7930_bclose(0, bcs->channel); + + if (test_bit(BC_FLG_INIT, &bcs->Flag)) { + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + } + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); +} + +static int +Bchan_open(struct BCState *bcs) +{ + struct amd7930_hw *hw = &bcs->hw.amd7930; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + + amd7930_bopen(0, bcs->channel, 0xff); + hw->rv_buff_in = 0; + hw->rv_buff_out = 0; + hw->tx_skb = NULL; + init_hdlc_state(hw->hdlc_state, 0); + amd7930_brecv(0, bcs->channel, + hw->rv_buff + hw->rv_buff_in, RCV_BUFSIZE/RCV_BUFBLKS, + (void *) &Bchan_recv_callback, (void *) bcs); + + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +Bchan_init(struct BCState *bcs) +{ + if (!(bcs->hw.amd7930.tx_buff = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for amd7930.tx_buff\n"); + return; + } + if (!(bcs->hw.amd7930.rv_buff = kmalloc(RCV_BUFSIZE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for amd7930.rv_buff\n"); + return; + } + if (!(bcs->hw.amd7930.rv_skb = dev_alloc_skb(HSCX_BUFMAX))) { + printk(KERN_WARNING + "HiSax: No memory for amd7930.rv_skb\n"); + return; + } + if (!(bcs->hw.amd7930.hdlc_state = kmalloc(sizeof(struct hdlc_state), + GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for amd7930.hdlc_state\n"); + return; + } + + bcs->hw.amd7930.tq_rcv.sync = 0; + bcs->hw.amd7930.tq_rcv.routine = (void (*)(void *)) &Bchan_rcv_bh; + bcs->hw.amd7930.tq_rcv.data = (void *) bcs; + + bcs->hw.amd7930.tq_xmt.sync = 0; + bcs->hw.amd7930.tq_xmt.routine = (void (*)(void *)) &Bchan_xmt_bh; + bcs->hw.amd7930.tq_xmt.data = (void *) bcs; +} + +static void +Bchan_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + Bchan_mode(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + Bchan_mode(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_amd7930(struct PStack *st, struct BCState *bcs) +{ + if (Bchan_open(bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = Bchan_l2l1; + st->ma.manl1 = Bchan_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + + +static void +amd7930_drecv_callback(void *arg, int error, unsigned int count) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) arg; + static struct tq_struct task; + struct sk_buff *skb; + + /* NOTE: This function is called directly from an interrupt handler */ + + if (1) { + if (!(skb = alloc_skb(count, GFP_ATOMIC))) + printk(KERN_WARNING "HiSax: D receive out of memory\n"); + else { + memcpy(skb_put(skb, count), cs->rcvbuf, count); + skb_queue_tail(&cs->rq, skb); + } + + task.routine = (void *) DChannel_proc_rcv; + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "amd7930 Drecv cnt %d", count); + if (error) t += sprintf(t, " ERR %x", error); + QuickHex(t, cs->rcvbuf, count); + debugl1(cs, tmp); + } + + amd7930_drecv(0, cs->rcvbuf, MAX_DFRAME_LEN, + &amd7930_drecv_callback, cs); +} + +static void +amd7930_dxmit_callback(void *arg, int error) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) arg; + static struct tq_struct task; + + /* NOTE: This function is called directly from an interrupt handler */ + + /* may wish to do retransmission here, if error indicates collision */ + + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "amd7930 Dxmit cnt %d", cs->tx_skb->len); + if (error) t += sprintf(t, " ERR %x", error); + QuickHex(t, cs->tx_skb->data, cs->tx_skb->len); + debugl1(cs, tmp); + } + + cs->tx_skb = NULL; + + task.routine = (void *) DChannel_proc_xmt; + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +amd7930_Dchan_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + char str[64]; + + switch (pr) { + case (PH_DATA_REQ): + 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 { + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { + /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data+4, skb->len-4, + str); + } + 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 + amd7930_dxmit(0, skb->data, skb->len, + &amd7930_dxmit_callback, cs); + } + break; + case (PH_PULL_IND): + 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->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + 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 + amd7930_dxmit(0, cs->tx_skb->data, cs->tx_skb->len, + &amd7930_dxmit_callback, cs); + break; + case (PH_PULL_REQ): +#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_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +int +setDstack_amd7930(struct PStack *st, struct IsdnCardState *cs) +{ + st->l2.l2l1 = amd7930_Dchan_l2l1; + if (! cs->rcvbuf) { + printk("setDstack_amd7930: No cs->rcvbuf!\n"); + } else { + amd7930_drecv(0, cs->rcvbuf, MAX_DFRAME_LEN, + &amd7930_drecv_callback, cs); + } + return (0); +} + +static void +manl1_msg(struct IsdnCardState *cs, int msg, void *arg) { + struct PStack *st; + + st = cs->stlist; + while (st) { + st->ma.manl1(st, msg, arg); + st = st->next; + } +} + +static void +amd7930_new_ph(struct IsdnCardState *cs) +{ + switch (amd7930_get_liu_state(0)) { + case 3: + manl1_msg(cs, PH_POWERUP_CNF, NULL); + break; + + case 7: + manl1_msg(cs, PH_I4_P8_IND, NULL); + break; + + case 8: + manl1_msg(cs, PH_RSYNC_IND, NULL); + break; + } +} + +/* amd7930 LIU state change callback */ + +static void +amd7930_liu_callback(struct IsdnCardState *cs) +{ + static struct tq_struct task; + + if (!cs) + return; + + if (cs->debug & L1_DEB_ISAC) { + char tmp[32]; + sprintf(tmp, "amd7930_liu state %d", amd7930_get_liu_state(0)); + debugl1(cs, tmp); + } + + task.sync = 0; + task.routine = (void *) &amd7930_new_ph; + task.data = (void *) cs; + queue_task(&task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +amd7930_l1cmd(struct IsdnCardState *cs, int msg, void *arg) +{ + u_char val; + char tmp[32]; + + if (cs->debug & L1_DEB_ISAC) { + char tmp[32]; + sprintf(tmp, "amd7930_l1cmd msg %x", msg); + debugl1(cs, tmp); + } + + switch(msg) { + case PH_RESET_REQ: + if (amd7930_get_liu_state(0) <= 3) + amd7930_liu_activate(0,0); + else + amd7930_liu_deactivate(0); + break; + case PH_ENABLE_REQ: + break; + case PH_INFO3_REQ: + amd7930_liu_activate(0,0); + break; + case PH_TESTLOOP_REQ: + break; + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "amd7930_l1cmd unknown %4x", msg); + debugl1(cs, tmp); + } + break; + } +} + +static void init_amd7930(struct IsdnCardState *cs) +{ + Bchan_init(&cs->bcs[0]); + Bchan_init(&cs->bcs[1]); + cs->bcs[0].BC_SetStack = setstack_amd7930; + cs->bcs[1].BC_SetStack = setstack_amd7930; + cs->bcs[0].BC_Close = Bchan_close; + cs->bcs[1].BC_Close = Bchan_close; + Bchan_mode(cs->bcs, 0, 0); + Bchan_mode(cs->bcs + 1, 0, 0); +} + +void +release_amd7930(struct IsdnCardState *cs) +{ +} + +static int +amd7930_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_amd7930(cs); + return(0); + case CARD_SETIRQ: + return(0); + case CARD_INIT: + cs->l1cmd = amd7930_l1cmd; + amd7930_liu_init(0, &amd7930_liu_callback, (void *)cs); + init_amd7930(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_amd7930(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, amd7930_revision); + printk(KERN_INFO "HiSax: AMD7930 driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_AMD7930) + return (0); + + cs->irq = amd7930_get_irqnum(0); + if (cs->irq == 0) + return (0); + + cs->cardmsg = &amd7930_card_msg; + + return (1); +} diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c new file mode 100644 index 000000000..dad1711c3 --- /dev/null +++ b/drivers/isdn/hisax/arcofi.c @@ -0,0 +1,51 @@ +/* $Id: arcofi.c,v 1.1 1997/10/29 18:51:20 keil Exp $ + + * arcofi.h Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * + * $Log: arcofi.c,v $ + * Revision 1.1 1997/10/29 18:51:20 keil + * New files + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isdnl1.h" +#include "isac.h" + +int +send_arcofi(struct IsdnCardState *cs, const u_char *msg) { + u_char val; + char tmp[32]; + long flags; + int cnt=2; + + cs->mon_txp = 0; + cs->mon_txc = msg[0]; + memcpy(cs->mon_tx, &msg[1], cs->mon_txc); + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags); + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + val = cs->readisac(cs, ISAC_MOSR); + cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]); + cs->mocr |= 0x10; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + save_flags(flags); + sti(); + while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) { + cnt--; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + } + restore_flags(flags); + sprintf(tmp, "arcofi tout %d", cnt); + debugl1(cs, tmp); + return(cnt); +} + diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h new file mode 100644 index 000000000..5e1bb9e99 --- /dev/null +++ b/drivers/isdn/hisax/arcofi.h @@ -0,0 +1,17 @@ +/* $Id: arcofi.h,v 1.1 1997/10/29 18:51:20 keil Exp $ + + * arcofi.h Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * + * $Log: arcofi.h,v $ + * Revision 1.1 1997/10/29 18:51:20 keil + * New files + * + */ + +#define ARCOFI_USE 1 + +extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg); diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c new file mode 100644 index 000000000..c84917446 --- /dev/null +++ b/drivers/isdn/hisax/asuscom.c @@ -0,0 +1,292 @@ +/* $Id: asuscom.c,v 1.2 1998/02/02 13:27:06 keil Exp $ + + * asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to ASUSCOM NETWORK INC. Taiwan and Dynalink NL for informations + * + * + * $Log: asuscom.c,v $ + * Revision 1.2 1998/02/02 13:27:06 keil + * New + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *Asuscom_revision = "$Revision: 1.2 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ASUS_ISAC 0 +#define ASUS_HSCX 1 +#define ASUS_ADR 2 +#define ASUS_CTRL_U7 3 +#define ASUS_CTRL_POTS 5 + +/* CARD_ADR (Write) */ +#define ASUS_RESET 0x80 /* Bit 7 Reset-Leitung */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.asus.adr, + cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, + cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \ + cs->hw.asus.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \ + cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \ + cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \ + cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_asuscom(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.asus.cfg_reg) + release_region(cs->hw.asus.cfg_reg, bytecnt); +} + +static void +reset_asuscom(struct IsdnCardState *cs) +{ + long flags; + + byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + byteout(cs->hw.asus.adr, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_asuscom(cs); + return(0); + case CARD_RELEASE: + release_io_asuscom(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &asuscom_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_asuscom(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Asuscom_revision); + printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_ASUSCOM) + return (0); + + bytecnt = 8; + cs->hw.asus.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR; + cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC; + cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX; + cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7; + cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS; + + if (check_region((cs->hw.asus.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.asus.cfg_reg, + cs->hw.asus.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn"); + } + + printk(KERN_INFO + "ISDNLink: defined at 0x%x IRQ %d\n", + cs->hw.asus.cfg_reg, + cs->irq); + printk(KERN_INFO "ISDNLink: resetting card\n"); + reset_asuscom(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Asus_card_msg; + ISACVersion(cs, "ISDNLink:"); + if (HscxVersion(cs, "ISDNLink:")) { + printk(KERN_WARNING + "ISDNLink: wrong HSCX versions check IO address\n"); + release_io_asuscom(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c index e6c775546..464bc33fd 100644 --- a/drivers/isdn/hisax/avm_a1.c +++ b/drivers/isdn/hisax/avm_a1.c @@ -1,4 +1,4 @@ -/* $Id: avm_a1.c,v 1.6 1997/04/13 19:54:07 keil Exp $ +/* $Id: avm_a1.c,v 2.7 1998/02/02 13:29:37 keil Exp $ * avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards * @@ -6,6 +6,30 @@ * * * $Log: avm_a1.c,v $ + * Revision 2.7 1998/02/02 13:29:37 keil + * fast io + * + * Revision 2.6 1998/01/13 23:09:46 keil + * really disable timer + * + * Revision 2.5 1998/01/02 06:50:29 calle + * Perodic timer of A1 now disabled, no need for linux driver. + * + * Revision 2.4 1997/11/08 21:35:42 keil + * new l1 init + * + * Revision 2.3 1997/11/06 17:13:32 keil + * New 2.1 init code + * + * Revision 2.2 1997/10/29 18:55:48 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 2.1 1997/07/27 21:47:13 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:48 keil + * New Layer and card interface + * * Revision 1.6 1997/04/13 19:54:07 keil * Change in IRQ check delay for SMP * @@ -27,17 +51,20 @@ * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "avm_a1.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include <linux/kernel_stat.h> extern const char *CardType[]; -const char *avm_revision = "$Revision: 1.6 $"; +const char *avm_revision = "$Revision: 2.7 $"; + +#define AVM_A1_STAT_ISAC 0x01 +#define AVM_A1_STAT_HSCX 0x02 +#define AVM_A1_STAT_TIMER 0x04 -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readreg(unsigned int adr, u_char off) @@ -55,906 +82,303 @@ writereg(unsigned int adr, u_char off, u_char data) static inline void read_fifo(unsigned int adr, u_char * data, int size) { - insb(adr - 0x400, data, size); + insb(adr, data, size); } static void write_fifo(unsigned int adr, u_char * data, int size) { - outsb(adr - 0x400, data, size); -} - -static inline void -waitforCEC(int adr) -{ - int to = 50; - - while ((readreg(adr, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "AVM A1: waitforCEC timeout\n"); + outsb(adr, data, size); } +/* Interface functions */ -static inline void -waitforXFW(int adr) -{ - int to = 50; - - while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "AVM A1: waitforXFW timeout\n"); -} - -static inline void -writehscxCMDR(int adr, u_char data) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr); - writereg(adr, HSCX_CMDR, data); - restore_flags(flags); + return (readreg(cs->hw.avm.isac, offset)); } -/* - * fast interrupt here - */ - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR)); + writereg(cs->hw.avm.isac, offset, value); } -void -avm_a1_report(struct IsdnCardState *sp) -{ - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); -} - -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + read_fifo(cs->hw.avm.isacfifo, data, size); } static void -hscx_fill_fifo(struct HscxState *hsp) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx]); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo(cs->hw.avm.isacfifo, data, size); } -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - } else { - count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + return (readreg(cs->hw.avm.hscx[hscx], offset)); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.avm.hscx[hscx], offset, value); } -static void -isac_fill_fifo(struct IsdnCardState *sp) -{ - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static void -ph_command(struct IsdnCardState *sp, unsigned int command) -{ - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CIX0, (command << 2) | 3); -} - - -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readreg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!exval & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writereg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = readreg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readreg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} - -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ - - u_char exval; - struct HscxState *hsp; - char tmp[32]; +/* + * fast interrupt HSCX stuff goes here + */ +#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt) - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readreg(sp->hscx[1], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readreg(sp->hscx[0], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readreg(sp->hscx[0], HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, sval, stat = 0; char tmp[32]; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "AVM A1: Spurious interrupt!\n"); return; } - while (((sval = bytein(sp->cfg_reg)) & 0xf) != 0x7) { + while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) { if (!(sval & AVM_A1_STAT_TIMER)) { - byteout(sp->cfg_reg, 0x14); - byteout(sp->cfg_reg, 0x18); - sval = bytein(sp->cfg_reg); - } else if (sp->debug & L1_DEB_INTSTAT) { + byteout(cs->hw.avm.cfg_reg, 0x1E); + sval = bytein(cs->hw.avm.cfg_reg); + } else if (cs->debug & L1_DEB_INTSTAT) { sprintf(tmp, "avm IntStatus %x", sval); - debugl1(sp, tmp); + debugl1(cs, tmp); } if (!(sval & AVM_A1_STAT_HSCX)) { - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA); if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } } if (!(sval & AVM_A1_STAT_ISAC)) { - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.avm.isac, ISAC_ISTA); if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } } } if (stat & 1) { - writereg(sp->hscx[0], HSCX_MASK, 0xFF); - writereg(sp->hscx[1], HSCX_MASK, 0xFF); - writereg(sp->hscx[0], HSCX_MASK, 0x0); - writereg(sp->hscx[1], HSCX_MASK, 0x0); + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0); } if (stat & 2) { - writereg(sp->isac, ISAC_MASK, 0xFF); - writereg(sp->isac, ISAC_MASK, 0x0); + writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.avm.isac, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) -{ - unsigned int adr = sp->isac; - - /* 16.3 IOM 2 Mode */ - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_ADF2, 0x80); - writereg(adr, ISAC_SQXR, 0x2f); - writereg(adr, ISAC_SPCR, 0x0); - writereg(adr, ISAC_ADF1, 0x2); - writereg(adr, ISAC_STCR, 0x70); - writereg(adr, ISAC_MODE, 0xc9); - writereg(adr, ISAC_TIMR, 0x0); - writereg(adr, ISAC_ADF1, 0x0); - writereg(adr, ISAC_CMDR, 0x41); - writereg(adr, ISAC_CIX0, (1 << 2) | 3); - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writereg(sp->hscx[hscx], HSCX_CCR1, 0x85); - writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF); - writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF); - writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF); - writereg(sp->hscx[hscx], HSCX_XBCH, 0x0); - writereg(sp->hscx[hscx], HSCX_RLCR, 0x0); - - switch (mode) { - case (0): - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0xff); - writereg(sp->hscx[hscx], HSCX_TSAR, 0xff); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - writereg(sp->hscx[hscx], HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0xe4); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0x8c); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - } - writereg(sp->hscx[hscx], HSCX_ISTA, 0x00); -} - inline static void -release_ioregs(struct IsdnCard *card, int mask) +release_ioregs(struct IsdnCardState *cs, int mask) { - release_region(card->sp->cfg_reg, 8); + release_region(cs->hw.avm.cfg_reg, 8); if (mask & 1) - release_region(card->sp->isac, 32); + release_region(cs->hw.avm.isac + 32, 32); if (mask & 2) - release_region(card->sp->isac - 0x400, 1); + release_region(cs->hw.avm.isacfifo, 1); if (mask & 4) - release_region(card->sp->hscx[0], 32); + release_region(cs->hw.avm.hscx[0] + 32, 32); if (mask & 8) - release_region(card->sp->hscx[0] - 0x400, 1); + release_region(cs->hw.avm.hscxfifo[0], 1); if (mask & 0x10) - release_region(card->sp->hscx[1], 32); + release_region(cs->hw.avm.hscx[1] + 32, 32); if (mask & 0x20) - release_region(card->sp->hscx[1] - 0x400, 1); + release_region(cs->hw.avm.hscxfifo[1], 1); } -void -release_io_avm_a1(struct IsdnCard *card) +static int +AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - release_ioregs(card, 0x3f); + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_ioregs(cs, 0x3f); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &avm_a1_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -static void -clear_pending_ints(struct IsdnCardState *sp) +__initfunc(int +setup_avm_a1(struct IsdnCard *card)) { - int val; - char tmp[64]; - - val = readreg(sp->hscx[1], HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->hscx[1], HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readreg(sp->hscx[0], HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readreg(sp->hscx[0], HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[1], HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[0], HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->isac, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readreg(sp->isac, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_MASK, 0); - writereg(sp->isac, ISAC_CMDR, 0x41); -} - -int -initavm_a1(struct IsdnCardState *sp) -{ - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat_irqs(sp->irq); - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &avm_a1_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat_irqs(sp->irq) > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat_irqs(sp->irq)); - debugl1(sp, tmp); - if (kstat_irqs(sp->irq) == sp->counter) { - printk(KERN_WARNING - "AVM A1: IRQ(%d) getting no interrupts during init\n", - sp->irq); - free_irq(sp->irq, sp); - return (0); - } - } - return (ret); -} - -int -setup_avm_a1(struct IsdnCard *card) -{ - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; + u_char val; + struct IsdnCardState *cs = card->cs; long flags; char tmp[64]; strcpy(tmp, avm_revision); - printk(KERN_NOTICE "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); - if (sp->typ != ISDN_CTYPE_A1) + printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_A1) return (0); - sp->cfg_reg = card->para[1] + 0x1800; - sp->isac = card->para[1] + 0x1400; - sp->hscx[0] = card->para[1] + 0x400; - sp->hscx[1] = card->para[1] + 0xc00; - sp->irq = card->para[0]; - if (check_region((sp->cfg_reg), 8)) { + cs->hw.avm.cfg_reg = card->para[1] + 0x1800; + cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20; + cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20; + cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20; + cs->hw.avm.isacfifo = card->para[1] + 0x1000; + cs->hw.avm.hscxfifo[0] = card->para[1]; + cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800; + cs->irq = card->para[0]; + if (check_region((cs->hw.avm.cfg_reg), 8)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); + cs->hw.avm.cfg_reg, + cs->hw.avm.cfg_reg + 8); return (0); } else { - request_region(sp->cfg_reg, 8, "avm cfg"); + request_region(cs->hw.avm.cfg_reg, 8, "avm cfg"); } - if (check_region((sp->isac), 32)) { + if (check_region((cs->hw.avm.isac + 32), 32)) { printk(KERN_WARNING "HiSax: %s isac ports %x-%x already in use\n", - CardType[sp->typ], - sp->isac, - sp->isac + 32); - release_ioregs(card, 0); + CardType[cs->typ], + cs->hw.avm.isac + 32, + cs->hw.avm.isac + 64); + release_ioregs(cs, 0); return (0); } else { - request_region(sp->isac, 32, "HiSax isac"); + request_region(cs->hw.avm.isac + 32, 32, "HiSax isac"); } - if (check_region((sp->isac - 0x400), 1)) { + if (check_region((cs->hw.avm.isacfifo), 1)) { printk(KERN_WARNING "HiSax: %s isac fifo port %x already in use\n", - CardType[sp->typ], - sp->isac - 0x400); - release_ioregs(card, 1); + CardType[cs->typ], + cs->hw.avm.isacfifo); + release_ioregs(cs, 1); return (0); } else { - request_region(sp->isac - 0x400, 1, "HiSax isac fifo"); + request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo"); } - if (check_region((sp->hscx[0]), 32)) { + if (check_region((cs->hw.avm.hscx[0]) + 32, 32)) { printk(KERN_WARNING "HiSax: %s hscx A ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 32); - release_ioregs(card, 3); + CardType[cs->typ], + cs->hw.avm.hscx[0] + 32, + cs->hw.avm.hscx[0] + 64); + release_ioregs(cs, 3); return (0); } else { - request_region(sp->hscx[0], 32, "HiSax hscx A"); + request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A"); } - if (check_region((sp->hscx[0] - 0x400), 1)) { + if (check_region(cs->hw.avm.hscxfifo[0], 1)) { printk(KERN_WARNING "HiSax: %s hscx A fifo port %x already in use\n", - CardType[sp->typ], - sp->hscx[0] - 0x400); - release_ioregs(card, 7); + CardType[cs->typ], + cs->hw.avm.hscxfifo[0]); + release_ioregs(cs, 7); return (0); } else { - request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo"); + request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo"); } - if (check_region((sp->hscx[1]), 32)) { + if (check_region(cs->hw.avm.hscx[1] + 32, 32)) { printk(KERN_WARNING "HiSax: %s hscx B ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[1], - sp->hscx[1] + 32); - release_ioregs(card, 0xf); + CardType[cs->typ], + cs->hw.avm.hscx[1] + 32, + cs->hw.avm.hscx[1] + 64); + release_ioregs(cs, 0xf); return (0); } else { - request_region(sp->hscx[1], 32, "HiSax hscx B"); + request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B"); } - if (check_region((sp->hscx[1] - 0x400), 1)) { + if (check_region(cs->hw.avm.hscxfifo[1], 1)) { printk(KERN_WARNING "HiSax: %s hscx B fifo port %x already in use\n", - CardType[sp->typ], - sp->hscx[1] - 0x400); - release_ioregs(card, 0x1f); + CardType[cs->typ], + cs->hw.avm.hscxfifo[1]); + release_ioregs(cs, 0x1f); return (0); } else { - request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo"); + request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo"); } save_flags(flags); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); sti(); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x1); + byteout(cs->hw.avm.cfg_reg, 0x1); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); HZDELAY(HZ / 5 + 1); - val = sp->irq; + val = cs->irq; if (val == 9) val = 2; - byteout(sp->cfg_reg + 1, val); + byteout(cs->hw.avm.cfg_reg + 1, val); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); HZDELAY(HZ / 5 + 1); restore_flags(flags); - val = bytein(sp->cfg_reg); + val = bytein(cs->hw.avm.cfg_reg); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg, val); - val = bytein(sp->cfg_reg + 3); + cs->hw.avm.cfg_reg, val); + val = bytein(cs->hw.avm.cfg_reg + 3); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg + 3, val); - val = bytein(sp->cfg_reg + 2); + cs->hw.avm.cfg_reg + 3, val); + val = bytein(cs->hw.avm.cfg_reg + 2); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg + 2, val); - byteout(sp->cfg_reg, 0x14); - byteout(sp->cfg_reg, 0x18); - val = bytein(sp->cfg_reg); + cs->hw.avm.cfg_reg + 2, val); + byteout(cs->hw.avm.cfg_reg, 0x1E); + val = bytein(cs->hw.avm.cfg_reg); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg, val); - - printk(KERN_NOTICE - "HiSax: %s config irq:%d cfg:%x\n", - CardType[sp->typ], sp->irq, - sp->cfg_reg); - printk(KERN_NOTICE - "HiSax: isac:%x/%x\n", - sp->isac, sp->isac - 0x400); - printk(KERN_NOTICE - "HiSax: hscx A:%x/%x hscx B:%x/%x\n", - sp->hscx[0], sp->hscx[0] - 0x400, - sp->hscx[1], sp->hscx[1] - 0x400); - verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf; - verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf; - printk(KERN_INFO "AVM A1: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readreg(sp->isac, ISAC_RBCH); - printk(KERN_INFO "AVM A1: ISAC %s\n", - ISACVersion(val)); - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + cs->hw.avm.cfg_reg, val); + + printk(KERN_INFO + "HiSax: %s config irq:%d cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.avm.cfg_reg); + printk(KERN_INFO + "HiSax: isac:0x%X/0x%X\n", + cs->hw.avm.isac + 32, cs->hw.avm.isacfifo); + printk(KERN_INFO + "HiSax: hscx A:0x%X/0x%X hscx B:0x%X/0x%X\n", + cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0], + cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &AVM_card_msg; + ISACVersion(cs, "AVM A1:"); + if (HscxVersion(cs, "AVM A1:")) { printk(KERN_WARNING "AVM A1: wrong HSCX versions check IO address\n"); - release_io_avm_a1(card); + release_ioregs(cs, 0x3f); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index fbf045574..6924e3f9e 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 1.30 1997/05/29 10:40:43 keil Exp $ +/* $Id: callc.c,v 2.13 1998/02/12 23:07:16 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,96 +7,54 @@ * Fritz Elfert * * $Log: callc.c,v $ - * Revision 1.30 1997/05/29 10:40:43 keil - * chanp->impair was uninitialised + * Revision 2.13 1998/02/12 23:07:16 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.29 1997/04/23 20:09:49 fritz - * Removed tmp, used by removed debugging code. + * Revision 2.12 1998/02/09 10:55:54 keil + * New leased line mode * - * Revision 1.28 1997/04/21 13:42:25 keil - * Remove unneeded debug + * Revision 2.11 1998/02/02 13:35:19 keil + * config B-channel delay * - * Revision 1.27 1997/04/16 14:21:01 keil - * remove unused variable + * Revision 2.10 1997/11/06 17:09:15 keil + * New 2.1 init code * - * Revision 1.26 1997/04/13 19:55:21 keil - * Changes in debugging code + * Revision 2.9 1997/10/29 19:01:58 keil + * new LL interface * - * Revision 1.25 1997/04/06 22:54:08 keil - * Using SKB's + * Revision 2.8 1997/10/10 20:56:44 fritz + * New HL interface. * - * Revision 1.24 1997/03/05 11:28:03 keil - * fixed undefined l2tei procedure - * a layer1 release delete now the drel timer + * Revision 2.7 1997/10/01 09:21:28 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.23 1997/03/04 23:07:42 keil - * bugfix dial parameter + * Revision 2.6 1997/09/11 17:26:58 keil + * Open B-channel if here are incomming packets * - * Revision 1.22 1997/02/27 13:51:55 keil - * Reset B-channel (dlc) statemachine in every release + * Revision 2.5 1997/08/07 17:46:05 keil + * Fix Incomming Call without broadcast * - * Revision 1.21 1997/02/19 09:24:27 keil - * Bugfix: Hangup to LL if a ttyI rings + * Revision 2.4 1997/08/03 14:37:58 keil + * Activate Layer2 in PtP mode * - * Revision 1.20 1997/02/17 00:32:47 keil - * Bugfix: No Busy reported to LL + * Revision 2.3 1997/07/31 19:23:40 keil + * LAYER2_WATCHING for PtP * - * Revision 1.19 1997/02/14 12:23:10 fritz - * Added support for new insmod parameter handling. + * Revision 2.2 1997/07/31 11:48:18 keil + * experimental REJECT after ALERTING * - * Revision 1.18 1997/02/11 01:36:58 keil - * Changed setup-interface (incoming and outgoing), cause reporting + * Revision 2.1 1997/07/30 17:12:59 keil + * more changes for 'One TEI per card' * - * Revision 1.17 1997/02/09 00:23:10 keil - * new interface handling, one interface per card - * some changes in debug and leased line mode + * Revision 2.0 1997/07/27 21:12:21 keil + * CRef based L3; new channel handling; many other stuff * - * Revision 1.16 1997/01/27 23:17:03 keil - * delete timers while unloading + * Revision 1.31 1997/06/26 11:09:23 keil + * New managment and minor changes * - * Revision 1.15 1997/01/27 16:00:38 keil - * D-channel shutdown delay; improved callback - * - * Revision 1.14 1997/01/21 22:16:39 keil - * new statemachine; leased line support; cleanup for 2.0 - * - * Revision 1.13 1996/12/08 19:51:17 keil - * bugfixes from Pekka Sarnila - * - * Revision 1.12 1996/11/26 20:20:03 keil - * fixed warning while compile - * - * Revision 1.11 1996/11/26 18:43:17 keil - * change ioctl 555 --> 55 (555 didn't work) - * - * Revision 1.10 1996/11/26 18:06:07 keil - * fixed missing break statement,ioctl 555 reset modcount - * - * Revision 1.9 1996/11/18 20:23:19 keil - * log writebuf channel not open changed - * - * Revision 1.8 1996/11/06 17:43:17 keil - * more changes for 2.1.X;block fixed ST_PRO_W - * - * Revision 1.7 1996/11/06 15:13:51 keil - * typo 0x64 --->64 in debug code - * - * Revision 1.6 1996/11/05 19:40:33 keil - * X.75 windowsize - * - * Revision 1.5 1996/10/30 10:11:06 keil - * debugging LOCK changed;ST_REL_W EV_HANGUP added - * - * Revision 1.4 1996/10/27 22:20:16 keil - * alerting bugfixes - * no static b-channel<->channel mapping - * - * Revision 1.2 1996/10/16 21:29:45 keil - * compile bug as "not module" - * Callback with euro - * - * Revision 1.1 1996/10/13 20:04:50 keil - * Initial revision + * old logs removed /KKe * */ @@ -104,53 +62,63 @@ #include "hisax.h" #ifdef MODULE -#if (LINUX_VERSION_CODE < 0x020111) -extern long mod_use_count_; -#define MOD_USE_COUNT mod_use_count_ -#else #define MOD_USE_COUNT ((&__this_module)->usecount) -#endif #endif /* MODULE */ -const char *l4_revision = "$Revision: 1.30 $"; +const char *lli_revision = "$Revision: 2.13 $"; extern struct IsdnCard cards[]; extern int nrcards; extern void HiSax_mod_dec_use_count(void); extern void HiSax_mod_inc_use_count(void); -static int init_ds(struct Channel *chanp, int incoming); -static void release_ds(struct Channel *chanp); +static int init_b_st(struct Channel *chanp, int incoming); +static void release_b_st(struct Channel *chanp); static struct Fsm callcfsm = -{NULL, 0, 0}; +{NULL, 0, 0, NULL, NULL}; static struct Fsm lcfsm = -{NULL, 0, 0}; +{NULL, 0, 0, NULL, NULL}; static int chancount = 0; -/* Flags for remembering action done in l4 */ - -#define FLG_START_D 0x0001 -#define FLG_ESTAB_D 0x0002 -#define FLG_CALL_SEND 0x0004 -#define FLG_CALL_REC 0x0008 -#define FLG_CALL_ALERT 0x0010 -#define FLG_START_B 0x0020 -#define FLG_CONNECT_B 0x0040 -#define FLG_LL_DCONN 0x0080 -#define FLG_LL_BCONN 0x0100 -#define FLG_DISC_SEND 0x0200 -#define FLG_DISC_REC 0x0400 -#define FLG_REL_REC 0x0800 - -#define SETBIT(flg, item) flg |= item -#define RESBIT(flg, item) flg &= (~item) +/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */ +#define ALERT_REJECT 1 + +/* Value to delay the sending of the first B-channel paket after CONNECT + * here is no value given by ITU, but experience shows that 300 ms will + * work on many networks, if you or your other side is behind local exchanges + * a greater value may be recommented. If the delay is to short the first paket + * will be lost and autodetect on many comercial routers goes wrong ! + * You can adjust this value on runtime with + * hisaxctrl <id> 2 <value> + * value is in milliseconds + */ +#define DEFAULT_B_DELAY 300 + +/* Flags for remembering action done in lli */ + +#define FLG_START_D 0 +#define FLG_ESTAB_D 1 +#define FLG_CALL_SEND 2 +#define FLG_CALL_REC 3 +#define FLG_CALL_ALERT 4 +#define FLG_START_B 5 +#define FLG_CONNECT_B 6 +#define FLG_LL_DCONN 7 +#define FLG_LL_BCONN 8 +#define FLG_DISC_SEND 9 +#define FLG_DISC_REC 10 +#define FLG_REL_REC 11 +#define FLG_DO_ALERT 12 +#define FLG_DO_HANGUP 13 +#define FLG_DO_CONNECT 14 +#define FLG_DO_ESTAB 15 /* * Because of callback it's a good idea to delay the shutdown of the d-channel */ -#define DREL_TIMER_VALUE 30000 +#define DREL_TIMER_VALUE 10000 /* * Find card with given driverId @@ -162,9 +130,9 @@ hisax_findcard(int driverid) int i; for (i = 0; i < nrcards; i++) - if (cards[i].sp) - if (cards[i].sp->myid == driverid) - return (cards[i].sp); + if (cards[i].cs) + if (cards[i].cs->myid == driverid) + return (cards[i].cs); return (struct IsdnCardState *) 0; } @@ -176,7 +144,7 @@ link_debug(struct Channel *chanp, char *s, int direction) jiftime(tm, jiffies); sprintf(tmp, "%s Channel %d %s %s\n", tm, chanp->chan, direction ? "LL->HL" : "HL->LL", s); - HiSax_putstatus(chanp->sp, tmp); + HiSax_putstatus(chanp->cs, tmp); } @@ -233,7 +201,7 @@ enum { EV_SETUP_CMPL_IND, /* 10 */ EV_BC_EST, /* 11 */ EV_WRITEBUF, /* 12 */ - EV_DATAIN, /* 13 */ + EV_ESTABLISH, /* 13 */ EV_HANGUP, /* 14 */ EV_BC_REL, /* 15 */ EV_CINF, /* 16 */ @@ -263,7 +231,7 @@ static char *strEvent[] = "EV_SETUP_CMPL_IND", "EV_BC_EST", "EV_WRITEBUF", - "EV_DATAIN", + "EV_ESTABLISH", "EV_HANGUP", "EV_BC_REL", "EV_CINF", @@ -283,7 +251,6 @@ enum { ST_LC_ESTABLISH_WAIT, ST_LC_CONNECTED, ST_LC_FLUSH_WAIT, - ST_LC_FLUSH_DELAY, ST_LC_RELEASE_WAIT, }; @@ -297,7 +264,6 @@ static char *strLcState[] = "ST_LC_ESTABLISH_WAIT", "ST_LC_CONNECTED", "ST_LC_FLUSH_WAIT", - "ST_LC_FLUSH_DELAY", "ST_LC_RELEASE_WAIT", }; @@ -307,9 +273,7 @@ enum { EV_LC_PH_DEACTIVATE, EV_LC_DL_ESTABLISH, EV_LC_TIMER, - EV_LC_DL_FLUSH, EV_LC_DL_RELEASE, - EV_LC_FLUSH, EV_LC_RELEASE, }; @@ -322,9 +286,7 @@ static char *strLcEvent[] = "EV_LC_PH_DEACTIVATE", "EV_LC_DL_ESTABLISH", "EV_LC_TIMER", - "EV_LC_DL_FLUSH", "EV_LC_DL_RELEASE", - "EV_LC_FLUSH", "EV_LC_RELEASE", }; @@ -332,788 +294,962 @@ static char *strLcEvent[] = #define LC_B 1 static inline void -l4_deliver_cause(struct Channel *chanp) +lli_deliver_cause(struct Channel *chanp) { isdn_ctrl ic; - if (chanp->para.cause < 0) + if (chanp->proc->para.cause < 0) return; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_CAUSE; ic.arg = chanp->chan; - if (chanp->sp->protocol == ISDN_PTYPE_EURO) - sprintf(ic.parm.num, "E%02X%02X", chanp->para.loc & 0x7f, - chanp->para.cause & 0x7f); + if (chanp->cs->protocol == ISDN_PTYPE_EURO) + sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f, + chanp->proc->para.cause & 0x7f); else - sprintf(ic.parm.num, "%02X%02X", chanp->para.loc & 0x7f, - chanp->para.cause & 0x7f); - chanp->sp->iif.statcallb(&ic); + sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f, + chanp->proc->para.cause & 0x7f); + chanp->cs->iif.statcallb(&ic); +} + +static void +lli_d_established(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + if (chanp->leased) { + isdn_ctrl ic; + int ret; + char txt[32]; + + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan); + FsmChangeState(fi, ST_IN_WAIT_LL); + test_and_set_bit(FLG_CALL_REC, &chanp->Flags); + if (chanp->debug & 1) + link_debug(chanp, "STAT_ICALL_LEASED", 0); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_ICALL; + ic.arg = chanp->chan; + ic.parm.setup.si1 = 7; + ic.parm.setup.si2 = 0; + ic.parm.setup.plan = 0; + ic.parm.setup.screen = 0; + sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); + sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid); + ret = chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) { + sprintf(txt, "statcallb ret=%d", ret); + link_debug(chanp, txt, 1); + } + if (!ret) { + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); + FsmChangeState(fi, ST_NULL); + } + } else if (fi->state == ST_WAIT_DSHUTDOWN) + FsmChangeState(fi, ST_NULL); +} + +static void +lli_d_released(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + test_and_clear_bit(FLG_START_D, &chanp->Flags); } /* * Dial out */ static void -l4_prep_dialout(struct FsmInst *fi, int event, void *arg) +lli_prep_dialout(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_OUT_WAIT_D); FsmDelTimer(&chanp->drel_timer, 60); FsmDelTimer(&chanp->dial_timer, 73); - chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = 0; - chanp->lc_b.l2_start = !0; + chanp->lc_b->l2_start = !0; switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): - chanp->lc_b.l2_establish = !0; + chanp->lc_b->l2_establish = !0; break; case (ISDN_PROTO_L2_HDLC): case (ISDN_PROTO_L2_TRANS): - chanp->lc_b.l2_establish = 0; + chanp->lc_b->l2_establish = 0; break; default: - printk(KERN_WARNING "l4_prep_dialout unknown protocol\n"); + printk(KERN_WARNING "lli_prep_dialout unknown protocol\n"); break; } - if (chanp->Flags & FLG_ESTAB_D) { + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { FsmEvent(fi, EV_DLEST, NULL); } else { - chanp->Flags = FLG_START_D; + chanp->Flags = 0; + test_and_set_bit(FLG_START_D, &chanp->Flags); if (chanp->leased) { - chanp->lc_d.l2_establish = 0; + chanp->lc_d->l2_establish = 0; } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL); } } static void -l4_do_dialout(struct FsmInst *fi, int event, void *arg) +lli_do_dialout(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_OUT_DIAL); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan); if (chanp->leased) { - chanp->para.bchannel = (chanp->chan & 1) + 1; FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); } else { - SETBIT(chanp->Flags, FLG_ESTAB_D); - chanp->para.callref = chanp->outcallref; - chanp->outcallref++; - if (chanp->outcallref == 128) - chanp->outcallref = 64; - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL); - SETBIT(chanp->Flags, FLG_CALL_SEND); + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_REQ, chanp); + test_and_set_bit(FLG_CALL_SEND, &chanp->Flags); } } static void -l4_init_bchan_out(struct FsmInst *fi, int event, void *arg) +lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - SETBIT(chanp->Flags, FLG_LL_DCONN); + test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DCONN", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - init_ds(chanp, 0); - SETBIT(chanp->Flags, FLG_START_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); + chanp->cs->iif.statcallb(&ic); + init_b_st(chanp, 0); + test_and_set_bit(FLG_START_B, &chanp->Flags); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_ESTABLISH, NULL); } static void -l4_go_active(struct FsmInst *fi, int event, void *arg) +lli_go_active(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_ACTIVE); chanp->data_open = !0; - SETBIT(chanp->Flags, FLG_CONNECT_B); + test_and_set_bit(FLG_CONNECT_B, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_BCONN", 0); - SETBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + test_and_set_bit(FLG_LL_BCONN, &chanp->Flags); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) chanp->chan); } /* incomming call */ static void -l4_start_dchan(struct FsmInst *fi, int event, void *arg) +lli_start_dchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_WAIT_D); FsmDelTimer(&chanp->drel_timer, 61); - if (chanp->Flags & FLG_ESTAB_D) { + if (event == EV_ACCEPTD) + test_and_set_bit(FLG_DO_CONNECT, &chanp->Flags); + else if (event == EV_HANGUP) { + test_and_set_bit(FLG_DO_HANGUP, &chanp->Flags); +#ifdef ALERT_REJECT + test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); +#endif + } + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { FsmEvent(fi, EV_DLEST, NULL); - } else { - chanp->Flags = FLG_START_D; - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); - } + } else if (!test_and_set_bit(FLG_START_D, &chanp->Flags)) + FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL); } static void -l4_deliver_call(struct FsmInst *fi, int event, void *arg) +lli_deliver_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; int ret; char txt[32]; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan); /* * Report incoming calls only once to linklevel, use CallFlags * which is set to 3 with each broadcast message in isdnl1.c * and resetted if a interface answered the STAT_ICALL. */ - if ((chanp->sp) && (chanp->sp->CallFlags == 3)) { + if (1) { /* for only one TEI */ FsmChangeState(fi, ST_IN_WAIT_LL); - SETBIT(chanp->Flags, FLG_ESTAB_D); - SETBIT(chanp->Flags, FLG_CALL_REC); + test_and_set_bit(FLG_CALL_REC, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_ICALL", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_ICALL; ic.arg = chanp->chan; /* * No need to return "unknown" for calls without OAD, * cause that's handled in linklevel now (replaced by '0') */ - ic.parm.setup = chanp->para.setup; - ret = chanp->sp->iif.statcallb(&ic); + ic.parm.setup = chanp->proc->para.setup; + ret = chanp->cs->iif.statcallb(&ic); if (chanp->debug & 1) { sprintf(txt, "statcallb ret=%d", ret); link_debug(chanp, txt, 1); } - if (ret) /* if a interface knows this call, reset the CallFlag - * to avoid a second Call report to the linklevel - */ - chanp->sp->CallFlags &= ~(chanp->chan + 1); switch (ret) { case 1: /* OK, anybody likes this call */ - FsmChangeState(fi, ST_IN_ALERT_SEND); - SETBIT(chanp->Flags, FLG_CALL_ALERT); - chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL); + FsmDelTimer(&chanp->drel_timer, 61); + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { + FsmChangeState(fi, ST_IN_ALERT_SEND); + test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc); + } else { + test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); + FsmChangeState(fi, ST_IN_WAIT_D); + test_and_set_bit(FLG_START_D, &chanp->Flags); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL); + } break; case 2: /* Rejecting Call */ - RESBIT(chanp->Flags, FLG_CALL_REC); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); break; case 0: /* OK, nobody likes this call */ default: /* statcallb problems */ - chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); +#ifndef LAYER2_WATCHING + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) + FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); +#endif break; } } else { - chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); + FsmChangeState(fi, ST_NULL); +#ifndef LAYER2_WATCHING + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) + FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); +#endif + } +} + +static void +lli_establish_d(struct FsmInst *fi, int event, void *arg) +{ + /* This establish the D-channel for pending L3 messages + * without blocking th channel + */ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags); + FsmChangeState(fi, ST_IN_WAIT_D); + test_and_set_bit(FLG_START_D, &chanp->Flags); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL); +} + +static void +lli_do_action(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + if (chanp->leased) { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags); + FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); + } else if (test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags) && + !test_bit(FLG_DO_HANGUP, &chanp->Flags)) { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc); + } else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) { + if (test_bit(FLG_DO_HANGUP, &chanp->Flags)) + FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); + FsmChangeState(fi, ST_IN_ALERT_SEND); + test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc); + } else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) { + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT_REQ, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); + } else if (test_and_clear_bit(FLG_DO_ESTAB, &chanp->Flags)) { FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); + chanp->Flags = 0; + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ESTABLISH, chanp->proc); + chanp->proc = NULL; +#ifndef LAYER2_WATCHING + FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); +#endif } } static void -l4_send_dconnect(struct FsmInst *fi, int event, void *arg) +lli_send_dconnect(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc); } static void -l4_init_bchan_in(struct FsmInst *fi, int event, void *arg) +lli_init_bchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - SETBIT(chanp->Flags, FLG_LL_DCONN); + test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DCONN", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = !0; - chanp->lc_b.l2_start = 0; + chanp->lc_b->l2_start = 0; switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): - chanp->lc_b.l2_establish = !0; + chanp->lc_b->l2_establish = !0; break; case (ISDN_PROTO_L2_HDLC): case (ISDN_PROTO_L2_TRANS): - chanp->lc_b.l2_establish = 0; + chanp->lc_b->l2_establish = 0; break; default: - printk(KERN_WARNING "r9 unknown protocol\n"); + printk(KERN_WARNING "bchannel unknown protocol\n"); break; } - init_ds(chanp, !0); - SETBIT(chanp->Flags, FLG_START_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); + init_b_st(chanp, !0); + test_and_set_bit(FLG_START_B, &chanp->Flags); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_ESTABLISH, NULL); } /* Call clearing */ static void -l4_reject_call(struct FsmInst *fi, int event, void *arg) -{ - struct Channel *chanp = fi->userdata; - - FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->para.cause = 0x15; /* Call Rejected */ - chanp->is.l4.l4l3(&chanp->is, CC_REJECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); -} - -static void -l4_cancel_call(struct FsmInst *fi, int event, void *arg) +lli_cancel_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + chanp->cs->iif.statcallb(&ic); } - chanp->para.cause = 0x10; /* Normal Call Clearing */ - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } static void -l4_shutdown_d(struct FsmInst *fi, int event, void *arg) +lli_shutdown_d(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); FsmDelTimer(&chanp->drel_timer, 62); - RESBIT(chanp->Flags, FLG_ESTAB_D); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); +#ifdef LAYER2_WATCHING + FsmChangeState(fi, ST_NULL); +#else + if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) { + if (chanp->chan) { + if (chanp->cs->channel[0].fi.state != ST_NULL) + return; + } else { + if (chanp->cs->channel[1].fi.state != ST_NULL) + return; + } + } + FsmChangeState(fi, ST_WAIT_DSHUTDOWN); + test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); +#endif } static void -l4_timeout_d(struct FsmInst *fi, int event, void *arg) +lli_timeout_d(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - if (chanp->Flags & FLG_LL_DCONN) { + if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; + chanp->Flags = 0; + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); +#ifndef LAYER2_WATCHING FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); +#endif + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); } static void -l4_go_null(struct FsmInst *fi, int event, void *arg) +lli_go_null(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_NULL); chanp->Flags = 0; FsmDelTimer(&chanp->drel_timer, 63); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); } static void -l4_disconn_bchan(struct FsmInst *fi, int event, void *arg) +lli_disconn_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_BRELEASE); - RESBIT(chanp->Flags, FLG_CONNECT_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } static void -l4_send_d_disc(struct FsmInst *fi, int event, void *arg) +lli_send_d_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - - if (chanp->Flags & (FLG_DISC_REC | FLG_REL_REC)) + if (test_bit(FLG_DISC_REC, &chanp->Flags) || + test_bit(FLG_REL_REC, &chanp->Flags)) return; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->leased) { + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L0010"); + chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, "STAT_DHUP", 0); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + FsmChangeState(fi, ST_WAIT_DSHUTDOWN); + test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); + } else { + if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) + chanp->proc->para.cause = 0x15; /* Call Reject */ + else + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } - chanp->para.cause = 0x10; /* Normal Call Clearing */ - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); } static void -l4_released_bchan(struct FsmInst *fi, int event, void *arg) +lli_released_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DCOMMAND); chanp->data_open = 0; - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + release_b_st(chanp); + test_and_clear_bit(FLG_START_B, &chanp->Flags); } static void -l4_release_bchan(struct FsmInst *fi, int event, void *arg) +lli_release_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; chanp->data_open = 0; - SETBIT(chanp->Flags, FLG_DISC_REC); + test_and_set_bit(FLG_DISC_REC, &chanp->Flags); FsmChangeState(fi, ST_WAIT_BREL_DISC); - RESBIT(chanp->Flags, FLG_CONNECT_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } static void -l4_received_d_rel(struct FsmInst *fi, int event, void *arg) +lli_received_d_rel(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - SETBIT(chanp->Flags, FLG_REL_REC); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + FsmChangeState(fi, ST_NULL); + test_and_set_bit(FLG_REL_REC, &chanp->Flags); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */ + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (test_bit(FLG_LL_DCONN, &chanp->Flags) || + test_bit(FLG_CALL_SEND, &chanp->Flags) || + test_bit(FLG_CALL_ALERT, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_FLUSH, NULL); - RESBIT(chanp->Flags, FLG_ESTAB_D); - RESBIT(chanp->Flags, FLG_DISC_SEND); - RESBIT(chanp->Flags, FLG_CALL_REC); - RESBIT(chanp->Flags, FLG_CALL_ALERT); - RESBIT(chanp->Flags, FLG_LL_DCONN); - RESBIT(chanp->Flags, FLG_CALL_SEND); + test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + lli_timeout_d(fi, event, arg); } static void -l4_received_d_relcnf(struct FsmInst *fi, int event, void *arg) +lli_received_d_relcnf(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + FsmChangeState(fi, ST_NULL); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */ + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); - } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (test_bit(FLG_LL_DCONN, &chanp->Flags) || + test_bit(FLG_CALL_SEND, &chanp->Flags) || + test_bit(FLG_CALL_ALERT, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_ESTAB_D); - RESBIT(chanp->Flags, FLG_DISC_SEND); - RESBIT(chanp->Flags, FLG_CALL_REC); - RESBIT(chanp->Flags, FLG_CALL_ALERT); - RESBIT(chanp->Flags, FLG_LL_DCONN); - RESBIT(chanp->Flags, FLG_CALL_SEND); + test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + lli_timeout_d(fi, event, arg); } static void -l4_received_d_disc(struct FsmInst *fi, int event, void *arg) +lli_received_d_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_D_REL_CNF); - SETBIT(chanp->Flags, FLG_DISC_REC); - if (chanp->Flags & FLG_LL_BCONN) { + test_and_set_bit(FLG_DISC_REC, &chanp->Flags); + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (test_bit(FLG_LL_DCONN, &chanp->Flags) || + test_bit(FLG_CALL_SEND, &chanp->Flags) || + test_bit(FLG_CALL_ALERT, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - RESBIT(chanp->Flags, FLG_LL_DCONN); - RESBIT(chanp->Flags, FLG_CALL_SEND); - RESBIT(chanp->Flags, FLG_CALL_ALERT); - chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE_REQ, chanp->proc); } /* processing charge info */ static void -l4_charge_info(struct FsmInst *fi, int event, void *arg) +lli_charge_info(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_CINF; ic.arg = chanp->chan; - sprintf(ic.parm.num, "%d", chanp->para.chargeinfo); - chanp->sp->iif.statcallb(&ic); + sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo); + chanp->cs->iif.statcallb(&ic); } /* error procedures */ static void -l4_no_dchan(struct FsmInst *fi, int event, void *arg) +lli_no_dchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; if (chanp->debug & 1) link_debug(chanp, "STAT_NODCH", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_NODCH; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); } static void -l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg) +lli_no_dchan_ready(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } static void -l4_no_dchan_in(struct FsmInst *fi, int event, void *arg) +lli_no_dchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; + isdn_ctrl ic; - chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL); + if (chanp->debug & 1) + link_debug(chanp, "STAT_DHUP", 0); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc); chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); } static void -l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg) +lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + lli_shutdown_d(fi, event, arg); } static void -l4_setup_err(struct FsmInst *fi, int event, void *arg) +lli_setup_err(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_DCONN) { + if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - SETBIT(chanp->Flags, FLG_DISC_SEND); /* DISCONN was sent from L3 */ + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ } static void -l4_connect_err(struct FsmInst *fi, int event, void *arg) +lli_connect_err(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_DCONN) { + if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - SETBIT(chanp->Flags, FLG_DISC_SEND); /* DISCONN was sent from L3 */ + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ } static void -l4_active_dlrl(struct FsmInst *fi, int event, void *arg) +lli_got_dlrl(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; FsmChangeState(fi, ST_NULL); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */ + FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_LL_DCONN) { - if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - if (chanp->sp->protocol == ISDN_PTYPE_EURO) { - chanp->para.cause = 0x2f; - chanp->para.loc = 0; - } else { - chanp->para.cause = 0x70; - chanp->para.loc = 0; - } - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->leased) { + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f); + chanp->cs->iif.statcallb(&ic); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + chanp->Flags = 0; + } else { + if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) { + if (chanp->debug & 1) + link_debug(chanp, "STAT_DHUP", 0); + if (chanp->cs->protocol == ISDN_PTYPE_EURO) { + chanp->proc->para.cause = 0x2f; + chanp->proc->para.loc = 0; + } else { + chanp->proc->para.cause = 0x70; + chanp->proc->para.loc = 0; + } + lli_deliver_cause(chanp); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + } + chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc); + chanp->Flags = 0; + FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL); } - chanp->Flags = 0; - chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan); } + /* *INDENT-OFF* */ -static struct FsmNode fnlist[] = -{ - {ST_NULL, EV_DIAL, l4_prep_dialout}, - {ST_NULL, EV_SETUP_IND, l4_start_dchan}, - {ST_NULL, EV_SHUTDOWN_D, l4_shutdown_d}, - {ST_NULL, EV_DLRL, l4_go_null}, - {ST_OUT_WAIT_D, EV_DLEST, l4_do_dialout}, - {ST_OUT_WAIT_D, EV_DLRL, l4_no_dchan}, - {ST_OUT_WAIT_D, EV_HANGUP, l4_no_dchan}, - {ST_IN_WAIT_D, EV_DLEST, l4_deliver_call}, - {ST_IN_WAIT_D, EV_DLRL, l4_no_dchan_in}, - {ST_IN_WAIT_D, EV_HANGUP, l4_no_dchan_in}, - {ST_OUT_DIAL, EV_SETUP_CNF, l4_init_bchan_out}, - {ST_OUT_DIAL, EV_HANGUP, l4_cancel_call}, - {ST_OUT_DIAL, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_OUT_DIAL, EV_RELEASE_IND, l4_received_d_rel}, - {ST_OUT_DIAL, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_OUT_DIAL, EV_NOSETUP_RSP, l4_no_setup_rsp}, - {ST_OUT_DIAL, EV_SETUP_ERR, l4_setup_err}, - {ST_IN_WAIT_LL, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_WAIT_LL, EV_ACCEPTD, l4_send_dconnect}, - {ST_IN_WAIT_LL, EV_HANGUP, l4_reject_call}, - {ST_IN_WAIT_LL, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_WAIT_LL, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_WAIT_LL, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_ALERT_SEND, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_ALERT_SEND, EV_ACCEPTD, l4_send_dconnect}, - {ST_IN_ALERT_SEND, EV_HANGUP, l4_send_d_disc}, - {ST_IN_ALERT_SEND, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_ALERT_SEND, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_ALERT_SEND, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_WAIT_CONN_ACK, EV_HANGUP, l4_send_d_disc}, - {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_WAIT_CONN_ACK, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_WAIT_CONN_ACK, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, l4_connect_err}, - {ST_WAIT_BCONN, EV_BC_EST, l4_go_active}, - {ST_WAIT_BCONN, EV_BC_REL, l4_send_d_disc}, - {ST_WAIT_BCONN, EV_HANGUP, l4_send_d_disc}, - {ST_WAIT_BCONN, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_BCONN, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_BCONN, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_ACTIVE, EV_CINF, l4_charge_info}, - {ST_ACTIVE, EV_BC_REL, l4_released_bchan}, - {ST_ACTIVE, EV_HANGUP, l4_disconn_bchan}, - {ST_ACTIVE, EV_DISCONNECT_IND, l4_release_bchan}, - {ST_ACTIVE, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_ACTIVE, EV_RELEASE_IND, l4_received_d_rel}, - {ST_ACTIVE, EV_DLRL, l4_active_dlrl}, - {ST_WAIT_BRELEASE, EV_BC_REL, l4_send_d_disc}, - {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_BRELEASE, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_BRELEASE, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_BREL_DISC, EV_BC_REL, l4_received_d_disc}, - {ST_WAIT_BREL_DISC, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_BREL_DISC, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_DCOMMAND, EV_HANGUP, l4_send_d_disc}, - {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_DCOMMAND, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_DCOMMAND, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_DRELEASE, EV_RELEASE_IND, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_RELEASE_CNF, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_RELEASE_ERR, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_DIAL, l4_no_dchan_ready}, - {ST_WAIT_D_REL_CNF, EV_RELEASE_CNF, l4_timeout_d}, - {ST_WAIT_D_REL_CNF, EV_RELEASE_ERR, l4_timeout_d}, - {ST_WAIT_D_REL_CNF, EV_DIAL, l4_no_dchan_ready}, - {ST_WAIT_DSHUTDOWN, EV_DLRL, l4_go_null}, - {ST_WAIT_DSHUTDOWN, EV_DIAL, l4_prep_dialout}, - {ST_WAIT_DSHUTDOWN, EV_SETUP_IND, l4_start_dchan}, +static struct FsmNode fnlist[] HISAX_INITDATA = +{ + {ST_NULL, EV_DIAL, lli_prep_dialout}, + {ST_NULL, EV_SETUP_IND, lli_deliver_call}, + {ST_NULL, EV_SHUTDOWN_D, lli_shutdown_d}, + {ST_NULL, EV_DLRL, lli_go_null}, + {ST_NULL, EV_DLEST, lli_d_established}, + {ST_NULL, EV_ESTABLISH, lli_establish_d}, + {ST_OUT_WAIT_D, EV_DLEST, lli_do_dialout}, + {ST_OUT_WAIT_D, EV_DLRL, lli_no_dchan}, + {ST_OUT_WAIT_D, EV_HANGUP, lli_no_dchan}, + {ST_IN_WAIT_D, EV_DLEST, lli_do_action}, + {ST_IN_WAIT_D, EV_DLRL, lli_no_dchan_in}, + {ST_IN_WAIT_D, EV_ACCEPTD, lli_start_dchan}, + {ST_IN_WAIT_D, EV_HANGUP, lli_start_dchan}, + {ST_OUT_DIAL, EV_SETUP_CNF, lli_init_bchan_out}, + {ST_OUT_DIAL, EV_HANGUP, lli_cancel_call}, + {ST_OUT_DIAL, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_OUT_DIAL, EV_RELEASE_IND, lli_received_d_rel}, + {ST_OUT_DIAL, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_OUT_DIAL, EV_NOSETUP_RSP, lli_no_setup_rsp}, + {ST_OUT_DIAL, EV_SETUP_ERR, lli_setup_err}, + {ST_OUT_DIAL, EV_DLRL, lli_got_dlrl}, + {ST_IN_WAIT_LL, EV_DLEST, lli_d_established}, + {ST_IN_WAIT_LL, EV_DLRL, lli_d_released}, + {ST_IN_WAIT_LL, EV_ACCEPTD, lli_start_dchan}, + {ST_IN_WAIT_LL, EV_HANGUP, lli_start_dchan}, + {ST_IN_WAIT_LL, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_WAIT_LL, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_WAIT_LL, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_ALERT_SEND, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_ALERT_SEND, EV_ACCEPTD, lli_send_dconnect}, + {ST_IN_ALERT_SEND, EV_HANGUP, lli_send_d_disc}, + {ST_IN_ALERT_SEND, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_ALERT_SEND, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_ALERT_SEND, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_ALERT_SEND, EV_DLRL, lli_got_dlrl}, + {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_WAIT_CONN_ACK, EV_HANGUP, lli_send_d_disc}, + {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, lli_connect_err}, + {ST_IN_WAIT_CONN_ACK, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BCONN, EV_BC_EST, lli_go_active}, + {ST_WAIT_BCONN, EV_BC_REL, lli_send_d_disc}, + {ST_WAIT_BCONN, EV_HANGUP, lli_send_d_disc}, + {ST_WAIT_BCONN, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_BCONN, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BCONN, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BCONN, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BCONN, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_BC_REL, lli_released_bchan}, + {ST_ACTIVE, EV_HANGUP, lli_disconn_bchan}, + {ST_ACTIVE, EV_DISCONNECT_IND, lli_release_bchan}, + {ST_ACTIVE, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_ACTIVE, EV_RELEASE_IND, lli_received_d_rel}, + {ST_ACTIVE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BRELEASE, EV_BC_REL, lli_send_d_disc}, + {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_BRELEASE, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BRELEASE, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BRELEASE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BREL_DISC, EV_BC_REL, lli_received_d_disc}, + {ST_WAIT_BREL_DISC, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BREL_DISC, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BREL_DISC, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DCOMMAND, EV_HANGUP, lli_send_d_disc}, + {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_DCOMMAND, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_DCOMMAND, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_DCOMMAND, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DRELEASE, EV_RELEASE_IND, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_RELEASE_CNF, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_RELEASE_ERR, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_DIAL, lli_no_dchan_ready}, + {ST_WAIT_DRELEASE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_D_REL_CNF, EV_RELEASE_CNF, lli_timeout_d}, + {ST_WAIT_D_REL_CNF, EV_RELEASE_ERR, lli_timeout_d}, +/* ETS 300-104 16.1 */ + {ST_WAIT_D_REL_CNF, EV_RELEASE_IND, lli_timeout_d}, + {ST_WAIT_D_REL_CNF, EV_DIAL, lli_no_dchan_ready}, + {ST_WAIT_D_REL_CNF, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DSHUTDOWN, EV_DLRL, lli_go_null}, + {ST_WAIT_DSHUTDOWN, EV_DLEST, lli_d_established}, + {ST_WAIT_DSHUTDOWN, EV_DIAL, lli_prep_dialout}, + {ST_WAIT_DSHUTDOWN, EV_SETUP_IND, lli_deliver_call}, }; /* *INDENT-ON* */ - - #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) static void -lc_r1(struct FsmInst *fi, int event, void *arg) +lc_activate_l1(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; + FsmDelTimer(&lf->act_timer, 50); FsmChangeState(fi, ST_LC_ACTIVATE_WAIT); - FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50); - lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL); + /* This timeout is to avoid a hang if no L1 activation is possible */ + FsmAddTimer(&lf->act_timer, 30000, EV_LC_TIMER, NULL, 50); + lf->st->ma.manl1(lf->st, PH_ACTIVATE_REQ, NULL); +} +static void +lc_activated_from_l1(struct FsmInst *fi, int event, void *arg) +{ + struct LcFsm *lf = fi->userdata; + + if (lf->l2_establish) + FsmChangeState(fi, ST_LC_DELAY); + else { + FsmChangeState(fi, ST_LC_CONNECTED); + lf->lccall(lf, LC_ESTABLISH, NULL); + } } static void -lc_r6(struct FsmInst *fi, int event, void *arg) +lc_l1_activated(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; FsmDelTimer(&lf->act_timer, 50); FsmChangeState(fi, ST_LC_DELAY); - FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51); + /* This timer is needed for delay the first paket on a channel + to be shure that the other side is ready too */ + if (lf->delay) + FsmAddTimer(&lf->act_timer, lf->delay, EV_LC_TIMER, NULL, 51); + else + FsmEvent(fi, EV_LC_TIMER, NULL); } static void -lc_r2(struct FsmInst *fi, int event, void *arg) +lc_start_l2(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; - if (lf->l2_establish) { +/* if (!lf->st->l1.act_state) + lf->st->l1.act_state = 2; +*/ if (lf->l2_establish) { FsmChangeState(fi, ST_LC_ESTABLISH_WAIT); if (lf->l2_start) lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL); @@ -1124,107 +1260,97 @@ lc_r2(struct FsmInst *fi, int event, void *arg) } static void -lc_r3(struct FsmInst *fi, int event, void *arg) +lc_connected(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; + FsmDelTimer(&lf->act_timer, 50); FsmChangeState(fi, ST_LC_CONNECTED); lf->lccall(lf, LC_ESTABLISH, NULL); } static void -lc_r7(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_FLUSH_WAIT); - lf->st->ma.manl2(lf->st, DL_FLUSH, NULL); -} - -static void -lc_r4(struct FsmInst *fi, int event, void *arg) +lc_release_l2(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; if (lf->l2_establish) { FsmChangeState(fi, ST_LC_RELEASE_WAIT); lf->st->ma.manl2(lf->st, DL_RELEASE, NULL); - /* This timer is for releasing the channel even - * there is a hang in layer 2 ; 5 sec are a try - */ - FsmAddTimer(&lf->act_timer, 5000, EV_LC_TIMER, NULL, 53); } else { FsmChangeState(fi, ST_LC_NULL); - lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL); + lf->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL); lf->lccall(lf, LC_RELEASE, NULL); } } static void -lc_r4_1(struct FsmInst *fi, int event, void *arg) +lc_l2_released(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; - FsmChangeState(fi, ST_LC_FLUSH_DELAY); - FsmAddTimer(&lf->act_timer, 50, EV_LC_TIMER, NULL, 52); + FsmChangeState(fi, ST_LC_RELEASE_WAIT); + FsmDelTimer(&lf->act_timer, 51); + /* This delay is needed for send out the UA frame before + * PH_DEACTIVATE the interface + */ + FsmAddTimer(&lf->act_timer, 20, EV_LC_TIMER, NULL, 54); } static void -lc_r5_1(struct FsmInst *fi, int event, void *arg) +lc_release_l1(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; - FsmChangeState(fi, ST_LC_RELEASE_WAIT); - /* This delay is needed for send out the UA frame before - * PH_DEACTIVATE the interface - */ - FsmAddTimer(&lf->act_timer, 10, EV_LC_TIMER, NULL, 54); + FsmDelTimer(&lf->act_timer, 54); + FsmChangeState(fi, ST_LC_NULL); + lf->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL); + lf->lccall(lf, LC_RELEASE, NULL); } static void -lc_r5(struct FsmInst *fi, int event, void *arg) +lc_l1_deactivated(struct FsmInst *fi, int event, void *arg) { struct LcFsm *lf = fi->userdata; FsmDelTimer(&lf->act_timer, 54); FsmChangeState(fi, ST_LC_NULL); - lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL); lf->lccall(lf, LC_RELEASE, NULL); } /* *INDENT-OFF* */ -static struct FsmNode LcFnList[] = -{ - {ST_LC_NULL, EV_LC_ESTABLISH, lc_r1}, - {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_r6}, - {ST_LC_DELAY, EV_LC_TIMER, lc_r2}, - {ST_LC_DELAY, EV_LC_DL_ESTABLISH, lc_r3}, - {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_r3}, - {ST_LC_ESTABLISH_WAIT, EV_LC_RELEASE, lc_r5}, - {ST_LC_CONNECTED, EV_LC_FLUSH, lc_r7}, - {ST_LC_CONNECTED, EV_LC_RELEASE, lc_r4}, - {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_r5_1}, - {ST_LC_FLUSH_WAIT, EV_LC_DL_FLUSH, lc_r4_1}, - {ST_LC_FLUSH_DELAY, EV_LC_TIMER, lc_r4}, - {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_r5}, - {ST_LC_RELEASE_WAIT, EV_LC_TIMER, lc_r5}, - {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_r5}, - {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_r5}, +static struct FsmNode LcFnList[] HISAX_INITDATA = +{ + {ST_LC_NULL, EV_LC_ESTABLISH, lc_activate_l1}, + {ST_LC_NULL, EV_LC_PH_ACTIVATE, lc_activated_from_l1}, + {ST_LC_NULL, EV_LC_DL_ESTABLISH, lc_connected}, + {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_l1_activated}, + {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_release_l1}, + {ST_LC_ACTIVATE_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_DELAY, EV_LC_ESTABLISH, lc_start_l2}, + {ST_LC_DELAY, EV_LC_TIMER, lc_start_l2}, + {ST_LC_DELAY, EV_LC_DL_ESTABLISH, lc_connected}, + {ST_LC_DELAY, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_connected}, + {ST_LC_ESTABLISH_WAIT, EV_LC_RELEASE, lc_release_l1}, + {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_release_l1}, + {ST_LC_ESTABLISH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_CONNECTED, EV_LC_ESTABLISH, lc_connected}, + {ST_LC_CONNECTED, EV_LC_RELEASE, lc_release_l2}, + {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_l2_released}, + {ST_LC_CONNECTED, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_FLUSH_WAIT, EV_LC_TIMER, lc_release_l2}, + {ST_LC_FLUSH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, + {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_release_l1}, + {ST_LC_RELEASE_WAIT, EV_LC_TIMER, lc_release_l1}, + {ST_LC_FLUSH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated}, }; /* *INDENT-ON* */ - - - - - - - - #define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode)) -void -CallcNew(void) +HISAX_INITFUNC(void +CallcNew(void)) { callcfsm.state_count = STATE_COUNT; callcfsm.event_count = EVENT_COUNT; @@ -1247,17 +1373,11 @@ CallcFree(void) } static void -release_ds(struct Channel *chanp) +release_b_st(struct Channel *chanp) { - struct PStack *st = &chanp->ds; - struct IsdnCardState *sp; - struct HscxState *hsp; - - sp = st->l1.hardware; - hsp = sp->hs + chanp->hscx; - - close_hscxstate(hsp); + struct PStack *st = chanp->b_st; + chanp->bcs->BC_Close(chanp->bcs); switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): releasestack_isdnl2(st); @@ -1268,90 +1388,145 @@ release_ds(struct Channel *chanp) break; } /* Reset B-Channel Statemachine */ - FsmDelTimer(&chanp->lc_b.act_timer, 79); - FsmChangeState(&chanp->lc_b.lcfi, ST_LC_NULL); + FsmDelTimer(&chanp->lc_b->act_timer, 79); + FsmChangeState(&chanp->lc_b->lcfi, ST_LC_NULL); } static void -cc_l1man(struct PStack *st, int pr, void *arg) +dc_l1man(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp; + chanp = (struct Channel *) st->lli.userdata; switch (pr) { - case (PH_ACTIVATE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL); + case (PH_ACTIVATE_CNF): + case (PH_ACTIVATE_IND): + FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_ACTIVATE, NULL); break; - case (PH_DEACTIVATE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL); + case (PH_DEACTIVATE_IND): + FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_DEACTIVATE, NULL); break; } } static void -cc_l2man(struct PStack *st, int pr, void *arg) +dc_l2man(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; switch (pr) { case (DL_ESTABLISH): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_DL_ESTABLISH, NULL); break; case (DL_RELEASE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL); - break; - case (DL_FLUSH): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_FLUSH, NULL); + FsmEvent(&chanp->lc_d->lcfi, EV_LC_DL_RELEASE, NULL); break; } } static void -dcc_l1man(struct PStack *st, int pr, void *arg) +bc_l1man(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; switch (pr) { - case (PH_ACTIVATE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL); + case (PH_ACTIVATE_IND): + case (PH_ACTIVATE_CNF): + FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_ACTIVATE, NULL); break; - case (PH_DEACTIVATE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL); + case (PH_DEACTIVATE_IND): + FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_DEACTIVATE, NULL); break; } } static void -dcc_l2man(struct PStack *st, int pr, void *arg) +bc_l2man(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; switch (pr) { case (DL_ESTABLISH): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL); break; case (DL_RELEASE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL); + FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_RELEASE, NULL); break; } } -static void -l2tei_dummy(struct PStack *st, int pr, void *arg) +struct Channel +*selectfreechannel(struct PStack *st) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; - char tmp[64], tm[32]; + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp = st->lli.userdata; + int i; - jiftime(tm, jiffies); - sprintf(tmp, "%s Channel %d Warning! Dummy l2tei called pr=%d\n", tm, chanp->chan, pr); - HiSax_putstatus(chanp->sp, tmp); + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i=1; + else + i=0; + while (i<2) { + if (chanp->fi.state == ST_NULL) + return (chanp); + chanp++; + i++; + } + return (NULL); +} + +int +is_activ(struct PStack *st) +{ + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp = st->lli.userdata; + int i; + + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i=1; + else + i=0; + while (i<2) { + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) + return (1); + chanp++; + i++; + } + return (0); } static void -ll_handler(struct PStack *st, int pr, void *arg) +ll_handler(struct l3_process *pc, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp; char tmp[64], tm[32]; + if (pr == CC_SETUP_IND) { + if (!(chanp = selectfreechannel(pc->st))) { + pc->st->lli.l4l3(pc->st, CC_DLRL, pc); + return; + } else { + chanp->proc = pc; + pc->chan = chanp; + FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); + return; + } + } else if (pr == CC_ESTABLISH) { + if (is_activ(pc->st)) { + pc->st->lli.l4l3(pc->st, CC_ESTABLISH, pc); + return; + } else if (!(chanp = selectfreechannel(pc->st))) { + pc->st->lli.l4l3(pc->st, CC_DLRL, pc); + return; + } else { + chanp->proc = pc; + FsmEvent(&chanp->fi, EV_ESTABLISH, NULL); + return; + } + + + } + chanp = pc->chan; switch (pr) { case (CC_DISCONNECT_IND): FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); @@ -1359,9 +1534,6 @@ ll_handler(struct PStack *st, int pr, void *arg) case (CC_RELEASE_CNF): FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); break; - case (CC_SETUP_IND): - FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); - break; case (CC_RELEASE_IND): FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL); break; @@ -1386,49 +1558,51 @@ ll_handler(struct PStack *st, int pr, void *arg) case (CC_RELEASE_ERR): FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL); break; + case (CC_PROCEEDING_IND): + case (CC_ALERTING_IND): + break; default: - if (chanp->debug & 2048) { + if (chanp->debug & 0x800) { jiftime(tm, jiffies); sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n", tm, chanp->chan, pr); - HiSax_putstatus(chanp->sp, tmp); + HiSax_putstatus(chanp->cs, tmp); } } } static void -init_is(struct Channel *chanp, unsigned int ces) +init_d_st(struct Channel *chanp) { - struct PStack *st = &chanp->is; - struct IsdnCardState *sp = chanp->sp; + struct PStack *st = chanp->d_st; + struct IsdnCardState *cs = chanp->cs; char tmp[128]; - setstack_HiSax(st, sp); + HiSax_addlist(cs, st); + setstack_HiSax(st, cs); st->l2.sap = 0; - st->l2.tei = 255; - st->l2.ces = ces; - st->l2.extended = !0; - st->l2.laptype = LAPD; + st->l2.tei = -1; + st->l2.flag = 0; + test_and_set_bit(FLG_MOD128, &st->l2.flag); + test_and_set_bit(FLG_LAPD, &st->l2.flag); + test_and_set_bit(FLG_ORIG, &st->l2.flag); + st->l2.maxlen = MAX_DFRAME_LEN; st->l2.window = 1; - st->l2.orig = !0; - st->l2.t200 = 1000; /* 1000 milliseconds */ - if (st->protocol == ISDN_PTYPE_1TR6) { - st->l2.n200 = 3; /* try 3 times */ - st->l2.t203 = 10000; /* 10000 milliseconds */ - } else { - st->l2.n200 = 4; /* try 4 times */ - st->l2.t203 = 5000; /* 5000 milliseconds */ - } + st->l2.T200 = 1000; /* 1000 milliseconds */ + st->l2.N200 = 3; /* try 3 times */ + if (st->protocol == ISDN_PTYPE_1TR6) + st->l2.T203 = 10000; /* 10000 milliseconds */ + else + st->l2.T203 = 10000; /* 5000 milliseconds */ + sprintf(tmp, "Channel %d q.921", chanp->chan); setstack_isdnl2(st, tmp); setstack_isdnl3(st, chanp); - st->l4.userdata = chanp; - st->l4.l2writewakeup = NULL; + st->lli.userdata = chanp; + st->lli.l2writewakeup = NULL; st->l3.l3l4 = ll_handler; - st->l1.l1man = cc_l1man; - st->l2.l2man = cc_l2man; - st->pa = &chanp->para; - HiSax_addlist(sp, st); + st->l1.l1man = dc_l1man; + st->l2.l2man = dc_l2man; } static void @@ -1439,7 +1613,7 @@ callc_debug(struct FsmInst *fi, char *s) jiftime(tm, jiffies); sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s); - HiSax_putstatus(chanp->sp, str); + HiSax_putstatus(chanp->cs, str); } static void @@ -1449,8 +1623,8 @@ lc_debug(struct FsmInst *fi, char *s) struct LcFsm *lf = fi->userdata; jiftime(tm, jiffies); - sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s); - HiSax_putstatus(lf->ch->sp, str); + sprintf(str, "%s Channel %d dc %s\n", tm, lf->ch->chan, s); + HiSax_putstatus(lf->ch->cs, str); } static void @@ -1460,22 +1634,35 @@ dlc_debug(struct FsmInst *fi, char *s) struct LcFsm *lf = fi->userdata; jiftime(tm, jiffies); - sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s); - HiSax_putstatus(lf->ch->sp, str); + sprintf(str, "%s Channel %d bc %s\n", tm, lf->ch->chan, s); + HiSax_putstatus(lf->ch->cs, str); } static void lccall_d(struct LcFsm *lf, int pr, void *arg) { - struct Channel *chanp = lf->ch; + struct IsdnCardState *cs = lf->st->l1.hardware; + struct Channel *chanp; + int i; - switch (pr) { - case (LC_ESTABLISH): - FsmEvent(&chanp->fi, EV_DLEST, NULL); - break; - case (LC_RELEASE): - FsmEvent(&chanp->fi, EV_DLRL, NULL); - break; + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) { + chanp = lf->ch; + i = 1; + } else { + chanp = cs->channel; + i = 0; + } + while (i < 2) { + switch (pr) { + case (LC_ESTABLISH): + FsmEvent(&chanp->fi, EV_DLEST, NULL); + break; + case (LC_RELEASE): + FsmEvent(&chanp->fi, EV_DLRL, NULL); + break; + } + chanp++; + i++; } } @@ -1495,20 +1682,19 @@ lccall_b(struct LcFsm *lf, int pr, void *arg) } static void -init_chan(int chan, struct IsdnCardState *csta, int hscx, - unsigned int ces) +init_chan(int chan, struct IsdnCardState *csta) { struct Channel *chanp = csta->channel + chan; - chanp->sp = csta; - chanp->hscx = hscx; + chanp->cs = csta; + chanp->bcs = csta->bcs + chan; chanp->chan = chan; chanp->incoming = 0; chanp->debug = 0; chanp->Flags = 0; chanp->leased = 0; - chanp->impair = 0; - init_is(chanp, ces); + chanp->b_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC); + chanp->b_st->next = NULL; chanp->fi.fsm = &callcfsm; chanp->fi.state = ST_NULL; @@ -1517,58 +1703,72 @@ init_chan(int chan, struct IsdnCardState *csta, int hscx, chanp->fi.printdebug = callc_debug; FsmInitTimer(&chanp->fi, &chanp->dial_timer); FsmInitTimer(&chanp->fi, &chanp->drel_timer); - - chanp->lc_d.lcfi.fsm = &lcfsm; - chanp->lc_d.lcfi.state = ST_LC_NULL; - chanp->lc_d.lcfi.debug = 0; - chanp->lc_d.lcfi.userdata = &chanp->lc_d; - chanp->lc_d.lcfi.printdebug = lc_debug; - chanp->lc_d.type = LC_D; - chanp->lc_d.ch = chanp; - chanp->lc_d.st = &chanp->is; - chanp->lc_d.l2_establish = !0; - chanp->lc_d.l2_start = !0; - chanp->lc_d.lccall = lccall_d; - FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer); - - chanp->lc_b.lcfi.fsm = &lcfsm; - chanp->lc_b.lcfi.state = ST_LC_NULL; - chanp->lc_b.lcfi.debug = 0; - chanp->lc_b.lcfi.userdata = &chanp->lc_b; - chanp->lc_b.lcfi.printdebug = dlc_debug; - chanp->lc_b.type = LC_B; - chanp->lc_b.ch = chanp; - chanp->lc_b.st = &chanp->ds; - chanp->lc_b.l2_establish = !0; - chanp->lc_b.l2_start = !0; - chanp->lc_b.lccall = lccall_b; - FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer); - chanp->outcallref = 64; + if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + chanp->d_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC); + chanp->d_st->next = NULL; + init_d_st(chanp); + chanp->lc_d = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC); + chanp->lc_d->lcfi.fsm = &lcfsm; + chanp->lc_d->lcfi.state = ST_LC_NULL; + chanp->lc_d->lcfi.debug = 0; + chanp->lc_d->lcfi.userdata = chanp->lc_d; + chanp->lc_d->lcfi.printdebug = lc_debug; + chanp->lc_d->type = LC_D; + chanp->lc_d->delay = 0; + chanp->lc_d->ch = chanp; + chanp->lc_d->st = chanp->d_st; + chanp->lc_d->l2_establish = !0; + chanp->lc_d->l2_start = !0; + chanp->lc_d->lccall = lccall_d; + FsmInitTimer(&chanp->lc_d->lcfi, &chanp->lc_d->act_timer); + } else { + chanp->d_st = csta->channel->d_st; + chanp->lc_d = csta->channel->lc_d; + } + chanp->lc_b = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC); + chanp->lc_b->lcfi.fsm = &lcfsm; + chanp->lc_b->lcfi.state = ST_LC_NULL; + chanp->lc_b->lcfi.debug = 0; + chanp->lc_b->lcfi.userdata = chanp->lc_b; + chanp->lc_b->lcfi.printdebug = dlc_debug; + chanp->lc_b->type = LC_B; + chanp->lc_b->delay = DEFAULT_B_DELAY; + chanp->lc_b->ch = chanp; + chanp->lc_b->st = chanp->b_st; + chanp->lc_b->l2_establish = !0; + chanp->lc_b->l2_start = !0; + chanp->lc_b->lccall = lccall_b; + FsmInitTimer(&chanp->lc_b->lcfi, &chanp->lc_b->act_timer); chanp->data_open = 0; } int CallcNewChan(struct IsdnCardState *csta) { - int ces; - chancount += 2; - ces = randomces(); - init_chan(0, csta, 1, ces++); - ces %= 0xffff; - init_chan(1, csta, 0, ces++); + init_chan(0, csta); + init_chan(1, csta); printk(KERN_INFO "HiSax: 2 channels added\n"); +#ifdef LAYER2_WATCHING + printk(KERN_INFO "LAYER2 ESTABLISH\n"); + test_and_set_bit(FLG_START_D, &csta->channel->Flags); + FsmEvent(&csta->channel->lc_d->lcfi, EV_LC_ESTABLISH, NULL); +#endif return (2); } static void -release_is(struct Channel *chanp) +release_d_st(struct Channel *chanp) { - struct PStack *st = &chanp->is; + struct PStack *st = chanp->d_st; + if (!st) + return; releasestack_isdnl2(st); releasestack_isdnl3(st); HiSax_rmlist(st->l1.hardware, st); + kfree(st); + chanp->d_st = NULL; } void @@ -1579,27 +1779,45 @@ CallcFreeChan(struct IsdnCardState *csta) for (i = 0; i < 2; i++) { FsmDelTimer(&csta->channel[i].drel_timer, 74); FsmDelTimer(&csta->channel[i].dial_timer, 75); - FsmDelTimer(&csta->channel[i].lc_b.act_timer, 76); - FsmDelTimer(&csta->channel[i].lc_d.act_timer, 77); - if (csta->channel[i].Flags & FLG_START_B) { - release_ds(csta->channel + i); + FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77); + FsmDelTimer(&csta->channel[i].lc_b->act_timer, 76); + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) + release_d_st(csta->channel + i); + if (csta->channel[i].b_st) { + if (test_and_clear_bit(FLG_START_B, &csta->channel[i].Flags)) + release_b_st(csta->channel + i); + kfree(csta->channel[i].b_st); + csta->channel[i].b_st = NULL; + } else + printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i); + if (csta->channel[i].lc_b) { + kfree(csta->channel[i].lc_b); + csta->channel[i].b_st = NULL; } - release_is(csta->channel + i); + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + release_d_st(csta->channel + i); + FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77); + if (csta->channel[i].lc_d) { + kfree(csta->channel[i].lc_d); + csta->channel[i].d_st = NULL; + } else + printk(KERN_WARNING "CallcFreeChan lc_d ch%d allready freed\n", i); + } else + csta->channel[i].d_st = NULL; } } static void lldata_handler(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; struct sk_buff *skb = arg; switch (pr) { case (DL_DATA): if (chanp->data_open) - chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } break; @@ -1613,16 +1831,22 @@ lldata_handler(struct PStack *st, int pr, void *arg) static void lltrans_handler(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; struct sk_buff *skb = arg; switch (pr) { - case (PH_DATA): + case (PH_DATA_IND): if (chanp->data_open) - chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + if (chanp->lc_b->lcfi.state == ST_LC_DELAY) + FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL); + if (chanp->data_open) { + link_debug(chanp, "channel now open", 0); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, + chanp->chan, skb); + } else + dev_kfree_skb(skb); } break; default: @@ -1633,73 +1857,79 @@ lltrans_handler(struct PStack *st, int pr, void *arg) } static void -ll_writewakeup(struct PStack *st) +ll_writewakeup(struct PStack *st, int len) { - struct Channel *chanp = st->l4.userdata; + struct Channel *chanp = st->lli.userdata; isdn_ctrl ic; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BSENT; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + ic.parm.length = len; + chanp->cs->iif.statcallb(&ic); } static int -init_ds(struct Channel *chanp, int incoming) +init_b_st(struct Channel *chanp, int incoming) { - struct PStack *st = &chanp->ds; - struct IsdnCardState *sp = chanp->sp; - struct HscxState *hsp = sp->hs + chanp->hscx; + struct PStack *st = chanp->b_st; + struct IsdnCardState *cs = chanp->cs; char tmp[128]; - st->l1.hardware = sp; - - hsp->mode = 2; - - if (setstack_hscx(st, hsp)) + st->l1.hardware = cs; + chanp->bcs->mode = 2; + if (chanp->bcs->BC_SetStack(st, chanp->bcs)) return (-1); - - st->l2.extended = 0; - st->l2.laptype = LAPB; - st->l2.orig = !incoming; - st->l2.t200 = 1000; /* 1000 milliseconds */ + st->l2.flag = 0; + test_and_set_bit(FLG_LAPB, &st->l2.flag); + st->l2.maxlen = MAX_DATA_SIZE; + if (!incoming) + test_and_set_bit(FLG_ORIG, &st->l2.flag); + st->l2.T200 = 1000; /* 1000 milliseconds */ st->l2.window = 7; - st->l2.n200 = 4; /* try 4 times */ - st->l2.t203 = 5000; /* 5000 milliseconds */ - + st->l2.N200 = 4; /* try 4 times */ + st->l2.T203 = 5000; /* 5000 milliseconds */ st->l3.debug = 0; switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): sprintf(tmp, "Channel %d x.75", chanp->chan); setstack_isdnl2(st, tmp); st->l2.l2l3 = lldata_handler; - st->l1.l1man = dcc_l1man; - st->l2.l2man = dcc_l2man; - st->l2.l2tei = l2tei_dummy; - st->l4.userdata = chanp; - st->l4.l1writewakeup = NULL; - st->l4.l2writewakeup = ll_writewakeup; + st->l1.l1man = bc_l1man; + st->l2.l2man = bc_l2man; + st->lli.userdata = chanp; + st->lli.l1writewakeup = NULL; + st->lli.l2writewakeup = ll_writewakeup; st->l2.l2m.debug = chanp->debug & 16; st->l2.debug = chanp->debug & 64; st->ma.manl2(st, MDL_NOTEIPROC, NULL); - st->l1.hscxmode = 2; /* Packet-Mode ? */ - st->l1.hscxchannel = chanp->para.bchannel - 1; + st->l1.mode = L1_MODE_HDLC; + if (chanp->leased) + st->l1.bc = chanp->chan & 1; + else + st->l1.bc = chanp->proc->para.bchannel - 1; break; case (ISDN_PROTO_L2_HDLC): st->l1.l1l2 = lltrans_handler; - st->l1.l1man = dcc_l1man; - st->l4.userdata = chanp; - st->l4.l1writewakeup = ll_writewakeup; - st->l1.hscxmode = 2; - st->l1.hscxchannel = chanp->para.bchannel - 1; + st->l1.l1man = bc_l1man; + st->lli.userdata = chanp; + st->lli.l1writewakeup = ll_writewakeup; + st->l1.mode = L1_MODE_HDLC; + if (chanp->leased) + st->l1.bc = chanp->chan & 1; + else + st->l1.bc = chanp->proc->para.bchannel - 1; break; case (ISDN_PROTO_L2_TRANS): st->l1.l1l2 = lltrans_handler; - st->l1.l1man = dcc_l1man; - st->l4.userdata = chanp; - st->l4.l1writewakeup = ll_writewakeup; - st->l1.hscxmode = 1; - st->l1.hscxchannel = chanp->para.bchannel - 1; + st->l1.l1man = bc_l1man; + st->lli.userdata = chanp; + st->lli.l1writewakeup = ll_writewakeup; + st->l1.mode = L1_MODE_TRANS; + if (chanp->leased) + st->l1.bc = chanp->chan & 1; + else + st->l1.bc = chanp->proc->para.bchannel - 1; break; } return (0); @@ -1719,15 +1949,17 @@ distr_debug(struct IsdnCardState *csta, int debugflags) for (i = 0; i < 2; i++) { chanp[i].debug = debugflags; chanp[i].fi.debug = debugflags & 2; - chanp[i].is.l2.l2m.debug = debugflags & 8; - chanp[i].ds.l2.l2m.debug = debugflags & 16; - chanp[i].is.l2.debug = debugflags & 32; - chanp[i].ds.l2.debug = debugflags & 64; - chanp[i].lc_d.lcfi.debug = debugflags & 128; - chanp[i].lc_b.lcfi.debug = debugflags & 256; + chanp[i].d_st->l2.l2m.debug = debugflags & 8; + chanp[i].b_st->l2.l2m.debug = debugflags & 0x10; + chanp[i].d_st->l2.debug = debugflags & 0x20; + chanp[i].b_st->l2.debug = debugflags & 0x40; + chanp[i].lc_d->lcfi.debug = debugflags & 0x80; + chanp[i].lc_b->lcfi.debug = debugflags & 0x100; + chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200; + chanp[i].b_st->ma.debug = debugflags & 0x200; + chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000; } csta->dlogflag = debugflags & 4; - csta->teistack->l2.l2m.debug = debugflags & 512; } int @@ -1748,8 +1980,11 @@ HiSax_command(isdn_ctrl * ic) switch (ic->command) { case (ISDN_CMD_SETEAZ): chanp = csta->channel + ic->arg; - if (chanp->debug & 1) - link_debug(chanp, "SETEAZ", 1); + if (chanp->debug & 1) { + sprintf(tmp, "SETEAZ card %d %s", csta->cardnr + 1, + ic->parm.num); + link_debug(chanp, tmp, 1); + } break; case (ISDN_CMD_SETL2): chanp = csta->channel + (ic->arg & 0xff); @@ -1768,9 +2003,9 @@ HiSax_command(isdn_ctrl * ic) ic->parm.setup.si1, ic->parm.setup.si2); link_debug(chanp, tmp, 1); } - chanp->para.setup = ic->parm.setup; - if (!strcmp(chanp->para.setup.eazmsn, "0")) - chanp->para.setup.eazmsn[0] = '\0'; + chanp->setup = ic->parm.setup; + if (!strcmp(chanp->setup.eazmsn, "0")) + chanp->setup.eazmsn[0] = '\0'; /* this solution is dirty and may be change, if * we make a callreference based callmanager */ if (chanp->fi.state == ST_NULL) { @@ -1817,7 +2052,7 @@ HiSax_command(isdn_ctrl * ic) case (ISDN_CMD_LOCK): HiSax_mod_inc_use_count(); #ifdef MODULE - if (csta->channel[0].debug & 1024) { + if (csta->channel[0].debug & 0x400) { jiftime(tmp, jiffies); i = strlen(tmp); sprintf(tmp + i, " LOCK modcnt %lx\n", MOD_USE_COUNT); @@ -1828,7 +2063,7 @@ HiSax_command(isdn_ctrl * ic) case (ISDN_CMD_UNLOCK): HiSax_mod_dec_use_count(); #ifdef MODULE - if (csta->channel[0].debug & 1024) { + if (csta->channel[0].debug & 0x400) { jiftime(tmp, jiffies); i = strlen(tmp); sprintf(tmp + i, " UNLOCK modcnt %lx\n", MOD_USE_COUNT); @@ -1852,16 +2087,13 @@ HiSax_command(isdn_ctrl * ic) printk(KERN_DEBUG "HiSax: %s", tmp); break; case (2): - num = *(unsigned int *) ic->parm.num; - i = num >> 8; - if (i >= 2) - break; - chanp = csta->channel + i; - chanp->impair = num & 0xff; - if (chanp->debug & 1) { - sprintf(tmp, "IMPAIR %x", chanp->impair); - link_debug(chanp, tmp, 1); - } + num = *(unsigned int *) ic->parm.num; + csta->channel[0].lc_b->delay = num; + csta->channel[1].lc_b->delay = num; + sprintf(tmp, "delay card %d set to %d ms\n", + csta->cardnr + 1, num); + HiSax_putstatus(csta, tmp); + printk(KERN_DEBUG "HiSax: %s", tmp); break; case (3): for (i = 0; i < *(unsigned int *) ic->parm.num; i++) @@ -1872,35 +2104,47 @@ HiSax_command(isdn_ctrl * ic) HiSax_mod_inc_use_count(); break; case (5): /* set card in leased mode */ - csta->channel[0].leased = 1; - csta->channel[1].leased = 1; - sprintf(tmp, "card %d set into leased mode\n", - csta->cardnr + 1); - HiSax_putstatus(csta, tmp); + num = *(unsigned int *) ic->parm.num; + if ((num <1) || (num > 2)) { + sprintf(tmp, "Set LEASED wrong channel %d\n", + num); + HiSax_putstatus(csta, tmp); + printk(KERN_WARNING "HiSax: %s", tmp); + } else { + num--; + csta->channel[num].leased = 1; + csta->channel[num].lc_d->l2_establish = 0; + sprintf(tmp, "card %d channel %d set leased mode\n", + csta->cardnr + 1, num + 1); + HiSax_putstatus(csta, tmp); + FsmEvent(&csta->channel[num].lc_d->lcfi, EV_LC_ESTABLISH, NULL); + } + break; + case (6): /* set B-channel test loop */ + num = *(unsigned int *) ic->parm.num; + if (csta->stlist) + csta->stlist->ma.manl1(csta->stlist, + PH_TESTLOOP_REQ, (void *) num); break; #ifdef MODULE case (55): -#if (LINUX_VERSION_CODE < 0x020111) - MOD_USE_COUNT = MOD_VISITED; -#else MOD_USE_COUNT = 0; -#endif HiSax_mod_inc_use_count(); break; #endif /* MODULE */ case (11): csta->debug = *(unsigned int *) ic->parm.num; sprintf(tmp, "l1 debugging flags card %d set to %x\n", - csta->cardnr + 1, csta->debug); - HiSax_putstatus(cards[0].sp, tmp); + csta->cardnr + 1, csta->debug); + HiSax_putstatus(cards[0].cs, tmp); printk(KERN_DEBUG "HiSax: %s", tmp); break; case (13): - csta->channel[0].is.l3.debug = *(unsigned int *) ic->parm.num; - csta->channel[1].is.l3.debug = *(unsigned int *) ic->parm.num; + csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num; + csta->channel[1].d_st->l3.debug = *(unsigned int *) ic->parm.num; sprintf(tmp, "l3 debugging flags card %d set to %x\n", csta->cardnr + 1, *(unsigned int *) ic->parm.num); - HiSax_putstatus(cards[0].sp, tmp); + HiSax_putstatus(cards[0].cs, tmp); printk(KERN_DEBUG "HiSax: %s", tmp); break; default: @@ -1917,7 +2161,7 @@ HiSax_command(isdn_ctrl * ic) } int -HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) +HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb) { struct IsdnCardState *csta = hisax_findcard(id); struct Channel *chanp; @@ -1933,7 +2177,7 @@ HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) return -ENODEV; } chanp = csta->channel + chan; - st = &chanp->ds; + st = chanp->b_st; if (!chanp->data_open) { link_debug(chanp, "writebuf: channel not open", 1); return -EIO; @@ -1945,11 +2189,11 @@ HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) return -EINVAL; } if (len) { - if ((len + csta->hs[chanp->hscx].tx_cnt) > MAX_DATA_MEM) { + if ((len + chanp->bcs->tx_cnt) > MAX_DATA_MEM) { /* Must return 0 here, since this is not an error * but a temporary lack of resources. */ - if (chanp->debug & 2048) { + if (chanp->debug & 0x800) { sprintf(tmp, "writebuf: no buffers for %d bytes", len); link_debug(chanp, tmp, 1); } @@ -1959,12 +2203,13 @@ HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb) cli(); nskb = skb_clone(skb, GFP_ATOMIC); if (nskb) { - if (chanp->lc_b.l2_establish) { - csta->hs[chanp->hscx].tx_cnt += len + st->l2.ihsize; - chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, nskb); - } else { - csta->hs[chanp->hscx].tx_cnt += len; - chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, nskb); + if (!ack) + nskb->pkt_type = PACKET_NOACK; + if (chanp->lc_b->l2_establish) + st->l3.l3l2(st, DL_DATA, nskb); + else { + chanp->bcs->tx_cnt += len; + st->l2.l2l1(st, PH_DATA_REQ, nskb); } dev_kfree_skb(skb); } else diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 7379c8a77..67e308e43 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1,58 +1,53 @@ -/* $Id: config.c,v 1.15 1997/04/06 22:57:24 keil Exp $ +/* $Id: config.c,v 2.12 1998/02/11 17:28:02 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden * * * $Log: config.c,v $ - * Revision 1.15 1997/04/06 22:57:24 keil - * Hisax version 2.1 + * Revision 2.12 1998/02/11 17:28:02 keil + * Niccy PnP/PCI support * - * Revision 1.14 1997/03/25 23:11:22 keil - * US NI-1 protocol + * Revision 2.11 1998/02/09 21:26:13 keil + * fix export module for 2.1 * - * Revision 1.13 1997/03/23 21:45:49 keil - * Add support for ELSA PCMCIA + * Revision 2.10 1998/02/09 18:46:05 keil + * Support for Sedlbauer PCMCIA (Marcus Niemann) * - * Revision 1.12 1997/03/11 21:01:43 keil - * nzproto is only used with modules + * Revision 2.9 1998/02/03 23:31:28 keil + * add AMD7930 support * - * Revision 1.11 1997/02/14 12:23:12 fritz - * Added support for new insmod parameter handling. + * Revision 2.8 1998/02/02 13:32:59 keil + * New card support * - * Revision 1.10 1997/02/14 09:22:09 keil - * Final 2.0 version + * Revision 2.7 1998/01/31 21:41:44 keil + * changes for newer 2.1 kernels * - * Revision 1.9 1997/02/10 11:45:09 fritz - * More changes for Kernel 2.1.X compatibility. + * Revision 2.6 1997/11/08 21:35:43 keil + * new l1 init * - * Revision 1.8 1997/02/09 00:28:05 keil - * new interface handling, one interface per card - * default protocol now works again + * Revision 2.5 1997/11/06 17:15:08 keil + * New 2.1 init; PCMCIA wrapper changes * - * Revision 1.7 1997/01/27 15:56:57 keil - * Teles PCMCIA ITK ix1 micro added + * Revision 2.4 1997/10/29 19:07:52 keil + * changes for 2.1 * - * Revision 1.6 1997/01/21 22:17:56 keil - * new module load syntax + * Revision 2.3 1997/10/01 09:21:33 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.5 1997/01/09 18:28:20 keil - * cosmetic cleanups + * Revision 2.2 1997/09/11 17:24:46 keil + * Add new cards * - * Revision 1.4 1996/11/05 19:35:17 keil - * using config.h; some spelling fixes - * - * Revision 1.3 1996/10/23 17:23:28 keil - * default config changes - * - * Revision 1.2 1996/10/23 11:58:48 fritz - * Changed default setup to reflect user's selection of supported - * cards/protocols. - * - * Revision 1.1 1996/10/13 20:04:51 keil - * Initial revision + * Revision 2.1 1997/07/27 21:41:35 keil + * version change * + * Revision 2.0 1997/06/26 11:06:28 keil + * New card and L1 interface. + * Eicon.Diehl Diva and Dynalink IS64PH support * + * old changes removed /KKe * */ #include <linux/types.h> @@ -68,16 +63,30 @@ * { type, protocol, p0, p1, p2, NULL } * * type - * 1 Teles 16.0 p0=irq p1=membase p2=iobase - * 2 Teles 8.0 p0=irq p1=membase - * 3 Teles 16.3 p0=irq p1=iobase - * 4 Creatix PNP p0=irq p1=IO0 (ISAC) p2=IO1 (HSCX) - * 5 AVM A1 (Fritz) p0=irq p1=iobase - * 6 ELSA PC [p0=iobase] or nothing (autodetect) - * 7 ELSA Quickstep p0=irq p1=iobase - * ELSA PCMCIA p0=irq p1=iobase - * 8 Teles PCMCIA p0=irq p1=iobase - * 9 ITK ix1-micro p0=irq p1=iobase + * 1 Teles 16.0 p0=irq p1=membase p2=iobase + * 2 Teles 8.0 p0=irq p1=membase + * 3 Teles 16.3 p0=irq p1=iobase + * 4 Creatix PNP p0=irq p1=IO0 (ISAC) p2=IO1 (HSCX) + * 5 AVM A1 (Fritz) p0=irq p1=iobase + * 6 ELSA PC [p0=iobase] or nothing (autodetect) + * 7 ELSA Quickstep p0=irq p1=iobase + * 8 Teles PCMCIA p0=irq p1=iobase + * 9 ITK ix1-micro p0=irq p1=iobase + * 10 ELSA PCMCIA p0=irq p1=iobase + * 11 Eicon.Diehl Diva p0=irq p1=iobase + * 12 Asuscom ISDNLink p0=irq p1=iobase + * 13 Teleint p0=irq p1=iobase + * 14 Teles 16.3c p0=irq p1=iobase + * 15 Sedlbauer speed p0=irq p1=iobase + * 16 USR Sportster internal p0=irq p1=iobase + * 17 MIC card p0=irq p1=iobase + * 18 ELSA Quickstep 1000PCI no parameter + * 19 Compaq ISDN S0 ISA card p0=irq p1=IO0 (HSCX) p2=IO1 (ISAC) p3=IO2 + * 20 Travers Technologies NETjet PCI card + * 21 reserved TELES PCI + * 22 Sedlbauer Speed Star p0=irq p1=iobase + * 23 reserved + * 24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only) * * * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 @@ -85,38 +94,108 @@ * */ -#ifdef CONFIG_HISAX_ELSA_PCC +#ifdef CONFIG_HISAX_ELSA #define DEFAULT_CARD ISDN_CTYPE_ELSA -#define DEFAULT_CFG {0,0,0} -#endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA -#define DEFAULT_CARD ISDN_CTYPE_ELSA_QS1000 -#define DEFAULT_CFG {3,0x2f8,0} +#define DEFAULT_CFG {0,0,0,0} +int elsa_init_pcmcia(void*, int, int*, int); +EXPORT_SYMBOL(elsa_init_pcmcia); #endif #ifdef CONFIG_HISAX_AVM_A1 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_A1 -#define DEFAULT_CFG {10,0x340,0} +#define DEFAULT_CFG {10,0x340,0,0} #endif #ifdef CONFIG_HISAX_16_3 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_16_3 -#define DEFAULT_CFG {15,0x180,0} +#define DEFAULT_CFG {15,0x180,0,0} #endif #ifdef CONFIG_HISAX_16_0 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_16_0 -#define DEFAULT_CFG {15,0xd0000,0xd80} +#define DEFAULT_CFG {15,0xd0000,0xd80,0} #endif #ifdef CONFIG_HISAX_IX1MICROR2 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2 -#define DEFAULT_CFG {5,0x390,0} +#define DEFAULT_CFG {5,0x390,0,0} +#endif + +#ifdef CONFIG_HISAX_DIEHLDIVA +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA +#define DEFAULT_CFG {0,0x0,0,0} +#endif + +#ifdef CONFIG_HISAX_ASUSCOM +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM +#define DEFAULT_CFG {5,0x200,0,0} +#endif + +#ifdef CONFIG_HISAX_TELEINT +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELEINT +#define DEFAULT_CFG {5,0x300,0,0} +#endif + +#ifdef CONFIG_HISAX_SEDLBAUER +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER +#define DEFAULT_CFG {11,0x270,0,0} +int sedl_init_pcmcia(void*, int, int*, int); +EXPORT_SYMBOL(sedl_init_pcmcia); +#endif + +#ifdef CONFIG_HISAX_SPORTSTER +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SPORTSTER +#define DEFAULT_CFG {7,0x268,0,0} +#endif + +#ifdef CONFIG_HISAX_MIC +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_MIC +#define DEFAULT_CFG {12,0x3e0,0,0} +#endif + +#ifdef CONFIG_HISAX_NETJET +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_NETJET +#define DEFAULT_CFG {0,0,0,0} +#endif + +#ifdef CONFIG_HISAX_TELES3C +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELES3C +#define DEFAULT_CFG {5,0x500,0,0} +#endif + +#ifdef CONFIG_HISAX_AMD7930 +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_AMD7930 +#define DEFAULT_CFG {12,0x3e0,0,0} +#endif + +#ifdef CONFIG_HISAX_NICCY +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_NICCY +#define DEFAULT_CFG {0,0x0,0,0} #endif #ifdef CONFIG_HISAX_1TR6 @@ -150,7 +229,7 @@ NULL, \ } -#define EMPTY_CARD {0, DEFAULT_PROTO, {0, 0, 0}, NULL} +#define EMPTY_CARD {0, DEFAULT_PROTO, {0, 0, 0, 0}, NULL} struct IsdnCard cards[] = { @@ -172,55 +251,63 @@ struct IsdnCard cards[] = EMPTY_CARD, }; -static char HiSaxID[96] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ +static char HiSaxID[96] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; -char *HiSax_id = HiSaxID; +char *HiSax_id HISAX_INITDATA = HiSaxID; #ifdef MODULE /* Variables for insmod */ -static int type[] = +static int type[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static int protocol[] = +static int protocol[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static int io[] = +static int io[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ -static int io0[] = +#undef IO0_IO1 +#ifdef CONFIG_HISAX_16_3 +#define IO0_IO1 +#endif +#ifdef CONFIG_HISAX_NICCY +#undef IO0_IO1 +#define IO0_IO1 +#endif +#ifdef IO0_IO1 +static int io0[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static int io1[] = +static int io1[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #endif -static int irq[] = +static int irq[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static int mem[] = +static int mem[] HISAX_INITDATA = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static char *id = HiSaxID; +static char *id HISAX_INITDATA = HiSaxID; -#if (LINUX_VERSION_CODE > 0x020111) MODULE_AUTHOR("Karsten Keil"); -MODULE_PARM(type, "1-16i"); -MODULE_PARM(protocol, "1-16i"); -MODULE_PARM(io, "1-16i"); -MODULE_PARM(irq, "1-16i"); -MODULE_PARM(mem, "1-16i"); +MODULE_PARM(type, "1-3i"); +MODULE_PARM(protocol, "1-2i"); +MODULE_PARM(io, "1-8i"); +MODULE_PARM(irq, "1-2i"); +MODULE_PARM(mem, "1-12i"); MODULE_PARM(id, "s"); #ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ -MODULE_PARM(io0, "1-16i"); -MODULE_PARM(io1, "1-16i"); -#endif +MODULE_PARM(io0, "1-8i"); +MODULE_PARM(io1, "1-8i"); #endif #endif +int nrcards; + extern char *l1_revision; extern char *l2_revision; extern char *l3_revision; -extern char *l4_revision; +extern char *lli_revision; extern char *tei_revision; -char * -HiSax_getrev(const char *revision) +HISAX_INITFUNC(char * +HiSax_getrev(const char *revision)) { char *rev; char *p; @@ -234,7 +321,27 @@ HiSax_getrev(const char *revision) return rev; } -int nrcards; +HISAX_INITFUNC(void +HiSaxVersion(void)) +{ + char tmp[64], rev[64]; + char *r = rev; + + strcpy(tmp, l1_revision); + r += sprintf(r, "%s/", HiSax_getrev(tmp)); + strcpy(tmp, l2_revision); + r += sprintf(r, "%s/", HiSax_getrev(tmp)); + strcpy(tmp, l3_revision); + r += sprintf(r, "%s/", HiSax_getrev(tmp)); + strcpy(tmp, lli_revision); + r += sprintf(r, "%s/", HiSax_getrev(tmp)); + strcpy(tmp, tei_revision); + r += sprintf(r, "%s", HiSax_getrev(tmp)); + + printk(KERN_INFO "HiSax: Driver for Siemens chip set ISDN cards\n"); + printk(KERN_INFO "HiSax: Version 2.8\n"); + printk(KERN_INFO "HiSax: Revisions %s\n", rev); +} void HiSax_mod_dec_use_count(void) @@ -251,8 +358,8 @@ HiSax_mod_inc_use_count(void) #ifdef MODULE #define HiSax_init init_module #else -void -HiSax_setup(char *str, int *ints) +__initfunc(void +HiSax_setup(char *str, int *ints)) { int i, j, argc; @@ -297,31 +404,28 @@ HiSax_setup(char *str, int *ints) } #endif -int -HiSax_init(void) +__initfunc(int +HiSax_init(void)) { int i; - char tmp[64], rev[64]; - char *r = rev; + #ifdef MODULE int nzproto = 0; +#ifdef CONFIG_HISAX_ELSA + if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) { + /* we have exported and return in this case */ + return 0; + } +#endif +#ifdef CONFIG_HISAX_SEDLBAUER + if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) { + /* we have to export and return in this case */ + return 0; + } +#endif #endif + HiSaxVersion(); nrcards = 0; - strcpy(tmp, l1_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l2_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l3_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l4_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, tei_revision); - r += sprintf(r, "%s", HiSax_getrev(tmp)); - - printk(KERN_NOTICE "HiSax: Driver for Siemens chip set ISDN cards\n"); - printk(KERN_NOTICE "HiSax: Version 2.1\n"); - printk(KERN_NOTICE "HiSax: Revisions %s\n", rev); - #ifdef MODULE if (id) /* If id= string used */ HiSax_id = id; @@ -343,37 +447,44 @@ HiSax_init(void) cards[i].para[1] = mem[i]; break; - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_TELESPCMCIA: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; - break; - -#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ +#ifdef IO0_IO1 case ISDN_CTYPE_PNP: + case ISDN_CTYPE_NICCY: cards[i].para[0] = irq[i]; cards[i].para[1] = io0[i]; cards[i].para[2] = io1[i]; break; -#endif - case ISDN_CTYPE_A1: + case ISDN_CTYPE_COMPAQ_ISA: cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; + cards[i].para[1] = io0[i]; + cards[i].para[2] = io1[i]; + cards[i].para[3] = io[i]; break; - +#endif case ISDN_CTYPE_ELSA: cards[i].para[0] = io[i]; break; - case ISDN_CTYPE_ELSA_QS1000: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; - break; - + case ISDN_CTYPE_16_3: + case ISDN_CTYPE_TELESPCMCIA: + case ISDN_CTYPE_A1: + case ISDN_CTYPE_ELSA_PNP: + case ISDN_CTYPE_ELSA_PCMCIA: case ISDN_CTYPE_IX1MICROR2: + case ISDN_CTYPE_DIEHLDIVA: + case ISDN_CTYPE_ASUSCOM: + case ISDN_CTYPE_TELEINT: + case ISDN_CTYPE_SEDLBAUER: + case ISDN_CTYPE_SEDLBAUER_PCMCIA: + case ISDN_CTYPE_SPORTSTER: + case ISDN_CTYPE_MIC: + case ISDN_CTYPE_TELES3C: cards[i].para[0] = irq[i]; cards[i].para[1] = io[i]; break; - + case ISDN_CTYPE_ELSA_PCI: + case ISDN_CTYPE_NETJET: + case ISDN_CTYPE_AMD7930: + break; } } if (!nzproto) { @@ -394,20 +505,20 @@ HiSax_init(void) CallcNew(); Isdnl2New(); - if (HiSax_inithardware()) { + TeiNew(); + Isdnl1New(); + if (HiSax_inithardware(NULL)) { /* Install only, if at least one card found */ /* No symbols to export, hide all symbols */ #ifdef MODULE -#if (LINUX_VERSION_CODE < 0x020111) - register_symtab(NULL); -#else EXPORT_NO_SYMBOLS; -#endif - printk(KERN_NOTICE "HiSax: module installed\n"); + printk(KERN_INFO "HiSax: module installed\n"); #endif return (0); } else { + Isdnl1Free(); + TeiFree(); Isdnl2Free(); CallcFree(); return -EIO; @@ -419,7 +530,101 @@ void cleanup_module(void) { HiSax_closehardware(); - printk(KERN_NOTICE "HiSax module removed\n"); + printk(KERN_INFO "HiSax module removed\n"); } +#ifdef CONFIG_HISAX_ELSA +int elsa_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) +{ + int i; + int nzproto = 0; + + nrcards = 0; + HiSaxVersion(); + if (id) /* If id= string used */ + HiSax_id = id; + /* Initialize all 16 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < 16; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = 10; + nzproto = 1; + + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < 16; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl2New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); + return (0); +} +#endif +#ifdef CONFIG_HISAX_SEDLBAUER +int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) +{ + int i; + int nzproto = 0; + + nrcards = 0; + HiSaxVersion(); + if (id) /* If id= string used */ + HiSax_id = id; + /* Initialize all 16 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < 16; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA; + nzproto = 1; + + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < 16; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl2New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); + return (0); +} #endif +#endif diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c new file mode 100644 index 000000000..fa3a45b72 --- /dev/null +++ b/drivers/isdn/hisax/diva.c @@ -0,0 +1,465 @@ +/* $Id: diva.c,v 1.5 1998/02/02 13:29:38 keil Exp $ + + * diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations + * + * + * $Log: diva.c,v $ + * Revision 1.5 1998/02/02 13:29:38 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:44 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:13:33 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:55:55 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 1.1 1997/09/18 17:11:20 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include <linux/config.h> +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include <linux/pci.h> +#include <linux/bios32.h> + +extern const char *CardType[]; + +const char *Diva_revision = "$Revision: 1.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define DIVA_HSCX_DATA 0 +#define DIVA_HSCX_ADR 4 +#define DIVA_ISA_ISAC_DATA 2 +#define DIVA_ISA_ISAC_ADR 6 +#define DIVA_ISA_CTRL 7 + +#define DIVA_PCI_ISAC_DATA 8 +#define DIVA_PCI_ISAC_ADR 0xc +#define DIVA_PCI_CTRL 0x10 + +/* SUB Types */ +#define DIVA_ISA 1 +#define DIVA_PCI 2 + +/* PCI stuff */ +#define PCI_VENDOR_EICON_DIEHL 0x1133 +#define PCI_DIVA20PRO_ID 0xe001 +#define PCI_DIVA20_ID 0xe002 +#define PCI_DIVA20PRO_U_ID 0xe003 +#define PCI_DIVA20_U_ID 0xe004 + +/* CTRL (Read) */ +#define DIVA_IRQ_STAT 0x01 +#define DIVA_EEPROM_SDA 0x02 +/* CTRL (Write) */ +#define DIVA_IRQ_REQ 0x01 +#define DIVA_RESET 0x08 +#define DIVA_EEPROM_CLK 0x40 +#define DIVA_PCI_LED_A 0x10 +#define DIVA_PCI_LED_B 0x20 +#define DIVA_ISA_LED_A 0x20 +#define DIVA_ISA_LED_B 0x40 +#define DIVA_IRQ_CLR 0x80 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return(readreg(cs->hw.diva.hscx_adr, + cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.diva.hscx_adr, + cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +diva_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval, stat = 0; + int cnt=8; + + if (!cs) { + printk(KERN_WARNING "Diva: Spurious interrupt!\n"); + return; + } + while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) { + val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40); + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + cnt--; + } + if (!cnt) + printk(KERN_WARNING "Diva: IRQ LOOP\n"); + if (stat & 1) { + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_diva(struct IsdnCardState *cs) +{ + int bytecnt; + + del_timer(&cs->hw.diva.tl); + if (cs->subtyp == DIVA_ISA) + bytecnt = 8; + else + bytecnt = 32; + if (cs->hw.diva.cfg_reg) { + byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */ + release_region(cs->hw.diva.cfg_reg, bytecnt); + } +} + +static void +reset_diva(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + cs->hw.diva.ctrl_reg = 0; /* Reset On */ + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */ + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + if (cs->subtyp == DIVA_ISA) + cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; + else + cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A; + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); +} + +#define DIVA_ASSIGN 1 + +static void +diva_led_handler(struct IsdnCardState *cs) +{ + int blink = 0; + + del_timer(&cs->hw.diva.tl); + if (cs->hw.diva.status & DIVA_ASSIGN) + cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_A : DIVA_PCI_LED_A; + else { + cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_A : DIVA_PCI_LED_A; + blink = 250; + } + if (cs->hw.diva.status & 0xf000) + cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B; + else if (cs->hw.diva.status & 0x0f00) { + cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B; + blink = 500; + } else + cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B); + + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + if (blink) { + init_timer(&cs->hw.diva.tl); + cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000); + add_timer(&cs->hw.diva.tl); + } +} + +static int +Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_diva(cs); + return(0); + case CARD_RELEASE: + release_io_diva(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &diva_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + case MDL_REMOVE_REQ: + cs->hw.diva.status = 0; + break; + case MDL_ASSIGN_REQ: + cs->hw.diva.status |= DIVA_ASSIGN; + break; + case MDL_INFO_SETUP: + if ((int)arg) + cs->hw.diva.status |= 0x0200; + else + cs->hw.diva.status |= 0x0100; + break; + case MDL_INFO_CONN: + if ((int)arg) + cs->hw.diva.status |= 0x2000; + else + cs->hw.diva.status |= 0x1000; + break; + case MDL_INFO_REL: + if ((int)arg) { + cs->hw.diva.status &= ~0x2000; + cs->hw.diva.status &= ~0x0200; + } else { + cs->hw.diva.status &= ~0x1000; + cs->hw.diva.status &= ~0x0100; + } + break; + } + diva_led_handler(cs); + return(0); +} + + + +static int pci_index __initdata = 0; + +__initfunc(int +setup_diva(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Diva_revision); + printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_DIEHLDIVA) + return(0); + cs->hw.diva.status = 0; + if (card->para[1]) { + cs->subtyp = DIVA_ISA; + cs->hw.diva.ctrl_reg = 0; + cs->hw.diva.cfg_reg = card->para[1]; + cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL; + cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA; + cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR; + cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR; + cs->irq = card->para[0]; + bytecnt = 8; + } else { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Diva: No PCI card found\n"); + return(0); + } + if (!pci_irq) { + printk(KERN_WARNING "Diva: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.diva.cfg_reg = pci_ioaddr; + cs->hw.diva.ctrl = pci_ioaddr + DIVA_PCI_CTRL; + cs->hw.diva.isac = pci_ioaddr + DIVA_PCI_ISAC_DATA; + cs->hw.diva.hscx = pci_ioaddr + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = pci_ioaddr + DIVA_PCI_ISAC_ADR; + cs->hw.diva.hscx_adr = pci_ioaddr + DIVA_HSCX_ADR; + cs->irq = pci_irq; + bytecnt = 32; +#else + printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Diva: unable to config DIVA PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } + + printk(KERN_INFO + "Diva: %s card configured at 0x%x IRQ %d\n", + (cs->subtyp == DIVA_ISA) ? "ISA" : "PCI", + cs->hw.diva.cfg_reg, cs->irq); + if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.diva.cfg_reg, + cs->hw.diva.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn"); + } + + reset_diva(cs); + cs->hw.diva.tl.function = (void *) diva_led_handler; + cs->hw.diva.tl.data = (long) cs; + init_timer(&cs->hw.diva.tl); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Diva_card_msg; + + ISACVersion(cs, "Diva:"); + if (HscxVersion(cs, "Diva:")) { + printk(KERN_WARNING + "Diva: wrong HSCX versions check IO address\n"); + release_io_diva(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index 5cea59d9d..6a9966431 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1,4 +1,4 @@ -/* $Id: elsa.c,v 1.14 1997/04/13 19:53:25 keil Exp $ +/* $Id: elsa.c,v 2.6 1998/02/02 13:29:40 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * @@ -8,1241 +8,726 @@ * * * $Log: elsa.c,v $ - * Revision 1.14 1997/04/13 19:53:25 keil - * Fixed QS1000 init, change in IRQ check delay for SMP + * Revision 2.6 1998/02/02 13:29:40 keil + * fast io * - * Revision 1.13 1997/04/07 22:58:07 keil - * need include config.h - * - * Revision 1.12 1997/04/06 22:54:14 keil - * Using SKB's + * Revision 2.5 1998/01/31 21:41:45 keil + * changes for newer 2.1 kernels * - * Revision 1.11 1997/03/23 21:45:46 keil - * Add support for ELSA PCMCIA + * Revision 2.4 1997/11/08 21:35:46 keil + * new l1 init * - * Revision 1.10 1997/03/12 21:42:19 keil - * Bugfix: IRQ hangs with QS1000 + * Revision 2.3 1997/11/06 17:15:09 keil + * New 2.1 init; PCMCIA wrapper changes * - * Revision 1.9 1997/03/04 15:57:39 keil - * bugfix IRQ reset Quickstep, ELSA PC changes, some stuff for new cards + * Revision 2.2 1997/10/29 18:57:09 keil + * changes for 2.1.60, arcofi support * - * Revision 1.8 1997/01/27 15:51:48 keil - * SMP proof,cosmetics + * Revision 2.1 1997/07/27 21:47:08 keil + * new interface structures * - * Revision 1.7 1997/01/21 22:20:48 keil - * Elsa Quickstep support + * Revision 2.0 1997/06/26 11:02:40 keil + * New Layer and card interface * - * Revision 1.6 1997/01/09 18:22:46 keil - * one more PCC-8 fix - * - * Revision 1.5 1996/12/08 19:46:14 keil - * PCC-8 correct IRQs; starting ARCOFI support - * - * Revision 1.4 1996/11/18 20:50:54 keil - * with PCF Pro release 16 Byte IO - * - * Revision 1.3 1996/11/18 15:33:04 keil - * PCC and PCFPro support + * Revision 1.14 1997/04/13 19:53:25 keil + * Fixed QS1000 init, change in IRQ check delay for SMP * - * Revision 1.2 1996/10/27 22:08:03 keil - * cosmetic changes + * Revision 1.13 1997/04/07 22:58:07 keil + * need include config.h * - * Revision 1.1 1996/10/13 20:04:52 keil - * Initial revision + * Revision 1.12 1997/04/06 22:54:14 keil + * Using SKB's * + * old changes removed KKe * */ -#define ARCOFI_USE 0 - #define __NO_VERSION__ #include <linux/config.h> -#include "siemens.h" #include "hisax.h" -#include "elsa.h" +#include "arcofi.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" #include "isdnl1.h" -#include <linux/kernel_stat.h> +#include <linux/pci.h> +#include <linux/bios32.h> extern const char *CardType[]; -const char *Elsa_revision = "$Revision: 1.14 $"; +const char *Elsa_revision = "$Revision: 2.6 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", - "PCMCIA", "QS 1000", "QS 3000"}; + "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI"}; const char *ITACVer[] = {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2", "B1", "A1"}; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ELSA_ISAC 0 +#define ELSA_ISAC_PCM 1 +#define ELSA_ITAC 1 +#define ELSA_HSCX 2 +#define ELSA_ALE 3 +#define ELSA_ALE_PCM 4 +#define ELSA_CONTROL 4 +#define ELSA_CONFIG 5 +#define ELSA_START_TIMER 6 +#define ELSA_TRIG_IRQ 7 + +#define ELSA_PC 1 +#define ELSA_PCC8 2 +#define ELSA_PCC16 3 +#define ELSA_PCF 4 +#define ELSA_PCFPRO 5 +#define ELSA_PCMCIA 6 +#define ELSA_QS1000 7 +#define ELSA_QS3000 8 +#define ELSA_QS1000PCI 9 + +/* PCI stuff */ +#define PCI_VENDOR_ELSA 0x1048 +#define PCI_QS1000_ID 0x1000 + + +/* ITAC Registeradressen (only Microlink PC) */ +#define ITAC_SYS 0x34 +#define ITAC_ISEN 0x48 +#define ITAC_RFIE 0x4A +#define ITAC_XFIE 0x4C +#define ITAC_SCIE 0x4E +#define ITAC_STIE 0x46 + +/*** *** + *** Makros als Befehle fuer die Kartenregister *** + *** (mehrere Befehle werden durch Bit-Oderung kombiniert) *** + *** ***/ + +/* Config-Register (Read) */ +#define ELSA_TIMER_RUN 0x02 /* Bit 1 des Config-Reg */ +#define ELSA_TIMER_RUN_PCC8 0x01 /* Bit 0 des Config-Reg bei PCC */ +#define ELSA_IRQ_IDX 0x38 /* Bit 3,4,5 des Config-Reg */ +#define ELSA_IRQ_IDX_PCC8 0x30 /* Bit 4,5 des Config-Reg */ +#define ELSA_IRQ_IDX_PC 0x0c /* Bit 2,3 des Config-Reg */ + +/* Control-Register (Write) */ +#define ELSA_LINE_LED 0x02 /* Bit 1 Gelbe LED */ +#define ELSA_STAT_LED 0x08 /* Bit 3 Gruene LED */ +#define ELSA_ISDN_RESET 0x20 /* Bit 5 Reset-Leitung */ +#define ELSA_ENA_TIMER_INT 0x80 /* Bit 7 Freigabe Timer Interrupt */ + +/* ALE-Register (Read) */ +#define ELSA_HW_RELEASE 0x07 /* Bit 0-2 Hardwarerkennung */ +#define ELSA_S0_POWER_BAD 0x08 /* Bit 3 S0-Bus Spannung fehlt */ + +/* Status Flags */ +#define ELSA_TIMER_AKTIV 1 +#define ELSA_BAD_PWR 2 +#define ELSA_ASSIGN 4 static inline u_char -readhscx(unsigned int adr, int hscx, u_char off) +readreg(unsigned int ale, unsigned int adr, u_char off) { register u_char ret; long flags; save_flags(flags); cli(); - byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20)); - ret = bytein(adr + CARD_HSCX); + byteout(ale, off); + ret = bytein(adr); restore_flags(flags); return (ret); } static inline void -read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { /* fifo read without cli because it's allready done */ - byteout(adr + CARD_ALE, (hscx ? 0x40 : 0)); - insb(adr + CARD_HSCX, data, size); + byteout(ale, off); + insb(adr, data, size); } static inline void -writehscx(unsigned int adr, int hscx, u_char off, u_char data) +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) { long flags; save_flags(flags); cli(); - byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20)); - byteout(adr + CARD_HSCX, data); + byteout(ale, off); + byteout(adr, data); restore_flags(flags); } static inline void -write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { /* fifo write without cli because it's allready done */ - byteout(adr + CARD_ALE, (hscx ? 0x40 : 0)); - outsb(adr + CARD_HSCX, data, size); -} - -static inline u_char -readisac(unsigned int adr, u_char off) -{ - register u_char ret; - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off + 0x20); - ret = bytein(adr + CARD_ISAC); - restore_flags(flags); - return (ret); + byteout(ale, off); + outsb(adr, data, size); } -static inline void -read_fifo_isac(unsigned int adr, u_char * data, int size) -{ - /* fifo read without cli because it's allready done */ - - byteout(adr + CARD_ALE, 0); - insb(adr + CARD_ISAC, data, size); -} - - -static inline void -writeisac(unsigned int adr, u_char off, u_char data) -{ - long flags; +/* Interface functions */ - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off + 0x20); - byteout(adr + CARD_ISAC, data); - restore_flags(flags); -} - -static inline void -write_fifo_isac(unsigned int adr, u_char * data, int size) -{ - /* fifo write without cli because it's allready done */ - - byteout(adr + CARD_ALE, 0); - outsb(adr + CARD_ISAC, data, size); -} - -#ifdef CONFIG_HISAX_ELSA_PCC -static inline u_char -readitac(unsigned int adr, u_char off) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - register u_char ret; - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off); - ret = bytein(adr + CARD_ITAC); - restore_flags(flags); - return (ret); + return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset)); } -static inline void -writeitac(unsigned int adr, u_char off, u_char data) +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off); - byteout(adr + CARD_ITAC, data); - restore_flags(flags); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value); } -static inline int -TimerRun(struct IsdnCardState *sp) +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - register u_char val; - - val = bytein(sp->cfg_reg + CARD_CONFIG); - if (sp->subtyp == ELSA_QS1000) - return (0 == (val & TIMER_RUN)); - else if (sp->subtyp == ELSA_PCC8) - return (val & TIMER_RUN_PCC8); - return (val & TIMER_RUN); + readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size); } -static inline void -elsa_led_handler(struct IsdnCardState *sp) +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - - u_char outval = 0xf0; - int stat = 0, cval; - - - if ((sp->ph_state == 0) || (sp->ph_state == 15)) { - stat = 1; - } else { - if (sp->hs[0].mode != 0) - stat |= 2; - if (sp->hs[1].mode != 0) - stat |= 4; - } - cval = (sp->counter >> 6) & 3; - switch (cval) { - case 0: - if (!stat) - outval |= STAT_LED; - else if (stat == 1) - outval |= LINE_LED | STAT_LED; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 1: - if (!stat) - outval |= LINE_LED; - else if (stat == 1) - outval |= LINE_LED | STAT_LED; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 2: - if (!stat) - outval |= STAT_LED; - else if (stat == 1) - outval |= 0; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 3: - if (!stat) - outval |= LINE_LED; - break; - } - byteout(sp->cfg_reg + CARD_CONTROL, outval); + writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size); } -#endif -static inline void -waitforCEC(int adr, int hscx) +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) { - int to = 50; - - while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Elsa: waitforCEC timeout\n"); + return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80)); } - -static inline void -waitforXFW(int adr, int hscx) +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) { - int to = 50; - - while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Elsa: waitforXFW timeout\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value); } -static inline void -writehscxCMDR(int adr, int hscx, u_char data) +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr, hscx); - writehscx(adr, hscx, HSCX_CMDR, data); - restore_flags(flags); + readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size); } -/* - * fast interrupt here - */ - - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->cfg_reg, hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_EXIR)); + writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size); } -void -elsa_report(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readisac(sp->cfg_reg, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readisac(sp->cfg_reg, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readisac(sp->cfg_reg, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + return (readreg(cs->hw.elsa.ale, + cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0))); } -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.elsa.ale, + cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value); } -static void -hscx_fill_fifo(struct HscxState *hsp) +static inline u_char +readitac(struct IsdnCardState *cs, u_char off) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; + register u_char ret; long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->cfg_reg, hsp->hscx); save_flags(flags); cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count); - writehscxCMDR(sp->cfg_reg, hsp->hscx, more ? 0x8 : 0xa); + byteout(cs->hw.elsa.ale, off); + ret = bytein(cs->hw.elsa.itac); restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (ret); } static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) -{ - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - } else { - count = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (sp->debug & L1_DEB_HSCX_FIFO) { - sprintf(tmp, "HX Frame %d", count); - debugl1(sp, tmp); - } - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "Elsa: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "elsa: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } -} - -/* - * ISAC stuff goes here - */ - -static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +writeitac(struct IsdnCardState *cs, u_char off, u_char data) { - u_char *ptr; long flags; - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; save_flags(flags); cli(); - read_fifo_isac(sp->cfg_reg, ptr, count); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); + byteout(cs->hw.elsa.ale, off); + byteout(cs->hw.elsa.itac, data); restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static inline int +TimerRun(struct IsdnCardState *cs) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo_isac(sp->cfg_reg, ptr, count); - writeisac(sp->cfg_reg, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static void -ph_command(struct IsdnCardState *sp, unsigned int command) -{ - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writeisac(sp->cfg_reg, ISAC_CIX0, (command << 2) | 3); + register u_char v; + + v = bytein(cs->hw.elsa.cfg); + if ((cs->subtyp == ELSA_QS1000) || (cs->subtyp == ELSA_QS3000)) + return (0 == (v & ELSA_TIMER_RUN)); + else if (cs->subtyp == ELSA_PCC8) + return (v & ELSA_TIMER_RUN_PCC8); + return (v & ELSA_TIMER_RUN); } +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval, v1; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; -#if ARCOFI_USE - struct BufHeader *ibh; - u_char *ptr; -#endif +#define READHSCX(cs, nr, reg) readreg(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0), data) - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readisac(sp->cfg_reg, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!exval & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); - } else { - count = readisac(sp->cfg_reg, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - sp->rcvidx = 0; - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "Elsa: D receive out of memory\n"); - else { - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readisac(sp->cfg_reg, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readisac(sp->cfg_reg, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - if (exval & 0x08) { - v1 = readisac(sp->cfg_reg, ISAC_MOSR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOSR %02x", v1); - debugl1(sp, tmp); - } -#if ARCOFI_USE - if (v1 & 0x08) { - if (!sp->mon_rx) - if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX out of buffers!"); - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - goto afterMONR0; - } else - sp->mon_rxp = 0; - ibh = sp->mon_rx; - ptr = DATAPTR(ibh); - ptr += sp->mon_rxp; - sp->mon_rxp++; - if (sp->mon_rxp >= 3072) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - sp->mon_rxp = 0; - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX overflow!"); - goto afterMONR0; - } - *ptr = readisac(sp->cfg_reg, ISAC_MOR0); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOR0 %02x", *ptr); - debugl1(sp, tmp); - } - } - afterMONR0: - if (v1 & 0x80) { - if (!sp->mon_rx) - if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX out of buffers!"); - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - goto afterMONR1; - } else - sp->mon_rxp = 0; - ibh = sp->mon_rx; - ptr = DATAPTR(ibh); - ptr += sp->mon_rxp; - sp->mon_rxp++; - if (sp->mon_rxp >= 3072) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - sp->mon_rxp = 0; - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX overflow!"); - goto afterMONR1; - } - *ptr = readisac(sp->cfg_reg, ISAC_MOR1); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOR1 %02x", *ptr); - debugl1(sp, tmp); - } - } - afterMONR1: - if (v1 & 0x04) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - sp->mon_rx->datasize = sp->mon_rxp; - sp->mon_flg |= MON0_RX; - } - if (v1 & 0x40) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - sp->mon_rx->datasize = sp->mon_rxp; - sp->mon_flg |= MON1_RX; - } - if (v1 == 0x02) { - ibh = sp->mon_tx; - if (!ibh) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - goto AfterMOX0; - } - count = ibh->datasize - sp->mon_txp; - if (count <= 0) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0f); - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sp->mon_flg |= MON0_TX; - goto AfterMOX0; - } - ptr = DATAPTR(ibh); - ptr += sp->mon_txp; - sp->mon_txp++; - writeisac(sp->cfg_reg, ISAC_MOX0, *ptr); - } - AfterMOX0: - if (v1 == 0x20) { - ibh = sp->mon_tx; - if (!ibh) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - goto AfterMOX1; - } - count = ibh->datasize - sp->mon_txp; - if (count <= 0) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xf0); - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sp->mon_flg |= MON1_TX; - goto AfterMOX1; - } - ptr = DATAPTR(ibh); - ptr += sp->mon_txp; - sp->mon_txp++; - writeisac(sp->cfg_reg, ISAC_MOX1, *ptr); - } - AfterMOX1: -#endif - } - } -} +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt) -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readhscx(sp->cfg_reg, 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readhscx(sp->cfg_reg, 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readhscx(sp->cfg_reg, 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val; + int icnt=20; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); return; } -#ifdef CONFIG_HISAX_ELSA_PCC - INT_RESTART: - if (!TimerRun(sp)) { - /* Timer Restart */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - if (!(sp->counter++ & 0x3f)) { - /* Call LEDs all 64 tics */ - elsa_led_handler(sp); - } - } -#endif - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); } - val = readisac(sp->cfg_reg, ISAC_ISTA); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); } -#ifdef CONFIG_HISAX_ELSA_PCC - if (!TimerRun(sp)) - goto INT_RESTART; -#endif - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); + if (val && icnt) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + icnt--; goto Start_HSCX; } - val = readisac(sp->cfg_reg, ISAC_ISTA); - if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA); + if (val && icnt) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + icnt--; goto Start_ISAC; } - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF); - writeisac(sp->cfg_reg, ISAC_MASK, 0xFF); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_QS1000) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); + if (!icnt) + printk(KERN_WARNING"ELSA IRQ LOOP\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0xFF); + if (cs->hw.elsa.status & ELSA_TIMER_AKTIV) { + if (!TimerRun(cs)) { + /* Timer Restart */ + byteout(cs->hw.elsa.timer, 0); + cs->hw.elsa.counter++; + } } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_MASK, 0x0); + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0x00); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0); } - static void -initisac(struct IsdnCardState *sp) +elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { - unsigned int adr = sp->cfg_reg; - - /* Elsa IOM 2 Mode */ - writeisac(adr, ISAC_MASK, 0xff); - writeisac(adr, ISAC_ADF2, 0x80); - writeisac(adr, ISAC_SQXR, 0x2f); - writeisac(adr, ISAC_SPCR, 0x00); - writeisac(adr, ISAC_STCR, 0x70); - writeisac(adr, ISAC_MODE, 0xc9); - writeisac(adr, ISAC_TIMR, 0x00); - writeisac(adr, ISAC_ADF1, 0x00); - writeisac(adr, ISAC_CIX0, (1 << 2) | 3); - writeisac(adr, ISAC_MASK, 0xff); - writeisac(adr, ISAC_MASK, 0x0); -} + struct IsdnCardState *cs = dev_id; + u_char ista,val; + char tmp[64]; + int icnt=20; -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); + if (!cs) { + printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); + return; } - hs->mode = mode; - writehscx(sp->cfg_reg, hscx, HSCX_CCR1, 0x85); - writehscx(sp->cfg_reg, hscx, HSCX_XAD1, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_XAD2, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_RAH2, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_XBCH, 0x0); - writehscx(sp->cfg_reg, hscx, HSCX_RLCR, 0x0); - writehscx(sp->cfg_reg, hscx, HSCX_CCR2, 0x30); - - switch (mode) { - case (0): - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0xff); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0xff); - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f); - } else { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3); - } - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0xe4); - writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f); - } else { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3); - } - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x8c); - writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41); - break; + if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) { + /* The card tends to generate interrupts while being removed + causing us to just crash the kernel. bad. */ + printk(KERN_WARNING "Elsa: card not available!\n"); + return; + } + ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA); +Start_IPAC: + if (cs->debug & L1_DEB_IPAC) { + sprintf(tmp, "IPAC ISTA %02X", ista); + debugl1(cs, tmp); + } + if (ista & 0x0f) { + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + hscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); } - writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00); + ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "ELSA IRQ LOOP\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xC0); } void -release_io_elsa(struct IsdnCard *card) +release_io_elsa(struct IsdnCardState *cs) { int bytecnt = 8; - if (card->sp->subtyp == ELSA_PCFPRO) + del_timer(&cs->hw.elsa.tl); + if (cs->hw.elsa.ctrl) + byteout(cs->hw.elsa.ctrl, 0); /* LEDs Out */ + if ((cs->subtyp == ELSA_PCFPRO) || + (cs->subtyp == ELSA_QS3000) || + (cs->subtyp == ELSA_PCF)) bytecnt = 16; - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, bytecnt); -} - -static void -reset_elsa(struct IsdnCardState *sp) -{ -#ifdef CONFIG_HISAX_ELSA_PCC - /* Wait 1 Timer */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - while (TimerRun(sp)); - byteout(sp->cfg_reg + CARD_CONTROL, 0x00); /* Reset On */ - /* Wait 1 Timer */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - while (TimerRun(sp)); - byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET); /* Reset Off */ - /* Wait 1 Timer */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - while (TimerRun(sp)); - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); -#endif + if (cs->subtyp == ELSA_QS1000PCI) { + byteout(cs->hw.elsa.cfg + 0x4c, 0x01); /* disable IRQ */ + bytecnt = 2; + release_region(cs->hw.elsa.cfg, 0x80); + } + if (cs->hw.elsa.base) + release_region(cs->hw.elsa.base, bytecnt); } static void -clear_pending_ints(struct IsdnCardState *sp) +reset_elsa(struct IsdnCardState *cs) { -#ifdef CONFIG_HISAX_ELSA_PCMCIA - int val; - char tmp[64]; + long flags; - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readhscx(sp->cfg_reg, 1, HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readhscx(sp->cfg_reg, 0, HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); + if (cs->hw.elsa.timer) { + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + cs->hw.elsa.ctrl_reg |= 0x50; + cs->hw.elsa.ctrl_reg &= ~ELSA_ISDN_RESET; /* Reset On */ + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + cs->hw.elsa.ctrl_reg |= ELSA_ISDN_RESET; /* Reset Off */ + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0xff); } - val = readhscx(sp->cfg_reg, 0, HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readhscx(sp->cfg_reg, 1, HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readhscx(sp->cfg_reg, 0, HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readisac(sp->cfg_reg, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readisac(sp->cfg_reg, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); - } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF); - writeisac(sp->cfg_reg, ISAC_MASK, 0xFF); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_QS1000) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); + if (cs->subtyp == ELSA_QS1000PCI) { + save_flags(flags); + sti(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); + schedule(); + restore_flags(flags); + byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */ } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x41); +} + +const u_char ARCOFI_VERSION[] = {2,0xa0,0}; +const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */ +const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */ +const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */ +const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */ +const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0x9e,0x88,0x00,0xc8,0xd8,0x80}; /* RX */ +const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */ +const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR Down */ +const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* PWR Down */ +const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}; + +static void +init_arcofi(struct IsdnCardState *cs) { + send_arcofi(cs, ARCOFI_COP_5); + send_arcofi(cs, ARCOFI_COP_6); + send_arcofi(cs, ARCOFI_COP_7); + send_arcofi(cs, ARCOFI_COP_8); + send_arcofi(cs, ARCOFI_COP_9); + send_arcofi(cs, ARCOFI_SOP_F); + send_arcofi(cs, ARCOFI_XOP_F); } static void -check_arcofi(struct IsdnCardState *sp) +check_arcofi(struct IsdnCardState *cs) { -#if 0 - u_char val; +#if ARCOFI_USE + int arcofi_present = 0; char tmp[40]; char *t; - long flags; u_char *p; - if (BufPoolGet(&(sp->mon_tx), &(sp->sbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON TX out of buffers!"); - return; - } else - sp->mon_txp = 0; - p = DATAPTR(sp->mon_tx); - *p++ = 0xa0; - *p++ = 0x0; - sp->mon_tx->datasize = 2; - sp->mon_txp = 1; - sp->mon_flg = 0; - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - val = readisac(sp->cfg_reg, ISAC_MOSR); - writeisac(sp->cfg_reg, ISAC_MOX1, 0xa0); - writeisac(sp->cfg_reg, ISAC_MOCR, 0xb0); - save_flags(flags); - sti(); - HZDELAY(3); - restore_flags(flags); - if (sp->mon_flg & MON1_TX) { - if (sp->mon_flg & MON1_RX) { - sprintf(tmp, "Arcofi response received %d bytes", sp->mon_rx->datasize); - debugl1(sp, tmp); - p = DATAPTR(sp->mon_rx); + if (!cs->mon_tx) + if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON TX out of buffers!"); + return; + } + send_arcofi(cs, ARCOFI_VERSION); + if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) { + if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) { + sprintf(tmp, "Arcofi response received %d bytes", cs->mon_rxp); + debugl1(cs, tmp); + p = cs->mon_rx; t = tmp; t += sprintf(tmp, "Arcofi data"); - QuickHex(t, p, sp->mon_rx->datasize); - debugl1(sp, tmp); - BufPoolRelease(sp->mon_rx); - sp->mon_rx = NULL; - sp->mon_rxp = 0; - sp->mon_flg = 0; + QuickHex(t, p, cs->mon_rxp); + debugl1(cs, tmp); + if ((cs->mon_rxp == 2) && (cs->mon_rx[0] == 0xa0)) { + switch(cs->mon_rx[1]) { + case 0x80: + debugl1(cs, "Arcofi 2160 detected"); + arcofi_present = 1; + break; + case 0x82: + debugl1(cs, "Arcofi 2165 detected"); + arcofi_present = 2; + break; + case 0x84: + debugl1(cs, "Arcofi 2163 detected"); + arcofi_present = 3; + break; + default: + debugl1(cs, "unknown Arcofi response"); + break; + } + } else + debugl1(cs, "undefined Monitor response"); + cs->mon_rxp = 0; } - } else if (sp->mon_tx) { - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; + } else if (cs->mon_tx) { sprintf(tmp, "Arcofi not detected"); - debugl1(sp, tmp); + debugl1(cs, tmp); + } + if (arcofi_present) { + if (cs->subtyp==ELSA_QS1000) { + cs->subtyp = ELSA_QS3000; + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + release_region(cs->hw.elsa.base, 8); + if (check_region(cs->hw.elsa.base, 16)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base + 8, + cs->hw.elsa.base + 16); + } else + request_region(cs->hw.elsa.base, 16, + "elsa isdn modem"); + } else if (cs->subtyp==ELSA_PCC16) { + cs->subtyp = ELSA_PCF; + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + release_region(cs->hw.elsa.base, 8); + if (check_region(cs->hw.elsa.base, 16)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base + 8, + cs->hw.elsa.base + 16); + } else + request_region(cs->hw.elsa.base, 16, + "elsa isdn modem"); + } else + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + init_arcofi(cs); } - sp->mon_flg = 0; #endif } -int -initelsa(struct IsdnCardState *sp) +static void +elsa_led_handler(struct IsdnCardState *cs) { - int ret, irq_cnt, cnt = 3; - long flags; + int blink = 0; - irq_cnt = kstat_irqs(sp->irq); - printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, irq_cnt); - ret = get_irq(sp->cardnr, &elsa_interrupt); -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); -#endif - while (ret && cnt) { - sp->counter = 0; - clear_pending_ints(sp); - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - save_flags(flags); - sp->counter = 0; - sti(); -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET | ENABLE_TIM_INT); - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (110 * HZ) / 1000; /* Timeout 110ms */ - schedule(); - restore_flags(flags); - printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n", - sp->counter); - if (abs(sp->counter - 13) < 3) { - printk(KERN_INFO "Elsa: timer and irq OK\n"); - } else { - printk(KERN_WARNING - "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n", - sp->counter, sp->irq); - } -#endif - printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, - kstat_irqs(sp->irq)); - if (kstat_irqs(sp->irq) == irq_cnt) { - printk(KERN_WARNING - "Elsa: IRQ(%d) getting no interrupts during init %d\n", - sp->irq, 4 - cnt); - if (cnt == 1) { - free_irq(sp->irq, sp); - return (0); + if ((cs->subtyp == ELSA_PCMCIA) && + (cs->subtyp == ELSA_QS1000PCI)) + return; + del_timer(&cs->hw.elsa.tl); + if (cs->hw.elsa.status & ELSA_ASSIGN) + cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED; + else if (cs->hw.elsa.status & ELSA_BAD_PWR) + cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED; + else { + cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED; + blink = 250; + } + if (cs->hw.elsa.status & 0xf000) + cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED; + else if (cs->hw.elsa.status & 0x0f00) { + cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED; + blink = 500; + } else + cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED; + + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + if (blink) { + init_timer(&cs->hw.elsa.tl); + cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000); + add_timer(&cs->hw.elsa.tl); + } +} + +static int +Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + int pwr, ret = 0; + long flags; + + switch (mt) { + case CARD_RESET: + reset_elsa(cs); + return(0); + case CARD_RELEASE: + release_io_elsa(cs); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == ELSA_QS1000PCI) + ret = request_irq(cs->irq, &elsa_interrupt_ipac, + I4L_IRQ_FLAG, "HiSax", cs); + else + ret = request_irq(cs->irq, &elsa_interrupt, + I4L_IRQ_FLAG, "HiSax", cs); + return(ret); + case CARD_INIT: + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0xff); + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + if (cs->subtyp == ELSA_QS1000) { + byteout(cs->hw.elsa.timer, 0); + byteout(cs->hw.elsa.trig, 0xff); + } + return(0); + case CARD_TEST: + if ((cs->subtyp != ELSA_PCMCIA) && + (cs->subtyp != ELSA_QS1000PCI)) { + save_flags(flags); + cs->hw.elsa.counter = 0; + sti(); + cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT; + cs->hw.elsa.status |= ELSA_TIMER_AKTIV; + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + byteout(cs->hw.elsa.timer, 0); + } else + return(0); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (110 * HZ) / 1000; /* Timeout 110ms */ + schedule(); + restore_flags(flags); + cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV; + printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n", + cs->hw.elsa.counter); + if (abs(cs->hw.elsa.counter - 13) < 3) { + printk(KERN_INFO "Elsa: timer and irq OK\n"); + ret = 0; } else { - reset_elsa(sp); - cnt--; + printk(KERN_WARNING + "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n", + cs->hw.elsa.counter, cs->irq); + ret = 1; } - } else { - check_arcofi(sp); - cnt = 0; - } + check_arcofi(cs); + elsa_led_handler(cs); + return(ret); + case MDL_REMOVE_REQ: + cs->hw.elsa.status &= 0; + break; + case MDL_ASSIGN_REQ: + cs->hw.elsa.status |= ELSA_ASSIGN; + break; + case MDL_INFO_SETUP: + if ((int) arg) + cs->hw.elsa.status |= 0x0200; + else + cs->hw.elsa.status |= 0x0100; + break; + case MDL_INFO_CONN: + if ((int) arg) + cs->hw.elsa.status |= 0x2000; + else + cs->hw.elsa.status |= 0x1000; + break; + case MDL_INFO_REL: + if ((int) arg) { + cs->hw.elsa.status &= ~0x2000; + cs->hw.elsa.status &= ~0x0200; + } else { + cs->hw.elsa.status &= ~0x1000; + cs->hw.elsa.status &= ~0x0100; + } + break; + case CARD_AUX_IND: + break; } - sp->counter = 0; - return (ret); + pwr = bytein(cs->hw.elsa.ale); + if (pwr & 0x08) + cs->hw.elsa.status |= ELSA_BAD_PWR; + else + cs->hw.elsa.status &= ~ELSA_BAD_PWR; + elsa_led_handler(cs); + return(ret); } -#ifdef CONFIG_HISAX_ELSA_PCC static unsigned char -probe_elsa_adr(unsigned int adr) +probe_elsa_adr(unsigned int adr, int typ) { int i, in1, in2, p16_1 = 0, p16_2 = 0, p8_1 = 0, p8_2 = 0, pc_1 = 0, pc_2 = 0, pfp_1 = 0, pfp_2 = 0; long flags; - if (check_region(adr, 8)) { + /* In case of the elsa pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(adr, 8)) { printk(KERN_WARNING "Elsa: Probing Port 0x%x: already in use\n", adr); @@ -1251,8 +736,8 @@ probe_elsa_adr(unsigned int adr) save_flags(flags); cli(); for (i = 0; i < 16; i++) { - in1 = inb(adr + CARD_CONFIG); /* 'toggelt' bei */ - in2 = inb(adr + CARD_CONFIG); /* jedem Zugriff */ + in1 = inb(adr + ELSA_CONFIG); /* 'toggelt' bei */ + in2 = inb(adr + ELSA_CONFIG); /* jedem Zugriff */ p16_1 += 0x04 & in1; p16_2 += 0x04 & in2; p8_1 += 0x02 & in1; @@ -1283,205 +768,295 @@ probe_elsa_adr(unsigned int adr) } static unsigned int -probe_elsa(struct IsdnCardState *sp) +probe_elsa(struct IsdnCardState *cs) { int i; unsigned int CARD_portlist[] = {0x160, 0x170, 0x260, 0x360, 0}; for (i = 0; CARD_portlist[i]; i++) { - if ((sp->subtyp = probe_elsa_adr(CARD_portlist[i]))) + if ((cs->subtyp = probe_elsa_adr(CARD_portlist[i], cs->typ))) break; } return (CARD_portlist[i]); } -#endif + +static int pci_index __initdata = 0; int setup_elsa(struct IsdnCard *card) { -#ifdef CONFIG_HISAX_ELSA_PCC long flags; -#endif int bytecnt; - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, Elsa_revision); - printk(KERN_NOTICE "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp)); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->typ == ISDN_CTYPE_ELSA) { - sp->cfg_reg = card->para[0]; + printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp)); + cs->hw.elsa.ctrl_reg = 0; + cs->hw.elsa.status = 0; + if (cs->typ == ISDN_CTYPE_ELSA) { + cs->hw.elsa.base = card->para[0]; printk(KERN_INFO "Elsa: Microlink IO probing\n"); - if (sp->cfg_reg) { - if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) { + if (cs->hw.elsa.base) { + if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base, + cs->typ))) { printk(KERN_WARNING "Elsa: no Elsa Microlink at 0x%x\n", - sp->cfg_reg); + cs->hw.elsa.base); return (0); } } else - sp->cfg_reg = probe_elsa(sp); - if (sp->cfg_reg) { - val = bytein(sp->cfg_reg + CARD_CONFIG); - if (sp->subtyp == ELSA_PC) { + cs->hw.elsa.base = probe_elsa(cs); + if (cs->hw.elsa.base) { + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + val = bytein(cs->hw.elsa.cfg); + if (cs->subtyp == ELSA_PC) { const u_char CARD_IrqTab[8] = {7, 3, 5, 9, 0, 0, 0, 0}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PC) >> 2]; - } else if (sp->subtyp == ELSA_PCC8) { + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2]; + } else if (cs->subtyp == ELSA_PCC8) { const u_char CARD_IrqTab[8] = {7, 3, 5, 9, 0, 0, 0, 0}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PCC8) >> 4]; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4]; } else { const u_char CARD_IrqTab[8] = {15, 10, 15, 3, 11, 5, 11, 9}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX) >> 3]; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3]; } - val = bytein(sp->cfg_reg + CARD_ALE) & 0x7; + val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE; if (val < 3) val |= 8; val += 'A' - 3; if (val == 'B' || val == 'C') val ^= 1; - if ((sp->subtyp == ELSA_PCFPRO) && (val = 'G')) + if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G')) val = 'C'; printk(KERN_INFO "Elsa: %s found at 0x%x Rev.:%c IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - val, sp->irq); - val = bytein(sp->cfg_reg + CARD_ALE) & 0x08; - if (val) + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + val, cs->irq); + val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD; + if (val) { printk(KERN_WARNING "Elsa: Microlink S0 bus power bad\n"); + cs->hw.elsa.status |= ELSA_BAD_PWR; + } } else { printk(KERN_WARNING "No Elsa Microlink found\n"); return (0); } - } else if (sp->typ == ISDN_CTYPE_ELSA_QS1000) { - sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - sp->subtyp = ELSA_QS1000; + } else if (cs->typ == ISDN_CTYPE_ELSA_PNP) { + cs->hw.elsa.base = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = ELSA_QS1000; + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; printk(KERN_INFO - "Elsa: %s found at 0x%x IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - sp->irq); - } else - return (0); -#endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA - if (sp->typ == ISDN_CTYPE_ELSA_QS1000) { - sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - sp->subtyp = ELSA_PCMCIA; + "Elsa: %s defined at 0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); + } else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) { + cs->hw.elsa.base = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = ELSA_PCMCIA; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + cs->hw.elsa.ctrl = 0; printk(KERN_INFO - "Elsa: %s found at 0x%x IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - sp->irq); - } else + "Elsa: %s defined at 0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); + } else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_ELSA, + PCI_QS1000_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = ELSA_QS1000PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.cfg = pci_ioaddr; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_3, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Elsa: No PCI card found\n"); + return(0); + } + if (!pci_irq) { + printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.base = pci_ioaddr; + cs->hw.elsa.ale = pci_ioaddr; + cs->hw.elsa.isac = pci_ioaddr +1; + cs->hw.elsa.hscx = pci_ioaddr +1; + cs->irq = pci_irq; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + printk(KERN_INFO + "Elsa: %s defined at 0x%x/0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->hw.elsa.cfg, + cs->irq); +#else + printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } else return (0); -#endif - switch (sp->subtyp) { + switch (cs->subtyp) { case ELSA_PC: - bytecnt = 8; - break; case ELSA_PCC8: - bytecnt = 8; - break; - case ELSA_PCFPRO: - bytecnt = 16; - break; case ELSA_PCC16: + case ELSA_QS1000: + case ELSA_PCMCIA: bytecnt = 8; break; + case ELSA_PCFPRO: case ELSA_PCF: bytecnt = 16; break; - case ELSA_QS1000: - bytecnt = 8; - break; - case ELSA_PCMCIA: - bytecnt = 8; + case ELSA_QS1000PCI: + bytecnt = 2; break; default: printk(KERN_WARNING - "Unknown ELSA subtype %d\n", sp->subtyp); + "Unknown ELSA subtype %d\n", cs->subtyp); return (0); } - - if (check_region((sp->cfg_reg), bytecnt)) { + /* In case of the elsa pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(cs->hw.elsa.base, bytecnt)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + bytecnt); + cs->hw.elsa.base, + cs->hw.elsa.base + bytecnt); return (0); } else { - request_region(sp->cfg_reg, bytecnt, "elsa isdn"); + request_region(cs->hw.elsa.base, bytecnt, "elsa isdn"); } - - /* Teste Timer */ -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - if (!TimerRun(sp)) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); /* 2. Versuch */ - if (!TimerRun(sp)) { + if (cs->subtyp == ELSA_QS1000PCI) { + if (check_region(cs->hw.elsa.cfg, 0x80)) { printk(KERN_WARNING - "Elsa: timer do not start\n"); - release_io_elsa(card); + "HiSax: %s pci port %x-%x already in use\n", + CardType[card->typ], + cs->hw.elsa.cfg, + cs->hw.elsa.cfg + 0x80); + release_region(cs->hw.elsa.base, bytecnt); return (0); + } else { + request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci"); } } - save_flags(flags); - sti(); - HZDELAY(1); /* wait >=10 ms */ - restore_flags(flags); - if (TimerRun(sp)) { - printk(KERN_WARNING "Elsa: timer do not run down\n"); - release_io_elsa(card); - return (0); + cs->hw.elsa.tl.function = (void *) elsa_led_handler; + cs->hw.elsa.tl.data = (long) cs; + init_timer(&cs->hw.elsa.tl); + /* Teste Timer */ + if (cs->hw.elsa.timer) { + byteout(cs->hw.elsa.trig, 0xff); + byteout(cs->hw.elsa.timer, 0); + if (!TimerRun(cs)) { + byteout(cs->hw.elsa.timer, 0); /* 2. Versuch */ + if (!TimerRun(cs)) { + printk(KERN_WARNING + "Elsa: timer do not start\n"); + release_io_elsa(cs); + return (0); + } + } + save_flags(flags); + sti(); + HZDELAY(1); /* wait >=10 ms */ + restore_flags(flags); + if (TimerRun(cs)) { + printk(KERN_WARNING "Elsa: timer do not run down\n"); + release_io_elsa(cs); + return (0); + } + printk(KERN_INFO "Elsa: timer OK; resetting card\n"); } - printk(KERN_INFO "Elsa: timer OK; resetting card\n"); - reset_elsa(sp); -#endif - verA = readhscx(sp->cfg_reg, 0, HSCX_VSTR) & 0xf; - verB = readhscx(sp->cfg_reg, 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "Elsa: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readisac(sp->cfg_reg, ISAC_RBCH); - printk(KERN_INFO "Elsa: ISAC %s\n", - ISACVersion(val)); - -#ifdef CONFIG_HISAX_ELSA_PCMCIA - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { - printk(KERN_WARNING - "Elsa: wrong HSCX versions check IO address\n"); - release_io_elsa(card); - return (0); + reset_elsa(cs); + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Elsa_card_msg; + if (cs->subtyp == ELSA_QS1000PCI) { + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID); + printk(KERN_INFO "Elsa: IPAC version %x\n", val); + } else { + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + ISACVersion(cs, "Elsa:"); + if (HscxVersion(cs, "Elsa:")) { + printk(KERN_WARNING + "Elsa: wrong HSCX versions check IO address\n"); + release_io_elsa(cs); + return (0); + } } -#endif - -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_PC) { - val = readitac(sp->cfg_reg, ITAC_SYS); + if (cs->subtyp == ELSA_PC) { + val = readitac(cs, ITAC_SYS); printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]); - writeitac(sp->cfg_reg, ITAC_ISEN, 0); - writeitac(sp->cfg_reg, ITAC_RFIE, 0); - writeitac(sp->cfg_reg, ITAC_XFIE, 0); - writeitac(sp->cfg_reg, ITAC_SCIE, 0); - writeitac(sp->cfg_reg, ITAC_STIE, 0); + writeitac(cs, ITAC_ISEN, 0); + writeitac(cs, ITAC_RFIE, 0); + writeitac(cs, ITAC_XFIE, 0); + writeitac(cs, ITAC_SCIE, 0); + writeitac(cs, ITAC_STIE, 0); } -#endif - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; - return (1); } + diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index d0bb2f14f..ae0662f3f 100644 --- a/drivers/isdn/hisax/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -1,4 +1,4 @@ -/* $Id: fsm.c,v 1.4 1997/04/06 22:56:42 keil Exp $ +/* $Id: fsm.c,v 1.7 1997/11/06 17:09:13 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,6 +7,15 @@ * Fritz Elfert * * $Log: fsm.c,v $ + * Revision 1.7 1997/11/06 17:09:13 keil + * New 2.1 init code + * + * Revision 1.6 1997/07/27 21:42:25 keil + * proof Fsm routines + * + * Revision 1.5 1997/06/26 11:10:05 keil + * Restart timer function added + * * Revision 1.4 1997/04/06 22:56:42 keil * Some cosmetic changes * @@ -26,9 +35,9 @@ #define FSM_TIMER_DEBUG 0 -void +HISAX_INITFUNC(void FsmNew(struct Fsm *fsm, - struct FsmNode *fnlist, int fncount) + struct FsmNode *fnlist, int fncount)) { int i; @@ -36,9 +45,14 @@ FsmNew(struct Fsm *fsm, kmalloc(4L * fsm->state_count * fsm->event_count, GFP_KERNEL); memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count); - for (i = 0; i < fncount; i++) - fsm->jumpmatrix[fsm->state_count * fnlist[i].event + - fnlist[i].state] = (int) fnlist[i].routine; + for (i = 0; i < fncount; i++) + if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) { + printk(KERN_ERR "FsmNew Error line %d st(%d/%d) ev(%d/%d)\n", + i,fnlist[i].state,fsm->state_count, + fnlist[i].event,fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (int) fnlist[i].routine; } void @@ -53,6 +67,11 @@ FsmEvent(struct FsmInst *fi, int event, void *arg) void (*r) (struct FsmInst *, int, void *); char str[80]; + if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) { + printk(KERN_ERR "FsmEvent Error st(%d/%d) ev(%d/%d)\n", + fi->state,fi->fsm->state_count,event,fi->fsm->event_count); + return(1); + } r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; if (r) { if (fi->debug) { @@ -155,10 +174,26 @@ FsmAddTimer(struct FsmTimer *ft, return 0; } -int -FsmTimerRunning(struct FsmTimer *ft) +void +FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) { - return (ft->tl.next != NULL); + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) { + char str[40]; + sprintf(str, "FsmRestartTimer %lx %d %d", (long) ft, millisec, where); + ft->fi->printdebug(ft->fi, str); + } +#endif + + if (ft->tl.next || ft->tl.prev) + del_timer(&ft->tl); + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); } void diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c new file mode 100644 index 000000000..734ec8bd3 --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -0,0 +1,1297 @@ +/* $Id: hfc_2bds0.c,v 1.3 1998/02/12 23:07:22 keil Exp $ + * + * specific routines for CCD's HFC 2BDS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bds0.c,v $ + * Revision 1.3 1998/02/12 23:07:22 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.2 1998/02/02 13:26:13 keil + * New + * + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" +#include <linux/interrupt.h> +/* +#define KDEBUG_DEF +#include "kdebug.h" +*/ + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static void +dummyf(struct IsdnCardState *cs, u_char * data, int size) +{ + printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n"); +} + +static inline u_char +ReadReg(struct IsdnCardState *cs, int data, u_char reg) +{ + register u_char ret; + + if (data) { + if (cs->hw.hfcD.cip != reg) { + cs->hw.hfcD.cip = reg; + byteout(cs->hw.hfcD.addr | 1, reg); + } + ret = bytein(cs->hw.hfcD.addr); +#if HFC_REG_DEBUG + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) { + char tmp[32]; + sprintf(tmp, "t3c RD %02x %02x", reg, ret); + debugl1(cs, tmp); + } +#endif + } else + ret = bytein(cs->hw.hfcD.addr | 1); + return (ret); +} + +static inline void +WriteReg(struct IsdnCardState *cs, int data, u_char reg, u_char value) +{ + if (cs->hw.hfcD.cip != reg) { + cs->hw.hfcD.cip = reg; + byteout(cs->hw.hfcD.addr | 1, reg); + } + if (data) + byteout(cs->hw.hfcD.addr, value); +#if HFC_REG_DEBUG + if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB)) { + char tmp[16]; + sprintf(tmp, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value); + debugl1(cs, tmp); + } +#endif +} + +/* Interface functions */ + +static u_char +readreghfcd(struct IsdnCardState *cs, u_char offset) +{ + return(ReadReg(cs, HFCD_DATA, offset)); +} + +static void +writereghfcd(struct IsdnCardState *cs, u_char offset, u_char value) +{ + WriteReg(cs, HFCD_DATA, offset, value); +} + +void +set_cs_func(struct IsdnCardState *cs) +{ + cs->readisac = &readreghfcd; + cs->writeisac = &writereghfcd; + cs->readisacfifo = &dummyf; + cs->writeisacfifo = &dummyf; + cs->BC_Read_Reg = &ReadReg; + cs->BC_Write_Reg = &WriteReg; +} + +static inline int +WaitForBusy(struct IsdnCardState *cs) +{ + int to = 130; + + while (!(ReadReg(cs, HFCD_DATA, HFCD_STAT) & HFCD_BUSY) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: WaitForBusy timeout\n"); + return (to); +} + +static inline int +WaitNoBusy(struct IsdnCardState *cs) +{ + long flags; + int to = 130; + + while ((ReadReg(cs, HFCD_STATUS, HFCD_STATUS) & HFCD_BUSY) && to) { + save_flags(flags); + sti(); + udelay(1); + to--; + restore_flags(flags); + } + if (!to) + printk(KERN_WARNING "HiSax: WaitNoBusy timeout\n"); + return (to); +} + +static int +SelFiFo(struct IsdnCardState *cs, u_char FiFo) +{ + u_char cip; + long flags; + + + if (cs->hw.hfcD.fifo == FiFo) + return(1); + save_flags(flags); + cli(); + switch(FiFo) { + case 0: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B1; + break; + case 1: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B1; + break; + case 2: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B2; + break; + case 3: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B2; + break; + case 4: cip = HFCD_FIFO | HFCD_Z1 | HFCD_SEND; + break; + case 5: cip = HFCD_FIFO | HFCD_Z1 | HFCD_REC; + break; + default: + restore_flags(flags); + debugl1(cs, "SelFiFo Error"); + return(0); + } + cs->hw.hfcD.fifo = FiFo; + WaitNoBusy(cs); + cs->BC_Write_Reg(cs, HFCD_DATA, cip, 0); + sti(); + WaitForBusy(cs); + restore_flags(flags); + return(2); +} +static int +GetFreeFifoBytes_B(struct BCState *bcs) +{ + int s; + + if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2) + return (bcs->cs->hw.hfcD.bfifosize); + s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2]; + if (s <= 0) + s += bcs->cs->hw.hfcD.bfifosize; + s = bcs->cs->hw.hfcD.bfifosize - s; + return (s); +} + +static int +GetFreeFifoBytes_D(struct IsdnCardState *cs) +{ + int s; + + if (cs->hw.hfcD.f1 == cs->hw.hfcD.f2) + return (cs->hw.hfcD.dfifosize); + s = cs->hw.hfcD.send[cs->hw.hfcD.f1] - cs->hw.hfcD.send[cs->hw.hfcD.f2]; + if (s <= 0) + s += cs->hw.hfcD.dfifosize; + s = cs->hw.hfcD.dfifosize - s; + return (s); +} + +static int +ReadZReg(struct IsdnCardState *cs, u_char reg) +{ + int val; + + WaitNoBusy(cs); + val = 256 * ReadReg(cs, HFCD_DATA, reg | HFCB_Z_HIGH); + WaitNoBusy(cs); + val += ReadReg(cs, HFCD_DATA, reg | HFCB_Z_LOW); + return (val); +} + +static void +hfc_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static struct sk_buff +*hfc_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int idx; + int chksum; + long flags; + u_char stat, cip; + char tmp[64]; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_empty_fifo"); + idx = 0; + save_flags(flags); + if (count > HSCX_BUFMAX + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too large"); + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + while (idx++ < count) { + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + } + skb = NULL; + } else if (count < 4) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too small"); + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + cli(); + while ((idx++ < count) && WaitNoBusy(cs)) + ReadReg(cs, HFCD_DATA_NODEB, cip); + skb = NULL; + } else if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFC: receive out of memory\n"); + else { + ptr = skb_put(skb, count - 3); + idx = 0; + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + cli(); + while (idx < (count - 3)) { + cli(); + if (!WaitNoBusy(cs)) + break; + *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + ptr++; + idx++; + } + if (idx != count - 3) { + sti(); + debugl1(cs, "RFIFO BUSY error"); + printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); + dev_kfree_skb(skb); + skb = NULL; + } else { + cli(); + WaitNoBusy(cs); + chksum = (ReadReg(cs, HFCD_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x", + bcs->channel, chksum, stat); + debugl1(cs, tmp); + } + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb); + skb = NULL; + } + } + } + sti(); + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC | + HFCB_REC | HFCB_CHANNEL(bcs->channel)); + sti(); + WaitForBusy(cs); + restore_flags(flags); + return (skb); +} + +static void +hfc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, fcnt; + int count; + u_char cip; + char tmp[64]; + + + if (!bcs->hw.hfc.tx_skb) + return; + if (bcs->hw.hfc.tx_skb->len <= 0) + return; + + save_flags(flags); + cli(); + SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + cip = HFCB_FIFO | HFCB_F1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f1 = ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + cip = HFCB_FIFO | HFCB_F2 | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip); + bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + sti(); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "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]); + debugl1(cs, tmp); + } + 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_B(bcs); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_fill_fifo %d count(%d/%d),%lx", + bcs->channel, bcs->hw.hfc.tx_skb->len, + count, current->state); + debugl1(cs, tmp); + } + if (count < bcs->hw.hfc.tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFCB_FIFO | HFCB_FIFO_IN | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + idx = 0; + cli(); + WaitForBusy(cs); + WaitNoBusy(cs); + WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]); + while (idx < bcs->hw.hfc.tx_skb->len) { + cli(); + if (!WaitNoBusy(cs)) + break; + WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx]); + sti(); + idx++; + } + if (idx != bcs->hw.hfc.tx_skb->len) { + sti(); + debugl1(cs, "FIFO Send BUSY error"); + printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel); + } else { + bcs->tx_cnt -= bcs->hw.hfc.tx_skb->len; + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->hw.hfc.tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hfc.tx_skb->len); + dev_kfree_skb(bcs->hw.hfc.tx_skb); + bcs->hw.hfc.tx_skb = NULL; + } + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F1_INC | HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + sti(); + WaitForBusy(cs); + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + restore_flags(flags); + return; +} + +static void +hfc_send_data(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + char tmp[32]; + + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"send_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } +} + +void +main_rec_2bds0(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int z1, z2, rcnt; + u_char f1, f2, cip; + int receive, count = 5; + struct sk_buff *skb; + char tmp[64]; + + save_flags(flags); + Begin: + count--; + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + sprintf(tmp,"rec_data %d blocked", bcs->channel); + debugl1(cs, tmp); + restore_flags(flags); + return; + } + SelFiFo(cs, HFCB_REC | HFCB_CHANNEL(bcs->channel)); + cip = HFCB_FIFO | HFCB_F1 | HFCB_REC | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f1 = ReadReg(cs, HFCD_DATA, cip); + cip = HFCB_FIFO | HFCB_F2 | HFCB_REC | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (f1 != f2) { + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc rec %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + debugl1(cs, tmp); + } + cli(); + z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel)); + z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel)); + sti(); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfcD.bfifosize; + rcnt++; + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + debugl1(cs, tmp); + } + if ((skb = hfc_empty_fifo(bcs, rcnt))) { + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfc_sched_event(bcs, B_RCVBUFREADY); + } + rcnt = f1 -f2; + if (rcnt<0) + rcnt += 32; + if (rcnt>1) + receive = 1; + else + receive = 0; + } else + receive = 0; + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + if (count && receive) + goto Begin; + restore_flags(flags); + return; +} + +void +mode_2bs0(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "HFCD bchannel mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; + bcs->channel = bc; + switch (mode) { + case (L1_MODE_NULL): + if (bc) { + cs->hw.hfcD.conn |= 0x18; + cs->hw.hfcD.sctrl &= ~SCTRL_B2_ENA; + } else { + cs->hw.hfcD.conn |= 0x3; + cs->hw.hfcD.sctrl &= ~SCTRL_B1_ENA; + } + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfcD.ctmt |= 2; + cs->hw.hfcD.conn &= ~0x18; + cs->hw.hfcD.sctrl |= SCTRL_B2_ENA; + } else { + cs->hw.hfcD.ctmt |= 1; + cs->hw.hfcD.conn &= ~0x3; + cs->hw.hfcD.sctrl |= SCTRL_B1_ENA; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfcD.ctmt &= ~2; + cs->hw.hfcD.conn &= ~0x18; + cs->hw.hfcD.sctrl |= SCTRL_B2_ENA; + } else { + cs->hw.hfcD.ctmt &= ~1; + cs->hw.hfcD.conn &= ~0x3; + cs->hw.hfcD.sctrl |= SCTRL_B1_ENA; + } + break; + } + WriteReg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl); + WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + WriteReg(cs, HFCD_DATA, HFCD_CONN, cs->hw.hfcD.conn); +} + +static void +hfc_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA_REQ): + save_flags(flags); + cli(); + if (st->l1.bcs->hw.hfc.tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->hw.hfc.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_IND): + if (st->l1.bcs->hw.hfc.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->hw.hfc.tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL_REQ): + if (!st->l1.bcs->hw.hfc.tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +close_2bs0(struct BCState *bcs) +{ + struct sk_buff *skb; + + mode_2bs0(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + if (bcs->hw.hfc.tx_skb) { + dev_kfree_skb(bcs->hw.hfc.tx_skb); + bcs->hw.hfc.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_hfcstate(struct IsdnCardState *cs, + int bc) +{ + struct BCState *bcs = cs->bcs + bc; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->hw.hfc.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +hfc_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + mode_2bs0(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_2b(struct PStack *st, struct BCState *bcs) +{ + if (open_hfcstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfc_l2l1; + st->ma.manl1 = hfc_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + +static void +manl1_msg(struct IsdnCardState *cs, int msg, void *arg) { + struct PStack *st; + + st = cs->stlist; + while (st) { + st->ma.manl1(st, msg, arg); + st = st->next; + } +} + +static void +hfcd_bh(struct IsdnCardState *cs) +{ +/* struct PStack *stptr; +*/ + if (!cs) + return; +#if 0 + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL); + stptr = stptr->next; + } + } +#endif + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + switch (cs->ph_state) { + case (0): + manl1_msg(cs, PH_RESET_IND, NULL); + break; + case (3): + manl1_msg(cs, PH_DEACT_IND, NULL); + break; + case (8): + manl1_msg(cs, PH_RSYNC_IND, NULL); + break; + case (6): + manl1_msg(cs, PH_INFO2_IND, NULL); + break; + case (7): + manl1_msg(cs, PH_I4_P8_IND, NULL); + break; + default: + break; + } + } + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); +} + +void +sched_event_D(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static +int receive_dmsg(struct IsdnCardState *cs) +{ + struct sk_buff *skb; + long flags; + int idx; + int rcnt, z1, z2; + u_char stat, cip, f1, f2; + int chksum; + int count=5; + u_char *ptr; + char tmp[64]; + + save_flags(flags); + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_dmsg blocked"); + restore_flags(flags); + return(1); + } + SelFiFo(cs, 4 | HFCD_REC); + cip = HFCD_FIFO | HFCD_F1 | HFCD_REC; + WaitNoBusy(cs); + f1 = cs->readisac(cs, cip) & 0xf; + cip = HFCD_FIFO | HFCD_F2 | HFCD_REC; + WaitNoBusy(cs); + f2 = cs->readisac(cs, cip) & 0xf; + while ((f1 != f2) && count--) { + z1 = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_REC); + z2 = ReadZReg(cs, HFCD_FIFO | HFCD_Z2 | HFCD_REC); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfcD.dfifosize; + rcnt++; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", + f1, f2, z1, z2, rcnt); + debugl1(cs, tmp); + } + sti(); + idx = 0; + cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC; + if (rcnt > MAX_DFRAME_LEN + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo d: incoming packet too large"); + while (idx < rcnt) { + cli(); + if (!(WaitNoBusy(cs))) + break; + ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + idx++; + } + } else if (rcnt < 4) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo d: incoming packet too small"); + cli(); + while ((idx++ < rcnt) && WaitNoBusy(cs)) + ReadReg(cs, HFCD_DATA_NODEB, cip); + } else if ((skb = dev_alloc_skb(rcnt - 3))) { + ptr = skb_put(skb, rcnt - 3); + while (idx < (rcnt - 3)) { + cli(); + if (!(WaitNoBusy(cs))) + break; + *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + idx++; + ptr++; + } + if (idx != (rcnt - 3)) { + sti(); + debugl1(cs, "RFIFO D BUSY error"); + printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n"); + dev_kfree_skb(skb); + skb = NULL; + } else { + cli(); + WaitNoBusy(cs); + chksum = (ReadReg(cs, HFCD_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "empty_dfifo chksum %x stat %x", + chksum, stat); + debugl1(cs, tmp); + } + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb); + skb = NULL; + } else { + skb_queue_tail(&cs->rq, skb); + sched_event_D(cs, D_RCVBUFREADY); + } + } + } else + printk(KERN_WARNING "HFC: D receive out of memory\n"); + sti(); + WaitForBusy(cs); + cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC; + cli(); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + WaitForBusy(cs); + cip = HFCD_FIFO | HFCD_F2 | HFCD_REC; + cli(); + WaitNoBusy(cs); + f2 = cs->readisac(cs, cip) & 0xf; + } + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + restore_flags(flags); + return(1); +} + +static void +hfc_fill_dfifo(struct IsdnCardState *cs) +{ + long flags; + int idx, fcnt; + int count; + u_char cip; + char tmp[64]; + + if (!cs->tx_skb) + return; + if (cs->tx_skb->len <= 0) + return; + + save_flags(flags); + cli(); + SelFiFo(cs, 4 | HFCD_SEND); + cip = HFCD_FIFO | HFCD_F1 | HFCD_SEND; + WaitNoBusy(cs); + cs->hw.hfcD.f1 = ReadReg(cs, HFCD_DATA, cip) & 0xf; + WaitNoBusy(cs); + cip = HFCD_FIFO | HFCD_F2 | HFCD_SEND; + cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf; + cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND); + sti(); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)", + cs->hw.hfcD.f1, cs->hw.hfcD.f2, + cs->hw.hfcD.send[cs->hw.hfcD.f1]); + debugl1(cs, tmp); + } + fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2; + if (fcnt < 0) + fcnt += 16; + if (fcnt > 14) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_Dfifo more as 14 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes_D(cs); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "hfc_fill_Dfifo count(%d/%d)", + cs->tx_skb->len, count); + debugl1(cs, tmp); + } + if (count < cs->tx_skb->len) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfc_fill_Dfifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFCD_FIFO | HFCD_FIFO_IN | HFCD_SEND; + idx = 0; + cli(); + WaitForBusy(cs); + WaitNoBusy(cs); + WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx++]); + while (idx < cs->tx_skb->len) { + cli(); + if (!(WaitNoBusy(cs))) + break; + WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx]); + sti(); + idx++; + } + if (idx != cs->tx_skb->len) { + sti(); + debugl1(cs, "DFIFO Send BUSY error"); + printk(KERN_WARNING "HFC S DFIFO channel BUSY Error\n"); + } + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND); + dev_kfree_skb(cs->tx_skb); + cs->tx_skb = NULL; + sti(); + WaitForBusy(cs); + restore_flags(flags); + return; +} + +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); +} + +void +hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) +{ + u_char exval; + struct BCState *bcs; + char tmp[32]; + int count=15; + long flags; + + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "HFCD irq %x %s", val, + test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ? + "locked" : "unlocked"); + debugl1(cs, tmp); + } + val &= cs->hw.hfcD.int_m1; + if (val & 0x40) { /* TE state machine irq */ + exval = cs->readisac(cs, HFCD_STATES) & 0xf; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "ph_state chg %d->%d", cs->ph_state, + exval); + debugl1(cs, tmp); + } + cs->ph_state = exval; + sched_event_D(cs, D_L1STATECHANGE); + val &= ~0x40; + } + while (val) { + save_flags(flags); + cli(); + if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + cs->hw.hfcD.int_s1 |= val; + restore_flags(flags); + return; + } + if (cs->hw.hfcD.int_s1 & 0x18) { + exval = val; + val = cs->hw.hfcD.int_s1; + cs->hw.hfcD.int_s1 = exval; + } + if (val & 0x08) { + if (!(bcs=Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x08 IRQ"); + } else + main_rec_2bds0(bcs); + } + if (val & 0x10) { + if (!(bcs=Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x10 IRQ"); + } else + main_rec_2bds0(bcs); + } + if (val & 0x01) { + if (!(bcs=Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x01 IRQ"); + } else { + if (bcs->hw.hfc.tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"fill_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } + } else { + if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"fill_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } + } else { + hfc_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x02) { + if (!(bcs=Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x02 IRQ"); + } else { + if (bcs->hw.hfc.tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"fill_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } + } else { + if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + sprintf(tmp,"fill_data %d blocked", bcs->channel); + debugl1(cs, tmp); + } + } else { + hfc_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(cs, D_CLEARBUSY); + if (cs->tx_skb) + if (cs->tx_skb->len) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfc_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)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfc_fill_dfifo irq blocked"); + } + } else + sched_event_D(cs, D_XMTBUFREADY); + } + afterXPR: + if (cs->hw.hfcD.int_s1 && count--) { + val = cs->hw.hfcD.int_s1; + cs->hw.hfcD.int_s1 = 0; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "HFCD irq %x loop %d", val, 15-count); + debugl1(cs, tmp); + } + } else + val = 0; + restore_flags(flags); + } +} + +static void +HFCD_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + char str[64]; + switch (pr) { + case (PH_DATA_REQ): + 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 { + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + 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)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfc_fill_dfifo blocked"); + + } + break; + case (PH_PULL_IND): + 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->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + 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)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfc_fill_dfifo blocked"); + break; + case (PH_PULL_REQ): +#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_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +hfcd_l1cmd(struct IsdnCardState *cs, int msg, void *arg) +{ + char tmp[32]; + switch(msg) { + case PH_RESET_REQ: + cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */ + cs->hw.hfcD.mst_m |= HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION); + manl1_msg(cs, PH_POWERUP_CNF, NULL); + break; + case PH_ENABLE_REQ: + cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION); + break; + case PH_DEACT_ACK: + cs->hw.hfcD.mst_m &= ~HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + break; + case PH_INFO3_REQ: + cs->hw.hfcD.mst_m |= HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + break; +#if 0 + case PH_TESTLOOP_REQ: + u_char val = 0; + if (1 & (int) arg) + val |= 0x0c; + if (2 & (int) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; +#endif + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "hfcd_l1cmd unknown %4x", msg); + debugl1(cs, tmp); + } + break; + } +} + +void +setstack_hfcd(struct PStack *st, struct IsdnCardState *cs) +{ + st->l2.l2l1 = HFCD_l2l1; +} + +static void +hfc_dbusy_timer(struct IsdnCardState *cs) +{ +#if 0 + struct PStack *stptr; + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy"); + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL); + stptr = stptr->next; + } + } +#endif +} + +__initfunc(unsigned int +*init_send_hfcd(int cnt)) +{ + int i, *send; + + if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfcd.send\n"); + return(NULL); + } + for (i = 0; i < cnt; i++) + send[i] = 0x1fff; + return(send); +} + +__initfunc(void +init2bds0(struct IsdnCardState *cs)) +{ + cs->setstack_d = setstack_hfcd; + cs->l1cmd = hfcd_l1cmd; + cs->dbusytimer.function = (void *) hfc_dbusy_timer; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->tqueue.routine = (void *) (void *) hfcd_bh; + if (!cs->hw.hfcD.send) + cs->hw.hfcD.send = init_send_hfcd(16); + if (!cs->bcs[0].hw.hfc.send) + cs->bcs[0].hw.hfc.send = init_send_hfcd(32); + if (!cs->bcs[1].hw.hfc.send) + cs->bcs[1].hw.hfc.send = init_send_hfcd(32); + cs->BC_Send_Data = &hfc_send_data; + cs->bcs[0].BC_SetStack = setstack_2b; + cs->bcs[1].BC_SetStack = setstack_2b; + cs->bcs[0].BC_Close = close_2bs0; + cs->bcs[1].BC_Close = close_2bs0; + mode_2bs0(cs->bcs, 0, 0); + mode_2bs0(cs->bcs + 1, 0, 1); +} + +void +release2bds0(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; + } + if (cs->hw.hfcD.send) { + kfree(cs->hw.hfcD.send); + cs->hw.hfcD.send = NULL; + } +} diff --git a/drivers/isdn/hisax/hfc_2bds0.h b/drivers/isdn/hisax/hfc_2bds0.h new file mode 100644 index 000000000..d11e8b503 --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bds0.h @@ -0,0 +1,131 @@ +/* $Id: hfc_2bds0.h,v 1.2 1998/02/02 13:26:15 keil Exp $ + + * specific defines for CCD's HFC 2BDS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bds0.h,v $ + * Revision 1.2 1998/02/02 13:26:15 keil + * New + * + * + * + */ + +#define HFCD_CIRM 0x18 +#define HFCD_CTMT 0x19 +#define HFCD_INT_M1 0x1A +#define HFCD_INT_M2 0x1B +#define HFCD_INT_S1 0x1E +#define HFCD_STAT 0x1C +#define HFCD_STAT_DISB 0x1D +#define HFCD_STATES 0x30 +#define HFCD_SCTRL 0x31 +#define HFCD_TEST 0x32 +#define HFCD_SQ 0x34 +#define HFCD_CLKDEL 0x37 +#define HFCD_MST_MODE 0x2E +#define HFCD_CONN 0x2F + +#define HFCD_FIFO 0x80 +#define HFCD_Z1 0x10 +#define HFCD_Z2 0x18 +#define HFCD_Z_LOW 0x00 +#define HFCD_Z_HIGH 0x04 +#define HFCD_F1_INC 0x12 +#define HFCD_FIFO_IN 0x16 +#define HFCD_F1 0x1a +#define HFCD_F2 0x1e +#define HFCD_F2_INC 0x22 +#define HFCD_FIFO_OUT 0x26 +#define HFCD_REC 0x01 +#define HFCD_SEND 0x00 + +#define HFCB_FIFO 0x80 +#define HFCB_Z1 0x00 +#define HFCB_Z2 0x08 +#define HFCB_Z_LOW 0x00 +#define HFCB_Z_HIGH 0x04 +#define HFCB_F1_INC 0x28 +#define HFCB_FIFO_IN 0x2c +#define HFCB_F1 0x30 +#define HFCB_F2 0x34 +#define HFCB_F2_INC 0x38 +#define HFCB_FIFO_OUT 0x3c +#define HFCB_REC 0x01 +#define HFCB_SEND 0x00 +#define HFCB_B1 0x00 +#define HFCB_B2 0x02 +#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1) + +#define HFCD_STATUS 0 +#define HFCD_DATA 1 +#define HFCD_DATA_NODEB 2 + +/* Status (READ) */ +#define HFCD_BUSY 0x01 +#define HFCD_BUSY_NBUSY 0x04 +#define HFCD_TIMER_ELAP 0x10 +#define HFCD_STATINT 0x20 +#define HFCD_FRAMEINT 0x40 +#define HFCD_ANYINT 0x80 + +/* CTMT (Write) */ +#define HFCD_CLTIMER 0x80 +#define HFCD_TIM25 0x00 +#define HFCD_TIM50 0x08 +#define HFCD_TIM400 0x10 +#define HFCD_TIM800 0x18 +#define HFCD_AUTO_TIMER 0x20 +#define HFCD_TRANSB2 0x02 +#define HFCD_TRANSB1 0x01 + +/* CIRM (Write) */ +#define HFCD_RESET 0x08 +#define HFCD_MEM8K 0x10 +#define HFCD_INTA 0x01 +#define HFCD_INTB 0x02 +#define HFCD_INTC 0x03 +#define HFCD_INTD 0x04 +#define HFCD_INTE 0x05 +#define HFCD_INTF 0x06 + +/* INT_M1;INT_S1 */ +#define HFCD_INTS_B1TRANS 0x01 +#define HFCD_INTS_B2TRANS 0x02 +#define HFCD_INTS_DTRANS 0x04 +#define HFCD_INTS_B1REC 0x08 +#define HFCD_INTS_B2REC 0x10 +#define HFCD_INTS_DREC 0x20 +#define HFCD_INTS_L1STATE 0x40 +#define HFCD_INTS_TIMER 0x80 + +/* INT_M2 */ +#define HFCD_IRQ_ENABLE 0x08 + +/* STATES */ +#define HFCD_LOAD_STATE 0x10 +#define HFCD_ACTIVATE 0x20 +#define HFCD_DO_ACTION 0x40 + +/* HFCD_MST_MODE */ +#define HFCD_MASTER 0x01 + +/* HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* HFCD_TEST */ +#define HFCD_AUTO_AWAKE 0x01 + +extern void main_irq_2bds0(struct BCState *bcs); +extern void init2bds0(struct IsdnCardState *cs); +extern void release2bds0(struct IsdnCardState *cs); +extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val); +extern void set_cs_func(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c new file mode 100644 index 000000000..2d9ce5bd5 --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bs0.c @@ -0,0 +1,615 @@ +/* $Id: hfc_2bs0.c,v 1.4 1998/02/12 23:07:29 keil Exp $ + + * specific routines for CCD's HFC 2BS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bs0.c,v $ + * Revision 1.4 1998/02/12 23:07:29 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.3 1997/11/06 17:13:35 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 19:04:47 keil + * changes for 2.1 + * + * Revision 1.1 1997/09/11 17:31:33 keil + * Common part for HFC 2BS0 based cards + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bs0.h" +#include "isac.h" +#include "isdnl1.h" +#include <linux/interrupt.h> + +static inline int +WaitForBusy(struct IsdnCardState *cs) +{ + int to = 130; + long flags; + u_char val; + + save_flags(flags); + cli(); + while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) { + val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 | + (cs->hw.hfc.cip & 3)); + udelay(1); + to--; + } + restore_flags(flags); + if (!to) { + printk(KERN_WARNING "HiSax: waitforBusy timeout\n"); + return (0); + } else + return (to); +} + +static inline int +WaitNoBusy(struct IsdnCardState *cs) +{ + int to = 125; + + while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) { + udelay(1); + to--; + } + if (!to) { + printk(KERN_WARNING "HiSax: waitforBusy timeout\n"); + return (0); + } else + return (to); +} + +int +GetFreeFifoBytes(struct BCState *bcs) +{ + int s; + + if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2) + return (bcs->cs->hw.hfc.fifosize); + s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2]; + if (s <= 0) + s += bcs->cs->hw.hfc.fifosize; + s = bcs->cs->hw.hfc.fifosize - s; + return (s); +} + +int +ReadZReg(struct BCState *bcs, u_char reg) +{ + int val; + + WaitNoBusy(bcs->cs); + val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH); + WaitNoBusy(bcs->cs); + val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW); + return (val); +} + +void +hfc_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +hfc_clear_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, cnt; + int rcnt, z1, z2; + u_char cip, f1, f2; + char tmp[64]; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_clear_fifo"); + save_flags(flags); + cli(); + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + 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); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + cnt = 32; + while (((f1 != f2) || (z1 != z2)) && cnt--) { + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc clear %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + debugl1(cs, tmp); + } + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfc.fifosize; + if (rcnt) + rcnt++; + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc clear %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + debugl1(cs, tmp); + } + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + idx = 0; + while ((idx < rcnt) && WaitNoBusy(cs)) { + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + idx++; + } + if (f1 != f2) { + WaitNoBusy(cs); + cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + } + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + 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); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + } + restore_flags(flags); + return; +} + + +static struct sk_buff +* +hfc_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int idx; + int chksum; + u_char stat, cip; + char tmp[64]; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_empty_fifo"); + idx = 0; + if (count > HSCX_BUFMAX + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too large"); + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + while ((idx++ < count) && WaitNoBusy(cs)) + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + 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); + } + if (count < 4) { + 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); + while ((idx++ < count) && WaitNoBusy(cs)) + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + 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); + } + if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFC: receive out of memory\n"); + else { + ptr = skb_put(skb, count - 3); + 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) { + 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); + 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) { + sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x", + bcs->channel, chksum, stat); + debugl1(cs, tmp); + } + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb); + skb = NULL; + } + 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); +} + +static void +hfc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, fcnt; + int count; + u_char cip; + char tmp[64]; + + if (!bcs->hw.hfc.tx_skb) + return; + if (bcs->hw.hfc.tx_skb->len <= 0) + return; + + save_flags(flags); + 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); + } + 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) { + sprintf(tmp, "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]); + debugl1(cs, tmp); + } + 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 (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "hfc_fill_fifo %d count(%d/%d)", + bcs->channel, bcs->hw.hfc.tx_skb->len, + count); + debugl1(cs, tmp); + } + if (count < bcs->hw.hfc.tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel); + idx = 0; + while ((idx < bcs->hw.hfc.tx_skb->len) && WaitNoBusy(cs)) + cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]); + if (idx != bcs->hw.hfc.tx_skb->len) { + debugl1(cs, "FIFO Send BUSY error"); + printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel); + } else { + count = bcs->hw.hfc.tx_skb->len; + bcs->tx_cnt -= count; + if (PACKET_NOACK == bcs->hw.hfc.tx_skb->pkt_type) + count = -1; + dev_kfree_skb(bcs->hw.hfc.tx_skb); + bcs->hw.hfc.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->st->lli.l1writewakeup && (count >= 0)) + bcs->st->lli.l1writewakeup(bcs->st, count); + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + restore_flags(flags); + return; +} + +void +main_irq_hfc(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int z1, z2, rcnt; + u_char f1, f2, cip; + int receive, transmit, count = 5; + struct sk_buff *skb; + char tmp[64]; + + save_flags(flags); + Begin: + cli(); + count--; + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + 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) { + sprintf(tmp, "hfc rec %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + debugl1(cs, tmp); + } + 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) { + sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + debugl1(cs, tmp); + } +/* 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(); + if (bcs->hw.hfc.tx_skb) { + transmit = 1; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hfc_fill_fifo(bcs); + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) + transmit = 0; + } else { + if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) { + transmit = 1; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hfc_fill_fifo(bcs); + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) + transmit = 0; + } else { + transmit = 0; + hfc_sched_event(bcs, B_XMTBUFREADY); + } + } + restore_flags(flags); + if ((receive || transmit) && count) + goto Begin; + return; +} + +void +mode_hfc(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "HFC 2BS0 mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; + + switch (mode) { + case (L1_MODE_NULL): + if (bc) + cs->hw.hfc.isac_spcr &= ~0x03; + else + cs->hw.hfc.isac_spcr &= ~0x0c; + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfc.ctmt |= 1; + cs->hw.hfc.isac_spcr &= ~0x03; + cs->hw.hfc.isac_spcr |= 0x02; + } else { + cs->hw.hfc.ctmt |= 2; + cs->hw.hfc.isac_spcr &= ~0x0c; + cs->hw.hfc.isac_spcr |= 0x08; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfc.ctmt &= ~1; + cs->hw.hfc.isac_spcr &= ~0x03; + cs->hw.hfc.isac_spcr |= 0x02; + } else { + cs->hw.hfc.ctmt &= ~2; + cs->hw.hfc.isac_spcr &= ~0x0c; + cs->hw.hfc.isac_spcr |= 0x08; + } + break; + } + 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) + hfc_clear_fifo(bcs); +} + +static void +hfc_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA_REQ): + save_flags(flags); + cli(); + if (st->l1.bcs->hw.hfc.tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->hw.hfc.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_IND): + if (st->l1.bcs->hw.hfc.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->hw.hfc.tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL_REQ): + if (!st->l1.bcs->hw.hfc.tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +close_hfcstate(struct BCState *bcs) +{ + struct sk_buff *skb; + + mode_hfc(bcs, 0, 0); + if (test_bit(BC_FLG_INIT, &bcs->Flag)) { + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + if (bcs->hw.hfc.tx_skb) { + dev_kfree_skb(bcs->hw.hfc.tx_skb); + bcs->hw.hfc.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); +} + +static int +open_hfcstate(struct IsdnCardState *cs, + int bc) +{ + struct BCState *bcs = cs->bcs + bc; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->hw.hfc.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +hfc_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + mode_hfc(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_hfc(struct PStack *st, struct BCState *bcs) +{ + if (open_hfcstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfc_l2l1; + st->ma.manl1 = hfc_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + +__initfunc(void +init_send(struct BCState *bcs)) +{ + int i; + + if (!(bcs->hw.hfc.send = kmalloc(32 * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfc.send\n"); + return; + } + for (i = 0; i < 32; i++) + bcs->hw.hfc.send[i] = 0x1fff; +} + +__initfunc(void +inithfc(struct IsdnCardState *cs)) +{ + init_send(&cs->bcs[0]); + init_send(&cs->bcs[1]); + cs->BC_Send_Data = &hfc_fill_fifo; + cs->bcs[0].BC_SetStack = setstack_hfc; + cs->bcs[1].BC_SetStack = setstack_hfc; + cs->bcs[0].BC_Close = close_hfcstate; + cs->bcs[1].BC_Close = close_hfcstate; + mode_hfc(cs->bcs, 0, 0); + mode_hfc(cs->bcs + 1, 0, 0); +} + +void +releasehfc(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; + } +} diff --git a/drivers/isdn/hisax/hfc_2bs0.h b/drivers/isdn/hisax/hfc_2bs0.h new file mode 100644 index 000000000..cce8e4a35 --- /dev/null +++ b/drivers/isdn/hisax/hfc_2bs0.h @@ -0,0 +1,62 @@ +/* $Id: hfc_2bs0.h,v 1.1 1997/09/11 17:31:34 keil Exp $ + + * specific defines for CCD's HFC 2BS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bs0.h,v $ + * Revision 1.1 1997/09/11 17:31:34 keil + * Common part for HFC 2BS0 based cards + * + * + */ + +#define HFC_CTMT 0xe0 +#define HFC_CIRM 0xc0 +#define HFC_CIP 0x80 +#define HFC_Z1 0x00 +#define HFC_Z2 0x08 +#define HFC_Z_LOW 0x00 +#define HFC_Z_HIGH 0x04 +#define HFC_F1_INC 0x28 +#define HFC_FIFO_IN 0x2c +#define HFC_F1 0x30 +#define HFC_F2 0x34 +#define HFC_F2_INC 0x38 +#define HFC_FIFO_OUT 0x3c +#define HFC_B1 0x00 +#define HFC_B2 0x02 +#define HFC_REC 0x01 +#define HFC_SEND 0x00 +#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1) + +#define HFC_STATUS 0 +#define HFC_DATA 1 +#define HFC_DATA_NODEB 2 + +/* Status (READ) */ +#define HFC_BUSY 0x01 +#define HFC_TIMINT 0x02 +#define HFC_EXTINT 0x04 + +/* CTMT (Write) */ +#define HFC_CLTIMER 0x10 +#define HFC_TIM50MS 0x08 +#define HFC_TIMIRQE 0x04 +#define HFC_TRANSB2 0x02 +#define HFC_TRANSB1 0x01 + +/* CIRM (Write) */ +#define HFC_RESET 0x08 +#define HFC_MEM8K 0x10 +#define HFC_INTA 0x01 +#define HFC_INTB 0x02 +#define HFC_INTC 0x03 +#define HFC_INTD 0x04 +#define HFC_INTE 0x05 +#define HFC_INTF 0x06 + +extern void main_irq_hfc(struct BCState *bcs); +extern void inithfc(struct IsdnCardState *cs); +extern void releasehfc(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index efe7d5329..f5b15e2bf 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1,52 +1,59 @@ -/* $Id: hisax.h,v 1.13 1997/04/06 22:54:12 keil Exp $ +/* $Id: hisax.h,v 2.14 1998/02/11 17:28:04 keil Exp $ * Basic declarations, defines and prototypes * * $Log: hisax.h,v $ - * Revision 1.13 1997/04/06 22:54:12 keil - * Using SKB's + * Revision 2.14 1998/02/11 17:28:04 keil + * Niccy PnP/PCI support * - * Revision 1.12 1997/03/23 21:45:45 keil - * Add support for ELSA PCMCIA + * Revision 2.13 1998/02/09 18:46:02 keil + * Support for Sedlbauer PCMCIA (Marcus Niemann) * - * Revision 1.11 1997/02/11 01:36:02 keil - * New Param structure + * Revision 2.12 1998/02/03 23:31:30 keil + * add AMD7930 support * - * Revision 1.10 1997/02/09 00:23:52 keil - * new interface handling, one interface per card + * Revision 2.11 1998/02/02 13:33:00 keil + * New card support * - * Revision 1.9 1997/01/27 23:18:44 keil - * prototype for releasestack_isdnl3 + * Revision 2.10 1997/11/08 21:37:52 keil + * new l1 init;new Compaq card * - * Revision 1.8 1997/01/27 16:02:37 keil - * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype + * Revision 2.9 1997/11/06 17:09:09 keil + * New 2.1 init code * - * Revision 1.7 1997/01/21 22:22:14 keil - * changes for 2.0; Elsa Quickstep support + * Revision 2.8 1997/10/29 19:04:13 keil + * new L1; changes for 2.1 * - * Revision 1.6 1997/01/04 13:48:28 keil - * primitiv for MDL_REMOVE added + * Revision 2.7 1997/10/10 20:56:47 fritz + * New HL interface. * - * Revision 1.5 1996/12/08 19:49:19 keil - * Monitor channel support + * Revision 2.6 1997/09/11 17:25:51 keil + * Add new cards * - * Revision 1.4 1996/11/18 15:35:39 keil - * some changes for ELSA cards + * Revision 2.5 1997/08/03 14:36:31 keil + * Implement RESTART procedure * - * Revision 1.3 1996/11/05 19:37:23 keil - * using config.h + * Revision 2.4 1997/07/31 19:25:20 keil + * PTP_DATA_LINK support * - * Revision 1.2 1996/10/27 22:21:52 keil - * CallFlags for broadcast messages + * Revision 2.3 1997/07/31 11:50:17 keil + * ONE TEI and FIXED TEI handling * - * Revision 1.1 1996/10/13 20:03:46 keil - * Initial revision + * Revision 2.2 1997/07/30 17:13:02 keil + * more changes for 'One TEI per card' * + * Revision 2.1 1997/07/27 21:45:13 keil + * new main structures * + * Revision 2.0 1997/06/26 11:06:27 keil + * New card and L1 interface. + * Eicon.Diehl Diva and Dynalink IS64PH support + * + * old changes removed KKe * */ -#include <linux/module.h> #include <linux/config.h> +#include <linux/module.h> #include <linux/version.h> #include <linux/errno.h> #include <linux/fs.h> @@ -64,140 +71,136 @@ #include <linux/wait.h> #include <linux/isdnif.h> #include <linux/tty.h> - -#define PH_ACTIVATE 1 -#define PH_DATA 2 -#define PH_DEACTIVATE 3 - -#define MDL_ASSIGN 4 -#define DL_UNIT_DATA 5 -#define SC_STARTUP 6 -#define CC_ESTABLISH 7 -#define DL_ESTABLISH 8 -#define DL_DATA 9 -#define CC_S_STATUS_ENQ 10 - -#define CC_CONNECT 15 -#define CC_CONNECT_ACKNOWLEDGE 16 -#define CO_EOF 17 -#define SC_DISCONNECT 18 -#define CO_DTMF 19 -#define DL_RELEASE 20 -#define DL_FLUSH 21 - -#define CO_ALARM 22 -#define CC_REJECT 23 - -#define CC_SETUP_REQ 24 -#define CC_SETUP_CNF 25 -#define CC_SETUP_IND 26 -#define CC_SETUP_RSP 27 -#define CC_SETUP_COMPLETE_IND 28 - -#define CC_DISCONNECT_REQ 29 -#define CC_DISCONNECT_IND 30 - -#define CC_RELEASE_CNF 31 -#define CC_RELEASE_IND 32 -#define CC_RELEASE_REQ 33 - -#define CC_REJECT_REQ 34 - -#define CC_PROCEEDING_IND 35 - -#define CC_DLRL 36 -#define CC_DLEST 37 - -#define CC_ALERTING_REQ 38 -#define CC_ALERTING_IND 39 - -#define DL_STOP 40 -#define DL_START 41 - -#define MDL_NOTEIPROC 46 - -#define LC_ESTABLISH 47 -#define LC_RELEASE 48 - -#define PH_REQUEST_PULL 49 -#define PH_PULL_ACK 50 -#define PH_DATA_PULLED 51 -#define CC_INFO_CHARGE 52 - -#define CC_MORE_INFO 53 -#define CC_IGNORE 54 - -#define MDL_REMOVE 56 -#define MDL_VERIFY 57 - -#define CC_T303 60 -#define CC_T304 61 -#define CC_T305 62 -#define CC_T308_1 64 -#define CC_T308_2 65 -#define CC_T310 66 -#define CC_T313 67 -#define CC_T318 68 -#define CC_T319 69 - -#define CC_NOSETUP_RSP_ERR 70 -#define CC_SETUP_ERR 71 -#define CC_CONNECT_ERR 72 -#define CC_RELEASE_ERR 73 - -/* - * Message-Types - */ - -#define MT_ALERTING 0x01 -#define MT_CALL_PROCEEDING 0x02 -#define MT_CONNECT 0x07 -#define MT_CONNECT_ACKNOWLEDGE 0x0f -#define MT_PROGRESS 0x03 -#define MT_SETUP 0x05 -#define MT_SETUP_ACKNOWLEDGE 0x0d -#define MT_RESUME 0x26 -#define MT_RESUME_ACKNOWLEDGE 0x2e -#define MT_RESUME_REJECT 0x22 -#define MT_SUSPEND 0x25 -#define MT_SUSPEND_ACKNOWLEDGE 0x2d -#define MT_SUSPEND_REJECT 0x21 -#define MT_USER_INFORMATION 0x20 -#define MT_DISCONNECT 0x45 -#define MT_RELEASE 0x4d -#define MT_RELEASE_COMPLETE 0x5a -#define MT_RESTART 0x46 -#define MT_RESTART_ACKNOWLEDGE 0x4e -#define MT_SEGMENT 0x60 -#define MT_CONGESTION_CONTROL 0x79 -#define MT_INFORMATION 0x7b -#define MT_FACILITY 0x62 -#define MT_NOTIFY 0x6e -#define MT_STATUS 0x7d -#define MT_STATUS_ENQUIRY 0x75 - -#define IE_CAUSE 0x08 - -struct HscxIoctlArg { - int channel; - int mode; - int transbufsize; -}; +#include <linux/init.h> + +#define PH_ACTIVATE_REQ 0x0010 +#define PH_ACTIVATE_CNF 0x0011 +#define PH_ACTIVATE_IND 0x0012 +#define PH_DEACTIVATE_REQ 0x0020 +#define PH_DEACTIVATE_CNF 0x0021 +#define PH_DEACTIVATE_IND 0x0022 +#define PH_DEACT_REQ 0x0024 +#define PH_DEACT_CNF 0x0025 +#define PH_DEACT_IND 0x0026 +#define PH_DEACT_ACK 0x0027 +#define PH_TESTLOOP_REQ 0x0030 +#define PH_PAUSE_CNF 0x0035 +#define PH_PAUSE_IND 0x0036 +#define PH_PULL_REQ 0x0038 +#define PH_PULL_CNF 0x0039 +#define PH_PULL_IND 0x003A +#define PH_DATA_REQ 0x0040 +#define PH_DATA_IND 0x0042 + +#define PH_INFO3_REQ 0x0008 +#define PH_INFO2_IND 0x000A +#define PH_ENABLE_REQ 0x0004 +#define PH_RSYNC_IND 0x0006 +#define PH_RESET_REQ 0x0000 +#define PH_RESET_IND 0x0002 +#define PH_POWERUP_CNF 0x0003 +#define PH_ACTIV_REQ 0x000C +#define PH_I4_P8_IND 0x000D +#define PH_I4_P10_IND 0x000F + +#define MDL_ASSIGN_REQ 0x0050 +#define MDL_ASSIGN_IND 0x0052 +#define MDL_REMOVE_REQ 0x0054 +#define MDL_ERROR_REQ 0x0058 +#define MDL_ERROR_IND 0x005A +#define CARD_AUX_IND 0x005E + +#define DL_UNIT_DATA 6 +#define CC_ESTABLISH 7 +#define DL_ESTABLISH 8 +#define DL_DATA 9 + +#define CC_CONNECT 15 +#define DL_RELEASE 20 +#define DL_FLUSH 21 + +#define CC_REJECT 23 + +#define CC_SETUP_REQ 24 +#define CC_SETUP_CNF 25 +#define CC_SETUP_IND 26 +#define CC_SETUP_RSP 27 +#define CC_SETUP_COMPLETE_IND 28 + +#define CC_DISCONNECT_REQ 29 +#define CC_DISCONNECT_IND 30 + +#define CC_RELEASE_CNF 31 +#define CC_RELEASE_IND 32 +#define CC_RELEASE_REQ 33 + +#define CC_REJECT_REQ 34 + +#define CC_PROCEEDING_IND 35 + +#define CC_DLRL 36 +#define CC_DLEST 37 + +#define CC_ALERTING_REQ 38 +#define CC_ALERTING_IND 39 + +#define DL_STOP 40 +#define DL_START 41 + +#define MDL_INFO_SETUP 42 +#define MDL_INFO_CONN 43 +#define MDL_INFO_REL 44 +#define MDL_NOTEIPROC 46 + +#define LC_ESTABLISH 47 +#define LC_RELEASE 48 + +#define CC_INFO_CHARGE 52 + +#define CC_MORE_INFO 53 +#define CC_IGNORE 54 +#define CC_RESTART 55 + + +#define CC_T303 60 +#define CC_T304 61 +#define CC_T305 62 +#define CC_T308_1 64 +#define CC_T308_2 65 +#define CC_T310 66 +#define CC_T313 67 +#define CC_T318 68 +#define CC_T319 69 + +#define CC_NOSETUP_RSP_ERR 70 +#define CC_SETUP_ERR 71 +#define CC_CONNECT_ERR 72 +#define CC_RELEASE_ERR 73 + +#define CARD_RESET 0x1001 +#define CARD_SETIRQ 0x1002 +#define CARD_INIT 0x1003 +#define CARD_RELEASE 0x1004 +#define CARD_TEST 0x1005 #ifdef __KERNEL__ -#undef DEBUG_MAGIC - -#define MAX_DFRAME_LEN 3072 +#define MAX_DFRAME_LEN 260 #define HSCX_BUFMAX 4096 #define MAX_DATA_SIZE (HSCX_BUFMAX - 4) -#define MAX_DATA_MEM (HSCX_BUFMAX * 2) +#define MAX_DATA_MEM (HSCX_BUFMAX + 64) +#define RAW_BUFMAX (((HSCX_BUFMAX*6)/5) + 5) #define MAX_HEADER_LEN 4 #define MAX_WINDOW 8 +#define MAX_MON_FRAME 32 + +/* #define I4L_IRQ_FLAG SA_INTERRUPT */ +#define I4L_IRQ_FLAG 0 /* * Statemachine */ + struct Fsm { int *jumpmatrix; int state_count, event_count; @@ -226,73 +229,107 @@ struct FsmTimer { }; struct L3Timer { - struct PStack *st; + struct l3_process *pc; struct timer_list tl; int event; }; +#define FLG_L1_ACTIVATING 1 +#define FLG_L1_ACTIVATED 2 +#define FLG_L1_DEACTTIMER 3 +#define FLG_L1_ACTTIMER 4 +#define FLG_L1_T3RUN 5 +#define FLG_L1_PULL_REQ 6 + struct Layer1 { void *hardware; - int hscx; + struct BCState *bcs; struct PStack **stlistp; - int act_state; + int Flags; + struct FsmInst l1m; + struct FsmTimer timer; void (*l1l2) (struct PStack *, int, void *); void (*l1man) (struct PStack *, int, void *); - int hscxmode, hscxchannel, requestpull; + void (*l1tei) (struct PStack *, int, void *); + int mode, bc; }; +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 +#define PACKET_NOACK 250 + +/* Layer2 Flags */ + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 + struct Layer2 { - int sap, tei, ces; - int extended, laptype; - int uihsize, ihsize; + int tei; + int tei_wanted; + int sap; + int maxlen; + unsigned int flag; int vs, va, vr; - struct sk_buff_head i_queue; - int window, orig; - int rejexp; - int debug; - struct sk_buff *windowar[MAX_WINDOW]; + int rc; + int window; int sow; - struct FsmInst l2m; + struct sk_buff *windowar[MAX_WINDOW]; + struct sk_buff_head i_queue; + struct sk_buff_head ui_queue; void (*l2l1) (struct PStack *, int, void *); - void (*l2l1discardq) (struct PStack *, int, void *, int); void (*l2man) (struct PStack *, int, void *); void (*l2l3) (struct PStack *, int, void *); void (*l2tei) (struct PStack *, int, void *); - struct FsmTimer t200_timer, t203_timer; - int t200, n200, t203; - int rc, t200_running; + struct FsmInst l2m; + struct FsmTimer t200, t203; + int T200, N200, T203; + int debug; char debug_id[32]; }; struct Layer3 { - void (*l3l4) (struct PStack *, int, void *); + void (*l3l4) (struct l3_process *, int, void *); void (*l3l2) (struct PStack *, int, void *); - int state, callref; - struct L3Timer timer; - int t303, t304, t305, t308, t310, t313, t318, t319; - int n_t303; + struct l3_process *proc; + struct l3_process *global; + int N303; int debug; - int channr; }; -struct Layer4 { +struct LLInterface { void (*l4l3) (struct PStack *, int, void *); void *userdata; - void (*l1writewakeup) (struct PStack *); - void (*l2writewakeup) (struct PStack *); + void (*l1writewakeup) (struct PStack *, int); + void (*l2writewakeup) (struct PStack *, int); }; + struct Management { + int ri; + struct FsmInst tei_m; + struct FsmTimer t202; + int T202, N202, debug; + void (*layer) (struct PStack *, int, void *); void (*manl1) (struct PStack *, int, void *); void (*manl2) (struct PStack *, int, void *); - void (*teil2) (struct PStack *, int, void *); }; + struct Param { int cause; int loc; int bchannel; - int callref; /* Callreferenz Number */ setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ int chargeinfo; /* Charge Info - only for 1tr6 in * the moment @@ -300,39 +337,114 @@ struct Param { int spv; /* SPV Flag */ }; + struct PStack { struct PStack *next; struct Layer1 l1; struct Layer2 l2; struct Layer3 l3; - struct Layer4 l4; + struct LLInterface lli; struct Management ma; - struct Param *pa; int protocol; /* EDSS1 or 1TR6 */ }; -struct HscxState { - int inuse, init, active; - struct IsdnCardState *sp; - int hscx, mode; - u_char *rcvbuf; /* B-Channel receive Buffer */ - int rcvidx; /* B-Channel receive Buffer Index */ - struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +struct l3_process { + int callref; + int state; + struct L3Timer timer; + int N303; + int debug; + struct Param para; + struct Channel *chan; + struct PStack *st; + struct l3_process *next; +}; + +struct hscx_hw { + int rcvidx; + int count; /* Current skb sent count */ + u_char *rcvbuf; /* B-Channel receive Buffer */ + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +}; + +struct hfcB_hw { + unsigned int *send; + int f1; + int f2; + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +}; + +struct tiger_hw { + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ + u_int *send; + u_int *s_irq; + u_int *s_end; + u_int *sendp; + u_int *rec; + int free; + u_char *rcvbuf; + u_char *sendbuf; + u_char *sp; + int sendcnt; + u_int s_tot; + u_int r_bitcnt; + u_int r_tot; + u_int r_err; + u_int r_fcs; + u_char r_state; + u_char r_one; + u_char r_val; + u_char s_state; +}; + +struct amd7930_hw { + u_char *tx_buff; + u_char *rv_buff; + int rv_buff_in; + int rv_buff_out; + struct sk_buff *rv_skb; + struct hdlc_state *hdlc_state; + struct tq_struct tq_rcv; + struct tq_struct tq_xmt; + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +}; + +#define BC_FLG_INIT 1 +#define BC_FLG_ACTIV 2 +#define BC_FLG_BUSY 3 +#define BC_FLG_NOFRAME 4 +#define BC_FLG_HALF 5 +#define BC_FLG_EMPTY 6 + +#define L1_MODE_NULL 0 +#define L1_MODE_TRANS 1 +#define L1_MODE_HDLC 2 + +struct BCState { + int channel; + int mode; + int Flag; + struct IsdnCardState *cs; int tx_cnt; /* B-Channel transmit counter */ - int count; /* Current skb sent count */ struct sk_buff_head rqueue; /* B-Channel receive Queue */ - struct sk_buff_head squeue; /* B-Channel receive Queue */ + struct sk_buff_head squeue; /* B-Channel send Queue */ struct PStack *st; struct tq_struct tqueue; int event; -#ifdef DEBUG_MAGIC - int magic; /* 301270 */ -#endif + int (*BC_SetStack) (struct PStack *, struct BCState *); + void (*BC_Close) (struct BCState *); + union { + struct hscx_hw hscx; + struct hfcB_hw hfc; + struct tiger_hw tiger; + struct amd7930_hw amd7930; + } hw; }; struct LcFsm { - struct FsmInst lcfi; int type; + int delay; + struct FsmInst lcfi; struct Channel *ch; void (*lccall) (struct LcFsm *, int, void *); struct PStack *st; @@ -343,52 +455,209 @@ struct LcFsm { }; struct Channel { - struct PStack ds, is; - struct IsdnCardState *sp; - int hscx; + struct PStack *b_st, *d_st; + struct IsdnCardState *cs; + struct BCState *bcs; int chan; int incoming; struct FsmInst fi; - struct LcFsm lc_d, lc_b; - struct Param para; + struct LcFsm *lc_d; + struct LcFsm *lc_b; struct FsmTimer drel_timer, dial_timer; int debug; -#ifdef DEBUG_MAGIC - int magic; /* 301272 */ -#endif int l2_protocol, l2_active_protocol; - int l2_primitive, l2_headersize; int data_open; - int outcallref; - int impair; + struct l3_process *proc; + setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ int Flags; /* for remembering action done in l4 */ int leased; }; -struct IsdnCardState { -#ifdef DEBUG_MAGIC - int magic; -#endif - unsigned char typ; - unsigned char subtyp; - int protocol; - unsigned int irq; +struct elsa_hw { + unsigned int base; + unsigned int cfg; + unsigned int ctrl; + unsigned int ale; + unsigned int isac; + unsigned int itac; + unsigned int hscx; + unsigned int trig; + unsigned int timer; + unsigned int counter; + unsigned int status; + struct timer_list tl; + u_char ctrl_reg; +}; + +struct teles3_hw { + unsigned int cfg_reg; + unsigned int isac; + unsigned int hscx[2]; + unsigned int isacfifo; + unsigned int hscxfifo[2]; +}; + +struct teles0_hw { unsigned int cfg_reg; unsigned int membase; +}; + +struct avm_hw { + unsigned int cfg_reg; unsigned int isac; unsigned int hscx[2]; + unsigned int isacfifo; + unsigned int hscxfifo[2]; unsigned int counter; +}; + +struct ix1_hw { + unsigned int cfg_reg; + unsigned int isac_ale; + unsigned int isac; + unsigned int hscx_ale; + unsigned int hscx; +}; + +struct diva_hw { + unsigned int cfg_reg; + unsigned int ctrl; + unsigned int isac_adr; + unsigned int isac; + unsigned int hscx_adr; + unsigned int hscx; + unsigned int status; + struct timer_list tl; + u_char ctrl_reg; +}; + +struct asus_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; + unsigned int u7; + unsigned int pots; +}; + + +struct hfc_hw { + unsigned int addr; + unsigned int fifosize; + unsigned char cirm; + unsigned char ctmt; + unsigned char cip; + u_char isac_spcr; + struct timer_list timer; +}; + +struct sedl_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; + unsigned int reset_on; + unsigned int reset_off; +}; + +struct spt_hw { + unsigned int cfg_reg; + unsigned int isac; + unsigned int hscx[2]; + unsigned char res_irq; +}; + +struct mic_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; +}; + +struct njet_hw { + unsigned int base; + unsigned int isac; + unsigned int auxa; + unsigned char auxd; + unsigned char dmactrl; + unsigned char ctrl_reg; + unsigned char irqmask0; + unsigned char irqstat0; + unsigned char last_is0; +}; + +struct hfcD_hw { + unsigned int addr; + unsigned int bfifosize; + unsigned int dfifosize; + unsigned char cirm; + unsigned char ctmt; + unsigned char cip; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char int_s1; + unsigned char sctrl; + unsigned char stat; + unsigned char fifo; + unsigned char f1; + unsigned char f2; + unsigned int *send; + struct timer_list timer; +}; + +#define HW_IOM1 0 +#define HW_IPAC 1 +#define FLG_TWO_DCHAN 4 +#define FLG_L1_DBUSY 5 +#define FLG_DBUSY_TIMER 6 +#define FLG_LOCK_ATOMIC 7 +#define HW_MON0_RX_END 8 +#define HW_MON1_RX_END 9 +#define HW_MON0_TX_END 10 +#define HW_MON1_TX_END 11 + +struct IsdnCardState { + unsigned char typ; + unsigned char subtyp; + int protocol; + unsigned int irq; + int HW_Flags; + int *busy_flag; + union { + struct elsa_hw elsa; + struct teles0_hw teles0; + struct teles3_hw teles3; + struct avm_hw avm; + struct ix1_hw ix1; + struct diva_hw diva; + struct asus_hw asus; + struct hfc_hw hfc; + struct sedl_hw sedl; + struct spt_hw spt; + struct mic_hw mic; + struct njet_hw njet; + struct hfcD_hw hfcD; + struct ix1_hw niccy; + } hw; int myid; isdn_if iif; u_char *status_buf; u_char *status_read; u_char *status_write; u_char *status_end; - void (*ph_command) (struct IsdnCardState *, unsigned int); - void (*modehscx) (struct HscxState *, int, int); - void (*hscx_fill_fifo) (struct HscxState *); - void (*isac_fill_fifo) (struct IsdnCardState *); + u_char (*readisac) (struct IsdnCardState *, u_char); + void (*writeisac) (struct IsdnCardState *, u_char, u_char); + void (*readisacfifo) (struct IsdnCardState *, u_char *, int); + void (*writeisacfifo) (struct IsdnCardState *, u_char *, int); + u_char (*BC_Read_Reg) (struct IsdnCardState *, int, u_char); + void (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char); + void (*BC_Send_Data) (struct BCState *); + int (*cardmsg) (struct IsdnCardState *, int, void *); + void (*l1cmd) (struct IsdnCardState *, int, void *); struct Channel channel[2]; + struct BCState bcs[2]; struct PStack *stlist; u_char *rcvbuf; int rcvidx; @@ -396,16 +665,20 @@ struct IsdnCardState { int tx_cnt; int event; struct tq_struct tqueue; - int ph_active; + struct timer_list dbusytimer; struct sk_buff_head rq, sq; /* D-channel queues */ - int cardnr; int ph_state; - struct PStack *teistack; - struct HscxState hs[2]; + int cardnr; int dlogflag; char *dlogspace; int debug; - unsigned int CallFlags; + u_char *mon_tx; + u_char *mon_rx; + int mon_txp; + int mon_txc; + int mon_rxp; + u_char mocr; + void (*setstack_d) (struct PStack *, struct IsdnCardState *); }; #define MON0_RX 1 @@ -419,99 +692,246 @@ struct IsdnCardState { #define ISDN_CTYPE_PNP 4 #define ISDN_CTYPE_A1 5 #define ISDN_CTYPE_ELSA 6 -#define ISDN_CTYPE_ELSA_QS1000 7 +#define ISDN_CTYPE_ELSA_PNP 7 #define ISDN_CTYPE_TELESPCMCIA 8 #define ISDN_CTYPE_IX1MICROR2 9 +#define ISDN_CTYPE_ELSA_PCMCIA 10 +#define ISDN_CTYPE_DIEHLDIVA 11 +#define ISDN_CTYPE_ASUSCOM 12 +#define ISDN_CTYPE_TELEINT 13 +#define ISDN_CTYPE_TELES3C 14 +#define ISDN_CTYPE_SEDLBAUER 15 +#define ISDN_CTYPE_SPORTSTER 16 +#define ISDN_CTYPE_MIC 17 +#define ISDN_CTYPE_ELSA_PCI 18 +#define ISDN_CTYPE_COMPAQ_ISA 19 +#define ISDN_CTYPE_NETJET 20 +#define ISDN_CTYPE_TELESPCI 21 +#define ISDN_CTYPE_SEDLBAUER_PCMCIA 22 +#define ISDN_CTYPE_AMD7930 23 +#define ISDN_CTYPE_NICCY 24 + +#define ISDN_CTYPE_COUNT 24 + +#ifdef ISDN_CHIP_ISAC +#undef ISDN_CHIP_ISAC +#endif -#define ISDN_CTYPE_COUNT 9 +#define HISAX_INITFUNC(__arginit) __initfunc(__arginit) +#define HISAX_INITDATA __initdata #ifdef CONFIG_HISAX_16_0 #define CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_TELES0 0 #endif #ifdef CONFIG_HISAX_16_3 #define CARD_TELES3 (1<< ISDN_CTYPE_16_3) | (1<< ISDN_CTYPE_PNP) | \ - (1<< ISDN_CTYPE_TELESPCMCIA) + (1<< ISDN_CTYPE_TELESPCMCIA) | (1<< ISDN_CTYPE_COMPAQ_ISA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_TELES3 0 #endif #ifdef CONFIG_HISAX_AVM_A1 #define CARD_AVM_A1 (1<< ISDN_CTYPE_A1) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_AVM_A1 0 #endif -#ifdef CONFIG_HISAX_ELSA_PCC -#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000) +#ifdef CONFIG_HISAX_ELSA +#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_PNP) | \ + (1<< ISDN_CTYPE_ELSA_PCMCIA) | (1<< ISDN_CTYPE_ELSA_PCI) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#undef HISAX_INITFUNC +#define HISAX_INITFUNC(__arginit) __arginit +#undef HISAX_INITDATA +#define HISAX_INITDATA #else #define CARD_ELSA 0 #endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA -#if CARD_ELSA -#error "You can't use a ELSA ISA card and a ELSA PCMCIA card with the same driver" -#else -#undef CARD_ELSA -#define CARD_ELSA (1<< ISDN_CTYPE_ELSA_QS1000) -#endif -#endif #ifdef CONFIG_HISAX_IX1MICROR2 #define CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_IX1MICROR2 0 #endif +#ifdef CONFIG_HISAX_DIEHLDIVA +#define CARD_DIEHLDIVA (1 << ISDN_CTYPE_DIEHLDIVA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_DIEHLDIVA 0 +#endif + +#ifdef CONFIG_HISAX_ASUSCOM +#define CARD_ASUSCOM (1 << ISDN_CTYPE_ASUSCOM) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_ASUSCOM 0 +#endif + +#ifdef CONFIG_HISAX_TELEINT +#define CARD_TELEINT (1 << ISDN_CTYPE_TELEINT) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_TELEINT 0 +#endif + +#ifdef CONFIG_HISAX_SEDLBAUER +#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_SEDLBAUER 0 +#endif + +#ifdef CONFIG_HISAX_SPORTSTER +#define CARD_SPORTSTER (1 << ISDN_CTYPE_SPORTSTER) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_SPORTSTER 0 +#endif + +#ifdef CONFIG_HISAX_MIC +#define CARD_MIC (1 << ISDN_CTYPE_MIC) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_MIC 0 +#endif + +#ifdef CONFIG_HISAX_NETJET +#define CARD_NETJET (1 << ISDN_CTYPE_NETJET) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_NETJET 0 +#endif + +#ifdef CONFIG_HISAX_TELES3C +#define CARD_TELES3C (1<< ISDN_CTYPE_TELES3C) +#else +#define CARD_TELES3C 0 +#endif + +#ifdef CONFIG_HISAX_AMD7930 +#define CARD_AMD7930 (1 << ISDN_CTYPE_AMD7930) +#else +#define CARD_AMD7930 0 +#endif + +#ifdef CONFIG_HISAX_NICCY +#define CARD_NICCY (1 << ISDN_CTYPE_NICCY) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_NICCY 0 +#endif + + #define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \ - | CARD_IX1MICROR2) + | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \ + | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \ + | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \ + | CARD_NICCY) + +#define TEI_PER_CARD 0 + +#ifdef CONFIG_HISAX_1TR6 +#undef TEI_PER_CARD +#define TEI_PER_CARD 1 +#endif + +#ifdef CONFIG_HISAX_EURO +#undef TEI_PER_CARD +#define TEI_PER_CARD 1 +#define HISAX_EURO_SENDCOMPLETE 1 +#ifdef CONFIG_HISAX_ML +#undef HISAX_EURO_SENDCOMPLETE +#endif +#undef HISAX_DE_AOC +#ifdef CONFIG_DE_AOC +#define HISAX_DE_AOC 1 +#endif +#endif + +#if TEI_PER_CARD +#undef TEI_FIXED +#endif + +#undef PTP_DATA_LINK + +#ifdef PTP_DATA_LINK +#undef TEI_FIXED +#define TEI_FIXED 0 +#define LAYER2_WATCHING +#endif struct IsdnCard { int typ; int protocol; /* EDSS1 or 1TR6 */ - unsigned int para[3]; - struct IsdnCardState *sp; + unsigned int para[4]; + struct IsdnCardState *cs; }; - -#define LAPD 0 -#define LAPB 1 - -void l2down(struct PStack *st, u_char pr, struct sk_buff *skb); -void l2up(struct PStack *st, u_char pr, struct sk_buff *skb); -void acceptph(struct PStack *st, struct sk_buff *skb); void setstack_isdnl2(struct PStack *st, char *debug_id); -int HiSax_inithardware(void); +int HiSax_inithardware(int *); void HiSax_closehardware(void); -void setstack_HiSax(struct PStack *st, struct IsdnCardState *sp); -unsigned int randomces(void); +void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs); +unsigned int random_ri(void); void setstack_isdnl3(struct PStack *st, struct Channel *chanp); void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st); void releasestack_isdnl2(struct PStack *st); void releasestack_isdnl3(struct PStack *st); void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st); -void newcallref(struct PStack *st); -int setstack_hscx(struct PStack *st, struct HscxState *hs); u_char *findie(u_char * p, int size, u_char ie, int wanted_set); int getcallref(u_char * p); +int newcallref(void); void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount); void FsmFree(struct Fsm *fsm); int FsmEvent(struct FsmInst *fi, int event, void *arg); void FsmChangeState(struct FsmInst *fi, int newstate); void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft); -int FsmAddTimer(struct FsmTimer *ft, int millisec, - int event, void *arg, int where); +int FsmAddTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); +void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); void FsmDelTimer(struct FsmTimer *ft, int where); -int FsmTimerRunning(struct FsmTimer *ft); void jiftime(char *s, long mark); int HiSax_command(isdn_ctrl * ic); -int HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb); +int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb); void HiSax_putstatus(struct IsdnCardState *csta, char *buf); void HiSax_reportcard(int cardnr); int QuickHex(char *txt, u_char * p, int cnt); @@ -520,10 +940,12 @@ void dlogframe(struct IsdnCardState *sp, u_char * p, int size, char *comment); void iecpy(u_char * dest, u_char * iestart, int ieoffset); void setstack_transl2(struct PStack *st); void releasestack_transl2(struct PStack *st); -void close_hscxstate(struct HscxState *); void setstack_tei(struct PStack *st); - -#endif /* __KERNEL__ */ +void setstack_manager(struct PStack *st); +#ifdef ISDN_CHIP_ISAC +void setstack_isac(struct PStack *st, struct IsdnCardState *cs); +#endif /* ISDN_CHIP_ISAC */ +#endif /* __KERNEL__ */ #define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);} @@ -533,8 +955,12 @@ void CallcNew(void); void CallcFree(void); int CallcNewChan(struct IsdnCardState *csta); void CallcFreeChan(struct IsdnCardState *csta); +void Isdnl1New(void); +void Isdnl1Free(void); void Isdnl2New(void); void Isdnl2Free(void); void init_tei(struct IsdnCardState *sp, int protocol); void release_tei(struct IsdnCardState *sp); char *HiSax_getrev(const char *revision); +void TeiNew(void); +void TeiFree(void); diff --git a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c new file mode 100644 index 000000000..c44cc54df --- /dev/null +++ b/drivers/isdn/hisax/hscx.c @@ -0,0 +1,280 @@ +/* $Id: hscx.c,v 1.7 1998/02/12 23:07:36 keil Exp $ + + * hscx.c HSCX specific routines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hscx.c,v $ + * Revision 1.7 1998/02/12 23:07:36 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.6 1998/02/02 13:41:12 keil + * new init + * + * Revision 1.5 1997/11/06 17:09:34 keil + * New 2.1 init code + * + * Revision 1.4 1997/10/29 19:01:06 keil + * changes for 2.1 + * + * Revision 1.3 1997/07/27 21:38:34 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:17 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hscx.h" +#include "isdnl1.h" +#include <linux/interrupt.h> + +static char *HSCXVer[] HISAX_INITDATA = +{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", + "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; + +HISAX_INITFUNC(int +HscxVersion(struct IsdnCardState *cs, char *s)) +{ + int verA, verB; + + verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf; + verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf; + printk(KERN_INFO "%s HSCX version A: %s B: %s\n", s, + HSCXVer[verA], HSCXVer[verB]); + if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) + return (1); + else + return (0); +} + +void +modehscx(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int hscx = bcs->channel; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "hscx %c mode %d ichan %d", + 'A' + hscx, mode, bc); + debugl1(cs, tmp); + } + bcs->mode = mode; + cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, 0x85); + cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0); + cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0); + cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30); + cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7); + cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7); + + /* Switch IOM 1 SSI */ + if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0)) + bc = 1 - bc; + + if (bc == 0) { + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + } else { + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x3); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x3); + } + switch (mode) { + case (L1_MODE_NULL): + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0xff); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0xff); + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84); + break; + case (L1_MODE_TRANS): + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4); + break; + case (L1_MODE_HDLC): + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c); + break; + } + if (mode) + cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41); + cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00); +} + +void +hscx_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +hscx_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA_REQ): + save_flags(flags); + cli(); + if (st->l1.bcs->hw.hscx.tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->hw.hscx.tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hscx.count = 0; + restore_flags(flags); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + } + break; + case (PH_PULL_IND): + if (st->l1.bcs->hw.hscx.tx_skb) { + printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hscx.tx_skb = skb; + st->l1.bcs->hw.hscx.count = 0; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + break; + case (PH_PULL_REQ): + if (!st->l1.bcs->hw.hscx.tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } + +} + +void +close_hscxstate(struct BCState *bcs) +{ + struct sk_buff *skb; + + modehscx(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.hscx.rcvbuf) { + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + } + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + if (bcs->hw.hscx.tx_skb) { + dev_kfree_skb(bcs->hw.hscx.tx_skb); + bcs->hw.hscx.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_hscxstate(struct IsdnCardState *cs, + int bc) +{ + struct BCState *bcs = cs->bcs + bc; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hscx.rcvbuf\n"); + return (1); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->hw.hscx.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->hw.hscx.rcvidx = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +hscx_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + modehscx(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + modehscx(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_hscx(struct PStack *st, struct BCState *bcs) +{ + if (open_hscxstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hscx_l2l1; + st->ma.manl1 = hscx_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + +HISAX_INITFUNC(void +clear_pending_hscx_ints(struct IsdnCardState *cs)) +{ + int val; + char tmp[64]; + + val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA); + sprintf(tmp, "HSCX B ISTA %x", val); + debugl1(cs, tmp); + if (val & 0x01) { + val = cs->BC_Read_Reg(cs, 1, HSCX_EXIR); + sprintf(tmp, "HSCX B EXIR %x", val); + debugl1(cs, tmp); + } else if (val & 0x02) { + val = cs->BC_Read_Reg(cs, 0, HSCX_EXIR); + sprintf(tmp, "HSCX A EXIR %x", val); + debugl1(cs, tmp); + } + val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA); + sprintf(tmp, "HSCX A ISTA %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 1, HSCX_STAR); + sprintf(tmp, "HSCX B STAR %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 0, HSCX_STAR); + sprintf(tmp, "HSCX A STAR %x", val); + debugl1(cs, tmp); + cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF); + cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF); + cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0); + cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0); +} + +HISAX_INITFUNC(void +inithscx(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_hscx; + cs->bcs[1].BC_SetStack = setstack_hscx; + cs->bcs[0].BC_Close = close_hscxstate; + cs->bcs[1].BC_Close = close_hscxstate; + modehscx(cs->bcs, 0, 0); + modehscx(cs->bcs + 1, 0, 0); +} diff --git a/drivers/isdn/hisax/hscx.h b/drivers/isdn/hisax/hscx.h new file mode 100644 index 000000000..ac2d38086 --- /dev/null +++ b/drivers/isdn/hisax/hscx.h @@ -0,0 +1,46 @@ +/* $Id: hscx.h,v 1.3 1997/07/27 21:38:35 keil Exp $ + + * hscx.h HSCX specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hscx.h,v $ + * Revision 1.3 1997/07/27 21:38:35 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:18 keil + * first version + * + * + */ + +/* All Registers original Siemens Spec */ + +#define HSCX_ISTA 0x20 +#define HSCX_CCR1 0x2f +#define HSCX_CCR2 0x2c +#define HSCX_TSAR 0x31 +#define HSCX_TSAX 0x30 +#define HSCX_XCCR 0x32 +#define HSCX_RCCR 0x33 +#define HSCX_MODE 0x22 +#define HSCX_CMDR 0x21 +#define HSCX_EXIR 0x24 +#define HSCX_XAD1 0x24 +#define HSCX_XAD2 0x25 +#define HSCX_RAH2 0x27 +#define HSCX_RSTA 0x27 +#define HSCX_TIMR 0x23 +#define HSCX_STAR 0x21 +#define HSCX_RBCL 0x25 +#define HSCX_XBCH 0x2d +#define HSCX_VSTR 0x2e +#define HSCX_RLCR 0x2e +#define HSCX_MASK 0x20 + +extern int HscxVersion(struct IsdnCardState *cs, char *s); +extern void hscx_sched_event(struct BCState *bcs, int event); +extern void modehscx(struct BCState *bcs, int mode, int bc); +extern void clear_pending_hscx_ints(struct IsdnCardState *cs); +extern void inithscx(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c new file mode 100644 index 000000000..64e80e7b2 --- /dev/null +++ b/drivers/isdn/hisax/hscx_irq.c @@ -0,0 +1,320 @@ +/* $Id: hscx_irq.c,v 1.7 1998/02/12 23:07:37 keil Exp $ + + * hscx_irq.c low level b-channel stuff for Siemens HSCX + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * This is an include file for fast inline IRQ stuff + * + * $Log: hscx_irq.c,v $ + * Revision 1.7 1998/02/12 23:07:37 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.6 1997/10/29 19:01:07 keil + * changes for 2.1 + * + * Revision 1.5 1997/10/01 09:21:35 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.4 1997/08/15 17:48:02 keil + * cosmetic + * + * Revision 1.3 1997/07/27 21:38:36 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:19 keil + * first version + * + * + */ + + +static inline void +waitforCEC(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC timeout\n"); +} + + +static inline void +waitforXFW(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); +} + +static inline void +WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(cs, hscx); + WRITEHSCX(cs, hscx, HSCX_CMDR, data); + restore_flags(flags); +} + + + +static void +hscx_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct IsdnCardState *cs = bcs->cs; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_empty_fifo"); + + if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hscx_empty_fifo: incoming packet too large"); + WriteHSCXCMDR(cs, bcs->channel, 0x80); + bcs->hw.hscx.rcvidx = 0; + return; + } + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + bcs->hw.hscx.rcvidx += count; + save_flags(flags); + cli(); + READHSCXFIFO(cs, bcs->channel, ptr, count); + WriteHSCXCMDR(cs, bcs->channel, 0x80); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[256]; + char *t = tmp; + + t += sprintf(t, "hscx_empty_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, tmp); + } +} + +static void +hscx_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int more, count; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + u_char *ptr; + long flags; + + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_fill_fifo"); + + if (!bcs->hw.hscx.tx_skb) + return; + if (bcs->hw.hscx.tx_skb->len <= 0) + return; + + more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; + if (bcs->hw.hscx.tx_skb->len > fifo_size) { + more = !0; + count = fifo_size; + } else + count = bcs->hw.hscx.tx_skb->len; + + waitforXFW(cs, bcs->channel); + save_flags(flags); + cli(); + ptr = bcs->hw.hscx.tx_skb->data; + skb_pull(bcs->hw.hscx.tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hscx.count += count; + WRITEHSCXFIFO(cs, bcs->channel, ptr, count); + WriteHSCXCMDR(cs, bcs->channel, more ? 0x8 : 0xa); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[256]; + char *t = tmp; + + t += sprintf(t, "hscx_fill_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, tmp); + } +} + +static inline void +hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) +{ + u_char r; + struct BCState *bcs = cs->bcs + hscx; + struct sk_buff *skb; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + int count; + char tmp[32]; + + if (!test_bit(BC_FLG_INIT, &bcs->Flag)) + return; + + if (val & 0x80) { /* RME */ + r = READHSCX(cs, hscx, HSCX_RSTA); + if ((r & 0xf0) != 0xa0) { + if (!(r & 0x80)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX invalid frame"); + if ((r & 0x40) && bcs->mode) + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "HSCX RDO mode=%d", + bcs->mode); + debugl1(cs, tmp); + } + if (!(r & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX CRC error"); + WriteHSCXCMDR(cs, hscx, 0x80); + } else { + count = READHSCX(cs, hscx, HSCX_RBCL) & ( + test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f); + if (count == 0) + count = fifo_size; + hscx_empty_fifo(bcs, count); + if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { + if (cs->debug & L1_DEB_HSCX_FIFO) { + sprintf(tmp, "HX Frame %d", count); + debugl1(cs, tmp); + } + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "HSCX: receive out of memory\n"); + else { + memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + } + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + hscx_empty_fifo(bcs, fifo_size); + if (bcs->mode == L1_MODE_TRANS) { + /* receive audio data */ + if (!(skb = dev_alloc_skb(fifo_size))) + printk(KERN_WARNING "HiSax: receive out of memory\n"); + else { + memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + } + if (val & 0x10) { /* XPR */ + if (bcs->hw.hscx.tx_skb) + if (bcs->hw.hscx.tx_skb->len) { + hscx_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->hw.hscx.tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); + dev_kfree_skb(bcs->hw.hscx.tx_skb); + bcs->hw.hscx.count = 0; + bcs->hw.hscx.tx_skb = NULL; + } + if ((bcs->hw.hscx.tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +static inline void +hscx_int_main(struct IsdnCardState *cs, u_char val) +{ + + u_char exval; + struct BCState *bcs; + char tmp[32]; + + if (val & 0x01) { + bcs = cs->bcs + 1; + exval = READHSCX(cs, 1, HSCX_EXIR); + if (exval == 0x40) { + if (bcs->mode == 1) + hscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->hw.hscx.tx_skb) { + skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteHSCXCMDR(cs, bcs->channel, 0x01); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); + debugl1(cs, tmp); + } + } + } else if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "HSCX B EXIR %x", exval); + debugl1(cs, tmp); + } + } + if (val & 0xf8) { + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "HSCX B interrupt %x", val); + debugl1(cs, tmp); + } + hscx_interrupt(cs, val, 1); + } + if (val & 0x02) { + bcs = cs->bcs; + exval = READHSCX(cs, 0, HSCX_EXIR); + if (exval == 0x40) { + if (bcs->mode == L1_MODE_TRANS) + hscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->hw.hscx.tx_skb) { + skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteHSCXCMDR(cs, bcs->channel, 0x01); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); + debugl1(cs, tmp); + } + } + } else if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "HSCX A EXIR %x", exval); + debugl1(cs, tmp); + } + } + if (val & 0x04) { + exval = READHSCX(cs, 0, HSCX_ISTA); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "HSCX A interrupt %x", exval); + debugl1(cs, tmp); + } + hscx_interrupt(cs, exval, 0); + } +} diff --git a/drivers/isdn/hisax/ipac.h b/drivers/isdn/hisax/ipac.h new file mode 100644 index 000000000..6d856ec6c --- /dev/null +++ b/drivers/isdn/hisax/ipac.h @@ -0,0 +1,35 @@ +/* $Id: ipac.h,v 1.2 1997/10/29 18:51:21 keil Exp $ + + * ipac.h IPAC specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: ipac.h,v $ + * Revision 1.2 1997/10/29 18:51:21 keil + * New files + * + * Revision 1.1.2.1 1997/10/17 22:10:48 keil + * new files on 2.0 + * + * + * + */ + + +/* All Registers original Siemens Spec */ + +#define IPAC_CONF 0xC0 +#define IPAC_MASK 0xC1 +#define IPAC_ISTA 0xC1 +#define IPAC_ID 0xC2 +#define IPAC_ACFG 0xC3 +#define IPAC_AOE 0xC4 +#define IPAC_ARX 0xC5 +#define IPAC_PITA1 0xC6 +#define IPAC_PITA2 0xC7 +#define IPAC_POTA1 0xC8 +#define IPAC_POTA2 0xC9 +#define IPAC_PCFG 0xCA +#define IPAC_SCFG 0xCB +#define IPAC_TIMR2 0xCC diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c new file mode 100644 index 000000000..9a2624ef4 --- /dev/null +++ b/drivers/isdn/hisax/isac.c @@ -0,0 +1,679 @@ +/* $Id: isac.c,v 1.12 1998/02/12 23:07:40 keil Exp $ + + * isac.c ISAC specific routines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: isac.c,v $ + * Revision 1.12 1998/02/12 23:07:40 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.11 1998/02/09 10:54:49 keil + * fixes for leased mode + * + * Revision 1.10 1998/02/02 13:37:37 keil + * new init + * + * Revision 1.9 1997/11/06 17:09:07 keil + * New 2.1 init code + * + * Revision 1.8 1997/10/29 19:00:03 keil + * new layer1,changes for 2.1 + * + * Revision 1.7 1997/10/01 09:21:37 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.6 1997/08/15 17:47:08 keil + * avoid oops because a uninitialised timer + * + * Revision 1.5 1997/08/07 17:48:49 keil + * fix wrong parenthesis + * + * Revision 1.4 1997/07/30 17:11:59 keil + * fixed Timer3 + * + * Revision 1.3 1997/07/27 21:37:40 keil + * T3 implemented; supervisor l1timer; B-channel TEST_LOOP + * + * Revision 1.2 1997/06/26 11:16:15 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "isdnl1.h" +#include <linux/interrupt.h> + +#define DBUSY_TIMER_VALUE 80 +#define ARCOFI_USE 1 + +static char *ISACVer[] HISAX_INITDATA = +{"2086/2186 V1.1", "2085 B1", "2085 B2", + "2085 V2.3"}; + +void +ISACVersion(struct IsdnCardState *cs, char *s) +{ + int val; + + val = cs->readisac(cs, ISAC_RBCH); + printk(KERN_INFO "%s ISAC version : %s\n", s, ISACVer[(val >> 5) & 3]); +} + +static void +ph_command(struct IsdnCardState *cs, unsigned int command) +{ + if (cs->debug & L1_DEB_ISAC) { + char tmp[32]; + sprintf(tmp, "ph_command %x", command); + debugl1(cs, tmp); + } + cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3); +} + +static void +manl1_msg(struct IsdnCardState *cs, int msg, void *arg) { + struct PStack *st; + + st = cs->stlist; + while (st) { + st->ma.manl1(st, msg, arg); + st = st->next; + } +} + +static void +isac_new_ph(struct IsdnCardState *cs) +{ + switch (cs->ph_state) { + case (ISAC_IND_RS): + case (ISAC_IND_EI): + ph_command(cs, ISAC_CMD_DUI); + manl1_msg(cs, PH_RESET_IND, NULL); + break; + case (ISAC_IND_DID): + manl1_msg(cs, PH_DEACT_CNF, NULL); + break; + case (ISAC_IND_DR): + manl1_msg(cs, PH_DEACT_IND, NULL); + break; + case (ISAC_IND_PU): + manl1_msg(cs, PH_POWERUP_CNF, NULL); + break; + case (ISAC_IND_RSY): + manl1_msg(cs, PH_RSYNC_IND, NULL); + break; + case (ISAC_IND_ARD): + manl1_msg(cs, PH_INFO2_IND, NULL); + break; + case (ISAC_IND_AI8): + manl1_msg(cs, PH_I4_P8_IND, NULL); + break; + case (ISAC_IND_AI10): + manl1_msg(cs, PH_I4_P10_IND, NULL); + break; + default: + break; + } +} + +static void +isac_bh(struct IsdnCardState *cs) +{ + struct PStack *stptr; + + if (!cs) + return; + + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL); + stptr = stptr->next; + } + } + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) + isac_new_ph(cs); + 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); + if (test_and_clear_bit(D_RX_MON0, &cs->event)) + test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_RX_MON1, &cs->event)) + test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_TX_MON0, &cs->event)) + test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_TX_MON1, &cs->event)) + test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags); +} + +void +isac_empty_fifo(struct IsdnCardState *cs, int count) +{ + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "isac_empty_fifo"); + + if ((cs->rcvidx + count) >= MAX_DFRAME_LEN) { + if (cs->debug & L1_DEB_WARN) { + char tmp[40]; + sprintf(tmp, "isac_empty_fifo overrun %d", + cs->rcvidx + count); + debugl1(cs, tmp); + } + cs->writeisac(cs, ISAC_CMDR, 0x80); + cs->rcvidx = 0; + return; + } + ptr = cs->rcvbuf + cs->rcvidx; + cs->rcvidx += count; + save_flags(flags); + cli(); + cs->readisacfifo(cs, ptr, count); + cs->writeisac(cs, ISAC_CMDR, 0x80); + restore_flags(flags); + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "isac_empty_fifo cnt %d", count); + QuickHex(t, ptr, count); + debugl1(cs, tmp); + } +} + +static void +isac_fill_fifo(struct IsdnCardState *cs) +{ + int count, more; + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "isac_fill_fifo"); + + if (!cs->tx_skb) + return; + + count = cs->tx_skb->len; + if (count <= 0) + return; + + more = 0; + if (count > 32) { + more = !0; + count = 32; + } + save_flags(flags); + cli(); + ptr = cs->tx_skb->data; + skb_pull(cs->tx_skb, count); + cs->tx_cnt += count; + cs->writeisacfifo(cs, ptr, count); + cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa); + restore_flags(flags); + if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + debugl1(cs, "isac_fill_fifo dbusytimer running"); + del_timer(&cs->dbusytimer); + } + init_timer(&cs->dbusytimer); + cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); + add_timer(&cs->dbusytimer); + if (cs->debug & L1_DEB_ISAC_FIFO) { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "isac_fill_fifo cnt %d", count); + QuickHex(t, ptr, count); + debugl1(cs, tmp); + } +} + +void +isac_sched_event(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +isac_interrupt(struct IsdnCardState *cs, u_char val) +{ + u_char exval, v1; + struct sk_buff *skb; + unsigned int count; + long flags; + char tmp[32]; + + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "ISAC interrupt %x", val); + debugl1(cs, tmp); + } + if (val & 0x80) { /* RME */ + exval = cs->readisac(cs, ISAC_RSTA); + if ((exval & 0x70) != 0x20) { + if (exval & 0x40) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC RDO"); + if (!(exval & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC CRC error"); + cs->writeisac(cs, ISAC_CMDR, 0x80); + } else { + count = cs->readisac(cs, ISAC_RBCL) & 0x1f; + if (count == 0) + count = 32; + isac_empty_fifo(cs, count); + save_flags(flags); + cli(); + if ((count = cs->rcvidx) > 0) { + cs->rcvidx = 0; + if (!(skb = alloc_skb(count, GFP_ATOMIC))) + printk(KERN_WARNING "HiSax: D receive out of memory\n"); + else { + memcpy(skb_put(skb, count), cs->rcvbuf, count); + skb_queue_tail(&cs->rq, skb); + } + } + restore_flags(flags); + } + cs->rcvidx = 0; + isac_sched_event(cs, D_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + isac_empty_fifo(cs, 32); + } + if (val & 0x20) { /* RSC */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC RSC interrupt"); + } + if (val & 0x10) { /* XPR */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + isac_sched_event(cs, D_CLEARBUSY); + if (cs->tx_skb) + if (cs->tx_skb->len) { + isac_fill_fifo(cs); + 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; + isac_fill_fifo(cs); + } else + isac_sched_event(cs, D_XMTBUFREADY); + } + afterXPR: + if (val & 0x04) { /* CISQ */ + cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf; + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "ph_state change %x", cs->ph_state); + debugl1(cs, tmp); + } + isac_sched_event(cs, D_L1STATECHANGE); + } + if (val & 0x02) { /* SIN */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC SIN interrupt"); + } + if (val & 0x01) { /* EXI */ + exval = cs->readisac(cs, ISAC_EXIR); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC EXIR %02x", exval); + debugl1(cs, tmp); + } + if (exval & 0x04) { + v1 = cs->readisac(cs, ISAC_MOSR); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC MOSR %02x", v1); + debugl1(cs, tmp); + } +#if ARCOFI_USE + if (v1 & 0x08) { + if (!cs->mon_rx) + if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX out of memory!"); + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto afterMONR0; + } else + cs->mon_rxp = 0; + if (cs->mon_rxp >= MAX_MON_FRAME) { + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mon_rxp = 0; + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX overflow!"); + goto afterMONR0; + } + cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR0); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]); + debugl1(cs, tmp); + } + if (cs->mon_rxp == 1) { + cs->mocr |= 0x04; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + } + } + afterMONR0: + if (v1 & 0x80) { + if (!cs->mon_rx) + if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX out of memory!"); + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto afterMONR1; + } else + cs->mon_rxp = 0; + if (cs->mon_rxp >= MAX_MON_FRAME) { + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mon_rxp = 0; + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX overflow!"); + goto afterMONR1; + } + cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR1); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]); + debugl1(cs, tmp); + } + if (cs->mon_rxp == 1) { + cs->mocr |= 0x40; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + } + } + afterMONR1: + if (v1 & 0x04) { + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + isac_sched_event(cs, D_RX_MON0); + } + if (v1 & 0x40) { + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + isac_sched_event(cs, D_RX_MON1); + } + if (v1 & 0x02) { + if (!cs->mon_tx) { + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto AfterMOX0; + } + if (cs->mon_txp >= cs->mon_txc) { + if (cs->mon_txc) + isac_sched_event(cs, D_TX_MON0); + goto AfterMOX0; + } + cs->writeisac(cs, ISAC_MOX0, + cs->mon_tx[cs->mon_txp++]); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, tmp); + } + } + AfterMOX0: + if (v1 & 0x20) { + if (!cs->mon_tx) { + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto AfterMOX1; + } + if (cs->mon_txp >= cs->mon_txc) { + if (cs->mon_txc) + isac_sched_event(cs, D_TX_MON1); + goto AfterMOX1; + } + cs->writeisac(cs, ISAC_MOX1, + cs->mon_tx[cs->mon_txp++]); + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]); + debugl1(cs, tmp); + } + } + AfterMOX1: +#endif + } + } +} + +static void +ISAC_l2l1(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + char str[64]; + + switch (pr) { + case (PH_DATA_REQ): + 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 { + if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + 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 + isac_fill_fifo(cs); + } + break; + case (PH_PULL_IND): + 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->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ + LogFrame(cs, skb->data, skb->len); + sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); + dlogframe(cs, skb->data + 4, skb->len - 4, + str); + } + 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 + isac_fill_fifo(cs); + break; + case (PH_PULL_REQ): +#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_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +isac_l1cmd(struct IsdnCardState *cs, int msg, void *arg) +{ + u_char val; + char tmp[32]; + + switch(msg) { + case PH_RESET_REQ: + if ((cs->ph_state == ISAC_IND_EI) || + (cs->ph_state == ISAC_IND_DR) || + (cs->ph_state == ISAC_IND_RS)) + ph_command(cs, ISAC_CMD_TIM); + else + ph_command(cs, ISAC_CMD_RS); + break; + case PH_ENABLE_REQ: + ph_command(cs, ISAC_CMD_TIM); + break; + case PH_INFO3_REQ: + ph_command(cs, ISAC_CMD_AR8); + break; + case PH_TESTLOOP_REQ: + val = 0; + if (1 & (int) arg) + val |= 0x0c; + if (2 & (int) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; + default: + if (cs->debug & L1_DEB_WARN) { + sprintf(tmp, "isac_l1cmd unknown %4x", msg); + debugl1(cs, tmp); + } + break; + } +} + +void +setstack_isac(struct PStack *st, struct IsdnCardState *cs) +{ + st->l2.l2l1 = ISAC_l2l1; +} + +static void +dbusy_timer_handler(struct IsdnCardState *cs) +{ + struct PStack *stptr; + + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy"); + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL); + stptr = stptr->next; + } + } +} + +HISAX_INITFUNC(void +initisac(struct IsdnCardState *cs)) +{ + cs->tqueue.routine = (void *) (void *) isac_bh; + cs->l1cmd = isac_l1cmd; + cs->setstack_d = setstack_isac; + cs->dbusytimer.function = (void *) dbusy_timer_handler; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->writeisac(cs, ISAC_MASK, 0xff); + cs->mocr = 0xaa; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + cs->writeisac(cs, ISAC_ADF2, 0x0); + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + cs->writeisac(cs, ISAC_STCR, 0x70); + cs->writeisac(cs, ISAC_MODE, 0xc9); + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_ADF2, 0x80); + cs->writeisac(cs, ISAC_SQXR, 0x2f); + cs->writeisac(cs, ISAC_SPCR, 0x00); + cs->writeisac(cs, ISAC_STCR, 0x70); + cs->writeisac(cs, ISAC_MODE, 0xc9); + cs->writeisac(cs, ISAC_TIMR, 0x00); + cs->writeisac(cs, ISAC_ADF1, 0x00); + } + ph_command(cs, ISAC_CMD_RS); + cs->writeisac(cs, ISAC_MASK, 0x0); +} + +HISAX_INITFUNC(void +clear_pending_isac_ints(struct IsdnCardState *cs)) +{ + int val; + char tmp[64]; + + val = cs->readisac(cs, ISAC_STAR); + sprintf(tmp, "ISAC STAR %x", val); + debugl1(cs, tmp); + val = cs->readisac(cs, ISAC_MODE); + sprintf(tmp, "ISAC MODE %x", val); + debugl1(cs, tmp); + val = cs->readisac(cs, ISAC_ADF2); + sprintf(tmp, "ISAC ADF2 %x", val); + debugl1(cs, tmp); + val = cs->readisac(cs, ISAC_ISTA); + sprintf(tmp, "ISAC ISTA %x", val); + debugl1(cs, tmp); + if (val & 0x01) { + val = cs->readisac(cs, ISAC_EXIR); + sprintf(tmp, "ISAC EXIR %x", val); + debugl1(cs, tmp); + } else if (val & 0x04) { + val = cs->readisac(cs, ISAC_CIR0); + sprintf(tmp, "ISAC CIR0 %x", val); + debugl1(cs, tmp); + cs->ph_state = (val >> 2) & 0xf; + } else { + cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf; + } + isac_sched_event(cs, D_L1STATECHANGE); + cs->writeisac(cs, ISAC_MASK, 0xFF); + cs->writeisac(cs, ISAC_MASK, 0); + cs->writeisac(cs, ISAC_CMDR, 0x41); +} diff --git a/drivers/isdn/hisax/isac.h b/drivers/isdn/hisax/isac.h new file mode 100644 index 000000000..ac4d55647 --- /dev/null +++ b/drivers/isdn/hisax/isac.h @@ -0,0 +1,74 @@ +/* $Id: isac.h,v 1.4 1997/10/29 19:09:34 keil Exp $ + + * isac.h ISAC specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: isac.h,v $ + * Revision 1.4 1997/10/29 19:09:34 keil + * new L1 + * + * Revision 1.3 1997/07/27 21:37:41 keil + * T3 implemented; supervisor l1timer; B-channel TEST_LOOP + * + * Revision 1.2 1997/06/26 11:16:16 keil + * first version + * + * + */ + + +/* All Registers original Siemens Spec */ + +#define ISAC_MASK 0x20 +#define ISAC_ISTA 0x20 +#define ISAC_STAR 0x21 +#define ISAC_CMDR 0x21 +#define ISAC_EXIR 0x24 +#define ISAC_RBCH 0x2a +#define ISAC_ADF2 0x39 +#define ISAC_SPCR 0x30 +#define ISAC_ADF1 0x38 +#define ISAC_CIR0 0x31 +#define ISAC_CIX0 0x31 +#define ISAC_STCR 0x37 +#define ISAC_MODE 0x22 +#define ISAC_RSTA 0x27 +#define ISAC_RBCL 0x25 +#define ISAC_TIMR 0x23 +#define ISAC_SQXR 0x3b +#define ISAC_MOSR 0x3a +#define ISAC_MOCR 0x3a +#define ISAC_MOR0 0x32 +#define ISAC_MOX0 0x32 +#define ISAC_MOR1 0x34 +#define ISAC_MOX1 0x34 + +#define ISAC_CMD_TIM 0x0 +#define ISAC_CMD_RS 0x1 +#define ISAC_CMD_SCZ 0x4 +#define ISAC_CMD_SSZ 0x2 +#define ISAC_CMD_AR8 0x8 +#define ISAC_CMD_AR10 0x9 +#define ISAC_CMD_ARL 0xA +#define ISAC_CMD_DUI 0xF + +#define ISAC_IND_RS 0x1 +#define ISAC_IND_PU 0x7 +#define ISAC_IND_DR 0x0 +#define ISAC_IND_SD 0x2 +#define ISAC_IND_DIS 0x3 +#define ISAC_IND_EI 0x6 +#define ISAC_IND_RSY 0x4 +#define ISAC_IND_ARD 0x8 +#define ISAC_IND_TI 0xA +#define ISAC_IND_ATI 0xB +#define ISAC_IND_AI8 0xC +#define ISAC_IND_AI10 0xD +#define ISAC_IND_DID 0xF + +extern void ISACVersion(struct IsdnCardState *cs, char *s); +extern void initisac(struct IsdnCardState *cs); +extern void isac_interrupt(struct IsdnCardState *cs, u_char val); +extern void clear_pending_isac_ints(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index 655e6a417..8c4431b04 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -1,4 +1,4 @@ -/* $Id: isdnl1.c,v 1.15 1997/05/27 15:17:55 fritz Exp $ +/* $Id: isdnl1.c,v 2.18 1998/02/12 23:07:42 keil Exp $ * isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards * based on the teles driver from Jan den Ouden @@ -11,131 +11,239 @@ * * * $Log: isdnl1.c,v $ - * Revision 1.15 1997/05/27 15:17:55 fritz - * Added changes for recent 2.1.x kernels: - * changed return type of isdn_close - * queue_task_* -> queue_task - * clear/set_bit -> test_and_... where apropriate. - * changed type of hard_header_cache parameter. + * Revision 2.18 1998/02/12 23:07:42 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 2.17 1998/02/11 17:28:07 keil + * Niccy PnP/PCI support + * + * Revision 2.16 1998/02/09 18:46:08 keil + * Support for Sedlbauer PCMCIA (Marcus Niemann) * - * Revision 1.14 1997/04/07 23:00:08 keil - * GFP_KERNEL ---> GFP_ATOMIC + * Revision 2.15 1998/02/09 10:54:51 keil + * fixes for leased mode * - * Revision 1.13 1997/04/06 22:55:50 keil - * Using SKB's + * Revision 2.14 1998/02/03 23:31:31 keil + * add AMD7930 support * - * Revision 1.12 1997/03/26 13:43:57 keil - * small cosmetics + * Revision 2.13 1998/02/02 13:33:02 keil + * New card support * - * Revision 1.11 1997/03/25 23:11:23 keil - * US NI-1 protocol + * Revision 2.12 1998/01/31 21:41:48 keil + * changes for newer 2.1 kernels * - * Revision 1.10 1997/03/13 14:45:05 keil - * using IRQ proof queue_task + * Revision 2.11 1997/11/12 15:01:23 keil + * COMPAQ_ISA changes * - * Revision 1.9 1997/03/12 21:44:21 keil - * change Interrupt routine from atomic quick to normal + * Revision 2.10 1997/11/08 21:35:48 keil + * new l1 init * - * Revision 1.8 1997/02/09 00:24:31 keil - * new interface handling, one interface per card + * Revision 2.9 1997/11/06 17:09:18 keil + * New 2.1 init code * - * Revision 1.7 1997/01/27 15:56:03 keil - * PCMCIA Teles card and ITK ix1 micro added + * Revision 2.8 1997/10/29 19:00:05 keil + * new layer1,changes for 2.1 * - * Revision 1.6 1997/01/21 22:20:00 keil - * changes for D-channel log; Elsa Quickstep support + * Revision 2.7 1997/10/10 20:56:50 fritz + * New HL interface. * - * Revision 1.5 1997/01/10 12:51:19 keil - * cleanup; set newversion + * Revision 2.6 1997/09/12 10:05:16 keil + * ISDN_CTRL_DEBUG define * - * Revision 1.4 1996/12/08 19:44:53 keil - * L2FRAME_DEBUG and other changes from Pekka Sarnila + * Revision 2.5 1997/09/11 17:24:45 keil + * Add new cards * - * Revision 1.3 1996/11/18 15:34:47 keil - * fix HSCX version code + * Revision 2.4 1997/08/15 17:47:09 keil + * avoid oops because a uninitialised timer * - * Revision 1.2 1996/10/27 22:16:54 keil - * ISAC/HSCX version lookup + * Revision 2.3 1997/08/01 11:16:40 keil + * cosmetics * - * Revision 1.1 1996/10/13 20:04:53 keil - * Initial revision + * Revision 2.2 1997/07/30 17:11:08 keil + * L1deactivated exported * + * Revision 2.1 1997/07/27 21:35:38 keil + * new layer1 interface + * + * Revision 2.0 1997/06/26 11:02:53 keil + * New Layer and card interface + * + * Revision 1.15 1997/05/27 15:17:55 fritz + * Added changes for recent 2.1.x kernels: + * changed return type of isdn_close + * queue_task_* -> queue_task + * clear/set_bit -> test_and_... where apropriate. + * changed type of hard_header_cache parameter. * + * old changes removed KKe * */ -const char *l1_revision = "$Revision: 1.15 $"; +const char *l1_revision = "$Revision: 2.18 $"; #define __NO_VERSION__ #include <linux/config.h> #include "hisax.h" #include "isdnl1.h" +#include <linux/kernel_stat.h> +#if (LINUX_VERSION_CODE < 0x020150) /* 2.1.80 */ +#define kstat_irqs( PAR ) kstat.interrupts( (PAR) ) +#endif + + #if CARD_TELES0 -#include "teles0.h" +extern int setup_teles0(struct IsdnCard *card); #endif #if CARD_TELES3 -#include "teles3.h" +extern int setup_teles3(struct IsdnCard *card); #endif #if CARD_AVM_A1 -#include "avm_a1.h" +extern int setup_avm_a1(struct IsdnCard *card); #endif #if CARD_ELSA -#include "elsa.h" +extern int setup_elsa(struct IsdnCard *card); #endif #if CARD_IX1MICROR2 -#include "ix1_micro.h" +extern int setup_ix1micro(struct IsdnCard *card); #endif -/* #define I4L_IRQ_FLAG SA_INTERRUPT */ -#define I4L_IRQ_FLAG 0 +#if CARD_DIEHLDIVA +extern int setup_diva(struct IsdnCard *card); +#endif -#define HISAX_STATUS_BUFSIZE 4096 +#if CARD_ASUSCOM +extern int setup_asuscom(struct IsdnCard *card); +#endif -#define INCLUDE_INLINE_FUNCS -#include <linux/tqueue.h> -#include <linux/interrupt.h> +#if CARD_TELEINT +extern int setup_TeleInt(struct IsdnCard *card); +#endif -const char *CardType[] = -{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", - "Creatix/Teles PnP", "AVM A1", "Elsa ML", -#ifdef CONFIG_HISAX_ELSA_PCMCIA - "Elsa PCMCIA", -#else - "Elsa Quickstep", +#if CARD_SEDLBAUER +extern int setup_sedlbauer(struct IsdnCard *card); +#endif + +#if CARD_SPORTSTER +extern int setup_sportster(struct IsdnCard *card); +#endif + +#if CARD_MIC +extern int setup_mic(struct IsdnCard *card); #endif - "Teles PCMCIA", "ITK ix1-micro Rev.2"}; -static char *HSCXVer[] = -{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", - "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; +#if CARD_NETJET +extern int setup_netjet(struct IsdnCard *card); +#endif + +#if CARD_TELES3C +extern int setup_t163c(struct IsdnCard *card); +#endif + +#if CARD_AMD7930 +extern int setup_amd7930(struct IsdnCard *card); +#endif + +#if CARD_NICCY +extern int setup_niccy(struct IsdnCard *card); +#endif -static char *ISACVer[] = -{"2086/2186 V1.1", "2085 B1", "2085 B2", - "2085 V2.3"}; +#define HISAX_STATUS_BUFSIZE 4096 +#define ISDN_CTRL_DEBUG 1 +#define INCLUDE_INLINE_FUNCS +#include <linux/tqueue.h> +#include <linux/interrupt.h> +const char *CardType[] = +{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", "Creatix/Teles PnP", + "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2", + "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c", + "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI", + "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)", + "AMD 7930", "NICCY" +}; extern struct IsdnCard cards[]; extern int nrcards; extern char *HiSax_id; +extern struct IsdnBuffers *tracebuf; + +#define TIMER3_VALUE 7 + +static +struct Fsm l1fsm = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1_STATE_COUNT (ST_L1_F8+1) + +static char *strL1State[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + EV_PH_ACTIVATE, + EV_RESET_IND, + EV_DEACT_CNF, + EV_DEACT_IND, + EV_POWER_UP, + EV_RSYNC_IND, + EV_INFO2_IND, + EV_INFO4_IND, + EV_TIMER_DEACT, + EV_TIMER_ACT, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_ACTIVATE", + "EV_RESET_IND", + "EV_DEACT_CNF", + "EV_DEACT_IND", + "EV_POWER_UP", + "EV_RSYNC_IND", + "EV_INFO2_IND", + "EV_INFO4_IND", + "EV_TIMER_DEACT", + "EV_TIMER_ACT", + "EV_TIMER3", +}; /* * Find card with given driverId */ static inline struct IsdnCardState -* -hisax_findcard(int driverid) +*hisax_findcard(int driverid) { int i; for (i = 0; i < nrcards; i++) - if (cards[i].sp) - if (cards[i].sp->myid == driverid) - return (cards[i].sp); - return (struct IsdnCardState *) 0; + if (cards[i].cs) + if (cards[i].cs->myid == driverid) + return (cards[i].cs); + return (NULL); } int @@ -162,6 +270,7 @@ HiSax_readstatus(u_char * buf, int len, int user, int id, int channel) } } +#if ISDN_CTRL_DEBUG void HiSax_putstatus(struct IsdnCardState *csta, char *buf) { @@ -194,6 +303,23 @@ HiSax_putstatus(struct IsdnCardState *csta, char *buf) csta->iif.statcallb(&ic); } } +#else +#define KDEBUG_DEF +#include "../kdebug.h" + +static int DbgLineNr=0,DbgSequenzNr=1; + +void +HiSax_putstatus(struct IsdnCardState *csta, char *buf) +{ + char tmp[512]; + + if (DbgLineNr==23) + DbgLineNr=0; + sprintf(tmp, "%5d %s",DbgSequenzNr++,buf); + gput_str(tmp,0,DbgLineNr++); +} +#endif int ll_run(struct IsdnCardState *csta) @@ -238,228 +364,119 @@ ll_unload(struct IsdnCardState *csta) } void -debugl1(struct IsdnCardState *sp, char *msg) +debugl1(struct IsdnCardState *cs, char *msg) { char tmp[256], tm[32]; jiftime(tm, jiffies); - sprintf(tmp, "%s Card %d %s\n", tm, sp->cardnr + 1, msg); - HiSax_putstatus(sp, tmp); -} - -/* - * HSCX stuff goes here - */ - - -char * -HscxVersion(u_char v) -{ - return (HSCXVer[v & 0xf]); -} - -void -hscx_sched_event(struct HscxState *hsp, int event) -{ - hsp->event |= 1 << event; - queue_task(&hsp->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); + sprintf(tmp, "%s Card %d %s\n", tm, cs->cardnr + 1, msg); + HiSax_putstatus(cs, tmp); } -/* - * ISAC stuff goes here - */ - -char * -ISACVersion(u_char v) +static void +l1m_debug(struct FsmInst *fi, char *s) { - return (ISACVer[(v >> 5) & 3]); + struct PStack *st = fi->userdata; + + debugl1(st->l1.hardware, s); } void -isac_sched_event(struct IsdnCardState *sp, int event) -{ - sp->event |= 1 << event; - queue_task(&sp->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -int -act_wanted(struct IsdnCardState *sp) +L1activated(struct IsdnCardState *cs) { struct PStack *st; - st = sp->stlist; - while (st) - if (st->l1.act_state) - return (!0); + st = cs->stlist; + while (st) { + if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); else - st = st->next; - return (0); -} - -void -isac_new_ph(struct IsdnCardState *sp) -{ - int enq; - - enq = act_wanted(sp); - - switch (sp->ph_state) { - case (6): - sp->ph_active = 0; - sp->ph_command(sp, 15); - break; - case (15): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 0); - break; - case (0): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 0); -#if 0 - else - sp->ph_command(sp, 15); -#endif - break; - case (7): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 9); - break; - case (12): - sp->ph_command(sp, 8); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->tx_skb) - sp->tx_skb = skb_dequeue(&sp->sq); - if (sp->tx_skb) { - sp->tx_cnt = 0; - sp->isac_fill_fifo(sp); - } - break; - case (13): - sp->ph_command(sp, 9); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->tx_skb) - sp->tx_skb = skb_dequeue(&sp->sq); - if (sp->tx_skb) { - sp->tx_cnt = 0; - sp->isac_fill_fifo(sp); - } - break; - case (4): - case (8): - sp->ph_active = 0; - break; - default: - sp->ph_active = 0; - break; - } -} - -static void -restart_ph(struct IsdnCardState *sp) -{ - if (!sp->ph_active) { - if ((sp->ph_state == 6) || (sp->ph_state == 0)) { - sp->ph_command(sp, 0); - sp->ph_active = 2; - } else { - sp->ph_command(sp, 1); - sp->ph_active = 1; - } - } else if (sp->ph_active == 2) { - sp->ph_command(sp, 1); - sp->ph_active = 1; + st->l1.l1man(st, PH_ACTIVATE_IND, NULL); + st = st->next; } } - -static void -act_ivated(struct IsdnCardState *sp) +void +L1deactivated(struct IsdnCardState *cs) { struct PStack *st; - st = sp->stlist; + st = cs->stlist; while (st) { - if (st->l1.act_state == 1) { - st->l1.act_state = 2; - st->l1.l1man(st, PH_ACTIVATE, NULL); - } + if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + st->l1.l1l2(st, PH_PAUSE_CNF, NULL); + st->l1.l1man(st, PH_DEACTIVATE_IND, NULL); st = st->next; } + test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); } -static void -process_new_ph(struct IsdnCardState *sp) -{ - if (sp->ph_active == 5) - act_ivated(sp); -} - -static void -process_xmt(struct IsdnCardState *sp) +void +DChannel_proc_xmt(struct IsdnCardState *cs) { struct PStack *stptr; - if (sp->tx_skb) + if (cs->tx_skb) return; - stptr = sp->stlist; + stptr = cs->stlist; while (stptr != NULL) - if (stptr->l1.requestpull) { - stptr->l1.requestpull = 0; - stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) { + stptr->l1.l1l2(stptr, PH_PULL_CNF, NULL); break; } else stptr = stptr->next; } -static void -process_rcv(struct IsdnCardState *sp) +void +DChannel_proc_rcv(struct IsdnCardState *cs) { struct sk_buff *skb, *nskb; - struct PStack *stptr; - int found, broadc; + struct PStack *stptr = cs->stlist; + int found, tei, sapi; char tmp[64]; - while ((skb = skb_dequeue(&sp->rq))) { + if (stptr) + if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags)) + FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL); + while ((skb = skb_dequeue(&cs->rq))) { #ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA", 1); + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 1); #endif - stptr = sp->stlist; - broadc = (skb->data[1] >> 1) == 127; - - if (broadc) { - if (!(skb->data[0] >> 2)) { /* sapi 0 */ - sp->CallFlags = 3; - if (sp->dlogflag) { - LogFrame(sp, skb->data, skb->len); - dlogframe(sp, skb->data + 3, skb->len - 3, + stptr = cs->stlist; + sapi = skb->data[0] >> 2; + tei = skb->data[1] >> 1; + + if (tei == GROUP_TEI) { + if (sapi == CTRL_SAPI) { /* sapi 0 */ + if (cs->dlogflag) { + LogFrame(cs, skb->data, skb->len); + dlogframe(cs, skb->data + 3, skb->len - 3, "Q.931 frame network->user broadcast"); } - } - while (stptr != NULL) { - if ((skb->data[0] >> 2) == stptr->l2.sap) + while (stptr != NULL) { if ((nskb = skb_clone(skb, GFP_ATOMIC))) - stptr->l1.l1l2(stptr, PH_DATA, nskb); + stptr->l1.l1l2(stptr, PH_DATA_IND, nskb); else printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n"); - stptr = stptr->next; + stptr = stptr->next; + } + } else if (sapi == TEI_SAPI) { + while (stptr != NULL) { + if ((nskb = skb_clone(skb, GFP_ATOMIC))) + stptr->l1.l1tei(stptr, PH_DATA_IND, nskb); + else + printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n"); + stptr = stptr->next; + } } - SET_SKB_FREE(skb); dev_kfree_skb(skb); - } else { + } else if (sapi == CTRL_SAPI) { found = 0; while (stptr != NULL) - if (((skb->data[0] >> 2) == stptr->l2.sap) && - ((skb->data[1] >> 1) == stptr->l2.tei)) { - stptr->l1.l1l2(stptr, PH_DATA, skb); + if (tei == stptr->l2.tei) { + stptr->l1.l1l2(stptr, PH_DATA_IND, skb); found = !0; break; } else @@ -474,167 +491,70 @@ process_rcv(struct IsdnCardState *sp) sprintf(tmp, "Q.931 frame network->user with tei %d (not for us)", skb->data[1] >> 1); - LogFrame(sp, skb->data, skb->len); - dlogframe(sp, skb->data + 4, skb->len - 4, tmp); + LogFrame(cs, skb->data, skb->len); + dlogframe(cs, skb->data + 4, skb->len - 4, tmp); } - SET_SKB_FREE(skb); dev_kfree_skb(skb); } } - - } - -} - -static void -isac_bh(struct IsdnCardState *sp) -{ - if (!sp) - return; - - if (test_and_clear_bit(ISAC_PHCHANGE, &sp->event)) - process_new_ph(sp); - if (test_and_clear_bit(ISAC_RCVBUFREADY, &sp->event)) - process_rcv(sp); - if (test_and_clear_bit(ISAC_XMTBUFREADY, &sp->event)) - process_xmt(sp); -} - -static void -l2l1(struct PStack *st, int pr, void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct sk_buff *skb = arg; - char str[64]; - - switch (pr) { - case (PH_DATA): - if (sp->tx_skb) { - skb_queue_tail(&sp->sq, skb); -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA Queued", 0); -#endif - } else { - if ((sp->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ - LogFrame(sp, skb->data, skb->len); - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize, - str); - } - sp->tx_skb = skb; - sp->tx_cnt = 0; -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA", 0); -#endif - sp->isac_fill_fifo(sp); - } - break; - case (PH_DATA_PULLED): - if (sp->tx_skb) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, " l2l1 tx_skb exist this shouldn't happen"); - break; - } - if ((sp->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ - LogFrame(sp, skb->data, skb->len); - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize, - str); - } - sp->tx_skb = skb; - sp->tx_cnt = 0; -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA_PULLED", 0); -#endif - sp->isac_fill_fifo(sp); - break; - case (PH_REQUEST_PULL): -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - debugl1(sp, "-> PH_REQUEST_PULL"); -#endif - if (!sp->tx_skb) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; } } - static void -hscx_process_xmt(struct HscxState *hsp) +BChannel_proc_xmt(struct BCState *bcs) { - struct PStack *st = hsp->st; + struct PStack *st = bcs->st; - if (hsp->tx_skb) + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) return; - if (st->l1.requestpull) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } - if (!hsp->active) - if ((!hsp->tx_skb) && (!skb_queue_len(&hsp->squeue))) - hsp->sp->modehscx(hsp, 0, 0); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) + st->l1.l1l2(st, PH_PULL_CNF, NULL); + if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) + if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) + st->ma.manl1(st, PH_DEACTIVATE_CNF, 0); } static void -hscx_process_rcv(struct HscxState *hsp) +BChannel_proc_rcv(struct BCState *bcs) { struct sk_buff *skb; -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n"); - return; - } -#endif - while ((skb = skb_dequeue(&hsp->rqueue))) { - hsp->st->l1.l1l2(hsp->st, PH_DATA, skb); + while ((skb = skb_dequeue(&bcs->rqueue))) { + bcs->st->l1.l1l2(bcs->st, PH_DATA_IND, skb); } } static void -hscx_bh(struct HscxState *hsp) +BChannel_bh(struct BCState *bcs) { - - if (!hsp) + if (!bcs) return; - - if (test_and_clear_bit(HSCX_RCVBUFREADY, &hsp->event)) - hscx_process_rcv(hsp); - if (test_and_clear_bit(HSCX_XMTBUFREADY, &hsp->event)) - hscx_process_xmt(hsp); - + if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event)) + BChannel_proc_rcv(bcs); + if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event)) + BChannel_proc_xmt(bcs); } -/* - * interrupt stuff ends here - */ - void -HiSax_addlist(struct IsdnCardState *sp, +HiSax_addlist(struct IsdnCardState *cs, struct PStack *st) { - st->next = sp->stlist; - sp->stlist = st; + st->next = cs->stlist; + cs->stlist = st; } void -HiSax_rmlist(struct IsdnCardState *sp, +HiSax_rmlist(struct IsdnCardState *cs, struct PStack *st) { struct PStack *p; - if (sp->stlist == st) - sp->stlist = st->next; + FsmDelTimer(&st->l1.timer, 0); + if (cs->stlist == st) + cs->stlist = st->next; else { - p = sp->stlist; + p = cs->stlist; while (p) if (p->next == st) { p->next = st->next; @@ -644,248 +564,117 @@ HiSax_rmlist(struct IsdnCardState *sp, } } -static void -check_ph_act(struct IsdnCardState *sp) -{ - struct PStack *st = sp->stlist; - - while (st) { - if (st->l1.act_state) - return; - st = st->next; - } - if (sp->ph_active == 5) - sp->ph_active = 4; -} - -static void -HiSax_manl1(struct PStack *st, int pr, - void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - long flags; - char tmp[32]; - - switch (pr) { - case (PH_ACTIVATE): - if (sp->debug) { - sprintf(tmp, "PH_ACT ph_active %d", sp->ph_active); - debugl1(sp, tmp); - } - save_flags(flags); - cli(); - if (sp->ph_active & 4) { - sp->ph_active = 5; - st->l1.act_state = 2; - restore_flags(flags); - st->l1.l1man(st, PH_ACTIVATE, NULL); - } else { - st->l1.act_state = 1; - if (sp->ph_active == 0) - restart_ph(sp); - restore_flags(flags); - } - break; - case (PH_DEACTIVATE): - st->l1.act_state = 0; - if (sp->debug) { - sprintf(tmp, "PH_DEACT ph_active %d", sp->ph_active); - debugl1(sp, tmp); - } - check_ph_act(sp); - break; - } -} - -static void -HiSax_l2l1discardq(struct PStack *st, int pr, - void *heldby, int releasetoo) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct sk_buff *skb; - -#ifdef DEBUG_MAGIC - if (sp->magic != 301271) { - printk(KERN_DEBUG "isac_discardq magic not 301271\n"); - return; - } -#endif - - while ((skb = skb_dequeue(&sp->sq))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - } -} - void -setstack_HiSax(struct PStack *st, struct IsdnCardState *sp) +init_bcstate(struct IsdnCardState *cs, + int bc) { - st->l1.hardware = sp; - st->protocol = sp->protocol; - - setstack_tei(st); - - st->l1.stlistp = &(sp->stlist); - st->l1.act_state = 0; - st->l2.l2l1 = l2l1; - st->l2.l2l1discardq = HiSax_l2l1discardq; - st->ma.manl1 = HiSax_manl1; - st->l1.requestpull = 0; -} - -void -init_hscxstate(struct IsdnCardState *sp, - int hscx) -{ - struct HscxState *hsp = sp->hs + hscx; - - hsp->sp = sp; - hsp->hscx = hscx; - - hsp->tqueue.next = 0; - hsp->tqueue.sync = 0; - hsp->tqueue.routine = (void *) (void *) hscx_bh; - hsp->tqueue.data = hsp; - - hsp->inuse = 0; - hsp->init = 0; - hsp->active = 0; - -#ifdef DEBUG_MAGIC - hsp->magic = 301270; -#endif -} - -int -get_irq(int cardnr, void *routine) -{ - struct IsdnCard *card = cards + cardnr; - long flags; - - save_flags(flags); - cli(); - if (request_irq(card->sp->irq, routine, - I4L_IRQ_FLAG, "HiSax", card->sp)) { - printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n", - card->sp->irq); - restore_flags(flags); - return (0); - } - restore_flags(flags); - return (1); -} - -static void -release_irq(int cardnr) -{ - struct IsdnCard *card = cards + cardnr; - - free_irq(card->sp->irq, card->sp); -} - -void -close_hscxstate(struct HscxState *hs) -{ - struct sk_buff *skb; - - hs->sp->modehscx(hs, 0, 0); - hs->inuse = 0; - if (hs->init) { - if (hs->rcvbuf) { - kfree(hs->rcvbuf); - hs->rcvbuf = NULL; - } - while ((skb = skb_dequeue(&hs->rqueue))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - } - while ((skb = skb_dequeue(&hs->squeue))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - } - if (hs->tx_skb) { - SET_SKB_FREE(hs->tx_skb); - dev_kfree_skb(hs->tx_skb); - hs->tx_skb = NULL; - } - } - hs->init = 0; + struct BCState *bcs = cs->bcs + bc; + + bcs->cs = cs; + bcs->channel = bc; + bcs->tqueue.next = 0; + bcs->tqueue.sync = 0; + bcs->tqueue.routine = (void *) (void *) BChannel_bh; + bcs->tqueue.data = bcs; + bcs->BC_SetStack = NULL; + bcs->BC_Close = NULL; + bcs->Flag = 0; } static void closecard(int cardnr) { - struct IsdnCardState *csta = cards[cardnr].sp; + struct IsdnCardState *csta = cards[cardnr].cs; struct sk_buff *skb; - - close_hscxstate(csta->hs + 1); - close_hscxstate(csta->hs); + + if (csta->bcs->BC_Close != NULL) { + csta->bcs->BC_Close(csta->bcs + 1); + csta->bcs->BC_Close(csta->bcs); + } if (csta->rcvbuf) { kfree(csta->rcvbuf); csta->rcvbuf = NULL; } while ((skb = skb_dequeue(&csta->rq))) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } while ((skb = skb_dequeue(&csta->sq))) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } if (csta->tx_skb) { - SET_SKB_FREE(csta->tx_skb); dev_kfree_skb(csta->tx_skb); csta->tx_skb = NULL; } - switch (csta->typ) { -#if CARD_TELES0 - case ISDN_CTYPE_16_0: - case ISDN_CTYPE_8_0: - release_io_teles0(cards + cardnr); - break; -#endif -#if CARD_TELES3 - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_TELESPCMCIA: - release_io_teles3(cards + cardnr); - break; -#endif -#if CARD_AVM_A1 - case ISDN_CTYPE_A1: - release_io_avm_a1(cards + cardnr); - break; -#endif -#if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: - release_io_elsa(cards + cardnr); - break; -#endif -#if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - release_io_ix1micro(cards + cardnr); - break; -#endif - default: - break; + if (csta->mon_rx) { + kfree(csta->mon_rx); + csta->mon_rx = NULL; } + if (csta->mon_tx) { + kfree(csta->mon_tx); + csta->mon_tx = NULL; + } + csta->cardmsg(csta, CARD_RELEASE, NULL); + del_timer(&csta->dbusytimer); ll_unload(csta); } -static int -checkcard(int cardnr, char *id) +HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) +{ + int irq_cnt, cnt = 3; + long flags; + + save_flags(flags); + cli(); + irq_cnt = kstat_irqs(cs->irq); + printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq, + irq_cnt); + if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) { + printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n", + cs->irq); + return(1); + } + while (cnt) { + cs->cardmsg(cs, CARD_INIT, NULL); + sti(); + current->state = TASK_INTERRUPTIBLE; + /* Timeout 10ms */ + current->timeout = jiffies + (10 * HZ) / 1000; + schedule(); + restore_flags(flags); + printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], + cs->irq, kstat_irqs(cs->irq)); + if (kstat_irqs(cs->irq) == irq_cnt) { + printk(KERN_WARNING + "%s: IRQ(%d) getting no interrupts during init %d\n", + CardType[cs->typ], cs->irq, 4 - cnt); + if (cnt == 1) { + free_irq(cs->irq, cs); + return (2); + } else { + cs->cardmsg(cs, CARD_RESET, NULL); + cnt--; + } + } else { + cs->cardmsg(cs, CARD_TEST, NULL); + return(0); + } + } + restore_flags(flags); + return(3); +} + +HISAX_INITFUNC(static int +checkcard(int cardnr, char *id, int *busy_flag)) { long flags; int ret = 0; struct IsdnCard *card = cards + cardnr; - struct IsdnCardState *sp; + struct IsdnCardState *cs; save_flags(flags); cli(); - if (!(sp = (struct IsdnCardState *) + if (!(cs = (struct IsdnCardState *) kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) { printk(KERN_WARNING "HiSax: No memory for IsdnCardState(card %d)\n", @@ -893,10 +682,16 @@ checkcard(int cardnr, char *id) restore_flags(flags); return (0); } - card->sp = sp; - sp->cardnr = cardnr; - sp->cfg_reg = 0; - sp->protocol = card->protocol; + card->cs = cs; + cs->cardnr = cardnr; + cs->debug = L1_DEB_WARN; + cs->HW_Flags = 0; + cs->busy_flag = busy_flag; +#if TEI_PER_CARD +#else + test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags); +#endif + cs->protocol = card->protocol; if ((card->typ > 0) && (card->typ < 31)) { if (!((1 << card->typ) & SUPORTED_CARDS)) { @@ -913,31 +708,34 @@ checkcard(int cardnr, char *id) restore_flags(flags); return (0); } - if (!(sp->dlogspace = kmalloc(4096, GFP_ATOMIC))) { + if (!(cs->dlogspace = kmalloc(4096, GFP_ATOMIC))) { printk(KERN_WARNING "HiSax: No memory for dlogspace(card %d)\n", cardnr + 1); restore_flags(flags); return (0); } - if (!(sp->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) { + if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) { printk(KERN_WARNING "HiSax: No memory for status_buf(card %d)\n", cardnr + 1); - kfree(sp->dlogspace); + kfree(cs->dlogspace); restore_flags(flags); return (0); } - sp->status_read = sp->status_buf; - sp->status_write = sp->status_buf; - sp->status_end = sp->status_buf + HISAX_STATUS_BUFSIZE - 1; - sp->typ = card->typ; - sp->CallFlags = 0; - strcpy(sp->iif.id, id); - sp->iif.channels = 2; - sp->iif.maxbufsize = MAX_DATA_SIZE; - sp->iif.hl_hdrlen = MAX_HEADER_LEN; - sp->iif.features = + cs->stlist = NULL; + cs->dlogflag = 0; + cs->mon_tx = NULL; + cs->mon_rx = NULL; + cs->status_read = cs->status_buf; + cs->status_write = cs->status_buf; + cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1; + cs->typ = card->typ; + strcpy(cs->iif.id, id); + cs->iif.channels = 2; + cs->iif.maxbufsize = MAX_DATA_SIZE; + cs->iif.hl_hdrlen = MAX_HEADER_LEN; + cs->iif.features = ISDN_FEATURE_L2_X75I | ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS | @@ -953,21 +751,19 @@ checkcard(int cardnr, char *id) #endif 0; - sp->iif.command = HiSax_command; - sp->iif.writebuf = NULL; - sp->iif.writecmd = NULL; - sp->iif.writebuf_skb = HiSax_writebuf_skb; - sp->iif.readstat = HiSax_readstatus; - register_isdn(&sp->iif); - sp->myid = sp->iif.channels; - restore_flags(flags); - printk(KERN_NOTICE + cs->iif.command = HiSax_command; + cs->iif.writecmd = NULL; + cs->iif.writebuf_skb = HiSax_writebuf_skb; + cs->iif.readstat = HiSax_readstatus; + register_isdn(&cs->iif); + cs->myid = cs->iif.channels; + printk(KERN_INFO "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1, (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" : (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" : (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" : (card->protocol == ISDN_PTYPE_NI1) ? "NI1" : - "NONE", sp->iif.id, sp->myid); + "NONE", cs->iif.id, cs->myid); switch (card->typ) { #if CARD_TELES0 case ISDN_CTYPE_16_0: @@ -979,6 +775,7 @@ checkcard(int cardnr, char *id) case ISDN_CTYPE_16_3: case ISDN_CTYPE_PNP: case ISDN_CTYPE_TELESPCMCIA: + case ISDN_CTYPE_COMPAQ_ISA: ret = setup_teles3(card); break; #endif @@ -989,7 +786,9 @@ checkcard(int cardnr, char *id) #endif #if CARD_ELSA case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: + case ISDN_CTYPE_ELSA_PNP: + case ISDN_CTYPE_ELSA_PCMCIA: + case ISDN_CTYPE_ELSA_PCI: ret = setup_elsa(card); break; #endif @@ -998,90 +797,103 @@ checkcard(int cardnr, char *id) ret = setup_ix1micro(card); break; #endif - default: - printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n", - card->typ); - ll_unload(sp); - return (0); - } - if (!ret) { - ll_unload(sp); - return (0); - } - if (!(sp->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for isac rcvbuf\n"); - return (1); - } - sp->rcvidx = 0; - sp->tx_skb = NULL; - sp->tx_cnt = 0; - sp->event = 0; - sp->tqueue.next = 0; - sp->tqueue.sync = 0; - sp->tqueue.routine = (void *) (void *) isac_bh; - sp->tqueue.data = sp; - - skb_queue_head_init(&sp->rq); - skb_queue_head_init(&sp->sq); - - sp->stlist = NULL; - sp->ph_active = 0; - sp->dlogflag = 0; - sp->debug = L1_DEB_WARN; -#ifdef DEBUG_MAGIC - sp->magic = 301271; +#if CARD_DIEHLDIVA + case ISDN_CTYPE_DIEHLDIVA: + ret = setup_diva(card); + break; #endif - - init_hscxstate(sp, 0); - init_hscxstate(sp, 1); - - switch (card->typ) { -#if CARD_TELES0 - case ISDN_CTYPE_16_0: - case ISDN_CTYPE_8_0: - ret = initteles0(sp); +#if CARD_ASUSCOM + case ISDN_CTYPE_ASUSCOM: + ret = setup_asuscom(card); break; #endif -#if CARD_TELES3 - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_TELESPCMCIA: - ret = initteles3(sp); +#if CARD_TELEINT + case ISDN_CTYPE_TELEINT: + ret = setup_TeleInt(card); break; #endif -#if CARD_AVM_A1 - case ISDN_CTYPE_A1: - ret = initavm_a1(sp); +#if CARD_SEDLBAUER + case ISDN_CTYPE_SEDLBAUER: + case ISDN_CTYPE_SEDLBAUER_PCMCIA: + ret = setup_sedlbauer(card); break; #endif -#if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: - ret = initelsa(sp); +#if CARD_SPORTSTER + case ISDN_CTYPE_SPORTSTER: + ret = setup_sportster(card); break; #endif -#if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - ret = initix1micro(sp); +#if CARD_MIC + case ISDN_CTYPE_MIC: + ret = setup_mic(card); break; #endif - default: - ret = 0; +#if CARD_NETJET + case ISDN_CTYPE_NETJET: + ret = setup_netjet(card); + break; +#endif +#if CARD_TELES3C + case ISDN_CTYPE_TELES3C: + ret = setup_t163c(card); + break; +#endif +#if CARD_NICCY + case ISDN_CTYPE_NICCY: + ret = setup_niccy(card); + break; +#endif +#if CARD_AMD7930 + case ISDN_CTYPE_AMD7930: + ret = setup_amd7930(card); break; +#endif + default: + printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n", + card->typ); + ll_unload(cs); + restore_flags(flags); + return (0); } if (!ret) { + ll_unload(cs); + restore_flags(flags); + return (0); + } + if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for isac rcvbuf\n"); + return (1); + } + cs->rcvidx = 0; + cs->tx_skb = NULL; + cs->tx_cnt = 0; + cs->event = 0; + cs->tqueue.next = 0; + cs->tqueue.sync = 0; + cs->tqueue.data = cs; + + skb_queue_head_init(&cs->rq); + skb_queue_head_init(&cs->sq); + + init_bcstate(cs, 0); + init_bcstate(cs, 1); + ret = init_card(cs); + if (ret) { closecard(cardnr); + restore_flags(flags); return (0); } - init_tei(sp, sp->protocol); - CallcNewChan(sp); - ll_run(sp); + init_tei(cs, cs->protocol); + CallcNewChan(cs); + ll_run(cs); + cs->l1cmd(cs, PH_RESET_REQ, NULL); + restore_flags(flags); return (1); } -void -HiSax_shiftcards(int idx) +HISAX_INITFUNC(void +HiSax_shiftcards(int idx)) { int i; @@ -1089,8 +901,8 @@ HiSax_shiftcards(int idx) memcpy(&cards[i], &cards[i + 1], sizeof(cards[i])); } -int -HiSax_inithardware(void) +HISAX_INITFUNC(int +HiSax_inithardware(int *busy_flag)) { int foundcards = 0; int i = 0; @@ -1120,15 +932,15 @@ HiSax_inithardware(void) else sprintf(ids, "%s%d", id, i); } - if (checkcard(i, ids)) { + if (checkcard(i, ids, busy_flag)) { foundcards++; i++; } else { printk(KERN_WARNING "HiSax: Card %s not installed !\n", CardType[cards[i].typ]); - if (cards[i].sp) - kfree((void *) cards[i].sp); - cards[i].sp = NULL; + if (cards[i].cs) + kfree((void *) cards[i].cs); + cards[i].cs = NULL; HiSax_shiftcards(i); } } @@ -1144,159 +956,66 @@ HiSax_closehardware(void) save_flags(flags); cli(); for (i = 0; i < nrcards; i++) - if (cards[i].sp) { - ll_stop(cards[i].sp); - CallcFreeChan(cards[i].sp); - release_tei(cards[i].sp); - release_irq(i); + if (cards[i].cs) { + ll_stop(cards[i].cs); + release_tei(cards[i].cs); closecard(i); - kfree((void *) cards[i].sp); - cards[i].sp = NULL; + free_irq(cards[i].cs->irq, cards[i].cs); + kfree((void *) cards[i].cs); + cards[i].cs = NULL; } + Isdnl1Free(); + TeiFree(); Isdnl2Free(); CallcFree(); restore_flags(flags); } -static void -hscx_l2l1(struct PStack *st, int pr, void *arg) -{ - struct sk_buff *skb = arg; - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - long flags; - - switch (pr) { - case (PH_DATA): - save_flags(flags); - cli(); - if (hsp->tx_skb) { - skb_queue_tail(&hsp->squeue, skb); - restore_flags(flags); - } else { - restore_flags(flags); - hsp->tx_skb = skb; - hsp->count = 0; - sp->hscx_fill_fifo(hsp); - } - break; - case (PH_DATA_PULLED): - if (hsp->tx_skb) { - printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n"); - break; - } - hsp->tx_skb = skb; - hsp->count = 0; - sp->hscx_fill_fifo(hsp); - break; - case (PH_REQUEST_PULL): - if (!hsp->tx_skb) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; - } - -} -extern struct IsdnBuffers *tracebuf; - -static void -hscx_l2l1discardq(struct PStack *st, int pr, void *heldby, - int releasetoo) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - struct sk_buff *skb; - -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_discardq magic not 301270\n"); - return; - } -#endif - - while ((skb = skb_dequeue(&hsp->squeue))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - } -} - -static int -open_hscxstate(struct IsdnCardState *sp, - int hscx) -{ - struct HscxState *hsp = sp->hs + hscx; - - if (!hsp->init) { - if (!(hsp->rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for hscx_rcvbuf\n"); - return (1); - } - skb_queue_head_init(&hsp->rqueue); - skb_queue_head_init(&hsp->squeue); - } - hsp->init = !0; - - hsp->tx_skb = NULL; - hsp->event = 0; - hsp->rcvidx = 0; - hsp->tx_cnt = 0; - return (0); -} - -static void -hscx_manl1(struct PStack *st, int pr, - void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - - switch (pr) { - case (PH_ACTIVATE): - hsp->active = !0; - sp->modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel); - st->l1.l1man(st, PH_ACTIVATE, NULL); - break; - case (PH_DEACTIVATE): - if (!hsp->tx_skb) - sp->modehscx(hsp, 0, 0); - - hsp->active = 0; - break; - } -} - -int -setstack_hscx(struct PStack *st, struct HscxState *hs) -{ - if (open_hscxstate(st->l1.hardware, hs->hscx)) - return (-1); - - st->l1.hscx = hs->hscx; - st->l2.l2l1 = hscx_l2l1; - st->ma.manl1 = hscx_manl1; - st->l2.l2l1discardq = hscx_l2l1discardq; - - st->l1.act_state = 0; - st->l1.requestpull = 0; - - hs->st = st; - return (0); -} - void HiSax_reportcard(int cardnr) { - struct IsdnCardState *sp = cards[cardnr].sp; + struct IsdnCardState *cs = cards[cardnr].cs; + struct PStack *stptr; + struct l3_process *pc; + int j, i = 1; printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1); - printk(KERN_DEBUG "HiSax: Type %s\n", CardType[sp->typ]); - printk(KERN_DEBUG "HiSax: debuglevel %x\n", sp->debug); + printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]); + printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug); printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n", (ulong) & HiSax_reportcard); + printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs); + printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist)); + stptr = cs->stlist; + while (stptr != NULL) { + printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr); + printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp); + printk(KERN_DEBUG "HiSax: tei %d sapi %d\n", + stptr->l2.tei, stptr->l2.sap); + printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); + pc = stptr->l3.proc; + while (pc) { + printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref, + (ulong) pc); + printk(KERN_DEBUG "HiSax: state %d st 0x%lX chan 0x%lX\n", + pc->state, (ulong) pc->st, (ulong) pc->chan); + pc = pc->next; + } + stptr = stptr->next; + i++; + } + for (j = 0; j < 2; j++) { + printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j, + (ulong) & cs->channel[j]); + stptr = cs->channel[j].b_st; + i = 1; + while (stptr != NULL) { + printk(KERN_DEBUG "HiSax: b_st%d 0x%lX\n", i, (ulong) stptr); + printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); + stptr = stptr->next; + i++; + } + } } #ifdef L2FRAME_DEBUG /* psa */ @@ -1366,7 +1085,7 @@ l2frames(u_char * ptr) } void -Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir) +Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir) { char tmp[132]; u_char *ptr; @@ -1374,13 +1093,293 @@ Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir) ptr = skb->data; if (ptr[0] & 1 || !(ptr[1] & 1)) - debugl1(sp, "Addres not LAPD"); + debugl1(cs, "Addres not LAPD"); else { sprintf(tmp, "%s %s: %s%c (sapi %d, tei %d)", (dir ? "<-" : "->"), buf, l2frames(ptr), ((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1); - debugl1(sp, tmp); + debugl1(cs, tmp); } } - #endif + +static void +l1_reset(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_deact_cnf(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + FsmChangeState(fi, ST_L1_F3); + if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + cs->l1cmd(cs, PH_ENABLE_REQ, NULL); +} + +static void +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); + } +} + +static void +l1_power_up(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) { + FsmChangeState(fi, ST_L1_F4); + cs->l1cmd(cs, PH_INFO3_REQ, NULL); + FsmDelTimer(&st->l1.timer, 1); + FsmAddTimer(&st->l1.timer, TIMER3_VALUE * HZ, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags); + } else + FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_go_F5(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F5); +} + +static void +l1_go_F8(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F8); +} + +static void +l1_info2_ind(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + FsmChangeState(fi, ST_L1_F6); + cs->l1cmd(cs, PH_INFO3_REQ, NULL); +} + +static void +l1_info4_ind(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + FsmChangeState(fi, ST_L1_F7); + cs->l1cmd(cs, PH_INFO3_REQ, NULL); + if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) + FsmDelTimer(&st->l1.timer, 4); + 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); + test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags); + } +} + +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + L1deactivated(cs); + if (st->l1.l1m.state != ST_L1_F6) + FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_timer_act(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags); + test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags); + L1activated(cs); +} + +static void +l1_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); + test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags); + L1deactivated(cs); + cs->l1cmd(cs, PH_DEACT_ACK, NULL); +} + +static void +l1_activate(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + + cs->l1cmd(cs, PH_RESET_REQ, NULL); +} + +static struct FsmNode L1FnList[] HISAX_INITDATA = +{ + {ST_L1_F3, EV_PH_ACTIVATE, l1_activate}, + {ST_L1_F3, EV_RESET_IND, l1_reset}, + {ST_L1_F4, EV_RESET_IND, l1_reset}, + {ST_L1_F5, EV_RESET_IND, l1_reset}, + {ST_L1_F6, EV_RESET_IND, l1_reset}, + {ST_L1_F7, EV_RESET_IND, l1_reset}, + {ST_L1_F8, EV_RESET_IND, l1_reset}, + {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F7, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F8, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F3, EV_POWER_UP, l1_power_up}, + {ST_L1_F4, EV_RSYNC_IND, l1_go_F5}, + {ST_L1_F6, EV_RSYNC_IND, l1_go_F8}, + {ST_L1_F7, EV_RSYNC_IND, l1_go_F8}, + {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F3, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F5, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, +}; + +#define L1_FN_COUNT (sizeof(L1FnList)/sizeof(struct FsmNode)) + +HISAX_INITFUNC(void Isdnl1New(void)) +{ + l1fsm.state_count = L1_STATE_COUNT; + l1fsm.event_count = L1_EVENT_COUNT; + l1fsm.strEvent = strL1Event; + l1fsm.strState = strL1State; + FsmNew(&l1fsm, L1FnList, L1_FN_COUNT); +} + +void Isdnl1Free(void) +{ + FsmFree(&l1fsm); +} + +static void +dch_manl1(struct PStack *st, int pr, + void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + char tmp[32]; + + switch (pr) { + case PH_ACTIVATE_REQ: + if (cs->debug) { + sprintf(tmp, "PH_ACTIVATE_REQ %s", + strL1State[st->l1.l1m.state]); + debugl1(cs, tmp); + } + if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + else { + test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags); + FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg); + } + break; + case PH_DEACTIVATE_REQ: + if (cs->debug) { + sprintf(tmp, "PH_DEACTIVATE_REQ %s", + strL1State[st->l1.l1m.state]); + debugl1(cs, tmp); + } + break; + case PH_TESTLOOP_REQ: + if (1 & (int) arg) + debugl1(cs, "PH_TEST_LOOP B1"); + if (2 & (int) arg) + debugl1(cs, "PH_TEST_LOOP B2"); + if (!(3 & (int) arg)) + debugl1(cs, "PH_TEST_LOOP DISABLED"); + cs->l1cmd(cs, PH_TESTLOOP_REQ, arg); + break; + case PH_RESET_IND: + FsmEvent(&st->l1.l1m, EV_RESET_IND, arg); + break; + case PH_DEACT_CNF: + FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg); + break; + case PH_DEACT_IND: + FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg); + break; + case PH_POWERUP_CNF: + FsmEvent(&st->l1.l1m, EV_POWER_UP, arg); + break; + case PH_RSYNC_IND: + FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg); + break; + case PH_INFO2_IND: + FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg); + break; + case PH_I4_P8_IND: + case PH_I4_P10_IND: + FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg); + break; + default: + if (cs->debug) { + sprintf(tmp, "dch_manl1 msg %04X unhandled", pr); + debugl1(cs, tmp); + } + break; + } +} + +void +setstack_HiSax(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.hardware = cs; + st->protocol = cs->protocol; + st->l1.l1m.fsm = &l1fsm; + st->l1.l1m.state = ST_L1_F3; + st->l1.l1m.debug = cs->debug; + st->l1.l1m.userdata = st; + st->l1.l1m.userint = 0; + st->l1.l1m.printdebug = l1m_debug; + FsmInitTimer(&st->l1.l1m, &st->l1.timer); + setstack_tei(st); + setstack_manager(st); + st->l1.stlistp = &(cs->stlist); + st->ma.manl1 = dch_manl1; + st->l1.Flags = 0; + cs->setstack_d(st, cs); +} diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h index d01d70123..71f081a07 100644 --- a/drivers/isdn/hisax/isdnl1.h +++ b/drivers/isdn/hisax/isdnl1.h @@ -1,18 +1,23 @@ -/* $Id: isdnl1.h,v 1.4 1997/04/06 22:55:52 keil Exp $ - * +/* $Id: isdnl1.h,v 2.5 1998/02/02 13:36:58 keil Exp $ + * $Log: isdnl1.h,v $ - * Revision 1.4 1997/04/06 22:55:52 keil - * Using SKB's + * Revision 2.5 1998/02/02 13:36:58 keil + * more debug + * + * Revision 2.4 1997/11/08 21:35:49 keil + * new l1 init * - * Revision 1.3 1996/12/08 19:41:55 keil - * L2FRAME_DEBUG + * Revision 2.3 1997/10/29 19:07:53 keil + * changes for 2.1 * - * Revision 1.2 1996/10/27 22:26:27 keil - * ISAC/HSCX version functions + * Revision 2.2 1997/07/30 17:11:09 keil + * L1deactivated exported * - * Revision 1.1 1996/10/13 20:03:47 keil - * Initial revision + * Revision 2.1 1997/07/27 21:43:58 keil + * new l1 interface * + * Revision 2.0 1997/06/26 11:02:55 keil + * New Layer and card interface * * */ @@ -29,22 +34,25 @@ #define L1_DEB_HSCX 0x10 #define L1_DEB_HSCX_FIFO 0x20 #define L1_DEB_LAPD 0x40 +#define L1_DEB_IPAC 0x80 +#define L1_DEB_RECEIVE_FRAME 0x100 +#define D_RCVBUFREADY 0 +#define D_XMTBUFREADY 1 +#define D_L1STATECHANGE 2 +#define D_CLEARBUSY 3 +#define D_RX_MON0 4 +#define D_RX_MON1 5 +#define D_TX_MON0 6 +#define D_TX_MON1 7 -#define ISAC_RCVBUFREADY 0 -#define ISAC_XMTBUFREADY 1 -#define ISAC_PHCHANGE 2 - -#define HSCX_RCVBUFREADY 0 -#define HSCX_XMTBUFREADY 1 +#define B_RCVBUFREADY 0 +#define B_XMTBUFREADY 1 extern void debugl1(struct IsdnCardState *sp, char *msg); -extern char *HscxVersion(u_char v); -extern char *ISACVersion(u_char v); -extern void hscx_sched_event(struct HscxState *hsp, int event); -extern void isac_sched_event(struct IsdnCardState *sp, int event); -extern void isac_new_ph(struct IsdnCardState *sp); -extern int get_irq(int cardnr, void *routine); +extern void DChannel_proc_xmt(struct IsdnCardState *cs); +extern void DChannel_proc_rcv(struct IsdnCardState *cs); + #ifdef L2FRAME_DEBUG extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir); diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index 9c58eef90..eadfa4c8a 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1,4 +1,4 @@ -/* $Id: isdnl2.c,v 1.10 1997/05/06 09:38:13 keil Exp $ +/* $Id: isdnl2.c,v 2.7 1998/02/12 23:07:47 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,56 +7,51 @@ * Fritz Elfert * * $Log: isdnl2.c,v $ - * Revision 1.10 1997/05/06 09:38:13 keil - * Bugfixes: - clear ack queue entries after resend - * - acknowlege each frame to linklevel - * - UA for SABM is Response, not command - * - only RR was send as supervisor frame (X.75 hangs after a - * sequence error) + * Revision 2.7 1998/02/12 23:07:47 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.9 1997/04/07 23:02:11 keil - * missing braces + * Revision 2.6 1998/02/02 13:36:15 keil + * bugfix X.75 win calculation * - * Revision 1.8 1997/04/06 22:59:59 keil - * Using SKB's; changing function names; some minor changes + * Revision 2.5 1997/11/06 17:09:22 keil + * New 2.1 init code * - * Revision 1.7 1997/02/09 00:25:44 keil - * new interface handling, one interface per card + * Revision 2.4 1997/10/29 19:02:01 keil + * new LL interface * - * Revision 1.6 1997/01/21 22:23:42 keil - * D-channel log changed + * Revision 2.3 1997/10/01 09:21:39 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.5 1997/01/04 13:47:06 keil - * handling of MDL_REMOVE added (Thanks to Sim Yskes) + * Revision 2.2 1997/07/31 11:49:05 keil + * Eroor handling for no TEI assign * - * Revision 1.4 1996/12/08 19:51:51 keil - * many fixes from Pekka Sarnila + * Revision 2.1 1997/07/27 21:34:38 keil + * cosmetics * - * Revision 1.3 1996/11/05 19:39:12 keil - * X.75 bugfixes Thank to Martin Maurer - * - * Revision 1.2 1996/10/30 10:20:58 keil - * X.75 answer of SABMX fixed to response address (AVM X.75 problem) - * - * Revision 1.1 1996/10/13 20:04:54 keil - * Initial revision + * Revision 2.0 1997/06/26 11:07:29 keil + * New q.921 and X.75 Layer2 * * + * Old log removed KKe * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 1.10 $"; +const char *l2_revision = "$Revision: 2.7 $"; static void l2m_debug(struct FsmInst *fi, char *s); +static struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; enum { ST_L2_1, + ST_L2_2, ST_L2_3, ST_L2_4, ST_L2_5, @@ -70,6 +65,7 @@ enum { static char *strL2State[] = { "ST_L2_1", + "ST_L2_2", "ST_L2_3", "ST_L2_4", "ST_L2_5", @@ -81,53 +77,53 @@ static char *strL2State[] = enum { EV_L2_UI, EV_L2_SABMX, - EV_L2_UA, EV_L2_DISC, - EV_L2_I, - EV_L2_RR, - EV_L2_REJ, + EV_L2_DM, + EV_L2_UA, EV_L2_FRMR, + EV_L2_SUPER, + EV_L2_I, EV_L2_DL_DATA, + EV_L2_ACK_PULL, + EV_L2_DL_UNIT_DATA, EV_L2_DL_ESTABLISH, + EV_L2_DL_RELEASE, EV_L2_MDL_ASSIGN, EV_L2_MDL_REMOVE, - EV_L2_DL_UNIT_DATA, - EV_L2_DL_RELEASE, + EV_L2_MDL_ERROR, EV_L2_MDL_NOTEIPROC, + EV_L1_DEACTIVATE, EV_L2_T200, - EV_L2_ACK_PULL, EV_L2_T203, - EV_L2_RNR, }; -#define L2_EVENT_COUNT (EV_L2_RNR+1) +#define L2_EVENT_COUNT (EV_L2_T203+1) static char *strL2Event[] = { "EV_L2_UI", "EV_L2_SABMX", - "EV_L2_UA", "EV_L2_DISC", - "EV_L2_I", - "EV_L2_RR", - "EV_L2_REJ", + "EV_L2_DM", + "EV_L2_UA", "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNIT_DATA", "EV_L2_DL_ESTABLISH", + "EV_L2_DL_RELEASE", "EV_L2_MDL_ASSIGN", "EV_L2_MDL_REMOVE", - "EV_L2_DL_UNIT_DATA", - "EV_L2_DL_RELEASE", + "EV_L2_MDL_ERROR", "EV_L2_MDL_NOTEIPROC", + "EV_L1_DEACTIVATE", "EV_L2_T200", - "EV_L2_ACK_PULL", "EV_L2_T203", - "EV_L2_RNR", }; -int errcount = 0; - -static int l2addrsize(struct Layer2 *tsp); +static int l2addrsize(struct Layer2 *l2); static void InitWin(struct Layer2 *l2) @@ -143,11 +139,9 @@ ReleaseWin(struct Layer2 *l2) { int i, cnt = 0; - for (i = 0; i < MAX_WINDOW; i++) { if (l2->windowar[i]) { cnt++; - SET_SKB_FREE(l2->windowar[i]); dev_kfree_skb(l2->windowar[i]); l2->windowar[i] = NULL; } @@ -156,13 +150,15 @@ ReleaseWin(struct Layer2 *l2) printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt); } -static int +inline int cansend(struct PStack *st) { int p1; - p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8); - return (st->l2.vs != p1); + p1 = st->l2.vs - st->l2.va; + if (p1 < 0) + p1 += (test_bit(FLG_MOD128, &st->l2.flag) ? 128 : 8); + return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag)); } static void @@ -171,40 +167,54 @@ discard_i_queue(struct PStack *st) struct sk_buff *skb; while ((skb = skb_dequeue(&st->l2.i_queue))) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } } -int -l2headersize(struct Layer2 *tsp, int ui) +static void +discard_ui_queue(struct PStack *st) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&st->l2.ui_queue))) { + dev_kfree_skb(skb); + } +} + +inline void +clear_exception(struct Layer2 *l2) { - return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1)); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); } -int -l2addrsize(struct Layer2 *tsp) +inline int +l2headersize(struct Layer2 *l2, int ui) { - return (tsp->laptype == LAPD ? 2 : 1); + return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1)); +} + +inline int +l2addrsize(struct Layer2 *l2) +{ + return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); } static int -sethdraddr(struct Layer2 *tsp, - u_char * header, int rsp) +sethdraddr(struct Layer2 *l2, u_char * header, int rsp) { u_char *ptr = header; - int crbit; + int crbit = rsp; - if (tsp->laptype == LAPD) { - crbit = rsp; - if (!tsp->orig) - crbit = !crbit; - *ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0); - *ptr++ = (tsp->tei << 1) | 1; + if (test_bit(FLG_LAPD, &l2->flag)) { + *ptr++ = (l2->sap << 2) | (rsp ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; return (2); } else { - crbit = rsp; - if (tsp->orig) + if (test_bit(FLG_ORIG, &l2->flag)) crbit = !crbit; if (crbit) *ptr++ = 1; @@ -218,79 +228,107 @@ static void enqueue_ui(struct PStack *st, struct sk_buff *skb) { - st->l2.l2l1(st, PH_DATA, skb); + st->l2.l2l1(st, PH_DATA_REQ, skb); } static void enqueue_super(struct PStack *st, struct sk_buff *skb) { - st->l2.l2l1(st, PH_DATA, skb); + st->l2.l2l1(st, PH_DATA_REQ, skb); } -static int -legalnr(struct PStack *st, int nr) +inline int +IsUI(u_char * data, int ext) { - struct Layer2 *l2 = &st->l2; - int lnr, lvs; + return ((data[0] & 0xef) == UI); +} - lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8); - lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8); - return (lnr <= lvs); +inline int +IsUA(u_char * data, int ext) +{ + return ((data[0] & 0xef) == UA); } -static void -setva(struct PStack *st, int nr) +inline int +IsDM(u_char * data, int ext) { - struct Layer2 *l2 = &st->l2; + return ((data[0] & 0xef) == DM); +} - if (l2->va != nr) { - while (l2->va != nr) { - l2->va = (l2->va + 1) % (l2->extended ? 128 : 8); - SET_SKB_FREE(l2->windowar[l2->sow]); - dev_kfree_skb(l2->windowar[l2->sow]); - l2->windowar[l2->sow] = NULL; - l2->sow = (l2->sow + 1) % l2->window; - if (st->l4.l2writewakeup) - st->l4.l2writewakeup(st); - } - } +inline int +IsDISC(u_char * data, int ext) +{ + return ((data[0] & 0xef) == DISC); } -static void -l2s1(struct FsmInst *fi, int event, void *arg) +inline int +IsRR(u_char * data, int ext) { - struct PStack *st = fi->userdata; + if (ext) + return (data[0] == RR); + else + return ((data[0] & 0xf) == 1); +} - st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces); - FsmChangeState(fi, ST_L2_3); +inline int +IsSABMX(u_char * data, int ext) +{ + u_char d = data[0] & ~0x10; + + return (ext ? d == SABME : d == SABM); } -static void -l2_send_ui(struct FsmInst *fi, int event, void *arg) +inline int +IsREJ(u_char * data, int ext) { - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - u_char header[MAX_HEADER_LEN]; - int i; + return (ext ? data[0] == REJ : (data[0] & 0xf) == REJ); +} - i = sethdraddr(&(st->l2), header, CMD); - header[i++] = UI; - memcpy(skb_push(skb, i), header, i); - enqueue_ui(st, skb); +inline int +IsFRMR(u_char * data, int ext) +{ + return ((data[0] & 0xef) == FRMR); } -static void -l2_receive_ui(struct FsmInst *fi, int event, void *arg) +inline int +IsRNR(u_char * data, int ext) { - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; + return (ext ? data[0] == RNR : (data[0] & 0xf) == RNR); +} - skb_pull(skb, l2headersize(&st->l2, 1)); - st->l2.l2l3(st, DL_UNIT_DATA, skb); +static int +legalnr(struct PStack *st, int nr) +{ + struct Layer2 *l2 = &st->l2; + int lnr, lvs; + + lvs = (l2->vs >= l2->va) ? l2->vs : + (l2->vs + (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8)); + lnr = (nr >= l2->va) ? nr : (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + return (lnr <= lvs); } -inline void +static void +setva(struct PStack *st, int nr) +{ + struct Layer2 *l2 = &st->l2; + int len; + + while (l2->va != nr) { + l2->va = (l2->va + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + len = l2->windowar[l2->sow]->len; + if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type) + len = -1; + dev_kfree_skb(l2->windowar[l2->sow]); + l2->windowar[l2->sow] = NULL; + l2->sow = (l2->sow + 1) % l2->window; + if (st->lli.l2writewakeup && (len >=0)) + st->lli.l2writewakeup(st, len); + } +} + +static void send_uframe(struct PStack *st, u_char cmd, u_char cr) { struct sk_buff *skb; @@ -307,53 +345,156 @@ send_uframe(struct PStack *st, u_char cmd, u_char cr) enqueue_super(st, skb); } +inline u_char +get_PollFlag(struct PStack * st, struct sk_buff * skb) +{ + return (skb->data[l2addrsize(&(st->l2))] & 0x10); +} + +inline void +FreeSkb(struct sk_buff *skb) +{ + dev_kfree_skb(skb); +} + + +inline u_char +get_PollFlagFree(struct PStack *st, struct sk_buff *skb) +{ + u_char PF; + + PF = get_PollFlag(st, skb); + FreeSkb(skb); + return (PF); +} + static void establishlink(struct FsmInst *fi) { struct PStack *st = fi->userdata; u_char cmd; - FsmChangeState(fi, ST_L2_5); + clear_exception(&st->l2); st->l2.rc = 0; + cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10; + send_uframe(st, cmd, CMD); + FsmDelTimer(&st->l2.t203, 1); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 1); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + FsmChangeState(fi, ST_L2_5); +} - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 1"); +static void +l2_mdl_error(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct PStack *st = fi->userdata; + switch (event) { + case EV_L2_UA: + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'C'); + else + st->ma.layer(st, MDL_ERROR_IND, (void *) 'D'); + break; + case EV_L2_DM: + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'B'); + else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } + break; + } +} - cmd = (st->l2.extended ? SABME : SABM) | 0x10; - send_uframe(st, cmd, CMD); +static void +l2_dl_establish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + int state = fi->state; + + FsmChangeState(fi, ST_L2_3); + if (state == ST_L2_1) + st->l2.l2tei(st, MDL_ASSIGN_IND, NULL); } static void -l2_establish(struct FsmInst *fi, int event, void *arg) +l2_send_ui(struct PStack *st) { - establishlink(fi); + struct sk_buff *skb; + u_char header[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(&(st->l2), header, CMD); + header[i++] = UI; + while ((skb = skb_dequeue(&st->l2.ui_queue))) { + memcpy(skb_push(skb, i), header, i); + enqueue_ui(st, skb); + } } static void -l2_send_disconn(struct FsmInst *fi, int event, void *arg) +l2_put_ui(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - - FsmChangeState(fi, ST_L2_6); + struct sk_buff *skb = arg; - FsmDelTimer(&st->l2.t203_timer, 1); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 2); - st->l2.t200_running = 0; + skb_queue_tail(&st->l2.ui_queue, skb); + if (fi->state == ST_L2_1) { + FsmChangeState(fi, ST_L2_2); + st->l2.l2tei(st, MDL_ASSIGN_IND, NULL); } - st->l2.rc = 0; - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 2"); + if (fi->state > ST_L2_3) + l2_send_ui(st); +} +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; - if (!((chanp->impair == 2) && (st->l2.laptype == LAPB))) - send_uframe(st, DISC | 0x10, CMD); + skb_pull(skb, l2headersize(&st->l2, 1)); + if (skb->len > st->l2.maxlen) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'O'); + FreeSkb(skb); + } else + st->l2.l2l3(st, DL_UNIT_DATA, skb); +} + +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + if (fi->state != ST_L2_4) + discard_i_queue(st); + if (fi->state != ST_L2_5) + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); +} + +static void +l2_dl_release(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (fi->state == ST_L2_4) { + st->l2.l2man(st, DL_RELEASE, NULL); + return; + } else if (fi->state == ST_L2_5) { + test_and_set_bit(FLG_PEND_REL, &st->l2.flag); + return; + } discard_i_queue(st); + FsmChangeState(fi, ST_L2_6); + st->l2.rc = 0; + send_uframe(st, DISC | 0x10, CMD); + FsmDelTimer(&st->l2.t203, 1); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 2); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } static void @@ -361,46 +502,59 @@ l2_got_SABMX(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int est = 1, state; + int est = 1, state, rsp; u_char PollFlag; state = fi->state; - - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - - if (ST_L2_4 != state) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + if (rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlagFree(st, skb); + if (ST_L2_6 == state) { + send_uframe(st, DM | PollFlag, RSP); + return; + } else + send_uframe(st, UA | PollFlag, RSP); + if (ST_L2_5 == state) + return; + if (ST_L2_4 != state) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'F'); if (st->l2.vs != st->l2.va) { discard_i_queue(st); est = 1; } else est = 0; - + } + clear_exception(&st->l2); st->l2.vs = 0; st->l2.va = 0; st->l2.vr = 0; st->l2.sow = 0; - if (ST_L2_7 != state) - FsmChangeState(fi, ST_L2_7); - - send_uframe(st, UA | PollFlag, RSP); - - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 15); - st->l2.t200_running = 0; - } - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 3"); + FsmChangeState(fi, ST_L2_7); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); if (est) st->l2.l2man(st, DL_ESTABLISH, NULL); if (ST_L2_8 == state) if (skb_queue_len(&st->l2.i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL_REQ, NULL); } static void @@ -408,87 +562,152 @@ l2_got_disconn(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - struct Channel *chanp = st->l4.userdata; - u_char PollFlag; - - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + u_char PollFlag, cmd = UA; + int state, rel = 1, cst = 1, rsp; - FsmChangeState(fi, ST_L2_4); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - FsmDelTimer(&st->l2.t203_timer, 3); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 4); - st->l2.t200_running = 0; + if (rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; } - if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) - send_uframe(st, UA | PollFlag, RSP); - - st->l2.l2man(st, DL_RELEASE, NULL); + PollFlag = get_PollFlagFree(st, skb); + if ((state == ST_L2_4) || (state == ST_L2_5)) { + rel = 0; + cst = 0; + cmd = DM; + } else if (state == ST_L2_6) { + rel = 0; + cst = 0; + } + if (cst) { + FsmChangeState(fi, ST_L2_4); + FsmDelTimer(&st->l2.t203, 3); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + } + send_uframe(st, cmd | PollFlag, RSP); + if (rel) + st->l2.l2man(st, DL_RELEASE, NULL); } + static void -l2_got_st4_disc(struct FsmInst *fi, int event, void *arg) +l2_got_ua(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - struct Channel *chanp = st->l4.userdata; - u_char PollFlag; + u_char PollFlag, est = 1; + int state,rsp; - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) - send_uframe(st, DM | (PollFlag ? 0x10 : 0x0), RSP); + if (!rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlag(st, skb); + if (!PollFlag) { + l2_mdl_error(fi, event, arg); + return; + } + FreeSkb(skb); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + if (fi->state == ST_L2_5) { + if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) { + discard_i_queue(st); + st->l2.rc = 0; + send_uframe(st, DISC | 0x10, CMD); + FsmChangeState(fi, ST_L2_6); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } else { + if (!test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) + if (st->l2.vs != st->l2.va) + discard_i_queue(st); + else + est = 0; + st->l2.vs = 0; + st->l2.va = 0; + st->l2.vr = 0; + st->l2.sow = 0; + FsmChangeState(fi, ST_L2_7); + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4); + if (est) + st->l2.l2man(st, DL_ESTABLISH, NULL); + } + } else { /* ST_L2_6 */ + st->l2.l2man(st, DL_RELEASE, NULL); + FsmChangeState(fi, ST_L2_4); + } } static void -l2_got_ua_establish(struct FsmInst *fi, int event, void *arg) +l2_got_dm(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - u_char f; + u_char PollFlag; + int state,rsp; - skb_pull(skb, l2addrsize(&(st->l2))); - f = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - if (f) { - st->l2.vs = 0; - st->l2.va = 0; - st->l2.vr = 0; - st->l2.sow = 0; - FsmChangeState(fi, ST_L2_7); - - FsmDelTimer(&st->l2.t200_timer, 5); - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 4"); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - st->l2.l2man(st, DL_ESTABLISH, NULL); + if (!rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) + establishlink(fi); + return; } -} - -static void -l2_got_ua_disconn(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - u_char f; - - skb_pull(skb, l2addrsize(&(st->l2))); - f = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - if (f) { - FsmDelTimer(&st->l2.t200_timer, 6); - FsmChangeState(fi, ST_L2_4); + PollFlag = get_PollFlagFree(st, skb); + if (!PollFlag) { + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } else { + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + if (fi->state == ST_L2_5 && !test_bit(FLG_L3_INIT, &st->l2.flag)) + discard_i_queue(st); st->l2.l2man(st, DL_RELEASE, NULL); + FsmChangeState(fi, ST_L2_4); } } @@ -502,7 +721,7 @@ enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf) l2 = &st->l2; i = sethdraddr(l2, tmp, cr); - if (l2->extended) { + if (test_bit(FLG_MOD128, &l2->flag)) { tmp[i++] = typ; tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); } else @@ -516,77 +735,133 @@ enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf) } inline void -enquiry_response(struct PStack *st, u_char typ, u_char final) +enquiry_response(struct PStack *st) { - enquiry_cr(st, typ, RSP, final); + if (test_bit(FLG_OWN_BUSY, &st->l2.flag)) + enquiry_cr(st, RNR, RSP, 1); + else + enquiry_cr(st, RR, RSP, 1); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); } inline void -enquiry_command(struct PStack *st, u_char typ, u_char poll) +transmit_enquiry(struct PStack *st) { - enquiry_cr(st, typ, CMD, poll); + if (test_bit(FLG_OWN_BUSY, &st->l2.flag)) + enquiry_cr(st, RNR, CMD, 1); + else + enquiry_cr(st, RR, CMD, 1); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 12); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } + static void nrerrorrecovery(struct FsmInst *fi) { - /* should log error here */ + struct PStack *st = fi->userdata; + + st->ma.layer(st, MDL_ERROR_IND, (void *) 'J'); establishlink(fi); } static void -l2_got_st7_RR(struct FsmInst *fi, int event, void *arg) +invoke_retransmission(struct PStack *st, int nr) +{ + struct Layer2 *l2 = &st->l2; + int p1; + long flags; + + if (l2->vs != nr) { + save_flags(flags); + cli(); + while (l2->vs != nr) { + l2->vs = l2->vs - 1; + if (l2->vs < 0) + l2->vs += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + p1 = l2->vs - l2->va; + if (p1 < 0) + p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + p1 = (p1 + l2->sow) % l2->window; + skb_queue_head(&l2->i_queue, l2->windowar[p1]); + l2->windowar[p1] = NULL; + } + restore_flags(flags); + st->l2.l2l1(st, PH_PULL_REQ, NULL); + } +} + +static void +l2_got_st7_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; + int PollFlag, nr, rsp, typ = RR; + struct Layer2 *l2 = &st->l2; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + typ = RNR; + } else + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + if (IsREJ(skb->data, test_bit(FLG_MOD128, &l2->flag))) + typ = REJ; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len == 2) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + if (skb->len == 1) { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + FreeSkb(skb); - if (!((chanp->impair == 4) && (st->l2.laptype == LAPB))) - if ((!rsp) && PollFlag) - enquiry_response(st, RR, PollFlag); - - if (legalnr(st, seq)) { - if (seq == l2->vs) { - setva(st, seq); - FsmDelTimer(&l2->t200_timer, 7); - l2->t200_running = 0; - FsmDelTimer(&l2->t203_timer, 8); - if (FsmAddTimer(&l2->t203_timer, l2->t203, EV_L2_T203, NULL, 5)) - if (l2->l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 5"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else if (l2->va != seq) { - setva(st, seq); - FsmDelTimer(&st->l2.t200_timer, 9); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 6"); - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if ((!rsp) && PollFlag) + enquiry_response(st); + if (rsp && PollFlag) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'A'); + if (legalnr(st, nr)) { + if (typ == REJ) { + setva(st, nr); + invoke_retransmission(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 9); + if (FsmAddTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { + setva(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 9); + FsmRestartTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(st, nr); + FsmDelTimer(&st->l2.t203, 9); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 6); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } + if (skb_queue_len(&st->l2.i_queue) && (typ == RR)) + st->l2.l2l1(st, PH_PULL_REQ, NULL); } else nrerrorrecovery(fi); @@ -602,122 +877,121 @@ l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - skb_queue_tail(&st->l2.i_queue, skb); - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); + if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag))) + skb_queue_tail(&st->l2.i_queue, skb); + if (fi->state == ST_L2_7) + st->l2.l2l1(st, PH_PULL_REQ, NULL); } -static int -icommandreceived(struct FsmInst *fi, int event, void *arg, int *nr) +static void +l2_got_iframe(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; struct sk_buff *skb = arg; struct IsdnCardState *sp = st->l1.hardware; struct Layer2 *l2 = &(st->l2); - int i, p, seq, wasok; + int PollFlag, ns, nr, i, hs, rsp; char str[64]; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + if (rsp) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + establishlink(fi); + return; + } i = l2addrsize(l2); - if (l2->extended) { - p = (skb->data[i + 1] & 0x1) == 0x1; - seq = skb->data[i] >> 1; - *nr = (skb->data[i + 1] >> 1) & 0x7f; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len <= (i + 1)) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } else if ((skb->len - i - 1) > l2->maxlen) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'O'); + FreeSkb(skb); + establishlink(fi); + return; + } + PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); + ns = skb->data[i] >> 1; + nr = (skb->data[i + 1] >> 1) & 0x7f; } else { - p = (skb->data[i] & 0x10); - seq = (skb->data[i] >> 1) & 0x7; - *nr = (skb->data[i] >> 5) & 0x7; + if (skb->len <= i) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } else if ((skb->len - i) > l2->maxlen) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'O'); + FreeSkb(skb); + establishlink(fi); + return; + } + PollFlag = (skb->data[i] & 0x10); + ns = (skb->data[i] >> 1) & 0x7; + nr = (skb->data[i] >> 5) & 0x7; } - - if (l2->vr == seq) { - wasok = !0; - - l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8); - l2->rejexp = 0; - - if (st->l2.laptype == LAPD) + if (test_bit(FLG_OWN_BUSY, &l2->flag)) { + FreeSkb(skb); + enquiry_response(st); + } else if (l2->vr == ns) { + l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (test_bit(FLG_LAPD, &l2->flag)) if (sp->dlogflag) { + hs = l2headersize(l2, 0); LogFrame(st->l1.hardware, skb->data, skb->len); sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei); - dlogframe(st->l1.hardware, skb->data + l2->ihsize, - skb->len - l2->ihsize, str); + dlogframe(st->l1.hardware, skb->data + hs, + skb->len - hs, str); } - if (!((chanp->impair == 3) && (st->l2.laptype == LAPB))) - if (p || (!skb_queue_len(&st->l2.i_queue))) - enquiry_response(st, RR, p); + if (PollFlag) + enquiry_response(st); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); skb_pull(skb, l2headersize(l2, 0)); + st->l2.l2l3(st, DL_DATA, skb); } else { /* n(s)!=v(r) */ - wasok = 0; - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - if (st->l2.rejexp) { - if (p) - if (!((chanp->impair == 3) && (st->l2.laptype == LAPB))) - enquiry_response(st, RR, p); + FreeSkb(skb); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(st); } else { - st->l2.rejexp = !0; - enquiry_command(st, REJ, 1); + enquiry_cr(st, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); } } - return wasok; -} - -static void -l2_got_st7_data(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int nr, wasok; - - wasok = icommandreceived(fi, event, arg, &nr); - - if (legalnr(st, nr)) { - if (nr == st->l2.vs) { - setva(st, nr); - FsmDelTimer(&st->l2.t200_timer, 10); - st->l2.t200_running = 0; - FsmDelTimer(&st->l2.t203_timer, 11); - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 5"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else if (nr != st->l2.va) { - setva(st, nr); - FsmDelTimer(&st->l2.t200_timer, 12); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 6"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } - } else - nrerrorrecovery(fi); - - if (wasok) - st->l2.l2l3(st, DL_DATA, skb); -} - -static void -l2_got_st8_data(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int nr, wasok; - - wasok = icommandreceived(fi, event, arg, &nr); if (legalnr(st, nr)) { setva(st, nr); - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else + if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) { + if (nr == st->l2.vs) { + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 10); + FsmRestartTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 7); + } else if (nr != st->l2.va) { + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, + NULL, 8); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } + } + } else { nrerrorrecovery(fi); + return; + } - if (wasok) - st->l2.l2l3(st, DL_DATA, skb); + if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7)) + st->l2.l2l1(st, PH_PULL_REQ, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag)) + enquiry_cr(st, RR, RSP, 0); } static void @@ -726,69 +1000,14 @@ l2_got_tei(struct FsmInst *fi, int event, void *arg) struct PStack *st = fi->userdata; st->l2.tei = (int) arg; - establishlink(fi); -} - -static void -invoke_retransmission(struct PStack *st, int nr) -{ - struct Layer2 *l2 = &st->l2; - int p1; - - if (l2->vs != nr) { - while (l2->vs != nr) { - - l2->vs = l2->vs - 1; - if (l2->vs < 0) - l2->vs += l2->extended ? 128 : 8; - p1 = l2->vs - l2->va; - if (p1 < 0) - p1 += l2->extended ? 128 : 8; - p1 = (p1 + l2->sow) % l2->window; - - skb_queue_head(&l2->i_queue, l2->windowar[p1]); - l2->windowar[p1] = NULL; - } - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } -} - -static void -l2_got_st7_rej(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; - - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) - rsp = !rsp; - - skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; - } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; - } - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - - if ((!rsp) && PollFlag) - enquiry_response(st, RR, PollFlag); - - if (!legalnr(st, seq)) - return; - - setva(st, seq); - invoke_retransmission(st, seq); + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); + } else + FsmChangeState(fi, ST_L2_4); + if (skb_queue_len(&st->l2.ui_queue)) + l2_send_ui(st); } static void @@ -801,21 +1020,21 @@ static void l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - u_char cmd; - if (st->l2.rc == st->l2.n200) { + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + } else if (st->l2.rc == st->l2.N200) { FsmChangeState(fi, ST_L2_4); - st->l2.l2tei(st, MDL_VERIFY, (void *) st->l2.tei); + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); + discard_i_queue(st); + st->ma.layer(st, MDL_ERROR_IND, (void *) 'G'); st->l2.l2man(st, DL_RELEASE, NULL); } else { st->l2.rc++; - - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 7"); - - cmd = (st->l2.extended ? SABME : SABM) | 0x10; - send_uframe(st, cmd, CMD); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) + | 0x10, CMD); } } @@ -823,30 +1042,65 @@ static void l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - if (st->l2.rc == st->l2.n200) { + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + } else if (st->l2.rc == st->l2.N200) { FsmChangeState(fi, ST_L2_4); + st->ma.layer(st, MDL_ERROR_IND, (void *) 'H'); st->l2.l2man(st, DL_RELEASE, NULL); } else { st->l2.rc++; + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, + NULL, 9); + send_uframe(st, DISC | 0x10, CMD); + } +} - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 8"); +static void +l2_st78_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); + if (fi->state == ST_L2_7) { + st->l2.rc = 0; + FsmChangeState(fi, ST_L2_8); + } + if (st->l2.rc == st->l2.N200) { + establishlink(fi); + } else { + transmit_enquiry(st); + st->l2.rc++; + } +} - if (!((chanp->impair == 2) && (st->l2.laptype == LAPB))) - send_uframe(st, DISC | 0x10, CMD); +static void +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9); + return; } + FsmChangeState(fi, ST_L2_8); + transmit_enquiry(st); + st->l2.rc = 0; } static void l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct sk_buff *skb; + struct sk_buff *skb, *oskb; struct Layer2 *l2 = &st->l2; u_char header[MAX_HEADER_LEN]; int p1, i; @@ -860,19 +1114,18 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) p1 = l2->vs - l2->va; if (p1 < 0) - p1 += l2->extended ? 128 : 8; + p1 += test_bit(FLG_MOD128, &l2->flag) ? 128 : 8; p1 = (p1 + l2->sow) % l2->window; if (l2->windowar[p1]) { printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", p1); - SET_SKB_FREE(l2->windowar[p1]); dev_kfree_skb(l2->windowar[p1]); } l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC); i = sethdraddr(&st->l2, header, CMD); - if (l2->extended) { + if (test_bit(FLG_MOD128, &l2->flag)) { header[i++] = l2->vs << 1; header[i++] = l2->vr << 1; l2->vs = (l2->vs + 1) % 128; @@ -880,88 +1133,86 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) header[i++] = (l2->vr << 5) | (l2->vs << 1); l2->vs = (l2->vs + 1) % 8; } - - memcpy(skb_push(skb, i), header, i); - st->l2.l2l1(st, PH_DATA_PULLED, skb); - if (!st->l2.t200_running) { - FsmDelTimer(&st->l2.t203_timer, 13); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 9"); - - st->l2.t200_running = !0; + p1 = skb->data - skb->head; + if (p1 >= i) + memcpy(skb_push(skb, i), header, i); + else { + printk(KERN_WARNING + "isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); + oskb = skb; + skb = alloc_skb(oskb->len + i, GFP_ATOMIC); + memcpy(skb_put(skb, i), header, i); + memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len); + FreeSkb(oskb); + } + st->l2.l2l1(st, PH_PULL_IND, skb); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) { + FsmDelTimer(&st->l2.t203, 13); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11); } if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); -} - -static void -transmit_enquiry(struct PStack *st) -{ - - enquiry_command(st, RR, 1); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 10"); - - st->l2.t200_running = !0; -} - -static void -l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - st->l2.t200_running = 0; - - st->l2.rc = 1; - FsmChangeState(fi, ST_L2_8); - transmit_enquiry(st); + st->l2.l2l1(st, PH_PULL_REQ, NULL); } static void -l2_got_st8_rr_rej(struct FsmInst *fi, int event, void *arg) +l2_got_st8_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; + int PollFlag, nr, rsp, rnr = 0; + struct Layer2 *l2 = &st->l2; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; - skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + + if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + rnr = 1; + } else + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len == 2) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + if (skb->len == 1) { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + FreeSkb(skb); if (rsp && PollFlag) { - if (legalnr(st, seq)) { + if (legalnr(st, nr)) { + setva(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 7); + FsmDelTimer(&l2->t203, 8); + if (rnr) { + FsmRestartTimer(&l2->t200, l2->T200, + EV_L2_T200, NULL, 14); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } else + FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + invoke_retransmission(st, nr); FsmChangeState(fi, ST_L2_7); - setva(st, seq); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 14); - st->l2.t200_running = 0; - } - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 11"); - - invoke_retransmission(st, seq); - if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL_REQ, NULL); else if (fi->userint & LC_FLUSH_WAIT) { fi->userint &= ~LC_FLUSH_WAIT; st->l2.l2man(st, DL_FLUSH, NULL); @@ -969,57 +1220,46 @@ l2_got_st8_rr_rej(struct FsmInst *fi, int event, void *arg) } } else { if (!rsp && PollFlag) - enquiry_response(st, RR, PollFlag); - if (legalnr(st, seq)) { - setva(st, seq); + enquiry_response(st); + if (legalnr(st, nr)) { + setva(st, nr); } } } static void -l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - st->l2.rc = 0; - FsmChangeState(fi, ST_L2_8); - transmit_enquiry(st); -} - -static void -l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - if (st->l2.rc == st->l2.n200) { - establishlink(fi); - } else { - st->l2.rc++; - transmit_enquiry(st); - } -} - -static void l2_got_FRMR(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; char tmp[64]; - skb_pull(skb, l2addrsize(&st->l2)); - if (st->l2.l2m.debug) { - if (st->l2.extended) + skb_pull(skb, l2addrsize(&st->l2) + 1); + if (test_bit(FLG_MOD128, &st->l2.flag)) { + if (skb->len < 5) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + else { sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x", skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4]); - else + l2m_debug(&st->l2.l2m, tmp); + } + } else { + if (skb->len < 3) + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + else { sprintf(tmp, "FRMR information %2x %2x %2x", skb->data[0], skb->data[1], skb->data[2]); - - l2m_debug(&st->l2.l2m, tmp); + l2m_debug(&st->l2.l2m, tmp); + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ + (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } + FreeSkb(skb); } static void @@ -1027,135 +1267,126 @@ l2_tei_remove(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; -/*TODO - if( DL_RELEASE.req outstanding ) { - ... issue DL_RELEASE.confirm - } else { - if( fi->state != ST_L2_4 ) { - ... issue DL_RELEASE.indication - } - } - TODO */ - discard_i_queue(st); /* There is no UI queue in layer 2 */ - st->l2.tei = 255; - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 18); - st->l2.t200_running = 0; - } - FsmDelTimer(&st->l2.t203_timer, 19); - st->l2.l2man(st, DL_RELEASE, NULL); /* TEMP */ + discard_i_queue(st); + discard_ui_queue(st); + st->l2.tei = -1; + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 18); + FsmDelTimer(&st->l2.t203, 19); + if (fi->state != ST_L2_4) + st->l2.l2man(st, DL_RELEASE, NULL); FsmChangeState(fi, ST_L2_1); } -inline int -IsUI(u_char * data, int ext) -{ - return ((data[0] & 0xef) == UI); -} - -inline int -IsUA(u_char * data, int ext) -{ - return ((data[0] & 0xef) == UA); -} - -inline int -IsDISC(u_char * data, int ext) -{ - return ((data[0] & 0xef) == DISC); -} - -inline int -IsRR(u_char * data, int ext) -{ - if (ext) - return (data[0] == RR); - else - return ((data[0] & 0xf) == 1); -} - -inline int -IsI(u_char * data, int ext) -{ - return ((data[0] & 0x1) == 0x0); -} - -inline int -IsSABMX(u_char * data, int ext) +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) { - u_char d = data[0] & ~0x10; + struct PStack *st = fi->userdata; - return (ext ? d == SABME : d == SABM); -} - -inline int -IsREJ(u_char * data, int ext) -{ - return (ext ? data[0] == REJ : (data[0] & 0xf) == 0x9); -} - -inline int -IsFRMR(u_char * data, int ext) -{ - return ((data[0] & 0xef) == FRMR); -} - -inline int -IsRNR(u_char * data, int ext) -{ - if (ext) - return (data[0] == RNR); - else - return ((data[0] & 0xf) == 5); + discard_i_queue(st); + discard_ui_queue(st); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 18); + FsmDelTimer(&st->l2.t203, 19); + test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); + clear_exception(&st->l2); + switch (fi->state) { + case ST_L2_3: + st->l2.l2man(st, DL_RELEASE, NULL); + case ST_L2_2: + FsmChangeState(fi, ST_L2_1); + break; + case ST_L2_5: + case ST_L2_6: + case ST_L2_7: + case ST_L2_8: + st->l2.l2man(st, DL_RELEASE, NULL); + FsmChangeState(fi, ST_L2_4); + break; + } } -static struct FsmNode L2FnList[] = +static struct FsmNode L2FnList[] HISAX_INITDATA = { - {ST_L2_1, EV_L2_DL_ESTABLISH, l2s1}, {ST_L2_1, EV_L2_MDL_NOTEIPROC, l2_no_tei}, - {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_1, EV_L2_DL_ESTABLISH, l2_dl_establish}, + {ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish}, {ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish}, - {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_7, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_8, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_4, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_5, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_7, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_8, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_iqueue}, {ST_L2_7, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_7, EV_L2_DL_RELEASE, l2_send_disconn}, - {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_8, EV_L2_DL_RELEASE, l2_send_disconn}, - - {ST_L2_1, EV_L2_UI, l2_receive_ui}, - {ST_L2_4, EV_L2_UI, l2_receive_ui}, + {ST_L2_1, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_2, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_3, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_5, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_6, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_8, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, {ST_L2_4, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_4, EV_L2_DISC, l2_got_st4_disc}, - {ST_L2_5, EV_L2_UA, l2_got_ua_establish}, - {ST_L2_6, EV_L2_UA, l2_got_ua_disconn}, - {ST_L2_7, EV_L2_UI, l2_receive_ui}, - {ST_L2_7, EV_L2_DISC, l2_got_disconn}, - {ST_L2_7, EV_L2_I, l2_got_st7_data}, - {ST_L2_7, EV_L2_RR, l2_got_st7_RR}, - {ST_L2_7, EV_L2_REJ, l2_got_st7_rej}, + {ST_L2_5, EV_L2_SABMX, l2_got_SABMX}, + {ST_L2_6, EV_L2_SABMX, l2_got_SABMX}, {ST_L2_7, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_RR, l2_got_st8_rr_rej}, - {ST_L2_8, EV_L2_REJ, l2_got_st8_rr_rej}, {ST_L2_8, EV_L2_SABMX, l2_got_SABMX}, + {ST_L2_4, EV_L2_DISC, l2_got_disconn}, + {ST_L2_5, EV_L2_DISC, l2_got_disconn}, + {ST_L2_6, EV_L2_DISC, l2_got_disconn}, + {ST_L2_7, EV_L2_DISC, l2_got_disconn}, {ST_L2_8, EV_L2_DISC, l2_got_disconn}, + {ST_L2_4, EV_L2_UA, l2_mdl_error}, + {ST_L2_5, EV_L2_UA, l2_got_ua}, + {ST_L2_6, EV_L2_UA, l2_got_ua}, + {ST_L2_7, EV_L2_UA, l2_mdl_error}, + {ST_L2_8, EV_L2_UA, l2_mdl_error}, + {ST_L2_4, EV_L2_DM, l2_got_dm}, + {ST_L2_5, EV_L2_DM, l2_got_dm}, + {ST_L2_6, EV_L2_DM, l2_got_dm}, + {ST_L2_7, EV_L2_DM, l2_mdl_error}, + {ST_L2_8, EV_L2_DM, l2_mdl_error}, + {ST_L2_1, EV_L2_UI, l2_got_ui}, + {ST_L2_2, EV_L2_UI, l2_got_ui}, + {ST_L2_3, EV_L2_UI, l2_got_ui}, + {ST_L2_4, EV_L2_UI, l2_got_ui}, + {ST_L2_5, EV_L2_UI, l2_got_ui}, + {ST_L2_6, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_UI, l2_got_ui}, + {ST_L2_8, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_I, l2_got_st8_data}, - + {ST_L2_7, EV_L2_SUPER, l2_got_st7_super}, + {ST_L2_8, EV_L2_SUPER, l2_got_st8_super}, + {ST_L2_7, EV_L2_I, l2_got_iframe}, + {ST_L2_8, EV_L2_I, l2_got_iframe}, {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, - {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st78_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st78_tout_200}, {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, - {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, - - {ST_L2_1, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_3, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, }; #define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) @@ -1165,40 +1396,53 @@ isdnl2_l1l2(struct PStack *st, int pr, void *arg) { struct sk_buff *skb = arg; u_char *datap; - int ret = !0; + int ret = 1, len; switch (pr) { - case (PH_DATA): + case (PH_DATA_IND): datap = skb->data; - datap += l2addrsize(&st->l2); - - if (IsI(datap, st->l2.extended)) + len = l2addrsize(&st->l2); + if (skb->len > len) + datap += len; + else { + st->ma.layer(st, MDL_ERROR_IND, (void *) 'N'); + FreeSkb(skb); + return; + } + if (!(*datap & 1)) /* I-Frame */ ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb); - else if (IsRR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RR, skb); - else if (IsUI(datap, st->l2.extended)) + else if ((*datap & 3) == 1) /* S-Frame */ + ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb); + else if (IsUI(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb); - else if (IsSABMX(datap, st->l2.extended)) + else if (IsSABMX(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb); - else if (IsUA(datap, st->l2.extended)) + else if (IsUA(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb); - else if (IsDISC(datap, st->l2.extended)) + else if (IsDISC(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb); - else if (IsREJ(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, skb); - else if (IsFRMR(datap, st->l2.extended)) + else if (IsDM(datap, test_bit(FLG_MOD128, &st->l2.flag))) + ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb); + else if (IsFRMR(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb); - else if (IsRNR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, skb); - + else { + ret = 0; + st->ma.layer(st, MDL_ERROR_IND, (void *) 'L'); + FreeSkb(skb); + } if (ret) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb); + FreeSkb(skb); } break; - case (PH_PULL_ACK): + case (PH_PULL_CNF): FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg); break; + case (PH_PAUSE_IND): + test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag); + break; + case (PH_PAUSE_CNF): + test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag); + break; } } @@ -1208,13 +1452,11 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) switch (pr) { case (DL_DATA): if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) { - SET_SKB_FREE(((struct sk_buff *) arg)); dev_kfree_skb((struct sk_buff *) arg); } break; case (DL_UNIT_DATA): if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) { - SET_SKB_FREE(((struct sk_buff *) arg)); dev_kfree_skb((struct sk_buff *) arg); } break; @@ -1237,28 +1479,28 @@ isdnl2_manl2(struct PStack *st, int pr, void *arg) case (DL_FLUSH): (&st->l2.l2m)->userint |= LC_FLUSH_WAIT; break; - } -} - -static void -isdnl2_teil2(struct PStack *st, int pr, void *arg) -{ - switch (pr) { - case (MDL_ASSIGN): + case (PH_DEACTIVATE_IND): + FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg); + break; + case (MDL_ASSIGN_REQ): FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg); break; - case (MDL_REMOVE): + case (MDL_REMOVE_REQ): FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg); break; + case (MDL_ERROR_REQ): + FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg); + break; } } void releasestack_isdnl2(struct PStack *st) { - FsmDelTimer(&st->l2.t200_timer, 15); - FsmDelTimer(&st->l2.t203_timer, 16); + FsmDelTimer(&st->l2.t200, 15); + FsmDelTimer(&st->l2.t203, 16); discard_i_queue(st); + discard_ui_queue(st); ReleaseWin(&st->l2); } @@ -1279,13 +1521,10 @@ setstack_isdnl2(struct PStack *st, char *debug_id) st->l1.l1l2 = isdnl2_l1l2; st->l3.l3l2 = isdnl2_l3l2; st->ma.manl2 = isdnl2_manl2; - st->ma.teil2 = isdnl2_teil2; - st->l2.uihsize = l2headersize(&st->l2, !0); - st->l2.ihsize = l2headersize(&st->l2, 0); skb_queue_head_init(&st->l2.i_queue); + skb_queue_head_init(&st->l2.ui_queue); InitWin(&st->l2); - st->l2.rejexp = 0; st->l2.debug = 0; st->l2.l2m.fsm = &l2fsm; @@ -1296,9 +1535,8 @@ setstack_isdnl2(struct PStack *st, char *debug_id) st->l2.l2m.printdebug = l2m_debug; strcpy(st->l2.debug_id, debug_id); - FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer); - FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer); - st->l2.t200_running = 0; + FsmInitTimer(&st->l2.l2m, &st->l2.t200); + FsmInitTimer(&st->l2.l2m, &st->l2.t203); } void @@ -1311,8 +1549,8 @@ releasestack_transl2(struct PStack *st) { } -void -Isdnl2New(void) +HISAX_INITFUNC(void +Isdnl2New(void)) { l2fsm.state_count = L2_STATE_COUNT; l2fsm.event_count = L2_EVENT_COUNT; diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c index 73b66c73b..026d8eb76 100644 --- a/drivers/isdn/hisax/isdnl3.c +++ b/drivers/isdn/hisax/isdnl3.c @@ -1,4 +1,4 @@ -/* $Id: isdnl3.c,v 1.10 1997/04/06 22:54:16 keil Exp $ +/* $Id: isdnl3.c,v 2.5 1998/02/12 23:07:52 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,38 +7,39 @@ * Fritz Elfert * * $Log: isdnl3.c,v $ - * Revision 1.10 1997/04/06 22:54:16 keil - * Using SKB's - * - * Revision 1.9 1997/03/25 23:11:25 keil - * US NI-1 protocol + * Revision 2.5 1998/02/12 23:07:52 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.8 1997/03/21 18:53:44 keil - * Report no protocol error to syslog too + * Revision 2.4 1997/11/06 17:09:25 keil + * New 2.1 init code * - * Revision 1.7 1997/03/17 18:34:38 keil - * fixed oops if no protocol selected during config + * Revision 2.3 1997/10/29 19:07:53 keil + * changes for 2.1 * - * Revision 1.6 1997/02/16 01:04:08 fritz - * Bugfix: Changed timer handling caused hang with 2.1.X + * Revision 2.2 1997/10/01 09:21:41 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.5 1997/02/09 00:26:27 keil - * new interface handling, one interface per card - * leased line changes + * Revision 2.1 1997/08/03 14:36:32 keil + * Implement RESTART procedure * - * Revision 1.4 1997/01/27 23:17:44 keil - * delete timers while unloading + * Revision 2.0 1997/07/27 21:15:42 keil + * New Callref based layer3 * - * Revision 1.3 1997/01/21 22:31:12 keil - * new statemachine; L3 timers + * Revision 1.11 1997/06/26 11:11:44 keil + * SET_SKBFREE now on creation of a SKB * - * Revision 1.2 1996/11/05 19:42:04 keil - * using config.h + * Revision 1.10 1997/04/06 22:54:16 keil + * Using SKB's * - * Revision 1.1 1996/10/13 20:04:54 keil - * Initial revision + * Revision 1.9 1997/03/25 23:11:25 keil + * US NI-1 protocol * + * Revision 1.8 1997/03/21 18:53:44 keil + * Report no protocol error to syslog too * + * Remove old logs /KKe * */ #define __NO_VERSION__ @@ -46,7 +47,72 @@ #include "isdnl3.h" #include <linux/config.h> -const char *l3_revision = "$Revision: 1.10 $"; +const char *l3_revision = "$Revision: 2.5 $"; + +u_char * +findie(u_char * p, int size, u_char ie, int wanted_set) +{ + int l, codeset, maincodeset; + u_char *pend = p + size; + + /* skip protocol discriminator, callref and message type */ + p++; + l = (*p++) & 0xf; + p += l; + p++; + codeset = 0; + maincodeset = 0; + /* while there are bytes left... */ + while (p < pend) { + if ((*p & 0xf0) == 0x90) { + codeset = *p & 0x07; + if (!(*p & 0x08)) + maincodeset = codeset; + } + if (*p & 0x80) + p++; + else { + if (codeset == wanted_set) { + if (*p == ie) + return (p); + if (*p > ie) + return (NULL); + } + p++; + l = *p++; + p += l; + codeset = maincodeset; + } + } + return (NULL); +} + +int +getcallref(u_char * p) +{ + int l, m = 1, cr = 0; + p++; /* prot discr */ + l = 0xf & *p++; /* callref length */ + if (!l) /* dummy CallRef */ + return(-1); + while (l--) { + cr += m * (*p++); + m *= 8; + } + return (cr); +} + +static int OrigCallRef = 0; + +int +newcallref(void) +{ + if (OrigCallRef == 127) + OrigCallRef = 1; + else + OrigCallRef++; + return (OrigCallRef); +} void l3_debug(struct PStack *st, char *s) @@ -54,34 +120,33 @@ l3_debug(struct PStack *st, char *s) char str[256], tm[32]; jiftime(tm, jiffies); - sprintf(str, "%s Channel %d l3 %s\n", tm, st->l3.channr, s); + sprintf(str, "%s l3 %s\n", tm, s); HiSax_putstatus(st->l1.hardware, str); } - - void -newl3state(struct PStack *st, int state) +newl3state(struct l3_process *pc, int state) { char tmp[80]; - if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "newstate %d --> %d", st->l3.state, state); - l3_debug(st, tmp); + if (pc->debug & L3_DEB_STATE) { + sprintf(tmp, "newstate cr %d %d --> %d", pc->callref, + pc->state, state); + l3_debug(pc->st, tmp); } - st->l3.state = state; + pc->state = state; } static void L3ExpireTimer(struct L3Timer *t) { - t->st->l4.l4l3(t->st, t->event, NULL); + t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc); } void -L3InitTimer(struct PStack *st, struct L3Timer *t) +L3InitTimer(struct l3_process *pc, struct L3Timer *t) { - t->st = st; + t->pc = pc; t->tl.function = (void *) L3ExpireTimer; t->tl.data = (long) t; init_timer(&t->tl); @@ -109,9 +174,9 @@ L3AddTimer(struct L3Timer *t, } void -StopAllL3Timer(struct PStack *st) +StopAllL3Timer(struct l3_process *pc) { - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); } struct sk_buff * @@ -132,9 +197,8 @@ no_l3_proto(struct PStack *st, int pr, void *arg) { struct sk_buff *skb = arg; - l3_debug(st, "no protocol"); + HiSax_putstatus(st->l1.hardware, "L3 no D protocol\n"); if (skb) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); } } @@ -151,14 +215,78 @@ extern void setstack_ni1(struct PStack *st); extern void setstack_1tr6(struct PStack *st); #endif +struct l3_process +*getl3proc(struct PStack *st, int cr) +{ + struct l3_process *p = st->l3.proc; + + while (p) + if (p->callref == cr) + return (p); + else + p = p->next; + return (NULL); +} + +struct l3_process +*new_l3_process(struct PStack *st, int cr) +{ + struct l3_process *p, *np; + + if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { + printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr); + return (NULL); + } + if (!st->l3.proc) + st->l3.proc = p; + else { + np = st->l3.proc; + while (np->next) + np = np->next; + np->next = p; + } + p->next = NULL; + p->debug = L3_DEB_WARN; + p->callref = cr; + p->state = 0; + p->chan = NULL; + p->st = st; + p->N303 = st->l3.N303; + L3InitTimer(p, &p->timer); + return (p); +}; + +void +release_l3_process(struct l3_process *p) +{ + struct l3_process *np, *pp = NULL; + + if (!p) + return; + np = p->st->l3.proc; + while (np) { + if (np == p) { + StopAllL3Timer(p); + if (pp) + pp->next = np->next; + else + p->st->l3.proc = np->next; + kfree(p); + return; + } + pp = np; + np = np->next; + } + printk(KERN_ERR "HiSax internal L3 error CR not in list\n"); +}; + void setstack_isdnl3(struct PStack *st, struct Channel *chanp) { char tmp[64]; - st->l3.debug = L3_DEB_WARN; - st->l3.channr = chanp->chan; - L3InitTimer(st, &st->l3.timer); + st->l3.proc = NULL; + st->l3.global = NULL; #ifdef CONFIG_HISAX_EURO if (st->protocol == ISDN_PTYPE_EURO) { @@ -176,11 +304,11 @@ setstack_isdnl3(struct PStack *st, struct Channel *chanp) } else #endif if (st->protocol == ISDN_PTYPE_LEASED) { - st->l4.l4l3 = no_l3_proto; + st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; - printk(KERN_NOTICE "HiSax: Leased line mode\n"); + printk(KERN_INFO "HiSax: Leased line mode\n"); } else { - st->l4.l4l3 = no_l3_proto; + st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; sprintf(tmp, "protocol %s not supported", (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" : @@ -188,15 +316,18 @@ setstack_isdnl3(struct PStack *st, struct Channel *chanp) (st->protocol == ISDN_PTYPE_NI1) ? "ni1" : "unknown"); printk(KERN_WARNING "HiSax: %s\n", tmp); - l3_debug(st, tmp); st->protocol = -1; } - st->l3.state = 0; - st->l3.callref = 0; } void releasestack_isdnl3(struct PStack *st) { - StopAllL3Timer(st); + while (st->l3.proc) + release_l3_process(st->l3.proc); + if (st->l3.global) { + StopAllL3Timer(st->l3.global); + kfree(st->l3.global); + st->l3.global = NULL; + } } diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h index bed989a18..2ff582b4a 100644 --- a/drivers/isdn/hisax/isdnl3.h +++ b/drivers/isdn/hisax/isdnl3.h @@ -1,6 +1,12 @@ -/* $Id: isdnl3.h,v 1.3 1997/04/06 22:54:17 keil Exp $ - * +/* $Id: isdnl3.h,v 2.0 1997/07/27 21:15:42 keil Exp $ + * $Log: isdnl3.h,v $ + * Revision 2.0 1997/07/27 21:15:42 keil + * New Callref based layer3 + * + * Revision 1.4 1997/06/26 11:20:57 keil + * ? + * * Revision 1.3 1997/04/06 22:54:17 keil * Using SKB's * @@ -24,15 +30,18 @@ #define L3_DEB_CHARGE 0x08 struct stateentry { - int state; - u_char primitive; - void (*rout) (struct PStack *, u_char, void *); + int state; + u_char primitive; + void (*rout) (struct l3_process *, u_char, void *); }; extern void l3_debug(struct PStack *st, char *s); -extern void newl3state(struct PStack *st, int state); -extern void L3InitTimer(struct PStack *st, struct L3Timer *t); +extern void newl3state(struct l3_process *pc, int state); +extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t); extern void L3DelTimer(struct L3Timer *t); -extern int L3AddTimer(struct L3Timer *t, int millisec, int event); -extern void StopAllL3Timer(struct PStack *st); +extern int L3AddTimer(struct L3Timer *t, int millisec, int event); +extern void StopAllL3Timer(struct l3_process *pc); extern struct sk_buff *l3_alloc_skb(int len); +extern struct l3_process *new_l3_process(struct PStack *st, int cr); +extern void release_l3_process(struct l3_process *p); +extern struct l3_process *getl3proc(struct PStack *st, int cr); diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c index f5f5e857a..0c075546b 100644 --- a/drivers/isdn/hisax/ix1_micro.c +++ b/drivers/isdn/hisax/ix1_micro.c @@ -1,4 +1,4 @@ -/* $Id: ix1_micro.c,v 1.3 1997/04/13 19:54:02 keil Exp $ +/* $Id: ix1_micro.c,v 2.6 1998/02/11 17:28:09 keil Exp $ * ix1_micro.c low level stuff for ITK ix1-micro Rev.2 isdn cards * derived from the original file teles3.c from Karsten Keil @@ -11,6 +11,27 @@ * Beat Doebeli * * $Log: ix1_micro.c,v $ + * Revision 2.6 1998/02/11 17:28:09 keil + * Niccy PnP/PCI support + * + * Revision 2.5 1998/02/02 13:29:42 keil + * fast io + * + * Revision 2.4 1997/11/08 21:35:50 keil + * new l1 init + * + * Revision 2.3 1997/11/06 17:09:35 keil + * New 2.1 init code + * + * Revision 2.2 1997/10/29 18:55:51 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 2.1 1997/07/27 21:47:09 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:50 keil + * New Layer and card interface + * * Revision 1.3 1997/04/13 19:54:02 keil * Change in IRQ check delay for SMP * @@ -54,17 +75,16 @@ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles3.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include <linux/kernel_stat.h> extern const char *CardType[]; -const char *ix1_revision = "$Revision: 1.3 $"; +const char *ix1_revision = "$Revision: 2.6 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) #define SPECIAL_PORT_OFFSET 3 @@ -73,865 +93,250 @@ const char *ix1_revision = "$Revision: 1.3 $"; #define HSCX_COMMAND_OFFSET 2 #define HSCX_DATA_OFFSET 1 -#define ISAC_FIFOSIZE 16 -#define HSCX_FIFOSIZE 16 - #define TIMEOUT 50 static inline u_char -IsacReadReg(unsigned int adr, u_char off) -{ - byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20); - return bytein(adr + ISAC_DATA_OFFSET); -} - -static inline void -IsacWriteReg(unsigned int adr, u_char off, u_char data) -{ - byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20); - byteout(adr + ISAC_DATA_OFFSET, data); -} - -#define HSCX_OFFSET(WhichHscx,offset) \ -( (WhichHscx) ? (offset+0x60) : (offset+0x20) ) - -static inline u_char -HscxReadReg(unsigned int adr, int WhichHscx, u_char off) +readreg(unsigned int ale, unsigned int adr, u_char off) { - byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off)); - return bytein(adr + HSCX_DATA_OFFSET); -} - -static inline void -HscxWriteReg(unsigned int adr, int WhichHscx, u_char off, u_char data) -{ - byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off)); - byteout(adr + HSCX_DATA_OFFSET, data); -} - - -static inline void -IsacReadFifo(unsigned int adr, u_char * data, int size) -{ - byteout(adr + ISAC_COMMAND_OFFSET, 0); - while (size--) - *data++ = bytein(adr + ISAC_DATA_OFFSET); -} - -static void -IsacWriteFifo(unsigned int adr, u_char * data, int size) -{ - byteout(adr + ISAC_COMMAND_OFFSET, 0); - while (size--) { - byteout(adr + ISAC_DATA_OFFSET, *data); - data++; - } -} - -static inline void -HscxReadFifo(unsigned int adr, int WhichHscx, u_char * data, int size) -{ - byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00); - while (size--) - *data++ = bytein(adr + HSCX_DATA_OFFSET); -} + register u_char ret; + long flags; -static void -HscxWriteFifo(unsigned int adr, int WhichHscx, u_char * data, int size) -{ - byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00); - while (size--) { - byteout(adr + HSCX_DATA_OFFSET, *data); - data++; - } + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); } static inline void -waitforCEC(int adr, int WhichHscx) +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { - int to = TIMEOUT; + /* fifo read without cli because it's allready done */ - while ((HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "ix1-Micro: waitforCEC timeout\n"); + byteout(ale, off); + insb(adr, data, size); } static inline void -waitforXFW(int adr, int WhichHscx) -{ - int to = TIMEOUT; - - while ((!(HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "ix1-Micro: waitforXFW timeout\n"); -} - -static inline void -writehscxCMDR(int adr, int WhichHscx, u_char data) +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) { long flags; save_flags(flags); cli(); - waitforCEC(adr, WhichHscx); - HscxWriteReg(adr, WhichHscx, HSCX_CMDR, data); + byteout(ale, off); + byteout(adr, data); restore_flags(flags); } -/* - * fast interrupt here - */ - -static void -hscxreport(struct IsdnCardState *sp, int hscx) -{ - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_EXIR)); -} - -void -ix1micro_report(struct IsdnCardState *sp) +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", IsacReadReg(sp->isac, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", IsacReadReg(sp->isac, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", IsacReadReg(sp->isac, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); } -/* - * HSCX stuff goes here - */ +/* Interface functions */ -static void -hscx_empty_fifo(struct HscxState *hsp, int count) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - HscxReadFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset)); } static void -hscx_fill_fifo(struct HscxState *hsp) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx], hsp->hscx); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - HscxWriteFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value); } -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - } else { - count = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (sp->debug & L1_DEB_HSCX_FIFO) { - sprintf(tmp, "HX Frame %d", count); - debugl1(sp, tmp); - } - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "IX1: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "IX1: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - IsacReadFifo(sp->isac, ptr, count); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - IsacWriteFifo(sp->isac, ptr, count); - IsacWriteReg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.ix1.hscx_ale, + cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0))); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_CIX0, (command << 2) | 3); + writereg(cs->hw.ix1.hscx_ale, + cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value); } +#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data) -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = IsacReadReg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!exval & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = IsacReadReg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - sp->rcvidx = 0; - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "IX1: D receive out of memory\n"); - else { - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (IsacReadReg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = IsacReadReg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { - printk(KERN_WARNING "Teles: Spurious interrupt!\n"); + if (!cs) { + printk(KERN_WARNING "IX1: Spurious interrupt!\n"); return; } - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = IsacReadReg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = IsacReadReg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (stat & 1) { - HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0xFF); - HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0xFF); - HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0x0); - HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0x0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0); } if (stat & 2) { - IsacWriteReg(sp->isac, ISAC_MASK, 0xFF); - IsacWriteReg(sp->isac, ISAC_MASK, 0x0); - } -} - - -static void -initisac(struct IsdnCardState *sp) -{ - unsigned int adr = sp->isac; - - /* 16.3 IOM 2 Mode */ - IsacWriteReg(adr, ISAC_MASK, 0xff); - IsacWriteReg(adr, ISAC_ADF2, 0x80); - IsacWriteReg(adr, ISAC_SQXR, 0x2f); - IsacWriteReg(adr, ISAC_SPCR, 0x0); - IsacWriteReg(adr, ISAC_ADF1, 0x2); - IsacWriteReg(adr, ISAC_STCR, 0x70); - IsacWriteReg(adr, ISAC_MODE, 0xc9); - IsacWriteReg(adr, ISAC_TIMR, 0x0); - IsacWriteReg(adr, ISAC_ADF1, 0x0); - IsacWriteReg(adr, ISAC_CMDR, 0x41); - IsacWriteReg(adr, ISAC_CIX0, (1 << 2) | 3); - IsacWriteReg(adr, ISAC_MASK, 0xff); - IsacWriteReg(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0); } - hs->mode = mode; - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR1, 0x85); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD1, 0xFF); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD2, 0xFF); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RAH2, 0xFF); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XBCH, 0x0); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RLCR, 0x0); - - switch (mode) { - case 0: - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0xff); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0xff); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x84); - break; - case 1: - if (ichan == 0) { - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - } else { - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - } - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0xe4); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41); - break; - case 2: - if (ichan == 0) { - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - } else { - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - } - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x8c); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41); - break; - } - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_ISTA, 0x00); } void -release_io_ix1micro(struct IsdnCard *card) +release_io_ix1micro(struct IsdnCardState *cs) { - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 4); + if (cs->hw.ix1.cfg_reg) + release_region(cs->hw.ix1.cfg_reg, 4); } static void -clear_pending_ints(struct IsdnCardState *sp) +ix1_reset(struct IsdnCardState *cs) { - int val; - char tmp[64]; + long flags; + int cnt; - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = HscxReadReg(sp->hscx[1], 1, HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = HscxReadReg(sp->hscx[0], 0, HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = IsacReadReg(sp->isac, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = IsacReadReg(sp->isac, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = IsacReadReg(sp->isac, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = IsacReadReg(sp->isac, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = IsacReadReg(sp->isac, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = IsacReadReg(sp->isac, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); + /* reset isac */ + save_flags(flags); + cnt = 3 * (HZ / 10) + 1; + sti(); + while (cnt--) { + byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1); + HZDELAY(1); /* wait >=10 ms */ } - IsacWriteReg(sp->isac, ISAC_MASK, 0); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x41); + byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0); + restore_flags(flags); } -int -initix1micro(struct IsdnCardState *sp) +static int +ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat_irqs(sp->irq); - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &ix1micro_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat_irqs(sp->irq) > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat_irqs(sp->irq)); - debugl1(sp, tmp); - if (kstat_irqs(sp->irq) == sp->counter) { - printk(KERN_WARNING - "ix1-Micro: IRQ(%d) getting no interrupts during init\n", - sp->irq); - free_irq(sp->irq, sp); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + ix1_reset(cs); + return(0); + case CARD_RELEASE: + release_io_ix1micro(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &ix1micro_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -int -setup_ix1micro(struct IsdnCard *card) + +__initfunc(int +setup_ix1micro(struct IsdnCard *card)) { - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, ix1_revision); - printk(KERN_NOTICE "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp)); - if (sp->typ != ISDN_CTYPE_IX1MICROR2) + printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_IX1MICROR2) return (0); /* IO-Ports */ - sp->isac = sp->hscx[0] = sp->hscx[1] = sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 4)) { + cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET; + cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET; + cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET; + cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET; + cs->hw.ix1.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (cs->hw.ix1.cfg_reg) { + if (check_region((cs->hw.ix1.cfg_reg), 4)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 4); + cs->hw.ix1.cfg_reg, + cs->hw.ix1.cfg_reg + 4); return (0); } else - request_region(sp->cfg_reg, 4, "ix1micro cfg"); - } - /* reset isac */ - save_flags(flags); - val = 3 * (HZ / 10) + 1; - sti(); - while (val--) { - byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 1); - HZDELAY(1); /* wait >=10 ms */ - } - byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 0); - restore_flags(flags); - - printk(KERN_NOTICE - "HiSax: %s config irq:%d io:0x%x\n", - CardType[sp->typ], sp->irq, - sp->cfg_reg); - verA = HscxReadReg(sp->hscx[0], 0, HSCX_VSTR) & 0xf; - verB = HscxReadReg(sp->hscx[1], 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "ix1-Micro: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = IsacReadReg(sp->isac, ISAC_RBCH); - printk(KERN_INFO "ix1-Micro: ISAC %s\n", - ISACVersion(val)); - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg"); + } + printk(KERN_INFO + "HiSax: %s config irq:%d io:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.ix1.cfg_reg); + ix1_reset(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &ix1_card_msg; + ISACVersion(cs, "ix1-Micro:"); + if (HscxVersion(cs, "ix1-Micro:")) { printk(KERN_WARNING "ix1-Micro: wrong HSCX versions check IO address\n"); - release_io_ix1micro(card); + release_io_ix1micro(cs); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c index c3ee6a73f..d60a5da66 100644 --- a/drivers/isdn/hisax/l3_1tr6.c +++ b/drivers/isdn/hisax/l3_1tr6.c @@ -1,4 +1,4 @@ -/* $Id: l3_1tr6.c,v 1.11 1997/04/06 22:54:18 keil Exp $ +/* $Id: l3_1tr6.c,v 2.4 1998/02/12 23:07:57 keil Exp $ * German 1TR6 D-channel protocol * @@ -6,39 +6,28 @@ * * * $Log: l3_1tr6.c,v $ - * Revision 1.11 1997/04/06 22:54:18 keil - * Using SKB's - * - * Revision 1.10 1997/03/13 20:37:58 keil - * channel request added - * - * Revision 1.9 1997/02/11 01:37:40 keil - * Changed setup-interface (incoming and outgoing) - * - * Revision 1.8 1997/01/27 23:20:21 keil - * report revision only ones - * - * Revision 1.7 1997/01/21 22:30:07 keil - * new statemachine; L3 timers + * Revision 2.4 1998/02/12 23:07:57 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.6 1996/12/14 21:07:20 keil - * additional states for CC_REJECT + * Revision 2.3 1997/11/06 17:12:24 keil + * KERN_NOTICE --> KERN_INFO * - * Revision 1.5 1996/12/08 19:55:17 keil - * change CC_REJECT_REQ routine + * Revision 2.2 1997/10/29 19:03:00 keil + * changes for 2.1 * - * Revision 1.4 1996/10/30 10:18:01 keil - * bugfixes in debugging output + * Revision 2.1 1997/08/03 15:28:09 keil + * release L3 empty processes * - * Revision 1.3 1996/10/27 22:15:37 keil - * bugfix reject handling + * Revision 2.0 1997/07/27 21:15:45 keil + * New Callref based layer3 * - * Revision 1.2 1996/10/13 23:08:56 keil - * added missing state for callback reject + * Revision 1.12 1997/06/26 11:11:45 keil + * SET_SKBFREE now on creation of a SKB * - * Revision 1.1 1996/10/13 20:04:55 keil - * Initial revision + * Revision 1.11 1997/04/06 22:54:18 keil + * Using SKB's * + * Old Log removed /KKe * */ @@ -49,16 +38,16 @@ #include <linux/ctype.h> extern char *HiSax_getrev(const char *revision); -const char *l3_1tr6_revision = "$Revision: 1.11 $"; +const char *l3_1tr6_revision = "$Revision: 2.4 $"; #define MsgHead(ptr, cref, mty, dis) \ *ptr++ = dis; \ *ptr++ = 0x1; \ - *ptr++ = cref; \ + *ptr++ = cref ^ 0x80; \ *ptr++ = mty static void -l3_1TR6_message(struct PStack *st, u_char mt, u_char pd) +l3_1TR6_message(struct l3_process *pc, u_char mt, u_char pd) { struct sk_buff *skb; u_char *p; @@ -66,12 +55,71 @@ l3_1TR6_message(struct PStack *st, u_char mt, u_char pd) if (!(skb = l3_alloc_skb(4))) return; p = skb_put(skb, 4); - MsgHead(p, st->l3.callref, mt, pd); - st->l3.l3l2(st, DL_DATA, skb); + MsgHead(p, pc->callref, mt, pd); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); +} + +static int +l31tr6_check_messagetype_validity(int mt, int pd) { +/* verify if a message type exists */ + + if (pd == PROTO_DIS_N0) + switch(mt) { + case MT_N0_REG_IND: + case MT_N0_CANC_IND: + case MT_N0_FAC_STA: + case MT_N0_STA_ACK: + case MT_N0_STA_REJ: + case MT_N0_FAC_INF: + case MT_N0_INF_ACK: + case MT_N0_INF_REJ: + case MT_N0_CLOSE: + case MT_N0_CLO_ACK: + return(1); + default: + return(0); + } + else if (pd == PROTO_DIS_N1) + switch(mt) { + case MT_N1_ESC: + case MT_N1_ALERT: + case MT_N1_CALL_SENT: + case MT_N1_CONN: + case MT_N1_CONN_ACK: + case MT_N1_SETUP: + case MT_N1_SETUP_ACK: + case MT_N1_RES: + case MT_N1_RES_ACK: + case MT_N1_RES_REJ: + case MT_N1_SUSP: + case MT_N1_SUSP_ACK: + case MT_N1_SUSP_REJ: + case MT_N1_USER_INFO: + case MT_N1_DET: + case MT_N1_DISC: + case MT_N1_REL: + case MT_N1_REL_ACK: + case MT_N1_CANC_ACK: + case MT_N1_CANC_REJ: + case MT_N1_CON_CON: + case MT_N1_FAC: + case MT_N1_FAC_ACK: + case MT_N1_FAC_CAN: + case MT_N1_FAC_REG: + case MT_N1_FAC_REJ: + case MT_N1_INFO: + case MT_N1_REG_ACK: + case MT_N1_REG_REJ: + case MT_N1_STAT: + return (1); + default: + return(0); + } + return(0); } static void -l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[128]; @@ -81,16 +129,13 @@ l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) u_char channel = 0; int l; - - st->l3.callref = st->pa->callref; - MsgHead(p, st->l3.callref, MT_N1_SETUP, PROTO_DIS_N1); - - teln = st->pa->setup.phone; - st->pa->spv = 0; + MsgHead(p, pc->callref, MT_N1_SETUP, PROTO_DIS_N1); + teln = pc->para.setup.phone; + pc->para.spv = 0; if (!isdigit(*teln)) { switch (0x5f & *teln) { case 'S': - st->pa->spv = 1; + pc->para.spv = 1; break; case 'C': channel = 0x08; @@ -103,8 +148,8 @@ l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) channel |= 0x02; break; default: - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "Wrong MSN Code"); + if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "Wrong MSN Code"); break; } teln++; @@ -114,22 +159,22 @@ l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) *p++ = 1; *p++ = channel; } - if (st->pa->spv) { /* SPV ? */ + if (pc->para.spv) { /* SPV ? */ /* NSF SPV */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->setup.si1; /* 0 for all Services */ - *p++ = st->pa->setup.si2; /* 0 for all Services */ + *p++ = pc->para.setup.si1; /* 0 for all Services */ + *p++ = pc->para.setup.si2; /* 0 for all Services */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_Activate; /* aktiviere SPV (default) */ - *p++ = st->pa->setup.si1; /* 0 for all Services */ - *p++ = st->pa->setup.si2; /* 0 for all Services */ + *p++ = pc->para.setup.si1; /* 0 for all Services */ + *p++ = pc->para.setup.si2; /* 0 for all Services */ } - eaz = st->pa->setup.eazmsn; + eaz = pc->para.setup.eazmsn; if (*eaz) { *p++ = WE0_origAddr; *p++ = strlen(eaz) + 1; @@ -149,22 +194,21 @@ l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) /* Codesatz 6 fuer Service */ *p++ = WE6_serviceInd; *p++ = 2; /* len=2 info,info2 */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303); - newl3state(st, 1); - st->l3.l3l2(st, DL_DATA, skb); - + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + newl3state(pc, 1); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } static void -l3_1tr6_setup(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg) { u_char *p; int bcfound = 0; @@ -172,110 +216,105 @@ l3_1tr6_setup(struct PStack *st, u_char pr, void *arg) struct sk_buff *skb = arg; p = skb->data; - st->pa->callref = getcallref(p); - st->l3.callref = 0x80 + st->pa->callref; /* Channel Identification */ p = skb->data; if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; + pc->para.bchannel = p[2] & 0x3; bcfound++; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); + } else if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); p = skb->data; if ((p = findie(p, skb->len, WE6_serviceInd, 6))) { - st->pa->setup.si1 = p[2]; - st->pa->setup.si2 = p[3]; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without service indicator"); + pc->para.setup.si1 = p[2]; + pc->para.setup.si2 = p[3]; + } else if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without service indicator"); p = skb->data; if ((p = findie(p, skb->len, WE0_destAddr, 0))) - iecpy(st->pa->setup.eazmsn, p, 1); + iecpy(pc->para.setup.eazmsn, p, 1); else - st->pa->setup.eazmsn[0] = 0; + pc->para.setup.eazmsn[0] = 0; p = skb->data; if ((p = findie(p, skb->len, WE0_origAddr, 0))) { - iecpy(st->pa->setup.phone, p, 1); + iecpy(pc->para.setup.phone, p, 1); } else - st->pa->setup.phone[0] = 0; + pc->para.setup.phone[0] = 0; p = skb->data; - st->pa->spv = 0; + pc->para.spv = 0; if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) { if ((FAC_SPV == p[3]) || (FAC_Activate == p[3])) - st->pa->spv = 1; + pc->para.spv = 1; } - SET_SKB_FREE(skb); dev_kfree_skb(skb); /* Signal all services, linklevel takes care of Service-Indicator */ if (bcfound) { - if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) { + if ((pc->para.setup.si1 != 7) && (pc->st->l3.debug & L3_DEB_WARN)) { sprintf(tmp, "non-digital call: %s -> %s", - st->pa->setup.phone, - st->pa->setup.eazmsn); - l3_debug(st, tmp); + pc->para.setup.phone, + pc->para.setup.eazmsn); + l3_debug(pc->st, tmp); } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } + newl3state(pc, 6); + pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL); + } else + release_l3_process(pc); } static void -l3_1tr6_setup_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - newl3state(st, 2); + newl3state(pc, 2); if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + pc->para.bchannel = p[2] & 0x3; + } else if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb); - L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); - st->l3.l3l4(st, CC_MORE_INFO, NULL); + L3AddTimer(&pc->timer, T304, CC_T304); + pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL); } static void -l3_1tr6_call_sent(struct PStack *st, u_char pr, void *arg) +l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + pc->para.bchannel = p[2] & 0x3; + } else if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb); - L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310); - newl3state(st, 3); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); + L3AddTimer(&pc->timer, T310, CC_T310); + newl3state(pc, 3); + pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL); } static void -l3_1tr6_alert(struct PStack *st, u_char pr, void *arg) +l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - L3DelTimer(&st->l3.timer); /* T304 */ - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL); } static void -l3_1tr6_info(struct PStack *st, u_char pr, void *arg) +l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg) { u_char *p; int i, tmpcharge = 0; @@ -289,45 +328,42 @@ l3_1tr6_info(struct PStack *st, u_char pr, void *arg) tmpcharge *= 10; tmpcharge += a_charge[i] & 0xf; } - if (tmpcharge > st->pa->chargeinfo) { - st->pa->chargeinfo = tmpcharge; - st->l3.l3l4(st, CC_INFO_CHARGE, NULL); + if (tmpcharge > pc->para.chargeinfo) { + pc->para.chargeinfo = tmpcharge; + pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL); } - if (st->l3.debug & L3_DEB_CHARGE) { - sprintf(tmp, "charging info %d", st->pa->chargeinfo); - l3_debug(st, tmp); + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); } - } else if (st->l3.debug & L3_DEB_CHARGE) - l3_debug(st, "charging info not found"); - SET_SKB_FREE(skb); + } else if (pc->st->l3.debug & L3_DEB_CHARGE) + l3_debug(pc->st, "charging info not found"); dev_kfree_skb(skb); } static void -l3_1tr6_info_s2(struct PStack *st, u_char pr, void *arg) +l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); } static void -l3_1tr6_connect(struct PStack *st, u_char pr, void *arg) +l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); /* T310 */ - newl3state(st, 10); - SET_SKB_FREE(skb); + L3DelTimer(&pc->timer); /* T310 */ + newl3state(pc, 10); dev_kfree_skb(skb); - st->pa->chargeinfo = 0; - st->l3.l3l4(st, CC_SETUP_CNF, NULL); + pc->para.chargeinfo = 0; + pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL); } static void -l3_1tr6_rel(struct PStack *st, u_char pr, void *arg) +l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; u_char *p; @@ -335,47 +371,47 @@ l3_1tr6_rel(struct PStack *st, u_char pr, void *arg) p = skb->data; if ((p = findie(p, skb->len, WE0_cause, 0))) { if (p[1] > 0) { - st->pa->cause = p[2]; + pc->para.cause = p[2]; if (p[1] > 1) - st->pa->loc = p[3]; + pc->para.loc = p[3]; else - st->pa->loc = 0; + pc->para.loc = 0; } else { - st->pa->cause = 0; - st->pa->loc = 0; + pc->para.cause = 0; + pc->para.loc = 0; } } else - st->pa->cause = -1; - SET_SKB_FREE(skb); + pc->para.cause = -1; dev_kfree_skb(skb); - StopAllL3Timer(st); - newl3state(st, 0); - l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + StopAllL3Timer(pc); + newl3state(pc, 0); + l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1); + pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL); + release_l3_process(pc); } static void -l3_1tr6_rel_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - StopAllL3Timer(st); - newl3state(st, 0); - st->pa->cause = -1; - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); + StopAllL3Timer(pc); + newl3state(pc, 0); + pc->para.cause = -1; + pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL); + release_l3_process(pc); } static void -l3_1tr6_disc(struct PStack *st, u_char pr, void *arg) +l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; u_char *p; int i, tmpcharge = 0; char a_charge[8], tmp[32]; - StopAllL3Timer(st); + StopAllL3Timer(pc); p = skb->data; if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) { iecpy(a_charge, p, 1); @@ -383,104 +419,102 @@ l3_1tr6_disc(struct PStack *st, u_char pr, void *arg) tmpcharge *= 10; tmpcharge += a_charge[i] & 0xf; } - if (tmpcharge > st->pa->chargeinfo) { - st->pa->chargeinfo = tmpcharge; - st->l3.l3l4(st, CC_INFO_CHARGE, NULL); + if (tmpcharge > pc->para.chargeinfo) { + pc->para.chargeinfo = tmpcharge; + pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL); } - if (st->l3.debug & L3_DEB_CHARGE) { - sprintf(tmp, "charging info %d", st->pa->chargeinfo); - l3_debug(st, tmp); + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); } - } else if (st->l3.debug & L3_DEB_CHARGE) - l3_debug(st, "charging info not found"); + } else if (pc->st->l3.debug & L3_DEB_CHARGE) + l3_debug(pc->st, "charging info not found"); p = skb->data; if ((p = findie(p, skb->len, WE0_cause, 0))) { if (p[1] > 0) { - st->pa->cause = p[2]; + pc->para.cause = p[2]; if (p[1] > 1) - st->pa->loc = p[3]; + pc->para.loc = p[3]; else - st->pa->loc = 0; + pc->para.loc = 0; } else { - st->pa->cause = 0; - st->pa->loc = 0; + pc->para.cause = 0; + pc->para.loc = 0; } } else { - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "cause not found"); - st->pa->cause = -1; + if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "cause not found"); + pc->para.cause = -1; } - SET_SKB_FREE(skb); dev_kfree_skb(skb); - newl3state(st, 12); - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); + newl3state(pc, 12); + pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL); } static void -l3_1tr6_connect_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - newl3state(st, 10); - st->pa->chargeinfo = 0; - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); + newl3state(pc, 10); + pc->para.chargeinfo = 0; + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL); } static void -l3_1tr6_alert_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 7); - l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1); + newl3state(pc, 7); + l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1); } static void -l3_1tr6_setup_rsp(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_rsp(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[24]; u_char *p = tmp; int l; - MsgHead(p, st->l3.callref, MT_N1_CONN, PROTO_DIS_N1); - if (st->pa->spv) { /* SPV ? */ + MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1); + if (pc->para.spv) { /* SPV ? */ /* NSF SPV */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_Activate; /* aktiviere SPV */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; } - newl3state(st, 8); + newl3state(pc, 8); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); } static void -l3_1tr6_reset(struct PStack *st, u_char pr, void *arg) +l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 0); + release_l3_process(pc); } static void -l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -489,8 +523,8 @@ l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) u_char cause = 0x10; u_char clen = 1; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; /* Map DSS1 causes */ switch (cause & 0x7f) { case 0x10: @@ -500,57 +534,55 @@ l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) cause = CAUSE_CallRejected; break; } - StopAllL3Timer(st); - MsgHead(p, st->l3.callref, MT_N1_DISC, PROTO_DIS_N1); + StopAllL3Timer(pc); + MsgHead(p, pc->callref, MT_N1_DISC, PROTO_DIS_N1); *p++ = WE0_cause; *p++ = clen; /* Laenge */ if (clen) *p++ = cause | 0x80; - newl3state(st, 11); + newl3state(pc, 11); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3AddTimer(&pc->timer, T305, CC_T305); } static void -l3_1tr6_release_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 19); - l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + StopAllL3Timer(pc); + newl3state(pc, 19); + l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3_1tr6_t303(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg) { - if (st->l3.n_t303 > 0) { - st->l3.n_t303--; - L3DelTimer(&st->l3.timer); - l3_1tr6_setup_req(st, pr, arg); + if (pc->N303 > 0) { + pc->N303--; + L3DelTimer(&pc->timer); + l3_1tr6_setup_req(pc, pr, arg); } else { - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); - st->l3.n_t303 = 1; - newl3state(st, 0); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL); + release_l3_process(pc); } } static void -l3_1tr6_t304(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t304(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); - + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL); } static void -l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -559,9 +591,9 @@ l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) u_char cause = 0x90; u_char clen = 1; - L3DelTimer(&st->l3.timer); - if (st->pa->cause > 0) - cause = st->pa->cause; + L3DelTimer(&pc->timer); + if (pc->para.cause > 0) + cause = pc->para.cause; /* Map DSS1 causes */ switch (cause & 0x7f) { case 0x10: @@ -571,53 +603,53 @@ l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) cause = CAUSE_CallRejected; break; } - MsgHead(p, st->l3.callref, MT_N1_REL, PROTO_DIS_N1); + MsgHead(p, pc->callref, MT_N1_REL, PROTO_DIS_N1); *p++ = WE0_cause; *p++ = clen; /* Laenge */ if (clen) *p++ = cause; - newl3state(st, 19); + newl3state(pc, 19); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3_1tr6_t310(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL); } static void -l3_1tr6_t313(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_CONNECT_ERR, NULL); + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL); } static void -l3_1tr6_t308_1(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t308_1(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2); - newl3state(st, 19); + L3DelTimer(&pc->timer); + l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1); + L3AddTimer(&pc->timer, T308, CC_T308_2); + newl3state(pc, 19); } static void -l3_1tr6_t308_2(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_RELEASE_ERR, NULL); - newl3state(st, 0); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL); + release_l3_process(pc); } /* *INDENT-OFF* */ static struct stateentry downstl[] = @@ -688,49 +720,79 @@ static struct stateentry datastln1[] = + static int datastln1_len = sizeof(datastln1) / sizeof(struct stateentry); static void up1tr6(struct PStack *st, int pr, void *arg) { - int i, mt; + int i, mt, cr; + struct l3_process *proc; struct sk_buff *skb = arg; char tmp[80]; + if (skb->len < 4) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 len only %d", skb->len); + l3_debug(st, tmp); + } + dev_kfree_skb(skb); + return; + } if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d state %d", + sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d", (pr == DL_DATA) ? " " : "(broadcast) ", - skb->data[0], skb->len, st->l3.state); + skb->data[0], skb->len); + l3_debug(st, tmp); + } + dev_kfree_skb(skb); + return; + } + if (skb->data[1] != 1) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 CR len not 1"); l3_debug(st, tmp); } - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; } - mt = skb->data[skb->data[1] + 2]; + cr = skb->data[2]; + mt = skb->data[3]; if (skb->data[0] == PROTO_DIS_N0) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "up1tr6%s N0 state %d mt %x unhandled", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + sprintf(tmp, "up1tr6%s N0 mt %x unhandled", + (pr == DL_DATA) ? " " : "(broadcast) ", mt); l3_debug(st, tmp); } } else if (skb->data[0] == PROTO_DIS_N1) { + if (!(proc = getl3proc(st, cr))) { + if ((mt == MT_N1_SETUP) && (cr < 128)) { + if (!(proc = new_l3_process(st, cr))) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 no roc mem"); + l3_debug(st, tmp); + } + dev_kfree_skb(skb); + return; + } + } else { + dev_kfree_skb(skb); + return; + } + } for (i = 0; i < datastln1_len; i++) if ((mt == datastln1[i].primitive) && - ((1 << st->l3.state) & datastln1[i].state)) + ((1 << proc->state) & datastln1[i].state)) break; if (i == datastln1_len) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x unhandled", (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + proc->state, mt); l3_debug(st, tmp); } return; @@ -738,10 +800,10 @@ up1tr6(struct PStack *st, int pr, void *arg) if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x", (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + proc->state, mt); l3_debug(st, tmp); } - datastln1[i].rout(st, pr, skb); + datastln1[i].rout(proc, pr, skb); } } } @@ -749,26 +811,44 @@ up1tr6(struct PStack *st, int pr, void *arg) static void down1tr6(struct PStack *st, int pr, void *arg) { - int i; + int i, cr; + struct l3_process *proc; + struct Channel *chan; char tmp[80]; + if (CC_SETUP_REQ == pr) { + chan = arg; + cr = newcallref(); + cr |= 0x80; + if (!(proc = new_l3_process(st, cr))) { + return; + } else { + proc->chan = chan; + chan->proc = proc; + proc->para.setup = chan->setup; + proc->callref = cr; + } + } else { + proc = arg; + } + for (i = 0; i < downstl_len; i++) if ((pr == downstl[i].primitive) && - ((1 << st->l3.state) & downstl[i].state)) + ((1 << proc->state) & downstl[i].state)) break; if (i == downstl_len) { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "down1tr6 state %d prim %d unhandled", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } } else { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "down1tr6 state %d prim %d", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } - downstl[i].rout(st, pr, arg); + downstl[i].rout(proc, pr, arg); } } @@ -777,20 +857,10 @@ setstack_1tr6(struct PStack *st) { char tmp[64]; - st->l4.l4l3 = down1tr6; + st->lli.l4l3 = down1tr6; st->l2.l2l3 = up1tr6; - st->l3.t303 = 4000; - st->l3.t304 = 20000; - st->l3.t305 = 4000; - st->l3.t308 = 4000; - st->l3.t310 = 120000; - st->l3.t313 = 4000; - st->l3.t318 = 4000; - st->l3.t319 = 4000; - st->l3.n_t303 = 0; - - if (st->l3.channr & 1) { - strcpy(tmp, l3_1tr6_revision); - printk(KERN_NOTICE "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp)); - } + st->l3.N303 = 0; + + strcpy(tmp, l3_1tr6_revision); + printk(KERN_INFO "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp)); } diff --git a/drivers/isdn/hisax/l3_1tr6.h b/drivers/isdn/hisax/l3_1tr6.h index 6e2fee72f..90d08793e 100644 --- a/drivers/isdn/hisax/l3_1tr6.h +++ b/drivers/isdn/hisax/l3_1tr6.h @@ -1,13 +1,14 @@ -/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil Exp $ +/* $Id: l3_1tr6.h,v 2.0 1997/07/27 21:15:47 keil Exp $ * * German 1TR6 D-channel protocol defines * * $Log: l3_1tr6.h,v $ + * Revision 2.0 1997/07/27 21:15:47 keil + * New Callref based layer3 + * * Revision 1.1 1996/10/13 20:03:48 keil * Initial revision * - * - * */ #ifndef l3_1tr6 #define l3_1tr6 @@ -29,7 +30,6 @@ #define MT_N0_CLOSE 0x75 #define MT_N0_CLO_ACK 0x77 - /* * MsgType N1 */ @@ -65,8 +65,6 @@ #define MT_N1_REG_REJ 0x6F #define MT_N1_STAT 0x63 - - /* * W Elemente */ @@ -156,5 +154,13 @@ #define CAUSE_RemoteUserResumed 0x73 #define CAUSE_UserInfoDiscarded 0x7F +#define T303 4000 +#define T304 20000 +#define T305 4000 +#define T308 4000 +#define T310 120000 +#define T313 4000 +#define T318 4000 +#define T319 4000 #endif diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c index d60898734..f8b97fd73 100644 --- a/drivers/isdn/hisax/l3dss1.c +++ b/drivers/isdn/hisax/l3dss1.c @@ -1,4 +1,4 @@ -/* $Id: l3dss1.c,v 1.15 1997/04/17 11:50:48 keil Exp $ +/* $Id: l3dss1.c,v 2.7 1998/02/12 23:08:01 keil Exp $ * EURO/DSS1 D-channel protocol * @@ -9,71 +9,272 @@ * Fritz Elfert * * $Log: l3dss1.c,v $ - * Revision 1.15 1997/04/17 11:50:48 keil - * pa->loc was undefined, if it was not send by the exchange - * - * Revision 1.14 1997/04/06 22:54:20 keil - * Using SKB's - * - * Revision 1.13 1997/03/13 20:37:28 keil - * CLIR and channel request added - * - * Revision 1.12 1997/02/17 00:34:26 keil - * Bugfix: Wrong cause delivered - * - * Revision 1.11 1997/02/16 12:12:47 fritz - * Bugfix: SI2 was nont initialized on incoming calls. - * - * Revision 1.10 1997/02/11 01:37:24 keil - * Changed setup-interface (incoming and outgoing) - * - * Revision 1.9 1997/01/27 23:20:52 keil - * report revision only ones + * Revision 2.7 1998/02/12 23:08:01 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.8 1997/01/21 22:29:41 keil - * new statemachine; L3 timers + * Revision 2.6 1998/02/03 23:26:35 keil + * V110 extensions from Thomas Pfeiffer * - * Revision 1.7 1996/12/14 21:06:59 keil - * additional states for CC_REJECT + * Revision 2.5 1998/02/02 13:34:28 keil + * Support australian Microlink net and german AOCD * - * Revision 1.6 1996/12/08 22:59:16 keil - * fixed calling party number without octet 3a + * Revision 2.4 1997/11/06 17:12:25 keil + * KERN_NOTICE --> KERN_INFO * - * Revision 1.5 1996/12/08 19:53:31 keil - * fixes from Pekka Sarnila + * Revision 2.3 1997/10/29 19:03:01 keil + * changes for 2.1 * - * Revision 1.4 1996/11/05 19:44:36 keil - * some fixes from Henner Eisen + * Revision 2.2 1997/08/07 17:44:36 keil + * Fix RESTART * - * Revision 1.3 1996/10/30 10:18:01 keil - * bugfixes in debugging output + * Revision 2.1 1997/08/03 14:36:33 keil + * Implement RESTART procedure * - * Revision 1.2 1996/10/27 22:15:16 keil - * bugfix reject handling + * Revision 2.0 1997/07/27 21:15:43 keil + * New Callref based layer3 * - * Revision 1.1 1996/10/13 20:04:55 keil - * Initial revision + * Revision 1.17 1997/06/26 11:11:46 keil + * SET_SKBFREE now on creation of a SKB * + * Revision 1.15 1997/04/17 11:50:48 keil + * pa->loc was undefined, if it was not send by the exchange * + * Old log removed /KKe * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl3.h" +#include "l3dss1.h" #include <linux/ctype.h> extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 1.15 $"; +const char *dss1_revision = "$Revision: 2.7 $"; + +#define EXT_BEARER_CAPS 1 #define MsgHead(ptr, cref, mty) \ *ptr++ = 0x8; \ *ptr++ = 0x1; \ - *ptr++ = cref; \ + *ptr++ = cref^0x80; \ *ptr++ = mty + +#ifdef HISAX_DE_AOC static void -l3dss1_message(struct PStack *st, u_char mt) +l3dss1_parse_facility(struct l3_process *pc, u_char *p) +{ + int qd_len = 0; + char tmp[32]; + + p++; + qd_len = *p++; + if (qd_len == 0) { + l3_debug(pc->st, "qd_len == 0"); + return; + } + if((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */ + l3_debug(pc->st, "supplementary service != 0x11"); + return; + } + while(qd_len > 0 && !(*p & 0x80)) { /* extension ? */ + p++; qd_len--; + } + if(qd_len < 2) { + l3_debug(pc->st, "qd_len < 2"); + return; + } + p++; qd_len--; + if((*p & 0xE0) != 0xA0) { /* class and form */ + l3_debug(pc->st, "class and form != 0xA0"); + return; + } + switch(*p & 0x1F) { /* component tag */ + case 1: /* invoke */ + { + unsigned char nlen, ilen; + int ident; + + p++; qd_len--; + if(qd_len < 1) { + l3_debug(pc->st, "qd_len < 1"); + break; + } + if(*p & 0x80) { /* length format */ + l3_debug(pc->st, "*p & 0x80 length format"); + break; + } + nlen = *p++; qd_len--; + if(qd_len < nlen) { + l3_debug(pc->st, "qd_len < nlen"); + return; + } + qd_len -= nlen; + + if(nlen < 2) { + l3_debug(pc->st, "nlen < 2"); + return; + } + if(*p != 0x02) { /* invoke identifier tag */ + l3_debug(pc->st, "invoke identifier tag !=0x02"); + return; + } + p++; nlen--; + if(*p & 0x80) { /* length format */ + l3_debug(pc->st, "*p & 0x80 length format 2"); + break; + } + ilen = *p++; nlen--; + if(ilen > nlen || ilen == 0) { + l3_debug(pc->st, "ilen > nlen || ilen == 0"); + return; + } + nlen -= ilen; + ident = 0; + while(ilen > 0) { + ident = (ident << 8) | (*p++ & 0xFF); /* invoke identifier */ + ilen--; + } + + if(nlen < 2) { + l3_debug(pc->st, "nlen < 2 22"); + return; + } + if(*p != 0x02) { /* operation value */ + l3_debug(pc->st, "operation value !=0x02"); + return; + } + p++; nlen--; + ilen = *p++; nlen--; + if(ilen > nlen || ilen == 0) { + l3_debug(pc->st, "ilen > nlen || ilen == 0 22"); + return; + } + nlen -= ilen; + ident = 0; + while(ilen > 0) { + ident = (ident << 8) | (*p++ & 0xFF); + ilen--; + } + + #define FOO1(s,a,b) \ + while(nlen > 1) { \ + int ilen = p[1]; \ + if(nlen < ilen+2) { \ + l3_debug(pc->st, "FOO1 nlen < ilen+2"); \ + return; \ + } \ + nlen -= ilen+2; \ + if((*p & 0xFF) == (a)) { \ + int nlen = ilen; \ + p += 2; \ + b; \ + } else { \ + p += ilen+2; \ + } \ + } + + switch(ident) { + default: + break; + case 0x22: /* during */ + FOO1("1A",0x30,FOO1("1C",0xA1,FOO1("1D",0x30,FOO1("1E",0x02,({ + ident = 0; + while(ilen > 0) { + ident = (ident<<8) | *p++; + ilen--; + } + if (ident > pc->para.chargeinfo) { + pc->para.chargeinfo = ident; + pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + if (*(p+2) == 0) { + sprintf(tmp, "charging info during %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); + } + else { + sprintf(tmp, "charging info final %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); + } + } + }))))) + break; + case 0x24: /* final */ + FOO1("2A",0x30,FOO1("2B",0x30,FOO1("2C",0xA1,FOO1("2D",0x30,FOO1("2E",0x02,({ + ident = 0; + while(ilen > 0) { + ident = (ident<<8) | *p++; + ilen--; + } + if (ident > pc->para.chargeinfo) { + pc->para.chargeinfo = ident; + pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info final %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); + } + })))))) + break; + } + #undef FOO1 + + } + break; + case 2: /* return result */ + l3_debug(pc->st, "return result break"); + break; + case 3: /* return error */ + l3_debug(pc->st, "return error break"); + break; + default: + l3_debug(pc->st, "default break"); + break; + } +} +#endif + +static int +l3dss1_check_messagetype_validity(int mt) { +/* verify if a message type exists */ + switch(mt) { + case MT_ALERTING: + case MT_CALL_PROCEEDING: + case MT_CONNECT: + case MT_CONNECT_ACKNOWLEDGE: + case MT_PROGRESS: + case MT_SETUP: + case MT_SETUP_ACKNOWLEDGE: + case MT_RESUME: + case MT_RESUME_ACKNOWLEDGE: + case MT_RESUME_REJECT: + case MT_SUSPEND: + case MT_SUSPEND_ACKNOWLEDGE: + case MT_SUSPEND_REJECT: + case MT_USER_INFORMATION: + case MT_DISCONNECT: + case MT_RELEASE: + case MT_RELEASE_COMPLETE: + case MT_RESTART: + case MT_RESTART_ACKNOWLEDGE: + case MT_SEGMENT: + case MT_CONGESTION_CONTROL: + case MT_INFORMATION: + case MT_FACILITY: + case MT_NOTIFY: + case MT_STATUS: + case MT_STATUS_ENQUIRY: + return(1); + default: + return(0); + } + return(0); +} + +static void +l3dss1_message(struct l3_process *pc, u_char mt) { struct sk_buff *skb; u_char *p; @@ -81,63 +282,226 @@ l3dss1_message(struct PStack *st, u_char mt) if (!(skb = l3_alloc_skb(4))) return; p = skb_put(skb, 4); - MsgHead(p, st->l3.callref, mt); - st->l3.l3l2(st, DL_DATA, skb); + MsgHead(p, pc->callref, mt); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } static void -l3dss1_release_req(struct PStack *st, u_char pr, void *arg) +l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 19); - l3dss1_message(st, MT_RELEASE); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + StopAllL3Timer(pc); + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3dss1_release_cmpl(struct PStack *st, u_char pr, void *arg) +l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int cause = -1; p = skb->data; - st->pa->loc = 0; + pc->para.loc = 0; if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); dev_kfree_skb(skb); - StopAllL3Timer(st); - st->pa->cause = cause; - newl3state(st, 0); - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); + StopAllL3Timer(pc); + pc->para.cause = cause; + newl3state(pc, 0); + pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL); + release_l3_process(pc); +} + +#ifdef EXT_BEARER_CAPS + +u_char *EncodeASyncParams(u_char *p, u_char si2) +{ // 7c 06 88 90 21 42 00 bb + + p[0] = p[1] = 0; p[2] = 0x80; + if (si2 & 32) // 7 data bits + p[2] += 16; + else // 8 data bits + p[2] +=24; + + if (si2 & 16) // 2 stop bits + p[2] += 96; + else // 1 stop bit + p[2] = 32; + + if (si2 & 8) // even parity + p[2] += 2; + else // no parity + p[2] += 3; + + switch (si2 & 0x07) + { + case 0: p[0] = 66; // 1200 bit/s + break; + case 1: p[0] = 88; // 1200/75 bit/s + break; + case 2: p[0] = 87; // 75/1200 bit/s + break; + case 3: p[0] = 67; // 2400 bit/s + break; + case 4: p[0] = 69; // 4800 bit/s + break; + case 5: p[0] = 72; // 9600 bit/s + break; + case 6: p[0] = 73; // 14400 bit/s + break; + case 7: p[0] = 75; // 19200 bit/s + break; + } + return p+3; +} + +u_char EncodeSyncParams(u_char si2, u_char ai) +{ + + switch (si2) + { + case 0: return ai + 2; // 1200 bit/s + case 1: return ai + 24; // 1200/75 bit/s + case 2: return ai + 23; // 75/1200 bit/s + case 3: return ai + 3; // 2400 bit/s + case 4: return ai + 5; // 4800 bit/s + case 5: return ai + 8; // 9600 bit/s + case 6: return ai + 9; // 14400 bit/s + case 7: return ai + 11; // 19200 bit/s + case 8: return ai + 14; // 48000 bit/s + case 9: return ai + 15; // 56000 bit/s + case 15: return ai + 40; // negotiate bit/s + default: break; + } + return ai; +} + + +static u_char DecodeASyncParams(u_char si2, u_char *p) +{ u_char info; + + switch (p[5]) + { + case 66: // 1200 bit/s + break; // si2 bleibt gleich + case 88: // 1200/75 bit/s + si2 += 1; + break; + case 87: // 75/1200 bit/s + si2 += 2; + break; + case 67: // 2400 bit/s + si2 += 3; + break; + case 69: // 4800 bit/s + si2 += 4; + break; + case 72: // 9600 bit/s + si2 += 5; + break; + case 73: // 14400 bit/s + si2 += 6; + break; + case 75: // 19200 bit/s + si2 += 7; + break; + } + + info = p[7] & 0x7f; + if ((info & 16) && (!(info & 8))) // 7 data bits + si2 += 32; // else 8 data bits + if ((info & 96) == 96) // 2 stop bits + si2 += 16; // else 1 stop bit + if ((info & 2) && (!(info & 1))) // even parity + si2 += 8; // else no parity + + return si2; +} + + +static u_char DecodeSyncParams(u_char si2, u_char info) +{ + info &= 0x7f; + switch (info) + { + case 40: // bit/s aushandeln --- hat nicht geklappt, ai wird 165 statt 175! + return si2 + 15; + case 15: // 56000 bit/s --- hat nicht geklappt, ai wird 0 statt 169 ! + return si2 + 9; + case 14: // 48000 bit/s + return si2 + 8; + case 11: // 19200 bit/s + return si2 + 7; + case 9: // 14400 bit/s + return si2 + 6; + case 8: // 9600 bit/s + return si2 + 5; + case 5: // 4800 bit/s + return si2 + 4; + case 3: // 2400 bit/s + return si2 + 3; + case 23: // 75/1200 bit/s + return si2 + 2; + case 24: // 1200/75 bit/s + return si2 + 1; + default: // 1200 bit/s + return si2; + } } +static u_char DecodeSI2(struct sk_buff *skb) +{ u_char *p; //, *pend=skb->data + skb->len; + + if ((p = findie(skb->data, skb->len, 0x7c, 0))) + { + switch (p[4] & 0x0f) + { + case 0x01: if (p[1] == 0x04) // sync. Bitratenadaption + return DecodeSyncParams(160, p[5]); // V.110/X.30 + else if (p[1] == 0x06) // async. Bitratenadaption + return DecodeASyncParams(192, p); // V.110/X.30 + break; + case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption + return DecodeSyncParams(176, p[5]); // V.120 + break; + } + } + return 0; +} + +#endif + + static void -l3dss1_setup_req(struct PStack *st, u_char pr, +l3dss1_setup_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[128]; u_char *p = tmp; u_char channel = 0; - u_char screen = 0; + u_char screen = 0x80; u_char *teln; u_char *msn; + u_char *sub; + u_char *sp; int l; - st->l3.callref = st->pa->callref; - MsgHead(p, st->l3.callref, MT_SETUP); + MsgHead(p, pc->callref, MT_SETUP); /* * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 */ +#ifdef HISAX_EURO_SENDCOMPLETE *p++ = 0xa1; /* complete indicator */ - switch (st->pa->setup.si1) { +#endif + switch (pc->para.setup.si1) { case 1: /* Telephony */ *p++ = 0x4; /* BC-IE-code */ *p++ = 0x3; /* Length */ @@ -157,7 +521,7 @@ l3dss1_setup_req(struct PStack *st, u_char pr, /* * What about info2? Mapping to High-Layer-Compatibility? */ - teln = st->pa->setup.phone; + teln = pc->para.setup.phone; if (*teln) { /* parse number for special things */ if (!isdigit(*teln)) { @@ -179,19 +543,28 @@ l3dss1_setup_req(struct PStack *st, u_char pr, screen = 0x80; break; default: - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "Wrong MSN Code"); + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "Wrong MSN Code"); break; } teln++; } } if (channel) { - *p++ = 0x18; /* channel indicator */ + *p++ = IE_CHANNEL_ID; *p++ = 1; *p++ = channel; } - msn = st->pa->setup.eazmsn; + msn = pc->para.setup.eazmsn; + sub = NULL; + sp = msn; + while (*sp) { + if ('.' == *sp) { + sub = sp; + *sp = 0; + } else + sp++; + } if (*msn) { *p++ = 0x6c; *p++ = strlen(msn) + (screen ? 2 : 1); @@ -204,241 +577,372 @@ l3dss1_setup_req(struct PStack *st, u_char pr, while (*msn) *p++ = *msn++ & 0x7f; } + if (sub) { + *sub++ = '.'; + *p++ = 0x6d; /* Calling party subaddress */ + *p++ = strlen(sub) + 2; + *p++ = 0x80; /* NSAP coded */ + *p++ = 0x50; /* local IDI format */ + while (*sub) + *p++ = *sub++ & 0x7f; + } + sub = NULL; + sp = teln; + while (*sp) { + if ('.' == *sp) { + sub = sp; + *sp = 0; + } else + sp++; + } *p++ = 0x70; *p++ = strlen(teln) + 1; /* Classify as AnyPref. */ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ - while (*teln) *p++ = *teln++ & 0x7f; + if (sub) { + *sub++ = '.'; + *p++ = 0x71; /* Called party subaddress */ + *p++ = strlen(sub) + 2; + *p++ = 0x80; /* NSAP coded */ + *p++ = 0x50; /* local IDI format */ + while (*sub) + *p++ = *sub++ & 0x7f; + } + +#ifdef EXT_BEARER_CAPS + if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) + { // sync. Bitratenadaption, V.110/X.30 + *p++ = 0x7c; *p++ = 0x04; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21; + *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80); + } + else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) + { // sync. Bitratenadaption, V.120 + *p++ = 0x7c; *p++ = 0x05; *p++ = 0x88; *p++ = 0x90; *p++ = 0x28; + *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0); + *p++ = 0x82; + } + else if (pc->para.setup.si2 >= 192) + { // async. Bitratenadaption, V.110/X.30 + *p++ = 0x7c; *p++ = 0x06; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21; + p = EncodeASyncParams(p, pc->para.setup.si2 - 192); + } +#endif l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303); - newl3state(st, 1); - st->l3.l3l2(st, DL_DATA, skb); - + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + newl3state(pc, 1); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } static void -l3dss1_call_proc(struct PStack *st, u_char pr, void *arg) +l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN)) - l3_debug(st, "setup answer without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "setup answer without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb); - newl3state(st, 3); - L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); + newl3state(pc, 3); + L3AddTimer(&pc->timer, T310, CC_T310); + pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL); } static void -l3dss1_setup_ack(struct PStack *st, u_char pr, void *arg) +l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN)) - l3_debug(st, "setup answer without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "setup answer without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb); - newl3state(st, 2); - L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); - st->l3.l3l4(st, CC_MORE_INFO, NULL); + newl3state(pc, 2); + L3AddTimer(&pc->timer, T304, CC_T304); + pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL); } static void -l3dss1_disconnect(struct PStack *st, u_char pr, void *arg) +l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int cause = -1; - StopAllL3Timer(st); + StopAllL3Timer(pc); p = skb->data; - st->pa->loc = 0; + pc->para.loc = 0; if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); dev_kfree_skb(skb); - newl3state(st, 12); - st->pa->cause = cause; - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); + newl3state(pc, 12); + pc->para.cause = cause; + pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL); } static void -l3dss1_connect(struct PStack *st, u_char pr, void *arg) +l3dss1_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - L3DelTimer(&st->l3.timer); /* T310 */ - newl3state(st, 10); - st->l3.l3l4(st, CC_SETUP_CNF, NULL); + L3DelTimer(&pc->timer); /* T310 */ + newl3state(pc, 10); + pc->para.chargeinfo = 0; + pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL); } static void -l3dss1_alerting(struct PStack *st, u_char pr, void *arg) +l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - L3DelTimer(&st->l3.timer); /* T304 */ - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL); } static void -l3dss1_setup(struct PStack *st, u_char pr, void *arg) +l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; + /* This routine is called if here was no SETUP made (checks in dss1up and in + * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code + * It is called after it is veryfied that Layer2 is up. + * The cause value is allready in pc->para.cause + * MT_STATUS_ENQUIRE in the NULL state is handled too + */ + u_char tmp[16]; + u_char *p=tmp; + int l; + struct sk_buff *skb; + + switch (pc->para.cause) { + case 81: /* 0x51 invalid callreference */ + case 96: /* 0x60 mandory IE missing */ + case 101: /* 0x65 incompatible Callstate */ + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = pc->para.cause | 0x80; + break; + default: + printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n"); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + release_l3_process(pc); +} + +static void +l3dss1_setup(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p, *ptmp[8]; + int i; int bcfound = 0; char tmp[80]; struct sk_buff *skb = arg; + /* ETS 300-104 1.3.4 and 1.3.5 + * we need to detect unknown inform. element from 0 to 7 + */ p = skb->data; - st->pa->callref = getcallref(p); - st->l3.callref = 0x80 + st->pa->callref; + for(i = 0; i < 8; i++) + ptmp[i] = skb->data; + if (findie(ptmp[1], skb->len, 0x01, 0) + || findie(ptmp[2], skb->len, 0x02, 0) + || findie(ptmp[3], skb->len, 0x03, 0) + || findie(ptmp[5], skb->len, 0x05, 0) + || findie(ptmp[6], skb->len, 0x06, 0) + || findie(ptmp[7], skb->len, 0x07, 0)) { + /* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE + * cause 0x60 + */ + pc->para.cause = 0x60; + dev_kfree_skb(skb); + if (pc->state == 0) + pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL); + else + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } /* * Channel Identification */ p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if (st->pa->bchannel) + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if (pc->para.bchannel) bcfound++; - else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); + else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); /* * Bearer Capabilities */ p = skb->data; if ((p = findie(p, skb->len, 0x04, 0))) { - st->pa->setup.si2 = 0; + pc->para.setup.si2 = 0; switch (p[2] & 0x1f) { case 0x00: /* Speech */ case 0x10: /* 3.1 Khz audio */ - st->pa->setup.si1 = 1; + pc->para.setup.si1 = 1; break; case 0x08: /* Unrestricted digital information */ - st->pa->setup.si1 = 7; + pc->para.setup.si1 = 7; +/* JIM, 05.11.97 I wanna set service indicator 2 */ +#ifdef EXT_BEARER_CAPS + pc->para.setup.si2 = DecodeSI2(skb); + printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n", + pc->para.setup.si1, pc->para.setup.si2); +#endif break; case 0x09: /* Restricted digital information */ - st->pa->setup.si1 = 2; + pc->para.setup.si1 = 2; break; case 0x11: /* Unrestr. digital information with tones/announcements */ - st->pa->setup.si1 = 3; + pc->para.setup.si1 = 3; break; case 0x18: /* Video */ - st->pa->setup.si1 = 4; + pc->para.setup.si1 = 4; break; default: - st->pa->setup.si1 = 0; + pc->para.setup.si1 = 0; } - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bearer capabilities"); + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bearer capabilities"); + /* ETS 300-104 1.3.3 */ + pc->para.cause = 0x60; + dev_kfree_skb(skb); + if (pc->state == 0) + pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL); + else + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } p = skb->data; if ((p = findie(p, skb->len, 0x70, 0))) - iecpy(st->pa->setup.eazmsn, p, 1); + iecpy(pc->para.setup.eazmsn, p, 1); else - st->pa->setup.eazmsn[0] = 0; + pc->para.setup.eazmsn[0] = 0; p = skb->data; + if ((p = findie(p, skb->len, 0x71, 0))) { + /* Called party subaddress */ + if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) { + tmp[0]='.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.eazmsn, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong called subaddress"); + } + p = skb->data; if ((p = findie(p, skb->len, 0x6c, 0))) { - st->pa->setup.plan = p[2]; + pc->para.setup.plan = p[2]; if (p[2] & 0x80) { - iecpy(st->pa->setup.phone, p, 1); - st->pa->setup.screen = 0; + iecpy(pc->para.setup.phone, p, 1); + pc->para.setup.screen = 0; } else { - iecpy(st->pa->setup.phone, p, 2); - st->pa->setup.screen = p[3]; + iecpy(pc->para.setup.phone, p, 2); + pc->para.setup.screen = p[3]; } } else { - st->pa->setup.phone[0] = 0; - st->pa->setup.plan = 0; - st->pa->setup.screen = 0; + pc->para.setup.phone[0] = 0; + pc->para.setup.plan = 0; + pc->para.setup.screen = 0; + } + p = skb->data; + if ((p = findie(p, skb->len, 0x6d, 0))) { + /* Calling party subaddress */ + if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) { + tmp[0]='.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.phone, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong calling subaddress"); } - SET_SKB_FREE(skb); + dev_kfree_skb(skb); if (bcfound) { - if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) { + if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) { sprintf(tmp, "non-digital call: %s -> %s", - st->pa->setup.phone, - st->pa->setup.eazmsn); - l3_debug(st, tmp); + pc->para.setup.phone, pc->para.setup.eazmsn); + l3_debug(pc->st, tmp); } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } + newl3state(pc, 6); + pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL); + } else + release_l3_process(pc); } static void -l3dss1_reset(struct PStack *st, u_char pr, void *arg) +l3dss1_reset(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 0); + release_l3_process(pc); } static void -l3dss1_setup_rsp(struct PStack *st, u_char pr, +l3dss1_setup_rsp(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 8); - l3dss1_message(st, MT_CONNECT); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313); + newl3state(pc, 8); + l3dss1_message(pc, MT_CONNECT); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); } static void -l3dss1_connect_ack(struct PStack *st, u_char pr, void *arg) +l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - newl3state(st, 10); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); + newl3state(pc, 10); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL); } static void -l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) +l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -446,12 +950,12 @@ l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) int l; u_char cause = 0x10; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; - StopAllL3Timer(st); + StopAllL3Timer(pc); - MsgHead(p, st->l3.callref, MT_DISCONNECT); + MsgHead(p, pc->callref, MT_DISCONNECT); *p++ = IE_CAUSE; *p++ = 0x2; @@ -462,13 +966,13 @@ l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - newl3state(st, 11); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305); + newl3state(pc, 11); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3AddTimer(&pc->timer, T305, CC_T305); } static void -l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) +l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -476,10 +980,10 @@ l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) int l; u_char cause = 0x95; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; - MsgHead(p, st->l3.callref, MT_RELEASE_COMPLETE); + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); *p++ = IE_CAUSE; *p++ = 0x2; @@ -490,13 +994,14 @@ l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - newl3state(st, 0); - st->l3.l3l2(st, DL_DATA, skb); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL); + newl3state(pc, 0); + release_l3_process(pc); } static void -l3dss1_release(struct PStack *st, u_char pr, void *arg) +l3dss1_release(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; @@ -506,38 +1011,45 @@ l3dss1_release(struct PStack *st, u_char pr, void *arg) if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); + p = skb->data; + if ((p = findie(p, skb->len, IE_FACILITY, 0))) { +#ifdef HISAX_DE_AOC + l3dss1_parse_facility(pc,p); +#else + p = NULL; +#endif + } dev_kfree_skb(skb); - StopAllL3Timer(st); - st->pa->cause = cause; - newl3state(st, 0); - l3dss1_message(st, MT_RELEASE_COMPLETE); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + StopAllL3Timer(pc); + pc->para.cause = cause; + l3dss1_message(pc, MT_RELEASE_COMPLETE); + pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL); + newl3state(pc, 0); + release_l3_process(pc); } static void -l3dss1_alert_req(struct PStack *st, u_char pr, +l3dss1_alert_req(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 7); - l3dss1_message(st, MT_ALERTING); + newl3state(pc, 7); + l3dss1_message(pc, MT_ALERTING); } static void -l3dss1_status_enq(struct PStack *st, u_char pr, void *arg) +l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg) { u_char tmp[16]; u_char *p = tmp; int l; struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb); - MsgHead(p, st->l3.callref, MT_STATUS); + MsgHead(p, pc->callref, MT_STATUS); *p++ = IE_CAUSE; *p++ = 0x2; @@ -546,42 +1058,96 @@ l3dss1_status_enq(struct PStack *st, u_char pr, void *arg) *p++ = 0x14; /* CallState */ *p++ = 0x1; - *p++ = st->l3.state & 0x3f; + *p++ = pc->state & 0x3f; + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); +} + +static void +l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg) +{ + /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1... + if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62 */ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb = arg; + + dev_kfree_skb(skb); + + MsgHead(p, pc->callref, MT_STATUS); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = 0x62 | 0x80; /* status sending */ + + *p++ = 0x14; /* CallState */ + *p++ = 0x1; + *p++ = pc->state & 0x3f; l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } static void -l3dss1_t303(struct PStack *st, u_char pr, void *arg) +l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg) { - if (st->l3.n_t303 > 0) { - st->l3.n_t303--; - L3DelTimer(&st->l3.timer); - l3dss1_setup_req(st, pr, arg); + u_char *p; + struct sk_buff *skb = arg; + int callState = 0; + p = skb->data; + + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1== *p++) + callState = *p; + } + if(callState == 0) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 + * set down layer 3 without sending any message + */ + pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL); + newl3state(pc, 0); + release_l3_process(pc); } else { - newl3state(st, 0); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); - st->l3.n_t303 = 1; + pc->st->l3.l3l4(pc, CC_IGNORE, NULL); } } static void -l3dss1_t304(struct PStack *st, u_char pr, void *arg) +l3dss1_t303(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + if (pc->N303 > 0) { + pc->N303--; + L3DelTimer(&pc->timer); + l3dss1_setup_req(pc, pr, arg); + } else { + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL); + release_l3_process(pc); + } +} + +static void +l3dss1_t304(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL); } static void -l3dss1_t305(struct PStack *st, u_char pr, void *arg) +l3dss1_t305(struct l3_process *pc, u_char pr, void *arg) { u_char tmp[16]; u_char *p = tmp; @@ -589,11 +1155,11 @@ l3dss1_t305(struct PStack *st, u_char pr, void *arg) struct sk_buff *skb; u_char cause = 0x90; - L3DelTimer(&st->l3.timer); - if (st->pa->cause > 0) - cause = st->pa->cause; + L3DelTimer(&pc->timer); + if (pc->para.cause > 0) + cause = pc->para.cause | 0x80; - MsgHead(p, st->l3.callref, MT_RELEASE); + MsgHead(p, pc->callref, MT_RELEASE); *p++ = IE_CAUSE; *p++ = 0x2; @@ -604,49 +1170,180 @@ l3dss1_t305(struct PStack *st, u_char pr, void *arg) if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - newl3state(st, 19); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + newl3state(pc, 19); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3dss1_t310(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL); +} + +static void +l3dss1_t313(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL); +} + +static void +l3dss1_t308_1(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 19); + L3DelTimer(&pc->timer); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_2); +} + +static void +l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL); + release_l3_process(pc); } static void -l3dss1_t310(struct PStack *st, u_char pr, void *arg) +l3dss1_restart(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc, CC_DLRL, NULL); + release_l3_process(pc); } static void -l3dss1_t313(struct PStack *st, u_char pr, void *arg) +l3dss1_status(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_CONNECT_ERR, NULL); + u_char *p; + char tmp[64], *t; + int l; + struct sk_buff *skb = arg; + int cause, callState; + + cause = callState = -1; + p = skb->data; + t = tmp; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + l = *p++; + t += sprintf(t,"Status CR %x Cause:", pc->callref); + while (l--) { + cause = *p; + t += sprintf(t," %2x",*p++); + } + } else + sprintf(t,"Status CR %x no Cause", pc->callref); + l3_debug(pc->st, tmp); + p = skb->data; + t = tmp; + t += sprintf(t,"Status state %x ", pc->state); + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1== *p++) { + callState = *p; + t += sprintf(t,"peer state %x" , *p); + } + else + t += sprintf(t,"peer state len error"); + } else + sprintf(t,"no peer state"); + l3_debug(pc->st, tmp); + if(((cause & 0x7f) == 0x6f) && (callState == 0)) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... + * if received MT_STATUS with cause == 0x6f and call + * state == 0, then we must set down layer 3 + */ + l3dss1_release_ind(pc, pr, arg); + } else + dev_kfree_skb(skb); } static void -l3dss1_t308_1(struct PStack *st, u_char pr, void *arg) +l3dss1_facility(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 19); - L3DelTimer(&st->l3.timer); - l3dss1_message(st, MT_RELEASE); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2); + u_char *p; + struct sk_buff *skb = arg; + + p = skb->data; + if ((p = findie(p, skb->len, IE_FACILITY, 0))) { +#ifdef HISAX_DE_AOC + l3dss1_parse_facility(pc,p); +#else + p = NULL; +#endif + } } + + static void -l3dss1_t308_2(struct PStack *st, u_char pr, void *arg) +l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 0); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_RELEASE_ERR, NULL); + u_char tmp[32]; + u_char *p; + u_char ri, chan=0; + int l; + struct sk_buff *skb = arg; + struct l3_process *up; + + newl3state(pc, 2); + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) { + ri = p[2]; + sprintf(tmp, "Restart %x", ri); + } else { + sprintf(tmp, "Restart without restart IE"); + ri = 0x86; + } + l3_debug(pc->st, tmp); + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + chan = p[2] & 3; + sprintf(tmp, "Restart for channel %d", chan); + l3_debug(pc->st, tmp); + } + dev_kfree_skb(skb); + newl3state(pc, 2); + up = pc->st->l3.proc; + while (up) { + if ((ri & 7)==7) + up->st->lli.l4l3(up->st, CC_RESTART, up); + else if (up->para.bchannel == chan) + up->st->lli.l4l3(up->st, CC_RESTART, up); + up = up->next; + } + p = tmp; + MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE); + if (chan) { + *p++ = IE_CHANNEL_ID; + *p++ = 1; + *p++ = chan | 0x80; + } + *p++ = 0x79; /* RESTART Ind */ + *p++ = 1; + *p++ = ri; + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + newl3state(pc, 0); + pc->st->l3.l3l2(pc->st, DL_DATA, skb); } + /* *INDENT-OFF* */ static struct stateentry downstatelist[] = { {SBIT(0), + CC_ESTABLISH, l3dss1_msg_without_setup}, + {SBIT(0), CC_SETUP_REQ, l3dss1_setup_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10), CC_DISCONNECT_REQ, l3dss1_disconnect_req}, @@ -654,6 +1351,8 @@ static struct stateentry downstatelist[] = CC_RELEASE_REQ, l3dss1_release_req}, {ALL_STATES, CC_DLRL, l3dss1_reset}, + {ALL_STATES, + CC_RESTART, l3dss1_restart}, {SBIT(6), CC_IGNORE, l3dss1_reset}, {SBIT(6), @@ -685,63 +1384,216 @@ static struct stateentry datastatelist[] = { {ALL_STATES, MT_STATUS_ENQUIRY, l3dss1_status_enq}, + {ALL_STATES, + MT_FACILITY, l3dss1_facility}, + {SBIT(19), + MT_STATUS, l3dss1_release_ind}, + {ALL_STATES, + MT_STATUS, l3dss1_status}, {SBIT(0) | SBIT(6), MT_SETUP, l3dss1_setup}, {SBIT(1) | SBIT(2), MT_CALL_PROCEEDING, l3dss1_call_proc}, + {SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_CALL_PROCEEDING, l3dss1_status_req}, {SBIT(1), MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_SETUP_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(1) | SBIT(2) | SBIT(3), MT_ALERTING, l3dss1_alerting}, + {SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_ALERTING, l3dss1_status_req}, {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, - {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | - SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) /*| SBIT(19)*/, MT_RELEASE, l3dss1_release}, + {SBIT(19), MT_RELEASE, l3dss1_release_ind}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10), MT_DISCONNECT, l3dss1_disconnect}, + {SBIT(11), + MT_DISCONNECT, l3dss1_release_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), MT_CONNECT, l3dss1_connect}, + {SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_CONNECT, l3dss1_status_req}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(11) | SBIT(19), + MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(8), MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_INVALID, l3dss1_status_req}, }; -/* *INDENT-ON* */ +static int datasllen = sizeof(datastatelist) / sizeof(struct stateentry); -static int datasllen = sizeof(datastatelist) / -sizeof(struct stateentry); +static struct stateentry globalmes_list[] = +{ + {ALL_STATES, + MT_STATUS, l3dss1_status}, + {SBIT(0), + MT_RESTART, l3dss1_global_restart}, +/* {SBIT(1), + MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack}, +*/ +}; +static int globalm_len = sizeof(globalmes_list) / sizeof(struct stateentry); + +#if 0 +static struct stateentry globalcmd_list[] = +{ + {ALL_STATES, + CC_STATUS, l3dss1_status_req}, + {SBIT(0), + CC_RESTART, l3dss1_restart_req}, +}; + +static int globalc_len = sizeof(globalcmd_list) / sizeof(struct stateentry); +#endif +/* *INDENT-ON* */ + +static void +global_handler(struct PStack *st, int mt, struct sk_buff *skb) +{ + int i; + char tmp[64]; + struct l3_process *proc = st->l3.global; + + for (i = 0; i < globalm_len; i++) + if ((mt == globalmes_list[i].primitive) && + ((1 << proc->state) & globalmes_list[i].state)) + break; + if (i == globalm_len) { + dev_kfree_skb(skb); + if (st->l3.debug & L3_DEB_STATE) { + sprintf(tmp, "dss1 global state %d mt %x unhandled", + proc->state, mt); + l3_debug(st, tmp); + } + return; + } else { + if (st->l3.debug & L3_DEB_STATE) { + sprintf(tmp, "dss1 global %d mt %x", + proc->state, mt); + l3_debug(st, tmp); + } + globalmes_list[i].rout(proc, mt, skb); + } +} static void dss1up(struct PStack *st, int pr, void *arg) { - int i, mt; + int i, mt, cr, cause, callState; + char *ptr; struct sk_buff *skb = arg; + struct l3_process *proc; char tmp[80]; if (skb->data[0] != PROTO_DIS_EURO) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d state %d", + sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d", (pr == DL_DATA) ? " " : "(broadcast) ", - skb->data[0], skb->len, st->l3.state); + skb->data[0], skb->len); l3_debug(st, tmp); } - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; } + cr = getcallref(skb->data); mt = skb->data[skb->data[1] + 2]; + if (!cr) { /* Global CallRef */ + global_handler(st, mt, skb); + return; + } else if (cr == -1) { /* Dummy Callref */ + dev_kfree_skb(skb); + return; + } else if (!(proc = getl3proc(st, cr))) { + /* No transaction process exist, that means no call with + * this callreference is active + */ + if (mt == MT_SETUP) { + /* Setup creates a new transaction process */ + if (!(proc = new_l3_process(st, cr))) { + /* May be to answer with RELEASE_COMPLETE and + * CAUSE 0x2f "Resource unavailable", but this + * need a new_l3_process too ... arghh + */ + dev_kfree_skb(skb); + return; + } + } else if (mt == MT_STATUS) { + cause = 0; + if((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + cause = *ptr & 0x7f; + } + callState = 0; + if((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + callState = *ptr; + } + if (callState == 0) { + /* ETS 300-104 part 2.4.1 + * if setup has not been made and a message type + * MT_STATUS is received with call state == 0, + * we must send nothing + */ + dev_kfree_skb(skb); + return; + } else { + /* ETS 300-104 part 2.4.2 + * if setup has not been made and a message type + * MT_STATUS is received with call state != 0, + * we must send MT_RELEASE_COMPLETE cause 101 + */ + dev_kfree_skb(skb); + if ((proc = new_l3_process(st, cr))) { + proc->para.cause = 0x65; /* 101 */ + proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL); + } + return; + } + } else if (mt == MT_RELEASE_COMPLETE){ + dev_kfree_skb(skb); + return; + } else { + /* ETS 300-104 part 2 + * if setup has not been made and a message type + * (except MT_SETUP and RELEASE_COMPLETE) is received, + * we must send MT_RELEASE_COMPLETE cause 81 */ + dev_kfree_skb(skb); + if ((proc = new_l3_process(st, cr))) { + proc->para.cause = 0x51; /* 81 */ + proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL); + } + return; + } + } else if (!l3dss1_check_messagetype_validity(mt)) { + /* ETS 300-104 7.4.2, 8.4.2, 10.3.2, 11.4.2, 12.4.2, 13.4.2, + * 14.4.2... + * if setup has been made and invalid message type is received, + * we must send MT_STATUS cause 0x62 + */ + mt = MT_INVALID; /* sorry, not clean, but do the right thing ;-) */ + } + for (i = 0; i < datasllen; i++) if ((mt == datastatelist[i].primitive) && - ((1 << st->l3.state) & datastatelist[i].state)) + ((1 << proc->state) & datastatelist[i].state)) break; if (i == datasllen) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "dss1up%sstate %d mt %x unhandled", (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + proc->state, mt); l3_debug(st, tmp); } return; @@ -749,36 +1601,55 @@ dss1up(struct PStack *st, int pr, void *arg) if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "dss1up%sstate %d mt %x", (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + proc->state, mt); l3_debug(st, tmp); } - datastatelist[i].rout(st, pr, skb); + datastatelist[i].rout(proc, pr, skb); } } static void dss1down(struct PStack *st, int pr, void *arg) { - int i; + int i, cr; + struct l3_process *proc; + struct Channel *chan; char tmp[80]; + if (CC_SETUP_REQ == pr) { + chan = arg; + cr = newcallref(); + cr |= 0x80; + if ((proc = new_l3_process(st, cr))) { + proc->chan = chan; + chan->proc = proc; + proc->para.setup = chan->setup; + proc->callref = cr; + } + } else { + proc = arg; + } + if (!proc) { + printk(KERN_ERR "HiSax internal error dss1down without proc\n"); + return; + } for (i = 0; i < downsllen; i++) if ((pr == downstatelist[i].primitive) && - ((1 << st->l3.state) & downstatelist[i].state)) + ((1 << proc->state) & downstatelist[i].state)) break; if (i == downsllen) { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "dss1down state %d prim %d unhandled", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } } else { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "dss1down state %d prim %d", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } - downstatelist[i].rout(st, pr, arg); + downstatelist[i].rout(proc, pr, arg); } } @@ -787,20 +1658,20 @@ setstack_dss1(struct PStack *st) { char tmp[64]; - st->l4.l4l3 = dss1down; + st->lli.l4l3 = dss1down; st->l2.l2l3 = dss1up; - st->l3.t303 = 4000; - st->l3.t304 = 30000; - st->l3.t305 = 30000; - st->l3.t308 = 4000; - st->l3.t310 = 30000; - st->l3.t313 = 4000; - st->l3.t318 = 4000; - st->l3.t319 = 4000; - st->l3.n_t303 = 1; - - if (st->l3.channr & 1) { - strcpy(tmp, dss1_revision); - printk(KERN_NOTICE "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp)); + st->l3.N303 = 1; + if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { + printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n"); + } else { + st->l3.global->state = 0; + st->l3.global->callref = 0; + st->l3.global->next = NULL; + st->l3.global->debug = L3_DEB_WARN; + st->l3.global->st = st; + st->l3.global->N303 = 1; + L3InitTimer(st->l3.global, &st->l3.global->timer); } + strcpy(tmp, dss1_revision); + printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp)); } diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h new file mode 100644 index 000000000..8508c3109 --- /dev/null +++ b/drivers/isdn/hisax/l3dss1.h @@ -0,0 +1,71 @@ +/* $Id: l3dss1.h,v 1.5 1998/02/02 13:34:30 keil Exp $ + * + * DSS1 (Euro) D-channel protocol defines + * + * $Log: l3dss1.h,v $ + * Revision 1.5 1998/02/02 13:34:30 keil + * Support australian Microlink net and german AOCD + * + * Revision 1.4 1997/10/29 19:07:54 keil + * changes for 2.1 + * + * Revision 1.3 1997/08/07 17:44:37 keil + * Fix RESTART + * + * Revision 1.2 1997/08/03 14:36:34 keil + * Implement RESTART procedure + * + * Revision 1.1 1997/07/27 21:08:38 keil + * new + * + * + * + */ +#define T303 4000 +#define T304 30000 +#define T305 30000 +#define T308 4000 +#define T310 30000 +#define T313 4000 +#define T318 4000 +#define T319 4000 + +/* + * Message-Types + */ + +#define MT_ALERTING 0x01 +#define MT_CALL_PROCEEDING 0x02 +#define MT_CONNECT 0x07 +#define MT_CONNECT_ACKNOWLEDGE 0x0f +#define MT_PROGRESS 0x03 +#define MT_SETUP 0x05 +#define MT_SETUP_ACKNOWLEDGE 0x0d +#define MT_RESUME 0x26 +#define MT_RESUME_ACKNOWLEDGE 0x2e +#define MT_RESUME_REJECT 0x22 +#define MT_SUSPEND 0x25 +#define MT_SUSPEND_ACKNOWLEDGE 0x2d +#define MT_SUSPEND_REJECT 0x21 +#define MT_USER_INFORMATION 0x20 +#define MT_DISCONNECT 0x45 +#define MT_RELEASE 0x4d +#define MT_RELEASE_COMPLETE 0x5a +#define MT_RESTART 0x46 +#define MT_RESTART_ACKNOWLEDGE 0x4e +#define MT_SEGMENT 0x60 +#define MT_CONGESTION_CONTROL 0x79 +#define MT_INFORMATION 0x7b +#define MT_FACILITY 0x62 +#define MT_NOTIFY 0x6e +#define MT_STATUS 0x7d +#define MT_STATUS_ENQUIRY 0x75 + +#define MT_INVALID 0xff + +#define IE_CAUSE 0x08 +#define IE_BEARER 0x04 +#define IE_FACILITY 0x1c +#define IE_CALL_STATE 0x14 +#define IE_CHANNEL_ID 0x18 +#define IE_RESTART_IND 0x79 diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c new file mode 100644 index 000000000..b3e9819b2 --- /dev/null +++ b/drivers/isdn/hisax/lmgr.c @@ -0,0 +1,58 @@ +/* $Id: lmgr.c,v 1.2 1997/10/29 19:09:34 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * Layermanagement module + * + * $Log: lmgr.c,v $ + * Revision 1.2 1997/10/29 19:09:34 keil + * new L1 + * + * Revision 1.1 1997/06/26 11:17:25 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" + +static void +error_handling_dchan(struct PStack *st, int Error) +{ + switch (Error) { + case 'C': + case 'D': + case 'G': + case 'H': + st->l2.l2tei(st, MDL_ERROR_REQ, NULL); + break; + } +} + +static void +hisax_manager(struct PStack *st, int pr, void *arg) +{ + char tm[32], str[256]; + int Code; + + switch (pr) { + case MDL_ERROR_IND: + Code = (int) arg; + jiftime(tm, jiffies); + sprintf(str, "%s manager: MDL_ERROR %c %s\n", tm, + Code, test_bit(FLG_LAPD, &st->l2.flag) ? + "D-channel" : "B-channel"); + HiSax_putstatus(st->l1.hardware, str); + if (test_bit(FLG_LAPD, &st->l2.flag)) + error_handling_dchan(st, Code); + break; + } +} + +void +setstack_manager(struct PStack *st) +{ + st->ma.layer = hisax_manager; +} diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c new file mode 100644 index 000000000..8bf4757c9 --- /dev/null +++ b/drivers/isdn/hisax/mic.c @@ -0,0 +1,284 @@ +/* $Id: mic.c,v 1.6 1998/02/17 15:39:57 keil Exp $ + + * mic.c low level stuff for mic cards + * + * Copyright (C) 1997 + * + * Author Stephan von Krawczynski <skraw@ithnet.com> + * + * + * $Log: mic.c,v $ + * Revision 1.6 1998/02/17 15:39:57 keil + * fix reset problem + * + * Revision 1.5 1998/02/02 13:29:43 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:51 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:09:11 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:51:17 keil + * New files + * + * Revision 1.1.2.1 1997/10/17 22:10:54 keil + * new files on 2.0 + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *mic_revision = "$Revision: 1.6 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define MIC_ISAC 2 +#define MIC_HSCX 1 +#define MIC_ADR 7 + +/* CARD_ADR (Write) */ +#define MIC_RESET 0x3 /* same as DOS driver */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.mic.adr, + cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.mic.adr, + cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \ + cs->hw.mic.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \ + cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \ + cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \ + cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "mic: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_mic(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.mic.cfg_reg) + release_region(cs->hw.mic.cfg_reg, bytecnt); +} + +static int +mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_io_mic(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &mic_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscx(cs); /* /RTSA := ISAC RST */ + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_mic(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, mic_revision); + printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_MIC) + return (0); + + bytecnt = 8; + cs->hw.mic.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR; + cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC; + cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX; + + if (check_region((cs->hw.mic.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.mic.cfg_reg, + cs->hw.mic.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn"); + } + + printk(KERN_INFO + "mic: defined at 0x%x IRQ %d\n", + cs->hw.mic.cfg_reg, + cs->irq); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &mic_card_msg; + ISACVersion(cs, "mic:"); + if (HscxVersion(cs, "mic:")) { + printk(KERN_WARNING + "mic: wrong HSCX versions check IO address\n"); + release_io_mic(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c new file mode 100644 index 000000000..0686598e2 --- /dev/null +++ b/drivers/isdn/hisax/netjet.c @@ -0,0 +1,1108 @@ +/* $Id: netjet.c,v 1.3 1998/02/12 23:08:05 keil Exp $ + + * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Traverse Technologie Australia for documents and informations + * + * + * $Log: netjet.c,v $ + * Revision 1.3 1998/02/12 23:08:05 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.2 1998/02/02 13:32:06 keil + * New + * + * + * + */ + +#define __NO_VERSION__ +#include <linux/config.h> +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/interrupt.h> +#define fcstab ppp_crc16_table +#include <linux/ppp_defs.h> +extern __u16 ppp_crc16_table[256]; /* from ppp code */ + +extern const char *CardType[]; + +const char *NETjet_revision = "$Revision: 1.3 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +/* PCI stuff */ +#define PCI_VENDOR_TRAVERSE_TECH 0xe159 +#define PCI_NETJET_ID 0x0001 + +#define NETJET_CTRL 0x00 +#define NETJET_DMACTRL 0x01 +#define NETJET_AUXCTRL 0x02 +#define NETJET_AUXDATA 0x03 +#define NETJET_IRQMASK0 0x04 +#define NETJET_IRQMASK1 0x05 +#define NETJET_IRQSTAT0 0x06 +#define NETJET_IRQSTAT1 0x07 +#define NETJET_DMA_READ_START 0x08 +#define NETJET_DMA_READ_IRQ 0x0c +#define NETJET_DMA_READ_END 0x10 +#define NETJET_DMA_READ_ADR 0x14 +#define NETJET_DMA_WRITE_START 0x18 +#define NETJET_DMA_WRITE_IRQ 0x1c +#define NETJET_DMA_WRITE_END 0x20 +#define NETJET_DMA_WRITE_ADR 0x24 +#define NETJET_PULSE_CNT 0x28 + +#define NETJET_ISAC_OFF 0xc0 +#define NETJET_ISACIRQ 0x10 + +#define NETJET_DMA_SIZE 512 + +#define HDLC_ZERO_SEARCH 0 +#define HDLC_FLAG_SEARCH 1 +#define HDLC_FLAG_FOUND 2 +#define HDLC_FRAME_FOUND 3 +#define HDLC_NULL 4 +#define HDLC_PART 5 +#define HDLC_FULL 6 + +#define HDLC_FLAG_VALUE 0x7e + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + long flags; + u_char ret; + + save_flags(flags); + cli(); + cs->hw.njet.auxd &= 0xfc; + cs->hw.njet.auxd |= (offset>>4) & 3; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2)); + restore_flags(flags); + return(ret); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + long flags; + + save_flags(flags); + cli(); + cs->hw.njet.auxd &= 0xfc; + cs->hw.njet.auxd |= (offset>>4) & 3; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value); + restore_flags(flags); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + cs->hw.njet.auxd &= 0xfc; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + insb(cs->hw.njet.isac, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + cs->hw.njet.auxd &= 0xfc; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + outsb(cs->hw.njet.isac, data, size); +} + +void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill) +{ + u_int mask=0x000000ff, val = 0, *p=pos; + u_int i; + + val |= fill; + if (chan) { + val <<= 8; + mask <<= 8; + } + mask ^= 0xffffffff; + for (i=0; i<cnt; i++) { + *p &= mask; + *p++ |= val; + if (p > bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } +} + +void +mode_tiger(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + char tmp[64]; + + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "Tiger mode %d bchan %d/%d", + mode, bc, bcs->channel); + debugl1(cs, tmp); + } + bcs->mode = mode; + bcs->channel = bc; + switch (mode) { + case (L1_MODE_NULL): + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, bc, 0xff); + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "Tiger stat rec %d/%d send %d", + bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err, + bcs->hw.tiger.s_tot); + debugl1(cs, tmp); + } + if ((cs->bcs[0].mode == L1_MODE_NULL) && + (cs->bcs[1].mode == L1_MODE_NULL)) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } + break; + case (L1_MODE_TRANS): + break; + case (L1_MODE_HDLC): + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, bc, 0xff); + bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH; + bcs->hw.tiger.r_tot = 0; + bcs->hw.tiger.r_bitcnt = 0; + bcs->hw.tiger.r_one = 0; + bcs->hw.tiger.r_err = 0; + bcs->hw.tiger.s_tot = 0; + if (! cs->hw.njet.dmactrl) { + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, !bc, 0xff); + cs->hw.njet.dmactrl = 1; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x3f); + } + bcs->hw.tiger.sendp = bcs->hw.tiger.send; + bcs->hw.tiger.free = NETJET_DMA_SIZE; + test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); + break; + } + if (cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "tiger: set %x %x %x %x/%x pulse=%d", + bytein(cs->hw.njet.base + NETJET_DMACTRL), + bytein(cs->hw.njet.base + NETJET_IRQMASK0), + bytein(cs->hw.njet.base + NETJET_IRQSTAT0), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); + debugl1(cs, tmp); + } +} + +static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off) +{ + return(5); +} + +static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value) +{ +} + +static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) { + char tmp[128]; + char *t = tmp; + int i=count,j; + u_char *p = buf; + + t += sprintf(t, "tiger %s(%4d)", s, count); + while (i>0) { + if (i>16) + j=16; + else + j=i; + QuickHex(t, p, j); + debugl1(cs, tmp); + p += j; + i -= j; + t = tmp; + t += sprintf(t, "tiger %s ", s); + } +} + +#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \ + bitcnt++;\ + s_val >>= 1;\ + if (val & 1) {\ + s_one++;\ + s_val |= 0x80;\ + } else {\ + s_one = 0;\ + s_val &= 0x7f;\ + }\ + if (bitcnt==8) {\ + bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\ + bitcnt = 0;\ + }\ + if (s_one == 5) {\ + s_val >>= 1;\ + s_val &= 0x7f;\ + bitcnt++;\ + s_one = 0;\ + }\ + if (bitcnt==8) {\ + bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\ + bitcnt = 0;\ + }\ + val >>= 1;\ + } + +static void make_raw_data(struct BCState *bcs) { + register u_int i,s_cnt=0; + register u_char j; + register u_char val; + register u_char s_one = 0; + register u_char s_val = 0; + register u_char bitcnt = 0; + u_int fcs; + char tmp[64]; + + + bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE; + fcs = PPP_INITFCS; + for (i=0; i<bcs->hw.tiger.tx_skb->len; i++) { + val = bcs->hw.tiger.tx_skb->data[i]; + fcs = PPP_FCS (fcs, val); + MAKE_RAW_BYTE; + } + fcs ^= 0xffff; + val = fcs & 0xff; + MAKE_RAW_BYTE; + val = (fcs>>8) & 0xff; + MAKE_RAW_BYTE; + val = HDLC_FLAG_VALUE; + for (j=0; j<8; j++) { + bitcnt++; + s_val >>= 1; + if (val & 1) + s_val |= 0x80; + else + s_val &= 0x7f; + if (bitcnt==8) { + bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + bitcnt = 0; + } + val >>= 1; + } + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger make_raw: in %d out %d.%d", + bcs->hw.tiger.tx_skb->len, s_cnt, bitcnt); + debugl1(bcs->cs,tmp); + } + if (bitcnt) { + while (8>bitcnt++) { + s_val >>= 1; + s_val |= 0x80; + } + bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + } + bcs->hw.tiger.sendcnt = s_cnt; + bcs->tx_cnt -= bcs->hw.tiger.tx_skb->len; + bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf; +} + +static void got_frame(struct BCState *bcs, int count) { + struct sk_buff *skb; + + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "TIGER: receive out of memory\n"); + else { + memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME) + printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec"); +} + + + +static void read_raw(struct BCState *bcs, u_int *buf, int cnt){ + int i; + register u_char j; + register u_char val; + u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -1; + register u_char state = bcs->hw.tiger.r_state; + register u_char r_one = bcs->hw.tiger.r_one; + register u_char r_val = bcs->hw.tiger.r_val; + register u_int bitcnt = bcs->hw.tiger.r_bitcnt; + u_int *p = buf; + char tmp[64]; + + for (i=0;i<cnt;i++) { + val = bcs->channel ? ((*p>>8) & 0xff) : (*p & 0xff); + p++; + if (p > pend) + p = bcs->hw.tiger.rec; + if (val == 0xff) { + state = HDLC_ZERO_SEARCH; + bcs->hw.tiger.r_tot++; + bitcnt = 0; + r_one = 0; + continue; + } + for (j=0;j<8;j++) { + if (state == HDLC_ZERO_SEARCH) { + if (val & 1) { + r_one++; + } else { + r_one=0; + state= HDLC_FLAG_SEARCH; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger read_raw: zBit(%d,%d,%d) %x", + bcs->hw.tiger.r_tot,i,j,val); + debugl1(bcs->cs,tmp); + } + } + } else if (state == HDLC_FLAG_SEARCH) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } + } else { + if (r_one==6) { + bitcnt=0; + r_val=0; + state=HDLC_FLAG_FOUND; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger read_raw: flag(%d,%d,%d) %x", + bcs->hw.tiger.r_tot,i,j,val); + debugl1(bcs->cs,tmp); + } + } + r_one=0; + } + } else if (state == HDLC_FLAG_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } else { + r_val >>= 1; + r_val |= 0x80; + bitcnt++; + } + } else { + if (r_one==6) { + bitcnt=0; + r_val=0; + r_one=0; + val >>= 1; + continue; + } else if (r_one!=5) { + r_val >>= 1; + r_val &= 0x7f; + bitcnt++; + } + r_one=0; + } + if ((state != HDLC_ZERO_SEARCH) && + !(bitcnt & 7)) { + state=HDLC_FRAME_FOUND; + bcs->hw.tiger.r_fcs = PPP_INITFCS; + bcs->hw.tiger.rcvbuf[0] = r_val; + bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val); + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x", + bcs->hw.tiger.r_tot,i,j,r_val,val, + bcs->cs->hw.njet.irqstat0); + debugl1(bcs->cs,tmp); + } + } + } else if (state == HDLC_FRAME_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + bitcnt=0; + } else { + r_val >>= 1; + r_val |= 0x80; + bitcnt++; + } + } else { + if (r_one==6) { + r_val=0; + r_one=0; + bitcnt++; + if (bitcnt & 7) { + debugl1(bcs->cs, "tiger: frame not byte aligned"); + state=HDLC_FLAG_SEARCH; + bcs->hw.tiger.r_err++; + } else { + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger frame end(%d,%d): fcs(%x) i %x", + i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0); + debugl1(bcs->cs, tmp); + } + if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) { + got_frame(bcs, (bitcnt>>3)-3); + } else + if (bcs->cs->debug) { + debugl1(bcs->cs, "tiger FCS error"); + printframe(bcs->cs, bcs->hw.tiger.rcvbuf, + (bitcnt>>3)-1, "rec"); + bcs->hw.tiger.r_err++; + } + state=HDLC_FLAG_FOUND; + } + bitcnt=0; + } else if (r_one==5) { + val >>= 1; + r_one=0; + continue; + } else { + r_val >>= 1; + r_val &= 0x7f; + bitcnt++; + } + r_one=0; + } + if ((state == HDLC_FRAME_FOUND) && + !(bitcnt & 7)) { + if ((bitcnt>>3)>=HSCX_BUFMAX) { + debugl1(bcs->cs, "tiger: frame to big"); + r_val=0; + state=HDLC_FLAG_SEARCH; + bcs->hw.tiger.r_err++; + } else { + bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val; + bcs->hw.tiger.r_fcs = + PPP_FCS (bcs->hw.tiger.r_fcs, r_val); + } + } + } + val >>= 1; + } + bcs->hw.tiger.r_tot++; + } + bcs->hw.tiger.r_state = state; + bcs->hw.tiger.r_one = r_one; + bcs->hw.tiger.r_val = r_val; + bcs->hw.tiger.r_bitcnt = bitcnt; +} + +static void read_tiger(struct IsdnCardState *cs) { + u_int *p; + int cnt = NETJET_DMA_SIZE/2; + + if (cs->hw.njet.irqstat0 & 4) + p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1; + else + p = cs->bcs[0].hw.tiger.rec + cnt - 1; + if (cs->bcs[0].mode == L1_MODE_HDLC) + read_raw(cs->bcs, p, cnt); + if (cs->bcs[1].mode == L1_MODE_HDLC) + read_raw(cs->bcs + 1, p, cnt); + cs->hw.njet.irqstat0 &= 0xf3; +} + +static void write_raw(struct BCState *bcs, u_int *buf, int cnt); + +static void fill_dma(struct BCState *bcs) +{ + char tmp[64]; + register u_int *p, *sp; + register int cnt; + + if (!bcs->hw.tiger.tx_skb) + return; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger fill_dma1: c%d %4x", bcs->channel, + bcs->Flag); + debugl1(bcs->cs,tmp); + } + if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag)) + return; + make_raw_data(bcs); + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger fill_dma2: c%d %4x", bcs->channel, + bcs->Flag); + debugl1(bcs->cs,tmp); + } + if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) { + write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free); + } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) { + p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR)); + sp = bcs->hw.tiger.sendp; + if (p == bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send -1; + if (sp == bcs->hw.tiger.s_end) + sp = bcs->hw.tiger.send -1; + cnt = p - sp; + if (cnt <0) { + write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free); + } else { + p++; + cnt++; + if (p > bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + p++; + cnt++; + if (p > bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + write_raw(bcs, p, bcs->hw.tiger.free - cnt); + } + } else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) { + p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR)); + cnt = bcs->hw.tiger.s_end - p; + if (cnt < 2) { + p = bcs->hw.tiger.send + 1; + cnt = NETJET_DMA_SIZE/2 - 2; + } else { + p++; + p++; + if (cnt <= (NETJET_DMA_SIZE/2)) + cnt += NETJET_DMA_SIZE/2; + cnt--; + cnt--; + } + write_raw(bcs, p, cnt); + } + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger fill_dma3: c%d %4x", bcs->channel, + bcs->Flag); + debugl1(bcs->cs,tmp); + } +} + +static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { + u_int mask, val, *p=buf; + u_int i, s_cnt; + char tmp[64]; + + if (cnt <= 0) + return; + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) { + if (bcs->hw.tiger.sendcnt> cnt) { + s_cnt = cnt; + bcs->hw.tiger.sendcnt -= cnt; + } else { + s_cnt = bcs->hw.tiger.sendcnt; + bcs->hw.tiger.sendcnt = 0; + } + if (bcs->channel) + mask = 0xffff00ff; + else + mask = 0xffffff00; + for (i=0; i<s_cnt; i++) { + val = bcs->channel ? ((bcs->hw.tiger.sp[i] <<8) & 0xff00) : + (bcs->hw.tiger.sp[i]); + *p &= mask; + *p++ |= val; + if (p>bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } + bcs->hw.tiger.s_tot += s_cnt; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel, + (u_int)buf, (u_int)p, s_cnt, cnt, bcs->hw.tiger.sendcnt, + bcs->cs->hw.njet.irqstat0); + debugl1(bcs->cs,tmp); + } + if (bcs->cs->debug & L1_DEB_HSCX_FIFO) + printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd"); + bcs->hw.tiger.sp += s_cnt; + bcs->hw.tiger.sendp = p; + if (!bcs->hw.tiger.sendcnt) { + if (!bcs->hw.tiger.tx_skb) { + sprintf(tmp,"tiger write_raw: NULL skb s_cnt %d", s_cnt); + debugl1(bcs->cs, tmp); + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->hw.tiger.tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.tiger.tx_skb->len); + dev_kfree_skb(bcs->hw.tiger.tx_skb); + bcs->hw.tiger.tx_skb = NULL; + } + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->hw.tiger.free = cnt - s_cnt; + if (bcs->hw.tiger.free > (NETJET_DMA_SIZE/2)) + test_and_set_bit(BC_FLG_HALF, &bcs->Flag); + else { + test_and_clear_bit(BC_FLG_HALF, &bcs->Flag); + test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag); + } + if ((bcs->hw.tiger.tx_skb = skb_dequeue(&bcs->squeue))) { + fill_dma(bcs); + } else { + mask ^= 0xffffffff; + if (s_cnt < cnt) { + for (i=s_cnt; i<cnt;i++) { + *p++ |= mask; + if (p>bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp, "tiger write_raw: fill rest %d", + cnt - s_cnt); + debugl1(bcs->cs,tmp); + } + } + bcs->event |= 1 << B_XMTBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + } else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) { + test_and_set_bit(BC_FLG_HALF, &bcs->Flag); + fill_mem(bcs, buf, cnt, bcs->channel, 0xff); + bcs->hw.tiger.free += cnt; + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger write_raw: fill half"); + debugl1(bcs->cs,tmp); + } + } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) { + test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); + fill_mem(bcs, buf, cnt, bcs->channel, 0xff); + if (bcs->cs->debug & L1_DEB_HSCX) { + sprintf(tmp,"tiger write_raw: fill full"); + debugl1(bcs->cs,tmp); + } + } +} + +static void write_tiger(struct IsdnCardState *cs) { + u_int *p, cnt = NETJET_DMA_SIZE/2; + + if (cs->hw.njet.irqstat0 & 1) + p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + else + p = cs->bcs[0].hw.tiger.send + cnt - 1; + if (cs->bcs[0].mode == L1_MODE_HDLC) + write_raw(cs->bcs, p, cnt); + if (cs->bcs[1].mode == L1_MODE_HDLC) + write_raw(cs->bcs + 1, p, cnt); + cs->hw.njet.irqstat0 &= 0xfc; +} + +static void +tiger_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA_REQ): + save_flags(flags); + cli(); + if (st->l1.bcs->hw.tiger.tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->hw.tiger.tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL_IND): + if (st->l1.bcs->hw.tiger.tx_skb) { + printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); + st->l1.bcs->hw.tiger.tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL_REQ): + if (!st->l1.bcs->hw.tiger.tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL_CNF, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + } +} + +void +close_tigerstate(struct BCState *bcs) +{ + struct sk_buff *skb; + + mode_tiger(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.tiger.rcvbuf) { + kfree(bcs->hw.tiger.rcvbuf); + bcs->hw.tiger.rcvbuf = NULL; + } + if (bcs->hw.tiger.sendbuf) { + kfree(bcs->hw.tiger.sendbuf); + bcs->hw.tiger.sendbuf = NULL; + } + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb); + } + if (bcs->hw.tiger.tx_skb) { + dev_kfree_skb(bcs->hw.tiger.tx_skb); + bcs->hw.tiger.tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_tigerstate(struct IsdnCardState *cs, int bc) +{ + struct BCState *bcs = cs->bcs + bc; + + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_KERNEL))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.rcvbuf\n"); + return (1); + } + if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_KERNEL))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.sendbuf\n"); + return (1); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->hw.tiger.tx_skb = NULL; + bcs->hw.tiger.sendcnt = 0; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +static void +tiger_manl1(struct PStack *st, int pr, + void *arg) +{ + switch (pr) { + case (PH_ACTIVATE_REQ): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc); + st->l1.l1man(st, PH_ACTIVATE_CNF, NULL); + break; + case (PH_DEACTIVATE_REQ): + if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) + mode_tiger(st->l1.bcs, 0, 0); + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + break; + } +} + +int +setstack_tiger(struct PStack *st, struct BCState *bcs) +{ + if (open_tigerstate(st->l1.hardware, bcs->channel)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = tiger_l2l1; + st->ma.manl1 = tiger_manl1; + setstack_manager(st); + bcs->st = st; + return (0); +} + + +__initfunc(void +inittiger(struct IsdnCardState *cs)) +{ + char tmp[128]; + + if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + GFP_KERNEL | GFP_DMA))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.send\n"); + return; + } + cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE/2 - 1; + cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send; + cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq; + cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end; + + memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int)); + sprintf(tmp, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send, + (u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1)); + debugl1(cs, tmp); + outl(virt_to_bus(cs->bcs[0].hw.tiger.send), + cs->hw.njet.base + NETJET_DMA_READ_START); + outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq), + cs->hw.njet.base + NETJET_DMA_READ_IRQ); + outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end), + cs->hw.njet.base + NETJET_DMA_READ_END); + if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + GFP_KERNEL | GFP_DMA))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.rec\n"); + return; + } + sprintf(tmp, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec, + (u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1)); + debugl1(cs, tmp); + cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec; + memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int)); + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec), + cs->hw.njet.base + NETJET_DMA_WRITE_START); + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE/2 - 1), + cs->hw.njet.base + NETJET_DMA_WRITE_IRQ); + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1), + cs->hw.njet.base + NETJET_DMA_WRITE_END); + sprintf(tmp, "tiger: dmacfg %x/%x pulse=%d", + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); + debugl1(cs, tmp); + cs->hw.njet.last_is0 = 0; + cs->bcs[0].BC_SetStack = setstack_tiger; + cs->bcs[1].BC_SetStack = setstack_tiger; + cs->bcs[0].BC_Close = close_tigerstate; + cs->bcs[1].BC_Close = close_tigerstate; +} + +void +releasetiger(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.tiger.send) { + kfree(cs->bcs[0].hw.tiger.send); + cs->bcs[0].hw.tiger.send = NULL; + } + if (cs->bcs[1].hw.tiger.send) { + cs->bcs[1].hw.tiger.send = NULL; + } + if (cs->bcs[0].hw.tiger.rec) { + kfree(cs->bcs[0].hw.tiger.rec); + cs->bcs[0].hw.tiger.rec = NULL; + } + if (cs->bcs[1].hw.tiger.rec) { + cs->bcs[1].hw.tiger.rec = NULL; + } +} + +static void +netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval, stat = 1; + char tmp[128]; + + if (!cs) { + printk(KERN_WARNING "NETjet: Spurious interrupt!\n"); + return; + } + if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) & + NETJET_ISACIRQ)) { + val = ReadISAC(cs, ISAC_ISTA); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "tiger: i1 %x %x", sval, val); + debugl1(cs, tmp); + } + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + } + if ((cs->hw.njet.irqstat0 = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) { +/* sprintf(tmp, "tiger: ist0 %x %x %x %x/%x pulse=%d", + sval, + bytein(cs->hw.njet.base + NETJET_DMACTRL), + bytein(cs->hw.njet.base + NETJET_IRQMASK0), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); + debugl1(cs, tmp); +*/ + if (cs->hw.njet.last_is0 & cs->hw.njet.irqstat0 & 0xf) { + sprintf(tmp, "tiger: ist0 %x->%x irq lost", + cs->hw.njet.last_is0, cs->hw.njet.irqstat0); + debugl1(cs, tmp); + } + cs->hw.njet.last_is0 = cs->hw.njet.irqstat0; +/* cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30; +*/ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0); +/* byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0); +*/ if (cs->hw.njet.irqstat0 & 0x0c) + read_tiger(cs); + if (cs->hw.njet.irqstat0 & 0x03) + write_tiger(cs); + } +/* if (!testcnt--) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } +*/ if (stat & 2) { + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); + } +} + +static void +reset_netjet(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + restore_flags(flags); + cs->hw.njet.auxd = 0; + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); +} + +void +release_io_netjet(struct IsdnCardState *cs) +{ + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0); + releasetiger(cs); + release_region(cs->hw.njet.base, 256); +} + + +static int +NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_netjet(cs); + return(0); + case CARD_RELEASE: + release_io_netjet(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &netjet_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inittiger(cs); + clear_pending_isac_ints(cs); + initisac(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + + + +static int pci_index __initdata = 0; + +__initfunc(int +setup_netjet(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr, found; +#endif + + strcpy(tmp, NETjet_revision); + printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NETJET) + return(0); +#if CONFIG_PCI + found = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH, + PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + found = 1; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + if (found) + break; + } + if (!found) { + printk(KERN_WARNING "NETjet: No PCI card found\n"); + return(0); + } + if (!pci_irq) { + printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n"); + return(0); + } + if (!pci_ioaddr) { + printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.njet.base = pci_ioaddr; + cs->hw.njet.auxa = pci_ioaddr + NETJET_AUXDATA; + cs->hw.njet.isac = pci_ioaddr | NETJET_ISAC_OFF; + cs->irq = pci_irq; + bytecnt = 256; +#else + printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n"); + printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO + "NETjet: PCI card configured at 0x%x IRQ %d\n", + cs->hw.njet.base, cs->irq); + if (check_region(cs->hw.njet.base, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.njet.base, + cs->hw.njet.base + bytecnt); + return (0); + } else { + request_region(cs->hw.njet.base, bytecnt, "netjet isdn"); + } + reset_netjet(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &dummyrr; + cs->BC_Write_Reg = &dummywr; + cs->BC_Send_Data = &fill_dma; + cs->cardmsg = &NETjet_card_msg; + ISACVersion(cs, "NETjet:"); + return (1); +} diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c new file mode 100644 index 000000000..59a8a94e3 --- /dev/null +++ b/drivers/isdn/hisax/niccy.c @@ -0,0 +1,354 @@ +/* $Id: niccy.c,v 1.2 1998/02/11 17:31:04 keil Exp $ + + * niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and + * compatible (SAGEM cybermodem) + * + * Author Karsten Keil + * + * Thanks to Dr. Neuhaus and SAGEM for informations + * + * $Log: niccy.c,v $ + * Revision 1.2 1998/02/11 17:31:04 keil + * new file + * + * + * + */ + +#include <linux/config.h> +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include <linux/pci.h> +#include <linux/bios32.h> + +extern const char *CardType[]; +const char *niccy_revision = "$Revision: 1.2 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISAC_PCI_DATA 0 +#define HSCX_PCI_DATA 1 +#define ISAC_PCI_ADDR 2 +#define HSCX_PCI_ADDR 3 +#define ISAC_PNP 0 +#define HSCX_PNP 1 + +/* SUB Types */ +#define NICCY_PNP 1 +#define NICCY_PCI 2 + +/* PCI stuff */ +#define PCI_VENDOR_DR_NEUHAUS 0x1267 +#define PCI_NICCY_ID 0x1016 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value); +} + +#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "Niccy: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); + } + if (stat & 2) { + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); + } +} + +void +release_io_niccy(struct IsdnCardState *cs) +{ + if (cs->subtyp == NICCY_PCI) + release_region(cs->hw.niccy.isac, 4); + else { + release_region(cs->hw.niccy.isac, 2); + release_region(cs->hw.niccy.isac_ale, 2); + } +} + +static void +niccy_reset(struct IsdnCardState *cs) +{ + // No reset procedure known +} + +static int +niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + niccy_reset(cs); + return(0); + case CARD_RELEASE: + release_io_niccy(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &niccy_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static int pci_index __initdata = 0; + +__initfunc(int +setup_niccy(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, niccy_revision); + printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NICCY) + return (0); + + if (card->para[1]) { + cs->hw.niccy.isac = card->para[1] + ISAC_PNP; + cs->hw.niccy.hscx = card->para[1] + HSCX_PNP; + cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP; + cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP; + cs->hw.niccy.cfg_reg = 0; + cs->subtyp = NICCY_PNP; + cs->irq = card->para[0]; + if (check_region((cs->hw.niccy.isac), 2)) { + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 1); + return (0); + } else + request_region(cs->hw.niccy.isac, 2, "niccy data"); + if (check_region((cs->hw.niccy.isac_ale), 2)) { + printk(KERN_WARNING + "HiSax: %s address port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac_ale, + cs->hw.niccy.isac_ale + 1); + release_region(cs->hw.niccy.isac, 2); + return (0); + } else + request_region(cs->hw.niccy.isac_ale, 2, "niccy addr"); + } else { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS, + PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = NICCY_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + /* if it won't work try the other PCI addresses + * PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_5 + */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Niccy: No PCI card found\n"); + return(0); + } + if (!pci_irq) { + printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA; + cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR; + cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA; + cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR; + cs->irq = pci_irq; + if (check_region((cs->hw.niccy.isac), 4)) { + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 4); + return (0); + } else + request_region(cs->hw.niccy.isac, 4, "niccy"); +#else + printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } + printk(KERN_INFO + "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n", + CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI", + cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale); + niccy_reset(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &niccy_card_msg; + ISACVersion(cs, "Niccy:"); + if (HscxVersion(cs, "Niccy:")) { + printk(KERN_WARNING + "Niccy: wrong HSCX versions check IO address\n"); + release_io_niccy(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c index 1e1ee4772..a0ba3645a 100644 --- a/drivers/isdn/hisax/q931.c +++ b/drivers/isdn/hisax/q931.c @@ -1,4 +1,4 @@ -/* $Id: q931.c,v 1.5 1997/04/06 22:56:43 keil Exp $ +/* $Id: q931.c,v 1.6 1997/07/27 21:09:44 keil Exp $ * q931.c code to decode ITU Q.931 call control messages * @@ -14,6 +14,9 @@ * * * $Log: q931.c,v $ + * Revision 1.6 1997/07/27 21:09:44 keil + * move functions to isdnl3.c + * * Revision 1.5 1997/04/06 22:56:43 keil * Some cosmetic changes * @@ -37,44 +40,6 @@ #include "hisax.h" #include "l3_1tr6.h" -u_char * -findie(u_char * p, int size, u_char ie, int wanted_set) -{ - int l, codeset, maincodeset; - u_char *pend = p + size; - - /* skip protocol discriminator, callref and message type */ - p++; - l = (*p++) & 0xf; - p += l; - p++; - codeset = 0; - maincodeset = 0; - /* while there are bytes left... */ - while (p < pend) { - if ((*p & 0xf0) == 0x90) { - codeset = *p & 0x07; - if (!(*p & 0x08)) - maincodeset = codeset; - } - if (*p & 0x80) - p++; - else { - if (codeset == wanted_set) { - if (*p == ie) - return (p); - if (*p > ie) - return (NULL); - } - p++; - l = *p++; - p += l; - codeset = maincodeset; - } - } - return (NULL); -} - void iecpy(u_char * dest, u_char * iestart, int ieoffset) { @@ -88,14 +53,6 @@ iecpy(u_char * dest, u_char * iestart, int ieoffset) *dest++ = '\0'; } -int -getcallref(u_char * p) -{ - p++; /* prot discr */ - p++; /* callref length */ - return (*p); /* assuming one-byte callref */ -} - /* * According to Table 4-2/Q.931 */ diff --git a/drivers/isdn/hisax/rawhdlc.c b/drivers/isdn/hisax/rawhdlc.c new file mode 100644 index 000000000..16938bc98 --- /dev/null +++ b/drivers/isdn/hisax/rawhdlc.c @@ -0,0 +1,539 @@ +/* $Id: rawhdlc.c,v 1.2 1998/02/09 10:53:51 keil Exp $ + + * rawhdlc.c support routines for cards that don't support HDLC + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Brent Baccala <baccala@FreeSoft.org> + * + * + * Some passive ISDN cards, such as the Traverse NETJet and the AMD 7930, + * don't perform HDLC encapsulation over the B channel. Drivers for + * such cards use support routines in this file to perform B channel HDLC. + * + * Bit-synchronous HDLC encapsulation is a means of encapsulating packets + * over a continuously transmitting serial communications link. + * It looks like this: + * + * 11111111101111110...........0111111011111111111 + * iiiiiiiiiffffffffdddddddddddffffffffiiiiiiiiiii + * + * i = idle f = flag d = data + * + * When idle, the channel sends a continuous string of ones (mark + * idle; illustrated), or a continuous string of flag characters (flag + * idle). The beginning of a data frame is marked by a flag character + * (01111110), then comes the actual data, followed by another flag + * character, after which another frame may be sent immediately (a + * single flag may serve as both the end of one frame and the start of + * the next), or the link may return to idle. Obviously, the flag + * character can not appear anywhere in the data (or a false + * end-of-frame would occur), so the transmitter performs + * "bit-stuffing" - inserting a zero bit after every five one bits, + * irregardless of the original bit after the five ones. Byte + * ordering is irrelevent at this point - the data is treated as a + * string of bits, not bytes. Since no more than 5 ones may now occur + * in a row, the flag sequence, with its 6 ones, is unique. + * + * Upon reception, a zero bit that occur after 5 one bits is simply + * discarded. A series of 6 one bits is end-of-frame, and a series of + * 7 one bits is an abort. Once bit-stuffing has been corrected for, + * an integer number of bytes should now be present. The last two + * of these bytes form the Frame Check Sequence, a CRC that is verified + * and then discarded. Note that bit-stuffing is performed on the FCS + * just as if it were regular data. + * + * + * + * int make_raw_hdlc_data(u_char *src, u_int slen, + * u_char *dst, u_int dsize) + * + * Used for transmission. Copies slen bytes from src to dst, performing + * HDLC encapsulation (flag bytes, bit-stuffing, CRC) in the process. + * dsize is size of destination buffer, and should be at least + * ((6*slen)/5)+5 bytes to ensure adequate space will be available. + * Function returns length (in bytes) of valid destination buffer, or + * 0 upon destination overflow. + * + * void init_hdlc_state(struct hdlc_state *stateptr, int mode) + * + * Initializes hdlc_state structure before first call to read_raw_hdlc_data + * + * mode = 0: Sane mode + * mode = 1/2: + * Insane mode; NETJet use a shared unsigned int memory block ( + * with busmaster DMA), the bit pattern of every word is + * <8 B1> <8 B2> <8 Mon> <2 D> <4 C/I> <MX> <MR> + * according to Siemens IOM-2 interface, so we have to handle + * the src buffer as unsigned int and have to shift/mask the + * B-channel bytes. + * mode 1 -> B1 mode 2 -> B2 data is used + * + * int read_raw_hdlc_data(struct hdlc_state *saved_state, + * u_char *src, u_int slen, + * u_char *dst, u_int dsize) + * + * Used for reception. Scans source buffer bit-by-bit looking for + * valid HDLC frames, which are copied to destination buffer. HDLC + * state information is stored in a structure, which allows this + * function to process frames spread across several blocks of raw + * HDLC data. Part of the state information is bit offsets into + * the source and destination buffers. + * + * A return value >0 indicates the length of a valid frame, now + * stored in the destination buffer. In this case, the source + * buffer might not be completely processed, so this function should + * be called again with the same source buffer, possibly with a + * different destination buffer. + * + * A return value of zero indicates that the source buffer was + * completely processed without finding a valid end-of-packet; + * however, we might be in the middle of packet reception, so + * the function should be called again with the next block of + * raw HDLC data and the same destination buffer. It is NOT + * permitted to change the destination buffer in this case, + * since data may already have begun to be stored there. + * + * A return value of -1 indicates some kind of error - destination + * buffer overflow, CRC check failed, frame not a multiple of 8 + * bits. Destination buffer probably contains invalid data, which + * should be discarded. Call function again with same source buffer + * and a new (or same) destination buffer. + * + * Suggested calling sequence: + * + * init_hdlc_state(...); + * for (EACH_RAW_DATA_BLOCK) { + * while (len = read_raw_hdlc_data(...)) { + * if (len == -1) DISCARD_FRAME; + * else PROCESS_FRAME; + * } + * } + * + * + * Test the code in this file as follows: + * gcc -DDEBUGME -o rawhdlctest rawhdlc.c + * ./rawhdlctest < rawdata + * + * The file "rawdata" can be easily generated from a HISAX B-channel + * hex dump (CF CF CF 02 ...) using the following perl script: + * + * while(<>) { + * @hexlist = split ' '; + * while ($hexstr = shift(@hexlist)) { + * printf "%c", hex($hexstr); + * } + * } + * + */ + +#ifdef DEBUGME +#include <stdio.h> +#endif + +#include <linux/types.h> +#include <linux/ppp_defs.h> +#include "rawhdlc.h" + +/* There's actually an identical copy of this table in the PPP code + * (ppp_crc16_table), but I don't want this code dependant on PPP + */ + +// static +__u16 fcstab[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define HDLC_ZERO_SEARCH 0 +#define HDLC_FLAG_SEARCH 1 +#define HDLC_FLAG_FOUND 2 +#define HDLC_FRAME_FOUND 3 +#define HDLC_NULL 4 +#define HDLC_PART 5 +#define HDLC_FULL 6 + +#define HDLC_FLAG_VALUE 0x7e + + +#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \ + bitcnt++;\ + out_val >>= 1;\ + if (val & 1) {\ + s_one++;\ + out_val |= 0x80;\ + } else {\ + s_one = 0;\ + out_val &= 0x7f;\ + }\ + if (bitcnt==8) {\ + if (d_cnt == dsize) return 0;\ + dst[d_cnt++] = out_val;\ + bitcnt = 0;\ + }\ + if (s_one == 5) {\ + out_val >>= 1;\ + out_val &= 0x7f;\ + bitcnt++;\ + s_one = 0;\ + }\ + if (bitcnt==8) {\ + if (d_cnt == dsize) return 0;\ + dst[d_cnt++] = out_val;\ + bitcnt = 0;\ + }\ + val >>= 1;\ + } + +/* Optimization suggestion: If needed, this function could be + * dramatically sped up using a state machine. Each state would + * correspond to having seen N one bits, and being offset M bits into + * the current output byte. N ranges from 0 to 4, M from 0 to 7, so + * we need 5*8 = 35 states. Each state would have a table with 256 + * entries, one for each input character. Each entry would contain + * three output characters, an output state, an a byte increment + * that's either 1 or 2. All this could fit in four bytes; so we need + * 4 bytes * 256 characters = 1 KB for each state (35 KB total). Zero + * the output buffer before you start. For each character in your + * input, you look it up in the current state's table and get three + * bytes to be or'ed into the output at the current byte offset, and + * an byte increment to move your pointer forward. A simple Perl + * script could generate the tables. Given HDLC semantics, probably + * would be better to set output to all 1s, then use ands instead of ors. + * A smaller state machine could operate on nibbles instead of bytes. + * A state machine for 32-bit architectures could use word offsets + * instead of byte offsets, requiring 5*32 = 160 states; probably + * best to work on nibbles in such a case. + */ + + +int make_raw_hdlc_data(u_char *src, u_int slen, u_char *dst, u_int dsize) +{ + register u_int i,d_cnt=0; + register u_char j; + register u_char val; + register u_char s_one = 0; + register u_char out_val = 0; + register u_char bitcnt = 0; + u_int fcs; + + + dst[d_cnt++] = HDLC_FLAG_VALUE; + fcs = PPP_INITFCS; + for (i=0; i<slen; i++) { + val = src[i]; + fcs = PPP_FCS (fcs, val); + MAKE_RAW_BYTE; + } + fcs ^= 0xffff; + val = fcs & 0xff; + MAKE_RAW_BYTE; + val = (fcs>>8) & 0xff; + MAKE_RAW_BYTE; + val = HDLC_FLAG_VALUE; + for (j=0; j<8; j++) { + bitcnt++; + out_val >>= 1; + if (val & 1) + out_val |= 0x80; + else + out_val &= 0x7f; + if (bitcnt==8) { + if (d_cnt == dsize) return 0; + dst[d_cnt++] = out_val; + bitcnt = 0; + } + val >>= 1; + } + if (bitcnt) { + while (8>bitcnt++) { + out_val >>= 1; + out_val |= 0x80; + } + if (d_cnt == dsize) return 0; + dst[d_cnt++] = out_val; + } + + return d_cnt; +} + +void init_hdlc_state(struct hdlc_state *stateptr, int mode) +{ + stateptr->state = HDLC_ZERO_SEARCH; + stateptr->r_one = 0; + stateptr->r_val = 0; + stateptr->o_bitcnt = 0; + stateptr->i_bitcnt = 0; + stateptr->insane_mode = mode; +} + +/* Optimization suggestion: A similar state machine could surely + * be developed for this function as well. + */ + +int read_raw_hdlc_data(struct hdlc_state *saved_state, + u_char *src, u_int slen, u_char *dst, u_int dsize) +{ + int retval=0; + register u_char val; + register u_char state = saved_state->state; + register u_char r_one = saved_state->r_one; + register u_char r_val = saved_state->r_val; + register u_int o_bitcnt = saved_state->o_bitcnt; + register u_int i_bitcnt = saved_state->i_bitcnt; + register u_int fcs = saved_state->fcs; + register u_int *isrc = (u_int *) src; + + /* Use i_bitcnt (bit offset into source buffer) to reload "val" + * in case we're starting up again partway through a source buffer + */ + + if ((i_bitcnt >> 3) < slen) { + if (saved_state->insane_mode==1) { + val = isrc[(i_bitcnt >> 3)] & 0xff; + } else if (saved_state->insane_mode==2) { + val = (isrc[i_bitcnt >> 3] >>8) & 0xff; + } else { + val = src[i_bitcnt >> 3]; + } + val >>= i_bitcnt & 7; + } + + /* One bit per loop. Keep going until we've got something to + * report (retval != 0), or we exhaust the source buffer + */ + + while ((retval == 0) && ((i_bitcnt >> 3) < slen)) { + if ((i_bitcnt & 7) == 0) { + if (saved_state->insane_mode==1) { + val = isrc[(i_bitcnt >> 3)] & 0xff; + } else if (saved_state->insane_mode==2) { + val = (isrc[i_bitcnt >> 3] >>8) & 0xff; + } else { + val = src[i_bitcnt >> 3]; + } +#ifdef DEBUGME + printf("Input byte %d: 0x%2x\n", i_bitcnt>>3, val); +#endif + if (val == 0xff) { + state = HDLC_ZERO_SEARCH; + o_bitcnt = 0; + r_one = 0; + i_bitcnt += 8; + continue; + } + } + +#ifdef DEBUGME + /* printf("Data bit=%d (%d/%d)\n", val&1, i_bitcnt>>3, i_bitcnt&7);*/ +#endif + + if (state == HDLC_ZERO_SEARCH) { + if (val & 1) { + r_one++; + } else { + r_one=0; + state= HDLC_FLAG_SEARCH; + } + } else if (state == HDLC_FLAG_SEARCH) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } + } else { + if (r_one==6) { + o_bitcnt=0; + r_val=0; + state=HDLC_FLAG_FOUND; + } + r_one=0; + } + } else if (state == HDLC_FLAG_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } else { + r_val >>= 1; + r_val |= 0x80; + o_bitcnt++; + } + } else { + if (r_one==6) { + o_bitcnt=0; + r_val=0; + r_one=0; + i_bitcnt++; + val >>= 1; + continue; + } else if (r_one!=5) { + r_val >>= 1; + r_val &= 0x7f; + o_bitcnt++; + } + r_one=0; + } + if ((state != HDLC_ZERO_SEARCH) && + !(o_bitcnt & 7)) { +#ifdef DEBUGME + printf("HDLC_FRAME_FOUND at i_bitcnt:%d\n",i_bitcnt); +#endif + state=HDLC_FRAME_FOUND; + fcs = PPP_INITFCS; + dst[0] = r_val; + fcs = PPP_FCS (fcs, r_val); + } + } else if (state == HDLC_FRAME_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + o_bitcnt=0; + } else { + r_val >>= 1; + r_val |= 0x80; + o_bitcnt++; + } + } else { + if (r_one==6) { + r_val=0; + r_one=0; + o_bitcnt++; + if (o_bitcnt & 7) { + /* Alignment error */ +#ifdef DEBUGME + printf("Alignment error\n"); +#endif + state=HDLC_FLAG_SEARCH; + retval = -1; + } else if (fcs==PPP_GOODFCS) { + /* Valid frame */ + state=HDLC_FLAG_FOUND; + retval = (o_bitcnt>>3)-3; + } else { + /* CRC error */ +#ifdef DEBUGME + printf("CRC error; fcs was 0x%x, should have been 0x%x\n", fcs, PPP_GOODFCS); +#endif + state=HDLC_FLAG_FOUND; + retval = -1; + } + } else if (r_one==5) { + r_one=0; + i_bitcnt++; + val >>= 1; + continue; + } else { + r_val >>= 1; + r_val &= 0x7f; + o_bitcnt++; + } + r_one=0; + } + if ((state == HDLC_FRAME_FOUND) && + !(o_bitcnt & 7)) { + if ((o_bitcnt>>3)>=dsize) { + /* Buffer overflow error */ +#ifdef DEBUGME + printf("Buffer overflow error\n"); +#endif + r_val=0; + state=HDLC_FLAG_SEARCH; + retval = -1; + } else { + dst[(o_bitcnt>>3)-1] = r_val; + fcs = PPP_FCS (fcs, r_val); +#ifdef DEBUGME + printf("Output byte %d: 0x%02x; FCS 0x%04x\n", (o_bitcnt>>3)-1, r_val, fcs); +#endif + } + } + } + i_bitcnt ++; + val >>= 1; + } + + /* We exhausted the source buffer before anything else happened + * (retval==0). Reset i_bitcnt in expectation of a new source + * buffer. Other, we either had an error or a valid frame, so + * reset o_bitcnt in expectation of a new destination buffer. + */ + + if (retval == 0) { + i_bitcnt = 0; + } else { + o_bitcnt = 0; + } + + saved_state->state = state; + saved_state->r_one = r_one; + saved_state->r_val = r_val; + saved_state->fcs = fcs; + saved_state->o_bitcnt = o_bitcnt; + saved_state->i_bitcnt = i_bitcnt; + + return (retval); +} + + + +#ifdef DEBUGME + +char buffer[1024]; +char obuffer[1024]; + +main() +{ + int buflen=0; + int len; + struct hdlc_state hdlc_state; + + while((buffer[buflen] = getc(stdin)) != EOF && buflen<1024) buflen++; + + printf("buflen = %d\n", buflen); + + init_hdlc_state(&hdlc_state, 0); + + while (len = read_raw_hdlc_data(&hdlc_state,buffer,buflen,obuffer,1024)) { + if (len == -1) printf("Error @ byte %d/bit %d\n", + hdlc_state.i_bitcnt>>3, hdlc_state.i_bitcnt & 7); + else { + printf("Frame received: len %d\n", len); + } + } + + printf("Done\n"); +} + +#endif diff --git a/drivers/isdn/hisax/rawhdlc.h b/drivers/isdn/hisax/rawhdlc.h new file mode 100644 index 000000000..d946fa0b3 --- /dev/null +++ b/drivers/isdn/hisax/rawhdlc.h @@ -0,0 +1,26 @@ +/* $Id: rawhdlc.h,v 1.2 1998/02/09 10:53:53 keil Exp $ + + * rawhdlc.h support routines for cards that don't support HDLC + * + * Author Brent Baccala <baccala@FreeSoft.org> + * + */ + +#ifndef RAWHDLC_H +struct hdlc_state { + char insane_mode; + u_char state; + u_char r_one; + u_char r_val; + u_int o_bitcnt; + u_int i_bitcnt; + u_int fcs; +}; + + +int make_raw_hdlc_data(u_char *src, u_int slen, u_char *dst, u_int dsize); +void init_hdlc_state(struct hdlc_state *stateptr, int mode); +int read_raw_hdlc_data(struct hdlc_state *saved_state, + u_char *src, u_int slen, u_char *dst, u_int dsize); +#define RAWHDLC_H +#endif diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c new file mode 100644 index 000000000..40dc8e622 --- /dev/null +++ b/drivers/isdn/hisax/sedlbauer.c @@ -0,0 +1,356 @@ +/* $Id: sedlbauer.c,v 1.6 1998/02/09 18:46:06 keil Exp $ + + * sedlbauer.c low level stuff for Sedlbauer cards + * includes Support for the Sedlbauer Speed Star + * derived from the original file dynalink.c from Karsten Keil + * + * Copyright (C) 1997,1998 Marcus Niemann (for the modifications to + * the original file dynalink.c) + * + * Author Marcus Niemann (niemann@www-bib.fh-bielefeld.de) + * + * Thanks to Karsten Keil + * Sedlbauer AG for informations + * Edgar Toernig + * + * $Log: sedlbauer.c,v $ + * Revision 1.6 1998/02/09 18:46:06 keil + * Support for Sedlbauer PCMCIA (Marcus Niemann) + * + * Revision 1.5 1998/02/02 13:29:45 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:52 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:09:28 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:55:52 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 1.1 1997/09/11 17:32:04 keil + * new + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *Sedlbauer_revision = "$Revision: 1.6 $"; + +const char *Sedlbauer_Types[] = +{"None", "Speed Card", "Speed Win", "Speed Star"}; + +#define SEDL_SPEED_CARD 1 +#define SEDL_SPEED_WIN 2 +#define SEDL_SPEED_STAR 3 + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define SEDL_RESET_ON 0 +#define SEDL_RESET_OFF 1 +#define SEDL_ISAC 2 +#define SEDL_HSCX 3 +#define SEDL_ADR 4 + +#define SEDL_PCMCIA_RESET 0 +#define SEDL_PCMCIA_ISAC 1 +#define SEDL_PCMCIA_HSCX 2 +#define SEDL_PCMCIA_ADR 4 + +#define SEDL_RESET 0x3 /* same as DOS driver */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.sedl.adr, + cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.sedl.adr, + cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); + return; + } + + if ((cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) && (*cs->busy_flag == 1)) { + /* The card tends to generate interrupts while being removed + causing us to just crash the kernel. bad. */ + printk(KERN_WARNING "Sedlbauer: card not available!\n"); + return; + } + + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_sedlbauer(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.sedl.cfg_reg) + release_region(cs->hw.sedl.cfg_reg, bytecnt); +} + +static void +reset_sedlbauer(struct IsdnCardState *cs) +{ + long flags; + + if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA) { + byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); + } +} + +static int +Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_sedlbauer(cs); + return(0); + case CARD_RELEASE: + release_io_sedlbauer(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &sedlbauer_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_sedlbauer(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Sedlbauer_revision); + printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ == ISDN_CTYPE_SEDLBAUER) { + cs->subtyp = SEDL_SPEED_CARD; + } else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) { + cs->subtyp = SEDL_SPEED_STAR; + } else + return (0); + + bytecnt = 8; + cs->hw.sedl.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (cs->subtyp == SEDL_SPEED_STAR) { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_HSCX; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET; + } else { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_RESET_ON; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_RESET_OFF; + } + + /* In case of the sedlbauer pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA && + check_region((cs->hw.sedl.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.sedl.cfg_reg, + cs->hw.sedl.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn"); + } + + printk(KERN_INFO + "Sedlbauer: defined at 0x%x IRQ %d\n", + cs->hw.sedl.cfg_reg, + cs->irq); + printk(KERN_WARNING + "Sedlbauer %s uses ports 0x%x-0x%x\n", + Sedlbauer_Types[cs->subtyp], + cs->hw.sedl.cfg_reg, + cs->hw.sedl.cfg_reg + bytecnt); + + printk(KERN_INFO "Sedlbauer: resetting card\n"); + reset_sedlbauer(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Sedl_card_msg; + ISACVersion(cs, "Sedlbauer:"); + if (HscxVersion(cs, "Sedlbauer:")) { + printk(KERN_WARNING + "Sedlbauer: wrong HSCX versions check IO address\n"); + release_io_sedlbauer(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c new file mode 100644 index 000000000..cb867a13f --- /dev/null +++ b/drivers/isdn/hisax/sportster.c @@ -0,0 +1,291 @@ +/* $Id: sportster.c,v 1.5 1998/02/02 13:29:46 keil Exp $ + + * sportster.c low level stuff for USR Sportster internal TA + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation + * + * $Log: sportster.c,v $ + * Revision 1.5 1998/02/02 13:29:46 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:52 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:09:29 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:51:18 keil + * New files + * + * Revision 1.1.2.1 1997/10/17 22:10:58 keil + * new files on 2.0 + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +const char *sportster_revision = "$Revision: 1.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define SPORTSTER_ISAC 0xC000 +#define SPORTSTER_HSCXA 0x0000 +#define SPORTSTER_HSCXB 0x4000 +#define SPORTSTER_RES_IRQ 0x8000 +#define SPORTSTER_RESET 0x80 +#define SPORTSTER_INTE 0x40 + +static inline int +calc_off(unsigned int base, unsigned int off) +{ + return(base + ((off & 0xfc)<<8) + ((off & 3)<<1)); +} + +static inline void +read_fifo(unsigned int adr, u_char * data, int size) +{ + insb(adr, data, size); +} + +static void +write_fifo(unsigned int adr, u_char * data, int size) +{ + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (bytein(calc_off(cs->hw.spt.isac, offset))); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + byteout(calc_off(cs->hw.spt.isac, offset), value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + read_fifo(cs->hw.spt.isac, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + write_fifo(cs->hw.spt.isac, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg)) +#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt) + +#include "hscx_irq.c" + +static void +sportster_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + + if (!cs) { + printk(KERN_WARNING "Sportster: Spurious interrupt!\n"); + return; + } + val = READHSCX(cs, 1, HSCX_ISTA); + Start_HSCX: + if (val) + hscx_int_main(cs, val); + val = ReadISAC(cs, ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = READHSCX(cs, 1, HSCX_ISTA); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = ReadISAC(cs, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + /* get a new irq impulse if there any pending */ + bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1); +} + +void +release_io_sportster(struct IsdnCardState *cs) +{ + int i, adr; + + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0); + for (i=0; i<64; i++) { + adr = cs->hw.spt.cfg_reg + i *1024; + release_region(adr, 8); + } +} + +void +reset_sportster(struct IsdnCardState *cs) +{ + long flags; + + cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_sportster(cs); + return(0); + case CARD_RELEASE: + release_io_sportster(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &sportster_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +get_io_range(struct IsdnCardState *cs)) +{ + int i, j, adr; + + for (i=0;i<64;i++) { + adr = cs->hw.spt.cfg_reg + i *1024; + if (check_region(adr, 8)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[cs->typ], adr, adr + 8); + break; + } else + request_region(adr, 8, "sportster"); + } + if (i==64) + return(1); + else { + for (j=0; j<i; j++) { + adr = cs->hw.spt.cfg_reg + j *1024; + release_region(adr, 8); + } + return(0); + } +} + +__initfunc(int +setup_sportster(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, sportster_revision); + printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_SPORTSTER) + return (0); + + cs->hw.spt.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (!get_io_range(cs)) + return (0); + cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC; + cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA; + cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB; + + switch(cs->irq) { + case 5: cs->hw.spt.res_irq = 1; + break; + case 7: cs->hw.spt.res_irq = 2; + break; + case 10:cs->hw.spt.res_irq = 3; + break; + case 11:cs->hw.spt.res_irq = 4; + break; + case 12:cs->hw.spt.res_irq = 5; + break; + case 14:cs->hw.spt.res_irq = 6; + break; + case 15:cs->hw.spt.res_irq = 7; + break; + default:release_io_sportster(cs); + printk(KERN_WARNING "Sportster: wrong IRQ\n"); + return(0); + } + reset_sportster(cs); + printk(KERN_INFO + "HiSax: %s config irq:%d cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.spt.cfg_reg); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Sportster_card_msg; + ISACVersion(cs, "Sportster:"); + if (HscxVersion(cs, "Sportster:")) { + printk(KERN_WARNING + "Sportster: wrong HSCX versions check IO address\n"); + release_io_sportster(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c index d97595938..a52078746 100644 --- a/drivers/isdn/hisax/tei.c +++ b/drivers/isdn/hisax/tei.c @@ -1,4 +1,4 @@ -/* $Id: tei.c,v 1.8 1997/04/07 22:59:08 keil Exp $ +/* $Id: tei.c,v 2.7 1998/02/12 23:08:11 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,52 +7,112 @@ * Fritz Elfert * * $Log: tei.c,v $ - * Revision 1.8 1997/04/07 22:59:08 keil - * GFP_KERNEL --> GFP_ATOMIC + * Revision 2.7 1998/02/12 23:08:11 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() * - * Revision 1.7 1997/04/06 22:54:03 keil - * Using SKB's + * Revision 2.6 1998/02/02 13:41:42 keil + * fix MDL_ASSIGN for PtP * - * Revision 1.6 1997/02/09 00:25:12 keil - * new interface handling, one interface per card + * Revision 2.5 1997/11/06 17:09:12 keil + * New 2.1 init code * - * Revision 1.5 1997/01/27 15:57:51 keil - * cosmetics + * Revision 2.4 1997/10/29 19:04:46 keil + * changes for 2.1 * - * Revision 1.4 1997/01/21 22:32:44 keil - * Tei verify request + * Revision 2.3 1997/10/01 09:21:43 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. * - * Revision 1.3 1997/01/04 13:45:02 keil - * cleanup,adding remove tei request (thanks to Sim Yskes) + * Revision 2.2 1997/07/31 19:24:39 keil + * fixed a warning * - * Revision 1.2 1996/12/08 19:52:39 keil - * minor debug fix + * Revision 2.1 1997/07/31 11:50:16 keil + * ONE TEI and FIXED TEI handling * - * Revision 1.1 1996/10/13 20:04:57 keil - * Initial revision + * Revision 2.0 1997/07/27 21:13:30 keil + * New TEI managment * + * Revision 1.9 1997/06/26 11:18:02 keil + * New managment * + * Revision 1.8 1997/04/07 22:59:08 keil + * GFP_KERNEL --> GFP_ATOMIC + * + * Revision 1.7 1997/04/06 22:54:03 keil + * Using SKB's + * + * Old log removed/ KKe * */ #define __NO_VERSION__ #include "hisax.h" +#include "isdnl2.h" +#include <linux/random.h> -extern struct IsdnCard cards[]; -extern int nrcards; +const char *tei_revision = "$Revision: 2.7 $"; -const char *tei_revision = "$Revision: 1.8 $"; +#define ID_REQUEST 1 +#define ID_ASSIGNED 2 +#define ID_DENIED 3 +#define ID_CHK_REQ 4 +#define ID_CHK_RES 5 +#define ID_REMOVE 6 +#define ID_VERIFY 7 -static struct PStack * -findces(struct PStack *st, int ces) +#define TEI_ENTITY_ID 0xf + +static +struct Fsm teifsm = +{NULL, 0, 0, NULL, NULL}; + +void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb); + +enum { + ST_TEI_NOP, + ST_TEI_IDREQ, + ST_TEI_IDVERIFY, +}; + +#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) + +static char *strTeiState[] = { - struct PStack *ptr = *(st->l1.stlistp); + "ST_TEI_NOP", + "ST_TEI_IDREQ", + "ST_TEI_IDVERIFY", +}; - while (ptr) - if (ptr->l2.ces == ces) - return (ptr); - else - ptr = ptr->next; - return (NULL); +enum { + EV_IDREQ, + EV_ASSIGN, + EV_DENIED, + EV_CHKREQ, + EV_REMOVE, + EV_VERIFY, + EV_T202, +}; + +#define TEI_EVENT_COUNT (EV_T202+1) + +static char *strTeiEvent[] = +{ + "EV_IDREQ", + "EV_ASSIGN", + "EV_DENIED", + "EV_CHKREQ", + "EV_REMOVE", + "EV_VERIFY", + "EV_T202", +}; + +unsigned int +random_ri(void) +{ + unsigned int x; + + get_random_bytes(&x, sizeof(x)); + return (x & 0xffff); } static struct PStack * @@ -72,246 +132,357 @@ findtei(struct PStack *st, int tei) } static void -mdl_unit_data_res(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei) { struct sk_buff *skb; u_char *bp; - if (!(skb = alloc_skb(6 + MAX_HEADER_LEN, GFP_ATOMIC))) { + if (!(skb = alloc_skb(8, GFP_ATOMIC))) { printk(KERN_WARNING "HiSax: No skb for TEI manager\n"); return; } - skb_reserve(skb, MAX_HEADER_LEN); + bp = skb_put(skb, 3); + bp[0] = (TEI_SAPI << 2); + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; bp = skb_put(skb, 5); - bp[0] = 0xf; + bp[0] = TEI_ENTITY_ID; bp[1] = ri >> 8; bp[2] = ri & 0xff; - bp[3] = mt; - bp[4] = (ai << 1) | 1; - st->l3.l3l2(st, DL_UNIT_DATA, skb); + bp[3] = m_id; + bp[4] = (tei << 1) | 1; + st->l2.l2l1(st, PH_DATA_REQ, skb); } static void -mdl_unit_data_ind(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +tei_id_request(struct FsmInst *fi, int event, void *arg) { - unsigned int tces; - struct PStack *otsp, *ptr; + struct PStack *st = fi->userdata; char tmp[64]; - switch (mt) { - case (2): - tces = ri; - if (st->l3.debug) { - sprintf(tmp, "identity assign ces %d ai %d", tces, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - if ((otsp = findces(st, tces))) { - if (st->l3.debug) { - sprintf(tmp, "ces %d --> tei %d", tces, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - otsp->ma.teil2(otsp, MDL_ASSIGN, (void *) (int) ai); - } - break; - case (3): - tces = ri; - if (st->l3.debug) { - sprintf(tmp, "identity denied for ces %d ai %d", tces, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - if ((otsp = findces(st, tces))) { - if (st->l3.debug) { - sprintf(tmp, "ces %d denied tei %d", tces, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - otsp->l2.tei = 255; - otsp->l2.ces = randomces(); - otsp->ma.teil2(otsp, MDL_REMOVE, 0); - } - break; - case (4): - if (st->l3.debug) { - sprintf(tmp, "checking identity for %d", ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - if (ai == 0x7f) { - ptr = *(st->l1.stlistp); - while (ptr) { - if ((ptr->l2.tei & 0x7f) != 0x7f) { - if (st->l3.debug) { - sprintf(tmp, "check response for ces %d with tei %d", - ptr->l2.ces, ptr->l2.tei); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - /* send identity check response (user->network) */ - mdl_unit_data_res(st, ptr->l2.ces, 5, ptr->l2.tei); - } - ptr = ptr->next; - } - } else { - otsp = findtei(st, ai); - if (!otsp) - break; - if (st->l3.debug) { - sprintf(tmp, "check response for ces %d with tei %d", - otsp->l2.ces, otsp->l2.tei); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - /* send identity check response (user->network) */ - mdl_unit_data_res(st, otsp->l2.ces, 5, otsp->l2.tei); - } - break; - case (6): - if (st->l3.debug) { - sprintf(tmp, "removal for %d", ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - if (ai == 0x7f) { - ptr = *(st->l1.stlistp); - while (ptr) { - if ((ptr->l2.tei & 0x7f) != 0x7f) { - if (st->l3.debug) { - sprintf(tmp, "rem ces %d with tei %d", - ptr->l2.ces, ptr->l2.tei); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - ptr->ma.teil2(ptr, MDL_REMOVE, 0); - } - ptr = ptr->next; - } - } else { - otsp = findtei(st, ai); - if (!otsp) - break; - if (st->l3.debug) { - sprintf(tmp, "rem ces %d with tei %d", - otsp->l2.ces, otsp->l2.tei); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - otsp->ma.teil2(otsp, MDL_REMOVE, 0); - } - break; - default: - if (st->l3.debug) { - sprintf(tmp, "message unknown %d ai %d", mt, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } + if (st->l2.tei != -1) { + sprintf(tmp, "assign request for allready asigned tei %d", + st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + return; + } + st->ma.ri = random_ri(); + if (st->ma.debug) { + sprintf(tmp, "assign request ri %d", st->ma.ri); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); } + put_tei_msg(st, ID_REQUEST, st->ma.ri, 127); + FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1); + st->ma.N202 = 3; } -void -tei_handler(struct PStack *st, - u_char pr, struct sk_buff *skb) +static void +tei_id_assign(struct FsmInst *fi, int event, void *arg) { - u_char *bp; - unsigned int data; - char tmp[32]; + struct PStack *ost, *st = fi->userdata; + struct sk_buff *skb = arg; + struct IsdnCardState *cs; + int ri, tei; + char tmp[64]; - switch (pr) { - case (MDL_ASSIGN): - data = (unsigned int) skb; - if (st->l3.debug) { - sprintf(tmp, "ces %d assign request", data); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - mdl_unit_data_res(st, data, 1, 127); - break; - case (MDL_VERIFY): - data = (unsigned int) skb; - if (st->l3.debug) { - sprintf(tmp, "%d id verify request", data); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - mdl_unit_data_res(st, 0, 7, data); - break; - case (DL_UNIT_DATA): - bp = skb->data; - if (bp[0] != 0xf) { - /* wrong management entity identifier, ignore */ - /* shouldn't ibh be released??? */ - printk(KERN_WARNING "tei handler wrong entity id %x\n", bp[0]); - } else - mdl_unit_data_ind(st, (bp[1] << 8) | bp[2], bp[3], bp[4] >> 1); - SET_SKB_FREE(skb); - dev_kfree_skb(skb); - break; - default: - break; + ri = ((unsigned int) skb->data[1] << 8) + skb->data[2]; + tei = skb->data[4] >> 1; + if (st->ma.debug) { + sprintf(tmp, "identity assign ri %d tei %d", ri, tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + if ((ost = findtei(st, tei))) { /* same tei is in use */ + if (ri != ost->ma.ri) { + sprintf(tmp, "possible duplicate assignment tei %d", tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + ost->l2.l2tei(ost, MDL_ERROR_REQ, NULL); + } + } else if (ri == st->ma.ri) { + FsmDelTimer(&st->ma.t202, 1); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) tei); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_ASSIGN_REQ, NULL); } } -unsigned int -randomces(void) +static void +tei_id_denied(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int ri, tei; + char tmp[64]; + + ri = ((unsigned int) skb->data[1] << 8) + skb->data[2]; + tei = skb->data[4] >> 1; + if (st->ma.debug) { + sprintf(tmp, "identity denied ri %d tei %d", ri, tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } +} + +static void +tei_id_chk_req(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int tei; + char tmp[64]; + + tei = skb->data[4] >> 1; + if (st->ma.debug) { + sprintf(tmp, "identity check req tei %d", tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) { + FsmDelTimer(&st->ma.t202, 4); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + put_tei_msg(st, ID_CHK_RES, random_ri(), st->l2.tei); + } +} + +static void +tei_id_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + struct IsdnCardState *cs; + int tei; + char tmp[64]; + + tei = skb->data[4] >> 1; + if (st->ma.debug) { + sprintf(tmp, "identity remove tei %d", tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) { + FsmDelTimer(&st->ma.t202, 5); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + st->ma.manl2(st, MDL_REMOVE_REQ, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE_REQ, NULL); + } +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) { - int x = jiffies & 0xffff; + struct PStack *st = fi->userdata; + char tmp[64]; - return (x); + if (st->ma.debug) { + sprintf(tmp, "id verify request for tei %d", st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + put_tei_msg(st, ID_VERIFY, 0, st->l2.tei); + FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2); + st->ma.N202 = 2; } static void -tei_man(struct PStack *sp, int i, void *v) +tei_id_req_tout(struct FsmInst *fi, int event, void *arg) { + struct PStack *st = fi->userdata; + char tmp[64]; + struct IsdnCardState *cs; - printk(KERN_DEBUG "tei_man\n"); + if (--st->ma.N202) { + st->ma.ri = random_ri(); + if (st->ma.debug) { + sprintf(tmp, "assign req(%d) ri %d", + 4 - st->ma.N202, st->ma.ri); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + put_tei_msg(st, ID_REQUEST, st->ma.ri, 127); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3); + } else { + sprintf(tmp, "assign req failed"); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + st->ma.manl2(st, MDL_ERROR_IND, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE_REQ, NULL); + FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + char tmp[64]; + struct IsdnCardState *cs; + + if (--st->ma.N202) { + if (st->ma.debug) { + sprintf(tmp, "id verify req(%d) for tei %d", + 3 - st->ma.N202, st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + put_tei_msg(st, ID_VERIFY, 0, st->l2.tei); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4); + } else { + sprintf(tmp, "verify req for tei %d failed", st->l2.tei); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + st->ma.manl2(st, MDL_REMOVE_REQ, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE_REQ, NULL); + FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_l1l2(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + int mt; + char tmp[64]; + + if (pr == PH_DATA_IND) { + if (skb->len < 3) { + sprintf(tmp, "short mgr frame %d/3", skb->len); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else if (((skb->data[0] >> 2) != TEI_SAPI) || + ((skb->data[1] >> 1) != GROUP_TEI)) { + sprintf(tmp, "wrong mgr sapi/tei %x/%x", + skb->data[0], skb->data[1]); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else if ((skb->data[2] & 0xef) != UI) { + sprintf(tmp, "mgr frame is not ui %x", + skb->data[2]); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else { + skb_pull(skb, 3); + if (skb->len < 5) { + sprintf(tmp, "short mgr frame %d/5", skb->len); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else if (skb->data[0] != TEI_ENTITY_ID) { + /* wrong management entity identifier, ignore */ + sprintf(tmp, "tei handler wrong entity id %x\n", + skb->data[0]); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } else { + mt = skb->data[3]; + if (mt == ID_ASSIGNED) + FsmEvent(&st->ma.tei_m, EV_ASSIGN, skb); + else if (mt == ID_DENIED) + FsmEvent(&st->ma.tei_m, EV_DENIED, skb); + else if (mt == ID_CHK_REQ) + FsmEvent(&st->ma.tei_m, EV_CHKREQ, skb); + else if (mt == ID_REMOVE) + FsmEvent(&st->ma.tei_m, EV_REMOVE, skb); + else { + sprintf(tmp, "tei handler wrong mt %x\n", + mt); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + } + } + } else { + sprintf(tmp, "tei handler wrong pr %x\n", pr); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + dev_kfree_skb(skb); } static void tei_l2tei(struct PStack *st, int pr, void *arg) { - struct IsdnCardState *sp = st->l1.hardware; + switch (pr) { + case (MDL_ASSIGN_IND): +#ifdef TEI_FIXED + if (st->ma.debug) { + char tmp[64]; + sprintf(tmp, "fixed assign tei %d", TEI_FIXED); + st->ma.tei_m.printdebug(&st->ma.tei_m, tmp); + } + st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) TEI_FIXED); +#else + FsmEvent(&st->ma.tei_m, EV_IDREQ, arg); +#endif + break; + case (MDL_ERROR_REQ): + FsmEvent(&st->ma.tei_m, EV_VERIFY, arg); + break; + default: + break; + } +} + +static void +tei_debug(struct FsmInst *fi, char *s) +{ + struct PStack *st = fi->userdata; + char tm[32], str[256]; - tei_handler(sp->teistack, pr, arg); + jiftime(tm, jiffies); + sprintf(str, "%s Tei %s\n", tm, s); + HiSax_putstatus(st->l1.hardware, str); } void setstack_tei(struct PStack *st) { st->l2.l2tei = tei_l2tei; + st->ma.T202 = 2000; /* T202 2000 milliseconds */ + st->l1.l1tei = tei_l1l2; + st->ma.debug = 1; + st->ma.tei_m.fsm = &teifsm; + st->ma.tei_m.state = ST_TEI_NOP; + st->ma.tei_m.debug = 1; + st->ma.tei_m.userdata = st; + st->ma.tei_m.userint = 0; + st->ma.tei_m.printdebug = tei_debug; + FsmInitTimer(&st->ma.tei_m, &st->ma.t202); } void init_tei(struct IsdnCardState *sp, int protocol) { - struct PStack *st; - char tmp[128]; - - st = (struct PStack *) kmalloc(sizeof(struct PStack), GFP_ATOMIC); - setstack_HiSax(st, sp); - st->l2.extended = !0; - st->l2.laptype = LAPD; - st->l2.window = 1; - st->l2.orig = !0; - st->protocol = protocol; -/* - * the following is not necessary for tei mng. (broadcast only) - */ - st->l2.t200 = 500; /* 500 milliseconds */ - st->l2.n200 = 4; /* try 4 times */ - st->l2.sap = 63; - st->l2.tei = 127; +} + +void +release_tei(struct IsdnCardState *cs) +{ + struct PStack *st = cs->stlist; - sprintf(tmp, "Card %d tei", sp->cardnr + 1); - setstack_isdnl2(st, tmp); - st->l2.debug = 0; - st->l3.debug = 0; + while (st) { + FsmDelTimer(&st->ma.t202, 1); + st = st->next; + } +} - st->ma.manl2(st, MDL_NOTEIPROC, NULL); +static struct FsmNode TeiFnList[] HISAX_INITDATA = +{ + {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, + {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, + {ST_TEI_IDREQ, EV_T202, tei_id_req_tout}, + {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, + {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, + {ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout}, + {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, + {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, +}; - st->l2.l2l3 = (void *) tei_handler; - st->l1.l1man = tei_man; - st->l2.l2man = tei_man; - st->l4.l2writewakeup = NULL; +#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode)) - HiSax_addlist(sp, st); - sp->teistack = st; +HISAX_INITFUNC(void +TeiNew(void)) +{ + teifsm.state_count = TEI_STATE_COUNT; + teifsm.event_count = TEI_EVENT_COUNT; + teifsm.strEvent = strTeiEvent; + teifsm.strState = strTeiState; + FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT); } void -release_tei(struct IsdnCardState *sp) +TeiFree(void) { - struct PStack *st = sp->teistack; - - HiSax_rmlist(sp, st); - kfree((void *) st); + FsmFree(&teifsm); } diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c new file mode 100644 index 000000000..d86dd79a4 --- /dev/null +++ b/drivers/isdn/hisax/teleint.c @@ -0,0 +1,363 @@ +/* $Id: teleint.c,v 1.5 1998/02/02 13:40:47 keil Exp $ + + * teleint.c low level stuff for TeleInt isdn cards + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: teleint.c,v $ + * Revision 1.5 1998/02/02 13:40:47 keil + * fast io + * + * Revision 1.4 1997/11/08 21:35:53 keil + * new l1 init + * + * Revision 1.3 1997/11/06 17:09:30 keil + * New 2.1 init code + * + * Revision 1.2 1997/10/29 18:55:53 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 1.1 1997/09/11 17:32:32 keil + * new + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hfc_2bs0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *TeleInt_revision = "$Revision: 1.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + int max_delay = 2000; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + restore_flags(flags); + return (0); + } + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + register u_char ret; + int max_delay = 2000; + byteout(ale, off); + + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + return; + } + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + register u_char ret; + int max_delay = 2000; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + restore_flags(flags); + return; + } + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + register u_char ret; + int max_delay = 2000; + + /* fifo write without cli because it's allready done */ + byteout(ale, off); + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + return; + } + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + cs->hw.hfc.cip = offset; + return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + cs->hw.hfc.cip = offset; + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + cs->hw.hfc.cip = 0; + readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + cs->hw.hfc.cip = 0; + writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); +} + +static u_char +ReadHFC(struct IsdnCardState *cs, int data, u_char reg) +{ + register u_char ret; + + if (data) { + cs->hw.hfc.cip = reg; + byteout(cs->hw.hfc.addr | 1, reg); + ret = bytein(cs->hw.hfc.addr); + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) { + char tmp[32]; + sprintf(tmp, "hfc RD %02x %02x", reg, ret); + debugl1(cs, tmp); + } + } else + ret = bytein(cs->hw.hfc.addr | 1); + return (ret); +} + +static void +WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value) +{ + byteout(cs->hw.hfc.addr | 1, reg); + cs->hw.hfc.cip = reg; + if (data) + byteout(cs->hw.hfc.addr, value); + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) { + char tmp[32]; + sprintf(tmp, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value); + debugl1(cs, tmp); + } +} + +static void +TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "TeleInt: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 2) { + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF); + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0); + } +} + +static void +TeleInt_Timer(struct IsdnCardState *cs) +{ + int stat = 0; + + if (cs->bcs[0].mode) { + stat |= 1; + main_irq_hfc(&cs->bcs[0]); + } + if (cs->bcs[1].mode) { + stat |= 2; + main_irq_hfc(&cs->bcs[1]); + } + cs->hw.hfc.timer.expires = jiffies + 1; + add_timer(&cs->hw.hfc.timer); +} + +void +release_io_TeleInt(struct IsdnCardState *cs) +{ + del_timer(&cs->hw.hfc.timer); + releasehfc(cs); + if (cs->hw.hfc.addr) + release_region(cs->hw.hfc.addr, 2); +} + +static void +reset_TeleInt(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "TeleInt: resetting card\n"); + cs->hw.hfc.cirm |= HFC_RESET; + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 3; + schedule(); + cs->hw.hfc.cirm &= ~HFC_RESET; + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_TeleInt(cs); + return(0); + case CARD_RELEASE: + release_io_TeleInt(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &TeleInt_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithfc(cs); + clear_pending_isac_ints(cs); + initisac(cs); + cs->hw.hfc.timer.expires = jiffies + 1; + add_timer(&cs->hw.hfc.timer); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_TeleInt(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, TeleInt_revision); + printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELEINT) + return (0); + + cs->hw.hfc.addr = card->para[1] & 0x3fe; + cs->irq = card->para[0]; + cs->hw.hfc.cirm = HFC_CIRM; + cs->hw.hfc.isac_spcr = 0x00; + cs->hw.hfc.cip = 0; + cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER; + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->hw.hfc.fifosize = 7 * 1024 + 512; + cs->hw.hfc.timer.function = (void *) TeleInt_Timer; + cs->hw.hfc.timer.data = (long) cs; + init_timer(&cs->hw.hfc.timer); + if (check_region((cs->hw.hfc.addr), 2)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.hfc.addr, + cs->hw.hfc.addr + 2); + return (0); + } else { + request_region(cs->hw.hfc.addr, 2, "TeleInt isdn"); + } + /* HW IO = IO */ + byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff); + byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54); + switch (cs->irq) { + case 3: + cs->hw.hfc.cirm |= HFC_INTA; + break; + case 4: + cs->hw.hfc.cirm |= HFC_INTB; + break; + case 5: + cs->hw.hfc.cirm |= HFC_INTC; + break; + case 7: + cs->hw.hfc.cirm |= HFC_INTD; + break; + case 10: + cs->hw.hfc.cirm |= HFC_INTE; + break; + case 11: + cs->hw.hfc.cirm |= HFC_INTF; + break; + default: + printk(KERN_WARNING "TeleInt: wrong IRQ\n"); + release_io_TeleInt(cs); + return (0); + } + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt); + + printk(KERN_INFO + "TeleInt: defined at 0x%x IRQ %d\n", + cs->hw.hfc.addr, + cs->irq); + + reset_TeleInt(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHFC; + cs->BC_Write_Reg = &WriteHFC; + cs->cardmsg = &TeleInt_card_msg; + ISACVersion(cs, "TeleInt:"); + return (1); +} diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c index 6aaba70d3..7cd173154 100644 --- a/drivers/isdn/hisax/teles0.c +++ b/drivers/isdn/hisax/teles0.c @@ -1,4 +1,4 @@ -/* $Id: teles0.c,v 1.8 1997/04/13 19:54:04 keil Exp $ +/* $Id: teles0.c,v 2.6 1998/02/03 23:27:47 keil Exp $ * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden @@ -10,71 +10,73 @@ * Beat Doebeli * * $Log: teles0.c,v $ - * Revision 1.8 1997/04/13 19:54:04 keil - * Change in IRQ check delay for SMP + * Revision 2.6 1998/02/03 23:27:47 keil + * IRQ 9 * - * Revision 1.7 1997/04/06 22:54:04 keil - * Using SKB's + * Revision 2.5 1998/02/02 13:29:47 keil + * fast io * - * Revision 1.6 1997/01/27 15:52:18 keil - * SMP proof,cosmetics + * Revision 2.4 1997/11/08 21:35:54 keil + * new l1 init * - * Revision 1.5 1997/01/21 22:25:59 keil - * cleanups + * Revision 2.3 1997/11/06 17:09:31 keil + * New 2.1 init code * - * Revision 1.4 1996/11/05 19:41:27 keil - * more changes for 2.1 + * Revision 2.2 1997/10/29 18:55:57 keil + * changes for 2.1.60 (irq2dev_map) * - * Revision 1.3 1996/10/30 10:22:58 keil - * Changes for 2.1 kernels + * Revision 2.1 1997/07/27 21:47:10 keil + * new interface structures * - * Revision 1.2 1996/10/27 22:08:34 keil - * cosmetic changes + * Revision 2.0 1997/06/26 11:02:43 keil + * New Layer and card interface * - * Revision 1.1 1996/10/13 20:04:58 keil - * Initial revision + * Revision 1.8 1997/04/13 19:54:04 keil + * Change in IRQ check delay for SMP * + * Revision 1.7 1997/04/06 22:54:04 keil + * Using SKB's * + * removed old log info /KKe * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles0.h" #include "isdnl1.h" -#include <linux/kernel_stat.h> +#include "isac.h" +#include "hscx.h" extern const char *CardType[]; -const char *teles0_revision = "$Revision: 1.8 $"; +const char *teles0_revision = "$Revision: 2.6 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readisac(unsigned int adr, u_char off) { - return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); + return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off); } static inline void writeisac(unsigned int adr, u_char off, u_char data) { - writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); + writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); } static inline u_char readhscx(unsigned int adr, int hscx, u_char off) { - return readb(adr + (hscx ? 0x1e0 : 0x1a0) + + return readb(adr + (hscx ? 0x1c0 : 0x180) + ((off & 1) ? 0x1ff : 0) + off); } static inline void writehscx(unsigned int adr, int hscx, u_char off, u_char data) { - writeb(data, adr + (hscx ? 0x1e0 : 0x1a0) + + writeb(data, adr + (hscx ? 0x1c0 : 0x180) + ((off & 1) ? 0x1ff : 0) + off); } @@ -87,7 +89,7 @@ read_fifo_isac(unsigned int adr, u_char * data, int size) data[i] = readb(ad); } -static void +static inline void write_fifo_isac(unsigned int adr, u_char * data, int size) { register int i; @@ -113,745 +115,204 @@ write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) for (i = 0; i < size; i++) writeb(data[i], ad); } -static inline void -waitforCEC(int adr, int hscx) -{ - int to = 50; - - while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles0: waitforCEC timeout\n"); -} +/* Interface functions */ -static inline void -waitforXFW(int adr, int hscx) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - int to = 50; - - while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles0: waitforXFW timeout\n"); + return (readisac(cs->hw.teles0.membase, offset)); } -static inline void -writehscxCMDR(int adr, int hscx, u_char data) -{ - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr, hscx); - writehscx(adr, hscx, HSCX_CMDR, data); - restore_flags(flags); -} - -/* - * fast interrupt here - */ - - static void -hscxreport(struct IsdnCardState *sp, int hscx) -{ - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->membase, hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readhscx(sp->membase, hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->membase, hscx, HSCX_EXIR)); -} - -void -teles0_report(struct IsdnCardState *sp) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readisac(sp->membase, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readisac(sp->membase, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readisac(sp->membase, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + writeisac(cs->hw.teles0.membase, offset, value); } -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_hscx(sp->membase, hsp->hscx, ptr, count); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + read_fifo_isac(cs->hw.teles0.membase, data, size); } static void -hscx_fill_fifo(struct HscxState *hsp) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->membase, hsp->hscx); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo_hscx(sp->membase, hsp->hscx, ptr, count); - writehscxCMDR(sp->membase, hsp->hscx, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo_isac(cs->hw.teles0.membase, data, size); } -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readhscx(sp->membase, hsp->hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - } else { - count = readhscx(sp->membase, hsp->hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + return (readhscx(cs->hw.teles0.membase, hscx, offset)); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writeisac(sp->membase, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_isac(sp->membase, ptr, count); - writeisac(sp->membase, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writehscx(cs->hw.teles0.membase, hscx, offset, value); } -static void -isac_fill_fifo(struct IsdnCardState *sp) -{ - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo_isac(sp->membase, ptr, count); - writeisac(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static void -ph_command(struct IsdnCardState *sp, unsigned int command) -{ - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writeisac(sp->membase, ISAC_CIX0, (command << 2) | 3); -} - -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readisac(sp->membase, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!exval & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writeisac(sp->membase, ISAC_CMDR, 0x80); - } else { - count = readisac(sp->membase, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readisac(sp->membase, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readisac(sp->membase, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readhscx(sp->membase, 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->membase, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readhscx(sp->membase, 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->membase, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readhscx(sp->membase, 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void -telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs) +teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; + int count = 0; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Teles0: Spurious interrupt!\n"); return; } - val = readhscx(sp->membase, 1, HSCX_ISTA); + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = readisac(sp->membase, ISAC_ISTA); + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } - val = readhscx(sp->membase, 1, HSCX_ISTA); - if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + count++; + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); + if (val && count < 20) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = readisac(sp->membase, ISAC_ISTA); - if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); + if (val && count < 20) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (stat & 1) { - writehscx(sp->membase, 0, HSCX_MASK, 0xFF); - writehscx(sp->membase, 1, HSCX_MASK, 0xFF); - writehscx(sp->membase, 0, HSCX_MASK, 0x0); - writehscx(sp->membase, 1, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); } if (stat & 2) { - writeisac(sp->membase, ISAC_MASK, 0xFF); - writeisac(sp->membase, ISAC_MASK, 0x0); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) -{ - unsigned int adr = sp->membase; - - /* 16.0 IOM 1 Mode */ - writeisac(adr, ISAC_MASK, 0xff); - writeisac(adr, ISAC_ADF2, 0x0); - writeisac(adr, ISAC_SPCR, 0xa); - writeisac(adr, ISAC_ADF1, 0x2); - writeisac(adr, ISAC_STCR, 0x70); - writeisac(adr, ISAC_MODE, 0xc9); - writeisac(adr, ISAC_CMDR, 0x41); - writeisac(adr, ISAC_CIX0, (1 << 2) | 3); - writeisac(adr, ISAC_MASK, 0xff); - writeisac(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writehscx(sp->membase, hscx, HSCX_CCR1, 0x85); - writehscx(sp->membase, hscx, HSCX_XAD1, 0xFF); - writehscx(sp->membase, hscx, HSCX_XAD2, 0xFF); - writehscx(sp->membase, hscx, HSCX_RAH2, 0xFF); - writehscx(sp->membase, hscx, HSCX_XBCH, 0x0); - - /* Switch IOM 1 SSI */ - if (hscx == 0) - ichan = 1 - ichan; - - switch (mode) { - case (0): - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0xff); - writehscx(sp->membase, hscx, HSCX_TSAR, 0xff); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - writehscx(sp->membase, hscx, HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0x7); - writehscx(sp->membase, hscx, HSCX_TSAR, 0x7); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - } else { - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0x3); - writehscx(sp->membase, hscx, HSCX_TSAR, 0x3); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - } - writehscx(sp->membase, hscx, HSCX_MODE, 0xe4); - writehscx(sp->membase, hscx, HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0x7); - writehscx(sp->membase, hscx, HSCX_TSAR, 0x7); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - } else { - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0x3); - writehscx(sp->membase, hscx, HSCX_TSAR, 0x3); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - } - writehscx(sp->membase, hscx, HSCX_MODE, 0x8c); - writehscx(sp->membase, hscx, HSCX_CMDR, 0x41); - break; - } - writehscx(sp->membase, hscx, HSCX_ISTA, 0x00); -} - void -release_io_teles0(struct IsdnCard *card) +release_io_teles0(struct IsdnCardState *cs) { - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 8); + if (cs->hw.teles0.cfg_reg) + release_region(cs->hw.teles0.cfg_reg, 8); } -static void -clear_pending_ints(struct IsdnCardState *sp) +static int +reset_teles0(struct IsdnCardState *cs) { - int val; - char tmp[64]; + u_char cfval; + long flags; - val = readhscx(sp->membase, 1, HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readhscx(sp->membase, 1, HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readhscx(sp->membase, 0, HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readhscx(sp->membase, 0, HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readhscx(sp->membase, 1, HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readhscx(sp->membase, 0, HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->membase, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->membase, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readisac(sp->membase, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readisac(sp->membase, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readisac(sp->membase, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readisac(sp->membase, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); + save_flags(flags); + sti(); + if (cs->hw.teles0.cfg_reg) { + switch (cs->irq) { + case 2: + case 9: + cfval = 0x00; + break; + case 3: + cfval = 0x02; + break; + case 4: + cfval = 0x04; + break; + case 5: + cfval = 0x06; + break; + case 10: + cfval = 0x08; + break; + case 11: + cfval = 0x0A; + break; + case 12: + cfval = 0x0C; + break; + case 15: + cfval = 0x0E; + break; + default: + return(1); + } + cfval |= ((cs->hw.teles0.membase >> 9) & 0xF0); + byteout(cs->hw.teles0.cfg_reg + 4, cfval); + HZDELAY(HZ / 10 + 1); + byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1); + HZDELAY(HZ / 10 + 1); } - writeisac(sp->membase, ISAC_MASK, 0); - writeisac(sp->membase, ISAC_CMDR, 0x41); + writeb(0, cs->hw.teles0.membase + 0x80); + HZDELAY(HZ / 5 + 1); + writeb(1, cs->hw.teles0.membase + 0x80); + HZDELAY(HZ / 5 + 1); + restore_flags(flags); + return(0); } -int -initteles0(struct IsdnCardState *sp) +static int +Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat_irqs(sp->irq); - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &telesS0_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat_irqs(sp->irq) > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat_irqs(sp->irq)); - debugl1(sp, tmp); - if (kstat_irqs(sp->irq) == sp->counter) { - printk(KERN_WARNING - "Teles0: IRQ(%d) getting no interrupts during init\n", - sp->irq); - free_irq(sp->irq, sp); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + reset_teles0(cs); + return(0); + case CARD_RELEASE: + release_io_teles0(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &teles0_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -int -setup_teles0(struct IsdnCard *card) +__initfunc(int +setup_teles0(struct IsdnCard *card)) { - u_char cfval, val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, teles0_revision); - printk(KERN_NOTICE "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp)); - if ((sp->typ != ISDN_CTYPE_16_0) && (sp->typ != ISDN_CTYPE_8_0)) + printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp)); + if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0)) return (0); - if (sp->typ == ISDN_CTYPE_16_0) - sp->cfg_reg = card->para[2]; + if (cs->typ == ISDN_CTYPE_16_0) + cs->hw.teles0.cfg_reg = card->para[2]; else /* 8.0 */ - sp->cfg_reg = 0; + cs->hw.teles0.cfg_reg = 0; if (card->para[1] < 0x10000) { card->para[1] <<= 4; @@ -859,110 +320,69 @@ setup_teles0(struct IsdnCard *card) "Teles0: membase configured DOSish, assuming 0x%lx\n", (unsigned long) card->para[1]); } - sp->membase = card->para[1]; - sp->irq = card->para[0]; - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 8)) { + cs->hw.teles0.membase = card->para[1]; + cs->irq = card->para[0]; + if (cs->hw.teles0.cfg_reg) { + if (check_region((cs->hw.teles0.cfg_reg), 8)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); + cs->hw.teles0.cfg_reg, + cs->hw.teles0.cfg_reg + 8); return (0); } else { - request_region(sp->cfg_reg, 8, "teles cfg"); + request_region(cs->hw.teles0.cfg_reg, 8, "teles cfg"); } } - switch (sp->irq) { - case 2: - cfval = 0x00; - break; - case 3: - cfval = 0x02; - break; - case 4: - cfval = 0x04; - break; - case 5: - cfval = 0x06; - break; - case 10: - cfval = 0x08; - break; - case 11: - cfval = 0x0A; - break; - case 12: - cfval = 0x0C; - break; - case 15: - cfval = 0x0E; - break; - default: - cfval = 0x00; - break; - } - cfval |= ((card->para[1] >> 9) & 0xF0); - if (sp->cfg_reg) { - if ((val = bytein(sp->cfg_reg + 0)) != 0x51) { + if (cs->hw.teles0.cfg_reg) { + if ((val = bytein(cs->hw.teles0.cfg_reg + 0)) != 0x51) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 0, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 0, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - if ((val = bytein(sp->cfg_reg + 1)) != 0x93) { + if ((val = bytein(cs->hw.teles0.cfg_reg + 1)) != 0x93) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 1, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 1, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - val = bytein(sp->cfg_reg + 2); /* 0x1e=without AB - * 0x1f=with AB - * 0x1c 16.3 ??? - */ + val = bytein(cs->hw.teles0.cfg_reg + 2); /* 0x1e=without AB + * 0x1f=with AB + * 0x1c 16.3 ??? + */ if (val != 0x1e && val != 0x1f) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 2, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 2, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - save_flags(flags); - byteout(sp->cfg_reg + 4, cfval); - sti(); - HZDELAY(HZ / 10 + 1); - byteout(sp->cfg_reg + 4, cfval | 1); - HZDELAY(HZ / 10 + 1); - restore_flags(flags); } - printk(KERN_NOTICE - "HiSax: %s config irq:%d mem:%x cfg:%x\n", - CardType[sp->typ], sp->irq, - sp->membase, sp->cfg_reg); - verA = readhscx(sp->membase, 0, HSCX_VSTR) & 0xf; - verB = readhscx(sp->membase, 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "Teles0: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readisac(sp->membase, ISAC_RBCH); - printk(KERN_INFO "Teles0: ISAC %s\n", - ISACVersion(val)); - - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + /* 16.0 and 8.0 designed for IOM1 */ + test_and_set_bit(HW_IOM1, &cs->HW_Flags); + printk(KERN_INFO + "HiSax: %s config irq:%d mem:0x%X cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.teles0.membase, cs->hw.teles0.cfg_reg); + if (reset_teles0(cs)) { + printk(KERN_WARNING "Teles0: wrong IRQ\n"); + release_io_teles0(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Teles_card_msg; + ISACVersion(cs, "Teles0:"); + if (HscxVersion(cs, "Teles0:")) { printk(KERN_WARNING "Teles0: wrong HSCX versions check IO/MEM addresses\n"); - release_io_teles0(card); + release_io_teles0(cs); return (0); } - save_flags(flags); - writeb(0, sp->membase + 0x80); - sti(); - HZDELAY(HZ / 5 + 1); - writeb(1, sp->membase + 0x80); - HZDELAY(HZ / 5 + 1); - restore_flags(flags); - - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c index 4b92e5ab7..261d054fb 100644 --- a/drivers/isdn/hisax/teles3.c +++ b/drivers/isdn/hisax/teles3.c @@ -1,4 +1,4 @@ -/* $Id: teles3.c,v 1.11 1997/04/13 19:54:05 keil Exp $ +/* $Id: teles3.c,v 2.7 1998/02/02 13:29:48 keil Exp $ * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * @@ -11,6 +11,30 @@ * Beat Doebeli * * $Log: teles3.c,v $ + * Revision 2.7 1998/02/02 13:29:48 keil + * fast io + * + * Revision 2.6 1997/11/13 16:22:44 keil + * COMPAQ_ISA reset + * + * Revision 2.5 1997/11/12 15:01:25 keil + * COMPAQ_ISA changes + * + * Revision 2.4 1997/11/08 21:35:56 keil + * new l1 init + * + * Revision 2.3 1997/11/06 17:09:33 keil + * New 2.1 init code + * + * Revision 2.2 1997/10/29 18:55:59 keil + * changes for 2.1.60 (irq2dev_map) + * + * Revision 2.1 1997/07/27 21:47:12 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:46 keil + * New Layer and card interface + * * Revision 1.11 1997/04/13 19:54:05 keil * Change in IRQ check delay for SMP * @@ -35,36 +59,20 @@ * Revision 1.6 1997/01/27 15:52:55 keil * SMP proof,cosmetics, PCMCIA added * - * Revision 1.5 1997/01/21 22:28:32 keil - * cleanups - * - * Revision 1.4 1996/12/14 21:05:41 keil - * Reset for 16.3 PnP - * - * Revision 1.3 1996/11/05 19:56:54 keil - * debug output fixed - * - * Revision 1.2 1996/10/27 22:09:15 keil - * cosmetic changes - * - * Revision 1.1 1996/10/13 20:04:59 keil - * Initial revision - * - * + * removed old log info /KKe * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles3.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include <linux/kernel_stat.h> extern const char *CardType[]; -const char *teles3_revision = "$Revision: 1.11 $"; +const char *teles3_revision = "$Revision: 2.7 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readreg(unsigned int adr, u_char off) @@ -82,953 +90,410 @@ writereg(unsigned int adr, u_char off, u_char data) static inline void read_fifo(unsigned int adr, u_char * data, int size) { - insb(adr + 0x1e, data, size); + insb(adr, data, size); } static void write_fifo(unsigned int adr, u_char * data, int size) { - outsb(adr + 0x1e, data, size); + outsb(adr, data, size); } -static inline void -waitforCEC(int adr) -{ - int to = 50; +/* Interface functions */ - while ((readreg(adr, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles3: waitforCEC timeout\n"); -} - - -static inline void -waitforXFW(int adr) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - int to = 50; - - while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles3: waitforXFW timeout\n"); + return (readreg(cs->hw.teles3.isac, offset)); } -static inline void -writehscxCMDR(int adr, u_char data) -{ - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr); - writereg(adr, HSCX_CMDR, data); - restore_flags(flags); -} - -/* - * fast interrupt here - */ - -static void -hscxreport(struct IsdnCardState *sp, int hscx) -{ - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR)); -} - -void -teles3_report(struct IsdnCardState *sp) -{ - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); -} - -/* - * HSCX stuff goes here - */ static void -hscx_empty_fifo(struct HscxState *hsp, int count) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.teles3.isac, offset, value); } static void -hscx_fill_fifo(struct HscxState *hsp) -{ - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx]); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!r & 0x80) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!r & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - } else { - count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - SET_SKB_FREE(hsp->tx_skb); - dev_kfree_skb(hsp->tx_skb); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + read_fifo(cs->hw.teles3.isacfifo, data, size); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo(cs->hw.teles3.isacfifo, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.teles3.hscx[hscx], offset)); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CIX0, (command << 2) | 3); + writereg(cs->hw.teles3.hscx[hscx], offset, value); } +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readreg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!exval & 0x20) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writereg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = readreg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - SET_SKB_FREE(sp->tx_skb); - dev_kfree_skb(sp->tx_skb); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readreg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} - -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ - - u_char exval; - struct HscxState *hsp; - char tmp[32]; +#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt) - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readreg(sp->hscx[1], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readreg(sp->hscx[0], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readreg(sp->hscx[0], HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) { #define MAXCOUNT 20 - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; int count = 0; - sp = (struct IsdnCardState *) dev_id; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Teles: Spurious interrupt!\n"); return; } - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } count++; - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); if (val && count < MAXCOUNT) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); if (val && count < MAXCOUNT) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (count >= MAXCOUNT) printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count); if (stat & 1) { - writereg(sp->hscx[0], HSCX_MASK, 0xFF); - writereg(sp->hscx[1], HSCX_MASK, 0xFF); - writereg(sp->hscx[0], HSCX_MASK, 0x0); - writereg(sp->hscx[1], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); } if (stat & 2) { - writereg(sp->isac, ISAC_MASK, 0xFF); - writereg(sp->isac, ISAC_MASK, 0x0); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) -{ - unsigned int adr = sp->isac; - - /* 16.3 IOM 2 Mode */ - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_ADF2, 0x80); - writereg(adr, ISAC_SQXR, 0x2f); - writereg(adr, ISAC_SPCR, 0x0); - writereg(adr, ISAC_ADF1, 0x2); - writereg(adr, ISAC_STCR, 0x70); - writereg(adr, ISAC_MODE, 0xc9); - writereg(adr, ISAC_TIMR, 0x0); - writereg(adr, ISAC_ADF1, 0x0); - writereg(adr, ISAC_CMDR, 0x41); - writereg(adr, ISAC_CIX0, (1 << 2) | 3); - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writereg(sp->hscx[hscx], HSCX_CCR1, 0x85); - writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF); - writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF); - writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF); - writereg(sp->hscx[hscx], HSCX_XBCH, 0x0); - writereg(sp->hscx[hscx], HSCX_RLCR, 0x0); - - switch (mode) { - case (0): - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0xff); - writereg(sp->hscx[hscx], HSCX_TSAR, 0xff); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - writereg(sp->hscx[hscx], HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0xe4); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0x8c); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - } - writereg(sp->hscx[hscx], HSCX_ISTA, 0x00); -} - inline static void -release_ioregs(struct IsdnCard *card, int mask) +release_ioregs(struct IsdnCardState *cs, int mask) { if (mask & 1) - release_region(card->sp->isac, 32); + release_region(cs->hw.teles3.isac + 32, 32); if (mask & 2) - release_region(card->sp->hscx[0], 32); + release_region(cs->hw.teles3.hscx[0] + 32, 32); if (mask & 4) - release_region(card->sp->hscx[1], 32); + release_region(cs->hw.teles3.hscx[1] + 32, 32); } void -release_io_teles3(struct IsdnCard *card) +release_io_teles3(struct IsdnCardState *cs) { - if (card->sp->typ == ISDN_CTYPE_TELESPCMCIA) - release_region(card->sp->hscx[0], 97); + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) + release_region(cs->hw.teles3.cfg_reg, 97); else { - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 8); - release_ioregs(card, 0x7); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 0x7); } } -static void -clear_pending_ints(struct IsdnCardState *sp) +static int +reset_teles3(struct IsdnCardState *cs) { - int val; - char tmp[64]; - - val = readreg(sp->hscx[1], HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->hscx[1], HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readreg(sp->hscx[0], HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readreg(sp->hscx[0], HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[1], HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[0], HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->isac, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readreg(sp->isac, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); + long flags; + u_char irqcfg; + + if (cs->typ != ISDN_CTYPE_TELESPCMCIA) { + if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) { + switch (cs->irq) { + case 2: + case 9: + irqcfg = 0x00; + break; + case 3: + irqcfg = 0x02; + break; + case 4: + irqcfg = 0x04; + break; + case 5: + irqcfg = 0x06; + break; + case 10: + irqcfg = 0x08; + break; + case 11: + irqcfg = 0x0A; + break; + case 12: + irqcfg = 0x0C; + break; + case 15: + irqcfg = 0x0E; + break; + default: + return(1); + } + save_flags(flags); + byteout(cs->hw.teles3.cfg_reg + 4, irqcfg); + sti(); + HZDELAY(HZ / 10 + 1); + byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1); + HZDELAY(HZ / 10 + 1); + restore_flags(flags); + } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + save_flags(flags); + byteout(cs->hw.teles3.cfg_reg, 0xff); + HZDELAY(2); + byteout(cs->hw.teles3.cfg_reg, 0x00); + HZDELAY(2); + restore_flags(flags); + } else { + /* Reset off for 16.3 PnP , thanks to Georg Acher */ + save_flags(flags); + byteout(cs->hw.teles3.isac + 0x3c, 0); + HZDELAY(2); + byteout(cs->hw.teles3.isac + 0x3c, 1); + HZDELAY(2); + restore_flags(flags); + } } - writereg(sp->isac, ISAC_MASK, 0); - writereg(sp->isac, ISAC_CMDR, 0x41); + return(0); } -int -initteles3(struct IsdnCardState *sp) +static int +Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat_irqs(sp->irq); - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &teles3_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - writereg(sp->hscx[sp->hs->hscx], HSCX_CMDR, 0x01); - sp->modehscx(sp->hs + 1, 0, 0); - writereg(sp->hscx[(sp->hs + 1)->hscx], HSCX_CMDR, 0x01); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat_irqs(sp->irq) > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d loop %d", sp->irq, - kstat_irqs(sp->irq), loop); - debugl1(sp, tmp); - if (kstat_irqs(sp->irq) <= sp->counter) { - printk(KERN_WARNING - "Teles3: IRQ(%d) getting no interrupts during init\n", - sp->irq); - free_irq(sp->irq, sp); - return (0); - } - } - return (ret); + switch (mt) { + case CARD_RESET: + reset_teles3(cs); + return(0); + case CARD_RELEASE: + release_io_teles3(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &teles3_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); } -int -setup_teles3(struct IsdnCard *card) +__initfunc(int +setup_teles3(struct IsdnCard *card)) { - u_char cfval = 0, val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, teles3_revision); - printk(KERN_NOTICE "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp)); - if ((sp->typ != ISDN_CTYPE_16_3) && (sp->typ != ISDN_CTYPE_PNP) - && (sp->typ != ISDN_CTYPE_TELESPCMCIA)) + printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp)); + if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP) + && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) return (0); - if (sp->typ == ISDN_CTYPE_16_3) { - sp->cfg_reg = card->para[1]; - switch (sp->cfg_reg) { + if (cs->typ == ISDN_CTYPE_16_3) { + cs->hw.teles3.cfg_reg = card->para[1]; + switch (cs->hw.teles3.cfg_reg) { case 0x180: case 0x280: case 0x380: - sp->cfg_reg |= 0xc00; + cs->hw.teles3.cfg_reg |= 0xc00; break; } - sp->isac = sp->cfg_reg - 0x400; - sp->hscx[0] = sp->cfg_reg - 0xc00; - sp->hscx[1] = sp->cfg_reg - 0x800; - } else if (sp->typ == ISDN_CTYPE_TELESPCMCIA) { - sp->cfg_reg = 0; - sp->hscx[0] = card->para[1]; - sp->hscx[1] = card->para[1] + 0x20; - sp->isac = card->para[1] + 0x40; - } else { /* PNP */ - sp->cfg_reg = 0; - sp->isac = card->para[1]; - sp->hscx[0] = card->para[2]; - sp->hscx[1] = card->para[2] + 0x20; - } - sp->irq = card->para[0]; - if (sp->typ == ISDN_CTYPE_TELESPCMCIA) { - if (check_region((sp->hscx[0]), 97)) { + cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420; + cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20; + cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820; + } else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { + cs->hw.teles3.cfg_reg = card->para[1]; + cs->hw.teles3.hscx[0] = card->para[1] - 0x20; + cs->hw.teles3.hscx[1] = card->para[1]; + cs->hw.teles3.isac = card->para[1] + 0x20; + } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + cs->hw.teles3.cfg_reg = card->para[3]; + cs->hw.teles3.isac = card->para[2] - 32; + cs->hw.teles3.hscx[0] = card->para[1] - 32; + cs->hw.teles3.hscx[1] = card->para[1]; + } else { /* PNP */ + cs->hw.teles3.cfg_reg = 0; + cs->hw.teles3.isac = card->para[1] - 32; + cs->hw.teles3.hscx[0] = card->para[2] - 32; + cs->hw.teles3.hscx[1] = card->para[2]; + } + cs->irq = card->para[0]; + cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e; + 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.cfg_reg), 97)) { printk(KERN_WARNING "HiSax: %s ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 96); + CardType[cs->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 96); return (0); } else - request_region(sp->hscx[0], 97, "HiSax Teles PCMCIA"); + request_region(cs->hw.teles3.hscx[0], 97, "HiSax Teles PCMCIA"); } else { - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 8)) { - printk(KERN_WARNING - "HiSax: %s config port %x-%x already in use\n", - CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); - return (0); + if (cs->hw.teles3.cfg_reg) { + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + if (check_region((cs->hw.teles3.cfg_reg), 1)) { + printk(KERN_WARNING + "HiSax: %s config port %x already in use\n", + CardType[card->typ], + cs->hw.teles3.cfg_reg); + return (0); + } else + request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg"); } else { - request_region(sp->cfg_reg, 8, "teles3 cfg"); + if (check_region((cs->hw.teles3.cfg_reg), 8)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 8); + return (0); + } else + request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg"); } } - if (check_region((sp->isac), 32)) { + if (check_region((cs->hw.teles3.isac + 32), 32)) { printk(KERN_WARNING "HiSax: %s isac ports %x-%x already in use\n", - CardType[sp->typ], - sp->isac, - sp->isac + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } + CardType[cs->typ], + cs->hw.teles3.isac + 32, + cs->hw.teles3.isac + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } return (0); - } else { - request_region(sp->isac, 32, "HiSax isac"); - } - if (check_region((sp->hscx[0]), 32)) { + } else + request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac"); + if (check_region((cs->hw.teles3.hscx[0] + 32), 32)) { printk(KERN_WARNING "HiSax: %s hscx A ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } - release_ioregs(card, 1); + CardType[cs->typ], + cs->hw.teles3.hscx[0] + 32, + cs->hw.teles3.hscx[0] + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 1); return (0); - } else { - request_region(sp->hscx[0], 32, "HiSax hscx A"); - } - if (check_region((sp->hscx[1]), 32)) { + } else + request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A"); + if (check_region((cs->hw.teles3.hscx[1] + 32), 32)) { printk(KERN_WARNING "HiSax: %s hscx B ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[1], - sp->hscx[1] + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } - release_ioregs(card, 3); + CardType[cs->typ], + cs->hw.teles3.hscx[1] + 32, + cs->hw.teles3.hscx[1] + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 3); return (0); - } else { - request_region(sp->hscx[1], 32, "HiSax hscx B"); - } - switch (sp->irq) { - case 2: - cfval = 0x00; - break; - case 3: - cfval = 0x02; - break; - case 4: - cfval = 0x04; - break; - case 5: - cfval = 0x06; - break; - case 10: - cfval = 0x08; - break; - case 11: - cfval = 0x0A; - break; - case 12: - cfval = 0x0C; - break; - case 15: - cfval = 0x0E; - break; - default: - cfval = 0x00; - break; - } + } else + request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B"); } - if (sp->cfg_reg) { - if ((val = bytein(sp->cfg_reg + 0)) != 0x51) { + if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) { + if ((val = bytein(cs->hw.teles3.cfg_reg + 0)) != 0x51) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 0, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 0, val); + release_io_teles3(cs); return (0); } - if ((val = bytein(sp->cfg_reg + 1)) != 0x93) { + if ((val = bytein(cs->hw.teles3.cfg_reg + 1)) != 0x93) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 1, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 1, val); + release_io_teles3(cs); return (0); } - val = bytein(sp->cfg_reg + 2); /* 0x1e=without AB - * 0x1f=with AB - * 0x1c 16.3 ??? - * 0x46 16.3 with AB + Video (Teles-Vision) - */ - if (val != 0x46 && val != 0x1c && val != 0x1e && val != 0x1f) { + val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB + * 0x1f=with AB + * 0x1c 16.3 ??? + * 0x39 16.3 1.1 + * 0x46 16.3 with AB + Video (Teles-Vision) + */ + if (val != 0x46 && val != 0x39 && val != 0x1c && val != 0x1e && val != 0x1f) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 2, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 2, val); + release_io_teles3(cs); return (0); } - save_flags(flags); - byteout(sp->cfg_reg + 4, cfval); - sti(); - HZDELAY(HZ / 10 + 1); - byteout(sp->cfg_reg + 4, cfval | 1); - HZDELAY(HZ / 10 + 1); - restore_flags(flags); - } else { - /* Reset off for 16.3 PnP , thanks to Georg Acher */ - save_flags(flags); - byteout(sp->isac + 0x1c, 1); - HZDELAY(2); - restore_flags(flags); } - printk(KERN_NOTICE - "HiSax: %s config irq:%d isac:%x cfg:%x\n", - CardType[sp->typ], sp->irq, - sp->isac, sp->cfg_reg); - printk(KERN_NOTICE - "HiSax: hscx A:%x hscx B:%x\n", - sp->hscx[0], sp->hscx[1]); - verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf; - verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf; - printk(KERN_INFO "Teles3: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readreg(sp->isac, ISAC_RBCH); - printk(KERN_INFO "Teles3: ISAC %s\n", - ISACVersion(val)); - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + printk(KERN_INFO + "HiSax: %s config irq:%d isac:0x%X cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg); + printk(KERN_INFO + "HiSax: hscx A:0x%X hscx B:0x%X\n", + cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32); + + if (reset_teles3(cs)) { + printk(KERN_WARNING "Teles3: wrong IRQ\n"); + release_io_teles3(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Teles_card_msg; + ISACVersion(cs, "Teles3:"); + if (HscxVersion(cs, "Teles3:")) { printk(KERN_WARNING "Teles3: wrong HSCX versions check IO address\n"); - release_io_teles3(card); + release_io_teles3(cs); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff --git a/drivers/isdn/hisax/teles3c.c b/drivers/isdn/hisax/teles3c.c new file mode 100644 index 000000000..848c46be9 --- /dev/null +++ b/drivers/isdn/hisax/teles3c.c @@ -0,0 +1,199 @@ +/* $Id: teles3c.c,v 1.2 1998/02/02 13:27:07 keil Exp $ + + * teles3c.c low level stuff for teles 16.3c + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: teles3c.c,v $ + * Revision 1.2 1998/02/02 13:27:07 keil + * New + * + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *teles163c_revision = "$Revision: 1.2 $"; + +static void +t163c_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat; + char tmp[32]; + + if (!cs) { + printk(KERN_WARNING "teles3c: Spurious interrupt!\n"); + return; + } + if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & + (stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) { + val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1); + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "teles3c: stat(%02x) s1(%02x)", stat, val); + debugl1(cs, tmp); + } + hfc2bds0_interrupt(cs, val); + } else { + if (cs->debug & L1_DEB_ISAC) { + sprintf(tmp, "teles3c: irq_no_irq stat(%02x)", stat); + debugl1(cs, tmp); + } + } +} + +static void +t163c_Timer(struct IsdnCardState *cs) +{ + cs->hw.hfcD.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80); + add_timer(&cs->hw.hfcD.timer); +*/ +} + +void +release_io_t163c(struct IsdnCardState *cs) +{ + release2bds0(cs); + del_timer(&cs->hw.hfcD.timer); + if (cs->hw.hfcD.addr) + release_region(cs->hw.hfcD.addr, 2); +} + +static void +reset_t163c(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "teles3c: resetting card\n"); + cs->hw.hfcD.cirm = HFCD_RESET | HFCD_MEM8K; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 3; + schedule(); + cs->hw.hfcD.cirm = HFCD_MEM8K; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + cs->hw.hfcD.cirm |= HFCD_INTB; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* INT B */ + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */ + cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE; + cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS | + HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC | + HFCD_INTS_DREC | HFCD_INTS_L1STATE; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */ + udelay(10); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */ + cs->hw.hfcD.mst_m = 0; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, HFCD_MASTER); /* HFC Master */ + cs->hw.hfcD.sctrl = 0; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl); + restore_flags(flags); +} + +static int +t163c_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + char tmp[32]; + + if (cs->debug & L1_DEB_ISAC) { + + sprintf(tmp, "teles3c: card_msg %x", mt); + debugl1(cs, tmp); + } + switch (mt) { + case CARD_RESET: + reset_t163c(cs); + return(0); + case CARD_RELEASE: + release_io_t163c(cs); + return(0); + case CARD_SETIRQ: + cs->hw.hfcD.timer.expires = jiffies + 75; + add_timer(&cs->hw.hfcD.timer); + return(request_irq(cs->irq, &t163c_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + init2bds0(cs); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (80*HZ)/1000; + schedule(); + cs->hw.hfcD.ctmt |= HFCD_TIM800; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + restore_flags(flags); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_t163c(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, teles163c_revision); + printk(KERN_INFO "HiSax: Teles 16.3c driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELES3C) + return (0); + cs->debug = 0xff; + cs->hw.hfcD.addr = card->para[1] & 0xfffe; + cs->irq = card->para[0]; + cs->hw.hfcD.cip = 0; + cs->hw.hfcD.int_s1 = 0; + cs->hw.hfcD.send = NULL; + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->hw.hfcD.bfifosize = 1024 + 512; + cs->hw.hfcD.dfifosize = 512; + cs->ph_state = 0; + cs->hw.hfcD.fifo = 255; + if (check_region((cs->hw.hfcD.addr), 2)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.hfcD.addr, + cs->hw.hfcD.addr + 2); + return (0); + } else { + request_region(cs->hw.hfcD.addr, 2, "teles3c isdn"); + } + /* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */ + outb(0x00, cs->hw.hfcD.addr); + outb(0x56, cs->hw.hfcD.addr | 1); + printk(KERN_INFO + "teles3c: defined at 0x%x IRQ %d HZ %d\n", + cs->hw.hfcD.addr, + cs->irq, HZ); + + set_cs_func(cs); + cs->hw.hfcD.timer.function = (void *) t163c_Timer; + cs->hw.hfcD.timer.data = (long) cs; + init_timer(&cs->hw.hfcD.timer); + reset_t163c(cs); + cs->cardmsg = &t163c_card_msg; + return (1); +} diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index 0d839512d..2cc0e17f4 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1,4 +1,4 @@ -/* $Id: icn.c,v 1.44 1997/03/30 16:51:26 calle Exp $ +/* $Id: icn.c,v 1.49 1998/02/13 11:14:15 keil Exp $ * ISDN low-level module for the ICN active ISDN-Card. * @@ -19,6 +19,25 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.c,v $ + * Revision 1.49 1998/02/13 11:14:15 keil + * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb() + * + * Revision 1.48 1997/10/10 15:56:14 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.47 1997/10/01 09:21:51 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.46 1997/08/21 22:38:33 fritz + * Fixed a typo. + * + * Revision 1.45 1997/06/21 10:42:06 fritz + * Added availability to select leased mode on only one channel. + * * Revision 1.44 1997/03/30 16:51:26 calle * changed calls to copy_from_user/copy_to_user and removed verify_area * were possible. @@ -190,7 +209,7 @@ #undef MAP_DEBUG static char -*revision = "$Revision: 1.44 $"; +*revision = "$Revision: 1.49 $"; static int icn_addcard(int, char *, char *); @@ -205,10 +224,20 @@ icn_free_queue(icn_card * card, int channel) { struct sk_buff_head *queue = &card->spqueue[channel]; struct sk_buff *skb; + unsigned long flags; while ((skb = skb_dequeue(queue))) dev_kfree_skb(skb); + save_flags(flags); + cli(); + card->xlen[channel] = 0; card->sndcount[channel] = 0; + if (card->xskb[channel]) { + card->xskb[channel] = NULL; + restore_flags(flags); + dev_kfree_skb(card->xskb[channel]); + } else + restore_flags(flags); } /* Put a value into a shift-register, highest bit first. @@ -440,12 +469,14 @@ icn_pollbchan_send(int channel, icn_card * card) struct sk_buff *skb; isdn_ctrl cmd; - if (!(card->sndcount[channel] || + if (!(card->sndcount[channel] || card->xskb[channel] || skb_queue_len(&card->spqueue[channel]))) return; if (icn_trymaplock_channel(card, mch)) { - while (sbfree && (card->sndcount[channel] || - skb_queue_len(&card->spqueue[channel]))) { + while (sbfree && + (card->sndcount[channel] || + skb_queue_len(&card->spqueue[channel]) || + card->xskb[channel])) { save_flags(flags); cli(); if (card->xmit_lock[channel]) { @@ -454,7 +485,19 @@ icn_pollbchan_send(int channel, icn_card * card) } card->xmit_lock[channel]++; restore_flags(flags); - skb = skb_dequeue(&card->spqueue[channel]); + skb = card->xskb[channel]; + if (!skb) { + skb = skb_dequeue(&card->spqueue[channel]); + if (skb) { + /* Pop ACK-flag off skb. + * Store length to xlen. + */ + if (*(skb_pull(skb,1))) + card->xlen[channel] = skb->len; + else + card->xlen[channel] = 0; + } + } if (!skb) break; if (skb->len > ICN_FRAGSIZE) { @@ -471,13 +514,22 @@ icn_pollbchan_send(int channel, icn_card * card) sbnext; /* switch to next buffer */ icn_maprelease_channel(card, mch & 2); if (!skb->len) { - dev_kfree_skb(skb); - cmd.command = ISDN_STAT_BSENT; - cmd.driver = card->myid; - cmd.arg = channel; - card->interface.statcallb(&cmd); - } else - skb_queue_head(&card->spqueue[channel], skb); + save_flags(flags); + cli(); + if (card->xskb[channel]) { + card->xskb[channel] = NULL; + restore_flags(flags); + dev_kfree_skb(skb); + } else + restore_flags(flags); + if (card->xlen[channel]) { + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = channel; + cmd.parm.length = card->xlen[channel]; + card->interface.statcallb(&cmd); + } + } card->xmit_lock[channel] = 0; if (!icn_trymaplock_channel(card, mch)) break; @@ -557,12 +609,11 @@ static icn_stat icn_stat_table[] = * This routine is called periodically via timer. */ -static int +static void icn_parse_status(u_char * status, int channel, icn_card * card) { icn_stat *s = icn_stat_table; int action = -1; - int dflag = 0; unsigned long flags; isdn_ctrl cmd; @@ -575,7 +626,7 @@ icn_parse_status(u_char * status, int channel, icn_card * card) s++; } if (action == -1) - return 0; + return; cmd.driver = card->myid; cmd.arg = channel; switch (action) { @@ -591,7 +642,6 @@ icn_parse_status(u_char * status, int channel, icn_card * card) cli(); card->rcvidx[channel] = 0; restore_flags(flags); - dflag |= (channel + 1); break; case 3: { @@ -645,7 +695,6 @@ icn_parse_status(u_char * status, int channel, icn_card * card) strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1); break; case 8: - dflag = 3; card->flags &= ~ICN_FLAGS_B1ACTIVE; icn_free_queue(card, 0); save_flags(flags); @@ -675,7 +724,7 @@ icn_parse_status(u_char * status, int channel, icn_card * card) break; } card->interface.statcallb(&cmd); - return dflag; + return; } static void @@ -701,7 +750,6 @@ icn_polldchan(unsigned long data) icn_card *card = (icn_card *) data; int mch = card->secondhalf ? 2 : 0; int avail = 0; - int dflag = 0; int left; u_char c; int ch; @@ -722,7 +770,7 @@ icn_polldchan(unsigned long data) card->imsg[1] <= '2' && card->imsg[2] == ';') { ch = (card->imsg[1] - '0') - 1; p = &card->imsg[3]; - dflag |= icn_parse_status(p, ch, card); + icn_parse_status(p, ch, card); } else { p = card->imsg; if (!strncmp(p, "DRV1.", 5)) { @@ -771,10 +819,6 @@ icn_polldchan(unsigned long data) cmd.arg = avail; card->interface.statcallb(&cmd); } - if (dflag & 1) - card->interface.rcvcallb(card->myid, 0, card->rcvbuf[0], 0); - if (dflag & 2) - card->interface.rcvcallb(card->myid, 1, card->rcvbuf[1], 0); if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) if (!(card->flags & ICN_FLAGS_RBTIMER)) { /* schedule b-channel polling */ @@ -807,7 +851,7 @@ icn_polldchan(unsigned long data) */ static int -icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card) +icn_sendbuf(int channel, int ack, struct sk_buff *skb, icn_card * card) { int len = skb->len; unsigned long flags; @@ -827,6 +871,10 @@ icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card) cli(); nskb = skb_clone(skb, GFP_ATOMIC); if (nskb) { + /* Push ACK flag as one + * byte in front of data. + */ + *(skb_push(nskb, 1)) = ack?1:0; skb_queue_tail(&card->spqueue[channel], nskb); dev_kfree_skb(skb); } else @@ -1382,7 +1430,8 @@ icn_command(isdn_ctrl * c, icn_card * card) } current->timeout = jiffies + ICN_BOOT_TIMEOUT1; schedule(); - sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n"); + sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n", + (a & 1)?'1':'C', (a & 2)?'2':'C'); i = icn_writecmd(cbuf, strlen(cbuf), 0, card); printk(KERN_INFO "icn: (%s) Leased-line mode enabled\n", @@ -1638,14 +1687,14 @@ if_readstatus(u_char * buf, int len, int user, int id, int channel) } static int -if_sendbuf(int id, int channel, struct sk_buff *skb) +if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) { icn_card *card = icn_findcard(id); if (card) { if (!card->flags & ICN_FLAGS_RUNNING) return -ENODEV; - return (icn_sendbuf(channel, skb, card)); + return (icn_sendbuf(channel, ack, skb, card)); } printk(KERN_ERR "icn: if_sendbuf called with invalid driverId!\n"); @@ -1669,6 +1718,7 @@ icn_initcard(int port, char *id) } memset((char *) card, 0, sizeof(icn_card)); card->port = port; + card->interface.hl_hdrlen = 1; card->interface.channels = ICN_BCH; card->interface.maxbufsize = 4000; card->interface.command = if_command; @@ -1781,11 +1831,7 @@ icn_init(void) dev.firstload = 1; /* No symbols to export, hide all symbols */ -#if (LINUX_VERSION_CODE < 0x020111) - register_symtab(NULL); -#else EXPORT_NO_SYMBOLS; -#endif if ((p = strchr(revision, ':'))) { strcpy(rev, p + 1); diff --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h index 991d57e4d..3bd2819ce 100644 --- a/drivers/isdn/icn/icn.h +++ b/drivers/isdn/icn/icn.h @@ -1,4 +1,4 @@ -/* $Id: icn.h,v 1.26 1997/02/14 12:23:16 fritz Exp $ +/* $Id: icn.h,v 1.28 1997/10/10 15:56:18 fritz Exp $ * ISDN lowlevel-module for the ICN active ISDN-Card. * @@ -19,6 +19,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.h,v $ + * Revision 1.28 1997/10/10 15:56:18 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * + * Revision 1.27 1997/10/01 09:21:56 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.26 1997/02/14 12:23:16 fritz * Added support for new insmod parameter handling. * @@ -265,6 +275,9 @@ typedef struct icn_card { char *msg_buf_read; /* Readpointer for statusbuffer */ char *msg_buf_end; /* Pointer to end of statusbuffer */ int sndcount[ICN_BCH]; /* Byte-counters for B-Ch.-send */ + int xlen[ICN_BCH]; /* Byte-counters/Flags for sent-ACK */ + struct sk_buff *xskb[ICN_BCH]; + /* Current transmitted skb */ struct sk_buff_head spqueue[ICN_BCH]; /* Sendqueue */ char regname[35]; /* Name used for request_region */ @@ -303,7 +316,6 @@ static char *icn_id = "\0"; static char *icn_id2 = "\0"; #ifdef MODULE -#if (LINUX_VERSION_CODE > 0x020111) MODULE_AUTHOR("Fritz Elfert"); MODULE_PARM(portbase, "i"); MODULE_PARM_DESC(portbase, "Port adress of first card"); @@ -314,7 +326,6 @@ MODULE_PARM_DESC(icn_id, "ID-String of first card"); MODULE_PARM(icn_id2, "s"); MODULE_PARM_DESC(icn_id2, "ID-String of first card, second S0 (4B only)"); #endif -#endif #endif /* __KERNEL__ */ diff --git a/drivers/isdn/isdn_audio.c b/drivers/isdn/isdn_audio.c index 84da94294..d097366ed 100644 --- a/drivers/isdn/isdn_audio.c +++ b/drivers/isdn/isdn_audio.c @@ -1,4 +1,4 @@ -/* $Id: isdn_audio.c,v 1.8 1997/03/02 14:29:16 fritz Exp $ +/* $Id: isdn_audio.c,v 1.10 1998/02/20 17:09:40 fritz Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * @@ -20,6 +20,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.c,v $ + * Revision 1.10 1998/02/20 17:09:40 fritz + * Changes for recent kernels. + * + * Revision 1.9 1997/10/01 09:20:25 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.8 1997/03/02 14:29:16 fritz * More ttyI related cleanup. * @@ -53,7 +61,7 @@ #include "isdn_audio.h" #include "isdn_common.h" -char *isdn_audio_revision = "$Revision: 1.8 $"; +char *isdn_audio_revision = "$Revision: 1.10 $"; /* * Misc. lookup-tables. @@ -531,7 +539,6 @@ isdn_audio_goertzel(int *sample, modem_info * info) info->line); return; } - SET_SKB_FREE(skb); result = (int *) skb_put(skb, sizeof(int) * NCOEFF); for (k = 0; k < NCOEFF; k++) { sk = sk1 = sk2 = 0; diff --git a/drivers/isdn/isdn_cards.c b/drivers/isdn/isdn_cards.c index c7f4e8b08..30a4a7703 100644 --- a/drivers/isdn/isdn_cards.c +++ b/drivers/isdn/isdn_cards.c @@ -1,4 +1,4 @@ -/* $Id: isdn_cards.c,v 1.6 1997/04/23 18:56:03 fritz Exp $ +/* $Id: isdn_cards.c,v 1.7 1998/02/20 17:24:28 fritz Exp $ * Linux ISDN subsystem, initialization for non-modularized drivers. * @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.c,v $ + * Revision 1.7 1998/02/20 17:24:28 fritz + * Added ACT2000 init. + * * Revision 1.6 1997/04/23 18:56:03 fritz * Old Teles driver removed, Changed doc and scripts accordingly. * @@ -82,4 +85,7 @@ isdn_cards_init(void) capi_init(); capidrv_init(); #endif +#if CONFIG_ISDN_DRV_ACT2000 + act2000_init(); +#endif } diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index c97c7781c..3f4953f06 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -1,4 +1,4 @@ -/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $ +/* $Id: isdn_common.c,v 1.55 1998/02/23 23:35:32 fritz Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -21,6 +21,55 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.55 1998/02/23 23:35:32 fritz + * Eliminated some compiler warnings. + * + * Revision 1.54 1998/02/22 19:44:19 fritz + * Bugfixes and improvements regarding V.110, V.110 now running. + * + * Revision 1.53 1998/02/20 17:18:05 fritz + * Changes for recent kernels. + * Added common stub for sending commands to lowlevel. + * Added V.110. + * + * Revision 1.52 1998/01/31 22:05:57 keil + * Lots of changes for X.25 support: + * Added generic support for connection-controlling encapsulation protocols + * Added support of BHUP status message + * Added support for additional p_encap X25IFACE + * Added support for kernels >= 2.1.72 + * + * Revision 1.51 1998/01/31 19:17:29 calle + * merged changes from and for 2.1.82 + * + * Revision 1.50 1997/12/12 06:12:11 calle + * moved EXPORT_SYMBOL(register_isdn) from isdn_syms.c to isdn_common.c + * + * Revision 1.49 1997/11/06 17:16:52 keil + * Sync to 2.1.62 changes + * + * Revision 1.48 1997/11/02 23:55:50 keil + * Andi Kleen's changes for 2.1.60 + * without it the isdninfo and isdnctrl devices won't work + * + * Revision 1.47 1997/10/09 21:28:46 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.46 1997/10/01 09:20:27 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.45 1997/08/21 23:11:41 fritz + * Added changes for kernels >= 2.1.45 + * * Revision 1.44 1997/05/27 15:17:23 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -197,12 +246,9 @@ */ #include <linux/config.h> -#define __NO_VERSION__ #include <linux/module.h> #include <linux/version.h> -#if (LINUX_VERSION_CODE >= 0x020117) #include <linux/poll.h> -#endif #include <linux/isdn.h> #include "isdn_common.h" #include "isdn_tty.h" @@ -211,6 +257,7 @@ #ifdef CONFIG_ISDN_AUDIO #include "isdn_audio.h" #endif +#include "isdn_v110.h" #include "isdn_cards.h" /* Debugflags */ @@ -218,7 +265,7 @@ isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.44 $"; +static char *isdn_revision = "$Revision: 1.55 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -232,6 +279,7 @@ extern char *isdn_audio_revision; #else static char *isdn_audio_revision = ": none $"; #endif +extern char *isdn_v110_revision; static int isdn_writebuf_stub(int, int, const u_char *, int, int); @@ -260,13 +308,6 @@ isdn_dumppkt(char *s, u_char * p, int len, int dumplen) } #endif -static __inline void -isdn_trash_skb(struct sk_buff *skb) -{ - SET_SKB_FREE(skb); - kfree_skb(skb); -} - static void isdn_free_queue(struct sk_buff_head *queue) { @@ -277,7 +318,7 @@ isdn_free_queue(struct sk_buff_head *queue) cli(); if (skb_queue_len(queue)) while ((skb = skb_dequeue(queue))) - isdn_trash_skb(skb); + dev_kfree_skb(skb); restore_flags(flags); } @@ -294,6 +335,7 @@ isdn_dc2minor(int di, int ch) static int isdn_timer_cnt1 = 0; static int isdn_timer_cnt2 = 0; static int isdn_timer_cnt3 = 0; +static int isdn_timer_cnt4 = 0; static void isdn_timer_funct(ulong dummy) @@ -323,6 +365,11 @@ isdn_timer_funct(ulong dummy) if (tf & ISDN_TIMER_MODEMRING) isdn_tty_modem_ring(); } + if (++isdn_timer_cnt4 > ISDN_TIMER_KEEPINT) { + isdn_timer_cnt4 = 0; + if (tf & ISDN_TIMER_KEEPALIVE) + isdn_net_slarp_out(); + } #if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP) if (tf & ISDN_TIMER_IPPP) isdn_ppp_timer_timeout(); @@ -374,22 +421,71 @@ isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb) int i; if ((i = isdn_dc2minor(di, channel)) == -1) { - isdn_trash_skb(skb); + dev_kfree_skb(skb); return; } /* Update statistics */ dev->ibytes[i] += skb->len; + /* First, try to deliver data to network-device */ if (isdn_net_rcv_skb(i, skb)) return; + + /* V.110 handling + * makes sense for async streams only, so it is + * called after possible net-device delivery. + */ + if (dev->v110[i]) { + atomic_inc(&dev->v110use[i]); + skb = isdn_v110_decode(dev->v110[i], skb); + atomic_dec(&dev->v110use[i]); + if (!skb) + return; + } + /* No network-device found, deliver to tty or raw-channel */ - SET_SKB_FREE(skb); if (skb->len) { if (isdn_tty_rcv_skb(i, di, channel, skb)) return; wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]); } else - isdn_trash_skb(skb); + dev_kfree_skb(skb); +} + +/* + * Intercept command from Linklevel to Lowlevel. + * If layer 2 protocol is V.110 and this is not supported by current + * lowlevel-driver, use driver's transparent mode and handle V.110 in + * linklevel instead. + */ +int +isdn_command(isdn_ctrl *cmd) +{ + 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; + } + } + return dev->drv[cmd->driver]->interface->command(cmd); } void @@ -403,7 +499,7 @@ isdn_all_eaz(int di, int ch) cmd.arg = ch; cmd.command = ISDN_CMD_SETEAZ; cmd.parm.num[0] = '\0'; - (void) dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); } static int @@ -424,7 +520,9 @@ isdn_status_callback(isdn_ctrl * c) return -1; if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) + return 0; + if (isdn_v110_stat_callback(i, c)) return 0; if (isdn_tty_stat_callback(i, c)) return 0; @@ -456,14 +554,14 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); return 0; } /* Try to find a network-interface which will accept incoming call */ cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_LOCK; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); r = isdn_net_find_icall(di, c->arg, i, c->parm.setup); switch (r) { case 0: @@ -476,7 +574,7 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); retval = 2; } break; @@ -486,7 +584,7 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_ACCEPTD; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); retval = 1; break; case 2: /* For calling back, first reject incoming call ... */ @@ -496,7 +594,7 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); if (r == 3) break; /* Fall through */ @@ -509,7 +607,7 @@ isdn_status_callback(isdn_ctrl * c) cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_UNLOCK; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); } return retval; break; @@ -522,7 +620,8 @@ isdn_status_callback(isdn_ctrl * c) if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; if (strcmp(c->parm.num, "0")) - isdn_net_stat_callback(i, c->command); + isdn_net_stat_callback(i, c); + isdn_tty_stat_callback(i, c); break; case ISDN_STAT_CAUSE: #ifdef ISDN_DEBUG_STATCALLB @@ -541,14 +640,15 @@ isdn_status_callback(isdn_ctrl * c) if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; /* Find any net-device, waiting for D-channel setup */ - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; + isdn_v110_stat_callback(i, c); /* Find any ttyI, waiting for D-channel setup */ if (isdn_tty_stat_callback(i, c)) { cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_ACCEPTB; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); break; } break; @@ -563,8 +663,9 @@ isdn_status_callback(isdn_ctrl * c) dev->drv[di]->flags &= ~(1 << (c->arg)); isdn_info_update(); /* Signal hangup to network-devices */ - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; + isdn_v110_stat_callback(i, c); if (isdn_tty_stat_callback(i, c)) break; break; @@ -579,8 +680,9 @@ isdn_status_callback(isdn_ctrl * c) return 0; dev->drv[di]->flags |= (1 << (c->arg)); isdn_info_update(); - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; + isdn_v110_stat_callback(i, c); if (isdn_tty_stat_callback(i, c)) break; break; @@ -594,6 +696,12 @@ isdn_status_callback(isdn_ctrl * c) return 0; dev->drv[di]->flags &= ~(1 << (c->arg)); isdn_info_update(); +#ifdef CONFIG_ISDN_X25 + /* Signal hangup to network-devices */ + if (isdn_net_stat_callback(i, c)) + break; +#endif + isdn_v110_stat_callback(i, c); if (isdn_tty_stat_callback(i, c)) break; break; @@ -605,7 +713,7 @@ isdn_status_callback(isdn_ctrl * c) #endif if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; if (isdn_tty_stat_callback(i, c)) break; @@ -636,6 +744,8 @@ isdn_status_callback(isdn_ctrl * c) isdn_info_update(); restore_flags(flags); return 0; + case ISDN_STAT_L1ERR: + break; default: return -1; } @@ -752,7 +862,7 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user ISDN_AUDIO_SKB_LOCK(skb) = 0; #endif skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]); - isdn_trash_skb(skb); + dev_kfree_skb(skb); } else { /* Not yet emptied this buff, so it * must stay in the queue, for further calls @@ -848,8 +958,8 @@ isdn_info_update(void) wake_up_interruptible(&(dev->info_waitq)); } -static RWTYPE -isdn_read(struct file *file, char *buf, RWARG count, loff_t *off) +static ssize_t +isdn_read(struct file *file, char *buf, size_t count, loff_t * off) { uint minor = MINOR(file->f_dentry->d_inode->i_rdev); int len = 0; @@ -857,6 +967,9 @@ isdn_read(struct file *file, char *buf, RWARG count, loff_t *off) int drvidx; int chidx; + if (off != &file->f_pos) + return -ESPIPE; + if (minor == ISDN_MINOR_STATUS) { char *p; if (!file->private_data) { @@ -922,18 +1035,22 @@ isdn_read(struct file *file, char *buf, RWARG count, loff_t *off) return -ENODEV; } -static LSTYPE isdn_lseek(struct file *file, LSARG offset, int orig) +static loff_t +isdn_lseek(struct file *file, loff_t offset, int orig) { return -ESPIPE; } -static RWTYPE -isdn_write(struct file *file, const char *buf, RWARG count, loff_t * off) +static ssize_t +isdn_write(struct file *file, const char *buf, size_t count, loff_t * off) { uint minor = MINOR(file->f_dentry->d_inode->i_rdev); int drvidx; int chidx; + if (off != &file->f_pos) + return -ESPIPE; + if (minor == ISDN_MINOR_STATUS) return -EPERM; if (!dev->drivers) @@ -972,41 +1089,6 @@ isdn_write(struct file *file, const char *buf, RWARG count, loff_t * off) return -ENODEV; } -#if (LINUX_VERSION_CODE < 0x020117) -static int -isdn_select(struct inode *inode, struct file *file, int type, select_table * st) -{ - uint minor = MINOR(inode->i_rdev); - int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); - - if (minor == ISDN_MINOR_STATUS) { - if (file->private_data) - return 1; - else { - if (st) - select_wait(&(dev->info_waitq), st); - return 0; - } - } - if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) { - if (drvidx < 0) - return -ENODEV; - if (dev->drv[drvidx]->stavail) - return 1; - else { - if (st) - select_wait(&(dev->drv[drvidx]->st_waitq), st); - return 0; - } - return 1; - } -#ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return (isdn_ppp_select(minor - ISDN_MINOR_PPP, file, type, st)); -#endif - return -ENODEV; -} -#else static unsigned int isdn_poll(struct file *file, poll_table * wait) { @@ -1041,7 +1123,6 @@ isdn_poll(struct file *file, poll_table * wait) printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n"); return POLLERR; } -#endif static int isdn_set_allcfg(char *src) @@ -1055,7 +1136,7 @@ isdn_set_allcfg(char *src) if ((ret = isdn_net_rmall())) return ret; if ((ret = copy_from_user((char *) &i, src, sizeof(int)))) - return ret; + return ret; save_flags(flags); cli(); src += sizeof(int); @@ -1082,7 +1163,7 @@ isdn_set_allcfg(char *src) restore_flags(flags); return ret; } - GET_USER(phone.phone[phone_len], src++); + get_user(phone.phone[phone_len], src++); if ((phone.phone[phone_len] == ' ') || (phone.phone[phone_len] == '\0')) { if (phone_len) { @@ -1122,28 +1203,30 @@ isdn_get_allcfg(char *dest) cli(); p = dev->netdev; while (p) { + isdn_net_local *lp = p->local; + if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) { restore_flags(flags); return ret; } - strcpy(cfg.eaz, p->local.msn); - cfg.exclusive = p->local.exclusive; - if (p->local.pre_device >= 0) { - sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device], - p->local.pre_channel); + strcpy(cfg.eaz, lp->msn); + cfg.exclusive = lp->exclusive; + if (lp->pre_device >= 0) { + sprintf(cfg.drvid, "%s,%d", dev->drvid[lp->pre_device], + lp->pre_channel); } else cfg.drvid[0] = '\0'; - cfg.onhtime = p->local.onhtime; - cfg.charge = p->local.charge; - cfg.l2_proto = p->local.l2_proto; - cfg.l3_proto = p->local.l3_proto; - cfg.p_encap = p->local.p_encap; - cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0; - cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0; - cfg.chargehup = (p->local.hupflags & ISDN_CHARGEHUP) ? 1 : 0; - cfg.ihup = (p->local.hupflags & ISDN_INHUP) ? 1 : 0; - cfg.chargeint = p->local.chargeint; - if (copy_to_user(dest, p->local.name, 10)) { + cfg.onhtime = lp->onhtime; + cfg.charge = lp->charge; + cfg.l2_proto = lp->l2_proto; + cfg.l3_proto = lp->l3_proto; + cfg.p_encap = lp->p_encap; + cfg.secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0; + cfg.callback = (lp->flags & ISDN_NET_CALLBACK) ? 1 : 0; + cfg.chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0; + cfg.ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0; + cfg.chargeint = lp->chargeint; + if (copy_to_user(dest, lp->name, 10)) { restore_flags(flags); return -EFAULT; } @@ -1153,14 +1236,14 @@ isdn_get_allcfg(char *dest) return -EFAULT; } dest += sizeof(cfg); - strcpy(phone.name, p->local.name); + strcpy(phone.name, lp->name); phone.outgoing = 0; if ((ret = isdn_net_getphones(&phone, dest)) < 0) { restore_flags(flags); return ret; } else dest += ret; - strcpy(phone.name, p->local.name); + strcpy(phone.name, lp->name); phone.outgoing = 1; if ((ret = isdn_net_getphones(&phone, dest)) < 0) { restore_flags(flags); @@ -1343,9 +1426,9 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) /* Force hangup of a network-interface */ if (!arg) return -EINVAL; - if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) - return ret; - return isdn_net_force_hangup(name); + if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) + return ret; + return isdn_net_force_hangup(name); break; #endif /* CONFIG_NETDEVICES */ case IIOCSETVER: @@ -1366,7 +1449,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) int i; char *p; if ((ret = copy_from_user((char *) &iocts, (char *) arg, - sizeof(isdn_ioctl_struct)))) + sizeof(isdn_ioctl_struct)))) return ret; if (strlen(iocts.drvid)) { if ((p = strchr(iocts.drvid, ','))) @@ -1440,7 +1523,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if ((ret = copy_from_user(dev->mdm.info[i].emu.profile, p, - ISDN_MODEM_ANZREG))) + ISDN_MODEM_ANZREG))) return ret; p += ISDN_MODEM_ANZREG; if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))) @@ -1482,7 +1565,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) while (1) { if ((ret = verify_area(VERIFY_READ, p, 1))) return ret; - GET_USER(bname[j], p++); + get_user(bname[j], p++); switch (bname[j]) { case '\0': loop = 0; @@ -1554,7 +1637,7 @@ isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) c.command = ISDN_CMD_IOCTL; c.arg = cmd; memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong)); - ret = dev->drv[drvidx]->interface->command(&c); + ret = isdn_command(&c); memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong)); if ((copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)))) return -EFAULT; @@ -1616,7 +1699,7 @@ isdn_open(struct inode *ino, struct file *filep) return -ENODEV; c.command = ISDN_CMD_LOCK; c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); + isdn_command(&c); MOD_INC_USE_COUNT; return 0; } @@ -1627,7 +1710,7 @@ isdn_open(struct inode *ino, struct file *filep) c.command = ISDN_CMD_LOCK; c.driver = drvidx; MOD_INC_USE_COUNT; - (void) dev->drv[drvidx]->interface->command(&c); + isdn_command(&c); return 0; } #ifdef CONFIG_ISDN_PPP @@ -1641,7 +1724,7 @@ isdn_open(struct inode *ino, struct file *filep) return -ENODEV; } -static CLOSETYPE +static int isdn_close(struct inode *ino, struct file *filep) { uint minor = MINOR(ino->i_rdev); @@ -1659,39 +1742,39 @@ isdn_close(struct inode *ino, struct file *filep) else dev->infochain = (infostruct *) (p->next); kfree(p); - return CLOSEVAL; + return 0; } q = p; p = (infostruct *) (p->next); } printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n"); - return CLOSEVAL; + return 0; } if (minor < ISDN_MINOR_CTRL) { drvidx = isdn_minor2drv(minor); if (drvidx < 0) - return CLOSEVAL; + return 0; c.command = ISDN_CMD_UNLOCK; c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); - return CLOSEVAL; + isdn_command(&c); + return 0; } if (minor <= ISDN_MINOR_CTRLMAX) { drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (drvidx < 0) - return CLOSEVAL; + return 0; if (dev->profd == current) dev->profd = NULL; c.command = ISDN_CMD_UNLOCK; c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); - return CLOSEVAL; + isdn_command(&c); + return 0; } #ifdef CONFIG_ISDN_PPP if (minor <= ISDN_MINOR_PPPMAX) isdn_ppp_release(minor - ISDN_MINOR_PPP, filep); #endif - return CLOSEVAL; + return 0; } static struct file_operations isdn_fops = @@ -1700,11 +1783,7 @@ static struct file_operations isdn_fops = isdn_read, isdn_write, NULL, /* isdn_readdir */ -#if (LINUX_VERSION_CODE < 0x020117) - isdn_select, /* isdn_select */ -#else isdn_poll, /* isdn_poll */ -#endif isdn_ioctl, /* isdn_ioctl */ NULL, /* isdn_mmap */ isdn_open, @@ -1731,6 +1810,8 @@ isdn_map_eaz2msn(char *msn, int di) * Find an unused ISDN-channel, whose feature-flags match the * given L2- and L3-protocols. */ +#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038)) + int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev ,int pre_chan) @@ -1738,11 +1819,18 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev int i; ulong flags; ulong features; + ulong vfeatures; isdn_ctrl cmd; save_flags(flags); cli(); - features = (1 << l2_proto) | (0x100 << l3_proto); + features = ((1 << l2_proto) | (0x10000 << l3_proto)); + vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) & + ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038)); + /* If Layer-2 protocol is V.110, accept drivers with + * transparent feature even if these don't support V.110 + * because we can emulate this in linklevel. + */ for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (USG_NONE(dev->usage[i]) && (dev->drvmap[i] != -1)) { @@ -1751,7 +1839,9 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev ((pre_dev != d) || (pre_chan != dev->chanmap[i]))) continue; if ((dev->drv[d]->running)) { - if ((dev->drv[d]->interface->features & features) == features) { + if (((dev->drv[d]->interface->features & features) == features) || + (((dev->drv[d]->interface->features & vfeatures) == vfeatures) && + (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) { if ((pre_dev < 0) || (pre_chan < 0)) { dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; dev->usage[i] |= usage; @@ -1759,7 +1849,7 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev cmd.driver = d; cmd.arg = 0; cmd.command = ISDN_CMD_LOCK; - (void) dev->drv[d]->interface->command(&cmd); + isdn_command(&cmd); restore_flags(flags); return i; } else { @@ -1770,7 +1860,7 @@ isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev cmd.driver = d; cmd.arg = 0; cmd.command = ISDN_CMD_LOCK; - (void) dev->drv[d]->interface->command(&cmd); + isdn_command(&cmd); restore_flags(flags); return i; } @@ -1808,7 +1898,7 @@ isdn_free_channel(int di, int ch, int usage) cmd.arg = ch; cmd.command = ISDN_CMD_UNLOCK; restore_flags(flags); - (void) dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); return; } restore_flags(flags); @@ -1837,31 +1927,6 @@ isdn_unexclusive_channel(int di, int ch) } /* - * receive callback handler for drivers not supporting sk_buff's. - * Parameters: - * - * di = Driver-Index. - * channel = Number of B-Channel (0...) - * buf = pointer to packet-data - * len = Length of packet-data - * - */ -static void -isdn_receive_callback(int drvidx, int chan, u_char * buf, int len) -{ - struct sk_buff *skb; - - if (dev->global_flags & ISDN_GLOBAL_STOPPED) - return; - skb = dev_alloc_skb(len); - if (skb) { - memcpy(skb_put(skb, len), buf, len); - isdn_receive_skb_callback(drvidx, chan, skb); - } else - printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n"); -} - -/* * writebuf replacement for SKB_ABLE drivers */ static int @@ -1869,60 +1934,69 @@ isdn_writebuf_stub(int drvidx, int chan, const u_char * buf, int len, int user) { int ret; + int hl = dev->drv[drvidx]->interface->hl_hdrlen; + struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC); - if (dev->drv[drvidx]->interface->writebuf) - ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf, - len, user); - else { - struct sk_buff *skb; - - skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len, - GFP_ATOMIC); - if (skb == NULL) - return 0; - - skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen); - SET_SKB_FREE(skb); - - if (user) - copy_from_user(skb_put(skb, len), buf, len); - else - memcpy(skb_put(skb, len), buf, len); + if (!skb) + return 0; + skb_reserve(skb, hl); + if (user) + copy_from_user(skb_put(skb, len), buf, len); + else + memcpy(skb_put(skb, len), buf, len); - ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, - chan, skb); - if (ret <= 0) - kfree_skb(skb); - } + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb); + if (ret <= 0) + dev_kfree_skb(skb); if (ret > 0) dev->obytes[isdn_dc2minor(drvidx, chan)] += ret; return ret; } /* - * writebuf_skb replacement for NON SKB_ABLE drivers - * If lowlevel-device does not support supports skbufs, use - * standard send-routine, else sind directly. - * * Return: length of data on success, -ERRcode on failure. */ - int -isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb) +isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb) { int ret; - int len = skb->len; /* skb pointer no longer valid after free */ - - if (dev->drv[drvidx]->interface->writebuf_skb) - ret = dev->drv[drvidx]->interface-> - writebuf_skb(drvidx, chan, skb); - else { - if ((ret = dev->drv[drvidx]->interface-> - writebuf(drvidx, chan, skb->data, skb->len, 0)) == len) + struct sk_buff *nskb = NULL; + int v110_ret = skb->len; + int idx = isdn_dc2minor(drvidx, chan); + + if (dev->v110[idx]) { + atomic_inc(&dev->v110use[idx]); + nskb = isdn_v110_encode(dev->v110[idx], skb); + atomic_dec(&dev->v110use[idx]); + if (!nskb) + return 0; + v110_ret = *((int *)nskb->data); + skb_pull(nskb, sizeof(int)); + if (!nskb->len) { + dev_kfree_skb(nskb); dev_kfree_skb(skb); - } - if (ret > 0) - dev->obytes[isdn_dc2minor(drvidx, chan)] += len; + return v110_ret; + } + /* V.110 must always be acknowledged */ + ack = 1; + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb); + } else + ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb); + if (ret > 0) { + dev->obytes[idx] += ret; + if (dev->v110[idx]) { + atomic_inc(&dev->v110use[idx]); + dev->v110[idx]->skbuser++; + atomic_dec(&dev->v110use[idx]); + dev_kfree_skb(skb); + /* For V.110 return unencoded data length */ + ret = v110_ret; + if (ret == skb->len) + dev_kfree_skb(skb); + } + } else + if (dev->v110[idx]) + dev_kfree_skb(nskb); return ret; } @@ -1930,6 +2004,8 @@ isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb) * Low-level-driver registration */ +EXPORT_SYMBOL(register_isdn); + int register_isdn(isdn_if * i) { @@ -1951,7 +2027,7 @@ register_isdn(isdn_if * i) ISDN_MAX_CHANNELS); return 0; } - if ((!i->writebuf_skb) && (!i->writebuf)) { + if (!i->writebuf_skb) { printk(KERN_WARNING "register_isdn: No write routine given.\n"); return 0; } @@ -2019,7 +2095,6 @@ register_isdn(isdn_if * i) i->channels = drvidx; i->rcvcallb_skb = isdn_receive_skb_callback; - i->rcvcallb = isdn_receive_callback; i->statcallb = isdn_status_callback; if (!strlen(i->id)) sprintf(i->id, "line%d", drvidx); @@ -2078,11 +2153,7 @@ int isdn_init(void) { int i; - char irev[50]; - char trev[50]; - char nrev[50]; - char prev[50]; - char arev[50]; + char tmprev[50]; sti(); if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) { @@ -2126,18 +2197,18 @@ isdn_init(void) } #endif /* CONFIG_ISDN_PPP */ - isdn_export_syms(); - - strcpy(irev, isdn_revision); - strcpy(trev, isdn_tty_revision); - strcpy(nrev, isdn_net_revision); - strcpy(prev, isdn_ppp_revision); - strcpy(arev, isdn_audio_revision); - printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(irev)); - printk("%s/", isdn_getrev(trev)); - printk("%s/", isdn_getrev(nrev)); - printk("%s/", isdn_getrev(prev)); - printk("%s", isdn_getrev(arev)); + strcpy(tmprev, isdn_revision); + printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_tty_revision); + printk("%s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_net_revision); + printk("%s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_ppp_revision); + printk("%s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_audio_revision); + printk("%s/", isdn_getrev(tmprev)); + strcpy(tmprev, isdn_v110_revision); + printk("%s", isdn_getrev(tmprev)); #ifdef MODULE printk(" loaded\n"); diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h index d0df7fe79..b9d5df715 100644 --- a/drivers/isdn/isdn_common.h +++ b/drivers/isdn/isdn_common.h @@ -1,4 +1,4 @@ -/* $Id: isdn_common.h,v 1.6 1997/02/28 02:32:44 fritz Exp $ +/* $Id: isdn_common.h,v 1.9 1998/02/20 17:19:01 fritz Exp $ * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel). * @@ -21,6 +21,24 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.h,v $ + * Revision 1.9 1998/02/20 17:19:01 fritz + * Added common stub for sending commands to lowlevel. + * + * Revision 1.8 1997/10/09 21:28:49 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.7 1997/10/01 09:20:30 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.6 1997/02/28 02:32:44 fritz * Cleanup: Moved some tty related stuff from isdn_common.c * to isdn_tty.c @@ -61,6 +79,7 @@ extern void isdn_MOD_INC_USE_COUNT(void); extern void isdn_MOD_DEC_USE_COUNT(void); extern void isdn_free_channel(int di, int ch, int usage); extern void isdn_all_eaz(int di, int ch); +extern int isdn_command(isdn_ctrl *); extern int isdn_dc2minor(int di, int ch); extern void isdn_info_update(void); extern char *isdn_map_eaz2msn(char *msn, int di); @@ -69,13 +88,8 @@ 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, int); extern int isdn_get_free_channel(int, int, int, int, int); -extern int isdn_writebuf_skb_stub(int, int, struct sk_buff *); +extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *); extern int register_isdn(isdn_if * i); -#if (LINUX_VERSION_CODE < 0x020111) -extern void isdn_export_syms(void); -#else -#define isdn_export_syms() -#endif #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) extern void isdn_dumppkt(char *, u_char *, int, int); #endif diff --git a/drivers/isdn/isdn_concap.c b/drivers/isdn/isdn_concap.c new file mode 100644 index 000000000..0d9abb226 --- /dev/null +++ b/drivers/isdn/isdn_concap.c @@ -0,0 +1,108 @@ +/* $Id: isdn_concap.c,v 1.2 1998/01/31 22:49:21 keil Exp $ + + * Stuff to support the concap_proto by isdn4linux. isdn4linux - specific + * stuff goes here. Stuff that depends only on the concap protocol goes to + * another -- protocol specific -- source file. + * + * $Log: isdn_concap.c,v $ + * Revision 1.2 1998/01/31 22:49:21 keil + * correct comments + * + * Revision 1.1 1998/01/31 22:27:57 keil + * New files from Henner Eisen for X.25 support + * + */ + + +#include <linux/isdn.h> +#include "isdn_x25iface.h" +#include "isdn_net.h" +#include <linux/concap.h> +#include "isdn_concap.h" + +/* The declaration of this (or a plublic variant thereof) should really go + in linux/isdn.h. But we really need it here (and isdn_ppp, like us, also + refers to that private function currently owned by isdn_net.c) */ +extern int isdn_net_force_dial_lp(isdn_net_local *); + + +/* The following set of device service operations are for encapsulation + protocols that require for reliable datalink sematics. That means: + + - before any data is to be submitted the connection must explicitly + be set up. + - after the successful set up of the connection is signalled the + connection is considered to be reliably up. + + Auto-dialing ist not compatible with this requirements. Thus, auto-dialing + is completely bypassed. + + It might be possible to implement a (non standardized) datalink protocol + that provides a reliable data link service while using some auto dialing + mechanism. Such a protocol would need an auxiliary channel (i.e. user-user- + signaling on the D-channel) while the B-channel is down. + */ + + +int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb) +{ + int tmp; + struct device *ndev = concap -> net_dev; + isdn_net_local *lp = (isdn_net_local *) ndev->priv; + + IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name); + lp->huptimer = 0; + tmp=isdn_net_send_skb(ndev, lp, skb); + IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, tmp); + return tmp; +} + + +int isdn_concap_dl_connect_req(struct concap_proto *concap) +{ + struct device *ndev = concap -> net_dev; + isdn_net_local *lp = (isdn_net_local *) ndev->priv; + int ret; + IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name); + + /* dial ... */ + ret = isdn_net_force_dial_lp( lp ); + if ( ret ) IX25DEBUG("dialing failed\n"); + return 0; +} + +int isdn_concap_dl_disconn_req(struct concap_proto *concap) +{ + IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name); + + isdn_net_hangup( concap -> net_dev ); + return 0; +} + +struct concap_device_ops isdn_concap_reliable_dl_dops = { + &isdn_concap_dl_data_req, + &isdn_concap_dl_connect_req, + &isdn_concap_dl_disconn_req +}; + +struct concap_device_ops isdn_concap_demand_dial_dops = { + NULL, /* set this first entry to something like &isdn_net_start_xmit, + but the entry part of the current isdn_net_start_xmit must be + separated first. */ + /* no connection control for demand dial semantics */ + NULL, + NULL, +}; + +/* The following should better go into a dedicated source file such that + this sourcefile does not need to include any protocol specific header + files. For now: + */ +struct concap_proto * isdn_concap_new( int encap ) +{ + switch ( encap ) { + case ISDN_NET_ENCAP_X25IFACE: + return isdn_x25iface_proto_new(); + } + return NULL; +} diff --git a/drivers/isdn/isdn_concap.h b/drivers/isdn/isdn_concap.h new file mode 100644 index 000000000..722f8ee33 --- /dev/null +++ b/drivers/isdn/isdn_concap.h @@ -0,0 +1,7 @@ +/* $Id: isdn_concap.h,v 1.2 1998/01/31 22:49:21 keil Exp $ + */ +extern struct concap_device_ops isdn_concap_reliable_dl_dops; +extern struct concap_device_ops isdn_concap_demand_dial_dops; +extern struct concap_proto * isdn_concap_new( int ); + + diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index c44fcc74a..46b460370 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -1,4 +1,4 @@ -/* $Id: isdn_net.c,v 1.44 1997/05/27 15:17:26 fritz Exp $ +/* $Id: isdn_net.c,v 1.55 1998/02/23 19:38:22 fritz Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * @@ -21,6 +21,54 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.55 1998/02/23 19:38:22 fritz + * Corrected check for modified feature-flags. + * + * Revision 1.54 1998/02/20 17:15:07 fritz + * Changes for recent kernels. + * Ugly workaround for adjusting Ethernet frames with recent kernels. + * replaced direct calls to lowlevel-driver command by common hook. + * + * Revision 1.53 1998/01/31 22:05:54 keil + * Lots of changes for X.25 support: + * Added generic support for connection-controlling encapsulation protocols + * Added support of BHUP status message + * Added support for additional p_encap X25IFACE + * Added support for kernels >= 2.1.72 + * + * Revision 1.52 1998/01/31 19:29:51 calle + * Merged changes from and for 2.1.82, not tested only compiled ... + * + * Revision 1.51 1997/10/09 21:28:50 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.50 1997/10/01 09:20:32 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.49 1997/08/21 14:38:13 fritz + * Bugfix: Did not compile without SyncPPP. + * + * Revision 1.48 1997/06/22 11:57:15 fritz + * Added ability to adjust slave triggerlevel. + * + * Revision 1.47 1997/06/21 10:52:05 fritz + * Removed wrong SET_SKB_FREE in isdn_net_send_skb() + * + * Revision 1.46 1997/06/17 13:05:24 hipp + * Applied Eric's underflow-patches (slightly modified) + * + * Revision 1.45 1997/06/10 16:24:22 hipp + * hard_header changes for syncPPP (now behaves like RAWIP) + * * Revision 1.44 1997/05/27 15:17:26 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -196,16 +244,18 @@ #include <linux/isdn.h> #include <net/arp.h> #include <net/icmp.h> -#if (LINUX_VERSION_CODE >= 0x020117) -#include <linux/poll.h> +#ifndef DEV_NUMBUFFS +#include <net/pkt_sched.h> #endif +#include <linux/inetdevice.h> #include "isdn_common.h" #include "isdn_net.h" #ifdef CONFIG_ISDN_PPP #include "isdn_ppp.h" #endif -#ifndef DEV_NUMBUFFS -#include <net/pkt_sched.h> +#ifdef CONFIG_ISDN_X25 +#include <linux/concap.h> +#include "isdn_concap.h" #endif /* Prototypes */ @@ -218,7 +268,7 @@ static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *); static void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */ #endif -char *isdn_net_revision = "$Revision: 1.44 $"; +char *isdn_net_revision = "$Revision: 1.55 $"; /* * Code for raw-networking over ISDN @@ -229,22 +279,28 @@ isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason) { printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n", dev->name, reason); - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0 -#if (LINUX_VERSION_CODE < 0x02010f) /* 2.1.15 */ - ,dev -#endif - ); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); } static void isdn_net_reset(struct device *dev) { +#ifdef CONFIG_ISDN_X25 + struct concap_device_ops * dops = + ( (isdn_net_local *) dev->priv ) -> dops; + struct concap_proto * cprot = + ( (isdn_net_local *) dev->priv ) -> netdev -> cprot; +#endif ulong flags; save_flags(flags); cli(); /* Avoid glitch on writes to CMD regs */ dev->interrupt = 0; dev->tbusy = 0; +#ifdef CONFIG_ISDN_X25 + if( cprot && cprot -> pops && dops ) + cprot -> pops -> restart ( cprot, dev, dops ); +#endif restore_flags(flags); } @@ -254,14 +310,22 @@ isdn_net_open(struct device *dev) { int i; struct device *p; + struct in_device *in_dev; isdn_net_reset(dev); dev->start = 1; - /* Fill in the MAC-level header. */ + /* 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; - memset(&(dev->dev_addr[i]), 0, sizeof(u32)); - + if ((in_dev = dev->ip_ptr) != NULL) { + /* + * Any address will do - we take the first + */ + struct in_ifaddr *ifa = in_dev->ifa_list; + if (ifa != NULL) + memcpy(dev->dev_addr+2, &ifa->ifa_local, 4); + } + /* If this interface has slaves, start them also */ if ((p = (((isdn_net_local *) dev->priv)->slave))) { @@ -356,7 +420,7 @@ isdn_net_autohup() anymore = 0; while (p) { - isdn_net_local *l = (isdn_net_local *) & (p->local); + isdn_net_local *l = p->local; if ((jiffies - last_jiffies) == 0) l->cps = l->transcount; else @@ -405,18 +469,24 @@ isdn_net_autohup() * Return: 1 = Event handled, 0 = not for us or unknown Event. */ int -isdn_net_stat_callback(int idx, int cmd) +isdn_net_stat_callback(int idx, isdn_ctrl *c) { isdn_net_dev *p = dev->st_netdev[idx]; - + int cmd = c->command; + if (p) { - isdn_net_local *lp = &(p->local); + isdn_net_local *lp = p->local; +#ifdef CONFIG_ISDN_X25 + struct concap_proto *cprot = lp -> netdev -> cprot; + struct concap_proto_ops *pops = cprot ? cprot -> pops : 0; +#endif switch (cmd) { case ISDN_STAT_BSENT: /* A packet has successfully been sent out */ if ((lp->flags & ISDN_NET_CONNECTED) && (!lp->dialstate)) { lp->stats.tx_packets++; + lp->stats.tx_bytes += c->parm.length; if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) { struct device *mdev; if (lp->master) @@ -449,6 +519,16 @@ isdn_net_stat_callback(int idx, int cmd) break; case ISDN_STAT_DHUP: /* Either D-Channel-hangup or error during dialout */ +#ifdef CONFIG_ISDN_X25 + /* If we are not connencted then dialing had + failed. If there are generic encap protocol + receiver routines signal the closure of + the link*/ + + if( !(lp->flags & ISDN_NET_CONNECTED) + && pops && pops -> disconn_ind ) + pops -> disconn_ind(cprot); +#endif /* CONFIG_ISDN_X25 */ if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) { lp->flags &= ~ISDN_NET_CONNECTED; if (lp->first_skb) { @@ -475,6 +555,18 @@ isdn_net_stat_callback(int idx, int cmd) return 1; } break; +#ifdef CONFIG_ISDN_X25 + case ISDN_STAT_BHUP: + /* B-Channel-hangup */ + /* try if there are generic encap protocol + receiver routines and signal the closure of + the link */ + if( pops && pops -> disconn_ind ){ + pops -> disconn_ind(cprot); + return 1; + } + break; +#endif /* CONFIG_ISDN_X25 */ case ISDN_STAT_BCONN: /* B-Channel is up */ switch (lp->dialstate) { @@ -492,6 +584,8 @@ isdn_net_stat_callback(int idx, int cmd) dev->rx_netdev[idx] = p; lp->dialstate = 0; isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1); + if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) + isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, 1); printk(KERN_INFO "isdn_net: %s connected\n", lp->name); /* If first Chargeinfo comes before B-Channel connect, * we correct the timestamp here. @@ -504,7 +598,15 @@ isdn_net_stat_callback(int idx, int cmd) if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_wakeup_daemon(lp); #endif +#ifdef CONFIG_ISDN_X25 + /* try if there are generic concap receiver routines */ + if( pops ) + if( pops->connect_ind) + pops->connect_ind(cprot); + +#endif /* CONFIG_ISDN_X25 */ if (lp->first_skb) { + if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb))) lp->first_skb = NULL; } @@ -573,11 +675,13 @@ isdn_net_dial(void) isdn_ctrl cmd; while (p) { + isdn_net_local *lp = p->local; + #ifdef ISDN_DEBUG_NET_DIAL - if (p->local.dialstate) - printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name, p->local.dialstate); + if (lp->dialstate) + printk(KERN_DEBUG "%s: dialstate=%d\n", lp->name, lp->dialstate); #endif - switch (p->local.dialstate) { + switch (lp->dialstate) { case 0: /* Nothing to do for this interface */ break; @@ -587,132 +691,133 @@ isdn_net_dial(void) */ save_flags(flags); cli(); - p->local.dial = p->local.phone[1]; + lp->dial = lp->phone[1]; restore_flags(flags); - if (!p->local.dial) { + if (!lp->dial) { printk(KERN_WARNING "%s: phone number deleted?\n", - p->local.name); + lp->name); isdn_net_hangup(&p->dev); break; } anymore = 1; - p->local.dialstate++; + lp->dialstate++; /* Fall through */ case 2: /* Prepare dialing. Clear EAZ, then set EAZ. */ - cmd.driver = p->local.isdn_device; - cmd.arg = p->local.isdn_channel; + cmd.driver = lp->isdn_device; + cmd.arg = lp->isdn_channel; cmd.command = ISDN_CMD_CLREAZ; - dev->drv[p->local.isdn_device]->interface->command(&cmd); - sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver)); + isdn_command(&cmd); + sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver)); cmd.command = ISDN_CMD_SETEAZ; - dev->drv[p->local.isdn_device]->interface->command(&cmd); - p->local.dialretry = 0; + isdn_command(&cmd); + lp->dialretry = 0; anymore = 1; - p->local.dialstate++; + lp->dialstate++; /* Falls through */ case 3: /* Setup interface, dial current phone-number, switch to next number. * If list of phone-numbers is exhausted, increment * retry-counter. */ - cmd.driver = p->local.isdn_device; + cmd.driver = lp->isdn_device; cmd.command = ISDN_CMD_SETL2; - cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8); - dev->drv[p->local.isdn_device]->interface->command(&cmd); - cmd.driver = p->local.isdn_device; + cmd.arg = lp->isdn_channel + (lp->l2_proto << 8); + isdn_command(&cmd); + cmd.driver = lp->isdn_device; cmd.command = ISDN_CMD_SETL3; - cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8); - dev->drv[p->local.isdn_device]->interface->command(&cmd); - cmd.driver = p->local.isdn_device; - cmd.arg = p->local.isdn_channel; + cmd.arg = lp->isdn_channel + (lp->l3_proto << 8); + isdn_command(&cmd); + cmd.driver = lp->isdn_device; + cmd.arg = lp->isdn_channel; save_flags(flags); cli(); - if (!p->local.dial) { + if (!lp->dial) { restore_flags(flags); printk(KERN_WARNING "%s: phone number deleted?\n", - p->local.name); + lp->name); isdn_net_hangup(&p->dev); break; } - if (!strcmp(p->local.dial->num, "LEASED")) { + if (!strcmp(lp->dial->num, "LEASED")) { restore_flags(flags); - p->local.dialstate = 4; - printk(KERN_INFO "%s: Open leased line ...\n", p->local.name); + lp->dialstate = 4; + printk(KERN_INFO "%s: Open leased line ...\n", lp->name); } else { - sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num); + sprintf(cmd.parm.setup.phone, "%s", lp->dial->num); /* * Switch to next number or back to start if at end of list. */ - if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) { - p->local.dial = p->local.phone[1]; - p->local.dialretry++; + if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) { + lp->dial = lp->phone[1]; + lp->dialretry++; } restore_flags(flags); + cmd.driver = lp->isdn_device; cmd.command = ISDN_CMD_DIAL; cmd.parm.setup.si1 = 7; cmd.parm.setup.si2 = 0; sprintf(cmd.parm.setup.eazmsn, "%s", - isdn_map_eaz2msn(p->local.msn, cmd.driver)); - i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel); + isdn_map_eaz2msn(lp->msn, cmd.driver)); + i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel); if (i >= 0) { strcpy(dev->num[i], cmd.parm.setup.phone); isdn_info_update(); } - printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name, - p->local.dialretry - 1, cmd.parm.setup.phone); - p->local.dtimer = 0; + printk(KERN_INFO "%s: dialing %d %s...\n", lp->name, + lp->dialretry - 1, cmd.parm.setup.phone); + lp->dtimer = 0; #ifdef ISDN_DEBUG_NET_DIAL - printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device, - p->local.isdn_channel); + printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device, + lp->isdn_channel); #endif - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); } - p->local.huptimer = 0; - p->local.outgoing = 1; - if (p->local.chargeint) { - p->local.hupflags |= ISDN_HAVECHARGE; - p->local.hupflags &= ~ISDN_WAITCHARGE; + lp->huptimer = 0; + lp->outgoing = 1; + if (lp->chargeint) { + lp->hupflags |= ISDN_HAVECHARGE; + lp->hupflags &= ~ISDN_WAITCHARGE; } else { - p->local.hupflags |= ISDN_WAITCHARGE; - p->local.hupflags &= ~ISDN_HAVECHARGE; + lp->hupflags |= ISDN_WAITCHARGE; + lp->hupflags &= ~ISDN_HAVECHARGE; } anymore = 1; - p->local.dialstate = - (p->local.cbdelay && - (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4; + lp->dialstate = + (lp->cbdelay && + (lp->flags & ISDN_NET_CBOUT)) ? 12 : 4; break; case 4: /* Wait for D-Channel-connect. * If timeout and max retries not * reached, switch back to state 3. */ - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) - if (p->local.dialretry < p->local.dialmax) { - p->local.dialstate = 3; + if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) + if (lp->dialretry < lp->dialmax) { + lp->dialstate = 3; } else isdn_net_hangup(&p->dev); anymore = 1; break; case 5: /* Got D-Channel-Connect, send B-Channel-request */ - cmd.driver = p->local.isdn_device; - cmd.arg = p->local.isdn_channel; + cmd.driver = lp->isdn_device; + cmd.arg = lp->isdn_channel; cmd.command = ISDN_CMD_ACCEPTB; anymore = 1; - p->local.dtimer = 0; - p->local.dialstate++; - dev->drv[p->local.isdn_device]->interface->command(&cmd); + lp->dtimer = 0; + lp->dialstate++; + isdn_command(&cmd); break; case 6: /* Wait for B- or D-Channel-connect. If timeout, * switch back to state 3. */ #ifdef ISDN_DEBUG_NET_DIAL - printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer); + printk(KERN_DEBUG "dialtimer2: %d\n", lp->dtimer); #endif - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) - p->local.dialstate = 3; + if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) + lp->dialstate = 3; anymore = 1; break; case 7: @@ -720,69 +825,69 @@ isdn_net_dial(void) * then wait for D-Channel-connect */ #ifdef ISDN_DEBUG_NET_DIAL - printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer); + printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer); #endif - cmd.driver = p->local.isdn_device; + cmd.driver = lp->isdn_device; cmd.command = ISDN_CMD_SETL2; - cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8); - dev->drv[p->local.isdn_device]->interface->command(&cmd); - cmd.driver = p->local.isdn_device; + cmd.arg = lp->isdn_channel + (lp->l2_proto << 8); + isdn_command(&cmd); + cmd.driver = lp->isdn_device; cmd.command = ISDN_CMD_SETL3; - cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8); - dev->drv[p->local.isdn_device]->interface->command(&cmd); - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15) + cmd.arg = lp->isdn_channel + (lp->l3_proto << 8); + isdn_command(&cmd); + if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15) isdn_net_hangup(&p->dev); else { anymore = 1; - p->local.dialstate++; + lp->dialstate++; } break; case 9: /* Got incoming D-Channel-Connect, send B-Channel-request */ - cmd.driver = p->local.isdn_device; - cmd.arg = p->local.isdn_channel; + cmd.driver = lp->isdn_device; + cmd.arg = lp->isdn_channel; cmd.command = ISDN_CMD_ACCEPTB; - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); anymore = 1; - p->local.dtimer = 0; - p->local.dialstate++; + lp->dtimer = 0; + lp->dialstate++; break; case 8: case 10: /* Wait for B- or D-channel-connect */ #ifdef ISDN_DEBUG_NET_DIAL - printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer); + printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer); #endif - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) + if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) isdn_net_hangup(&p->dev); else anymore = 1; break; case 11: /* Callback Delay */ - if (p->local.dtimer++ > p->local.cbdelay) - p->local.dialstate = 1; + if (lp->dtimer++ > lp->cbdelay) + lp->dialstate = 1; anymore = 1; break; case 12: /* Remote does callback. Hangup after cbdelay, then wait for incoming * call (in state 4). */ - if (p->local.dtimer++ > p->local.cbdelay) { - printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->local.name); - p->local.dtimer = 0; - p->local.dialstate = 4; - cmd.driver = p->local.isdn_device; + if (lp->dtimer++ > lp->cbdelay) { + printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name); + lp->dtimer = 0; + lp->dialstate = 4; + cmd.driver = lp->isdn_device; cmd.command = ISDN_CMD_HANGUP; - cmd.arg = p->local.isdn_channel; - (void) dev->drv[cmd.driver]->interface->command(&cmd); - isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel); + cmd.arg = lp->isdn_channel; + isdn_command(&cmd); + isdn_all_eaz(lp->isdn_device, lp->isdn_channel); } anymore = 1; break; default: printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n", - p->local.dialstate, p->local.name); + lp->dialstate, lp->name); } p = (isdn_net_dev *) p->next; } @@ -797,6 +902,10 @@ isdn_net_hangup(struct device *d) { isdn_net_local *lp = (isdn_net_local *) d->priv; isdn_ctrl cmd; +#ifdef CONFIG_ISDN_X25 + struct concap_proto *cprot = lp -> netdev -> cprot; + struct concap_proto_ops *pops = cprot ? cprot -> pops : 0; +#endif if (lp->flags & ISDN_NET_CONNECTED) { lp->flags &= ~ISDN_NET_CONNECTED; @@ -804,10 +913,18 @@ isdn_net_hangup(struct device *d) #ifdef CONFIG_ISDN_PPP isdn_ppp_free(lp); #endif +#ifdef CONFIG_ISDN_X25 + /* try if there are generic encap protocol + receiver routines and signal the closure of + the link */ + if( pops && pops -> disconn_ind ) + pops -> disconn_ind(cprot); +#endif /* CONFIG_ISDN_X25 */ + cmd.driver = lp->isdn_device; cmd.command = ISDN_CMD_HANGUP; cmd.arg = lp->isdn_channel; - (void) dev->drv[cmd.driver]->interface->command(&cmd); + isdn_command(&cmd); printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge); isdn_all_eaz(lp->isdn_device, lp->isdn_channel); } @@ -820,28 +937,43 @@ typedef struct { } ip_ports; static void -isdn_net_log_packet(u_char * buf, isdn_net_local * lp) +isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) { - u_char *p = buf; - unsigned short proto = ETH_P_IP; + u_char *p = skb->nh.raw; /* hopefully, this was set correctly */ + unsigned short proto = ntohs(skb->protocol); int data_ofs; ip_ports *ipp; char addinfo[100]; addinfo[0] = '\0'; - switch (lp->p_encap) { - case ISDN_NET_ENCAP_IPTYP: - proto = ntohs(*(unsigned short *) &buf[0]); - p = &buf[2]; - break; - case ISDN_NET_ENCAP_ETHER: - proto = ntohs(*(unsigned short *) &buf[12]); - p = &buf[14]; - break; - case ISDN_NET_ENCAP_CISCOHDLC: - proto = ntohs(*(unsigned short *) &buf[2]); - p = &buf[4]; - break; + /* This check stolen from 2.1.72 dev_queue_xmit_nit() */ + if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) { + /* fall back to old isdn_net_log_packet method() */ + char * buf = skb->data; + + printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->name); + p = buf; + proto = ETH_P_IP; + switch (lp->p_encap) { + case ISDN_NET_ENCAP_IPTYP: + proto = ntohs(*(unsigned short *) &buf[0]); + p = &buf[2]; + break; + case ISDN_NET_ENCAP_ETHER: + proto = ntohs(*(unsigned short *) &buf[12]); + p = &buf[14]; + break; + case ISDN_NET_ENCAP_CISCOHDLC: + proto = ntohs(*(unsigned short *) &buf[2]); + p = &buf[4]; + break; +#ifdef CONFIG_ISDN_PPP + case ISDN_NET_ENCAP_SYNCPPP: + proto = ntohs(skb->protocol); + p = &buf[IPPP_MAX_HEADER]; + break; +#endif + } } data_ofs = ((p[0] & 15) * 4); switch (proto) { @@ -891,7 +1023,7 @@ isdn_net_log_packet(u_char * buf, isdn_net_local * lp) /* * Generic routine to send out an skbuf. - * If lowlevel-device does not support supports skbufs, use + * If lowlevel-device does not support support skbufs, use * standard send-routine, else send directly. * * Return: 0 on success, !0 on failure. @@ -904,14 +1036,13 @@ isdn_net_send_skb(struct device *ndev, isdn_net_local * lp, int ret; int len = skb->len; /* save len */ - ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb); + ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); if (ret == len) { lp->transcount += len; clear_bit(0, (void *) &(ndev->tbusy)); return 0; } if (ret < 0) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); lp->stats.tx_errors++; clear_bit(0, (void *) &(ndev->tbusy)); @@ -945,7 +1076,7 @@ isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) #endif /* Reset hangup-timeout */ lp->huptimer = 0; - if (lp->cps > 7000) { + if (lp->cps > lp->triggercps) { /* Device overloaded */ /* @@ -988,35 +1119,64 @@ isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb) return ret; } +static void +isdn_net_adjust_hdr(struct sk_buff *skb, struct device *dev) +{ + isdn_net_local *lp = (isdn_net_local *) dev->priv; + if (!skb) + return; + if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { + ulong pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN; + if (pullsize) + skb_pull(skb, pullsize); + } +} + /* * Try sending a packet. * If this interface isn't connected to a ISDN-Channel, find a free channel, * and start dialing. */ -int +static int isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) { isdn_net_local *lp = (isdn_net_local *) ndev->priv; +#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->tbusy = 0; ndev->trans_start = jiffies; } - if (skb == NULL) { - return 0; - } - /* Avoid timer-based retransmission conflicts. */ - if (test_and_set_bit(0, (void *) &ndev->tbusy) != 0) - printk(KERN_WARNING - "%s: Transmitter access conflict.\n", - ndev->name); - else { - u_char *buf = skb->data; + ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */ +#ifdef CONFIG_ISDN_X25 +/* At this point hard_start_xmit() passes control to the encapsulation + protocol (if present). + For X.25 auto-dialing is completly bypassed because: + - It does not conform with the semantics of a reliable datalink + service as needed by X.25 PLP. + - I don't want that the interface starts dialing when the network layer + sends a message which requests to disconnect the lapb link (or if it + sends any other message not resulting in data transmission). + Instead, dialing will be initiated by the encapsulation protocol entity + when a dl_establish request is received from the upper layer. +*/ + if( cprot ) { + return cprot -> pops -> encap_and_xmit ( cprot , skb); + } else +#endif + /* auto-dialing xmit function */ + { +#ifdef ISDN_DEBUG_NET_DUMP + u_char *buf; +#endif + isdn_net_adjust_hdr(skb, ndev); #ifdef ISDN_DEBUG_NET_DUMP + buf = skb->data; isdn_dumppkt("S:", buf, skb->len, 40); #endif if (!(lp->flags & ISDN_NET_CONNECTED)) { @@ -1033,24 +1193,15 @@ isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev) lp->pre_device, lp->pre_channel)) < 0) { restore_flags(flags); -#if 0 - printk(KERN_WARNING - "isdn_net_start_xmit: No channel for %s\n", - ndev->name); - /* we probably should drop the skb here and return 0 to omit - 'socket destroy delayed' messages */ - return 1; -#else isdn_net_unreachable(ndev, skb, "No channel"); dev_kfree_skb(skb); ndev->tbusy = 0; return 0; -#endif } /* Log packet, which triggered dialing */ if (dev->net_verbose) - isdn_net_log_packet(buf, lp); + isdn_net_log_skb(skb, lp); lp->dialstate = 1; lp->flags |= ISDN_NET_CONNECTED; /* Connect interface with channel */ @@ -1114,12 +1265,26 @@ static int isdn_net_close(struct device *dev) { struct device *p; +#ifdef CONFIG_ISDN_X25 + struct concap_proto * cprot = + ( (isdn_net_local *) dev->priv ) -> netdev -> cprot; + /* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */ +#endif +#ifdef CONFIG_ISDN_X25 + if( cprot && cprot -> pops ) cprot -> pops -> close( cprot ); +#endif dev->tbusy = 1; dev->start = 0; if ((p = (((isdn_net_local *) dev->priv)->slave))) { /* If this interface has slaves, stop them also */ while (p) { +#ifdef CONFIG_ISDN_X25 + cprot = ( (isdn_net_local *) p->priv ) + -> netdev -> cprot; + if( cprot && cprot -> pops ) + cprot -> pops -> close( cprot ); +#endif isdn_net_hangup(p); p->tbusy = 1; p->start = 0; @@ -1156,6 +1321,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct device *dev) struct ethhdr *eth; unsigned char *rawp; + skb->mac.raw = skb->data; skb_pull(skb, ETH_HLEN); eth = skb->mac.ethernet; @@ -1170,7 +1336,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct device *dev) * so don't forget to remove it. */ - else if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { + else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) { if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN)) skb->pkt_type = PACKET_OTHERHOST; } @@ -1193,6 +1359,97 @@ isdn_net_type_trans(struct sk_buff *skb, struct device *dev) return htons(ETH_P_802_2); } +static void +isdn_net_slarp_send(isdn_net_local *lp, int is_reply) +{ + unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + struct sk_buff *skb = dev_alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp)); + unsigned long t = (jiffies / HZ * 1000000); + int len; + cisco_hdr *ch; + cisco_slarp *s; + + if (!skb) { + printk(KERN_WARNING + "%s: Could not allocate SLARP reply\n", lp->name); + return; + } + skb_reserve(skb, hl); + ch = (cisco_hdr *)skb_put(skb, sizeof(cisco_hdr)); + ch->addr = CISCO_ADDR_UNICAST; + ch->ctrl = 0; + ch->type = htons(CISCO_TYPE_SLARP); + s = (cisco_slarp *)skb_put(skb, sizeof(cisco_slarp)); + if (is_reply) { + s->code = htonl(CISCO_SLARP_REPLY); + memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32)); + memset(&s->slarp.reply.netmask, 0, sizeof(__u32)); + } else { + lp->cisco_myseq++; + s->code = htonl(CISCO_SLARP_KEEPALIVE); + s->slarp.keepalive.my_seq = htonl(lp->cisco_myseq); + s->slarp.keepalive.your_seq = htonl(lp->cisco_yourseq); + } + s->rel = 0xffff; + s->t1 = t >> 16; + s->t0 = t & 0xffff; + len = skb->len; + if (isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 0, skb) != len) + dev_kfree_skb(skb); +} + +static void +isdn_net_slarp_in(isdn_net_local *lp, struct sk_buff *skb) +{ + cisco_slarp *s = (cisco_slarp *)skb->data; + + switch (ntohl(s->code)) { + case CISCO_SLARP_REQUEST: + isdn_net_slarp_send(lp, 1); + break; + case CISCO_SLARP_REPLY: + /* Ignore replies */ + break; + case CISCO_SLARP_KEEPALIVE: + lp->cisco_yourseq = s->slarp.keepalive.my_seq; + if (ntohl(s->slarp.keepalive.my_seq == lp->cisco_myseq)) { + if (lp->cisco_loop++ == 2) { + printk(KERN_WARNING "%s: Keepalive Loop\n", + lp->name); + lp->cisco_myseq ^= jiffies; + } + } else + lp->cisco_loop = 0; + break; + } + kfree_skb(skb); +} + +/* + * Called every 10 sec. via timer-interrupt if + * any network-interface has Cisco-Keepalive-Encapsulation + * and is online. + * Send Keepalive-Packet and re-schedule. + */ +void +isdn_net_slarp_out(void) +{ + isdn_net_dev *p = dev->netdev; + int anymore = 0; + + while (p) { + isdn_net_local *l = p->local; + if ((l->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) && + (l->flags & ISDN_NET_CONNECTED) && + (!l->dialstate) ) { + anymore = 1; + isdn_net_slarp_send(l, 0); + } + p = (isdn_net_dev *) p->next; + } + isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, anymore); +} + /* * Got a packet from ISDN-Channel. */ @@ -1200,23 +1457,19 @@ static void isdn_net_receive(struct device *ndev, struct sk_buff *skb) { isdn_net_local *lp = (isdn_net_local *) ndev->priv; -#ifdef CONFIG_ISDN_PPP isdn_net_local *olp = lp; /* original 'lp' */ +#ifdef CONFIG_ISDN_PPP int proto = PPP_PROTOCOL(skb->data); #endif +#ifdef CONFIG_ISDN_X25 + struct concap_proto *cprot = lp -> netdev -> cprot; +#endif + cisco_hdr *ch; lp->transcount += skb->len; - lp->stats.rx_packets++; -#ifdef CONFIG_ISDN_PPP - /* - * If encapsulation is syncppp, don't reset - * huptimer on LCP packets. - */ - if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP || - (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP)) -#endif - lp->huptimer = 0; + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; if (lp->master) { /* Bundling: If device is a slave-device, deliver to master, also * handle master's statistics and hangup-timeout @@ -1224,16 +1477,9 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) ndev = lp->master; lp = (isdn_net_local *) ndev->priv; lp->stats.rx_packets++; -#ifdef CONFIG_ISDN_PPP - /* - * If encapsulation is syncppp, don't reset - * huptimer on LCP packets. - */ - if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP || - (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP)) -#endif - lp->huptimer = 0; + lp->stats.rx_bytes += skb->len; } + skb->dev = ndev; skb->pkt_type = PACKET_HOST; skb->mac.raw = skb->data; @@ -1243,22 +1489,61 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) switch (lp->p_encap) { case ISDN_NET_ENCAP_ETHER: /* Ethernet over ISDN */ + olp->huptimer = 0; + lp->huptimer = 0; skb->protocol = isdn_net_type_trans(skb, ndev); break; case ISDN_NET_ENCAP_UIHDLC: /* HDLC with UI-frame (for ispa with -h1 option) */ + olp->huptimer = 0; + lp->huptimer = 0; skb_pull(skb, 2); /* Fall through */ case ISDN_NET_ENCAP_RAWIP: /* RAW-IP without MAC-Header */ + olp->huptimer = 0; + lp->huptimer = 0; skb->protocol = htons(ETH_P_IP); break; + case ISDN_NET_ENCAP_CISCOHDLCK: + ch = (cisco_hdr *)skb->data; + if ((ch->addr != CISCO_ADDR_UNICAST) && + (ch->addr != CISCO_ADDR_BROADCAST) ) { + printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n", + lp->name, ch->addr); + kfree_skb(skb); + return; + } + if (ch->ctrl != 0) { + printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n", + lp->name, ch->ctrl); + kfree_skb(skb); + return; + } + switch (ntohs(ch->type)) { + case CISCO_TYPE_INET: + skb_pull(skb, 4); + skb->protocol = htons(ETH_P_IP); + break; + case CISCO_TYPE_SLARP: + skb_pull(skb, 4); + isdn_net_slarp_in(olp, skb); + return; + default: + printk(KERN_WARNING "%s: Unknown Cisco type 0x%04x\n", + lp->name, ch->type); + kfree_skb(skb); + return; + } + break; case ISDN_NET_ENCAP_CISCOHDLC: /* CISCO-HDLC IP with type field and fake I-frame-header */ skb_pull(skb, 2); /* Fall through */ case ISDN_NET_ENCAP_IPTYP: /* IP with type field */ + olp->huptimer = 0; + lp->huptimer = 0; skb->protocol = *(unsigned short *) &(skb->data[0]); skb_pull(skb, 2); if (*(unsigned short *) skb->data == 0xFFFF) @@ -1266,10 +1551,26 @@ isdn_net_receive(struct device *ndev, struct sk_buff *skb) break; #ifdef CONFIG_ISDN_PPP case ISDN_NET_ENCAP_SYNCPPP: + /* + * If encapsulation is syncppp, don't reset + * huptimer on LCP packets. + */ + if (proto != PPP_LCP) { + olp->huptimer = 0; + lp->huptimer = 0; + } isdn_ppp_receive(lp->netdev, olp, skb); return; #endif default: +#ifdef CONFIG_ISDN_X25 + /* try if there are generic sync_device receiver routines */ + if(cprot) if(cprot -> pops) + if( cprot -> pops -> data_ind){ + cprot -> pops -> data_ind(cprot,skb); + return; + }; +#endif /* CONFIG_ISDN_X25 */ printk(KERN_WARNING "%s: unknown encapsulation, dropping\n", lp->name); kfree_skb(skb); @@ -1290,7 +1591,7 @@ isdn_net_rcv_skb(int idx, struct sk_buff *skb) isdn_net_dev *p = dev->rx_netdev[idx]; if (p) { - isdn_net_local *lp = &p->local; + isdn_net_local *lp = p->local; if ((lp->flags & ISDN_NET_CONNECTED) && (!lp->dialstate)) { isdn_net_receive(&p->dev, skb); @@ -1329,15 +1630,15 @@ my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type, * Anyway, the loopback-device should never use this function... */ - if (dev->flags & IFF_LOOPBACK) { + if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { memset(eth->h_dest, 0, dev->addr_len); - return (dev->hard_header_len); + return ETH_HLEN /*(dev->hard_header_len)*/; } if (daddr) { memcpy(eth->h_dest, daddr, dev->addr_len); - return dev->hard_header_len; + return ETH_HLEN /*dev->hard_header_len*/; } - return -dev->hard_header_len; + return -ETH_HLEN /*dev->hard_header_len*/; } /* @@ -1356,6 +1657,13 @@ isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, case ISDN_NET_ENCAP_ETHER: len = my_eth_header(skb, dev, type, daddr, saddr, plen); break; +#ifdef CONFIG_ISDN_PPP + case ISDN_NET_ENCAP_SYNCPPP: + /* stick on a fake header to keep fragmentation code happy. */ + len = IPPP_MAX_HEADER; + skb_push(skb,len); + break; +#endif case ISDN_NET_ENCAP_RAWIP: printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n"); len = 0; @@ -1377,43 +1685,21 @@ isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, *((ushort *) & skb->data[2]) = htons(type); len = 4; break; +#ifdef CONFIG_ISDN_X25 + default: + /* try if there are generic concap protocol routines */ + if( lp-> netdev -> cprot ){ + printk(KERN_WARNING "isdn_net_header called with concap_proto!\n"); + len = 0; + break; + } + break; +#endif /* CONFIG_ISDN_X25 */ } return len; } /* We don't need to send arp, because we have point-to-point connections. */ -#if (LINUX_VERSION_CODE < 0x02010F) -static int -isdn_net_rebuild_header(void *buff, struct device *dev, unsigned long dst, - struct sk_buff *skb) -{ - isdn_net_local *lp = dev->priv; - int ret = 0; - - if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { - struct ethhdr *eth = (struct ethhdr *) buff; - - /* - * Only ARP/IP is currently supported - */ - - if (eth->h_proto != htons(ETH_P_IP)) { - printk(KERN_WARNING - "isdn_net: %s don't know how to resolve type %d addresses?\n", - dev->name, (int) eth->h_proto); - memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - return 0; - } - /* - * Try to get ARP to resolve the header. - */ -#ifdef CONFIG_INET - ret = arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb) ? 1 : 0; -#endif - } - return ret; -} -#else static int isdn_net_rebuild_header(struct sk_buff *skb) { @@ -1439,12 +1725,12 @@ isdn_net_rebuild_header(struct sk_buff *skb) * Try to get ARP to resolve the header. */ #ifdef CONFIG_INET - ret = arp_find(eth->h_dest, skb) ? 1 : 0; + ret = arp_find(eth->h_dest, skb); #endif } return ret; } -#endif + /* * Interface-setup. (called just after registering a new interface) */ @@ -1465,21 +1751,13 @@ isdn_net_init(struct device *ndev) return -ENODEV; } ether_setup(ndev); -#if (LINUX_VERSION_CODE < 0x02010F) - lp->org_hcb = ndev->header_cache_bind; -#else lp->org_hhc = ndev->hard_header_cache; -#endif lp->org_hcu = ndev->header_cache_update; /* Setup the generic properties */ ndev->hard_header = NULL; -#if (LINUX_VERSION_CODE < 0x02010F) - ndev->header_cache_bind = NULL; -#else ndev->hard_header_cache = NULL; -#endif ndev->header_cache_update = NULL; ndev->mtu = 1500; ndev->flags = IFF_NOARP|IFF_POINTOPOINT; @@ -1599,13 +1877,13 @@ isdn_net_swapbind(int drvidx) #endif p = dev->netdev; while (p) { - if (p->local.pre_device == drvidx) - switch (p->local.pre_channel) { + if (p->local->pre_device == drvidx) + switch (p->local->pre_channel) { case 0: - p->local.pre_channel = 1; + p->local->pre_channel = 1; break; case 1: - p->local.pre_channel = 0; + p->local->pre_channel = 0; break; } p = (isdn_net_dev *) p->next; @@ -1676,6 +1954,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz); /* Accept only calls with Si1 = 7 (Data-Transmission) */ if (si1 != 7) { + restore_flags(flags); if (dev->net_verbose > 1) printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n"); return 0; @@ -1689,6 +1968,8 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) #endif swapped = 0; while (p) { + isdn_net_local *lp = p->local; + /* If last check has triggered as binding-swap, revert it */ switch (swapped) { case 2: @@ -1699,25 +1980,25 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) break; } swapped = 0; - if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) + if (!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz)) ematch = 1; #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n", - p->local.name, p->local.msn, p->local.flags, p->local.dialstate); + lp->name, lp->msn, lp->flags, lp->dialstate); #endif - if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) && /* EAZ is matching */ - (((!(p->local.flags & ISDN_NET_CONNECTED)) && /* but not connected */ + if ((!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz)) && /* EAZ is matching */ + (((!(lp->flags & ISDN_NET_CONNECTED)) && /* but not connected */ (USG_NONE(dev->usage[idx]))) || /* and ch. unused or */ - ((((p->local.dialstate == 4) || (p->local.dialstate == 12)) && /* if dialing */ - (!(p->local.flags & ISDN_NET_CALLBACK))) /* but no callback */ + ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing */ + (!(lp->flags & ISDN_NET_CALLBACK))) /* but no callback */ ))) { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n", - p->local.pre_device, p->local.pre_channel); + lp->pre_device, lp->pre_channel); #endif if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) { - if ((p->local.pre_channel != ch) || - (p->local.pre_device != di)) { + if ((lp->pre_channel != ch) || + (lp->pre_device != di)) { /* Here we got a problem: * If using an ICN-Card, an incoming call is always signaled on * on the first channel of the card, if both channels are @@ -1741,8 +2022,8 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) #endif /* Yes, swap bindings only, if the original * binding is bound to channel 1 of this driver */ - if ((p->local.pre_device == di) && - (p->local.pre_channel == 1)) { + if ((lp->pre_device == di) && + (lp->pre_channel == 1)) { isdn_net_swapbind(di); swapped = 1; } else { @@ -1764,8 +2045,8 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) printk(KERN_DEBUG "n_fi: final check\n"); #endif if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) && - ((p->local.pre_channel != ch) || - (p->local.pre_device != di))) { + ((lp->pre_channel != ch) || + (lp->pre_device != di))) { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: final check failed\n"); #endif @@ -1786,16 +2067,15 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match2\n"); #endif - n = p->local.phone[0]; - if (p->local.flags & ISDN_NET_SECURE) { + n = lp->phone[0]; + if (lp->flags & ISDN_NET_SECURE) { while (n) { if (isdn_net_wildmat(nr, n->num)) break; n = (isdn_net_phone *) n->next; } } - if (n || (!(p->local.flags & ISDN_NET_SECURE))) { - isdn_net_local *lp = &(p->local); + if (n || (!(lp->flags & ISDN_NET_SECURE))) { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match3\n"); #endif @@ -1804,7 +2084,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) */ if (!p->dev.start) { restore_flags(flags); - printk(KERN_INFO "%s: incoming call, if down -> rejected\n", + printk(KERN_INFO "%s: incoming call, interface down -> rejected\n", lp->name); return 3; } @@ -1872,12 +2152,12 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) eaz); /* if this interface is dialing, it does it probably on a different device, so free this device */ - if ((p->local.dialstate == 4) || (p->local.dialstate == 12)) { + if ((lp->dialstate == 4) || (lp->dialstate == 12)) { #ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_free(lp); #endif - isdn_free_channel(p->local.isdn_device, p->local.isdn_channel, + isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET); } dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; @@ -1885,16 +2165,16 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) strcpy(dev->num[idx], nr); isdn_info_update(); dev->st_netdev[idx] = lp->netdev; - p->local.isdn_device = di; - p->local.isdn_channel = ch; - p->local.ppp_slot = -1; - p->local.flags |= ISDN_NET_CONNECTED; - p->local.dialstate = 7; - p->local.dtimer = 0; - p->local.outgoing = 0; - p->local.huptimer = 0; - p->local.hupflags |= ISDN_WAITCHARGE; - p->local.hupflags &= ~ISDN_HAVECHARGE; + lp->isdn_device = di; + lp->isdn_channel = ch; + lp->ppp_slot = -1; + lp->flags |= ISDN_NET_CONNECTED; + lp->dialstate = 7; + lp->dtimer = 0; + lp->outgoing = 0; + lp->huptimer = 0; + lp->hupflags |= ISDN_WAITCHARGE; + lp->hupflags &= ~ISDN_HAVECHARGE; #ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) if (isdn_ppp_bind(lp) < 0) { @@ -1926,7 +2206,7 @@ isdn_net_findif(char *name) isdn_net_dev *p = dev->netdev; while (p) { - if (!strcmp(p->local.name, name)) + if (!strcmp(p->local->name, name)) return p; p = (isdn_net_dev *) p->next; } @@ -1989,7 +2269,7 @@ isdn_net_force_dial(char *name) if (!p) return -ENODEV; - return (isdn_net_force_dial_lp(&p->local)); + return (isdn_net_force_dial_lp(p->local)); } /* @@ -2010,20 +2290,25 @@ isdn_net_new(char *name, struct device *master) return NULL; } memset(netdev, 0, sizeof(isdn_net_dev)); + if (!(netdev->local = (isdn_net_local *) kmalloc(sizeof(isdn_net_local), GFP_KERNEL))) { + printk(KERN_WARNING "isdn_net: Could not allocate device locals\n"); + return NULL; + } + memset(netdev->local, 0, sizeof(isdn_net_local)); if (name == NULL) - strcpy(netdev->local.name, " "); + strcpy(netdev->local->name, " "); else - strcpy(netdev->local.name, name); - netdev->dev.name = netdev->local.name; - netdev->dev.priv = &netdev->local; + strcpy(netdev->local->name, name); + netdev->dev.name = netdev->local->name; + netdev->dev.priv = netdev->local; netdev->dev.init = isdn_net_init; - netdev->local.p_encap = ISDN_NET_ENCAP_RAWIP; + netdev->local->p_encap = ISDN_NET_ENCAP_RAWIP; if (master) { /* Device shall be a slave */ struct device *p = (((isdn_net_local *) master->priv)->slave); struct device *q = master; - netdev->local.master = master; + netdev->local->master = master; /* Put device at end of slave-chain */ while (p) { q = p; @@ -2037,41 +2322,43 @@ isdn_net_new(char *name, struct device *master) /* Device shall be a master */ if (register_netdev(&netdev->dev) != 0) { printk(KERN_WARNING "isdn_net: Could not register net-device\n"); + kfree(netdev->local); kfree(netdev); return NULL; } } - netdev->local.magic = ISDN_NET_MAGIC; + netdev->local->magic = ISDN_NET_MAGIC; #ifdef CONFIG_ISDN_PPP netdev->mp_last = NULL; /* mpqueue is empty */ netdev->ib.next_num = 0; netdev->ib.last = NULL; #endif - netdev->queue = &netdev->local; - netdev->local.last = &netdev->local; - netdev->local.netdev = netdev; - netdev->local.next = &netdev->local; - - netdev->local.isdn_device = -1; - netdev->local.isdn_channel = -1; - netdev->local.pre_device = -1; - netdev->local.pre_channel = -1; - netdev->local.exclusive = -1; - netdev->local.ppp_slot = -1; - netdev->local.pppbind = -1; - netdev->local.sav_skb = NULL; - netdev->local.first_skb = NULL; - netdev->local.l2_proto = ISDN_PROTO_L2_X75I; - netdev->local.l3_proto = ISDN_PROTO_L3_TRANS; - netdev->local.slavedelay = 10 * HZ; - netdev->local.srobin = &netdev->dev; - netdev->local.hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */ - netdev->local.onhtime = 10; /* Default hangup-time for saving costs + netdev->queue = netdev->local; + netdev->local->last = netdev->local; + netdev->local->netdev = netdev; + netdev->local->next = netdev->local; + + netdev->local->isdn_device = -1; + netdev->local->isdn_channel = -1; + netdev->local->pre_device = -1; + netdev->local->pre_channel = -1; + netdev->local->exclusive = -1; + netdev->local->ppp_slot = -1; + netdev->local->pppbind = -1; + netdev->local->sav_skb = NULL; + netdev->local->first_skb = NULL; + netdev->local->l2_proto = ISDN_PROTO_L2_X75I; + netdev->local->l3_proto = ISDN_PROTO_L3_TRANS; + netdev->local->triggercps = 6000; + netdev->local->slavedelay = 10 * HZ; + netdev->local->srobin = &netdev->dev; + netdev->local->hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */ + netdev->local->onhtime = 10; /* Default hangup-time for saving costs of those who forget configuring this */ - netdev->local.dialmax = 1; - netdev->local.flags = ISDN_NET_CBHUP; /* Hangup before Callback */ - netdev->local.cbdelay = 25; /* Wait 5 secs before Callback */ + netdev->local->dialmax = 1; + netdev->local->flags = ISDN_NET_CBHUP; /* Hangup before Callback */ + netdev->local->cbdelay = 25; /* Wait 5 secs before Callback */ /* Put into to netdev-chain */ netdev->next = (void *) dev->netdev; dev->netdev = netdev; @@ -2095,7 +2382,7 @@ isdn_net_newslave(char *parm) if (!(n = isdn_net_findif(parm))) return NULL; /* Master must be a real interface, not a slave */ - if (n->local.master) + if (n->local->master) return NULL; /* Master must not be started yet */ if (n->dev.start) @@ -2120,10 +2407,15 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) int drvidx; int chidx; char drvid[25]; - +#ifdef CONFIG_ISDN_X25 + ulong flags; +#endif if (p) { + isdn_net_local *lp = p->local; + /* See if any registered driver supports the features we want */ - features = (1 << cfg->l2_proto) | (256 << cfg->l3_proto); + features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) | + ((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT); for (i = 0; i < ISDN_MAX_DRIVERS; i++) if (dev->drv[i]) if ((dev->drv[i]->interface->features & features) == features) @@ -2132,22 +2424,68 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) printk(KERN_WARNING "isdn_net: No driver with selected features\n"); return -ENODEV; } - if (p->local.p_encap != cfg->p_encap) + if (lp->p_encap != cfg->p_encap){ +#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", - p->local.name); + lp->name); return -EBUSY; } - if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) { +#ifdef CONFIG_ISDN_X25 + /* delete old encapsulation protocol if present ... */ + save_flags(flags); + cli(); /* avoid races with incoming events trying to + call cprot->pops methods */ + if( cprot && cprot -> pops ) + cprot -> pops -> proto_del ( cprot ); + p -> cprot = NULL; + lp -> dops = NULL; + restore_flags(flags); + /* ... , prepare for configuration of new one ... */ + switch ( cfg -> p_encap ){ + case ISDN_NET_ENCAP_X25IFACE: + lp -> dops = &isdn_concap_reliable_dl_dops; + } + /* ... and allocate new one ... */ + p -> cprot = isdn_concap_new( cfg -> p_encap ); + /* p -> cprot == NULL now if p_encap is not supported + by means of the concap_proto mechanism */ + /* the protocol is not configured yet; this will + happen later when isdn_net_reset() is called */ +#endif + } + switch ( cfg->p_encap ) { + case ISDN_NET_ENCAP_SYNCPPP: #ifndef CONFIG_ISDN_PPP printk(KERN_WARNING "%s: SyncPPP support not configured\n", - p->local.name); + lp->name); return -EINVAL; #else p->dev.type = ARPHRD_PPP; /* change ARP type */ p->dev.addr_len = 0; #endif + break; + case ISDN_NET_ENCAP_X25IFACE: +#ifndef CONFIG_ISDN_X25 + printk(KERN_WARNING "%s: isdn-x25 support not configured\n", + p->local->name); + return -EINVAL; +#else + p->dev.type = ARPHRD_X25; /* change ARP type */ + p->dev.addr_len = 0; +#endif + break; + default: + if( cfg->p_encap >= 0 && + cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP ) + break; + printk(KERN_WARNING + "%s: encapsulation protocol %d not supported\n", + p->local->name, cfg->p_encap); + return -EINVAL; } if (strlen(cfg->drvid)) { /* A bind has been requested ... */ @@ -2175,20 +2513,20 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) return -ENODEV; } else { /* Parameters are valid, so get them */ - drvidx = p->local.pre_device; - chidx = p->local.pre_channel; + drvidx = lp->pre_device; + chidx = lp->pre_channel; } if (cfg->exclusive > 0) { int flags; /* If binding is exclusive, try to grab the channel */ save_flags(flags); - if ((i = isdn_get_free_channel(ISDN_USAGE_NET, p->local.l2_proto, - p->local.l3_proto, + if ((i = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto, + lp->l3_proto, drvidx, chidx)) < 0) { /* Grab failed, because desired channel is in use */ - p->local.exclusive = -1; + lp->exclusive = -1; restore_flags(flags); return -EBUSY; } @@ -2196,93 +2534,82 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) dev->usage[i] = ISDN_USAGE_EXCLUSIVE; isdn_info_update(); restore_flags(flags); - p->local.exclusive = i; + lp->exclusive = i; } else { /* Non-exclusive binding or unbind. */ - p->local.exclusive = -1; - if ((p->local.pre_device != -1) && (cfg->exclusive == -1)) { - isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel); - isdn_free_channel(p->local.pre_device, p->local.pre_channel, ISDN_USAGE_NET); + lp->exclusive = -1; + if ((lp->pre_device != -1) && (cfg->exclusive == -1)) { + isdn_unexclusive_channel(lp->pre_device, lp->pre_channel); + isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET); drvidx = -1; chidx = -1; } } - strcpy(p->local.msn, cfg->eaz); - p->local.pre_device = drvidx; - p->local.pre_channel = chidx; - p->local.onhtime = cfg->onhtime; - p->local.charge = cfg->charge; - p->local.l2_proto = cfg->l2_proto; - p->local.l3_proto = cfg->l3_proto; - p->local.cbdelay = cfg->cbdelay; - p->local.dialmax = cfg->dialmax; - p->local.slavedelay = cfg->slavedelay * HZ; - p->local.pppbind = cfg->pppbind; + strcpy(lp->msn, cfg->eaz); + lp->pre_device = drvidx; + lp->pre_channel = chidx; + lp->onhtime = cfg->onhtime; + lp->charge = cfg->charge; + lp->l2_proto = cfg->l2_proto; + lp->l3_proto = cfg->l3_proto; + lp->cbdelay = cfg->cbdelay; + lp->dialmax = cfg->dialmax; + lp->triggercps = cfg->triggercps; + lp->slavedelay = cfg->slavedelay * HZ; + lp->pppbind = cfg->pppbind; if (cfg->secure) - p->local.flags |= ISDN_NET_SECURE; + lp->flags |= ISDN_NET_SECURE; else - p->local.flags &= ~ISDN_NET_SECURE; + lp->flags &= ~ISDN_NET_SECURE; if (cfg->cbhup) - p->local.flags |= ISDN_NET_CBHUP; + lp->flags |= ISDN_NET_CBHUP; else - p->local.flags &= ~ISDN_NET_CBHUP; + lp->flags &= ~ISDN_NET_CBHUP; switch (cfg->callback) { case 0: - p->local.flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT); + lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT); break; case 1: - p->local.flags |= ISDN_NET_CALLBACK; - p->local.flags &= ~ISDN_NET_CBOUT; + lp->flags |= ISDN_NET_CALLBACK; + lp->flags &= ~ISDN_NET_CBOUT; break; case 2: - p->local.flags |= ISDN_NET_CBOUT; - p->local.flags &= ~ISDN_NET_CALLBACK; + lp->flags |= ISDN_NET_CBOUT; + lp->flags &= ~ISDN_NET_CALLBACK; break; } if (cfg->chargehup) - p->local.hupflags |= ISDN_CHARGEHUP; + lp->hupflags |= ISDN_CHARGEHUP; else - p->local.hupflags &= ~ISDN_CHARGEHUP; + lp->hupflags &= ~ISDN_CHARGEHUP; if (cfg->ihup) - p->local.hupflags |= ISDN_INHUP; + lp->hupflags |= ISDN_INHUP; else - p->local.hupflags &= ~ISDN_INHUP; + lp->hupflags &= ~ISDN_INHUP; if (cfg->chargeint > 10) { - p->local.hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE; - p->local.chargeint = cfg->chargeint * HZ; + lp->hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE; + lp->chargeint = cfg->chargeint * HZ; } - if (cfg->p_encap != p->local.p_encap) { + if (cfg->p_encap != lp->p_encap) { if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) { p->dev.hard_header = NULL; -#if (LINUX_VERSION_CODE < 0x02010F) - p->dev.header_cache_bind = NULL; -#else p->dev.hard_header_cache = NULL; -#endif p->dev.header_cache_update = NULL; p->dev.flags = IFF_NOARP|IFF_POINTOPOINT; } else { p->dev.hard_header = isdn_net_header; if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) { -#if (LINUX_VERSION_CODE < 0x02010F) - p->dev.header_cache_bind = p->local.org_hcb; -#else - p->dev.hard_header_cache = p->local.org_hhc; -#endif - p->dev.header_cache_update = p->local.org_hcu; + p->dev.hard_header_cache = lp->org_hhc; + p->dev.header_cache_update = lp->org_hcu; p->dev.flags = IFF_BROADCAST | IFF_MULTICAST; } else { -#if (LINUX_VERSION_CODE < 0x02010F) - p->dev.header_cache_bind = NULL; -#else p->dev.hard_header_cache = NULL; -#endif p->dev.header_cache_update = NULL; p->dev.flags = IFF_NOARP|IFF_POINTOPOINT; } } } - p->local.p_encap = cfg->p_encap; + lp->p_encap = cfg->p_encap; return 0; } return -ENODEV; @@ -2297,39 +2624,42 @@ isdn_net_getcfg(isdn_net_ioctl_cfg * cfg) isdn_net_dev *p = isdn_net_findif(cfg->name); if (p) { - strcpy(cfg->eaz, p->local.msn); - cfg->exclusive = p->local.exclusive; - if (p->local.pre_device >= 0) { - sprintf(cfg->drvid, "%s,%d", dev->drvid[p->local.pre_device], - p->local.pre_channel); + isdn_net_local *lp = p->local; + + strcpy(cfg->eaz, lp->msn); + cfg->exclusive = lp->exclusive; + if (lp->pre_device >= 0) { + sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device], + lp->pre_channel); } else cfg->drvid[0] = '\0'; - cfg->onhtime = p->local.onhtime; - cfg->charge = p->local.charge; - cfg->l2_proto = p->local.l2_proto; - cfg->l3_proto = p->local.l3_proto; - cfg->p_encap = p->local.p_encap; - cfg->secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0; + cfg->onhtime = lp->onhtime; + cfg->charge = lp->charge; + cfg->l2_proto = lp->l2_proto; + cfg->l3_proto = lp->l3_proto; + cfg->p_encap = lp->p_encap; + cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0; cfg->callback = 0; - if (p->local.flags & ISDN_NET_CALLBACK) + if (lp->flags & ISDN_NET_CALLBACK) cfg->callback = 1; - if (p->local.flags & ISDN_NET_CBOUT) + if (lp->flags & ISDN_NET_CBOUT) cfg->callback = 2; - cfg->cbhup = (p->local.flags & ISDN_NET_CBHUP) ? 1 : 0; - cfg->chargehup = (p->local.hupflags & 4) ? 1 : 0; - cfg->ihup = (p->local.hupflags & 8) ? 1 : 0; - cfg->cbdelay = p->local.cbdelay; - cfg->dialmax = p->local.dialmax; - cfg->slavedelay = p->local.slavedelay / HZ; - cfg->chargeint = (p->local.hupflags & ISDN_CHARGEHUP) ? - (p->local.chargeint / HZ) : 0; - cfg->pppbind = p->local.pppbind; - if (p->local.slave) - strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name); + cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0; + cfg->chargehup = (lp->hupflags & 4) ? 1 : 0; + cfg->ihup = (lp->hupflags & 8) ? 1 : 0; + cfg->cbdelay = lp->cbdelay; + cfg->dialmax = lp->dialmax; + cfg->triggercps = lp->triggercps; + cfg->slavedelay = lp->slavedelay / HZ; + cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ? + (lp->chargeint / HZ) : 0; + cfg->pppbind = lp->pppbind; + if (lp->slave) + strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name); else cfg->slave[0] = '\0'; - if (p->local.master) - strcpy(cfg->master, ((isdn_net_local *) p->local.master->priv)->name); + if (lp->master) + strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->name); else cfg->master[0] = '\0'; return 0; @@ -2352,8 +2682,8 @@ isdn_net_addphone(isdn_net_ioctl_phone * phone) if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL))) return -ENOMEM; strcpy(n->num, phone->phone); - n->next = p->local.phone[phone->outgoing & 1]; - p->local.phone[phone->outgoing & 1] = n; + n->next = p->local->phone[phone->outgoing & 1]; + p->local->phone[phone->outgoing & 1] = n; return 0; } return -ENODEV; @@ -2378,7 +2708,7 @@ isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones) save_flags(flags); cli(); inout &= 1; - for (n = p->local.phone[inout]; n; n = n->next) { + for (n = p->local->phone[inout]; n; n = n->next) { if (more) { put_user(' ', phones++); count++; @@ -2413,16 +2743,16 @@ isdn_net_delphone(isdn_net_ioctl_phone * phone) if (p) { save_flags(flags); cli(); - n = p->local.phone[inout]; + n = p->local->phone[inout]; m = NULL; while (n) { if (!strcmp(n->num, phone->phone)) { - if (p->local.dial == n) - p->local.dial = n->next; + if (p->local->dial == n) + p->local->dial = n->next; if (m) m->next = n->next; else - p->local.phone[inout] = n->next; + p->local->phone[inout] = n->next; kfree(n); return 0; } @@ -2449,15 +2779,15 @@ isdn_net_rmallphone(isdn_net_dev * p) save_flags(flags); cli(); for (i = 0; i < 2; i++) { - n = p->local.phone[i]; + n = p->local->phone[i]; while (n) { m = n->next; kfree(n); n = m; } - p->local.phone[i] = NULL; + p->local->phone[i] = NULL; } - p->local.dial = NULL; + p->local->dial = NULL; restore_flags(flags); return 0; } @@ -2472,9 +2802,9 @@ isdn_net_force_hangup(char *name) struct device *q; if (p) { - if (p->local.isdn_device < 0) + if (p->local->isdn_device < 0) return 1; - q = p->local.slave; + q = p->local->slave; /* If this interface has slaves, do a hangup for them also. */ while (q) { isdn_net_hangup(q); @@ -2496,7 +2826,7 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) save_flags(flags); cli(); - if (p->local.master) { + 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. */ @@ -2507,30 +2837,37 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) restore_flags(flags); return -EBUSY; } +#ifdef CONFIG_ISDN_X25 + if( p -> cprot && p -> cprot -> pops ) + p -> cprot -> pops -> proto_del ( p -> cprot ); +#endif /* Free all phone-entries */ isdn_net_rmallphone(p); /* If interface is bound exclusive, free channel-usage */ - if (p->local.exclusive != -1) - isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel); - if (p->local.master) { + if (p->local->exclusive != -1) + isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel); + if (p->local->master) { /* It's a slave-device, so update master's slave-pointer if necessary */ - if (((isdn_net_local *) (p->local.master->priv))->slave == &p->dev) - ((isdn_net_local *) (p->local.master->priv))->slave = p->local.slave; - } else + if (((isdn_net_local *) (p->local->master->priv))->slave == &p->dev) + ((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave; + } else { /* Unregister only if it's a master-device */ + p->dev.hard_header_cache = p->local->org_hhc; + p->dev.header_cache_update = p->local->org_hcu; unregister_netdev(&p->dev); + } /* Unlink device from chain */ if (q) q->next = p->next; else dev->netdev = p->next; - if (p->local.slave) { + if (p->local->slave) { /* If this interface has a slave, remove it also */ - char *slavename = ((isdn_net_local *) (p->local.slave->priv))->name; + char *slavename = ((isdn_net_local *) (p->local->slave->priv))->name; isdn_net_dev *n = dev->netdev; q = NULL; while (n) { - if (!strcmp(n->local.name, slavename)) { + if (!strcmp(n->local->name, slavename)) { isdn_net_realrm(n, q); break; } @@ -2543,6 +2880,7 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0); restore_flags(flags); + kfree(p->local); kfree(p); return 0; @@ -2561,7 +2899,7 @@ isdn_net_rm(char *name) p = dev->netdev; q = NULL; while (p) { - if (!strcmp(p->local.name, name)) + if (!strcmp(p->local->name, name)) return (isdn_net_realrm(p, q)); q = p; p = (isdn_net_dev *) p->next; @@ -2585,7 +2923,7 @@ isdn_net_rmall(void) save_flags(flags); cli(); while (dev->netdev) { - if (!dev->netdev->local.master) { + if (!dev->netdev->local->master) { /* Remove master-devices only, slaves get removed with their master */ if ((ret = isdn_net_realrm(dev->netdev, NULL))) { restore_flags(flags); diff --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h index 56df21081..19a084dd2 100644 --- a/drivers/isdn/isdn_net.h +++ b/drivers/isdn/isdn_net.h @@ -1,4 +1,4 @@ -/* $Id: isdn_net.h,v 1.5 1997/02/10 20:12:47 fritz Exp $ +/* $Id: isdn_net.h,v 1.6 1997/10/09 21:28:54 fritz Exp $ * header for Linux ISDN subsystem, network related functions (linklevel). * @@ -21,6 +21,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.h,v $ + * Revision 1.6 1997/10/09 21:28:54 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * * Revision 1.5 1997/02/10 20:12:47 fritz * Changed interface for reporting incoming calls. * @@ -45,11 +55,46 @@ #define ISDN_INHUP 8 /* Even if incoming, close after huptimeout */ #define ISDN_MANCHARGE 16 /* Charge Interval manually set */ +/* + * Definitions for Cisco-HDLC header. + */ + +typedef struct cisco_hdr { + __u8 addr; /* unicast/broadcast */ + __u8 ctrl; /* Always 0 */ + __u16 type; /* IP-typefield */ +} cisco_hdr; + +typedef struct cisco_slarp { + __u32 code; /* SLREQ/SLREPLY/KEEPALIVE */ + union { + struct { + __u32 ifaddr; /* My interface address */ + __u32 netmask; /* My interface netmask */ + } reply; + struct { + __u32 my_seq; /* Packet sequence number */ + __u32 your_seq; + } keepalive; + } slarp; + __u16 rel; /* Always 0xffff */ + __u16 t1; /* Uptime in usec >> 16 */ + __u16 t0; /* Uptime in usec & 0xffff */ +} cisco_slarp; + +#define CISCO_ADDR_UNICAST 0x0f +#define CISCO_ADDR_BROADCAST 0x8f +#define CISCO_TYPE_INET 0x0800 +#define CISCO_TYPE_SLARP 0x8035 +#define CISCO_SLARP_REPLY 0 +#define CISCO_SLARP_REQUEST 1 +#define CISCO_SLARP_KEEPALIVE 2 + extern char *isdn_net_new(char *, struct device *); extern char *isdn_net_newslave(char *); extern int isdn_net_rm(char *); extern int isdn_net_rmall(void); -extern int isdn_net_stat_callback(int, int); +extern int isdn_net_stat_callback(int, isdn_ctrl *); extern int isdn_net_setcfg(isdn_net_ioctl_cfg *); extern int isdn_net_getcfg(isdn_net_ioctl_cfg *); extern int isdn_net_addphone(isdn_net_ioctl_phone *); @@ -65,3 +110,4 @@ extern isdn_net_dev *isdn_net_findif(char *); extern int isdn_net_send_skb(struct device *, isdn_net_local *, struct sk_buff *); extern int isdn_net_rcv_skb(int, struct sk_buff *); +extern void isdn_net_slarp_out(void); diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index 0f29e340a..a4a45d9ea 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.27 1997/03/30 16:51:17 calle Exp $ +/* $Id: isdn_ppp.c,v 1.33 1998/02/20 17:11:54 fritz Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,37 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.33 1998/02/20 17:11:54 fritz + * Changes for recent kernels. + * + * Revision 1.32 1998/01/31 19:29:55 calle + * Merged changes from and for 2.1.82, not tested only compiled ... + * + * Revision 1.31 1997/10/09 21:29:01 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.30 1997/10/01 09:20:38 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.29 1997/08/21 23:11:44 fritz + * Added changes for kernels >= 2.1.45 + * + * Revision 1.28 1997/06/17 13:05:57 hipp + * Applied Eric's underflow-patches (slightly modified) + * more compression changes (but disabled at the moment) + * changed one copy_to_user() to run with enabled IRQs + * a few MP changes + * changed 'proto' handling in the isdn_ppp receive code + * * Revision 1.27 1997/03/30 16:51:17 calle * changed calls to copy_from_user/copy_to_user and removed verify_area * were possible. @@ -128,9 +159,7 @@ #include <linux/module.h> #include <linux/version.h> #include <linux/isdn.h> -#if (LINUX_VERSION_CODE >= 0x020117) #include <linux/poll.h> -#endif #include "isdn_common.h" #include "isdn_ppp.h" #include "isdn_net.h" @@ -148,6 +177,12 @@ static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto); static int isdn_ppp_if_get_unit(char *namebuf); static int isdn_ppp_set_compressor(struct ippp_struct *is,int num); +static struct sk_buff *isdn_ppp_decompress(struct sk_buff *, + struct ippp_struct *,struct ippp_struct *); +static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp, + struct sk_buff *skb); +static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, + struct ippp_struct *is,struct ippp_struct *master,int type); #ifdef CONFIG_ISDN_MPP static int isdn_ppp_bundle(struct ippp_struct *, int unit); @@ -160,7 +195,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.27 $"; +char *isdn_ppp_revision = "$Revision: 1.33 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; static struct isdn_ppp_compressor *ipc_head = NULL; @@ -267,7 +302,7 @@ isdn_ppp_bind(isdn_net_local * lp) char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */ memset(exclusive, 0, ISDN_MAX_CHANNELS); while (net_dev) { /* step through net devices to find exclusive minors */ - isdn_net_local *lp = &net_dev->local; + isdn_net_local *lp = net_dev->local; if (lp->pppbind >= 0) exclusive[lp->pppbind] = 1; net_dev = net_dev->next; @@ -382,7 +417,12 @@ isdn_ppp_open(int min, struct file *file) if (is->debug & 0x1) printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state); + /* compression stuff */ is->compressor = NULL; + is->decomp_stat = is->comp_stat = NULL; + is->link_compressor = NULL; + is->link_decomp_stat = is->link_comp_stat = NULL; + is->lp = NULL; is->mp_seqno = 0; /* MP sequence number */ is->pppcfg = 0; /* ppp configuration */ @@ -644,50 +684,6 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) return 0; } -#if (LINUX_VERSION_CODE < 0x020117) -int -isdn_ppp_select(int min, struct file *file, int type, select_table * st) -{ - struct ippp_buf_queue *bf, - *bl; - unsigned long flags; - struct ippp_struct *is; - - is = file->private_data; - - if (is->debug & 0x2) - printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n", min, type); - - if (!(is->state & IPPP_OPEN)) - return -EINVAL; - - switch (type) { - case SEL_IN: - save_flags(flags); - cli(); - bl = is->last; - bf = is->first; - /* - * if IPPP_NOBLOCK is set we return even if we have nothing to read - */ - if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) { - select_wait(&is->wq, st); - restore_flags(flags); - return 0; - } - is->state &= ~IPPP_NOBLOCK; - restore_flags(flags); - return 1; - case SEL_OUT: - /* we're always ready to send .. */ - return 1; - case SEL_EX: - select_wait(&is->wq1, st); - return 0; - } - return 1; -} -#else unsigned int isdn_ppp_poll(struct file *file, poll_table * wait) { @@ -700,7 +696,8 @@ isdn_ppp_poll(struct file *file, poll_table * wait) is = file->private_data; if (is->debug & 0x2) - printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_dentry->d_inode->i_rdev)); + printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", + MINOR(file->f_dentry->d_inode->i_rdev)); poll_wait(file, &is->wq, wait); @@ -725,8 +722,6 @@ isdn_ppp_poll(struct file *file, poll_table * wait) restore_flags(flags); return mask; } -#endif - /* * fill up isdn_ppp_read() queue .. @@ -798,31 +793,35 @@ isdn_ppp_read(int min, struct file *file, char *buf, int count) struct ippp_buf_queue *b; int r; unsigned long flags; + unsigned char *save_buf; is = file->private_data; if (!(is->state & IPPP_OPEN)) return 0; + if ((r = verify_area(VERIFY_WRITE, (void *) buf, count))) + return r; + save_flags(flags); cli(); b = is->first->next; - if (!b->buf) { + save_buf = b->buf; + if (!save_buf) { restore_flags(flags); return -EAGAIN; } if (b->len < count) count = b->len; - if ((r = copy_to_user(buf, b->buf, count))) { - restore_flags(flags); - return r; - } - kfree(b->buf); b->buf = NULL; is->first = b; + restore_flags(flags); + copy_to_user(buf, save_buf, count); + kfree(save_buf); + return count; } @@ -872,14 +871,13 @@ isdn_ppp_write(int min, struct file *file, const char *buf, int count) printk(KERN_WARNING "isdn_ppp_write: out of memory!\n"); return count; } - SET_SKB_FREE(skb); if (copy_from_user(skb_put(skb, count), buf, count)) return -EFAULT; if (is->debug & 0x40) { printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32); } - if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb)) != count) { + if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb)) != count) { if (lp->sav_skb) { dev_kfree_skb(lp->sav_skb); printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", cnt, count); @@ -935,45 +933,65 @@ isdn_ppp_cleanup(void) } /* + * get the PPP protocol header and pull skb + */ +static int isdn_ppp_strip_proto(struct sk_buff *skb) +{ + int proto; + if (skb->data[0] & 0x1) { + proto = skb->data[0]; + skb_pull(skb, 1); /* protocol ID is only 8 bit */ + } else { + proto = ((int) skb->data[0] << 8) + skb->data[1]; + skb_pull(skb, 2); + } + return proto; +} + + +/* * handler for incoming packets on a syncPPP interface */ -void -isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb) +void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb) { struct ippp_struct *is; + int proto; + is = ippp_table[lp->ppp_slot]; if (is->debug & 0x4) { printk(KERN_DEBUG "ippp_receive: len: %d\n", (int) skb->len); isdn_ppp_frame_log("receive", skb->data, skb->len, 32); } - if (net_dev->local.master) { + if (net_dev->local->master) { printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n"); - net_dev = ((isdn_net_local *) net_dev->local.master->priv)->netdev; + net_dev = ((isdn_net_local *) net_dev->local->master->priv)->netdev; } if (skb->data[0] == 0xff && skb->data[1] == 0x03) skb_pull(skb, 2); else if (is->pppcfg & SC_REJ_COMP_AC) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; /* discard it silently */ } + + proto = isdn_ppp_strip_proto(skb); + #ifdef CONFIG_ISDN_MPP if (!(is->mpppcfg & SC_REJ_MP_PROT)) { - int proto; int sqno_end; - if (skb->data[0] & 0x1) { - proto = skb->data[0]; - skb_pull(skb, 1); /* protocol ID is only 8 bit */ - } else { - proto = ((int) skb->data[0] << 8) + skb->data[1]; - skb_pull(skb, 2); + + if(proto == PPP_LINK_COMP) { + printk(KERN_DEBUG "received single link compressed frame\n"); + skb = isdn_ppp_decompress(skb,is,NULL); + if(!skb) + return; + proto = isdn_ppp_strip_proto(skb); } + if (proto == PPP_MP) { isdn_net_local *lpq; - long sqno, - min_sqno, - tseq; + long sqno, min_sqno, tseq; + u_char BEbyte = skb->data[0]; if (is->debug & 0x8) printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto, @@ -987,6 +1005,10 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk skb_pull(skb, 2); } + /* + * new sequence number lower than last number? (this is only allowed + * for overflow case) + */ if ((tseq = is->last_link_seqno) >= sqno) { int range = is->range; if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */ @@ -995,9 +1017,14 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk sqno += range; is->last_link_seqno = sqno; } - } else + } else { + /* here, we should also add an redundancy check */ is->last_link_seqno = sqno; + } + /* + * step over all links to find lowest link number + */ for (min_sqno = LONG_MAX, lpq = net_dev->queue;;) { long lls = ippp_table[lpq->ppp_slot]->last_link_seqno; if (lls >= 0 && lls < min_sqno) @@ -1006,11 +1033,14 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk if (lpq == net_dev->queue) break; } - if (min_sqno >= ippp_table[lpq->ppp_slot]->range) { /* OK, every link overflowed */ - int mask = ippp_table[lpq->ppp_slot]->range - 1; /* range is a power of 2 */ -#if 0 - isdn_ppp_cleanup_queue(net_dev, min_sqno); -#endif + + /* + * for the case, that the last frame numbers of all + * links are overflowed: mask/reduce the sequenece number to + * 'normal' numbering. + */ + if (min_sqno >= ippp_table[lpq->ppp_slot]->range) { + int mask = ippp_table[lpq->ppp_slot]->range-1; /* range is power of two, so a mask will do the job */ isdn_ppp_mask_queue(net_dev, mask); net_dev->ib.next_num &= mask; { @@ -1064,7 +1094,6 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk if (!q) { net_dev->ib.modify = 0; printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n"); - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; /* discard */ } @@ -1093,7 +1122,8 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk * packet was 'in order' .. push it higher */ net_dev->ib.next_num = sqno_end + 1; - isdn_ppp_push_higher(net_dev, lp, skb, -1); + proto = isdn_ppp_strip_proto(skb); + isdn_ppp_push_higher(net_dev, lp, skb, proto); } isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno); net_dev->ib.modify = 0; @@ -1102,7 +1132,7 @@ isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *sk isdn_ppp_push_higher(net_dev, lp, skb, proto); } else #endif - isdn_ppp_push_higher(net_dev, lp, skb, -1); + isdn_ppp_push_higher(net_dev, lp, skb, proto); } /* @@ -1115,19 +1145,21 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff struct device *dev = &net_dev->dev; struct ippp_struct *is = ippp_table[lp->ppp_slot]; - if (proto < 0) { /* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */ - if (skb->data[0] & 0x01) { /* is it odd? */ - proto = (unsigned char) skb->data[0]; - skb_pull(skb, 1); /* protocol ID is only 8 bit */ - } else { - proto = ((int) (unsigned char) skb->data[0] << 8) + (unsigned char) skb->data[1]; - skb_pull(skb, 2); - } - } if (is->debug & 0x10) { printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto); isdn_ppp_frame_log("rpush", skb->data, skb->len, 32); } + + if(proto == PPP_COMP) { + if(!lp->master) + skb = isdn_ppp_decompress(skb,is,is); + else + skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]); + if(!skb) + return; + proto = isdn_ppp_strip_proto(skb); + } + switch (proto) { case PPP_IPX: /* untested */ if (is->debug & 0x20) @@ -1140,10 +1172,9 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff case PPP_VJC_UNCOMP: if (is->debug & 0x20) printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n"); - if (slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) { + if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) { printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n"); - net_dev->local.stats.rx_dropped++; - SET_SKB_FREE(skb); + net_dev->local->stats.rx_dropped++; dev_kfree_skb(skb); return; } @@ -1164,11 +1195,9 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff int pkt_len; skb = dev_alloc_skb(skb_old->len + 40); - SET_SKB_FREE(skb_old); - if (!skb) { printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); - net_dev->local.stats.rx_dropped++; + net_dev->local->stats.rx_dropped++; dev_kfree_skb(skb_old); return; } @@ -1176,11 +1205,10 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff skb_put(skb, skb_old->len + 40); memcpy(skb->data, skb_old->data, skb_old->len); skb->mac.raw = skb->data; - pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp, + pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb_old->len); dev_kfree_skb(skb_old); if (pkt_len < 0) { - SET_SKB_FREE(skb); dev_kfree_skb(skb); lp->stats.rx_dropped++; return; @@ -1191,20 +1219,21 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff #else printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n"); lp->stats.rx_dropped++; - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; #endif break; + case PPP_CCP: + isdn_ppp_receive_ccp(net_dev,lp,skb); + /* fall through */ default: isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot); /* push data to pppd device */ - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; } netif_rx(skb); - /* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */ + /* net_dev->local->stats.rx_packets++; *//* done in isdn_net.c */ /* Reset hangup-timer */ lp->huptimer = 0; @@ -1212,6 +1241,24 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff } /* + * isdn_ppp_skb_push .. + * checks whether we have enough space at the beginning of the SKB + * and allocs a new SKB if necessary + */ +static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) +{ + struct sk_buff *skb = *skb_p; + + if(skb_headroom(skb) < len) { + printk(KERN_ERR "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len); + dev_kfree_skb(skb); + return NULL; + } + return skb_push(skb,len); +} + + +/* * send ppp frame .. we expect a PIDCOMPressable proto -- * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP) * @@ -1223,12 +1270,10 @@ int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) { struct device *mdev = ((isdn_net_local *) (dev->priv))->master; /* get master (for redundancy) */ - isdn_net_local *lp, - *mlp; + isdn_net_local *lp,*mlp; isdn_net_dev *nd; unsigned int proto = PPP_IP; /* 0x21 */ - struct ippp_struct *ipt, - *ipts; + struct ippp_struct *ipt,*ipts; if (mdev) mlp = (isdn_net_local *) (mdev->priv); @@ -1250,6 +1295,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) printk(KERN_INFO "%s: IP frame delayed.\n", dev->name); return 1; } + switch (ntohs(skb->protocol)) { case ETH_P_IP: proto = PPP_IP; @@ -1297,11 +1343,18 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) * after this line .. requeueing in the device queue is no longer allowed!!! */ + /* Pull off the fake header we stuck on earlier to keep + * the fragemntation code happy. + * this will break the ISDN_SYNCPPP_READDRESS hack a few lines + * above. So, enabling this is no longer allowed + */ + skb_pull(skb,IPPP_MAX_HEADER); + if (ipt->debug & 0x4) printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len); #ifdef CONFIG_ISDN_PPP_VJ - if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes .. but this check again */ + if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */ struct sk_buff *new_skb; new_skb = dev_alloc_skb(skb->len); @@ -1310,14 +1363,13 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) int pktlen; new_skb->dev = skb->dev; - SET_SKB_FREE(new_skb); skb_put(new_skb, skb->len); buf = skb->data; pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data, &buf, !(ipts->pppcfg & SC_NO_TCP_CCID)); - if (buf != skb->data) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*) */ + if (buf != skb->data) { if (new_skb->data != buf) printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n"); dev_kfree_skb(skb); @@ -1339,6 +1391,11 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) } #endif + /* + * normal or bundle compression + */ + skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0); + if (ipt->debug & 0x24) printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto); @@ -1349,13 +1406,17 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) ipts->mp_seqno++; nd->queue = nd->queue->next; if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) { - unsigned char *data = skb_push(skb, 3); + unsigned char *data = isdn_ppp_skb_push(&skb, 3); + if(!data) + return 0; mp_seqno &= 0xfff; - data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8); /* (B)egin & (E)ndbit .. */ + data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf); /* (B)egin & (E)ndbit .. */ data[1] = mp_seqno & 0xff; data[2] = proto; /* PID compression */ } else { - unsigned char *data = skb_push(skb, 5); + unsigned char *data = isdn_ppp_skb_push(&skb, 5); + if(!data) + return 0; data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */ data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */ data[2] = (mp_seqno >> 8) & 0xff; @@ -1365,17 +1426,29 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *dev) proto = PPP_MP; /* MP Protocol, 0x003d */ } #endif + + /* + * 'link' compression + */ + skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1); + if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) { - unsigned char *data = skb_push(skb,1); + unsigned char *data = isdn_ppp_skb_push(&skb,1); + if(!data) + return 0; data[0] = proto & 0xff; } else { - unsigned char *data = skb_push(skb,2); + unsigned char *data = isdn_ppp_skb_push(&skb,2); + if(!data) + return 0; data[0] = (proto >> 8) & 0xff; data[1] = proto & 0xff; } if(!(ipt->pppcfg & SC_COMP_AC)) { - unsigned char *data = skb_push(skb,2); + unsigned char *data = isdn_ppp_skb_push(&skb,2); + if(!data) + return 0; data[0] = 0xff; /* All Stations */ data[1] = 0x03; /* Unnumbered information */ } @@ -1406,10 +1479,8 @@ isdn_ppp_free_sqqueue(isdn_net_dev * p) p->ib.sq = NULL; while (q) { struct sqqueue *qn = q->next; - if (q->skb) { - SET_SKB_FREE(q->skb); + if (q->skb) dev_kfree_skb(q->skb); - } kfree(q); q = qn; } @@ -1424,7 +1495,6 @@ isdn_ppp_free_mpqueue(isdn_net_dev * p) while (q) { struct mpqueue *ql = q->next; - SET_SKB_FREE(q->skb); dev_kfree_skb(q->skb); kfree(q); q = ql; @@ -1599,7 +1669,6 @@ isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long if (!(*skb)) { while (q) { struct mpqueue *ql = q->next; - SET_SKB_FREE(q->skb); dev_kfree_skb(q->skb); kfree(q); q = ql; @@ -1612,7 +1681,6 @@ isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long struct mpqueue *ql = q->next; memcpy((*skb)->data + cnt, q->skb->data, q->skb->len); cnt += q->skb->len; - SET_SKB_FREE(q->skb); dev_kfree_skb(q->skb); kfree(q); q = ql; @@ -1632,13 +1700,15 @@ isdn_ppp_cleanup_sqqueue(isdn_net_dev * net_dev, isdn_net_local * lp, long min_s struct sqqueue *q; while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno)) { + int proto; if (q->sqno_start != net_dev->ib.next_num) { printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n", net_dev->ib.next_num); #ifdef CONFIG_ISDN_PPP_VJ - slhc_toss(ippp_table[net_dev->local.ppp_slot]->slcomp); + slhc_toss(ippp_table[net_dev->local->ppp_slot]->slcomp); #endif } - isdn_ppp_push_higher(net_dev, lp, q->skb, -1); + proto = isdn_ppp_strip_proto(q->skb); + isdn_ppp_push_higher(net_dev, lp, q->skb, proto); net_dev->ib.sq = q->next; net_dev->ib.next_num = q->sqno_end + 1; kfree(q); @@ -1663,32 +1733,30 @@ isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno) struct mpqueue *ql, *q = dev->mp_last; - while (q) { - if (q->sqno < min_sqno) { - if (q->BEbyte & MP_END_FRAG) { - printk(KERN_DEBUG "ippp: freeing stale packet!\n"); - if ((dev->mp_last = q->next)) - q->next->last = NULL; - while (q) { - ql = q->last; - SET_SKB_FREE(q->skb); - dev_kfree_skb(q->skb); - kfree(q); + while(q && (q->sqno < min_sqno) ) { + if ( (q->BEbyte & MP_END_FRAG) || + (q->next && (q->next->sqno <= min_sqno) && (q->next->BEbyte & MP_BEGIN_FRAG)) ) { + printk(KERN_DEBUG "ippp: freeing stale packet(s), min_sq: %ld!\n",min_sqno); + if ((dev->mp_last = q->next)) + q->next->last = NULL; + while (q) { + ql = q->last; + printk(KERN_DEBUG "ippp, freeing packet with sqno: %ld\n",q->sqno); + dev_kfree_skb(q->skb); + kfree(q); #ifdef CONFIG_ISDN_PPP_VJ - toss = 1; + toss = 1; #endif - q = ql; - } - q = dev->mp_last; - } else - q = q->next; + q = ql; + } + q = dev->mp_last; } else - break; + q = q->next; } #ifdef CONFIG_ISDN_PPP_VJ /* did we free a stale frame ? */ if (toss) - slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp); + slhc_toss(ippp_table[dev->local->ppp_slot]->slcomp); #endif } @@ -1708,7 +1776,7 @@ isdn_ppp_timer_timeout(void) *qn; while (net_dev) { - isdn_net_local *lp = &net_dev->local; + isdn_net_local *lp = net_dev->local; if (net_dev->ib.modify || lp->master) { /* interface locked or slave? */ net_dev = net_dev->next; continue; @@ -1728,7 +1796,8 @@ isdn_ppp_timer_timeout(void) net_dev->ib.next_num = q->sqno_end + 1; q->next = NULL; for (; ql;) { - isdn_ppp_push_higher(net_dev, lp, ql->skb, -1); + int proto = isdn_ppp_strip_proto(ql->skb); + isdn_ppp_push_higher(net_dev, lp, ql->skb, proto); qn = ql->next; kfree(ql); ql = qn; @@ -1804,7 +1873,7 @@ isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd) case SIOCGPPPVER: r = (char *) ifr->ifr_ifru.ifru_data; len = strlen(PPP_VERSION) + 1; - error = copy_to_user(r, PPP_VERSION, len); + error = copy_to_user(r, PPP_VERSION, len); break; case SIOCGPPPSTATS: error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev); @@ -1853,7 +1922,7 @@ isdn_ppp_dial_slave(char *name) if (!(ndev = isdn_net_findif(name))) return 1; - lp = &ndev->local; + lp = ndev->local; if (!(lp->flags & ISDN_NET_CONNECTED)) return 5; @@ -1884,7 +1953,7 @@ isdn_ppp_hangup_slave(char *name) if (!(ndev = isdn_net_findif(name))) return 1; - lp = &ndev->local; + lp = ndev->local; if (!(lp->flags & ISDN_NET_CONNECTED)) return 5; @@ -1905,6 +1974,128 @@ isdn_ppp_hangup_slave(char *name) #endif } +/* + * PPP compression stuff + */ +static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master) +{ +#if 1 + printk(KERN_ERR "compression not included!\n"); + dev_kfree_skb(skb); + return NULL; +#else + if(!master) { + /* + * single link compression + */ + if(!is->link_compressor) { + printk(KERN_ERR "ippp: no (link) compressor defined!\n"); + dev_kfree_skb(skb); + return NULL; + } + if(!is->link_decomp_stat) { + printk(KERN_DEBUG "ippp: initialize link compressor\n"); + } +/* + -> decompress link +*/ + } + else { + /* + * 'normal' or bundle-compression + */ + if(!master->compressor) { + printk(KERN_ERR "ippp: no (link) compressor defined!\n"); + dev_kfree_skb(skb); + return NULL; + } + if(!master->decomp_stat) { +#if 0 + master->decomp_stat = (master->compressor->decomp_alloc)( .. ); +#endif + printk(KERN_DEBUG "ippp: initialize compressor\n"); + } + } + + return skb; +#endif +} + +/* + * compress a frame + * type=0: normal/bundle compression + * =1: link compression + * returns original skb if we haven't compressed the frame + * and a new skb pointer if we've done it + */ +static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, + struct ippp_struct *is,struct ippp_struct *master,int type) +{ +#if 1 + return skb_in; +#else + int ret; + int new_proto; + struct isdn_ppp_compressor *compressor; + void *stat; + struct sk_buff *skb_out; + + if(type) { /* type=1 => Link compression */ + compressor = is->link_compressor; + stat = is->link_comp_stat; + new_proto = PPP_LINK_COMP; + } + else { + if(!master) { + compressor = is->compressor; + stat = is->comp_stat; + } + else { + compressor = master->compressor; + stat = master->comp_stat; + } + new_proto = PPP_COMP; + } + + if(!compressor) { + printk(KERN_ERR "No compressor set!\n"); + return skb_in; + } + if(!stat) { + /* init here ? */ + return skb_in; + } + + skb_out = dev_alloc_skb(skb_in->len); + if(!skb_out) + return skb_in; + + ret = (compressor->compress)(stat,skb_in,skb_out,*proto); + if(!ret) { + dev_kfree_skb(skb_out); + return skb_in; + } + + dev_kfree_skb(skb_in); + *proto = new_proto; + return skb_out; +#endif + +} + +/* + * we received a CCP frame .. + * not a clean solution, but we SHOULD handle a few cased in the kernel + */ +static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, + struct sk_buff *skb) +{ +#if 0 + printk(KERN_DEBUG "isdn_ppp_receive_cpp: %02x %02x %02x %02x %02x %02x %02x %02x\n", + skb->data[0],skb->data[1],skb->data[2],skb->data[3], + skb->data[4],skb->data[5],skb->data[6],skb->data[7] ); +#endif +} int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc) { @@ -1937,6 +2128,7 @@ static int isdn_ppp_set_compressor(struct ippp_struct *is,int num) if(ipc->num == num) { return 0; is->compressor = ipc; + is->link_compressor = ipc; } ipc = ipc->next; } diff --git a/drivers/isdn/isdn_ppp.h b/drivers/isdn/isdn_ppp.h index 11184b8c0..0e05af08b 100644 --- a/drivers/isdn/isdn_ppp.h +++ b/drivers/isdn/isdn_ppp.h @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.h,v 1.9 1997/02/11 18:32:59 fritz Exp $ +/* $Id: isdn_ppp.h,v 1.12 1998/01/31 22:07:48 keil Exp $ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,21 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.h,v $ + * Revision 1.12 1998/01/31 22:07:48 keil + * changes for newer kernels + * + * Revision 1.11 1997/10/01 09:20:44 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.10 1997/06/17 13:06:00 hipp + * Applied Eric's underflow-patches (slightly modified) + * more compression changes (but disabled at the moment) + * changed one copy_to_user() to run with enabled IRQs + * a few MP changes + * changed 'proto' handling in the isdn_ppp receive code + * * Revision 1.9 1997/02/11 18:32:59 fritz * Bugfix in isdn_ppp_free_mpqueue(). * @@ -63,11 +78,7 @@ extern int isdn_ppp_bind(isdn_net_local *); extern int isdn_ppp_xmit(struct sk_buff *, struct device *); extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *); extern int isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int); -#if (LINUX_VERSION_CODE < 0x020117) -extern int isdn_ppp_select(int, struct file *, int, select_table *); -#else -extern unsigned int isdn_ppp_poll(struct file *, poll_table *); -#endif +extern unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *); extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long); extern void isdn_ppp_release(int, struct file *); extern int isdn_ppp_dial_slave(char *); @@ -78,3 +89,7 @@ extern void isdn_ppp_wakeup_daemon(isdn_net_local *); #define IPPP_CLOSEWAIT 0x04 #define IPPP_NOBLOCK 0x08 #define IPPP_ASSIGNED 0x10 + +#define IPPP_MAX_HEADER 10 + + diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index 0929dbc26..fd976ff50 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.c,v 1.41 1997/05/27 15:17:31 fritz Exp $ +/* $Id: isdn_tty.c,v 1.47 1998/02/22 19:44:14 fritz Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -20,6 +20,35 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.47 1998/02/22 19:44:14 fritz + * Bugfixes and improvements regarding V.110, V.110 now running. + * + * Revision 1.46 1998/02/20 17:23:08 fritz + * Changes for recent kernels. + * Merged in contributions by Thomas Pfeiffer (V.110 T.70+ Extended FAX stuff) + * Added symbolic constants for Modem-Registers. + * + * Revision 1.45 1998/01/31 22:07:49 keil + * changes for newer kernels + * + * Revision 1.44 1998/01/31 19:30:02 calle + * Merged changes from and for 2.1.82, not tested only compiled ... + * + * Revision 1.43 1997/10/09 21:29:04 fritz + * New HL<->LL interface: + * New BSENT callback with nr. of bytes included. + * Sending without ACK. + * New L1 error status (not yet in use). + * Cleaned up obsolete structures. + * Implemented Cisco-SLARP. + * Changed local net-interface data to be dynamically allocated. + * Removed old 2.0 compatibility stuff. + * + * Revision 1.42 1997/10/01 09:20:49 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * * Revision 1.41 1997/05/27 15:17:31 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -199,6 +228,8 @@ #define VBUFX (VBUF/16) #endif +#define FIX_FILE_TRANSFER + /* Prototypes */ static int isdn_tty_edit_at(const char *, int, modem_info *, int); @@ -223,12 +254,63 @@ static int bit2si[8] = static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.41 $"; +char *isdn_tty_revision = "$Revision: 1.47 $"; #define DLE 0x10 #define ETX 0x03 #define DC4 0x14 +/* + * Definition of some special Registers of AT-Emulator + */ +#define REG_RINGATA 0 +#define REG_RINGCNT 1 +#define REG_ESC 2 +#define REG_CR 3 +#define REG_LF 4 +#define REG_BS 5 + +#define REG_RESP 12 +#define BIT_RESP 1 +#define REG_RESPNUM 12 +#define BIT_RESPNUM 2 +#define REG_ECHO 12 +#define BIT_ECHO 4 +#define REG_DCD 12 +#define BIT_DCD 8 +#define REG_CTS 12 +#define BIT_CTS 16 +#define REG_DTRR 12 +#define BIT_DTRR 32 +#define REG_DSR 12 +#define BIT_DSR 64 +#define REG_CPPP 12 +#define BIT_CPPP 128 + +#define REG_DELXMT 13 +#define BIT_DELXMT 1 +#define REG_T70 13 +#define BIT_T70 2 +#define BIT_T70_EXT 32 +#define REG_DTRHUP 13 +#define BIT_DTRHUP 4 +#define REG_RESPXT 13 +#define BIT_RESPXT 8 +#define REG_CIDONCE 13 +#define BIT_CIDONCE 16 +#define REG_RUNG 13 +#define BIT_RUNG 64 + +#define REG_L2PROT 14 +#define REG_L3PROT 15 +#define REG_PSIZE 16 +#define REG_WSIZE 17 +#define REG_SI1 18 +#define REG_SI2 19 +#define REG_SI1I 20 +#define REG_PLAN 21 +#define REG_SCREEN 22 + /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() * to stuff incoming data directly into a tty's flip-buffer. This * is done to speed up tty-receiving if the receive-queue is empty. @@ -272,10 +354,9 @@ isdn_tty_try_read(modem_info * info, struct sk_buff *skb) #ifdef CONFIG_ISDN_AUDIO } #endif - if (info->emu.mdmreg[12] & 128) + if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP) tty->flip.flag_buf_ptr[len - 1] = 0xff; queue_task(&tty->flip.tqueue, &tq_timer); - SET_SKB_FREE(skb); kfree_skb(skb); return 1; } @@ -319,7 +400,7 @@ isdn_tty_readmodem(void) tty->flip.char_buf_ptr, tty->flip.flag_buf_ptr, c, 0); /* CISCO AsyncPPP Hack */ - if (!(info->emu.mdmreg[12] & 128)) + if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP)) memset(tty->flip.flag_buf_ptr, 0, r); tty->flip.count += r; tty->flip.flag_buf_ptr += r; @@ -371,19 +452,26 @@ isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) #endif ) { /* If Modem not listening, drop data */ - SET_SKB_FREE(skb); kfree_skb(skb); return 1; } - if (info->emu.mdmreg[13] & 2) - /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */ - if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1))) - skb_pull(skb, 4); + if (info->emu.mdmreg[REG_T70] & BIT_T70) { + if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) { + /* T.70 decoding: throw away the T.70 header (2 or 4 bytes) */ + if (skb->data[0] == 3) /* pure data packet -> 4 byte headers */ + skb_pull(skb, 4); + else + if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr */ + skb_pull(skb, 2); + } else + /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */ + if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1))) + skb_pull(skb, 4); + } #ifdef CONFIG_ISDN_AUDIO if (skb_headroom(skb) < sizeof(isdn_audio_skb)) { printk(KERN_WARNING "isdn_audio: insufficient skb_headroom, dropping\n"); - SET_SKB_FREE(skb); kfree_skb(skb); return 1; } @@ -454,16 +542,12 @@ isdn_tty_cleanup_xmit(modem_info * info) save_flags(flags); cli(); if (skb_queue_len(&info->xmit_queue)) - while ((skb = skb_dequeue(&info->xmit_queue))) { - SET_SKB_FREE(skb); + while ((skb = skb_dequeue(&info->xmit_queue))) kfree_skb(skb); - } #ifdef CONFIG_ISDN_AUDIO if (skb_queue_len(&info->dtmf_queue)) - while ((skb = skb_dequeue(&info->dtmf_queue))) { - SET_SKB_FREE(skb); + while ((skb = skb_dequeue(&info->dtmf_queue))) kfree_skb(skb); - } #endif restore_flags(flags); } @@ -479,7 +563,7 @@ isdn_tty_tint(modem_info * info) return; len = skb->len; if ((slen = isdn_writebuf_skb_stub(info->isdn_driver, - info->isdn_channel, skb)) == len) { + info->isdn_channel, 1, skb)) == len) { struct tty_struct *tty = info->tty; info->send_outstanding++; info->msr |= UART_MSR_CTS; @@ -492,7 +576,6 @@ isdn_tty_tint(modem_info * info) } if (slen < 0) { /* Error: no channel, already shutdown, or wrong parameter */ - SET_SKB_FREE(skb); dev_kfree_skb(skb); return; } @@ -590,7 +673,7 @@ isdn_tty_end_vrx(const char *buf, int c, int from_user) while (c--) { if (from_user) - GET_USER(ch, buf); + get_user(ch, buf); else ch = *buf; if ((ch != 0x11) && (ch != 0x13)) @@ -640,7 +723,7 @@ isdn_tty_senddown(modem_info * info) restore_flags(flags); return; } - if ((info->emu.mdmreg[12] & 0x10) != 0) + if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0) info->msr &= ~UART_MSR_CTS; info->lsr &= ~UART_LSR_TEMT; if (info->isdn_driver < 0) { @@ -710,10 +793,13 @@ isdn_tty_senddown(modem_info * info) } } #endif /* CONFIG_ISDN_AUDIO */ - SET_SKB_FREE(skb); - if (info->emu.mdmreg[13] & 2) + if (info->emu.mdmreg[REG_T70] & BIT_T70) { /* Add T.70 simplified header */ - memcpy(skb_push(skb, 4), "\1\0\1\0", 4); + if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) + memcpy(skb_push(skb, 2), "\1\0", 2); + else + memcpy(skb_push(skb, 4), "\1\0\1\0", 4); + } skb_queue_tail(&info->xmit_queue, skb); } @@ -761,27 +847,27 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) { int usg = ISDN_USAGE_MODEM; int si = 7; - int l2 = m->mdmreg[14]; + int l2 = m->mdmreg[REG_L2PROT]; isdn_ctrl cmd; ulong flags; int i; int j; for (j = 7; j >= 0; j--) - if (m->mdmreg[18] & (1 << j)) { + if (m->mdmreg[REG_SI1] & (1 << j)) { si = bit2si[j]; break; } #ifdef CONFIG_ISDN_AUDIO if (si == 1) { - l2 = 4; + l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } #endif - m->mdmreg[20] = si2bit[si]; + m->mdmreg[REG_SI1I] = si2bit[si]; save_flags(flags); cli(); - i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1); + i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1); if (i < 0) { restore_flags(flags); isdn_tty_modem_result(6, info); @@ -798,32 +884,32 @@ isdn_tty_dial(char *n, modem_info * info, atemu * m) cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; cmd.command = ISDN_CMD_CLREAZ; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETEAZ; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL2; info->last_l2 = l2; cmd.arg = info->isdn_channel + (l2 << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; - cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; sprintf(cmd.parm.setup.phone, "%s", n); sprintf(cmd.parm.setup.eazmsn, "%s", isdn_map_eaz2msn(m->msn, info->isdn_driver)); cmd.parm.setup.si1 = si; - cmd.parm.setup.si2 = m->mdmreg[19]; + cmd.parm.setup.si2 = m->mdmreg[REG_SI2]; cmd.command = ISDN_CMD_DIAL; info->dialing = 1; strcpy(dev->num[i], n); isdn_info_update(); - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); } } @@ -865,6 +951,9 @@ isdn_tty_modem_hup(modem_info * info, int local) info->adpcmr = NULL; } #endif + if ((info->msr & UART_MSR_RI) && + (info->emu.mdmreg[REG_RUNG] & BIT_RUNG)) + isdn_tty_modem_result(12, info); info->msr &= ~(UART_MSR_DCD | UART_MSR_RI); info->lsr |= UART_LSR_TEMT; if (info->isdn_driver >= 0) { @@ -872,11 +961,11 @@ isdn_tty_modem_hup(modem_info * info, int local) cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_HANGUP; cmd.arg = info->isdn_channel; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); } isdn_all_eaz(info->isdn_driver, info->isdn_channel); - info->emu.mdmreg[1] = 0; - usage = (info->emu.mdmreg[20] == 1) ? + info->emu.mdmreg[REG_RINGCNT] = 0; + usage = (info->emu.mdmreg[REG_SI1I] == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM; isdn_free_channel(info->isdn_driver, info->isdn_channel, usage); @@ -937,7 +1026,7 @@ isdn_tty_change_speed(modem_info * info) isdn_tty_modem_ncarrier(info); } else { info->mcr &= ~UART_MCR_DTR; - if (info->emu.mdmreg[13] & 4) { + if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in changespeed\n"); #endif @@ -1020,7 +1109,7 @@ isdn_tty_shutdown(modem_info * info) info->msr &= ~UART_MSR_RI; if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); - if (info->emu.mdmreg[13] & 4) { + if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n"); @@ -1047,8 +1136,8 @@ isdn_tty_shutdown(modem_info * info) static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count) { - int c, - total = 0; + int c; + int total = 0; ulong flags; modem_info *info = (modem_info *) tty->driver_data; @@ -1074,7 +1163,7 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co #ifdef CONFIG_ISDN_AUDIO if (!info->vonline) #endif - isdn_tty_check_esc(buf, m->mdmreg[2], c, + isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c, &(m->pluscount), &(m->lastplus), from_user); @@ -1116,7 +1205,7 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co } else #endif info->xmit_count += c; - if (m->mdmreg[13] & 1) { + if (m->mdmreg[REG_DELXMT] & BIT_DELXMT) { isdn_tty_senddown(info); isdn_tty_tint(info); } @@ -1304,7 +1393,7 @@ isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value) uint arg; int pre_dtr; - GET_USER(arg, (uint *) value); + get_user(arg, (uint *) value); switch (cmd) { case TIOCMBIS: #ifdef ISDN_DEBUG_MODEM_IOCTL @@ -1327,7 +1416,7 @@ isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value) } if (arg & TIOCM_DTR) { info->mcr &= ~UART_MCR_DTR; - if (info->emu.mdmreg[13] & 4) { + if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in TIOCMBIC\n"); @@ -1348,7 +1437,7 @@ isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value) | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); if (pre_dtr |= (info->mcr & UART_MCR_DTR)) { if (!(info->mcr & UART_MCR_DTR)) { - if (info->emu.mdmreg[13] & 4) { + if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in TIOCMSET\n"); @@ -1414,7 +1503,7 @@ isdn_tty_ioctl(struct tty_struct *tty, struct file *file, error = verify_area(VERIFY_READ, (void *) arg, sizeof(long)); if (error) return error; - GET_USER(arg, (ulong *) arg); + get_user(arg, (ulong *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); @@ -1443,7 +1532,6 @@ isdn_tty_ioctl(struct tty_struct *tty, struct file *file, return error; else return isdn_tty_get_lsr_info(info, (uint *) arg); - default: #ifdef ISDN_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line); @@ -1823,10 +1911,10 @@ static void isdn_tty_modem_reset_regs(modem_info * info, int force) { atemu *m = &info->emu; - if ((m->mdmreg[12] & 32) || force) { + if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) { memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG); memcpy(m->msn, m->pmsn, ISDN_MSNLEN); - info->xmit_size = m->mdmreg[16] * 16; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16; } #ifdef CONFIG_ISDN_AUDIO isdn_tty_modem_reset_vpar(m); @@ -1881,6 +1969,7 @@ isdn_tty_modem_init(void) m->tty_modem.stop = NULL; m->tty_modem.start = NULL; m->tty_modem.hangup = isdn_tty_hangup; + m->tty_modem.driver_name = "isdn_tty"; /* * The callout device is just like normal device except for * major number and the subtype code. @@ -1905,7 +1994,7 @@ isdn_tty_modem_init(void) sprintf(info->last_num, "none"); info->last_dir = 0; info->last_lhup = 1; - info->last_l2 = 0; + info->last_l2 = -1; info->last_si = 0; isdn_tty_reset_profile(&info->emu); isdn_tty_modem_reset_regs(info, 1); @@ -1927,7 +2016,7 @@ isdn_tty_modem_init(void) #ifdef CONFIG_ISDN_AUDIO skb_queue_head_init(&info->dtmf_queue); #endif - if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) { + if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) { printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); return -3; } @@ -1977,12 +2066,12 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i, info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di), - info->emu.mdmreg[18], info->emu.mdmreg[19]); + info->emu.mdmreg[REG_SI1], info->emu.mdmreg[REG_SI2]); #endif if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di), - eaz)) && /* EAZ is matching */ - (info->emu.mdmreg[18] & si2bit[si1]) && /* SI1 is matching */ - ((info->emu.mdmreg[19] == si2) || !si2)) { /* SI2 is matching or 0 */ + eaz)) && /* EAZ is matching */ + (info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */ + (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ idx = isdn_dc2minor(di, ch); #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: match1\n"); @@ -1990,7 +2079,10 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) info->flags, info->isdn_driver, info->isdn_channel, dev->usage[idx]); #endif - if ((info->flags & ISDN_ASYNC_NORMAL_ACTIVE) && + if ( +#ifndef FIX_FILE_TRANSFER + (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) && +#endif (info->isdn_driver == -1) && (info->isdn_channel == -1) && (USG_NONE(dev->usage[idx]))) { @@ -2001,9 +2093,9 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM; strcpy(dev->num[idx], nr); - info->emu.mdmreg[20] = si2bit[si1]; - info->emu.mdmreg[21] = setup.plan; - info->emu.mdmreg[22] = setup.screen; + info->emu.mdmreg[REG_SI1I] = si2bit[si1]; + info->emu.mdmreg[REG_PLAN] = setup.plan; + info->emu.mdmreg[REG_SCREEN] = setup.screen; isdn_info_update(); restore_flags(flags); printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr, @@ -2029,12 +2121,20 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) { int mi; modem_info *info; + char *e; if (i < 0) return 0; if ((mi = dev->m_idx[i]) >= 0) { info = &dev->mdm.info[mi]; switch (c->command) { + case ISDN_STAT_CINF: + printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num); + info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10); + if (e == c->parm.num) + info->emu.charge = 0; + + break; case ISDN_STAT_BSENT: #ifdef ISDN_TTY_STAT_DEBUG printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line); @@ -2093,6 +2193,7 @@ isdn_tty_stat_callback(int i, isdn_ctrl * c) */ if (TTY_IS_ACTIVE(info)) { info->msr |= UART_MSR_DCD; + info->emu.charge = 0; if (info->dialing) { info->dialing = 0; info->last_dir = 1; @@ -2184,13 +2285,13 @@ isdn_tty_at_cout(char *msg, modem_info * info) for (p = msg; *p; p++) { switch (*p) { case '\r': - c = m->mdmreg[3]; + c = m->mdmreg[REG_CR]; break; case '\n': - c = m->mdmreg[4]; + c = m->mdmreg[REG_LF]; break; case '\b': - c = m->mdmreg[5]; + c = m->mdmreg[REG_BS]; break; default: c = *p; @@ -2293,14 +2394,14 @@ isdn_tty_modem_result(int code, modem_info * info) static char *msg[] = {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR", "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER", - "RINGING", "NO MSN/EAZ", "VCON"}; + "RINGING", "NO MSN/EAZ", "VCON", "RUNG"}; ulong flags; char s[10]; switch (code) { case 2: - m->mdmreg[1]++; /* RING */ - if (m->mdmreg[1] == m->mdmreg[0]) + m->mdmreg[REG_RINGCNT]++; /* RING */ + if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA]) /* Automatically accept incoming call */ isdn_tty_cmd_ATA(info); break; @@ -2313,7 +2414,7 @@ isdn_tty_modem_result(int code, modem_info * info) #endif save_flags(flags); cli(); - m->mdmreg[1] = 0; + m->mdmreg[REG_RINGCNT] = 0; del_timer(&info->nc_timer); info->ncarrier = 0; if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { @@ -2356,15 +2457,19 @@ isdn_tty_modem_result(int code, modem_info * info) info->online = 1; break; } - if (m->mdmreg[12] & 1) { + if (m->mdmreg[REG_RESP] & BIT_RESP) { /* Show results */ isdn_tty_at_cout("\r\n", info); - if (m->mdmreg[12] & 2) { + if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) { /* Show numeric results */ sprintf(s, "%d", code); isdn_tty_at_cout(s, info); } else { - if ((code == 2) && (!(m->mdmreg[13] & 16))) { + if ((code == 2) && + (m->mdmreg[REG_RUNG] & BIT_RUNG) && + (m->mdmreg[REG_RINGCNT] > 1)) + return; + if ((code == 2) && (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE))) { isdn_tty_at_cout("CALLER NUMBER: ", info); isdn_tty_at_cout(dev->num[info->drv_index], info); isdn_tty_at_cout("\r\n", info); @@ -2373,7 +2478,8 @@ isdn_tty_modem_result(int code, modem_info * info) switch (code) { case 2: /* Print CID only once, _after_ 1.st RING */ - if ((m->mdmreg[13] & 16) && (m->mdmreg[1] == 1)) { + if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) && + (m->mdmreg[REG_RINGCNT] == 1)) { isdn_tty_at_cout("\r\n", info); isdn_tty_at_cout("CALLER NUMBER: ", info); isdn_tty_at_cout(dev->num[info->drv_index], info); @@ -2383,18 +2489,39 @@ isdn_tty_modem_result(int code, modem_info * info) case 6: case 7: case 8: - m->mdmreg[1] = 0; + m->mdmreg[REG_RINGCNT] = 0; /* Append Cause-Message if enabled */ - if (m->mdmreg[13] & 8) { + if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) { sprintf(s, "/%s", info->last_cause); isdn_tty_at_cout(s, info); } break; case 5: /* Append Protocol to CONNECT message */ - isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info); - if (m->mdmreg[13] & 2) + switch (m->mdmreg[REG_L2PROT]) { + case ISDN_PROTO_L2_X75I: + case ISDN_PROTO_L2_X75UI: + case ISDN_PROTO_L2_X75BUI: + isdn_tty_at_cout("/X.75", info); + break; + case ISDN_PROTO_L2_HDLC: + isdn_tty_at_cout("/HDLC", info); + break; + case ISDN_PROTO_L2_V11096: + isdn_tty_at_cout("/V110/9600", info); + break; + case ISDN_PROTO_L2_V11019: + isdn_tty_at_cout("/V110/19200", info); + break; + case ISDN_PROTO_L2_V11038: + isdn_tty_at_cout("/V110/38400", info); + break; + } + if (m->mdmreg[REG_T70] & BIT_T70) { isdn_tty_at_cout("/T.70", info); + if (m->mdmreg[REG_T70] & BIT_T70_EXT) + isdn_tty_at_cout("+", info); + } break; } } @@ -2479,16 +2606,25 @@ isdn_tty_report(modem_info * info) isdn_tty_at_cout(" Layer-2 Protocol: ", info); switch (info->last_l2) { case ISDN_PROTO_L2_X75I: - isdn_tty_at_cout("x75i", info); + isdn_tty_at_cout("X.75i", info); break; case ISDN_PROTO_L2_X75UI: - isdn_tty_at_cout("x75ui", info); + isdn_tty_at_cout("X.75ui", info); break; case ISDN_PROTO_L2_X75BUI: - isdn_tty_at_cout("x75bui", info); + isdn_tty_at_cout("X.75bui", info); break; case ISDN_PROTO_L2_HDLC: - isdn_tty_at_cout("hdlc", info); + isdn_tty_at_cout("HDLC", info); + break; + case ISDN_PROTO_L2_V11096: + isdn_tty_at_cout("V.110 9600 Baud", info); + break; + case ISDN_PROTO_L2_V11019: + isdn_tty_at_cout("V.110 19200 Baud", info); + break; + case ISDN_PROTO_L2_V11038: + isdn_tty_at_cout("V.110 38400 Baud", info); break; case ISDN_PROTO_L2_TRANS: isdn_tty_at_cout("transparent", info); @@ -2497,7 +2633,12 @@ isdn_tty_report(modem_info * info) isdn_tty_at_cout("unknown", info); break; } - isdn_tty_at_cout((m->mdmreg[13] & 2) ? "/t.70\r\n" : "\r\n", info); + if (m->mdmreg[REG_T70] & BIT_T70) { + isdn_tty_at_cout("/T.70", info); + if (m->mdmreg[REG_T70] & BIT_T70_EXT) + isdn_tty_at_cout("+", info); + } + isdn_tty_at_cout("\r\n", info); isdn_tty_at_cout(" Service: ", info); switch (info->last_si) { case 1: @@ -2535,30 +2676,36 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) /* &B - Set Buffersize */ p[0]++; i = isdn_getnum(p); - if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE)) + if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX)) PARSE_ERROR1; #ifdef CONFIG_ISDN_AUDIO - if ((m->mdmreg[18] & 1) && (i > VBUF)) + if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF)) PARSE_ERROR1; #endif - m->mdmreg[16] = i / 16; - info->xmit_size = m->mdmreg[16] * 16; + m->mdmreg[REG_PSIZE] = i / 16; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16; + switch (m->mdmreg[REG_L2PROT]) { + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: + info->xmit_size /= 10; + } break; case 'D': /* &D - Set DCD-Low-behavior */ p[0]++; switch (isdn_getnum(p)) { case 0: - m->mdmreg[13] &= ~4; - m->mdmreg[12] &= ~32; + m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP; + m->mdmreg[REG_DTRR] &= ~BIT_DTRR; break; case 2: - m->mdmreg[13] |= 4; - m->mdmreg[12] &= ~32; + m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; + m->mdmreg[REG_DTRR] &= ~BIT_DTRR; break; case 3: - m->mdmreg[13] |= 4; - m->mdmreg[12] |= 32; + m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; + m->mdmreg[REG_DTRR] |= BIT_DTRR; break; default: PARSE_ERROR1 @@ -2575,12 +2722,46 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) isdn_tty_reset_profile(m); isdn_tty_modem_reset_regs(info, 1); break; + case 'R': + /* &R - Set V.110 bitrate adaption */ + p[0]++; + i = isdn_getnum(p); + switch (i) { + case 0: + /* Switch off V.110, back to X.75 */ + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + m->mdmreg[REG_SI2] = 0; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16; + break; + case 9600: + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096; + m->mdmreg[REG_SI2] = 197; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; + break; + case 19200: + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019; + m->mdmreg[REG_SI2] = 199; + info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; + break; + case 38400: + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038; + m->mdmreg[REG_SI2] = 198; /* no existing standard for this */ + info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; + break; + default: + PARSE_ERROR1; + } + /* Switch off T.70 */ + m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); + /* Set Service 7 */ + m->mdmreg[REG_SI1] |= 4; + break; case 'S': /* &S - Set Windowsize */ p[0]++; i = isdn_getnum(p); if ((i > 0) && (i < 9)) - m->mdmreg[17] = i; + m->mdmreg[REG_WSIZE] = i; else PARSE_ERROR1; break; @@ -2610,19 +2791,27 @@ isdn_tty_cmd_ATand(char **p, modem_info * info) } break; case 'X': - /* &X - Switch to BTX-Mode */ + /* &X - Switch to BTX-Mode and T.70 */ p[0]++; switch (isdn_getnum(p)) { case 0: - m->mdmreg[13] &= ~2; - info->xmit_size = m->mdmreg[16] * 16; + m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); + info->xmit_size = m->mdmreg[REG_PSIZE] * 16; break; case 1: - m->mdmreg[13] |= 2; - m->mdmreg[14] = 0; + m->mdmreg[REG_T70] |= BIT_T70; + m->mdmreg[REG_T70] &= ~BIT_T70_EXT; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + info->xmit_size = 112; + m->mdmreg[REG_SI1] = 4; + m->mdmreg[REG_SI2] = 0; + break; + case 2: + m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT); + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; info->xmit_size = 112; - m->mdmreg[18] = 4; - m->mdmreg[19] = 0; + m->mdmreg[REG_SI1] = 4; + m->mdmreg[REG_SI2] = 0; break; default: PARSE_ERROR1; @@ -2639,22 +2828,28 @@ isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m) { /* Some plausibility checks */ switch (mreg) { - case 14: - if (mval > ISDN_PROTO_L2_TRANS) + case REG_L2PROT: + if (mval > ISDN_PROTO_L2_MAX) return 1; break; - case 16: - if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE) + case REG_PSIZE: + if ((mval * 16) > ISDN_SERIAL_XMIT_MAX) return 1; #ifdef CONFIG_ISDN_AUDIO - if ((m->mdmreg[18] & 1) && (mval > VBUFX)) + if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX)) return 1; #endif info->xmit_size = mval * 16; + switch (m->mdmreg[REG_L2PROT]) { + case ISDN_PROTO_L2_V11096: + case ISDN_PROTO_L2_V11019: + case ISDN_PROTO_L2_V11038: + info->xmit_size /= 10; + } break; - case 20: - case 21: - case 22: + case REG_SI1I: + case REG_PLAN: + case REG_SCREEN: /* readonly registers */ return 1; } @@ -2741,31 +2936,31 @@ isdn_tty_cmd_ATA(modem_info * info) /* Accept incoming call */ info->last_dir = 0; strcpy(info->last_num, dev->num[info->drv_index]); - m->mdmreg[1] = 0; + m->mdmreg[REG_RINGCNT] = 0; info->msr &= ~UART_MSR_RI; - l2 = m->mdmreg[14]; + l2 = m->mdmreg[REG_L2PROT]; #ifdef CONFIG_ISDN_AUDIO /* If more than one bit set in reg18, autoselect Layer2 */ - if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18]) { - if (m->mdmreg[20] == 1) - l2 = 4; + if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) { + if (m->mdmreg[REG_SI1I] == 1) + l2 = ISDN_PROTO_L2_TRANS; else - l2 = 0; + l2 = ISDN_PROTO_L2_X75I; } #endif cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL2; cmd.arg = info->isdn_channel + (l2 << 8); info->last_l2 = l2; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; - cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; cmd.command = ISDN_CMD_ACCEPTD; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); } else isdn_tty_modem_result(8, info); } @@ -2787,7 +2982,7 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) case '?': p[0]++; sprintf(rs, "\r\n%d", - (m->mdmreg[18] & 1) ? 8 : 0); + (m->mdmreg[REG_SI1] & 1) ? 8 : 0); isdn_tty_at_cout(rs, info); break; case '=': @@ -2795,18 +2990,22 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) switch (*p[0]) { case '0': p[0]++; - m->mdmreg[18] = 4; + m->mdmreg[REG_SI1] = 4; info->xmit_size = - m->mdmreg[16] * 16; + m->mdmreg[REG_PSIZE] * 16; + break; + case '2': + printk(KERN_DEBUG "isdn_tty: FCLASS=2\n"); + p[0]++; break; case '8': p[0]++; - m->mdmreg[18] = 5; + m->mdmreg[REG_SI1] = 5; info->xmit_size = VBUF; break; case '?': p[0]++; - isdn_tty_at_cout("\r\n0,8", + isdn_tty_at_cout("\r\n0,2,8", info); break; default: @@ -2824,7 +3023,7 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) case '?': p[0]++; sprintf(rs, "\r\n%d", - m->mdmreg[0]); + m->mdmreg[REG_RINGATA]); isdn_tty_at_cout(rs, info); break; case '=': @@ -2832,13 +3031,100 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) par = isdn_getnum(p); if ((par < 0) || (par > 255)) PARSE_ERROR1; - m->mdmreg[0] = par; + m->mdmreg[REG_RINGATA] = par; break; default: PARSE_ERROR1; } return 0; } + if (!strncmp(p[0], "TBC=", 4)) { /* UNKLAR !! */ + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); + switch (*p[0]) { + case '0': + p[0]++; + break; + default: + PARSE_ERROR1; + } + return 0; + } + if (!strncmp(p[0], "BOR=", 4)) { /* UNKLAR !! */ + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: Fax FBOR=%c\n", *p[0]); + switch (*p[0]) { + case '0': + p[0]++; + break; + default: + PARSE_ERROR1; + } + return 0; + } + if (!strncmp(p[0], "DCC=", 4)) { /* SETUP irgendwie !! */ + int i, val[]={0,0,0,0,0,0,0,0}; + + p[0] += 4; + if (*p[0] == '?') { + isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0,1),(0),(0),(0-7)",info); + p[0]++; + } else { + for (i=0; (*p[0]>='0') && (*p[0]<='9'); i++) { + val[i] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } + printk(KERN_DEBUG "isdn_tty: Fax Setup values=%d,%d,%d,%d,%d,%d,%d,%d\n", + val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); + } + return 0; + } + if (!strncmp(p[0], "LID=", 4)) { /* set sender ID !! */ + char senderID[80]; + int i; + + p[0] += 4; + if (*p[0] =='"') + p[0]++; + for(i=0; (*p[0]) && (*p[0] != '"'); i++) + senderID[i] = *p[0]++; + senderID[i] = 0; + if (*p[0] =='"') + p[0]++; + printk(KERN_DEBUG "isdn_tty: Fax sender=>%s<\n", senderID); + return 0; + } + if (!strncmp(p[0], "MFR?", 4)) { + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: FMFR?\n"); + isdn_tty_at_cout("\r\nISDNfax", info); + return 0; + } + if (!strncmp(p[0], "MDL?", 4)) { + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: FMDL?\n"); + isdn_tty_at_cout("\r\nAVM-B1", info); + return 0; + } + if (!strncmp(p[0], "AP=?", 4)) { + p[0] += 4; + printk(KERN_DEBUG "isdn_tty: FAP=?\n"); + return 0; + } + if (!strncmp(p[0], "PHCTO=", 6)) { + /* beim trace mit dem zyxel folgt der wert 30 ;*/ + p[0] += 6; + printk(KERN_DEBUG "isdn_tty: FPHCTO=%s\n", p[0]); + return 0; + } + if (!strncmp(p[0], "CR=", 3)) { + p[0] += 3; + printk(KERN_DEBUG "isdn_tty: FCR=%s\n", p[0]); + return 0; + } + printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); PARSE_ERROR1; } @@ -3110,10 +3396,10 @@ isdn_tty_parse_at(modem_info * info) p++; switch (isdn_getnum(&p)) { case 0: - m->mdmreg[12] &= ~4; + m->mdmreg[REG_ECHO] &= ~BIT_ECHO; break; case 1: - m->mdmreg[12] |= 4; + m->mdmreg[REG_ECHO] |= BIT_ECHO; break; default: PARSE_ERROR; @@ -3149,6 +3435,11 @@ isdn_tty_parse_at(modem_info * info) p++; isdn_tty_report(info); break; + case '3': + p++; + sprintf(ds, "\r\n%d", info->emu.charge); + isdn_tty_at_cout(ds, info); + break; default: } break; @@ -3166,10 +3457,10 @@ isdn_tty_parse_at(modem_info * info) p++; switch (isdn_getnum(&p)) { case 0: - m->mdmreg[12] |= 1; + m->mdmreg[REG_RESP] |= BIT_RESP; break; case 1: - m->mdmreg[12] &= ~1; + m->mdmreg[REG_RESP] &= ~BIT_RESP; break; default: PARSE_ERROR; @@ -3186,10 +3477,10 @@ isdn_tty_parse_at(modem_info * info) p++; switch (isdn_getnum(&p)) { case 0: - m->mdmreg[12] |= 2; + m->mdmreg[REG_RESP] |= BIT_RESPNUM; break; case 1: - m->mdmreg[12] &= ~2; + m->mdmreg[REG_RESP] &= ~BIT_RESPNUM; break; default: PARSE_ERROR; @@ -3210,7 +3501,7 @@ isdn_tty_parse_at(modem_info * info) return; break; case 'V': - if (!(m->mdmreg[18] & 1)) + if (!(m->mdmreg[REG_SI1] & 1)) PARSE_ERROR; p++; if (isdn_tty_cmd_PLUSV(&p, info)) @@ -3261,14 +3552,14 @@ isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) for (cnt = count; cnt > 0; p++, cnt--) { if (user) - GET_USER(c, p); + get_user(c, p); else c = *p; total++; - if (c == m->mdmreg[3] || c == m->mdmreg[4]) { + if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) { /* Separator (CR oder LF) */ m->mdmcmd[m->mdmcmdl] = 0; - if (m->mdmreg[12] & 4) { + if (m->mdmreg[REG_ECHO] & BIT_ECHO) { eb[0] = c; eb[1] = 0; isdn_tty_at_cout(eb, info); @@ -3278,18 +3569,18 @@ isdn_tty_edit_at(const char *p, int count, modem_info * info, int user) m->mdmcmdl = 0; continue; } - if (c == m->mdmreg[5] && m->mdmreg[5] < 128) { + if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) { /* Backspace-Funktion */ if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) { if (m->mdmcmdl) m->mdmcmdl--; - if (m->mdmreg[12] & 4) + if (m->mdmreg[REG_ECHO] & BIT_ECHO) isdn_tty_at_cout("\b", info); } continue; } if (cmdchar(c)) { - if (m->mdmreg[12] & 4) { + if (m->mdmreg[REG_ECHO] & BIT_ECHO) { eb[0] = c; eb[1] = 0; isdn_tty_at_cout(eb, info); diff --git a/drivers/isdn/isdn_v110.c b/drivers/isdn/isdn_v110.c new file mode 100644 index 000000000..ae62378b8 --- /dev/null +++ b/drivers/isdn/isdn_v110.c @@ -0,0 +1,645 @@ +/* $Id: isdn_v110.c,v 1.2 1998/02/22 19:44:25 fritz Exp $ + + * Linux ISDN subsystem, V.110 related functions (linklevel). + * + * Copyright by Thomas Pfeiffer (pfeiffer@pds.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_v110.c,v $ + * Revision 1.2 1998/02/22 19:44:25 fritz + * Bugfixes and improvements regarding V.110, V.110 now running. + * + * Revision 1.1 1998/02/20 17:32:09 fritz + * First checkin (not yet completely functionable). + * + */ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/mm.h> + +#include <linux/isdn.h> +#include "isdn_v110.h" + +#undef ISDN_V110_DEBUG + +char *isdn_v110_revision = "$Revision: 1.2 $"; + +#define V110_38400 255 +#define V110_19200 15 +#define V110_9600 3 + +/* Die folgenden Daten sind fertig kodierte Matrizen, jeweils + als online und offline matrix für 9600, 19200 und 38400 + */ +static unsigned char V110_OnMatrix_9600[] = +{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, + 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, + 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, + 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd}; + +static unsigned char V110_OffMatrix_9600[] = +{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +static unsigned char V110_OnMatrix_19200[] = +{0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, + 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7}; + +static unsigned char V110_OffMatrix_19200[] = +{0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +static unsigned char V110_OnMatrix_38400[] = +{0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f}; + +static unsigned char V110_OffMatrix_38400[] = +{0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff}; + + +/* FlipBits dreht die Reihenfolge von jeweils keylen bits in einem byte um. + Aus der Bitreihenfolge 76543210 werden bei keylen=4 die bits 45670123, + bei keylen=2 die bits 67452301. Dies ist notwendig, weil die reihenfolge + auf der isdn-leitung falsch herum ist. + */ + +static __inline unsigned char +FlipBits(unsigned char c, int keylen) +{ + unsigned char b = c; + unsigned char bit = 128; + int i; + int j; + int hunks = (8 / keylen); + + c = 0; + for (i = 0; i < hunks; i++) { + for (j = 0; j < keylen; j++) { + if (b & (bit >> j)) + c |= bit >> (keylen - j - 1); + } + bit >>= keylen; + } + return c; +} + + +/* isdn_v110_open allocates and initializes private V.110 data + * structures and returns a pointer to these. + */ +static isdn_v110_stream * +isdn_v110_open(unsigned char key, int hdrlen, int maxsize) +{ + int i; + isdn_v110_stream *v; + + if ((v = kmalloc(sizeof(isdn_v110_stream), GFP_KERNEL)) == NULL) + return NULL; + memset(v, 0, sizeof(isdn_v110_stream)); + v->key = key; + v->nbits = 0; + for (i = 0; key & (1 << i); i++) + v->nbits++; + + v->nbytes = 8 / v->nbits; + v->decodelen = 0; + + switch (key) { + case V110_38400: + v->OnlineFrame = V110_OnMatrix_38400; + v->OfflineFrame = V110_OffMatrix_38400; + break; + case V110_19200: + v->OnlineFrame = V110_OnMatrix_19200; + v->OfflineFrame = V110_OffMatrix_19200; + break; + default: + v->OnlineFrame = V110_OnMatrix_9600; + v->OfflineFrame = V110_OffMatrix_9600; + break; + } + v->framelen = v->nbytes * 10; + v->SyncInit = 5; + v->introducer = 0; + v->dbit = 1; + v->b = 0; + v->skbres = hdrlen; + v->maxsize = maxsize - hdrlen; + if ((v->encodebuf = kmalloc(maxsize, GFP_KERNEL)) == NULL) { + kfree(v); + return NULL; + } + return v; +} + +/* isdn_v110_close frees private V.110 data structures */ +static void +isdn_v110_close(isdn_v110_stream * v) +{ + if (v == NULL) + return; +#ifdef ISDN_V110_DEBUG + printk(KERN_DEBUG "v110 close\n"); +#if 0 + printk(KERN_DEBUG "isdn_v110_close: nbytes=%d\n", v->nbytes); + printk(KERN_DEBUG "isdn_v110_close: nbits=%d\n", v->nbits); + printk(KERN_DEBUG "isdn_v110_close: key=%d\n", v->key); + printk(KERN_DEBUG "isdn_v110_close: SyncInit=%d\n", v->SyncInit); + printk(KERN_DEBUG "isdn_v110:close: decodelen=%d\n", v->decodelen); + printk(KERN_DEBUG "isdn_v110_close: framelen=%d\n", v->framelen); +#endif +#endif + kfree(v->encodebuf); + kfree(v); +} + + +/* ValidHeaderBytes prüft, wieviele bytes in v->decodebuf gültig sind */ + +static int +ValidHeaderBytes(isdn_v110_stream * v) +{ + int i; + for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++) + if ((v->decodebuf[i] & v->key) != 0) + break; + return i; +} + +/* SyncHeader schiebt den decodebuf pointer auf den nächsten gültigen header */ + +static void +SyncHeader(isdn_v110_stream * v) +{ + unsigned char *rbuf = v->decodebuf; + int len = v->decodelen; + + if (len == 0) + return; + for (rbuf++, len--; len > 0; len--, rbuf++) /* such den SyncHeader in buf ! */ + if ((*rbuf & v->key) == 0) /* erstes byte gefunden ? */ + break; /* jupp! */ + if (len) + memcpy(v->decodebuf, rbuf, len); + + v->decodelen = len; +#ifdef ISDN_V110_DEBUG + printk(KERN_DEBUG "isdn_v110: Header resync\n"); +#endif +} + +/* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where + len is the number of matrix-lines. len must be a multiple of 10, i.e. + only complete matices must be given. + From these, netto data is extracted and returned in buf. The return-value + is the bytecount of the decoded data. + */ +static int +DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf) +{ + int line = 0; + int buflen = 0; + int mbit = 64; + int introducer = v->introducer; + int dbit = v->dbit; + unsigned char b = v->b; + + while (line < len) { /* sind schon alle matrizenzeilen abgearbeitet? */ + if ((line % 10) == 0) { /* die 0. zeile der matrix ist immer null ! */ + if (m[line] != 0x00) { /* nicht 0 ? dann fehler! */ +#ifdef ISDN_V110_DEBUG + printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n"); +#endif + +/* + dann einen return zu machen, ist auch irgendwie nicht das richtige! :-( + v->introducer = 0; v->dbit = 1; v->b = 0; + return buflen; anzahl schon erzeugter daten zurückgeben! + */ + } + line++; /* sonst die nächste matrixzeile nehmen */ + continue; + } else if ((line % 10) == 5) { /* in zeile 5 stehen nur e-bits ! */ + if ((m[line] & 0x70) != 0x30) { /* 011 muß am anfang stehen! */ +#ifdef ISDN_V110_DEBUG + printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n"); +#endif +/* dann einen return zu machen, ist auch irgendwie nicht das richtige! :-( + v->introducer = 0; v->dbit = 1; v->b = 0; + return buflen; + */ + } + line++; /* alles klar, nächste zeile */ + continue; + } else if (!introducer) { /* every byte starts with 10 (stopbit, startbit) */ + introducer = (m[line] & mbit) ? 0 : 1; /* aktuelles bit der matrix */ + next_byte: + if (mbit > 2) { /* war es das letzte bit dieser matrixzeile ? */ + mbit >>= 1; /* nein, nimm das nächste in dieser zeile */ + continue; + } /* sonst links in der nächsten zeile anfangen */ + mbit = 64; + line++; + continue; + } else { /* sonst müssen wir ein datenbit setzen */ + if (m[line] & mbit) /* war das bit in der matrix gesetzt ? */ + b |= dbit; /* ja, dann setz es auch im datenbyte */ + else + b &= dbit - 1; /* nein, lösch bit im datenbyte */ + if (dbit < 128) /* haben wir schon ein ganzes byte voll ? */ + dbit <<= 1; /* nein, auf zum nächsten datenbit */ + else { /* ein ganzes datenbyte ist voll */ + buf[buflen++] = b; /* byte in den output buffer kopieren */ + introducer = b = 0; /* Init der Introsequenz und des datenbytes */ + dbit = 1; /* als nächstes suchen wir das nullte bit */ + } + goto next_byte; /* suche das nächste bit in der matrix */ + } + } + v->introducer = introducer; + v->dbit = dbit; + v->b = b; + return buflen; /* return anzahl der bytes im output buffer */ +} + +/* DecodeStream erhält vom input stream V110 kodierte Daten, die zu den + V110 frames zusammengepackt werden müssen. Die Daten können an diese + Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne + darauf achten zu müssen, das frames usw. eingehalten werden. + */ +struct sk_buff * +isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb) +{ + int i; + int j; + int len; + unsigned char *v110_buf; + unsigned char *rbuf; + + if (!skb) { + printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n"); + return NULL; + } + rbuf = skb->data; + len = skb->len; + if (v == NULL) { + /* invalid handle, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n"); + dev_kfree_skb(skb); + return NULL; + } + if (v->decodelen == 0) /* cache empty? */ + for (; len > 0; len--, rbuf++) /* scan for SyncHeader in buf */ + if ((*rbuf & v->key) == 0) + break; /* found first byte */ + if (len == 0) { + dev_kfree_skb(skb); + return NULL; + } + /* copy new data to decode-buffer */ + memcpy(&(v->decodebuf[v->decodelen]), rbuf, len); + v->decodelen += len; + ReSync: + if (v->decodelen < v->nbytes) { /* got a new header ? */ + dev_kfree_skb(skb); + return NULL; /* no, try later */ + } + if (ValidHeaderBytes(v) != v->nbytes) { /* ist es ein ungültiger header ? */ + SyncHeader(v); /* nein, such einen header */ + goto ReSync; + } + len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes; + if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) { + printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n"); + dev_kfree_skb(skb); + return NULL; + } + for (i = 0; i < len; i++) { + v110_buf[i] = 0; + for (j = 0; j < v->nbytes; j++) + v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits)); + v110_buf[i] = FlipBits(v110_buf[i], v->nbits); + } + v->decodelen = (v->decodelen % (10 * v->nbytes)); + memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen); + + skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data)); + kfree(v110_buf); + if (skb->len) + return skb; + else { + kfree_skb(skb); + return NULL; + } +} + +/* EncodeMatrix takes input data in buf, len is the bytecount. + Data is encoded into v110 frames in m. Return value is the number of + matrix-lines generated. + */ +static int +EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen) +{ + int line = 0; + int i = 0; + int mbit = 128; + int dbit = 1; + int introducer = 3; + int ibit[] = {0, 1, 1}; + + while ((i < len) && (line < mlen)) { /* solange noch input da ist */ + switch (line % 10) { /* in welcher matrixzeile sind wir ? */ + case 0: + m[line++] = 0x00; /* zeile 0 ist immer 0 */ + mbit = 128; /* und es geht mit dem 7. bit weiter */ + break; + case 5: + m[line++] = 0xbf; /* zeile 5 ist immer 10111111 */ + mbit = 128; /* und es geht mit dem 7. bit weiter */ + break; + } + if (line >= mlen) { + printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n"); + return line; + } + next_bit: + switch (mbit) { /* ganz linkes oder rechtes bit ? */ + case 1: + line++; /* ganz rechts ! dann in die nächste */ + if (line >= mlen) { + printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n"); + return line; + } + case 128: + m[line] = 128; /* ganz links byte auf 1000000 setzen */ + mbit = 64; /* aktuelles bit in der matrixzeile */ + continue; + } + if (introducer) { /* 110 sequenz setzen ? */ + introducer--; /* ein digit weniger setzen */ + m[line] |= ibit[introducer] ? mbit : 0; /* entsprechendes bit setzen */ + mbit >>= 1; /* bit der matrixzeile >> 1 */ + goto next_bit; /* und dort weiter machen */ + } /* else datenbits in die matrix packen! */ + m[line] |= (buf[i] & dbit) ? mbit : 0; /* datenbit in matrix setzen */ + if (dbit == 128) { /* war es das letzte datenbit ? */ + dbit = 1; /* dann mach beim nächsten weiter */ + i++; /* nächste datenbyte des input buffers */ + if (i < len) /* war es schon das letzte ? */ + introducer = 3; /* nein, schreib den introducer 110 */ + else { /* war das letzte datenbyte ! */ + m[line] |= (mbit - 1) & 0xfe; /* setz restliche bits der zeile auf 1 */ + break; + } + } else /* nicht das letzte datenbit */ + dbit <<= 1; /* dann gehe zum nächsten datenbit */ + mbit >>= 1; /* und setz bit der matrix weiter */ + goto next_bit; + + } + /* evtl. noch restliche zeilen in der matrix generieren... */ + if ((line) && ((line + 10) < mlen)) + switch (++line % 10) { + case 1: + m[line++] = 0xfe; + case 2: + m[line++] = 0xfe; + case 3: + m[line++] = 0xfe; + case 4: + m[line++] = 0xfe; + case 5: + m[line++] = 0xbf; + case 6: + m[line++] = 0xfe; + case 7: + m[line++] = 0xfe; + case 8: + m[line++] = 0xfe; + case 9: + m[line++] = 0xfe; + } + return line; /* soviele matrixzeilen sind es */ +} + +/* + * Build a sync frame. + */ +static struct sk_buff * +isdn_v110_sync(isdn_v110_stream *v) +{ + struct sk_buff *skb; + + if (v == NULL) { + /* invalid handle, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n"); + return NULL; + } + if ((skb = dev_alloc_skb(v->framelen + v->skbres))) { + skb_reserve(skb, v->skbres); + memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen); + } + return skb; +} + +/* + * Build an idle frame. + */ +static struct sk_buff * +isdn_v110_idle(isdn_v110_stream *v) +{ + struct sk_buff *skb; + + if (v == NULL) { + /* invalid handle, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n"); + return NULL; + } + if ((skb = dev_alloc_skb(v->framelen + v->skbres))) { + skb_reserve(skb, v->skbres); + memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen); + } + return skb; +} + +struct sk_buff * +isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb) +{ + int i; + int j; + int rlen; + int mlen; + int olen; + int size; + int sval1; + int sval2; + int nframes; + unsigned char *v110buf; + unsigned char *rbuf; + struct sk_buff *nskb; + + if (v == NULL) { + /* invalid handle, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n"); + return NULL; + } + if (!skb) { + /* invalid skb, no chance to proceed */ + printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n"); + return NULL; + } + rlen = skb->len; + nframes = (rlen + 3) / 4; + v110buf = v->encodebuf; + if ((nframes * 40) > v->maxsize) { + size = v->maxsize; + rlen = v->maxsize / 40; + } else + size = nframes * 40; + if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) { + printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n"); + return NULL; + } + skb_reserve(nskb, v->skbres + sizeof(int)); + if (skb->len == 0) { + memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen); + *((int *)skb_push(nskb, sizeof(int))) = 0; + return nskb; + } + mlen = EncodeMatrix(skb->data, rlen, v110buf, size); + /* jetzt noch jeweils 2 oder 4 bits auf den output stream verteilen! */ + rbuf = skb_put(nskb, size); + olen = 0; + sval1 = 8 - v->nbits; + sval2 = v->key << sval1; + for (i = 0; i < mlen; i++) { + v110buf[i] = FlipBits(v110buf[i], v->nbits); + for (j = 0; j < v->nbytes; j++) { + if (size--) + *rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1); + else { + printk(KERN_WARNING "isdn_v110_encode: buffers full!\n"); + goto buffer_full; + } + olen++; + } + } +buffer_full: + skb_trim(nskb, olen); + *((int *)skb_push(nskb, sizeof(int))) = rlen; + return nskb; +} + +int +isdn_v110_stat_callback(int idx, isdn_ctrl * c) +{ + isdn_v110_stream *v = NULL; + int i; + int ret; + + if (idx < 0) + return 0; + switch (c->command) { + case ISDN_STAT_BSENT: + /* Keep the send-queue of the driver filled + * with frames: + * If number of outstanding frames < 3, + * send down an Idle-Frame (or an Sync-Frame, if + * v->SyncInit != 0). + */ + if (!(v = dev->v110[idx])) + return 0; + atomic_inc(&dev->v110use[idx]); + if (v->skbidle > 0) { + v->skbidle--; + ret = 1; + } else { + if (v->skbuser > 0) + v->skbuser--; + ret = 0; + } + for (i = v->skbuser + v->skbidle; i < 2; i++) { + struct sk_buff *skb; + if (v->SyncInit > 0) + skb = isdn_v110_sync(v); + else + skb = isdn_v110_idle(v); + if (skb) { + if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) { + dev_kfree_skb(skb); + break; + } else { + if (v->SyncInit) + v->SyncInit--; + v->skbidle++; + } + } else + break; + } + atomic_dec(&dev->v110use[idx]); + return ret; + case ISDN_STAT_DHUP: + case ISDN_STAT_BHUP: + while (1) { + atomic_inc(&dev->v110use[idx]); + if (atomic_dec_and_test(&dev->v110use[idx])) { + isdn_v110_close(dev->v110[idx]); + dev->v110[idx] = NULL; + break; + } + sti(); + } + break; + case ISDN_STAT_BCONN: + if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) { + int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen; + int maxsize = dev->drv[c->driver]->interface->maxbufsize; + atomic_inc(&dev->v110use[idx]); + switch (dev->v110emu[idx]) { + case ISDN_PROTO_L2_V11096: + dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize); + break; + case ISDN_PROTO_L2_V11019: + dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize); + break; + case ISDN_PROTO_L2_V11038: + dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize); + break; + default: + } + if ((v = dev->v110[idx])) { + while (v->SyncInit) { + struct sk_buff *skb = isdn_v110_sync(v); + if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) { + dev_kfree_skb(skb); + /* Unable to send, try later */ + break; + } + v->SyncInit--; + v->skbidle++; + } + } else + printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx); + atomic_dec(&dev->v110use[idx]); + } + break; + default: + return 0; + } + return 0; +} diff --git a/drivers/isdn/isdn_v110.h b/drivers/isdn/isdn_v110.h new file mode 100644 index 000000000..9ab5a93f3 --- /dev/null +++ b/drivers/isdn/isdn_v110.h @@ -0,0 +1,45 @@ +/* $Id: isdn_v110.h,v 1.1 1998/02/20 17:32:11 fritz Exp $ + + * Linux ISDN subsystem, V.110 related functions (linklevel). + * + * Copyright by Thomas Pfeiffer (pfeiffer@pds.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: isdn_v110.h,v $ + * Revision 1.1 1998/02/20 17:32:11 fritz + * First checkin (not yet completely functionable). + * + */ +#ifndef _isdn_v110_h_ +#define _isdn_v110_h_ + +/* isdn_v110_encode erhält len Nettodaten in buf, kodiert diese und liefert + das Ergebnis wieder in buf. Wieviele Bytes kodiert wurden wird als + return zurück gegeben. Diese Daten können dann 1:1 auf die Leitung + gegeben werden. +*/ +extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *); + +/* isdn_v110_decode erhält vom input stream V110 kodierte Daten, die zu den + V110 frames zusammengepackt werden müssen. Die Daten können an diese + Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne + darauf achten zu müssen, das frames usw. eingehalten werden. + */ +extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *); + +extern int isdn_v110_stat_callback(int, isdn_ctrl *); + +#endif diff --git a/drivers/isdn/isdn_x25iface.c b/drivers/isdn/isdn_x25iface.c new file mode 100644 index 000000000..17d291cdf --- /dev/null +++ b/drivers/isdn/isdn_x25iface.c @@ -0,0 +1,340 @@ +/* $Id: isdn_x25iface.c,v 1.3 1998/02/20 17:25:20 fritz Exp $ + * stuff needed to support the Linux X.25 PLP code on top of devices that + * can provide a lab_b service using the concap_proto mechanism. + * This module supports a network interface wich provides lapb_sematics + * -- as defined in ../../Documentation/networking/x25-iface.txt -- to + * the upper layer and assumes that the lower layer provides a reliable + * data link service by means of the the concap_device_ops callbacks. + * + * Only protocol specific stuff goes here. Device specific stuff + * goes to another -- device related -- concap_proto support source file. + * + * $Log: isdn_x25iface.c,v $ + * Revision 1.3 1998/02/20 17:25:20 fritz + * Changes for recent kernels. + * + * Revision 1.2 1998/01/31 22:49:22 keil + * correct comments + * + * Revision 1.1 1998/01/31 22:27:58 keil + * New files from Henner Eisen for X.25 support + * + */ + +/* #include <linux/isdn.h> */ +#include <linux/netdevice.h> +#include <linux/concap.h> +#include <linux/wanrouter.h> +#include "isdn_x25iface.h" + +/* for debugging messages not to cause an oops when device pointer is NULL*/ +#define MY_DEVNAME(dev) ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" ) + + +typedef struct isdn_x25iface_proto_data { + int magic; + enum wan_states state; + /* Private stuff, not to be accessed via proto_data. We provide the + other storage for the concap_proto instance here as well, + enabling us to allocate both with just one kmalloc(): */ + struct concap_proto priv; +} ix25_pdata_t; + + + +/* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */ +void isdn_x25iface_proto_del( struct concap_proto * ); +int isdn_x25iface_proto_close( struct concap_proto * ); +int isdn_x25iface_proto_restart( struct concap_proto *, + struct device *, + struct concap_device_ops *); +int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * ); +int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * ); +int isdn_x25iface_connect_ind( struct concap_proto * ); +int isdn_x25iface_disconn_ind( struct concap_proto * ); + + +static struct concap_proto_ops ix25_pops = { + &isdn_x25iface_proto_new, + &isdn_x25iface_proto_del, + &isdn_x25iface_proto_restart, + &isdn_x25iface_proto_close, + &isdn_x25iface_xmit, + &isdn_x25iface_receive, + &isdn_x25iface_connect_ind, + &isdn_x25iface_disconn_ind +}; + +/* error message helper fuction */ +static void illegal_state_warn( unsigned state, unsigned char firstbyte) +{ + printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in" + "current state %d\n",firstbyte, state ); +} + +/* check protocol data field for consistency */ +static int pdata_is_bad( ix25_pdata_t * pda ){ + + if( pda && pda -> magic == ISDN_X25IFACE_MAGIC ) return 0; + printk( KERN_WARNING + "isdn_x25iface_xxx: illegal pointer to proto data\n" ); + return 1; +} + +/* create a new x25 interface protocol instance + */ +struct concap_proto * isdn_x25iface_proto_new() +{ + ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL); + IX25DEBUG("isdn_x25iface_proto_new\n"); + if( tmp ){ + tmp -> magic = ISDN_X25IFACE_MAGIC; + tmp -> state = WAN_UNCONFIGURED; + /* private data space used to hold the concap_proto data. + Only to be accessed via the returned pointer */ + tmp -> priv.dops = NULL; + tmp -> priv.net_dev = NULL; + tmp -> priv.pops = &ix25_pops; + tmp -> priv.flags = 0; + tmp -> priv.proto_data = tmp; + return( &(tmp -> priv) ); + } + return NULL; +}; + +/* close the x25iface encapsulation protocol + */ +int isdn_x25iface_proto_close(struct concap_proto *cprot){ + + ix25_pdata_t *tmp; + int ret = 0; + ulong flags; + + if( ! cprot ){ + printk( KERN_ERR "isdn_x25iface_proto_close: " + "invalid concap_proto pointer\n" ); + return -1; + } + IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) ); + save_flags(flags); + cli(); /* avoid races with incoming events calling pops methods while + cprot members are inconsistent */ + cprot -> dops = NULL; + cprot -> net_dev = NULL; + tmp = cprot -> proto_data; + if( pdata_is_bad( tmp ) ){ + ret = -1; + } else { + tmp -> state = WAN_UNCONFIGURED; + } + restore_flags(flags); + + return ret; +} + +/* Delete the x25iface encapsulation protocol instance + */ +void isdn_x25iface_proto_del(struct concap_proto *cprot){ + + ix25_pdata_t * tmp; + + IX25DEBUG( "isdn_x25iface_proto_del \n" ); + if( ! cprot ){ + printk( KERN_ERR "isdn_x25iface_proto_del: " + "concap_proto pointer is NULL\n" ); + return; + } + tmp = cprot -> proto_data; + if( tmp == NULL ){ + printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent " + "proto_data pointer (maybe already deleted?)\n"); + return; + } + /* close if the protocol is still open */ + if( cprot -> dops ) isdn_x25iface_proto_close(cprot); + /* freeing the storage should be sufficient now. But some additional + settings might help to catch wild pointer bugs */ + tmp -> magic = 0; + cprot -> proto_data = NULL; + + kfree( tmp ); + return; +} + +/* (re-)initialize the data structures for x25iface encapsulation + */ +int isdn_x25iface_proto_restart(struct concap_proto *cprot, + struct device *ndev, + struct concap_device_ops *dops) +{ + ix25_pdata_t * pda = cprot -> proto_data ; + ulong flags; + + IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) ); + + if ( pdata_is_bad( pda ) ) return -1; + + if( !( dops && dops -> data_req && dops -> connect_req + && dops -> disconn_req ) ){ + printk( KERN_WARNING "isdn_x25iface_restart: required dops" + " missing\n" ); + isdn_x25iface_proto_close(cprot); + return -1; + } + save_flags(flags); + cli(); /* avoid races with incoming events calling pops methods while + cprot members are inconsistent */ + cprot -> net_dev = ndev; + cprot -> pops = &ix25_pops; + cprot -> dops = dops; + pda -> state = WAN_DISCONNECTED; + restore_flags(flags); + return 0; +} + +/* deliver a dl_data frame received from i4l HL driver to the network layer + */ +int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb) +{ + IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) ); + if ( ( (ix25_pdata_t*) (cprot->proto_data) ) + -> state == WAN_CONNECTED ){ + skb -> dev = cprot -> net_dev; + skb -> protocol = htons(ETH_P_X25); + skb -> pkt_type = PACKET_HOST; + if( skb_push(skb, 1)){ + skb -> data[0]=0x00; + skb -> mac.raw = skb -> data; + netif_rx(skb); + return 0; + } + } + printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) ); + dev_kfree_skb(skb); + return -1; +} + +/* a connection set up is indicated by lower layer + */ +int isdn_x25iface_connect_ind(struct concap_proto *cprot) +{ + struct sk_buff * skb = dev_alloc_skb(1); + enum wan_states *state_p + = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state); + IX25DEBUG( "isdn_x25iface_connect_ind %s \n" + , MY_DEVNAME(cprot->net_dev) ); + if( *state_p == WAN_UNCONFIGURED ){ + printk(KERN_WARNING + "isdn_x25iface_connect_ind while unconfigured %s\n" + , MY_DEVNAME(cprot->net_dev) ); + return -1; + } + *state_p = WAN_CONNECTED; + if( skb ){ + *( skb_put(skb, 1) ) = 0x01; + skb -> mac.raw = skb -> data; + skb -> dev = cprot -> net_dev; + skb -> protocol = htons(ETH_P_X25); + skb -> pkt_type = PACKET_HOST; + netif_rx(skb); + return 0; + } else { + printk(KERN_WARNING "isdn_x25iface_connect_ind: " + " out of memory -- disconnecting\n"); + cprot -> dops -> disconn_req(cprot); + return -1; + } +} + +/* a disconnect is indicated by lower layer + */ +int isdn_x25iface_disconn_ind(struct concap_proto *cprot) +{ + struct sk_buff *skb; + enum wan_states *state_p + = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state); + IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) ); + if( *state_p == WAN_UNCONFIGURED ){ + printk(KERN_WARNING + "isdn_x25iface_disconn_ind while unconfigured\n"); + return -1; + } + if(! cprot -> net_dev) return -1; + *state_p = WAN_DISCONNECTED; + skb = dev_alloc_skb(1); + if( skb ){ + *( skb_put(skb, 1) ) = 0x02; + skb -> mac.raw = skb -> data; + skb -> dev = cprot -> net_dev; + skb -> protocol = htons(ETH_P_X25); + skb -> pkt_type = PACKET_HOST; + netif_rx(skb); + return 0; + } else { + printk(KERN_WARNING "isdn_x25iface_disconn_ind:" + " out of memory\n"); + return -1; + } +} + +/* process a frame handed over to us from linux network layer. First byte + semantics as defined in ../../Documentation/networking/x25-iface.txt + */ +int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb) +{ + unsigned char firstbyte = skb->data[0]; + unsigned *state = + &( ( (ix25_pdata_t*) (cprot -> proto_data) ) -> state ); + int ret = 0; + IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state ); + switch ( firstbyte ){ + case 0x00: /* dl_data request */ + if( *state == WAN_CONNECTED ){ + skb_pull(skb, 1); + cprot -> net_dev -> trans_start = jiffies; + ret = ( cprot -> dops -> data_req(cprot, skb) ); + /* prepare for future retransmissions */ + if( ret ) skb_push(skb,1); + return ret; + } + illegal_state_warn( *state, firstbyte ); + break; + case 0x01: /* dl_connect request */ + if( *state == WAN_DISCONNECTED ){ + *state = WAN_CONNECTING; + cprot -> dops -> connect_req(cprot); + } else { + illegal_state_warn( *state, firstbyte ); + } + break; + case 0x02: /* dl_disconnect request */ + switch ( *state ){ + case WAN_DISCONNECTED: + /* Should not happen. However, give upper layer a + chance to recover from inconstistency but don't + trust the lower layer sending the disconn_confirm + when already disconnected */ + printk(KERN_WARNING "isdn_x25iface_xmit: disconnect " + " requested while disconnected\n" ); + isdn_x25iface_disconn_ind(cprot); + break; /* prevent infinite loops */ + case WAN_CONNECTING: + case WAN_CONNECTED: + *state = WAN_DISCONNECTED; + cprot -> dops -> disconn_req(cprot); + break; + default: + illegal_state_warn( *state, firstbyte ); + } + break; + case 0x03: /* changing lapb parameters requested */ + printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb" + " options not yet supported\n"); + break; + default: + printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal" + " first byte %x ignored:\n", firstbyte); + } + dev_kfree_skb(skb); + return 0; +} diff --git a/drivers/isdn/isdn_x25iface.h b/drivers/isdn/isdn_x25iface.h new file mode 100644 index 000000000..146eeefff --- /dev/null +++ b/drivers/isdn/isdn_x25iface.h @@ -0,0 +1,32 @@ +/* $Id: isdn_x25iface.h,v 1.2 1998/01/31 22:49:23 keil Exp $ + */ +#ifndef _LINUX_ISDN_X25IFACE_H +#define _LINUX_ISDN_X25IFACE_H + +#define ISDN_X25IFACE_MAGIC 0x1e75a2b9 +/* #define DEBUG_ISDN_X25 if you want isdn_x25 debugging messages */ +#ifdef DEBUG_ISDN_X25 +# define IX25DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) +#else +# define IX25DEBUG(fmt,args...) +#endif + +#include <linux/skbuff.h> +#include <linux/wanrouter.h> +#include <linux/isdn.h> +#include <linux/concap.h> + +extern struct concap_proto_ops * isdn_x25iface_concap_proto_ops_pt; +extern struct concap_proto * isdn_x25iface_proto_new(void); + + + +#endif + + + + + + + + diff --git a/drivers/isdn/isdnloop/.cvsignore b/drivers/isdn/isdnloop/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/isdn/isdnloop/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/isdn/isdnloop/Makefile b/drivers/isdn/isdnloop/Makefile new file mode 100644 index 000000000..588d80760 --- /dev/null +++ b/drivers/isdn/isdnloop/Makefile @@ -0,0 +1,11 @@ +L_OBJS := +M_OBJS := + +ifeq ($(CONFIG_ISDN_DRV_LOOP),y) + L_OBJS += isdnloop.o +else + M_OBJS += isdnloop.o +endif + +include $(TOPDIR)/Rules.make + diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c new file mode 100644 index 000000000..0235ccb59 --- /dev/null +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -0,0 +1,1620 @@ +/* $Id: isdnloop.c,v 1.4 1998/02/24 21:39:05 he Exp $ + + * ISDN low-level module implementing a dummy loop driver. + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.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: isdnloop.c,v $ + * Revision 1.4 1998/02/24 21:39:05 he + * L2_PROT_X25DTE / DCE + * additional state 17 and new internal signal messages "BCON_I" + * (for reliable connect confirmation primitive as needed by x.25 upper layer) + * Changes for new LL-HL interface + * + * Revision 1.3 1998/02/20 17:33:30 fritz + * Changes for recent kernels. + * + * Revision 1.2 1997/10/01 09:22:03 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.1 1997/03/24 23:02:04 fritz + * Added isdnloop driver. + * + */ + +#include <linux/config.h> +#include "isdnloop.h" + +static char +*revision = "$Revision: 1.4 $"; + +static int isdnloop_addcard(char *); + +/* + * Free queue completely. + * + * Parameter: + * card = pointer to card struct + * channel = channel number + */ +static void +isdnloop_free_queue(isdnloop_card * card, int channel) +{ + struct sk_buff_head *queue = &card->bqueue[channel]; + struct sk_buff *skb; + + while ((skb = skb_dequeue(queue))) + dev_kfree_skb(skb); + card->sndcount[channel] = 0; +} + +/* + * Send B-Channel data to another virtual card. + * This routine is called via timer-callback from isdnloop_pollbchan(). + * + * Parameter: + * card = pointer to card struct. + * ch = channel number (0-based) + */ +static void +isdnloop_bchan_send(isdnloop_card * card, int ch) +{ + isdnloop_card *rcard = card->rcard[ch]; + int rch = card->rch[ch], len, ack; + struct sk_buff *skb; + isdn_ctrl cmd; + + while (card->sndcount[ch]) { + if ((skb = skb_dequeue(&card->bqueue[ch]))) { + len = skb->len; + card->sndcount[ch] -= len; + ack = *(skb->head); /* used as scratch area */ + cmd.driver = card->myid; + cmd.arg = ch; + if (rcard){ + rcard->interface.rcvcallb_skb(rcard->myid, rch, skb); + } else { + printk(KERN_WARNING "isdnloop: no rcard, skb dropped\n"); + dev_kfree_skb(skb); + + cmd.command = ISDN_STAT_L1ERR; + cmd.parm.errcode = ISDN_STAT_L1ERR_SEND; + card->interface.statcallb(&cmd); + }; + cmd.command = ISDN_STAT_BSENT; + cmd.parm.length = len; + if ( ack ) card->interface.statcallb(&cmd); + } else + card->sndcount[ch] = 0; + } +} + +/* + * Send/Receive Data to/from the B-Channel. + * This routine is called via timer-callback. + * It schedules itself while any B-Channel is open. + * + * Parameter: + * data = pointer to card struct, set by kernel timer.data + */ +static void +isdnloop_pollbchan(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + unsigned long flags; + + if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE) + isdnloop_bchan_send(card, 0); + if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE) + isdnloop_bchan_send(card, 1); + if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) { + /* schedule b-channel polling again */ + save_flags(flags); + cli(); + card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; + add_timer(&card->rb_timer); + card->flags |= ISDNLOOP_FLAGS_RBTIMER; + restore_flags(flags); + } else + card->flags &= ~ISDNLOOP_FLAGS_RBTIMER; +} + +/* + * Parse ICN-type setup string and fill fields of setup-struct + * with parsed data. + * + * Parameter: + * setup = setup string, format: [caller-id],si1,si2,[called-id] + * cmd = pointer to struct to be filled. + */ +static void +isdnloop_parse_setup(char *setup, isdn_ctrl * cmd) +{ + char *t = setup; + char *s = strpbrk(t, ","); + + *s++ = '\0'; + strncpy(cmd->parm.setup.phone, t, sizeof(cmd->parm.setup.phone)); + s = strpbrk(t = s, ","); + *s++ = '\0'; + if (!strlen(t)) + cmd->parm.setup.si1 = 0; + else + cmd->parm.setup.si1 = simple_strtoul(t, NULL, 10); + s = strpbrk(t = s, ","); + *s++ = '\0'; + if (!strlen(t)) + cmd->parm.setup.si2 = 0; + else + cmd->parm.setup.si2 = + simple_strtoul(t, NULL, 10); + strncpy(cmd->parm.setup.eazmsn, s, sizeof(cmd->parm.setup.eazmsn)); + cmd->parm.setup.plan = 0; + cmd->parm.setup.screen = 0; +} + +typedef struct isdnloop_stat { + char *statstr; + int command; + int action; +} isdnloop_stat; +/* *INDENT-OFF* */ +static isdnloop_stat isdnloop_stat_table[] = +{ + {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ + {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ + {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ + {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ + {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */ + {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */ + {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */ + {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */ + {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */ + {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */ + {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */ + {"NO D-CHAN", ISDN_STAT_NODCH, 0}, /* No D-channel available */ + {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ + {"E_L2: DATA LIN", ISDN_STAT_BHUP, 8}, /* Layer-2 data link lost */ + {"E_L1: ACTIVATION FAILED", + ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ + {NULL, 0, -1} +}; +/* *INDENT-ON* */ + + +/* + * Parse Status message-strings from virtual card. + * Depending on status, call statcallb for sending messages to upper + * levels. Also set/reset B-Channel active-flags. + * + * Parameter: + * status = status string to parse. + * channel = channel where message comes from. + * card = card where message comes from. + */ +static void +isdnloop_parse_status(u_char * status, int channel, isdnloop_card * card) +{ + isdnloop_stat *s = isdnloop_stat_table; + int action = -1; + isdn_ctrl cmd; + + while (s->statstr) { + if (!strncmp(status, s->statstr, strlen(s->statstr))) { + cmd.command = s->command; + action = s->action; + break; + } + s++; + } + if (action == -1) + return; + cmd.driver = card->myid; + cmd.arg = channel; + switch (action) { + case 1: + /* BCON_x */ + card->flags |= (channel) ? + ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE; + break; + case 2: + /* BDIS_x */ + card->flags &= ~((channel) ? + ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE); + isdnloop_free_queue(card, channel); + break; + case 3: + /* DCAL_I and DSCA_I */ + isdnloop_parse_setup(status + 6, &cmd); + break; + case 4: + /* FCALL */ + sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid); + sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1); + cmd.parm.setup.si1 = 7; + cmd.parm.setup.si2 = 0; + cmd.parm.setup.plan = 0; + cmd.parm.setup.screen = 0; + break; + case 5: + /* CIF */ + strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1); + break; + case 6: + /* AOC */ + sprintf(cmd.parm.num, "%d", + (int) simple_strtoul(status + 7, NULL, 16)); + break; + case 7: + /* CAU */ + status += 3; + if (strlen(status) == 4) + sprintf(cmd.parm.num, "%s%c%c", + status + 2, *status, *(status + 1)); + else + strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1); + break; + case 8: + /* Misc Errors on L1 and L2 */ + card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE; + isdnloop_free_queue(card, 0); + cmd.arg = 0; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_DHUP; + cmd.arg = 0; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_BHUP; + card->flags &= ~ISDNLOOP_FLAGS_B2ACTIVE; + isdnloop_free_queue(card, 1); + cmd.arg = 1; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_DHUP; + cmd.arg = 1; + cmd.driver = card->myid; + break; + } + card->interface.statcallb(&cmd); +} + +/* + * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl + * + * Parameter: + * card = pointer to card struct. + * c = char to store. + */ +static void +isdnloop_putmsg(isdnloop_card * card, unsigned char c) +{ + ulong flags; + + save_flags(flags); + cli(); + *card->msg_buf_write++ = (c == 0xff) ? '\n' : c; + if (card->msg_buf_write == card->msg_buf_read) { + if (++card->msg_buf_read > card->msg_buf_end) + card->msg_buf_read = card->msg_buf; + } + if (card->msg_buf_write > card->msg_buf_end) + card->msg_buf_write = card->msg_buf; + restore_flags(flags); +} + +/* + * Poll a virtual cards message queue. + * If there are new status-replies from the card, copy them to + * ringbuffer for reading on /dev/isdnctrl and call + * isdnloop_parse_status() for processing them. Watch for special + * Firmware bootmessage and parse it, to get the D-Channel protocol. + * If there are B-Channels open, initiate a timer-callback to + * isdnloop_pollbchan(). + * This routine is called periodically via timer interrupt. + * + * Parameter: + * data = pointer to card struct + */ +static void +isdnloop_polldchan(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + struct sk_buff *skb; + int avail; + int left; + u_char c; + int ch; + int flags; + u_char *p; + isdn_ctrl cmd; + + if ((skb = skb_dequeue(&card->dqueue))) + avail = skb->len; + else + avail = 0; + for (left = avail; left > 0; left--) { + c = *skb->data; + skb_pull(skb, 1); + isdnloop_putmsg(card, c); + card->imsg[card->iptr] = c; + if (card->iptr < 59) + card->iptr++; + if (!skb->len) { + avail++; + isdnloop_putmsg(card, '\n'); + card->imsg[card->iptr] = 0; + card->iptr = 0; + if (card->imsg[0] == '0' && card->imsg[1] >= '0' && + card->imsg[1] <= '2' && card->imsg[2] == ';') { + ch = (card->imsg[1] - '0') - 1; + p = &card->imsg[3]; + isdnloop_parse_status(p, ch, card); + } else { + p = card->imsg; + if (!strncmp(p, "DRV1.", 5)) { + printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p); + if (!strncmp(p + 7, "TC", 2)) { + card->ptype = ISDN_PTYPE_1TR6; + card->interface.features |= ISDN_FEATURE_P_1TR6; + printk(KERN_INFO + "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID); + } + if (!strncmp(p + 7, "EC", 2)) { + card->ptype = ISDN_PTYPE_EURO; + card->interface.features |= ISDN_FEATURE_P_EURO; + printk(KERN_INFO + "isdnloop: (%s) Euro-Protocol loaded and running\n", CID); + } + continue; + + } + } + } + } + if (avail) { + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = avail; + card->interface.statcallb(&cmd); + } + if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) + if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) { + /* schedule b-channel polling */ + card->flags |= ISDNLOOP_FLAGS_RBTIMER; + save_flags(flags); + cli(); + del_timer(&card->rb_timer); + card->rb_timer.function = isdnloop_pollbchan; + card->rb_timer.data = (unsigned long) card; + card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; + add_timer(&card->rb_timer); + restore_flags(flags); + } + /* schedule again */ + save_flags(flags); + cli(); + card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; + add_timer(&card->st_timer); + restore_flags(flags); +} + +/* + * Append a packet to the transmit buffer-queue. + * + * Parameter: + * channel = Number of B-channel + * skb = packet to send. + * card = pointer to card-struct + * Return: + * Number of bytes transferred, -E??? on error + */ +static int +isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card) +{ + int len = skb->len; + unsigned long flags; + struct sk_buff *nskb; + + if (len > 4000) { + printk(KERN_WARNING + "isdnloop: Send packet too large\n"); + return -EINVAL; + } + if (len) { + if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE)) + return 0; + if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE) + return 0; + save_flags(flags); + cli(); + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + skb_queue_tail(&card->bqueue[channel], nskb); + dev_kfree_skb(skb); + } else + len = 0; + card->sndcount[channel] += len; + restore_flags(flags); + } + return len; +} + +/* + * Read the messages from the card's ringbuffer + * + * Parameter: + * buf = pointer to buffer. + * len = number of bytes to read. + * user = flag, 1: called from userlevel 0: called from kernel. + * card = pointer to card struct. + * Return: + * number of bytes actually transferred. + */ +static int +isdnloop_readstatus(u_char * buf, int len, int user, isdnloop_card * card) +{ + int count; + u_char *p; + + for (p = buf, count = 0; count < len; p++, count++) { + if (card->msg_buf_read == card->msg_buf_write) + return count; + if (user) + put_user(*card->msg_buf_read++, p); + else + *p = *card->msg_buf_read++; + if (card->msg_buf_read > card->msg_buf_end) + card->msg_buf_read = card->msg_buf; + } + return count; +} + +/* + * Simulate a card's response by appending it to the cards + * message queue. + * + * Parameter: + * card = pointer to card struct. + * s = pointer to message-string. + * ch = channel: 0 = generic messages, 1 and 2 = D-channel messages. + * Return: + * 0 on success, 1 on memory squeeze. + */ +static int +isdnloop_fake(isdnloop_card * card, char *s, int ch) +{ + struct sk_buff *skb; + int len = strlen(s) + ((ch >= 0) ? 3 : 0); + + if (!(skb = dev_alloc_skb(len))) { + printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n"); + return 1; + } + if (ch >= 0) + sprintf(skb_put(skb, 3), "%02d;", ch); + memcpy(skb_put(skb, strlen(s)), s, strlen(s)); + skb_queue_tail(&card->dqueue, skb); + return 0; +} +/* *INDENT-OFF* */ +static isdnloop_stat isdnloop_cmd_table[] = +{ + {"BCON_R", 0, 1}, /* B-Channel connect */ + {"BCON_I", 0, 17}, /* B-Channel connect ind */ + {"BDIS_R", 0, 2}, /* B-Channel disconnect */ + {"DDIS_R", 0, 3}, /* D-Channel disconnect */ + {"DCON_R", 0, 16}, /* D-Channel connect */ + {"DSCA_R", 0, 4}, /* Dial 1TR6-SPV */ + {"DCAL_R", 0, 5}, /* Dial */ + {"EAZC", 0, 6}, /* Clear EAZ listener */ + {"EAZ", 0, 7}, /* Set EAZ listener */ + {"SEEAZ", 0, 8}, /* Get EAZ listener */ + {"MSN", 0, 9}, /* Set/Clear MSN listener */ + {"MSALL", 0, 10}, /* Set multi MSN listeners */ + {"SETSIL", 0, 11}, /* Set SI list */ + {"SEESIL", 0, 12}, /* Get SI list */ + {"SILC", 0, 13}, /* Clear SI list */ + {"LOCK", 0, -1}, /* LOCK channel */ + {"UNLOCK", 0, -1}, /* UNLOCK channel */ + {"FV2ON", 1, 14}, /* Leased mode on */ + {"FV2OFF", 1, 15}, /* Leased mode off */ + {NULL, 0, -1} +}; +/* *INDENT-ON* */ + + +/* + * Simulate an error-response from a card. + * + * Parameter: + * card = pointer to card struct. + */ +static void +isdnloop_fake_err(isdnloop_card * card) +{ + char buf[60]; + + sprintf(buf, "E%s", card->omsg); + isdnloop_fake(card, buf, -1); + isdnloop_fake(card, "NAK", -1); +} + +static u_char ctable_eu[] = +{0x00, 0x11, 0x01, 0x12}; +static u_char ctable_1t[] = +{0x00, 0x3b, 0x01, 0x3a}; + +/* + * Assemble a simplified cause message depending on the + * D-channel protocol used. + * + * Parameter: + * card = pointer to card struct. + * loc = location: 0 = local, 1 = remote. + * cau = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding. + * Return: + * Pointer to buffer containing the assembled message. + */ +static char * +isdnloop_unicause(isdnloop_card * card, int loc, int cau) +{ + static char buf[6]; + + switch (card->ptype) { + case ISDN_PTYPE_EURO: + sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]); + break; + case ISDN_PTYPE_1TR6: + sprintf(buf, "%02X44", ctable_1t[cau]); + break; + default: + return ("0000"); + } + return (buf); +} + +/* + * Release a virtual connection. Called from timer interrupt, when + * called party did not respond. + * + * Parameter: + * card = pointer to card struct. + * ch = channel (0-based) + */ +static void +isdnloop_atimeout(isdnloop_card * card, int ch) +{ + unsigned long flags; + char buf[60]; + + save_flags(flags); + cli(); + if (card->rcard) { + isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1); + card->rcard[ch]->rcard[card->rch[ch]] = NULL; + card->rcard[ch] = NULL; + } + isdnloop_fake(card, "DDIS_I", ch + 1); + /* No user responding */ + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3)); + isdnloop_fake(card, buf, ch + 1); + restore_flags(flags); +} + +/* + * Wrapper for isdnloop_atimeout(). + */ +static void +isdnloop_atimeout0(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + isdnloop_atimeout(card, 0); +} + +/* + * Wrapper for isdnloop_atimeout(). + */ +static void +isdnloop_atimeout1(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + isdnloop_atimeout(card, 1); +} + +/* + * Install a watchdog for a user, not responding. + * + * Parameter: + * card = pointer to card struct. + * ch = channel to watch for. + */ +static void +isdnloop_start_ctimer(isdnloop_card * card, int ch) +{ + unsigned long flags; + + save_flags(flags); + cli(); + init_timer(&card->c_timer[ch]); + card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT; + if (ch) + card->c_timer[ch].function = isdnloop_atimeout1; + else + card->c_timer[ch].function = isdnloop_atimeout0; + card->c_timer[ch].data = (unsigned long) card; + add_timer(&card->c_timer[ch]); + restore_flags(flags); +} + +/* + * Kill a pending channel watchdog. + * + * Parameter: + * card = pointer to card struct. + * ch = channel (0-based). + */ +static void +isdnloop_kill_ctimer(isdnloop_card * card, int ch) +{ + unsigned long flags; + + save_flags(flags); + cli(); + del_timer(&card->c_timer[ch]); + restore_flags(flags); +} + +static u_char si2bit[] = +{0, 1, 0, 0, 0, 2, 0, 4, 0, 0}; +static u_char bit2si[] = +{1, 5, 7}; + +/* + * Try finding a listener for an outgoing call. + * + * Parameter: + * card = pointer to calling card. + * p = pointer to ICN-type setup-string. + * lch = channel of calling card. + * cmd = pointer to struct to be filled when parsing setup. + * Return: + * 0 = found match, alerting should happen. + * 1 = found matching number but it is busy. + * 2 = no matching listener. + * 3 = found matching number but SI does not match. + */ +static int +isdnloop_try_call(isdnloop_card * card, char *p, int lch, isdn_ctrl * cmd) +{ + isdnloop_card *cc = cards; + unsigned long flags; + int ch; + int num_match; + int i; + char *e; + char nbuf[32]; + + isdnloop_parse_setup(p, cmd); + while (cc) { + for (ch = 0; ch < 2; ch++) { + /* Exclude ourself */ + if ((cc == card) && (ch == lch)) + continue; + num_match = 0; + switch (cc->ptype) { + case ISDN_PTYPE_EURO: + for (i = 0; i < 3; i++) + if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone))) + num_match = 1; + break; + case ISDN_PTYPE_1TR6: + e = cc->eazlist[ch]; + while (*e) { + sprintf(nbuf, "%s%c", cc->s0num[0], *e); + if (!(strcmp(nbuf, cmd->parm.setup.phone))) + num_match = 1; + e++; + } + } + if (num_match) { + save_flags(flags); + cli(); + /* channel idle? */ + if (!(cc->rcard[ch])) { + /* Check SI */ + if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) { + restore_flags(flags); + return 3; + } + /* ch is idle, si and number matches */ + cc->rcard[ch] = card; + cc->rch[ch] = lch; + card->rcard[lch] = cc; + card->rch[lch] = ch; + restore_flags(flags); + return 0; + } else { + restore_flags(flags); + /* num matches, but busy */ + if (ch == 1) + return 1; + } + } + } + cc = cc->next; + } + return 2; +} + +/* + * Depending on D-channel protocol and caller/called, modify + * phone number. + * + * Parameter: + * card = pointer to card struct. + * phone = pointer phone number. + * caller = flag: 1 = caller, 0 = called. + * Return: + * pointer to new phone number. + */ +static char * +isdnloop_vstphone(isdnloop_card * card, char *phone, int caller) +{ + int i; + static char nphone[30]; + + switch (card->ptype) { + case ISDN_PTYPE_EURO: + if (caller) { + for (i = 0; i < 2; i++) + if (!(strcmp(card->s0num[i], phone))) + return (phone); + return (card->s0num[0]); + } + return (phone); + break; + case ISDN_PTYPE_1TR6: + if (caller) { + sprintf(nphone, "%s%c", card->s0num[0], phone[0]); + return (nphone); + } else + return (&phone[strlen(phone) - 1]); + break; + } + return ("\0"); +} + +/* + * Parse an ICN-type command string sent to the 'card'. + * Perform misc. actions depending on the command. + * + * Parameter: + * card = pointer to card struct. + */ +static void +isdnloop_parse_cmd(isdnloop_card * card) +{ + char *p = card->omsg; + isdn_ctrl cmd; + char buf[60]; + isdnloop_stat *s = isdnloop_cmd_table; + int action = -1; + int i; + int ch; + + if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) { + isdnloop_fake_err(card); + return; + } + ch = card->omsg[1] - '0'; + if ((ch < 0) || (ch > 2)) { + isdnloop_fake_err(card); + return; + } + p += 3; + while (s->statstr) { + if (!strncmp(p, s->statstr, strlen(s->statstr))) { + action = s->action; + if (s->command && (ch != 0)) { + isdnloop_fake_err(card); + return; + } + break; + } + s++; + } + if (action == -1) + return; + switch (action) { + case 1: + /* 0x;BCON_R */ + if (card->rcard[ch - 1]) { + isdnloop_fake(card->rcard[ch - 1], "BCON_I", + card->rch[ch - 1] + 1); + } + break; + case 17: + /* 0x;BCON_I */ + if (card->rcard[ch - 1]) { + isdnloop_fake(card->rcard[ch - 1], "BCON_C", + card->rch[ch - 1] + 1); + } + break; + case 2: + /* 0x;BDIS_R */ + isdnloop_fake(card, "BDIS_C", ch); + if (card->rcard[ch - 1]) { + isdnloop_fake(card->rcard[ch - 1], "BDIS_I", + card->rch[ch - 1] + 1); + } + break; + case 16: + /* 0x;DCON_R */ + isdnloop_kill_ctimer(card, ch - 1); + if (card->rcard[ch - 1]) { + isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); + isdnloop_fake(card->rcard[ch - 1], "DCON_C", + card->rch[ch - 1] + 1); + isdnloop_fake(card, "DCON_C", ch); + } + break; + case 3: + /* 0x;DDIS_R */ + isdnloop_kill_ctimer(card, ch - 1); + if (card->rcard[ch - 1]) { + isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); + isdnloop_fake(card->rcard[ch - 1], "DDIS_I", + card->rch[ch - 1] + 1); + card->rcard[ch - 1] = NULL; + } + isdnloop_fake(card, "DDIS_C", ch); + break; + case 4: + /* 0x;DSCA_Rdd,yy,zz,oo */ + if (card->ptype != ISDN_PTYPE_1TR6) { + isdnloop_fake_err(card); + return; + } + /* Fall through */ + case 5: + /* 0x;DCAL_Rdd,yy,zz,oo */ + p += 6; + switch (isdnloop_try_call(card, p, ch - 1, &cmd)) { + case 0: + /* Alerting */ + sprintf(buf, "D%s_I%s,%02d,%02d,%s", + (action == 4) ? "SCA" : "CAL", + isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1), + cmd.parm.setup.si1, + cmd.parm.setup.si2, + isdnloop_vstphone(card->rcard[ch], + cmd.parm.setup.phone, 0)); + isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1); + /* Fall through */ + case 3: + /* si1 does not match, dont alert but start timer */ + isdnloop_start_ctimer(card, ch - 1); + break; + case 1: + /* Remote busy */ + isdnloop_fake(card, "DDIS_I", ch); + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1)); + isdnloop_fake(card, buf, ch); + break; + case 2: + /* No such user */ + isdnloop_fake(card, "DDIS_I", ch); + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2)); + isdnloop_fake(card, buf, ch); + break; + } + break; + case 6: + /* 0x;EAZC */ + card->eazlist[ch - 1][0] = '\0'; + break; + case 7: + /* 0x;EAZ */ + p += 3; + strcpy(card->eazlist[ch - 1], p); + break; + case 8: + /* 0x;SEEAZ */ + sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]); + isdnloop_fake(card, buf, ch + 1); + break; + case 9: + /* 0x;MSN */ + break; + case 10: + /* 0x;MSNALL */ + break; + case 11: + /* 0x;SETSIL */ + p += 6; + i = 0; + while (strchr("0157", *p)) { + if (i) + card->sil[ch - 1] |= si2bit[*p - '0']; + i = (*p++ == '0'); + } + if (*p) + isdnloop_fake_err(card); + break; + case 12: + /* 0x;SEESIL */ + sprintf(buf, "SIN-LIST: "); + p = buf + 10; + for (i = 0; i < 3; i++) + if (card->sil[ch - 1] & (1 << i)) + p += sprintf(p, "%02d", bit2si[i]); + isdnloop_fake(card, buf, ch + 1); + break; + case 13: + /* 0x;SILC */ + card->sil[ch - 1] = 0; + break; + case 14: + /* 00;FV2ON */ + break; + case 15: + /* 00;FV2OFF */ + break; + } +} + +/* + * Put command-strings into the of the 'card'. In reality, execute them + * right in place by calling isdnloop_parse_cmd(). Also copy every + * command to the read message ringbuffer, preceeding it with a '>'. + * These mesagges can be read at /dev/isdnctrl. + * + * Parameter: + * buf = pointer to command buffer. + * len = length of buffer data. + * user = flag: 1 = called form userlevel, 0 called from kernel. + * card = pointer to card struct. + * Return: + * number of bytes transfered (currently always equals len). + */ +static int +isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card) +{ + int xcount = 0; + int ocount = 1; + isdn_ctrl cmd; + + while (len) { + int count = MIN(255, len); + u_char *p; + u_char msg[0x100]; + + if (user) + copy_from_user(msg, buf, count); + else + memcpy(msg, buf, count); + isdnloop_putmsg(card, '>'); + for (p = msg; count > 0; count--, p++) { + len--; + xcount++; + isdnloop_putmsg(card, *p); + card->omsg[card->optr] = *p; + if (*p == '\n') { + card->omsg[card->optr] = '\0'; + card->optr = 0; + isdnloop_parse_cmd(card); + if (len) { + isdnloop_putmsg(card, '>'); + ocount++; + } + } else { + if (card->optr < 59) + card->optr++; + } + ocount++; + } + } + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = ocount; + card->interface.statcallb(&cmd); + return xcount; +} + +/* + * Delete card's pending timers, send STOP to linklevel + */ +static void +isdnloop_stopcard(isdnloop_card * card) +{ + unsigned long flags; + isdn_ctrl cmd; + + save_flags(flags); + cli(); + if (card->flags & ISDNLOOP_FLAGS_RUNNING) { + card->flags &= ~ISDNLOOP_FLAGS_RUNNING; + del_timer(&card->st_timer); + del_timer(&card->rb_timer); + del_timer(&card->c_timer[0]); + del_timer(&card->c_timer[1]); + cmd.command = ISDN_STAT_STOP; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + } + restore_flags(flags); +} + +/* + * Stop all cards before unload. + */ +static void +isdnloop_stopallcards(void) +{ + isdnloop_card *p = cards; + + while (p) { + isdnloop_stopcard(p); + p = p->next; + } +} + +/* + * Start a 'card'. Simulate card's boot message and set the phone + * number(s) of the virtual 'S0-Interface'. Install D-channel + * poll timer. + * + * Parameter: + * card = pointer to card struct. + * sdefp = pointer to struct holding ioctl parameters. + * Return: + * 0 on success, -E??? otherwise. + */ +static int +isdnloop_start(isdnloop_card * card, isdnloop_sdef * sdefp) +{ + unsigned long flags; + isdnloop_sdef sdef; + int i; + + if (card->flags & ISDNLOOP_FLAGS_RUNNING) + return -EBUSY; + copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef)); + save_flags(flags); + cli(); + switch (sdef.ptype) { + case ISDN_PTYPE_EURO: + if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96", + -1)) { + restore_flags(flags); + return -ENOMEM; + } + card->sil[0] = card->sil[1] = 4; + if (isdnloop_fake(card, "TEI OK", 0)) { + restore_flags(flags); + return -ENOMEM; + } + for (i = 0; i < 3; i++) + strcpy(card->s0num[i], sdef.num[i]); + break; + case ISDN_PTYPE_1TR6: + if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95", + -1)) { + restore_flags(flags); + return -ENOMEM; + } + card->sil[0] = card->sil[1] = 4; + if (isdnloop_fake(card, "TEI OK", 0)) { + restore_flags(flags); + return -ENOMEM; + } + strcpy(card->s0num[0], sdef.num[0]); + card->s0num[1][0] = '\0'; + card->s0num[2][0] = '\0'; + break; + default: + restore_flags(flags); + printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n", + sdef.ptype); + return -EINVAL; + } + init_timer(&card->st_timer); + card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; + card->st_timer.function = isdnloop_polldchan; + card->st_timer.data = (unsigned long) card; + add_timer(&card->st_timer); + card->flags |= ISDNLOOP_FLAGS_RUNNING; + restore_flags(flags); + return 0; +} + +/* + * Main handler for commands sent by linklevel. + */ +static int +isdnloop_command(isdn_ctrl * c, isdnloop_card * card) +{ + ulong a; + int i; + char cbuf[60]; + isdn_ctrl cmd; + isdnloop_cdef cdef; + + switch (c->command) { + case ISDN_CMD_IOCTL: + memcpy(&a, c->parm.num, sizeof(ulong)); + switch (c->arg) { + case ISDNLOOP_IOCTL_DEBUGVAR: + return (ulong) card; + case ISDNLOOP_IOCTL_STARTUP: + if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef)))) + return i; + return (isdnloop_start(card, (isdnloop_sdef *) a)); + break; + case ISDNLOOP_IOCTL_ADDCARD: + if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_cdef)))) + return i; + copy_from_user((char *) &cdef, (char *) a, sizeof(cdef)); + return (isdnloop_addcard(cdef.id1)); + break; + case ISDNLOOP_IOCTL_LEASEDCFG: + if (a) { + if (!card->leased) { + card->leased = 1; + while (card->ptype == ISDN_PTYPE_UNKNOWN) { + current->timeout = jiffies + 10; + schedule(); + } + current->timeout = jiffies + 10; + schedule(); + sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "isdnloop: (%s) Leased-line mode enabled\n", + CID); + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + card->interface.statcallb(&cmd); + } + } else { + if (card->leased) { + card->leased = 0; + sprintf(cbuf, "00;FV2OFF\n"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "isdnloop: (%s) Leased-line mode disabled\n", + CID); + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + card->interface.statcallb(&cmd); + } + } + return 0; + default: + return -EINVAL; + } + break; + case ISDN_CMD_DIAL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if ((c->arg & 255) < ISDNLOOP_BCH) { + char *p; + char dial[50]; + char dcode[4]; + + a = c->arg; + p = c->parm.setup.phone; + if (*p == 's' || *p == 'S') { + /* Dial for SPV */ + p++; + strcpy(dcode, "SCA"); + } else + /* Normal Dial */ + strcpy(dcode, "CAL"); + strcpy(dial, p); + sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), + dcode, dial, c->parm.setup.si1, + c->parm.setup.si2, c->parm.setup.eazmsn); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTD: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + cbuf[0] = 0; + switch (card->l2_proto[a - 1]) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BX75\n", (int) a); + break; +#ifdef CONFIG_ISDN_X25 + case ISDN_PROTO_L2_X25DTE: + sprintf(cbuf, "%02d;BX2T\n", (int) a); + break; + case ISDN_PROTO_L2_X25DCE: + sprintf(cbuf, "%02d;BX2C\n", (int) a); + break; +#endif + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BTRA\n", (int) a); + break; + } + if (strlen(cbuf)) + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + sprintf(cbuf, "%02d;DCON_R\n", (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTB: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + switch (card->l2_proto[a - 1]) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a); + break; +#ifdef CONFIG_ISDN_X25 + case ISDN_PROTO_L2_X25DTE: + sprintf(cbuf, "%02d;BCON_R,BX2T\n", (int) a); + break; + case ISDN_PROTO_L2_X25DCE: + sprintf(cbuf, "%02d;BCON_R,BX2C\n", (int) a); + break; +#endif + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a); + break; + default: + sprintf(cbuf, "%02d;BCON_R\n", (int) a); + } + printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + break; + case ISDN_CMD_HANGUP: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETEAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + if (card->ptype == ISDN_PTYPE_EURO) { + sprintf(cbuf, "%02d;MS%s%s\n", (int) a, + c->parm.num[0] ? "N" : "ALL", c->parm.num); + } else + sprintf(cbuf, "%02d;EAZ%s\n", (int) a, + c->parm.num[0] ? c->parm.num : "0123456789"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_CLREAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + if (card->ptype == ISDN_PTYPE_EURO) + sprintf(cbuf, "%02d;MSNC\n", (int) a); + else + sprintf(cbuf, "%02d;EAZC\n", (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETL2: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) { + a = c->arg; + switch (a >> 8) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1); + break; +#ifdef CONFIG_ISDN_X25 + case ISDN_PROTO_L2_X25DTE: + sprintf(cbuf, "%02d;BX2T\n", (int) (a & 255) + 1); + break; + case ISDN_PROTO_L2_X25DCE: + sprintf(cbuf, "%02d;BX2C\n", (int) (a & 255) + 1); + break; +#endif + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1); + break; + default: + return -EINVAL; + } + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + card->l2_proto[a & 255] = (a >> 8); + } + break; + case ISDN_CMD_GETL2: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) + return card->l2_proto[c->arg & 255]; + else + return -ENODEV; + case ISDN_CMD_SETL3: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return 0; + case ISDN_CMD_GETL3: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) + return ISDN_PROTO_L3_TRANS; + else + return -ENODEV; + case ISDN_CMD_GETEAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_SETSIL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_GETSIL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_LOCK: + MOD_INC_USE_COUNT; + break; + case ISDN_CMD_UNLOCK: + MOD_DEC_USE_COUNT; + break; + default: + return -EINVAL; + } + } + return 0; +} + +/* + * Find card with given driverId + */ +static inline isdnloop_card * +isdnloop_findcard(int driverid) +{ + isdnloop_card *p = cards; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (isdnloop_card *) 0; +} + +/* + * Wrapper functions for interface to linklevel + */ +static int +if_command(isdn_ctrl * c) +{ + isdnloop_card *card = isdnloop_findcard(c->driver); + + if (card) + return (isdnloop_command(c, card)); + printk(KERN_ERR + "isdnloop: if_command called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_writecmd(const u_char * buf, int len, int user, int id, int channel) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_writecmd(buf, len, user, card)); + } + printk(KERN_ERR + "isdnloop: if_writecmd called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_readstatus(u_char * buf, int len, int user, int id, int channel) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_readstatus(buf, len, user, card)); + } + printk(KERN_ERR + "isdnloop: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + /* ack request stored in skb scratch area */ + *(skb->head) = ack; + return (isdnloop_sendbuf(channel, skb, card)); + } + printk(KERN_ERR + "isdnloop: if_sendbuf called with invalid driverId!\n"); + return -ENODEV; +} + +/* + * Allocate a new card-struct, initialize it + * link it into cards-list and register it at linklevel. + */ +static isdnloop_card * +isdnloop_initcard(char *id) +{ + isdnloop_card *card; + int i; + + if (!(card = (isdnloop_card *) kmalloc(sizeof(isdnloop_card), GFP_KERNEL))) { + printk(KERN_WARNING + "isdnloop: (%s) Could not allocate card-struct.\n", id); + return (isdnloop_card *) 0; + } + memset((char *) card, 0, sizeof(isdnloop_card)); + card->interface.channels = ISDNLOOP_BCH; + card->interface.hl_hdrlen = 1; /* scratch area for storing ack flag*/ + card->interface.maxbufsize = 4000; + card->interface.command = if_command; + card->interface.writebuf_skb = if_sendbuf; + card->interface.writecmd = if_writecmd; + card->interface.readstat = if_readstatus; + card->interface.features = ISDN_FEATURE_L2_X75I | +#ifdef CONFIG_ISDN_X25 + ISDN_FEATURE_L2_X25DTE | + ISDN_FEATURE_L2_X25DCE | +#endif + ISDN_FEATURE_L2_HDLC | + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN; + card->ptype = ISDN_PTYPE_UNKNOWN; + strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); + card->msg_buf_write = card->msg_buf; + card->msg_buf_read = card->msg_buf; + card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1]; + for (i = 0; i < ISDNLOOP_BCH; i++) { + card->l2_proto[i] = ISDN_PROTO_L2_X75I; + skb_queue_head_init(&card->bqueue[i]); + } + skb_queue_head_init(&card->dqueue); + card->next = cards; + cards = card; + if (!register_isdn(&card->interface)) { + cards = cards->next; + printk(KERN_WARNING + "isdnloop: Unable to register %s\n", id); + kfree(card); + return (isdnloop_card *) 0; + } + card->myid = card->interface.channels; + return card; +} + +static int +isdnloop_addcard(char *id1) +{ + ulong flags; + isdnloop_card *card; + + save_flags(flags); + cli(); + if (!(card = isdnloop_initcard(id1))) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + printk(KERN_INFO + "isdnloop: (%s) virtual card added\n", + card->interface.id); + return 0; +} + +#ifdef MODULE +#define isdnloop_init init_module +#else +void +isdnloop_setup(char *str, int *ints) +{ + static char sid[20]; + + if (strlen(str)) { + strcpy(sid, str); + isdnloop_id = sid; + } +} +#endif + +int +isdnloop_init(void) +{ + char *p; + char rev[10]; + + /* No symbols to export, hide all symbols */ + EXPORT_NO_SYMBOLS; + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev); + return (isdnloop_addcard(isdnloop_id)); +} + +#ifdef MODULE +void +cleanup_module(void) +{ + isdn_ctrl cmd; + isdnloop_card *card = cards; + isdnloop_card *last; + int i; + + isdnloop_stopallcards(); + while (card) { + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + for (i = 0; i < ISDNLOOP_BCH; i++) + isdnloop_free_queue(card, i); + card = card->next; + } + card = cards; + while (card) { + struct sk_buff *skb; + + last = card; + while ((skb = skb_dequeue(&card->dqueue))) + dev_kfree_skb(skb); + card = card->next; + kfree(last); + } + printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n"); +} +#endif diff --git a/drivers/isdn/isdnloop/isdnloop.h b/drivers/isdn/isdnloop/isdnloop.h new file mode 100644 index 000000000..cacd686d7 --- /dev/null +++ b/drivers/isdn/isdnloop/isdnloop.h @@ -0,0 +1,144 @@ +/* $Id: isdnloop.h,v 1.2 1997/10/01 09:22:07 fritz Exp $ + + * Loopback lowlevel module for testing of linklevel. + * + * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.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: isdnloop.h,v $ + * Revision 1.2 1997/10/01 09:22:07 fritz + * Removed old compatibility stuff for 2.0.X kernels. + * From now on, this code is for 2.1.X ONLY! + * Old stuff is still in the separate branch. + * + * Revision 1.1 1997/03/24 23:02:05 fritz + * Added isdnloop driver. + * + */ + +#ifndef isdnloop_h +#define isdnloop_h + +#define ISDNLOOP_IOCTL_DEBUGVAR 0 +#define ISDNLOOP_IOCTL_ADDCARD 1 +#define ISDNLOOP_IOCTL_LEASEDCFG 2 +#define ISDNLOOP_IOCTL_STARTUP 3 + +/* Struct for adding new cards */ +typedef struct isdnloop_cdef { + char id1[10]; +} isdnloop_cdef; + +/* Struct for configuring cards */ +typedef struct isdnloop_sdef { + int ptype; + char num[3][20]; +} isdnloop_sdef; + +#if defined(__KERNEL__) || defined(__DEBUGVAR__) + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/major.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/malloc.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/ioport.h> +#include <linux/timer.h> +#include <linux/wait.h> +#include <linux/isdnif.h> + +#endif /* __KERNEL__ */ + +#define ISDNLOOP_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */ +#define ISDNLOOP_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */ +#define ISDNLOOP_FLAGS_RUNNING 4 /* Cards driver activated */ +#define ISDNLOOP_FLAGS_RBTIMER 8 /* scheduling of B-Channel-poll */ +#define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle */ +#define ISDNLOOP_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */ +#define ISDNLOOP_TIMER_ALERTWAIT (10*HZ) /* Alert timeout */ +#define ISDNLOOP_MAX_SQUEUE 65536 /* Max. outstanding send-data */ +#define ISDNLOOP_BCH 2 /* channels per card */ + +/* + * Per card driver data + */ +typedef struct isdnloop_card { + struct isdnloop_card *next; /* Pointer to next device struct */ + struct isdnloop_card + *rcard[ISDNLOOP_BCH]; /* Pointer to 'remote' card */ + int rch[ISDNLOOP_BCH]; /* 'remote' channel */ + int myid; /* Driver-Nr. assigned by linklevel */ + int leased; /* Flag: This Adapter is connected */ + /* to a leased line */ + int sil[ISDNLOOP_BCH]; /* SI's to listen for */ + char eazlist[ISDNLOOP_BCH][11]; + /* EAZ's to listen for */ + char s0num[3][20]; /* 1TR6 base-number or MSN's */ + unsigned short flags; /* Statusflags */ + int ptype; /* Protocol type (1TR6 or Euro) */ + struct timer_list st_timer; /* Timer for Status-Polls */ + struct timer_list rb_timer; /* Timer for B-Channel-Polls */ + struct timer_list + c_timer[ISDNLOOP_BCH]; /* Timer for Alerting */ + int l2_proto[ISDNLOOP_BCH]; /* Current layer-2-protocol */ + isdn_if interface; /* Interface to upper layer */ + int iptr; /* Index to imsg-buffer */ + char imsg[60]; /* Internal buf for status-parsing */ + int optr; /* Index to omsg-buffer */ + char omsg[60]; /* Internal buf for cmd-parsing */ + char msg_buf[2048]; /* Buffer for status-messages */ + char *msg_buf_write; /* Writepointer for statusbuffer */ + char *msg_buf_read; /* Readpointer for statusbuffer */ + char *msg_buf_end; /* Pointer to end of statusbuffer */ + int sndcount[ISDNLOOP_BCH]; /* Byte-counters for B-Ch.-send */ + struct sk_buff_head + bqueue[ISDNLOOP_BCH]; /* B-Channel queues */ + struct sk_buff_head dqueue; /* D-Channel queue */ +} isdnloop_card; + +/* + * Main driver data + */ +#ifdef __KERNEL__ +static isdnloop_card *cards = (isdnloop_card *) 0; +static char *isdnloop_id = "\0"; + +#ifdef MODULE +MODULE_AUTHOR("Fritz Elfert"); +MODULE_PARM(isdnloop_id, "s"); +MODULE_PARM_DESC(isdnloop_id, "ID-String of first card"); +#endif + +#endif /* __KERNEL__ */ + +/* Utility-Macros */ + +#define CID (card->interface.id) +#define MIN(a,b) ((a<b)?a:b) +#define MAX(a,b) ((a>b)?a:b) + +#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ +#endif /* isdnloop_h */ diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c index 790da8792..ed681f375 100644 --- a/drivers/isdn/pcbit/capi.c +++ b/drivers/isdn/pcbit/capi.c @@ -147,9 +147,6 @@ int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - - *((ushort*) skb_put(*skb, 2) ) = chan->callref; *(skb_put(*skb, 1)) = 0x01; /* ACCEPT_CALL */ *(skb_put(*skb, 1)) = 0; @@ -170,8 +167,6 @@ int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; #ifdef DEBUG @@ -200,8 +195,6 @@ int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; return 2; @@ -222,8 +215,6 @@ int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb, return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; /* Layer2 protocol */ @@ -285,8 +276,6 @@ int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; @@ -338,8 +327,6 @@ int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = chan->callref; *(skb_put(*skb, 1)) = chan->layer2link; @@ -357,8 +344,6 @@ int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2) ) = callref; *(skb_put(*skb, 1)) = 2; /* Cause.Length = 2; */ @@ -382,8 +367,6 @@ int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb) return -1; } - SET_SKB_FREE((*skb)); - *((ushort*) skb_put(*skb, 2)) = chan->callref; return 2; diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c index cacb714be..43c36fa9e 100644 --- a/drivers/isdn/pcbit/drv.c +++ b/drivers/isdn/pcbit/drv.c @@ -61,7 +61,7 @@ static char* pcbit_devname[MAX_PCBIT_CARDS] = { int pcbit_command(isdn_ctrl* ctl); int pcbit_stat(u_char* buf, int len, int user, int, int); -int pcbit_xmit(int driver, int chan, struct sk_buff *skb); +int pcbit_xmit(int driver, int chan, int ack, struct sk_buff *skb); int pcbit_writecmd(const u_char*, int, int, int, int); static int set_protocol_running(struct pcbit_dev * dev); @@ -164,7 +164,6 @@ int pcbit_init_dev(int board, int mem_base, int irq) ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS ); dev_if->writebuf_skb = pcbit_xmit; - dev_if->writebuf = NULL; dev_if->hl_hdrlen = 10; dev_if->maxbufsize = MAXBUFSIZE; @@ -330,7 +329,7 @@ static void pcbit_block_timer(unsigned long data) } #endif -int pcbit_xmit(int driver, int chnum, struct sk_buff *skb) +int pcbit_xmit(int driver, int chnum, int ack, struct sk_buff *skb) { ushort hdrlen; int refnum, len; @@ -731,8 +730,6 @@ void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, #endif } - SET_SKB_FREE(skb); - kfree_skb(skb); } diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c index 0ccb2b7c9..8283b7367 100644 --- a/drivers/isdn/pcbit/layer2.c +++ b/drivers/isdn/pcbit/layer2.c @@ -380,10 +380,8 @@ pcbit_receive(struct pcbit_dev *dev) return; #else /* discard previous queued frame */ - if (dev->read_frame->skb) { - SET_SKB_FREE(dev->read_frame->skb); + if (dev->read_frame->skb) kfree_skb(dev->read_frame->skb); - } kfree(dev->read_frame); dev->read_frame = NULL; #endif @@ -648,10 +646,8 @@ pcbit_l2_err_recover(unsigned long data) dev->w_busy = dev->r_busy = 1; if (dev->read_frame) { - if (dev->read_frame->skb) { - SET_SKB_FREE(dev->read_frame->skb); + if (dev->read_frame->skb) kfree_skb(dev->read_frame->skb); - } kfree(dev->read_frame); dev->read_frame = NULL; } diff --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c index 34f8fc1c5..33aee3360 100644 --- a/drivers/isdn/pcbit/module.c +++ b/drivers/isdn/pcbit/module.c @@ -35,10 +35,8 @@ extern void pcbit_terminate(int board); extern int pcbit_init_dev(int board, int mem_base, int irq); #ifdef MODULE -#if (LINUX_VERSION_CODE > 0x020111) MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i"); -#endif #define pcbit_init init_module #endif @@ -87,11 +85,7 @@ int pcbit_init(void) } /* No symbols to export, hide all symbols */ -#if (LINUX_VERSION_CODE < 0x020111) - register_symtab(NULL); -#else EXPORT_NO_SYMBOLS; -#endif return 0; } diff --git a/drivers/isdn/sc/.cvsignore b/drivers/isdn/sc/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/isdn/sc/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/isdn/sc/debug.c b/drivers/isdn/sc/debug.c index 3a814de93..c5312cd83 100644 --- a/drivers/isdn/sc/debug.c +++ b/drivers/isdn/sc/debug.c @@ -1,5 +1,5 @@ /* - * $Id: debug.c,v 1.2 1996/11/20 17:49:50 fritz Exp $ + * $Id: debug.c,v 1.3 1997/10/01 09:22:20 fritz Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -29,13 +29,8 @@ #define NULL 0x0 -#if LINUX_VERSION_CODE < 66363 /* Linux 1.3.59 there was a change to interrupts */ - #define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d) - #define FREE_IRQ(a,b) free_irq(a) -#else - #define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e) - #define FREE_IRQ(a,b) free_irq(a,b) -#endif +#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e) +#define FREE_IRQ(a,b) free_irq(a,b) inline char *strcpy(char *, const char *); diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c index 3452cbf36..23cd53f07 100644 --- a/drivers/isdn/sc/event.c +++ b/drivers/isdn/sc/event.c @@ -1,5 +1,5 @@ /* - * $Id: event.c,v 1.3 1997/02/11 22:53:41 fritz Exp $ + * $Id: event.c,v 1.4 1997/10/09 22:30:58 fritz Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -62,10 +62,16 @@ int indicate_status(int card, int event,ulong Channel,char *Data) if (Data != NULL){ pr_debug("%s: Event data: %s\n", adapter[card]->devicename, Data); - if (event == ISDN_STAT_ICALL) - memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup)); - else - strcpy(cmd.parm.num, Data); + switch (event) { + case ISDN_STAT_BSENT: + memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length)); + break; + case ISDN_STAT_ICALL: + memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup)); + break; + default: + strcpy(cmd.parm.num, Data); + } } cmd.command = event; diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h index 4a7698225..b0f07ac3c 100644 --- a/drivers/isdn/sc/hardware.h +++ b/drivers/isdn/sc/hardware.h @@ -16,6 +16,11 @@ this, you must also change the number of elements in io, irq, and ram to match. Initialized in init.c */ +/* +extern unsigned int io[]; +extern unsigned char irq[]; +extern unsigned long ram[]; +*/ #define SIGNATURE 0x87654321 /* Board reset signature */ #define SIG_OFFSET 0x1004 /* Where to find signature in shared RAM */ diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index c9eb24035..d34dd03b9 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -20,7 +20,7 @@ static int sup_irq[] = { 11, 10, 9, 5, 12, 14, 7, 3, 4, 6 }; #define MAX_IRQS 10 extern void interrupt_handler(int, void *, struct pt_regs *); -extern int sndpkt(int, int, struct sk_buff *); +extern int sndpkt(int, int, int, struct sk_buff *); extern int command(isdn_ctrl *); extern int indicate_status(int, int, ulong, char*); extern int reset(int); @@ -38,12 +38,10 @@ int irq_supported(int irq_x) } #ifdef MODULE -#if (LINUX_VERSION_CODE > 0x020111) MODULE_PARM(io, "1-4i"); MODULE_PARM(irq, "1-4i"); MODULE_PARM(ram, "1-4i"); MODULE_PARM(do_reset, "i"); -#endif #define init_sc init_module #else /* diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c index 6b5b369e3..25964752b 100644 --- a/drivers/isdn/sc/interrupt.c +++ b/drivers/isdn/sc/interrupt.c @@ -1,5 +1,5 @@ /* - * $Id: interrupt.c,v 1.3 1997/02/11 22:53:43 fritz Exp $ + * $Id: interrupt.c,v 1.4 1998/01/31 22:10:52 keil Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c index e104fada6..2cbdcdae8 100644 --- a/drivers/isdn/sc/message.c +++ b/drivers/isdn/sc/message.c @@ -1,5 +1,5 @@ /* - * $Id: message.c,v 1.2 1996/11/20 17:49:54 fritz Exp $ + * $Id: message.c,v 1.3 1998/01/31 22:10:55 keil Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * message.c - functions for sending and receiving control messages @@ -33,7 +33,6 @@ #include "hardware.h" #include "message.h" #include "card.h" -#include <asm/io.h> extern board *adapter[]; extern unsigned int cinst; @@ -203,7 +202,7 @@ int sendmessage(int card, * wait for an empty slot in the queue */ while (!(inb(adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL)) - __SLOW_DOWN_IO; + udelay(1); /* * Disable interrupts and map in shared memory diff --git a/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c index 563d1821d..d75cb04d7 100644 --- a/drivers/isdn/sc/packet.c +++ b/drivers/isdn/sc/packet.c @@ -1,5 +1,5 @@ /* - * $Id: packet.c,v 1.2 1996/11/20 17:49:55 fritz Exp $ + * $Id: packet.c,v 1.4 1998/02/12 23:08:50 keil Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ extern board *adapter[]; extern unsigned int cinst; extern int get_card_from_id(int); -extern int indicate_status(int, int,ulong,char*); +extern int indicate_status(int, int,ulong, char*); extern void *memcpy_toshmem(int, void *, const void *, size_t); extern void *memcpy_fromshmem(int, void *, const void *, size_t); extern int sendmessage(int, unsigned int, unsigned int, unsigned int, @@ -47,6 +47,7 @@ int sndpkt(int devId, int channel, struct sk_buff *data) LLData ReqLnkWrite; int status; int card; + unsigned long len; card = get_card_from_id(devId); @@ -89,6 +90,7 @@ int sndpkt(int devId, int channel, struct sk_buff *data) status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite, channel+1, sizeof(LLData), (unsigned int*)&ReqLnkWrite); + len = data->len; if(status) { pr_debug("%s: Failed to send packet, status = %d\n", adapter[card]->devicename, status); return -1; @@ -101,9 +103,9 @@ int sndpkt(int devId, int channel, struct sk_buff *data) adapter[card]->channel[channel].next_sendbuf; pr_debug("%s: Packet sent successfully\n", adapter[card]->devicename); dev_kfree_skb(data); - indicate_status(card,ISDN_STAT_BSENT,channel,NULL); + indicate_status(card,ISDN_STAT_BSENT,channel, (char *)&len); } - return data->len; + return len; } void rcvpkt(int card, RspMessage *rcvmsg) |