diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e3bade1 --- /dev/null +++ b/Makefile @@ -0,0 +1,77 @@ +BASEDIR=$(shell pwd) + + +INSTALL_PREFIX := / +export INSTALL_PREFIX + +#PATH to linux source/headers +#LINUX=/usr/src/linux +MODS=/lib/modules/$(shell uname -r) +LINUX=$(MODS)/build +LINUX_SOURCE=$(MODS)/source + + +MISDNDIR=$(BASEDIR) +MISDN_SRC=$(MISDNDIR)/drivers/isdn/hardware/mISDN + +######################################## +# USER CONFIGS END +######################################## + +CONFIGS+=CONFIG_MISDN_DRV=m CONFIG_MISDN_DSP=m +CONFIGS+=CONFIG_MISDN_HFCMULTI=m +CONFIGS+=CONFIG_MISDN_HFCPCI=m +CONFIGS+=CONFIG_MISDN_HFCUSB=m +CONFIGS+=CONFIG_MISDN_XHFC=m +CONFIGS+=CONFIG_MISDN_HFCMINI=m +CONFIGS+=CONFIG_MISDN_W6692=m +CONFIGS+=CONFIG_MISDN_SPEEDFAX=m +CONFIGS+=CONFIG_MISDN_AVM_FRITZ=m + + +MINCLUDES+=-I$(MISDNDIR)/include + +all: test_old_misdn + @echo + @echo "Makeing mISDN" + @echo "=============" + @echo + cp $(MISDNDIR)/drivers/isdn/hardware/mISDN/Makefile.v2.6 $(MISDNDIR)/drivers/isdn/hardware/mISDN/Makefile + + export MINCLUDES=$(MISDNDIR)/include ; make -C $(LINUX) SUBDIRS=$(MISDN_SRC) modules $(CONFIGS) + + +install: all + cd $(LINUX) ; make SUBDIRS=$(MISDN_SRC) modules_install + cp $(MISDNDIR)/include/linux/*.h $(INSTALL_PREFIX)/usr/include/linux/ + install -m755 misdn-init /etc/init.d/ + depmod + + +test_old_misdn: + @if echo -ne "#include " | gcc -C -E - 2>/dev/null 1>/dev/null ; then \ + if ! echo -ne "#include \n#ifndef FLG_MSG_DOWN\n#error old mISDNif.h\n#endif\n" | gcc -C -E - 2>/dev/null 1>/dev/null ; then \ + echo -ne "\n!!You should remove the following files:\n\n$(LINUX)/include/linux/mISDNif.h\n$(LINUX)/include/linux/isdn_compat.h\n/usr/include/linux/mISDNif.h\n/usr/include/linux/isdn_compat.h\n\nIn order to upgrade to the mqueue branch\n\n"; \ + echo -ne "I can do that for you, just type: make force\n\n" ; \ + exit 1; \ + fi ;\ + fi + + +.PHONY: install all clean + +force: + rm -f $(LINUX)/include/linux/mISDNif.h + rm -f $(LINUX)/include/linux/isdn_compat.h + rm -f /usr/include/linux/mISDNif.h + rm -f /usr/include/linux/isdn_compat.h + +clean: + rm -rf drivers/isdn/hardware/mISDN/*.o + rm -rf drivers/isdn/hardware/mISDN/*.ko + rm -rf *~ + find . -iname ".*.cmd" -exec rm -rf {} \; + find . -iname ".*.d" -exec rm -rf {} \; + find . -iname "*.mod.c" -exec rm -rf {} \; + find . -iname "*.mod" -exec rm -rf {} \; + diff --git a/README.misdn-init b/README.misdn-init new file mode 100644 index 0000000..a59f8df --- /dev/null +++ b/README.misdn-init @@ -0,0 +1,55 @@ +misdn-init: init-script to auto-configure and load the mISDN kernel drivers +=========================================================================== + +This script makes it easy to configure and activate mISDN compatible +adapter cards. It scans an eyecandy config file named misdn-init.conf +for your card and port settings, then it loads the driver modules properly. +The misdn-init.conf can also be autogenerated by the misdn-init script. + +--------------------------------------------------------------------------- +Usage: /etc/init.d/misdn-init start|stop|restart|config|scan|help + + --start scan /etc/misdn-init.conf and load the mISDN drivers + --stop unload the mISDN drivers + --restart see stop, then start + --config scan your PCI bus for mISDN compatible hardware and generate + a /etc/misdn-init.conf + --scan scan your PCI bus for mISDN compatible hardware and print + the results to the console + --help print the usage info +--------------------------------------------------------------------------- + + +* Here is a quick overview on how to use misdn-init: + +1) Get and install misdn-init: + $ wget http://www.beronet.com/downloads/chan_misdn/stable/chan_misdn.tar.gz + $ tar zxf chan_misdn.tar.gz + $ (as root) cp chan_misdn/misdn-init /etc/init.d/misdn-init + +2) Let misdn-init scan your PCI bus for mISDN compatible hardware and write + the results into /etc/misdn-init.conf: + $ (as root) /etc/init.d/misdn-init config + +3) (optional) Edit /etc/misdn-init.conf and set everything the way you want it. + This file is heavily commented, hence it should be self-explaining. + +4) (optional, but recommended) Add misdn-init to your run level. + This is distribution dependend. Here an example for a debian system: + ATTENTION: If you have services in your runlevels that depend + on mISDN, make sure that misdn-init starts before, and + stops after them (this is done by changing the values + that are set to 60 in this example, more info: read the + manpage for update-rc.d). + $ (as root) update-rc.d misdn-init start 60 2 3 4 5 . stop 60 0 1 6 . + +5) Run the following to start mISDN: + $ (as root) /etc/init.d/misdn-init start + + + +--------------------------------------------------------------------------- +* Report Bugs: +If you experience any bugs or have a feature request, please visit: +www.isdn4linux.de/mantis + diff --git a/drivers/isdn/hardware/mISDN/channel.c b/drivers/isdn/hardware/mISDN/channel.c new file mode 100644 index 0000000..7771980 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/channel.c @@ -0,0 +1,88 @@ +/* $Id$ + * + * Author (c) Karsten Keil + * + * This file is released under the GPLv2 + * + */ + +#include +#include "channel.h" +#include "layer1.h" + +int +mISDN_initchannel(channel_t *ch, ulong prop, int maxlen) +{ + ch->log = kmalloc(MAX_LOG_SPACE, GFP_ATOMIC); + if (!ch->log) { + printk(KERN_WARNING + "mISDN: No memory for channel log\n"); + return(-ENOMEM); + } + ch->Flags = prop; + ch->maxlen = maxlen; + ch->hw = NULL; + ch->rx_skb = NULL; + ch->tx_skb = NULL; + ch->tx_idx = 0; + ch->next_skb = NULL; + return(0); +} + +int +mISDN_freechannel(channel_t *ch) +{ + if (ch->tx_skb) { + dev_kfree_skb(ch->tx_skb); + ch->tx_skb = NULL; + } + if (ch->rx_skb) { + dev_kfree_skb(ch->rx_skb); + ch->rx_skb = NULL; + } + if (ch->next_skb) { + dev_kfree_skb(ch->next_skb); + ch->next_skb = NULL; + } + kfree(ch->log); + ch->log = NULL; + return(0); +} + +/* need called with HW lock */ +int +mISDN_setpara(channel_t *ch, mISDN_stPara_t *stp) +{ + if (!stp) { // clear parameters + ch->maxlen = 0; + ch->up_headerlen = 0; + return(0); + } + if (stp->up_headerlen) + ch->up_headerlen = stp->up_headerlen; + if (stp->maxdatalen) { + if (ch->maxlen < stp->maxdatalen) { + if (ch->rx_skb) { + struct sk_buff *skb; + + skb = alloc_skb(stp->maxdatalen + + ch->up_headerlen, GFP_ATOMIC); + if (!skb) { + int_errtxt("no skb for %d+%d", stp->maxdatalen, ch->up_headerlen); + return(-ENOMEM); + } + skb_reserve(skb, ch->up_headerlen); + memcpy(skb_put(skb, ch->rx_skb->len), + ch->rx_skb->data, ch->rx_skb->len); + dev_kfree_skb(ch->rx_skb); + ch->rx_skb = skb; + } + } + ch->maxlen = stp->maxdatalen; + } + return(0); +} + +EXPORT_SYMBOL(mISDN_initchannel); +EXPORT_SYMBOL(mISDN_freechannel); +EXPORT_SYMBOL(mISDN_setpara); diff --git a/drivers/isdn/hardware/mISDN/channel.h b/drivers/isdn/hardware/mISDN/channel.h new file mode 100644 index 0000000..033603f --- /dev/null +++ b/drivers/isdn/hardware/mISDN/channel.h @@ -0,0 +1,148 @@ +/* $Id$ + * + * Basic declarations for a mISDN HW channel + * + * Author (c) Karsten Keil + * + * This file is released under the GPLv2 + * + */ + +#ifndef MISDN_CHANNEL_H +#define MISDN_CHANNEL_H +#include +#include +#include +#include "helper.h" +#ifdef MISDN_MEMDEBUG +#include "memdbg.h" +#endif + +#define MAX_DFRAME_LEN_L1 300 +#define MAX_MON_FRAME 32 +#define MAX_LOG_SPACE 2048 +#define MISDN_COPY_SIZE 32 + +/* channel->Flags bit field */ +#define FLG_TX_BUSY 0 // tx_buf in use +#define FLG_TX_NEXT 1 // next_skb in use +#define FLG_L1_BUSY 2 // L1 is permanent busy +#define FLG_USED 5 // channel is in use +#define FLG_ACTIVE 6 // channel is activated +#define FLG_BUSY_TIMER 7 +/* channel type */ +#define FLG_DCHANNEL 8 // channel is D-channel +#define FLG_BCHANNEL 9 // channel is B-channel +#define FLG_ECHANNEL 10 // channel is E-channel +#define FLG_TRANSPARENT 12 // channel use transparent data +#define FLG_HDLC 13 // channel use hdlc data +#define FLG_L2DATA 14 // channel use L2 DATA primitivs +#define FLG_ORIGIN 15 // channel is on origin site +/* channel specific stuff */ +/* arcofi specific */ +#define FLG_ARCOFI_TIMER 16 +#define FLG_ARCOFI_ERROR 17 +/* isar specific */ +#define FLG_INITIALIZED 16 +#define FLG_DLEETX 17 +#define FLG_LASTDLE 18 +#define FLG_FIRST 19 +#define FLG_LASTDATA 20 +#define FLG_NMD_DATA 21 +#define FLG_FTI_RUN 22 +#define FLG_LL_OK 23 +#define FLG_LL_CONN 24 +#define FLG_DTMFSEND 25 + + +#define MSK_INIT_DCHANNEL ((1<Flags) ? DL_DATA : PH_DATA; + if (!skb) + err = mISDN_queue_data(&ch->inst, FLG_MSG_UP, pr, dinfo, 0, NULL, ch->up_headerlen); + else + err = mISDN_queueup_newhead(&ch->inst, 0, pr, dinfo, skb); + if (unlikely(err)) { + int_errtxt("err=%d", err); + if (skb) + dev_kfree_skb(skb); + } +} + +static inline int +channel_senddata(channel_t *ch, int di, struct sk_buff *skb) +{ + /* HW lock must be obtained */ + /* check oversize */ + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__); + return(-EINVAL); + } + if (skb->len > ch->maxlen) { + printk(KERN_WARNING "%s: skb too large(%d/%d)\n", + __FUNCTION__, skb->len, ch->maxlen); + return(-EINVAL); + } + /* check for pending next_skb */ + if (ch->next_skb) { + printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", + __FUNCTION__, skb->len, ch->next_skb->len); + return(-EBUSY); + } + if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { + test_and_set_bit(FLG_TX_NEXT, &ch->Flags); + ch->next_skb = skb; + return(0); + } else { + /* write to fifo */ + ch->tx_skb = skb; + ch->tx_idx = 0; + queue_ch_frame(ch, CONFIRM, di, NULL); + return(skb->len); + } +} + +#endif diff --git a/drivers/isdn/hardware/mISDN/dsp_arith.h b/drivers/isdn/hardware/mISDN/dsp_arith.h new file mode 100644 index 0000000..96541c8 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_arith.h @@ -0,0 +1,347 @@ +#ifndef _ZAPTEL_ARITH_H +#define _ZAPTEL_ARITH_H +/* + * Handy add/subtract functions to operate on chunks of shorts. + * Feel free to add customizations for additional architectures + * + */ + +#ifdef CONFIG_ZAPTEL_MMX +#ifdef ZT_CHUNKSIZE +static inline void __ACSS(volatile short *dst, const short *src) +{ + __asm__ __volatile__ ( + "movq 0(%0), %%mm0;\n" + "movq 0(%1), %%mm1;\n" + "movq 8(%0), %%mm2;\n" + "movq 8(%1), %%mm3;\n" + "paddsw %%mm1, %%mm0;\n" + "paddsw %%mm3, %%mm2;\n" + "movq %%mm0, 0(%0);\n" + "movq %%mm2, 8(%0);\n" + : "=r" (dst) + : "r" (src), "0" (dst) + : "memory" +#if CLOBBERMMX + , "%mm0", "%mm1", "%mm2", "%mm3" +#endif + ); + +} +static inline void __SCSS(volatile short *dst, const short *src) +{ + __asm__ __volatile__ ( + "movq 0(%0), %%mm0;\n" + "movq 0(%1), %%mm1;\n" + "movq 8(%0), %%mm2;\n" + "movq 8(%1), %%mm3;\n" + "psubsw %%mm1, %%mm0;\n" + "psubsw %%mm3, %%mm2;\n" + "movq %%mm0, 0(%0);\n" + "movq %%mm2, 8(%0);\n" + : "=r" (dst) + : "r" (src), "0" (dst) + : "memory" +#if CLOBBERMMX + , "%mm0", "%mm1", "%mm2", "%mm3" +#endif + ); + +} + +#if (ZT_CHUNKSIZE == 8) +#define ACSS(a,b) __ACSS(a,b) +#define SCSS(a,b) __SCSS(a,b) +#elif (ZT_CHUNKSIZE > 8) +static inline void ACSS(volatile short *dst, const short *src) +{ + int x; + for (x=0;x>= 4; + + /* Clear our accumulator, mm4 */ + + /* + + For every set of eight... + + Load 16 coefficients into four registers... + Shift each word right 16 to make them shorts... + Pack the resulting shorts into two registers... + With the coefficients now in mm0 and mm2, load the + history into mm1 and mm3... + Multiply/add mm1 into mm0, and mm3 into mm2... + Add mm2 into mm0 (without saturation, alas). Now we have two half-results. + Accumulate in mm4 (again, without saturation, alas) + */ + __asm__ ( + "pxor %%mm4, %%mm4;\n" + "mov %1, %%edi;\n" + "mov %2, %%esi;\n" + "mov %3, %%ecx;\n" + "1:" + "movq 0(%%edi), %%mm0;\n" + "movq 8(%%edi), %%mm1;\n" + "movq 16(%%edi), %%mm2;\n" + "movq 24(%%edi), %%mm3;\n" + /* can't use 4/5 since 4 is the accumulator for us */ + "movq 32(%%edi), %%mm6;\n" + "movq 40(%%edi), %%mm7;\n" + "psrad $16, %%mm0;\n" + "psrad $16, %%mm1;\n" + "psrad $16, %%mm2;\n" + "psrad $16, %%mm3;\n" + "psrad $16, %%mm6;\n" + "psrad $16, %%mm7;\n" + "packssdw %%mm1, %%mm0;\n" + "packssdw %%mm3, %%mm2;\n" + "packssdw %%mm7, %%mm6;\n" + "movq 0(%%esi), %%mm1;\n" + "movq 8(%%esi), %%mm3;\n" + "movq 16(%%esi), %%mm7;\n" + "pmaddwd %%mm1, %%mm0;\n" + "pmaddwd %%mm3, %%mm2;\n" + "pmaddwd %%mm7, %%mm6;\n" + "paddd %%mm6, %%mm4;\n" + "paddd %%mm2, %%mm4;\n" + "paddd %%mm0, %%mm4;\n" + /* Come back and do for the last few bytes */ + "movq 48(%%edi), %%mm6;\n" + "movq 56(%%edi), %%mm7;\n" + "psrad $16, %%mm6;\n" + "psrad $16, %%mm7;\n" + "packssdw %%mm7, %%mm6;\n" + "movq 24(%%esi), %%mm7;\n" + "pmaddwd %%mm7, %%mm6;\n" + "paddd %%mm6, %%mm4;\n" + "add $64, %%edi;\n" + "add $32, %%esi;\n" + "dec %%ecx;\n" + "jnz 1b;\n" + "movq %%mm4, %%mm0;\n" + "psrlq $32, %%mm0;\n" + "paddd %%mm0, %%mm4;\n" + "movd %%mm4, %0;\n" + : "=r" (sum) + : "r" (coeffs), "r" (hist), "r" (len) + : "%ecx", "%edi", "%esi" + ); + + return sum; +} + +static inline void UPDATE(volatile int *taps, const short *history, const int nsuppr, const int ntaps) +{ + int i; + int correction; + for (i=0;i>= 4; + /* First, load up taps, */ + __asm__ ( + "pxor %%mm4, %%mm4;\n" + "mov %0, %%edi;\n" + "mov %1, %%esi;\n" + "mov %3, %%ecx;\n" + "1:" + "jnz 1b;\n" + "movq %%mm4, %%mm0;\n" + "psrlq $32, %%mm0;\n" + "paddd %%mm0, %%mm4;\n" + "movd %%mm4, %0;\n" + : "=r" (taps), "=r" (taps_short) + : "r" (history), "r" (nsuppr), "r" (ntaps), "0" (taps) + : "%ecx", "%edi", "%esi" + ); +#endif +#if 1 + for (i=0;i> 16; + } +#endif +} + +static inline int CONVOLVE2(const short *coeffs, const short *hist, int len) +{ + int sum; + /* Divide length by 16 */ + len >>= 4; + + /* Clear our accumulator, mm4 */ + + /* + + For every set of eight... + Load in eight coefficients and eight historic samples, multliply add and + accumulate the result + */ + __asm__ ( + "pxor %%mm4, %%mm4;\n" + "mov %1, %%edi;\n" + "mov %2, %%esi;\n" + "mov %3, %%ecx;\n" + "1:" + "movq 0(%%edi), %%mm0;\n" + "movq 8(%%edi), %%mm2;\n" + "movq 0(%%esi), %%mm1;\n" + "movq 8(%%esi), %%mm3;\n" + "pmaddwd %%mm1, %%mm0;\n" + "pmaddwd %%mm3, %%mm2;\n" + "paddd %%mm2, %%mm4;\n" + "paddd %%mm0, %%mm4;\n" + "movq 16(%%edi), %%mm0;\n" + "movq 24(%%edi), %%mm2;\n" + "movq 16(%%esi), %%mm1;\n" + "movq 24(%%esi), %%mm3;\n" + "pmaddwd %%mm1, %%mm0;\n" + "pmaddwd %%mm3, %%mm2;\n" + "paddd %%mm2, %%mm4;\n" + "paddd %%mm0, %%mm4;\n" + "add $32, %%edi;\n" + "add $32, %%esi;\n" + "dec %%ecx;\n" + "jnz 1b;\n" + "movq %%mm4, %%mm0;\n" + "psrlq $32, %%mm0;\n" + "paddd %%mm0, %%mm4;\n" + "movd %%mm4, %0;\n" + : "=r" (sum) + : "r" (coeffs), "r" (hist), "r" (len) + : "%ecx", "%edi", "%esi" + ); + + return sum; +} +static inline short MAX16(const short *y, int len, int *pos) +{ + int k; + short max = 0; + int bestpos = 0; + for (k=0;k 32767) + sum = 32767; + else if (sum < -32768) + sum = -32768; + dst[x] = sum; + } +} + +static inline void SCSS(short *dst, short *src) +{ + int x,sum; + /* Add src to dst with saturation, storing in dst */ + for (x=0;x 32767) + sum = 32767; + else if (sum < -32768) + sum = -32768; + dst[x] = sum; + } +} + +#endif /* ZT_CHUNKSIZE */ + +static inline int CONVOLVE(const int *coeffs, const short *hist, int len) +{ + int x; + int sum = 0; + for (x=0;x> 16) * hist[x]; + return sum; +} + +static inline int CONVOLVE2(const short *coeffs, const short *hist, int len) +{ + int x; + int sum = 0; + for (x=0;x> 16; + } +} + +static inline short MAX16(const short *y, int len, int *pos) +{ + int k; + short max = 0; + int bestpos = 0; + for (k=0;k + * + * Copyright (C) 2001 Steve Underwood + * + * All rights reserved. + * + * 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 of the License, 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. + * + */ + +typedef struct +{ + int32_t gain; + int32_t a1; + int32_t a2; + int32_t b1; + int32_t b2; + + int32_t z1; + int32_t z2; +} biquad2_state_t; + +static inline void biquad2_init (biquad2_state_t *bq, + int32_t gain, + int32_t a1, + int32_t a2, + int32_t b1, + int32_t b2) +{ + bq->gain = gain; + bq->a1 = a1; + bq->a2 = a2; + bq->b1 = b1; + bq->b2 = b2; + + bq->z1 = 0; + bq->z2 = 0; +} +/*- End of function --------------------------------------------------------*/ + +static inline int16_t biquad2 (biquad2_state_t *bq, int16_t sample) +{ + int32_t y; + int32_t z0; + + z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2; + y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2; + + bq->z2 = bq->z1; + bq->z1 = z0 >> 15; + y >>= 15; + return y; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/drivers/isdn/hardware/mISDN/dsp_cancel.c b/drivers/isdn/hardware/mISDN/dsp_cancel.c new file mode 100644 index 0000000..096e609 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_cancel.c @@ -0,0 +1,317 @@ +/* + * + * Simple but fast Echo cancellation for mISDN_dsp. + * + * Copyright Chrisian Richter + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include "layer1.h" +#include "helper.h" +#include "debug.h" +#include "dsp.h" +#include + + +/* + * how this works: + * + * + * + */ +void bchdev_echocancel_chunk(dsp_t* dev, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size); +int bchdev_echocancel_activate(dsp_t* dev, int deftaps, int train); +void bchdev_echocancel_deactivate(dsp_t* dev); + + +void +dsp_cancel_tx(dsp_t *dsp, u8 *data, int len) +{ + if (!dsp ) return ; + if (!data) return; + + if (dsp->txbuflen + len < ECHOCAN_BUFLEN) { + memcpy(&dsp->txbuf[dsp->txbuflen],data,len); + dsp->txbuflen+=len; + } else { + static int i=0; + if(i==4000) { + printk("ECHOCAN: i:%d TXBUF Overflow txbuflen:%d txcancellen:%d\n", i, dsp->txbuflen,len); + i=0; + } + i+=len; + + dsp->txbuflen=0; + } + +} + +void +dsp_cancel_rx(dsp_t *dsp, u8 *data, int len) +{ + if (!dsp ) return ; + if (!data) return; + + if (len <= dsp->txbuflen) { + char tmp[ECHOCAN_BUFLEN]; + + int delta=dsp->txbuflen-len; + + memcpy(tmp,&dsp->txbuf[len],delta); + + kernel_fpu_begin(); + bchdev_echocancel_chunk(dsp, data, dsp->txbuf, len); + kernel_fpu_end(); + + memcpy(dsp->txbuf,tmp,delta); + dsp->txbuflen=delta; + } else { + static int i=0; + if(i==4000) { + printk("ECHOCAN: i:%d TXBUF Underrun txbuflen:%d rxcancellen:%d\n",i,dsp->txbuflen,len); + i=0; + } + i+=len; + } + +} + +int +dsp_cancel_init(dsp_t *dsp, int deftaps, int training, int delay) +{ + + if (!dsp) return -1; + + printk("DSP_CANCEL_INIT called\n"); + + if (delay < 0) + { + printk("Disabling EC\n"); + dsp->cancel_enable = 0; + + dsp->txbuflen=0; + + bchdev_echocancel_deactivate(dsp); + + return(0); + } + + dsp->txbuflen=0; + dsp->rxbuflen=0; + + + bchdev_echocancel_activate(dsp,deftaps, training); + + printk("Enabling EC\n"); + dsp->cancel_enable = 1; + return(0); +} + + + + + +/*****************************************************/ +#define __ECHO_STATE_MUTE (1 << 8) +#define ECHO_STATE_IDLE (0) +#define ECHO_STATE_PRETRAINING (1 | (__ECHO_STATE_MUTE)) +#define ECHO_STATE_STARTTRAINING (2 | (__ECHO_STATE_MUTE)) +#define ECHO_STATE_AWAITINGECHO (3 | (__ECHO_STATE_MUTE)) +#define ECHO_STATE_TRAINING (4 | (__ECHO_STATE_MUTE)) +#define ECHO_STATE_ACTIVE (5) + +#define AMI_MASK 0x55 + + + +/** @return string of given echo cancellation state */ +char* bchdev_echocancel_statestr(uint16_t state) +{ + switch(state) { + case ECHO_STATE_IDLE: + return "idle"; + break; + case ECHO_STATE_PRETRAINING: + return "pre-training"; + break; + case ECHO_STATE_STARTTRAINING: + return "transmit impulse"; + break; + case ECHO_STATE_AWAITINGECHO: + return "awaiting echo"; + break; + case ECHO_STATE_TRAINING: + return "training start"; + break; + case ECHO_STATE_ACTIVE: + return "training finished"; + break; + default: + return "unknown"; + } +} + +/** Changes state of echo cancellation to given state */ +void bchdev_echocancel_setstate(dsp_t* dev, uint16_t state) +{ + char* statestr = bchdev_echocancel_statestr(state); + + printk("bchdev: echo cancel state %d (%s)\n", state & 0xff, statestr); + if (state == ECHO_STATE_ACTIVE) + printk("bchdev: %d taps trained\n", dev->echolastupdate); + dev->echostate = state; +} + +static int buf_size=0; +static int ec_timer=2000; +//static int ec_timer=1000; + + +/** Activates echo cancellation for the given bch_dev, device must have been locked before! */ +int bchdev_echocancel_activate(dsp_t* dev, int deftaps, int training) +{ + int taps; + + if (! dev) return -EINVAL; + + if (dev->ec && dev->ecdis_rd && dev->ecdis_wr) { + // already active + return 0; + } + + if (deftaps>0) { + taps=deftaps; + } else { + taps=128; + } + + + switch (buf_size) { + case 0: taps += 0; break; + case 1: taps += 256-128; break; + case 2: taps += 512-128; break; + default: taps += 1024-128; + } + + if (!dev->ec) dev->ec = echo_can_create(taps, 0); + if (!dev->ec) { + return -ENOMEM; + } + + dev->echolastupdate = 0; + + if (!training) { + dev->echotimer=0; + bchdev_echocancel_setstate(dev, ECHO_STATE_IDLE); + } else { + if (training<10) + training= ec_timer; + + dev->echotimer = training; + bchdev_echocancel_setstate(dev, ECHO_STATE_PRETRAINING); + + } + + if (!dev->ecdis_rd) dev->ecdis_rd = kmalloc(sizeof(echo_can_disable_detector_state_t), GFP_KERNEL); + if (!dev->ecdis_rd) { + kfree(dev->ec); dev->ec = NULL; + return -ENOMEM; + } + echo_can_disable_detector_init(dev->ecdis_rd); + + if (!dev->ecdis_wr) dev->ecdis_wr = kmalloc(sizeof(echo_can_disable_detector_state_t), GFP_KERNEL); + if (!dev->ecdis_wr) { + kfree(dev->ec); dev->ec = NULL; + kfree(dev->ecdis_rd); dev->ecdis_rd = NULL; + return -ENOMEM; + } + echo_can_disable_detector_init(dev->ecdis_wr); + + return 0; +} + +/** Deactivates echo cancellation for the given bch_dev, device must have been locked before! */ +void bchdev_echocancel_deactivate(dsp_t* dev) +{ + if (! dev) return; + + //chan_misdn_log("bchdev: deactivating echo cancellation on port=%04x, chan=%02x\n", dev->stack->port, dev->channel); + + if (dev->ec) echo_can_free(dev->ec); + dev->ec = NULL; + + dev->echolastupdate = 0; + dev->echotimer = 0; + bchdev_echocancel_setstate(dev, ECHO_STATE_IDLE); + + if (dev->ecdis_rd) kfree(dev->ecdis_rd); + dev->ecdis_rd = NULL; + + if (dev->ecdis_wr) kfree(dev->ecdis_wr); + dev->ecdis_wr = NULL; +} + +/** Processes one TX- and one RX-packet with echocancellation */ +void bchdev_echocancel_chunk(dsp_t* dev, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size) +{ + int16_t rxlin, txlin; + uint16_t pos; + + /* Perform echo cancellation on a chunk if requested */ + if (dev->ec) { + if (dev->echostate & __ECHO_STATE_MUTE) { + if (dev->echostate == ECHO_STATE_STARTTRAINING) { + // Transmit impulse now + txchunk[0] = dsp_audio_s16_to_law[16384 & 0xffff]; + memset(txchunk+1, 0, size-1); + bchdev_echocancel_setstate(dev, ECHO_STATE_TRAINING); //AWAITINGECHO); + } else { + // train the echo cancellation + for (pos = 0; pos < size; pos++) { + rxlin = dsp_audio_law_to_s32[rxchunk[pos]]; + txlin = dsp_audio_law_to_s32[txchunk[pos]]; + if (dev->echostate == ECHO_STATE_PRETRAINING) { + if (dev->echotimer <= 0) { + dev->echotimer = 0; + bchdev_echocancel_setstate(dev, ECHO_STATE_STARTTRAINING); + } else { + dev->echotimer--; + } + } + if ((dev->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) { + dev->echolastupdate = 0; + bchdev_echocancel_setstate(dev, ECHO_STATE_TRAINING); + } + if (dev->echostate == ECHO_STATE_TRAINING) { + if (echo_can_traintap(dev->ec, dev->echolastupdate++, rxlin)) { + bchdev_echocancel_setstate(dev, ECHO_STATE_ACTIVE); + } + } + + rxchunk[pos] = dsp_silence; + txchunk[pos] = dsp_silence; + } + } + } else { + for (pos = 0; pos < size; pos++) { + rxlin = dsp_audio_law_to_s32[rxchunk[pos]]; + txlin = dsp_audio_law_to_s32[txchunk[pos]]; + + if (echo_can_disable_detector_update(dev->ecdis_rd, rxlin) || + echo_can_disable_detector_update(dev->ecdis_wr, txlin)) { + bchdev_echocancel_deactivate(dev); + printk("EC: Disable tone detected\n"); + return ; + } else { + rxlin = echo_can_update(dev->ec, txlin, rxlin); + rxchunk[pos] = dsp_audio_s16_to_law[rxlin & 0xffff]; + } + } + } + } +} + +/******************************************************/ diff --git a/drivers/isdn/hardware/mISDN/dsp_ecdis.h b/drivers/isdn/hardware/mISDN/dsp_ecdis.h new file mode 100644 index 0000000..9bc0cfa --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_ecdis.h @@ -0,0 +1,118 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * ec_disable_detector.h - A detector which should eventually meet the + * G.164/G.165 requirements for detecting the + * 2100Hz echo cancellor disable tone. + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * All rights reserved. + * + * 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 of the License, 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. + * + */ + +#include "dsp_biquad.h" + +typedef struct +{ + biquad2_state_t notch; + int notch_level; + int channel_level; + int tone_present; + int tone_cycle_duration; + int good_cycles; + int hit; +} echo_can_disable_detector_state_t; + + +#define FALSE 0 +#define TRUE (!FALSE) + +static inline void echo_can_disable_detector_init (echo_can_disable_detector_state_t *det) +{ + /* Elliptic notch */ + /* This is actually centred at 2095Hz, but gets the balance we want, due + to the asymmetric walls of the notch */ + biquad2_init (&det->notch, + (int32_t) (-0.7600000*32768.0), + (int32_t) (-0.1183852*32768.0), + (int32_t) (-0.5104039*32768.0), + (int32_t) ( 0.1567596*32768.0), + (int32_t) ( 1.0000000*32768.0)); + + det->channel_level = 0; + det->notch_level = 0; + det->tone_present = FALSE; + det->tone_cycle_duration = 0; + det->good_cycles = 0; + det->hit = 0; +} +/*- End of function --------------------------------------------------------*/ + +static inline int echo_can_disable_detector_update (echo_can_disable_detector_state_t *det, + int16_t amp) +{ + int16_t notched; + + notched = biquad2 (&det->notch, amp); + /* Estimate the overall energy in the channel, and the energy in + the notch (i.e. overall channel energy - tone energy => noise). + Use abs instead of multiply for speed (is it really faster?). + Damp the overall energy a little more for a stable result. + Damp the notch energy a little less, so we don't damp out the + blip every time the phase reverses */ + det->channel_level += ((abs(amp) - det->channel_level) >> 5); + det->notch_level += ((abs(notched) - det->notch_level) >> 4); + if (det->channel_level > 280) + { + /* There is adequate energy in the channel. Is it mostly at 2100Hz? */ + if (det->notch_level*6 < det->channel_level) + { + /* The notch says yes, so we have the tone. */ + if (!det->tone_present) + { + /* Do we get a kick every 450+-25ms? */ + if (det->tone_cycle_duration >= 425*8 + && + det->tone_cycle_duration <= 475*8) + { + det->good_cycles++; + if (det->good_cycles > 2) + det->hit = TRUE; + } + det->tone_cycle_duration = 0; + } + det->tone_present = TRUE; + } + else + { + det->tone_present = FALSE; + } + det->tone_cycle_duration++; + } + else + { + det->tone_present = FALSE; + det->tone_cycle_duration = 0; + det->good_cycles = 0; + } + return det->hit; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/drivers/isdn/hardware/mISDN/dsp_kb1ec.h b/drivers/isdn/hardware/mISDN/dsp_kb1ec.h new file mode 100644 index 0000000..6d18873 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_kb1ec.h @@ -0,0 +1,574 @@ +/* + * ECHO_CAN_KB1 + * + * by Kris Boutilier + * + * Based upon mech2.h + * + * Copyright (C) 2002, Digium, Inc. + * + * This program is free software and may be used and + * distributed according to the terms of the GNU + * General Public License, incorporated herein by + * reference. + * + * Additional background on the techniques used in this code can be found in: + * + * Messerschmitt, David; Hedberg, David; Cole, Christopher; Haoui, Amine; + * Winship, Peter; "Digital Voice Echo Canceller with a TMS32020," + * in Digital Signal Processing Applications with the TMS320 Family, + * pp. 415-437, Texas Instruments, Inc., 1986. + * + * A pdf of which is available by searching on the document title at http://www.ti.com/ + * + */ + +#ifndef _MARK2_ECHO_H +#define _MARK2_ECHO_H + +#define EC_TYPE "KB1" + +#ifdef __KERNEL__ +#include +#include +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#else +#include +#include +#include +#include +#include +#define MALLOC(a) malloc(a) +#define FREE(a) free(a) +#endif + +/* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */ +/* #define MEC2_STATS 4000 */ + +/* Uncomment to generate per-sample statistics - this will severely degrade system performance and audio quality */ +/* #define MEC2_STATS_DETAILED */ + +/* Get optimized routines for math */ +#include "dsp_arith.h" + +/* Bring in definitions for the various constants and thresholds */ +#include "dsp_kb1ec_const.h" + +#ifndef NULL +#define NULL 0 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +/* Generic circular buffer definition */ +typedef struct { + /* Pointer to the relative 'start' of the buffer */ + int idx_d; + /* The absolute size of the buffer */ + int size_d; + /* The actual sample - twice as large as we need, however we do store values at idx_d and idx_d+size_d */ + short *buf_d; +} echo_can_cb_s; + +/* Echo canceller definition */ +struct echo_can_state { + /* an arbitrary ID for this echo can - this really should be settable from the calling channel... */ + int id; + + /* absolute time - aka. sample number index - essentially the number of samples since this can was init'ed */ + int i_d; + + /* Pre-computed constants */ + /* ---------------------- */ + /* Number of filter coefficents */ + int N_d; + /* Rate of adaptation of filter */ + int beta2_i; + + /* Accumulators for power computations */ + /* ----------------------------------- */ + /* reference signal power estimate - aka. Average absolute value of y(k) */ + int Ly_i; + /* ... */ + int Lu_i; + + /* Accumulators for signal detectors */ + /* --------------------------------- */ + /* Power estimate of the recent past of the near-end hybrid signal - aka. Short-time average of: 2 x |s(i)| */ + int s_tilde_i; + /* Power estimate of the recent past of the far-end receive signal - aka. Short-time average of: |y(i)| */ + int y_tilde_i; + + /* Near end speech detection counter - stores Hangover counter time remaining, in samples */ + int HCNTR_d; + + /* Circular buffers and coefficients */ + /* --------------------------------- */ + /* ... */ + int *a_i; + /* ... */ + short *a_s; + /* Reference samples of far-end receive signal */ + echo_can_cb_s y_s; + /* Reference samples of near-end signal */ + echo_can_cb_s s_s; + /* Reference samples of near-end signal minus echo estimate */ + echo_can_cb_s u_s; + /* Reference samples of far-end receive signal used to calculate short-time average */ + echo_can_cb_s y_tilde_s; + + /* Peak far-end receive signal */ + /* --------------------------- */ + /* Highest y_tilde value in the sample buffer */ + short max_y_tilde; + /* Index of the sample containing the max_y_tilde value */ + int max_y_tilde_pos; + +#ifdef MEC2_STATS + /* Storage for performance statistics */ + int cntr_nearend_speech_frames; + int cntr_residualcorrected_frames; + int cntr_residualcorrected_framesskipped; + int cntr_coeff_updates; + int cntr_coeff_missedupdates; + + int avg_Lu_i_toolow; + int avg_Lu_i_ok; +#endif + +}; + +static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where) +{ + cb->buf_d = (short *)where; + cb->idx_d = 0; + cb->size_d = len; +} + +static inline void add_cc_s(echo_can_cb_s *cb, short newval) +{ + /* Can't use modulus because N+M isn't a power of two (generally) */ + cb->idx_d--; + if (cb->idx_d < (int)0) + /* Whoops - the pointer to the 'start' wrapped around so reset it to the top of the buffer */ + cb->idx_d += cb->size_d; + + /* Load two copies into memory */ + cb->buf_d[cb->idx_d] = newval; + cb->buf_d[cb->idx_d + cb->size_d] = newval; +} + +static inline short get_cc_s(echo_can_cb_s *cb, int pos) +{ + /* Load two copies into memory */ + return cb->buf_d[cb->idx_d + pos]; +} + +static inline void init_cc(struct echo_can_state *ec, int N, int maxy, int maxu) +{ + + void *ptr = ec; + unsigned long tmp; + /* Double-word align past end of state */ + ptr += sizeof(struct echo_can_state); + tmp = (unsigned long)ptr; + tmp += 3; + tmp &= ~3L; + ptr = (void *)tmp; + + /* Reset parameters */ + ec->N_d = N; + ec->beta2_i = DEFAULT_BETA1_I; + + /* Allocate coefficient memory */ + ec->a_i = ptr; + ptr += (sizeof(int) * ec->N_d); + ec->a_s = ptr; + ptr += (sizeof(short) * ec->N_d); + + /* Reset Y circular buffer (short version) */ + init_cb_s(&ec->y_s, maxy, ptr); + ptr += (sizeof(short) * (maxy) * 2); + + /* Reset Sigma circular buffer (short version for FIR filter) */ + init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr); + ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2); + + init_cb_s(&ec->u_s, maxu, ptr); + ptr += (sizeof(short) * maxu * 2); + + /* Allocate a buffer for the reference signal power computation */ + init_cb_s(&ec->y_tilde_s, ec->N_d, ptr); + + /* Reset the absolute time index */ + ec->i_d = (int)0; + + /* Reset the power computations (for y and u) */ + ec->Ly_i = DEFAULT_CUTOFF_I; + ec->Lu_i = DEFAULT_CUTOFF_I; + +#ifdef MEC2_STATS + /* set the identity */ + ec->id = (int)&ptr; + + /* Reset performance stats */ + ec->cntr_nearend_speech_frames = (int)0; + ec->cntr_residualcorrected_frames = (int)0; + ec->cntr_residualcorrected_framesskipped = (int)0; + ec->cntr_coeff_updates = (int)0; + ec->cntr_coeff_missedupdates = (int)0; + + ec->avg_Lu_i_toolow = (int)0; + ec->avg_Lu_i_ok = (int)0; +#endif + + /* Reset the near-end speech detector */ + ec->s_tilde_i = (int)0; + ec->y_tilde_i = (int)0; + ec->HCNTR_d = (int)0; + +} + +static inline void echo_can_free(struct echo_can_state *ec) +{ + FREE(ec); +} + +static inline short echo_can_update(struct echo_can_state *ec, short iref, short isig) +{ + + /* Declare local variables that are used more than once */ + /* ... */ + int k; + /* ... */ + int rs; + /* ... */ + short u; + /* ... */ + int Py_i; + /* ... */ + int two_beta_i; + + /* flow A on pg. 428 */ + /* eq. (16): high-pass filter the input to generate the next value; + * push the current value into the circular buffer + * + * sdc_im1_d = sdc_d; + * sdc_d = sig; + * s_i_d = sdc_d; + * s_d = s_i_d; + * s_i_d = (float)(1.0 - gamma_d) * s_i_d + * + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d); + */ + + /* Update the Far-end receive signal circular buffers and accumulators */ + /* ------------------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I; + /* Add the new sample to the power estimate accumulator */ + ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I; + /* Push a copy of the new sample into its circular buffer */ + add_cc_s(&ec->y_s, iref); + + + /* eq. (2): compute r in fixed-point */ + rs = CONVOLVE2(ec->a_s, + ec->y_s.buf_d + ec->y_s.idx_d, + ec->N_d); + rs >>= 15; + + /* eq. (3): compute the output value (see figure 3) and the error + * note: the error is the same as the output signal when near-end + * speech is not present + */ + u = isig - rs; + + /* Push a copy of the output value sample into its circular buffer */ + add_cc_s(&ec->u_s, u); + + + /* Update the Near-end hybrid signal circular buffers and accumulators */ + /* ------------------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 )); + /* Add the new sample to the power estimate accumulator */ + ec->s_tilde_i += abs(isig); + /* Push a copy of the new sample into it's circular buffer */ + add_cc_s(&ec->s_s, isig); + + + /* Push a copy of the current short-time average of the far-end receive signal into it's circular buffer */ + add_cc_s(&ec->y_tilde_s, ec->y_tilde_i); + + /* flow B on pg. 428 */ + + /* If the hangover timer isn't running then compute the new convergence factor, otherwise set Py_i to 32768 */ + if (!ec->HCNTR_d) { + Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I); + Py_i >>= 15; + } else { + Py_i = (1 << 15); + } + +#if 0 + /* Vary rate of adaptation depending on position in the file + * Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech + * has begun of the file to allow the echo cancellor to estimate the + * channel accurately + * Still needs conversion! + */ + + if (ec->start_speech_d != 0 ){ + if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){ + ec->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - ec->start_speech_d))); + } + } else { + ec->beta2_d = DEFAULT_BETA1; + } +#endif + + /* Fixed point, inverted */ + ec->beta2_i = DEFAULT_BETA1_I; + + /* Fixed point version, inverted */ + two_beta_i = (ec->beta2_i * Py_i) >> 15; + if (!two_beta_i) + two_beta_i++; + + /* Update the Suppressed signal power estimate accumulator */ + /* ------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ; + /* Add the new sample to the power estimate accumulator */ + ec->Lu_i += abs(u); + + /* Update the Far-end reference signal power estimate accumulator */ + /* -------------------------------------------------------------- */ + /* eq. (10): update power estimate of the reference */ + /* Delete the oldest sample from the power estimate accumulator */ + ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ; + /* Add the new sample to the power estimate accumulator */ + ec->Ly_i += abs(iref); + + if (ec->Ly_i < DEFAULT_CUTOFF_I) + ec->Ly_i = DEFAULT_CUTOFF_I; + + + /* Update the Peak far-end receive signal detected */ + /* ----------------------------------------------- */ + if (ec->y_tilde_i > ec->max_y_tilde) { + /* New highest y_tilde with full life */ + ec->max_y_tilde = ec->y_tilde_i; + ec->max_y_tilde_pos = ec->N_d - 1; + } else if (--ec->max_y_tilde_pos < 0) { + /* Time to find new max y tilde... */ + ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos); + } + + /* Determine if near end speech was detected in this sample */ + /* -------------------------------------------------------- */ + if (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde) + && (ec->max_y_tilde > 0)) { + /* Then start the Hangover counter */ + ec->HCNTR_d = DEFAULT_HANGT; +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde); +#endif +#ifdef MEC2_STATS + ++ec->cntr_nearend_speech_frames; +#endif + } else if (ec->HCNTR_d > (int)0) { + /* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */ +#ifdef MEC2_STATS + ++ec->cntr_nearend_speech_frames; +#endif + ec->HCNTR_d--; + } + + /* Update coefficients if no near-end speech in this sample (ie. HCNTR_d = 0) + * and we have enough signal to bother trying to update. + * -------------------------------------------------------------------------- + */ + if (!ec->HCNTR_d && /* no near-end speech present */ + !(ec->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */ + if (ec->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */ + /* so loop over all the filter coefficients */ +#ifdef MEC2_STATS_DETAILED + printk( KERN_INFO "updating coefficients with: ec->Lu_i %9d\n", ec->Lu_i); +#endif +#ifdef MEC2_STATS + ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i; + ++ec->cntr_coeff_updates; +#endif + for (k=0; k < ec->N_d; k++) { + /* eq. (7): compute an expectation over M_d samples */ + int grad2; + grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d, + ec->y_s.buf_d + ec->y_s.idx_d + k, + DEFAULT_M); + /* eq. (7): update the coefficient */ + ec->a_i[k] += grad2 / two_beta_i; + ec->a_s[k] = ec->a_i[k] >> 16; + } + } else { +#ifdef MEC2_STATS_DETAILED + printk( KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I); +#endif +#ifdef MEC2_STATS + ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i; + ++ec->cntr_coeff_missedupdates; +#endif + } + } + + /* paragraph below eq. (15): if no near-end speech in the sample and + * the reference signal power estimate > cutoff threshold + * then perform residual error suppression + */ +#ifdef MEC2_STATS_DETAILED + if (ec->HCNTR_d == 0) + printk( KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); +#endif + +#ifndef NO_ECHO_SUPPRESSOR +#ifdef AGGRESSIVE_SUPPRESSOR + if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) { + for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); + } +#ifdef MEC2_STATS_DETAILED + printk( KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); +#endif +#ifdef MEC2_STATS + ++ec->cntr_residualcorrected_frames; +#endif + } +#else + if (ec->HCNTR_d == 0) { + if ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I) { + for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); + } +#ifdef MEC2_STATS_DETAILED + printk( KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); +#endif +#ifdef MEC2_STATS + ++ec->cntr_residualcorrected_frames; +#endif + } +#ifdef MEC2_STATS + else { + ++ec->cntr_residualcorrected_framesskipped; + } +#endif + } +#endif +#endif + +#if 0 + /* This will generate a non-linear supression factor, once converted */ + if ((ec->HCNTR_d == 0) && + ((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) && + (ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) { + suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(ec->Lu_d/ec->Ly_d) + - SUPPR_CEIL / (float)(SUPPR_FLOOR - SUPPR_CEIL); + u_suppr = pow(10.0, (suppr_factor) * RES_SUPR_FACTOR / 10.0) * u_suppr; + } +#endif + +#ifdef MEC2_STATS + /* Periodically dump performance stats */ + if ((ec->i_d % MEC2_STATS) == 0) { + /* make sure to avoid div0's! */ + if (ec->cntr_coeff_missedupdates > 0) + ec->avg_Lu_i_toolow = (int)(ec->avg_Lu_i_toolow / ec->cntr_coeff_missedupdates); + else + ec->avg_Lu_i_toolow = -1; + + if (ec->cntr_coeff_updates > 0) + ec->avg_Lu_i_ok = (ec->avg_Lu_i_ok / ec->cntr_coeff_updates); + else + ec->avg_Lu_i_ok = -1; + + printk( KERN_INFO "%d: Near end speech: %5d Residuals corrected/skipped: %5d/%5d Coefficients updated ok/low sig: %3d/%3d Lu_i avg ok/low sig %6d/%5d\n", + ec->id, + ec->cntr_nearend_speech_frames, + ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped, + ec->cntr_coeff_updates, ec->cntr_coeff_missedupdates, + ec->avg_Lu_i_ok, ec->avg_Lu_i_toolow); + + ec->cntr_nearend_speech_frames = 0; + ec->cntr_residualcorrected_frames = 0; + ec->cntr_residualcorrected_framesskipped = 0; + ec->cntr_coeff_updates = 0; + ec->cntr_coeff_missedupdates = 0; + ec->avg_Lu_i_ok = 0; + ec->avg_Lu_i_toolow = 0; + } +#endif + + /* Increment the sample index and return the corrected sample */ + ec->i_d++; + return u; +} + +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) +{ + struct echo_can_state *ec; + int maxy; + int maxu; + maxy = len + DEFAULT_M; + maxu = DEFAULT_M; + if (maxy < (1 << DEFAULT_ALPHA_YT_I)) + maxy = (1 << DEFAULT_ALPHA_YT_I); + if (maxy < (1 << DEFAULT_SIGMA_LY_I)) + maxy = (1 << DEFAULT_SIGMA_LY_I); + if (maxu < (1 << DEFAULT_SIGMA_LU_I)) + maxu = (1 << DEFAULT_SIGMA_LU_I); + ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + 2 * sizeof(short) * (maxy) + /* y_s */ + 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ + 2 * sizeof(short) * (maxu) + /* u_s */ + 2 * sizeof(short) * len); /* y_tilde_s */ + if (ec) { + memset(ec, 0, sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + 2 * sizeof(short) * (maxy) + /* y_s */ + 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ + 2 * sizeof(short) * (maxu) + /* u_s */ + 2 * sizeof(short) * len); /* y_tilde_s */ + init_cc(ec, len, maxy, maxu); + } + return ec; +} + +static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + /* Set the hangover counter to the length of the can to + * avoid adjustments occuring immediately after initial forced training + */ + ec->HCNTR_d = ec->N_d << 1; + + if (pos >= ec->N_d) + return 1; + + ec->a_i[pos] = val << 17; + ec->a_s[pos] = val << 1; + + if (++pos >= ec->N_d) + return 1; + + return 0; +} + +#endif diff --git a/drivers/isdn/hardware/mISDN/dsp_kb1ec_const.h b/drivers/isdn/hardware/mISDN/dsp_kb1ec_const.h new file mode 100644 index 0000000..fb2daef --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_kb1ec_const.h @@ -0,0 +1,81 @@ +/* + Important constants for tuning kb1 echo can + */ +#ifndef _MEC2_CONST_H +#define _MEC2_CONST_H + + +/* Convergence (aka. adaptation) speed -- higher means slower */ +#define DEFAULT_BETA1_I 2048 + +/* Constants for various power computations */ +#define DEFAULT_SIGMA_LY_I 7 +#define DEFAULT_SIGMA_LU_I 7 +#define DEFAULT_ALPHA_ST_I 5 /* near-end speech detection sensitivity factor */ +#define DEFAULT_ALPHA_YT_I 5 + +#define DEFAULT_CUTOFF_I 128 + +/* Define the near-end speech hangover counter: if near-end speech + * is declared, hcntr is set equal to hangt (see pg. 432) + */ +#define DEFAULT_HANGT 600 /* in samples, so 600 samples = 75ms */ + +/* define the residual error suppression threshold */ +#define DEFAULT_SUPPR_I 16 /* 16 = -24db */ + +/* This is the minimum reference signal power estimate level + * that will result in filter adaptation. + * If this is too low then background noise will cause the filter + * coefficients to constantly be updated. + */ +#define MIN_UPDATE_THRESH_I 4096 + +/* The number of samples used to update coefficients using the + * the block update method (M). It should be related back to the + * length of the echo can. + * ie. it only updates coefficients when (sample number MOD default_m) = 0 + * + * Getting this wrong may cause an oops. Consider yourself warned! + */ +#define DEFAULT_M 16 /* every 16th sample */ + +/* If AGGRESSIVE supression is enabled, then we start cancelling residual + * echos again even while there is potentially the very end of a near-side + * signal present. + * This defines how many samples of DEFAULT_HANGT can remain before we + * kick back in + */ +#define AGGRESSIVE_HCNTR 160 /* in samples, so 160 samples = 20ms */ + +/* This knob controls the number of passes the residual echo supression + * algorithm makes. + */ +#ifdef AGGRESSIVE_SUPPRESSOR + #define RESIDUAL_SUPRESSION_PASSES 2 +#else + #define RESIDUAL_SUPRESSION_PASSES 1 +#endif + + +/***************************************************************/ +/* The following knobs are not implemented in the current code */ + +/* we need a dynamic level of suppression varying with the ratio of the + power of the echo to the power of the reference signal this is + done so that we have a smoother background. + we have a higher suppression when the power ratio is closer to + suppr_ceil and reduces logarithmically as we approach suppr_floor. + */ +#define SUPPR_FLOOR -64 +#define SUPPR_CEIL -24 + +/* in a second departure, we calculate the residual error suppression + * as a percentage of the reference signal energy level. The threshold + * is defined in terms of dB below the reference signal. + */ +#define RES_SUPR_FACTOR -20 + + +#endif /* _MEC2_CONST_H */ + diff --git a/drivers/isdn/hardware/mISDN/dsp_mec2.h b/drivers/isdn/hardware/mISDN/dsp_mec2.h new file mode 100644 index 0000000..45dc3d0 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_mec2.h @@ -0,0 +1,424 @@ +/* + * Mark's Second Echo Canceller + * + * Copyright (C) 2002, Digium, Inc. + * + * This program is free software and may be used and + * distributed according to the terms of the GNU + * General Public License, incorporated herein by + * reference. + * + */ +#ifndef _MARK2_ECHO_H +#define _MARK2_ECHO_H + +#define EC_TYPE "MARK2" + +#ifdef __KERNEL__ +#include +#include +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#else +#include +#include +#include +#include +#include +#define MALLOC(a) malloc(a) +#define FREE(a) free(a) +#endif + +/* Get optimized routines for math */ +#include "dsp_arith.h" + +#ifndef NULL +#define NULL 0 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#include "dsp_mec2_const.h" + +/* Circular buffer definition */ +typedef struct { + int idx_d; + int size_d; + short *buf_d; /* Twice as large as we need */ +} echo_can_cb_s; + +// class definition +// +struct echo_can_state { + /* Echo canceller definition */ + + /* absolute time */ + int i_d; + + /* pre-computed constants */ + + int N_d; + int beta2_i; + + // declare accumulators for power computations + // + int Ly_i; + int Lu_i; + + // declare an accumulator for the near-end signal detector + // + int s_tilde_i; + int HCNTR_d; + + // circular buffers and coefficients + // + int *a_i; + short *a_s; + echo_can_cb_s y_s; + echo_can_cb_s s_s; + echo_can_cb_s u_s; + echo_can_cb_s y_tilde_s; + int y_tilde_i; + + /* Max memory */ + short max_y_tilde; + int max_y_tilde_pos; + +}; + +static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where) +{ + cb->buf_d = (short *)where; + cb->idx_d = 0; + cb->size_d = len; +} + +static inline void add_cc_s(echo_can_cb_s *cb, short newval) +{ + /* Can't use modulus because N+M isn't a power of two (generally) */ + cb->idx_d--; + if (cb->idx_d < (int)0) + {cb->idx_d += cb->size_d;} + /* Load two copies into memory */ + cb->buf_d[cb->idx_d] = newval; + cb->buf_d[cb->idx_d + cb->size_d] = newval; +} + +static inline short get_cc_s(echo_can_cb_s *cb, int pos) +{ + /* Load two copies into memory */ + return cb->buf_d[cb->idx_d + pos]; +} + +static inline void init_cc(struct echo_can_state *ec, int N, int maxy, int maxu) { + + void *ptr = ec; + unsigned long tmp; + /* double-word align past end of state */ + ptr += sizeof(struct echo_can_state); + tmp = (unsigned long)ptr; + tmp += 3; + tmp &= ~3L; + ptr = (void *)tmp; + + // reset parameters + // + ec->N_d = N; + ec->beta2_i = DEFAULT_BETA1_I; + + // allocate coefficient memory + // + ec->a_i = ptr; + ptr += (sizeof(int) * ec->N_d); + ec->a_s = ptr; + ptr += (sizeof(short) * ec->N_d); + + /* Reset Y circular buffer (short version) */ + init_cb_s(&ec->y_s, maxy, ptr); + ptr += (sizeof(short) * (maxy) * 2); + + /* Reset Sig circular buffer (short version for FIR filter) */ + init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr); + ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2); + + init_cb_s(&ec->u_s, maxu, ptr); + ptr += (sizeof(short) * maxu * 2); + + // allocate a buffer for the reference signal power computation + // + init_cb_s(&ec->y_tilde_s, ec->N_d, ptr); + + + // reset absolute time + // + ec->i_d = (int)0; + + // reset the power computations (for y and u) + // + ec->Ly_i = DEFAULT_CUTOFF_I; + ec->Lu_i = DEFAULT_CUTOFF_I; + + // reset the near-end speech detector + // + ec->s_tilde_i = 0; + ec->y_tilde_i = 0; + ec->HCNTR_d = (int)0; + + // exit gracefully + // +} + +static inline void echo_can_free(struct echo_can_state *ec) +{ + FREE(ec); +} + +static inline short echo_can_update(struct echo_can_state *ec, short iref, short isig) { + + /* declare local variables that are used more than once + */ + int k; + int rs; + short u; + int Py_i; + int two_beta_i; + + /*************************************************************************** + // + // flow A on pg. 428 + // + ***************************************************************************/ + + /* eq. (16): high-pass filter the input to generate the next value; + // push the current value into the circular buffer + // + // sdc_im1_d = sdc_d; + // sdc_d = sig; + // s_i_d = sdc_d; + // s_d = s_i_d; + // s_i_d = (float)(1.0 - gamma_d) * s_i_d + + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d); */ + + + /* Delete last sample from power estimate */ + ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I; + /* push the reference data onto the circular buffer */ + add_cc_s(&ec->y_s, iref); + + /* eq. (2): compute r in fixed-point */ + rs = CONVOLVE2(ec->a_s, ec->y_s.buf_d + ec->y_s.idx_d, ec->N_d); + rs >>= 15; + + /* eq. (3): compute the output value (see figure 3) and the error + // note: the error is the same as the output signal when near-end + // speech is not present + */ + u = isig - rs; + + add_cc_s(&ec->u_s, u); + + + + /* Delete oldest part of received s_tilde */ + ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 )); + + /* push the signal on the circular buffer, too */ + add_cc_s(&ec->s_s, isig); + ec->s_tilde_i += abs(isig); + ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_YT_I; + + /* Add to our list of recent y_tilde's */ + add_cc_s(&ec->y_tilde_s, ec->y_tilde_i); + + /**************************************************************************** + // + // flow B on pg. 428 + // + ****************************************************************************/ + + /* compute the new convergence factor + */ + if (!ec->HCNTR_d) { + Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I); + Py_i >>= 15; + } else { + Py_i = (1 << 15); + } + +#if 0 + printf("Py: %e, Py_i: %e\n", Py, Py_i * AMPL_SCALE_1); +#endif + + /* Vary rate of adaptation depending on position in the file + // Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech + // has begun of the file to allow the echo cancellor to estimate the + // channel accurately + */ +#if 0 + if (ec->start_speech_d != 0 ){ + if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){ + ec->beta2_d = max_cc_float(MIN_BETA, + DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) - + DEFAULT_T0 - + ec->start_speech_d))); + } + } + else {ec->beta2_d = DEFAULT_BETA1;} +#endif + + ec->beta2_i = DEFAULT_BETA1_I; /* Fixed point, inverted */ + + two_beta_i = (ec->beta2_i * Py_i) >> 15; /* Fixed point version, inverted */ + if (!two_beta_i) + two_beta_i++; + + /* Update Lu_i (Suppressed power estimate) */ + ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ; + ec->Lu_i += abs(u); + + /* eq. (10): update power estimate of the reference + */ + ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ; + ec->Ly_i += abs(iref); + + if (ec->Ly_i < DEFAULT_CUTOFF_I) + ec->Ly_i = DEFAULT_CUTOFF_I; + +#if 0 + printf("Float: %e, Int: %e\n", ec->Ly_d, (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * AMPL_SCALE_1); +#endif + + if (ec->y_tilde_i > ec->max_y_tilde) { + /* New highest y_tilde with full life */ + ec->max_y_tilde = ec->y_tilde_i; + ec->max_y_tilde_pos = ec->N_d - 1; + } else if (--ec->max_y_tilde_pos < 0) { + /* Time to find new max y tilde... */ + ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos); + } + + if ((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde) + { + ec->HCNTR_d = DEFAULT_HANGT; + } + else if (ec->HCNTR_d > (int)0) + { + ec->HCNTR_d--; + } + + /* update coefficients if no near-end speech and we have enough signal + * to bother trying to update. + */ + if (!ec->HCNTR_d && !(ec->i_d % DEFAULT_M) && + (ec->Lu_i > MIN_UPDATE_THRESH_I)) { + // loop over all filter coefficients + // + for (k=0; kN_d; k++) { + + // eq. (7): compute an expectation over M_d samples + // + int grad2; + grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d, + ec->y_s.buf_d + ec->y_s.idx_d + k, DEFAULT_M); + // eq. (7): update the coefficient + // + ec->a_i[k] += grad2 / two_beta_i; + ec->a_s[k] = ec->a_i[k] >> 16; + } + } + + /* paragraph below eq. (15): if no near-end speech, + // check for residual error suppression + */ +#ifndef NO_ECHO_SUPPRESSOR +#ifdef AGGRESSIVE_SUPPRESSOR +#ifdef AGGRESSIVE_TIMELIMIT /* This allows the aggressive suppressor to turn off after set amount of time */ + if (ec->i_d > AGGRESSIVE_TIMELIMIT ) { + if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); + } + } + else { +#endif + if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); + } +#ifdef AGGRESSIVE_TIMELIMIT + } +#endif +#else + if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); + } +#endif +#endif + +#if 0 + if ((ec->HCNTR_d == 0) && ((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) && + (ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) { + suppr_factor = (10/(float)(SUPPR_FLOOR-SUPPR_CEIL))*log(ec->Lu_d/ec->Ly_d) + - SUPPR_CEIL/(float)(SUPPR_FLOOR - SUPPR_CEIL); + + u_suppr = pow(10.0,(suppr_factor)*RES_SUPR_FACTOR/10.0)*u_suppr; + + } +#endif + ec->i_d++; + return u; +} + +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) +{ + struct echo_can_state *ec; + int maxy; + int maxu; + maxy = len + DEFAULT_M; + maxu = DEFAULT_M; + if (maxy < (1 << DEFAULT_ALPHA_YT_I)) + maxy = (1 << DEFAULT_ALPHA_YT_I); + if (maxy < (1 << DEFAULT_SIGMA_LY_I)) + maxy = (1 << DEFAULT_SIGMA_LY_I); + if (maxu < (1 << DEFAULT_SIGMA_LU_I)) + maxu = (1 << DEFAULT_SIGMA_LU_I); + ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + 2 * sizeof(short) * (maxy) + /* y_s */ + 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ + 2 * sizeof(short) * (maxu) + /* u_s */ + 2 * sizeof(short) * len); /* y_tilde_s */ + if (ec) { + memset(ec, 0, sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + 2 * sizeof(short) * (maxy) + /* y_s */ + 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ + 2 * sizeof(short) * (maxu) + /* u_s */ + 2 * sizeof(short) * len); /* y_tilde_s */ + init_cc(ec, len, maxy, maxu); + } + return ec; +} + +static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + /* Reset hang counter to avoid adjustments after + initial forced training */ + ec->HCNTR_d = ec->N_d << 1; + if (pos >= ec->N_d) + return 1; + ec->a_i[pos] = val << 17; + ec->a_s[pos] = val << 1; + if (++pos >= ec->N_d) + return 1; + return 0; +} + +#endif diff --git a/drivers/isdn/hardware/mISDN/dsp_mec2_const.h b/drivers/isdn/hardware/mISDN/dsp_mec2_const.h new file mode 100644 index 0000000..4c7e8c9 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_mec2_const.h @@ -0,0 +1,28 @@ +/* + Important constants for tuning mec2 echo can + */ +#ifndef _MEC2_CONST_H +#define _MEC2_CONST_H + + +/* Convergence speed -- higher means slower */ +#define DEFAULT_BETA1_I 2048 +#define DEFAULT_SIGMA_LY_I 7 +#define DEFAULT_SIGMA_LU_I 7 +#define DEFAULT_ALPHA_ST_I 5 +#define DEFAULT_ALPHA_YT_I 5 +#define DEFAULT_CUTOFF_I 128 +#define DEFAULT_HANGT 600 +#define DEFAULT_SUPPR_I 16 +#define MIN_UPDATE_THRESH_I 4096 +#define DEFAULT_M 16 +#define SUPPR_FLOOR -64 +#define SUPPR_CEIL -24 +#define RES_SUPR_FACTOR -20 +#define AGGRESSIVE_HCNTR 160 /* 20ms */ + +/* Only use agressive echo cancellation for this amount of time then go back to normal cancelation */ +/* #define AGGRESSIVE_TIMELIMIT 150000 */ /* 8 = 1ms */ + +#endif /* _MEC2_CONST_H */ + diff --git a/drivers/isdn/hardware/mISDN/dsp_mg2ec.h b/drivers/isdn/hardware/mISDN/dsp_mg2ec.h new file mode 100644 index 0000000..4c5d041 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_mg2ec.h @@ -0,0 +1,682 @@ +/* + * ECHO_CAN_MG2 + * + * by Michael Gernoth + * + * Based upon kb1ec.h and mec2.h + * + * Copyright (C) 2002, Digium, Inc. + * + * This program is free software and may be used and + * distributed according to the terms of the GNU + * General Public License, incorporated herein by + * reference. + * + * Additional background on the techniques used in this code can be found in: + * + * Messerschmitt, David; Hedberg, David; Cole, Christopher; Haoui, Amine; + * Winship, Peter; "Digital Voice Echo Canceller with a TMS32020," + * in Digital Signal Processing Applications with the TMS320 Family, + * pp. 415-437, Texas Instruments, Inc., 1986. + * + * A pdf of which is available by searching on the document title at http://www.ti.com/ + * + */ + +#ifndef _MG2_ECHO_H +#define _MG2_ECHO_H + + +#define EC_TYPE "MG2" + +#ifdef __KERNEL__ +#include +#include +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#else +#include +#include +#include +#include +#include +#define MALLOC(a) malloc(a) +#define FREE(a) free(a) +#endif + +#define ABS(a) abs(a!=-32768?a:-32767) + +#define RESTORE_COEFFS {\ + int x;\ + memcpy(ec->a_i, ec->c_i, ec->N_d*sizeof(int));\ + for (x=0;xN_d;x++) {\ + ec->a_s[x] = ec->a_i[x] >> 16;\ + }\ + ec->backup = BACKUP;\ + } + +/* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */ +/* #define MEC2_STATS 4000 */ + +/* Uncomment to generate per-sample statistics - this will severely degrade system performance and audio quality */ +/* #define MEC2_STATS_DETAILED */ + +/* Get optimized routines for math */ +#include "dsp_arith.h" + +/* Bring in definitions for the various constants and thresholds */ +#include "dsp_mg2ec_const.h" + +#ifndef NULL +#define NULL 0 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +/* Generic circular buffer definition */ +typedef struct { + /* Pointer to the relative 'start' of the buffer */ + int idx_d; + /* The absolute size of the buffer */ + int size_d; + /* The actual sample - twice as large as we need, however we do store values at idx_d and idx_d+size_d */ + short *buf_d; +} echo_can_cb_s; + +/* Echo canceller definition */ +struct echo_can_state { + /* an arbitrary ID for this echo can - this really should be settable from the calling channel... */ + int id; + + /* absolute time - aka. sample number index - essentially the number of samples since this can was init'ed */ + int i_d; + + /* Pre-computed constants */ + /* ---------------------- */ + /* Number of filter coefficents */ + int N_d; + /* Rate of adaptation of filter */ + int beta2_i; + + /* Accumulators for power computations */ + /* ----------------------------------- */ + /* reference signal power estimate - aka. Average absolute value of y(k) */ + int Ly_i; + /* ... */ + int Lu_i; + + /* Accumulators for signal detectors */ + /* --------------------------------- */ + /* Power estimate of the recent past of the near-end hybrid signal - aka. Short-time average of: 2 x |s(i)| */ + int s_tilde_i; + /* Power estimate of the recent past of the far-end receive signal - aka. Short-time average of: |y(i)| */ + int y_tilde_i; + + /* Near end speech detection counter - stores Hangover counter time remaining, in samples */ + int HCNTR_d; + + /* Circular buffers and coefficients */ + /* --------------------------------- */ + /* ... */ + int *a_i; + /* ... */ + short *a_s; + /* Backups */ + int *b_i; + int *c_i; + /* Reference samples of far-end receive signal */ + echo_can_cb_s y_s; + /* Reference samples of near-end signal */ + echo_can_cb_s s_s; + /* Reference samples of near-end signal minus echo estimate */ + echo_can_cb_s u_s; + /* Reference samples of far-end receive signal used to calculate short-time average */ + echo_can_cb_s y_tilde_s; + + /* Peak far-end receive signal */ + /* --------------------------- */ + /* Highest y_tilde value in the sample buffer */ + short max_y_tilde; + /* Index of the sample containing the max_y_tilde value */ + int max_y_tilde_pos; + +#ifdef MEC2_STATS + /* Storage for performance statistics */ + int cntr_nearend_speech_frames; + int cntr_residualcorrected_frames; + int cntr_residualcorrected_framesskipped; + int cntr_coeff_updates; + int cntr_coeff_missedupdates; + + int avg_Lu_i_toolow; + int avg_Lu_i_ok; +#endif + short lastsig[256]; + int lastpos; + int backup; + +}; + +static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where) +{ + cb->buf_d = (short *)where; + cb->idx_d = 0; + cb->size_d = len; +} + +static inline void add_cc_s(echo_can_cb_s *cb, short newval) +{ + /* Can't use modulus because N+M isn't a power of two (generally) */ + cb->idx_d--; + if (cb->idx_d < (int)0) + /* Whoops - the pointer to the 'start' wrapped around so reset it to the top of the buffer */ + cb->idx_d += cb->size_d; + + /* Load two copies into memory */ + cb->buf_d[cb->idx_d] = newval; + cb->buf_d[cb->idx_d + cb->size_d] = newval; +} + +static inline short get_cc_s(echo_can_cb_s *cb, int pos) +{ + /* Load two copies into memory */ + return cb->buf_d[cb->idx_d + pos]; +} + +static inline void init_cc(struct echo_can_state *ec, int N, int maxy, int maxu) +{ + + void *ptr = ec; + unsigned long tmp; + /* Double-word align past end of state */ + ptr += sizeof(struct echo_can_state); + tmp = (unsigned long)ptr; + tmp += 3; + tmp &= ~3L; + ptr = (void *)tmp; + + /* Reset parameters */ + ec->N_d = N; + ec->beta2_i = DEFAULT_BETA1_I; + + /* Allocate coefficient memory */ + ec->a_i = ptr; + ptr += (sizeof(int) * ec->N_d); + ec->a_s = ptr; + ptr += (sizeof(short) * ec->N_d); + + /* Allocate backup memory */ + ec->b_i = ptr; + ptr += (sizeof(int) * ec->N_d); + ec->c_i = ptr; + ptr += (sizeof(int) * ec->N_d); + + /* Reset Y circular buffer (short version) */ + init_cb_s(&ec->y_s, maxy, ptr); + ptr += (sizeof(short) * (maxy) * 2); + + /* Reset Sigma circular buffer (short version for FIR filter) */ + init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr); + ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2); + + init_cb_s(&ec->u_s, maxu, ptr); + ptr += (sizeof(short) * maxu * 2); + + /* Allocate a buffer for the reference signal power computation */ + init_cb_s(&ec->y_tilde_s, ec->N_d, ptr); + + /* Reset the absolute time index */ + ec->i_d = (int)0; + + /* Reset the power computations (for y and u) */ + ec->Ly_i = DEFAULT_CUTOFF_I; + ec->Lu_i = DEFAULT_CUTOFF_I; + +#ifdef MEC2_STATS + /* set the identity */ + ec->id = (int)&ptr; + + /* Reset performance stats */ + ec->cntr_nearend_speech_frames = (int)0; + ec->cntr_residualcorrected_frames = (int)0; + ec->cntr_residualcorrected_framesskipped = (int)0; + ec->cntr_coeff_updates = (int)0; + ec->cntr_coeff_missedupdates = (int)0; + + ec->avg_Lu_i_toolow = (int)0; + ec->avg_Lu_i_ok = (int)0; +#endif + + /* Reset the near-end speech detector */ + ec->s_tilde_i = (int)0; + ec->y_tilde_i = (int)0; + ec->HCNTR_d = (int)0; + +} + +static inline void echo_can_free(struct echo_can_state *ec) +{ + FREE(ec); +} + +static inline short echo_can_update(struct echo_can_state *ec, short iref, short isig) +{ + + /* Declare local variables that are used more than once */ + /* ... */ + int k; + /* ... */ + int rs; + /* ... */ + short u; + /* ... */ + int Py_i; + /* ... */ + int two_beta_i; + + /* flow A on pg. 428 */ + /* eq. (16): high-pass filter the input to generate the next value; + * push the current value into the circular buffer + * + * sdc_im1_d = sdc_d; + * sdc_d = sig; + * s_i_d = sdc_d; + * s_d = s_i_d; + * s_i_d = (float)(1.0 - gamma_d) * s_i_d + * + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d); + */ + + /* Update the Far-end receive signal circular buffers and accumulators */ + /* ------------------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I; + /* Add the new sample to the power estimate accumulator */ + ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I; + /* Push a copy of the new sample into its circular buffer */ + add_cc_s(&ec->y_s, iref); + + + /* eq. (2): compute r in fixed-point */ + rs = CONVOLVE2(ec->a_s, + ec->y_s.buf_d + ec->y_s.idx_d, + ec->N_d); + rs >>= 15; + + ec->lastsig[ec->lastpos++] = isig; + if (ec->lastpos >= 256) + ec->lastpos = 0; + + for (k=0; k < 256; k++) { + if (isig != ec->lastsig[k]) + break; + } + + if (isig == 0) { + u = 0; + } else if (k == 256) { + u = isig; + } else { + if (rs < -32768) { + rs = -32768; + ec->HCNTR_d = DEFAULT_HANGT; + RESTORE_COEFFS; + } else if (rs > 32767) { + rs = 32767; + ec->HCNTR_d = DEFAULT_HANGT; + RESTORE_COEFFS; + } + + if (ABS(ABS(rs)-ABS(isig)) > MAX_SIGN_ERROR) + { + rs = 0; + RESTORE_COEFFS; + } + + /* eq. (3): compute the output value (see figure 3) and the error + * note: the error is the same as the output signal when near-end + * speech is not present + */ + u = isig - rs; + + if (u / isig < 0) + u = isig - (rs >> 1); + } + + /* Push a copy of the output value sample into its circular buffer */ + add_cc_s(&ec->u_s, u); + + if (!ec->backup) { + /* Backup coefficients periodically */ + ec->backup = BACKUP; + memcpy(ec->c_i,ec->b_i,ec->N_d*sizeof(int)); + memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int)); + } else + ec->backup--; + + + /* Update the Near-end hybrid signal circular buffers and accumulators */ + /* ------------------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 )); + /* Add the new sample to the power estimate accumulator */ + ec->s_tilde_i += abs(isig); + /* Push a copy of the new sample into it's circular buffer */ + add_cc_s(&ec->s_s, isig); + + + /* Push a copy of the current short-time average of the far-end receive signal into it's circular buffer */ + add_cc_s(&ec->y_tilde_s, ec->y_tilde_i); + + /* flow B on pg. 428 */ + + /* If the hangover timer isn't running then compute the new convergence factor, otherwise set Py_i to 32768 */ + if (!ec->HCNTR_d) { + Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I); + Py_i >>= 15; + } else { + Py_i = (1 << 15); + } + +#if 0 + /* Vary rate of adaptation depending on position in the file + * Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech + * has begun of the file to allow the echo cancellor to estimate the + * channel accurately + * Still needs conversion! + */ + + if (ec->start_speech_d != 0 ){ + if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){ + ec->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - ec->start_speech_d))); + } + } else { + ec->beta2_d = DEFAULT_BETA1; + } +#endif + + /* Fixed point, inverted */ + ec->beta2_i = DEFAULT_BETA1_I; + + /* Fixed point version, inverted */ + two_beta_i = (ec->beta2_i * Py_i) >> 15; + if (!two_beta_i) + two_beta_i++; + + /* Update the Suppressed signal power estimate accumulator */ + /* ------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ; + /* Add the new sample to the power estimate accumulator */ + ec->Lu_i += abs(u); + + /* Update the Far-end reference signal power estimate accumulator */ + /* -------------------------------------------------------------- */ + /* eq. (10): update power estimate of the reference */ + /* Delete the oldest sample from the power estimate accumulator */ + ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ; + /* Add the new sample to the power estimate accumulator */ + ec->Ly_i += abs(iref); + + if (ec->Ly_i < DEFAULT_CUTOFF_I) + ec->Ly_i = DEFAULT_CUTOFF_I; + + + /* Update the Peak far-end receive signal detected */ + /* ----------------------------------------------- */ + if (ec->y_tilde_i > ec->max_y_tilde) { + /* New highest y_tilde with full life */ + ec->max_y_tilde = ec->y_tilde_i; + ec->max_y_tilde_pos = ec->N_d - 1; + } else if (--ec->max_y_tilde_pos < 0) { + /* Time to find new max y tilde... */ + ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos); + } + + /* Determine if near end speech was detected in this sample */ + /* -------------------------------------------------------- */ + if (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde) + && (ec->max_y_tilde > 0)) { + /* Then start the Hangover counter */ + ec->HCNTR_d = DEFAULT_HANGT; + RESTORE_COEFFS; +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde); +#endif +#ifdef MEC2_STATS + ++ec->cntr_nearend_speech_frames; +#endif + } else if (ec->HCNTR_d > (int)0) { + /* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */ +#ifdef MEC2_STATS + ++ec->cntr_nearend_speech_frames; +#endif + ec->HCNTR_d--; + } + + /* Update coefficients if no near-end speech in this sample (ie. HCNTR_d = 0) + * and we have enough signal to bother trying to update. + * -------------------------------------------------------------------------- + */ + if (!ec->HCNTR_d && /* no near-end speech present */ + !(ec->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */ + if (ec->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */ + /* so loop over all the filter coefficients */ +#ifdef USED_COEFFS + int max_coeffs[USED_COEFFS]; + int *pos; + + if (ec->N_d > USED_COEFFS) + memset(max_coeffs, 0, USED_COEFFS*sizeof(int)); +#endif +#ifdef MEC2_STATS_DETAILED + printk( KERN_INFO "updating coefficients with: ec->Lu_i %9d\n", ec->Lu_i); +#endif +#ifdef MEC2_STATS + ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i; + ++ec->cntr_coeff_updates; +#endif + for (k=0; k < ec->N_d; k++) { + /* eq. (7): compute an expectation over M_d samples */ + int grad2; + grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d, + ec->y_s.buf_d + ec->y_s.idx_d + k, + DEFAULT_M); + /* eq. (7): update the coefficient */ + ec->a_i[k] += grad2 / two_beta_i; + ec->a_s[k] = ec->a_i[k] >> 16; + +#ifdef USED_COEFFS + if (ec->N_d > USED_COEFFS) { + if (abs(ec->a_i[k]) > max_coeffs[USED_COEFFS-1]) { + /* More or less insertion-sort... */ + pos = max_coeffs; + while (*pos > abs(ec->a_i[k])) + pos++; + + if (*pos > max_coeffs[USED_COEFFS-1]) + memmove(pos+1, pos, (USED_COEFFS-(pos-max_coeffs)-1)*sizeof(int)); + + *pos = abs(ec->a_i[k]); + } + } +#endif + } + +#ifdef USED_COEFFS + /* Filter out irrelevant coefficients */ + if (ec->N_d > USED_COEFFS) + for (k=0; k < ec->N_d; k++) + if (abs(ec->a_i[k]) < max_coeffs[USED_COEFFS-1]) + ec->a_i[k] = ec->a_s[k] = 0; +#endif + } else { +#ifdef MEC2_STATS_DETAILED + printk( KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I); +#endif +#ifdef MEC2_STATS + ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i; + ++ec->cntr_coeff_missedupdates; +#endif + } + } + + /* paragraph below eq. (15): if no near-end speech in the sample and + * the reference signal power estimate > cutoff threshold + * then perform residual error suppression + */ +#ifdef MEC2_STATS_DETAILED + if (ec->HCNTR_d == 0) + printk( KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); +#endif + +#ifndef NO_ECHO_SUPPRESSOR +#ifdef AGGRESSIVE_SUPPRESSOR + if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) { + for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); + } +#ifdef MEC2_STATS_DETAILED + printk( KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); +#endif +#ifdef MEC2_STATS + ++ec->cntr_residualcorrected_frames; +#endif + } +#else + if (ec->HCNTR_d == 0) { + if ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I) { + for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); + } +#ifdef MEC2_STATS_DETAILED + printk( KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); +#endif +#ifdef MEC2_STATS + ++ec->cntr_residualcorrected_frames; +#endif + } +#ifdef MEC2_STATS + else { + ++ec->cntr_residualcorrected_framesskipped; + } +#endif + } +#endif +#endif + +#if 0 + /* This will generate a non-linear supression factor, once converted */ + if ((ec->HCNTR_d == 0) && + ((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) && + (ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) { + suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(ec->Lu_d/ec->Ly_d) + - SUPPR_CEIL / (float)(SUPPR_FLOOR - SUPPR_CEIL); + u_suppr = pow(10.0, (suppr_factor) * RES_SUPR_FACTOR / 10.0) * u_suppr; + } +#endif + +#ifdef MEC2_STATS + /* Periodically dump performance stats */ + if ((ec->i_d % MEC2_STATS) == 0) { + /* make sure to avoid div0's! */ + if (ec->cntr_coeff_missedupdates > 0) + ec->avg_Lu_i_toolow = (int)(ec->avg_Lu_i_toolow / ec->cntr_coeff_missedupdates); + else + ec->avg_Lu_i_toolow = -1; + + if (ec->cntr_coeff_updates > 0) + ec->avg_Lu_i_ok = (ec->avg_Lu_i_ok / ec->cntr_coeff_updates); + else + ec->avg_Lu_i_ok = -1; + + printk( KERN_INFO "%d: Near end speech: %5d Residuals corrected/skipped: %5d/%5d Coefficients updated ok/low sig: %3d/%3d Lu_i avg ok/low sig %6d/%5d\n", + ec->id, + ec->cntr_nearend_speech_frames, + ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped, + ec->cntr_coeff_updates, ec->cntr_coeff_missedupdates, + ec->avg_Lu_i_ok, ec->avg_Lu_i_toolow); + + ec->cntr_nearend_speech_frames = 0; + ec->cntr_residualcorrected_frames = 0; + ec->cntr_residualcorrected_framesskipped = 0; + ec->cntr_coeff_updates = 0; + ec->cntr_coeff_missedupdates = 0; + ec->avg_Lu_i_ok = 0; + ec->avg_Lu_i_toolow = 0; + } +#endif + + /* Increment the sample index and return the corrected sample */ + ec->i_d++; + return u; +} + +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) +{ + struct echo_can_state *ec; + int maxy; + int maxu; + maxy = len + DEFAULT_M; + maxu = DEFAULT_M; + if (maxy < (1 << DEFAULT_ALPHA_YT_I)) + maxy = (1 << DEFAULT_ALPHA_YT_I); + if (maxy < (1 << DEFAULT_SIGMA_LY_I)) + maxy = (1 << DEFAULT_SIGMA_LY_I); + if (maxu < (1 << DEFAULT_SIGMA_LU_I)) + maxu = (1 << DEFAULT_SIGMA_LU_I); + ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + sizeof(int) * len + /* b_i */ + sizeof(int) * len + /* c_i */ + 2 * sizeof(short) * (maxy) + /* y_s */ + 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ + 2 * sizeof(short) * (maxu) + /* u_s */ + 2 * sizeof(short) * len); /* y_tilde_s */ + if (ec) { + memset(ec, 0, sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + sizeof(int) * len + /* b_i */ + sizeof(int) * len + /* c_i */ + 2 * sizeof(short) * (maxy) + /* y_s */ + 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ + 2 * sizeof(short) * (maxu) + /* u_s */ + 2 * sizeof(short) * len); /* y_tilde_s */ + init_cc(ec, len, maxy, maxu); + } + return ec; +} + +static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + /* Set the hangover counter to the length of the can to + * avoid adjustments occuring immediately after initial forced training + */ + ec->HCNTR_d = ec->N_d << 1; + + if (pos >= ec->N_d) { + memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int)); + memcpy(ec->c_i,ec->a_i,ec->N_d*sizeof(int)); + return 1; + } + + ec->a_i[pos] = val << 17; + ec->a_s[pos] = val << 1; + + if (++pos >= ec->N_d) { + memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int)); + memcpy(ec->c_i,ec->a_i,ec->N_d*sizeof(int)); + return 1; + } + + return 0; +} + +#endif diff --git a/drivers/isdn/hardware/mISDN/dsp_mg2ec_const.h b/drivers/isdn/hardware/mISDN/dsp_mg2ec_const.h new file mode 100644 index 0000000..c34a5c4 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/dsp_mg2ec_const.h @@ -0,0 +1,97 @@ +/* + Important constants for tuning mg2 echo can + */ +#ifndef _MG2_CONST_H +#define _MG2_CONST_H + + +/* Convergence (aka. adaptation) speed -- higher means slower */ +#define DEFAULT_BETA1_I 2048 + +/* Constants for various power computations */ +#define DEFAULT_SIGMA_LY_I 7 +#define DEFAULT_SIGMA_LU_I 7 +#define DEFAULT_ALPHA_ST_I 5 /* near-end speech detection sensitivity factor */ +#define DEFAULT_ALPHA_YT_I 5 + +#define DEFAULT_CUTOFF_I 128 + +/* Define the near-end speech hangover counter: if near-end speech + * is declared, hcntr is set equal to hangt (see pg. 432) + */ +#define DEFAULT_HANGT 600 /* in samples, so 600 samples = 75ms */ + +/* define the residual error suppression threshold */ +#define DEFAULT_SUPPR_I 16 /* 16 = -24db */ + +/* This is the minimum reference signal power estimate level + * that will result in filter adaptation. + * If this is too low then background noise will cause the filter + * coefficients to constantly be updated. + */ +#define MIN_UPDATE_THRESH_I 4096 + +/* The number of samples used to update coefficients using the + * the block update method (M). It should be related back to the + * length of the echo can. + * ie. it only updates coefficients when (sample number MOD default_m) = 0 + * + * Getting this wrong may cause an oops. Consider yourself warned! + */ +#define DEFAULT_M 16 /* every 16th sample */ + +/* If AGGRESSIVE supression is enabled, then we start cancelling residual + * echos again even while there is potentially the very end of a near-side + * signal present. + * This defines how many samples of DEFAULT_HANGT can remain before we + * kick back in + */ +#define AGGRESSIVE_HCNTR 160 /* in samples, so 160 samples = 20ms */ + +/* This knob controls the number of passes the residual echo supression + * algorithm makes. + */ +#ifdef AGGRESSIVE_SUPPRESSOR + #define RESIDUAL_SUPRESSION_PASSES 2 +#else + #define RESIDUAL_SUPRESSION_PASSES 1 +#endif + +/* Treat sample as error if it has a different sign as the + * input signal and is this number larger in ABS() as + * the input-signal */ +#define MAX_SIGN_ERROR 3000 + +/* Number of coefficients really used for calculating the + * simulated echo. The value specifies how many of the + * biggest coefficients are used for calculating rs. + * This helps on long echo-tails by artificially limiting + * the number of coefficients for the calculation and + * preventing overflows. + * Comment this to deactivate the code */ +#define USED_COEFFS 64 + +/* Backup coefficients every this number of samples */ +#define BACKUP 256 + +/***************************************************************/ +/* The following knobs are not implemented in the current code */ + +/* we need a dynamic level of suppression varying with the ratio of the + power of the echo to the power of the reference signal this is + done so that we have a smoother background. + we have a higher suppression when the power ratio is closer to + suppr_ceil and reduces logarithmically as we approach suppr_floor. + */ +#define SUPPR_FLOOR -64 +#define SUPPR_CEIL -24 + +/* in a second departure, we calculate the residual error suppression + * as a percentage of the reference signal energy level. The threshold + * is defined in terms of dB below the reference signal. + */ +#define RES_SUPR_FACTOR -20 + + +#endif /* _MG2_CONST_H */ + diff --git a/drivers/isdn/hardware/mISDN/hfcs_mini.c b/drivers/isdn/hardware/mISDN/hfcs_mini.c new file mode 100755 index 0000000..84ef0f6 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcs_mini.c @@ -0,0 +1,1864 @@ +/* $Id$ + * + * mISDN driver for Colognechip HFC-S mini Evaluation Card + * + * Authors : Martin Bachem, Joerg Ciesielski + * Contact : info@colognechip.com + * + * 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. + * + ******************************************************************************* + * + * MODULE PARAMETERS: + * (NOTE: layermask and protocol must be given for all ports, + * not for the number of cards.) + * + * - protocol=[,p2,p3...] + * Values: + * D-channel protocol id + * Flags for special features + * Spare (set to 0) + * + * D-channel protocol ids + * - 1 1TR6 (not released yet) + * - 2 DSS1 + * + * Feature Flags + * 0x0010 Net side stack (NT mode) + * 0x0020 PCI mode (0=master, 1=slave) + * 0x0040 not in use + * 0x0080 B channel loop (for layer1 tests) + * + * - layermask=[,l2,l3...] (32bit): + * mask of layers to be used for D-channel stack + * + * - debug: + * enable debugging (see hfcs_mini.h for debug options) + * + */ + +#include +#include +#include +#include +#include "layer1.h" +#include "debug.h" +#include "hfcs_mini.h" +#include "hfcsmcc.h" + +#if HFCBRIDGE == BRIDGE_HFCPCI +#include +#endif + +static const char hfcsmini_rev[] = "$Revision$"; + +#define MAX_CARDS 8 +static int card_cnt; +static u_int protocol[MAX_CARDS]; +static int layermask[MAX_CARDS]; + +#ifdef MODULE +MODULE_LICENSE("GPL"); +#define MODULE_PARM_T "1-8i" +MODULE_PARM(debug, "1i"); +MODULE_PARM(protocol, MODULE_PARM_T); +MODULE_PARM(layermask, MODULE_PARM_T); +#endif + +static mISDNobject_t hw_mISDNObj; +static int debug = 0; + + +#if HFCBRIDGE == BRIDGE_HFCPCI + +static inline void +hfcsmini_sel_reg(hfcsmini_hw * hw, __u8 reg_addr) +{ + outb(6, hw->iobase + 3); /* A0 = 1, reset = 1 */ + outb(reg_addr, hw->iobase + 1); /* write register number */ + outb(4, hw->iobase + 3); /* A0 = 0, reset = 1 */ +} + + +static inline __u8 +read_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr) +{ + register u_char ret; + +#ifdef SPIN_LOCK_HFCSMINI_REGISTER + spin_lock_irq(&hw->rlock); +#endif + hfcsmini_sel_reg(hw, reg_addr); + ret = inb(hw->iobase + 1); +#ifdef SPIN_LOCK_HFCSMINI_REGISTER + spin_unlock_irq(&hw->rlock); +#endif + return(ret); +} + + +/* read register in already spin-locked irq context */ +static inline __u8 +read_hfcsmini_irq(hfcsmini_hw * hw, __u8 reg_addr) +{ + register u_char ret; + hfcsmini_sel_reg(hw, reg_addr); + ret = inb(hw->iobase + 1); + return(ret); +} + + +static inline __u8 +read_hfcsmini_stable(hfcsmini_hw * hw, __u8 reg_addr) +{ + register u_char in1, in2; + +#ifdef SPIN_LOCK_HFCSMINI_REGISTER + spin_lock_irq(&hw->rlock); +#endif + hfcsmini_sel_reg(hw, reg_addr); + + in1 = inb(hw->iobase + 1); + // loop until 2 equal accesses + while((in2=inb(hw->iobase + 1))!=in1) in1=in2; + +#ifdef SPIN_LOCK_HFCSMINI_REGISTER + spin_unlock_irq(&hw->rlock); +#endif + return(in1); +} + + +static inline void +write_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr, __u8 value) +{ +#ifdef SPIN_LOCK_HFCSMINI_REGISTER + spin_lock_irq(&hw->rlock); +#endif + hfcsmini_sel_reg(hw, reg_addr); + outb(value, hw->iobase + 1); +#ifdef SPIN_LOCK_HFCSMINI_REGISTER + spin_unlock_irq(&hw->rlock); +#endif +} + +#endif + + +static void +hfcsmini_ph_command(channel_t * dch, u_char command) +{ + hfcsmini_hw *hw = dch->hw; + + if (dch->debug) + mISDN_debugprint(&dch->inst, + "%s command(%i) channel(%i)", + __FUNCTION__, command, dch->channel); + + switch (command) { + case HFC_L1_ACTIVATE_TE: + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) { + mISDN_debugprint(&dch->inst, + "HFC_L1_ACTIVATE_TE channel(%i) command(%i)", + dch->channel, command); + } + + write_hfcsmini(hw, R_ST_WR_STA, (M_ST_LD_STA | (M1_ST_SET_STA*4))); + udelay(125); /* to be sure INFO1 signals are sent */ + write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_SET_STA * 4)); + break; + + case HFC_L1_FORCE_DEACTIVATE_TE: + write_hfcsmini(hw, R_ST_WR_STA, (M_ST_LD_STA | (M1_ST_SET_STA*3))); + udelay(7); /* wait at least 5,21 us */ + write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_SET_STA*3)); + break; + + + case HFC_L1_ACTIVATE_NT: + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "HFC_L1_ACTIVATE_NT channel(%i)"); + + write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_ACT | M_SET_G2_G3)); + break; + + case HFC_L1_DEACTIVATE_NT: + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "HFC_L1_DEACTIVATE_NT channel(%i)"); + + write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_ACT * 2)); + break; + + case HFC_L1_TESTLOOP_B1: + break; + + case HFC_L1_TESTLOOP_B2: + break; + + } +} + + +/*********************************/ +/* S0 state change event handler */ +/*********************************/ +static void +s0_new_state(channel_t * dch) +{ + u_int prim = PH_SIGNAL | INDICATION; + u_int para = 0; + hfcsmini_hw *hw = dch->hw; + + if (hw->portmode & PORT_MODE_TE) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "%s: TE %d", + __FUNCTION__, dch->state); + + switch (dch->state) { + case (0): + prim = PH_CONTROL | INDICATION; + para = HW_RESET; + break; + case (3): + prim = PH_CONTROL | INDICATION; + para = HW_DEACTIVATE; + break; + case (6): + para = INFO2; + break; + case (7): + para = INFO4_P8; + break; + case (5): + case (8): + para = ANYSIGNAL; + break; + default: + return; + } + if (dch->state== 7) + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + else + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + } // PORT_MODE_TE + + if (hw->portmode & PORT_MODE_NT) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "%s: NT %d", + __FUNCTION__, dch->state); + + switch (dch->state) { + case (1): + hw->nt_timer = 0; + hw->portmode &= ~NT_TIMER; + prim = PH_DEACTIVATE | INDICATION; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + para = 0; + break; + case (2): + if (hw->nt_timer < 0) { + hw->nt_timer = 0; + hw->portmode &= ~NT_TIMER; + hfcsmini_ph_command(dch, + HFC_L1_DEACTIVATE_NT); + } else { + hw->nt_timer = NT_T1_COUNT; + hw->portmode |= NT_TIMER; + write_hfcsmini(hw, R_ST_WR_STA, M_SET_G2_G3); + } + return; + case (3): + hw->nt_timer = 0; + hw->portmode &= ~NT_TIMER; + prim = PH_ACTIVATE | INDICATION; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + para = 0; + break; + case (4): + hw->nt_timer = 0; + hw->portmode &= ~NT_TIMER; + return; + default: + break; + } + } // PORT_MODE_NT + mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0); +} + +/*************************************/ +/* Layer 1 D-channel hardware access */ +/*************************************/ +static int +handle_dmsg(channel_t *dch, struct sk_buff *skb) +{ + int ret = 0; + mISDN_head_t *hh = mISDN_HEAD_P(skb); + hfcsmini_hw *hw = dch->hw; + u_long flags; + + if (hh->prim == (PH_SIGNAL | REQUEST)) { + ret = -EINVAL; + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + spin_lock_irqsave(&hw->rlock, flags); + if (hh->dinfo == HW_RESET) { + if (dch->state != 0) + hfcsmini_ph_command(dch, HFC_L1_ACTIVATE_TE); + spin_unlock_irqrestore(&hw->rlock, flags); + skb_trim(skb, 0); + return(mISDN_queueup_newhead(&dch->inst, 0, PH_CONTROL | INDICATION,HW_POWERUP, skb)); + } else if (hh->dinfo == HW_DEACTIVATE) { + if (dch->next_skb) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_DBUSY, &dch->Flags)) + dchannel_sched_event(dch, D_CLEARBUSY); +#endif + } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) { + if (1 & hh->dinfo) + hfcsmini_ph_command(dch, HFC_L1_TESTLOOP_B1); + + if (2 & hh->dinfo) + hfcsmini_ph_command(dch, HFC_L1_TESTLOOP_B2); + + } else if (hh->dinfo == HW_POWERUP) { + hfcsmini_ph_command(dch, HFC_L1_FORCE_DEACTIVATE_TE); + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, + "hfcsmini_l1hw unknown ctrl %x", + hh->dinfo); + ret = -EINVAL; + } + spin_unlock_irqrestore(&hw->rlock, flags); + } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { + spin_lock_irqsave(&hw->rlock, flags); + if (hw->portmode & PORT_MODE_NT) { + hfcsmini_ph_command(dch, HFC_L1_ACTIVATE_NT); + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, + "%s: PH_ACTIVATE none NT mode", + __FUNCTION__); + ret = -EINVAL; + } + spin_unlock_irqrestore(&hw->rlock, flags); + } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { + spin_lock_irqsave(&hw->rlock, flags); + if (hw->portmode & PORT_MODE_NT) { + hfcsmini_ph_command(dch, HFC_L1_DEACTIVATE_NT); + if (test_and_clear_bit(FLG_TX_NEXT, &dch->Flags)) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + } else { + if (dch->debug & L1_DEB_WARN) + mISDN_debugprint(&dch->inst, + "%s: PH_DEACTIVATE none NT mode", + __FUNCTION__); + ret = -EINVAL; + } + spin_unlock_irqrestore(&hw->rlock, flags); + } else if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS) { + u_int temp = hh->dinfo & SSTATUS_ALL; // remove SSTATUS_BROADCAST_BIT + if ((hw->portmode & PORT_MODE_NT) && + (temp == SSTATUS_ALL || temp == SSTATUS_L1)) { + if (hh->dinfo & SSTATUS_BROADCAST_BIT) + temp = dch->inst.id | MSG_BROADCAST; + else + temp = hh->addr | FLG_MSG_TARGET; + skb_trim(skb, 0); + hh->dinfo = test_bit(FLG_ACTIVE, &dch->Flags) ? + SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED; + hh->prim = MGR_SHORTSTATUS | CONFIRM; + return(mISDN_queue_message(&dch->inst, temp, skb)); + } + ret = -EOPNOTSUPP; + } else { + printk(KERN_WARNING "%s %s: unknown prim(%x)\n", + hw->card_name, __FUNCTION__, hh->prim); + ret = -EAGAIN; + } + if (!ret) + dev_kfree_skb(skb); + return (ret); +} + +/*************************************/ +/* Layer 1 B-channel hardware access */ +/*************************************/ +static int +handle_bmsg(channel_t *bch, struct sk_buff *skb) +{ + hfcsmini_hw *hw = bch->hw; + int ret = 0; + mISDN_head_t *hh = mISDN_HEAD_P(skb); + u_long flags; + + if ((hh->prim == (PH_ACTIVATE | REQUEST)) || + (hh->prim == (DL_ESTABLISH | REQUEST))) { + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + spin_lock_irqsave(&hw->rlock, flags); + ret = setup_channel(hw, bch->channel, + bch->inst.pid.protocol[1]); + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) + test_and_set_bit(FLG_L2DATA, &bch->Flags); + spin_unlock_irqrestore(&hw->rlock, flags); + } +#ifdef FIXME + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, + hh->prim | CONFIRM, 0, 0, NULL, 0); +#endif + skb_trim(skb, 0); + return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb)); + } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || + (hh->prim == (DL_RELEASE | REQUEST)) || + ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { + + spin_lock_irqsave(&hw->rlock, flags); + if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->tx_skb) { + dev_kfree_skb(bch->tx_skb); + bch->tx_skb = NULL; + } + bch->tx_idx = 0; + if (bch->rx_skb) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + } + test_and_clear_bit(FLG_L2DATA, &bch->Flags); + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + setup_channel(hw, bch->channel, ISDN_PID_NONE); + test_and_clear_bit(FLG_ACTIVE, &bch->Flags); + spin_unlock_irqrestore(&hw->rlock, flags); + skb_trim(skb, 0); + if (hh->prim != (PH_CONTROL | REQUEST)) { +#ifdef FIXME + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, + hh->prim | CONFIRM, 0, 0, NULL, 0); +#endif + if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb)) + return(0); + } + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + // do not handle PH_CONTROL | REQUEST ?? + } else { + printk(KERN_WARNING "%s %s: unknown prim(%x)\n", + hw->card_name, __FUNCTION__, hh->prim); + ret = -EAGAIN; + } + if (!ret) + dev_kfree_skb(skb); + return (ret); +} + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ +static int +hfcsmini_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) +{ + channel_t *chan = container_of(inst, channel_t, inst); + hfcsmini_hw *hw = chan->hw; + int ret = 0; + mISDN_head_t *hh = mISDN_HEAD_P(skb); + u_long flags; + + if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) { + spin_lock_irqsave(inst->hwlock, flags); + ret = channel_senddata(chan, hh->dinfo, skb); + if (ret > 0) { /* direct TX */ + tasklet_schedule(&hw->tasklet); + ret = 0; + } + spin_unlock_irqrestore(inst->hwlock, flags); + return(ret); + } + if (test_bit(FLG_DCHANNEL, &chan->Flags)) { + ret = handle_dmsg(chan, skb); + if (ret != -EAGAIN) + return(ret); + ret = -EINVAL; + } + if (test_bit(FLG_BCHANNEL, &chan->Flags)) { + ret = handle_bmsg(chan, skb); + if (ret != -EAGAIN) + return(ret); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + + +static int +hfcsmini_manager(void *data, u_int prim, void *arg) +{ + hfcsmini_hw *hw = NULL; + mISDNinstance_t *inst = data; + struct sk_buff *skb; + int channel = -1; + int i; + channel_t *chan = NULL; + u_long flags; + + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim, arg, &hw_mISDNObj) + printk(KERN_ERR "%s %s: no data prim %x arg %p\n", + hw->card_name, __FUNCTION__, prim, arg); + return (-EINVAL); + } + + spin_lock_irqsave(&hw_mISDNObj.lock, flags); + + /* find channel and card */ + list_for_each_entry(hw, &hw_mISDNObj.ilist, list) { + i = 0; + while (i < MAX_CHAN) { + if (hw->chan[i].Flags && + &hw->chan[i].inst == inst) { + channel = i; + chan = &hw->chan[i]; + break; + } + i++; + } + if (channel >= 0) + break; + } + spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); + + if (channel < 0) { + printk(KERN_ERR + "%s: no card/channel found data %p prim %x arg %p\n", + __FUNCTION__, data, prim, arg); + return (-EINVAL); + } + + switch (prim) { + case MGR_REGLAYER | CONFIRM: + mISDN_setpara(chan, &inst->st->para); + break; + case MGR_UNREGLAYER | REQUEST: + if ((skb = create_link_skb(PH_CONTROL | REQUEST, + HW_DEACTIVATE, 0, NULL, 0))) { + if (hfcsmini_l2l1(inst, skb)) + dev_kfree_skb(skb); + } else + printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); + hw_mISDNObj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + case MGR_ADDSTPARA | INDICATION: + mISDN_setpara(chan, arg); + break; + case MGR_RELEASE | INDICATION: + if (channel == 2) { + release_card(hw); + } else { + hw_mISDNObj.refcnt--; + } + break; + case MGR_SETSTACK | INDICATION: + if ((channel != 2) && (inst->pid.global == 2)) { + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, + 0, 0, NULL, 0))) { + if (hfcsmini_l2l1(inst, skb)) + dev_kfree_skb(skb); + } + if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) + mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, + 0, 0, NULL, 0); + else + mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, + 0, 0, NULL, 0); + } + break; + case MGR_GLOBALOPT | REQUEST: + if (arg) { + // FIXME: detect cards with HEADSET + u_int *gopt = arg; + *gopt = GLOBALOPT_INTERNAL_CTRL | + GLOBALOPT_EXTERNAL_EQUIPMENT | + GLOBALOPT_HANDSET; + } else + return (-EINVAL); + break; + case MGR_SELCHANNEL | REQUEST: + // no special procedure + return (-EINVAL); + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + default: + printk(KERN_WARNING "%s %s: prim %x not handled\n", + hw->card_name, __FUNCTION__, prim); + return (-EINVAL); + } + return (0); +} + + +/***********************************/ +/* check if new buffer for channel */ +/* is waitinng is transmitt queue */ +/***********************************/ +int +next_tx_frame(hfcsmini_hw * hw, __u8 channel) +{ + channel_t *ch = &hw->chan[channel]; + + if (ch->tx_skb) + dev_kfree_skb(ch->tx_skb); + if (test_and_clear_bit(FLG_TX_NEXT, &ch->Flags)) { + ch->tx_skb = ch->next_skb; + if (ch->tx_skb) { + mISDN_head_t *hh = mISDN_HEAD_P(ch->tx_skb); + ch->next_skb = NULL; + test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); + ch->tx_idx = 0; + queue_ch_frame(ch, CONFIRM, hh->dinfo, NULL); + return (1); + } else { + printk(KERN_WARNING + "%s channel(%i) TX_NEXT without skb\n", + hw->card_name, channel); + test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); + } + } else + ch->tx_skb = NULL; + test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); + return (0); +} + +static inline void +hfcsmini_waitbusy(hfcsmini_hw *hw) +{ + while (read_hfcsmini(hw, R_STATUS) & M_BUSY); +} + + +static inline void +hfcsmini_selfifo(hfcsmini_hw *hw, __u8 fifo) +{ + write_hfcsmini(hw, R_FIFO, fifo); + hfcsmini_waitbusy(hw); +} + + +static inline void +hfcsmini_inc_f(hfcsmini_hw *hw) +{ + write_hfcsmini(hw, A_INC_RES_FIFO, M_INC_F); + hfcsmini_waitbusy(hw); +} + + +static inline void +hfcsmini_resetfifo(hfcsmini_hw *hw) +{ + write_hfcsmini(hw, A_INC_RES_FIFO, M_RES_FIFO); + hfcsmini_waitbusy(hw); +} + + +/**************************/ +/* fill fifo with TX data */ +/**************************/ +void +hfcsmini_write_fifo(hfcsmini_hw *hw, __u8 channel) +{ + __u8 fcnt, tcnt, i; + __u8 free; + __u8 f1, f2; + __u8 fstat; + __u8 *data; + int remain; + channel_t *ch = &hw->chan[channel]; + +send_buffer: + if (!ch->tx_skb) + return; + remain = ch->tx_skb->len - ch->tx_idx; + if (remain <= 0) + return; + hfcsmini_selfifo(hw, (channel * 2)); + free = (hw->max_z - (read_hfcsmini_stable(hw, A_USAGE))); + tcnt = (free >= remain) ? remain : free; + + fstat = read_hfcsmini(hw, R_ST_RD_STA); + f1 = read_hfcsmini_stable(hw, A_F1); + f2 = read_hfcsmini(hw, A_F2); + fcnt = 0x07 - ((f1 - f2) & 0x07); /* free frame count in tx fifo */ + + if (debug & DEBUG_HFC_FIFO) { + mISDN_debugprint(&ch->inst, + "%s channel(%i) len(%i) idx(%i) f1(%i) " + "f2(%i) fcnt(%i) tcnt(%i) free(%i) fstat(%i)", + __FUNCTION__, channel, ch->tx_skb->len, ch->tx_idx, + f1, f2, fcnt, tcnt, free, fstat); + } + + if (free && fcnt && tcnt) { + data = ch->tx_skb->data + ch->tx_idx; + ch->tx_idx += tcnt; + + if (debug & DEBUG_HFC_FIFO) { + printk(KERN_DEBUG "%s channel(%i) writing: ", + hw->card_name, channel); + } + i = tcnt; + /* write data to Fifo */ + while (i--) { + if (debug & DEBUG_HFC_FIFO) + printk("%02x ", *data); + write_hfcsmini(hw, A_FIFO_DATA, *data++); + } + if (debug & DEBUG_HFC_FIFO) + printk("\n"); + + if (ch->tx_idx == ch->tx_skb->len) { + if (test_bit(FLG_HDLC, &ch->Flags)) { + /* terminate frame */ + hfcsmini_inc_f(hw); + } else { + hfcsmini_selfifo(hw, (channel * 2)); + } + if (debug & DEBUG_HFC_BTRACE) + mISDN_debugprint(&ch->inst, + "TX frame channel(%i) completed", + channel); + if (next_tx_frame(hw, channel)) { + if (debug & DEBUG_HFC_BTRACE) + mISDN_debugprint(&ch->inst, + "channel(%i) has next_tx_frame", + channel); + if ((free - tcnt) > 8) { + if (debug & DEBUG_HFC_BTRACE) + mISDN_debugprint(&ch->inst, + "channel(%i) continue B-TX immediatetly", + channel); + goto send_buffer; + } + } + } else { + /* tx buffer not complete, but fifo filled to maximum */ + hfcsmini_selfifo(hw, (channel * 2)); + } + } +} + + +/****************************/ +/* read RX data out of fifo */ +/****************************/ +void +hfcsmini_read_fifo(hfcsmini_hw *hw, __u8 channel) +{ + __u8 f1 = 0, f2 = 0, z1, z2; + __u8 fstat = 0; + int i; + int rcnt; /* read rcnt bytes out of fifo */ + __u8 *data; /* new data pointer */ + struct sk_buff *skb; /* data buffer for upper layer */ + channel_t *ch = &hw->chan[channel]; + +receive_buffer: + hfcsmini_selfifo(hw, (channel * 2) + 1); + if (test_bit(FLG_HDLC, &ch->Flags)) { + /* hdlc rcnt */ + f1 = read_hfcsmini_stable(hw, A_F1); + f2 = read_hfcsmini(hw, A_F2); + z1 = read_hfcsmini_stable(hw, A_Z1); + z2 = read_hfcsmini(hw, A_Z2); + fstat = read_hfcsmini(hw, R_ST_RD_STA); + rcnt = (z1 - z2) & hw->max_z; + if (f1 != f2) + rcnt++; + } else { + /* transparent rcnt */ + rcnt = read_hfcsmini_stable(hw, A_USAGE) - 1; + f1=f2=z1=z2=0; + } + if (debug & DEBUG_HFC_FIFO) { + if (ch->rx_skb) + i = ch->rx_skb->len; + else + i = 0; + mISDN_debugprint(&ch->inst, "reading %i bytes channel(%i) " + "irq_cnt(%i) fstat(%i) idx(%i) f1(%i) f2(%i) z1(%i) z2(%i)", + rcnt, channel, hw->irq_cnt, fstat, i, f1, f2, z1, z2); + } + if (rcnt > 0) { + if (!ch->rx_skb) { + ch->rx_skb = alloc_stack_skb(ch->maxlen + 3, ch->up_headerlen); + if (!ch->rx_skb) { + printk(KERN_DEBUG "%s: No mem for rx_skb\n", __FUNCTION__); + return; + } + } + data = skb_put(ch->rx_skb, rcnt); + /* read data from FIFO*/ + while (rcnt--) + *data++ = read_hfcsmini(hw, A_FIFO_DATA); + } else + return; + + + if (test_bit(FLG_HDLC, &ch->Flags)) { + if (f1 != f2) { + hfcsmini_inc_f(hw); + /* check minimum frame size */ + if (ch->rx_skb->len < 4) { + if (debug & DEBUG_HFC_FIFO_ERR) + mISDN_debugprint(&ch->inst, + "%s: frame in channel(%i) < minimum size", + __FUNCTION__, channel); + goto read_exit; + } + /* check crc */ + if (ch->rx_skb->data[ch->rx_skb->len - 1]) { + if (debug & DEBUG_HFC_FIFO_ERR) + mISDN_debugprint(&ch->inst, + "%s: channel(%i) CRC-error", + __FUNCTION__, channel); + goto read_exit; + } + /* remove cksum */ + skb_trim(ch->rx_skb, ch->rx_skb->len - 3); + + if (ch->rx_skb->len < MISDN_COPY_SIZE) { + skb = alloc_stack_skb(ch->rx_skb->len, ch->up_headerlen); + if (skb) { + memcpy(skb_put(skb, ch->rx_skb->len), + ch->rx_skb->data, ch->rx_skb->len); + skb_trim(ch->rx_skb, 0); + } else { + skb = ch->rx_skb; + ch->rx_skb = NULL; + } + } else { + skb = ch->rx_skb; + ch->rx_skb = NULL; + } + if ((ch->debug) && (debug & DEBUG_HFC_DTRACE)) { + mISDN_debugprint(&ch->inst, + "channel(%i) new RX len(%i): ", + channel, skb->len); + i = 0; + printk(" "); + while (i < skb->len) + printk("%02x ", skb->data[i++]); + printk("\n"); + } + queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, skb); +read_exit: + if (ch->rx_skb) + skb_trim(ch->rx_skb, 0); + if (read_hfcsmini_stable(hw, A_USAGE) > 8) { + if (debug & DEBUG_HFC_FIFO) + mISDN_debugprint(&ch->inst, + "%s: channel(%i) continue hfcsmini_read_fifo", + __FUNCTION__, channel); + goto receive_buffer; + } + return; + } else { + hfcsmini_selfifo(hw, (channel * 2) + 1); + } + } else { /* transparent data */ + hfcsmini_selfifo(hw, (channel * 2) + 1); + if (ch->rx_skb->len >= 128) { + /* deliver transparent data to layer2 */ + queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, ch->rx_skb); + ch->rx_skb = NULL; + } + } +} + + +/*************************************/ +/* bottom half handler for interrupt */ +/*************************************/ +static void +hfcsmini_bh_handler(unsigned long ul_hw) +{ + hfcsmini_hw *hw = (hfcsmini_hw *) ul_hw; + reg_r_st_rd_sta state; + int i; + + /* Timer Int */ + if (hw->misc_irq.bit.v_ti_irq) { + hw->misc_irq.bit.v_ti_irq = 0; + /* add Fifo-Fill info into int_s1 bitfield */ + hw->fifo_irq.reg |= ((read_hfcsmini(hw, R_FILL) ^ FIFO_MASK_TX) & hw->fifomask); + /* Handle TX Fifos */ + for (i = 0; i < hw->max_fifo; i++) { + if ((1 << (i * 2)) & (hw->fifo_irq.reg)) { + hw->fifo_irq.reg &= ~(1 << (i * 2)); + if (test_bit(FLG_TX_BUSY, &hw->chan[i].Flags)) + hfcsmini_write_fifo(hw, i); + } + } + /* handle NT Timer */ + if ((hw->portmode & PORT_MODE_NT) && (hw->portmode & NT_TIMER)) + if ((--hw->nt_timer) < 0) + s0_new_state(&hw->chan[2]); + } + /* Handle RX Fifos */ + for (i = 0; i < hw->max_fifo; i++) { + if ((1 << (i * 2 + 1)) & (hw->fifo_irq.reg)) { + hw->fifo_irq.reg &= ~(1 << (i * 2 + 1)); + hfcsmini_read_fifo(hw, i); + } + } + /* state machine IRQ */ + if (hw->misc_irq.bit.v_st_irq) { + hw->misc_irq.bit.v_st_irq = 0; + state.reg = read_hfcsmini(hw, R_ST_RD_STA); + /* + mISDN_debugprint(&dch->inst, + "new_l1_state(0x%02x)", state.bit.v_st_sta); + */ + if (state.bit.v_st_sta != hw->chan[2].state) { + hw->chan[2].state = state.bit.v_st_sta; + s0_new_state(&hw->chan[2]); + } + } + return; +} + + +/*********************/ +/* Interrupt handler */ +/*********************/ +static irqreturn_t +hfcsmini_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + __u8 fifo_irq, misc_irq; + hfcsmini_hw *hw = dev_id; + + spin_lock(&hw->rlock); + + if (!(hw->misc_irqmsk.bit.v_irq_en)) { + if (!(hw->testirq)) + printk(KERN_INFO + "%s %s GLOBAL INTERRUPT DISABLED\n", + hw->card_name, __FUNCTION__); + spin_unlock(&hw->rlock); + return IRQ_NONE; + } + + fifo_irq = read_hfcsmini_irq(hw, R_FIFO_IRQ) & hw->fifo_irqmsk.reg; + misc_irq = read_hfcsmini_irq(hw, R_MISC_IRQ) & hw->misc_irqmsk.reg; + + if (!fifo_irq && !misc_irq) { + spin_unlock(&hw->rlock); + return IRQ_NONE; /* other hardware interrupted */ + } + + hw->irq_cnt++; + + hw->fifo_irq.reg |= fifo_irq; + hw->misc_irq.reg |= misc_irq; + + /* queue bottom half */ + if (!(hw->testirq)) { + tasklet_schedule(&hw->tasklet); + } + + spin_unlock(&hw->rlock); + + return IRQ_HANDLED; +} + + +/*************************************/ +/* free memory for all used channels */ +/*************************************/ +void +release_channels(hfcsmini_hw * hw) +{ + int i = 0; + + while (i < MAX_CHAN) { + if (hw->chan[i].Flags) { + if (debug & DEBUG_HFC_INIT) + printk(KERN_DEBUG "%s %s: free channel %d\n", + hw->card_name, __FUNCTION__, i); + mISDN_freechannel(&hw->chan[i]); + hw_mISDNObj.ctrl(&hw->chan[i].inst, MGR_UNREGLAYER | REQUEST, NULL); + } + i++; + } +} + + +/******************************************/ +/* Setup Fifo using HDLC_PAR and CON_HDLC */ +/******************************************/ +void setup_fifo(hfcsmini_hw * hw, int fifo, __u8 hdlcreg, __u8 con_reg, __u8 irq_enable, __u8 enable) +{ + + if (enable) + /* mark fifo to be 'in use' */ + hw->fifomask |= (1 << fifo); + else + hw->fifomask &= ~(1 << fifo); + + if (irq_enable) + hw->fifo_irqmsk.reg |= (1 << fifo); + else + hw->fifo_irqmsk.reg &= ~(1 << fifo); + + write_hfcsmini(hw, R_FIFO_IRQMSK, hw->fifo_irqmsk.reg); + + hfcsmini_selfifo(hw, fifo); + write_hfcsmini(hw, A_HDLC_PAR, hdlcreg); + write_hfcsmini(hw, A_CON_HDLC, con_reg); + hfcsmini_resetfifo(hw); +} + + +/*************************************************/ +/* Setup ST interface, enable/disable B-Channels */ +/*************************************************/ +void +setup_st(hfcsmini_hw * hw, __u8 bc, __u8 enable) +{ + if (!((bc == 0) || (bc == 1))) { + printk(KERN_INFO "%s %s: ERROR: bc(%i) unvalid!\n", + hw->card_name, __FUNCTION__, bc); + return; + } + + if (bc) { + hw->st_ctrl0.bit.v_b2_en = (enable?1:0); + hw->st_ctrl2.bit.v_b2_rx_en = (enable?1:0); + } else { + hw->st_ctrl0.bit.v_b1_en = (enable?1:0); + hw->st_ctrl2.bit.v_b1_rx_en = (enable?1:0); + } + + write_hfcsmini(hw, R_ST_CTRL0, hw->st_ctrl0.reg); + write_hfcsmini(hw, R_ST_CTRL2, hw->st_ctrl2.reg); + + if (debug & DEBUG_HFC_MODE) { + printk(KERN_INFO + "%s %s: bc(%i) %s, R_ST_CTRL0(0x%02x) R_ST_CTRL2(0x%02x)\n", + hw->card_name, __FUNCTION__, bc, enable?"enable":"disable", + hw->st_ctrl0.reg, hw->st_ctrl2.reg); + } +} + + +/*********************************************/ +/* (dis-) connect D/B-Channel using protocol */ +/*********************************************/ +int +setup_channel(hfcsmini_hw *hw, __u8 channel, int protocol) +{ + if (test_bit(FLG_BCHANNEL, &hw->chan[channel].Flags)) { + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].inst, + "channel(%i) protocol %x-->%x", + channel, hw->chan[channel].state, protocol); + switch (protocol) { + case (-1): /* used for init */ + hw->chan[channel].state = -1; + hw->chan[channel].channel = channel; + /* fall trough */ + case (ISDN_PID_NONE): + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].inst, + "ISDN_PID_NONE"); + if (hw->chan[channel].state == ISDN_PID_NONE) + return (0); /* already in idle state */ + hw->chan[channel].state = ISDN_PID_NONE; + /* B-TX */ + setup_fifo(hw, (channel << 1), 0, 0, + FIFO_IRQ_OFF, FIFO_DISABLE); + /* B-RX */ + setup_fifo(hw, (channel << 1) + 1, 0, 0, + FIFO_IRQ_OFF, FIFO_DISABLE); + setup_st(hw, channel, 0); + test_and_clear_bit(FLG_HDLC, &hw->chan[channel].Flags); + test_and_clear_bit(FLG_TRANSPARENT, &hw->chan[channel].Flags); + break; + case (ISDN_PID_L1_B_64TRANS): + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].inst, + "ISDN_PID_L1_B_64TRANS"); + /* B-TX */ + setup_fifo(hw, (channel << 1), HDLC_PAR_BCH, + CON_HDLC_B_TRANS, FIFO_IRQ_OFF, + FIFO_ENABLE); + /* B-RX */ + setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_BCH, + CON_HDLC_B_TRANS, FIFO_IRQ_OFF, + FIFO_ENABLE); + setup_st(hw, channel, 1); + hw->chan[channel].state = ISDN_PID_L1_B_64TRANS; + test_and_set_bit(FLG_TRANSPARENT, &hw->chan[channel].Flags); + break; + case (ISDN_PID_L1_B_64HDLC): + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].inst, + "ISDN_PID_L1_B_64HDLC"); + /* B-TX */ + setup_fifo(hw, (channel << 1), HDLC_PAR_BCH, + CON_HDLC_B_HDLC, FIFO_IRQ_OFF, + FIFO_ENABLE); + /* B-RX */ + setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_BCH, + CON_HDLC_B_HDLC, FIFO_IRQ_ON, + FIFO_ENABLE); + setup_st(hw, channel, 1); + hw->chan[channel].state = ISDN_PID_L1_B_64HDLC; + test_and_set_bit(FLG_HDLC, &hw->chan[channel].Flags); + break; + default: + mISDN_debugprint(&hw->chan[channel].inst, + "prot not known %x", protocol); + return (-ENOPROTOOPT); + } + return (0); + } + + if (test_bit(FLG_DCHANNEL, &hw->chan[channel].Flags)) { + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].inst, + "D channel(%i) protocol(%i)",channel, protocol); + + /* init the D-channel fifos */ + /* D-TX */ + setup_fifo(hw, (channel << 1), HDLC_PAR_DCH, + CON_HDLC_D_HDLC, FIFO_IRQ_OFF, FIFO_ENABLE); + /* D-RX */ + setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_DCH, + CON_HDLC_D_HDLC, FIFO_IRQ_ON, FIFO_DISABLE); + return (0); + } + printk(KERN_INFO "%s %s ERROR: channel(%i) is NEITHER B nor D !!!\n", + hw->card_name, __FUNCTION__, channel); + return (-1); +} + + +/*****************************************************/ +/* register ISDN stack for one HFC-S mini instance */ +/* - register all ports and channels */ +/* - set param_idx */ +/* */ +/* channel mapping in mISDN in hw->chan[] */ +/* 0=B1, 1=B2, 2=D, 3=PCM */ +/*****************************************************/ +int +init_mISDN_channels(hfcsmini_hw * hw) +{ + int err; + int ch; + int b; + mISDN_pid_t pid; + u_long flags; + + /* clear PCM */ + memset(&hw->chan[3], 0, sizeof(channel_t)); + /* init D channels */ + ch = 2; + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO + "%s %s: Registering D-channel, card(%d) protocol(%x)\n", + hw->card_name, __FUNCTION__, hw->cardnum, + hw->dpid); + + memset(&hw->chan[ch], 0, sizeof(channel_t)); + hw->chan[ch].channel = ch; + hw->chan[ch].debug = debug; + hw->chan[ch].inst.obj = &hw_mISDNObj; + hw->chan[ch].inst.hwlock = &hw->mlock; + hw->chan[ch].inst.class_dev.dev = &hw->pdev->dev; + mISDN_init_instance(&hw->chan[ch].inst, &hw_mISDNObj, hw, hfcsmini_l2l1); + + hw->chan[ch].inst.pid.layermask = ISDN_LAYER(0); + sprintf(hw->chan[ch].inst.name, "%s", hw->card_name); + err = mISDN_initchannel(&hw->chan[ch], MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); + if (err) + goto free_channels; + hw->chan[ch].hw = hw; + + /* init B channels */ + for (b = 0; b < 2; b++) { + if (debug & DEBUG_HFC_INIT) + printk(KERN_DEBUG + "%s %s: Registering B-channel, card(%d) " + "ch(%d)\n", hw->card_name, + __FUNCTION__, hw->cardnum, b); + + memset(&hw->chan[b], 0, sizeof(channel_t)); + hw->chan[b].channel = b; + hw->chan[b].debug = debug; + mISDN_init_instance(&hw->chan[b].inst, &hw_mISDNObj, hw, hfcsmini_l2l1); + hw->chan[b].inst.pid.layermask = ISDN_LAYER(0); + hw->chan[b].inst.hwlock = &hw->mlock; + hw->chan[b].inst.class_dev.dev = &hw->pdev->dev; + + sprintf(hw->chan[b].inst.name, "%s B%d", + hw->chan[ch].inst.name, b + 1); + if (mISDN_initchannel(&hw->chan[b], MSK_INIT_BCHANNEL, MAX_DATA_MEM)) { + err = -ENOMEM; + goto free_channels; + } + hw->chan[b].hw = hw; + } + + mISDN_set_dchannel_pid(&pid, hw->dpid, layermask[hw->param_idx]); + + /* set protocol for NT/TE */ + if (hw->portmode & PORT_MODE_NT) { + /* NT-mode */ + hw->portmode |= NT_TIMER; + hw->nt_timer = 0; + + hw->chan[ch].inst.pid.protocol[0] = ISDN_PID_L0_NT_S0; + hw->chan[ch].inst.pid.protocol[1] = ISDN_PID_L1_NT_S0; + pid.protocol[0] = ISDN_PID_L0_NT_S0; + pid.protocol[1] = ISDN_PID_L1_NT_S0; + hw->chan[ch].inst.pid.layermask |= ISDN_LAYER(1); + pid.layermask |= ISDN_LAYER(1); + if (layermask[hw->param_idx] & ISDN_LAYER(2)) + pid.protocol[2] = ISDN_PID_L2_LAPD_NET; + } else { + /* TE-mode */ + hw->portmode |= PORT_MODE_TE; + hw->chan[ch].inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; + pid.protocol[0] = ISDN_PID_L0_TE_S0; + } + + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO + "%s %s: registering Stack\n", + hw->card_name, __FUNCTION__); + + /* register stack */ + err = hw_mISDNObj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &hw->chan[ch].inst); + if (err) { + printk(KERN_ERR "%s %s: MGR_NEWSTACK | REQUEST err(%d)\n", + hw->card_name, __FUNCTION__, err); + goto free_channels; + } + + hw->chan[ch].state = 0; + for (b = 0; b < 2; b++) { + err = hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_NEWSTACK | REQUEST, &hw->chan[b].inst); + if (err) { + printk(KERN_ERR + "%s %s: MGR_ADDSTACK bchan error %d\n", + hw->card_name, __FUNCTION__, err); + goto free_stack; + } + } + + err = hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_SETSTACK | REQUEST, &pid); + + if (err) { + printk(KERN_ERR + "%s %s: MGR_SETSTACK REQUEST dch err(%d)\n", + hw->card_name, __FUNCTION__, err); + hw_mISDNObj.ctrl(hw->chan[ch].inst.st, + MGR_DELSTACK | REQUEST, NULL); + goto free_stack; + } + + setup_channel(hw, hw->chan[ch].channel, -1); + for (b = 0; b < 2; b++) { + setup_channel(hw, b, -1); + } + + /* delay some time */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ + + hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_CTRLREADY | INDICATION, + NULL); + return (0); + + free_stack: + hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_DELSTACK | REQUEST, NULL); + free_channels: + spin_lock_irqsave(&hw_mISDNObj.lock, flags); + release_channels(hw); + list_del(&hw->list); + spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); + + return (err); +} + + +/********************************/ +/* parse module paramaters like */ +/* NE/TE and S0/Up port mode */ +/********************************/ +void +parse_module_params(hfcsmini_hw * hw) +{ + + /* D-Channel protocol: (2=DSS1) */ + hw->dpid = (protocol[hw->param_idx] & 0x0F); + if (hw->dpid == 0) { + printk(KERN_INFO + "%s %s: WARNING: wrong value for protocol[%i], " + "assuming 0x02 (DSS1)...\n", + hw->card_name, __FUNCTION__, + hw->param_idx); + hw->dpid = 0x02; + } + + /* Line Interface TE or NT */ + if (protocol[hw->param_idx] & 0x10) + hw->portmode |= PORT_MODE_NT; + else + hw->portmode |= PORT_MODE_TE; + + /* Line Interface in S0 or Up mode */ + if (!(protocol[hw->param_idx] & 0x40)) + hw->portmode |= PORT_MODE_BUS_MASTER; + + + /* link B-channel loop */ + if (protocol[hw->param_idx] & 0x80) + hw->portmode |= PORT_MODE_LOOP; + + + if (debug & DEBUG_HFC_INIT) + printk ("%s %s: protocol[%i]=0x%02x, dpid=%d,%s bus-mode:%s %s\n", + hw->card_name, __FUNCTION__, hw->param_idx, + protocol[hw->param_idx], + hw->dpid, + (hw->portmode & PORT_MODE_TE)?"TE":"NT", + (hw->portmode & PORT_MODE_BUS_MASTER)?"MASTER":"SLAVE", + (hw->portmode & PORT_MODE_LOOP)?"B-LOOP":"" + ); +} + + +/*****************************************/ +/* initialise the HFC-S mini ISDN Chip */ +/* return 0 on success. */ +/*****************************************/ +int +init_hfcsmini(hfcsmini_hw * hw) +{ + int err = 0; + reg_r_fifo_thres threshold; + +#if HFCBRIDGE == BRIDGE_HFCPCI + err = init_pci_bridge(hw); + if (err) + return(-ENODEV); +#endif + + hw->chip_id.reg = read_hfcsmini(hw, R_CHIP_ID); + + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO "%s %s ChipID: 0x%x\n", hw->card_name, + __FUNCTION__, hw->chip_id.bit.v_chip_id); + + switch (hw->chip_id.bit.v_chip_id) { + case CHIP_ID_HFCSMINI: + hw->max_fifo = 4; + hw->ti.reg = 5; /* 8 ms timer interval */ + hw->max_z = 0x7F; + break; + default: + err = -ENODEV; + } + + if (err) { + if (debug & DEBUG_HFC_INIT) + printk(KERN_ERR "%s %s: unkown Chip ID 0x%x\n", + hw->card_name, __FUNCTION__, hw->chip_id.bit.v_chip_id); + return (err); + } + + /* reset card */ + write_hfcsmini(hw, R_CIRM, M_SRES); /* Reset On */ + udelay(10); + write_hfcsmini(hw, R_CIRM, 0); /* Reset Off */ + + /* wait until fifo controller init sequence is finished */ + hfcsmini_waitbusy(hw); + + /* reset D-Channel S/T controller */ + write_hfcsmini(hw, R_ST_CTRL1, M_D_RES); + + if (hw->portmode & PORT_MODE_TE) { + /* TE mode */ + hw->st_ctrl0.reg = 0; + write_hfcsmini(hw, R_ST_CLK_DLY, (M1_ST_CLK_DLY* 0xF)); + write_hfcsmini(hw, R_ST_CTRL1, 0); + } else { + /* NT mode */ + hw->st_ctrl0.reg = 4; + write_hfcsmini(hw, R_ST_CLK_DLY, ((M1_ST_SMPL * 0x6) | (M1_ST_CLK_DLY*0xC))); + write_hfcsmini(hw, R_ST_CTRL1, M_E_IGNO); + } + + hw->st_ctrl2.reg = 0; + write_hfcsmini(hw, R_ST_CTRL0, hw->st_ctrl0.reg); + write_hfcsmini(hw, R_ST_CTRL2, hw->st_ctrl2.reg); + + /* HFC Master/Slave Mode */ + if (hw->portmode & PORT_MODE_BUS_MASTER) + hw->pcm_md0.bit.v_pcm_md = 1; + else + hw->pcm_md0.bit.v_pcm_md = 0; + + write_hfcsmini(hw, R_PCM_MD0, hw->pcm_md0.reg); + write_hfcsmini(hw, R_PCM_MD1, 0); + write_hfcsmini(hw, R_PCM_MD2, 0); + + /* setup threshold register */ + threshold.bit.v_thres_tx = (HFCSMINI_TX_THRESHOLD / 8); + threshold.bit.v_thres_rx = (HFCSMINI_RX_THRESHOLD / 8); + write_hfcsmini(hw, R_FIFO_THRES, threshold.reg); + + /* test timer irq */ + enable_interrupts(hw); + mdelay(((1 << hw->ti.reg)+1)*2); + hw->testirq = 0; + + if (hw->irq_cnt) { + printk(KERN_INFO + "%s %s: test IRQ OK, irq_cnt %i\n", + hw->card_name, __FUNCTION__, hw->irq_cnt); + disable_interrupts(hw); + return (0); + } else { + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO + "%s %s: ERROR getting IRQ (irq_cnt %i)\n", + hw->card_name, __FUNCTION__, hw->irq_cnt); + disable_interrupts(hw); + free_irq(hw->irq, hw); + return (-EIO); + } +} + + +/*****************************************************/ +/* disable all interrupts by disabling M_GLOB_IRQ_EN */ +/*****************************************************/ +void +disable_interrupts(hfcsmini_hw * hw) +{ + u_long flags; + if (debug & DEBUG_HFC_IRQ) + printk(KERN_INFO "%s %s\n", hw->card_name, __FUNCTION__); + + spin_lock_irqsave(&hw->mlock, flags); + hw->fifo_irqmsk.reg = 0; + hw->misc_irqmsk.reg = 0; + write_hfcsmini(hw, R_FIFO_IRQMSK, hw->fifo_irqmsk.reg); + write_hfcsmini(hw, R_MISC_IRQMSK, hw->misc_irqmsk.reg); + spin_unlock_irqrestore(&hw->mlock, flags); +} + + +/******************************************/ +/* start interrupt and set interrupt mask */ +/******************************************/ +void +enable_interrupts(hfcsmini_hw * hw) +{ + u_long flags; + + if (debug & DEBUG_HFC_IRQ) + printk(KERN_INFO "%s %s\n", hw->card_name, __FUNCTION__); + + spin_lock_irqsave(&hw->mlock, flags); + + hw->fifo_irq.reg = 0; + hw->misc_irq.reg = 0; + + write_hfcsmini(hw, R_TI, hw->ti.reg); + + /* D-RX and D-TX interrupts enable */ + hw->fifo_irqmsk.bit.v_fifo2_tx_irqmsk = 1; + hw->fifo_irqmsk.bit.v_fifo2_rx_irqmsk = 1; + + /* clear pending ints */ + if (read_hfcsmini(hw, R_FIFO_IRQ)); + if (read_hfcsmini(hw, R_MISC_IRQ)); + + /* Finally enable IRQ output */ + hw->misc_irqmsk.bit.v_st_irqmsk = 1; /* enable L1-state change irq */ + hw->misc_irqmsk.bit.v_ti_irqmsk = 1; /* enable timer irq */ + hw->misc_irqmsk.bit.v_irq_en = 1; /* IRQ global enable */ + + write_hfcsmini(hw, R_MISC_IRQMSK, hw->misc_irqmsk.reg); + + spin_unlock_irqrestore(&hw->mlock, flags); + + return; +} + + +/**************************************/ +/* initialise the HFC-S mini hardware */ +/* return 0 on success. */ +/**************************************/ +static int __devinit +setup_instance(hfcsmini_hw * hw) +{ + int err; + hfcsmini_hw *previous_hw; + u_long flags; + + + if (debug & DEBUG_HFC_INIT) + printk(KERN_WARNING "%s %s\n", + hw->card_name, __FUNCTION__); + + spin_lock_init(&hw->mlock); + spin_lock_init(&hw->rlock); + tasklet_init(&hw->tasklet, hfcsmini_bh_handler, (unsigned long) hw); + + /* search previous instances to index protocol[] array */ + list_for_each_entry(previous_hw, &hw_mISDNObj.ilist, list) + hw->param_idx++; + + /* add this instance to hardware list */ + spin_lock_irqsave(&hw_mISDNObj.lock, flags); + list_add_tail(&hw->list, &hw_mISDNObj.ilist); + spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); + + /* init interrupt engine */ + hw->testirq = 1; + if (debug & DEBUG_HFC_INIT) + printk(KERN_WARNING "%s %s: requesting IRQ %d\n", + hw->card_name, __FUNCTION__, hw->irq); + + if (request_irq(hw->irq, hfcsmini_interrupt, SA_SHIRQ, "HFC-S mini", hw)) { + printk(KERN_WARNING "%s %s: couldn't get interrupt %d\n", + hw->card_name, __FUNCTION__, hw->irq); + + hw->irq = 0; + err = -EIO; + goto out; + } + + parse_module_params(hw); + + err = init_hfcsmini(hw); + if (err) + goto out; + + /* register all channels at ISDN procol stack */ + err = init_mISDN_channels(hw); + if (err) + goto out; + + /* delay some time to have mISDN initialazed complete */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ + + /* Clear already pending ints */ + if (read_hfcsmini(hw, R_FIFO_IRQ)); + + enable_interrupts(hw); + + /* enable state machine */ + write_hfcsmini(hw, R_ST_RD_STA, 0x0); + + return(0); + + out: + return (err); +} + + +#if HFCBRIDGE == BRIDGE_HFCPCI + +/***********************/ +/* PCI Bridge ID List */ +/***********************/ +static struct pci_device_id hfcsmini_ids[] = { + {.vendor = PCI_VENDOR_ID_CCD, + .device = 0xA001, + .subvendor = PCI_VENDOR_ID_CCD, + .subdevice = 0xFFFF, + .driver_data = + (unsigned long) &((hfcsmini_param) {0xFF, "HFC-S mini Evaluation Board"}), + }, + {} +}; + +/******************************/ +/* initialise the PCI Bridge */ +/* return 0 on success. */ +/******************************/ +int +init_pci_bridge(hfcsmini_hw * hw) +{ + outb(0x58, hw->iobase + 4); /* ID-register of bridge */ + if ((inb(hw->iobase) & 0xf0) != 0x30) { + printk(KERN_INFO "%s %s: chip ID for PCI bridge invalid\n", + hw->card_name, __FUNCTION__); + release_region(hw->iobase, 8); + return(-EIO); + } + + outb(0x60, hw->iobase + 4); /* CIRM register of bridge */ + outb(0x07, hw->iobase); /* 15 PCI clocks aux access */ + + /* reset sequence */ + outb(2, hw->iobase + 3); /* A0 = 1, reset = 0 (active) */ + udelay(10); + outb(6, hw->iobase + 3); /* A0 = 1, reset = 1 (inactive) */ + outb(0, hw->iobase + 1); /* write dummy register number */ + + /* wait until reset sequence finished, can be redefined after schematic review */ + mdelay(300); + + return (0); +} + +/************************/ +/* release single card */ +/************************/ +static void +release_card(hfcsmini_hw * hw) +{ + u_long flags; + + disable_interrupts(hw); + free_irq(hw->irq, hw); + + /* wait for pending tasklet to finish */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ + + spin_lock_irqsave(&hw_mISDNObj.lock, flags); + release_channels(hw); + list_del(&hw->list); + spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); + + kfree(hw); +} + +/*****************************************/ +/* PCI hotplug interface: probe new card */ +/*****************************************/ +static int __devinit +hfcsmini_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + hfcsmini_param *driver_data = (hfcsmini_param *) ent->driver_data; + hfcsmini_hw *hw; + + int err = -ENOMEM; + + if (!(hw = kmalloc(sizeof(hfcsmini_hw), GFP_ATOMIC))) { + printk(KERN_ERR "%s %s: No kmem for HFC-S mini card\n", + hw->card_name, __FUNCTION__); + return (err); + } + memset(hw, 0, sizeof(hfcsmini_hw)); + + hw->pdev = pdev; + err = pci_enable_device(pdev); + + if (err) + goto out; + + hw->cardnum = card_cnt; + sprintf(hw->card_name, "%s_%d", DRIVER_NAME, hw->cardnum); + printk(KERN_INFO "%s %s: adapter '%s' found on PCI bus %02x dev %02x\n", + hw->card_name, __FUNCTION__, driver_data->device_name, + pdev->bus->number, pdev->devfn); + + hw->driver_data = *driver_data; + hw->irq = pdev->irq; + + hw->iobase = (u_int) get_pcibase(pdev, 0); + if (!hw->iobase) { + printk(KERN_WARNING "%s no IO for PCI card found\n", + hw->card_name); + return(-EIO); + } + + if (!request_region(hw->iobase, 8, "hfcmulti")) { + printk(KERN_WARNING "%s failed to request " + "address space at 0x%04x\n", + hw->card_name, + hw->iobase); + } + + printk(KERN_INFO "%s defined at IOBASE 0x%#x IRQ %d HZ %d\n", + hw->card_name, + (u_int) hw->iobase, + hw->irq, + HZ); + + /* enable IO */ + pci_write_config_word(pdev, PCI_COMMAND, 0x01); + + pci_set_drvdata(pdev, hw); + err = setup_instance(hw); + + if (!err) { + card_cnt++; + return (0); + } else { + goto out; + } + + out: + kfree(hw); + return (err); +}; + + +/**************************************/ +/* PCI hotplug interface: remove card */ +/**************************************/ +static void __devexit +hfcsmini_pci_remove(struct pci_dev *pdev) +{ + hfcsmini_hw *hw = pci_get_drvdata(pdev); + printk(KERN_INFO "%s %s: removing card\n", hw->card_name, + __FUNCTION__); + release_card(hw); + card_cnt--; + pci_disable_device(pdev); + return; +}; + + +/*****************************/ +/* Module PCI driver exports */ +/*****************************/ +static struct pci_driver hfcsmini_driver = { + name:DRIVER_NAME, + probe:hfcsmini_pci_probe, + remove:__devexit_p(hfcsmini_pci_remove), + id_table:hfcsmini_ids, +}; + +MODULE_DEVICE_TABLE(pci, hfcsmini_ids); + +#endif + +/***************/ +/* Module init */ +/***************/ +static int __init +hfcsmini_init(void) +{ + int err; + + printk(KERN_INFO "HFC-S mini: %s driver Rev. %s (debug=%i)\n", + __FUNCTION__, mISDN_getrev(hfcsmini_rev), debug); + +#ifdef MODULE + hw_mISDNObj.owner = THIS_MODULE; +#endif + + INIT_LIST_HEAD(&hw_mISDNObj.ilist); + spin_lock_init(&hw_mISDNObj.lock); + hw_mISDNObj.name = DRIVER_NAME; + hw_mISDNObj.own_ctrl = hfcsmini_manager; + + hw_mISDNObj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | + ISDN_PID_L0_NT_S0; + hw_mISDNObj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0; + hw_mISDNObj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | + ISDN_PID_L1_B_64HDLC; + hw_mISDNObj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | + ISDN_PID_L2_B_RAWDEV; + + card_cnt = 0; + + if ((err = mISDN_register(&hw_mISDNObj))) { + printk(KERN_ERR "HFC-S mini: can't register HFC-S mini, error(%d)\n", + err); + goto out; + } + +#if HFCBRIDGE == BRIDGE_HFCPCI + err = pci_register_driver(&hfcsmini_driver); + if (err < 0) { + goto out; + } +#if !defined(CONFIG_HOTPLUG) + if (err == 0) { + err = -ENODEV; + pci_unregister_driver(&hfcsmini_driver); + goto out; + } +#endif +#endif + + printk(KERN_INFO "HFC-S mini: %d cards installed\n", card_cnt); + return 0; + + out: + return (err); +} + + +static void __exit +hfcsmini_cleanup(void) +{ + int err; + +#if HFCBRIDGE == BRIDGE_HFCPCI + pci_unregister_driver(&hfcsmini_driver); +#endif + + if ((err = mISDN_unregister(&hw_mISDNObj))) { + printk(KERN_ERR "HFC-S mini: can't unregister HFC-S mini, error(%d)\n", + err); + } + printk(KERN_INFO "%s: driver removed\n", __FUNCTION__); +} + +module_init(hfcsmini_init); +module_exit(hfcsmini_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfcs_mini.h b/drivers/isdn/hardware/mISDN/hfcs_mini.h new file mode 100755 index 0000000..9e5e0c6 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcs_mini.h @@ -0,0 +1,187 @@ +/* $Id$ + * + * mISDN driver for Colognechip HFC-S mini Evaluation Card + * + * Authors : Martin Bachem, Joerg Ciesielski + * Contact : info@colognechip.com + * + * 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. + * + */ + +#ifndef __HFCMINI_H__ +#define __HFCMINI_H__ + +#include "channel.h" +#include "hfcsmcc.h" + + +#define BRIDGE_UNKWOWN 0 +#define BRIDGE_HFCPCI 1 + +/* use HFC-S PCI as PCI Bridge */ +#define HFCBRIDGE BRIDGE_HFCPCI +#define SPIN_LOCK_HFCSMINI_REGISTER + +#define DRIVER_NAME "HFCMINI" +#define CHIP_ID_HFCSMINI CHIP_ID + +#define MAX_CHAN 4 /* D, B1, B2, PCM */ + +/* flags in _u16 port mode */ +#define PORT_UNUSED 0x0000 +#define PORT_MODE_NT 0x0001 +#define PORT_MODE_TE 0x0002 +#define PORT_MODE_BUS_MASTER 0x0004 +#define PORT_MODE_UP 0x0008 +#define PORT_MODE_EXCH_POL 0x0010 +#define PORT_MODE_LOOP 0x0020 +#define NT_TIMER 0x8000 + + +/* NT / TE defines */ +#define NT_T1_COUNT 12 /* number of 8ms interrupts for G2 timeout */ +#define CLK_DLY_TE 0x0e /* CLKDEL in TE mode */ +#define CLK_DLY_NT 0x6c /* CLKDEL in NT mode */ +#define STA_ACTIVATE 0x60 /* start activation in A_SU_WR_STA */ +#define STA_DEACTIVATE 0x40 /* start deactivation in A_SU_WR_STA */ +#define LIF_MODE_NT 0x04 /* Line Interface NT mode */ + + +/* HFC-S mini Layer1 physical commands */ +#define HFC_L1_ACTIVATE_TE 0x01 +#define HFC_L1_FORCE_DEACTIVATE_TE 0x02 +#define HFC_L1_ACTIVATE_NT 0x03 +#define HFC_L1_DEACTIVATE_NT 0x04 +#define HFC_L1_TESTLOOP_B1 0x05 +#define HFC_L1_TESTLOOP_B2 0x06 + +/* FIFO handling support values */ +#define FIFO_IRQ_OFF 0 +#define FIFO_IRQ_ON 1 +#define FIFO_DISABLE 0 +#define FIFO_ENABLE 1 +#define FIFO_MASK_TX 0x55 +#define FIFO_MASK_RX 0xAA +#define HDLC_PAR_BCH 0 /* init value for all B-channel fifos */ +#define HDLC_PAR_DCH (M1_BIT_CNT*2) /* same for D- and E-channel */ +#define CON_HDLC_B_TRANS (M_HDLC_TRP | M1_TRP_IRQ*2) /* transparent mode B-channel 32 byte threshold */ +#define CON_HDLC_B_HDLC (M1_TRP_IRQ*2) /* HDLC mode b-channel */ +#define CON_HDLC_D_HDLC (M1_TRP_IRQ*2 | M_IFF) /* HDLC mode D-channel, 1 fill mode */ +#define CON_HDLC_B_LOOP (M1_TRP_IRQ*2 | M1_DATA_FLOW*6) /* B-channel loopback mode */ +#define HFCSMINI_RX_THRESHOLD 32 +#define HFCSMINI_TX_THRESHOLD 96 +#define DCH_RX_SIZE 267 +#define BCH_RX_SIZE 2051 +#define BCH_RX_SIZE_TRANS 64 + +/* DEBUG flags, use combined value for module parameter debug=x */ +#define DEBUG_HFC_INIT 0x0001 +#define DEBUG_HFC_MODE 0x0002 +#define DEBUG_HFC_S0_STATES 0x0004 +#define DEBUG_HFC_IRQ 0x0008 +#define DEBUG_HFC_FIFO_ERR 0x0010 +#define DEBUG_HFC_DTRACE 0x2000 +#define DEBUG_HFC_BTRACE 0x4000 /* very(!) heavy messageslog load */ +#define DEBUG_HFC_FIFO 0x8000 /* very(!) heavy messageslog load */ + + +/* private driver_data */ +typedef struct { + int chip_id; + char *device_name; +} hfcsmini_param; + +struct _hfcmini_hw; + +/**********************/ +/* hardware structure */ +/**********************/ +typedef struct _hfcmini_hw { + + struct list_head list; + __u32 irq_cnt; /* count irqs */ + struct tasklet_struct tasklet; /* interrupt bottom half */ + spinlock_t mlock; /* mISDN mq lock */ + spinlock_t rlock; /* register access lock */ + + int cardnum; + __u8 param_idx; /* used to access module param arrays */ + __u8 testirq; + int irq; + int iobase; + u_char *membase; + u_char *hw_membase; + struct pci_dev *pdev; + hfcsmini_param driver_data; + char card_name[60]; + + int max_fifo; /* always 4 fifos per port */ + __u8 max_z; /* fifo depth -1 */ + + channel_t chan[MAX_CHAN]; /* line interfaces */ + + __u8 fifomask; + + /* HFC-S MINI regsister */ + reg_r_chip_id chip_id; /* Chip ID */ + + reg_r_pcm_md0 pcm_md0; /* PCM config */ + reg_r_pcm_md1 pcm_md1; /* PCM config */ + reg_r_pcm_md2 pcm_md2; /* PCM config */ + + reg_r_ti ti; /* timer interrupt configuration */ + + reg_r_fifo_irqmsk fifo_irqmsk; /* FIFO interrupt mask */ + reg_r_fifo_irq fifo_irq; /* FIFO interrupt state */ + + reg_r_misc_irqmsk misc_irqmsk; /* MISC interrupt mask */ + reg_r_misc_irq misc_irq; /* MISC interrupt state */ + + reg_r_st_ctrl0 st_ctrl0; + reg_r_st_ctrl2 st_ctrl2; + + int nt_timer; + __u8 dpid; /* DChannel Protocoll ID */ + __u16 portmode; /* NT/TE */ + +} hfcsmini_hw; + + +/* function prototypes */ +int setup_channel(hfcsmini_hw * hw, __u8 channel, int protocol); +void hfcsmini_write_fifo(hfcsmini_hw * hw, __u8 channel); +void hfcsmini_read_fifo(hfcsmini_hw * hw, __u8 channel); +void print_fc(hfcsmini_hw * hw, __u8 fifo); +void setup_fifo(hfcsmini_hw * hw, int fifo, __u8 hdlcreg, __u8 con_reg, __u8 irq_enable, __u8 enable); +void setup_s(hfcsmini_hw * hw, __u8 bc, __u8 enable); +void disable_interrupts(hfcsmini_hw * hw); +void enable_interrupts(hfcsmini_hw * hw); +static void release_card(hfcsmini_hw * hw); + + +#if HFCBRIDGE == BRIDGE_HFCPCI +int init_pci_bridge(hfcsmini_hw * hw); +#endif + +/* HFC-S MINI register access functions */ +static inline void hfcsmini_sel_reg(hfcsmini_hw * hw, __u8 reg_addr); +static inline __u8 read_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr); +static inline __u8 read_hfcsmini_irq(hfcsmini_hw * hw, __u8 reg_addr); +static inline __u8 read_hfcsmini_stable(hfcsmini_hw * hw, __u8 reg_addr); +static inline void write_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr, __u8 value); + + +#endif /* __hfcsmini_H__ */ diff --git a/drivers/isdn/hardware/mISDN/hfcsmcc.h b/drivers/isdn/hardware/mISDN/hfcsmcc.h new file mode 100755 index 0000000..98a74d9 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsmcc.h @@ -0,0 +1,1100 @@ +/*___________________________________________________________________________________*/ +/* */ +/* (C) Copyright Cologne Chip AG, 2005 */ +/*___________________________________________________________________________________*/ +/* */ + +/* */ +/* File name: hfcsmcc.h */ +/* File content: This file contains the HFC-S mini register definitions. */ +/* Creation date: 24.10.2005 10:45 */ +/* Creator: Genero 3.2 */ +/* Data base: HFC XML 1.6 for HFC-S mini and HFC-S USB (unreleased) */ +/* Address range: 0x00 - 0xFC */ +/* */ +/* The information presented can not be considered as assured characteristics. */ +/* Data can change without notice. Please check version numbers in case of doubt. */ +/* */ +/* For further information or questions please contact support@CologneChip.com */ +/* */ +/* */ +/*___________________________________________________________________________________*/ +/* */ +/* WARNING: This file has been generated automatically and should not be */ +/* changed to maintain compatibility with later versions. */ +/*___________________________________________________________________________________*/ +/* */ + + +#ifndef _HFCSMCC_H_ +#define _HFCSMCC_H_ + + +typedef unsigned char BYTE; + +typedef BYTE REGWORD; // maximum register length (standard) +typedef BYTE REGADDR; // address width + +/*___________________________________________________________________________________*/ +/* */ +/* The following definitions are only used for multi-register access and can be */ +/* switched off. Define MULTIREG below if you want to use multi-register accesses. */ +/*___________________________________________________________________________________*/ +/* */ +#define MULTIREG +/*___________________________________________________________________________________*/ + +#ifdef MULTIREG + typedef unsigned short REGWORD16; // multi-register access, 2 registers + typedef unsigned int REGWORD32; // multi-register access, 4 registers +#endif + + + +typedef enum {no=0, yes} REGBOOL; + + +typedef enum +{ + // register and bitmap access modes: + writeonly=0, // write only + readonly, // read only + readwrite, // read/write + // following modes only for mixed mode registers: + readwrite_write, // read/write and write only + readwrite_read, // read/write and read only + write_read, // write only and read only + readwrite_write_read // read/write, write only and read only +} ACCESSMODE; + + + +/*___________________________________________________________________________________*/ +/* */ +/* common chip information: */ +/*___________________________________________________________________________________*/ + + #define CHIP_NAME "HFC-S mini" + #define CHIP_TITLE "ISDN HDLC FIFO controller with S/T interface and integrated FIFOs" + #define CHIP_MANUFACTURER "Cologne Chip" + #define CHIP_ID 0x05 + #define CHIP_REGISTER_COUNT 71 + #define CHIP_DATABASE "Version HFC-XMLHFC XML 1.6 for HFC-S mini and HFC-S USB (unreleased) -GeneroGenero 3.2 " + + + + + +/*___________________________________________________________________________________*/ +/* */ +/* Begin of HFC-S mini register definitions. */ +/*___________________________________________________________________________________*/ +/* */ + +#define R_CIRM 0x00 // register access + #define M_SRES 0x08 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD reserved_28:3; + REGWORD v_sres:1; + REGWORD reserved_29:4; + } bit_r_cirm; // register and bitmap data + typedef union {REGWORD reg; bit_r_cirm bit;} reg_r_cirm; // register and bitmap access + + +#define A_Z1 0x04 // register access + #define M_Z1 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_z1:8; + } bit_a_z1; // register and bitmap data + typedef union {REGWORD reg; bit_a_z1 bit;} reg_a_z1; // register and bitmap access + + +#define A_Z2 0x06 // register access + #define M_Z2 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_z2:8; + } bit_a_z2; // register and bitmap data + typedef union {REGWORD reg; bit_a_z2 bit;} reg_a_z2; // register and bitmap access + + +#define R_RAM_ADDR0 0x08 // register access + #define M_RAM_ADDR0 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_ram_addr0:8; + } bit_r_ram_addr0; // register and bitmap data + typedef union {REGWORD reg; bit_r_ram_addr0 bit;} reg_r_ram_addr0; // register and bitmap access + + +#define R_RAM_ADDR1 0x09 // register access + #define M_RAM_ADDR1 0x07 // bitmap mask (3bit) + #define M1_RAM_ADDR1 0x01 + #define M_ADDR_RES 0x40 // bitmap mask (1bit) + #define M_ADDR_INC 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_ram_addr1:3; + REGWORD reserved_0:3; + REGWORD v_addr_res:1; + REGWORD v_addr_inc:1; + } bit_r_ram_addr1; // register and bitmap data + typedef union {REGWORD reg; bit_r_ram_addr1 bit;} reg_r_ram_addr1; // register and bitmap access + + +#define R_FIFO_REV 0x0B // register access + #define M_FIFO0_TX_REV 0x01 // bitmap mask (1bit) + #define M_FIFO0_RX_REV 0x02 // bitmap mask (1bit) + #define M_FIFO1_TX_REV 0x04 // bitmap mask (1bit) + #define M_FIFO1_RX_REV 0x08 // bitmap mask (1bit) + #define M_FIFO2_TX_REV 0x10 // bitmap mask (1bit) + #define M_FIFO2_RX_REV 0x20 // bitmap mask (1bit) + #define M_FIFO3_TX_REV 0x40 // bitmap mask (1bit) + #define M_FIFO3_RX_REV 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo0_tx_rev:1; + REGWORD v_fifo0_rx_rev:1; + REGWORD v_fifo1_tx_rev:1; + REGWORD v_fifo1_rx_rev:1; + REGWORD v_fifo2_tx_rev:1; + REGWORD v_fifo2_rx_rev:1; + REGWORD v_fifo3_tx_rev:1; + REGWORD v_fifo3_rx_rev:1; + } bit_r_fifo_rev; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_rev bit;} reg_r_fifo_rev; // register and bitmap access + + +#define A_F1 0x0C // register access + #define M_F1 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_f1:8; + } bit_a_f1; // register and bitmap data + typedef union {REGWORD reg; bit_a_f1 bit;} reg_a_f1; // register and bitmap access + + +#define R_FIFO_THRES 0x0C // register access + #define M_THRES_TX 0x0F // bitmap mask (4bit) + #define M1_THRES_TX 0x01 + #define M_THRES_RX 0xF0 // bitmap mask (4bit) + #define M1_THRES_RX 0x10 + + typedef struct // bitmap construction + { + REGWORD v_thres_tx:4; + REGWORD v_thres_rx:4; + } bit_r_fifo_thres; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_thres bit;} reg_r_fifo_thres; // register and bitmap access + + +#define A_F2 0x0D // register access + #define M_F2 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_f2:8; + } bit_a_f2; // register and bitmap data + typedef union {REGWORD reg; bit_a_f2 bit;} reg_a_f2; // register and bitmap access + + +#define R_DF_MD 0x0D // register access + #define M_CSM 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD reserved_1:7; + REGWORD v_csm:1; + } bit_r_df_md; // register and bitmap data + typedef union {REGWORD reg; bit_r_df_md bit;} reg_r_df_md; // register and bitmap access + + +#define A_INC_RES_FIFO 0x0E // register access + #define M_INC_F 0x01 // bitmap mask (1bit) + #define M_RES_FIFO 0x02 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_inc_f:1; + REGWORD v_res_fifo:1; + REGWORD reserved_2:6; + } bit_a_inc_res_fifo; // register and bitmap data + typedef union {REGWORD reg; bit_a_inc_res_fifo bit;} reg_a_inc_res_fifo; // register and bitmap access + + +#define R_FIFO 0x0F // register access + #define M_FIFO_DIR 0x01 // bitmap mask (1bit) + #define M_FIFO_NUM 0x06 // bitmap mask (2bit) + #define M1_FIFO_NUM 0x02 + + typedef struct // bitmap construction + { + REGWORD v_fifo_dir:1; + REGWORD v_fifo_num:2; + REGWORD reserved_3:5; + } bit_r_fifo; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo bit;} reg_r_fifo; // register and bitmap access + + +#define R_FIFO_IRQ 0x10 // register access + #define M_FIFO0_TX_IRQ 0x01 // bitmap mask (1bit) + #define M_FIFO0_RX_IRQ 0x02 // bitmap mask (1bit) + #define M_FIFO1_TX_IRQ 0x04 // bitmap mask (1bit) + #define M_FIFO1_RX_IRQ 0x08 // bitmap mask (1bit) + #define M_FIFO2_TX_IRQ 0x10 // bitmap mask (1bit) + #define M_FIFO2_RX_IRQ 0x20 // bitmap mask (1bit) + #define M_FIFO3_TX_IRQ 0x40 // bitmap mask (1bit) + #define M_FIFO3_RX_IRQ 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo0_tx_irq:1; + REGWORD v_fifo0_rx_irq:1; + REGWORD v_fifo1_tx_irq:1; + REGWORD v_fifo1_rx_irq:1; + REGWORD v_fifo2_tx_irq:1; + REGWORD v_fifo2_rx_irq:1; + REGWORD v_fifo3_tx_irq:1; + REGWORD v_fifo3_rx_irq:1; + } bit_r_fifo_irq; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_irq bit;} reg_r_fifo_irq; // register and bitmap access + + +#define R_MISC_IRQ 0x11 // register access + #define M_ST_IRQ 0x01 // bitmap mask (1bit) + #define M_TI_IRQ 0x02 // bitmap mask (1bit) + #define M_PROC_IRQ 0x04 // bitmap mask (1bit) + #define M_CI_IRQ 0x08 // bitmap mask (1bit) + #define M_MON_RX_IRQ 0x10 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_st_irq:1; + REGWORD v_ti_irq:1; + REGWORD v_proc_irq:1; + REGWORD v_ci_irq:1; + REGWORD v_mon_rx_irq:1; + REGWORD reserved_31:3; + } bit_r_misc_irq; // register and bitmap data + typedef union {REGWORD reg; bit_r_misc_irq bit;} reg_r_misc_irq; // register and bitmap access + + +#define R_PCM_MD0 0x14 // register access + #define M_PCM_MD 0x01 // bitmap mask (1bit) + #define M_C4_POL 0x02 // bitmap mask (1bit) + #define M_F0_NEG 0x04 // bitmap mask (1bit) + #define M_F0_LEN 0x08 // bitmap mask (1bit) + #define M_SL_CODECA 0x30 // bitmap mask (2bit) + #define M1_SL_CODECA 0x10 + #define M_SL_CODECB 0xC0 // bitmap mask (2bit) + #define M1_SL_CODECB 0x40 + + typedef struct // bitmap construction + { + REGWORD v_pcm_md:1; + REGWORD v_c4_pol:1; + REGWORD v_f0_neg:1; + REGWORD v_f0_len:1; + REGWORD v_sl_codeca:2; + REGWORD v_sl_codecb:2; + } bit_r_pcm_md0; // register and bitmap data + typedef union {REGWORD reg; bit_r_pcm_md0 bit;} reg_r_pcm_md0; // register and bitmap access + + +#define R_PCM_MD1 0x15 // register access + #define M_AUX1_MIR 0x01 // bitmap mask (1bit) + #define M_AUX2_MIR 0x02 // bitmap mask (1bit) + #define M_PLL_ADJ 0x0C // bitmap mask (2bit) + #define M1_PLL_ADJ 0x04 + #define M_PCM_DR 0x30 // bitmap mask (2bit) + #define M1_PCM_DR 0x10 + #define M_PCM_LOOP 0x40 // bitmap mask (1bit) + #define M_GCI_EN 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_aux1_mir:1; + REGWORD v_aux2_mir:1; + REGWORD v_pll_adj:2; + REGWORD v_pcm_dr:2; + REGWORD v_pcm_loop:1; + REGWORD v_gci_en:1; + } bit_r_pcm_md1; // register and bitmap data + typedef union {REGWORD reg; bit_r_pcm_md1 bit;} reg_r_pcm_md1; // register and bitmap access + + +#define R_PCM_MD2 0x16 // register access + #define M_OKI_CODECA 0x01 // bitmap mask (1bit) + #define M_OKI_CODECB 0x02 // bitmap mask (1bit) + #define M_SYNC_SRC 0x04 // bitmap mask (1bit) + #define M_SYNC_OUT 0x08 // bitmap mask (1bit) + #define M_SL_BL 0x30 // bitmap mask (2bit) + #define M1_SL_BL 0x10 + #define M_PLL_ICR 0x40 // bitmap mask (1bit) + #define M_PLL_MAN 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_oki_codeca:1; + REGWORD v_oki_codecb:1; + REGWORD v_sync_src:1; + REGWORD v_sync_out:1; + REGWORD v_sl_bl:2; + REGWORD v_pll_icr:1; + REGWORD v_pll_man:1; + } bit_r_pcm_md2; // register and bitmap data + typedef union {REGWORD reg; bit_r_pcm_md2 bit;} reg_r_pcm_md2; // register and bitmap access + + +#define R_CHIP_ID 0x16 // register access + #define M_CHIP_ID 0xF0 // bitmap mask (4bit) + #define M1_CHIP_ID 0x10 + + typedef struct // bitmap construction + { + REGWORD reserved_22:4; + REGWORD v_chip_id:4; + } bit_r_chip_id; // register and bitmap data + typedef union {REGWORD reg; bit_r_chip_id bit;} reg_r_chip_id; // register and bitmap access + + +#define R_F0_CNTL 0x18 // register access + #define M_F0_CNTL 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_f0_cntl:8; + } bit_r_f0_cntl; // register and bitmap data + typedef union {REGWORD reg; bit_r_f0_cntl bit;} reg_r_f0_cntl; // register and bitmap access + + +#define R_F0_CNTH 0x19 // register access + #define M_F0_CNTH 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_f0_cnth:8; + } bit_r_f0_cnth; // register and bitmap data + typedef union {REGWORD reg; bit_r_f0_cnth bit;} reg_r_f0_cnth; // register and bitmap access + + +#define A_USAGE 0x1A // register access + #define M_USAGE 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_usage:8; + } bit_a_usage; // register and bitmap data + typedef union {REGWORD reg; bit_a_usage bit;} reg_a_usage; // register and bitmap access + + +#define R_FIFO_IRQMSK 0x1A // register access + #define M_FIFO0_TX_IRQMSK 0x01 // bitmap mask (1bit) + #define M_FIFO0_RX_IRQMSK 0x02 // bitmap mask (1bit) + #define M_FIFO1_TX_IRQMSK 0x04 // bitmap mask (1bit) + #define M_FIFO1_RX_IRQMSK 0x08 // bitmap mask (1bit) + #define M_FIFO2_TX_IRQMSK 0x10 // bitmap mask (1bit) + #define M_FIFO2_RX_IRQMSK 0x20 // bitmap mask (1bit) + #define M_FIFO3_TX_IRQMSK 0x40 // bitmap mask (1bit) + #define M_FIFO3_RX_IRQMSK 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo0_tx_irqmsk:1; + REGWORD v_fifo0_rx_irqmsk:1; + REGWORD v_fifo1_tx_irqmsk:1; + REGWORD v_fifo1_rx_irqmsk:1; + REGWORD v_fifo2_tx_irqmsk:1; + REGWORD v_fifo2_rx_irqmsk:1; + REGWORD v_fifo3_tx_irqmsk:1; + REGWORD v_fifo3_rx_irqmsk:1; + } bit_r_fifo_irqmsk; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_irqmsk bit;} reg_r_fifo_irqmsk; // register and bitmap access + + +#define R_FILL 0x1B // register access + #define M_FIFO0_TX_FILL 0x01 // bitmap mask (1bit) + #define M_FIFO0_RX_FILL 0x02 // bitmap mask (1bit) + #define M_FIFO1_TX_FILL 0x04 // bitmap mask (1bit) + #define M_FIFO1_RX_FILL 0x08 // bitmap mask (1bit) + #define M_FIFO2_TX_FILL 0x10 // bitmap mask (1bit) + #define M_FIFO2_RX_FILL 0x20 // bitmap mask (1bit) + #define M_FIFO3_TX_FILL 0x40 // bitmap mask (1bit) + #define M_FIFO3_RX_FILL 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo0_tx_fill:1; + REGWORD v_fifo0_rx_fill:1; + REGWORD v_fifo1_tx_fill:1; + REGWORD v_fifo1_rx_fill:1; + REGWORD v_fifo2_tx_fill:1; + REGWORD v_fifo2_rx_fill:1; + REGWORD v_fifo3_tx_fill:1; + REGWORD v_fifo3_rx_fill:1; + } bit_r_fill; // register and bitmap data + typedef union {REGWORD reg; bit_r_fill bit;} reg_r_fill; // register and bitmap access + + +#define R_MISC_IRQMSK 0x1B // register access + #define M_ST_IRQMSK 0x01 // bitmap mask (1bit) + #define M_TI_IRQMSK 0x02 // bitmap mask (1bit) + #define M_PROC_IRQMSK 0x04 // bitmap mask (1bit) + #define M_CI_IRQMSK 0x08 // bitmap mask (1bit) + #define M_MON_IRQMSK 0x10 // bitmap mask (1bit) + #define M_IRQ_REV 0x40 // bitmap mask (1bit) + #define M_IRQ_EN 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_st_irqmsk:1; + REGWORD v_ti_irqmsk:1; + REGWORD v_proc_irqmsk:1; + REGWORD v_ci_irqmsk:1; + REGWORD v_mon_irqmsk:1; + REGWORD reserved_4:1; + REGWORD v_irq_rev:1; + REGWORD v_irq_en:1; + } bit_r_misc_irqmsk; // register and bitmap data + typedef union {REGWORD reg; bit_r_misc_irqmsk bit;} reg_r_misc_irqmsk; // register and bitmap access + + +#define R_TI 0x1C // register access + #define M_EV_TS 0x0F // bitmap mask (4bit) + #define M1_EV_TS 0x01 + + typedef struct // bitmap construction + { + REGWORD v_ev_ts:4; + REGWORD reserved_30:4; + } bit_r_ti; // register and bitmap data + typedef union {REGWORD reg; bit_r_ti bit;} reg_r_ti; // register and bitmap access + + +#define R_STATUS 0x1C // register access + #define M_BUSY 0x01 // bitmap mask (1bit) + #define M_PROC 0x02 // bitmap mask (1bit) + #define M_AWAKE_IN 0x08 // bitmap mask (1bit) + #define M_SYNC_IN 0x10 // bitmap mask (1bit) + #define M_MISC_IRQSTA 0x40 // bitmap mask (1bit) + #define M_FIFO_IRQSTA 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_busy:1; + REGWORD v_proc:1; + REGWORD reserved_32:1; + REGWORD v_awake_in:1; + REGWORD v_sync_in:1; + REGWORD reserved_33:1; + REGWORD v_misc_irqsta:1; + REGWORD v_fifo_irqsta:1; + } bit_r_status; // register and bitmap data + typedef union {REGWORD reg; bit_r_status bit;} reg_r_status; // register and bitmap access + + +#define R_B1_TX_SL 0x20 // register access + #define M_B1_TX_SL 0x1F // bitmap mask (5bit) + #define M1_B1_TX_SL 0x01 + #define M_B1_TX_ROUT 0xC0 // bitmap mask (2bit) + #define M1_B1_TX_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_b1_tx_sl:5; + REGWORD reserved_5:1; + REGWORD v_b1_tx_rout:2; + } bit_r_b1_tx_sl; // register and bitmap data + typedef union {REGWORD reg; bit_r_b1_tx_sl bit;} reg_r_b1_tx_sl; // register and bitmap access + + +#define R_B2_TX_SL 0x21 // register access + #define M_B2_TX_SL 0x1F // bitmap mask (5bit) + #define M1_B2_TX_SL 0x01 + #define M_B2_TX_ROUT 0xC0 // bitmap mask (2bit) + #define M1_B2_TX_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_b2_tx_sl:5; + REGWORD reserved_6:1; + REGWORD v_b2_tx_rout:2; + } bit_r_b2_tx_sl; // register and bitmap data + typedef union {REGWORD reg; bit_r_b2_tx_sl bit;} reg_r_b2_tx_sl; // register and bitmap access + + +#define R_AUX1_TX_SL 0x22 // register access + #define M_AUX1_TX_SL 0x1F // bitmap mask (5bit) + #define M1_AUX1_TX_SL 0x01 + #define M_AUX1_TX_ROUT 0xC0 // bitmap mask (2bit) + #define M1_AUX1_TX_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_aux1_tx_sl:5; + REGWORD reserved_7:1; + REGWORD v_aux1_tx_rout:2; + } bit_r_aux1_tx_sl; // register and bitmap data + typedef union {REGWORD reg; bit_r_aux1_tx_sl bit;} reg_r_aux1_tx_sl; // register and bitmap access + + +#define R_AUX2_TX_SL 0x23 // register access + #define M_AUX2_TX_SL 0x1F // bitmap mask (5bit) + #define M1_AUX2_TX_SL 0x01 + #define M_AUX2_TX_ROUT 0xC0 // bitmap mask (2bit) + #define M1_AUX2_TX_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_aux2_tx_sl:5; + REGWORD reserved_8:1; + REGWORD v_aux2_tx_rout:2; + } bit_r_aux2_tx_sl; // register and bitmap data + typedef union {REGWORD reg; bit_r_aux2_tx_sl bit;} reg_r_aux2_tx_sl; // register and bitmap access + + +#define R_B1_RX_SL 0x24 // register access + #define M_B1_RX_SL 0x1F // bitmap mask (5bit) + #define M1_B1_RX_SL 0x01 + #define M_B1_RX_ROUT 0xC0 // bitmap mask (2bit) + #define M1_B1_RX_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_b1_rx_sl:5; + REGWORD reserved_9:1; + REGWORD v_b1_rx_rout:2; + } bit_r_b1_rx_sl; // register and bitmap data + typedef union {REGWORD reg; bit_r_b1_rx_sl bit;} reg_r_b1_rx_sl; // register and bitmap access + + +#define R_B2_RX_SL 0x25 // register access + #define M_B2_RX_SL 0x1F // bitmap mask (5bit) + #define M1_B2_RX_SL 0x01 + #define M_B2_RX_ROUT 0xC0 // bitmap mask (2bit) + #define M1_B2_RX_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_b2_rx_sl:5; + REGWORD reserved_10:1; + REGWORD v_b2_rx_rout:2; + } bit_r_b2_rx_sl; // register and bitmap data + typedef union {REGWORD reg; bit_r_b2_rx_sl bit;} reg_r_b2_rx_sl; // register and bitmap access + + +#define R_AUX1_RX_SL 0x26 // register access + #define M_AUX1_RX_SL 0x1F // bitmap mask (5bit) + #define M1_AUX1_RX_SL 0x01 + #define M_AUX1_RX_ROUT 0xC0 // bitmap mask (2bit) + #define M1_AUX1_RX_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_aux1_rx_sl:5; + REGWORD reserved_11:1; + REGWORD v_aux1_rx_rout:2; + } bit_r_aux1_rx_sl; // register and bitmap data + typedef union {REGWORD reg; bit_r_aux1_rx_sl bit;} reg_r_aux1_rx_sl; // register and bitmap access + + +#define R_AUX2_RX_SL 0x27 // register access + #define M_AUX2_RX_SL 0x1F // bitmap mask (5bit) + #define M1_AUX2_RX_SL 0x01 + #define M_AUX2_RX_ROUT 0xC0 // bitmap mask (2bit) + #define M1_AUX2_RX_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_aux2_rx_sl:5; + REGWORD reserved_12:1; + REGWORD v_aux2_rx_rout:2; + } bit_r_aux2_rx_sl; // register and bitmap data + typedef union {REGWORD reg; bit_r_aux2_rx_sl bit;} reg_r_aux2_rx_sl; // register and bitmap access + + +#define R_CI_RX 0x28 // register access + #define M_GCI_I 0x0F // bitmap mask (4bit) + #define M1_GCI_I 0x01 + + typedef struct // bitmap construction + { + REGWORD v_gci_i:4; + REGWORD reserved_23:4; + } bit_r_ci_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_ci_rx bit;} reg_r_ci_rx; // register and bitmap access + + +#define R_CI_TX 0x28 // register access + #define M_GCI_C 0x0F // bitmap mask (4bit) + #define M1_GCI_C 0x01 + + typedef struct // bitmap construction + { + REGWORD v_gci_c:4; + REGWORD reserved_13:4; + } bit_r_ci_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_ci_tx bit;} reg_r_ci_tx; // register and bitmap access + + +#define R_PCM_GCI_STA 0x29 // register access + #define M_MON_RXR 0x01 // bitmap mask (1bit) + #define M_MON_TXR 0x02 // bitmap mask (1bit) + #define M_STIO2_IN 0x40 // bitmap mask (1bit) + #define M_STIO1_IN 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_mon_rxr:1; + REGWORD v_mon_txr:1; + REGWORD reserved_24:4; + REGWORD v_stio2_in:1; + REGWORD v_stio1_in:1; + } bit_r_pcm_gci_sta; // register and bitmap data + typedef union {REGWORD reg; bit_r_pcm_gci_sta bit;} reg_r_pcm_gci_sta; // register and bitmap access + + +#define R_MON1_RX 0x2A // register access + #define M_MON1_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_mon1_rx:8; + } bit_r_mon1_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_mon1_rx bit;} reg_r_mon1_rx; // register and bitmap access + + +#define R_MON1_TX 0x2A // register access + #define M_MON1_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_mon1_tx:8; + } bit_r_mon1_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_mon1_tx bit;} reg_r_mon1_tx; // register and bitmap access + + +#define R_MON2_RX 0x2B // register access + #define M_MON2_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_mon2_rx:8; + } bit_r_mon2_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_mon2_rx bit;} reg_r_mon2_rx; // register and bitmap access + + +#define R_MON2_TX 0x2B // register access + #define M_MON2_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_mon2_tx:8; + } bit_r_mon2_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_mon2_tx bit;} reg_r_mon2_tx; // register and bitmap access + + +#define R_B1_RX 0x2C // register access + #define M_B1_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_b1_rx:8; + } bit_r_b1_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_b1_rx bit;} reg_r_b1_rx; // register and bitmap access + + +#define R_B1_TX 0x2C // register access + #define M_B1_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_b1_tx:8; + } bit_r_b1_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_b1_tx bit;} reg_r_b1_tx; // register and bitmap access + + +#define R_B2_RX 0x2D // register access + #define M_B2_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_b2_rx:8; + } bit_r_b2_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_b2_rx bit;} reg_r_b2_rx; // register and bitmap access + + +#define R_B2_TX 0x2D // register access + #define M_B2_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_b2_tx:8; + } bit_r_b2_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_b2_tx bit;} reg_r_b2_tx; // register and bitmap access + + +#define R_AUX1_RX 0x2E // register access + #define M_AUX1_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_aux1_rx:8; + } bit_r_aux1_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_aux1_rx bit;} reg_r_aux1_rx; // register and bitmap access + + +#define R_AUX1_TX 0x2E // register access + #define M_AUX1_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_aux1_tx:8; + } bit_r_aux1_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_aux1_tx bit;} reg_r_aux1_tx; // register and bitmap access + + +#define R_AUX2_RX 0x2F // register access + #define M_AUX2_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_aux2_rx:8; + } bit_r_aux2_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_aux2_rx bit;} reg_r_aux2_rx; // register and bitmap access + + +#define R_AUX2_TX 0x2F // register access + #define M_AUX2_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_aux2_tx:8; + } bit_r_aux2_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_aux2_tx bit;} reg_r_aux2_tx; // register and bitmap access + + +#define R_ST_RD_STA 0x30 // register access + #define M_ST_STA 0x0F // bitmap mask (4bit) + #define M1_ST_STA 0x01 + #define M_FR_SYNC 0x10 // bitmap mask (1bit) + #define M_T2_EXP 0x20 // bitmap mask (1bit) + #define M_INFO0 0x40 // bitmap mask (1bit) + #define M_G2_G3 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_st_sta:4; + REGWORD v_fr_sync:1; + REGWORD v_t2_exp:1; + REGWORD v_info0:1; + REGWORD v_g2_g3:1; + } bit_r_st_rd_sta; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_rd_sta bit;} reg_r_st_rd_sta; // register and bitmap access + + +#define R_ST_WR_STA 0x30 // register access + #define M_ST_SET_STA 0x0F // bitmap mask (4bit) + #define M1_ST_SET_STA 0x01 + #define M_ST_LD_STA 0x10 // bitmap mask (1bit) + #define M_ST_ACT 0x60 // bitmap mask (2bit) + #define M1_ST_ACT 0x20 + #define M_SET_G2_G3 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_st_set_sta:4; + REGWORD v_st_ld_sta:1; + REGWORD v_st_act:2; + REGWORD v_set_g2_g3:1; + } bit_r_st_wr_sta; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_wr_sta bit;} reg_r_st_wr_sta; // register and bitmap access + + +#define R_ST_CTRL0 0x31 // register access + #define M_B1_EN 0x01 // bitmap mask (1bit) + #define M_B2_EN 0x02 // bitmap mask (1bit) + #define M_ST_MD 0x04 // bitmap mask (1bit) + #define M_D_PRIO 0x08 // bitmap mask (1bit) + #define M_SQ_EN 0x10 // bitmap mask (1bit) + #define M_96KHZ 0x20 // bitmap mask (1bit) + #define M_TX_LI 0x40 // bitmap mask (1bit) + #define M_ST_STOP 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_b1_en:1; + REGWORD v_b2_en:1; + REGWORD v_st_md:1; + REGWORD v_d_prio:1; + REGWORD v_sq_en:1; + REGWORD v_96khz:1; + REGWORD v_tx_li:1; + REGWORD v_st_stop:1; + } bit_r_st_ctrl0; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_ctrl0 bit;} reg_r_st_ctrl0; // register and bitmap access + + +#define R_ST_CTRL1 0x32 // register access + #define M_G2_G3_EN 0x01 // bitmap mask (1bit) + #define M_D_RES 0x04 // bitmap mask (1bit) + #define M_E_IGNO 0x08 // bitmap mask (1bit) + #define M_E_LO 0x10 // bitmap mask (1bit) + #define M_B12_SWAP 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_g2_g3_en:1; + REGWORD reserved_14:1; + REGWORD v_d_res:1; + REGWORD v_e_igno:1; + REGWORD v_e_lo:1; + REGWORD reserved_15:2; + REGWORD v_b12_swap:1; + } bit_r_st_ctrl1; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_ctrl1 bit;} reg_r_st_ctrl1; // register and bitmap access + + +#define R_ST_CTRL2 0x33 // register access + #define M_B1_RX_EN 0x01 // bitmap mask (1bit) + #define M_B2_RX_EN 0x02 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_b1_rx_en:1; + REGWORD v_b2_rx_en:1; + REGWORD reserved_16:6; + } bit_r_st_ctrl2; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_ctrl2 bit;} reg_r_st_ctrl2; // register and bitmap access + + +#define R_ST_SQ_RD 0x34 // register access + #define M_ST_SQ_RD 0x0F // bitmap mask (4bit) + #define M1_ST_SQ_RD 0x01 + #define M_MF_RX_RDY 0x10 // bitmap mask (1bit) + #define M_MF_TX_RDY 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_st_sq_rd:4; + REGWORD v_mf_rx_rdy:1; + REGWORD reserved_25:2; + REGWORD v_mf_tx_rdy:1; + } bit_r_st_sq_rd; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_sq_rd bit;} reg_r_st_sq_rd; // register and bitmap access + + +#define R_ST_SQ_WR 0x34 // register access + #define M_ST_SQ_WR 0x0F // bitmap mask (4bit) + #define M1_ST_SQ_WR 0x01 + + typedef struct // bitmap construction + { + REGWORD v_st_sq_wr:4; + REGWORD reserved_17:4; + } bit_r_st_sq_wr; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_sq_wr bit;} reg_r_st_sq_wr; // register and bitmap access + + +#define R_ST_CLK_DLY 0x37 // register access + #define M_ST_CLK_DLY 0x0F // bitmap mask (4bit) + #define M1_ST_CLK_DLY 0x01 + #define M_ST_SMPL 0x70 // bitmap mask (3bit) + #define M1_ST_SMPL 0x10 + + typedef struct // bitmap construction + { + REGWORD v_st_clk_dly:4; + REGWORD v_st_smpl:3; + REGWORD reserved_18:1; + } bit_r_st_clk_dly; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_clk_dly bit;} reg_r_st_clk_dly; // register and bitmap access + + +#define R_ST_B1_RX 0x3C // register access + #define M_ST_B1_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_st_b1_rx:8; + } bit_r_st_b1_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_b1_rx bit;} reg_r_st_b1_rx; // register and bitmap access + + +#define R_ST_B1_TX 0x3C // register access + #define M_ST_B1_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_st_b1_tx:8; + } bit_r_st_b1_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_b1_tx bit;} reg_r_st_b1_tx; // register and bitmap access + + +#define R_ST_B2_RX 0x3D // register access + #define M_ST_B2_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_st_b2_rx:8; + } bit_r_st_b2_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_b2_rx bit;} reg_r_st_b2_rx; // register and bitmap access + + +#define R_ST_B2_TX 0x3D // register access + #define M_ST_B2_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_st_b2_tx:8; + } bit_r_st_b2_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_b2_tx bit;} reg_r_st_b2_tx; // register and bitmap access + + +#define R_ST_D_RX 0x3E // register access + #define M_ST_D_RX 0xC0 // bitmap mask (2bit) + #define M1_ST_D_RX 0x40 + + typedef struct // bitmap construction + { + REGWORD reserved_26:6; + REGWORD v_st_d_rx:2; + } bit_r_st_d_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_d_rx bit;} reg_r_st_d_rx; // register and bitmap access + + +#define R_ST_D_TX 0x3E // register access + #define M_ST_D_TX 0xC0 // bitmap mask (2bit) + #define M1_ST_D_TX 0x40 + + typedef struct // bitmap construction + { + REGWORD reserved_19:6; + REGWORD v_st_d_tx:2; + } bit_r_st_d_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_d_tx bit;} reg_r_st_d_tx; // register and bitmap access + + +#define R_ST_E_RX 0x3F // register access + #define M_ST_E_RX 0xC0 // bitmap mask (2bit) + #define M1_ST_E_RX 0x40 + + typedef struct // bitmap construction + { + REGWORD reserved_27:6; + REGWORD v_st_e_rx:2; + } bit_r_st_e_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_st_e_rx bit;} reg_r_st_e_rx; // register and bitmap access + + +#define A_FIFO_DATA 0x80 // register access + #define M_FIFO_DATA 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_data:8; + } bit_a_fifo_data; // register and bitmap data + typedef union {REGWORD reg; bit_a_fifo_data bit;} reg_a_fifo_data; // register and bitmap access + + +#define A_FIFO_DATA_NOINC 0x84 // register access + #define M_FIFO_DATA_NOINC 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_data_noinc:8; + } bit_a_fifo_data_noinc; // register and bitmap data + typedef union {REGWORD reg; bit_a_fifo_data_noinc bit;} reg_a_fifo_data_noinc; // register and bitmap access + + +#define R_RAM_DATA 0xC0 // register access + #define M_RAM_DATA 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_ram_data:8; + } bit_r_ram_data; // register and bitmap data + typedef union {REGWORD reg; bit_r_ram_data bit;} reg_r_ram_data; // register and bitmap access + + +#define A_CH_MSK 0xF4 // register access + #define M_CH_MSK 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_ch_msk:8; + } bit_a_ch_msk; // register and bitmap data + typedef union {REGWORD reg; bit_a_ch_msk bit;} reg_a_ch_msk; // register and bitmap access + + +#define A_CON_HDLC 0xFA // register access + #define M_IFF 0x01 // bitmap mask (1bit) + #define M_HDLC_TRP 0x02 // bitmap mask (1bit) + #define M_TRP_IRQ 0x0C // bitmap mask (2bit) + #define M1_TRP_IRQ 0x04 + #define M_DATA_FLOW 0xE0 // bitmap mask (3bit) + #define M1_DATA_FLOW 0x20 + + typedef struct // bitmap construction + { + REGWORD v_iff:1; + REGWORD v_hdlc_trp:1; + REGWORD v_trp_irq:2; + REGWORD reserved_20:1; + REGWORD v_data_flow:3; + } bit_a_con_hdlc; // register and bitmap data + typedef union {REGWORD reg; bit_a_con_hdlc bit;} reg_a_con_hdlc; // register and bitmap access + + +#define A_HDLC_PAR 0xFB // register access + #define M_BIT_CNT 0x07 // bitmap mask (3bit) + #define M1_BIT_CNT 0x01 + #define M_START_BIT 0x38 // bitmap mask (3bit) + #define M1_START_BIT 0x08 + #define M_LOOP_FIFO 0x40 // bitmap mask (1bit) + #define M_INV_DATA 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_bit_cnt:3; + REGWORD v_start_bit:3; + REGWORD v_loop_fifo:1; + REGWORD v_inv_data:1; + } bit_a_hdlc_par; // register and bitmap data + typedef union {REGWORD reg; bit_a_hdlc_par bit;} reg_a_hdlc_par; // register and bitmap access + + +#define A_CHANNEL 0xFC // register access + #define M_CH_DIR 0x01 // bitmap mask (1bit) + #define M_CH_NUM 0x06 // bitmap mask (2bit) + #define M1_CH_NUM 0x02 + + typedef struct // bitmap construction + { + REGWORD v_ch_dir:1; + REGWORD v_ch_num:2; + REGWORD reserved_21:5; + } bit_a_channel; // register and bitmap data + typedef union {REGWORD reg; bit_a_channel bit;} reg_a_channel; // register and bitmap access + + +#endif /* _HFCSMCC_H_ */ + +/*___________________________________________________________________________________*/ +/* */ +/* End of HFC-S mini register definitions. */ +/* */ +/* Total number of registers processed: 71 of 71 */ +/* Total number of bitmaps processed : 209 */ +/* */ +/*___________________________________________________________________________________*/ +/* */ diff --git a/drivers/isdn/hardware/mISDN/hw_lock.h b/drivers/isdn/hardware/mISDN/hw_lock.h deleted file mode 100644 index 9716701..0000000 --- a/drivers/isdn/hardware/mISDN/hw_lock.h +++ /dev/null @@ -1,182 +0,0 @@ -/* $Id$ - * - * hw_lock.h Hardware locking inline routines - * - * Author Karsten Keil (keil@isdn4linux.de) - * - * This file is (c) under GNU PUBLIC LICENSE - * -*/ - -/* Description of the locking mechanism - * - * The locking must grant serialisized and atomic - * access to the ISDN hardware registers, if the lock - * is aquired no other process or IRQ is alloed to - * access ISDN hardware registers. - * - * In general here are 3 possible entry points: - * 1. the ISDN interrupt routine - * 2. ISDN timer routines in the hardware module - * 3. messages that came from upper layers - * - * Since most work must be do in the interrupt routine - * (to grant minimum IRQ latency) and only few things with - * need direct HW access must be done for messages from upper - * layers, we should allow other IRQs in our IRQ routines and - * only block our own routines in this case. Since the common IRQ - * routines allready mask the same IRQ, we only need to protect us - * from timer and uper layers. The disadvantage is, that we need to - * disable local IRQ for entry points 2. and 3., but since the routines - * which need hardware access are well known and small, the impact - * is very small. - * - * We have a two stage locking to make this working: - * A spinlock which protect the state LOCK Flag (STATE_FLAG_BUSY) and - * also protect us from local IRQs from the entry points 2 and 3. - * - * In the hardware IRQ we aquire the spinlock, set the STATE_FLAG_BUSY - * LOCK Flag and then release the spinlock. It can never happen that - * the STATE_FLAG_BUSY is already set in this case, see later. - * - * In the other cases (from timer or upper layers) we aquire the spinlock - * test_and_set the STATE_FLAG_BUSY LOCK Flag, if it was allready set - * (a ISDN IRQ is running on the other CPU) we schedule timeout or add a other - * small timeout. - * If it was not set, we have the lock and we don't release the spinlock until we have - * done the harware work. - * - * To avoid any kind of deadlocking, it is important that we release the lock - * before we call functions that deliver to upper layers. - * To leave the impact of disabled local IRQ small, it is important to only protect - * small areas where hardware is accessed. - * - * The following routines handle the lock in the entry point from upper layers and other - * none IRQ cases (module init/exit stuff). - * - * They never called directly, but via the wrappers assigned to the instance - * inst.lock / inst.unlock pointers. - * - * Here are two defines which can be used for DEBUGING and PROFILING - * SPIN_DEBUG and LOCK_STATISTIC - * - */ -#ifndef __hw_lock__ -#define __hw_lock__ - -typedef struct _mISDN_HWlock { - u_long flags; - spinlock_t lock; -#ifdef SPIN_DEBUG - void *spin_adr; - void *busy_adr; -#endif - volatile u_long state; -#ifdef LOCK_STATISTIC - u_int try_ok; - u_int try_wait; - u_int try_inirq; - u_int try_mult; - u_int irq_ok; - u_int irq_fail; -#endif -} mISDN_HWlock_t; - -#define STATE_FLAG_BUSY 1 -#define STATE_FLAG_INIRQ 2 - - -/* - * returns 0 if the lock was aquired - * returns 1 if nowait != 0 and the lock is not aquired - */ -static inline int lock_HW(mISDN_HWlock_t *lock, int nowait) -{ - u_long flags; -#ifdef LOCK_STATISTIC - int wait = 0; -#endif - - spin_lock_irqsave(&lock->lock, flags); -#ifdef SPIN_DEBUG - lock->spin_adr = __builtin_return_address(0); -#endif - while (test_and_set_bit(STATE_FLAG_BUSY, &lock->state)) { - /* allready busy so we delay */ - spin_unlock_irqrestore(&lock->lock, flags); -#ifdef SPIN_DEBUG - lock->spin_adr = NULL; -#endif - if (nowait) { -#ifdef LOCK_STATISTIC - lock->try_wait++; -#endif - return(1); - } - /* delay 1 jiffie is enought */ - if (in_interrupt()) { - /* Should never happen */ -#ifdef LOCK_STATISTIC - lock->try_inirq++; -#endif - printk(KERN_ERR "lock_HW: try to schedule in IRQ state(%lx)\n", - lock->state); - mdelay(1); - } else { -#ifdef LOCK_STATISTIC - if (wait++) - lock->try_mult++; - else - lock->try_wait++; -#endif - schedule_timeout(1); - } - spin_lock_irqsave(&lock->lock, flags); -#ifdef SPIN_DEBUG - lock->spin_adr = __builtin_return_address(0); -#endif - } - /* get the LOCK */ - lock->flags = flags; -#ifdef SPIN_DEBUG - lock->busy_adr = __builtin_return_address(0); -#endif -#ifdef LOCK_STATISTIC - if (!wait) - lock->try_ok++; -#endif - return(0); -} - -static inline void unlock_HW(mISDN_HWlock_t *lock) -{ - if (!test_and_clear_bit(STATE_FLAG_BUSY, &lock->state)) { - printk(KERN_ERR "lock_HW: STATE_FLAG_BUSY not locked state(%lx)\n", - lock->state); - } -#ifdef SPIN_DEBUG - lock->busy_adr = NULL; - lock->spin_adr = NULL; -#endif - spin_unlock_irqrestore(&lock->lock, lock->flags); -} - -static inline void lock_HW_init(mISDN_HWlock_t *lock) -{ - spin_lock_init(&lock->lock); - lock->state = 0; -#ifdef SPIN_DEBUG - lock->busy_adr = NULL; - lock->spin_adr = NULL; -#endif -#ifdef LOCK_STATISTIC - lock->try_ok = 0; - lock->try_wait = 0; - lock->try_inirq = 0; - lock->try_mult = 0; - lock->irq_ok = 0; - lock->irq_fail = 0; -#endif -} - -#endif diff --git a/drivers/isdn/hardware/mISDN/l1oip.c b/drivers/isdn/hardware/mISDN/l1oip.c new file mode 100644 index 0000000..59c30ec --- /dev/null +++ b/drivers/isdn/hardware/mISDN/l1oip.c @@ -0,0 +1,1196 @@ +/* + + * l1oip.c low level driver for tunneling layer 1 over IP + * + * NOTE: It is not compatible with TDMoIP nor "ISDN over IP". + * + * Author Andreas Eversberg (jolly@jolly.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. + * + */ + +/* module parameters: + * type: + Value 1 = BRI + Value 2 = PRI + Value 3 = BRI (multi channel frame) + Value 4 = PRI (multi channel frame) + A multi channel frame reduces overhead to a single frame for all + b-channels, but increases delay. + + * codec: + Value 0 = aLaw transparent + Value 1 = uLaw transparent (instead of aLaw) + Value 2 = aLaw to TADPCM + Value 3 = uLaw to TADPCM + + * protocol: + Bit 0-3 = protocol + Bit 4 = NT-Mode + Bit 5 = PTP (instead of multipoint) + + * layermask: + NOTE: Must be given for all ports, not for the number of cards. + mask of layers to be used for D-channel stack + + * limit: + limitation of bchannels to control bandwidth (1...29) + + * ip: + binary representation of remote ip address (127.0.0.0 -> 0x7f000001) + If not given, no remote address is set. + + * port: + port number + If not given or 0, port 931 is used. + + * id: + mandatory value to identify frames. This value must be equal on both + peers and should be random. + + * debug: + NOTE: only one debug value must be given for all cards + enable debugging (see l1oip.h for debug options) + + +Special PH_CONTROL messages: + + dinfo = L1OIP_SETPEER + data bytes 0-3 : IP address in network order (MSB first) + data bytes 4-5 : local port in network order + data bytes 6-7 : remote port in network order + + dinfo = L1OIP_UNSETPEER + + * Use l1oipctrl for setting or removin ip address + + +L1oIP-Protocol +-------------- + +The Layer 1 over IP protocol tunnels frames and audio streams over IP. It will +be directly attached to the layer 2 or interconnected to layer 1 of a different +stack. + +It also provides layer 1 control and keeps dynamic IP connectivity up. + +Frame structure: + ++---------------------------------------------------------------+ +| ID | ++---------------+---------------+-------------------------------+ +| Coding |B| Channel | Time Base / Layer 1 message | ++---------------+---------------+-------------------------------+ +| Channel Map | ++---------------------------------------------------------------+ +| | +. Data . +. . + + +The "ID" should be a random number. It makes shure that missrouted frames get +dropped due to wrong id. Also it provides simple security agains DOS attacs. + +The "Coding" byte defines the data format. It can be + +0 HDLC-data +1 TADPCM (table ADPCM) +2 A-law +3 u-law + +The "B"-Flag shows the interface type: +0 BRI +1 PRI + +The "Channel" will give the timeslot or channel number. + +0 Layer 1 message +1-2 B-Channel for BRI interface +1-15 B-Channel 1-15 for PRI interface +16 D-Channel for PRI and BRI interface +17-31 B-Channel 17-31 for PRI interface +127 B-Channels as given by "Channel Map" + +The "Time Base" is used to rearange packets and to detect packet loss. +The 16 bits are sent in network order (MSB first) and count 1/8000 th of a +second. This causes a wrap arround each 8,192 seconds. There is no requirement +for the initial "Time Base", but 0 should be used for the first packet. + +The "Channel Map" are 4 bytes in network order (MSB first). They only exist, if +the Channel Map was selected with the Channel value. Bits 1-31 represent the +existance of data for each channel Bit 0 is not used and shall be 0. +The length of each channel data is defined by the total number of bytes +divided by the number of bits set in the Channel Map. The coding and length must +be equal for all existing channels. +NOTE: D-Channel data must be sent via seperate frame, because length and +coding are differen. Also packet mode data should be sent in a seperate frame. + +The total length of data is defined by the maximum packet size (without header). + + +Validity check at the receiver: Packets will be dropped if + +- the length is less than 4 bytes. +- there is no data in the packet. +- the Coding is not supported. False coding should produce a warning once. +- the B-flag does not equal the expected interface type. +- the channel is out of range. +- the channel does not exists by interface. A warning should be produced once. +- the channel map is selected, but length is less than 8 bytes. +- the channel map's bit 0 is set if channel map is selected. +- the channel map is completely 0, but the packet has data anyway. +- the length of data is not a multiple of the channels indicated by channel map. +- the data exceeds the maximum frame length of the ISDN driver. +- the layer 1 message is unknown. + +Layer 1 Message: + +This is a special type of frame. In this case the "Time Base" contains two +bytes with the message. The first byte (MSB) contains the sequence number and the +second byte the message. + +The sequence number is used to detect the reception of a message. If the message +is received, the new sequence number is acknowledged using message 0 (keepalive) +If the keepalive is received with the sequence number last sent, the next +message can be sent with incremented sequence number. + +If no message is to be sent, the sequence number is not incremented and the +last sequence number is repeated. + +The keepalive is sent every 10 seconds. If a message is about to be sent, the +message is repeated every second until the keepalive is received with the +incremented sequence number. + +0,x IP link keepalive. X is a sequence to detect packet loss. +1,1 Activate layer 1 +1,0 Deactivate layer 1 +2,1 AIS on (alarm on the remote interface) +2,0 AIS off +3,1 Maintainance blocked +3,0 Maintainance unblocked +16,x Application specific information. +32,0 Announce new IP +32,1 Acknowledge new IP + +IP Announcement: + +One or both sides may have dynamic IP address. A simple trick is used to get +the remote IP if it changes. It is assumed, that both sides don't change their +IP at the same time. + +If IP changes, the peer must announce it's new IP address. A layer 1 message +with the new IP address (4 extra bytes) and the "peer's password" (more extra +bytes). +The transmission interval is one second. +The remote peer will receive the new IP address with the password. If the +password matches, the new IP will be used. The passwort is used to prevent +"take over" connections. An acknowledge will be generated, by the remote peer +to make the local peer stop sending "Announces". + +The initial value will be given by application. It is only required for one +peer to give the initial IP address. After an IP address is given, it will be +announced. + +*/ + + +#include +#include +#include + +#include "dchannel.h" +#include "bchannel.h" +#include "layer1.h" +#include "dsp.h" +#include "debug.h" +#include + +#include "l1oip.h" + +//static void ph_state_change(dchannel_t *dch); + +extern const char *CardType[]; + +static const char *l1oip_revision = "$Revision$"; + +static int l1oip_cnt; + +static mISDNobject_t l1oip_obj; + +static char l1oipName[] = "Layer1oIP"; + + +/****************/ +/* module stuff */ +/****************/ + +#define MAX_CARDS 16 +#define MODULE_CARDS_T "1-16i" +static u_int type[MAX_CARDS]; +static u_int codec[MAX_CARDS]; +static u_int protocol[MAX_CARDS]; +static int layermask[MAX_CARDS]; +static int debug; + +#ifdef MODULE +MODULE_AUTHOR("Andreas Eversberg"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(type, MODULE_CARDS_T); +MODULE_PARM(codec, MODULE_CARDS_T); +MODULE_PARM(protocol, MODULE_CARDS_T); +MODULE_PARM(layermask, MODULE_CARDS_T); +MODULE_PARM(debug, "1i"); +#endif + + +/********************/ +/* D-channel access */ +/********************/ + +locking bedenken +/* message transfer from layer 2 + */ +static int l1oip_dchannel(mISDNinstance_t *inst, struct sk_buff *skb) +{ + dchannel_t *dch = container_of(inst, dchannel_t, inst); + l1oip_t *hc; + int ret = 0; + mISDN_head_t *hh; + u_long flags; + + hh = mISDN_HEAD_P(skb); + hc = dch->inst.privat; + if (hh->prim == PH_DATA_REQ) { + /* check oversize */ + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__); + return(-EINVAL); + } + if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > MAX_L1OIP_LEN) { + printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__); + return(-EINVAL); + } + /* check for pending next_skb */ + spin_lock_irqsave(inst->hwlock, flags); + if (dch->next_skb) { + printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", + __FUNCTION__, skb->len, dch->next_skb->len); + spin_unlock_irqrestore(inst->hwlock, flags); + return(-EBUSY); + } + if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) { + test_and_set_bit(FLG_TX_NEXT, &dch->DFlags); + dch->next_skb = skb; + spin_unlock_irqrestore(inst->hwlock, flags); + return(0); + } + /* send/queue frame */ + l1oip_tx(hc, 16, skb, CODEC_L1OIP_DATA); + spin_unlock_irqrestore(inst->hwlock, flags); + skb_trim(skb, 0); + return(mISDN_queueup_newhead(inst, 0, PH_DATA_CNF,hh->dinfo, skb)); + } else if (hh->prim == (PH_CONTROL | REQUEST)) { + spin_lock_irqsave(inst->hwlock, flags); + switch (hh->dinfo) { + case L1OIP_SETPEER: + lkkllkö + return(mISDN_queueup_newhead(inst, 0, PH_CONTROL | INDICATION, L1OIP_SETPEER, skb)); + break; + + case L1OIP_UNSETPEER: + lkkllkö + return(mISDN_queueup_newhead(inst, 0, PH_CONTROL | INDICATION, L1OIP_UNSETPEER, skb)); + break; + + default: + printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); + ret = -EINVAL; + } + spin_unlock_irqrestore(inst->hwlock, flags); + } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { + if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->pri?30:2); + spin_lock_irqsave(inst->hwlock, flags); + /* start activation */ + if (pri) { + //dchannel_sched_event(dch, D_L1STATECHANGE); + ph_state_change(dch); + if (debug & DEBUG_L1OIP_STATE) + printk(KERN_DEBUG "%s: E1 report state %x \n", __FUNCTION__, dch->ph_state); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); /* G1 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 1); + HFC_outb(hc, A_ST_WR_STATE, 1 | (V_ST_ACT*3)); /* activate */ + dch->ph_state = 1; + } + spin_unlock_irqrestore(inst->hwlock, flags); + } else { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->pri?30:2); + ret = -EINVAL; + } + } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { + if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: PH_DEACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->pri?30:2); + spin_lock_irqsave(inst->hwlock, flags); + hw_deactivate: /* after lock */ + dch->ph_state = 0; + /* start deactivation */ + if (hc->pri) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: PH_DEACTIVATE no BRI\n", __FUNCTION__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); /* deactivate */ + } + if (dch->next_skb) { + dev_kfree_skb(dch->next_skb); + dch->next_skb = NULL; + } + dch->tx_idx = dch->tx_len = hc->chan[dch->channel].rx_idx = 0; + test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags); + test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); + if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) + del_timer(&dch->dbusytimer); + spin_unlock_irqrestore(inst->hwlock, flags); + } else { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: PH_DEACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->pri?30:2); + ret = -EINVAL; + } + } else + if (hh->prim == MGR_SHORTSTATUS) { + if(hh->dinfo==SSTATUS_ALL || hh->dinfo==SSTATUS_L1) { + int new_addr; + if(hh->dinfo&SSTATUS_BROADCAST_BIT) new_addr= dch->inst.id | MSG_BROADCAST; + else new_addr=hh->addr | FLG_MSG_TARGET; + return(mISDN_queueup_newhead(inst, new_addr, MGR_SHORTSTATUS,(dch->l1_up) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED, skb)); + } + } else { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: unknown prim %x\n", __FUNCTION__, hh->prim); + ret = -EINVAL; + } + if (!ret) { +// printk("1\n"); + dev_kfree_skb(skb); +// printk("2\n"); + } + return(ret); +} + + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ + +/* messages from layer 2 to layer 1 are processed here. + */ +static int +l1oip_bchannel(mISDNinstance_t *inst, struct sk_buff *skb) +{ + u_long flags, num; + int slot_tx, slot_rx, bank_tx, bank_rx; + bchannel_t *bch = container_of(inst, bchannel_t, inst); + int ret = -EINVAL; + mISDN_head_t *hh; + hfc_multi_t *hc; + struct dsp_features *features; + + hh = mISDN_HEAD_P(skb); + hc = bch->inst.privat; + + if ((hh->prim == PH_DATA_REQ) + || (hh->prim == (DL_DATA | REQUEST))) { + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__); + return(-EINVAL); + } + if (skb->len > MAX_DATA_MEM) { + printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__); + return(-EINVAL); + } + /* check for pending next_skb */ + spin_lock_irqsave(inst->hwlock, flags); + if (bch->next_skb) { + printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", __FUNCTION__, skb->len, bch->next_skb->len); + spin_unlock_irqrestore(inst->hwlock, flags); + return(-EBUSY); + } + /* if we have currently a pending tx skb */ + if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) { + test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag); + bch->next_skb = skb; + spin_unlock_irqrestore(inst->hwlock, flags); + return(0); + } + /* write to fifo */ + bch->tx_len = skb->len; + memcpy(bch->tx_buf, skb->data, bch->tx_len); + bch->tx_idx = 0; + hfcmulti_tx(hc, bch->channel, NULL, bch); + /* start fifo */ + HFC_outb_(hc, R_FIFO, 0); + HFC_wait_(hc); + spin_unlock_irqrestore(inst->hwlock, flags); +#ifdef FIXME // TODO changed + if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + && bch->dev) + hif = &bch->dev->rport.pif; + else + hif = &bch->inst.up; +#endif + skb_trim(skb, 0); + return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, hh->dinfo, skb)); + } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) + || (hh->prim == (DL_ESTABLISH | REQUEST))) { + /* activate B-channel if not already activated */ + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel); + if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag)) + ret = 0; + else { + spin_lock_irqsave(inst->hwlock, flags); + ret = mode_hfcmulti(hc, bch->channel, bch->inst.pid.protocol[1], hc->chan[bch->channel].slot_tx, hc->chan[bch->channel].bank_tx, hc->chan[bch->channel].slot_rx, hc->chan[bch->channel].bank_rx); + if (!ret) { + bch->protocol = bch->inst.pid.protocol[1]; + if (bch->protocol==ISDN_PID_L1_B_64TRANS && !hc->dtmf) { + /* start decoder */ + hc->dtmf = 1; + if (debug & DEBUG_L1OIP_DTMF) + printk(KERN_DEBUG "%s: start dtmf decoder\n", __FUNCTION__); + HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); + } + } + spin_unlock_irqrestore(inst->hwlock, flags); + } +#ifdef FIXME // TODO changed + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); +#endif + skb_trim(skb, 0); + return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, ret, skb)); + } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) + || (hh->prim == (DL_RELEASE | REQUEST)) + || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: PH_DEACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel); + /* deactivate B-channel if not already deactivated */ + spin_lock_irqsave(inst->hwlock, flags); + if (bch->next_skb) { + test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag); + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + bch->tx_idx = bch->tx_len = bch->rx_idx = 0; + test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); + hc->chan[bch->channel].slot_tx = -1; + hc->chan[bch->channel].slot_rx = -1; + hc->chan[bch->channel].conf = -1; + mode_hfcmulti(hc, bch->channel, ISDN_PID_NONE, hc->chan[bch->channel].slot_tx, hc->chan[bch->channel].bank_tx, hc->chan[bch->channel].slot_rx, hc->chan[bch->channel].bank_rx); + bch->protocol = ISDN_PID_NONE; + test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag); + spin_unlock_irqrestore(inst->hwlock, flags); + skb_trim(skb, 0); +//printk("5\n"); + if (hh->prim != (PH_CONTROL | REQUEST)) { +#ifdef FIXME // TODO changed + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); +#endif + return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, ret, skb)); +//printk("6\n"); + } +//printk("7\n"); + ret = 0; + } else + if (hh->prim == (PH_CONTROL | REQUEST)) { + spin_lock_irqsave(inst->hwlock, flags); + switch (hh->dinfo) { + /* fill features structure */ + case HW_FEATURES: + if (skb->len != sizeof(void *)) { + printk(KERN_WARNING "%s: HW_FEATURES lacks parameters\n", __FUNCTION__); + break; + } + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: HW_FEATURE request\n", __FUNCTION__); + features = *((struct dsp_features **)skb->data); + features->hfc_id = hc->id; + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) + features->hfc_dtmf = 1; + features->hfc_loops = 0; + features->pcm_id = hc->pcm; + features->pcm_slots = hc->slots; + features->pcm_banks = 2; + ret = 0; + break; + + /* connect interface to pcm timeslot (0..N) */ + case HW_PCM_CONN: + if (skb->len < 4*sizeof(u_long)) { + printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__); + break; + } + slot_tx = ((int *)skb->data)[0]; + bank_tx = ((int *)skb->data)[1]; + slot_rx = ((int *)skb->data)[2]; + bank_rx = ((int *)skb->data)[3]; + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); + if (slot_tx<=hc->slots && bank_tx<=2 && slot_rx<=hc->slots && bank_rx<=2) + hfcmulti_pcm(hc, bch->channel, slot_tx, bank_tx, slot_rx, bank_rx); + else + printk(KERN_WARNING "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX) out of range\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); + ret = 0; + break; + + /* release interface from pcm timeslot */ + case HW_PCM_DISC: + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: HW_PCM_DISC\n", __FUNCTION__); + hfcmulti_pcm(hc, bch->channel, -1, -1, -1, -1); + ret = 0; + break; + + /* join conference (0..7) */ + case HW_CONF_JOIN: + if (skb->len < sizeof(u_long)) { + printk(KERN_WARNING "%s: HW_CONF_JOIN lacks parameters\n", __FUNCTION__); + break; + } + num = ((u_long *)skb->data)[0]; + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: HW_CONF_JOIN conf %ld\n", __FUNCTION__, num); + if (num <= 7) { + hfcmulti_conf(hc, bch->channel, num); + ret = 0; + } else + printk(KERN_WARNING "%s: HW_CONF_JOIN conf %ld out of range\n", __FUNCTION__, num); + break; + + /* split conference */ + case HW_CONF_SPLIT: + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: HW_CONF_SPLIT\n", __FUNCTION__); + hfcmulti_conf(hc, bch->channel, -1); + ret = 0; + break; + + /* set sample loop */ + case HW_SPL_LOOP_ON: + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: HW_SPL_LOOP_ON (len = %d)\n", __FUNCTION__, skb->len); + hfcmulti_splloop(hc, bch->channel, skb->data, skb->len); + ret = 0; + break; + + /* set silence */ + case HW_SPL_LOOP_OFF: + if (debug & DEBUG_L1OIP_MSG) + printk(KERN_DEBUG "%s: HW_SPL_LOOP_OFF\n", __FUNCTION__); + hfcmulti_splloop(hc, bch->channel, NULL, 0); + ret = 0; + break; + + default: + printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); + ret = -EINVAL; + } + spin_unlock_irqrestore(inst->hwlock, flags); + } else { + printk(KERN_WARNING "%s: unknown prim(%x)\n", __FUNCTION__, hh->prim); + ret = -EINVAL; + } + if (!ret) { +// printk("3\n"); + dev_kfree_skb(skb); +// printk("4\n"); + } + return(ret); +} + + + +/* MGR stuff */ + +static int +l1oip_manager(void *data, u_int prim, void *arg) +{ + hfc_multi_t *hc; + mISDNinstance_t *inst = data; + struct sk_buff *skb; + dchannel_t *dch = NULL; + bchannel_t *bch = NULL; + int ch; + int i; + u_long flags; + + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim,arg,&HFCM_obj) + printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); + return(-EINVAL); + } + + /* find channel and card */ + spin_lock_irqsave(&HFCM_obj.lock, flags); + if (hc->dch[i]) + if (&hc->dch[i]->inst == inst) { + dch = hc->dch[i]; + ch = dch->channel; + break; + } + list_for_each_entry(hc, &HFCM_obj.ilist, list) { + i = 0; + while(i < 30) { +//printk(KERN_DEBUG "comparing (D-channel) card=%08x inst=%08x with inst=%08x\n", hc, &hc->dch[i].inst, inst); + if (hc->bch[i]) + if (&hc->bch[i]->inst == inst) { + bch = hc->bch[i]; + ch = dch->channel; + goto found; + } + i++; + } + } + spin_unlock_irqrestore(&HFCM_obj.lock, flags); + printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); + return(-EINVAL); + + found: + spin_unlock_irqrestore(&HFCM_obj.lock, flags); + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: channel %d (0..31) data %p prim %x arg %p\n", __FUNCTION__, ch, data, prim, arg); + + switch(prim) { + case MGR_REGLAYER | CONFIRM: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_REGLAYER\n", __FUNCTION__); + if (dch) + dch_set_para(dch, &inst->st->para); + if (bch) + bch_set_para(bch, &inst->st->para); + break; + + case MGR_UNREGLAYER | REQUEST: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_UNREGLAYER\n", __FUNCTION__); + if (dch) { + if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { + if (hfcmulti_l1hw(inst, skb)) dev_kfree_skb(skb); + } + } else + if (bch) { + if ((skb = create_link_skb(PH_CONTROL | REQUEST, 0, 0, NULL, 0))) { + if (hfcmulti_l2l1(inst, skb)) dev_kfree_skb(skb); + } + } + HFCM_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + // fall through + case MGR_ADDSTPARA | INDICATION: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_***STPARA\n", __FUNCTION__); + if (dch) + dch_set_para(dch, arg); + if (bch) + bch_set_para(bch, arg); + break; + + case MGR_RELEASE | INDICATION: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_RELEASE = remove port from mISDN\n", __FUNCTION__); + if (dch) { + release_network(hc); + release_card(hc); + } + break; +#ifdef FIXME + case MGR_CONNECT | REQUEST: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_CONNECT\n", __FUNCTION__); + return(mISDN_ConnectIF(inst, arg)); + + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_SETIF\n", __FUNCTION__); + if (dch) + return(mISDN_SetIF(inst, arg, prim, hfcmulti_l1hw, NULL, dch)); + if (bch) + return(mISDN_SetIF(inst, arg, prim, hfcmulti_l2l1, NULL, bch)); + break; + + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_DISCONNECT\n", __FUNCTION__); + return(mISDN_DisConnectIF(inst, arg)); +#endif + case MGR_SELCHANNEL | REQUEST: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_SELCHANNEL\n", __FUNCTION__); + if (!dch) { + printk(KERN_WARNING "%s(MGR_SELCHANNEL|REQUEST): selchannel not dinst\n", __FUNCTION__); + return(-EINVAL); + } + return(SelFreeBChannel(hc, ch, arg)); + + case MGR_SETSTACK | INDICATION: + if (debug & DEBUG_L1OIP_MGR) + printk(KERN_DEBUG "%s: MGR_SETSTACK\n", __FUNCTION__); + if (bch && inst->pid.global==2) { + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { + if (hfcmulti_l2l1(inst, skb)) dev_kfree_skb(skb); + } + if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) + mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); + else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); + } + break; + + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST); + default: + printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim); + return(-EINVAL); + } + return(0); +} + + +/************************** + * remove card from stack * + **************************/ + +static void +release_card(hfc_multi_t *hc) +{ + int i = 0; + u_long flags; + + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); + + if (hc->dch) { + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: free port %d D-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, i); + mISDN_free_dch(hc->chan[i].dch); + HFCM_obj.ctrl(&hc->chan[i].dch->inst, MGR_UNREGLAYER | REQUEST, NULL); + kfree(hc->chan[i].dch); + hc->chan[i].dch = NULL; + } +// if (hc->chan[i].rx_buf) { +// kfree(hc->chan[i].rx_buf); +// hc->chan[i].rx_buf = NULL; +// } + i = 0; + while(i < 30) { + if (hc->bch[i]) { + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: free port %d B-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, hc->bch[i].channel); + mISDN_free_bch(hc->bch[i]); + kfree(hc->bch[i]); + hc->bch[i] = NULL; + } + i++; + } + + /* remove us from list and delete */ + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_WARNING "%s: remove instance from list\n", __FUNCTION__); + spin_lock_irqsave(&HFCM_obj.lock, flags); + list_del(&hc->list); + spin_unlock_irqrestore(&HFCM_obj.lock, flags); + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_WARNING "%s: delete instance\n", __FUNCTION__); + kfree(hc); + HFC_cnt--; + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_WARNING "%s: card successfully removed\n", __FUNCTION__); +} + +static void __exit +l1oip_cleanup(void) +{ + l1oip_t *hc,*next; + int err; + + /* unregister mISDN object */ + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: entered (refcnt = %d l1oip_cnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt, l1oip_cnt); + if ((err = mISDN_unregister(&l1oip_obj))) { + printk(KERN_ERR "Can't unregister L1oIP error(%d)\n", err); + } + + /* remove remaining devices, but this should never happen */ + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: now checking ilist (refcnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt); + + list_for_each_entry_safe(hc, next, &l1oip_obj.ilist, list) { + printk(KERN_ERR "L1oIP devices struct not empty refs %d\n", l1oip_obj.refcnt); + release_network(hc); + release_card(hc); + } + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: done (refcnt = %d l1oip_cnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt, l1oip_cnt); +} + +static int __init +l1oip_init(void) +{ + int err, i; + char tmpstr[64]; + +#if !defined(CONFIG_HOTPLUG) || !defined(MODULE) +#error "CONFIG_HOTPLUG and MODULE are not defined." +#endif + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: init entered\n", __FUNCTION__); + +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif + strcpy(tmpstr, l1oip_revision); + printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n", mISDN_getrev(tmpstr)); + + memset(&l1oip_obj, 0, sizeof(l1oip_obj)); +#ifdef MODULE + l1oip_obj.owner = THIS_MODULE; +#endif + spin_lock_init(&l1oip_obj.lock); + INIT_LIST_HEAD(&l1oip_obj.ilist); + l1oip_obj.name = l1oipName; + l1oip_obj.own_ctrl = l1oip_manager; + l1oip_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0 + | ISDN_PID_L0_TE_E1 | ISDN_PID_L0_NT_E1; + l1oip_obj.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0 | ISDN_PID_L1_NT_S0 + | ISDN_PID_L1_TE_E1 | ISDN_PID_L1_NT_E1; + l1oip_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; + l1oip_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; + + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: registering l1oip_obj\n", __FUNCTION__); + if ((err = mISDN_register(&l1oip_obj))) { + printk(KERN_ERR "Can't register L1oIP error(%d)\n", err); + return(err); + } + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: new mISDN object (refcnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt); + + l1oip_cnt = 0; + + /* check card type */ + switch (type[l1oip_cnt] & 0xff) { + case 1: + pri = 0; + multichannel = 0; + break; + + case 2: + pri = 1; + multichannel = 0; + break; + + case 3: + pri = 0; + multichannel = 1; + break; + + case 4: + pri = 1; + multichannel = 1; + break; + + case 0: + printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt); + return(0); + + default: + printk(KERN_ERR "Card type(%d) not supported.\n", type[HFC_idx] & 0xff); + ret_err = -EINVAL; + goto free_object; + } + + + /* allocate card+fifo structure */ + if (!(hc = kmalloc(sizeof(l1oip_t), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for L1-over-IP driver.\n"); + ret_err = -ENOMEM; + goto free_object; + } + memset(hc, 0, sizeof(hfc_multi_t)); + hc->idx = l1oip_cnt; + hc->pri = pri; + hc->multichannel = multichannel; + hc->limit = limit[l1oip_cnt]; + + if (hc->pri) + sprintf(hc->name, "L1oIP-E1#%d", HFC_cnt+1); + else + sprintf(hc->name, "L1oIP-S0#%d", HFC_cnt+1); + + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); + + spin_lock_irqsave(&HFCM_obj.lock, flags); + list_add_tail(&hc->list, &HFCM_obj.ilist); + spin_unlock_irqrestore(&HFCM_obj.lock, flags); + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); + + spin_lock_init(&hc->lock); + + /* check codec */ + switch (codec[l1oip_cnt]) { + case 0: + break; + + case 1: + hc->ulaw = 1; + break; + + case 2: + hc->tadpcm = 1; + break; + + case 3: + hc->ulaw = 1; + hc->tadpcm = 1; + break; + + default: + printk(KERN_ERR "Codec(%d) not supported.\n", codec[l1oip_cnt]); + ret_err = -EINVAL; + goto free_channels; + } + + if (id[l1oip_cnt] == 0) { + printk(KERN_ERR "No 'id' value given. Please use 32 bit randmom number 0x...\n"); + ret_err = -EINVAL; + goto free_channels; + } + + /* check protocol */ + if (protocol[l1oip_cnt] == 0) { + printk(KERN_ERR "No 'protocol' value given.\n"); + ret_err = -EINVAL; + goto free_channels; + } + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: Registering D-channel, card(%d) protocol(%x)\n", __FUNCTION__, l1oip_cnt+1, protocol[l1oip_cnt]); + dch = kmalloc(sizeof(dchannel_t), GFP_ATOMIC); + if (!dch) { + ret_err = -ENOMEM; + goto free_channels; + } + memset(dch, 0, sizeof(dchannel_t)); + if (hc->pri) + dch->channel = 16; + //dch->debug = debug; + dch->inst.obj = &l1oip_obj; + dch->inst.hwlock = &hc->lock; + dch->inst.class_dev.dev = &pdev->dev; + mISDN_init_instance(&dch->inst, &l1oip_obj, hc, l1oip_dchannel); + dch->inst.pid.layermask = ISDN_LAYER(0); + sprintf(dch->inst.name, "L1OIP%d", l1oip_cnt); +// if (!(hc->chan[ch].rx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) { +// ret_err = -ENOMEM; +// goto free_channels; +// } + if (mISDN_init_dch(dch)) { + ret_err = -ENOMEM; + goto free_channels; + } + hc->dch = dch; + + i=0; + while(i < ((hc->pri)?30:2)) { + if (hc->pri) + ch = i + 1 + (i>=15); + else + ch = i + 1; + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: Registering B-channel, card(%d) channel(%d)\n", __FUNCTION__, l1oip_cnt, ch); + bch = kmalloc(sizeof(bchannel_t), GFP_ATOMIC); + if (!bch) { + ret_err = -ENOMEM; + goto free_channels; + } + memset(bch, 0, sizeof(bchannel_t)); + bch->channel = ch; + mISDN_init_instance(&bch->inst, &l1oip_obj, hc, l1oip_bchannel); + bch->inst.pid.layermask = ISDN_LAYER(0); + bch->inst.hwlock = &hc->lock; + bch->inst.class_dev.dev = &pdev->dev; + //bch->debug = debug; + sprintf(bch->inst.name, "%s B%d", + dch->inst.name, i+1); + if (mISDN_init_bch(bch)) { + kfree(bch); + ret_err = -ENOMEM; + goto free_channels; + } + hc->bch[i] = bch; +#ifdef FIXME // TODO + if (bch->dev) { + bch->dev->wport.pif.func = l1oip_bchannel; + bch->dev->wport.pif.fdata = bch; + } +#endif + i++; + } + + /* set D-channel */ + mISDN_set_dchannel_pid(&pid, protocol[l1oip_cnt], layermask[l1oip_cnt]); + + /* set PRI */ + if (hc->pri == 1) { + if (layermask[l1oip_cnt] & ISDN_LAYER(2)) { + pid.protocol[2] |= ISDN_PID_L2_DF_PTP; + } + if (layermask[l1oip_cnt] & ISDN_LAYER(3)) { + pid.protocol[3] |= ISDN_PID_L3_DF_PTP; + pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID; + pid.protocol[3] |= ISDN_PID_L3_DF_CRLEN2; + } + } + + /* set protocol type */ + dch->inst.pid.protocol[0] = (hc->pri)?ISDN_PID_L0_IP_E1:ISDN_PID_L0_IP_S0; + pid.protocol[0] = (hc->pri)?ISDN_PID_L0_IP_E1:ISDN_PID_L0_IP_S0; + if (protocol[l1oip_cnt] & 0x10) { + /* NT-mode */ + dch->inst.pid.protocol[1] = (hc->pri)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0; + pid.protocol[1] = (hc->pri)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0; + if (layermask[l1oip_cnt] & ISDN_LAYER(2)) + pid.protocol[2] = ISDN_PID_L2_LAPD_NET; + } else { + /* TE-mode */ + dch->inst.pid.protocol[1] = (hc->pri)?ISDN_PID_L1_TE_E1:ISDN_PID_L1_TE_S0; + pid.protocol[1] = (hc->pri)?ISDN_PID_L1_TE_E1:ISDN_PID_L1_TE_S0; + } + dch->inst.pid.layermask |= ISDN_LAYER(1); + pid.layermask |= ISDN_LAYER(1); + + + /* run card setup */ + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: Setting up network(%d)\n", __FUNCTION__, l1oip_cnt+1); + if ((ret_err = setup_network(hc))) { + goto free_channels; + } + /* add stacks */ + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: Adding d-stack: card(%d)\n", __FUNCTION__, l1oip_cnt+1); + if ((ret_err = l1oip_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &dch->inst))) { + printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", ret_err); + free_release: + release_network(hc); + goto free_object; + } + dst = dch->inst.st; + i=0; + while(i < ((hc->pri)?30:2)) { + bch = hc->bch; + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: Adding b-stack: card(%d) B-channel(%d)\n", __FUNCTION__, l1oip_cnt+1, bch->channel); + if ((ret_err = l1oip_obj.ctrl(dst, MGR_NEWSTACK | REQUEST, &bch->inst))) { + printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", ret_err); + free_delstack: + l1oip_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL); + goto free_release; + } + bch->st = bch->inst.st; + i++; + } + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: (before MGR_SETSTACK REQUEST) layermask=0x%x\n", __FUNCTION__, pids[pt].layermask); + + if ((ret_err = l1oip_obj.ctrl(dst, MGR_SETSTACK | REQUEST, &pids[pt]))) { + printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", ret_err); + goto free_delstack; + } + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: (after MGR_SETSTACK REQUEST)\n", __FUNCTION__); + + /* delay some time */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ + + /* tell stack, that we are ready */ + l1oip_obj.ctrl(dst, MGR_CTRLREADY | INDICATION, NULL); + + HFC_cnt++; + goto next_card; + + /* if an error ocurred */ + + free_channels: + if (hc->dch) { + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: free D-channel %d (1..32)\n", __FUNCTION__, i); + mISDN_free_dch(hc->dch); + kfree(hc->dch); + hc->dch = NULL; + } +// if (hc->rx_buf) { +// kfree(hc->rx_buf); +// hc->rx_buf = NULL; +// } + i = 0; + while(i < 30) { + if (hc->bch[i]) { + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: free B-channel %d (1..32)\n", __FUNCTION__, hc->bch[i].channel); + mISDN_free_bch(hc->bch[i]); + kfree(hc->bch[i]); + hc->bch[i] = NULL; + } + i++; + } + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: before REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt); + spin_lock_irqsave(&l1oip_obj.lock, flags); + list_del(&hc->list); + spin_unlock_irqrestore(&l1oip_obj.lock, flags); + if (debug & DEBUG_L1OIP_INIT) + printk(KERN_DEBUG "%s: after REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt); + kfree(hc); + + free_object: + l1oip_cleanup(); + return(ret_err); +} + + +#ifdef MODULE +module_init(l1oip_init); +module_exit(l1oip_cleanup); +#endif + + diff --git a/drivers/isdn/hardware/mISDN/loop.c b/drivers/isdn/hardware/mISDN/loop.c new file mode 100644 index 0000000..e227ad4 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/loop.c @@ -0,0 +1,598 @@ +/* + * loop.c loop driver for looped bchannel pairs + * + * Author Andreas Eversberg (jolly@jolly.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. + * + */ + +/* module parameters: + * interfaces: + Number of loop interfaces. Default is 1. + + */ + +#include +#include +#include + +#include "channel.h" +#include "layer1.h" +#include "debug.h" +#include + +#include "loop.h" + +static const char *loop_revision = "$Revision$"; + +static int loop_cnt; + +static mISDNobject_t loop_obj; + +static char LoopName[] = "loop"; + + +/****************/ +/* module stuff */ +/****************/ + +static int interfaces; +static int debug; + +#ifdef MODULE +MODULE_AUTHOR("Andreas Eversberg"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(interfaces, "1i"); +MODULE_PARM(debug, "1i"); +#endif + + +/****************************/ +/* Layer 1 D-channel access */ +/****************************/ + +/* message transfer from layer 1. + */ +static int loop_l1hw(mISDNinstance_t *inst, struct sk_buff *skb) +{ + channel_t *dch = container_of(inst, channel_t, inst); + loop_t *hc; + int ret = 0; + mISDN_head_t *hh; + + hh = mISDN_HEAD_P(skb); + hc = dch->inst.privat; + + if (debug & DEBUG_LOOP_MSG) + printk(KERN_DEBUG "%s: unsupported prim %x\n", __FUNCTION__, hh->prim); + ret = -EINVAL; + if (!ret) + dev_kfree_skb(skb); + return(ret); +} +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ + +/* messages from layer 2 to layer 1 are processed here. + */ +static int +loop_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) +{ + int ch; + channel_t *bch = container_of(inst, channel_t, inst); + int ret = -EINVAL; + mISDN_head_t *hh; + loop_t *hc; + struct sk_buff *nskb; + + hh = mISDN_HEAD_P(skb); + hc = bch->inst.privat; + ch = bch->channel; + + if ((hh->prim == PH_DATA_REQ) + || (hh->prim == (DL_DATA | REQUEST))) { + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__); + return(-EINVAL); + } + if (skb->len > MAX_DATA_MEM) { + printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__); + return(-EINVAL); + } + if ((nskb = skb_clone(skb, GFP_ATOMIC))) + queue_ch_frame(hc->bch[ch^1], INDICATION, MISDN_ID_ANY, nskb); + skb_trim(skb, 0); + return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, hh->dinfo, skb)); + } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) + || (hh->prim == (DL_ESTABLISH | REQUEST))) { + /* activate B-channel if not already activated */ + skb_trim(skb, 0); + return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, ret, skb)); + } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) + || (hh->prim == (DL_RELEASE | REQUEST)) + || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { + skb_trim(skb, 0); + return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, ret, skb)); + } else + if (hh->prim == (PH_CONTROL | REQUEST)) { + switch (hh->dinfo) { + default: + printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); + ret = -EINVAL; + } + } else { + printk(KERN_WARNING "%s: unknown prim(%x)\n", __FUNCTION__, hh->prim); + ret = -EINVAL; + } + if (!ret) { + dev_kfree_skb(skb); + } + return(ret); +} + + + +/************************** + * remove card from stack * + **************************/ + +static void +loop_delete(loop_t *hc) +{ + int ch; + u_long flags; + + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); + + /* free channels */ + if (hc->dch) { + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: free D-channel\n", __FUNCTION__); + mISDN_freechannel(hc->dch); + kfree(hc->dch); + hc->dch = NULL; + } + ch = 0; + while(ch < LOOP_CHANNELS) { + if (hc->bch[ch]) { + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: free B-channel %d\n", __FUNCTION__, ch); + mISDN_freechannel(hc->bch[ch]); + kfree(hc->bch[ch]); + hc->bch[ch] = NULL; + } + ch++; + } + + /* remove us from list and delete */ + if (debug & DEBUG_LOOP_INIT) + printk(KERN_WARNING "%s: remove instance from list\n", __FUNCTION__); + spin_lock_irqsave(&loop_obj.lock, flags); + list_del(&hc->list); + spin_unlock_irqrestore(&loop_obj.lock, flags); + if (debug & DEBUG_LOOP_INIT) + printk(KERN_WARNING "%s: delete instance\n", __FUNCTION__); + kfree(hc); + loop_cnt--; + if (debug & DEBUG_LOOP_INIT) + printk(KERN_WARNING "%s: card successfully removed\n", __FUNCTION__); +} + +static int +loop_manager(void *data, u_int prim, void *arg) +{ + loop_t *hc; + mISDNinstance_t *inst = data; + struct sk_buff *skb; + channel_t *dch = NULL; + channel_t *bch = NULL; + int ch = 0; + u_long flags; + + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim,arg,&loop_obj) + printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); + return(-EINVAL); + } + + /* find channel and card */ + spin_lock_irqsave(&loop_obj.lock, flags); + list_for_each_entry(hc, &loop_obj.ilist, list) { + if (hc->dch) if (&hc->dch->inst == inst) { + dch = hc->dch; + spin_unlock_irqrestore(&loop_obj.lock, flags); + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: D-channel data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); + goto found; + } + + ch = 0; + while(ch < LOOP_CHANNELS) { + if (hc->bch[ch]) if (&hc->bch[ch]->inst == inst) { + bch = hc->bch[ch]; + spin_unlock_irqrestore(&loop_obj.lock, flags); + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: B-channel %d (0..%d) data %p prim %x arg %p\n", __FUNCTION__, ch, LOOP_CHANNELS-1, data, prim, arg); + goto found; + } + } + } + spin_unlock_irqrestore(&loop_obj.lock, flags); + printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); + return(-EINVAL); + +found: + switch(prim) { + case MGR_REGLAYER | CONFIRM: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_REGLAYER\n", __FUNCTION__); + mISDN_setpara(dch, &inst->st->para); + break; + + case MGR_UNREGLAYER | REQUEST: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_UNREGLAYER\n", __FUNCTION__); + if (dch) { + if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { + if (loop_l1hw(inst, skb)) dev_kfree_skb(skb); + } + } else + if (bch) { + if ((skb = create_link_skb(PH_CONTROL | REQUEST, 0, 0, NULL, 0))) { + if (loop_l2l1(inst, skb)) dev_kfree_skb(skb); + } + } + loop_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + // fall through + case MGR_ADDSTPARA | INDICATION: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_***STPARA\n", __FUNCTION__); + mISDN_setpara(dch, arg); + break; + + case MGR_RELEASE | INDICATION: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_RELEASE = remove port from mISDN\n", __FUNCTION__); + if (dch) { + loop_delete(hc); /* hc is free */ + } + break; +#ifdef FIXME + case MGR_CONNECT | REQUEST: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_CONNECT\n", __FUNCTION__); + return(mISDN_ConnectIF(inst, arg)); + + case MGR_SETIF | REQUEST: + case MGR_SETIF | INDICATION: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_SETIF\n", __FUNCTION__); + if (dch) + return(mISDN_SetIF(inst, arg, prim, loop_l1hw, NULL, dch)); + if (bch) + return(mISDN_SetIF(inst, arg, prim, loop_l2l1, NULL, bch)); + break; + + case MGR_DISCONNECT | REQUEST: + case MGR_DISCONNECT | INDICATION: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_DISCONNECT\n", __FUNCTION__); + return(mISDN_DisConnectIF(inst, arg)); +#endif +#if 0 + case MGR_SELCHANNEL | REQUEST: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_SELCHANNEL\n", __FUNCTION__); + if (!dch) { + printk(KERN_WARNING "%s(MGR_SELCHANNEL|REQUEST): selchannel not dinst\n", __FUNCTION__); + return(-EINVAL); + } + return(SelFreeBChannel(hc, ch, arg)); +#endif + + case MGR_SETSTACK | INDICATION: + if (debug & DEBUG_LOOP_MGR) + printk(KERN_DEBUG "%s: MGR_SETSTACK\n", __FUNCTION__); + if (bch && inst->pid.global==2) { + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { + if (loop_l2l1(inst, skb)) dev_kfree_skb(skb); + } + if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) + mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); + else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); + } + break; + + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST); + default: + printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim); + return(-EINVAL); + } + return(0); +} + +/************************* + * create cards instance * + *************************/ + +static int __devinit loop_new(void) +{ + int ret_err=0; + int ch; + loop_t *hc; + mISDN_pid_t pid; + mISDNstack_t *dst = NULL; /* make gcc happy */ + channel_t *dch; + channel_t *bch; + u_long flags; + + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: Registering loop driver #%d\n", __FUNCTION__, loop_cnt+1); + + /* allocate structure */ + if (!(hc = kmalloc(sizeof(loop_t), GFP_ATOMIC))) { + printk(KERN_ERR "No kmem for loop driver\n"); + ret_err = -ENOMEM; + goto free_object; + } + memset(hc, 0, sizeof(loop_t)); + hc->idx = loop_cnt; + hc->id = loop_cnt + 1; + + sprintf(hc->name, "LOOP#%d", loop_cnt+1); + + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); + + spin_lock_irqsave(&loop_obj.lock, flags); + list_add_tail(&hc->list, &loop_obj.ilist); + spin_unlock_irqrestore(&loop_obj.lock, flags); + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); + + spin_lock_init(&hc->lock); + + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: Registering D-channel, card(%d)\n", __FUNCTION__, loop_cnt+1); + dch = kmalloc(sizeof(channel_t), GFP_ATOMIC); + if (!dch) { + ret_err = -ENOMEM; + goto free_channels; + } + memset(dch, 0, sizeof(channel_t)); + dch->channel = 0; + //dch->debug = debug; + dch->inst.obj = &loop_obj; + dch->inst.hwlock = &hc->lock; + mISDN_init_instance(&dch->inst, &loop_obj, hc, loop_l1hw); + dch->inst.pid.layermask = ISDN_LAYER(0); + sprintf(dch->inst.name, "LOOP%d", loop_cnt+1); + if (mISDN_initchannel(dch, MSK_INIT_DCHANNEL, MAX_DATA_MEM)) { + ret_err = -ENOMEM; + goto free_channels; + } + hc->dch = dch; + + ch=0; + while(ch < LOOP_CHANNELS) { + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: Registering B-channel, card(%d) ch(%d)\n", __FUNCTION__, loop_cnt+1, ch); + bch = kmalloc(sizeof(channel_t), GFP_ATOMIC); + if (!bch) { + ret_err = -ENOMEM; + goto free_channels; + } + memset(bch, 0, sizeof(channel_t)); + bch->channel = ch; + mISDN_init_instance(&bch->inst, &loop_obj, hc, loop_l2l1); + bch->inst.pid.layermask = ISDN_LAYER(0); + bch->inst.hwlock = &hc->lock; + //bch->debug = debug; + sprintf(bch->inst.name, "%s B%d", + dch->inst.name, ch+1); + if (mISDN_initchannel(bch, MSK_INIT_BCHANNEL, MAX_DATA_MEM)) { + kfree(bch); + ret_err = -ENOMEM; + goto free_channels; + } + hc->bch[ch] = bch; +#ifdef FIXME // TODO + if (bch->dev) { + bch->dev->wport.pif.func = loop_l2l1; + bch->dev->wport.pif.fdata = bch; + } +#endif + ch++; + } + + /* set D-channel */ + mISDN_set_dchannel_pid(&pid, 0x00, ISDN_LAYER(0)); + pid.protocol[0] = ISDN_PID_L0_LOOP; + pid.layermask = ISDN_LAYER(0); + + /* add stacks */ + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: Adding d-stack: card(%d)\n", __FUNCTION__, loop_cnt+1); + if ((ret_err = loop_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &dch->inst))) { + printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", ret_err); + free_release: + loop_delete(hc); /* hc is free */ + goto free_object; + } + + dst = dch->inst.st; + + ch = 0; + while(ch < LOOP_CHANNELS) { + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: Adding b-stack: card(%d) B-channel(%d)\n", __FUNCTION__, loop_cnt+1, ch+1); + bch = hc->bch[ch]; + if ((ret_err = loop_obj.ctrl(dst, MGR_NEWSTACK | REQUEST, &bch->inst))) { + printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", ret_err); + free_delstack: + loop_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL); + goto free_release; + } + ch++; + } + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: (before MGR_SETSTACK REQUEST) layermask=0x%x\n", __FUNCTION__, pid.layermask); + + if ((ret_err = loop_obj.ctrl(dst, MGR_SETSTACK | REQUEST, &pid))) { + printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", ret_err); + goto free_delstack; + } + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: (after MGR_SETSTACK REQUEST)\n", __FUNCTION__); + + /* delay some time */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ + + /* tell stack, that we are ready */ + loop_obj.ctrl(dst, MGR_CTRLREADY | INDICATION, NULL); + + loop_cnt++; + return(0); + + /* if an error ocurred */ + free_channels: + if (hc->dch) { + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: free D-channel\n", __FUNCTION__); + mISDN_freechannel(hc->dch); + kfree(hc->dch); + hc->dch = NULL; + } + ch = 0; + while(ch < LOOP_CHANNELS) { + if (hc->bch[ch]) { + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: free B-channel %d\n", __FUNCTION__, ch); + mISDN_freechannel(hc->bch[ch]); + kfree(hc->bch[ch]); + hc->bch[ch] = NULL; + } + ch++; + } + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: before REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, loop_obj.refcnt); + spin_lock_irqsave(&loop_obj.lock, flags); + list_del(&hc->list); + spin_unlock_irqrestore(&loop_obj.lock, flags); + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: after REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, loop_obj.refcnt); + kfree(hc); + + free_object: + return(ret_err); +} + + +static void __exit +loop_cleanup(void) +{ + loop_t *hc,*next; + int err; + + /* unregister mISDN object */ + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: entered (refcnt = %d loop_cnt = %d)\n", __FUNCTION__, loop_obj.refcnt, loop_cnt); + if ((err = mISDN_unregister(&loop_obj))) { + printk(KERN_ERR "Can't unregister Loop cards error(%d)\n", err); + } + + /* remove remaining devices, but this should never happen */ + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: now checking ilist (refcnt = %d)\n", __FUNCTION__, loop_obj.refcnt); + + list_for_each_entry_safe(hc, next, &loop_obj.ilist, list) { + printk(KERN_ERR "Loop card struct not empty refs %d\n", loop_obj.refcnt); + loop_delete(hc); + } + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: done (refcnt = %d loop_cnt = %d)\n", __FUNCTION__, loop_obj.refcnt, loop_cnt); + +} + +static int __init +loop_init(void) +{ + int err; + char tmpstr[64]; + + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: init entered\n", __FUNCTION__); + + strcpy(tmpstr, loop_revision); + printk(KERN_INFO "mISDN: loop-driver Rev. %s\n", mISDN_getrev(tmpstr)); + + memset(&loop_obj, 0, sizeof(loop_obj)); +#ifdef MODULE + loop_obj.owner = THIS_MODULE; +#endif + spin_lock_init(&loop_obj.lock); + INIT_LIST_HEAD(&loop_obj.ilist); + loop_obj.name = LoopName; + loop_obj.own_ctrl = loop_manager; + loop_obj.DPROTO.protocol[0] = ISDN_PID_L0_LOOP; + loop_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; + loop_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; + + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: registering loop_obj\n", __FUNCTION__); + if ((err = mISDN_register(&loop_obj))) { + printk(KERN_ERR "Can't register Loop cards error(%d)\n", err); + return(err); + } + if (debug & DEBUG_LOOP_INIT) + printk(KERN_DEBUG "%s: new mISDN object (refcnt = %d)\n", __FUNCTION__, loop_obj.refcnt); + + if (interfaces < 1) + interfaces = 1; + loop_cnt = 0; + while(loop_cnt < interfaces) + { + if ((err = loop_new())) + break; + } + + if (err) + { + printk(KERN_ERR "error registering pci driver:%x\n",err); + loop_cleanup(); + return(err); + } + printk(KERN_INFO "%d devices registered\n", loop_cnt); + + return(0); +} + + +#ifdef MODULE +module_init(loop_init); +module_exit(loop_cleanup); +#endif + diff --git a/drivers/isdn/hardware/mISDN/loop.h b/drivers/isdn/hardware/mISDN/loop.h new file mode 100644 index 0000000..dab0eee --- /dev/null +++ b/drivers/isdn/hardware/mISDN/loop.h @@ -0,0 +1,28 @@ +/* + + * see notice in loop.c + */ + +#define DEBUG_LOOP_MSG 0x0001 +#define DEBUG_LOOP_INIT 0x0004 +#define DEBUG_LOOP_MGR 0x0008 + +#define MAX_FRAME_SIZE 2048 + +#define LOOP_CHANNELS 128 + +struct misdn_loop { + struct list_head list; + char name[32]; + int idx; /* chip index for module parameters */ + int id; /* chip number starting with 1 */ + + spinlock_t lock; /* the lock */ + + channel_t *dch; + channel_t *bch[LOOP_CHANNELS]; +}; + +typedef struct misdn_loop loop_t; + + diff --git a/drivers/isdn/hardware/mISDN/socket.c b/drivers/isdn/hardware/mISDN/socket.c new file mode 100644 index 0000000..5a8efa2 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/socket.c @@ -0,0 +1,174 @@ +/* + + * socket handling, transfer, receive-process for Voice over IP + * + * Author Andreas Eversberg (jolly@jolly.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. + * + */ + + +#include +#include +#include +#include + +#include "debug.h" +#include "socket.h" +#include + + +/* socket thread */ +static int +mISDN_socket_thread(void *data) +{ + mISDN_socket_t *ms = (mISDN_socket_t *)data; + + /* make daemon */ + daemonize("mISDN-socket"); + allow_signal(SIGTERM); + + /* read loop */ + while(!signal_lending(current)) { + ms->iov.iov_base = ms->rcvbuf; + ms->iov_len = sizeof(ms->rcvbuf); + ms->oldfs = get_fs(); + set_fs(KERNEL_DS); + ms->rcvlen = sock_recvmsg(ms->socket, &ms->msg, sizeof(ms->rcvbuf), 0); + set_fs(oldfs); + if (ms->rcvlen>0) { + ms->func(ms, ms->pri); + } + } + + /* show that we terminate */ + ms->thread_pid = 0; + + /* if we got killed, signal completion */ + if (ms->thread_complete) + complete(&ms->thread_complete); + + return(0); +} + +/* + * Adds a new socket with receive process. + * func will be the pointer to the receive process. + * -> The packet information is included in the mISDN_socket_t. + * pri will be the private data when calling receive process. + * local/remote ip/port will be the tupple for sending an receiving udp data. + * -> local-ip must be 0 if any local ip is accepted. + */ +mISDN_socket_t +*mISDN_add_socket(sock_func_t *func, void *pri, u32 local_ip, u32 remote_ip, u16 local_port, u16 remote_port) +{ + mISDN_socket_t *ms; + + /* alloc memory structure */ + if (!(sm = vmalloc(sizeof(mISDN_sock_t)))) { + printk(KERN_ERR "%s: No memory for mISDN_sock_t.\n", __FUNCTION__); + return(NULL); + } + memset(ms, 0, sizeof(mISDN_sock_t)); + ms->func = func; + ms->pri = pri; + + /* create socket */ + if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &ms->socket)) { + printk(KERN_ERR "%s: Failed to create socket.\n", __FUNCTION__); + mISDN_free_socket(ms); + return(NULL); + } + + /* bind socket */ + ms->server.sin_family = AF_INET; + ms->server.sin_addr.s_addr = (local_ip)?local_ip:INADDR_ANY; + ms->server.sin_port = htons((unsigned short)local_port); + if (ms->socket->ops->bind(ms->socket, (struct sockaddr *)ms->server, sizeof(ms->server))) { + printk(KERN_ERR "%s: Failed to bind socket.\n", __FUNCTION__); + mISDN_free_socket(ms); + return(NULL); + } + + /* check sk */ + if (ms->socket->sk == NULL) { + printk(KERN_ERR "%s: socket->sk == NULL\n", __FUNCTION__); + return(NULL); + } + + /* build message */ + ms->msg.msg_name = &ms->client; + ms->msg.msg_namelen = sizeof(ms->client); + ms->msg.msg_control = NULL; + ms->msg.msg_controllen = 0; + ms->msg.msg_iov = &ms->iov; + ms->msg.msg_iovlen = 1; + + /* create receive process */ + if ((ms->thread_pid = kernel_thread(mISDN_socket_thread, ms, CLONE_KERNEL)) < 0) { + printk(KERN_ERR "%s: Failed to create receive process.\n", __FUNCTION__); + mISDN_free_socket(ms); + return(NULL); + } +} + + +/* + * free given socket with all resources + */ +void +mISDN_free_socket(mISDN_sock_t *ms) +{ + if (!ms) + return; + + /* kill thread */ + if (ms->thread_pid) { + DECLARE_COMPLETION(thread_complete); + ms->thread_complete = &thread_complete; + kill_proc(ms->thread_pid, SIGTERM, 0); + wait_for_completion(&thread_complete); + } + + /* release socket */ + if (ms->socket) + sock_release(ms->socket); + + /* free memory structure */ + vfree(sm); +} + + +/* + * transfer a frame via given socket + */ +int +mISDN_send_socket(mISDN_sock_t *ms, u8 *buf, int len) +{ + + /* send packet */ + ms->send_iov.iov_base = ms->sndbuf; + ms->send_iov_len = sizeof(ms->sndbuf); + ms->send_oldfs = get_fs(); + set_fs(KERNEL_DS); + ms->rcvlen = sock_recvmsg(ms->socket, &ms->msg, sizeof(ms->rcvbuf), 0); + set_fs(oldfs); + if (ms->rcvlen>0) { + ms->func(ms, ms->pri); + } +} + + diff --git a/drivers/isdn/hardware/mISDN/socket.h b/drivers/isdn/hardware/mISDN/socket.h new file mode 100644 index 0000000..47efcc4 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/socket.h @@ -0,0 +1,39 @@ +/* + + * socket handling, transfer, receive-process for Voice over IP + * + * Author Andreas Eversberg (jolly@jolly.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. + * + */ + +struct _mISDN_socket_t; +typedef void (sock_func_t)(_mISDN_socket_t *ms, void *pri); + +typedef struct _mISDN_socket_t { + sock_func_t *func; + void *pri; + int thread_pid; + struct completion *thread_complete; + struct socket *socket; + struct sockaddr_in server, client; + struct sockaddr *address; + struct msghdr msg; + struct iovec iov; + mm_segment_t oldfs; + u8 rcvbuf[1500]; + int rcvlen; +} mISDN_socket_t; diff --git a/drivers/isdn/hardware/mISDN/sysfs.h b/drivers/isdn/hardware/mISDN/sysfs.h new file mode 100644 index 0000000..a579761 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/sysfs.h @@ -0,0 +1,77 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * mISDN sysfs common defines + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#include + +extern ssize_t mISDN_show_pid_protocol(mISDN_pid_t *, char *); +extern ssize_t mISDN_show_pid_parameter(mISDN_pid_t *, char *); + +static inline ssize_t show_pid_layermask(mISDN_pid_t *pid, char *buf) +{ + return sprintf(buf, "0x%08x\n", pid->layermask); +} + +static inline ssize_t show_pid_global(mISDN_pid_t *pid, char *buf) +{ + return sprintf(buf, "0x%04x\n", pid->global); +} + +static inline ssize_t show_pid_maxplen(mISDN_pid_t *pid, char *buf) +{ + return sprintf(buf, "%d\n", pid->maxplen); +} + +#define MISDN_PROTO(_type, _name, _mode) \ +static ssize_t show_protocol_##_name(struct class_device *class_dev, char *buf) \ +{ \ + _type##_t *p = to_##_type(class_dev); \ + return(mISDN_show_pid_protocol(&p->_name, buf)); \ +} \ +struct class_device_attribute _type##_attr_protocol_##_name = \ + __ATTR(protocol,_mode,show_protocol_##_name, NULL); \ +static ssize_t show_parameter_##_name(struct class_device *class_dev, char *buf) \ +{ \ + _type##_t *p = to_##_type(class_dev); \ + return(mISDN_show_pid_parameter(&p->_name, buf)); \ +} \ +struct class_device_attribute _type##_attr_parameter_##_name = \ + __ATTR(parameter,_mode,show_parameter_##_name, NULL); \ +static ssize_t show_layermask_##_name(struct class_device *class_dev, char *buf) \ +{ \ + _type##_t *p = to_##_type(class_dev); \ + return(show_pid_layermask(&p->_name, buf)); \ +} \ +struct class_device_attribute _type##_attr_layermask_##_name = \ + __ATTR(layermask,_mode,show_layermask_##_name, NULL); \ +static ssize_t show_global_##_name(struct class_device *class_dev, char *buf) \ +{ \ + _type##_t *p = to_##_type(class_dev); \ + return(show_pid_global(&p->_name, buf)); \ +} \ +struct class_device_attribute _type##_attr_global_##_name = \ + __ATTR(global,_mode,show_global_##_name, NULL); \ +static ssize_t show_maxplen_##_name(struct class_device *class_dev, char *buf) \ +{ \ + _type##_t *p = to_##_type(class_dev); \ + return(show_pid_maxplen(&p->_name, buf)); \ +} \ +struct class_device_attribute _type##_attr_maxplen_##_name = \ + __ATTR(maxplen,_mode,show_maxplen_##_name, NULL); \ +static struct attribute *attr_##_name[] = { \ + &_type##_attr_global_##_name.attr, \ + &_type##_attr_layermask_##_name.attr, \ + &_type##_attr_maxplen_##_name.attr, \ + &_type##_attr_parameter_##_name.attr, \ + &_type##_attr_protocol_##_name.attr, \ + NULL \ +}; \ +static struct attribute_group _name##_group = { \ + .name = __stringify(_name), \ + .attrs = attr_##_name, \ +} diff --git a/drivers/isdn/hardware/mISDN/sysfs_inst.c b/drivers/isdn/hardware/mISDN/sysfs_inst.c new file mode 100644 index 0000000..08ebd90 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/sysfs_inst.c @@ -0,0 +1,124 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * mISDN sysfs stuff for isnstances + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#include "core.h" +#include "sysfs.h" + +#define to_mISDNinstance(d) container_of(d, mISDNinstance_t, class_dev) + +static ssize_t show_inst_id(struct class_device *class_dev, char *buf) +{ + mISDNinstance_t *inst = to_mISDNinstance(class_dev); + return sprintf(buf, "%08x\n", inst->id); +} +static CLASS_DEVICE_ATTR(id, S_IRUGO, show_inst_id, NULL); + +static ssize_t show_inst_name(struct class_device *class_dev, char *buf) +{ + mISDNinstance_t *inst = to_mISDNinstance(class_dev); + return sprintf(buf, "%s\n", inst->name); +} +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_inst_name, NULL); + +static ssize_t show_inst_extentions(struct class_device *class_dev, char *buf) +{ + mISDNinstance_t *inst = to_mISDNinstance(class_dev); + return sprintf(buf, "%08x\n", inst->extentions); +} +static CLASS_DEVICE_ATTR(extentions, S_IRUGO, show_inst_extentions, NULL); + +static ssize_t show_inst_regcnt(struct class_device *class_dev, char *buf) +{ + mISDNinstance_t *inst = to_mISDNinstance(class_dev); + return sprintf(buf, "%d\n", inst->regcnt); +} +static CLASS_DEVICE_ATTR(regcnt, S_IRUGO, show_inst_regcnt, NULL); + +MISDN_PROTO(mISDNinstance, pid, S_IRUGO); + +static void release_mISDN_inst(struct class_device *dev) +{ + mISDNinstance_t *inst = to_mISDNinstance(dev); + + if (inst->obj) + sysfs_remove_link(&dev->kobj, "obj"); + sysfs_remove_group(&inst->class_dev.kobj, &pid_group); + printk(KERN_INFO "release instance class dev %s\n", dev->class_id); +} + +static struct class inst_dev_class = { + .name = "mISDN-instances", +#ifndef CLASS_WITHOUT_OWNER + .owner = THIS_MODULE, +#endif + .release = &release_mISDN_inst, +}; + +int +mISDN_register_sysfs_inst(mISDNinstance_t *inst) { + int err; + char name[8]; + + inst->class_dev.class = &inst_dev_class; + snprintf(inst->class_dev.class_id, BUS_ID_SIZE, "inst-%08x", inst->id); + err = class_device_register(&inst->class_dev); + if (err) + return(err); + class_device_create_file(&inst->class_dev, &class_device_attr_id); + class_device_create_file(&inst->class_dev, &class_device_attr_name); + class_device_create_file(&inst->class_dev, &class_device_attr_extentions); + class_device_create_file(&inst->class_dev, &class_device_attr_regcnt); + err = sysfs_create_group(&inst->class_dev.kobj, &pid_group); + if (err) + goto out_unreg; + if (inst->obj) + sysfs_create_link(&inst->class_dev.kobj, &inst->obj->class_dev.kobj, "obj"); + if (inst->st) { + sprintf(name,"layer.%d", inst->id & LAYER_ID_MASK); + sysfs_create_link(&inst->st->class_dev.kobj, &inst->class_dev.kobj, name); + sysfs_create_link(&inst->class_dev.kobj, &inst->st->class_dev.kobj, "stack"); + if (inst->st->mgr == inst) { + sysfs_create_link(&inst->st->class_dev.kobj, &inst->class_dev.kobj, "mgr"); + } + } + return(err); + +out_unreg: + class_device_unregister(&inst->class_dev); + return(err); +} + +void +mISDN_unregister_sysfs_inst(mISDNinstance_t *inst) +{ + char name[8]; + + if (inst->id) { + if (inst->st) { + sprintf(name,"layer.%d", inst->id & LAYER_ID_MASK); + sysfs_remove_link(&inst->st->class_dev.kobj, name); + sysfs_remove_link(&inst->class_dev.kobj, "stack"); + if (inst->st->mgr == inst) + sysfs_remove_link(&inst->st->class_dev.kobj, "mgr"); + } + class_device_unregister(&inst->class_dev); + } +} + +int +mISDN_sysfs_inst_init(void) +{ + return(class_register(&inst_dev_class)); +} + +void +mISDN_sysfs_inst_cleanup(void) +{ + class_unregister(&inst_dev_class); +} diff --git a/drivers/isdn/hardware/mISDN/sysfs_obj.c b/drivers/isdn/hardware/mISDN/sysfs_obj.c new file mode 100644 index 0000000..f7829c4 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/sysfs_obj.c @@ -0,0 +1,153 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * mISDN sysfs object and common stuff + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#include "core.h" +#include "sysfs.h" + +#define to_mISDNobject(d) container_of(d, mISDNobject_t, class_dev) + +static ssize_t show_obj_name(struct class_device *class_dev, char *buf) +{ + mISDNobject_t *obj = to_mISDNobject(class_dev); + return sprintf(buf, "%s\n", obj->name); +} + +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_obj_name, NULL); + +static ssize_t show_obj_id(struct class_device *class_dev, char *buf) +{ + mISDNobject_t *obj = to_mISDNobject(class_dev); + return sprintf(buf, "%d\n", obj->id); +} +static CLASS_DEVICE_ATTR(id, S_IRUGO, show_obj_id, NULL); + +static ssize_t show_obj_refcnt(struct class_device *class_dev, char *buf) +{ + mISDNobject_t *obj = to_mISDNobject(class_dev); + return sprintf(buf, "%d\n", obj->refcnt); +} +static CLASS_DEVICE_ATTR(refcnt, S_IRUGO, show_obj_refcnt, NULL); + +ssize_t mISDN_show_pid_protocol(mISDN_pid_t *pid, char *buf) +{ + char *p = buf; + uint i; + + for (i=0; i <= MAX_LAYER_NR; i++) + p += sprintf(p,"0x%08x,", pid->protocol[i]); + p--; + *p++ = '\n'; + return (p -buf); +} + +ssize_t mISDN_show_pid_parameter(mISDN_pid_t *pid, char *buf) +{ + char *p = buf, *t; + uint i, l; + + for (i=0; i <= MAX_LAYER_NR; i++) { + if (pid->param[i]) { + t = pid->param[i]; + l = *t++; + p += sprintf(p,"0x%02x,", l); + while(l--) + p += sprintf(p,"0x%02x,", *t++); + }else { + p += sprintf(p,"0x00,"); + } + } + p--; + *p++ = '\n'; + return (p -buf); +} + +MISDN_PROTO(mISDNobject, BPROTO, S_IRUGO); +MISDN_PROTO(mISDNobject, DPROTO, S_IRUGO); + +static void release_mISDN_obj(struct class_device *dev) +{ + mISDNobject_t *obj = to_mISDNobject(dev); + + printk(KERN_INFO "release object class dev %s\n", dev->class_id); + if (obj->owner) +#ifdef MODULE_MKOBJ_POINTER + if (obj->owner->mkobj) +#endif + sysfs_remove_link(&dev->kobj, "module"); + sysfs_remove_group(&obj->class_dev.kobj, &BPROTO_group); + sysfs_remove_group(&obj->class_dev.kobj, &DPROTO_group); +} + +static struct class obj_dev_class = { + .name = "mISDN-objects", +#ifndef CLASS_WITHOUT_OWNER + .owner = THIS_MODULE, +#endif + .release = &release_mISDN_obj, +}; + +int +mISDN_register_sysfs_obj(mISDNobject_t *obj) { + int err; + + obj->class_dev.class = &obj_dev_class; + snprintf(obj->class_dev.class_id, BUS_ID_SIZE, "obj-%d", obj->id); + err = class_device_register(&obj->class_dev); + if (err) + goto out; + class_device_create_file(&obj->class_dev, &class_device_attr_id); + class_device_create_file(&obj->class_dev, &class_device_attr_name); + class_device_create_file(&obj->class_dev, &class_device_attr_refcnt); + err = sysfs_create_group(&obj->class_dev.kobj, &BPROTO_group); + if (err) + goto out_unreg; + err = sysfs_create_group(&obj->class_dev.kobj, &DPROTO_group); + if (err) + goto out_unreg; + if (obj->owner) +#ifdef MODULE_MKOBJ_POINTER + if (obj->owner->mkobj) + sysfs_create_link(&obj->class_dev.kobj, &obj->owner->mkobj->kobj, "module"); +#else + sysfs_create_link(&obj->class_dev.kobj, &obj->owner->mkobj.kobj, "module"); +#endif + return(err); +out_unreg: + class_device_unregister(&obj->class_dev); +out: + return(err); +} + +int +mISDN_sysfs_init(void) { + int err; + + err = class_register(&obj_dev_class); + if (err) + return(err); + err = mISDN_sysfs_inst_init(); + if (err) + goto unreg_obj; + err = mISDN_sysfs_st_init(); + if (err) + goto unreg_inst; + return(err); +unreg_inst: + mISDN_sysfs_inst_cleanup(); +unreg_obj: + class_unregister(&obj_dev_class); + return(err); +} + +void +mISDN_sysfs_cleanup(void) { + class_unregister(&obj_dev_class); + mISDN_sysfs_inst_cleanup(); + mISDN_sysfs_st_cleanup(); +} diff --git a/drivers/isdn/hardware/mISDN/sysfs_st.c b/drivers/isdn/hardware/mISDN/sysfs_st.c new file mode 100644 index 0000000..407dacd --- /dev/null +++ b/drivers/isdn/hardware/mISDN/sysfs_st.c @@ -0,0 +1,270 @@ +/* $Id$ + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * mISDN sysfs stack stuff + * + * This file is (c) under GNU PUBLIC LICENSE + * + */ +#include "core.h" +#include "sysfs.h" + +#define to_mISDNstack(d) container_of(d, mISDNstack_t, class_dev) + +static ssize_t show_st_id(struct class_device *class_dev, char *buf) +{ + mISDNstack_t *st = to_mISDNstack(class_dev); + return sprintf(buf, "%08x\n", st->id); +} +static CLASS_DEVICE_ATTR(id, S_IRUGO, show_st_id, NULL); + +static ssize_t show_st_status(struct class_device *class_dev, char *buf) +{ + mISDNstack_t *st = to_mISDNstack(class_dev); + return sprintf(buf, "0x%08lx\n", st->status); +} + +static ssize_t store_st_status(struct class_device *class_dev, const char *buf, size_t count) +{ + mISDNstack_t *st = to_mISDNstack(class_dev); + ulong status; + int err; + + status = simple_strtol(buf, NULL, 0); + printk(KERN_DEBUG "%s: status %08lx\n", __FUNCTION__, status); + if (status == (1<new_pid activ */ + err = clear_stack(st, 1); + if (err) { + int_errtxt("clear_stack:%d", err); + return(err); + } + err = set_stack(st ,&st->new_pid); + if (err) { + int_errtxt("set_stack:%d", err); + return(err); + } + return(count); + } else if (status == (1<status = status; + wake_up_interruptible(&st->workq); + return(count); +} +static CLASS_DEVICE_ATTR(status, S_IRUGO | S_IWUSR, show_st_status, store_st_status); + +static ssize_t store_st_protocol(struct class_device *class_dev, const char *buf, size_t count) +{ + mISDNstack_t *st = to_mISDNstack(class_dev); + ulong tmp; + char *p = (char *)buf; + u_int i; + + memset(&st->new_pid.protocol, 0, (MAX_LAYER_NR + 1)*sizeof(st->new_pid.protocol[0])); + for (i=0; i<=MAX_LAYER_NR; i++) { + if (!*p) + break; + tmp = simple_strtol(p, &p, 0); + st->new_pid.protocol[i] = tmp; + if (*p) + p++; + } + if (*p) + int_errtxt("overflow"); + return(count); +} + +static ssize_t store_st_layermask(struct class_device *class_dev, const char *buf, size_t count) +{ + mISDNstack_t *st = to_mISDNstack(class_dev); + ulong mask = (1<<(MAX_LAYER_NR + 1)) -1; + + st->new_pid.layermask = simple_strtol(buf, NULL, 0); + if (st->new_pid.layermask > mask) { + int_errtxt("overflow"); + st->new_pid.layermask &= mask; + } + return(count); +} + +static ssize_t store_st_parameter(struct class_device *class_dev, const char *buf, size_t count) +{ + mISDNstack_t *st = to_mISDNstack(class_dev); + ulong tmp; + char *d, *p = (char *)buf; + u_int i, j, l; + + memset(&st->new_pid.param, 0, (MAX_LAYER_NR + 1)*sizeof(st->new_pid.param[0])); + kfree(st->new_pid.pbuf); + l = 0; + for (i=0; i<=MAX_LAYER_NR; i++) { + if (!*p) + break; + tmp = simple_strtol(p, &p, 0); + if (*p) + p++; + if (tmp) { + j = tmp; + l += j+1; + while(j--) { + if (!*p) + break; + tmp = simple_strtol(p, &p, 0); + if (*p) + p++; + else + break; + } + } + } + if (*p) + int_errtxt("overflow"); + if (l == 0) { + st->new_pid.maxplen = 0; + return(count); + } + st->new_pid.pbuf = kmalloc(l, GFP_ATOMIC); + if (!st->new_pid.pbuf) + return(-ENOMEM); + st->new_pid.maxplen = l; + d = st->new_pid.pbuf; + memset(d, 0, l); + p = (char *)buf; + for (i=0; i<=MAX_LAYER_NR; i++) { + if (!*p) + break; + tmp = simple_strtol(p, &p, 0); + if (*p) + p++; + if (tmp) { + j = tmp; + st->new_pid.param[i] = d; + *d++ = tmp & 0xff; + while(j--) { + if (!*p) + break; + tmp = simple_strtol(p, &p, 0); + *d++ = tmp & 0xff; + if (*p) + p++; + else + break; + } + } + } + return(count); +} + +MISDN_PROTO(mISDNstack, pid, S_IRUGO); +MISDN_PROTO(mISDNstack, new_pid, S_IRUGO); + +static ssize_t show_st_qlen(struct class_device *class_dev, char *buf) +{ + mISDNstack_t *st = to_mISDNstack(class_dev); + return sprintf(buf, "%d\n", skb_queue_len(&st->msgq)); +} +static CLASS_DEVICE_ATTR(qlen, S_IRUGO, show_st_qlen, NULL); + +static void release_mISDN_stack(struct class_device *dev) +{ + mISDNstack_t *st = to_mISDNstack(dev); + char name[12]; + + sysfs_remove_group(&st->class_dev.kobj, &pid_group); + sysfs_remove_group(&st->class_dev.kobj, &new_pid_group); + printk(KERN_INFO "release stack class dev %s\n", dev->class_id); + if (st->parent) { + sysfs_remove_link(&dev->kobj, "parent"); + snprintf(name, 12, "child%d", (CHILD_ID_MASK & st->id) >> 16); + sysfs_remove_link(&st->parent->class_dev.kobj, name); + } + if (st->master) { + sysfs_remove_link(&dev->kobj, "master"); + snprintf(name, 12, "clone%d", (CLONE_ID_MASK & st->id) >> 16); + sysfs_remove_link(&st->master->class_dev.kobj, name); + } +} + +static struct class stack_dev_class = { + .name = "mISDN-stacks", +#ifndef CLASS_WITHOUT_OWNER + .owner = THIS_MODULE, +#endif + .release = &release_mISDN_stack, +}; + +int +mISDN_register_sysfs_stack(mISDNstack_t *st) +{ + int err; + char name[12]; + + st->class_dev.class = &stack_dev_class; + if (st->id & FLG_CHILD_STACK) + snprintf(st->class_dev.class_id, BUS_ID_SIZE, "chst-%08x", st->id); + else if (st->id & FLG_CLONE_STACK) + snprintf(st->class_dev.class_id, BUS_ID_SIZE, "clst-%08x", st->id); + else + snprintf(st->class_dev.class_id, BUS_ID_SIZE, "st-%08x", st->id); + if (st->mgr) + st->class_dev.dev = st->mgr->class_dev.dev; + err = class_device_register(&st->class_dev); + if (err) + return(err); + err = sysfs_create_group(&st->class_dev.kobj, &pid_group); + if (err) + goto out_unreg; + mISDNstack_attr_protocol_new_pid.attr.mode |= S_IWUSR; + mISDNstack_attr_protocol_new_pid.store = store_st_protocol; + mISDNstack_attr_parameter_new_pid.attr.mode |= S_IWUSR; + mISDNstack_attr_parameter_new_pid.store = store_st_parameter; + mISDNstack_attr_layermask_new_pid.attr.mode |= S_IWUSR; + mISDNstack_attr_layermask_new_pid.store = store_st_layermask; + err = sysfs_create_group(&st->class_dev.kobj, &new_pid_group); + if (err) + goto out_unreg; + class_device_create_file(&st->class_dev, &class_device_attr_id); + class_device_create_file(&st->class_dev, &class_device_attr_qlen); + class_device_create_file(&st->class_dev, &class_device_attr_status); + if (st->parent) { + sysfs_create_link(&st->class_dev.kobj, &st->parent->class_dev.kobj, "parent"); + snprintf(name, 12, "child%d", (CHILD_ID_MASK & st->id) >> 16); + sysfs_create_link(&st->parent->class_dev.kobj, &st->class_dev.kobj, name); + } + if (st->master) { + sysfs_create_link(&st->class_dev.kobj, &st->master->class_dev.kobj, "master"); + snprintf(name, 12, "clone%d", (CLONE_ID_MASK & st->id) >> 16); + sysfs_create_link(&st->master->class_dev.kobj, &st->class_dev.kobj, name); + } + return(err); + +out_unreg: + class_device_unregister(&st->class_dev); + return(err); +} + +void +mISDN_unregister_sysfs_st(mISDNstack_t *st) +{ + class_device_unregister(&st->class_dev); +} + +int +mISDN_sysfs_st_init(void) +{ + return(class_register(&stack_dev_class)); +} + +void +mISDN_sysfs_st_cleanup(void) +{ + class_unregister(&stack_dev_class); +} diff --git a/drivers/isdn/hardware/mISDN/xhfc24succ.h b/drivers/isdn/hardware/mISDN/xhfc24succ.h new file mode 100644 index 0000000..aa878b6 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/xhfc24succ.h @@ -0,0 +1,2126 @@ +/*___________________________________________________________________________________*/ +/* */ +/* (C) Copyright Cologne Chip AG, 2006 */ +/*___________________________________________________________________________________*/ +/* */ + +/* */ +/* File name: xhfc24succ.h */ +/* File content: This file contains the XHFC-2S4U / XHFC-4SU register definitions. */ +/* Creation date: 22.02.2006 13:14 */ +/* Creator: Genero 3.4 */ +/* Data base: HFC XML 1.6 for XHFC-1SU, XHFC-2SU, XHFC-2S4U and XHFC-4SU */ +/* Address range: 0x00 - 0xFF */ +/* */ +/* The information presented can not be considered as assured characteristics. */ +/* Data can change without notice. Please check version numbers in case of doubt. */ +/* */ +/* For further information or questions please contact support@CologneChip.com */ +/* */ +/* */ +/*___________________________________________________________________________________*/ +/* */ +/* WARNING: This file has been generated automatically and should not be */ +/* changed to maintain compatibility with later versions. */ +/*___________________________________________________________________________________*/ +/* */ + + +#ifndef _XHFC24SUCC_H_ +#define _XHFC24SUCC_H_ + + +typedef unsigned char BYTE; + +typedef BYTE REGWORD; // maximum register length (standard) +typedef BYTE REGADDR; // address width + + + +typedef enum {no=0, yes} REGBOOL; + + +typedef enum +{ + // register and bitmap access modes: + writeonly=0, // write only + readonly, // read only + readwrite, // read/write + // following modes only for mixed mode registers: + readwrite_write, // read/write and write only + readwrite_read, // read/write and read only + write_read, // write only and read only + readwrite_write_read // read/write, write only and read only +} ACCESSMODE; + + + +/*___________________________________________________________________________________*/ +/* */ +/* common chip information: */ +/*___________________________________________________________________________________*/ + + #define CHIP_NAME_2S4U "XHFC-2S4U" + #define CHIP_NAME_4SU "XHFC-4SU" + #define CHIP_TITLE_2S4U "ISDN HDLC FIFO controller 2 S/T interfaces combined with 4 Up interfaces (Universal ISDN Ports)" + #define CHIP_TITLE_4SU "ISDN HDLC FIFO controller with 4 S/T interfaces combined with 4 Up interfaces (Universal ISDN Ports)" + #define CHIP_MANUFACTURER "Cologne Chip" + #define CHIP_ID_2S4U 0x62 + #define CHIP_ID_4SU 0x63 + #define CHIP_REGISTER_COUNT 122 + #define CHIP_DATABASE "Version HFC-XMLHFC XML 1.6 for XHFC-1SU, XHFC-2SU, XHFC-2S4U and XHFC-4SU - GeneroGenero 3.4 " + +// This register file can also be used for XHFC-2SU and XHFC-1SU programming. +// For this reason these chip names, IDs and titles are defined here as well: + + #define CHIP_NAME_2SU "XHFC-2SU" + #define CHIP_TITLE_2SU "ISDN HDLC FIFO controller with 2 combined S/T and Up Interfaces" + #define CHIP_ID_2SU 0x61 + + #define CHIP_NAME_1SU "XHFC-1SU" + #define CHIP_TITLE_1SU "ISDN HDLC FIFO controller with a combined S/T and Up Interface" + #define CHIP_ID_1SU 0x60 + + + + +/*___________________________________________________________________________________*/ +/* */ +/* Begin of XHFC-2S4U / XHFC-4SU register definitions. */ +/*___________________________________________________________________________________*/ +/* */ + +#define R_CIRM 0x00 // register access + #define M_CLK_OFF 0x01 // bitmap mask (1bit) + #define M_WAIT_PROC 0x02 // bitmap mask (1bit) + #define M_WAIT_REG 0x04 // bitmap mask (1bit) + #define M_SRES 0x08 // bitmap mask (1bit) + #define M_HFC_RES 0x10 // bitmap mask (1bit) + #define M_PCM_RES 0x20 // bitmap mask (1bit) + #define M_SU_RES 0x40 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_clk_off:1; + REGWORD v_wait_proc:1; + REGWORD v_wait_reg:1; + REGWORD v_sres:1; + REGWORD v_hfc_res:1; + REGWORD v_pcm_res:1; + REGWORD v_su_res:1; + REGWORD reserved_0:1; + } bit_r_cirm; // register and bitmap data + typedef union {REGWORD reg; bit_r_cirm bit;} reg_r_cirm; // register and bitmap access + + +#define R_CTRL 0x01 // register access + #define M_FIFO_LPRIO 0x02 // bitmap mask (1bit) + #define M_NT_SYNC 0x08 // bitmap mask (1bit) + #define M_OSC_OFF 0x20 // bitmap mask (1bit) + #define M_SU_CLK 0xC0 // bitmap mask (2bit) + #define M1_SU_CLK 0x40 + + typedef struct // bitmap construction + { + REGWORD reserved_1:1; + REGWORD v_fifo_lprio:1; + REGWORD reserved_2:1; + REGWORD v_nt_sync:1; + REGWORD reserved_3:1; + REGWORD v_osc_off:1; + REGWORD v_su_clk:2; + } bit_r_ctrl; // register and bitmap data + typedef union {REGWORD reg; bit_r_ctrl bit;} reg_r_ctrl; // register and bitmap access + + +#define R_CLK_CFG 0x02 // register access + #define M_CLK_PLL 0x01 // bitmap mask (1bit) + #define M_CLKO_HI 0x02 // bitmap mask (1bit) + #define M_CLKO_PLL 0x04 // bitmap mask (1bit) + #define M_PCM_CLK 0x20 // bitmap mask (1bit) + #define M_CLKO_OFF 0x40 // bitmap mask (1bit) + #define M_CLK_F1 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_clk_pll:1; + REGWORD v_clko_hi:1; + REGWORD v_clko_pll:1; + REGWORD reserved_4:2; + REGWORD v_pcm_clk:1; + REGWORD v_clko_off:1; + REGWORD v_clk_f1:1; + } bit_r_clk_cfg; // register and bitmap data + typedef union {REGWORD reg; bit_r_clk_cfg bit;} reg_r_clk_cfg; // register and bitmap access + + +#define A_Z1 0x04 // register access + #define M_Z1 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_z1:8; + } bit_a_z1; // register and bitmap data + typedef union {REGWORD reg; bit_a_z1 bit;} reg_a_z1; // register and bitmap access + + +#define A_Z2 0x06 // register access + #define M_Z2 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_z2:8; + } bit_a_z2; // register and bitmap data + typedef union {REGWORD reg; bit_a_z2 bit;} reg_a_z2; // register and bitmap access + + +#define R_RAM_ADDR 0x08 // register access + #define M_RAM_ADDR0 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_ram_addr0:8; + } bit_r_ram_addr; // register and bitmap data + typedef union {REGWORD reg; bit_r_ram_addr bit;} reg_r_ram_addr; // register and bitmap access + + +#define R_RAM_CTRL 0x09 // register access + #define M_RAM_ADDR1 0x0F // bitmap mask (4bit) + #define M1_RAM_ADDR1 0x01 + #define M_ADDR_RES 0x40 // bitmap mask (1bit) + #define M_ADDR_INC 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_ram_addr1:4; + REGWORD reserved_5:2; + REGWORD v_addr_res:1; + REGWORD v_addr_inc:1; + } bit_r_ram_ctrl; // register and bitmap data + typedef union {REGWORD reg; bit_r_ram_ctrl bit;} reg_r_ram_ctrl; // register and bitmap access + + +#define R_FIRST_FIFO 0x0B // register access + #define M_FIRST_FIFO_DIR 0x01 // bitmap mask (1bit) + #define M_FIRST_FIFO_NUM 0x1E // bitmap mask (4bit) + #define M1_FIRST_FIFO_NUM 0x02 + + typedef struct // bitmap construction + { + REGWORD v_first_fifo_dir:1; + REGWORD v_first_fifo_num:4; + REGWORD reserved_6:3; + } bit_r_first_fifo; // register and bitmap data + typedef union {REGWORD reg; bit_r_first_fifo bit;} reg_r_first_fifo; // register and bitmap access + + +#define R_FIFO_THRES 0x0C // register access + #define M_THRES_TX 0x0F // bitmap mask (4bit) + #define M1_THRES_TX 0x01 + #define M_THRES_RX 0xF0 // bitmap mask (4bit) + #define M1_THRES_RX 0x10 + + typedef struct // bitmap construction + { + REGWORD v_thres_tx:4; + REGWORD v_thres_rx:4; + } bit_r_fifo_thres; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_thres bit;} reg_r_fifo_thres; // register and bitmap access + + +#define A_F1 0x0C // register access + #define M_F1 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_f1:8; + } bit_a_f1; // register and bitmap data + typedef union {REGWORD reg; bit_a_f1 bit;} reg_a_f1; // register and bitmap access + + +#define R_FIFO_MD 0x0D // register access + #define M_FIFO_MD 0x03 // bitmap mask (2bit) + #define M1_FIFO_MD 0x01 + #define M_DF_MD 0x0C // bitmap mask (2bit) + #define M1_DF_MD 0x04 + #define M_UNIDIR_MD 0x10 // bitmap mask (1bit) + #define M_UNIDIR_RX 0x20 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_md:2; + REGWORD v_df_md:2; + REGWORD v_unidir_md:1; + REGWORD v_unidir_rx:1; + REGWORD reserved_7:2; + } bit_r_fifo_md; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_md bit;} reg_r_fifo_md; // register and bitmap access + + +#define A_F2 0x0D // register access + #define M_F2 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_f2:8; + } bit_a_f2; // register and bitmap data + typedef union {REGWORD reg; bit_a_f2 bit;} reg_a_f2; // register and bitmap access + + +#define A_INC_RES_FIFO 0x0E // register access + #define M_INC_F 0x01 // bitmap mask (1bit) + #define M_RES_FIFO 0x02 // bitmap mask (1bit) + #define M_RES_LOST 0x04 // bitmap mask (1bit) + #define M_RES_FIFO_ERR 0x08 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_inc_f:1; + REGWORD v_res_fifo:1; + REGWORD v_res_lost:1; + REGWORD v_res_fifo_err:1; + REGWORD reserved_8:4; + } bit_a_inc_res_fifo; // register and bitmap data + typedef union {REGWORD reg; bit_a_inc_res_fifo bit;} reg_a_inc_res_fifo; // register and bitmap access + + +#define A_FIFO_STA 0x0E // register access + #define M_FIFO_ERR 0x01 // bitmap mask (1bit) + #define M_ABO_DONE 0x10 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_err:1; + REGWORD reserved_11:3; + REGWORD v_abo_done:1; + REGWORD reserved_12:3; + } bit_a_fifo_sta; // register and bitmap data + typedef union {REGWORD reg; bit_a_fifo_sta bit;} reg_a_fifo_sta; // register and bitmap access + + +#define R_FSM_IDX 0x0F // register access + #define M_IDX 0x1F // bitmap mask (5bit) + #define M1_IDX 0x01 + + typedef struct // bitmap construction + { + REGWORD v_idx:5; + REGWORD reserved_10:3; + } bit_r_fsm_idx; // register and bitmap data + typedef union {REGWORD reg; bit_r_fsm_idx bit;} reg_r_fsm_idx; // register and bitmap access + #define IDX_FSM_IDX 0x01 // index value selecting this multi-register + + +#define R_FIFO 0x0F // register access + #define M_FIFO_DIR 0x01 // bitmap mask (1bit) + #define M_FIFO_NUM 0x1E // bitmap mask (4bit) + #define M1_FIFO_NUM 0x02 + #define M_REV 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_dir:1; + REGWORD v_fifo_num:4; + REGWORD reserved_9:2; + REGWORD v_rev:1; + } bit_r_fifo; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo bit;} reg_r_fifo; // register and bitmap access + #define IDX_FIFO 0x00 // index value selecting this multi-register + + +#define R_SLOT 0x10 // register access + #define M_SL_DIR 0x01 // bitmap mask (1bit) + #define M_SL_NUM 0xFE // bitmap mask (7bit) + + typedef struct // bitmap construction + { + REGWORD v_sl_dir:1; + REGWORD v_sl_num:7; + } bit_r_slot; // register and bitmap data + typedef union {REGWORD reg; bit_r_slot bit;} reg_r_slot; // register and bitmap access + + +#define R_IRQ_OVIEW 0x10 // register access + #define M_FIFO_BL0_IRQ 0x01 // bitmap mask (1bit) + #define M_FIFO_BL1_IRQ 0x02 // bitmap mask (1bit) + #define M_FIFO_BL2_IRQ 0x04 // bitmap mask (1bit) + #define M_FIFO_BL3_IRQ 0x08 // bitmap mask (1bit) + #define M_MISC_IRQ 0x10 // bitmap mask (1bit) + #define M_CH_IRQ 0x20 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_bl0_irq:1; + REGWORD v_fifo_bl1_irq:1; + REGWORD v_fifo_bl2_irq:1; + REGWORD v_fifo_bl3_irq:1; + REGWORD v_misc_irq:1; + REGWORD v_ch_irq:1; + REGWORD reserved_19:2; + } bit_r_irq_oview; // register and bitmap data + typedef union {REGWORD reg; bit_r_irq_oview bit;} reg_r_irq_oview; // register and bitmap access + + +#define R_MISC_IRQMSK 0x11 // register access + #define M_SLIP_IRQMSK 0x01 // bitmap mask (1bit) + #define M_TI_IRQMSK 0x02 // bitmap mask (1bit) + #define M_PROC_IRQMSK 0x04 // bitmap mask (1bit) + #define M_CI_IRQMSK 0x10 // bitmap mask (1bit) + #define M_WAK_IRQMSK 0x20 // bitmap mask (1bit) + #define M_MON_TX_IRQMSK 0x40 // bitmap mask (1bit) + #define M_MON_RX_IRQMSK 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_slip_irqmsk:1; + REGWORD v_ti_irqmsk:1; + REGWORD v_proc_irqmsk:1; + REGWORD reserved_13:1; + REGWORD v_ci_irqmsk:1; + REGWORD v_wak_irqmsk:1; + REGWORD v_mon_tx_irqmsk:1; + REGWORD v_mon_rx_irqmsk:1; + } bit_r_misc_irqmsk; // register and bitmap data + typedef union {REGWORD reg; bit_r_misc_irqmsk bit;} reg_r_misc_irqmsk; // register and bitmap access + + +#define R_MISC_IRQ 0x11 // register access + #define M_SLIP_IRQ 0x01 // bitmap mask (1bit) + #define M_TI_IRQ 0x02 // bitmap mask (1bit) + #define M_PROC_IRQ 0x04 // bitmap mask (1bit) + #define M_CI_IRQ 0x10 // bitmap mask (1bit) + #define M_WAK_IRQ 0x20 // bitmap mask (1bit) + #define M_MON_TX_IRQ 0x40 // bitmap mask (1bit) + #define M_MON_RX_IRQ 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_slip_irq:1; + REGWORD v_ti_irq:1; + REGWORD v_proc_irq:1; + REGWORD reserved_20:1; + REGWORD v_ci_irq:1; + REGWORD v_wak_irq:1; + REGWORD v_mon_tx_irq:1; + REGWORD v_mon_rx_irq:1; + } bit_r_misc_irq; // register and bitmap data + typedef union {REGWORD reg; bit_r_misc_irq bit;} reg_r_misc_irq; // register and bitmap access + + +#define R_SU_IRQ 0x12 // register access + #define M_SU0_IRQ 0x01 // bitmap mask (1bit) + #define M_SU1_IRQ 0x02 // bitmap mask (1bit) + #define M_SU2_IRQ 0x04 // bitmap mask (1bit) + #define M_SU3_IRQ 0x08 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_su0_irq:1; + REGWORD v_su1_irq:1; + REGWORD v_su2_irq:1; + REGWORD v_su3_irq:1; + REGWORD reserved_27:4; + } bit_r_su_irq; // register and bitmap data + typedef union {REGWORD reg; bit_r_su_irq bit;} reg_r_su_irq; // register and bitmap access + + +#define R_SU_IRQMSK 0x12 // register access + #define M_SU0_IRQMSK 0x01 // bitmap mask (1bit) + #define M_SU1_IRQMSK 0x02 // bitmap mask (1bit) + #define M_SU2_IRQMSK 0x04 // bitmap mask (1bit) + #define M_SU3_IRQMSK 0x08 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_su0_irqmsk:1; + REGWORD v_su1_irqmsk:1; + REGWORD v_su2_irqmsk:1; + REGWORD v_su3_irqmsk:1; + REGWORD reserved_29:4; + } bit_r_su_irqmsk; // register and bitmap data + typedef union {REGWORD reg; bit_r_su_irqmsk bit;} reg_r_su_irqmsk; // register and bitmap access + + +#define R_AF0_OVIEW 0x13 // register access + #define M_SU0_AF0 0x01 // bitmap mask (1bit) + #define M_SU1_AF0 0x02 // bitmap mask (1bit) + #define M_SU2_AF0 0x04 // bitmap mask (1bit) + #define M_SU3_AF0 0x08 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_su0_af0:1; + REGWORD v_su1_af0:1; + REGWORD v_su2_af0:1; + REGWORD v_su3_af0:1; + REGWORD reserved_28:4; + } bit_r_af0_oview; // register and bitmap data + typedef union {REGWORD reg; bit_r_af0_oview bit;} reg_r_af0_oview; // register and bitmap access + + +#define R_IRQ_CTRL 0x13 // register access + #define M_FIFO_IRQ_EN 0x01 // bitmap mask (1bit) + #define M_GLOB_IRQ_EN 0x08 // bitmap mask (1bit) + #define M_IRQ_POL 0x10 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_irq_en:1; + REGWORD reserved_14:2; + REGWORD v_glob_irq_en:1; + REGWORD v_irq_pol:1; + REGWORD reserved_15:3; + } bit_r_irq_ctrl; // register and bitmap data + typedef union {REGWORD reg; bit_r_irq_ctrl bit;} reg_r_irq_ctrl; // register and bitmap access + + +#define A_USAGE 0x14 // register access + #define M_USAGE 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_usage:8; + } bit_a_usage; // register and bitmap data + typedef union {REGWORD reg; bit_a_usage bit;} reg_a_usage; // register and bitmap access + + +#define R_PCM_MD0 0x14 // register access + #define M_PCM_MD 0x01 // bitmap mask (1bit) + #define M_C4_POL 0x02 // bitmap mask (1bit) + #define M_F0_NEG 0x04 // bitmap mask (1bit) + #define M_F0_LEN 0x08 // bitmap mask (1bit) + #define M_PCM_IDX 0xF0 // bitmap mask (4bit) + #define M1_PCM_IDX 0x10 + + typedef struct // bitmap construction + { + REGWORD v_pcm_md:1; + REGWORD v_c4_pol:1; + REGWORD v_f0_neg:1; + REGWORD v_f0_len:1; + REGWORD v_pcm_idx:4; + } bit_r_pcm_md0; // register and bitmap data + typedef union {REGWORD reg; bit_r_pcm_md0 bit;} reg_r_pcm_md0; // register and bitmap access + + +#define R_SL_SEL0 0x15 // register access + #define M_SL_SEL0 0x7F // bitmap mask (7bit) + #define M_SH_SEL0 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_sl_sel0:7; + REGWORD v_sh_sel0:1; + } bit_r_sl_sel0; // register and bitmap data + typedef union {REGWORD reg; bit_r_sl_sel0 bit;} reg_r_sl_sel0; // register and bitmap access + #define IDX_SL_SEL0 0x00 // index value selecting this multi-register + + +#define R_SL_SEL1 0x15 // register access + #define M_SL_SEL1 0x7F // bitmap mask (7bit) + #define M_SH_SEL1 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_sl_sel1:7; + REGWORD v_sh_sel1:1; + } bit_r_sl_sel1; // register and bitmap data + typedef union {REGWORD reg; bit_r_sl_sel1 bit;} reg_r_sl_sel1; // register and bitmap access + #define IDX_SL_SEL1 0x01 // index value selecting this multi-register + + +#define R_SL_SEL7 0x15 // register access + #define M_SL_SEL7 0x7F // bitmap mask (7bit) + + typedef struct // bitmap construction + { + REGWORD v_sl_sel7:7; + REGWORD reserved_30:1; + } bit_r_sl_sel7; // register and bitmap data + typedef union {REGWORD reg; bit_r_sl_sel7 bit;} reg_r_sl_sel7; // register and bitmap access + #define IDX_SL_SEL7 0x07 // index value selecting this multi-register + + +#define R_MSS0 0x15 // register access + #define M_MSS_MOD 0x01 // bitmap mask (1bit) + #define M_MSS_MOD_REP 0x02 // bitmap mask (1bit) + #define M_MSS_SRC_EN 0x04 // bitmap mask (1bit) + #define M_MSS_SRC_GRD 0x08 // bitmap mask (1bit) + #define M_MSS_OUT_EN 0x10 // bitmap mask (1bit) + #define M_MSS_OUT_REP 0x20 // bitmap mask (1bit) + #define M_MSS_SRC 0xC0 // bitmap mask (2bit) + #define M1_MSS_SRC 0x40 + + typedef struct // bitmap construction + { + REGWORD v_mss_mod:1; + REGWORD v_mss_mod_rep:1; + REGWORD v_mss_src_en:1; + REGWORD v_mss_src_grd:1; + REGWORD v_mss_out_en:1; + REGWORD v_mss_out_rep:1; + REGWORD v_mss_src:2; + } bit_r_mss0; // register and bitmap data + typedef union {REGWORD reg; bit_r_mss0 bit;} reg_r_mss0; // register and bitmap access + #define IDX_MSS0 0x08 // index value selecting this multi-register + + +#define R_PCM_MD1 0x15 // register access + #define M_PCM_OD 0x02 // bitmap mask (1bit) + #define M_PLL_ADJ 0x0C // bitmap mask (2bit) + #define M1_PLL_ADJ 0x04 + #define M_PCM_DR 0x30 // bitmap mask (2bit) + #define M1_PCM_DR 0x10 + #define M_PCM_LOOP 0x40 // bitmap mask (1bit) + #define M_PCM_SMPL 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD reserved_31:1; + REGWORD v_pcm_od:1; + REGWORD v_pll_adj:2; + REGWORD v_pcm_dr:2; + REGWORD v_pcm_loop:1; + REGWORD v_pcm_smpl:1; + } bit_r_pcm_md1; // register and bitmap data + typedef union {REGWORD reg; bit_r_pcm_md1 bit;} reg_r_pcm_md1; // register and bitmap access + #define IDX_PCM_MD1 0x09 // index value selecting this multi-register + + +#define R_PCM_MD2 0x15 // register access + #define M_SYNC_OUT1 0x02 // bitmap mask (1bit) + #define M_SYNC_SRC 0x04 // bitmap mask (1bit) + #define M_SYNC_OUT2 0x08 // bitmap mask (1bit) + #define M_C2O_EN 0x10 // bitmap mask (1bit) + #define M_C2I_EN 0x20 // bitmap mask (1bit) + #define M_PLL_ICR 0x40 // bitmap mask (1bit) + #define M_PLL_MAN 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD reserved_32:1; + REGWORD v_sync_out1:1; + REGWORD v_sync_src:1; + REGWORD v_sync_out2:1; + REGWORD v_c2o_en:1; + REGWORD v_c2i_en:1; + REGWORD v_pll_icr:1; + REGWORD v_pll_man:1; + } bit_r_pcm_md2; // register and bitmap data + typedef union {REGWORD reg; bit_r_pcm_md2 bit;} reg_r_pcm_md2; // register and bitmap access + #define IDX_PCM_MD2 0x0A // index value selecting this multi-register + + +#define R_MSS1 0x15 // register access + #define M_MSS_OFFS 0x07 // bitmap mask (3bit) + #define M1_MSS_OFFS 0x01 + #define M_MS_SSYNC1 0x08 // bitmap mask (1bit) + #define M_MSS_DLY 0xF0 // bitmap mask (4bit) + #define M1_MSS_DLY 0x10 + + typedef struct // bitmap construction + { + REGWORD v_mss_offs:3; + REGWORD v_ms_ssync1:1; + REGWORD v_mss_dly:4; + } bit_r_mss1; // register and bitmap data + typedef union {REGWORD reg; bit_r_mss1 bit;} reg_r_mss1; // register and bitmap access + #define IDX_MSS1 0x0B // index value selecting this multi-register + + +#define R_SH0L 0x15 // register access + #define M_SH0L 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_sh0l:8; + } bit_r_sh0l; // register and bitmap data + typedef union {REGWORD reg; bit_r_sh0l bit;} reg_r_sh0l; // register and bitmap access + #define IDX_SH0L 0x0C // index value selecting this multi-register + + +#define R_SH0H 0x15 // register access + #define M_SH0H 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_sh0h:8; + } bit_r_sh0h; // register and bitmap data + typedef union {REGWORD reg; bit_r_sh0h bit;} reg_r_sh0h; // register and bitmap access + #define IDX_SH0H 0x0D // index value selecting this multi-register + + +#define R_SH1L 0x15 // register access + #define M_SH1L 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_sh1l:8; + } bit_r_sh1l; // register and bitmap data + typedef union {REGWORD reg; bit_r_sh1l bit;} reg_r_sh1l; // register and bitmap access + #define IDX_SH1L 0x0E // index value selecting this multi-register + + +#define R_SH1H 0x15 // register access + #define M_SH1H 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_sh1h:8; + } bit_r_sh1h; // register and bitmap data + typedef union {REGWORD reg; bit_r_sh1h bit;} reg_r_sh1h; // register and bitmap access + #define IDX_SH1H 0x0F // index value selecting this multi-register + + +#define R_RAM_USE 0x15 // register access + #define M_SRAM_USE 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_sram_use:8; + } bit_r_ram_use; // register and bitmap data + typedef union {REGWORD reg; bit_r_ram_use bit;} reg_r_ram_use; // register and bitmap access + + +#define R_SU_SEL 0x16 // register access + #define M_SU_SEL 0x03 // bitmap mask (2bit) + #define M1_SU_SEL 0x01 + #define M_MULT_SU 0x08 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_su_sel:2; + REGWORD reserved_25:1; + REGWORD v_mult_su:1; + REGWORD reserved_26:4; + } bit_r_su_sel; // register and bitmap data + typedef union {REGWORD reg; bit_r_su_sel bit;} reg_r_su_sel; // register and bitmap access + + +#define R_CHIP_ID 0x16 // register access + #define M_CHIP_ID 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_chip_id:8; + } bit_r_chip_id; // register and bitmap data + typedef union {REGWORD reg; bit_r_chip_id bit;} reg_r_chip_id; // register and bitmap access + + +#define R_SU_SYNC 0x17 // register access + #define M_SYNC_SEL 0x07 // bitmap mask (3bit) + #define M1_SYNC_SEL 0x01 + #define M_MAN_SYNC 0x08 // bitmap mask (1bit) + #define M_AUTO_SYNCI 0x10 // bitmap mask (1bit) + #define M_D_MERGE_TX 0x20 // bitmap mask (1bit) + #define M_E_MERGE_RX 0x40 // bitmap mask (1bit) + #define M_D_MERGE_RX 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_sync_sel:3; + REGWORD v_man_sync:1; + REGWORD v_auto_synci:1; + REGWORD v_d_merge_tx:1; + REGWORD v_e_merge_rx:1; + REGWORD v_d_merge_rx:1; + } bit_r_su_sync; // register and bitmap data + typedef union {REGWORD reg; bit_r_su_sync bit;} reg_r_su_sync; // register and bitmap access + + +#define R_BERT_STA 0x17 // register access + #define M_RD_SYNC_SRC 0x07 // bitmap mask (3bit) + #define M1_RD_SYNC_SRC 0x01 + #define M_BERT_SYNC 0x10 // bitmap mask (1bit) + #define M_BERT_INV_DATA 0x20 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_rd_sync_src:3; + REGWORD reserved_21:1; + REGWORD v_bert_sync:1; + REGWORD v_bert_inv_data:1; + REGWORD reserved_22:2; + } bit_r_bert_sta; // register and bitmap data + typedef union {REGWORD reg; bit_r_bert_sta bit;} reg_r_bert_sta; // register and bitmap access + + +#define R_F0_CNTL 0x18 // register access + #define M_F0_CNTL 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_f0_cntl:8; + } bit_r_f0_cntl; // register and bitmap data + typedef union {REGWORD reg; bit_r_f0_cntl bit;} reg_r_f0_cntl; // register and bitmap access + + +#define R_F0_CNTH 0x19 // register access + #define M_F0_CNTH 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_f0_cnth:8; + } bit_r_f0_cnth; // register and bitmap data + typedef union {REGWORD reg; bit_r_f0_cnth bit;} reg_r_f0_cnth; // register and bitmap access + + +#define R_BERT_ECL 0x1A // register access + #define M_BERT_ECL 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_bert_ecl:8; + } bit_r_bert_ecl; // register and bitmap data + typedef union {REGWORD reg; bit_r_bert_ecl bit;} reg_r_bert_ecl; // register and bitmap access + + +#define R_TI_WD 0x1A // register access + #define M_EV_TS 0x0F // bitmap mask (4bit) + #define M1_EV_TS 0x01 + #define M_WD_TS 0xF0 // bitmap mask (4bit) + #define M1_WD_TS 0x10 + + typedef struct // bitmap construction + { + REGWORD v_ev_ts:4; + REGWORD v_wd_ts:4; + } bit_r_ti_wd; // register and bitmap data + typedef union {REGWORD reg; bit_r_ti_wd bit;} reg_r_ti_wd; // register and bitmap access + + +#define R_BERT_ECH 0x1B // register access + #define M_BERT_ECH 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_bert_ech:8; + } bit_r_bert_ech; // register and bitmap data + typedef union {REGWORD reg; bit_r_bert_ech bit;} reg_r_bert_ech; // register and bitmap access + + +#define R_BERT_WD_MD 0x1B // register access + #define M_PAT_SEQ 0x07 // bitmap mask (3bit) + #define M1_PAT_SEQ 0x01 + #define M_BERT_ERR 0x08 // bitmap mask (1bit) + #define M_AUTO_WD_RES 0x20 // bitmap mask (1bit) + #define M_WD_RES 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_pat_seq:3; + REGWORD v_bert_err:1; + REGWORD reserved_16:1; + REGWORD v_auto_wd_res:1; + REGWORD reserved_17:1; + REGWORD v_wd_res:1; + } bit_r_bert_wd_md; // register and bitmap data + typedef union {REGWORD reg; bit_r_bert_wd_md bit;} reg_r_bert_wd_md; // register and bitmap access + + +#define R_STATUS 0x1C // register access + #define M_BUSY 0x01 // bitmap mask (1bit) + #define M_PROC 0x02 // bitmap mask (1bit) + #define M_LOST_STA 0x08 // bitmap mask (1bit) + #define M_PCM_INIT 0x10 // bitmap mask (1bit) + #define M_WAK_STA 0x20 // bitmap mask (1bit) + #define M_MISC_IRQSTA 0x40 // bitmap mask (1bit) + #define M_FR_IRQSTA 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_busy:1; + REGWORD v_proc:1; + REGWORD reserved_23:1; + REGWORD v_lost_sta:1; + REGWORD v_pcm_init:1; + REGWORD v_wak_sta:1; + REGWORD v_misc_irqsta:1; + REGWORD v_fr_irqsta:1; + } bit_r_status; // register and bitmap data + typedef union {REGWORD reg; bit_r_status bit;} reg_r_status; // register and bitmap access + + +#define R_SL_MAX 0x1D // register access + #define M_SL_MAX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_sl_max:8; + } bit_r_sl_max; // register and bitmap data + typedef union {REGWORD reg; bit_r_sl_max bit;} reg_r_sl_max; // register and bitmap access + + +#define R_PWM_CFG 0x1E // register access + #define M_PWM0_16KHZ 0x10 // bitmap mask (1bit) + #define M_PWM1_16KHZ 0x20 // bitmap mask (1bit) + #define M_PWM_FRQ 0xC0 // bitmap mask (2bit) + #define M1_PWM_FRQ 0x40 + + typedef struct // bitmap construction + { + REGWORD reserved_18:4; + REGWORD v_pwm0_16khz:1; + REGWORD v_pwm1_16khz:1; + REGWORD v_pwm_frq:2; + } bit_r_pwm_cfg; // register and bitmap data + typedef union {REGWORD reg; bit_r_pwm_cfg bit;} reg_r_pwm_cfg; // register and bitmap access + + +#define R_CHIP_RV 0x1F // register access + #define M_CHIP_RV 0x0F // bitmap mask (4bit) + #define M1_CHIP_RV 0x01 + + typedef struct // bitmap construction + { + REGWORD v_chip_rv:4; + REGWORD reserved_24:4; + } bit_r_chip_rv; // register and bitmap data + typedef union {REGWORD reg; bit_r_chip_rv bit;} reg_r_chip_rv; // register and bitmap access + + +#define R_FIFO_BL0_IRQ 0x20 // register access + #define M_FIFO0_TX_IRQ 0x01 // bitmap mask (1bit) + #define M_FIFO0_RX_IRQ 0x02 // bitmap mask (1bit) + #define M_FIFO1_TX_IRQ 0x04 // bitmap mask (1bit) + #define M_FIFO1_RX_IRQ 0x08 // bitmap mask (1bit) + #define M_FIFO2_TX_IRQ 0x10 // bitmap mask (1bit) + #define M_FIFO2_RX_IRQ 0x20 // bitmap mask (1bit) + #define M_FIFO3_TX_IRQ 0x40 // bitmap mask (1bit) + #define M_FIFO3_RX_IRQ 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo0_tx_irq:1; + REGWORD v_fifo0_rx_irq:1; + REGWORD v_fifo1_tx_irq:1; + REGWORD v_fifo1_rx_irq:1; + REGWORD v_fifo2_tx_irq:1; + REGWORD v_fifo2_rx_irq:1; + REGWORD v_fifo3_tx_irq:1; + REGWORD v_fifo3_rx_irq:1; + } bit_r_fifo_bl0_irq; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_bl0_irq bit;} reg_r_fifo_bl0_irq; // register and bitmap access + + +#define R_FIFO_BL1_IRQ 0x21 // register access + #define M_FIFO4_TX_IRQ 0x01 // bitmap mask (1bit) + #define M_FIFO4_RX_IRQ 0x02 // bitmap mask (1bit) + #define M_FIFO5_TX_IRQ 0x04 // bitmap mask (1bit) + #define M_FIFO5_RX_IRQ 0x08 // bitmap mask (1bit) + #define M_FIFO6_TX_IRQ 0x10 // bitmap mask (1bit) + #define M_FIFO6_RX_IRQ 0x20 // bitmap mask (1bit) + #define M_FIFO7_TX_IRQ 0x40 // bitmap mask (1bit) + #define M_FIFO7_RX_IRQ 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo4_tx_irq:1; + REGWORD v_fifo4_rx_irq:1; + REGWORD v_fifo5_tx_irq:1; + REGWORD v_fifo5_rx_irq:1; + REGWORD v_fifo6_tx_irq:1; + REGWORD v_fifo6_rx_irq:1; + REGWORD v_fifo7_tx_irq:1; + REGWORD v_fifo7_rx_irq:1; + } bit_r_fifo_bl1_irq; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_bl1_irq bit;} reg_r_fifo_bl1_irq; // register and bitmap access + + +#define R_FIFO_BL2_IRQ 0x22 // register access + #define M_FIFO8_TX_IRQ 0x01 // bitmap mask (1bit) + #define M_FIFO8_RX_IRQ 0x02 // bitmap mask (1bit) + #define M_FIFO9_TX_IRQ 0x04 // bitmap mask (1bit) + #define M_FIFO9_RX_IRQ 0x08 // bitmap mask (1bit) + #define M_FIFO10_TX_IRQ 0x10 // bitmap mask (1bit) + #define M_FIFO10_RX_IRQ 0x20 // bitmap mask (1bit) + #define M_FIFO11_TX_IRQ 0x40 // bitmap mask (1bit) + #define M_FIFO11_RX_IRQ 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo8_tx_irq:1; + REGWORD v_fifo8_rx_irq:1; + REGWORD v_fifo9_tx_irq:1; + REGWORD v_fifo9_rx_irq:1; + REGWORD v_fifo10_tx_irq:1; + REGWORD v_fifo10_rx_irq:1; + REGWORD v_fifo11_tx_irq:1; + REGWORD v_fifo11_rx_irq:1; + } bit_r_fifo_bl2_irq; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_bl2_irq bit;} reg_r_fifo_bl2_irq; // register and bitmap access + + +#define R_FIFO_BL3_IRQ 0x23 // register access + #define M_FIFO12_TX_IRQ 0x01 // bitmap mask (1bit) + #define M_FIFO12_RX_IRQ 0x02 // bitmap mask (1bit) + #define M_FIFO13_TX_IRQ 0x04 // bitmap mask (1bit) + #define M_FIFO13_RX_IRQ 0x08 // bitmap mask (1bit) + #define M_FIFO14_TX_IRQ 0x10 // bitmap mask (1bit) + #define M_FIFO14_RX_IRQ 0x20 // bitmap mask (1bit) + #define M_FIFO15_TX_IRQ 0x40 // bitmap mask (1bit) + #define M_FIFO15_RX_IRQ 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo12_tx_irq:1; + REGWORD v_fifo12_rx_irq:1; + REGWORD v_fifo13_tx_irq:1; + REGWORD v_fifo13_rx_irq:1; + REGWORD v_fifo14_tx_irq:1; + REGWORD v_fifo14_rx_irq:1; + REGWORD v_fifo15_tx_irq:1; + REGWORD v_fifo15_rx_irq:1; + } bit_r_fifo_bl3_irq; // register and bitmap data + typedef union {REGWORD reg; bit_r_fifo_bl3_irq bit;} reg_r_fifo_bl3_irq; // register and bitmap access + + +#define R_FILL_BL0 0x24 // register access + #define M_FILL_FIFO0_TX 0x01 // bitmap mask (1bit) + #define M_FILL_FIFO0_RX 0x02 // bitmap mask (1bit) + #define M_FILL_FIFO1_TX 0x04 // bitmap mask (1bit) + #define M_FILL_FIFO1_RX 0x08 // bitmap mask (1bit) + #define M_FILL_FIFO2_TX 0x10 // bitmap mask (1bit) + #define M_FILL_FIFO2_RX 0x20 // bitmap mask (1bit) + #define M_FILL_FIFO3_TX 0x40 // bitmap mask (1bit) + #define M_FILL_FIFO3_RX 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fill_fifo0_tx:1; + REGWORD v_fill_fifo0_rx:1; + REGWORD v_fill_fifo1_tx:1; + REGWORD v_fill_fifo1_rx:1; + REGWORD v_fill_fifo2_tx:1; + REGWORD v_fill_fifo2_rx:1; + REGWORD v_fill_fifo3_tx:1; + REGWORD v_fill_fifo3_rx:1; + } bit_r_fill_bl0; // register and bitmap data + typedef union {REGWORD reg; bit_r_fill_bl0 bit;} reg_r_fill_bl0; // register and bitmap access + + +#define R_FILL_BL1 0x25 // register access + #define M_FILL_FIFO4_TX 0x01 // bitmap mask (1bit) + #define M_FILL_FIFO4_RX 0x02 // bitmap mask (1bit) + #define M_FILL_FIFO5_TX 0x04 // bitmap mask (1bit) + #define M_FILL_FIFO5_RX 0x08 // bitmap mask (1bit) + #define M_FILL_FIFO6_TX 0x10 // bitmap mask (1bit) + #define M_FILL_FIFO6_RX 0x20 // bitmap mask (1bit) + #define M_FILL_FIFO7_TX 0x40 // bitmap mask (1bit) + #define M_FILL_FIFO7_RX 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fill_fifo4_tx:1; + REGWORD v_fill_fifo4_rx:1; + REGWORD v_fill_fifo5_tx:1; + REGWORD v_fill_fifo5_rx:1; + REGWORD v_fill_fifo6_tx:1; + REGWORD v_fill_fifo6_rx:1; + REGWORD v_fill_fifo7_tx:1; + REGWORD v_fill_fifo7_rx:1; + } bit_r_fill_bl1; // register and bitmap data + typedef union {REGWORD reg; bit_r_fill_bl1 bit;} reg_r_fill_bl1; // register and bitmap access + + +#define R_FILL_BL2 0x26 // register access + #define M_FILL_FIFO8_TX 0x01 // bitmap mask (1bit) + #define M_FILL_FIFO8_RX 0x02 // bitmap mask (1bit) + #define M_FILL_FIFO9_TX 0x04 // bitmap mask (1bit) + #define M_FILL_FIFO9_RX 0x08 // bitmap mask (1bit) + #define M_FILL_FIFO10_TX 0x10 // bitmap mask (1bit) + #define M_FILL_FIFO10_RX 0x20 // bitmap mask (1bit) + #define M_FILL_FIFO11_TX 0x40 // bitmap mask (1bit) + #define M_FILL_FIFO11_RX 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fill_fifo8_tx:1; + REGWORD v_fill_fifo8_rx:1; + REGWORD v_fill_fifo9_tx:1; + REGWORD v_fill_fifo9_rx:1; + REGWORD v_fill_fifo10_tx:1; + REGWORD v_fill_fifo10_rx:1; + REGWORD v_fill_fifo11_tx:1; + REGWORD v_fill_fifo11_rx:1; + } bit_r_fill_bl2; // register and bitmap data + typedef union {REGWORD reg; bit_r_fill_bl2 bit;} reg_r_fill_bl2; // register and bitmap access + + +#define R_FILL_BL3 0x27 // register access + #define M_FILL_FIFO12_TX 0x01 // bitmap mask (1bit) + #define M_FILL_FIFO12_RX 0x02 // bitmap mask (1bit) + #define M_FILL_FIFO13_TX 0x04 // bitmap mask (1bit) + #define M_FILL_FIFO13_RX 0x08 // bitmap mask (1bit) + #define M_FILL_FIFO14_TX 0x10 // bitmap mask (1bit) + #define M_FILL_FIFO14_RX 0x20 // bitmap mask (1bit) + #define M_FILL_FIFO15_TX 0x40 // bitmap mask (1bit) + #define M_FILL_FIFO15_RX 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fill_fifo12_tx:1; + REGWORD v_fill_fifo12_rx:1; + REGWORD v_fill_fifo13_tx:1; + REGWORD v_fill_fifo13_rx:1; + REGWORD v_fill_fifo14_tx:1; + REGWORD v_fill_fifo14_rx:1; + REGWORD v_fill_fifo15_tx:1; + REGWORD v_fill_fifo15_rx:1; + } bit_r_fill_bl3; // register and bitmap data + typedef union {REGWORD reg; bit_r_fill_bl3 bit;} reg_r_fill_bl3; // register and bitmap access + + +#define R_CI_TX 0x28 // register access + #define M_GCI_C 0x3F // bitmap mask (6bit) + + typedef struct // bitmap construction + { + REGWORD v_gci_c:6; + REGWORD reserved_33:2; + } bit_r_ci_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_ci_tx bit;} reg_r_ci_tx; // register and bitmap access + + +#define R_CI_RX 0x28 // register access + #define M_GCI_I 0x3F // bitmap mask (6bit) + + typedef struct // bitmap construction + { + REGWORD v_gci_i:6; + REGWORD reserved_35:2; + } bit_r_ci_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_ci_rx bit;} reg_r_ci_rx; // register and bitmap access + + +#define R_GCI_CFG0 0x29 // register access + #define M_MON_END 0x01 // bitmap mask (1bit) + #define M_MON_SLOW 0x02 // bitmap mask (1bit) + #define M_MON_DLL 0x04 // bitmap mask (1bit) + #define M_MON_CI6 0x08 // bitmap mask (1bit) + #define M_GCI_SWAP_TXHS 0x10 // bitmap mask (1bit) + #define M_GCI_SWAP_RXHS 0x20 // bitmap mask (1bit) + #define M_GCI_SWAP_STIO 0x40 // bitmap mask (1bit) + #define M_GCI_EN 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_mon_end:1; + REGWORD v_mon_slow:1; + REGWORD v_mon_dll:1; + REGWORD v_mon_ci6:1; + REGWORD v_gci_swap_txhs:1; + REGWORD v_gci_swap_rxhs:1; + REGWORD v_gci_swap_stio:1; + REGWORD v_gci_en:1; + } bit_r_gci_cfg0; // register and bitmap data + typedef union {REGWORD reg; bit_r_gci_cfg0 bit;} reg_r_gci_cfg0; // register and bitmap access + + +#define R_GCI_STA 0x29 // register access + #define M_MON_RXR 0x01 // bitmap mask (1bit) + #define M_MON_TXR 0x02 // bitmap mask (1bit) + #define M_GCI_MX 0x04 // bitmap mask (1bit) + #define M_GCI_MR 0x08 // bitmap mask (1bit) + #define M_GCI_RX 0x10 // bitmap mask (1bit) + #define M_GCI_ABO 0x20 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_mon_rxr:1; + REGWORD v_mon_txr:1; + REGWORD v_gci_mx:1; + REGWORD v_gci_mr:1; + REGWORD v_gci_rx:1; + REGWORD v_gci_abo:1; + REGWORD reserved_36:2; + } bit_r_gci_sta; // register and bitmap data + typedef union {REGWORD reg; bit_r_gci_sta bit;} reg_r_gci_sta; // register and bitmap access + + +#define R_GCI_CFG1 0x2A // register access + #define M_GCI_SL 0x1F // bitmap mask (5bit) + #define M1_GCI_SL 0x01 + + typedef struct // bitmap construction + { + REGWORD v_gci_sl:5; + REGWORD reserved_34:3; + } bit_r_gci_cfg1; // register and bitmap data + typedef union {REGWORD reg; bit_r_gci_cfg1 bit;} reg_r_gci_cfg1; // register and bitmap access + + +#define R_MON_RX 0x2A // register access + #define M_MON_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_mon_rx:8; + } bit_r_mon_rx; // register and bitmap data + typedef union {REGWORD reg; bit_r_mon_rx bit;} reg_r_mon_rx; // register and bitmap access + + +#define R_MON_TX 0x2B // register access + #define M_MON_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_mon_tx:8; + } bit_r_mon_tx; // register and bitmap data + typedef union {REGWORD reg; bit_r_mon_tx bit;} reg_r_mon_tx; // register and bitmap access + + +#define A_SU_WR_STA 0x30 // register access + #define M_SU_SET_STA 0x0F // bitmap mask (4bit) + #define M1_SU_SET_STA 0x01 + #define M_SU_LD_STA 0x10 // bitmap mask (1bit) + #define M_SU_ACT 0x60 // bitmap mask (2bit) + #define M1_SU_ACT 0x20 + #define M_SU_SET_G2_G3 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_su_set_sta:4; + REGWORD v_su_ld_sta:1; + REGWORD v_su_act:2; + REGWORD v_su_set_g2_g3:1; + } bit_a_su_wr_sta; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_wr_sta bit;} reg_a_su_wr_sta; // register and bitmap access + + +#define A_SU_RD_STA 0x30 // register access + #define M_SU_STA 0x0F // bitmap mask (4bit) + #define M1_SU_STA 0x01 + #define M_SU_FR_SYNC 0x10 // bitmap mask (1bit) + #define M_SU_T2_EXP 0x20 // bitmap mask (1bit) + #define M_SU_INFO0 0x40 // bitmap mask (1bit) + #define M_G2_G3 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_su_sta:4; + REGWORD v_su_fr_sync:1; + REGWORD v_su_t2_exp:1; + REGWORD v_su_info0:1; + REGWORD v_g2_g3:1; + } bit_a_su_rd_sta; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_rd_sta bit;} reg_a_su_rd_sta; // register and bitmap access + + +#define A_SU_CTRL0 0x31 // register access + #define M_B1_TX_EN 0x01 // bitmap mask (1bit) + #define M_B2_TX_EN 0x02 // bitmap mask (1bit) + #define M_SU_MD 0x04 // bitmap mask (1bit) + #define M_ST_D_LPRIO 0x08 // bitmap mask (1bit) + #define M_ST_SQ_EN 0x10 // bitmap mask (1bit) + #define M_SU_TST_SIG 0x20 // bitmap mask (1bit) + #define M_ST_PU_CTRL 0x40 // bitmap mask (1bit) + #define M_SU_STOP 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_b1_tx_en:1; + REGWORD v_b2_tx_en:1; + REGWORD v_su_md:1; + REGWORD v_st_d_lprio:1; + REGWORD v_st_sq_en:1; + REGWORD v_su_tst_sig:1; + REGWORD v_st_pu_ctrl:1; + REGWORD v_su_stop:1; + } bit_a_su_ctrl0; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_ctrl0 bit;} reg_a_su_ctrl0; // register and bitmap access + + +#define A_SU_DLYL 0x31 // register access + #define M_SU_DLYL 0x1F // bitmap mask (5bit) + #define M1_SU_DLYL 0x01 + + typedef struct // bitmap construction + { + REGWORD v_su_dlyl:5; + REGWORD reserved_46:3; + } bit_a_su_dlyl; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_dlyl bit;} reg_a_su_dlyl; // register and bitmap access + + +#define A_SU_CTRL1 0x32 // register access + #define M_G2_G3_EN 0x01 // bitmap mask (1bit) + #define M_D_RES 0x04 // bitmap mask (1bit) + #define M_ST_E_IGNO 0x08 // bitmap mask (1bit) + #define M_ST_E_LO 0x10 // bitmap mask (1bit) + #define M_BAC_D 0x40 // bitmap mask (1bit) + #define M_B12_SWAP 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_g2_g3_en:1; + REGWORD reserved_37:1; + REGWORD v_d_res:1; + REGWORD v_st_e_igno:1; + REGWORD v_st_e_lo:1; + REGWORD reserved_38:1; + REGWORD v_bac_d:1; + REGWORD v_b12_swap:1; + } bit_a_su_ctrl1; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_ctrl1 bit;} reg_a_su_ctrl1; // register and bitmap access + + +#define A_SU_DLYH 0x32 // register access + #define M_SU_DLYH 0x1F // bitmap mask (5bit) + #define M1_SU_DLYH 0x01 + + typedef struct // bitmap construction + { + REGWORD v_su_dlyh:5; + REGWORD reserved_47:3; + } bit_a_su_dlyh; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_dlyh bit;} reg_a_su_dlyh; // register and bitmap access + + +#define A_SU_CTRL2 0x33 // register access + #define M_B1_RX_EN 0x01 // bitmap mask (1bit) + #define M_B2_RX_EN 0x02 // bitmap mask (1bit) + #define M_MS_SSYNC2 0x04 // bitmap mask (1bit) + #define M_BAC_S_SEL 0x08 // bitmap mask (1bit) + #define M_SU_SYNC_NT 0x10 // bitmap mask (1bit) + #define M_SU_2KHZ 0x20 // bitmap mask (1bit) + #define M_SU_TRI 0x40 // bitmap mask (1bit) + #define M_SU_EXCHG 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_b1_rx_en:1; + REGWORD v_b2_rx_en:1; + REGWORD v_ms_ssync2:1; + REGWORD v_bac_s_sel:1; + REGWORD v_su_sync_nt:1; + REGWORD v_su_2khz:1; + REGWORD v_su_tri:1; + REGWORD v_su_exchg:1; + } bit_a_su_ctrl2; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_ctrl2 bit;} reg_a_su_ctrl2; // register and bitmap access + + +#define A_MS_TX 0x34 // register access + #define M_MS_TX 0x0F // bitmap mask (4bit) + #define M1_MS_TX 0x01 + #define M_UP_S_TX 0x40 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_ms_tx:4; + REGWORD reserved_39:2; + REGWORD v_up_s_tx:1; + REGWORD reserved_40:1; + } bit_a_ms_tx; // register and bitmap data + typedef union {REGWORD reg; bit_a_ms_tx bit;} reg_a_ms_tx; // register and bitmap access + + +#define A_MS_RX 0x34 // register access + #define M_MS_RX 0x0F // bitmap mask (4bit) + #define M1_MS_RX 0x01 + #define M_MS_RX_RDY 0x10 // bitmap mask (1bit) + #define M_UP_S_RX 0x40 // bitmap mask (1bit) + #define M_MS_TX_RDY 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_ms_rx:4; + REGWORD v_ms_rx_rdy:1; + REGWORD reserved_48:1; + REGWORD v_up_s_rx:1; + REGWORD v_ms_tx_rdy:1; + } bit_a_ms_rx; // register and bitmap data + typedef union {REGWORD reg; bit_a_ms_rx bit;} reg_a_ms_rx; // register and bitmap access + + +#define A_ST_CTRL3 0x35 // register access + #define M_ST_SEL 0x01 // bitmap mask (1bit) + #define M_ST_PULSE 0xFE // bitmap mask (7bit) + + typedef struct // bitmap construction + { + REGWORD v_st_sel:1; + REGWORD v_st_pulse:7; + } bit_a_st_ctrl3; // register and bitmap data + typedef union {REGWORD reg; bit_a_st_ctrl3 bit;} reg_a_st_ctrl3; // register and bitmap access + #define IDX_ST_CTRL3 0x00 // index value selecting this multi-register + + +#define A_UP_CTRL3 0x35 // register access + #define M_UP_SEL 0x01 // bitmap mask (1bit) + #define M_UP_VIO 0x02 // bitmap mask (1bit) + #define M_UP_DC_STR 0x04 // bitmap mask (1bit) + #define M_UP_DC_OFF 0x08 // bitmap mask (1bit) + #define M_UP_RPT_PAT 0x10 // bitmap mask (1bit) + #define M_UP_SCRM_MD 0x20 // bitmap mask (1bit) + #define M_UP_SCRM_TX_OFF 0x40 // bitmap mask (1bit) + #define M_UP_SCRM_RX_OFF 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_up_sel:1; + REGWORD v_up_vio:1; + REGWORD v_up_dc_str:1; + REGWORD v_up_dc_off:1; + REGWORD v_up_rpt_pat:1; + REGWORD v_up_scrm_md:1; + REGWORD v_up_scrm_tx_off:1; + REGWORD v_up_scrm_rx_off:1; + } bit_a_up_ctrl3; // register and bitmap data + typedef union {REGWORD reg; bit_a_up_ctrl3 bit;} reg_a_up_ctrl3; // register and bitmap access + #define IDX_UP_CTRL3 0x01 // index value selecting this multi-register + + +#define A_SU_STA 0x35 // register access + #define M_ST_D_HPRIO9 0x01 // bitmap mask (1bit) + #define M_ST_D_LPRIO11 0x02 // bitmap mask (1bit) + #define M_ST_D_CONT 0x04 // bitmap mask (1bit) + #define M_ST_D_ACT 0x08 // bitmap mask (1bit) + #define M_SU_AF0 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_st_d_hprio9:1; + REGWORD v_st_d_lprio11:1; + REGWORD v_st_d_cont:1; + REGWORD v_st_d_act:1; + REGWORD reserved_49:3; + REGWORD v_su_af0:1; + } bit_a_su_sta; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_sta bit;} reg_a_su_sta; // register and bitmap access + + +#define A_MS_DF 0x36 // register access + #define M_BAC_NINV 0x01 // bitmap mask (1bit) + #define M_SG_AB_INV 0x02 // bitmap mask (1bit) + #define M_SQ_T_SRC 0x04 // bitmap mask (1bit) + #define M_M_S_SRC 0x08 // bitmap mask (1bit) + #define M_SQ_T_DST 0x10 // bitmap mask (1bit) + #define M_SU_RX_VAL 0x20 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_bac_ninv:1; + REGWORD v_sg_ab_inv:1; + REGWORD v_sq_t_src:1; + REGWORD v_m_s_src:1; + REGWORD v_sq_t_dst:1; + REGWORD v_su_rx_val:1; + REGWORD reserved_41:2; + } bit_a_ms_df; // register and bitmap data + typedef union {REGWORD reg; bit_a_ms_df bit;} reg_a_ms_df; // register and bitmap access + + +#define A_SU_CLK_DLY 0x37 // register access + #define M_SU_CLK_DLY 0x0F // bitmap mask (4bit) + #define M1_SU_CLK_DLY 0x01 + #define M_ST_SMPL 0x70 // bitmap mask (3bit) + #define M1_ST_SMPL 0x10 + + typedef struct // bitmap construction + { + REGWORD v_su_clk_dly:4; + REGWORD v_st_smpl:3; + REGWORD reserved_42:1; + } bit_a_su_clk_dly; // register and bitmap data + typedef union {REGWORD reg; bit_a_su_clk_dly bit;} reg_a_su_clk_dly; // register and bitmap access + + +#define R_PWM0 0x38 // register access + #define M_PWM0 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_pwm0:8; + } bit_r_pwm0; // register and bitmap data + typedef union {REGWORD reg; bit_r_pwm0 bit;} reg_r_pwm0; // register and bitmap access + + +#define R_PWM1 0x39 // register access + #define M_PWM1 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_pwm1:8; + } bit_r_pwm1; // register and bitmap data + typedef union {REGWORD reg; bit_r_pwm1 bit;} reg_r_pwm1; // register and bitmap access + + +#define A_B1_TX 0x3C // register access + #define M_B1_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_b1_tx:8; + } bit_a_b1_tx; // register and bitmap data + typedef union {REGWORD reg; bit_a_b1_tx bit;} reg_a_b1_tx; // register and bitmap access + + +#define A_B1_RX 0x3C // register access + #define M_B1_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_b1_rx:8; + } bit_a_b1_rx; // register and bitmap data + typedef union {REGWORD reg; bit_a_b1_rx bit;} reg_a_b1_rx; // register and bitmap access + + +#define A_B2_TX 0x3D // register access + #define M_B2_TX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_b2_tx:8; + } bit_a_b2_tx; // register and bitmap data + typedef union {REGWORD reg; bit_a_b2_tx bit;} reg_a_b2_tx; // register and bitmap access + + +#define A_B2_RX 0x3D // register access + #define M_B2_RX 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_b2_rx:8; + } bit_a_b2_rx; // register and bitmap data + typedef union {REGWORD reg; bit_a_b2_rx bit;} reg_a_b2_rx; // register and bitmap access + + +#define A_D_TX 0x3E // register access + #define M_D_TX_S 0x01 // bitmap mask (1bit) + #define M_D_TX_BAC 0x20 // bitmap mask (1bit) + #define M_D_TX 0xC0 // bitmap mask (2bit) + #define M1_D_TX 0x40 + + typedef struct // bitmap construction + { + REGWORD v_d_tx_s:1; + REGWORD reserved_43:4; + REGWORD v_d_tx_bac:1; + REGWORD v_d_tx:2; + } bit_a_d_tx; // register and bitmap data + typedef union {REGWORD reg; bit_a_d_tx bit;} reg_a_d_tx; // register and bitmap access + + +#define A_D_RX 0x3E // register access + #define M_D_RX_S 0x01 // bitmap mask (1bit) + #define M_D_RX_AB 0x10 // bitmap mask (1bit) + #define M_D_RX_SG 0x20 // bitmap mask (1bit) + #define M_D_RX 0xC0 // bitmap mask (2bit) + #define M1_D_RX 0x40 + + typedef struct // bitmap construction + { + REGWORD v_d_rx_s:1; + REGWORD reserved_50:3; + REGWORD v_d_rx_ab:1; + REGWORD v_d_rx_sg:1; + REGWORD v_d_rx:2; + } bit_a_d_rx; // register and bitmap data + typedef union {REGWORD reg; bit_a_d_rx bit;} reg_a_d_rx; // register and bitmap access + + +#define A_E_RX 0x3F // register access + #define M_E_RX_S 0x01 // bitmap mask (1bit) + #define M_E_RX_AB 0x10 // bitmap mask (1bit) + #define M_E_RX_SG 0x20 // bitmap mask (1bit) + #define M_E_RX 0xC0 // bitmap mask (2bit) + #define M1_E_RX 0x40 + + typedef struct // bitmap construction + { + REGWORD v_e_rx_s:1; + REGWORD reserved_51:3; + REGWORD v_e_rx_ab:1; + REGWORD v_e_rx_sg:1; + REGWORD v_e_rx:2; + } bit_a_e_rx; // register and bitmap data + typedef union {REGWORD reg; bit_a_e_rx bit;} reg_a_e_rx; // register and bitmap access + + +#define A_BAC_S_TX 0x3F // register access + #define M_S_TX 0x01 // bitmap mask (1bit) + #define M_BAC_TX 0x20 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_s_tx:1; + REGWORD reserved_44:4; + REGWORD v_bac_tx:1; + REGWORD reserved_45:2; + } bit_a_bac_s_tx; // register and bitmap data + typedef union {REGWORD reg; bit_a_bac_s_tx bit;} reg_a_bac_s_tx; // register and bitmap access + + +#define R_GPIO_OUT1 0x40 // register access + #define M_GPIO_OUT8 0x01 // bitmap mask (1bit) + #define M_GPIO_OUT9 0x02 // bitmap mask (1bit) + #define M_GPIO_OUT10 0x04 // bitmap mask (1bit) + #define M_GPIO_OUT11 0x08 // bitmap mask (1bit) + #define M_GPIO_OUT12 0x10 // bitmap mask (1bit) + #define M_GPIO_OUT13 0x20 // bitmap mask (1bit) + #define M_GPIO_OUT14 0x40 // bitmap mask (1bit) + #define M_GPIO_OUT15 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_out8:1; + REGWORD v_gpio_out9:1; + REGWORD v_gpio_out10:1; + REGWORD v_gpio_out11:1; + REGWORD v_gpio_out12:1; + REGWORD v_gpio_out13:1; + REGWORD v_gpio_out14:1; + REGWORD v_gpio_out15:1; + } bit_r_gpio_out1; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_out1 bit;} reg_r_gpio_out1; // register and bitmap access + + +#define R_GPIO_IN1 0x40 // register access + #define M_GPIO_IN8 0x01 // bitmap mask (1bit) + #define M_GPIO_IN9 0x02 // bitmap mask (1bit) + #define M_GPIO_IN10 0x04 // bitmap mask (1bit) + #define M_GPIO_IN11 0x08 // bitmap mask (1bit) + #define M_GPIO_IN12 0x10 // bitmap mask (1bit) + #define M_GPIO_IN13 0x20 // bitmap mask (1bit) + #define M_GPIO_IN14 0x40 // bitmap mask (1bit) + #define M_GPIO_IN15 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_in8:1; + REGWORD v_gpio_in9:1; + REGWORD v_gpio_in10:1; + REGWORD v_gpio_in11:1; + REGWORD v_gpio_in12:1; + REGWORD v_gpio_in13:1; + REGWORD v_gpio_in14:1; + REGWORD v_gpio_in15:1; + } bit_r_gpio_in1; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_in1 bit;} reg_r_gpio_in1; // register and bitmap access + + +#define R_GPIO_OUT3 0x41 // register access + #define M_GPIO_OUT24 0x01 // bitmap mask (1bit) + #define M_GPIO_OUT25 0x02 // bitmap mask (1bit) + #define M_GPIO_OUT26 0x04 // bitmap mask (1bit) + #define M_GPIO_OUT27 0x08 // bitmap mask (1bit) + #define M_GPIO_OUT28 0x10 // bitmap mask (1bit) + #define M_GPIO_OUT29 0x20 // bitmap mask (1bit) + #define M_GPIO_OUT30 0x40 // bitmap mask (1bit) + #define M_GPIO_OUT31 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_out24:1; + REGWORD v_gpio_out25:1; + REGWORD v_gpio_out26:1; + REGWORD v_gpio_out27:1; + REGWORD v_gpio_out28:1; + REGWORD v_gpio_out29:1; + REGWORD v_gpio_out30:1; + REGWORD v_gpio_out31:1; + } bit_r_gpio_out3; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_out3 bit;} reg_r_gpio_out3; // register and bitmap access + + +#define R_GPIO_IN3 0x41 // register access + #define M_GPIO_IN24 0x01 // bitmap mask (1bit) + #define M_GPIO_IN25 0x02 // bitmap mask (1bit) + #define M_GPIO_IN26 0x04 // bitmap mask (1bit) + #define M_GPIO_IN27 0x08 // bitmap mask (1bit) + #define M_GPIO_IN28 0x10 // bitmap mask (1bit) + #define M_GPIO_IN29 0x20 // bitmap mask (1bit) + #define M_GPIO_IN30 0x40 // bitmap mask (1bit) + #define M_GPIO_IN31 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_in24:1; + REGWORD v_gpio_in25:1; + REGWORD v_gpio_in26:1; + REGWORD v_gpio_in27:1; + REGWORD v_gpio_in28:1; + REGWORD v_gpio_in29:1; + REGWORD v_gpio_in30:1; + REGWORD v_gpio_in31:1; + } bit_r_gpio_in3; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_in3 bit;} reg_r_gpio_in3; // register and bitmap access + + +#define R_GPIO_EN1 0x42 // register access + #define M_GPIO_EN8 0x01 // bitmap mask (1bit) + #define M_GPIO_EN9 0x02 // bitmap mask (1bit) + #define M_GPIO_EN10 0x04 // bitmap mask (1bit) + #define M_GPIO_EN11 0x08 // bitmap mask (1bit) + #define M_GPIO_EN12 0x10 // bitmap mask (1bit) + #define M_GPIO_EN13 0x20 // bitmap mask (1bit) + #define M_GPIO_EN14 0x40 // bitmap mask (1bit) + #define M_GPIO_EN15 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_en8:1; + REGWORD v_gpio_en9:1; + REGWORD v_gpio_en10:1; + REGWORD v_gpio_en11:1; + REGWORD v_gpio_en12:1; + REGWORD v_gpio_en13:1; + REGWORD v_gpio_en14:1; + REGWORD v_gpio_en15:1; + } bit_r_gpio_en1; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_en1 bit;} reg_r_gpio_en1; // register and bitmap access + + +#define R_GPIO_EN3 0x43 // register access + #define M_GPIO_EN24 0x01 // bitmap mask (1bit) + #define M_GPIO_EN25 0x02 // bitmap mask (1bit) + #define M_GPIO_EN26 0x04 // bitmap mask (1bit) + #define M_GPIO_EN27 0x08 // bitmap mask (1bit) + #define M_GPIO_EN28 0x10 // bitmap mask (1bit) + #define M_GPIO_EN29 0x20 // bitmap mask (1bit) + #define M_GPIO_EN30 0x40 // bitmap mask (1bit) + #define M_GPIO_EN31 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_en24:1; + REGWORD v_gpio_en25:1; + REGWORD v_gpio_en26:1; + REGWORD v_gpio_en27:1; + REGWORD v_gpio_en28:1; + REGWORD v_gpio_en29:1; + REGWORD v_gpio_en30:1; + REGWORD v_gpio_en31:1; + } bit_r_gpio_en3; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_en3 bit;} reg_r_gpio_en3; // register and bitmap access + + +#define R_GPIO_SEL_BL 0x44 // register access + #define M_GPIO_BL0 0x01 // bitmap mask (1bit) + #define M_GPIO_BL1 0x02 // bitmap mask (1bit) + #define M_GPIO_BL2 0x04 // bitmap mask (1bit) + #define M_GPIO_BL3 0x08 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_bl0:1; + REGWORD v_gpio_bl1:1; + REGWORD v_gpio_bl2:1; + REGWORD v_gpio_bl3:1; + REGWORD reserved_54:4; + } bit_r_gpio_sel_bl; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_sel_bl bit;} reg_r_gpio_sel_bl; // register and bitmap access + + +#define R_GPIO_OUT2 0x45 // register access + #define M_GPIO_OUT16 0x01 // bitmap mask (1bit) + #define M_GPIO_OUT17 0x02 // bitmap mask (1bit) + #define M_GPIO_OUT18 0x04 // bitmap mask (1bit) + #define M_GPIO_OUT19 0x08 // bitmap mask (1bit) + #define M_GPIO_OUT20 0x10 // bitmap mask (1bit) + #define M_GPIO_OUT21 0x20 // bitmap mask (1bit) + #define M_GPIO_OUT22 0x40 // bitmap mask (1bit) + #define M_GPIO_OUT23 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_out16:1; + REGWORD v_gpio_out17:1; + REGWORD v_gpio_out18:1; + REGWORD v_gpio_out19:1; + REGWORD v_gpio_out20:1; + REGWORD v_gpio_out21:1; + REGWORD v_gpio_out22:1; + REGWORD v_gpio_out23:1; + } bit_r_gpio_out2; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_out2 bit;} reg_r_gpio_out2; // register and bitmap access + + +#define R_GPIO_IN2 0x45 // register access + #define M_GPIO_IN16 0x01 // bitmap mask (1bit) + #define M_GPIO_IN17 0x02 // bitmap mask (1bit) + #define M_GPIO_IN18 0x04 // bitmap mask (1bit) + #define M_GPIO_IN19 0x08 // bitmap mask (1bit) + #define M_GPIO_IN20 0x10 // bitmap mask (1bit) + #define M_GPIO_IN21 0x20 // bitmap mask (1bit) + #define M_GPIO_IN22 0x40 // bitmap mask (1bit) + #define M_GPIO_IN23 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_in16:1; + REGWORD v_gpio_in17:1; + REGWORD v_gpio_in18:1; + REGWORD v_gpio_in19:1; + REGWORD v_gpio_in20:1; + REGWORD v_gpio_in21:1; + REGWORD v_gpio_in22:1; + REGWORD v_gpio_in23:1; + } bit_r_gpio_in2; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_in2 bit;} reg_r_gpio_in2; // register and bitmap access + + +#define R_PWM_MD 0x46 // register access + #define M_WAK_EN 0x02 // bitmap mask (1bit) + #define M_PWM0_MD 0x30 // bitmap mask (2bit) + #define M1_PWM0_MD 0x10 + #define M_PWM1_MD 0xC0 // bitmap mask (2bit) + #define M1_PWM1_MD 0x40 + + typedef struct // bitmap construction + { + REGWORD reserved_52:1; + REGWORD v_wak_en:1; + REGWORD reserved_53:2; + REGWORD v_pwm0_md:2; + REGWORD v_pwm1_md:2; + } bit_r_pwm_md; // register and bitmap data + typedef union {REGWORD reg; bit_r_pwm_md bit;} reg_r_pwm_md; // register and bitmap access + + +#define R_GPIO_EN2 0x47 // register access + #define M_GPIO_EN16 0x01 // bitmap mask (1bit) + #define M_GPIO_EN17 0x02 // bitmap mask (1bit) + #define M_GPIO_EN18 0x04 // bitmap mask (1bit) + #define M_GPIO_EN19 0x08 // bitmap mask (1bit) + #define M_GPIO_EN20 0x10 // bitmap mask (1bit) + #define M_GPIO_EN21 0x20 // bitmap mask (1bit) + #define M_GPIO_EN22 0x40 // bitmap mask (1bit) + #define M_GPIO_EN23 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_en16:1; + REGWORD v_gpio_en17:1; + REGWORD v_gpio_en18:1; + REGWORD v_gpio_en19:1; + REGWORD v_gpio_en20:1; + REGWORD v_gpio_en21:1; + REGWORD v_gpio_en22:1; + REGWORD v_gpio_en23:1; + } bit_r_gpio_en2; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_en2 bit;} reg_r_gpio_en2; // register and bitmap access + + +#define R_GPIO_IN0 0x48 // register access + #define M_GPIO_IN0 0x01 // bitmap mask (1bit) + #define M_GPIO_IN1 0x02 // bitmap mask (1bit) + #define M_GPIO_IN2 0x04 // bitmap mask (1bit) + #define M_GPIO_IN3 0x08 // bitmap mask (1bit) + #define M_GPIO_IN4 0x10 // bitmap mask (1bit) + #define M_GPIO_IN5 0x20 // bitmap mask (1bit) + #define M_GPIO_IN6 0x40 // bitmap mask (1bit) + #define M_GPIO_IN7 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_in0:1; + REGWORD v_gpio_in1:1; + REGWORD v_gpio_in2:1; + REGWORD v_gpio_in3:1; + REGWORD v_gpio_in4:1; + REGWORD v_gpio_in5:1; + REGWORD v_gpio_in6:1; + REGWORD v_gpio_in7:1; + } bit_r_gpio_in0; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_in0 bit;} reg_r_gpio_in0; // register and bitmap access + + +#define R_GPIO_OUT0 0x48 // register access + #define M_GPIO_OUT0 0x01 // bitmap mask (1bit) + #define M_GPIO_OUT1 0x02 // bitmap mask (1bit) + #define M_GPIO_OUT2 0x04 // bitmap mask (1bit) + #define M_GPIO_OUT3 0x08 // bitmap mask (1bit) + #define M_GPIO_OUT4 0x10 // bitmap mask (1bit) + #define M_GPIO_OUT5 0x20 // bitmap mask (1bit) + #define M_GPIO_OUT6 0x40 // bitmap mask (1bit) + #define M_GPIO_OUT7 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_out0:1; + REGWORD v_gpio_out1:1; + REGWORD v_gpio_out2:1; + REGWORD v_gpio_out3:1; + REGWORD v_gpio_out4:1; + REGWORD v_gpio_out5:1; + REGWORD v_gpio_out6:1; + REGWORD v_gpio_out7:1; + } bit_r_gpio_out0; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_out0 bit;} reg_r_gpio_out0; // register and bitmap access + + +#define R_GPIO_EN0 0x4A // register access + #define M_GPIO_EN0 0x01 // bitmap mask (1bit) + #define M_GPIO_EN1 0x02 // bitmap mask (1bit) + #define M_GPIO_EN2 0x04 // bitmap mask (1bit) + #define M_GPIO_EN3 0x08 // bitmap mask (1bit) + #define M_GPIO_EN4 0x10 // bitmap mask (1bit) + #define M_GPIO_EN5 0x20 // bitmap mask (1bit) + #define M_GPIO_EN6 0x40 // bitmap mask (1bit) + #define M_GPIO_EN7 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_en0:1; + REGWORD v_gpio_en1:1; + REGWORD v_gpio_en2:1; + REGWORD v_gpio_en3:1; + REGWORD v_gpio_en4:1; + REGWORD v_gpio_en5:1; + REGWORD v_gpio_en6:1; + REGWORD v_gpio_en7:1; + } bit_r_gpio_en0; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_en0 bit;} reg_r_gpio_en0; // register and bitmap access + + +#define R_GPIO_SEL 0x4C // register access + #define M_GPIO_SEL0 0x01 // bitmap mask (1bit) + #define M_GPIO_SEL1 0x02 // bitmap mask (1bit) + #define M_GPIO_SEL2 0x04 // bitmap mask (1bit) + #define M_GPIO_SEL3 0x08 // bitmap mask (1bit) + #define M_GPIO_SEL4 0x10 // bitmap mask (1bit) + #define M_GPIO_SEL5 0x20 // bitmap mask (1bit) + #define M_GPIO_SEL6 0x40 // bitmap mask (1bit) + #define M_GPIO_SEL7 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_gpio_sel0:1; + REGWORD v_gpio_sel1:1; + REGWORD v_gpio_sel2:1; + REGWORD v_gpio_sel3:1; + REGWORD v_gpio_sel4:1; + REGWORD v_gpio_sel5:1; + REGWORD v_gpio_sel6:1; + REGWORD v_gpio_sel7:1; + } bit_r_gpio_sel; // register and bitmap data + typedef union {REGWORD reg; bit_r_gpio_sel bit;} reg_r_gpio_sel; // register and bitmap access + + +#define R_PLL_STA 0x50 // register access + #define M_PLL_LOCK 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD reserved_56:7; + REGWORD v_pll_lock:1; + } bit_r_pll_sta; // register and bitmap data + typedef union {REGWORD reg; bit_r_pll_sta bit;} reg_r_pll_sta; // register and bitmap access + + +#define R_PLL_CTRL 0x50 // register access + #define M_PLL_NRES 0x01 // bitmap mask (1bit) + #define M_PLL_TST 0x02 // bitmap mask (1bit) + #define M_PLL_FREEZE 0x20 // bitmap mask (1bit) + #define M_PLL_M 0xC0 // bitmap mask (2bit) + #define M1_PLL_M 0x40 + + typedef struct // bitmap construction + { + REGWORD v_pll_nres:1; + REGWORD v_pll_tst:1; + REGWORD reserved_55:3; + REGWORD v_pll_freeze:1; + REGWORD v_pll_m:2; + } bit_r_pll_ctrl; // register and bitmap data + typedef union {REGWORD reg; bit_r_pll_ctrl bit;} reg_r_pll_ctrl; // register and bitmap access + + +#define R_PLL_P 0x51 // register access + #define M_PLL_P 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_pll_p:8; + } bit_r_pll_p; // register and bitmap data + typedef union {REGWORD reg; bit_r_pll_p bit;} reg_r_pll_p; // register and bitmap access + + +#define R_PLL_N 0x52 // register access + #define M_PLL_N 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_pll_n:8; + } bit_r_pll_n; // register and bitmap data + typedef union {REGWORD reg; bit_r_pll_n bit;} reg_r_pll_n; // register and bitmap access + + +#define R_PLL_S 0x53 // register access + #define M_PLL_S 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_pll_s:8; + } bit_r_pll_s; // register and bitmap data + typedef union {REGWORD reg; bit_r_pll_s bit;} reg_r_pll_s; // register and bitmap access + + +#define A_FIFO_DATA 0x80 // register access + #define M_FIFO_DATA 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_data:8; + } bit_a_fifo_data; // register and bitmap data + typedef union {REGWORD reg; bit_a_fifo_data bit;} reg_a_fifo_data; // register and bitmap access + + +#define A_FIFO_DATA_NOINC 0x84 // register access + #define M_FIFO_DATA_NOINC 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_data_noinc:8; + } bit_a_fifo_data_noinc; // register and bitmap data + typedef union {REGWORD reg; bit_a_fifo_data_noinc bit;} reg_a_fifo_data_noinc; // register and bitmap access + + +#define R_INT_DATA 0x88 // register access + #define M_INT_DATA 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_int_data:8; + } bit_r_int_data; // register and bitmap data + typedef union {REGWORD reg; bit_r_int_data bit;} reg_r_int_data; // register and bitmap access + + +#define R_RAM_DATA 0xC0 // register access + #define M_RAM_DATA 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_ram_data:8; + } bit_r_ram_data; // register and bitmap data + typedef union {REGWORD reg; bit_r_ram_data bit;} reg_r_ram_data; // register and bitmap access + + +#define A_SL_CFG 0xD0 // register access + #define M_CH_SDIR 0x01 // bitmap mask (1bit) + #define M_CH_SNUM 0x3E // bitmap mask (5bit) + #define M1_CH_SNUM 0x02 + #define M_ROUT 0xC0 // bitmap mask (2bit) + #define M1_ROUT 0x40 + + typedef struct // bitmap construction + { + REGWORD v_ch_sdir:1; + REGWORD v_ch_snum:5; + REGWORD v_rout:2; + } bit_a_sl_cfg; // register and bitmap data + typedef union {REGWORD reg; bit_a_sl_cfg bit;} reg_a_sl_cfg; // register and bitmap access + + +#define A_CH_MSK 0xF4 // register access + #define M_CH_MSK 0xFF // bitmap mask (8bit) + + typedef struct // bitmap construction + { + REGWORD v_ch_msk:8; + } bit_a_ch_msk; // register and bitmap data + typedef union {REGWORD reg; bit_a_ch_msk bit;} reg_a_ch_msk; // register and bitmap access + + +#define A_CON_HDLC 0xFA // register access + #define M_IFF 0x01 // bitmap mask (1bit) + #define M_HDLC_TRP 0x02 // bitmap mask (1bit) + #define M_FIFO_IRQ 0x1C // bitmap mask (3bit) + #define M1_FIFO_IRQ 0x04 + #define M_DATA_FLOW 0xE0 // bitmap mask (3bit) + #define M1_DATA_FLOW 0x20 + + typedef struct // bitmap construction + { + REGWORD v_iff:1; + REGWORD v_hdlc_trp:1; + REGWORD v_fifo_irq:3; + REGWORD v_data_flow:3; + } bit_a_con_hdlc; // register and bitmap data + typedef union {REGWORD reg; bit_a_con_hdlc bit;} reg_a_con_hdlc; // register and bitmap access + + +#define A_SUBCH_CFG 0xFB // register access + #define M_BIT_CNT 0x07 // bitmap mask (3bit) + #define M1_BIT_CNT 0x01 + #define M_START_BIT 0x38 // bitmap mask (3bit) + #define M1_START_BIT 0x08 + #define M_LOOP_FIFO 0x40 // bitmap mask (1bit) + #define M_INV_DATA 0x80 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_bit_cnt:3; + REGWORD v_start_bit:3; + REGWORD v_loop_fifo:1; + REGWORD v_inv_data:1; + } bit_a_subch_cfg; // register and bitmap data + typedef union {REGWORD reg; bit_a_subch_cfg bit;} reg_a_subch_cfg; // register and bitmap access + + +#define A_CHANNEL 0xFC // register access + #define M_CH_FDIR 0x01 // bitmap mask (1bit) + #define M_CH_FNUM 0x1E // bitmap mask (4bit) + #define M1_CH_FNUM 0x02 + + typedef struct // bitmap construction + { + REGWORD v_ch_fdir:1; + REGWORD v_ch_fnum:4; + REGWORD reserved_57:3; + } bit_a_channel; // register and bitmap data + typedef union {REGWORD reg; bit_a_channel bit;} reg_a_channel; // register and bitmap access + + +#define A_FIFO_SEQ 0xFD // register access + #define M_NEXT_FIFO_DIR 0x01 // bitmap mask (1bit) + #define M_NEXT_FIFO_NUM 0x1E // bitmap mask (4bit) + #define M1_NEXT_FIFO_NUM 0x02 + #define M_SEQ_END 0x40 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_next_fifo_dir:1; + REGWORD v_next_fifo_num:4; + REGWORD reserved_58:1; + REGWORD v_seq_end:1; + REGWORD reserved_59:1; + } bit_a_fifo_seq; // register and bitmap data + typedef union {REGWORD reg; bit_a_fifo_seq bit;} reg_a_fifo_seq; // register and bitmap access + + +#define A_FIFO_CTRL 0xFF // register access + #define M_FIFO_IRQMSK 0x01 // bitmap mask (1bit) + #define M_BERT_EN 0x02 // bitmap mask (1bit) + #define M_MIX_IRQ 0x04 // bitmap mask (1bit) + #define M_FR_ABO 0x08 // bitmap mask (1bit) + #define M_NO_CRC 0x10 // bitmap mask (1bit) + #define M_NO_REP 0x20 // bitmap mask (1bit) + + typedef struct // bitmap construction + { + REGWORD v_fifo_irqmsk:1; + REGWORD v_bert_en:1; + REGWORD v_mix_irq:1; + REGWORD v_fr_abo:1; + REGWORD v_no_crc:1; + REGWORD v_no_rep:1; + REGWORD reserved_60:2; + } bit_a_fifo_ctrl; // register and bitmap data + typedef union {REGWORD reg; bit_a_fifo_ctrl bit;} reg_a_fifo_ctrl; // register and bitmap access + + +#endif /* _XHFC24SUCC_H_ */ + +/*___________________________________________________________________________________*/ +/* */ +/* End of XHFC-2S4U / XHFC-4SU register definitions. */ +/* */ +/* Total number of registers processed: 122 of 122 */ +/* Total number of bitmaps processed : 523 */ +/* */ +/*___________________________________________________________________________________*/ +/* */ diff --git a/drivers/isdn/hardware/mISDN/xhfc_pci2pi.c b/drivers/isdn/hardware/mISDN/xhfc_pci2pi.c new file mode 100644 index 0000000..7f2fe55 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/xhfc_pci2pi.c @@ -0,0 +1,134 @@ +/* $Id$ + * + * PCI2PI Pci Bridge support for xhfc_su.c + * + * Authors : Martin Bachem, Joerg Ciesielski + * Contact : info@colognechip.com + * + * 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. + * + */ + +#include +#include +#include +#include "xhfc_su.h" +#include "xhfc_pci2pi.h" + + + +static PCI2PI_cfg PCI2PI_config = { + /* default PI_INTELMX config */ + .del_cs = 0, + .del_rd = 0, + .del_wr = 0, + .del_ale = 0, + .del_adr = 0, + .del_dout = 0, + .default_adr = 0x00, + .default_dout = 0x00, + .pi_mode = PI_MODE, + .setup = 1, + .hold = 1, + .cycle = 1, + .ale_adr_first = 0, + .ale_adr_setup = 0, + .ale_adr_hold = 1, + .ale_adr_wait = 0, + .pause_seq = 1, + .pause_end = 0, + .gpio_out = 0, + .status_int_enable = 1, + .pi_int_pol = 0, + .pi_wait_enable = 0, + .spi_cfg0 = 0, + .spi_cfg1 = 0, + .spi_cfg2 = 0, + .spi_cfg3 = 0, + .eep_recover = 4, +}; + + + +/***********************************/ +/* initialise the XHFC PCI Bridge */ +/* return 0 on success. */ +/***********************************/ +int +init_pci_bridge(xhfc_hw * hw) +{ + int err = -ENODEV; + + printk(KERN_INFO "%s %s: using PCI2PI Bridge at 0x%p\n", + hw->card_name, __FUNCTION__, hw->hw_membase); + + /* test if Bridge regsiter accessable */ + WritePCI2PI_u32(hw, PCI2PI_DEL_CS, 0x0); + if (ReadPCI2PI_u32(hw, PCI2PI_DEL_CS) == 0x00) { + WritePCI2PI_u32(hw, PCI2PI_DEL_CS, 0xFFFFFFFF); + if (ReadPCI2PI_u32(hw, PCI2PI_DEL_CS) == 0xF) { + err = 0; + } + } + if (err) + return (err); + + /* enable hardware reset XHFC */ + WritePCI2PI_u32(hw, PCI2PI_GPIO_OUT, GPIO_OUT_VAL); + + WritePCI2PI_u32(hw, PCI2PI_PI_MODE, PCI2PI_config.pi_mode); + WritePCI2PI_u32(hw, PCI2PI_DEL_CS, PCI2PI_config.del_cs); + WritePCI2PI_u32(hw, PCI2PI_DEL_RD, PCI2PI_config.del_rd); + WritePCI2PI_u32(hw, PCI2PI_DEL_WR, PCI2PI_config.del_wr); + WritePCI2PI_u32(hw, PCI2PI_DEL_ALE, PCI2PI_config.del_ale); + WritePCI2PI_u32(hw, PCI2PI_DEL_ADR, PCI2PI_config.del_adr); + WritePCI2PI_u32(hw, PCI2PI_DEL_DOUT, PCI2PI_config.del_dout); + WritePCI2PI_u32(hw, PCI2PI_DEFAULT_ADR, PCI2PI_config.default_adr); + WritePCI2PI_u32(hw, PCI2PI_DEFAULT_DOUT, + PCI2PI_config.default_dout); + + WritePCI2PI_u32(hw, PCI2PI_CYCLE_SHD, 0x80 * PCI2PI_config.setup + + 0x40 * PCI2PI_config.hold + PCI2PI_config.cycle); + + WritePCI2PI_u32(hw, PCI2PI_ALE_ADR_WHSF, + PCI2PI_config.ale_adr_first + + PCI2PI_config.ale_adr_setup * 2 + + PCI2PI_config.ale_adr_hold * 4 + + PCI2PI_config.ale_adr_wait * 8); + + WritePCI2PI_u32(hw, PCI2PI_CYCLE_PAUSE, + 0x10 * PCI2PI_config.pause_seq + + PCI2PI_config.pause_end); + WritePCI2PI_u32(hw, PCI2PI_STATUS_INT_ENABLE, + PCI2PI_config.status_int_enable); + + WritePCI2PI_u32(hw, PCI2PI_PI_INT_POL, + 2 * PCI2PI_config.pi_wait_enable + + PCI2PI_config.pi_int_pol); + + WritePCI2PI_u32(hw, PCI2PI_SPI_CFG0, PCI2PI_config.spi_cfg0); + WritePCI2PI_u32(hw, PCI2PI_SPI_CFG1, PCI2PI_config.spi_cfg1); + WritePCI2PI_u32(hw, PCI2PI_SPI_CFG2, PCI2PI_config.spi_cfg2); + WritePCI2PI_u32(hw, PCI2PI_SPI_CFG3, PCI2PI_config.spi_cfg3); + WritePCI2PI_u32(hw, PCI2PI_EEP_RECOVER, PCI2PI_config.eep_recover); + ReadPCI2PI_u32(hw, PCI2PI_STATUS); + + + /* release hardware reset XHFC */ + WritePCI2PI_u32(hw, PCI2PI_GPIO_OUT, GPIO_OUT_VAL | PCI2PI_GPIO7_NRST); + udelay(10); + + return (err); +} diff --git a/drivers/isdn/hardware/mISDN/xhfc_pci2pi.h b/drivers/isdn/hardware/mISDN/xhfc_pci2pi.h new file mode 100644 index 0000000..2f4bce8 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/xhfc_pci2pi.h @@ -0,0 +1,674 @@ +/* $Id$ + * + * PCI2PI Pci Bridge support for xhfc_su.c + * + * Authors : Martin Bachem, Joerg Ciesielski + * Contact : info@colognechip.com + * + * 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. + * + */ + +#ifndef _XHFC_PCI2PI_H_ +#define _XHFC_PCI2PI_H_ + +#include "xhfc24succ.h" + +/* differnt PCI modes supported by PCI2PI */ +#define PI_INTELNOMX 0 +#define PI_INTELMX 1 +#define PI_MOT 2 +#define PI_MOTMX 3 +#define PI_SPI 4 +#define PI_EEPPRG 6 +#define PI_AUTOEEP 7 + +/* PI_MODE to GPIO mapping */ +#define PI_INTELMX_GPIO PCI2PI_GPIO3_MODE1 /* ALE pulse switches to MULTIPLEXED */ +#define PI_INTELNOMX_GPIO PCI2PI_GPIO3_MODE1 +#define PI_MOT_GPIO PCI2PI_GPIO2_MODE0 +#define PI_MOTMX_GPIO PCI2PI_GPIO2_MODE0 +#define PI_SPI_GPIO 0 +#define PI_AUTOEEP_GPIO PCI2PI_GPIO2_MODE0 | PCI2PI_GPIO3_MODE1 + + +/* PCI2PI GPIO to XHFC signal mapping */ +#define PCI2PI_GPIO7_NRST 0x80 +#define PCI2PI_GPIO6_TLA3 0x40 +#define PCI2PI_GPIO5_TLA2 0x20 +#define PCI2PI_GPIO4_TLA1 0x10 +#define PCI2PI_GPIO3_MODE1 0x08 +#define PCI2PI_GPIO2_MODE0 0x04 +#define PCI2PI_GPIO1_BOND1 0x02 +#define PCI2PI_GPIO0_BOND0 0x01 + +/* PCI2PI GPIO XHFC Bond out selection */ +#define XHFC_1SU_BOND 0 +#define XHFC_2SU_BOND PCI2PI_GPIO0_BOND0 +#define XHFC_2S4U_BOND PCI2PI_GPIO1_BOND1 +#define XHFC_4SU_BOND PCI2PI_GPIO1_BOND1 | PCI2PI_GPIO0_BOND0 + + +/*******************************************************/ +/*******************************************************/ + +/* Select processor interface mode and Bond option */ +/* of PCI2PI bridge */ +#define PI_MODE PI_INTELMX +#define XHFC_BOND XHFC_4SU_BOND + +/*******************************************************/ +/*******************************************************/ + +#if (PI_MODE == PI_INTELNOMX) +#define GPIO_OUT_VAL XHFC_BOND | PI_INTELNOMX_GPIO + +#elif (PI_MODE == PI_INTELMX) +#define GPIO_OUT_VAL XHFC_BOND | PI_INTELMX_GPIO + +#elif (PI_MODE == PI_MOT) +#define GPIO_OUT_VAL XHFC_BOND | PI_MOT_GPIO + +#elif (PI_MODE == PI_MOTMX) +#define GPIO_OUT_VAL XHFC_BOND | PI_MOTMX_GPIO + +#elif (PI_MODE == PI_SPI) +#define GPIO_OUT_VAL XHFC_BOND | PI_SPI_GPIO + +#elif (PI_MODE == PI_AUTOEEP) +#define GPIO_OUT_VAL XHFC_BOND | PI_AUTOEEP_GPIO +#endif + +/*******************************************************/ + + +#define PCI2PI_VENDORID 0x1397 + +#define PCI2PI_DEVICEID 0xA003 +#define MAX_PCI2PI 2 + +#define RV_PCI2PI_OK 0 +#define RV_PCI2PI_ERROR 1 + +/* PCI2PI register definitions */ +#define PCI2PI_OFFSET 0x00000800 +#define PCI2PI_DEL_CS 4*0x00 + PCI2PI_OFFSET +#define PCI2PI_DEL_RD 4*0x01 + PCI2PI_OFFSET +#define PCI2PI_DEL_WR 4*0x02 + PCI2PI_OFFSET +#define PCI2PI_DEL_ALE 4*0x03 + PCI2PI_OFFSET +#define PCI2PI_DEL_ADR 4*0x04 + PCI2PI_OFFSET +#define PCI2PI_DEL_DOUT 4*0x05 + PCI2PI_OFFSET +#define PCI2PI_DEFAULT_ADR 4*0x06 + PCI2PI_OFFSET +#define PCI2PI_DEFAULT_DOUT 4*0x07 + PCI2PI_OFFSET + + +/* +PCI2PI_PI_MODE bit 2..0: +000: Intel non multiplexed +001: Intel multiplexed +010: Motorola +100: SPI Motorola +110: EEPROM programming mode throug PCIIF00 Target +111: XHFC AutoEEPROM mode +*/ +#define PCI2PI_PI_MODE 4*0x08 + PCI2PI_OFFSET + +#define PCI2PI_CYCLE_SHD 4*0x09 + PCI2PI_OFFSET +#define PCI2PI_ALE_ADR_WHSF 4*0x0a + PCI2PI_OFFSET +#define PCI2PI_CYCLE_PAUSE 4*0x0b + PCI2PI_OFFSET +#define PCI2PI_GPIO_OUT 4*0x0c + PCI2PI_OFFSET +#define PCI2PI_G1 4*0x0e + PCI2PI_OFFSET +#define PCI2PI_G0 4*0x0f + PCI2PI_OFFSET + +/* +bit0: is set by PI_INT active, is reseted by reading this register +bit1: is set by PI_WAIT active, is reseted by reading this register +*/ +#define PCI2PI_STATUS 4*0x10 + PCI2PI_OFFSET + + +/* bit0: enable PCI interrupt output */ +#define PCI2PI_STATUS_INT_ENABLE 4*0x11 + PCI2PI_OFFSET + +/* +bit0: 0 = low active interrupt is detected at PI_INT +bit0: 1 = high active interrupt is detected at PI_INT +*/ +#define PCI2PI_PI_INT_POL 4*0x12 + PCI2PI_OFFSET + +/* SPI registers */ +/* 32 bit SPI master data output register */ +#define PCI2PI_SPI_MO_DATA 4*0x20 + PCI2PI_OFFSET + +/* 32 bit SPI master data input register */ +#define PCI2PI_SPI_MI_DATA 4*0x21 + PCI2PI_OFFSET + +/* +bit 0: 0 SPI bits are processing on the serial input/output +bit 0: 1 SPI bits are processed, new data can be written or read +bit 1..31: unused +*/ +#define PCI2PI_SPI_STATUS 4*0x22 + PCI2PI_OFFSET + +/* +bit 0: spi clock polarity, defines level for SPISEL1 +bit 1: spi clock phase, defines sampling edge, 0?, 1? +bit 2: 0MSB first (default) , 1LSB first +bit 3: 1SPI clock permanent during SPISEL1 +*/ +#define PCI2PI_SPI_CFG0 4*0x28 + PCI2PI_OFFSET + +/* +bit 0..3: spi clock frequency, SPI clock period (value+1) x 2 x PCIperiod +0: 2 PCIperiods +1: +*/ +#define PCI2PI_SPI_CFG1 4*0x29 + PCI2PI_OFFSET + +/* bit 0..3: SPI Device SEL: defines level of D0..D3, XHFC SPI address */ +#define PCI2PI_SPI_CFG2 4*0x2A + PCI2PI_OFFSET + +/* +bit 0: 1spi master out permanent driven +bit 1: 1SPISEL remains low between bytes of a sequence +bit 2: 1SPISEL remains low permanent +*/ +#define PCI2PI_SPI_CFG3 4*0x2B + PCI2PI_OFFSET + +/* bit 0..3: default 0100 */ +#define PCI2PI_EEP_RECOVER 4*0x30 + PCI2PI_OFFSET + +typedef struct _PCI2PI_cfg { + __u32 del_cs; /* Bit 3..0, bit 3: 0.5 PCI clk, + bits 2..0: gate delay */ + __u32 del_rd; + __u32 del_wr; + __u32 del_ale; + + __u32 del_adr; /* delay between default address + value and selected address */ + __u32 del_dout; /* delay between default data + value and written data */ + __u32 default_adr; /* default address value bit 0..7 */ + + __u32 default_dout; /* default data value bit 0..7 */ + + + __u32 pi_mode; /* pi_mode bit 2..0: + 000: Intel non multiplexed + 001: Intel multiplexed + 010: Motorola + 100: SPI Motorola + 110: EEPROM programming mode throug PCIIF00 Target + 111: XHFC AutoEEPROM mode + */ + + __u32 setup; /* address/data setup berfore rd/wr/cs, + 0 or 1 PCI clk */ + + __u32 hold; /* address/data hold after rd/wr/cs, + 0 or 1 PCI clk */ + + __u32 cycle; /* bit 0 only + address/data adds 0 or 1 PCI clk to /rd/wr/cs + access time (cycle+1) PCI clk + */ + + __u32 ale_adr_first; /* bit 0 = 0: address valid before ALE=1 + bit 0 = 1: ALE=1 before adress valid */ + + __u32 ale_adr_setup; /* bit 0 = 0 setup for ALE/addr = 0s + bit 0 = 1:setup for ALE/addr = 1 PCI clk + ALE/addr depends on ale_adr_first setting */ + + __u32 ale_adr_hold; /* bit 0 = 0 hold for address after ALE: 0s + bit 0 = 1:hold for address after ALE: 1 PCI clk */ + + __u32 ale_adr_wait; /* bit 0 = 0: 0 PCI clk delay between address and data phase + bit 0 = 1: 1 PCI clk delay between address and data phase */ + + __u32 pause_seq; /* bit 0..3: number of PCI clocks between read/write + acceses due to DWORD/WORD burst access */ + + __u32 pause_end; /* bit 0..3: number of PCI clocks after DWORD/WORD + burst access /delays PCI_TRDY signal */ + + __u32 gpio_out; /* bit 0..7: GPIO output value */ + + __u32 status_int_enable; /* bit 0: enables PCI interrupt for PI_INT signal + bit 1: enables PCI interrupt for PI_NWAIT signal */ + + __u32 pi_int_pol; /* bit 0: polarity of PI_INT signal */ + + __u32 pi_wait_enable; /* access length can be controled by /wait signal + 0 wait disabled, 1 wait enabled */ + + __u32 spi_cfg0; + __u32 spi_cfg1; + __u32 spi_cfg2; + __u32 spi_cfg3; + __u32 eep_recover; + +} PCI2PI_cfg; + + +/* + +read and write functions to access registers of the PCI bridge + +*/ + +static inline __u8 +ReadPCI2PI_u8(xhfc_hw * hw, __u16 reg_addr) +{ + return (*(volatile __u8 *) (hw->membase + reg_addr)); +} + +static inline __u16 +ReadPCI2PI_u16(xhfc_hw * hw, __u16 reg_addr) +{ + return (*(volatile __u16 *) (hw->membase + reg_addr)); +} + +static inline __u32 +ReadPCI2PI_u32(xhfc_hw * hw, __u16 reg_addr) +{ + return (*(volatile __u32 *) (hw->membase + reg_addr)); +} + +static inline void +WritePCI2PI_u8(xhfc_hw * hw, __u16 reg_addr, __u8 value) +{ + *((volatile __u8 *) (hw->membase + reg_addr)) = value; +} + +static inline void +WritePCI2PI_u16(xhfc_hw * hw, __u16 reg_addr, __u16 value) +{ + *((volatile __u16 *) (hw->membase + reg_addr)) = value; +} + +static inline void +WritePCI2PI_u32(xhfc_hw * hw, __u16 reg_addr, __u32 value) +{ + *((volatile __u32 *) (hw->membase + reg_addr)) = value; +} + + +/* + +read and write functions to access a XHFC at the local bus interface of the PCI +bridge + +there are two sets of functions to access the XHFC in the following different +interface modes: + +multiplexed bus interface modes PI_INTELMX and PI_MOTMX +- these modes use a single (atomic) PCI cycle to read or write a XHFC register + +non multiplexed bus interface modes PI_INTELNOMX, PI_MOTMX and PI_SPI + +- these modes use a separate PCI cycles to select the XHFC register and to read +or write data. That means these register accesses are non atomic and could be +interrupted by an interrupt. The driver must take care that a register access in +these modes is not interrupted by its own interrupt handler. + + +*/ + + +/*****************************************************************************/ + + +#if ((PI_MODE==PI_INTELMX) || (PI_MODE==PI_MOTMX)) + + +/* +functions for multiplexed access +*/ +static inline __u8 +read_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + return (*(volatile __u8 *) (hw->membase + (reg_addr << 2))); +} + + +/* +read four bytes from the same register address +e.g. A_FIFO_DATA +this function is only defined for software compatibility here +*/ +static inline __u32 +read32_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + __u32 value; + + value = (*(volatile __u8 *) (hw->membase + (reg_addr << 2))); + value |= (*(volatile __u8 *) (hw->membase + (reg_addr << 2))) << 8; + value |= (*(volatile __u8 *) (hw->membase + (reg_addr << 2))) << 16; + value |= (*(volatile __u8 *) (hw->membase + (reg_addr << 2))) << 24; + + return (value); +} + +static inline void +write_xhfc(xhfc_hw * hw, __u8 reg_addr, __u8 value) +{ + *((volatile __u8 *) (hw->membase + (reg_addr << 2))) = value; +} + +/* +writes four bytes to the same register address +e.g. A_FIFO_DATA +this function is only defined for software compatibility here +*/ +static inline void +write32_xhfc(xhfc_hw * hw, __u8 reg_addr, __u32 value) +{ + *((volatile __u8 *) (hw->membase + (reg_addr << 2))) = value & 0xff; + *((volatile __u8 *) (hw->membase + (reg_addr << 2))) = (value >>8) & 0xff; + *((volatile __u8 *) (hw->membase + (reg_addr << 2))) = (value >>16) & 0xff; + *((volatile __u8 *) (hw->membase + (reg_addr << 2))) = (value >>24) & 0xff; +} + + +/* always reads a single byte with short read method +this allows to read ram based registers +that normally requires long read access times +*/ +static inline __u8 +sread_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + (*(volatile __u8 *) (hw->membase + (reg_addr << 2))); + return (*(volatile __u8 *) (hw->membase + (R_INT_DATA << 2))); +} + +/* this function reads the currently selected regsiter from XHFC and is only +required for non multiplexed access modes. For multiplexed access modes this +function is only defined for for software compatibility. */ + +static inline __u8 +read_xhfcregptr(xhfc_hw * hw) +{ + return 0; +} + +/* this function writes the XHFC register address pointer and is only +required for non multiplexed access modes. For multiplexed access modes this +function is only defined for for software compatibility. */ + +static inline void +write_xhfcregptr(xhfc_hw * hw, __u8 reg_addr) +{ +} + + +#endif /* PI_MODE==PI_INTELMX || PI_MODE==PI_MOTMX */ + + + +/*****************************************************************************/ + + +#if PI_MODE==PI_INTELNOMX || PI_MODE==PI_MOT +/* +functions for non multiplexed access + +XHFC register address pointer is accessed with PCI address A2=1 and XHFC data +port is accessed with PCI address A2=0 + +*/ + +static inline __u8 +read_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + *((volatile __u8 *) (hw->membase + 4)) = reg_addr; + return (*(volatile __u8 *) (hw->membase)); +} + + +/* + +read four bytes from the same register address by using a 32bit PCI access. The +PCI bridge generates for 8 bit data read cycles at the local bus interface. + +*/ + +static inline __u32 +read32_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + *((volatile __u8 *) (hw->membase + 4)) = reg_addr; + return (*(volatile __u32 *) hw->membase); +} + +static inline void +write_xhfc(xhfc_hw * hw, __u8 reg_addr, __u8 value) +{ + *((volatile __u8 *) (hw->membase + 4)) = reg_addr; + *((volatile __u8 *) (hw->membase)) = value; +} + +/* + +writes four bytes to the same register address (e.g. A_FIFO_DATA) by using a +32bit PCI access. The PCI bridge generates for 8 bit data write cycles at the +local bus interface. + +*/ +static inline void +write32_xhfc(xhfc_hw * hw, __u8 reg_addr, __u32 value) +{ + *((volatile __u8 *) (hw->membase + 4)) = reg_addr; + *((volatile __u32 *) (hw->membase)) = value; +} + + +/* + +reads a single byte with short read method (r*). This allows to read ram based +registers that normally requires long read access times + +*/ +static inline __u8 +sread_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + + *((volatile __u8 *) (hw->membase + 4)) = reg_addr; + (*(volatile __u8 *) (hw->membase)); + *((volatile __u8 *) (hw->membase + 4)) = R_INT_DATA; + return (*(volatile __u8 *) (hw->membase)); +} + +/* + +this function reads the currently selected regsiter from XHFC + +*/ +static inline __u8 +read_xhfcregptr(xhfc_hw * hw) +{ + return (*(volatile __u8 *) (hw->membase + 4)); +} + +/* this function writes the XHFC register address pointer */ + +static inline void +write_xhfcregptr(xhfc_hw * hw, __u8 reg_addr) +{ + *((volatile __u8 *) (hw->membase + 4)) = reg_addr; +} + + + +#endif /* PI_MODE==PI_INTELNOMX || PI_MODE==PI_MOT */ + + +/*****************************************************************************/ + +#if PI_MODE == PI_SPI + +// SPI mode transaction bit definitions +#define SPI_ADDR 0x40 +#define SPI_DATA 0x00 +#define SPI_RD 0x80 +#define SPI_WR 0x00 +#define SPI_BROAD 0x20 +#define SPI_MULTI 0x20 + + +/* +functions for SPI access + +*/ + + +static inline __u8 +read_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 32 clock SPI master transfer + WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 24) | (reg_addr << 16) | ((SPI_DATA | SPI_RD) << 8)); + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // read data from the SPI data receive register and return one byte + return (__u8) (ReadPCI2PI_u32(hw, PCI2PI_SPI_MI_DATA) & 0xFF); +} + + +/* + +read four bytes from the same register address by using a SPI multiple read access + +*/ + +static inline __u32 +read32_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 16 clock SPI master transfer + WritePCI2PI_u16(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 8) | reg_addr); + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 8 clock SPI master transfer + WritePCI2PI_u8(hw, PCI2PI_SPI_MO_DATA, (SPI_DATA | SPI_RD | SPI_MULTI)); + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 32 clock SPI master transfer + // output data is arbitrary + WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, 0); + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // read data from the SPI data receive register and return four bytes + return (__u32) be32_to_cpu(ReadPCI2PI_u32(hw, PCI2PI_SPI_MI_DATA)); +} + +static inline void +write_xhfc(xhfc_hw * hw, __u8 reg_addr, __u8 value) +{ + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 32 clock SPI master transfer + WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 24) | (reg_addr << 16) | ((SPI_DATA | SPI_WR) << 8) | value); +} + +/* + +writes four bytes to the same register address (e.g. A_FIFO_DATA) by using a SPI +multiple write access + +*/ +static inline void +write32_xhfc(xhfc_hw * hw, __u8 reg_addr, __u32 value) +{ + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 16 clock SPI master transfer + WritePCI2PI_u16(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 8) | reg_addr); + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 8 clock SPI master transfer + WritePCI2PI_u8(hw, PCI2PI_SPI_MO_DATA, (SPI_DATA | SPI_WR | SPI_MULTI)); + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 32 clock SPI master transfer + WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, cpu_to_be32(value)); +} + + +/* + +reads a single byte with short read method (r*). This allows to read ram based +registers that normally requires long read access times + +*/ +static inline __u8 +sread_xhfc(xhfc_hw * hw, __u8 reg_addr) +{ + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 32 clock SPI master transfer + WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA ,((SPI_ADDR | SPI_WR) << 24) | (reg_addr << 16) | ((SPI_DATA | SPI_RD) << 8)); + + + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 32 clock SPI master transfer to read R_INT_DATA register + WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 24) | (R_INT_DATA << 16) | ((SPI_DATA | SPI_RD) << 8)); + + + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // read data from the SPI data receive register and return one byte + return (__u8) (ReadPCI2PI_u32(hw, PCI2PI_SPI_MI_DATA) & 0xFF); +} + +/* + +this function reads the currently selected regsiter from XHFC + +*/ +static inline __u8 +read_xhfcregptr(xhfc_hw * hw) +{ + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 16 clock SPI master transfer + WritePCI2PI_u16(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_RD) << 8)); + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // read data from the SPI data receive register and return one byte + return (__u8) (ReadPCI2PI_u32(hw, PCI2PI_SPI_MI_DATA) & 0xFF); +} + +/* this function writes the XHFC register address pointer */ + +static inline void +write_xhfcregptr(xhfc_hw * hw, __u8 reg_addr) +{ + // wait until SPI master is idle + while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1)); + // initiate a 16 clock SPI master transfer + WritePCI2PI_u16(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 8) | reg_addr); +} + +#endif /* PI_MODE == PI_SPI */ + + + +/* Function Prototypes */ +int init_pci_bridge(xhfc_hw * hw); + +#endif /* _XHFC_PCI2PI_H_ */ diff --git a/drivers/isdn/hardware/mISDN/xhfc_su.c b/drivers/isdn/hardware/mISDN/xhfc_su.c new file mode 100644 index 0000000..1b62b71 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/xhfc_su.c @@ -0,0 +1,2081 @@ +/* $Id$ + * + * mISDN driver for CologneChip AG's XHFC + * + * Authors : Martin Bachem, Joerg Ciesielski + * Contact : info@colognechip.com + * + * 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. + * + ******************************************************************************* + * + * MODULE PARAMETERS: + * (NOTE: layermask and protocol must be given for all ports, + * not for the number of cards.) + * + * - protocol=[,p2,p3...] + * Values: + * D-channel protocol id + * Flags for special features + * Spare (set to 0) + * + * D-channel protocol ids + * - 1 1TR6 (not released yet) + * - 2 DSS1 + * + * Feature Flags + * 0x0010 Net side stack (NT mode) + * 0x0020 Line Interface Mode (0=S0, 1=Up) + * 0x0040 st line polarity (1=exchanged) + * 0x0080 B channel loop (for layer1 tests) + * + * - layermask=[,l2,l3...] (32bit): + * mask of layers to be used for D-channel stack + * + * - debug: + * enable debugging (see xhfc_su.h for debug options) + * + */ + +#include +#include +#include +#include +#include +#include +#include "helper.h" +#include "debug.h" +#include "xhfc_su.h" +#include "xhfc24succ.h" + +#if BRIDGE == BRIDGE_PCI2PI +#include "xhfc_pci2pi.h" +#endif + +static const char xhfc_rev[] = "$Revision$"; + +#define MAX_CARDS 8 +static int card_cnt; +static u_int protocol[MAX_CARDS * MAX_PORT]; +static int layermask[MAX_CARDS * MAX_PORT]; + +#ifdef MODULE +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#define MODULE_PARM_T "1-8i" +MODULE_PARM(debug, "1i"); +MODULE_PARM(protocol, MODULE_PARM_T); +MODULE_PARM(layermask, MODULE_PARM_T); +#endif + +static mISDNobject_t hw_mISDNObj; +static int debug = 0; + +static void release_card(xhfc_hw * hw); + + + +/****************************************************/ +/* Physical S/U commands to control Line Interface */ +/****************************************************/ +static void +xhfc_ph_command(channel_t * dch, u_char command) +{ + xhfc_hw *hw = dch->hw; + xhfc_port_t *port = hw->chan[dch->channel].port; + + if (dch->debug) + mISDN_debugprint(&dch->inst, + "%s command(%i) channel(%i) port(%i)", + __FUNCTION__, command, dch->channel, + port->idx); + + switch (command) { + case HFC_L1_ACTIVATE_TE: + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) { + mISDN_debugprint(&dch->inst, + "HFC_L1_ACTIVATE_TE channel(%i))", + dch->channel); + } + + write_xhfc(hw, R_SU_SEL, port->idx); + write_xhfc(hw, A_SU_WR_STA, STA_ACTIVATE); + break; + + case HFC_L1_FORCE_DEACTIVATE_TE: + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "HFC_L1_FORCE_DEACTIVATE_TE channel(%i)", + dch->channel); + + write_xhfc(hw, R_SU_SEL, port->idx); + write_xhfc(hw, A_SU_WR_STA, STA_DEACTIVATE); + break; + + case HFC_L1_ACTIVATE_NT: + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "HFC_L1_ACTIVATE_NT channel(%i)", + dch->channel); + + write_xhfc(hw, R_SU_SEL, port->idx); + write_xhfc(hw, A_SU_WR_STA, + STA_ACTIVATE | M_SU_SET_G2_G3); + break; + + case HFC_L1_DEACTIVATE_NT: + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "HFC_L1_DEACTIVATE_NT channel(%i)", + dch->channel); + + write_xhfc(hw, R_SU_SEL, port->idx); + write_xhfc(hw, A_SU_WR_STA, STA_DEACTIVATE); + break; + + case HFC_L1_TESTLOOP_B1: + setup_fifo(hw, port->idx*8, 0xC6, 0, 0, 0); /* connect B1-SU RX with PCM TX */ + setup_fifo(hw, port->idx*8+1, 0xC6, 0, 0, 0); /* connect B1-SU TX with PCM RX */ + + write_xhfc(hw, R_SLOT, port->idx*8); /* PCM timeslot B1 TX */ + write_xhfc(hw, A_SL_CFG, port->idx*8 + 0x80); /* enable B1 TX timeslot on STIO1 */ + + write_xhfc(hw, R_SLOT, port->idx*8+1); /* PCM timeslot B1 RX */ + write_xhfc(hw, A_SL_CFG, port->idx*8+1 + 0xC0); /* enable B1 RX timeslot on STIO1*/ + + setup_su(hw, port->idx, 0, 1); + break; + + case HFC_L1_TESTLOOP_B2: + setup_fifo(hw, port->idx*8+2, 0xC6, 0, 0, 0); /* connect B2-SU RX with PCM TX */ + setup_fifo(hw, port->idx*8+3, 0xC6, 0, 0, 0); /* connect B2-SU TX with PCM RX */ + + write_xhfc(hw, R_SLOT, port->idx*8+2); /* PCM timeslot B2 TX */ + write_xhfc(hw, A_SL_CFG, port->idx*8+2 + 0x80); /* enable B2 TX timeslot on STIO1 */ + + write_xhfc(hw, R_SLOT, port->idx*8+3); /* PCM timeslot B2 RX */ + write_xhfc(hw, A_SL_CFG, port->idx*8+3 + 0xC0); /* enable B2 RX timeslot on STIO1*/ + + setup_su(hw, port->idx, 1, 1); + break; + } +} + + +static void +l1_timer_start_t3(channel_t * dch) +{ + xhfc_hw * hw = dch->hw; + xhfc_port_t * port = hw->chan[dch->channel].port; + + if (!timer_pending(&port->t3_timer)) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "%s channel(%i) state(F%i)", + __FUNCTION__, dch->channel, dch->state); + + port->t3_timer.expires = jiffies + (XHFC_TIMER_T3 * HZ) / 1000; + add_timer(&port->t3_timer); + } +} + +static void +l1_timer_stop_t3(channel_t * dch) +{ + xhfc_hw * hw = dch->hw; + xhfc_port_t * port = hw->chan[dch->channel].port; + + clear_bit(HFC_L1_ACTIVATING, &port->l1_flags); + if (timer_pending(&port->t3_timer)) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "%s channel(%i) state(F%i)", + __FUNCTION__, dch->channel, dch->state); + + del_timer(&port->t3_timer); + } +} + +/***********************************/ +/* called when timer t3 expires */ +/* -> activation failed */ +/* force clean L1 deactivation */ +/***********************************/ +static void +l1_timer_expire_t3(channel_t * dch) +{ + xhfc_hw * hw = dch->hw; + xhfc_port_t * port = hw->chan[dch->channel].port; + + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "%s channel(%i) state(F%i), " + "l1->l2 (PH_DEACTIVATE | INDICATION)", + __FUNCTION__, dch->channel, dch->state); + + clear_bit(HFC_L1_ACTIVATING, &port->l1_flags), + xhfc_ph_command(dch, HFC_L1_FORCE_DEACTIVATE_TE); + + mISDN_queue_data(&dch->inst, FLG_MSG_UP, + (PH_DEACTIVATE | INDICATION), + 0, 0, NULL, 0); + mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, + MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_DEACTIVATED, + 0, NULL, 0); +} + +static void +l1_timer_start_t4(channel_t * dch) +{ + xhfc_hw * hw = dch->hw; + xhfc_port_t * port = hw->chan[dch->channel].port; + + set_bit(HFC_L1_DEACTTIMER, &port->l1_flags); + + if (!timer_pending(&port->t4_timer)) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "%s channel(%i) state(F%i)", + __FUNCTION__, dch->channel, dch->state); + + port->t4_timer.expires = + jiffies + (XHFC_TIMER_T4 * HZ) / 1000; + add_timer(&port->t4_timer); + } +} + +static void +l1_timer_stop_t4(channel_t * dch) +{ + xhfc_hw * hw = dch->hw; + xhfc_port_t * port = hw->chan[dch->channel].port; + + clear_bit(HFC_L1_DEACTTIMER, &port->l1_flags); + if (timer_pending(&port->t4_timer)) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "%s channel(%i) state(F%i)", + __FUNCTION__, dch->channel, dch->state); + del_timer(&port->t4_timer); + } +} + +/*****************************************************/ +/* called when timer t4 expires */ +/* send (PH_DEACTIVATE | INDICATION) to upper layer */ +/*****************************************************/ +static void +l1_timer_expire_t4(channel_t * dch) +{ + xhfc_hw * hw = dch->hw; + xhfc_port_t * port = hw->chan[dch->channel].port; + + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "%s channel(%i) state(F%i), " + "l1->l2 (PH_DEACTIVATE | INDICATION)", + __FUNCTION__, dch->channel, dch->state); + + clear_bit(HFC_L1_DEACTTIMER, &port->l1_flags); + mISDN_queue_data(&dch->inst, FLG_MSG_UP, + (PH_DEACTIVATE | INDICATION), 0, 0, NULL, 0); + mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, + MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_DEACTIVATED, + 0, NULL, 0); +} + +/*********************************/ +/* Line Interface State handler */ +/*********************************/ +static void +su_new_state(channel_t * dch) +{ + xhfc_hw * hw = dch->hw; + xhfc_port_t * port = hw->chan[dch->channel].port; + + u_int prim = 0; + + if (port->mode & PORT_MODE_TE) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, "%s: TE F%d", + __FUNCTION__, dch->state); + + if ((dch->state <= 3) || (dch->state >= 7)) + l1_timer_stop_t3(dch); + + switch (dch->state) { + case (3): + if (test_and_clear_bit(HFC_L1_ACTIVATED, &port->l1_flags)) + l1_timer_start_t4(dch); + return; + + case (7): + if (timer_pending(&port->t4_timer)) + l1_timer_stop_t4(dch); + + if (test_and_clear_bit(HFC_L1_ACTIVATING, &port->l1_flags)) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "l1->l2 (PH_ACTIVATE | CONFIRM)"); + + set_bit(HFC_L1_ACTIVATED, &port->l1_flags); + prim = PH_ACTIVATE | CONFIRM; + } else { + if (!(test_and_set_bit(HFC_L1_ACTIVATED, &port->l1_flags))) { + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "l1->l2 (PH_ACTIVATE | INDICATION)"); + prim = PH_ACTIVATE | INDICATION; + } else { + // L1 was already activated (e.g. F8->F7) + return; + } + } + mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, + MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_ACTIVATED, + 0, NULL, 0); + break; + + case (8): + l1_timer_stop_t4(dch); + return; + + /* + case (0): + case (1): + case (2): + case (4): + case (5): + case (6): + */ + default: + return; + } + + } else if (port->mode & PORT_MODE_NT) { + + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, "%s: NT G%d", + __FUNCTION__, dch->state); + + switch (dch->state) { + + case (1): + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + port->nt_timer = 0; + port->mode &= ~NT_TIMER; + prim = (PH_DEACTIVATE | INDICATION); + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "l1->l2 (PH_DEACTIVATE | INDICATION)"); + break; + case (2): + if (port->nt_timer < 0) { + port->nt_timer = 0; + port->mode &= ~NT_TIMER; + xhfc_ph_command(dch, HFC_L1_DEACTIVATE_NT); + } else { + port->nt_timer = NT_T1_COUNT; + port->mode |= NT_TIMER; + + write_xhfc(hw, R_SU_SEL, port->idx); + write_xhfc(hw, A_SU_WR_STA, M_SU_SET_G2_G3); + } + return; + case (3): + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + port->nt_timer = 0; + port->mode &= ~NT_TIMER; + prim = (PH_ACTIVATE | INDICATION); + + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "l1->l2 (PH_ACTIVATE | INDICATION)"); + break; + case (4): + port->nt_timer = 0; + port->mode &= ~NT_TIMER; + return; + default: + break; + } + mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, + MGR_SHORTSTATUS | INDICATION, test_bit(FLG_ACTIVE, &dch->Flags) ? + SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED, + 0, NULL, 0); + } + + mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, 0, 0, NULL, 0); +} + +/*************************************/ +/* Layer 1 D-channel hardware access */ +/*************************************/ +static int +handle_dmsg(channel_t *dch, struct sk_buff *skb) +{ + xhfc_hw * hw = dch->hw; + xhfc_port_t * port = hw->chan[dch->channel].port; + + int ret = 0; + mISDN_head_t *hh = mISDN_HEAD_P(skb); + + switch (hh->prim) { + case (PH_ACTIVATE | REQUEST): + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "l2->l1 (PH_ACTIVATE | REQUEST)"); + + if (port->mode & PORT_MODE_TE) { + if (test_bit(HFC_L1_ACTIVATED, &port->l1_flags)) { + + if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) + mISDN_debugprint(&dch->inst, + "l1->l2 (PH_ACTIVATE | CONFIRM)"); + + mISDN_queue_data(&dch->inst, FLG_MSG_UP, + PH_ACTIVATE | CONFIRM, + 0, 0, NULL, 0); + } else { + test_and_set_bit(HFC_L1_ACTIVATING, &port->l1_flags); + + xhfc_ph_command(dch, HFC_L1_ACTIVATE_TE); + l1_timer_start_t3(dch); + } + } else { + xhfc_ph_command(dch, HFC_L1_ACTIVATE_NT); + } + break; + + case (PH_DEACTIVATE | REQUEST): + if (port->mode & PORT_MODE_TE) { + // no deact request in TE mode ! + ret = -EINVAL; + } else { + xhfc_ph_command(dch, HFC_L1_DEACTIVATE_NT); + } + break; + + case (MDL_FINDTEI | REQUEST): + return(mISDN_queue_up(&dch->inst, 0, skb)); + break; + } + + return(ret); +} + +/*************************************/ +/* Layer 1 B-channel hardware access */ +/*************************************/ +static int +handle_bmsg(channel_t *bch, struct sk_buff *skb) +{ + xhfc_hw *hw = bch->hw; + int ret = 0; + mISDN_head_t *hh = mISDN_HEAD_P(skb); + u_long flags; + + if ((hh->prim == (PH_ACTIVATE | REQUEST)) || + (hh->prim == (DL_ESTABLISH | REQUEST))) { + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + spin_lock_irqsave(&hw->lock, flags); + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) + test_and_set_bit(FLG_L2DATA, &bch->Flags); + ret = + setup_channel(hw, bch->channel, + bch->inst.pid.protocol[1]); + spin_unlock_irqrestore(&hw->lock, flags); + } +#ifdef FIXME + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, + hh->prim | CONFIRM, 0, 0, NULL, 0); +#endif + skb_trim(skb, 0); + return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb)); + + } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || + (hh->prim == (DL_RELEASE | REQUEST)) || + ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { + + spin_lock_irqsave(&hw->lock, flags); + if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->tx_skb) { + dev_kfree_skb(bch->tx_skb); + bch->tx_skb = NULL; + } + bch->tx_idx = 0; + if (bch->rx_skb) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + } + test_and_clear_bit(FLG_L2DATA, &bch->Flags); + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + setup_channel(hw, bch->channel, ISDN_PID_NONE); + test_and_clear_bit(FLG_ACTIVE, &bch->Flags); + spin_unlock_irqrestore(&hw->lock, flags); + skb_trim(skb, 0); + if (hh->prim != (PH_CONTROL | REQUEST)) { +#ifdef FIXME + if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) + if (bch->dev) + if_link(&bch->dev->rport.pif, + hh->prim | CONFIRM, 0, 0, NULL, 0); +#endif + if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb)) + return(0); + } + + } else { + printk(KERN_WARNING "%s %s: unknown prim(%x)\n", + hw->card_name, __FUNCTION__, hh->prim); + } + if (!ret) + dev_kfree_skb(skb); + return (ret); +} + +/***********************************************/ +/* handle Layer2 -> Layer 1 D-Channel messages */ +/***********************************************/ +static int +xhfc_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) +{ + channel_t *chan = container_of(inst, channel_t, inst); + mISDN_head_t *hh = mISDN_HEAD_P(skb); + xhfc_hw *hw = inst->privat; + int ret = 0; + u_long flags; + + if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) { + spin_lock_irqsave(inst->hwlock, flags); + ret = channel_senddata(chan, hh->dinfo, skb); + if (ret > 0) { /* direct TX */ + tasklet_schedule(&hw->tasklet); + // printk ("PH_DATA_REQ: %i bytes in channel(%i)\n", ret, chan->channel); + ret = 0; + } + spin_unlock_irqrestore(inst->hwlock, flags); + return(ret); + } + if (test_bit(FLG_DCHANNEL, &chan->Flags)) { + ret = handle_dmsg(chan, skb); + if (ret != -EAGAIN) + return(ret); + ret = -EINVAL; + } + if (test_bit(FLG_BCHANNEL, &chan->Flags)) { + ret = handle_bmsg(chan, skb); + if (ret != -EAGAIN) + return(ret); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return(ret); +} + +static int +xhfc_manager(void *data, u_int prim, void *arg) +{ + xhfc_hw *hw = NULL; + mISDNinstance_t *inst = data; + struct sk_buff *skb; + int channel = -1; + int i; + channel_t *chan = NULL; + u_long flags; + + if (!data) { + MGR_HASPROTOCOL_HANDLER(prim, arg, &hw_mISDNObj) + printk(KERN_ERR "%s %s: no data prim %x arg %p\n", + hw->card_name, __FUNCTION__, prim, arg); + return (-EINVAL); + } + + spin_lock_irqsave(&hw_mISDNObj.lock, flags); + + /* find channel and card */ + list_for_each_entry(hw, &hw_mISDNObj.ilist, list) { + i = 0; + while (i < MAX_CHAN) { + if (hw->chan[i].ch.Flags && + &hw->chan[i].ch.inst == inst) { + channel = i; + chan = &hw->chan[i].ch; + break; + } + i++; + } + if (channel >= 0) + break; + } + spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); + + if (channel < 0) { + printk(KERN_ERR + "%s: no card/channel found data %p prim %x arg %p\n", + __FUNCTION__, data, prim, arg); + return (-EINVAL); + } + + switch (prim) { + case MGR_REGLAYER | CONFIRM: + mISDN_setpara(chan, &inst->st->para); + break; + case MGR_UNREGLAYER | REQUEST: + if ((skb = create_link_skb(PH_CONTROL | REQUEST, + HW_DEACTIVATE, 0, NULL, 0))) { + if (xhfc_l2l1(inst, skb)) + dev_kfree_skb(skb); + } else + printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); + hw_mISDNObj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); + break; + case MGR_CLRSTPARA | INDICATION: + arg = NULL; + case MGR_ADDSTPARA | INDICATION: + mISDN_setpara(chan, arg); + break; + case MGR_RELEASE | INDICATION: + if (channel == 2) { + release_card(hw); + } else { + hw_mISDNObj.refcnt--; + } + break; + case MGR_SETSTACK | INDICATION: + if ((channel != 2) && (inst->pid.global == 2)) { + if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, + 0, 0, NULL, 0))) { + if (xhfc_l2l1(inst, skb)) + dev_kfree_skb(skb); + } + if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) + mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, + 0, 0, NULL, 0); + else + mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, + 0, 0, NULL, 0); + } + break; + case MGR_GLOBALOPT | REQUEST: + if (arg) { + // FIXME: detect cards with HEADSET + u_int *gopt = arg; + *gopt = GLOBALOPT_INTERNAL_CTRL | + GLOBALOPT_EXTERNAL_EQUIPMENT | + GLOBALOPT_HANDSET; + } else + return (-EINVAL); + break; + case MGR_SELCHANNEL | REQUEST: + // no special procedure + return (-EINVAL); + PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); + default: + printk(KERN_WARNING "%s %s: prim %x not handled\n", + hw->card_name, __FUNCTION__, prim); + return (-EINVAL); + } + return (0); +} + +/***********************************/ +/* check if new buffer for channel */ +/* is waitinng is transmitt queue */ +/***********************************/ +int +next_tx_frame(xhfc_hw * hw, __u8 channel) +{ + channel_t *ch = &hw->chan[channel].ch; + + if (ch->tx_skb) + dev_kfree_skb(ch->tx_skb); + if (test_and_clear_bit(FLG_TX_NEXT, &ch->Flags)) { + ch->tx_skb = ch->next_skb; + if (ch->tx_skb) { + mISDN_head_t *hh = mISDN_HEAD_P(ch->tx_skb); + ch->next_skb = NULL; + test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); + ch->tx_idx = 0; + queue_ch_frame(ch, CONFIRM, hh->dinfo, NULL); + return (1); + } else { + printk(KERN_WARNING + "%s channel(%i) TX_NEXT without skb\n", + hw->card_name, channel); + test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); + } + } else + ch->tx_skb = NULL; + test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); + return (0); +} + +static inline void +xhfc_waitbusy(xhfc_hw * hw) +{ + while (read_xhfc(hw, R_STATUS) & M_BUSY); +} + +static inline void +xhfc_selfifo(xhfc_hw * hw, __u8 fifo) +{ + write_xhfc(hw, R_FIFO, fifo); + xhfc_waitbusy(hw); +} + +static inline void +xhfc_inc_f(xhfc_hw * hw) +{ + write_xhfc(hw, A_INC_RES_FIFO, M_INC_F); + xhfc_waitbusy(hw); +} + +static inline void +xhfc_resetfifo(xhfc_hw * hw) +{ + write_xhfc(hw, A_INC_RES_FIFO, M_RES_FIFO | M_RES_FIFO_ERR); + xhfc_waitbusy(hw); +} + +/**************************/ +/* fill fifo with TX data */ +/**************************/ +void +xhfc_write_fifo(xhfc_hw * hw, __u8 channel) +{ + __u8 fcnt, tcnt, i; + __u8 free; + __u8 f1, f2; + __u8 fstat; + __u8 *data; + int remain; + channel_t *ch = &hw->chan[channel].ch; + + + send_buffer: + if (!ch->tx_skb) + return; + remain = ch->tx_skb->len - ch->tx_idx; + if (remain <= 0) + return; + + xhfc_selfifo(hw, (channel * 2)); + + fstat = read_xhfc(hw, A_FIFO_STA); + free = (hw->max_z - (read_xhfc(hw, A_USAGE))); + tcnt = (free >= remain) ? remain : free; + + f1 = read_xhfc(hw, A_F1); + f2 = read_xhfc(hw, A_F2); + + fcnt = 0x07 - ((f1 - f2) & 0x07); /* free frame count in tx fifo */ + + if (debug & DEBUG_HFC_FIFO) { + mISDN_debugprint(&ch->inst, + "%s channel(%i) len(%i) idx(%i) f1(%i) f2(%i) fcnt(%i) tcnt(%i) free(%i) fstat(%i)", + __FUNCTION__, channel, ch->tx_skb->len, ch->tx_idx, + f1, f2, fcnt, tcnt, free, fstat); + } + + /* check for fifo underrun during frame transmission */ + fstat = read_xhfc(hw, A_FIFO_STA); + if (fstat & M_FIFO_ERR) { + if (debug & DEBUG_HFC_FIFO_ERR) { + mISDN_debugprint(&ch->inst, + "%s transmit fifo channel(%i) underrun idx(%i), A_FIFO_STA(0x%02x)", + __FUNCTION__, channel, + ch->tx_idx, fstat); + } + + write_xhfc(hw, A_INC_RES_FIFO, M_RES_FIFO_ERR); + + /* restart frame transmission */ + if ((test_bit(FLG_HDLC, &ch->Flags)) && ch->tx_idx) { + ch->tx_idx = 0; + goto send_buffer; + } + } + + if (free && fcnt && tcnt) { + data = ch->tx_skb->data + ch->tx_idx; + ch->tx_idx += tcnt; + + if (debug & DEBUG_HFC_FIFO) { + printk("%s channel(%i) writing: ", + hw->card_name, channel); + + i=0; + while (i= 4) { + write32_xhfc(hw, A_FIFO_DATA, *((__u32 *) (data+i))); + i += 4; + } else { + write_xhfc(hw, A_FIFO_DATA, *(data+i)); + i++; + } + } + + if (ch->tx_idx == ch->tx_skb->len) { + if (test_bit(FLG_HDLC, &ch->Flags)) { + /* terminate frame */ + xhfc_inc_f(hw); + } else { + xhfc_selfifo(hw, (channel * 2)); + } + + /* check for fifo underrun during frame transmission */ + fstat = read_xhfc(hw, A_FIFO_STA); + if (fstat & M_FIFO_ERR) { + if (debug & DEBUG_HFC_FIFO_ERR) { + mISDN_debugprint(&ch->inst, + "%s transmit fifo channel(%i) underrun " + "during transmission, A_FIFO_STA(0x%02x)\n", + __FUNCTION__, + channel, + fstat); + } + write_xhfc(hw, A_INC_RES_FIFO, M_RES_FIFO_ERR); + + if (test_bit(FLG_HDLC, &ch->Flags)) { + // restart frame transmission + ch->tx_idx = 0; + goto send_buffer; + } + } + + if (next_tx_frame(hw, channel)) { + if (debug & DEBUG_HFC_BTRACE) + mISDN_debugprint(&ch->inst, + "channel(%i) has next_tx_frame", + channel); + if ((free - tcnt) > 8) { + if (debug & DEBUG_HFC_BTRACE) + mISDN_debugprint(&ch->inst, + "channel(%i) continue B-TX immediatetly", + channel); + goto send_buffer; + } + } + + + } else { + /* tx buffer not complete, but fifo filled to maximum */ + xhfc_selfifo(hw, (channel * 2)); + } + } +} + +/****************************/ +/* read RX data out of fifo */ +/****************************/ +void +xhfc_read_fifo(xhfc_hw * hw, __u8 channel) +{ + __u8 f1=0, f2=0, z1=0, z2=0; + __u8 fstat = 0; + int i; + int rcnt; /* read rcnt bytes out of fifo */ + __u8 *data; /* new data pointer */ + struct sk_buff *skb; /* data buffer for upper layer */ + channel_t *ch = &hw->chan[channel].ch; + + receive_buffer: + + xhfc_selfifo(hw, (channel * 2) + 1); + + fstat = read_xhfc(hw, A_FIFO_STA); + if (fstat & M_FIFO_ERR) { + if (debug & DEBUG_HFC_FIFO_ERR) + mISDN_debugprint(&ch->inst, + "RX fifo overflow channel(%i), " + "A_FIFO_STA(0x%02x) f0cnt(%i)", + channel, fstat, hw->f0_akku); + write_xhfc(hw, A_INC_RES_FIFO, M_RES_FIFO_ERR); + } + + if (test_bit(FLG_HDLC, &ch->Flags)) { + /* hdlc rcnt */ + f1 = read_xhfc(hw, A_F1); + f2 = read_xhfc(hw, A_F2); + z1 = read_xhfc(hw, A_Z1); + z2 = read_xhfc(hw, A_Z2); + + rcnt = (z1 - z2) & hw->max_z; + if (f1 != f2) + rcnt++; + + } else { + /* transparent rcnt */ + rcnt = read_xhfc(hw, A_USAGE) - 1; + } + + if (debug & DEBUG_HFC_FIFO) { + if (ch->rx_skb) + i = ch->rx_skb->len; + else + i = 0; + mISDN_debugprint(&ch->inst, "reading %i bytes channel(%i) " + "irq_cnt(%i) fstat(%i) idx(%i) f1(%i) f2(%i) z1(%i) z2(%i)", + rcnt, channel, hw->irq_cnt, fstat, i, f1, f2, z1, z2); + } + + if (rcnt > 0) { + if (!ch->rx_skb) { + ch->rx_skb = alloc_stack_skb(ch->maxlen + 3, ch->up_headerlen); + if (!ch->rx_skb) { + printk(KERN_DEBUG "%s: No mem for rx_skb\n", __FUNCTION__); + return; + } + } + data = skb_put(ch->rx_skb, rcnt); + + /* read data from FIFO */ + i=0; + while (i= 4) { + *((__u32 *) (data+i)) = read32_xhfc(hw, A_FIFO_DATA); + i += 4; + } else { + *(data+i) = read_xhfc(hw, A_FIFO_DATA); + i++; + } + } + } else + return; + + + if (test_bit(FLG_HDLC, &ch->Flags)) { + if (f1 != f2) { + xhfc_inc_f(hw); + + /* check minimum frame size */ + if (ch->rx_skb->len < 4) { + if (debug & DEBUG_HFC_FIFO_ERR) + mISDN_debugprint(&ch->inst, + "%s: frame in channel(%i) < minimum size", + __FUNCTION__, + channel); + goto read_exit; + } + + /* check crc */ + if (ch->rx_skb->data[ch->rx_skb->len - 1]) { + if (debug & DEBUG_HFC_FIFO_ERR) + mISDN_debugprint(&ch->inst, + "%s: channel(%i) CRC-error", + __FUNCTION__, + channel); + goto read_exit; + } + + /* remove cksum */ + skb_trim(ch->rx_skb, ch->rx_skb->len - 3); + + if (ch->rx_skb->len < MISDN_COPY_SIZE) { + skb = alloc_stack_skb(ch->rx_skb->len, ch->up_headerlen); + if (skb) { + memcpy(skb_put(skb, ch->rx_skb->len), + ch->rx_skb->data, ch->rx_skb->len); + skb_trim(ch->rx_skb, 0); + } else { + skb = ch->rx_skb; + ch->rx_skb = NULL; + } + } else { + skb = ch->rx_skb; + ch->rx_skb = NULL; + } + + queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, skb); + + if ((ch->debug) && (debug & DEBUG_HFC_DTRACE)) { + mISDN_debugprint(&ch->inst, + "channel(%i) new RX len(%i): ", + channel, skb->len); + i = 0; + printk(" "); + while (i < skb->len) + printk("%02x ", skb->data[i++]); + printk("\n"); + } + + read_exit: + if (ch->rx_skb) + skb_trim(ch->rx_skb, 0); + if (read_xhfc(hw, A_USAGE) > 8) { + if (debug & DEBUG_HFC_FIFO) + mISDN_debugprint(&ch->inst, + "%s: channel(%i) continue xhfc_read_fifo", + __FUNCTION__, + channel); + goto receive_buffer; + } + return; + + + } else { + xhfc_selfifo(hw, (channel * 2) + 1); + } + } else { + xhfc_selfifo(hw, (channel * 2) + 1); + if (ch->rx_skb->len >= TRANSP_PACKET_SIZE) { + /* deliver transparent data to layer2 */ + queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, ch->rx_skb); + ch->rx_skb = NULL; + } + } +} + +/*************************************/ +/* bottom half handler for interrupt */ +/*************************************/ +static void +xhfc_bh_handler(unsigned long ul_hw) +{ + xhfc_hw *hw = (xhfc_hw *) ul_hw; + int i; + reg_a_su_rd_sta su_state; + channel_t *dch; + + /* timer interrupt */ + if (hw->misc_irq.bit.v_ti_irq) { + hw->misc_irq.bit.v_ti_irq = 0; + + /* Handle tx Fifos */ + for (i = 0; i < hw->max_fifo; i++) { + if ((1 << (i * 2)) & (hw->fifo_irqmsk)) { + hw->fifo_irq &= ~(1 << (i * 2)); + if (test_bit(FLG_TX_BUSY, &hw->chan[i].ch.Flags)) + xhfc_write_fifo(hw, i); + } + } + + /* handle NT Timer */ + for (i = 0; i < hw->num_ports; i++) { + if ((hw->port[i].mode & PORT_MODE_NT) + && (hw->port[i].mode & NT_TIMER)) { + if ((--hw->port[i].nt_timer) < 0) + su_new_state(&hw->chan[(i << 2) + 2].ch); + } + } + } + + /* set fifo_irq when RX data over treshold */ + for (i = 0; i < hw->num_ports; i++) { + hw->fifo_irq |= read_xhfc(hw, R_FILL_BL0 + i) << (i * 8); + } + + /* Handle rx Fifos */ + if ((hw->fifo_irq & hw->fifo_irqmsk) & FIFO_MASK_RX) { + for (i = 0; i < hw->max_fifo; i++) { + if ((hw->fifo_irq & (1 << (i * 2 + 1))) + & (hw->fifo_irqmsk)) { + + hw->fifo_irq &= ~(1 << (i * 2 + 1)); + xhfc_read_fifo(hw, i); + } + } + } + + /* su interrupt */ + if (hw->su_irq.reg & hw->su_irqmsk.reg) { + hw->su_irq.reg = 0; + for (i = 0; i < hw->num_ports; i++) { + write_xhfc(hw, R_SU_SEL, i); + su_state.reg = read_xhfc(hw, A_SU_RD_STA); + + dch = &hw->chan[(i << 2) + 2].ch; + if (su_state.bit.v_su_sta != dch->state) { + dch->state = su_state.bit.v_su_sta; + su_new_state(dch); + } + } + } +} + +/*********************/ +/* Interrupt handler */ +/*********************/ +static irqreturn_t +xhfc_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + xhfc_hw *hw = dev_id; + __u8 i; + __u32 f0_cnt; + + if (!(hw->irq_ctrl.bit.v_glob_irq_en) + && (read_xhfc(hw, R_IRQ_OVIEW))) { + if (debug & DEBUG_HFC_IRQ) + printk(KERN_INFO + "%s %s NOT M_GLOB_IRQ_EN or R_IRQ_OVIEW \n", + hw->card_name, __FUNCTION__); + return IRQ_NONE; + } + + hw->misc_irq.reg |= read_xhfc(hw, R_MISC_IRQ); + hw->su_irq.reg |= read_xhfc(hw, R_SU_IRQ); + + /* get fifo IRQ states in bundle */ + for (i = 0; i < 4; i++) { + hw->fifo_irq |= + (read_xhfc(hw, R_FIFO_BL0_IRQ + i) << (i * 8)); + } + + /* call bottom half at events + * - Timer Interrupt (or other misc_irq sources) + * - SU State change + * - Fifo FrameEnd interrupts (only at rx fifos enabled) + */ + if (!((hw->misc_irq.reg & hw->misc_irqmsk.reg) + || (hw->su_irq.reg & hw->su_irqmsk.reg) + || (hw->fifo_irq & hw->fifo_irqmsk))) + return IRQ_NONE; + + /* queue bottom half */ + if (!(hw->testirq)) + tasklet_schedule(&hw->tasklet); + + /* count irqs */ + hw->irq_cnt++; + +#ifdef USE_F0_COUNTER + /* akkumulate f0 counter diffs */ + f0_cnt = read_xhfc(hw, R_F0_CNTL); + f0_cnt += read_xhfc(hw, R_F0_CNTH) << 8; + hw->f0_akku += (f0_cnt - hw->f0_cnt); + if ((f0_cnt - hw->f0_cnt) < 0) + hw->f0_akku += 0xFFFF; + hw->f0_cnt = f0_cnt; +#endif + + return IRQ_HANDLED; +} + +/*****************************************************/ +/* disable all interrupts by disabling M_GLOB_IRQ_EN */ +/*****************************************************/ +void +disable_interrupts(xhfc_hw * hw) +{ + if (debug & DEBUG_HFC_IRQ) + printk(KERN_INFO "%s %s\n", hw->card_name, __FUNCTION__); + hw->irq_ctrl.bit.v_glob_irq_en = 0; + write_xhfc(hw, R_IRQ_CTRL, hw->irq_ctrl.reg); +} + +/******************************************/ +/* start interrupt and set interrupt mask */ +/******************************************/ +void +enable_interrupts(xhfc_hw * hw) +{ + if (debug & DEBUG_HFC_IRQ) + printk(KERN_INFO "%s %s\n", hw->card_name, __FUNCTION__); + + write_xhfc(hw, R_SU_IRQMSK, hw->su_irqmsk.reg); + + /* use defined timer interval */ + write_xhfc(hw, R_TI_WD, hw->ti_wd.reg); + hw->misc_irqmsk.bit.v_ti_irqmsk = 1; + write_xhfc(hw, R_MISC_IRQMSK, hw->misc_irqmsk.reg); + + /* clear all pending interrupts bits */ + read_xhfc(hw, R_MISC_IRQ); + read_xhfc(hw, R_SU_IRQ); + read_xhfc(hw, R_FIFO_BL0_IRQ); + read_xhfc(hw, R_FIFO_BL1_IRQ); + read_xhfc(hw, R_FIFO_BL2_IRQ); + read_xhfc(hw, R_FIFO_BL3_IRQ); + + /* enable global interrupts */ + hw->irq_ctrl.bit.v_glob_irq_en = 1; + hw->irq_ctrl.bit.v_fifo_irq_en = 1; + write_xhfc(hw, R_IRQ_CTRL, hw->irq_ctrl.reg); +} + +/***********************************/ +/* initialise the XHFC ISDN Chip */ +/* return 0 on success. */ +/***********************************/ +int +init_xhfc(xhfc_hw * hw) +{ + int err = 0; + int timeout = 0x2000; + + hw->chip_id = read_xhfc(hw, R_CHIP_ID); + + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO "%s %s ChipID: 0x%x\n", hw->card_name, + __FUNCTION__, hw->chip_id); + + switch (hw->chip_id) { + case CHIP_ID_1SU: + hw->num_ports = 1; + hw->max_fifo = 4; + hw->max_z = 0xFF; + hw->ti_wd.bit.v_ev_ts = 0x6; /* timer irq interval 16 ms */ + write_xhfc(hw, R_FIFO_MD, M1_FIFO_MD * 2); + hw->su_irqmsk.bit.v_su0_irqmsk = 1; + break; + case CHIP_ID_2SU: + hw->num_ports = 2; + hw->max_fifo = 8; + hw->max_z = 0x7F; + hw->ti_wd.bit.v_ev_ts = 0x5; /* timer irq interval 8 ms */ + write_xhfc(hw, R_FIFO_MD, M1_FIFO_MD * 1); + hw->su_irqmsk.bit.v_su0_irqmsk = 1; + hw->su_irqmsk.bit.v_su1_irqmsk = 1; + break; + case CHIP_ID_2S4U: + case CHIP_ID_4SU: + hw->num_ports = 4; + hw->max_fifo = 16; + hw->max_z = 0x3F; + hw->ti_wd.bit.v_ev_ts = 0x4; /* timer irq interval 4 ms */ + write_xhfc(hw, R_FIFO_MD, M1_FIFO_MD * 0); + hw->su_irqmsk.bit.v_su0_irqmsk = 1; + hw->su_irqmsk.bit.v_su1_irqmsk = 1; + hw->su_irqmsk.bit.v_su2_irqmsk = 1; + hw->su_irqmsk.bit.v_su3_irqmsk = 1; + break; + default: + err = -ENODEV; + } + + if (err) { + if (debug & DEBUG_HFC_INIT) + printk(KERN_ERR "%s %s: unkown Chip ID 0x%x\n", + hw->card_name, __FUNCTION__, hw->chip_id); + return (err); + } + + /* software reset to enable R_FIFO_MD setting */ + write_xhfc(hw, R_CIRM, M_SRES); + udelay(5); + write_xhfc(hw, R_CIRM, 0); + + /* amplitude */ + write_xhfc(hw, R_PWM_MD, 0x80); + write_xhfc(hw, R_PWM1, 0x18); + + write_xhfc(hw, R_FIFO_THRES, 0x11); + + while ((read_xhfc(hw, R_STATUS) & (M_BUSY | M_PCM_INIT)) + && (timeout)) + timeout--; + + if (!(timeout)) { + if (debug & DEBUG_HFC_INIT) + printk(KERN_ERR + "%s %s: initialization sequence could not finish\n", + hw->card_name, __FUNCTION__); + return (-ENODEV); + } + + /* set PCM master mode */ + hw->pcm_md0.bit.v_pcm_md = 1; + write_xhfc(hw, R_PCM_MD0, hw->pcm_md0.reg); + + /* set pll adjust */ + hw->pcm_md0.bit.v_pcm_idx = IDX_PCM_MD1; + hw->pcm_md1.bit.v_pll_adj = 3; + write_xhfc(hw, R_PCM_MD0, hw->pcm_md0.reg); + write_xhfc(hw, R_PCM_MD1, hw->pcm_md1.reg); + + enable_interrupts(hw); + + mdelay(1 << hw->ti_wd.bit.v_ev_ts); + if (hw->irq_cnt > 2) { + disable_interrupts(hw); + hw->testirq = 0; + return (0); + } else { + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO + "%s %s: ERROR getting IRQ (irq_cnt %i)\n", + hw->card_name, __FUNCTION__, hw->irq_cnt); + disable_interrupts(hw); + free_irq(hw->irq, hw); + return (-EIO); + } +} + +/*************************************/ +/* free memory for all used channels */ +/*************************************/ +void +release_channels(xhfc_hw * hw) +{ + int i = 0; + + while (i < MAX_CHAN) { + if (hw->chan[i].ch.Flags) { + if (debug & DEBUG_HFC_INIT) + printk(KERN_DEBUG "%s %s: free channel %d\n", + hw->card_name, __FUNCTION__, i); + mISDN_freechannel(&hw->chan[i].ch); + hw_mISDNObj.ctrl(&hw->chan[i].ch.inst, MGR_UNREGLAYER | REQUEST, NULL); + } + i++; + } +} + +/*********************************************/ +/* setup port (line interface) with SU_CRTLx */ +/*********************************************/ +void +init_su(xhfc_hw * hw, __u8 pt) +{ + xhfc_port_t *port = &hw->port[pt]; + + if (debug & DEBUG_HFC_MODE) + printk(KERN_INFO "%s %s port(%i)\n", hw->card_name, + __FUNCTION__, pt); + + write_xhfc(hw, R_SU_SEL, pt); + + if (port->mode & PORT_MODE_NT) + port->su_ctrl0.bit.v_su_md = 1; + + if (port->mode & PORT_MODE_EXCH_POL) + port->su_ctrl2.reg = M_SU_EXCHG; + + if (port->mode & PORT_MODE_UP) { + port->st_ctrl3.bit.v_st_sel = 1; + write_xhfc(hw, A_MS_TX, 0x0F); + port->su_ctrl0.bit.v_st_sq_en = 1; + } + + if (debug & DEBUG_HFC_MODE) + printk(KERN_INFO "%s %s su_ctrl0(0x%02x) " + "su_ctrl1(0x%02x) " + "su_ctrl2(0x%02x) " + "st_ctrl3(0x%02x)\n", + hw->card_name, __FUNCTION__, + port->su_ctrl0.reg, + port->su_ctrl1.reg, + port->su_ctrl2.reg, + port->st_ctrl3.reg); + + write_xhfc(hw, A_ST_CTRL3, port->st_ctrl3.reg); + write_xhfc(hw, A_SU_CTRL0, port->su_ctrl0.reg); + write_xhfc(hw, A_SU_CTRL1, port->su_ctrl1.reg); + write_xhfc(hw, A_SU_CTRL2, port->su_ctrl2.reg); + + if (port->mode & PORT_MODE_TE) + write_xhfc(hw, A_SU_CLK_DLY, CLK_DLY_TE); + else + write_xhfc(hw, A_SU_CLK_DLY, CLK_DLY_NT); + + write_xhfc(hw, A_SU_WR_STA, 0); +} + +/*********************************************************/ +/* Setup Fifo using A_CON_HDLC, A_SUBCH_CFG, A_FIFO_CTRL */ +/*********************************************************/ +void +setup_fifo(xhfc_hw * hw, __u8 fifo, __u8 conhdlc, __u8 subcfg, + __u8 fifoctrl, __u8 enable) +{ + xhfc_selfifo(hw, fifo); + write_xhfc(hw, A_CON_HDLC, conhdlc); + write_xhfc(hw, A_SUBCH_CFG, subcfg); + write_xhfc(hw, A_FIFO_CTRL, fifoctrl); + + if (enable) + hw->fifo_irqmsk |= (1 << fifo); + else + hw->fifo_irqmsk &= ~(1 << fifo); + + xhfc_resetfifo(hw); + xhfc_selfifo(hw, fifo); + + if (debug & DEBUG_HFC_MODE) { + printk(KERN_INFO + "%s %s: fifo(%i) conhdlc(0x%02x) subcfg(0x%02x) fifoctrl(0x%02x)\n", + hw->card_name, __FUNCTION__, fifo, sread_xhfc(hw, + A_CON_HDLC), + sread_xhfc(hw, A_SUBCH_CFG), sread_xhfc(hw, + A_FIFO_CTRL) + ); + } +} + +/**************************************************/ +/* Setup S/U interface, enable/disable B-Channels */ +/**************************************************/ +void +setup_su(xhfc_hw * hw, __u8 pt, __u8 bc, __u8 enable) +{ + xhfc_port_t *port = &hw->port[pt]; + + if (!((bc == 0) || (bc == 1))) { + printk(KERN_INFO "%s %s: pt(%i) ERROR: bc(%i) unvalid!\n", + hw->card_name, __FUNCTION__, pt, bc); + return; + } + + if (debug & DEBUG_HFC_MODE) + printk(KERN_INFO "%s %s %s pt(%i) bc(%i)\n", + hw->card_name, __FUNCTION__, + (enable) ? ("enable") : ("disable"), pt, bc); + + if (bc) { + port->su_ctrl2.bit.v_b2_rx_en = (enable?1:0); + port->su_ctrl0.bit.v_b2_tx_en = (enable?1:0); + } else { + port->su_ctrl2.bit.v_b1_rx_en = (enable?1:0); + port->su_ctrl0.bit.v_b1_tx_en = (enable?1:0); + } + + if (hw->port[pt].mode & PORT_MODE_NT) + hw->port[pt].su_ctrl0.bit.v_su_md = 1; + + write_xhfc(hw, R_SU_SEL, pt); + write_xhfc(hw, A_SU_CTRL0, hw->port[pt].su_ctrl0.reg); + write_xhfc(hw, A_SU_CTRL2, hw->port[pt].su_ctrl2.reg); +} + +/*********************************************/ +/* (dis-) connect D/B-Channel using protocol */ +/*********************************************/ +int +setup_channel(xhfc_hw * hw, __u8 channel, int protocol) +{ + xhfc_port_t *port = hw->chan[channel].port; + + if (test_bit(FLG_BCHANNEL, &hw->chan[channel].ch.Flags)) { + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].ch.inst, + "channel(%i) protocol %x-->%x", + channel, + hw->chan[channel].ch.state, + protocol); + + switch (protocol) { + case (-1): /* used for init */ + hw->chan[channel].ch.state = -1; + hw->chan[channel].ch.channel = channel; + /* fall trough */ + case (ISDN_PID_NONE): + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw-> + chan[channel].ch.inst, + "ISDN_PID_NONE"); + if (hw->chan[channel].ch.state == ISDN_PID_NONE) + return (0); /* already in idle state */ + hw->chan[channel].ch.state = ISDN_PID_NONE; + + setup_fifo(hw, (channel << 1), 4, 0, 0, 0); /* B-TX fifo */ + setup_fifo(hw, (channel << 1) + 1, 4, 0, 0, 0); /* B-RX fifo */ + + setup_su(hw, port->idx, (channel % 4) ? 1 : 0, 0); + + test_and_clear_bit(FLG_HDLC, &hw->chan[channel].ch.Flags); + test_and_clear_bit(FLG_TRANSPARENT, &hw->chan[channel].ch.Flags); + + break; + + case (ISDN_PID_L1_B_64TRANS): + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].ch.inst, + "ISDN_PID_L1_B_64TRANS"); + setup_fifo(hw, (channel << 1), 6, 0, 0, 1); /* B-TX Fifo */ + setup_fifo(hw, (channel << 1) + 1, 6, 0, 0, 1); /* B-RX Fifo */ + + setup_su(hw, port->idx, (channel % 4) ? 1 : 0, 1); + + hw->chan[channel].ch.state = ISDN_PID_L1_B_64TRANS; + test_and_set_bit(FLG_TRANSPARENT, &hw->chan[channel].ch.Flags); + + break; + + case (ISDN_PID_L1_B_64HDLC): + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].ch.inst, + "ISDN_PID_L1_B_64HDLC"); + setup_fifo(hw, (channel << 1), 4, 0, M_FR_ABO, 1); // TX Fifo + setup_fifo(hw, (channel << 1) + 1, 4, 0, M_FR_ABO | M_FIFO_IRQMSK, 1); // RX Fifo + + setup_su(hw, port->idx, (channel % 4) ? 1 : 0, 1); + + hw->chan[channel].ch.state = ISDN_PID_L1_B_64HDLC; + test_and_set_bit(FLG_HDLC, &hw->chan[channel].ch.Flags); + + break; + default: + mISDN_debugprint(&hw->chan[channel].ch.inst, + "prot not known %x", + protocol); + return (-ENOPROTOOPT); + } + return (0); + } + else if (test_bit(FLG_DCHANNEL, &hw->chan[channel].ch.Flags)) { + if (debug & DEBUG_HFC_MODE) + mISDN_debugprint(&hw->chan[channel].ch.inst, + "D channel(%i) protocol(%i)", + channel, protocol); + + setup_fifo(hw, (channel << 1), 5, 2, M_FR_ABO, 1); /* D TX fifo */ + setup_fifo(hw, (channel << 1) + 1, 5, 2, M_FR_ABO | M_FIFO_IRQMSK, 1); /* D RX fifo */ + + return (0); + } + + printk(KERN_INFO + "%s %s ERROR: channel(%i) is NEITHER B nor D !!!\n", + hw->card_name, __FUNCTION__, channel); + + return (-1); +} + +/*****************************************************/ +/* register ISDN stack for one XHFC card */ +/* - register all ports and channels */ +/* - set param_idx */ +/* */ +/* channel mapping in mISDN in hw->chan[MAX_CHAN]: */ +/* 1st line interf: 0=B1, 1=B2, 2=D, 3=PCM */ +/* 2nd line interf: 4=B1, 5=B2, 6=D, 7=PCM */ +/* 3rd line interf: 8=B1, 9=B2, 10=D, 11=PCM */ +/* 4th line interf; 12=B1, 13=B2, 14=D, 15=PCM */ +/*****************************************************/ +int +init_mISDN_channels(xhfc_hw * hw) +{ + int err; + int pt; /* ST/U port index */ + int ch_idx; /* channel index */ + int b; + channel_t *ch; + mISDN_pid_t pid; + u_long flags; + + for (pt = 0; pt < hw->num_ports; pt++) { + /* init D channels */ + ch_idx = (pt << 2) + 2; + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO + "%s %s: Registering D-channel, card(%d) " + "ch(%d) port(%d) protocol(%x)\n", + hw->card_name, __FUNCTION__, hw->cardnum, + ch_idx, pt, hw->port[pt].dpid); + + hw->port[pt].idx = pt; + hw->chan[ch_idx].port = &hw->port[pt]; + ch = &hw->chan[ch_idx].ch; + + memset(ch, 0, sizeof(channel_t)); + ch->channel = ch_idx; + ch->debug = debug; + ch->inst.obj = &hw_mISDNObj; + ch->inst.hwlock = &hw->lock; + ch->inst.class_dev.dev = &hw->pdev->dev; + mISDN_init_instance(&ch->inst, &hw_mISDNObj, hw, xhfc_l2l1); + ch->inst.pid.layermask = ISDN_LAYER(0); + sprintf(ch->inst.name, "%s/%d", hw->card_name, pt); + err = mISDN_initchannel(ch, MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); + if (err) + goto free_channels; + ch->hw = hw; + + /* init t3 timer */ + init_timer(&hw->port[pt].t3_timer); + hw->port[pt].t3_timer.data = (long) ch; + hw->port[pt].t3_timer.function = (void *) l1_timer_expire_t3; + + /* init t4 timer */ + init_timer(&hw->port[pt].t4_timer); + hw->port[pt].t4_timer.data = (long) ch; + hw->port[pt].t4_timer.function = (void *) l1_timer_expire_t4; + + /* init B channels */ + for (b = 0; b < 2; b++) { + ch_idx = (pt << 2) + b; + if (debug & DEBUG_HFC_INIT) + printk(KERN_DEBUG + "%s %s: Registering B-channel, card(%d) " + "ch(%d) port(%d)\n", hw->card_name, + __FUNCTION__, hw->cardnum, ch_idx, pt); + + hw->chan[ch_idx].port = &hw->port[pt]; + ch = &hw->chan[ch_idx].ch; + + memset(ch, 0, sizeof(channel_t)); + ch->channel = ch_idx; + ch->debug = debug; + mISDN_init_instance(&ch->inst, &hw_mISDNObj, hw, xhfc_l2l1); + ch->inst.pid.layermask = ISDN_LAYER(0); + ch->inst.hwlock = &hw->lock; + ch->inst.class_dev.dev = &hw->pdev->dev; + + sprintf(ch->inst.name, "%s/%d B%d", + hw->card_name, pt, b + 1); + + if (mISDN_initchannel(ch, MSK_INIT_BCHANNEL, MAX_DATA_MEM)) { + err = -ENOMEM; + goto free_channels; + } + ch->hw = hw; + } + + /* clear PCM */ + memset(&hw->chan[(pt << 2) + 3], 0, sizeof(channel_t)); + + mISDN_set_dchannel_pid(&pid, hw->port[pt].dpid, + layermask[hw->param_idx + pt]); + + /* register D Channel */ + ch = &hw->chan[(pt << 2) + 2].ch; + + /* set protocol for NT/TE */ + if (hw->port[pt].mode & PORT_MODE_NT) { + /* NT-mode */ + hw->port[hw->param_idx + pt].mode |= NT_TIMER; + hw->port[hw->param_idx + pt].nt_timer = 0; + + ch->inst.pid.protocol[0] = ISDN_PID_L0_NT_S0; + ch->inst.pid.protocol[1] = ISDN_PID_L1_NT_S0; + pid.protocol[0] = ISDN_PID_L0_NT_S0; + pid.protocol[1] = ISDN_PID_L1_NT_S0; + ch->inst.pid.layermask |= ISDN_LAYER(1); + pid.layermask |= ISDN_LAYER(1); + if (layermask[hw->param_idx + pt] & ISDN_LAYER(2)) + pid.protocol[2] = ISDN_PID_L2_LAPD_NET; + } else { + /* TE-mode */ + hw->port[hw->param_idx + pt].mode |= PORT_MODE_TE; + ch->inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; + ch->inst.pid.protocol[1] = ISDN_PID_L1_TE_S0; + pid.protocol[0] = ISDN_PID_L0_TE_S0; + pid.protocol[1] = ISDN_PID_L1_TE_S0; + } + + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO + "%s %s: registering Stack for Port %i\n", + hw->card_name, __FUNCTION__, pt); + + /* register stack */ + err = hw_mISDNObj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &ch->inst); + if (err) { + printk(KERN_ERR + "%s %s: MGR_NEWSTACK | REQUEST err(%d)\n", + hw->card_name, __FUNCTION__, err); + goto free_channels; + } + ch->state = 0; + + /* attach two BChannels to this DChannel (ch) */ + for (b = 0; b < 2; b++) { + err = hw_mISDNObj.ctrl(ch->inst.st, + MGR_NEWSTACK | REQUEST, + &hw->chan[(pt << 2) + b].ch.inst); + if (err) { + printk(KERN_ERR + "%s %s: MGR_ADDSTACK bchan error %d\n", + hw->card_name, __FUNCTION__, err); + goto free_stack; + } + } + + err = hw_mISDNObj.ctrl(ch->inst.st, MGR_SETSTACK | REQUEST, &pid); + + if (err) { + printk(KERN_ERR + "%s %s: MGR_SETSTACK REQUEST dch err(%d)\n", + hw->card_name, __FUNCTION__, err); + hw_mISDNObj.ctrl(ch->inst.st, + MGR_DELSTACK | REQUEST, NULL); + goto free_stack; + } + + /* initial setup of each channel */ + setup_channel(hw, ch->channel, -1); + for (b = 0; b < 2; b++) + setup_channel(hw, (pt << 2) + b, -1); + + /* delay some time */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ + + hw_mISDNObj.ctrl(ch->inst.st, MGR_CTRLREADY | INDICATION, NULL); + } + return (0); + + free_stack: + hw_mISDNObj.ctrl(ch->inst.st, MGR_DELSTACK | REQUEST, NULL); + free_channels: + spin_lock_irqsave(&hw_mISDNObj.lock, flags); + release_channels(hw); + list_del(&hw->list); + spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); + + return (err); +} + +/********************************/ +/* parse module paramaters like */ +/* NE/TE and S0/Up port mode */ +/********************************/ +void +parse_module_params(xhfc_hw * hw) +{ + __u8 pt; + + /* parse module parameters */ + for (pt = 0; pt < hw->num_ports; pt++) { + /* D-Channel protocol: (2=DSS1) */ + hw->port[pt].dpid = (protocol[hw->param_idx + pt] & 0x0F); + if (hw->port[pt].dpid == 0) { + printk(KERN_INFO + "%s %s: WARNING: wrong value for protocol[%i], " + "assuming 0x02 (DSS1)...\n", + hw->card_name, __FUNCTION__, + hw->param_idx + pt); + hw->port[pt].dpid = 0x02; + } + + /* Line Interface TE or NT */ + if (protocol[hw->param_idx + pt] & 0x10) + hw->port[pt].mode |= PORT_MODE_NT; + else + hw->port[pt].mode |= PORT_MODE_TE; + + /* Line Interface in S0 or Up mode */ + if (protocol[hw->param_idx + pt] & 0x20) + hw->port[pt].mode |= PORT_MODE_UP; + else + hw->port[pt].mode |= PORT_MODE_S0; + + /* st line polarity */ + if (protocol[hw->param_idx + pt] & 0x40) + hw->port[pt].mode |= PORT_MODE_EXCH_POL; + + /* link B-channel loop */ + if (protocol[hw->param_idx + pt] & 0x80) + hw->port[pt].mode |= PORT_MODE_LOOP; + + + if (debug & DEBUG_HFC_INIT) + printk ("%s %s: protocol[%i]=0x%02x, dpid=%d, mode:%s,%s %s %s\n", + hw->card_name, __FUNCTION__, hw->param_idx+pt, + protocol[hw->param_idx + pt], + hw->port[pt].dpid, + (hw->port[pt].mode & PORT_MODE_TE)?"TE":"NT", + (hw->port[pt].mode & PORT_MODE_S0)?"S0":"Up", + (hw->port[pt].mode & PORT_MODE_EXCH_POL)?"SU_EXCH":"", + (hw->port[pt].mode & PORT_MODE_LOOP)?"B-LOOP":"" + ); + } +} + +/********************************/ +/* initialise the XHFC hardware */ +/* return 0 on success. */ +/********************************/ +static int __devinit +setup_instance(xhfc_hw * hw) +{ + int err; + int pt; + xhfc_hw *previous_hw; + u_long flags; + +#if BRIDGE == BRIDGE_PCI2PI + err = init_pci_bridge(hw); + if (err) + goto out; +#endif + + if (debug & DEBUG_HFC_INIT) + printk(KERN_WARNING "%s %s: requesting IRQ %d\n", + hw->card_name, __FUNCTION__, hw->irq); + + spin_lock_init(&hw->lock); + tasklet_init(&hw->tasklet, xhfc_bh_handler, (unsigned long) hw); + + /* search previous instances to index protocol[] array */ + list_for_each_entry(previous_hw, &hw_mISDNObj.ilist, list) { + hw->param_idx += previous_hw->num_ports; + } + + spin_lock_irqsave(&hw_mISDNObj.lock, flags); + /* add this instance to hardware list */ + list_add_tail(&hw->list, &hw_mISDNObj.ilist); + spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); + + /* init interrupt engine */ + hw->testirq = 1; + if (request_irq(hw->irq, xhfc_interrupt, SA_SHIRQ, "XHFC", hw)) { + printk(KERN_WARNING "%s %s: couldn't get interrupt %d\n", + hw->card_name, __FUNCTION__, hw->irq); + hw->irq = 0; + err = -EIO; + goto out; + } + + err = init_xhfc(hw); + if (err) + goto out; + + parse_module_params(hw); + + /* init line interfaces (ports) */ + for (pt = 0; pt < hw->num_ports; pt++) { + init_su(hw, pt); + } + + /* register all channels at ISDN procol stack */ + err = init_mISDN_channels(hw); + if (err) + goto out; + + /* delay some time */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ + + enable_interrupts(hw); + + /* force initial layer1 statechanges */ + hw->su_irq.reg = hw->su_irqmsk.reg; + + + /* init B cbannel loops if desired */ + for (pt = 0; pt < hw->num_ports; pt++) { + if (hw->port[pt].mode & PORT_MODE_LOOP) { + if (debug & DEBUG_HFC_INIT) + printk(KERN_INFO "%s %s init B-channel loop in port(%i)\n", + hw->card_name, __FUNCTION__, pt); + + xhfc_ph_command(&hw->chan[(pt << 2) + 2].ch, HFC_L1_TESTLOOP_B1); + xhfc_ph_command(&hw->chan[(pt << 2) + 2].ch, HFC_L1_TESTLOOP_B2); + } + } + + return (0); + + out: + return (err); +} + +/************************/ +/* release single card */ +/************************/ +static void +release_card(xhfc_hw * hw) +{ + u_long flags; + + disable_interrupts(hw); + free_irq(hw->irq, hw); + + /* wait for pending tasklet to finish */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ + + spin_lock_irqsave(&hw_mISDNObj.lock, flags); + release_channels(hw); + list_del(&hw->list); + spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); + + kfree(hw); +} + +#if BRIDGE == BRIDGE_PCI2PI + +/*****************************************/ +/* PCI hotplug interface: probe new card */ +/*****************************************/ +static int __devinit +xhfc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + xhfc_param *driver_data = (xhfc_param *) ent->driver_data; + xhfc_hw *hw; + + int err = -ENOMEM; + + + if (!(hw = kmalloc(sizeof(xhfc_hw), GFP_ATOMIC))) { + printk(KERN_ERR "%s %s: No kmem for XHFC card\n", + hw->card_name, __FUNCTION__); + return (err); + } + memset(hw, 0, sizeof(xhfc_hw)); + + hw->pdev = pdev; + err = pci_enable_device(pdev); + + if (err) + goto out; + + hw->cardnum = card_cnt; + sprintf(hw->card_name, "%s_%d", DRIVER_NAME, hw->cardnum); + printk(KERN_INFO "%s %s: adapter '%s' found on PCI bus %02x dev %02x\n", + hw->card_name, __FUNCTION__, driver_data->device_name, + pdev->bus->number, pdev->devfn); + + hw->driver_data = *driver_data; + hw->irq = pdev->irq; + + hw->hw_membase = (u_char *) pci_resource_start(pdev, 1); + hw->membase = ioremap((ulong) hw->hw_membase, 4096); + + pci_set_drvdata(pdev, hw); + err = setup_instance(hw); + if (!err) { + card_cnt++; + return (0); + } else { + goto out; + } + + out: + kfree(hw); + return (err); +}; + +/**************************************/ +/* PCI hotplug interface: remove card */ +/**************************************/ +static void __devexit +xhfc_pci_remove(struct pci_dev *pdev) +{ + xhfc_hw *hw = pci_get_drvdata(pdev); + printk(KERN_INFO "%s %s: removing card\n", hw->card_name, + __FUNCTION__); + release_card(hw); + card_cnt--; + pci_disable_device(pdev); + return; +}; + + +static struct pci_device_id xhfc_ids[] = { + {.vendor = PCI_VENDOR_ID_CCD, + .device = 0xA003, + .subvendor = 0x1397, + .subdevice = 0xA003, + .driver_data = + (unsigned long) &((xhfc_param) {CHIP_ID_4SU, "XHFC Evaluation Board"}), + }, + {} +}; + +/***************/ +/* Module init */ +/***************/ +static struct pci_driver xhfc_driver = { + name:DRIVER_NAME, + probe:xhfc_pci_probe, + remove:__devexit_p(xhfc_pci_remove), + id_table:xhfc_ids, +}; + + +MODULE_DEVICE_TABLE(pci, xhfc_ids); + +#endif // BRIDGE_PCI2PI + +/***************/ +/* Module init */ +/***************/ +static int __init +xhfc_init(void) +{ + int err; + + printk(KERN_INFO "XHFC: %s driver Rev. %s (debug=%i)\n", + __FUNCTION__, mISDN_getrev(xhfc_rev), debug); + +#ifdef MODULE + hw_mISDNObj.owner = THIS_MODULE; +#endif + + INIT_LIST_HEAD(&hw_mISDNObj.ilist); + spin_lock_init(&hw_mISDNObj.lock); + hw_mISDNObj.name = DRIVER_NAME; + hw_mISDNObj.own_ctrl = xhfc_manager; + + hw_mISDNObj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0; + hw_mISDNObj.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0 | ISDN_PID_L1_NT_S0; + hw_mISDNObj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; + hw_mISDNObj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; + card_cnt = 0; + + if ((err = mISDN_register(&hw_mISDNObj))) { + printk(KERN_ERR "XHFC: can't register xhfc error(%d)\n", + err); + goto out; + } + +#if BRIDGE == BRIDGE_PCI2PI + err = pci_register_driver(&xhfc_driver); + if (err < 0) { + goto out; + } +#endif + + printk(KERN_INFO "XHFC: %d cards installed\n", card_cnt); + +#if !defined(CONFIG_HOTPLUG) + if (err == 0) { + err = -ENODEV; + pci_unregister_driver(&xhfc_driver); + goto out; + } +#endif + + return 0; + + out: + return (err); +} + +static void __exit +xhfc_cleanup(void) +{ + int err; + +#if BRIDGE == BRIDGE_PCI2PI + pci_unregister_driver(&xhfc_driver); +#endif + + if ((err = mISDN_unregister(&hw_mISDNObj))) { + printk(KERN_ERR "XHFC: can't unregister xhfc, error(%d)\n", + err); + } + printk(KERN_INFO "%s: driver removed\n", __FUNCTION__); +} + +module_init(xhfc_init); +module_exit(xhfc_cleanup); diff --git a/drivers/isdn/hardware/mISDN/xhfc_su.h b/drivers/isdn/hardware/mISDN/xhfc_su.h new file mode 100644 index 0000000..4657b7f --- /dev/null +++ b/drivers/isdn/hardware/mISDN/xhfc_su.h @@ -0,0 +1,211 @@ +/* $Id$ + * + * mISDN driver for Colognechip xHFC chip + * + * Authors : Martin Bachem, Joerg Ciesielski + * Contact : info@colognechip.com + * + * 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. + * + */ + +#ifndef _XHFC_SU_H_ +#define _XHFC_SU_H_ + +#include +#include "channel.h" +#include "xhfc24succ.h" + +#define DRIVER_NAME "XHFC" + +#ifndef CHIP_ID_2S4U +#define CHIP_ID_2S4U 0x62 +#endif +#ifndef CHIP_ID_4SU +#define CHIP_ID_4SU 0x63 +#endif +#ifndef CHIP_ID_1SU +#define CHIP_ID_1SU 0x60 +#endif +#ifndef CHIP_ID_2SU +#define CHIP_ID_2SU 0x61 +#endif + + +/* define bridge for chip register access */ +#define BRIDGE_UNKWOWN 0 +#define BRIDGE_PCI2PI 1 /* used at Cologne Chip AG's Evaluation Card */ +#define BRIDGE BRIDGE_PCI2PI + + +#define MAX_PORT 4 +#define CHAN_PER_PORT 4 /* D, B1, B2, PCM */ +#define MAX_CHAN MAX_PORT * CHAN_PER_PORT + +/* flags in _u16 port mode */ +#define PORT_UNUSED 0x0000 +#define PORT_MODE_NT 0x0001 +#define PORT_MODE_TE 0x0002 +#define PORT_MODE_S0 0x0004 +#define PORT_MODE_UP 0x0008 +#define PORT_MODE_EXCH_POL 0x0010 +#define PORT_MODE_LOOP 0x0020 +#define NT_TIMER 0x8000 + + +/* NT / TE defines */ +#define NT_T1_COUNT 25 /* number of 4ms interrupts for G2 timeout */ +#define CLK_DLY_TE 0x0e /* CLKDEL in TE mode */ +#define CLK_DLY_NT 0x6c /* CLKDEL in NT mode */ +#define STA_ACTIVATE 0x60 /* start activation in A_SU_WR_STA */ +#define STA_DEACTIVATE 0x40 /* start deactivation in A_SU_WR_STA */ +#define LIF_MODE_NT 0x04 /* Line Interface NT mode */ +#define XHFC_TIMER_T3 8000 /* 8s activation timer T3 */ +#define XHFC_TIMER_T4 500 /* 500ms deactivation timer T4 */ + +/* xhfc Layer1 physical commands */ +#define HFC_L1_ACTIVATE_TE 0x01 +#define HFC_L1_FORCE_DEACTIVATE_TE 0x02 +#define HFC_L1_ACTIVATE_NT 0x03 +#define HFC_L1_DEACTIVATE_NT 0x04 +#define HFC_L1_TESTLOOP_B1 0x05 +#define HFC_L1_TESTLOOP_B2 0x06 + +/* xhfc Layer1 Flags (stored in xhfc_port_t->l1_flags) */ +#define HFC_L1_ACTIVATING 1 +#define HFC_L1_ACTIVATED 2 +#define HFC_L1_DEACTTIMER 4 +#define HFC_L1_ACTTIMER 8 + +#define FIFO_MASK_TX 0x55555555 +#define FIFO_MASK_RX 0xAAAAAAAA + + +/* DEBUG flags, use combined value for module parameter debug=x */ +#define DEBUG_HFC_INIT 0x0001 +#define DEBUG_HFC_MODE 0x0002 +#define DEBUG_HFC_S0_STATES 0x0004 +#define DEBUG_HFC_IRQ 0x0008 +#define DEBUG_HFC_FIFO_ERR 0x0010 +#define DEBUG_HFC_DTRACE 0x2000 +#define DEBUG_HFC_BTRACE 0x4000 /* very(!) heavy messageslog load */ +#define DEBUG_HFC_FIFO 0x8000 /* very(!) heavy messageslog load */ + +#define USE_F0_COUNTER 1 /* akkumulate F0 counter diff every irq */ +#define TRANSP_PACKET_SIZE 0 /* minium tranparent packet size for transmittion to upper layer */ + + +/* private driver_data */ +typedef struct { + int chip_id; + char *device_name; +} xhfc_param; + + +/* port struct for each S/U port */ +typedef struct { + int idx; + + __u8 dpid; /* DChannel Protocoll ID */ + __u16 mode; /* NT/TE + ST/U */ + int nt_timer; + + u_long l1_flags; + struct timer_list t3_timer; /* timer 3 for activation/deactivation */ + struct timer_list t4_timer; /* timer 4 for activation/deactivation */ + + /* chip registers */ + reg_a_su_ctrl0 su_ctrl0; + reg_a_su_ctrl1 su_ctrl1; + reg_a_su_ctrl2 su_ctrl2; + reg_a_st_ctrl3 st_ctrl3; +} xhfc_port_t; + + +/* channel struct for each fifo */ +typedef struct { + channel_t ch; + xhfc_port_t * port; +} xhfc_chan_t; + + +struct _xhfx_hw; + +/**********************/ +/* hardware structure */ +/**********************/ +typedef struct _xhfx_hw { + + struct list_head list; + spinlock_t lock; + struct tasklet_struct tasklet; /* interrupt bottom half */ + + int cardnum; + __u8 param_idx; /* used to access module param arrays */ + int ifnum; + __u8 testirq; + int irq; + int iobase; + int nt_mode; + u_char *membase; + u_char *hw_membase; + +#if BRIDGE == BRIDGE_PCI2PI + struct pci_dev *pdev; +#endif + + xhfc_param driver_data; + char card_name[60]; + + int chip_id; + int num_ports; /* number of S and U interfaces */ + int max_fifo; /* always 4 fifos per port */ + __u8 max_z; /* fifo depth -1 */ + + xhfc_port_t port[MAX_PORT]; /* one for each Line intercace */ + xhfc_chan_t chan[MAX_CHAN]; /* one each D/B/PCM channel */ + + __u32 irq_cnt; /* count irqs */ + __u32 f0_cnt; /* last F0 counter value */ + __u32 f0_akku; /* akkumulated f0 counter deltas */ + + /* chip registers */ + reg_r_irq_ctrl irq_ctrl; + reg_r_misc_irqmsk misc_irqmsk; /* mask of enabled interrupt sources */ + reg_r_misc_irq misc_irq; /* collect interrupt status bits */ + + reg_r_su_irqmsk su_irqmsk; /* mask of line interface state change interrupts */ + reg_r_su_irq su_irq; /* collect interrupt status bits */ + reg_r_ti_wd ti_wd; /* timer interval */ + + reg_r_pcm_md0 pcm_md0; + reg_r_pcm_md1 pcm_md1; + + __u32 fifo_irq; /* fifo bl irq */ + __u32 fifo_irqmsk; /* fifo bl irq */ + +} xhfc_hw; + + +/* function prototypes */ +int setup_channel(xhfc_hw * hw, __u8 channel, int protocol); +void xhfc_write_fifo(xhfc_hw * hw, __u8 channel); +void xhfc_read_fifo(xhfc_hw * hw, __u8 channel); +void print_fc(xhfc_hw * hw, __u8 fifo); +void setup_fifo(xhfc_hw * hw, __u8 fifo, __u8 conhdlc, __u8 subcfg, + __u8 fifoctrl, __u8 enable); +void setup_su(xhfc_hw * hw, __u8 pt, __u8 bc, __u8 enable); + +#endif /* _XHFC_SU_H_ */ diff --git a/misdn-init b/misdn-init new file mode 100755 index 0000000..3a7d757 --- /dev/null +++ b/misdn-init @@ -0,0 +1,544 @@ +#!/bin/bash + +################################################################################ +# +# misdn-init init script +# +# Copyright (C) 2005, Nadi Sarrar +# +# Nadi Sarrar +# +# This program is free software, distributed under the terms of +# the GNU General Public License +# + +# +# USAGE: +# +# /etc/init.d/misdn-init start|stop|restart|config|scan|help +# + +# +# CONFIGURATION: +# +# Path to your misdn-init.conf: +# +misdn_init_conf="/etc/misdn-init.conf" +# +################################################################################ + + +MODPROBE=modprobe +RMMOD=rmmod +INSMOD=insmod +LSPCI=lspci +MKNOD=mknod + +# HFC 8/4 (S0) Options +master_clock=16 + +# HFC-E1 Options +optical=16 +los=18 +ais=19 +slip=20 +nocrc4=24 + +# Card Settings +ulaw=8 +dtmf=9 +pcm_slave=11 + + +function remove_preloaded_modules { + tmp=$(lsmod) + cards="hfcmulti hfcpci avmfritz w6692 hfcusb" + + for i in $cards ; do + echo $tmp | grep $i >/dev/null && (echo "$i already loaded, removing it"; modprobe -r $i) + done +} + +function load_card_modules { + + IFS=$'\n' + + for line in $(sed -n -e '/^[^#]/p' ${misdn_init_conf}); + do + var=`echo "${line}" | sed -e "s/^\(.*\)=.*/\1/"` + val=`echo "${line}" | sed -e "s/^.*=\(.*\)/\1/"` + + case "${var}" in + card) + nr=`echo "${val}" | sed -e "s/^\([0-9]*\),.*/\1/"` + mod=`echo "${val}" | sed -e "s/^[^,]*,\([^,]*\).*/\1/"` + if [ ${#val} -gt $(echo "obase=10;${#nr}+${#mod}+1" | bc) ]; then + opns=`echo "${val}" | sed -e "s/^[^,]*,[^,]*,\(.*\)/\1/"` + else + opns="" + fi + case "${mod}" in + 0x*) + hfcmulti[${nr}]=$(echo ${mod} | sed -e "s/^0x\([0-9]*\)/\1/") + let "hfcports = ${hfcports} + ${hfcmulti[${nr}]}" + IFS=$',' + for li in ${opns}; do + hfcmulti[${nr}]=$(echo "obase=10;2^(${!li}-1)+${hfcmulti[${nr}]}" | bc) + done + IFS=$'\n' + ;; + *) + other_card[${nr}]=${mod} + ;; + esac + ;; + te_ptp) + IFS=$',' + for li in ${val}; do + layermask[${li}]="0xf" + protocol[${li}]=34 # 0x22 == 34 + done + IFS=$'\n' + ;; + te_ptmp) + IFS=$',' + for li in ${val}; do + layermask[${li}]="0xf" + protocol[${li}]=2 # 0x2 == 2 + done + IFS=$'\n' + ;; + nt_*) + IFS=$',' + for li in ${val}; do + layermask[${li}]="0x3" + protocol[${li}]=18 # 0x12 == 18 + done + IFS=$'\n' + ;; + te_capi_ptp) + IFS=$',' + for li in ${val}; do + layermask[${li}]="0x0" + protocol[${li}]=34 # 0x22 == 34 + done + IFS=$'\n' + + addcapi=1 + ;; + te_capi_ptmp) + IFS=$',' + for li in ${val}; do + layermask[${li}]="0x0" + protocol[${li}]=2 # 0x2 == 2 + done + IFS=$'\n' + addcapi=1 + ;; + + option) + port=`echo "${val}" | sed -e "s/^\([0-9]*\),.*/\1/"` + opt=`echo "${val}" | sed -e "s/^[0-9]*,\(.*\)/\1/"` + + if [ -z ${protocol[${port}]} ]; then + protocol[${port}]="0" + fi + + IFS=$',' + for li in ${opt}; do + protocol[${port}]=$(echo "obase=10;2^(${!li}-1)+${protocol[${port}]}" | bc) + done + IFS=$'\n' + ;; + poll) + poll=${val} + ;; + pcm) + pcm=${val} + ;; + debug) + debug=${val} + ;; + *) + echo "unknown variable: ${var}" + ;; + esac + + done + + echo "-----------------------------------------" + echo " Loading module(s) for your misdn-cards:" + echo "-----------------------------------------" + + card_index=1 + port_index=1 + while [ ! -z ${hfcmulti[${card_index}]} ] || [ ! -z ${other_card[${card_index}]} ]; + do + if [ ! -z ${hfcmulti[${card_index}]} ]; then + # MODPROBE COMMAND FOR hfcmulti CARD + hfcmulti_type="type=" + hfcmulti_prot="protocol=" + hfcmulti_layer="layermask=" + while [ ! -z ${hfcmulti[${card_index}]} ]; + do + hfcmulti_type="${hfcmulti_type}$(echo "obase=16;\"0x\";${hfcmulti[${card_index}]}" | bc )," + let "card_index = ${card_index} + 1" + done + while [ ${hfcports} -gt 0 ]; + do + if [ ! -z ${protocol[${port_index}]} ]; then + hfcmulti_prot="${hfcmulti_prot}$(echo "obase=16;\"0x\";${protocol[${port_index}]}" | bc)," + else + hfcmulti_prot="${hfcmulti_prot}0x2," + fi + if [ ! -z ${layermask[${port_index}]} ]; then + hfcmulti_layer="${hfcmulti_layer}${layermask[${port_index}]}," + else + hfcmulti_layer="${hfcmulti_layer}0xf," + fi + let "port_index = ${port_index} + 1" + let "hfcports = ${hfcports} - 1" + done + hfcmulti_type="$(echo ${hfcmulti_type} | sed -e 's/^\(.*\),$/\1/')" + hfcmulti_prot="$(echo ${hfcmulti_prot} | sed -e 's/^\(.*\),$/\1/')" + hfcmulti_layer="$(echo ${hfcmulti_layer} | sed -e 's/^\(.*\),$/\1/')" + hfcmulti_cmd="modprobe hfcmulti ${hfcmulti_type} ${hfcmulti_prot} ${hfcmulti_layer}" + if [ ! -z ${poll} ]; then + hfcmulti_cmd="${hfcmulti_cmd} poll=${poll}" + fi + if [ ! -z ${pcm} ]; then + hfcmulti_cmd="${hfcmulti_cmd} pcm=${pcm}" + fi + if [ ! -z ${debug} ]; then + hfcmulti_cmd="${hfcmulti_cmd} debug=${debug}" + fi + + echo ${hfcmulti_cmd} + eval ${hfcmulti_cmd} + else + # MODPROBE COMMAND FOR _NON_ hfcmulti CARD + other_cmd="modprobe ${other_card[${card_index}]}" + if [ ! -z ${protocol[${port_index}]} ]; then + other_prot="protocol=$(echo "obase=16;\"0x\";${protocol[${port_index}]}" | bc)," + else + other_prot="protocol=0x2," + fi + if [ ! -z ${layermask[${port_index}]} ]; then + other_layer="layermask=${layermask[${port_index}]}," + else + other_layer="layermask=0xf," + fi + + let "prev = ${card_index}" + let "card_index = ${card_index} + 1" + let "port_index = ${port_index} + 1" + while [ "${other_card[${card_index}]}" == "${other_card[${prev}]}" ]; + do + if [ ! -z ${protocol[${port_index}]} ]; then + other_prot="${other_prot}$(echo "obase=16;\"0x\";${protocol[${port_index}]}" | bc)," + else + other_prot="${other_prot}0x2," + fi + if [ ! -z ${layermask[${port_index}]} ]; then + other_layer="${other_layer}${layermask[${port_index}]}," + else + other_layer="${other_layer}0xf," + fi + let "prev = ${card_index}" + let "card_index = ${card_index} + 1" + let "port_index = ${port_index} + 1" + done + + other_prot="$(echo ${other_prot} | sed -e 's/^\(.*\),$/\1/')" + other_layer="$(echo ${other_layer} | sed -e 's/^\(.*\),$/\1/')" + other_cmd="${other_cmd} ${other_prot} ${other_layer}" + echo "${other_cmd}" + eval ${other_cmd} + fi + done +} + +function unload_card_modules { + + IFS=$'\n' + + for line in $(sed -ne '/^[^#]/p' ${misdn_init_conf}); + do + var=`echo "${line}" | sed -e "s/^\(.*\)=.*/\1/"` + val=`echo "${line}" | sed -e "s/^.*=\(.*\)/\1/"` + + case "${var}" in + card) + nr=`echo "${val}" | sed -e "s/^\([0-9]*\),.*/\1/"` + mod=`echo "${val}" | sed -e "s/^[^,]*,\([^,]*\).*/\1/"` + case "${mod}" in + 0x*) + modulelist[${nr}]=hfcmulti + ;; + *) + modulelist[${nr}]=${mod} + ;; + esac + ;; + esac + + done + + echo "-------------------------------------------" + echo " Unloading module(s) for your misdn-cards:" + echo "-------------------------------------------" + + rmmod_cmd="${RMMOD} ${modulelist[1]}" + echo "${rmmod_cmd}" + eval ${rmmod_cmd} + + index=2 + prev=1 + while [ ! -z ${modulelist[${index}]} ]; + do + if [ ${modulelist[${index}]} != ${modulelist[${prev}]} ]; then + rmmod_cmd="${RMMOD} ${modulelist[${index}]}" + echo "${rmmod_cmd}" + eval ${rmmod_cmd} + fi + let "prev = ${index}" + let "index = ${index} + 1" + done +} + +function create_misdn_init_conf { + + cardcount=1 + cardconf="" + IFS=$'\n' + NL=" +" + + function die { + echo "[!!] ${1}" + exit 1 + } + + function addcard { + cardline="${cardline}card=${cardcount},${1}${NL}" + let "cardcount = ${cardcount} + 1" + } + + function addport { + let "portcount = ${portcount} + ${1}" + } + + portcount=0 + + for line in $(${LSPCI} -n -d 1397:30b1); do + addcard "0x1" + addport 1 + done + for line in $(${LSPCI} -n -d 1397:08b4); do + addcard "0x4" + addport 4 + done + for line in $(${LSPCI} -n -d 1397:16b8); do + addcard "0x8" + addport 8 + done + for line in $(${LSPCI} -n | grep "1397:\(2bd\(0\|6\|7\|8\|9\|a\|b\|c\)\|b100\)\|1043:0675\|0871:ffa\(1\|2\)\|1051:0100\|15b0:2bd0\|114f:007\(0\|1\|2\|3\)\|13d1:2bd1\|182d:3069"); do + addcard "hfcpci" + addport 1 + done + for line in $(${LSPCI} -n -d 1244:0xa00); do + addcard "avmfritz" + addport 1 + done + for line in $(${LSPCI} -n -d 1050:6692); do + addcard "w6692pci" + addport 1 + done + if [ "${1}" == "scan" ]; then + echo "[OK] found the following devices:" + echo "${cardline}[ii] run \"/etc/init.d/misdn-init config\" to store this information to ${misdn_init_conf}" + else + + index=1 + portline="te_ptmp=" + while [ ${index} -le ${portcount} ]; do + portline="${portline}${index}," + let "index = ${index} + 1" + done + portline="$(echo ${portline} | sed -e 's/^\(.*\),$/\1/')" + + misdn_cfg_pt1="# +# Configuration file for your misdn hardware +# +# Usage: /etc/init.d/misdn-init start|stop|restart|config|scan|help +# + +# +# Card Settings +# +# Syntax: card=,[,