Compare commits
142 Commits
Author | SHA1 | Date |
---|---|---|
Andreas Eversberg | 13b0b86b8c | |
Martin Bachem | 8403b5d665 | |
Karsten Keil | 2b27730824 | |
Martin Bachem | 86c4c32518 | |
Martin Bachem | 76194ba679 | |
Karsten Keil | a081dd41b5 | |
Karsten Keil | 646c6aaec2 | |
Chrisian Richter | a8c8fb9d64 | |
Martin Bachem | 6541634bdf | |
Martin Bachem | 226ac84366 | |
Martin Bachem | 93c491eec5 | |
Chrisian Richter | 40c277506a | |
Chrisian Richter | 03e4ea51db | |
Martin Bachem | 95c37c4208 | |
Chrisian Richter | 9dd14368ee | |
Chrisian Richter | 824a3f4a5f | |
Chrisian Richter | a00448dea6 | |
Martin Bachem | 78322daa7d | |
Chrisian Richter | a8cac1057a | |
Chrisian Richter | 98e8418132 | |
Chrisian Richter | 5e8b1b43f3 | |
Chrisian Richter | 9ac71a30b3 | |
Chrisian Richter | eb8acfed40 | |
Martin Bachem | 44fc461e01 | |
Chrisian Richter | 60999c882e | |
Andreas Eversberg | fd576a94f5 | |
Andreas Eversberg | 7be8b664cc | |
Karsten Keil | f604d153eb | |
Andreas Eversberg | c3669c66a0 | |
Andreas Eversberg | 6cb9fd45ae | |
Andreas Eversberg | abf892687b | |
Andreas Eversberg | c3bed6b64f | |
Chrisian Richter | 62762480db | |
Karsten Keil | 66a95b62db | |
Karsten Keil | 0bee77c02b | |
Chrisian Richter | e769d4a829 | |
Martin Bachem | ae2adf6c7f | |
Martin Bachem | 2e887b0c04 | |
Chrisian Richter | e7facfbafe | |
Martin Bachem | 26f3a2b940 | |
Martin Bachem | 9d40b9fb64 | |
Martin Bachem | 69301b97d7 | |
Martin Bachem | b59714d7cd | |
Chrisian Richter | 330b1c3b02 | |
Chrisian Richter | 2a3309d053 | |
Chrisian Richter | 8828921356 | |
Chrisian Richter | 2dbbd53ef6 | |
Chrisian Richter | ce26b92218 | |
Chrisian Richter | c1b1125207 | |
Martin Bachem | 12c9aac89a | |
Martin Bachem | 23fb3808e3 | |
Martin Bachem | 14463646ba | |
srichter | 054549f836 | |
Karsten Keil | 32dd9af3eb | |
srichter | ab7fd00493 | |
Chrisian Richter | 591674cb3b | |
Andreas Eversberg | bbe04955ef | |
Martin Bachem | 5ccc714c19 | |
Chrisian Richter | 86d23dc746 | |
Karsten Keil | 60ba3b689f | |
Karsten Keil | 66319c89ef | |
Karsten Keil | 0913082112 | |
Martin Bachem | e1b97ac317 | |
Martin Bachem | aad4a831a4 | |
Martin Bachem | e4442d1014 | |
Chrisian Richter | 9e4f839496 | |
Andreas Eversberg | 0e241668db | |
Chrisian Richter | 4da89691bc | |
Chrisian Richter | ba58127def | |
Chrisian Richter | c3e596c162 | |
Chrisian Richter | 39c27195e1 | |
Karsten Keil | 526c241c06 | |
Andreas Eversberg | e906ebf312 | |
Andreas Eversberg | 94762d412a | |
Andreas Eversberg | 052818c38c | |
Chrisian Richter | 3a185417f1 | |
Chrisian Richter | 01d5d96214 | |
Karsten Keil | c2ba762cc6 | |
Karsten Keil | 1939d96c3e | |
Chrisian Richter | d298512a5a | |
Martin Bachem | b567a02093 | |
Martin Bachem | 0fb681d79b | |
Martin Bachem | 04974882dc | |
Karsten Keil | 0d53a2acb4 | |
Martin Bachem | c4a730317a | |
Martin Bachem | 1b126dbf2c | |
Martin Bachem | ec80024786 | |
Karsten Keil | 08c5463d36 | |
Karsten Keil | f5a08ff371 | |
Martin Bachem | a6debe87e6 | |
Karsten Keil | 98043b2034 | |
Karsten Keil | 9f59a14b76 | |
Karsten Keil | b645e6ca3d | |
Karsten Keil | 653d44dc82 | |
Karsten Keil | 8698d511b8 | |
Andreas Eversberg | 143679e28d | |
Karsten Keil | ff3e27b2bc | |
Karsten Keil | 9f4e63a6c0 | |
Karsten Keil | e872a653bd | |
Karsten Keil | ac4c62c371 | |
Karsten Keil | 36957da2e9 | |
Karsten Keil | bf14432f45 | |
Karsten Keil | 2605f160d3 | |
Karsten Keil | 69bba1de9b | |
Karsten Keil | eb973639fb | |
Andreas Eversberg | f85380c49c | |
Karsten Keil | aa7d9e187f | |
Karsten Keil | bd1c13ae2b | |
Karsten Keil | a3b6908c6f | |
Karsten Keil | a45ecad811 | |
Karsten Keil | 4ca4a72946 | |
Karsten Keil | 0a386d3456 | |
Karsten Keil | 45c35318ca | |
Karsten Keil | 375e643a39 | |
Andreas Eversberg | b914ab29eb | |
Karsten Keil | 82d37c7fc3 | |
Karsten Keil | 0cc92c0d61 | |
Andreas Eversberg | f285da122c | |
Andreas Eversberg | 7c4f82a743 | |
Andreas Eversberg | 276b42c675 | |
psprenger | ece4ff91c7 | |
psprenger | 68cb061bec | |
psprenger | 102f12b346 | |
psprenger | 1ea587a505 | |
Karsten Keil | 63f37c3939 | |
psprenger | eac2a36d7c | |
Karsten Keil | 73cce70fa6 | |
Karsten Keil | 26200fa375 | |
Karsten Keil | a1420e85b2 | |
Karsten Keil | 542f8588c4 | |
Karsten Keil | 2fb2b854ef | |
Karsten Keil | 822b8d28ee | |
Karsten Keil | 08ac4af99a | |
Karsten Keil | d46b857d67 | |
psprenger | 3f71c60ef2 | |
Karsten Keil | 865dfe93b2 | |
Karsten Keil | 9aa4b41d68 | |
Karsten Keil | 3f5a6d398f | |
Karsten Keil | b901a8dfcd | |
Karsten Keil | fbd7e82890 | |
Karsten Keil | 7ce8e6cc8c | |
Karsten Keil | 4cbd60f063 |
|
@ -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 <linux/mISDNif.h>" | gcc -C -E - 2>/dev/null 1>/dev/null ; then \
|
||||
if ! echo -ne "#include <linux/mISDNif.h>\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 {} \;
|
||||
|
|
@ -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
|
||||
|
|
@ -67,6 +67,18 @@ config MISDN_HFCUSB
|
|||
Enable support for USB ISDN TAs with Cologne Chip AG's
|
||||
HFC-S USB ISDN Controller
|
||||
|
||||
config MISDN_HFCMINI
|
||||
bool "Support for 'HFC-S mini' based TAs"
|
||||
depends on PCI
|
||||
help
|
||||
Enable support for Cologne Chip AG's 'HFC-S mini' Evaluation Card
|
||||
|
||||
config MISDN_XHFC
|
||||
bool "Support for XHFC based cards"
|
||||
depends on PCI
|
||||
help
|
||||
Enable support for Cologne Chips AG's XHFC Evaluation Card
|
||||
|
||||
config MISDN_SPEEDFAX
|
||||
bool "Support for Sedlbauer Speedfax+"
|
||||
depends on PCI || ISA
|
||||
|
@ -85,12 +97,39 @@ config MISDN_DSP
|
|||
Enable support for digital audio processing capability.
|
||||
This module may be used for special applications that require
|
||||
cross connecting of bchannels, conferencing, dtmf decoding
|
||||
tone generation, and Blowfish encryption and decryption.
|
||||
echo cancelation, tone generation, and Blowfish encryption and
|
||||
decryption.
|
||||
It may use hardware features if available.
|
||||
E.g. it is required for PBX4Linux. Go to http://isdn.jolly.de
|
||||
and get more informations about this module and it's usage.
|
||||
If unsure, say 'N'.
|
||||
|
||||
config MISDN_LOOP
|
||||
bool "Loop device"
|
||||
help
|
||||
Enable support for loop device.
|
||||
This module may be used for special applications that provide
|
||||
bchannel data from user space. Applications can directly
|
||||
access bchannels, so applications can be integrated into DSP
|
||||
audio processing.
|
||||
E.g. it is required for PBX4Linux. Go to http://isdn.jolly.de
|
||||
and get more informations about this module and it's usage.
|
||||
If unsure, say 'N'.
|
||||
|
||||
config MISDN_L1OIP
|
||||
bool "ISDN over IP tunnel"
|
||||
help
|
||||
Enable support for ISDN over IP tunnel.
|
||||
|
||||
It features:
|
||||
- layer 1 control via network keepalive frames
|
||||
- dynamic IP exchange, if one or both peers have dynamic IPs
|
||||
- channel bundeling for reduced IP overhead
|
||||
- BRI (S0) and PRI (S2M) interface
|
||||
|
||||
NOTE: This protocol is called 'Layer 1 over IP' and is not
|
||||
compatible with ISDNoIP (Agfeo) or TDMoIP.
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
# EXTRA_CFLAGS += -S -g
|
||||
#
|
||||
|
||||
ifdef MINCLUDES
|
||||
CFLAGS += -I$(MINCLUDES)
|
||||
endif
|
||||
ifdef CONFIG_MISDN_MEMDEBUG
|
||||
EXTRA_CFLAGS += -DMISDN_MEMDEBUG
|
||||
endif
|
||||
|
@ -28,8 +31,13 @@ ifdef CONFIG_MISDN_HFCUSB
|
|||
obj-$(CONFIG_MISDN_DRV) += hfcsusb.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_HFCMINI
|
||||
obj-$(CONFIG_MISDN_DRV) += hfcsmini.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_SPEEDFAX
|
||||
obj-$(CONFIG_MISDN_DRV) += sedlfax.o
|
||||
# obj-$(CONFIG_MISDN_DRV) += faxl3.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_W6692
|
||||
|
@ -40,10 +48,18 @@ ifdef CONFIG_MISDN_HFCMULTI
|
|||
obj-$(CONFIG_MISDN_DRV) += hfcmulti.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_XHFC
|
||||
obj-$(CONFIG_MISDN_DRV) += xhfc.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_DSP
|
||||
obj-$(CONFIG_MISDN_DRV) += mISDN_dsp.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_LOOP
|
||||
obj-$(CONFIG_MISDN_DRV) += mISDN_loop.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_I4L_CAPI_LAYER
|
||||
obj-$(CONFIG_MISDN_DRV) += I4LmISDN.o
|
||||
endif
|
||||
|
@ -54,11 +70,14 @@ sedlfax-objs := sedl_fax.o isar.o
|
|||
avmfritz-objs := avm_fritz.o
|
||||
hfcpci-objs := hfc_pci.o
|
||||
hfcsusb-objs := hfcs_usb.o
|
||||
hfcsmini-objs := hfcs_mini.o
|
||||
w6692pci-objs := w6692.o
|
||||
hfcmulti-objs := hfc_multi.o
|
||||
xhfc-objs := xhfc_su.o xhfc_pci2pi.o
|
||||
mISDN_isac-objs := isac.o arcofi.o
|
||||
mISDN_core-objs := core.o stack.o udevice.o helper.o debug.o fsm.o \
|
||||
dchannel.o bchannel.o l3helper.o
|
||||
channel.o l3helper.o \
|
||||
sysfs_obj.o sysfs_inst.o sysfs_st.o
|
||||
ifdef CONFIG_MISDN_MEMDEBUG
|
||||
mISDN_core-objs += memdbg.o
|
||||
endif
|
||||
|
@ -70,8 +89,9 @@ mISDN_capi-objs := capi.o contr.o listen.o appl.o plci.o app_plci.o ncci.o asn1.
|
|||
asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \
|
||||
supp_serv.o
|
||||
mISDN_dtmf-objs := dtmf.o
|
||||
mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o
|
||||
mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o
|
||||
mISDN_loop-objs := loop.o
|
||||
mISDN_x25dte-objs := x25_dte.o x25_l3.o
|
||||
I4LmISDN-objs := i4l_mISDN.o
|
||||
|
||||
include Rules.mISDN
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
# EXTRA_CFLAGS += -S -g
|
||||
#
|
||||
|
||||
ifdef MINCLUDES
|
||||
CFLAGS += -I$(MINCLUDES)
|
||||
endif
|
||||
ifdef CONFIG_MISDN_MEMDEBUG
|
||||
EXTRA_CFLAGS += -DMISDN_MEMDEBUG
|
||||
endif
|
||||
|
@ -28,9 +31,13 @@ ifdef CONFIG_MISDN_HFCUSB
|
|||
obj-$(CONFIG_MISDN_DRV) += hfcsusb.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_HFCMINI
|
||||
obj-$(CONFIG_MISDN_DRV) += hfcsmini.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_SPEEDFAX
|
||||
obj-$(CONFIG_MISDN_DRV) += sedlfax.o
|
||||
obj-$(CONFIG_MISDN_DRV) += faxl3.o
|
||||
# obj-$(CONFIG_MISDN_DRV) += faxl3.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_W6692
|
||||
|
@ -41,10 +48,18 @@ ifdef CONFIG_MISDN_HFCMULTI
|
|||
obj-$(CONFIG_MISDN_DRV) += hfcmulti.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_XHFC
|
||||
obj-$(CONFIG_MISDN_DRV) += xhfc.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_DSP
|
||||
obj-$(CONFIG_MISDN_DRV) += mISDN_dsp.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MISDN_LOOP
|
||||
obj-$(CONFIG_MISDN_DRV) += mISDN_loop.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_I4L_CAPI_LAYER
|
||||
obj-$(CONFIG_MISDN_DRV) += I4LmISDN.o
|
||||
endif
|
||||
|
@ -55,11 +70,14 @@ sedlfax-objs := sedl_fax.o isar.o
|
|||
avmfritz-objs := avm_fritz.o
|
||||
hfcpci-objs := hfc_pci.o
|
||||
hfcsusb-objs := hfcs_usb.o
|
||||
hfcsmini-objs := hfcs_mini.o
|
||||
w6692pci-objs := w6692.o
|
||||
hfcmulti-objs := hfc_multi.o
|
||||
xhfc-objs := xhfc_su.o xhfc_pci2pi.o
|
||||
mISDN_isac-objs := isac.o arcofi.o
|
||||
mISDN_core-objs := core.o stack.o udevice.o helper.o debug.o fsm.o \
|
||||
dchannel.o bchannel.o l3helper.o
|
||||
channel.o l3helper.o \
|
||||
sysfs_obj.o sysfs_inst.o sysfs_st.o
|
||||
ifdef CONFIG_MISDN_MEMDEBUG
|
||||
mISDN_core-objs += memdbg.o
|
||||
endif
|
||||
|
@ -71,6 +89,9 @@ mISDN_capi-objs := capi.o contr.o listen.o appl.o plci.o app_plci.o ncci.o asn1.
|
|||
asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \
|
||||
supp_serv.o
|
||||
mISDN_dtmf-objs := dtmf.o
|
||||
mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o
|
||||
mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o
|
||||
mISDN_loop-objs := loop.o
|
||||
mISDN_x25dte-objs := x25_dte.o x25_l3.o
|
||||
I4LmISDN-objs := i4l_mISDN.o
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -161,37 +161,14 @@ FacilityReq(Application_t *appl, struct sk_buff *skb)
|
|||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
__u16
|
||||
void
|
||||
ApplicationSendMessage(Application_t *appl, struct sk_buff *skb)
|
||||
{
|
||||
Plci_t *plci;
|
||||
AppPlci_t *aplci;
|
||||
Ncci_t *ncci;
|
||||
__u16 ret = CAPI_NOERROR;
|
||||
__u16 ret;
|
||||
|
||||
switch (CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data))) {
|
||||
// for NCCI state machine
|
||||
case CAPI_DATA_B3_REQ:
|
||||
case CAPI_DATA_B3_RESP:
|
||||
case CAPI_CONNECT_B3_RESP:
|
||||
case CAPI_CONNECT_B3_ACTIVE_RESP:
|
||||
case CAPI_DISCONNECT_B3_REQ:
|
||||
case CAPI_DISCONNECT_B3_RESP:
|
||||
case CAPI_RESET_B3_REQ:
|
||||
case CAPI_RESET_B3_RESP:
|
||||
aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
|
||||
if (!aplci) {
|
||||
AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
|
||||
goto free;
|
||||
}
|
||||
ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_EXACT);
|
||||
if (!ncci) {
|
||||
int_error();
|
||||
AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
|
||||
goto free;
|
||||
}
|
||||
ret = ncciSendMessage(ncci, skb);
|
||||
break;
|
||||
// new NCCI
|
||||
case CAPI_CONNECT_B3_REQ:
|
||||
aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
|
||||
|
@ -201,6 +178,15 @@ ApplicationSendMessage(Application_t *appl, struct sk_buff *skb)
|
|||
}
|
||||
ConnectB3Request(aplci, skb);
|
||||
break;
|
||||
// maybe already down NCCI
|
||||
case CAPI_DISCONNECT_B3_RESP:
|
||||
aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
|
||||
if (!aplci) {
|
||||
AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
|
||||
goto free;
|
||||
}
|
||||
DisconnectB3Request(aplci, skb);
|
||||
break;
|
||||
// for PLCI state machine
|
||||
case CAPI_INFO_REQ:
|
||||
case CAPI_ALERT_REQ:
|
||||
|
@ -215,6 +201,9 @@ ApplicationSendMessage(Application_t *appl, struct sk_buff *skb)
|
|||
goto free;
|
||||
}
|
||||
ret = AppPlciSendMessage(aplci, skb);
|
||||
if (ret) {
|
||||
int_error();
|
||||
}
|
||||
break;
|
||||
case CAPI_CONNECT_REQ:
|
||||
if (ControllerNewPlci(appl->contr, &plci, MISDN_ID_ANY)) {
|
||||
|
@ -227,11 +216,17 @@ ApplicationSendMessage(Application_t *appl, struct sk_buff *skb)
|
|||
goto free;
|
||||
}
|
||||
ret = AppPlciSendMessage(aplci, skb);
|
||||
if (ret) {
|
||||
int_error();
|
||||
}
|
||||
break;
|
||||
|
||||
// for LISTEN state machine
|
||||
case CAPI_LISTEN_REQ:
|
||||
ret = listenSendMessage(appl, skb);
|
||||
if (ret) {
|
||||
int_error();
|
||||
}
|
||||
break;
|
||||
|
||||
// other
|
||||
|
@ -248,13 +243,12 @@ ApplicationSendMessage(Application_t *appl, struct sk_buff *skb)
|
|||
default:
|
||||
applDebug(appl, CAPI_DBG_WARN, "applSendMessage: %#x %#x not handled!",
|
||||
CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
|
||||
ret = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
|
||||
break;
|
||||
}
|
||||
return(ret);
|
||||
return;
|
||||
free:
|
||||
dev_kfree_skb(skb);
|
||||
return(ret);
|
||||
return;
|
||||
}
|
||||
|
||||
AppPlci_t *
|
||||
|
|
|
@ -8,19 +8,20 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "dchannel.h"
|
||||
#include "channel.h"
|
||||
#include "layer1.h"
|
||||
#include "isac.h"
|
||||
#include "arcofi.h"
|
||||
#include "debug.h"
|
||||
#include "helper.h"
|
||||
|
||||
#define ARCOFI_TIMER_VALUE 20
|
||||
|
||||
static void
|
||||
add_arcofi_timer(dchannel_t *dch) {
|
||||
add_arcofi_timer(channel_t *dch) {
|
||||
isac_chip_t *isac = dch->hw;
|
||||
|
||||
if (test_and_set_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) {
|
||||
if (test_and_set_bit(FLG_ARCOFI_TIMER, &dch->Flags)) {
|
||||
del_timer(&isac->arcofitimer);
|
||||
}
|
||||
init_timer(&isac->arcofitimer);
|
||||
|
@ -29,7 +30,7 @@ add_arcofi_timer(dchannel_t *dch) {
|
|||
}
|
||||
|
||||
static void
|
||||
send_arcofi(dchannel_t *dch) {
|
||||
send_arcofi(channel_t *dch) {
|
||||
u_char val;
|
||||
isac_chip_t *isac = dch->hw;
|
||||
|
||||
|
@ -45,15 +46,15 @@ send_arcofi(dchannel_t *dch) {
|
|||
}
|
||||
isac->mocr &= 0x0f;
|
||||
isac->mocr |= 0xa0;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
val = dch->read_reg(dch->inst.data, ISAC_MOSR);
|
||||
dch->write_reg(dch->inst.data, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_MOSR);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);
|
||||
isac->mocr |= 0x10;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
}
|
||||
|
||||
int
|
||||
arcofi_fsm(dchannel_t *dch, int event, void *data) {
|
||||
arcofi_fsm(channel_t *dch, int event, void *data) {
|
||||
isac_chip_t *isac = dch->hw;
|
||||
|
||||
if (dch->debug & L1_DEB_MONITOR) {
|
||||
|
@ -61,7 +62,7 @@ arcofi_fsm(dchannel_t *dch, int event, void *data) {
|
|||
}
|
||||
if (event == ARCOFI_TIMEOUT) {
|
||||
isac->arcofi_state = ARCOFI_NOP;
|
||||
test_and_set_bit(FLG_ARCOFI_ERROR, &dch->DFlags);
|
||||
test_and_set_bit(FLG_ARCOFI_ERROR, &dch->Flags);
|
||||
wake_up(&isac->arcofi_wait);
|
||||
return(1);
|
||||
}
|
||||
|
@ -84,7 +85,7 @@ arcofi_fsm(dchannel_t *dch, int event, void *data) {
|
|||
isac->arcofi_list->next;
|
||||
send_arcofi(dch);
|
||||
} else {
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) {
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->Flags)) {
|
||||
del_timer(&isac->arcofitimer);
|
||||
}
|
||||
isac->arcofi_state = ARCOFI_NOP;
|
||||
|
@ -95,13 +96,20 @@ arcofi_fsm(dchannel_t *dch, int event, void *data) {
|
|||
break;
|
||||
case ARCOFI_RECEIVE:
|
||||
if (event == ARCOFI_RX_END) {
|
||||
struct sk_buff *skb = data;
|
||||
|
||||
// FIXME handle message
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
else
|
||||
int_error();
|
||||
if (isac->arcofi_list->next) {
|
||||
isac->arcofi_list =
|
||||
isac->arcofi_list->next;
|
||||
isac->arcofi_state = ARCOFI_TRANSMIT;
|
||||
send_arcofi(dch);
|
||||
} else {
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) {
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->Flags)) {
|
||||
del_timer(&isac->arcofitimer);
|
||||
}
|
||||
isac->arcofi_state = ARCOFI_NOP;
|
||||
|
@ -117,21 +125,21 @@ arcofi_fsm(dchannel_t *dch, int event, void *data) {
|
|||
}
|
||||
|
||||
static void
|
||||
arcofi_timer(dchannel_t *dch) {
|
||||
arcofi_timer(channel_t *dch) {
|
||||
arcofi_fsm(dch, ARCOFI_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
clear_arcofi(dchannel_t *dch) {
|
||||
clear_arcofi(channel_t *dch) {
|
||||
isac_chip_t *isac = dch->hw;
|
||||
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) {
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->Flags)) {
|
||||
del_timer(&isac->arcofitimer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
init_arcofi(dchannel_t *dch) {
|
||||
init_arcofi(channel_t *dch) {
|
||||
isac_chip_t *isac = dch->hw;
|
||||
|
||||
isac->arcofitimer.function = (void *) arcofi_timer;
|
||||
|
|
|
@ -27,6 +27,6 @@ struct arcofi_msg {
|
|||
u_char msg[10];
|
||||
};
|
||||
|
||||
extern int arcofi_fsm(dchannel_t *, int, void *);
|
||||
extern void init_arcofi(dchannel_t *);
|
||||
extern void clear_arcofi(dchannel_t *);
|
||||
extern int arcofi_fsm(channel_t *, int, void *);
|
||||
extern void init_arcofi(channel_t *);
|
||||
extern void clear_arcofi(channel_t *);
|
||||
|
|
|
@ -15,6 +15,13 @@ typedef enum {
|
|||
reject = 4,
|
||||
} asn1Component;
|
||||
|
||||
typedef enum {
|
||||
GeneralP = 0,
|
||||
InvokeP = 1,
|
||||
ReturnResultP= 2,
|
||||
ReturnErrorP = 3,
|
||||
} asn1Problem;
|
||||
|
||||
struct PublicPartyNumber {
|
||||
int publicTypeOfNumber;
|
||||
char numberDigits[30];
|
||||
|
@ -88,20 +95,28 @@ struct asn1ReturnError {
|
|||
__u16 errorValue;
|
||||
};
|
||||
|
||||
struct asn1Reject {
|
||||
int invokeId;
|
||||
asn1Problem problem;
|
||||
__u16 problemValue;
|
||||
};
|
||||
|
||||
struct asn1_parm {
|
||||
asn1Component comp;
|
||||
union {
|
||||
struct asn1Invoke inv;
|
||||
struct asn1ReturnResult retResult;
|
||||
struct asn1ReturnError retError;
|
||||
struct asn1Reject reject;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
#undef ASN1_DEBUG
|
||||
// #define ASN1_DEBUG
|
||||
|
||||
#ifdef ASN1_DEBUG
|
||||
#define print_asn1msg(dummy, fmt, args...) printk(fmt, ## args)
|
||||
#define print_asn1msg(dummy, fmt, args...) printk(KERN_DEBUG fmt, ## args)
|
||||
#else
|
||||
#define print_asn1msg(dummy, fmt, args...)
|
||||
#endif
|
||||
|
@ -194,7 +209,7 @@ int ParseLen(u_char *p, u_char *end, int *len);
|
|||
} else { \
|
||||
if (!(the_tag) & ASN1_TAG_OPT) { \
|
||||
print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 4 %s:%d\n", __FUNCTION__, __LINE__); \
|
||||
return -1; \
|
||||
return -1; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
|
|
@ -137,6 +137,68 @@ ParseReturnErrorComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dumm
|
|||
return p - beg;
|
||||
}
|
||||
|
||||
int
|
||||
ParseProblemValue(struct asn1_parm *pc, u_char *p, u_char *end, asn1Problem prob)
|
||||
{
|
||||
INIT;
|
||||
|
||||
pc->u.reject.problem = prob;
|
||||
|
||||
print_asn1msg(PRT_DEBUG_DECODE, "ParseProblemValue: %d %d\n", prob, *p);
|
||||
pc->u.reject.problemValue = *p++;
|
||||
return p - beg;
|
||||
}
|
||||
|
||||
int
|
||||
ParseRejectProblem(struct asn1_parm *pc, u_char *p, u_char *end)
|
||||
{
|
||||
INIT;
|
||||
|
||||
XCHOICE_1(ParseProblemValue, ASN1_TAG_CONTEXT_SPECIFIC, 0, GeneralP);
|
||||
XCHOICE_1(ParseProblemValue, ASN1_TAG_CONTEXT_SPECIFIC, 1, InvokeP);
|
||||
XCHOICE_1(ParseProblemValue, ASN1_TAG_CONTEXT_SPECIFIC, 2, ReturnResultP);
|
||||
XCHOICE_1(ParseProblemValue, ASN1_TAG_CONTEXT_SPECIFIC, 3, ReturnErrorP);
|
||||
XCHOICE_DEFAULT;
|
||||
}
|
||||
|
||||
int
|
||||
ParseRejectComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
|
||||
{
|
||||
int invokeId = -1;
|
||||
int rval;
|
||||
INIT;
|
||||
|
||||
pc->comp = reject;
|
||||
|
||||
XSEQUENCE_OPT_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId);
|
||||
XSEQUENCE_OPT(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED);
|
||||
|
||||
print_asn1msg(PRT_DEBUG_DECODE, "ParseRejectComponent: invokeId %d\n", invokeId);
|
||||
|
||||
pc->u.reject.invokeId = invokeId;
|
||||
|
||||
rval = ParseRejectProblem(pc, p, end);
|
||||
|
||||
print_asn1msg(PRT_DEBUG_DECODE, "ParseRejectComponent: rval %d\n", rval);
|
||||
|
||||
if (rval > 0)
|
||||
p += rval;
|
||||
else
|
||||
return(-1);
|
||||
|
||||
return p - beg;
|
||||
}
|
||||
|
||||
int
|
||||
ParseUnknownComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
|
||||
{
|
||||
int invokeId;
|
||||
INIT;
|
||||
|
||||
pc->comp = tag;
|
||||
return end - beg;
|
||||
}
|
||||
|
||||
int
|
||||
ParseComponent(struct asn1_parm *pc, u_char *p, u_char *end)
|
||||
{
|
||||
|
@ -145,7 +207,17 @@ ParseComponent(struct asn1_parm *pc, u_char *p, u_char *end)
|
|||
XCHOICE(ParseInvokeComponent, ASN1_TAG_SEQUENCE, 1);
|
||||
XCHOICE(ParseReturnResultComponent, ASN1_TAG_SEQUENCE, 2);
|
||||
XCHOICE(ParseReturnErrorComponent, ASN1_TAG_SEQUENCE, 3);
|
||||
// XCHOICE(ParseRejectComponent, ASN1_TAG_SEQUENCE, 4);
|
||||
XCHOICE(ParseRejectComponent, ASN1_TAG_SEQUENCE, 4);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 5);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 6);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 7);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 8);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 9);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 10);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 11);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 12);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 13);
|
||||
XCHOICE(ParseUnknownComponent, ASN1_TAG_SEQUENCE, 14);
|
||||
XCHOICE_DEFAULT;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,14 @@ int encodeNull(__u8 *dest)
|
|||
return 2;
|
||||
}
|
||||
|
||||
int encodeBoolean(__u8 *dest, __u32 i)
|
||||
{
|
||||
dest[0] = 0x01; // BOOLEAN
|
||||
dest[1] = 1; // length 1
|
||||
dest[3] = i ? 1:0; // Value
|
||||
return 3;
|
||||
}
|
||||
|
||||
int encodeInt(__u8 *dest, __u32 i)
|
||||
{
|
||||
__u8 *p;
|
||||
|
@ -176,3 +184,18 @@ int encodeInterrogationDiversion(__u8 *dest, struct FacReqCFInterrogateParameter
|
|||
return p - dest;
|
||||
}
|
||||
|
||||
int encodeInvokeDeflection(__u8 *dest, struct FacReqCDeflection *CD)
|
||||
{
|
||||
__u8 *p;
|
||||
|
||||
dest[0] = 0x30; // sequence
|
||||
dest[1] = 0; // length
|
||||
p = &dest[2];
|
||||
|
||||
p += encodeAddress(p, CD->DeflectedToNumber, CD->DeflectedToSubaddress);
|
||||
p += encodeBoolean(p, CD->PresentationAllowed);
|
||||
|
||||
dest[1] = p - &dest[2];
|
||||
return p - dest;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "asn1.h"
|
||||
|
||||
int encodeNull(__u8 *dest);
|
||||
int encodeBoolean(__u8 *dest, __u32 i);
|
||||
int encodeInt(__u8 *dest, __u32 i);
|
||||
int encodeEnum(__u8 *dest, __u32 i);
|
||||
int encodeNumberDigits(__u8 *dest, __u8 *nd, __u8 len);
|
||||
|
@ -15,3 +16,4 @@ int encodeAddress(__u8 *dest, __u8 *facilityPartyNumber, __u8 *calledPartySubadd
|
|||
int encodeActivationDiversion(__u8 *dest, struct FacReqCFActivate *CFActivate);
|
||||
int encodeDeactivationDiversion(__u8 *dest,struct FacReqCFDeactivate *CFDeactivate);
|
||||
int encodeInterrogationDiversion(__u8 *dest, struct FacReqCFInterrogateParameters *params);
|
||||
int encodeInvokeDeflection(__u8 *dest, struct FacReqCDeflection *CD);
|
||||
|
|
|
@ -17,16 +17,11 @@
|
|||
#include <linux/isapnp.h>
|
||||
#endif
|
||||
#include <linux/delay.h>
|
||||
#include "dchannel.h"
|
||||
#include "bchannel.h"
|
||||
#include "channel.h"
|
||||
#include "isac.h"
|
||||
#include "layer1.h"
|
||||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define SPIN_DEBUG
|
||||
#define LOCK_STATISTIC
|
||||
#include "hw_lock.h"
|
||||
|
||||
static const char *avm_fritz_rev = "$Revision$";
|
||||
|
||||
|
@ -131,34 +126,29 @@ typedef struct hdlc_hw {
|
|||
|
||||
typedef struct _fritzpnppci {
|
||||
struct list_head list;
|
||||
void *pdev;
|
||||
union {
|
||||
#if defined(CONFIG_PNP)
|
||||
#ifdef NEW_ISAPNP
|
||||
struct pnp_dev *pnp;
|
||||
#else
|
||||
struct pci_dev *pnp;
|
||||
#endif
|
||||
#endif
|
||||
struct pci_dev *pci;
|
||||
} dev;
|
||||
u_int type;
|
||||
u_int irq;
|
||||
u_int irqcnt;
|
||||
u_int addr;
|
||||
mISDN_HWlock_t lock;
|
||||
spinlock_t lock;
|
||||
isac_chip_t isac;
|
||||
hdlc_hw_t hdlc[2];
|
||||
dchannel_t dch;
|
||||
bchannel_t bch[2];
|
||||
channel_t dch;
|
||||
channel_t bch[2];
|
||||
u_char ctrlreg;
|
||||
} fritzpnppci;
|
||||
|
||||
|
||||
static int lock_dev(void *data, int nowait)
|
||||
{
|
||||
register mISDN_HWlock_t *lock = &((fritzpnppci *)data)->lock;
|
||||
|
||||
return(lock_HW(lock, nowait));
|
||||
}
|
||||
|
||||
static void unlock_dev(void *data)
|
||||
{
|
||||
register mISDN_HWlock_t *lock = &((fritzpnppci *)data)->lock;
|
||||
|
||||
unlock_HW(lock);
|
||||
}
|
||||
|
||||
/* Interface functions */
|
||||
|
||||
static u_char
|
||||
|
@ -244,11 +234,11 @@ fcpci2_write_isac_fifo(void *fc, unsigned char * data, int size)
|
|||
}
|
||||
|
||||
static inline
|
||||
bchannel_t *Sel_BCS(fritzpnppci *fc, int channel)
|
||||
channel_t *Sel_BCS(fritzpnppci *fc, int channel)
|
||||
{
|
||||
if (fc->bch[0].protocol && (fc->bch[0].channel == channel))
|
||||
if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && (fc->bch[0].channel == channel))
|
||||
return(&fc->bch[0]);
|
||||
else if (fc->bch[1].protocol && (fc->bch[1].channel == channel))
|
||||
else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && (fc->bch[1].channel == channel))
|
||||
return(&fc->bch[1]);
|
||||
else
|
||||
return(NULL);
|
||||
|
@ -281,8 +271,8 @@ __write_ctrl_pciv2(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel) {
|
|||
}
|
||||
|
||||
void
|
||||
write_ctrl(bchannel_t *bch, int which) {
|
||||
fritzpnppci *fc = bch->inst.data;
|
||||
write_ctrl(channel_t *bch, int which) {
|
||||
fritzpnppci *fc = bch->inst.privat;
|
||||
hdlc_hw_t *hdlc = bch->hw;
|
||||
|
||||
if (fc->dch.debug & L1_DEB_HSCX)
|
||||
|
@ -343,48 +333,64 @@ read_status(fritzpnppci *fc, int channel)
|
|||
return(0);
|
||||
}
|
||||
|
||||
static void
|
||||
enable_hwirq(fritzpnppci *fc)
|
||||
{
|
||||
fc->ctrlreg |= AVM_STATUS0_ENA_IRQ;
|
||||
outb(fc->ctrlreg, fc->addr + 2);
|
||||
}
|
||||
|
||||
static void
|
||||
disable_hwirq(fritzpnppci *fc)
|
||||
{
|
||||
fc->ctrlreg &= ~((u_char)AVM_STATUS0_ENA_IRQ);
|
||||
outb(fc->ctrlreg, fc->addr + 2);
|
||||
}
|
||||
|
||||
static int
|
||||
modehdlc(bchannel_t *bch, int bc, int protocol)
|
||||
modehdlc(channel_t *bch, int bc, int protocol)
|
||||
{
|
||||
hdlc_hw_t *hdlc = bch->hw;
|
||||
|
||||
if (bch->debug & L1_DEB_HSCX)
|
||||
mISDN_debugprint(&bch->inst, "hdlc %c protocol %x-->%x ch %d-->%d",
|
||||
'A' + bch->channel, bch->protocol, protocol, bch->channel, bc);
|
||||
'A' + bch->channel, bch->state, protocol, bch->channel, bc);
|
||||
if ((protocol != -1) && (bc != bch->channel))
|
||||
printk(KERN_WARNING "%s: fritzcard mismatch channel(%d/%d)\n", __FUNCTION__, bch->channel, bc);
|
||||
hdlc->ctrl.ctrl = 0;
|
||||
switch (protocol) {
|
||||
case (-1): /* used for init */
|
||||
bch->protocol = -1;
|
||||
bch->state = -1;
|
||||
bch->channel = bc;
|
||||
case (ISDN_PID_NONE):
|
||||
if (bch->protocol == ISDN_PID_NONE)
|
||||
if (bch->state == ISDN_PID_NONE)
|
||||
break;
|
||||
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
|
||||
hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
|
||||
write_ctrl(bch, 5);
|
||||
bch->protocol = ISDN_PID_NONE;
|
||||
bch->state = ISDN_PID_NONE;
|
||||
test_and_clear_bit(FLG_HDLC, &bch->Flags);
|
||||
test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
|
||||
break;
|
||||
case (ISDN_PID_L1_B_64TRANS):
|
||||
bch->protocol = protocol;
|
||||
bch->state = protocol;
|
||||
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
|
||||
hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
|
||||
write_ctrl(bch, 5);
|
||||
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
|
||||
write_ctrl(bch, 1);
|
||||
hdlc->ctrl.sr.cmd = 0;
|
||||
bch_sched_event(bch, B_XMTBUFREADY);
|
||||
test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
|
||||
break;
|
||||
case (ISDN_PID_L1_B_64HDLC):
|
||||
bch->protocol = protocol;
|
||||
bch->state = protocol;
|
||||
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
|
||||
hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
|
||||
write_ctrl(bch, 5);
|
||||
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
|
||||
write_ctrl(bch, 1);
|
||||
hdlc->ctrl.sr.cmd = 0;
|
||||
bch_sched_event(bch, B_XMTBUFREADY);
|
||||
test_and_set_bit(FLG_HDLC, &bch->Flags);
|
||||
break;
|
||||
default:
|
||||
mISDN_debugprint(&bch->inst, "prot not known %x", protocol);
|
||||
|
@ -394,24 +400,30 @@ modehdlc(bchannel_t *bch, int bc, int protocol)
|
|||
}
|
||||
|
||||
static void
|
||||
hdlc_empty_fifo(bchannel_t *bch, int count)
|
||||
hdlc_empty_fifo(channel_t *bch, int count)
|
||||
{
|
||||
register u_int *ptr;
|
||||
u_char *p;
|
||||
u_char idx = bch->channel ? AVM_HDLC_2 : AVM_HDLC_1;
|
||||
int cnt=0;
|
||||
fritzpnppci *fc = bch->inst.data;
|
||||
fritzpnppci *fc = bch->inst.privat;
|
||||
|
||||
if ((fc->dch.debug & L1_DEB_HSCX) && !(fc->dch.debug & L1_DEB_HSCX_FIFO))
|
||||
mISDN_debugprint(&bch->inst, "hdlc_empty_fifo %d", count);
|
||||
if (bch->rx_idx + count > MAX_DATA_MEM) {
|
||||
if (fc->dch.debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&bch->inst, "hdlc_empty_fifo: incoming packet too large");
|
||||
if (!bch->rx_skb) {
|
||||
if (!(bch->rx_skb = alloc_stack_skb(bch->maxlen, bch->up_headerlen))) {
|
||||
printk(KERN_WARNING "mISDN: B receive out of memory\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((bch->rx_skb->len + count) > bch->maxlen) {
|
||||
if (bch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&bch->inst, "hdlc_empty_fifo overrun %d",
|
||||
bch->rx_skb->len + count);
|
||||
return;
|
||||
}
|
||||
p = bch->rx_buf + bch->rx_idx;
|
||||
p = skb_put(bch->rx_skb, count);
|
||||
ptr = (u_int *)p;
|
||||
bch->rx_idx += count;
|
||||
if (fc->type == AVM_FRITZ_PCIV2) {
|
||||
while (cnt < count) {
|
||||
#ifdef __powerpc__
|
||||
|
@ -447,23 +459,23 @@ hdlc_empty_fifo(bchannel_t *bch, int count)
|
|||
}
|
||||
}
|
||||
if (fc->dch.debug & L1_DEB_HSCX_FIFO) {
|
||||
char *t = bch->blog;
|
||||
char *t = bch->log;
|
||||
|
||||
if (fc->type == AVM_FRITZ_PNP)
|
||||
p = (u_char *) ptr;
|
||||
t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
|
||||
bch->channel ? 'B' : 'A', count);
|
||||
mISDN_QuickHex(t, p, count);
|
||||
mISDN_debugprint(&bch->inst, bch->blog);
|
||||
mISDN_debugprint(&bch->inst, bch->log);
|
||||
}
|
||||
}
|
||||
|
||||
#define HDLC_FIFO_SIZE 32
|
||||
|
||||
static void
|
||||
hdlc_fill_fifo(bchannel_t *bch)
|
||||
hdlc_fill_fifo(channel_t *bch)
|
||||
{
|
||||
fritzpnppci *fc = bch->inst.data;
|
||||
fritzpnppci *fc = bch->inst.privat;
|
||||
hdlc_hw_t *hdlc = bch->hw;
|
||||
int count, cnt =0;
|
||||
u_char *p;
|
||||
|
@ -471,15 +483,17 @@ hdlc_fill_fifo(bchannel_t *bch)
|
|||
|
||||
if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
|
||||
mISDN_debugprint(&bch->inst, "%s", __FUNCTION__);
|
||||
count = bch->tx_len - bch->tx_idx;
|
||||
if (!bch->tx_skb)
|
||||
return;
|
||||
count = bch->tx_skb->len - bch->tx_idx;
|
||||
if (count <= 0)
|
||||
return;
|
||||
p = bch->tx_buf + bch->tx_idx;
|
||||
p = bch->tx_skb->data + bch->tx_idx;
|
||||
hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME;
|
||||
if (count > HDLC_FIFO_SIZE) {
|
||||
count = HDLC_FIFO_SIZE;
|
||||
} else {
|
||||
if (bch->protocol != ISDN_PID_L1_B_64TRANS)
|
||||
if (test_bit(FLG_HDLC, &bch->Flags))
|
||||
hdlc->ctrl.sr.cmd |= HDLC_CMD_XME;
|
||||
}
|
||||
if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
|
||||
|
@ -524,44 +538,49 @@ hdlc_fill_fifo(bchannel_t *bch)
|
|||
}
|
||||
}
|
||||
if (bch->debug & L1_DEB_HSCX_FIFO) {
|
||||
char *t = bch->blog;
|
||||
char *t = bch->log;
|
||||
|
||||
if (fc->type == AVM_FRITZ_PNP)
|
||||
p = (u_char *) ptr;
|
||||
t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
|
||||
bch->channel ? 'B' : 'A', count);
|
||||
mISDN_QuickHex(t, p, count);
|
||||
mISDN_debugprint(&bch->inst, bch->blog);
|
||||
mISDN_debugprint(&bch->inst, bch->log);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
HDLC_irq_xpr(bchannel_t *bch)
|
||||
HDLC_irq_xpr(channel_t *bch)
|
||||
{
|
||||
if (bch->tx_idx < bch->tx_len)
|
||||
if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
|
||||
hdlc_fill_fifo(bch);
|
||||
else {
|
||||
if (bch->tx_skb)
|
||||
dev_kfree_skb(bch->tx_skb);
|
||||
bch->tx_idx = 0;
|
||||
if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
|
||||
if (bch->next_skb) {
|
||||
bch->tx_len = bch->next_skb->len;
|
||||
memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len);
|
||||
if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
|
||||
bch->tx_skb = bch->next_skb;
|
||||
if (bch->tx_skb) {
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(bch->tx_skb);
|
||||
|
||||
bch->next_skb = NULL;
|
||||
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
|
||||
queue_ch_frame(bch, CONFIRM, hh->dinfo, NULL);
|
||||
hdlc_fill_fifo(bch);
|
||||
} else {
|
||||
bch->tx_len = 0;
|
||||
printk(KERN_WARNING "hdlc tx irq TX_NEXT without skb\n");
|
||||
test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
|
||||
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
|
||||
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
|
||||
}
|
||||
} else {
|
||||
bch->tx_len = 0;
|
||||
test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
|
||||
bch->tx_skb = NULL;
|
||||
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
|
||||
}
|
||||
bch_sched_event(bch, B_XMTBUFREADY);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
HDLC_irq(bchannel_t *bch, u_int stat)
|
||||
HDLC_irq(channel_t *bch, u_int stat)
|
||||
{
|
||||
int len;
|
||||
struct sk_buff *skb;
|
||||
|
@ -580,43 +599,58 @@ HDLC_irq(bchannel_t *bch, u_int stat)
|
|||
write_ctrl(bch, 1);
|
||||
hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
|
||||
write_ctrl(bch, 1);
|
||||
bch->rx_idx = 0;
|
||||
if (bch->rx_skb)
|
||||
skb_trim(bch->rx_skb, 0);
|
||||
} else {
|
||||
if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
|
||||
len = 32;
|
||||
hdlc_empty_fifo(bch, len);
|
||||
if ((stat & HDLC_STAT_RME) || (bch->protocol == ISDN_PID_L1_B_64TRANS)) {
|
||||
if (!bch->rx_skb)
|
||||
goto handle_tx;
|
||||
if ((stat & HDLC_STAT_RME) || test_bit(FLG_TRANSPARENT, &bch->Flags)) {
|
||||
if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
|
||||
(bch->protocol == ISDN_PID_L1_B_64TRANS)) {
|
||||
if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen)))
|
||||
printk(KERN_WARNING "HDLC: receive out of memory\n");
|
||||
else {
|
||||
memcpy(skb_put(skb, bch->rx_idx),
|
||||
bch->rx_buf, bch->rx_idx);
|
||||
skb_queue_tail(&bch->rqueue, skb);
|
||||
test_bit(FLG_TRANSPARENT, &bch->Flags)) {
|
||||
if (bch->rx_skb->len < MISDN_COPY_SIZE) {
|
||||
skb = alloc_stack_skb(bch->rx_skb->len, bch->up_headerlen);
|
||||
if (skb) {
|
||||
memcpy(skb_put(skb, bch->rx_skb->len),
|
||||
bch->rx_skb->data, bch->rx_skb->len);
|
||||
skb_trim(bch->rx_skb, 0);
|
||||
} else {
|
||||
skb = bch->rx_skb;
|
||||
bch->rx_skb = NULL;
|
||||
}
|
||||
} else {
|
||||
skb = bch->rx_skb;
|
||||
bch->rx_skb = NULL;
|
||||
}
|
||||
bch->rx_idx = 0;
|
||||
bch_sched_event(bch, B_RCVBUFREADY);
|
||||
queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, skb);
|
||||
} else {
|
||||
if (bch->debug & L1_DEB_HSCX)
|
||||
mISDN_debugprint(&bch->inst, "invalid frame");
|
||||
else
|
||||
mISDN_debugprint(&bch->inst, "ch%d invalid frame %#x", bch->channel, stat);
|
||||
bch->rx_idx = 0;
|
||||
skb_trim(bch->rx_skb, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handle_tx:
|
||||
if (stat & HDLC_INT_XDU) {
|
||||
/* Here we lost an TX interrupt, so
|
||||
* restart transmitting the whole frame on HDLC
|
||||
* in transparent mode we send the next data
|
||||
*/
|
||||
if (bch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&bch->inst, "ch%d XDU tx_len(%d) tx_idx(%d) Flag(%lx)",
|
||||
bch->channel, bch->tx_len, bch->tx_idx, bch->Flag);
|
||||
if (bch->tx_len) {
|
||||
if (bch->protocol != ISDN_PID_L1_B_64TRANS)
|
||||
if (bch->debug & L1_DEB_WARN) {
|
||||
if (bch->tx_skb)
|
||||
mISDN_debugprint(&bch->inst, "ch%d XDU tx_len(%d) tx_idx(%d) Flags(%lx)",
|
||||
bch->channel, bch->tx_skb->len, bch->tx_idx, bch->Flags);
|
||||
else
|
||||
mISDN_debugprint(&bch->inst, "ch%d XDU no tx_skb Flags(%lx)",
|
||||
bch->channel, bch->Flags);
|
||||
}
|
||||
if (bch->tx_skb && bch->tx_skb->len) {
|
||||
if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
|
||||
bch->tx_idx = 0;
|
||||
}
|
||||
hdlc->ctrl.sr.xml = 0;
|
||||
|
@ -633,7 +667,7 @@ static inline void
|
|||
HDLC_irq_main(fritzpnppci *fc)
|
||||
{
|
||||
u_int stat;
|
||||
bchannel_t *bch;
|
||||
channel_t *bch;
|
||||
|
||||
stat = read_status(fc, 0);
|
||||
if (stat & HDLC_INT_MASK) {
|
||||
|
@ -657,50 +691,20 @@ static irqreturn_t
|
|||
avm_fritz_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
fritzpnppci *fc = dev_id;
|
||||
u_long flags;
|
||||
u_char val;
|
||||
u_char sval;
|
||||
|
||||
spin_lock_irqsave(&fc->lock.lock, flags);
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.spin_adr = (void *)0x2001;
|
||||
#endif
|
||||
spin_lock(&fc->lock);
|
||||
sval = inb(fc->addr + 2);
|
||||
if (fc->dch.debug & L1_DEB_INTSTAT)
|
||||
mISDN_debugprint(&fc->dch.inst, "irq stat0 %x", sval);
|
||||
if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
|
||||
/* possible a shared IRQ reqest */
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&fc->lock.lock, flags);
|
||||
spin_unlock(&fc->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
fc->irqcnt++;
|
||||
if (test_and_set_bit(STATE_FLAG_BUSY, &fc->lock.state)) {
|
||||
printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
|
||||
__FUNCTION__, fc->lock.state);
|
||||
#ifdef SPIN_DEBUG
|
||||
printk(KERN_ERR "%s: previous lock:%p\n",
|
||||
__FUNCTION__, fc->lock.busy_adr);
|
||||
#endif
|
||||
#ifdef LOCK_STATISTIC
|
||||
fc->lock.irq_fail++;
|
||||
#endif
|
||||
} else {
|
||||
#ifdef LOCK_STATISTIC
|
||||
fc->lock.irq_ok++;
|
||||
#endif
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.busy_adr = avm_fritz_interrupt;
|
||||
#endif
|
||||
}
|
||||
|
||||
test_and_set_bit(STATE_FLAG_INIRQ, &fc->lock.state);
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&fc->lock.lock, flags);
|
||||
if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
|
||||
val = ReadISAC(fc, ISAC_ISTA);
|
||||
mISDN_isac_interrupt(&fc->dch, val);
|
||||
|
@ -712,21 +716,7 @@ avm_fritz_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
|||
WriteISAC(fc, ISAC_MASK, 0xFF);
|
||||
WriteISAC(fc, ISAC_MASK, 0x0);
|
||||
}
|
||||
spin_lock_irqsave(&fc->lock.lock, flags);
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.spin_adr = (void *)0x2002;
|
||||
#endif
|
||||
if (!test_and_clear_bit(STATE_FLAG_INIRQ, &fc->lock.state)) {
|
||||
}
|
||||
if (!test_and_clear_bit(STATE_FLAG_BUSY, &fc->lock.state)) {
|
||||
printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
|
||||
__FUNCTION__, fc->lock.state);
|
||||
}
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.busy_adr = NULL;
|
||||
fc->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&fc->lock.lock, flags);
|
||||
spin_unlock(&fc->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -734,50 +724,20 @@ static irqreturn_t
|
|||
avm_fritzv2_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
fritzpnppci *fc = dev_id;
|
||||
u_long flags;
|
||||
u_char val;
|
||||
u_char sval;
|
||||
|
||||
spin_lock_irqsave(&fc->lock.lock, flags);
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.spin_adr = (void *)0x2001;
|
||||
#endif
|
||||
spin_lock(&fc->lock);
|
||||
sval = inb(fc->addr + 2);
|
||||
if (fc->dch.debug & L1_DEB_INTSTAT)
|
||||
mISDN_debugprint(&fc->dch.inst, "irq stat0 %x", sval);
|
||||
if (!(sval & AVM_STATUS0_IRQ_MASK)) {
|
||||
/* possible a shared IRQ reqest */
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&fc->lock.lock, flags);
|
||||
spin_unlock(&fc->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
fc->irqcnt++;
|
||||
if (test_and_set_bit(STATE_FLAG_BUSY, &fc->lock.state)) {
|
||||
printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
|
||||
__FUNCTION__, fc->lock.state);
|
||||
#ifdef SPIN_DEBUG
|
||||
printk(KERN_ERR "%s: previous lock:%p\n",
|
||||
__FUNCTION__, fc->lock.busy_adr);
|
||||
#endif
|
||||
#ifdef LOCK_STATISTIC
|
||||
fc->lock.irq_fail++;
|
||||
#endif
|
||||
} else {
|
||||
#ifdef LOCK_STATISTIC
|
||||
fc->lock.irq_ok++;
|
||||
#endif
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.busy_adr = avm_fritz_interrupt;
|
||||
#endif
|
||||
}
|
||||
|
||||
test_and_set_bit(STATE_FLAG_INIRQ, &fc->lock.state);
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&fc->lock.lock, flags);
|
||||
if (sval & AVM_STATUS0_IRQ_HDLC) {
|
||||
HDLC_irq_main(fc);
|
||||
}
|
||||
|
@ -792,86 +752,59 @@ avm_fritzv2_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
|||
udelay(1);
|
||||
outb(fc->ctrlreg, fc->addr + 2);
|
||||
}
|
||||
spin_lock_irqsave(&fc->lock.lock, flags);
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.spin_adr = (void *)0x2002;
|
||||
#endif
|
||||
if (!test_and_clear_bit(STATE_FLAG_INIRQ, &fc->lock.state)) {
|
||||
}
|
||||
if (!test_and_clear_bit(STATE_FLAG_BUSY, &fc->lock.state)) {
|
||||
printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
|
||||
__FUNCTION__, fc->lock.state);
|
||||
}
|
||||
#ifdef SPIN_DEBUG
|
||||
fc->lock.busy_adr = NULL;
|
||||
fc->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&fc->lock.lock, flags);
|
||||
spin_unlock(&fc->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
hdlc_down(mISDNif_t *hif, struct sk_buff *skb)
|
||||
hdlc_down(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
bchannel_t *bch;
|
||||
int ret = -EINVAL;
|
||||
mISDN_head_t *hh;
|
||||
channel_t *bch;
|
||||
int ret = 0;
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
||||
u_long flags;
|
||||
|
||||
if (!hif || !skb)
|
||||
return(ret);
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
bch = hif->fdata;
|
||||
if ((hh->prim == PH_DATA_REQ) ||
|
||||
(hh->prim == (DL_DATA | REQUEST))) {
|
||||
if (bch->next_skb) {
|
||||
mISDN_debugprint(&bch->inst, " l2l1 next_skb exist this shouldn't happen");
|
||||
return(-EBUSY);
|
||||
}
|
||||
bch->inst.lock(bch->inst.data, 0);
|
||||
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;
|
||||
bch->inst.unlock(bch->inst.data);
|
||||
return(0);
|
||||
} else {
|
||||
bch->tx_len = skb->len;
|
||||
memcpy(bch->tx_buf, skb->data, bch->tx_len);
|
||||
bch->tx_idx = 0;
|
||||
bch = container_of(inst, channel_t, inst);
|
||||
if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) {
|
||||
spin_lock_irqsave(inst->hwlock, flags);
|
||||
ret = channel_senddata(bch, hh->dinfo, skb);
|
||||
if (ret > 0) { /* direct TX */
|
||||
hdlc_fill_fifo(bch);
|
||||
bch->inst.unlock(bch->inst.data);
|
||||
skb_trim(skb, 0);
|
||||
return(if_newhead(&bch->inst.up, hh->prim | CONFIRM,
|
||||
hh->dinfo, skb));
|
||||
}
|
||||
} else if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
|
||||
(hh->prim == (DL_ESTABLISH | REQUEST))) {
|
||||
if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag))
|
||||
ret = 0;
|
||||
else {
|
||||
bch->inst.lock(bch->inst.data,0);
|
||||
}
|
||||
spin_unlock_irqrestore(inst->hwlock, flags);
|
||||
return(ret);
|
||||
}
|
||||
if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
|
||||
(hh->prim == (DL_ESTABLISH | REQUEST))) {
|
||||
if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) {
|
||||
spin_lock_irqsave(inst->hwlock, flags);
|
||||
ret = modehdlc(bch, bch->channel,
|
||||
bch->inst.pid.protocol[1]);
|
||||
bch->inst.unlock(bch->inst.data);
|
||||
spin_unlock_irqrestore(inst->hwlock, flags);
|
||||
}
|
||||
skb_trim(skb, 0);
|
||||
return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb));
|
||||
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 == (MGR_DISCONNECT | REQUEST))) {
|
||||
bch->inst.lock(bch->inst.data,0);
|
||||
if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
|
||||
((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) {
|
||||
spin_lock_irqsave(inst->hwlock, flags);
|
||||
if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
|
||||
dev_kfree_skb(bch->next_skb);
|
||||
bch->next_skb = NULL;
|
||||
}
|
||||
test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
|
||||
if (bch->tx_skb) {
|
||||
dev_kfree_skb(bch->tx_skb);
|
||||
bch->tx_skb = NULL;
|
||||
bch->tx_idx = 0;
|
||||
}
|
||||
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
|
||||
modehdlc(bch, bch->channel, 0);
|
||||
test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag);
|
||||
bch->inst.unlock(bch->inst.data);
|
||||
test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
|
||||
spin_unlock_irqrestore(inst->hwlock, flags);
|
||||
skb_trim(skb, 0);
|
||||
if (hh->prim != (MGR_DISCONNECT | REQUEST))
|
||||
if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb))
|
||||
return(0);
|
||||
ret = 0;
|
||||
if (hh->prim != (PH_CONTROL | REQUEST))
|
||||
ret = mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, 0, skb);
|
||||
} else {
|
||||
printk(KERN_WARNING "hdlc_down unknown prim(%x)\n", hh->prim);
|
||||
ret = -EINVAL;
|
||||
|
@ -912,18 +845,22 @@ reset_avmpcipnp(fritzpnppci *fc)
|
|||
break;
|
||||
}
|
||||
printk(KERN_INFO "AVM PCI/PnP: reset\n");
|
||||
outb(fc->ctrlreg, fc->addr + 2);
|
||||
disable_hwirq(fc);
|
||||
mdelay(5);
|
||||
switch (fc->type) {
|
||||
case AVM_FRITZ_PNP:
|
||||
fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER;
|
||||
disable_hwirq(fc);
|
||||
outb(AVM_STATUS1_ENA_IOM | fc->irq, fc->addr + 3);
|
||||
break;
|
||||
case AVM_FRITZ_PCI:
|
||||
fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER;
|
||||
outb(fc->ctrlreg, fc->addr + 2);
|
||||
outb(AVM_STATUS1_ENA_IOM | fc->irq, fc->addr + 3);
|
||||
disable_hwirq(fc);
|
||||
outb(AVM_STATUS1_ENA_IOM, fc->addr + 3);
|
||||
break;
|
||||
case AVM_FRITZ_PCIV2:
|
||||
fc->ctrlreg = 0;
|
||||
outb(fc->ctrlreg, fc->addr + 2);
|
||||
disable_hwirq(fc);
|
||||
break;
|
||||
}
|
||||
mdelay(1);
|
||||
|
@ -934,44 +871,44 @@ static int init_card(fritzpnppci *fc)
|
|||
{
|
||||
int cnt = 3;
|
||||
u_int shared = SA_SHIRQ;
|
||||
u_long flags;
|
||||
u_char *id = "AVM Fritz!PCI";
|
||||
|
||||
if (fc->type == AVM_FRITZ_PNP)
|
||||
if (fc->type == AVM_FRITZ_PNP) {
|
||||
shared = 0;
|
||||
lock_dev(fc, 0);
|
||||
id = "AVM Fritz!PnP";
|
||||
}
|
||||
reset_avmpcipnp(fc); /* disable IRQ */
|
||||
if (fc->type == AVM_FRITZ_PCIV2) {
|
||||
if (request_irq(fc->irq, avm_fritzv2_interrupt, SA_SHIRQ,
|
||||
"AVM Fritz!PCI", fc)) {
|
||||
if (request_irq(fc->irq, avm_fritzv2_interrupt, shared, id, fc)) {
|
||||
printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
|
||||
fc->irq);
|
||||
unlock_dev(fc);
|
||||
return(-EIO);
|
||||
}
|
||||
} else {
|
||||
if (request_irq(fc->irq, avm_fritz_interrupt, shared,
|
||||
"AVM Fritz!PCI", fc)) {
|
||||
if (request_irq(fc->irq, avm_fritz_interrupt, shared, id, fc)) {
|
||||
printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
|
||||
fc->irq);
|
||||
unlock_dev(fc);
|
||||
return(-EIO);
|
||||
}
|
||||
}
|
||||
reset_avmpcipnp(fc);
|
||||
while (cnt) {
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&fc->lock, flags);
|
||||
mISDN_clear_isac(&fc->dch);
|
||||
if ((ret=mISDN_isac_init(&fc->dch))) {
|
||||
printk(KERN_WARNING "mISDN: mISDN_isac_init failed with %d\n", ret);
|
||||
spin_unlock_irqrestore(&fc->lock, flags);
|
||||
break;
|
||||
}
|
||||
clear_pending_hdlc_ints(fc);
|
||||
inithdlc(fc);
|
||||
outb(fc->ctrlreg, fc->addr + 2);
|
||||
WriteISAC(fc, ISAC_MASK, 0);
|
||||
fc->ctrlreg |= AVM_STATUS0_ENA_IRQ;
|
||||
outb(fc->ctrlreg, fc->addr + 2);
|
||||
enable_hwirq(fc);
|
||||
/* RESET Receiver and Transmitter */
|
||||
WriteISAC(fc, ISAC_CMDR, 0x41);
|
||||
unlock_dev(fc);
|
||||
spin_unlock_irqrestore(&fc->lock, flags);
|
||||
/* Timeout 10ms */
|
||||
current->state = TASK_UNINTERRUPTIBLE;
|
||||
schedule_timeout((10*HZ)/1000);
|
||||
|
@ -990,9 +927,7 @@ static int init_card(fritzpnppci *fc)
|
|||
} else {
|
||||
return(0);
|
||||
}
|
||||
lock_dev(fc, 0);
|
||||
}
|
||||
unlock_dev(fc);
|
||||
return(-EIO);
|
||||
}
|
||||
|
||||
|
@ -1080,44 +1015,39 @@ setup_fritz(fritzpnppci *fc)
|
|||
fc->irq, fc->addr);
|
||||
|
||||
fc->dch.hw = &fc->isac;
|
||||
lock_dev(fc, 0);
|
||||
#ifdef SPIN_DEBUG
|
||||
printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &fc->lock.busy_adr, fc->lock.busy_adr);
|
||||
printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &fc->lock.busy_adr, fc->lock.busy_adr);
|
||||
#endif
|
||||
unlock_dev(fc);
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void
|
||||
release_card(fritzpnppci *card)
|
||||
{
|
||||
#ifdef LOCK_STATISTIC
|
||||
printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n",
|
||||
card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq);
|
||||
printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
|
||||
card->lock.irq_ok, card->lock.irq_fail);
|
||||
#endif
|
||||
lock_dev(card, 0);
|
||||
outb(0, card->addr + 2);
|
||||
free_irq(card->irq, card);
|
||||
u_long flags;
|
||||
|
||||
disable_hwirq(card);
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
modehdlc(&card->bch[0], 0, ISDN_PID_NONE);
|
||||
modehdlc(&card->bch[1], 1, ISDN_PID_NONE);
|
||||
mISDN_isac_free(&card->dch);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
free_irq(card->irq, card);
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
release_region(card->addr, 32);
|
||||
mISDN_free_bch(&card->bch[1]);
|
||||
mISDN_free_bch(&card->bch[0]);
|
||||
mISDN_free_dch(&card->dch);
|
||||
fritz.ctrl(card->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &card->dch.inst.up);
|
||||
mISDN_freechannel(&card->bch[1]);
|
||||
mISDN_freechannel(&card->bch[0]);
|
||||
mISDN_freechannel(&card->dch);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
fritz.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
spin_lock_irqsave(&fritz.lock, flags);
|
||||
list_del(&card->list);
|
||||
unlock_dev(card);
|
||||
spin_unlock_irqrestore(&fritz.lock, flags);
|
||||
if (card->type == AVM_FRITZ_PNP) {
|
||||
pnp_disable_dev(card->pdev);
|
||||
pnp_set_drvdata(card->pdev, NULL);
|
||||
#if defined(CONFIG_PNP)
|
||||
pnp_disable_dev(card->dev.pnp);
|
||||
pnp_set_drvdata(card->dev.pnp, NULL);
|
||||
#endif
|
||||
} else {
|
||||
pci_disable_device(card->pdev);
|
||||
pci_set_drvdata(card->pdev, NULL);
|
||||
pci_disable_device(card->dev.pci);
|
||||
pci_set_drvdata(card->dev.pci, NULL);
|
||||
}
|
||||
kfree(card);
|
||||
}
|
||||
|
@ -1127,6 +1057,7 @@ fritz_manager(void *data, u_int prim, void *arg) {
|
|||
fritzpnppci *card;
|
||||
mISDNinstance_t *inst = data;
|
||||
struct sk_buff *skb;
|
||||
u_long flags;
|
||||
int channel = -1;
|
||||
|
||||
if (debug & 0x10000)
|
||||
|
@ -1138,6 +1069,7 @@ fritz_manager(void *data, u_int prim, void *arg) {
|
|||
__FUNCTION__, prim, arg);
|
||||
return(-EINVAL);
|
||||
}
|
||||
spin_lock_irqsave(&fritz.lock, flags);
|
||||
list_for_each_entry(card, &fritz.ilist, list) {
|
||||
if (&card->dch.inst == inst) {
|
||||
channel = 2;
|
||||
|
@ -1152,6 +1084,7 @@ fritz_manager(void *data, u_int prim, void *arg) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&fritz.lock, flags);
|
||||
if (channel<0) {
|
||||
printk(KERN_WARNING "%s: no channel data %p prim %x arg %p\n",
|
||||
__FUNCTION__, data, prim, arg);
|
||||
|
@ -1161,36 +1094,31 @@ fritz_manager(void *data, u_int prim, void *arg) {
|
|||
switch(prim) {
|
||||
case MGR_REGLAYER | CONFIRM:
|
||||
if (channel == 2)
|
||||
dch_set_para(&card->dch, &inst->st->para);
|
||||
mISDN_setpara(&card->dch, &inst->st->para);
|
||||
else
|
||||
bch_set_para(&card->bch[channel], &inst->st->para);
|
||||
mISDN_setpara(&card->bch[channel], &inst->st->para);
|
||||
break;
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
if (channel == 2) {
|
||||
inst->down.fdata = &card->dch;
|
||||
if ((skb = create_link_skb(PH_CONTROL | REQUEST,
|
||||
HW_DEACTIVATE, 0, NULL, 0))) {
|
||||
if (mISDN_ISAC_l1hw(&inst->down, skb))
|
||||
if ((skb = create_link_skb(PH_CONTROL | REQUEST,
|
||||
HW_DEACTIVATE, 0, NULL, 0))) {
|
||||
if (channel == 2) {
|
||||
if (mISDN_ISAC_l1hw(inst, skb))
|
||||
dev_kfree_skb(skb);
|
||||
} else {
|
||||
if (hdlc_down(inst, skb))
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
} else {
|
||||
inst->down.fdata = &card->bch[channel];
|
||||
if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST,
|
||||
0, 0, NULL, 0))) {
|
||||
if (hdlc_down(&inst->down, skb))
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
fritz.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
} else
|
||||
printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__);
|
||||
fritz.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
break;
|
||||
case MGR_CLRSTPARA | INDICATION:
|
||||
arg = NULL;
|
||||
case MGR_ADDSTPARA | INDICATION:
|
||||
if (channel == 2)
|
||||
dch_set_para(&card->dch, arg);
|
||||
mISDN_setpara(&card->dch, arg);
|
||||
else
|
||||
bch_set_para(&card->bch[channel], arg);
|
||||
mISDN_setpara(&card->bch[channel], arg);
|
||||
break;
|
||||
case MGR_RELEASE | INDICATION:
|
||||
if (channel == 2) {
|
||||
|
@ -1199,33 +1127,18 @@ fritz_manager(void *data, u_int prim, void *arg) {
|
|||
fritz.refcnt--;
|
||||
}
|
||||
break;
|
||||
case MGR_CONNECT | REQUEST:
|
||||
return(mISDN_ConnectIF(inst, arg));
|
||||
case MGR_SETIF | REQUEST:
|
||||
case MGR_SETIF | INDICATION:
|
||||
if (channel==2)
|
||||
return(mISDN_SetIF(inst, arg, prim, mISDN_ISAC_l1hw, NULL,
|
||||
&card->dch));
|
||||
else
|
||||
return(mISDN_SetIF(inst, arg, prim, hdlc_down, NULL,
|
||||
&card->bch[channel]));
|
||||
break;
|
||||
case MGR_DISCONNECT | REQUEST:
|
||||
case MGR_DISCONNECT | INDICATION:
|
||||
return(mISDN_DisConnectIF(inst, arg));
|
||||
case MGR_SETSTACK | CONFIRM:
|
||||
case MGR_SETSTACK | INDICATION:
|
||||
if ((channel!=2) && (inst->pid.global == 2)) {
|
||||
inst->down.fdata = &card->bch[channel];
|
||||
if ((skb = create_link_skb(PH_ACTIVATE | REQUEST,
|
||||
0, 0, NULL, 0))) {
|
||||
if (hdlc_down(&inst->down, skb))
|
||||
if (hdlc_down(inst, skb))
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
|
||||
if_link(&inst->up, DL_ESTABLISH | INDICATION,
|
||||
mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION,
|
||||
0, 0, NULL, 0);
|
||||
else
|
||||
if_link(&inst->up, PH_ACTIVATE | INDICATION,
|
||||
mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION,
|
||||
0, 0, NULL, 0);
|
||||
}
|
||||
break;
|
||||
|
@ -1243,37 +1156,52 @@ static int __devinit setup_instance(fritzpnppci *card)
|
|||
{
|
||||
int i, err;
|
||||
mISDN_pid_t pid;
|
||||
|
||||
u_long flags;
|
||||
struct device *dev;
|
||||
|
||||
if (card->type == AVM_FRITZ_PNP) {
|
||||
#if defined(CONFIG_PNP)
|
||||
dev = &card->dev.pnp->dev;
|
||||
#else
|
||||
dev = NULL;
|
||||
#endif
|
||||
} else {
|
||||
dev = &card->dev.pci->dev;
|
||||
}
|
||||
spin_lock_irqsave(&fritz.lock, flags);
|
||||
list_add_tail(&card->list, &fritz.ilist);
|
||||
spin_unlock_irqrestore(&fritz.lock, flags);
|
||||
card->dch.debug = debug;
|
||||
lock_HW_init(&card->lock);
|
||||
card->dch.inst.lock = lock_dev;
|
||||
card->dch.inst.unlock = unlock_dev;
|
||||
spin_lock_init(&card->lock);
|
||||
card->dch.inst.hwlock = &card->lock;
|
||||
card->dch.inst.class_dev.dev = dev;
|
||||
card->dch.inst.pid.layermask = ISDN_LAYER(0);
|
||||
card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
|
||||
mISDN_init_instance(&card->dch.inst, &fritz, card);
|
||||
mISDN_init_instance(&card->dch.inst, &fritz, card, mISDN_ISAC_l1hw);
|
||||
sprintf(card->dch.inst.name, "Fritz%d", fritz_cnt+1);
|
||||
mISDN_set_dchannel_pid(&pid, protocol[fritz_cnt], layermask[fritz_cnt]);
|
||||
mISDN_init_dch(&card->dch);
|
||||
mISDN_initchannel(&card->dch, MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1);
|
||||
for (i=0; i<2; i++) {
|
||||
card->bch[i].channel = i;
|
||||
mISDN_init_instance(&card->bch[i].inst, &fritz, card);
|
||||
mISDN_init_instance(&card->bch[i].inst, &fritz, card, hdlc_down);
|
||||
card->bch[i].inst.pid.layermask = ISDN_LAYER(0);
|
||||
card->bch[i].inst.lock = lock_dev;
|
||||
card->bch[i].inst.unlock = unlock_dev;
|
||||
card->bch[i].inst.hwlock = &card->lock;
|
||||
card->bch[i].inst.class_dev.dev = dev;
|
||||
card->bch[i].debug = debug;
|
||||
sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1);
|
||||
mISDN_init_bch(&card->bch[i]);
|
||||
mISDN_initchannel(&card->bch[i], MSK_INIT_BCHANNEL, MAX_DATA_MEM);
|
||||
card->bch[i].hw = &card->hdlc[i];
|
||||
}
|
||||
printk(KERN_DEBUG "fritz card %p dch %p bch1 %p bch2 %p\n",
|
||||
card, &card->dch, &card->bch[0], &card->bch[1]);
|
||||
err = setup_fritz(card);
|
||||
if (err) {
|
||||
mISDN_free_dch(&card->dch);
|
||||
mISDN_free_bch(&card->bch[1]);
|
||||
mISDN_free_bch(&card->bch[0]);
|
||||
mISDN_freechannel(&card->dch);
|
||||
mISDN_freechannel(&card->bch[1]);
|
||||
mISDN_freechannel(&card->bch[0]);
|
||||
spin_lock_irqsave(&fritz.lock, flags);
|
||||
list_del(&card->list);
|
||||
spin_unlock_irqrestore(&fritz.lock, flags);
|
||||
kfree(card);
|
||||
return(err);
|
||||
}
|
||||
|
@ -1321,7 +1249,7 @@ static int __devinit fritzpci_probe(struct pci_dev *pdev, const struct pci_devic
|
|||
card->type = AVM_FRITZ_PCIV2;
|
||||
else
|
||||
card->type = AVM_FRITZ_PCI;
|
||||
card->pdev = pdev;
|
||||
card->dev.pci = pdev;
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
kfree(card);
|
||||
|
@ -1359,7 +1287,7 @@ static int __devinit fritzpnp_probe(struct pci_dev *pdev, const struct isapnp_de
|
|||
}
|
||||
memset(card, 0, sizeof(fritzpnppci));
|
||||
card->type = AVM_FRITZ_PNP;
|
||||
card->pdev = pdev;
|
||||
card->dev.pnp = pdev;
|
||||
pnp_disable_dev(pdev);
|
||||
err = pnp_activate_dev(pdev);
|
||||
if (err<0) {
|
||||
|
@ -1465,6 +1393,7 @@ static int __init Fritz_init(void)
|
|||
#ifdef MODULE
|
||||
fritz.owner = THIS_MODULE;
|
||||
#endif
|
||||
spin_lock_init(&fritz.lock);
|
||||
INIT_LIST_HEAD(&fritz.ilist);
|
||||
fritz.name = FritzName;
|
||||
fritz.own_ctrl = fritz_manager;
|
||||
|
@ -1505,7 +1434,9 @@ static int __init Fritz_init(void)
|
|||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if defined(CONFIG_PNP)
|
||||
out_unregister_pci:
|
||||
#endif
|
||||
pci_unregister_driver(&fcpci_driver);
|
||||
out:
|
||||
return err;
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* Author Karsten Keil (keil@isdn4linux.de)
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mISDNif.h>
|
||||
#include "layer1.h"
|
||||
#include "bchannel.h"
|
||||
#include "helper.h"
|
||||
|
||||
static void
|
||||
bchannel_bh(bchannel_t *bch)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u_int pr;
|
||||
int ret;
|
||||
mISDN_head_t *hh;
|
||||
mISDNif_t *hif;
|
||||
|
||||
if (!bch)
|
||||
return;
|
||||
if (!bch->inst.up.func) {
|
||||
printk(KERN_WARNING "%s: without up.func\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
#if 0
|
||||
printk(KERN_DEBUG "%s: event %x\n", __FUNCTION__, bch->event);
|
||||
if (bch->dev)
|
||||
printk(KERN_DEBUG "%s: rpflg(%x) wpflg(%x)\n", __FUNCTION__,
|
||||
bch->dev->rport.Flag, bch->dev->wport.Flag);
|
||||
#endif
|
||||
if (test_and_clear_bit(B_XMTBUFREADY, &bch->event)) {
|
||||
skb = bch->next_skb;
|
||||
if (skb) {
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
bch->next_skb = NULL;
|
||||
if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS)
|
||||
pr = DL_DATA | CONFIRM;
|
||||
else
|
||||
pr = PH_DATA | CONFIRM;
|
||||
if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
|
||||
&& bch->dev)
|
||||
hif = &bch->dev->rport.pif;
|
||||
else
|
||||
hif = &bch->inst.up;
|
||||
if (if_newhead(hif, pr, hh->dinfo, skb))
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
if (test_and_clear_bit(B_RCVBUFREADY, &bch->event)) {
|
||||
if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
|
||||
&& bch->dev)
|
||||
hif = &bch->dev->rport.pif;
|
||||
else
|
||||
hif = &bch->inst.up;
|
||||
while ((skb = skb_dequeue(&bch->rqueue))) {
|
||||
if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS)
|
||||
pr = DL_DATA | INDICATION;
|
||||
else
|
||||
pr = PH_DATA | INDICATION;
|
||||
ret = if_newhead(hif, pr, MISDN_ID_ANY, skb);
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "%s: deliver err %d\n",
|
||||
__FUNCTION__, ret);
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bch->hw_bh)
|
||||
bch->hw_bh(bch);
|
||||
}
|
||||
|
||||
int
|
||||
mISDN_init_bch(bchannel_t *bch) {
|
||||
int devtyp = mISDN_RAW_DEVICE;
|
||||
|
||||
if (!(bch->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
|
||||
printk(KERN_WARNING
|
||||
"mISDN: No memory for blog\n");
|
||||
return(-ENOMEM);
|
||||
}
|
||||
if (!(bch->rx_buf = kmalloc(MAX_DATA_MEM, GFP_ATOMIC))) {
|
||||
printk(KERN_WARNING
|
||||
"mISDN: No memory for bchannel rx_buf\n");
|
||||
kfree(bch->blog);
|
||||
bch->blog = NULL;
|
||||
return (-ENOMEM);
|
||||
}
|
||||
if (!(bch->tx_buf = kmalloc(MAX_DATA_MEM, GFP_ATOMIC))) {
|
||||
printk(KERN_WARNING
|
||||
"mISDN: No memory for bchannel tx_buf\n");
|
||||
kfree(bch->blog);
|
||||
bch->blog = NULL;
|
||||
kfree(bch->rx_buf);
|
||||
bch->rx_buf = NULL;
|
||||
return (-ENOMEM);
|
||||
}
|
||||
skb_queue_head_init(&bch->rqueue);
|
||||
bch->next_skb = NULL;
|
||||
bch->Flag = 0;
|
||||
bch->event = 0;
|
||||
bch->rx_idx = 0;
|
||||
bch->tx_len = 0;
|
||||
bch->tx_idx = 0;
|
||||
INIT_WORK(&bch->work, (void *)(void *)bchannel_bh, bch);
|
||||
bch->hw_bh = NULL;
|
||||
if (!bch->dev) {
|
||||
if (bch->inst.obj->ctrl(&bch->dev, MGR_GETDEVICE | REQUEST,
|
||||
&devtyp)) {
|
||||
printk(KERN_WARNING
|
||||
"mISDN: no raw device for bchannel\n");
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int
|
||||
mISDN_free_bch(bchannel_t *bch) {
|
||||
#ifdef HAS_WORKQUEUE
|
||||
if (bch->work.pending)
|
||||
printk(KERN_ERR "mISDN_free_bch work:(%lx)\n", bch->work.pending);
|
||||
#else
|
||||
if (bch->work.sync)
|
||||
printk(KERN_ERR "mISDN_free_bch work:(%lx)\n", bch->work.sync);
|
||||
#endif
|
||||
discard_queue(&bch->rqueue);
|
||||
if (bch->blog) {
|
||||
kfree(bch->blog);
|
||||
bch->blog = NULL;
|
||||
}
|
||||
if (bch->rx_buf) {
|
||||
kfree(bch->rx_buf);
|
||||
bch->rx_buf = NULL;
|
||||
}
|
||||
if (bch->tx_buf) {
|
||||
kfree(bch->tx_buf);
|
||||
bch->tx_buf = NULL;
|
||||
}
|
||||
if (bch->next_skb) {
|
||||
dev_kfree_skb(bch->next_skb);
|
||||
bch->next_skb = NULL;
|
||||
}
|
||||
if (bch->inst.obj->ctrl(bch->dev, MGR_DELDEVICE | REQUEST, NULL)) {
|
||||
printk(KERN_WARNING
|
||||
"mISDN: del raw device error\n");
|
||||
} else
|
||||
bch->dev = NULL;
|
||||
return(0);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mISDN_init_bch);
|
||||
EXPORT_SYMBOL(mISDN_free_bch);
|
|
@ -1,98 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* Basic declarations, defines for Bchannel hardware
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mISDNif.h>
|
||||
#ifdef HAS_WORKQUEUE
|
||||
#include <linux/workqueue.h>
|
||||
#else
|
||||
#include <linux/tqueue.h>
|
||||
#endif
|
||||
#include <linux/smp.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/timer.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/skbuff.h>
|
||||
#ifdef MISDN_MEMDEBUG
|
||||
#include "memdbg.h"
|
||||
#endif
|
||||
|
||||
#define MAX_BLOG_SPACE 256
|
||||
|
||||
#define BC_FLG_INIT 1
|
||||
#define BC_FLG_ACTIV 2
|
||||
#define BC_FLG_TX_BUSY 3
|
||||
#define BC_FLG_NOFRAME 4
|
||||
#define BC_FLG_HALF 5
|
||||
#define BC_FLG_EMPTY 6
|
||||
#define BC_FLG_ORIG 7
|
||||
#define BC_FLG_DLEETX 8
|
||||
#define BC_FLG_LASTDLE 9
|
||||
#define BC_FLG_FIRST 10
|
||||
#define BC_FLG_LASTDATA 11
|
||||
#define BC_FLG_NMD_DATA 12
|
||||
#define BC_FLG_FTI_RUN 13
|
||||
#define BC_FLG_LL_OK 14
|
||||
#define BC_FLG_LL_CONN 15
|
||||
#define BC_FLG_TX_NEXT 16
|
||||
#define BC_FLG_DTMFSEND 17
|
||||
|
||||
typedef struct _bchannel_t {
|
||||
int channel;
|
||||
int protocol;
|
||||
u_long Flag;
|
||||
int debug;
|
||||
mISDNstack_t *st;
|
||||
mISDNinstance_t inst;
|
||||
mISDNdevice_t *dev;
|
||||
void *hw;
|
||||
u_char (*Read_Reg)(void *, int, u_char);
|
||||
void (*Write_Reg)(void *, int, u_char, u_char);
|
||||
struct sk_buff *next_skb;
|
||||
u_char *tx_buf;
|
||||
int tx_idx;
|
||||
int tx_len;
|
||||
u_char *rx_buf;
|
||||
int rx_idx;
|
||||
struct sk_buff_head rqueue; /* B-Channel receive Queue */
|
||||
u_char *blog;
|
||||
u_char *conmsg;
|
||||
struct timer_list transbusy;
|
||||
struct work_struct work;
|
||||
void (*hw_bh) (struct _bchannel_t *);
|
||||
u_long event;
|
||||
int maxdatasize;
|
||||
int up_headerlen;
|
||||
int err_crc;
|
||||
int err_tx;
|
||||
int err_rdo;
|
||||
int err_inv;
|
||||
} bchannel_t;
|
||||
|
||||
extern int mISDN_init_bch(bchannel_t *);
|
||||
extern int mISDN_free_bch(bchannel_t *);
|
||||
|
||||
static inline void
|
||||
bch_set_para(bchannel_t *bch, mISDN_stPara_t *stp)
|
||||
{
|
||||
if (stp) {
|
||||
bch->maxdatasize = stp->maxdatalen;
|
||||
bch->up_headerlen = stp->up_headerlen;
|
||||
} else {
|
||||
bch->maxdatasize = 0;
|
||||
bch->up_headerlen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
bch_sched_event(bchannel_t *bch, int event)
|
||||
{
|
||||
test_and_set_bit(event, &bch->event);
|
||||
schedule_work(&bch->work);
|
||||
}
|
|
@ -311,11 +311,13 @@ capi20_manager(void *data, u_int prim, void *arg) {
|
|||
int found=0;
|
||||
PLInst_t *plink = NULL;
|
||||
Controller_t *ctrl;
|
||||
u_long flags;
|
||||
|
||||
if (CAPI_DBG_INFO & debug)
|
||||
printk(KERN_DEBUG "capi20_manager data:%p prim:%x arg:%p\n", data, prim, arg);
|
||||
if (!data)
|
||||
return(-EINVAL);
|
||||
spin_lock_irqsave(&capi_obj.lock, flags);
|
||||
list_for_each_entry(ctrl, &capi_obj.ilist, list) {
|
||||
if (&ctrl->inst == inst) {
|
||||
found++;
|
||||
|
@ -333,6 +335,7 @@ capi20_manager(void *data, u_int prim, void *arg) {
|
|||
}
|
||||
if (&ctrl->list == &capi_obj.ilist)
|
||||
ctrl = NULL;
|
||||
spin_unlock_irqrestore(&capi_obj.lock, flags);
|
||||
if (prim == (MGR_NEWLAYER | REQUEST)) {
|
||||
int ret = ControllerConstr(&ctrl, data, arg, &capi_obj);
|
||||
if (!ret)
|
||||
|
@ -346,8 +349,9 @@ capi20_manager(void *data, u_int prim, void *arg) {
|
|||
}
|
||||
switch(prim) {
|
||||
case MGR_NEWENTITY | CONFIRM:
|
||||
ctrl->entity = (int)arg;
|
||||
ctrl->entity = (u_long)arg & 0xffffffff;
|
||||
break;
|
||||
#ifdef FIXME
|
||||
case MGR_CONNECT | REQUEST:
|
||||
return(mISDN_ConnectIF(inst, arg));
|
||||
case MGR_SETIF | INDICATION:
|
||||
|
@ -359,6 +363,11 @@ capi20_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_DISCONNECT | REQUEST:
|
||||
case MGR_DISCONNECT | INDICATION:
|
||||
return(mISDN_DisConnectIF(inst, arg));
|
||||
#endif
|
||||
case MGR_SETSTACK | INDICATION:
|
||||
if (!(&ctrl->inst == inst))
|
||||
return(AppPlcimISDN_Active(inst->privat));
|
||||
return(0);
|
||||
case MGR_RELEASE | INDICATION:
|
||||
if (CAPI_DBG_INFO & debug)
|
||||
printk(KERN_DEBUG "release_capi20 id %x\n", ctrl->inst.st->id);
|
||||
|
@ -366,8 +375,7 @@ capi20_manager(void *data, u_int prim, void *arg) {
|
|||
break;
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
if (plink) {
|
||||
capi_obj.ctrl(plink->inst.down.peer, MGR_DISCONNECT | REQUEST,
|
||||
&plink->inst.down);
|
||||
plink->inst.function = NULL;
|
||||
capi_obj.ctrl(&plink->inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
}
|
||||
break;
|
||||
|
@ -397,6 +405,7 @@ int Capi20Init(void)
|
|||
capi_obj.BPROTO.protocol[4] = ISDN_PID_L4_B_CAPI20;
|
||||
capi_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_TRANS;
|
||||
capi_obj.own_ctrl = capi20_manager;
|
||||
spin_lock_init(&capi_obj.lock);
|
||||
INIT_LIST_HEAD(&capi_obj.ilist);
|
||||
if ((err = CapiNew()))
|
||||
return(err);
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/* $Id$
|
||||
*
|
||||
* Author (c) Karsten Keil <kkeil@suse.de>
|
||||
*
|
||||
* This file is released under the GPLv2
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#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);
|
|
@ -0,0 +1,148 @@
|
|||
/* $Id$
|
||||
*
|
||||
* Basic declarations for a mISDN HW channel
|
||||
*
|
||||
* Author (c) Karsten Keil <kkeil@suse.de>
|
||||
*
|
||||
* This file is released under the GPLv2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MISDN_CHANNEL_H
|
||||
#define MISDN_CHANNEL_H
|
||||
#include <linux/mISDNif.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/skbuff.h>
|
||||
#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<<FLG_DCHANNEL)|(1<<FLG_HDLC))
|
||||
#define MSK_INIT_BCHANNEL (1<<FLG_BCHANNEL)
|
||||
#define MSK_INIT_ECHANNEL (1<<FLG_ECHANNEL)
|
||||
|
||||
|
||||
typedef struct _channel_t {
|
||||
mISDNinstance_t inst;
|
||||
int channel;
|
||||
/* basic properties */
|
||||
u_long Flags;
|
||||
u_int type;
|
||||
u_int state;
|
||||
/* HW access */
|
||||
u_char (*read_reg) (void *, u_char);
|
||||
void (*write_reg) (void *, u_char, u_char);
|
||||
void (*read_fifo) (void *, u_char *, int);
|
||||
void (*write_fifo) (void *, u_char *, int);
|
||||
void *hw;
|
||||
struct timer_list timer;
|
||||
/* receive data */
|
||||
struct sk_buff *rx_skb;
|
||||
int maxlen;
|
||||
int up_headerlen;
|
||||
/* send data */
|
||||
struct sk_buff *next_skb;
|
||||
struct sk_buff *tx_skb;
|
||||
int tx_idx;
|
||||
/* debug */
|
||||
int debug;
|
||||
char *log;
|
||||
/* statistics */
|
||||
int err_crc;
|
||||
int err_tx;
|
||||
int err_rx;
|
||||
} channel_t;
|
||||
|
||||
extern int mISDN_initchannel(channel_t *, ulong, int);
|
||||
extern int mISDN_freechannel(channel_t *);
|
||||
extern int mISDN_setpara(channel_t *, mISDN_stPara_t *);
|
||||
|
||||
static inline void
|
||||
queue_ch_frame(channel_t *ch, u_int pr, int dinfo, struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
|
||||
pr |= test_bit(FLG_L2DATA, &ch->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
|
|
@ -58,6 +58,7 @@ ControllerDestr(Controller_t *contr)
|
|||
}
|
||||
#endif
|
||||
contr->ctrl = NULL;
|
||||
#ifdef FIXME
|
||||
if (inst->up.peer) {
|
||||
inst->up.peer->obj->ctrl(inst->up.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
|
@ -66,13 +67,14 @@ ControllerDestr(Controller_t *contr)
|
|||
inst->down.peer->obj->ctrl(inst->down.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->down);
|
||||
}
|
||||
#endif
|
||||
list_for_each_safe(item, next, &contr->linklist) {
|
||||
PLInst_t *plink = list_entry(item, PLInst_t, list);
|
||||
list_del(&plink->list);
|
||||
kfree(plink);
|
||||
}
|
||||
if (contr->entity != MISDN_ENTITY_NONE)
|
||||
inst->obj->ctrl(inst, MGR_DELENTITY | REQUEST, (void *)contr->entity);
|
||||
inst->obj->ctrl(inst, MGR_DELENTITY | REQUEST, (void *)((u_long)contr->entity));
|
||||
inst->obj->ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
list_del(&contr->list);
|
||||
spin_unlock_irqrestore(&contr->list_lock, flags);
|
||||
|
@ -230,15 +232,92 @@ SendMessage(struct capi_ctr *ctrl, struct sk_buff *skb)
|
|||
Controller_t *contr = ctrl->driverdata;
|
||||
Application_t *appl;
|
||||
int ApplId;
|
||||
int err;
|
||||
int err = CAPI_NOERROR;
|
||||
u16 cmd;
|
||||
AppPlci_t *aplci;
|
||||
Ncci_t *ncci;
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
||||
|
||||
ApplId = CAPIMSG_APPID(skb->data);
|
||||
appl = getApplication4Id(contr, ApplId);
|
||||
if (!appl) {
|
||||
int_error();
|
||||
err = CAPI_ILLAPPNR;
|
||||
} else
|
||||
err = ApplicationSendMessage(appl, skb);
|
||||
goto end;
|
||||
}
|
||||
|
||||
hh->prim = CAPI_MESSAGE_REQUEST;
|
||||
hh->dinfo = ApplId;
|
||||
cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
|
||||
contrDebug(contr, CAPI_DBG_CONTR_MSG, "SendMessage: %s caddr(%x)",
|
||||
capi_cmd2str(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)),
|
||||
CAPIMSG_CONTROL(skb->data));
|
||||
switch (cmd) {
|
||||
// for NCCI state machine
|
||||
case CAPI_DATA_B3_REQ:
|
||||
case CAPI_DATA_B3_RESP:
|
||||
case CAPI_CONNECT_B3_RESP:
|
||||
case CAPI_CONNECT_B3_ACTIVE_RESP:
|
||||
case CAPI_DISCONNECT_B3_REQ:
|
||||
case CAPI_RESET_B3_REQ:
|
||||
case CAPI_RESET_B3_RESP:
|
||||
aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
|
||||
if (!aplci) {
|
||||
AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_EXACT);
|
||||
if ((!ncci) || (!ncci->link)) {
|
||||
int_error();
|
||||
AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
err = mISDN_queue_message(&ncci->link->inst, 0, skb);
|
||||
if (err) {
|
||||
int_errtxt("mISDN_queue_message return(%d)", err);
|
||||
err = CAPI_MSGBUSY;
|
||||
}
|
||||
break;
|
||||
// new NCCI
|
||||
case CAPI_CONNECT_B3_REQ:
|
||||
// maybe already down NCCI
|
||||
case CAPI_DISCONNECT_B3_RESP:
|
||||
// for PLCI state machine
|
||||
case CAPI_INFO_REQ:
|
||||
case CAPI_ALERT_REQ:
|
||||
case CAPI_CONNECT_REQ:
|
||||
case CAPI_CONNECT_RESP:
|
||||
case CAPI_CONNECT_ACTIVE_RESP:
|
||||
case CAPI_DISCONNECT_REQ:
|
||||
case CAPI_DISCONNECT_RESP:
|
||||
case CAPI_SELECT_B_PROTOCOL_REQ:
|
||||
// for LISTEN state machine
|
||||
case CAPI_LISTEN_REQ:
|
||||
// other
|
||||
case CAPI_FACILITY_REQ:
|
||||
case CAPI_MANUFACTURER_REQ:
|
||||
case CAPI_INFO_RESP:
|
||||
err = mISDN_queue_message(&contr->inst, 0, skb);
|
||||
if (err) {
|
||||
int_errtxt("mISDN_queue_message return(%d)", err);
|
||||
err = CAPI_MSGBUSY;
|
||||
}
|
||||
break;
|
||||
/* need not further action currently, so it can be released here too avoid
|
||||
* overlap with a release application
|
||||
*/
|
||||
case CAPI_FACILITY_RESP:
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
default:
|
||||
contrDebug(contr, CAPI_DBG_WARN, "SendMessage: %#x %#x not handled!",
|
||||
CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
|
||||
err = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
|
||||
break;
|
||||
}
|
||||
end:
|
||||
#ifndef OLDCAPI_DRIVER_INTERFACE
|
||||
return(err);
|
||||
#endif
|
||||
|
@ -468,25 +547,33 @@ SSProcess_t
|
|||
return(sp);
|
||||
}
|
||||
|
||||
int
|
||||
ControllerL3L4(mISDNif_t *hif, struct sk_buff *skb)
|
||||
static int
|
||||
Controller_function(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
Controller_t *contr;
|
||||
Plci_t *plci;
|
||||
int ret = -EINVAL;
|
||||
mISDN_head_t *hh;
|
||||
|
||||
if (!hif || !skb)
|
||||
return(ret);
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
contr = hif->fdata;
|
||||
contr = inst->privat;
|
||||
contrDebug(contr, CAPI_DBG_CONTR_INFO, "%s: prim(%x) id(%x)",
|
||||
__FUNCTION__, hh->prim, hh->dinfo);
|
||||
if (hh->prim == (CC_NEW_CR | INDICATION)) {
|
||||
if (hh->prim == CAPI_MESSAGE_REQUEST) {
|
||||
Application_t *appl = getApplication4Id(contr, hh->dinfo);
|
||||
if (!appl) {
|
||||
int_error();
|
||||
return(ret);
|
||||
}
|
||||
ApplicationSendMessage(appl, skb);
|
||||
return(0);
|
||||
} else if (hh->prim == (CC_NEW_CR | INDICATION)) {
|
||||
ret = ControllerNewPlci(contr, &plci, hh->dinfo);
|
||||
if(!ret)
|
||||
dev_kfree_skb(skb);
|
||||
} else if (hh->dinfo == MISDN_ID_DUMMY) {
|
||||
contrDebug(contr, CAPI_DBG_CONTR_INFO, "%s: call Supplementary_l3l4 len %d",
|
||||
__FUNCTION__, skb->len);
|
||||
ret = Supplementary_l3l4(contr, hh->prim, skb);
|
||||
} else {
|
||||
if (!(plci = getPlci4L3id(contr, hh->dinfo))) {
|
||||
|
@ -503,7 +590,7 @@ ControllerL3L4(mISDNif_t *hif, struct sk_buff *skb)
|
|||
int
|
||||
ControllerL4L3(Controller_t *contr, u_int prim, int dinfo, struct sk_buff *skb)
|
||||
{
|
||||
return(if_newhead(&contr->inst.down, prim, dinfo, skb));
|
||||
return(mISDN_queuedown_newhead(&contr->inst, 0, prim, dinfo, skb));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -520,6 +607,7 @@ ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mIS
|
|||
int retval;
|
||||
mISDNstack_t *cst;
|
||||
PLInst_t *plink;
|
||||
u_long flags;
|
||||
|
||||
if (!st)
|
||||
return(-EINVAL);
|
||||
|
@ -544,7 +632,6 @@ ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mIS
|
|||
INIT_LIST_HEAD(&contr->SSProcesse);
|
||||
INIT_LIST_HEAD(&contr->linklist);
|
||||
spin_lock_init(&contr->list_lock);
|
||||
spin_lock_init(&contr->id_lock);
|
||||
contr->next_id = 1;
|
||||
memcpy(&contr->inst.pid, pid, sizeof(mISDN_pid_t));
|
||||
#ifndef OLDCAPI_DRIVER_INTERFACE
|
||||
|
@ -577,10 +664,9 @@ ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mIS
|
|||
ControllerDestr(contr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
// FIXME ???
|
||||
contr->addr = st->id;
|
||||
sprintf(contr->inst.name, "CAPI %d", st->id);
|
||||
mISDN_init_instance(&contr->inst, ocapi, contr);
|
||||
contr->addr = (st->id >> 8) & 0xff;
|
||||
sprintf(contr->inst.name, "CAPI %d", contr->addr);
|
||||
mISDN_init_instance(&contr->inst, ocapi, contr, Controller_function);
|
||||
if (!mISDN_SetHandledPID(ocapi, &contr->inst.pid)) {
|
||||
int_error();
|
||||
ControllerDestr(contr);
|
||||
|
@ -596,12 +682,14 @@ ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mIS
|
|||
memset(plink, 0, sizeof(PLInst_t));
|
||||
plink->st = cst;
|
||||
plink->inst.st = cst;
|
||||
mISDN_init_instance(&plink->inst, ocapi, plink);
|
||||
mISDN_init_instance(&plink->inst, ocapi, plink, NULL);
|
||||
plink->inst.pid.layermask |= ISDN_LAYER(4);
|
||||
plink->inst.down.stat = IF_NOACTIV;
|
||||
// plink->inst.down.stat = IF_NOACTIV;
|
||||
list_add_tail(&plink->list, &contr->linklist);
|
||||
}
|
||||
spin_lock_irqsave(&ocapi->lock, flags);
|
||||
list_add_tail(&contr->list, &ocapi->ilist);
|
||||
spin_unlock_irqrestore(&ocapi->lock, flags);
|
||||
contr->entity = MISDN_ENTITY_NONE;
|
||||
retval = ocapi->ctrl(&contr->inst, MGR_NEWENTITY | REQUEST, NULL);
|
||||
if (retval) {
|
||||
|
@ -613,14 +701,14 @@ ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mIS
|
|||
{
|
||||
char tmp[10];
|
||||
|
||||
sprintf(tmp, "mISDN%d", st->id);
|
||||
sprintf(tmp, "mISDN%d", (st->id >> 8) & 0xff);
|
||||
contr->ctrl = cdrv_if->attach_ctr(&mISDN_driver, tmp, contr);
|
||||
if (!contr->ctrl)
|
||||
retval = -ENODEV;
|
||||
}
|
||||
#else
|
||||
contr->ctrl->owner = THIS_MODULE;
|
||||
sprintf(contr->ctrl->name, "mISDN%d", st->id);
|
||||
sprintf(contr->ctrl->name, "mISDN%d", contr->addr);
|
||||
contr->ctrl->driver_name = "mISDN";
|
||||
contr->ctrl->driverdata = contr;
|
||||
contr->ctrl->register_appl = RegisterApplication;
|
||||
|
@ -633,10 +721,12 @@ ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mIS
|
|||
retval = attach_capi_ctr(contr->ctrl);
|
||||
#endif
|
||||
if (!retval) {
|
||||
printk(KERN_DEBUG "contr->addr(%02x) cnr(%02x) st(%08x)\n",
|
||||
contr->addr, contr->ctrl->cnr, st->id);
|
||||
contr->addr = contr->ctrl->cnr;
|
||||
plciInit(contr);
|
||||
ocapi->ctrl(st, MGR_REGLAYER | INDICATION, &contr->inst);
|
||||
contr->inst.up.stat = IF_DOWN;
|
||||
// contr->inst.up.stat = IF_DOWN;
|
||||
*contr_p = contr;
|
||||
} else {
|
||||
ControllerDestr(contr);
|
||||
|
@ -674,14 +764,11 @@ ControllerSelChannel(Controller_t *contr, u_int channel)
|
|||
int
|
||||
ControllerNextId(Controller_t *contr)
|
||||
{
|
||||
u_long flags;
|
||||
int id;
|
||||
|
||||
spin_lock_irqsave(&contr->id_lock, flags);
|
||||
id = contr->next_id++;
|
||||
if (id == 0x7fff)
|
||||
contr->next_id = 1;
|
||||
spin_unlock_irqrestore(&contr->id_lock, flags);
|
||||
id |= (contr->entity << 16);
|
||||
return(id);
|
||||
}
|
||||
|
|
|
@ -22,14 +22,14 @@
|
|||
static char *mISDN_core_revision = "$Revision$";
|
||||
|
||||
LIST_HEAD(mISDN_objectlist);
|
||||
rwlock_t mISDN_objects_lock = RW_LOCK_UNLOCKED;
|
||||
static rwlock_t mISDN_objects_lock = RW_LOCK_UNLOCKED;
|
||||
|
||||
int core_debug;
|
||||
|
||||
static u_char entityarray[MISDN_MAX_ENTITY/8];
|
||||
static spinlock_t entity_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static int debug;
|
||||
static uint debug;
|
||||
static int obj_id;
|
||||
|
||||
#ifdef MODULE
|
||||
|
@ -37,7 +37,8 @@ MODULE_AUTHOR("Karsten Keil");
|
|||
#ifdef MODULE_LICENSE
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
MODULE_PARM(debug, "1i");
|
||||
module_param (debug, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC (debug, "mISDN core debug mask");
|
||||
#endif
|
||||
|
||||
typedef struct _mISDN_thread {
|
||||
|
@ -49,10 +50,10 @@ typedef struct _mISDN_thread {
|
|||
struct sk_buff_head workq;
|
||||
} mISDN_thread_t;
|
||||
|
||||
#define mISDN_TFLAGS_STARTED 1
|
||||
#define mISDN_TFLAGS_RMMOD 2
|
||||
#define mISDN_TFLAGS_ACTIV 3
|
||||
#define mISDN_TFLAGS_TEST 4
|
||||
#define mISDN_TFLAGS_STARTED 0
|
||||
#define mISDN_TFLAGS_RMMOD 1
|
||||
#define mISDN_TFLAGS_ACTIV 2
|
||||
#define mISDN_TFLAGS_TEST 3
|
||||
|
||||
static mISDN_thread_t mISDN_thread;
|
||||
|
||||
|
@ -97,7 +98,7 @@ mISDNd(void *data)
|
|||
break;
|
||||
if (hkt->notify != NULL)
|
||||
up(hkt->notify);
|
||||
interruptible_sleep_on(&hkt->waitq);
|
||||
wait_event_interruptible(hkt->waitq, ((!skb_queue_empty(&hkt->workq)) || (hkt->Flags & 0xfffffffe)));
|
||||
if (test_and_clear_bit(mISDN_TFLAGS_RMMOD, &hkt->Flags))
|
||||
break;
|
||||
while ((skb = skb_dequeue(&hkt->workq))) {
|
||||
|
@ -118,6 +119,7 @@ mISDNd(void *data)
|
|||
err--; /* to free skb */
|
||||
}
|
||||
break;
|
||||
#ifdef FIXME
|
||||
case MGR_QUEUEIF:
|
||||
err = hhe->func.iff(hhe->data[0], skb);
|
||||
if (err) {
|
||||
|
@ -125,6 +127,7 @@ mISDNd(void *data)
|
|||
hhe->addr, hhe->prim, err);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
int_error();
|
||||
printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) unknown\n",
|
||||
|
@ -219,21 +222,7 @@ find_object_module(int protocol) {
|
|||
return(NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_object(mISDNobject_t *obj) {
|
||||
mISDNstack_t *st, *nst;
|
||||
mISDNlayer_t *layer, *nl;
|
||||
mISDNinstance_t *inst;
|
||||
|
||||
list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) {
|
||||
list_for_each_entry_safe(layer, nl, &st->layerlist, list) {
|
||||
inst = layer->inst;
|
||||
if (inst && inst->obj == obj)
|
||||
inst->obj->own_ctrl(st, MGR_RELEASE | INDICATION, inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FIXME
|
||||
static int
|
||||
dummy_if(mISDNif_t *hif, struct sk_buff *skb)
|
||||
{
|
||||
|
@ -251,6 +240,7 @@ dummy_if(mISDNif_t *hif, struct sk_buff *skb)
|
|||
dev_kfree_skb_any(skb);
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
mISDNinstance_t *
|
||||
get_next_instance(mISDNstack_t *st, mISDN_pid_t *pid)
|
||||
|
@ -335,6 +325,7 @@ sel_channel(mISDNstack_t *st, channel_info_t *ci)
|
|||
return(err);
|
||||
}
|
||||
|
||||
#ifdef FIXME
|
||||
static int
|
||||
disconnect_if(mISDNinstance_t *inst, u_int prim, mISDNif_t *hif) {
|
||||
int err = 0;
|
||||
|
@ -371,6 +362,7 @@ add_if(mISDNinstance_t *inst, u_int prim, mISDNif_t *hif) {
|
|||
inst->obj->own_ctrl(inst, prim, hif);
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static char tmpbuf[4096];
|
||||
static int
|
||||
|
@ -394,15 +386,18 @@ get_hdevice(mISDNdevice_t **dev, int *typ)
|
|||
return(-EINVAL);
|
||||
if (!typ)
|
||||
return(-EINVAL);
|
||||
#ifdef FIXME
|
||||
if (*typ == mISDN_RAW_DEVICE) {
|
||||
*dev = get_free_rawdevice();
|
||||
if (!(*dev))
|
||||
return(-ENODEV);
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
return(-EINVAL);
|
||||
}
|
||||
|
||||
#ifdef FIXME
|
||||
static int
|
||||
mgr_queue(void *data, u_int prim, struct sk_buff *skb)
|
||||
{
|
||||
|
@ -414,6 +409,8 @@ mgr_queue(void *data, u_int prim, struct sk_buff *skb)
|
|||
return(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int central_manager(void *, u_int, void *);
|
||||
|
||||
static int
|
||||
|
@ -423,6 +420,7 @@ set_stack_req(mISDNstack_t *st, mISDN_pid_t *pid)
|
|||
mISDN_headext_t *hhe;
|
||||
mISDN_pid_t *npid;
|
||||
u_char *pbuf = NULL;
|
||||
int err;
|
||||
|
||||
if (!(skb = alloc_skb(sizeof(mISDN_pid_t) + pid->maxplen, GFP_ATOMIC)))
|
||||
return(-ENOMEM);
|
||||
|
@ -431,9 +429,11 @@ set_stack_req(mISDNstack_t *st, mISDN_pid_t *pid)
|
|||
hhe->addr = MGR_FUNCTION;
|
||||
hhe->data[0] = st;
|
||||
npid = (mISDN_pid_t *)skb_put(skb, sizeof(mISDN_pid_t));
|
||||
if (pid->pbuf)
|
||||
if (pid->maxplen)
|
||||
pbuf = skb_put(skb, pid->maxplen);
|
||||
copy_pid(npid, pid, pbuf);
|
||||
err = copy_pid(npid, pid, pbuf);
|
||||
if (err) // FIXME error handling
|
||||
int_errtxt("copy_pid error %d", err);
|
||||
hhe->func.ctrl = central_manager;
|
||||
skb_queue_tail(&mISDN_thread.workq, skb);
|
||||
wake_up_interruptible(&mISDN_thread.waitq);
|
||||
|
@ -506,7 +506,7 @@ new_entity(mISDNinstance_t *inst)
|
|||
printk(KERN_WARNING "mISDN: no more entity available(max %d)\n", MISDN_MAX_ENTITY);
|
||||
return(ret);
|
||||
}
|
||||
ret = inst->obj->own_ctrl(inst, MGR_NEWENTITY | CONFIRM, (void *)entity);
|
||||
ret = inst->obj->own_ctrl(inst, MGR_NEWENTITY | CONFIRM, (void *)((u_long)entity));
|
||||
if (ret)
|
||||
mISDN_delete_entity(entity);
|
||||
return(ret);
|
||||
|
@ -524,7 +524,7 @@ static int central_manager(void *data, u_int prim, void *arg) {
|
|||
return(new_entity(data));
|
||||
case MGR_DELENTITY | REQUEST:
|
||||
case MGR_DELENTITY | INDICATION:
|
||||
return(mISDN_delete_entity((int)arg));
|
||||
return(mISDN_delete_entity((u_long)arg & 0xffffffff));
|
||||
case MGR_REGLAYER | INDICATION:
|
||||
return(register_layer(st, arg));
|
||||
case MGR_REGLAYER | REQUEST:
|
||||
|
@ -535,48 +535,62 @@ static int central_manager(void *data, u_int prim, void *arg) {
|
|||
return(-EINVAL);
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
return(unregister_instance(data));
|
||||
#ifdef FIXME
|
||||
case MGR_DISCONNECT | REQUEST:
|
||||
case MGR_DISCONNECT | INDICATION:
|
||||
return(disconnect_if(data, prim, arg));
|
||||
#endif
|
||||
case MGR_GETDEVICE | REQUEST:
|
||||
return(get_hdevice(data, arg));
|
||||
case MGR_DELDEVICE | REQUEST:
|
||||
return(free_device(data));
|
||||
#ifdef FIXME
|
||||
case MGR_QUEUEIF | REQUEST:
|
||||
return(mgr_queue(data, MGR_QUEUEIF, arg));
|
||||
#endif
|
||||
}
|
||||
if (!data)
|
||||
return(-EINVAL);
|
||||
switch(prim) {
|
||||
case MGR_ADDLAYER | REQUEST:
|
||||
return(preregister_layer(st, arg));
|
||||
case MGR_SETSTACK | REQUEST:
|
||||
/* can sleep in case of module reload */
|
||||
return(set_stack_req(st, arg));
|
||||
case MGR_SETSTACK_NW | REQUEST:
|
||||
return(set_stack(st, arg));
|
||||
case MGR_CLEARSTACK | REQUEST:
|
||||
return(clear_stack(st));
|
||||
return(clear_stack(st, arg ? 1 : 0));
|
||||
case MGR_DELSTACK | REQUEST:
|
||||
return(release_stack(st));
|
||||
case MGR_SELCHANNEL | REQUEST:
|
||||
return(sel_channel(st, arg));
|
||||
case MGR_STOPSTACK | REQUEST:
|
||||
return(mISDN_start_stop(st, 0));
|
||||
case MGR_STARTSTACK | REQUEST:
|
||||
return(mISDN_start_stop(st, 1));
|
||||
#ifdef FIXME
|
||||
case MGR_ADDIF | REQUEST:
|
||||
return(add_if(data, prim, arg));
|
||||
#endif
|
||||
case MGR_CTRLREADY | INDICATION:
|
||||
return(queue_ctrl_ready(st, arg));
|
||||
case MGR_ADDSTPARA | REQUEST:
|
||||
case MGR_CLRSTPARA | REQUEST:
|
||||
return(change_stack_para(st, prim, arg));
|
||||
#ifdef FIXME
|
||||
case MGR_CONNECT | REQUEST:
|
||||
return(mISDN_ConnectIF(data, arg));
|
||||
#endif
|
||||
case MGR_EVALSTACK | REQUEST:
|
||||
return(evaluate_stack_pids(data, arg));
|
||||
case MGR_GLOBALOPT | REQUEST:
|
||||
case MGR_LOADFIRM | REQUEST:
|
||||
if (st->mgr && st->mgr->obj && st->mgr->obj->own_ctrl)
|
||||
return(st->mgr->obj->own_ctrl(st->mgr, prim, arg));
|
||||
break;
|
||||
break;
|
||||
case MGR_DEBUGDATA | REQUEST:
|
||||
return(debugout(data, arg));
|
||||
return(debugout(data, arg));
|
||||
default:
|
||||
if (debug)
|
||||
printk(KERN_WARNING "manager prim %x not handled\n", prim);
|
||||
|
@ -587,6 +601,7 @@ static int central_manager(void *data, u_int prim, void *arg) {
|
|||
|
||||
int mISDN_register(mISDNobject_t *obj) {
|
||||
u_long flags;
|
||||
int retval;
|
||||
|
||||
if (!obj)
|
||||
return(-EINVAL);
|
||||
|
@ -601,7 +616,14 @@ int mISDN_register(mISDNobject_t *obj) {
|
|||
obj->id);
|
||||
if (core_debug & DEBUG_CORE_FUNC)
|
||||
printk(KERN_DEBUG "mISDN_register: obj(%p)\n", obj);
|
||||
return(0);
|
||||
retval = mISDN_register_sysfs_obj(obj);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "mISDN_register class_device_register return(%d)\n", retval);
|
||||
write_lock_irqsave(&mISDN_objects_lock, flags);
|
||||
list_del(&obj->list);
|
||||
write_unlock_irqrestore(&mISDN_objects_lock, flags);
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
int mISDN_unregister(mISDNobject_t *obj) {
|
||||
|
@ -615,13 +637,14 @@ int mISDN_unregister(mISDNobject_t *obj) {
|
|||
if (obj->DPROTO.protocol[0])
|
||||
release_stacks(obj);
|
||||
else
|
||||
remove_object(obj);
|
||||
cleanup_object(obj);
|
||||
write_lock_irqsave(&mISDN_objects_lock, flags);
|
||||
list_del(&obj->list);
|
||||
write_unlock_irqrestore(&mISDN_objects_lock, flags);
|
||||
if (core_debug & DEBUG_CORE_FUNC)
|
||||
printk(KERN_DEBUG "mISDN_unregister: mISDN_objectlist(%p<-%p->%p)\n",
|
||||
mISDN_objectlist.prev, &mISDN_objectlist, mISDN_objectlist.next);
|
||||
class_device_unregister(&obj->class_dev);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
@ -638,9 +661,14 @@ mISDNInit(void)
|
|||
if (err)
|
||||
return(err);
|
||||
#endif
|
||||
err = mISDN_sysfs_init();
|
||||
if (err)
|
||||
goto sysfs_fail;
|
||||
|
||||
err = init_mISDNdev(debug);
|
||||
if (err)
|
||||
return(err);
|
||||
goto dev_fail;
|
||||
|
||||
init_waitqueue_head(&mISDN_thread.waitq);
|
||||
skb_queue_head_init(&mISDN_thread.workq);
|
||||
mISDN_thread.notify = &sem;
|
||||
|
@ -650,27 +678,24 @@ mISDNInit(void)
|
|||
test_and_set_bit(mISDN_TFLAGS_TEST, &mISDN_thread.Flags);
|
||||
wake_up_interruptible(&mISDN_thread.waitq);
|
||||
return(err);
|
||||
|
||||
dev_fail:
|
||||
mISDN_sysfs_cleanup();
|
||||
sysfs_fail:
|
||||
#ifdef MISDN_MEMDEBUG
|
||||
__mid_cleanup();
|
||||
#endif
|
||||
return(err);
|
||||
}
|
||||
|
||||
void mISDN_cleanup(void) {
|
||||
DECLARE_MUTEX_LOCKED(sem);
|
||||
mISDNstack_t *st, *nst;
|
||||
|
||||
free_mISDNdev();
|
||||
if (!list_empty(&mISDN_objectlist)) {
|
||||
printk(KERN_WARNING "mISDNcore mISDN_objects not empty\n");
|
||||
}
|
||||
if (!list_empty(&mISDN_stacklist)) {
|
||||
printk(KERN_WARNING "mISDNcore mISDN_stacklist not empty\n");
|
||||
list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) {
|
||||
printk(KERN_WARNING "mISDNcore st %x still in list\n",
|
||||
st->id);
|
||||
if (list_empty(&st->list)) {
|
||||
printk(KERN_WARNING "mISDNcore st == next\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
check_stacklist();
|
||||
if (mISDN_thread.thread) {
|
||||
/* abort mISDNd kernel thread */
|
||||
mISDN_thread.notify = &sem;
|
||||
|
@ -682,6 +707,7 @@ void mISDN_cleanup(void) {
|
|||
#ifdef MISDN_MEMDEBUG
|
||||
__mid_cleanup();
|
||||
#endif
|
||||
mISDN_sysfs_cleanup();
|
||||
printk(KERN_DEBUG "mISDNcore unloaded\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -19,46 +19,65 @@
|
|||
|
||||
/* debugging */
|
||||
#define DEBUG_CORE_FUNC 0x0001
|
||||
#define DEBUG_DUMMY_FUNC 0x0002
|
||||
//#define DEBUG_DUMMY_FUNC 0x0002
|
||||
#define DEBUG_MSG_THREAD_ERR 0x0010
|
||||
#define DEBUG_MSG_THREAD_INFO 0x0020
|
||||
#define DEBUG_QUEUE_FUNC 0x0040
|
||||
#define DEBUG_DEV_OP 0x0100
|
||||
#define DEBUG_MGR_FUNC 0x0200
|
||||
#define DEBUG_DEV_TIMER 0x0400
|
||||
#define DEBUG_RDATA 0x1000
|
||||
#define DEBUG_WDATA 0x2000
|
||||
|
||||
/* from mISDN_dev.c */
|
||||
/* from udevice.c */
|
||||
|
||||
extern int init_mISDNdev(int);
|
||||
extern int free_mISDNdev(void);
|
||||
extern mISDNdevice_t *get_free_rawdevice(void);
|
||||
extern int free_device(mISDNdevice_t *dev);
|
||||
|
||||
/* from mISDN_stack.c */
|
||||
|
||||
extern struct list_head mISDN_stacklist;
|
||||
extern struct list_head mISDN_instlist;
|
||||
/* from stack.c */
|
||||
|
||||
extern void get_stack_info(struct sk_buff *);
|
||||
extern int get_stack_cnt(void);
|
||||
extern void check_stacklist(void);
|
||||
extern void cleanup_object(mISDNobject_t *);
|
||||
extern mISDNstack_t *get_stack4id(u_int);
|
||||
extern int mISDN_start_stack_thread(mISDNstack_t *);
|
||||
extern mISDNstack_t *new_stack(mISDNstack_t *, mISDNinstance_t *);
|
||||
extern int mISDN_start_stop(mISDNstack_t *, int);
|
||||
extern int release_stack(mISDNstack_t *);
|
||||
extern int do_for_all_layers(void *, u_int, void *);
|
||||
extern int change_stack_para(mISDNstack_t *, u_int, mISDN_stPara_t *);
|
||||
extern void release_stacks(mISDNobject_t *);
|
||||
extern int copy_pid(mISDN_pid_t *, mISDN_pid_t *, u_char *);
|
||||
extern int set_stack(mISDNstack_t *, mISDN_pid_t *);
|
||||
extern int clear_stack(mISDNstack_t *);
|
||||
extern int clear_stack(mISDNstack_t *, int);
|
||||
extern int evaluate_stack_pids(mISDNstack_t *, mISDN_pid_t *);
|
||||
extern mISDNlayer_t *getlayer4lay(mISDNstack_t *, int);
|
||||
extern mISDNinstance_t *getlayer4lay(mISDNstack_t *, int);
|
||||
extern mISDNinstance_t *get_instance(mISDNstack_t *, int, int);
|
||||
|
||||
/* from mISDN_core.c */
|
||||
/* from sysfs_obj.c */
|
||||
extern int mISDN_register_sysfs_obj(mISDNobject_t *);
|
||||
extern int mISDN_sysfs_init(void);
|
||||
extern void mISDN_sysfs_cleanup(void);
|
||||
|
||||
extern struct list_head mISDN_objectlist;
|
||||
/* from sysfs_inst.c */
|
||||
extern int mISDN_register_sysfs_inst(mISDNinstance_t *);
|
||||
extern void mISDN_unregister_sysfs_inst(mISDNinstance_t *);
|
||||
extern int mISDN_sysfs_inst_init(void);
|
||||
extern void mISDN_sysfs_inst_cleanup(void);
|
||||
|
||||
/* from sysfs_stack.c */
|
||||
extern int mISDN_register_sysfs_stack(mISDNstack_t *);
|
||||
extern void mISDN_unregister_sysfs_st(mISDNstack_t *);
|
||||
extern int mISDN_sysfs_st_init(void);
|
||||
extern void mISDN_sysfs_st_cleanup(void);
|
||||
|
||||
/* from core.c */
|
||||
extern int core_debug;
|
||||
|
||||
extern int register_layer(mISDNstack_t *, mISDNinstance_t *);
|
||||
extern int preregister_layer(mISDNstack_t *, mISDNinstance_t *);
|
||||
extern int unregister_instance(mISDNinstance_t *);
|
||||
extern mISDNinstance_t *get_next_instance(mISDNstack_t *, mISDN_pid_t *);
|
||||
extern mISDNobject_t *get_object(int);
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* Author Karsten Keil (keil@isdn4linux.de)
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mISDNif.h>
|
||||
#include "layer1.h"
|
||||
#include "helper.h"
|
||||
#include "dchannel.h"
|
||||
|
||||
static void
|
||||
dchannel_bh(dchannel_t *dch)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
mISDN_head_t *hh;
|
||||
|
||||
if (!dch)
|
||||
return;
|
||||
if (dch->debug)
|
||||
printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event);
|
||||
#if 0
|
||||
if (test_and_clear_bit(D_CLEARBUSY, &dch->event)) {
|
||||
if (dch->debug)
|
||||
mISDN_debugprint(&dch->inst, "D-Channel Busy cleared");
|
||||
stptr = dch->stlist;
|
||||
while (stptr != NULL) {
|
||||
stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (test_and_clear_bit(D_XMTBUFREADY, &dch->event)) {
|
||||
if ((skb = dch->next_skb)) {
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
dch->next_skb = NULL;
|
||||
skb_trim(skb, 0);
|
||||
if (if_newhead(&dch->inst.up, PH_DATA_CNF, hh->dinfo, skb))
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(D_RCVBUFREADY, &dch->event)) {
|
||||
while ((skb = skb_dequeue(&dch->rqueue))) {
|
||||
err = if_newhead(&dch->inst.up, PH_DATA_IND, MISDN_ID_ANY, skb);
|
||||
if (err < 0) {
|
||||
printk(KERN_WARNING "%s: deliver err %d\n", __FUNCTION__, err);
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dch->hw_bh)
|
||||
dch->hw_bh(dch);
|
||||
}
|
||||
|
||||
int
|
||||
mISDN_init_dch(dchannel_t *dch) {
|
||||
if (!(dch->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
|
||||
printk(KERN_WARNING
|
||||
"mISDN: No memory for dlog\n");
|
||||
return(-ENOMEM);
|
||||
}
|
||||
if (!(dch->tx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
|
||||
printk(KERN_WARNING
|
||||
"mISDN: No memory for dchannel tx_buf\n");
|
||||
kfree(dch->dlog);
|
||||
dch->dlog = NULL;
|
||||
return(-ENOMEM);
|
||||
}
|
||||
dch->hw = NULL;
|
||||
dch->rx_skb = NULL;
|
||||
dch->tx_idx = 0;
|
||||
dch->next_skb = NULL;
|
||||
dch->event = 0;
|
||||
INIT_WORK(&dch->work, (void *)(void *)dchannel_bh, dch);
|
||||
dch->hw_bh = NULL;
|
||||
skb_queue_head_init(&dch->rqueue);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int
|
||||
mISDN_free_dch(dchannel_t *dch) {
|
||||
#ifdef HAS_WORKQUEUE
|
||||
if (dch->work.pending)
|
||||
printk(KERN_ERR "mISDN_free_dch work:(%lx)\n", dch->work.pending);
|
||||
#else
|
||||
if (dch->work.sync)
|
||||
printk(KERN_ERR "mISDN_free_dch work:(%lx)\n", dch->work.sync);
|
||||
#endif
|
||||
discard_queue(&dch->rqueue);
|
||||
if (dch->rx_skb) {
|
||||
dev_kfree_skb(dch->rx_skb);
|
||||
dch->rx_skb = NULL;
|
||||
}
|
||||
if (dch->tx_buf) {
|
||||
kfree(dch->tx_buf);
|
||||
dch->tx_buf = NULL;
|
||||
}
|
||||
if (dch->next_skb) {
|
||||
dev_kfree_skb(dch->next_skb);
|
||||
dch->next_skb = NULL;
|
||||
}
|
||||
if (dch->dlog) {
|
||||
kfree(dch->dlog);
|
||||
dch->dlog = NULL;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mISDN_init_dch);
|
||||
EXPORT_SYMBOL(mISDN_free_dch);
|
|
@ -1,92 +0,0 @@
|
|||
/* $Id$
|
||||
*
|
||||
* Basic declarations for dchannel HW
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mISDNif.h>
|
||||
#ifdef HAS_WORKQUEUE
|
||||
#include <linux/workqueue.h>
|
||||
#else
|
||||
#include <linux/tqueue.h>
|
||||
#endif
|
||||
#include <linux/smp.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/timer.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/skbuff.h>
|
||||
#ifdef MISDN_MEMDEBUG
|
||||
#include "memdbg.h"
|
||||
#endif
|
||||
|
||||
#define MAX_DFRAME_LEN_L1 300
|
||||
#define MAX_MON_FRAME 32
|
||||
#define MAX_DLOG_SPACE 2048
|
||||
|
||||
#define FLG_TWO_DCHAN 4
|
||||
#define FLG_TX_BUSY 5
|
||||
#define FLG_TX_NEXT 6
|
||||
#define FLG_L1_DBUSY 7
|
||||
#define FLG_DBUSY_TIMER 8
|
||||
#define FLG_LOCK_ATOMIC 9
|
||||
#define FLG_ARCOFI_TIMER 10
|
||||
#define FLG_ARCOFI_ERROR 11
|
||||
#define FLG_HW_L1_UINT 12
|
||||
#define FLG_HW_INIT 13
|
||||
|
||||
typedef struct _dchannel_t {
|
||||
int channel;
|
||||
mISDNinstance_t inst;
|
||||
u_long DFlags;
|
||||
u_int type;
|
||||
u_int ph_state;
|
||||
u_char (*read_reg) (void *, u_char);
|
||||
void (*write_reg) (void *, u_char, u_char);
|
||||
void (*read_fifo) (void *, u_char *, int);
|
||||
void (*write_fifo) (void *, u_char *, int);
|
||||
char *dlog;
|
||||
int debug;
|
||||
struct sk_buff *rx_skb;
|
||||
struct sk_buff *next_skb;
|
||||
u_char *tx_buf;
|
||||
int tx_idx;
|
||||
int tx_len;
|
||||
int up_headerlen;
|
||||
int err_crc;
|
||||
int err_tx;
|
||||
int err_rx;
|
||||
void *hw;
|
||||
struct timer_list dbusytimer;
|
||||
u_long event;
|
||||
struct sk_buff_head rqueue; /* D-channel receive queue */
|
||||
struct work_struct work;
|
||||
void (*hw_bh) (struct _dchannel_t *);
|
||||
} dchannel_t;
|
||||
|
||||
#define MON0_RX 1
|
||||
#define MON1_RX 2
|
||||
#define MON0_TX 4
|
||||
#define MON1_TX 8
|
||||
|
||||
extern int mISDN_init_dch(dchannel_t *);
|
||||
extern int mISDN_free_dch(dchannel_t *);
|
||||
|
||||
static inline void
|
||||
dch_set_para(dchannel_t *dch, mISDN_stPara_t *stp)
|
||||
{
|
||||
if (stp)
|
||||
dch->up_headerlen = stp->up_headerlen;
|
||||
else
|
||||
dch->up_headerlen = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
dchannel_sched_event(dchannel_t *dch, int event)
|
||||
{
|
||||
test_and_set_bit(event, &dch->event);
|
||||
schedule_work(&dch->work);
|
||||
}
|
|
@ -5,9 +5,13 @@
|
|||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*
|
||||
*/
|
||||
#ifndef MISDN_DEBUG_H
|
||||
|
||||
#define MISDN_DEBUG_MANAGER 0x10000
|
||||
|
||||
extern void vmISDN_debug(int id, char *head, char *fmt, va_list args);
|
||||
extern void mISDN_debug(int id, char *head, char *fmt, ...);
|
||||
extern char * mISDN_getrev(const char *revision);
|
||||
extern void mISDN_debugprint(mISDNinstance_t *inst, char *fmt, ...);
|
||||
extern int mISDN_QuickHex(char *, u_char *, int);
|
||||
#endif
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
//#define AUTOJITTER
|
||||
|
||||
#define DEBUG_DSP_MGR 0x0001
|
||||
#define DEBUG_DSP_CORE 0x0002
|
||||
#define DEBUG_DSP_DTMF 0x0004
|
||||
|
@ -29,19 +27,31 @@
|
|||
#define DSP_OPT_ULAW (1<<0)
|
||||
#define DSP_OPT_NOHARDWARE (1<<1)
|
||||
|
||||
#ifdef HAS_WORKQUEUE
|
||||
#include <linux/workqueue.h>
|
||||
#else
|
||||
#include <linux/tqueue.h>
|
||||
#endif
|
||||
#define FEAT_STATE_INIT 1
|
||||
#define FEAT_STATE_WAIT 2
|
||||
|
||||
#include <linux/timer.h>
|
||||
|
||||
#ifdef MISDN_MEMDEBUG
|
||||
#include "memdbg.h"
|
||||
#endif
|
||||
|
||||
#include "dsp_ecdis.h"
|
||||
|
||||
/*
|
||||
* You are now able to choose between the Mark2 and the
|
||||
* kb1 Echo cancellor. Just comment the one and comment
|
||||
* out the other.
|
||||
*/
|
||||
|
||||
//#include "dsp_mec2.h"
|
||||
//#include "dsp_kb1ec.h"
|
||||
#include "dsp_mg2ec.h"
|
||||
|
||||
extern int dsp_options;
|
||||
extern int dsp_debug;
|
||||
extern int dsp_poll;
|
||||
extern int dsp_tics;
|
||||
|
||||
/***************
|
||||
* audio stuff *
|
||||
|
@ -60,16 +70,25 @@ extern void dsp_audio_generate_seven(void);
|
|||
extern void dsp_audio_generate_mix_table(void);
|
||||
extern void dsp_audio_generate_ulaw_samples(void);
|
||||
extern void dsp_audio_generate_volume_changes(void);
|
||||
extern u8 silence;
|
||||
extern u8 dsp_silence;
|
||||
|
||||
|
||||
/*************
|
||||
* cmx stuff *
|
||||
*************/
|
||||
|
||||
#define CMX_BUFF_SIZE 0x4000 /* must be 2**n */
|
||||
#define CMX_BUFF_HALF 0x2000 /* CMX_BUFF_SIZE / 2 */
|
||||
#define CMX_BUFF_MASK 0x3fff /* CMX_BUFF_SIZE - 1 */
|
||||
#define MAX_POLL 256 /* maximum number of send-chunks */
|
||||
|
||||
#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */
|
||||
#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */
|
||||
#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */
|
||||
|
||||
/* how many seconds will we check the lowest delay until the jitter buffer
|
||||
is reduced by that delay */
|
||||
#define MAX_SECONDS_JITTER_CHECK 5
|
||||
|
||||
extern struct timer_list dsp_spl_tl;
|
||||
extern u64 dsp_spl_jiffies;
|
||||
|
||||
/* the structure of conferences:
|
||||
*
|
||||
|
@ -93,11 +112,6 @@ typedef struct _conference {
|
|||
struct list_head mlist;
|
||||
int software; /* conf is processed by software */
|
||||
int hardware; /* conf is processed by hardware */
|
||||
//#ifndef AUTOJITTER
|
||||
int largest; /* largest frame received in conf's life. */
|
||||
//#endif
|
||||
int W_min, W_max; /* min/maximum rx-write pointer of members */
|
||||
s32 conf_buff[CMX_BUFF_SIZE];
|
||||
} conference_t;
|
||||
|
||||
extern mISDNobject_t dsp_obj;
|
||||
|
@ -109,6 +123,8 @@ extern mISDNobject_t dsp_obj;
|
|||
|
||||
#define DSP_DTMF_NPOINTS 102
|
||||
|
||||
#define ECHOCAN_BUFLEN 4*128
|
||||
|
||||
typedef struct _dtmf_t {
|
||||
int software; /* dtmf uses software decoding */
|
||||
int hardware; /* dtmf uses hardware decoding */
|
||||
|
@ -120,6 +136,12 @@ typedef struct _dtmf_t {
|
|||
} dtmf_t;
|
||||
|
||||
|
||||
/****************
|
||||
* cancel stuff *
|
||||
****************/
|
||||
|
||||
|
||||
|
||||
/***************
|
||||
* tones stuff *
|
||||
***************/
|
||||
|
@ -147,6 +169,7 @@ struct dsp_features {
|
|||
int pcm_id; /* unique id to identify the pcm bus (or -1) */
|
||||
int pcm_slots; /* number of slots on the pcm bus */
|
||||
int pcm_banks; /* number of IO banks of pcm bus */
|
||||
int has_jitter; /* data is jittered and unsorted */
|
||||
};
|
||||
|
||||
typedef struct _dsp {
|
||||
|
@ -159,8 +182,6 @@ typedef struct _dsp {
|
|||
tone_t tone;
|
||||
dtmf_t dtmf;
|
||||
int tx_volume, rx_volume;
|
||||
struct work_struct sendwork; /* event for sending data */
|
||||
int tx_pending;
|
||||
|
||||
/* conference stuff */
|
||||
u32 conf_id;
|
||||
|
@ -168,20 +189,18 @@ typedef struct _dsp {
|
|||
conf_member_t *member;
|
||||
|
||||
/* buffer stuff */
|
||||
int largest; /* largest frame received in dsp's life. */
|
||||
int R_tx, W_tx; /* pointers of transmit buffer */
|
||||
int R_rx, W_rx; /* pointers of receive buffer and conference buffer */
|
||||
int rx_W; /* current write pos for data without timestamp */
|
||||
int rx_R; /* current read pos for transmit clock */
|
||||
int tx_W; /* current write pos for transmit data */
|
||||
int tx_R; /* current read pos for transmit clock */
|
||||
int delay[MAX_SECONDS_JITTER_CHECK];
|
||||
u8 tx_buff[CMX_BUFF_SIZE];
|
||||
u8 rx_buff[CMX_BUFF_SIZE];
|
||||
#ifdef AUTOJITTER
|
||||
int tx_delay; /* used to find the delay of tx buffer */
|
||||
int tx_delay_count;
|
||||
int rx_delay; /* used to find the delay of rx buffer */
|
||||
int rx_delay_count;
|
||||
#endif
|
||||
|
||||
/* hardware stuff */
|
||||
struct dsp_features features; /* features */
|
||||
struct timer_list feature_tl;
|
||||
int feature_state;
|
||||
int pcm_slot_rx; /* current PCM slot (or -1) */
|
||||
int pcm_bank_rx;
|
||||
int pcm_slot_tx;
|
||||
|
@ -200,6 +219,25 @@ typedef struct _dsp {
|
|||
u8 bf_crypt_inring[16];
|
||||
u8 bf_data_out[9];
|
||||
int bf_sync;
|
||||
|
||||
/* echo cancellation stuff */
|
||||
int cancel_enable;
|
||||
struct echo_can_state * ec; /**< == NULL: echo cancellation disabled;
|
||||
!= NULL: echo cancellation enabled */
|
||||
|
||||
echo_can_disable_detector_state_t* ecdis_rd;
|
||||
echo_can_disable_detector_state_t* ecdis_wr;
|
||||
|
||||
uint16_t echotimer;
|
||||
uint16_t echostate;
|
||||
uint16_t echolastupdate;
|
||||
|
||||
char txbuf[ECHOCAN_BUFLEN];
|
||||
int txbuflen;
|
||||
|
||||
char rxbuf[ECHOCAN_BUFLEN];
|
||||
int rxbuflen;
|
||||
|
||||
} dsp_t;
|
||||
|
||||
/* functions */
|
||||
|
@ -211,7 +249,11 @@ extern void dsp_cmx_debug(dsp_t *dsp);
|
|||
extern void dsp_cmx_hardware(conference_t *conf, dsp_t *dsp);
|
||||
extern int dsp_cmx_conf(dsp_t *dsp, u32 conf_id);
|
||||
extern void dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb);
|
||||
#ifdef OLDCMX
|
||||
extern struct sk_buff *dsp_cmx_send(dsp_t *dsp, int len, int dinfo);
|
||||
#else
|
||||
extern void dsp_cmx_send(void *data);
|
||||
#endif
|
||||
extern void dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb);
|
||||
extern int dsp_cmx_del_conf_member(dsp_t *dsp);
|
||||
extern int dsp_cmx_del_conf(conference_t *conf);
|
||||
|
@ -228,4 +270,8 @@ extern void dsp_bf_decrypt(dsp_t *dsp, u8 *data, int len);
|
|||
extern int dsp_bf_init(dsp_t *dsp, const u8 *key, unsigned int keylen);
|
||||
extern void dsp_bf_cleanup(dsp_t *dsp);
|
||||
|
||||
extern void dsp_cancel_tx(dsp_t *dsp, u8 *data, int len);
|
||||
extern void dsp_cancel_rx(dsp_t *dsp, u8 *data, int len);
|
||||
extern int dsp_cancel_init(dsp_t *dsp, int taps, int training, int delay);
|
||||
|
||||
|
||||
|
|
|
@ -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<ZT_CHUNKSIZE;x+=8)
|
||||
__ACSS(dst + x, src + x);
|
||||
}
|
||||
static inline void SCSS(volatile short *dst, const short *src)
|
||||
{
|
||||
int x;
|
||||
for (x=0;x<ZT_CHUNKSIZE;x+=8)
|
||||
__SCSS(dst + x, src + x);
|
||||
}
|
||||
#else
|
||||
#error No MMX for ZT_CHUNKSIZE < 8
|
||||
#endif
|
||||
#endif
|
||||
static inline int CONVOLVE(const int *coeffs, const short *hist, int len)
|
||||
{
|
||||
int sum;
|
||||
/* Divide length by 16 */
|
||||
len >>= 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<ntaps;i++) {
|
||||
correction = history[i] * nsuppr;
|
||||
taps[i] += correction;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void UPDATE2(volatile int *taps, volatile short *taps_short, const short *history, const int nsuppr, const int ntaps)
|
||||
{
|
||||
int i;
|
||||
int correction;
|
||||
#if 0
|
||||
ntaps >>= 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<ntaps;i++) {
|
||||
correction = history[i] * nsuppr;
|
||||
taps[i] += correction;
|
||||
taps_short[i] = taps[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<len;k++) {
|
||||
if (max < y[k]) {
|
||||
bestpos = k;
|
||||
max = y[k];
|
||||
}
|
||||
}
|
||||
*pos = (len - 1 - bestpos);
|
||||
return max;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#ifdef ZT_CHUNKSIZE
|
||||
static inline void ACSS(short *dst, short *src)
|
||||
{
|
||||
int x,sum;
|
||||
/* Add src to dst with saturation, storing in dst */
|
||||
for (x=0;x<ZT_CHUNKSIZE;x++) {
|
||||
sum = dst[x]+src[x];
|
||||
if (sum > 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<ZT_CHUNKSIZE;x++) {
|
||||
sum = dst[x]-src[x];
|
||||
if (sum > 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<len;x++)
|
||||
sum += (coeffs[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<len;x++)
|
||||
sum += coeffs[x] * hist[x];
|
||||
return sum;
|
||||
}
|
||||
|
||||
static inline void UPDATE(int *taps, const short *history, const int nsuppr, const int ntaps)
|
||||
{
|
||||
int i;
|
||||
int correction;
|
||||
for (i=0;i<ntaps;i++) {
|
||||
correction = history[i] * nsuppr;
|
||||
taps[i] += correction;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void UPDATE2(int *taps, short *taps_short, const short *history, const int nsuppr, const int ntaps)
|
||||
{
|
||||
int i;
|
||||
int correction;
|
||||
for (i=0;i<ntaps;i++) {
|
||||
correction = history[i] * nsuppr;
|
||||
taps[i] += correction;
|
||||
taps_short[i] = taps[i] >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
static inline short MAX16(const short *y, int len, int *pos)
|
||||
{
|
||||
int k;
|
||||
short max = 0;
|
||||
int bestpos = 0;
|
||||
for (k=0;k<len;k++) {
|
||||
if (max < y[k]) {
|
||||
bestpos = k;
|
||||
max = y[k];
|
||||
}
|
||||
}
|
||||
*pos = (len - 1 - bestpos);
|
||||
return max;
|
||||
}
|
||||
|
||||
#endif /* MMX */
|
||||
#endif /* _ZAPTEL_ARITH_H */
|
|
@ -301,7 +301,7 @@ u8 dsp_audio_ulaw_to_alaw[256] =
|
|||
0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
|
||||
};
|
||||
|
||||
u8 silence;
|
||||
u8 dsp_silence;
|
||||
|
||||
|
||||
/*****************************************************
|
||||
|
@ -324,7 +324,7 @@ dsp_audio_generate_s2law_table(void)
|
|||
}
|
||||
j = 255;
|
||||
while(i < 65536) {
|
||||
if (i-0x32768 > dsp_audio_law_to_s32[j])
|
||||
if (i-32768 > dsp_audio_law_to_s32[j])
|
||||
j--;
|
||||
dsp_audio_s16_to_law[(i-32768) & 0xffff] = j;
|
||||
i++;
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* biquad.h - General telephony bi-quad section routines (currently this just
|
||||
* handles canonic/type 2 form)
|
||||
*
|
||||
* Written by Steve Underwood <steveu@coppice.org>
|
||||
*
|
||||
* 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 ------------------------------------------------------------*/
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
* Blowfish encryption/decryption for mISDN_dsp.
|
||||
*
|
||||
* Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de)
|
||||
* Copyright Andreas Eversberg (jolly@jolly.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
|
@ -594,7 +594,7 @@ dsp_bf_init(dsp_t *dsp, const u8 *key, uint keylen)
|
|||
while(i < 9)
|
||||
{
|
||||
dsp->bf_crypt_out[i] = 0xff;
|
||||
dsp->bf_data_out[i] = silence;
|
||||
dsp->bf_data_out[i] = dsp_silence;
|
||||
i++;
|
||||
}
|
||||
dsp->bf_crypt_pos = 0;
|
||||
|
|
|
@ -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 <asm/i387.h>
|
||||
|
||||
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************/
|
|
@ -30,7 +30,7 @@
|
|||
/* HOW THE CMX WORKS:
|
||||
*
|
||||
* There are 3 types of interaction: One member is alone, in this case only
|
||||
* data flow is done.
|
||||
* data flow from upper to lower layer is done.
|
||||
* Two members will also exchange their data so they are crossconnected.
|
||||
* Three or more members will be added in a conference and will hear each
|
||||
* other but will not receive their own speech (echo) if not enabled.
|
||||
|
@ -40,37 +40,24 @@
|
|||
* - Force mixing of transmit data with other crossconnect/conference members.
|
||||
* - Echo generation to benchmark the delay of audio processing.
|
||||
* - Use hardware to minimize cpu load, disable FIFO load and minimize delay.
|
||||
* - Dejittering and clock generation.
|
||||
*
|
||||
* There are 3 buffers:
|
||||
*
|
||||
* The conference buffer
|
||||
*
|
||||
* R-3 R-2 R-1 W-2 W-1 W-3
|
||||
* | | | | | |
|
||||
* --+-------+-----+------+------+---+---------------
|
||||
* | |
|
||||
* W-min W-max
|
||||
*
|
||||
* The conference buffer is a ring buffer used to mix all data from all members.
|
||||
* To compensate the echo, data of individual member will later be substracted
|
||||
* before it is sent to that member. Each conference has a W-min and a W-max
|
||||
* pointer. Each individual member has a write pointer (W-x) and a read pointer
|
||||
* (R-x). W-min shows the lowest value of all W-x. The W-max shows the highest
|
||||
* value of all W-x. Whenever data is written, it is mixed by adding to the
|
||||
* existing sample value in the buffer. If W-max would increase, the additional
|
||||
* range is cleared so old data will be erased in the ring buffer.
|
||||
* There are 2 buffers:
|
||||
*
|
||||
*
|
||||
* RX-Buffer
|
||||
* R-1 W-1
|
||||
* R W
|
||||
* | |
|
||||
* ----------------+-------------+-------------------
|
||||
*
|
||||
* The rx-buffer is a ring buffer used to store the received data for each
|
||||
* individual member. To compensate echo, this data will later be substracted
|
||||
* from the conference's data before it is sent to that member. If only two
|
||||
* members are in one conference, this data is used to get the queued data from
|
||||
* the other member.
|
||||
* individual member. This is only the case if data needs to be dejittered
|
||||
* or in case of a conference where different clocks require reclocking.
|
||||
* The transmit-clock (R) will read the buffer.
|
||||
* If the clock overruns the write-pointer, we will have a buffer underrun.
|
||||
* If the write pointer always has a certain distance from the transmit-
|
||||
* clock, we will have a delay. The delay will dynamically be increased and
|
||||
* reduced.
|
||||
*
|
||||
*
|
||||
* TX-Buffer
|
||||
|
@ -84,28 +71,25 @@
|
|||
* (some) data is dropped so that it will not overrun.
|
||||
*
|
||||
*
|
||||
* Clock:
|
||||
*
|
||||
* A Clock is not required, if the data source has exactly one clock. In this
|
||||
* case the data source is forwarded to the destination.
|
||||
*
|
||||
* A Clock is required, because the data source
|
||||
* - has multiple clocks.
|
||||
* - has no clock due to jitter (VoIP).
|
||||
* In this case the system's clock is used. The clock resolution depends on
|
||||
* the jiffie resolution.
|
||||
*
|
||||
* If a member joins a conference:
|
||||
*
|
||||
* - If a member joins, its rx_buff is set to silence.
|
||||
* - If the conference reaches three members, the conf-buffer is cleared.
|
||||
* - When a member is joined, it will set its write and read pointer to W_max.
|
||||
* - If a member joins, its rx_buff is set to silence and change read pointer
|
||||
* to transmit clock.
|
||||
*
|
||||
* The procedure of received data from card is explained in cmx_receive.
|
||||
* The procedure of received data from user space is explained in cmx_transmit.
|
||||
*
|
||||
*
|
||||
* LIMITS:
|
||||
*
|
||||
* The max_queue value is 2* the samples of largest packet ever received by any
|
||||
* conference member from her card. It also changes during life of conference.
|
||||
*
|
||||
*
|
||||
* AUDIO PROCESS:
|
||||
*
|
||||
* Writing data to conference's and member's buffer is done by adding the sample
|
||||
* value to the existing ring buffer. Writing user space data to the member's
|
||||
* buffer is done by substracting the sample value from the existing ring
|
||||
* buffer.
|
||||
* The procedure of transmit data to card is cmx_send.
|
||||
*
|
||||
*
|
||||
* Interaction with other features:
|
||||
|
@ -143,12 +127,10 @@
|
|||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
#include "dsp.h"
|
||||
#include "hw_lock.h"
|
||||
|
||||
//#define CMX_CONF_DEBUG /* debugging of multi party conference, by using conference even with two members */
|
||||
//#define CMX_DEBUG /* massive read/write pointer output */
|
||||
|
||||
extern mISDN_HWlock_t dsp_lock;
|
||||
LIST_HEAD(Conf_list);
|
||||
|
||||
/*
|
||||
|
@ -157,13 +139,12 @@ LIST_HEAD(Conf_list);
|
|||
void
|
||||
dsp_cmx_debug(dsp_t *dsp)
|
||||
{
|
||||
conference_t *conf;
|
||||
conf_member_t *member;
|
||||
dsp_t *odsp;
|
||||
conference_t *conf;
|
||||
conf_member_t *member;
|
||||
dsp_t *odsp;
|
||||
|
||||
printk(KERN_DEBUG "-----Current DSP\n");
|
||||
list_for_each_entry(odsp, &dsp_obj.ilist, list)
|
||||
{
|
||||
list_for_each_entry(odsp, &dsp_obj.ilist, list) {
|
||||
printk(KERN_DEBUG "* %s echo=%d txmix=%d", odsp->inst.name, odsp->echo, odsp->tx_mix);
|
||||
if (odsp->conf)
|
||||
printk(" (Conf %d)", odsp->conf->id);
|
||||
|
@ -171,14 +152,15 @@ dsp_cmx_debug(dsp_t *dsp)
|
|||
printk(" *this*");
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "-----Current Conf:\n");
|
||||
list_for_each_entry(conf, &Conf_list, list)
|
||||
{
|
||||
printk(KERN_DEBUG "* Conf %d (0x%x)\n", conf->id, (u32)conf);
|
||||
list_for_each_entry(member, &conf->mlist, list)
|
||||
{
|
||||
printk(KERN_DEBUG " - member = %s (slot_tx %d, bank_tx %d, slot_rx %d, bank_rx %d hfc_conf %d)%s\n", member->dsp->inst.name, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx, member->dsp->hfc_conf, (member->dsp==dsp)?" *this*":"");
|
||||
list_for_each_entry(conf, &Conf_list, list) {
|
||||
printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf);
|
||||
list_for_each_entry(member, &conf->mlist, list) {
|
||||
printk(KERN_DEBUG
|
||||
" - member = %s (slot_tx %d, bank_tx %d, slot_rx %d, bank_rx %d hfc_conf %d)%s\n",
|
||||
member->dsp->inst.name, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
|
||||
member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx, member->dsp->hfc_conf,
|
||||
(member->dsp==dsp)?" *this*":"");
|
||||
}
|
||||
}
|
||||
printk(KERN_DEBUG "-----end\n");
|
||||
|
@ -231,26 +213,18 @@ dsp_cmx_add_conf_member(dsp_t *dsp, conference_t *conf)
|
|||
return(-EINVAL);
|
||||
}
|
||||
|
||||
unlock_HW(&dsp_lock);
|
||||
if (!(member = vmalloc(sizeof(conf_member_t)))) {
|
||||
lock_HW(&dsp_lock, 0);
|
||||
printk(KERN_ERR "vmalloc conf_member_t failed\n");
|
||||
if (!(member = kmalloc(sizeof(conf_member_t), GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "kmalloc conf_member_t failed\n");
|
||||
return(-ENOMEM);
|
||||
}
|
||||
lock_HW(&dsp_lock, 0);
|
||||
memset(member, 0, sizeof(conf_member_t));
|
||||
memset(dsp->rx_buff, silence, sizeof(dsp->rx_buff));
|
||||
member->dsp = dsp;
|
||||
/* set initial values */
|
||||
dsp->W_rx = conf->W_max;
|
||||
dsp->R_rx = conf->W_max;
|
||||
/* clear rx buffer */
|
||||
memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
|
||||
dsp->rx_W = dsp->rx_R = -1; /* reset pointers */
|
||||
|
||||
list_add_tail(&member->list, &conf->mlist);
|
||||
|
||||
/* zero conf-buffer if we change from 2 to 3 members */
|
||||
if (3 == count_list_member(&conf->mlist))
|
||||
memset(conf->conf_buff, 0, sizeof(conf->conf_buff));
|
||||
|
||||
dsp->conf = conf;
|
||||
dsp->member = member;
|
||||
|
||||
|
@ -290,9 +264,7 @@ dsp_cmx_del_conf_member(dsp_t *dsp)
|
|||
list_del(&member->list);
|
||||
dsp->conf = NULL;
|
||||
dsp->member = NULL;
|
||||
unlock_HW(&dsp_lock);
|
||||
vfree(member);
|
||||
lock_HW(&dsp_lock, 0);
|
||||
kfree(member);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
@ -317,13 +289,10 @@ static conference_t
|
|||
return(NULL);
|
||||
}
|
||||
|
||||
unlock_HW(&dsp_lock);
|
||||
if (!(conf = vmalloc(sizeof(conference_t)))) {
|
||||
lock_HW(&dsp_lock, 0);
|
||||
printk(KERN_ERR "vmalloc conference_t failed\n");
|
||||
if (!(conf = kmalloc(sizeof(conference_t), GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "kmalloc conference_t failed\n");
|
||||
return(NULL);
|
||||
}
|
||||
lock_HW(&dsp_lock, 0);
|
||||
memset(conf, 0, sizeof(conference_t));
|
||||
INIT_LIST_HEAD(&conf->mlist);
|
||||
conf->id = id;
|
||||
|
@ -352,9 +321,7 @@ dsp_cmx_del_conf(conference_t *conf)
|
|||
return(-EINVAL);
|
||||
}
|
||||
list_del(&conf->list);
|
||||
unlock_HW(&dsp_lock);
|
||||
vfree(conf);
|
||||
lock_HW(&dsp_lock, 0);
|
||||
kfree(conf);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
@ -379,7 +346,7 @@ dsp_cmx_hw_message(dsp_t *dsp, u32 message, u32 param1, u32 param2, u32 param3,
|
|||
return;
|
||||
}
|
||||
/* unlocking is not required, because we don't expect a response */
|
||||
if (dsp->inst.down.func(&dsp->inst.down, nskb))
|
||||
if (mISDN_queue_down(&dsp->inst, 0, nskb))
|
||||
dev_kfree_skb(nskb);
|
||||
}
|
||||
|
||||
|
@ -395,12 +362,12 @@ dsp_cmx_hw_message(dsp_t *dsp, u32 message, u32 param1, u32 param2, u32 param3,
|
|||
void
|
||||
dsp_cmx_hardware(conference_t *conf, dsp_t *dsp)
|
||||
{
|
||||
conf_member_t *member, *nextm;
|
||||
dsp_t *finddsp;
|
||||
int memb = 0, i, ii, i1, i2;
|
||||
int freeunits[8];
|
||||
u_char freeslots[256];
|
||||
int same_hfc = -1, same_pcm = -1, current_conf = -1, all_conf = 1;
|
||||
conf_member_t *member, *nextm;
|
||||
dsp_t *finddsp;
|
||||
int memb = 0, i, ii, i1, i2;
|
||||
int freeunits[8];
|
||||
u_char freeslots[256];
|
||||
int same_hfc = -1, same_pcm = -1, current_conf = -1, all_conf = 1;
|
||||
|
||||
/* dsp gets updated (no conf) */
|
||||
//printk("-----1\n");
|
||||
|
@ -558,6 +525,12 @@ dsp_cmx_hardware(conference_t *conf, dsp_t *dsp)
|
|||
printk(KERN_DEBUG "%s dsp %s cannot form a conf, because encryption is enabled\n", __FUNCTION__, member->dsp->inst.name);
|
||||
goto conf_software;
|
||||
}
|
||||
/* check if echo cancellation is enabled */
|
||||
if (member->dsp->cancel_enable) {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "%s dsp %s cannot form a conf, because echo cancellation is enabled\n", __FUNCTION__, member->dsp->inst.name);
|
||||
goto conf_software;
|
||||
}
|
||||
/* check if member is on a card with PCM support */
|
||||
if (member->dsp->features.pcm_id < 0) {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
|
@ -960,216 +933,127 @@ dsp_cmx_conf(dsp_t *dsp, u32 conf_id)
|
|||
/*
|
||||
* audio data is received from card
|
||||
*/
|
||||
|
||||
void
|
||||
dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb)
|
||||
{
|
||||
conference_t *conf = dsp->conf;
|
||||
conf_member_t *member;
|
||||
s32 *c;
|
||||
// s32 *c;
|
||||
u8 *d, *p;
|
||||
int len = skb->len;
|
||||
int w, ww, i, ii;
|
||||
int W_min, W_max;
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
||||
int w, i, ii;
|
||||
// int direct = 0; /* use rx data to clock tx-data */
|
||||
|
||||
/* check if we have sompen */
|
||||
if (len < 1)
|
||||
return;
|
||||
|
||||
//#ifndef AUTOJITTER
|
||||
/* -> if length*2 is greater largest */
|
||||
if (dsp->largest < (len<<1))
|
||||
dsp->largest = (len<<1);
|
||||
//#endif
|
||||
#if 0
|
||||
/* check if we can use our clock and directly forward data */
|
||||
if (!dsp->features.has_jitter) {
|
||||
if (!conf)
|
||||
direct = 1;
|
||||
else {
|
||||
if (count_list_member(&conf->mlist) <= 2)
|
||||
direct = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* half of the buffer should be 4 time larger than maximum packet size */
|
||||
if (len >= (CMX_BUFF_HALF>>2)) {
|
||||
/* half of the buffer should be larger than maximum packet size */
|
||||
if (len >= CMX_BUFF_HALF) {
|
||||
printk(KERN_ERR "%s line %d: packet from card is too large (%d bytes). please make card send smaller packets OR increase CMX_BUFF_SIZE\n", __FILE__, __LINE__, len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* STEP 1: WRITE DOWN WHAT WE GOT (into the buffer(s)) */
|
||||
|
||||
/* -> new W-min & W-max is calculated:
|
||||
* W_min will be the write pointer of this dsp (after writing 'len'
|
||||
* of bytes).
|
||||
* If there are other members in a conference, W_min will be the
|
||||
* lowest of all member's writer pointers.
|
||||
* W_max respectively
|
||||
*/
|
||||
W_max = W_min = (dsp->W_rx + len) & CMX_BUFF_MASK;
|
||||
if (conf) {
|
||||
/* -> who is larger? dsp or conf */
|
||||
if (conf->largest < dsp->largest)
|
||||
conf->largest = dsp->largest;
|
||||
else if (conf->largest > dsp->largest)
|
||||
dsp->largest = conf->largest;
|
||||
|
||||
list_for_each_entry(member, &conf->mlist, list) {
|
||||
if (member != dsp->member) {
|
||||
/* if W_rx is lower */
|
||||
if (((member->dsp->W_rx - W_min) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
|
||||
W_min = member->dsp->W_rx;
|
||||
/* if W_rx is higher */
|
||||
if (((W_max - member->dsp->W_rx) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
|
||||
W_max = member->dsp->W_rx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "cmx_receive(dsp=%lx): W_rx(dsp)=%05x W_min=%05x W_max=%05x largest=%05x %s\n", dsp, dsp->W_rx, W_min, W_max, dsp->largest, dsp->inst.name);
|
||||
#endif
|
||||
|
||||
/* -> if data is not too fast (exceed maximum queue):
|
||||
* data is written if 'new W_rx' is not too far behind W_min.
|
||||
*/
|
||||
if (((dsp->W_rx + len - W_min) & CMX_BUFF_MASK) <= dsp->largest) {
|
||||
/* -> received data is written to rx-buffer */
|
||||
p = skb->data;
|
||||
d = dsp->rx_buff;
|
||||
w = dsp->W_rx;
|
||||
i = 0;
|
||||
ii = len;
|
||||
while(i < ii) {
|
||||
d[w++ & CMX_BUFF_MASK] = *p++;
|
||||
i++;
|
||||
}
|
||||
/* -> if conference has three or more members */
|
||||
if (conf) {
|
||||
#ifdef CMX_CONF_DEBUG
|
||||
#warning CMX_CONF_DEBUG is enabled, it causes performance loss with normal 2-party crossconnects
|
||||
if (2 <= count_list_member(&conf->mlist)) {
|
||||
#else
|
||||
if (3 <= count_list_member(&conf->mlist)) {
|
||||
#endif
|
||||
//printk(KERN_DEBUG "cmxing dsp:%s dsp->W_rx=%04x conf->W_max=%04x\n", dsp->inst.name, dsp->W_rx, conf->W_max);
|
||||
/* -> received data is added to conf-buffer
|
||||
* new space is overwritten */
|
||||
p = skb->data;
|
||||
c = conf->conf_buff;
|
||||
w = dsp->W_rx;
|
||||
ww = conf->W_max;
|
||||
i = 0;
|
||||
ii = len;
|
||||
/* loop until done or old W_max is reached */
|
||||
while(i<ii && w!=ww) {
|
||||
c[w] += dsp_audio_law_to_s32[*p++]; /* add to existing */
|
||||
w = (w+1) & CMX_BUFF_MASK; /* must be always masked, for loop condition */
|
||||
i++;
|
||||
}
|
||||
/* loop the rest */
|
||||
while(i < ii) {
|
||||
c[w++ & CMX_BUFF_MASK] = dsp_audio_law_to_s32[*p++]; /* write to new */
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/* if W_max is lower new dsp->W_rx */
|
||||
if (((W_max - (dsp->W_rx+len)) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
|
||||
W_max = (dsp->W_rx + len) & CMX_BUFF_MASK;
|
||||
/* store for dsp_cmx_send */
|
||||
conf->W_min = W_min;
|
||||
/* -> write new W_max */
|
||||
conf->W_max = W_max;
|
||||
}
|
||||
/* -> write new W_rx */
|
||||
dsp->W_rx = (dsp->W_rx + len) & CMX_BUFF_MASK;
|
||||
/* initialize pointers if not already */
|
||||
if (dsp->rx_W < 0) {
|
||||
if (dsp->features.has_jitter)
|
||||
dsp->rx_R = dsp->rx_W = (hh->dinfo & CMX_BUFF_MASK);
|
||||
else
|
||||
dsp->rx_R = dsp->rx_W = 0;
|
||||
} else {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "CMX: receiving too fast (rx_buff) dsp=%x\n", (u32)dsp);
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "W_max=%x-W_min=%x = %d, largest = %d\n", W_max, W_min, (W_max - W_min) & CMX_BUFF_MASK, dsp->largest);
|
||||
#endif
|
||||
if (dsp->features.has_jitter) {
|
||||
dsp->rx_W = (hh->dinfo & CMX_BUFF_MASK);
|
||||
}
|
||||
/* if we underrun (or maybe overrun), we set our new read pointer, and write silence to buffer */
|
||||
if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "cmx_receive(dsp=%lx): UNDERRUN (or overrun), adjusting read pointer! (inst %s)\n", (u_long)dsp, dsp->inst.name);
|
||||
dsp->rx_R = dsp->rx_W;
|
||||
memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
|
||||
}
|
||||
}
|
||||
|
||||
/* show where to write */
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "cmx_receive(dsp=%lx): rx_R(dsp) rx_W(dsp)=%05x len=%d %s\n", (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->inst.name);
|
||||
#endif
|
||||
|
||||
/* write data into rx_buffer */
|
||||
p = skb->data;
|
||||
d = dsp->rx_buff;
|
||||
w = dsp->rx_W;
|
||||
i = 0;
|
||||
ii = len;
|
||||
while(i < ii) {
|
||||
d[w++ & CMX_BUFF_MASK] = *p++;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* increase write-pointer */
|
||||
dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* send mixed audio data to card
|
||||
*/
|
||||
|
||||
struct sk_buff
|
||||
*dsp_cmx_send(dsp_t *dsp, int len, int dinfo)
|
||||
/*
|
||||
* send (mixed) audio data to card and control jitter
|
||||
*/
|
||||
static void
|
||||
dsp_cmx_send_member(dsp_t *dsp, int len, s32 *c, int members)
|
||||
{
|
||||
int dinfo = 0;
|
||||
conference_t *conf = dsp->conf;
|
||||
dsp_t *member, *other;
|
||||
register s32 sample;
|
||||
s32 *c;
|
||||
u8 *d, *o, *p, *q;
|
||||
u8 *d, *p, *q, *o_q;
|
||||
struct sk_buff *nskb;
|
||||
int r, rr, t, tt;
|
||||
int r, rr, t, tt, o_r, o_rr;
|
||||
|
||||
/* don't process if: */
|
||||
if (dsp->pcm_slot_tx >= 0 /* connected to pcm slot */
|
||||
&& dsp->tx_R == dsp->tx_W /* AND no tx-data */
|
||||
&& !(dsp->tone.tone && dsp->tone.software)) /* AND not soft tones */
|
||||
return;
|
||||
if (!dsp->b_active) /* if not active */
|
||||
return;
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", members, dsp->inst.name, conf, dsp->rx_R, dsp->rx_W);
|
||||
#endif
|
||||
|
||||
/* PREPARE RESULT */
|
||||
nskb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (!nskb) {
|
||||
printk(KERN_ERR "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", len);
|
||||
return(NULL);
|
||||
return;
|
||||
}
|
||||
mISDN_sethead(PH_DATA | REQUEST, dinfo, nskb);
|
||||
|
||||
/* set pointers, indexes and stuff */
|
||||
member = dsp;
|
||||
p = dsp->tx_buff; /* transmit data */
|
||||
q = dsp->rx_buff; /* received data */
|
||||
d = skb_put(nskb, len); /* result */
|
||||
t = dsp->R_tx; /* tx-pointers */
|
||||
tt = dsp->W_tx;
|
||||
r = dsp->R_rx; /* rx-pointers */
|
||||
if (conf) {
|
||||
/* special hardware access */
|
||||
if (conf->hardware) {
|
||||
if (dsp->tone.tone && dsp->tone.software) {
|
||||
/* -> copy tone */
|
||||
dsp_tone_copy(dsp, d, len);
|
||||
dsp->R_tx = dsp->W_tx = 0; /* clear tx buffer */
|
||||
return(nskb);
|
||||
}
|
||||
if (t != tt) {
|
||||
while(len && t!=tt) {
|
||||
*d++ = p[t]; /* write tx_buff */
|
||||
t = (t+1) & CMX_BUFF_MASK;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
if (len)
|
||||
memset(d, silence, len);
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
}
|
||||
/* W_min is also limit for read */
|
||||
rr = conf->W_min;
|
||||
} else
|
||||
rr = dsp->W_rx;
|
||||
t = dsp->tx_R; /* tx-pointers */
|
||||
tt = dsp->tx_W;
|
||||
r = dsp->rx_R; /* rx-pointers */
|
||||
rr = (r + len) & CMX_BUFF_MASK;
|
||||
|
||||
/* increase r, if too far behind rr
|
||||
* (this happens if interrupts get lost, so transmission is delayed) */
|
||||
if (((rr - r) & CMX_BUFF_MASK) > dsp->largest) {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "r=%04x is too far behind rr=%04x, correcting. (larger than %04x)\n", r, rr, dsp->largest);
|
||||
r = (rr - dsp->largest) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* calculate actual r (if r+len would overrun rr) */
|
||||
if (((rr - r - len) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "r+len=%04x overruns rr=%04x\n", (r+len) & CMX_BUFF_MASK, rr);
|
||||
#endif
|
||||
/* r is set "len" bytes before W_min */
|
||||
r = (rr - len) & CMX_BUFF_MASK;
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "CMX: sending too fast (tx_buff) dsp=%x\n", (u32)dsp);
|
||||
} else
|
||||
/* rr is set "len" bytes after R_rx */
|
||||
rr = (r + len) & CMX_BUFF_MASK;
|
||||
dsp->R_rx = rr;
|
||||
/* now: rr is exactly "len" bytes after r now */
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "CMX_SEND(dsp=%lx) %d bytes from tx:0x%05x-0x%05x rx:0x%05x-0x%05x echo=%d %s\n", dsp, len, t, tt, r, rr, dsp->echo, dsp->inst.name);
|
||||
#endif
|
||||
|
||||
/* STEP 2.0: PROCESS TONES/TX-DATA ONLY */
|
||||
/* PROCESS TONES/TX-DATA ONLY */
|
||||
if (dsp->tone.tone && dsp->tone.software) {
|
||||
/* -> copy tone */
|
||||
dsp_tone_copy(dsp, d, len);
|
||||
dsp->R_tx = dsp->W_tx = 0; /* clear tx buffer */
|
||||
return(nskb);
|
||||
dsp->tx_R = dsp->tx_W = 0; /* clear tx buffer */
|
||||
goto send_packet;
|
||||
}
|
||||
/* if we have tx-data but do not use mixing */
|
||||
if (!dsp->tx_mix && t!=tt) {
|
||||
|
@ -1180,14 +1064,13 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
if(r == rr) {
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
}
|
||||
}
|
||||
|
||||
/* STEP 2.1: PROCESS DATA (one member / no conf) */
|
||||
if (!conf) {
|
||||
single:
|
||||
/* PROCESS DATA (one member / no conf) */
|
||||
if (!conf || members<=1) {
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo) {
|
||||
/* -> send tx-data if available or use 0-volume */
|
||||
|
@ -1197,7 +1080,7 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
if(r != rr)
|
||||
memset(d, silence, (rr-r)&CMX_BUFF_MASK);
|
||||
memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK);
|
||||
/* -> if echo is enabled */
|
||||
} else {
|
||||
/* -> mix tx-data with echo if available, or use echo only */
|
||||
|
@ -1211,40 +1094,40 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
}
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
}
|
||||
if (1 == count_list_member(&conf->mlist)) {
|
||||
goto single;
|
||||
}
|
||||
/* STEP 2.2: PROCESS DATA (two members) */
|
||||
/* PROCESS DATA (two members) */
|
||||
#ifdef CMX_CONF_DEBUG
|
||||
if (0) {
|
||||
#else
|
||||
if (2 == count_list_member(&conf->mlist)) {
|
||||
if (members == 2) {
|
||||
#endif
|
||||
/* "other" becomes other party */
|
||||
other = (list_entry(conf->mlist.next, conf_member_t, list))->dsp;
|
||||
if (other == member)
|
||||
other = (list_entry(conf->mlist.prev, conf_member_t, list))->dsp;
|
||||
o = other->rx_buff; /* received data */
|
||||
o_q = other->rx_buff; /* received data */
|
||||
o_r = other->rx_R; /* rx-pointers */
|
||||
o_rr = (o_r + len) & CMX_BUFF_MASK;
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo) {
|
||||
//if (o_r!=o_rr) printk(KERN_DEBUG "receive data=0x%02x\n", o_q[o_r]); else printk(KERN_DEBUG "NO R!!!\n");
|
||||
/* -> copy other member's rx-data, if tx-data is available, mix */
|
||||
while(r!=rr && t!=tt) {
|
||||
*d++ = dsp_audio_mix_law[(p[t]<<8)|o[r]];
|
||||
while(o_r!=o_rr && t!=tt) {
|
||||
*d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]];
|
||||
t = (t+1) & CMX_BUFF_MASK;
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while(r != rr) {
|
||||
*d++ = o[r];
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
while(o_r != o_rr) {
|
||||
*d++ = o_q[o_r];
|
||||
o_r = (o_r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* -> if echo is enabled */
|
||||
} else {
|
||||
/* -> mix other member's rx-data with echo, if tx-data is available, mix */
|
||||
while(r!=rr && t!=tt) {
|
||||
sample = dsp_audio_law_to_s32[p[t]] + dsp_audio_law_to_s32[o[r]] + dsp_audio_law_to_s32[q[r]];
|
||||
sample = dsp_audio_law_to_s32[p[t]] + dsp_audio_law_to_s32[q[r]] + dsp_audio_law_to_s32[o_q[o_r]];
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1252,22 +1135,23 @@ struct sk_buff
|
|||
*d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* tx-data + rx_data + echo */
|
||||
t = (t+1) & CMX_BUFF_MASK;
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while(r != rr) {
|
||||
*d++ = dsp_audio_mix_law[(o[r]<<8)|q[r]];
|
||||
*d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]];
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
}
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
}
|
||||
/* STEP 2.3: PROCESS DATA (three or more members) */
|
||||
c = conf->conf_buff;
|
||||
/* PROCESS DATA (three or more members) */
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo) {
|
||||
/* -> substract rx-data from conf-data, if tx-data is available, mix */
|
||||
while(r!=rr && t!=tt) {
|
||||
sample = dsp_audio_law_to_s32[p[t]] + c[r] - dsp_audio_law_to_s32[q[r]];
|
||||
sample = dsp_audio_law_to_s32[p[t]] + *c++ - dsp_audio_law_to_s32[q[r]];
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1277,7 +1161,7 @@ struct sk_buff
|
|||
t = (t+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while(r != rr) {
|
||||
sample = c[r] - dsp_audio_law_to_s32[q[r]];
|
||||
sample = *c++ - dsp_audio_law_to_s32[q[r]];
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1289,7 +1173,7 @@ struct sk_buff
|
|||
} else {
|
||||
/* -> encode conf-data, if tx-data is available, mix */
|
||||
while(r!=rr && t!=tt) {
|
||||
sample = dsp_audio_law_to_s32[p[t]] + c[r];
|
||||
sample = dsp_audio_law_to_s32[p[t]] + *c++;
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1299,7 +1183,7 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while(r != rr) {
|
||||
sample = c[r];
|
||||
sample = *c++;
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
|
@ -1308,8 +1192,171 @@ struct sk_buff
|
|||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
}
|
||||
dsp->R_tx = t;
|
||||
return(nskb);
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
|
||||
send_packet:
|
||||
/* adjust volume */
|
||||
if (dsp->tx_volume)
|
||||
dsp_change_volume(nskb, dsp->tx_volume);
|
||||
|
||||
/* cancel echo */
|
||||
if (dsp->cancel_enable)
|
||||
dsp_cancel_tx(dsp, nskb->data, nskb->len);
|
||||
|
||||
/* crypt */
|
||||
if (dsp->bf_enable)
|
||||
dsp_bf_encrypt(dsp, nskb->data, nskb->len);
|
||||
|
||||
/* send packet */
|
||||
if (mISDN_queue_down(&dsp->inst, 0, nskb)) {
|
||||
dev_kfree_skb(nskb);
|
||||
printk(KERN_ERR "%s: failed to send tx-packet\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
u32 samplecount;
|
||||
struct timer_list dsp_spl_tl;
|
||||
u64 dsp_spl_jiffies;
|
||||
|
||||
void dsp_cmx_send(void *data)
|
||||
{
|
||||
conference_t *conf;
|
||||
conf_member_t *member;
|
||||
dsp_t *dsp;
|
||||
int mustmix, members;
|
||||
s32 mixbuffer[MAX_POLL], *c;
|
||||
u8 *q;
|
||||
int r, rr;
|
||||
int jittercheck = 0, delay, i;
|
||||
u_long flags;
|
||||
|
||||
/* lock */
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
|
||||
/* check if jitter needs to be checked */
|
||||
samplecount += dsp_poll;
|
||||
if (samplecount%8000 < dsp_poll)
|
||||
jittercheck = 1;
|
||||
|
||||
/* loop all members that do not require conference mixing */
|
||||
list_for_each_entry(dsp, &dsp_obj.ilist, list) {
|
||||
conf = dsp->conf;
|
||||
mustmix = 0;
|
||||
members = 0;
|
||||
if (conf) {
|
||||
members = count_list_member(&conf->mlist);
|
||||
#ifdef CMX_CONF_DEBUG
|
||||
if (conf->software && members>1)
|
||||
#else
|
||||
if (conf->software && members>2)
|
||||
#endif
|
||||
mustmix = 1;
|
||||
}
|
||||
|
||||
/* transmission required */
|
||||
if (!mustmix)
|
||||
dsp_cmx_send_member(dsp, dsp_poll, mixbuffer, members); // unused mixbuffer is given to prevent a potential null-pointer-bug
|
||||
}
|
||||
|
||||
/* loop all members that require conference mixing */
|
||||
list_for_each_entry(conf, &Conf_list, list) {
|
||||
/* count members and check hardware */
|
||||
members = count_list_member(&conf->mlist);
|
||||
#ifdef CMX_CONF_DEBUG
|
||||
if (conf->software && members>1) {
|
||||
#else
|
||||
if (conf->software && members>2) {
|
||||
#endif
|
||||
/* mix all data */
|
||||
memset(mixbuffer, 0, dsp_poll*sizeof(s32));
|
||||
list_for_each_entry(member, &conf->mlist, list) {
|
||||
dsp = member->dsp;
|
||||
/* get range of data to mix */
|
||||
c = mixbuffer;
|
||||
q = dsp->rx_buff;
|
||||
r = dsp->rx_R;
|
||||
rr = (r + dsp_poll) & CMX_BUFF_MASK;
|
||||
/* add member's data */
|
||||
while(r != rr) {
|
||||
*c++ += dsp_audio_law_to_s32[q[r]];
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/* process each member */
|
||||
list_for_each_entry(member, &conf->mlist, list) {
|
||||
/* transmission */
|
||||
dsp_cmx_send_member(member->dsp, dsp_poll, mixbuffer, members);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* delete rx-data, increment buffers, change pointers */
|
||||
list_for_each_entry(dsp, &dsp_obj.ilist, list) {
|
||||
q = dsp->rx_buff;
|
||||
r = dsp->rx_R;
|
||||
rr = (r + dsp_poll) & CMX_BUFF_MASK;
|
||||
/* delete rx-data */
|
||||
while(r != rr) {
|
||||
q[r] = dsp_silence;
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* increment rx-buffer pointer */
|
||||
dsp->rx_R = r; /* write incremented read pointer */
|
||||
|
||||
/* check current delay */
|
||||
delay = (dsp->rx_W-r) & CMX_BUFF_MASK;
|
||||
if (delay >= CMX_BUFF_HALF)
|
||||
delay = 0; /* will be the delay before next write */
|
||||
/* check for lower delay */
|
||||
if (delay < dsp->delay[0])
|
||||
dsp->delay[0] = delay;
|
||||
if (jittercheck) {
|
||||
/* find the lowest of all delays */
|
||||
delay = dsp->delay[0];
|
||||
i = 1;
|
||||
while (i < MAX_SECONDS_JITTER_CHECK) {
|
||||
if (delay > dsp->delay[i])
|
||||
delay = dsp->delay[i];
|
||||
i++;
|
||||
}
|
||||
/* remove delay */
|
||||
if (delay) {
|
||||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
printk(KERN_DEBUG "%s lowest delay of %d bytes for dsp %s are now removed.\n", __FUNCTION__, delay, dsp->inst.name);
|
||||
r = dsp->rx_R;
|
||||
rr = (r + delay) & CMX_BUFF_MASK;
|
||||
/* delete rx-data */
|
||||
while(r != rr) {
|
||||
q[r] = dsp_silence;
|
||||
r = (r+1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* increment rx-buffer pointer */
|
||||
dsp->rx_R = r; /* write incremented read pointer */
|
||||
}
|
||||
/* scroll up delays */
|
||||
i = MAX_SECONDS_JITTER_CHECK - 1;
|
||||
while (i) {
|
||||
dsp->delay[i] = dsp->delay[i-1];
|
||||
i--;
|
||||
}
|
||||
dsp->delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
|
||||
}
|
||||
}
|
||||
|
||||
/* restart timer */
|
||||
// init_timer(&dsp_spl_tl);
|
||||
if (dsp_spl_jiffies + dsp_tics < jiffies) /* if next event would be in the past ... */
|
||||
dsp_spl_jiffies = jiffies;
|
||||
else
|
||||
dsp_spl_jiffies += dsp_tics;
|
||||
|
||||
dsp_spl_tl.expires = dsp_spl_jiffies;
|
||||
add_timer(&dsp_spl_tl);
|
||||
|
||||
/* unlock */
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1321,36 +1368,15 @@ dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb)
|
|||
u_int w, ww;
|
||||
u8 *d, *p;
|
||||
int space, l;
|
||||
#ifdef AUTOJITTER
|
||||
int use;
|
||||
#endif
|
||||
|
||||
/* check if we have sompen */
|
||||
l = skb->len;
|
||||
w = dsp->W_tx;
|
||||
ww = dsp->R_tx;
|
||||
if (l < 1)
|
||||
return;
|
||||
|
||||
#ifdef AUTOJITTER
|
||||
/* check the delay */
|
||||
use = w-ww;
|
||||
if (use < 0)
|
||||
use += CMX_BUFF_SIZE;
|
||||
if (!dsp->tx_delay || dsp->tx_delay>use)
|
||||
dsp->tx_delay = use;
|
||||
dsp->tx_delay_count += l;
|
||||
if (dsp->tx_delay_count >= DELAY_CHECK) {
|
||||
/* now remove the delay */
|
||||
if (dsp_debug & DEBUG_DSP_DELAY)
|
||||
printk(KERN_DEBUG "%s(dsp=0x%x) removing delay of %d bytes\n", __FUNCTION__, (u32)dsp, dsp->tx_delay);
|
||||
dsp->tx_delay_count = 0;
|
||||
dsp->R_tx = ww = (ww + dsp->tx_delay) & CMX_BUFF_MASK;
|
||||
dsp->tx_delay = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* check if there is enough space, and then copy */
|
||||
w = dsp->tx_W;
|
||||
ww = dsp->tx_R;
|
||||
p = dsp->tx_buff;
|
||||
d = skb->data;
|
||||
space = ww-w;
|
||||
|
@ -1363,10 +1389,11 @@ dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb)
|
|||
else
|
||||
/* write until all byte are copied */
|
||||
ww = (w + skb->len) & CMX_BUFF_MASK;
|
||||
dsp->W_tx = ww;
|
||||
dsp->tx_W = ww;
|
||||
|
||||
/* show current buffer */
|
||||
#ifdef CMX_DEBUG
|
||||
printk(KERN_DEBUG "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->inst.name);
|
||||
printk(KERN_DEBUG "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->inst.name);
|
||||
#endif
|
||||
|
||||
/* copy transmit data to tx-buffer */
|
||||
|
@ -1377,5 +1404,3 @@ dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb)
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
* - (4) echo generation for delay test
|
||||
* - (5) volume control
|
||||
* - (6) disable receive data
|
||||
* - (7) encryption/decryption
|
||||
* - (7) echo cancelation
|
||||
* - (8) encryption/decryption
|
||||
*
|
||||
* Look:
|
||||
* TX RX
|
||||
|
@ -42,17 +43,11 @@
|
|||
* v |
|
||||
* +-----+-------------+-----+
|
||||
* |(3)(4) |
|
||||
* | |
|
||||
* | |
|
||||
* | CMX |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | +-------------+
|
||||
* | | ^
|
||||
* | | |
|
||||
* | | |
|
||||
* |+---------+| +----+----+
|
||||
* ||(1) || |(5) |
|
||||
* || || | |
|
||||
|
@ -62,7 +57,6 @@
|
|||
* |+----+----+| +----+----+
|
||||
* +-----+-----+ ^
|
||||
* | |
|
||||
* | |
|
||||
* v |
|
||||
* +----+----+ +----+----+
|
||||
* |(5) | |(2) |
|
||||
|
@ -74,8 +68,18 @@
|
|||
* | ^
|
||||
* | |
|
||||
* v |
|
||||
* +----+-------------+----+
|
||||
* |(7) |
|
||||
* | |
|
||||
* | Echo Cancellation |
|
||||
* | |
|
||||
* | |
|
||||
* +----+-------------+----+
|
||||
* | ^
|
||||
* | |
|
||||
* v |
|
||||
* +----+----+ +----+----+
|
||||
* |(7) | |(7) |
|
||||
* |(8) | |(8) |
|
||||
* | | | |
|
||||
* | Encrypt | | Decrypt |
|
||||
* | | | |
|
||||
|
@ -115,6 +119,13 @@
|
|||
* data to/form upper layer may be swithed on/off individually without loosing
|
||||
* features of CMX, Tones and DTMF.
|
||||
*
|
||||
* Echo Cancellation: Sometimes we like to cancel echo from the interface.
|
||||
* Note that a VoIP call may not have echo caused by the IP phone. The echo
|
||||
* is generated by the telephone line connected to it. Because the delay
|
||||
* is high, it becomes an echo. RESULT: Echo Cachelation is required if
|
||||
* both echo AND delay is applied to an interface.
|
||||
* Remember that software CMX always generates a more or less delay.
|
||||
*
|
||||
* If all used features can be realized in hardware, and if transmit and/or
|
||||
* receive data ist disabled, the card may not send/receive any data at all.
|
||||
* Not receiving is usefull if only announcements are played. Not sending is
|
||||
|
@ -153,9 +164,6 @@ There are three things that need to transmit data to card:
|
|||
- software cmx
|
||||
- upper layer, if tx-data is written to tx-buffer
|
||||
|
||||
Whenever cmx is changed, or data is sent from upper layer, the transmission is triggered by an silence freame (if not already tx_pending==1). When the confirm is received from card, next frame is
|
||||
sent using software cmx, if tx-data is still available, or if software tone generation is used,
|
||||
or if cmx is currently using software.
|
||||
|
||||
|
||||
|
||||
|
@ -171,68 +179,27 @@ const char *dsp_revision = "$Revision$";
|
|||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
#include "dsp.h"
|
||||
#include "hw_lock.h"
|
||||
|
||||
static char DSPName[] = "DSP";
|
||||
mISDNobject_t dsp_obj;
|
||||
mISDN_HWlock_t dsp_lock;
|
||||
|
||||
static int debug = 0;
|
||||
int dsp_debug;
|
||||
static int options = 0;
|
||||
int dsp_options;
|
||||
#ifndef AUTOJITTER
|
||||
int poll = 0;
|
||||
#endif
|
||||
static int poll = 0;
|
||||
int dsp_poll, dsp_tics;
|
||||
|
||||
#ifdef MODULE
|
||||
MODULE_AUTHOR("Andreas Eversberg");
|
||||
MODULE_PARM(debug, "1i");
|
||||
MODULE_PARM(options, "1i");
|
||||
#ifndef AUTOJITTER
|
||||
MODULE_PARM(poll, "1i");
|
||||
#endif
|
||||
#ifdef MODULE_LICENSE
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sending next frame to card (triggered by PH_DATA_IND)
|
||||
*/
|
||||
static void
|
||||
sendevent(dsp_t *dsp)
|
||||
{
|
||||
struct sk_buff *nskb;
|
||||
|
||||
lock_HW(&dsp_lock, 0);
|
||||
if (dsp->b_active && dsp->tx_pending) {
|
||||
/* get data from cmx */
|
||||
nskb = dsp_cmx_send(dsp, dsp->tx_pending, 0);
|
||||
dsp->tx_pending = 0;
|
||||
if (!nskb) {
|
||||
unlock_HW(&dsp_lock);
|
||||
printk(KERN_ERR "%s: failed to create tx packet\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
/* crypt if enabled */
|
||||
if (dsp->bf_enable)
|
||||
dsp_bf_encrypt(dsp, nskb->data, nskb->len);
|
||||
/* change volume if requested */
|
||||
if (dsp->tx_volume)
|
||||
dsp_change_volume(nskb, dsp->tx_volume);
|
||||
/* send subsequent requests to card */
|
||||
unlock_HW(&dsp_lock);
|
||||
if (dsp->inst.down.func(&dsp->inst.down, nskb)) {
|
||||
dev_kfree_skb(nskb);
|
||||
printk(KERN_ERR "%s: failed to send tx packet\n", __FUNCTION__);
|
||||
}
|
||||
} else {
|
||||
dsp->tx_pending = 0;
|
||||
unlock_HW(&dsp_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* special message process for DL_CONTROL | REQUEST
|
||||
|
@ -314,7 +281,7 @@ dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb)
|
|||
dsp_cmx_hardware(dsp->conf, dsp);
|
||||
/* reset tx buffers (user space data) */
|
||||
tone_off:
|
||||
dsp->R_tx = dsp->W_tx = 0;
|
||||
dsp->tx_R = dsp->tx_W = 0;
|
||||
break;
|
||||
case VOL_CHANGE_TX: /* change volume */
|
||||
if (len != sizeof(int)) {
|
||||
|
@ -380,6 +347,27 @@ dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb)
|
|||
if (dsp_debug & DEBUG_DSP_CMX)
|
||||
dsp_cmx_debug(dsp);
|
||||
break;
|
||||
case ECHOCAN_ON: /* turn echo calcellation on */
|
||||
if (len<4) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
int ec_arr[2];
|
||||
memcpy(&ec_arr,data,sizeof(ec_arr));
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: turn echo cancelation on (delay=%d attenuation-shift=%d\n",
|
||||
__FUNCTION__, ec_arr[0], ec_arr[1]);
|
||||
|
||||
ret = dsp_cancel_init(dsp, ec_arr[0], ec_arr[1] ,1);
|
||||
dsp_cmx_hardware(dsp->conf, dsp);
|
||||
}
|
||||
break;
|
||||
case ECHOCAN_OFF: /* turn echo calcellation off */
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: turn echo cancelation off\n", __FUNCTION__);
|
||||
|
||||
ret = dsp_cancel_init(dsp, 0,0,-1);
|
||||
dsp_cmx_hardware(dsp->conf, dsp);
|
||||
break;
|
||||
case BF_ENABLE_KEY: /* turn blowfish on */
|
||||
if (len<4 || len>56) {
|
||||
ret = -EINVAL;
|
||||
|
@ -395,12 +383,8 @@ dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb)
|
|||
cont = BF_REJECT;
|
||||
/* send indication if it worked to set it */
|
||||
nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0);
|
||||
unlock_HW(&dsp_lock);
|
||||
if (nskb) {
|
||||
if (dsp->inst.up.func(&dsp->inst.up, nskb))
|
||||
dev_kfree_skb(nskb);
|
||||
}
|
||||
lock_HW(&dsp_lock, 0);
|
||||
if (mISDN_queue_up(&dsp->inst, 0, nskb))
|
||||
dev_kfree_skb(nskb);
|
||||
if (!ret)
|
||||
dsp_cmx_hardware(dsp->conf, dsp);
|
||||
break;
|
||||
|
@ -415,6 +399,8 @@ dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb)
|
|||
printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", __FUNCTION__, cont);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (!ret)
|
||||
dev_kfree_skb(skb);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
@ -423,68 +409,76 @@ dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb)
|
|||
* messages from upper layers
|
||||
*/
|
||||
static int
|
||||
dsp_from_up(mISDNif_t *hif, struct sk_buff *skb)
|
||||
dsp_from_up(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
dsp_t *dsp;
|
||||
mISDN_head_t *hh;
|
||||
int ret = 0;
|
||||
u_long flags;
|
||||
|
||||
if (!hif || !hif->fdata || !skb)
|
||||
if (!skb)
|
||||
return(-EINVAL);
|
||||
dsp = hif->fdata;
|
||||
if (!dsp->inst.down.func)
|
||||
return(-ENXIO);
|
||||
dsp = inst->privat;
|
||||
if (!dsp) {
|
||||
return(-EIO);
|
||||
}
|
||||
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
switch(hh->prim) {
|
||||
case DL_DATA | RESPONSE:
|
||||
case PH_DATA | RESPONSE:
|
||||
/* ignore response */
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
case DL_DATA | REQUEST:
|
||||
case PH_DATA | REQUEST:
|
||||
lock_HW(&dsp_lock, 0);
|
||||
if (skb->len < 1)
|
||||
return(-EINVAL);
|
||||
|
||||
/* send data to tx-buffer (if no tone is played) */
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
if (!dsp->tone.tone)
|
||||
dsp_cmx_transmit(dsp, skb);
|
||||
unlock_HW(&dsp_lock);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
case PH_CONTROL | REQUEST:
|
||||
lock_HW(&dsp_lock, 0);
|
||||
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
ret = dsp_control_req(dsp, hh, skb);
|
||||
unlock_HW(&dsp_lock);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
|
||||
break;
|
||||
case DL_ESTABLISH | REQUEST:
|
||||
case PH_ACTIVATE | REQUEST:
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: activating b_channel %s\n", __FUNCTION__, dsp->inst.name);
|
||||
lock_HW(&dsp_lock, 0);
|
||||
dsp->tx_pending = 0;
|
||||
|
||||
if (dsp->dtmf.hardware || dsp->dtmf.software)
|
||||
dsp_dtmf_goertzel_init(dsp);
|
||||
unlock_HW(&dsp_lock);
|
||||
hh->prim = PH_ACTIVATE | REQUEST;
|
||||
return(dsp->inst.down.func(&dsp->inst.down, skb));
|
||||
ret = mISDN_queue_down(&dsp->inst, 0, skb);
|
||||
|
||||
break;
|
||||
case DL_RELEASE | REQUEST:
|
||||
case PH_DEACTIVATE | REQUEST:
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: releasing b_channel %s\n", __FUNCTION__, dsp->inst.name);
|
||||
lock_HW(&dsp_lock, 0);
|
||||
dsp->tx_pending = 0;
|
||||
|
||||
dsp->tone.tone = dsp->tone.hardware = dsp->tone.software = 0;
|
||||
if (timer_pending(&dsp->tone.tl))
|
||||
del_timer(&dsp->tone.tl);
|
||||
unlock_HW(&dsp_lock);
|
||||
hh->prim = PH_DEACTIVATE | REQUEST;
|
||||
return(dsp->inst.down.func(&dsp->inst.down, skb));
|
||||
ret = mISDN_queue_down(&dsp->inst, 0, skb);
|
||||
|
||||
break;
|
||||
default:
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: msg %x unhandled %s\n", __FUNCTION__, hh->prim, dsp->inst.name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (!ret)
|
||||
dev_kfree_skb(skb);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
@ -493,7 +487,7 @@ dsp_from_up(mISDNif_t *hif, struct sk_buff *skb)
|
|||
* messages from lower layers
|
||||
*/
|
||||
static int
|
||||
dsp_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
||||
dsp_from_down(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
dsp_t *dsp;
|
||||
mISDN_head_t *hh;
|
||||
|
@ -501,27 +495,35 @@ dsp_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
u8 *digits;
|
||||
int cont;
|
||||
struct sk_buff *nskb;
|
||||
u_long flags;
|
||||
|
||||
if (!hif || !hif->fdata || !skb)
|
||||
if (!skb)
|
||||
return(-EINVAL);
|
||||
dsp = hif->fdata;
|
||||
if (!dsp->inst.up.func)
|
||||
return(-ENXIO);
|
||||
dsp = inst->privat;
|
||||
if (!dsp)
|
||||
return(-EIO);
|
||||
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
switch(hh->prim)
|
||||
{
|
||||
case PH_DATA | CONFIRM:
|
||||
case DL_DATA | CONFIRM:
|
||||
/* flush response, because no relation to upper layer */
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
case PH_DATA | INDICATION:
|
||||
case DL_DATA | INDICATION:
|
||||
if (skb->len < 1)
|
||||
break;
|
||||
lock_HW(&dsp_lock, 0);
|
||||
return(-EINVAL);
|
||||
|
||||
|
||||
|
||||
/* decrypt if enabled */
|
||||
if (dsp->bf_enable)
|
||||
dsp_bf_decrypt(dsp, skb->data, skb->len);
|
||||
/* if echo cancellation is enabled */
|
||||
if (dsp->cancel_enable)
|
||||
dsp_cancel_rx(dsp, skb->data, skb->len);
|
||||
/* check if dtmf soft decoding is turned on */
|
||||
if (dsp->dtmf.software) {
|
||||
digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, (dsp_options&DSP_OPT_ULAW)?1:0);
|
||||
|
@ -530,14 +532,8 @@ dsp_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
printk(KERN_DEBUG "%s: sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name);
|
||||
cont = DTMF_TONE_VAL | *digits;
|
||||
nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0);
|
||||
unlock_HW(&dsp_lock);
|
||||
if (!nskb) {
|
||||
lock_HW(&dsp_lock, 0);
|
||||
break;
|
||||
}
|
||||
if (dsp->inst.up.func(&dsp->inst.up, nskb))
|
||||
if (mISDN_queue_up(&dsp->inst, 0, nskb))
|
||||
dev_kfree_skb(nskb);
|
||||
lock_HW(&dsp_lock, 0);
|
||||
digits++;
|
||||
}
|
||||
}
|
||||
|
@ -545,32 +541,24 @@ dsp_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
if (dsp->rx_volume)
|
||||
dsp_change_volume(skb, dsp->rx_volume);
|
||||
/* we need to process receive data if software */
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
if (dsp->pcm_slot_tx<0 && dsp->pcm_slot_rx<0) {
|
||||
/* process data from card at cmx */
|
||||
dsp_cmx_receive(dsp, skb);
|
||||
}
|
||||
/* we send data only if software or if we have some
|
||||
* or if we cannot do tones with hardware
|
||||
*/
|
||||
if ((dsp->pcm_slot_tx<0 && !dsp->features.hfc_loops) /* software crossconnects OR software loops */
|
||||
|| dsp->R_tx != dsp->W_tx /* data in buffer */
|
||||
|| (dsp->echo==1 && dsp->pcm_slot_tx<0) /* software echo */
|
||||
|| (dsp->tone.tone && dsp->tone.software)) { /* software loops */
|
||||
/* schedule sending skb->len bytes */
|
||||
dsp->tx_pending = skb->len;
|
||||
schedule_work(&dsp->sendwork);
|
||||
}
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
if (dsp->rx_disabled) {
|
||||
/* if receive is not allowed */
|
||||
dev_kfree_skb(skb);
|
||||
unlock_HW(&dsp_lock);
|
||||
return(0);
|
||||
|
||||
break;
|
||||
}
|
||||
unlock_HW(&dsp_lock);
|
||||
hh->prim = DL_DATA | INDICATION;
|
||||
return(dsp->inst.up.func(&dsp->inst.up, skb));
|
||||
ret = mISDN_queue_up(&dsp->inst, 0, skb);
|
||||
|
||||
break;
|
||||
case PH_CONTROL | INDICATION:
|
||||
lock_HW(&dsp_lock, 0);
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
|
||||
printk(KERN_DEBUG "%s: PH_CONTROL received: %x (len %d) %s\n", __FUNCTION__, hh->dinfo, skb->len, dsp->inst.name);
|
||||
switch (hh->dinfo) {
|
||||
|
@ -578,28 +566,22 @@ dsp_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
if (!dsp->dtmf.hardware) {
|
||||
if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
|
||||
printk(KERN_DEBUG "%s: ignoring DTMF coefficients from HFC\n", __FUNCTION__);
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
if (dsp->inst.up.func) {
|
||||
digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, 2);
|
||||
if (digits) while(*digits) {
|
||||
int k;
|
||||
struct sk_buff *nskb;
|
||||
if (dsp_debug & DEBUG_DSP_DTMF)
|
||||
printk(KERN_DEBUG "%s: now sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name);
|
||||
k = *digits | DTMF_TONE_VAL;
|
||||
nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &k, 0);
|
||||
unlock_HW(&dsp_lock);
|
||||
if (!nskb) {
|
||||
lock_HW(&dsp_lock, 0);
|
||||
break;
|
||||
}
|
||||
if (dsp->inst.up.func(&dsp->inst.up, nskb))
|
||||
dev_kfree_skb(nskb);
|
||||
lock_HW(&dsp_lock, 0);
|
||||
digits++;
|
||||
}
|
||||
digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, 2);
|
||||
if (digits) while(*digits) {
|
||||
int k;
|
||||
struct sk_buff *nskb;
|
||||
if (dsp_debug & DEBUG_DSP_DTMF)
|
||||
printk(KERN_DEBUG "%s: now sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name);
|
||||
k = *digits | DTMF_TONE_VAL;
|
||||
nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &k, 0);
|
||||
if (mISDN_queue_up(&dsp->inst, 0, nskb))
|
||||
dev_kfree_skb(nskb);
|
||||
digits++;
|
||||
}
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -607,48 +589,68 @@ dsp_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
printk(KERN_DEBUG "%s: ctrl ind %x unhandled %s\n", __FUNCTION__, hh->dinfo, dsp->inst.name);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
unlock_HW(&dsp_lock);
|
||||
|
||||
break;
|
||||
case PH_ACTIVATE | CONFIRM:
|
||||
lock_HW(&dsp_lock, 0);
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: b_channel is now active %s\n", __FUNCTION__, dsp->inst.name);
|
||||
/* bchannel now active */
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
dsp->b_active = 1;
|
||||
dsp->W_tx = dsp->R_tx = 0; /* clear TX buffer */
|
||||
dsp->W_rx = dsp->R_rx = 0; /* clear RX buffer */
|
||||
if (dsp->conf)
|
||||
dsp->W_rx = dsp->R_rx = dsp->conf->W_max;
|
||||
dsp->tx_W = dsp->tx_R = 0; /* clear TX buffer */
|
||||
dsp->rx_W = dsp->rx_R = -1; /* reset RX buffer */
|
||||
memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
|
||||
dsp_cmx_hardware(dsp->conf, dsp);
|
||||
// /* now trigger transmission */
|
||||
//#ifndef AUTOJITTER
|
||||
// dsp->tx_pending = 1;
|
||||
// schedule_work(&dsp->sendwork);
|
||||
//#endif
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: done with activation, sending confirm to user space. %s\n", __FUNCTION__, dsp->inst.name);
|
||||
/* send activation to upper layer */
|
||||
hh->prim = DL_ESTABLISH | CONFIRM;
|
||||
unlock_HW(&dsp_lock);
|
||||
return(dsp->inst.up.func(&dsp->inst.up, skb));
|
||||
ret = mISDN_queue_up(&dsp->inst, 0, skb);
|
||||
|
||||
break;
|
||||
case PH_DEACTIVATE | CONFIRM:
|
||||
lock_HW(&dsp_lock, 0);
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", __FUNCTION__, dsp->inst.name);
|
||||
/* bchannel now inactive */
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
dsp->b_active = 0;
|
||||
dsp_cmx_hardware(dsp->conf, dsp);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
hh->prim = DL_RELEASE | CONFIRM;
|
||||
unlock_HW(&dsp_lock);
|
||||
return(dsp->inst.up.func(&dsp->inst.up, skb));
|
||||
ret = mISDN_queue_up(&dsp->inst, 0, skb);
|
||||
|
||||
break;
|
||||
default:
|
||||
if (dsp_debug & DEBUG_DSP_CORE)
|
||||
printk(KERN_DEBUG "%s: msg %x unhandled %s\n", __FUNCTION__, hh->prim, dsp->inst.name);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (!ret)
|
||||
dev_kfree_skb(skb);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* messages from queue
|
||||
*/
|
||||
static int
|
||||
dsp_function(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
mISDN_head_t *hh;
|
||||
int ret = -EINVAL;
|
||||
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
switch (hh->addr & MSG_DIR_MASK) {
|
||||
case FLG_MSG_DOWN:
|
||||
ret = dsp_from_up(inst, skb);
|
||||
break;
|
||||
case FLG_MSG_UP:
|
||||
ret = dsp_from_down(inst, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
@ -660,18 +662,14 @@ static void
|
|||
release_dsp(dsp_t *dsp)
|
||||
{
|
||||
mISDNinstance_t *inst = &dsp->inst;
|
||||
conference_t *conf;
|
||||
conference_t *conf;
|
||||
u_long flags;
|
||||
|
||||
lock_HW(&dsp_lock, 0);
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
if (timer_pending(&dsp->feature_tl))
|
||||
del_timer(&dsp->feature_tl);
|
||||
if (timer_pending(&dsp->tone.tl))
|
||||
del_timer(&dsp->tone.tl);
|
||||
#ifdef HAS_WORKQUEUE
|
||||
if (dsp->sendwork.pending)
|
||||
printk(KERN_ERR "%s: pending sendwork: %lx %s\n", __FUNCTION__, dsp->sendwork.pending, dsp->inst.name);
|
||||
#else
|
||||
if (dsp->sendwork.sync)
|
||||
printk(KERN_ERR "%s: pending sendwork: %lx %s\n", __FUNCTION__, dsp->sendwork.sync, dsp->inst.name);
|
||||
#endif
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: removing conferences %s\n", __FUNCTION__, dsp->inst.name);
|
||||
conf = dsp->conf;
|
||||
|
@ -682,22 +680,11 @@ release_dsp(dsp_t *dsp)
|
|||
}
|
||||
}
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: disconnecting instances %s\n", __FUNCTION__, dsp->inst.name);
|
||||
if (inst->up.peer) {
|
||||
inst->up.peer->obj->ctrl(inst->up.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
}
|
||||
if (inst->down.peer) {
|
||||
inst->down.peer->obj->ctrl(inst->down.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->down);
|
||||
}
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: remove & destroy object %s\n", __FUNCTION__, dsp->inst.name);
|
||||
list_del(&dsp->list);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
dsp_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
unlock_HW(&dsp_lock);
|
||||
vfree(dsp);
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
|
@ -705,14 +692,57 @@ release_dsp(dsp_t *dsp)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* ask for hardware features
|
||||
*/
|
||||
static void
|
||||
dsp_feat(void *arg)
|
||||
{
|
||||
dsp_t *dsp = arg;
|
||||
struct sk_buff *nskb;
|
||||
void *feat;
|
||||
|
||||
switch (dsp->feature_state) {
|
||||
case FEAT_STATE_INIT:
|
||||
feat = &dsp->features;
|
||||
nskb = create_link_skb(PH_CONTROL | REQUEST, HW_FEATURES, sizeof(feat), &feat, 0);
|
||||
if (!nskb)
|
||||
break;
|
||||
if (mISDN_queue_down(&dsp->inst, 0, nskb)) {
|
||||
dev_kfree_skb(nskb);
|
||||
break;
|
||||
}
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: features will be quered now for instance %s\n", __FUNCTION__, dsp->inst.name);
|
||||
dsp->feature_state = FEAT_STATE_WAIT;
|
||||
init_timer(&dsp->feature_tl);
|
||||
dsp->feature_tl.expires = jiffies + (HZ / 100);
|
||||
add_timer(&dsp->feature_tl);
|
||||
break;
|
||||
case FEAT_STATE_WAIT:
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: features of %s are: hfc_id=%d hfc_dtmf=%d hfc_loops=%d pcm_id=%d pcm_slots=%d pcm_banks=%d\n",
|
||||
__FUNCTION__, dsp->inst.name,
|
||||
dsp->features.hfc_id,
|
||||
dsp->features.hfc_dtmf,
|
||||
dsp->features.hfc_loops,
|
||||
dsp->features.pcm_id,
|
||||
dsp->features.pcm_slots,
|
||||
dsp->features.pcm_banks);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create new DSP instances
|
||||
*/
|
||||
static int
|
||||
new_dsp(mISDNstack_t *st, mISDN_pid_t *pid)
|
||||
{
|
||||
int err = 0;
|
||||
dsp_t *ndsp;
|
||||
int err = 0;
|
||||
dsp_t *ndsp;
|
||||
u_long flags;
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: creating new dsp instance\n", __FUNCTION__);
|
||||
|
@ -725,8 +755,7 @@ new_dsp(mISDNstack_t *st, mISDN_pid_t *pid)
|
|||
}
|
||||
memset(ndsp, 0, sizeof(dsp_t));
|
||||
memcpy(&ndsp->inst.pid, pid, sizeof(mISDN_pid_t));
|
||||
ndsp->inst.obj = &dsp_obj;
|
||||
ndsp->inst.data = ndsp;
|
||||
mISDN_init_instance(&ndsp->inst, &dsp_obj, ndsp, dsp_function);
|
||||
if (!mISDN_SetHandledPID(&dsp_obj, &ndsp->inst.pid)) {
|
||||
int_error();
|
||||
err = -ENOPROTOOPT;
|
||||
|
@ -735,13 +764,8 @@ new_dsp(mISDNstack_t *st, mISDN_pid_t *pid)
|
|||
return(err);
|
||||
}
|
||||
sprintf(ndsp->inst.name, "DSP_S%x/C%x",
|
||||
(st->id&0xff), (st->id&0xff00)>>8);
|
||||
ndsp->inst.up.owner = &ndsp->inst;
|
||||
ndsp->inst.down.owner = &ndsp->inst;
|
||||
//#ifndef AUTOJITTER
|
||||
(st->id&0xff00)>>8, (st->id&0xff0000)>>16);
|
||||
/* set frame size to start */
|
||||
ndsp->largest = 64 << 1;
|
||||
//#endif
|
||||
ndsp->features.hfc_id = -1; /* current PCM id */
|
||||
ndsp->features.pcm_id = -1; /* current PCM id */
|
||||
ndsp->pcm_slot_rx = -1; /* current CPM slot */
|
||||
|
@ -749,81 +773,59 @@ new_dsp(mISDNstack_t *st, mISDN_pid_t *pid)
|
|||
ndsp->pcm_bank_rx = -1;
|
||||
ndsp->pcm_bank_tx = -1;
|
||||
ndsp->hfc_conf = -1; /* current conference number */
|
||||
/* set timer */
|
||||
/* set tone timer */
|
||||
ndsp->tone.tl.function = (void *)dsp_tone_timeout;
|
||||
ndsp->tone.tl.data = (long) ndsp;
|
||||
init_timer(&ndsp->tone.tl);
|
||||
/* init send que */
|
||||
INIT_WORK(&ndsp->sendwork, (void *)(void *)sendevent, ndsp);
|
||||
lock_HW(&dsp_lock, 0);
|
||||
/* set dsp feture timer */
|
||||
ndsp->feature_tl.function = (void *)dsp_feat;
|
||||
ndsp->feature_tl.data = (long) ndsp;
|
||||
ndsp->feature_state = FEAT_STATE_INIT;
|
||||
init_timer(&ndsp->feature_tl);
|
||||
if (!(dsp_options & DSP_OPT_NOHARDWARE)) {
|
||||
ndsp->feature_tl.expires = jiffies + (HZ / 100);
|
||||
add_timer(&ndsp->feature_tl);
|
||||
}
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
/* append and register */
|
||||
list_add_tail(&ndsp->list, &dsp_obj.ilist);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
err = dsp_obj.ctrl(st, MGR_REGLAYER | INDICATION, &ndsp->inst);
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: failed to register layer %s\n", __FUNCTION__, ndsp->inst.name);
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
list_del(&ndsp->list);
|
||||
unlock_HW(&dsp_lock);
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
goto free_mem;
|
||||
}
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: dsp instance created %s\n", __FUNCTION__, ndsp->inst.name);
|
||||
unlock_HW(&dsp_lock);
|
||||
return(err);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ask for hardware features
|
||||
*/
|
||||
static void
|
||||
dsp_feat(dsp_t *dsp)
|
||||
{
|
||||
struct sk_buff *nskb;
|
||||
void *feat;
|
||||
|
||||
if (!(dsp_options & DSP_OPT_NOHARDWARE)) {
|
||||
feat = &dsp->features;
|
||||
nskb = create_link_skb(PH_CONTROL | REQUEST, HW_FEATURES, sizeof(feat), &feat, 0);
|
||||
if (nskb) {
|
||||
if (dsp->inst.down.func(&dsp->inst.down, nskb)) {
|
||||
dev_kfree_skb(nskb);
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: no features supported by %s\n", __FUNCTION__, dsp->inst.name);
|
||||
} else {
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: features of %s: hfc_id=%d hfc_dtmf=%d hfc_loops=%d pcm_id=%d pcm_slots=%d pcm_banks=%d\n",
|
||||
__FUNCTION__, dsp->inst.name,
|
||||
dsp->features.hfc_id,
|
||||
dsp->features.hfc_dtmf,
|
||||
dsp->features.hfc_loops,
|
||||
dsp->features.pcm_id,
|
||||
dsp->features.pcm_slots,
|
||||
dsp->features.pcm_banks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* manager for DSP instances
|
||||
*/
|
||||
static int
|
||||
dsp_manager(void *data, u_int prim, void *arg) {
|
||||
mISDNinstance_t *inst = data;
|
||||
dsp_t *dspl;
|
||||
int ret = -EINVAL;
|
||||
mISDNinstance_t *inst = data;
|
||||
dsp_t *dspl;
|
||||
int ret = -EINVAL;
|
||||
u_long flags;
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", __FUNCTION__, data, prim, arg);
|
||||
if (!data)
|
||||
return(ret);
|
||||
spin_lock_irqsave(&dsp_obj.lock, flags);
|
||||
list_for_each_entry(dspl, &dsp_obj.ilist, list) {
|
||||
if (&dspl->inst == inst) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&dsp_obj.lock, flags);
|
||||
if (ret && (prim != (MGR_NEWLAYER | REQUEST))) {
|
||||
printk(KERN_WARNING "%s: given instance(%p) not in ilist.\n", __FUNCTION__, data);
|
||||
return(ret);
|
||||
|
@ -833,9 +835,11 @@ dsp_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_NEWLAYER | REQUEST:
|
||||
ret = new_dsp(data, arg);
|
||||
break;
|
||||
case MGR_SETSTACK | INDICATION:
|
||||
break;
|
||||
#ifdef OBSOLETE
|
||||
case MGR_CONNECT | REQUEST:
|
||||
ret = mISDN_ConnectIF(inst, arg);
|
||||
dsp_feat(dspl);
|
||||
break;
|
||||
case MGR_SETIF | REQUEST:
|
||||
case MGR_SETIF | INDICATION:
|
||||
|
@ -845,6 +849,7 @@ dsp_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_DISCONNECT | INDICATION:
|
||||
ret = mISDN_DisConnectIF(inst, arg);
|
||||
break;
|
||||
#endif
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
case MGR_RELEASE | INDICATION:
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
|
@ -873,44 +878,47 @@ static int dsp_init(void)
|
|||
dsp_debug = debug;
|
||||
|
||||
/* display revision */
|
||||
printk(KERN_INFO "mISDN_dsp: Audio DSP Rev. %s (debug=0x%x)\n", mISDN_getrev(dsp_revision), debug);
|
||||
printk(KERN_INFO "mISDN_dsp: Audio DSP Rev. %s (debug=0x%x) EchoCancellor %s\n", mISDN_getrev(dsp_revision), debug, EC_TYPE);
|
||||
|
||||
#ifndef AUTOJITTER
|
||||
/* set packet size */
|
||||
switch(poll) {
|
||||
case 8:
|
||||
break;
|
||||
case 16:
|
||||
break;
|
||||
case 32:
|
||||
break;
|
||||
case 64: case 0:
|
||||
poll = 64;
|
||||
break;
|
||||
case 128:
|
||||
break;
|
||||
case 256:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: Wrong poll value (%d).\n", __FUNCTION__, poll);
|
||||
if (poll == 0) {
|
||||
if (HZ == 100)
|
||||
poll = 80;
|
||||
else
|
||||
poll = 64;
|
||||
}
|
||||
|
||||
if (poll > MAX_POLL) {
|
||||
printk(KERN_ERR "%s: Wrong poll value (%d), using %d.\n", __FUNCTION__, poll, MAX_POLL);
|
||||
poll = MAX_POLL;
|
||||
}
|
||||
if (poll < 8) {
|
||||
printk(KERN_ERR "%s: Wrong poll value (%d), using 8.\n", __FUNCTION__, poll);
|
||||
poll = 8;
|
||||
}
|
||||
dsp_poll = poll;
|
||||
dsp_tics = poll * HZ / 8000;
|
||||
if (dsp_tics * 8000 == poll * HZ)
|
||||
printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals %d jiffies.\n", poll, dsp_tics);
|
||||
else {
|
||||
printk(KERN_INFO "mISDN_dsp: Cannot clock ever %d samples. Use a multiple of %d (samples)\n", poll, 8000 / HZ);
|
||||
err = -EINVAL;
|
||||
return(err);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* fill mISDN object (dsp_obj) */
|
||||
memset(&dsp_obj, 0, sizeof(dsp_obj));
|
||||
#ifdef MODULE
|
||||
SET_MODULE_OWNER(&dsp_obj);
|
||||
#endif
|
||||
spin_lock_init(&dsp_obj.lock);
|
||||
dsp_obj.name = DSPName;
|
||||
dsp_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_DSP;
|
||||
dsp_obj.own_ctrl = dsp_manager;
|
||||
INIT_LIST_HEAD(&dsp_obj.ilist);
|
||||
|
||||
/* initialize audio tables */
|
||||
silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a;
|
||||
dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a;
|
||||
dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32:dsp_audio_alaw_to_s32;
|
||||
dsp_audio_generate_s2law_table();
|
||||
dsp_audio_generate_seven();
|
||||
|
@ -919,15 +927,20 @@ static int dsp_init(void)
|
|||
dsp_audio_generate_ulaw_samples();
|
||||
dsp_audio_generate_volume_changes();
|
||||
|
||||
/* init global lock */
|
||||
lock_HW_init(&dsp_lock);
|
||||
|
||||
/* register object */
|
||||
if ((err = mISDN_register(&dsp_obj))) {
|
||||
printk(KERN_ERR "mISDN_dsp: Can't register %s error(%d)\n", DSPName, err);
|
||||
return(err);
|
||||
}
|
||||
|
||||
/* set sample timer */
|
||||
dsp_spl_tl.function = (void *)dsp_cmx_send;
|
||||
dsp_spl_tl.data = 0;
|
||||
init_timer(&dsp_spl_tl);
|
||||
dsp_spl_tl.expires = jiffies + dsp_tics + 1; /* safer */
|
||||
dsp_spl_jiffies = dsp_spl_tl.expires;
|
||||
add_timer(&dsp_spl_tl);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
@ -940,6 +953,9 @@ static void dsp_cleanup(void)
|
|||
dsp_t *dspl, *nd;
|
||||
int err;
|
||||
|
||||
if (timer_pending(&dsp_spl_tl))
|
||||
del_timer(&dsp_spl_tl);
|
||||
|
||||
if (dsp_debug & DEBUG_DSP_MGR)
|
||||
printk(KERN_DEBUG "%s: removing module\n", __FUNCTION__);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
* DTMF decoder.
|
||||
*
|
||||
* Copyright 2003 by Andreas Eversberg (jolly@jolly.de)
|
||||
* Copyright by Andreas Eversberg (jolly@jolly.de)
|
||||
* based on different decoders such as ISDN4Linux
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
|
|
|
@ -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 <steveu@coppice.org>
|
||||
*
|
||||
* 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 ------------------------------------------------------------*/
|
|
@ -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 <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#define MALLOC(a) kmalloc((a), GFP_KERNEL)
|
||||
#define FREE(a) kfree(a)
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#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
|
|
@ -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 */
|
||||
|
|
@ -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 <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#define MALLOC(a) kmalloc((a), GFP_KERNEL)
|
||||
#define FREE(a) kfree(a)
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#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; 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
|
@ -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 */
|
||||
|
|
@ -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 <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#define MALLOC(a) kmalloc((a), GFP_KERNEL)
|
||||
#define FREE(a) kfree(a)
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#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;x<ec->N_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
|
|
@ -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 */
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
* Audio support data for ISDN4Linux.
|
||||
*
|
||||
* Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de)
|
||||
* Copyright Andreas Eversberg (jolly@jolly.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
|
@ -373,7 +373,7 @@ void dsp_tone_copy(dsp_t *dsp, u8 *data, int len)
|
|||
|
||||
/* if we have no tone, we copy silence */
|
||||
if (!tone->tone) {
|
||||
memset(data, silence, len);
|
||||
memset(data, dsp_silence, len);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -437,7 +437,7 @@ dsp_tone_hw_message(dsp_t *dsp, u8 *sample, int len)
|
|||
return;
|
||||
}
|
||||
/* unlocking is not required, because we don't expect a response */
|
||||
if (dsp->inst.down.func(&dsp->inst.down, nskb))
|
||||
if (mISDN_queue_down(&dsp->inst, 0, nskb))
|
||||
dev_kfree_skb(nskb);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#define N303 1
|
||||
#define T_CTRL 180000
|
||||
|
||||
#define THOLD 4000
|
||||
#define TRETRIEVE 4000
|
||||
|
||||
/* private TIMER events */
|
||||
#define CC_T302 0x030201
|
||||
#define CC_T303 0x030301
|
||||
|
@ -36,6 +39,8 @@
|
|||
#define CC_T318 0x031801
|
||||
#define CC_T319 0x031901
|
||||
#define CC_TCTRL 0x031f01
|
||||
#define CC_THOLD 0x03a001
|
||||
#define CC_TRETRIEVE 0x03a101
|
||||
/*
|
||||
* Message-Types
|
||||
*/
|
||||
|
@ -47,6 +52,12 @@
|
|||
#define MT_PROGRESS 0x03
|
||||
#define MT_SETUP 0x05
|
||||
#define MT_SETUP_ACKNOWLEDGE 0x0d
|
||||
#define MT_HOLD 0x24
|
||||
#define MT_HOLD_ACKNOWLEDGE 0x28
|
||||
#define MT_HOLD_REJECT 0x30
|
||||
#define MT_RETRIEVE 0x31
|
||||
#define MT_RETRIEVE_ACKNOWLEDGE 0x33
|
||||
#define MT_RETRIEVE_REJECT 0x37
|
||||
#define MT_RESUME 0x26
|
||||
#define MT_RESUME_ACKNOWLEDGE 0x2e
|
||||
#define MT_RESUME_REJECT 0x22
|
||||
|
@ -137,6 +148,16 @@
|
|||
|
||||
#define NO_CAUSE 254
|
||||
|
||||
#define AUX_IDLE 0
|
||||
#define AUX_HOLD_REQ 1
|
||||
#define AUX_CALL_HELD 2
|
||||
#define AUX_RETRIEVE_REQ 3
|
||||
#define AUX_HOLD_IND 4
|
||||
#define AUX_RETRIEVE_IND 5
|
||||
|
||||
#define VALID_HOLD_STATES_PTMP (SBIT(3) | SBIT(4) | SBIT(10))
|
||||
#define VALID_HOLD_STATES_PTP (SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10))
|
||||
|
||||
#else /* only l3dss1_process */
|
||||
|
||||
/* l3dss1 specific data in l3 process */
|
||||
|
|
|
@ -36,7 +36,7 @@ typedef struct _dtmf {
|
|||
#define FLG_DTMF_ULAW 1
|
||||
#define FLG_DTMF_ACTIV 2
|
||||
|
||||
static int debug = 0;
|
||||
static u_int debug = 0;
|
||||
|
||||
#define DEBUG_DTMF_MGR 0x001
|
||||
#define DEBUG_DTMF_TONE 0x010
|
||||
|
@ -350,7 +350,7 @@ isdn_audio_goertzel(dtmf_t *dtmf)
|
|||
if (dtmf->debug & DEBUG_DTMF_TONE)
|
||||
printk(KERN_DEBUG "DTMF: tone='%c'\n", what);
|
||||
k = what | DTMF_TONE_VAL;
|
||||
if_link(&dtmf->inst.up, PH_CONTROL | INDICATION,
|
||||
mISDN_queue_data(&dtmf->inst, FLG_MSG_UP, PH_CONTROL | INDICATION,
|
||||
0, sizeof(int), &k, 0);
|
||||
}
|
||||
dtmf->last = what;
|
||||
|
@ -397,20 +397,16 @@ dtmf_reset(dtmf_t *dtmf)
|
|||
dtmf->idx = 0;
|
||||
}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
static int
|
||||
dtmf_from_up(mISDNif_t *hif, struct sk_buff *skb)
|
||||
dtmf_from_up(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
dtmf_t *dtmf;
|
||||
mISDN_head_t *hh;
|
||||
int *data;
|
||||
int err = 0;
|
||||
|
||||
if (!hif || !hif->fdata || !skb)
|
||||
return(-EINVAL);
|
||||
dtmf = hif->fdata;
|
||||
if (!dtmf->inst.down.func) {
|
||||
return(-ENXIO);
|
||||
}
|
||||
dtmf = inst->privat;
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
switch(hh->prim) {
|
||||
case (PH_CONTROL | REQUEST):
|
||||
|
@ -431,25 +427,21 @@ dtmf_from_up(mISDNif_t *hif, struct sk_buff *skb)
|
|||
}
|
||||
/* Fall trough in case of not handled function */
|
||||
default:
|
||||
return(dtmf->inst.down.func(&dtmf->inst.down, skb));
|
||||
return(mISDN_queue_down(inst, 0, skb));
|
||||
}
|
||||
if (!err)
|
||||
dev_kfree_skb(skb);
|
||||
return(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
dtmf_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
||||
dtmf_function(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
dtmf_t *dtmf;
|
||||
mISDN_head_t *hh;
|
||||
|
||||
if (!hif || !hif->fdata || !skb)
|
||||
return(-EINVAL);
|
||||
dtmf = hif->fdata;
|
||||
if (!dtmf->inst.up.func) {
|
||||
return(-ENXIO);
|
||||
}
|
||||
dtmf = inst->privat;
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
switch(hh->prim) {
|
||||
case (PH_DATA | CONFIRM):
|
||||
|
@ -461,6 +453,25 @@ dtmf_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
isdn_audio_calc_dtmf(dtmf, skb);
|
||||
hh->prim = DL_DATA_IND;
|
||||
break;
|
||||
case (PH_CONTROL | REQUEST):
|
||||
if ((hh->dinfo == 0) && (skb->len >= sizeof(int))) {
|
||||
int *data = (int *)skb->data;
|
||||
if (dtmf->debug & DEBUG_DTMF_CTRL)
|
||||
printk(KERN_DEBUG "DTMF: PH_CONTROL REQ data %04x\n",
|
||||
*data);
|
||||
if (*data == DTMF_TONE_START) {
|
||||
test_and_set_bit(FLG_DTMF_ACTIV, &dtmf->Flags);
|
||||
dtmf_reset(dtmf);
|
||||
dev_kfree_skb(skb);
|
||||
return(0);
|
||||
} else if (*data == DTMF_TONE_STOP) {
|
||||
test_and_clear_bit(FLG_DTMF_ACTIV, &dtmf->Flags);
|
||||
dtmf_reset(dtmf);
|
||||
dev_kfree_skb(skb);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case (PH_ACTIVATE | CONFIRM):
|
||||
hh->prim = DL_ESTABLISH | CONFIRM;
|
||||
break;
|
||||
|
@ -474,30 +485,26 @@ dtmf_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
hh->prim = DL_RELEASE | INDICATION;
|
||||
break;
|
||||
}
|
||||
return(dtmf->inst.up.func(&dtmf->inst.up, skb));
|
||||
return(mISDN_queue_message(inst, hh->addr & MSG_DIR_MASK, skb));
|
||||
}
|
||||
|
||||
static void
|
||||
release_dtmf(dtmf_t *dtmf) {
|
||||
mISDNinstance_t *inst = &dtmf->inst;
|
||||
u_long flags;
|
||||
|
||||
if (inst->up.peer) {
|
||||
inst->up.peer->obj->ctrl(inst->up.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
}
|
||||
if (inst->down.peer) {
|
||||
inst->down.peer->obj->ctrl(inst->down.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->down);
|
||||
}
|
||||
spin_lock_irqsave(&dtmf_obj.lock, flags);
|
||||
list_del(&dtmf->list);
|
||||
spin_unlock_irqrestore(&dtmf_obj.lock, flags);
|
||||
dtmf_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
kfree(dtmf);
|
||||
}
|
||||
|
||||
static int
|
||||
new_dtmf(mISDNstack_t *st, mISDN_pid_t *pid) {
|
||||
dtmf_t *n_dtmf;
|
||||
int err;
|
||||
dtmf_t *n_dtmf;
|
||||
int err;
|
||||
u_long flags;
|
||||
|
||||
if (!st || !pid)
|
||||
return(-EINVAL);
|
||||
|
@ -507,14 +514,16 @@ new_dtmf(mISDNstack_t *st, mISDN_pid_t *pid) {
|
|||
}
|
||||
memset(n_dtmf, 0, sizeof(dtmf_t));
|
||||
memcpy(&n_dtmf->inst.pid, pid, sizeof(mISDN_pid_t));
|
||||
mISDN_init_instance(&n_dtmf->inst, &dtmf_obj, n_dtmf);
|
||||
mISDN_init_instance(&n_dtmf->inst, &dtmf_obj, n_dtmf, dtmf_function);
|
||||
if (!mISDN_SetHandledPID(&dtmf_obj, &n_dtmf->inst.pid)) {
|
||||
int_error();
|
||||
kfree(n_dtmf);
|
||||
return(-ENOPROTOOPT);
|
||||
}
|
||||
n_dtmf->debug = debug;
|
||||
spin_lock_irqsave(&dtmf_obj.lock, flags);
|
||||
list_add_tail(&n_dtmf->list, &dtmf_obj.ilist);
|
||||
spin_unlock_irqrestore(&dtmf_obj.lock, flags);
|
||||
err = dtmf_obj.ctrl(st, MGR_REGLAYER | INDICATION, &n_dtmf->inst);
|
||||
if (err) {
|
||||
list_del(&n_dtmf->list);
|
||||
|
@ -550,7 +559,8 @@ static char MName[] = "DTMF";
|
|||
|
||||
#ifdef MODULE
|
||||
MODULE_AUTHOR("Karsten Keil");
|
||||
MODULE_PARM(debug, "1i");
|
||||
module_param (debug, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC (debug, "dtmf debug mask");
|
||||
#ifdef MODULE_LICENSE
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
@ -561,17 +571,20 @@ dtmf_manager(void *data, u_int prim, void *arg) {
|
|||
mISDNinstance_t *inst = data;
|
||||
dtmf_t *dtmf_l;
|
||||
int ret = -EINVAL;
|
||||
u_long flags;
|
||||
|
||||
if (debug & DEBUG_DTMF_MGR)
|
||||
printk(KERN_DEBUG "dtmf_manager data:%p prim:%x arg:%p\n", data, prim, arg);
|
||||
if (!data)
|
||||
return(ret);
|
||||
spin_lock_irqsave(&dtmf_obj.lock, flags);
|
||||
list_for_each_entry(dtmf_l, &dtmf_obj.ilist, list) {
|
||||
if (&dtmf_l->inst == inst) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&dtmf_obj.lock, flags);
|
||||
if (prim == (MGR_NEWLAYER | REQUEST))
|
||||
return(new_dtmf(data, arg));
|
||||
if (ret) {
|
||||
|
@ -580,6 +593,8 @@ dtmf_manager(void *data, u_int prim, void *arg) {
|
|||
}
|
||||
switch(prim) {
|
||||
case MGR_CLRSTPARA | INDICATION:
|
||||
break;
|
||||
#ifdef OBSOLETE
|
||||
case MGR_CLONELAYER | REQUEST:
|
||||
break;
|
||||
case MGR_CONNECT | REQUEST:
|
||||
|
@ -590,6 +605,7 @@ dtmf_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_DISCONNECT | REQUEST:
|
||||
case MGR_DISCONNECT | INDICATION:
|
||||
return(mISDN_DisConnectIF(inst, arg));
|
||||
#endif
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
case MGR_RELEASE | INDICATION:
|
||||
if (debug & DEBUG_DTMF_MGR)
|
||||
|
@ -617,6 +633,7 @@ static int dtmf_init(void)
|
|||
dtmf_obj.name = MName;
|
||||
dtmf_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANSDTMF;
|
||||
dtmf_obj.own_ctrl = dtmf_manager;
|
||||
spin_lock_init(&dtmf_obj.lock);
|
||||
INIT_LIST_HEAD(&dtmf_obj.ilist);
|
||||
if ((err = mISDN_register(&dtmf_obj))) {
|
||||
printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
|
||||
|
|
|
@ -240,6 +240,7 @@ mISDN_get_up_layer(int layermask) {
|
|||
return(uplayer);
|
||||
}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
int
|
||||
mISDN_SetIF(mISDNinstance_t *owner, mISDNif_t *hif, u_int prim, void *upfunc,
|
||||
void *downfunc, void *data)
|
||||
|
@ -339,9 +340,10 @@ mISDN_DisConnectIF(mISDNinstance_t *inst, mISDNif_t *hif) {
|
|||
}
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
mISDN_init_instance(mISDNinstance_t *inst, mISDNobject_t *obj, void *data)
|
||||
mISDN_init_instance(mISDNinstance_t *inst, mISDNobject_t *obj, void *data, if_func_t *function)
|
||||
{
|
||||
if (!obj) {
|
||||
int_error();
|
||||
|
@ -351,16 +353,10 @@ mISDN_init_instance(mISDNinstance_t *inst, mISDNobject_t *obj, void *data)
|
|||
int_error();
|
||||
return;
|
||||
}
|
||||
INIT_LIST_HEAD(&inst->list);
|
||||
inst->obj = obj;
|
||||
inst->data = data;
|
||||
inst->up.owner = inst;
|
||||
inst->down.owner = inst;
|
||||
inst->up.clone = NULL;
|
||||
inst->up.predecessor = NULL;
|
||||
inst->down.clone = NULL;
|
||||
inst->down.predecessor = NULL;
|
||||
obj->ctrl(NULL, MGR_DISCONNECT | REQUEST, &inst->down);
|
||||
obj->ctrl(NULL, MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
inst->privat = data;
|
||||
inst->function = function;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mISDN_set_dchannel_pid);
|
||||
|
@ -373,7 +369,7 @@ EXPORT_SYMBOL(mISDN_HasProtocol);
|
|||
EXPORT_SYMBOL(mISDN_SetHandledPID);
|
||||
EXPORT_SYMBOL(mISDN_RemoveUsedPID);
|
||||
EXPORT_SYMBOL(mISDN_init_instance);
|
||||
EXPORT_SYMBOL(mISDN_SetIF);
|
||||
EXPORT_SYMBOL(mISDN_ConnectIF);
|
||||
EXPORT_SYMBOL(mISDN_DisConnectIF);
|
||||
// EXPORT_SYMBOL(mISDN_SetIF);
|
||||
// EXPORT_SYMBOL(mISDN_ConnectIF);
|
||||
// EXPORT_SYMBOL(mISDN_DisConnectIF);
|
||||
EXPORT_SYMBOL(mISDN_bprotocol2pid);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
* Basic declarations, defines and prototypes
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
* This file is released under the GPLv2
|
||||
*
|
||||
*/
|
||||
#ifndef _mISDN_HELPER_H
|
||||
|
@ -43,14 +43,14 @@ discard_queue(struct sk_buff_head *q)
|
|||
#ifdef MISDN_MEMDEBUG
|
||||
#define alloc_stack_skb(s, r) __mid_alloc_stack_skb(s, r, __FILE__, __LINE__)
|
||||
static inline struct sk_buff *
|
||||
__mid_alloc_stack_skb(size_t size, size_t reserve, char *fn, int line)
|
||||
__mid_alloc_stack_skb(u_int size, u_int reserve, char *fn, int line)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!(skb = __mid_alloc_skb(size + reserve, GFP_ATOMIC, fn, line)))
|
||||
#else
|
||||
static inline struct sk_buff *
|
||||
alloc_stack_skb(size_t size, size_t reserve)
|
||||
alloc_stack_skb(u_int size, u_int reserve)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
|
@ -141,7 +141,7 @@ extern void mISDN_RemoveUsedPID(mISDN_pid_t *, mISDN_pid_t *);
|
|||
*
|
||||
* initialisize the mISDNinstance_t struct <inst>
|
||||
*/
|
||||
extern void mISDN_init_instance(mISDNinstance_t *, mISDNobject_t *, void *);
|
||||
extern void mISDN_init_instance(mISDNinstance_t *, mISDNobject_t *, void *, if_func_t *);
|
||||
|
||||
/* returns the member count of a list */
|
||||
static inline int
|
||||
|
@ -176,32 +176,48 @@ mISDN_sethead(u_int prim, int dinfo, struct sk_buff *skb)
|
|||
hh->dinfo = dinfo;
|
||||
}
|
||||
|
||||
/* send the skb through this interface with new header values */
|
||||
#define mISDN_queue_up(i, a, s) mISDN_queue_message(i, a | FLG_MSG_UP, s)
|
||||
#define mISDN_queue_down(i, a, s) mISDN_queue_message(i, a | FLG_MSG_DOWN, s)
|
||||
|
||||
static inline int
|
||||
if_newhead(mISDNif_t *i, u_int prim, int dinfo, struct sk_buff *skb)
|
||||
mISDN_queueup_newhead(mISDNinstance_t *inst, u_int aflag, u_int prim, int dinfo, struct sk_buff *skb)
|
||||
{
|
||||
if (!i->func || !skb)
|
||||
return(-ENXIO);
|
||||
mISDN_sethead(prim, dinfo, skb);
|
||||
return(i->func(i, skb));
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
||||
|
||||
hh->prim = prim;
|
||||
hh->dinfo = dinfo;
|
||||
return(mISDN_queue_up(inst, aflag, skb));
|
||||
}
|
||||
|
||||
static inline int
|
||||
mISDN_queuedown_newhead(mISDNinstance_t *inst, u_int aflag, u_int prim, int dinfo, struct sk_buff *skb)
|
||||
{
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
||||
|
||||
hh->prim = prim;
|
||||
hh->dinfo = dinfo;
|
||||
return(mISDN_queue_down(inst, aflag, skb));
|
||||
}
|
||||
|
||||
|
||||
/* allocate a mISDN message SKB with enough headroom and set the header fields
|
||||
* the MEMDEBUG version is for debugging memory leaks in the mISDN stack
|
||||
*/
|
||||
#ifdef MISDN_MEMDEBUG
|
||||
#define create_link_skb(p, d, l, a, r) __mid_create_link_skb(p, d, l, a, r, __FILE__, __LINE__)
|
||||
#define create_link_skb(p, d, l, dp, r) __mid_create_link_skb(p, d, l, dp, r, __FILE__, __LINE__)
|
||||
static inline struct sk_buff *
|
||||
__mid_create_link_skb(u_int prim, int dinfo, int len, void *arg, int reserve, char *fn, int line)
|
||||
__mid_create_link_skb(u_int prim, int dinfo, u_int len, void *dp, u_int reserve, char *fn, int line)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
mISDN_head_t *hh;
|
||||
|
||||
if (!(skb = __mid_alloc_skb(len + reserve, GFP_ATOMIC, fn, line))) {
|
||||
#else
|
||||
static inline struct sk_buff *
|
||||
create_link_skb(u_int prim, int dinfo, int len, void *arg, int reserve)
|
||||
create_link_skb(u_int prim, int dinfo, u_int len, void *dp, u_int reserve)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
mISDN_head_t *hh;
|
||||
|
||||
if (!(skb = alloc_skb(len + reserve, GFP_ATOMIC))) {
|
||||
#endif
|
||||
|
@ -211,8 +227,11 @@ create_link_skb(u_int prim, int dinfo, int len, void *arg, int reserve)
|
|||
} else
|
||||
skb_reserve(skb, reserve);
|
||||
if (len)
|
||||
memcpy(skb_put(skb, len), arg, len);
|
||||
mISDN_sethead(prim, dinfo, skb);
|
||||
memcpy(skb_put(skb, len), dp, len);
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
hh->prim = prim;
|
||||
hh->dinfo = dinfo;
|
||||
hh->len = len;
|
||||
return(skb);
|
||||
}
|
||||
|
||||
|
@ -221,28 +240,25 @@ create_link_skb(u_int prim, int dinfo, int len, void *arg, int reserve)
|
|||
* the MEMDEBUG version is for debugging memory leaks in the mISDN stack
|
||||
*/
|
||||
#ifdef MISDN_MEMDEBUG
|
||||
#define if_link(i, p, d, l, a, r) __mid_if_link(i, p, d, l, a, r, __FILE__, __LINE__)
|
||||
#define mISDN_queue_data(i, a, p, d, l, dp, r) __mid_queue_data(i, a, p, d, l, dp, r, __FILE__, __LINE__)
|
||||
static inline int
|
||||
__mid_if_link(mISDNif_t *i, u_int prim, int dinfo, int len, void *arg, int reserve, char *fn, int line)
|
||||
__mid_queue_data(mISDNinstance_t *inst, u_int aflag, u_int prim, int dinfo, u_int len, void *dp, u_int reserve, char *fn, int line)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
if (!(skb = __mid_create_link_skb(prim, dinfo, len, arg, reserve, fn, line)))
|
||||
if (!(skb = __mid_create_link_skb(prim, dinfo, len, dp, reserve, fn, line)))
|
||||
#else
|
||||
static inline int
|
||||
if_link(mISDNif_t *i, u_int prim, int dinfo, int len, void *arg, int reserve)
|
||||
mISDN_queue_data(mISDNinstance_t *inst, u_int aflag, u_int prim, int dinfo, u_int len, void *dp, u_int reserve)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
if (!(skb = create_link_skb(prim, dinfo, len, arg, reserve)))
|
||||
if (!(skb = create_link_skb(prim, dinfo, len, dp, reserve)))
|
||||
#endif
|
||||
return(-ENOMEM);
|
||||
if (!i)
|
||||
err = -ENXIO;
|
||||
else
|
||||
err = i->func(i, skb);
|
||||
err = mISDN_queue_message(inst, aflag, skb);
|
||||
if (err)
|
||||
kfree_skb(skb);
|
||||
return(err);
|
||||
|
@ -261,6 +277,8 @@ extern struct sk_buff *mISDN_alloc_l3msg(int, u_char);
|
|||
#endif
|
||||
extern void mISDN_AddvarIE(struct sk_buff *, u_char *);
|
||||
extern void mISDN_AddIE(struct sk_buff *, u_char, u_char *);
|
||||
extern ie_info_t *mISDN_get_last_repeated_ie(Q931_info_t *, ie_info_t *);
|
||||
extern int mISDN_get_free_ext_ie(Q931_info_t *);
|
||||
extern void mISDN_LogL3Msg(struct sk_buff *);
|
||||
|
||||
/* manager default handler helper macros */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,13 +3,27 @@
|
|||
* see notice in hfc_multi.c
|
||||
*/
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
// IMPORTANT!!! use CONFIG_PLX_PCI_BRIDGE only in conjunction with CONFIG_HFCMULTI_PCIMEM
|
||||
//#define CONFIG_PLX_PCI_BRIDGE // TODO should be defined in kernel config
|
||||
|
||||
#ifdef CONFIG_PLX_PCI_BRIDGE
|
||||
#undef FIFO_32BIT_ACCESS
|
||||
#ifndef CONFIG_HFCMULTI_PCIMEM
|
||||
#define CONFIG_HFCMULTI_PCIMEM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DEBUG_HFCMULTI_FIFO 0x0001
|
||||
#define DEBUG_HFCMULTI_CRC 0x0002
|
||||
#define DEBUG_HFCMULTI_INIT 0x0004
|
||||
#define DEBUG_HFCMULTI_INIT 0x0004
|
||||
#define DEBUG_HFCMULTI_MGR 0x0008
|
||||
#define DEBUG_HFCMULTI_MODE 0x0010
|
||||
#define DEBUG_HFCMULTI_MSG 0x0020
|
||||
#define DEBUG_HFCMULTI_STATE 0x0040
|
||||
#define DEBUG_HFCMULTI_STATE 0x0040
|
||||
#define DEBUG_HFCMULTI_SYNC 0x0100
|
||||
#define DEBUG_HFCMULTI_DTMF 0x0200
|
||||
#define DEBUG_HFCMULTI_LOCK 0x8000
|
||||
|
@ -21,28 +35,22 @@
|
|||
also registers are assigned differen for HFC-4s/8s and HFC-E1
|
||||
*/
|
||||
|
||||
#define MAX_FRAME_SIZE 2048
|
||||
// #define MAX_FRAME_SIZE 2048
|
||||
|
||||
struct hfc_chan {
|
||||
/* dch or bch is set, otherwhise this channel is not used */
|
||||
dchannel_t *dch; /* link if channel is a D-channel */
|
||||
bchannel_t *bch; /* link if channel is a B-channel */
|
||||
int rx_idx; /* for D-channel */
|
||||
unsigned char *rx_buf; /* for D-channel */
|
||||
int port; /* the interface port this channel is associated with */
|
||||
int nt_mode;
|
||||
channel_t *ch; /* link if channel is a D-channel */
|
||||
int port; /* the interface port this channel is associated with */
|
||||
int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */
|
||||
int los, ais, slip_tx, slip_rx; /* current alarms */
|
||||
int jitter;
|
||||
u_long cfg; /* port configuration */
|
||||
int sync; /* sync state (used by E1) */
|
||||
struct sk_buff_head dtmfque;
|
||||
unsigned long protocol; /* current protocol */
|
||||
u_long cfg; /* port configuration */
|
||||
int sync; /* sync state (used by E1) */
|
||||
DWORD protocol;/* current protocol */
|
||||
int slot_tx; /* current pcm slot */
|
||||
int bank_tx; /* current pcm bank */
|
||||
int slot_rx;
|
||||
int bank_rx;
|
||||
int conf; /* conference setting of TX slot */
|
||||
int conf; /* conference setting of TX slot */
|
||||
int txpending; /* if there is currently data in the FIFO 0=no, 1=yes, 2=splloop */
|
||||
int e1_state; /* keep track of last state */
|
||||
};
|
||||
|
@ -50,17 +58,17 @@ struct hfc_chan {
|
|||
typedef struct hfc_chan hfc_chan_t;
|
||||
|
||||
struct hfcmulti_hw {
|
||||
unsigned char r_ctrl;
|
||||
unsigned char r_irq_ctrl;
|
||||
unsigned char r_cirm;
|
||||
unsigned char r_ram_sz;
|
||||
unsigned char r_pcm_md0;
|
||||
unsigned char r_irqmsk_misc;
|
||||
unsigned char r_dtmf;
|
||||
unsigned char r_st_sync;
|
||||
unsigned char r_sci_msk;
|
||||
unsigned char r_tx0, r_tx1;
|
||||
unsigned char a_st_ctrl0[8];
|
||||
BYTE r_ctrl;
|
||||
BYTE r_irq_ctrl;
|
||||
BYTE r_cirm;
|
||||
BYTE r_ram_sz;
|
||||
BYTE r_pcm_md0;
|
||||
BYTE r_irqmsk_misc;
|
||||
BYTE r_dtmf;
|
||||
BYTE r_st_sync;
|
||||
BYTE r_sci_msk;
|
||||
BYTE r_tx0, r_tx1;
|
||||
BYTE a_st_ctrl0[8];
|
||||
timer_t timer;
|
||||
};
|
||||
|
||||
|
@ -76,6 +84,8 @@ typedef struct hfcmulti_hw hfcmulti_hw_t;
|
|||
#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */
|
||||
#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */
|
||||
#define HFC_CFG_DTMF 8 /* enable DTMF-detection */
|
||||
#define HFC_CFG_CRC4 9 /* disable CRC-4 Multiframe mode,
|
||||
use double frame instead. */
|
||||
|
||||
#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */
|
||||
#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */
|
||||
|
@ -86,10 +96,14 @@ typedef struct hfcmulti_hw hfcmulti_hw_t;
|
|||
#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */
|
||||
#define HFC_CHIP_ULAW 7 /* ULAW mode */
|
||||
#define HFC_CHIP_CLOCK2 8 /* double clock mode */
|
||||
#define HFC_CHIP_CRYSTAL_CLOCK 9 /* autarc clocking mode */
|
||||
|
||||
struct hfc_multi {
|
||||
struct list_head list;
|
||||
#warning
|
||||
void *davor, *danach;
|
||||
char name[32];
|
||||
int idx; /* chip index for module parameters */
|
||||
int id; /* chip number starting with 1 */
|
||||
int pcm; /* id of pcm bus */
|
||||
int type;
|
||||
|
@ -98,7 +112,10 @@ struct hfc_multi {
|
|||
u_int irqcnt;
|
||||
struct pci_dev *pci_dev;
|
||||
#ifdef CONFIG_HFCMULTI_PCIMEM
|
||||
unsigned char *pci_membase;/* PCI memory (MUST BE BYTE POINTER) */
|
||||
u_long pci_origmembase, plx_origmembase, dsp_origmembase;
|
||||
u_char *pci_membase;/* PCI memory (MUST BE BYTE POINTER) */
|
||||
u_char *plx_membase;/* PCI memory (MUST BE BYTE POINTER) */
|
||||
u_char *dsp_membase;/* PCI memory (MUST BE BYTE POINTER) */
|
||||
#else
|
||||
u_int pci_iobase;/* PCI IO (MUST BE BYTE POINTER) */
|
||||
#endif
|
||||
|
@ -118,10 +135,7 @@ struct hfc_multi {
|
|||
u_int ledcount; /* used to animate leds */
|
||||
u_int activity[8]; /* if there is any action on this port (will be cleared after showing led-states) */
|
||||
|
||||
mISDN_HWlock_t lock; /* the lock */
|
||||
#ifdef SPIN_DEBUG
|
||||
void *lock_adr;
|
||||
#endif
|
||||
spinlock_t lock; /* the lock */
|
||||
|
||||
/* the channel index is counted from 0, regardless where the channel
|
||||
* is located on the hfc-channel.
|
||||
|
@ -188,6 +202,7 @@ typedef struct hfc_multi hfc_multi_t;
|
|||
#define R_TX0 0x28
|
||||
#define R_TX1 0x29
|
||||
#define R_TX_FR0 0x2C
|
||||
|
||||
#define R_TX_FR1 0x2D
|
||||
#define R_TX_FR2 0x2E
|
||||
#define R_JATT_ATT 0x2F /* undocumented */
|
||||
|
@ -537,9 +552,9 @@ typedef struct hfc_multi hfc_multi_t;
|
|||
#define V_PCM_SYNC 0x04
|
||||
#define V_NEG_CLK 0x08
|
||||
#define V_HCLK 0x10
|
||||
#define V_JATT_AUTO_DEL 0x20
|
||||
#define V_JATT_AUTO 0x40
|
||||
#define V_JATT_EN 0x80
|
||||
//#define V_JATT_AUTO_DEL 0x20
|
||||
//#define V_JATT_AUTO 0x40
|
||||
#define V_JATT_OFF 0x80
|
||||
/* R_STATE */
|
||||
#define V_E1_STA 0x01
|
||||
#define V_ALT_FR_RX 0x40
|
||||
|
@ -1083,34 +1098,51 @@ struct hfc_register_names {
|
|||
|
||||
/* ACCESS TO PCI MEMORY MAPPED REGISTERS */
|
||||
|
||||
#define ADDR_MULT 1 // can be defined to other values if there
|
||||
// is e.g. an offset in a bridge chip addressing
|
||||
|
||||
#ifdef CONFIG_HFCMULTI_PCIMEM
|
||||
|
||||
#define HFC_outl(a,b,c) (*((volatile u_long *)((a->pci_membase)+b)) = c)
|
||||
#define HFC_inl(a,b) (*((volatile u_long *)((a->pci_membase)+b)))
|
||||
#define HFC_outl(a,b,c) (*((volatile u_long *)((a->pci_membase)+((b)*ADDR_MULT))) = c)
|
||||
#define HFC_inl(a,b) (*((volatile u_long *)((a->pci_membase)+((b)*ADDR_MULT))))
|
||||
#define HFC_outw_(a,b,c) (*((volatile u_short *)((a->pci_membase)+((b)*ADDR_MULT))) = c)
|
||||
#define HFC_inw_(a,b) (*((volatile u_short *)((a->pci_membase)+((b)*ADDR_MULT))))
|
||||
/*
|
||||
// version of HFC_inw_(a,b) that makes 8bit access instead of 16bit
|
||||
// only for test purposes
|
||||
u_short HFC_inw_(hfc_multi_t *a, int b)
|
||||
{
|
||||
u_short zl,zh;
|
||||
|
||||
zl=(*((volatile u_char *)((a->pci_membase)+(b*4))));
|
||||
zh=(*((volatile u_char *)((a->pci_membase)+((b+1)*4))));
|
||||
return(zl|(zh<<8));
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
#define HFC_outb_(a,b,c) (*((volatile u_char *)((a->pci_membase)+((b)*ADDR_MULT))) = c)
|
||||
#define HFC_inb_(a,b) (*((volatile u_char *)((a->pci_membase)+((b)*ADDR_MULT))))
|
||||
#define HFC_wait_(a) while((*((volatile u_char *)((a->pci_membase)+(R_STATUS*ADDR_MULT)))) & V_BUSY)
|
||||
|
||||
|
||||
/* no debug */
|
||||
#define HFC_outl_(a,b,c) (*((volatile u_long *)((a->pci_membase)+b)) = c)
|
||||
#define HFC_inl_(a,b) (*((volatile u_long *)((a->pci_membase)+b)))
|
||||
#define HFC_inw_(a,b) (*((volatile u_short *)((a->pci_membase)+b)))
|
||||
#define HFC_outb_(a,b,c) (*((volatile u_char *)((a->pci_membase)+b)) = c)
|
||||
#define HFC_inb_(a,b) (*((volatile u_char *)((a->pci_membase)+b)))
|
||||
#define HFC_wait_(a) while((*((volatile u_char *)((a->pci_membase)+R_STATUS))) & V_BUSY)
|
||||
|
||||
/* macros */
|
||||
#ifndef HFC_REGISTER_MAP
|
||||
|
||||
/* usage: HFC_outX(card,register,value); */
|
||||
#define HFC_outb(a,b,c) (*((volatile u_char *)((a->pci_membase)+b)) = c)
|
||||
#define HFC_outb(a,b,c) (*((volatile u_char *)((a->pci_membase)+((b)*ADDR_MULT))) = c)
|
||||
#define HFC_outw(a,b,c) (*((volatile u_short *)((a->pci_membase)+((b)*ADDR_MULT))) = c)
|
||||
/* usage: register=HFC_inX(card,register); */
|
||||
#define HFC_inb(a,b) (*((volatile u_char *)((a->pci_membase)+b)))
|
||||
#define HFC_inw(a,b) (*((volatile u_short *)((a->pci_membase)+b)))
|
||||
#define HFC_inb(a,b) (*((volatile u_char *)((a->pci_membase)+((b)*ADDR_MULT))))
|
||||
#define HFC_inw(a,b) (*((volatile u_short *)((a->pci_membase)+((b)*ADDR_MULT))))
|
||||
/* usage: HFC_wait(card); */
|
||||
#define HFC_wait(a) while((*((volatile u_char *)((a->pci_membase)+R_STATUS))) & V_BUSY)
|
||||
#define HFC_wait(a) while((*((volatile u_char *)((a->pci_membase)+(R_STATUS*ADDR_MULT)))) & V_BUSY)
|
||||
|
||||
#else /* HFC_REGISTER_MAP */
|
||||
|
||||
#define HFC_outb(a,b,c) _HFC_outb(a, b, c, __FUNCTION__, __LINE__)
|
||||
static unsigned char _HFC_outb(hfc_multi_t *a, unsigned char b, unsigned char c, char *function, int line)
|
||||
static BYTE _HFC_outb(hfc_multi_t *a, BYTE b, BYTE c, char *function, int line)
|
||||
{
|
||||
char regname[256]="", bits[9]="xxxxxxxx";
|
||||
int i;
|
||||
|
@ -1135,10 +1167,10 @@ static unsigned char _HFC_outb(hfc_multi_t *a, unsigned char b, unsigned char c,
|
|||
return(*(((volatile u_char *)a->pci_membase)+b) = c);
|
||||
}
|
||||
#define HFC_inb(a,b) _HFC_inb(a, b, __FUNCTION__, __LINE__)
|
||||
static unsigned char _HFC_inb(hfc_multi_t *a, unsigned char b, char *function, int line)
|
||||
static BYTE _HFC_inb(hfc_multi_t *a, BYTE b, char *function, int line)
|
||||
{
|
||||
char regname[256]="", bits[9]="xxxxxxxx";
|
||||
u_char c = (*(((volatile u_char *)a->pci_membase)+b));
|
||||
u_char c = (*(((volatile u_char *)a->pci_membase)+((b)*ADDR_MULT));
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
|
@ -1163,10 +1195,10 @@ static unsigned char _HFC_inb(hfc_multi_t *a, unsigned char b, char *function, i
|
|||
return(c);
|
||||
}
|
||||
#define HFC_inw(a,b) _HFC_inw(a, b, __FUNCTION__, __LINE__)
|
||||
static unsigned short _HFC_inw(hfc_multi_t *a, unsigned char b, char *function, int line)
|
||||
static WORD _HFC_inw(hfc_multi_t *a, BYTE b, char *function, int line)
|
||||
{
|
||||
char regname[256]="";
|
||||
u_short c = (*(((volatile u_short *)a->pci_membase)+b));
|
||||
u_short c = (*(((volatile u_short *)a->pci_membase)+((b)*ADDR_MULT)));
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
|
@ -1241,10 +1273,12 @@ static inline void HFC_wait(hfc_multi_t *a)
|
|||
/* usage: HFC_putX(card,value); */
|
||||
#define HFC_putb(a,b) outb(b,a->pci_iobase)
|
||||
#define HFC_putl(a,b) outl(b,a->pci_iobase)
|
||||
#define HFC_putw(a,b) outw(b,a->pci_iobase)
|
||||
|
||||
/* usage: value=HFC_getX(card); */
|
||||
#define HFC_getb(a) inb((volatile u_int)a->pci_iobase)
|
||||
#define HFC_getl(a) inl((volatile u_int)a->pci_iobase)
|
||||
#define HFC_getw(a) inw((volatile u_int)a->pci_iobase)
|
||||
|
||||
/* no debug */
|
||||
#define HFC_outl_(a,b,c) HFC_outl(a,b,c)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -20,25 +20,24 @@
|
|||
#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */
|
||||
#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */
|
||||
|
||||
/* hfcsusb Layer1 commands */
|
||||
#define HFC_L1_ACTIVATE_TE 0x01
|
||||
#define HFC_L1_ACTIVATE_NT 0x02
|
||||
#define HFC_L1_DEACTIVATE_NT 0x03
|
||||
#define HFC_L1_FORCE_DEACTIVATE_TE 0x04
|
||||
|
||||
/* bits in STATES */
|
||||
/* cmd FLAGS in HFCUSB_STATES register */
|
||||
#define HFCUSB_LOAD_STATE 0x10
|
||||
#define HFCUSB_ACTIVATE 0x20
|
||||
#define HFCUSB_DO_ACTION 0x40
|
||||
#define HFCUSB_NT_G2_G3 0x80
|
||||
|
||||
/* bits in hw_mode */
|
||||
#define HW_MODE_TE 0x01
|
||||
#define HW_MODE_NT 0x02
|
||||
#define PORT_MODE_TE 0x01
|
||||
#define PORT_MODE_NT 0x02
|
||||
#define NT_ACTIVATION_TIMER 0x04 /* enables NT mode activation Timer */
|
||||
#define NT_T1_COUNT 10
|
||||
|
||||
|
||||
#define HFCUSB_L1_STATECHANGE 0 /* L1 state changed */
|
||||
#define HFCUSB_L1_DRX 1 /* D-frame received */
|
||||
#define HFCUSB_L1_ERX 2 /* E-frame received */
|
||||
#define HFCUSB_L1_DTX 4 /* D-frames completed */
|
||||
|
||||
#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */
|
||||
|
||||
#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */
|
||||
|
@ -79,6 +78,15 @@
|
|||
#define HFCUSB_PCM_TX 6
|
||||
#define HFCUSB_PCM_RX 7
|
||||
|
||||
/*************/
|
||||
/* Chan idx */
|
||||
/*************/
|
||||
#define B1 0
|
||||
#define B2 1
|
||||
#define D 2
|
||||
#define PCM 3
|
||||
#define MAX_CHAN 4
|
||||
|
||||
/*
|
||||
* used to switch snd_transfer_mode for different TA modes e.g. the Billion USB TA just
|
||||
* supports ISO out, while the Cologne Chip EVAL TA just supports BULK out
|
||||
|
@ -90,7 +98,7 @@
|
|||
#define ISOC_PACKETS_D 8
|
||||
#define ISOC_PACKETS_B 8
|
||||
#define ISO_BUFFER_SIZE 128
|
||||
|
||||
#define TRANSP_PACKET_SIZE 0
|
||||
|
||||
/* defines how much ISO packets are handled in one URB */
|
||||
static int iso_packets[8] =
|
||||
|
@ -127,7 +135,6 @@ static int iso_packets[8] =
|
|||
#define HFC_CTRL_BUFSIZE 32
|
||||
|
||||
|
||||
|
||||
/*************************************************/
|
||||
/* entry and size of output/input control buffer */
|
||||
/*************************************************/
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
{
|
||||
register 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
|
|
@ -1376,7 +1376,7 @@ I4Lcapi_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_DISCONNECT | REQUEST:
|
||||
case MGR_DISCONNECT | INDICATION:
|
||||
return(mISDN_DisConnectIF(inst, arg));
|
||||
case MGR_SETSTACK | CONFIRM:
|
||||
case MGR_SETSTACK | INDICATION:
|
||||
if (nr_ch >= 0) {
|
||||
if (inst->pid.protocol[2] != ISDN_PID_L2_B_TRANS)
|
||||
test_and_set_bit(I4L_FLG_LAYER1, &channel->Flags);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include "dchannel.h"
|
||||
#include "channel.h"
|
||||
#include "isac.h"
|
||||
#include "arcofi.h"
|
||||
#include "layer1.h"
|
||||
|
@ -19,8 +19,8 @@
|
|||
#endif
|
||||
|
||||
|
||||
#define DBUSY_TIMER_VALUE 80
|
||||
#define ARCOFI_USE 1
|
||||
#define DBUSY_TIMER_VALUE 80
|
||||
#define ARCOFI_USE 1
|
||||
|
||||
const char *isac_revision = "$Revision$";
|
||||
|
||||
|
@ -37,29 +37,26 @@ EXPORT_SYMBOL(mISDN_ISAC_l1hw);
|
|||
#endif
|
||||
|
||||
static inline void
|
||||
ph_command(dchannel_t *dch, unsigned int command)
|
||||
ph_command(channel_t *dch, unsigned int command)
|
||||
{
|
||||
if (dch->debug & L1_DEB_ISAC)
|
||||
mISDN_debugprint(&dch->inst, "ph_command %x", command);
|
||||
if (dch->type & ISAC_TYPE_ISACSX)
|
||||
dch->write_reg(dch->inst.data, ISACSX_CIX0, (command << 4) | 0xE);
|
||||
dch->write_reg(dch->inst.privat, ISACSX_CIX0, (command << 4) | 0xE);
|
||||
else
|
||||
dch->write_reg(dch->inst.data, ISAC_CIX0, (command << 2) | 3);
|
||||
dch->write_reg(dch->inst.privat, ISAC_CIX0, (command << 2) | 3);
|
||||
}
|
||||
|
||||
static void
|
||||
isac_new_ph(dchannel_t *dch)
|
||||
isac_ph_state_change(channel_t *dch)
|
||||
{
|
||||
u_int prim = PH_SIGNAL | INDICATION;
|
||||
u_int para = 0;
|
||||
mISDNif_t *upif = &dch->inst.up;
|
||||
|
||||
switch (dch->ph_state) {
|
||||
switch (dch->state) {
|
||||
case (ISAC_IND_RS):
|
||||
case (ISAC_IND_EI):
|
||||
dch->inst.lock(dch->inst.data,0);
|
||||
ph_command(dch, ISAC_CMD_DUI);
|
||||
dch->inst.unlock(dch->inst.data);
|
||||
prim = PH_CONTROL | INDICATION;
|
||||
para = HW_RESET;
|
||||
break;
|
||||
|
@ -90,28 +87,15 @@ isac_new_ph(dchannel_t *dch)
|
|||
default:
|
||||
return;
|
||||
}
|
||||
while(upif) {
|
||||
if_link(upif, prim, para, 0, NULL, 0);
|
||||
upif = upif->clone;
|
||||
}
|
||||
mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0);
|
||||
}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
static void
|
||||
isac_hwbh(dchannel_t *dch)
|
||||
isac_hwbh(channel_t *dch)
|
||||
{
|
||||
if (dch->debug)
|
||||
printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event);
|
||||
#if 0
|
||||
if (test_and_clear_bit(D_CLEARBUSY, &dch->event)) {
|
||||
if (dch->debug)
|
||||
mISDN_debugprint(&dch->inst, "D-Channel Busy cleared");
|
||||
stptr = dch->stlist;
|
||||
while (stptr != NULL) {
|
||||
stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (test_and_clear_bit(D_L1STATECHANGE, &dch->event))
|
||||
isac_new_ph(dch);
|
||||
#if ARCOFI_USE
|
||||
|
@ -123,9 +107,10 @@ isac_hwbh(dchannel_t *dch)
|
|||
arcofi_fsm(dch, ARCOFI_TX_END, NULL);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
isac_empty_fifo(dchannel_t *dch, int count)
|
||||
isac_empty_fifo(channel_t *dch, int count)
|
||||
{
|
||||
u_char *ptr;
|
||||
|
||||
|
@ -135,7 +120,7 @@ isac_empty_fifo(dchannel_t *dch, int count)
|
|||
if (!dch->rx_skb) {
|
||||
if (!(dch->rx_skb = alloc_stack_skb(MAX_DFRAME_LEN_L1, dch->up_headerlen))) {
|
||||
printk(KERN_WARNING "mISDN: D receive out of memory\n");
|
||||
dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80);
|
||||
dch->write_reg(dch->inst.privat, ISAC_CMDR, 0x80);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -143,31 +128,32 @@ isac_empty_fifo(dchannel_t *dch, int count)
|
|||
if (dch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&dch->inst, "isac_empty_fifo overrun %d",
|
||||
dch->rx_skb->len + count);
|
||||
dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80);
|
||||
dch->write_reg(dch->inst.privat, ISAC_CMDR, 0x80);
|
||||
return;
|
||||
}
|
||||
ptr = skb_put(dch->rx_skb, count);
|
||||
dch->read_fifo(dch->inst.data, ptr, count);
|
||||
dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80);
|
||||
dch->read_fifo(dch->inst.privat, ptr, count);
|
||||
dch->write_reg(dch->inst.privat, ISAC_CMDR, 0x80);
|
||||
if (dch->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = dch->dlog;
|
||||
char *t = dch->log;
|
||||
|
||||
t += sprintf(t, "isac_empty_fifo cnt %d", count);
|
||||
mISDN_QuickHex(t, ptr, count);
|
||||
mISDN_debugprint(&dch->inst, dch->dlog);
|
||||
mISDN_debugprint(&dch->inst, dch->log);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isac_fill_fifo(dchannel_t *dch)
|
||||
isac_fill_fifo(channel_t *dch)
|
||||
{
|
||||
int count, more;
|
||||
u_char *ptr;
|
||||
|
||||
if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
|
||||
mISDN_debugprint(&dch->inst, "isac_fill_fifo");
|
||||
|
||||
count = dch->tx_len - dch->tx_idx;
|
||||
if (!dch->tx_skb)
|
||||
return;
|
||||
count = dch->tx_skb->len - dch->tx_idx;
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
|
@ -176,33 +162,33 @@ isac_fill_fifo(dchannel_t *dch)
|
|||
more = !0;
|
||||
count = 32;
|
||||
}
|
||||
ptr = dch->tx_buf + dch->tx_idx;
|
||||
ptr = dch->tx_skb->data + dch->tx_idx;
|
||||
dch->tx_idx += count;
|
||||
dch->write_fifo(dch->inst.data, ptr, count);
|
||||
dch->write_reg(dch->inst.data, ISAC_CMDR, more ? 0x8 : 0xa);
|
||||
if (test_and_set_bit(FLG_DBUSY_TIMER, &dch->DFlags)) {
|
||||
dch->write_fifo(dch->inst.privat, ptr, count);
|
||||
dch->write_reg(dch->inst.privat, ISAC_CMDR, more ? 0x8 : 0xa);
|
||||
if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) {
|
||||
mISDN_debugprint(&dch->inst, "isac_fill_fifo dbusytimer running");
|
||||
del_timer(&dch->dbusytimer);
|
||||
del_timer(&dch->timer);
|
||||
}
|
||||
init_timer(&dch->dbusytimer);
|
||||
dch->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
|
||||
add_timer(&dch->dbusytimer);
|
||||
init_timer(&dch->timer);
|
||||
dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
|
||||
add_timer(&dch->timer);
|
||||
if (dch->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = dch->dlog;
|
||||
char *t = dch->log;
|
||||
|
||||
t += sprintf(t, "isac_fill_fifo cnt %d", count);
|
||||
mISDN_QuickHex(t, ptr, count);
|
||||
mISDN_debugprint(&dch->inst, dch->dlog);
|
||||
mISDN_debugprint(&dch->inst, dch->log);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isac_rme_irq(dchannel_t *dch)
|
||||
isac_rme_irq(channel_t *dch)
|
||||
{
|
||||
u_char val;
|
||||
u_int count;
|
||||
|
||||
val = dch->read_reg(dch->inst.data, ISAC_RSTA);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_RSTA);
|
||||
if ((val & 0x70) != 0x20) {
|
||||
if (val & 0x40) {
|
||||
if (dch->debug & L1_DEB_WARN)
|
||||
|
@ -218,86 +204,97 @@ isac_rme_irq(dchannel_t *dch)
|
|||
dch->err_crc++;
|
||||
#endif
|
||||
}
|
||||
dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80);
|
||||
dch->write_reg(dch->inst.privat, ISAC_CMDR, 0x80);
|
||||
if (dch->rx_skb)
|
||||
dev_kfree_skb(dch->rx_skb);
|
||||
} else {
|
||||
count = dch->read_reg(dch->inst.data, ISAC_RBCL) & 0x1f;
|
||||
count = dch->read_reg(dch->inst.privat, ISAC_RBCL) & 0x1f;
|
||||
if (count == 0)
|
||||
count = 32;
|
||||
isac_empty_fifo(dch, count);
|
||||
if (dch->rx_skb) {
|
||||
skb_queue_tail(&dch->rqueue, dch->rx_skb);
|
||||
}
|
||||
if (dch->rx_skb)
|
||||
if (unlikely(mISDN_queueup_newhead(&dch->inst, 0, PH_DATA_IND, MISDN_ID_ANY, dch->rx_skb))) {
|
||||
int_error();
|
||||
dev_kfree_skb(dch->rx_skb);
|
||||
}
|
||||
}
|
||||
dch->rx_skb = NULL;
|
||||
dchannel_sched_event(dch, D_RCVBUFREADY);
|
||||
}
|
||||
|
||||
static void
|
||||
isac_xpr_irq(dchannel_t *dch)
|
||||
isac_xpr_irq(channel_t *dch)
|
||||
{
|
||||
if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
|
||||
del_timer(&dch->dbusytimer);
|
||||
if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
|
||||
dchannel_sched_event(dch, D_CLEARBUSY);
|
||||
if (dch->tx_idx < dch->tx_len) {
|
||||
if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
|
||||
del_timer(&dch->timer);
|
||||
if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len) {
|
||||
isac_fill_fifo(dch);
|
||||
} else {
|
||||
if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
|
||||
if (dch->next_skb) {
|
||||
dch->tx_len = dch->next_skb->len;
|
||||
memcpy(dch->tx_buf,
|
||||
dch->next_skb->data, dch->tx_len);
|
||||
if (dch->tx_skb)
|
||||
dev_kfree_skb(dch->tx_skb);
|
||||
if (test_bit(FLG_TX_NEXT, &dch->Flags)) {
|
||||
dch->tx_skb = dch->next_skb;
|
||||
if (dch->tx_skb) {
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(dch->tx_skb);
|
||||
|
||||
dch->next_skb = NULL;
|
||||
test_and_clear_bit(FLG_TX_NEXT, &dch->Flags);
|
||||
dch->tx_idx = 0;
|
||||
queue_ch_frame(dch, CONFIRM, hh->dinfo, NULL);
|
||||
isac_fill_fifo(dch);
|
||||
dchannel_sched_event(dch, D_XMTBUFREADY);
|
||||
} else {
|
||||
printk(KERN_WARNING "isac tx irq TX_NEXT without skb\n");
|
||||
test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
|
||||
test_and_clear_bit(FLG_TX_NEXT, &dch->Flags);
|
||||
test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
|
||||
}
|
||||
} else
|
||||
test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
|
||||
} else {
|
||||
dch->tx_skb = NULL;
|
||||
test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isac_retransmit(dchannel_t *dch)
|
||||
isac_retransmit(channel_t *dch)
|
||||
{
|
||||
if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
|
||||
del_timer(&dch->dbusytimer);
|
||||
if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
|
||||
dchannel_sched_event(dch, D_CLEARBUSY);
|
||||
if (test_bit(FLG_TX_BUSY, &dch->DFlags)) {
|
||||
if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
|
||||
del_timer(&dch->timer);
|
||||
if (test_bit(FLG_TX_BUSY, &dch->Flags)) {
|
||||
/* Restart frame */
|
||||
dch->tx_idx = 0;
|
||||
isac_fill_fifo(dch);
|
||||
} else if (dch->tx_skb) { /* should not happen */
|
||||
int_error();
|
||||
test_and_set_bit(FLG_TX_BUSY, &dch->Flags);
|
||||
dch->tx_idx = 0;
|
||||
isac_fill_fifo(dch);
|
||||
} else {
|
||||
printk(KERN_WARNING "mISDN: ISAC XDU no TX_BUSY\n");
|
||||
mISDN_debugprint(&dch->inst, "ISAC XDU no TX_BUSY");
|
||||
if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
|
||||
if (dch->next_skb) {
|
||||
dch->tx_len = dch->next_skb->len;
|
||||
memcpy(dch->tx_buf,
|
||||
dch->next_skb->data,
|
||||
dch->tx_len);
|
||||
if (test_bit(FLG_TX_NEXT, &dch->Flags)) {
|
||||
dch->tx_skb = dch->next_skb;
|
||||
if (dch->tx_skb) {
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(dch->next_skb);
|
||||
|
||||
dch->next_skb = NULL;
|
||||
test_and_clear_bit(FLG_TX_NEXT, &dch->Flags);
|
||||
dch->tx_idx = 0;
|
||||
queue_ch_frame(dch, CONFIRM, hh->dinfo, NULL);
|
||||
isac_fill_fifo(dch);
|
||||
dchannel_sched_event(dch, D_XMTBUFREADY);
|
||||
} else {
|
||||
printk(KERN_WARNING "isac xdu irq TX_NEXT without skb\n");
|
||||
test_and_clear_bit(FLG_TX_NEXT, &dch->Flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isac_mos_irq(dchannel_t *dch)
|
||||
isac_mos_irq(channel_t *dch)
|
||||
{
|
||||
u_char val;
|
||||
isac_chip_t *isac = dch->hw;
|
||||
|
||||
val = dch->read_reg(dch->inst.data, ISAC_MOSR);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_MOSR);
|
||||
if (dch->debug & L1_DEB_MONITOR)
|
||||
mISDN_debugprint(&dch->inst, "ISAC MOSR %02x", val);
|
||||
#if ARCOFI_USE
|
||||
|
@ -308,7 +305,7 @@ isac_mos_irq(dchannel_t *dch)
|
|||
mISDN_debugprint(&dch->inst, "ISAC MON RX out of memory!");
|
||||
isac->mocr &= 0xf0;
|
||||
isac->mocr |= 0x0a;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
goto afterMONR0;
|
||||
} else
|
||||
isac->mon_rxp = 0;
|
||||
|
@ -316,18 +313,18 @@ isac_mos_irq(dchannel_t *dch)
|
|||
if (isac->mon_rxp >= MAX_MON_FRAME) {
|
||||
isac->mocr &= 0xf0;
|
||||
isac->mocr |= 0x0a;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
isac->mon_rxp = 0;
|
||||
if (dch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&dch->inst, "ISAC MON RX overflow!");
|
||||
goto afterMONR0;
|
||||
}
|
||||
isac->mon_rx[isac->mon_rxp++] = dch->read_reg(dch->inst.data, ISAC_MOR0);
|
||||
isac->mon_rx[isac->mon_rxp++] = dch->read_reg(dch->inst.privat, ISAC_MOR0);
|
||||
if (dch->debug & L1_DEB_MONITOR)
|
||||
mISDN_debugprint(&dch->inst, "ISAC MOR0 %02x", isac->mon_rx[isac->mon_rxp -1]);
|
||||
if (isac->mon_rxp == 1) {
|
||||
isac->mocr |= 0x04;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
}
|
||||
}
|
||||
afterMONR0:
|
||||
|
@ -338,7 +335,7 @@ afterMONR0:
|
|||
mISDN_debugprint(&dch->inst, "ISAC MON RX out of memory!");
|
||||
isac->mocr &= 0x0f;
|
||||
isac->mocr |= 0xa0;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
goto afterMONR1;
|
||||
} else
|
||||
isac->mon_rxp = 0;
|
||||
|
@ -346,49 +343,53 @@ afterMONR0:
|
|||
if (isac->mon_rxp >= MAX_MON_FRAME) {
|
||||
isac->mocr &= 0x0f;
|
||||
isac->mocr |= 0xa0;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
isac->mon_rxp = 0;
|
||||
if (dch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&dch->inst, "ISAC MON RX overflow!");
|
||||
goto afterMONR1;
|
||||
}
|
||||
isac->mon_rx[isac->mon_rxp++] = dch->read_reg(dch->inst.data, ISAC_MOR1);
|
||||
isac->mon_rx[isac->mon_rxp++] = dch->read_reg(dch->inst.privat, ISAC_MOR1);
|
||||
if (dch->debug & L1_DEB_MONITOR)
|
||||
mISDN_debugprint(&dch->inst, "ISAC MOR1 %02x", isac->mon_rx[isac->mon_rxp -1]);
|
||||
isac->mocr |= 0x40;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
}
|
||||
afterMONR1:
|
||||
if (val & 0x04) {
|
||||
isac->mocr &= 0xf0;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
isac->mocr |= 0x0a;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dchannel_sched_event(dch, D_RX_MON0);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
mISDN_queue_data(&dch->inst, 0, PH_SIGNAL | INDICATION, D_RX_MON0,
|
||||
isac->mon_rxp, isac->mon_rx, 0);
|
||||
}
|
||||
if (val & 0x40) {
|
||||
isac->mocr &= 0x0f;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
isac->mocr |= 0xa0;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dchannel_sched_event(dch, D_RX_MON1);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
mISDN_queue_data(&dch->inst, 0, PH_SIGNAL | INDICATION, D_RX_MON1,
|
||||
isac->mon_rxp, isac->mon_rx, 0);
|
||||
}
|
||||
if (val & 0x02) {
|
||||
if ((!isac->mon_tx) || (isac->mon_txc &&
|
||||
(isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) {
|
||||
isac->mocr &= 0xf0;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
isac->mocr |= 0x0a;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc))
|
||||
dchannel_sched_event(dch, D_TX_MON0);
|
||||
mISDN_queue_data(&dch->inst, 0, PH_SIGNAL | INDICATION,
|
||||
D_TX_MON0, 0, NULL, 0);
|
||||
goto AfterMOX0;
|
||||
}
|
||||
if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
|
||||
dchannel_sched_event(dch, D_TX_MON0);
|
||||
mISDN_queue_data(&dch->inst, 0, PH_SIGNAL | INDICATION,
|
||||
D_TX_MON0, 0, NULL, 0);
|
||||
goto AfterMOX0;
|
||||
}
|
||||
dch->write_reg(dch->inst.data, ISAC_MOX0,
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOX0,
|
||||
isac->mon_tx[isac->mon_txp++]);
|
||||
if (dch->debug & L1_DEB_MONITOR)
|
||||
mISDN_debugprint(&dch->inst, "ISAC %02x -> MOX0", isac->mon_tx[isac->mon_txp -1]);
|
||||
|
@ -398,18 +399,20 @@ AfterMOX0:
|
|||
if ((!isac->mon_tx) || (isac->mon_txc &&
|
||||
(isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) {
|
||||
isac->mocr &= 0x0f;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
isac->mocr |= 0xa0;
|
||||
dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr);
|
||||
if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc))
|
||||
dchannel_sched_event(dch, D_TX_MON1);
|
||||
mISDN_queue_data(&dch->inst, 0, PH_SIGNAL | INDICATION,
|
||||
D_TX_MON1, 0, NULL, 0);
|
||||
goto AfterMOX1;
|
||||
}
|
||||
if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
|
||||
dchannel_sched_event(dch, D_TX_MON1);
|
||||
mISDN_queue_data(&dch->inst, 0, PH_SIGNAL | INDICATION,
|
||||
D_TX_MON1, 0, NULL, 0);
|
||||
goto AfterMOX1;
|
||||
}
|
||||
dch->write_reg(dch->inst.data, ISAC_MOX1,
|
||||
dch->write_reg(dch->inst.privat, ISAC_MOX1,
|
||||
isac->mon_tx[isac->mon_txp++]);
|
||||
if (dch->debug & L1_DEB_MONITOR)
|
||||
mISDN_debugprint(&dch->inst, "ISAC %02x -> MOX1", isac->mon_tx[isac->mon_txp -1]);
|
||||
|
@ -420,50 +423,50 @@ AfterMOX1:
|
|||
}
|
||||
|
||||
static void
|
||||
isac_cisq_irq(dchannel_t *dch) {
|
||||
isac_cisq_irq(channel_t *dch) {
|
||||
unsigned char val;
|
||||
|
||||
val = dch->read_reg(dch->inst.data, ISAC_CIR0);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_CIR0);
|
||||
if (dch->debug & L1_DEB_ISAC)
|
||||
mISDN_debugprint(&dch->inst, "ISAC CIR0 %02X", val);
|
||||
if (val & 2) {
|
||||
if (dch->debug & L1_DEB_ISAC)
|
||||
mISDN_debugprint(&dch->inst, "ph_state change %x->%x",
|
||||
dch->ph_state, (val >> 2) & 0xf);
|
||||
dch->ph_state = (val >> 2) & 0xf;
|
||||
dchannel_sched_event(dch, D_L1STATECHANGE);
|
||||
dch->state, (val >> 2) & 0xf);
|
||||
dch->state = (val >> 2) & 0xf;
|
||||
isac_ph_state_change(dch);
|
||||
}
|
||||
if (val & 1) {
|
||||
val = dch->read_reg(dch->inst.data, ISAC_CIR1);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_CIR1);
|
||||
if (dch->debug & L1_DEB_ISAC)
|
||||
mISDN_debugprint(&dch->inst, "ISAC CIR1 %02X", val );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isacsx_cic_irq(dchannel_t *dch)
|
||||
isacsx_cic_irq(channel_t *dch)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
val = dch->read_reg(dch->inst.data, ISACSX_CIR0);
|
||||
val = dch->read_reg(dch->inst.privat, ISACSX_CIR0);
|
||||
if (dch->debug & L1_DEB_ISAC)
|
||||
mISDN_debugprint(&dch->inst, "ISACSX CIR0 %02X", val);
|
||||
if (val & ISACSX_CIR0_CIC0) {
|
||||
if (dch->debug & L1_DEB_ISAC)
|
||||
mISDN_debugprint(&dch->inst, "ph_state change %x->%x",
|
||||
dch->ph_state, val >> 4);
|
||||
dch->ph_state = val >> 4;
|
||||
dchannel_sched_event(dch, D_L1STATECHANGE);
|
||||
dch->state, val >> 4);
|
||||
dch->state = val >> 4;
|
||||
isac_ph_state_change(dch);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isacsx_rme_irq(dchannel_t *dch)
|
||||
isacsx_rme_irq(channel_t *dch)
|
||||
{
|
||||
int count;
|
||||
unsigned char val;
|
||||
|
||||
val = dch->read_reg(dch->inst.data, ISACSX_RSTAD);
|
||||
val = dch->read_reg(dch->inst.privat, ISACSX_RSTAD);
|
||||
if ((val & (ISACSX_RSTAD_VFR |
|
||||
ISACSX_RSTAD_RDO |
|
||||
ISACSX_RSTAD_CRC |
|
||||
|
@ -477,25 +480,27 @@ isacsx_rme_irq(dchannel_t *dch)
|
|||
else
|
||||
dch->err_crc++;
|
||||
#endif
|
||||
dch->write_reg(dch->inst.data, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
|
||||
dch->write_reg(dch->inst.privat, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
|
||||
if (dch->rx_skb)
|
||||
dev_kfree_skb(dch->rx_skb);
|
||||
} else {
|
||||
count = dch->read_reg(dch->inst.data, ISACSX_RBCLD) & 0x1f;
|
||||
count = dch->read_reg(dch->inst.privat, ISACSX_RBCLD) & 0x1f;
|
||||
if (count == 0)
|
||||
count = 32;
|
||||
isac_empty_fifo(dch, count);
|
||||
if (dch->rx_skb) {
|
||||
skb_trim(dch->rx_skb, dch->rx_skb->len - 1);
|
||||
skb_queue_tail(&dch->rqueue, dch->rx_skb);
|
||||
if (unlikely(mISDN_queueup_newhead(&dch->inst, 0, PH_DATA_IND, MISDN_ID_ANY, dch->rx_skb))) {
|
||||
int_error();
|
||||
dev_kfree_skb(dch->rx_skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
dch->rx_skb = NULL;
|
||||
dchannel_sched_event(dch, D_RCVBUFREADY);
|
||||
}
|
||||
|
||||
void
|
||||
mISDN_isac_interrupt(dchannel_t *dch, u_char val)
|
||||
mISDN_isac_interrupt(channel_t *dch, u_char val)
|
||||
{
|
||||
if (dch->debug & L1_DEB_ISAC)
|
||||
mISDN_debugprint(&dch->inst, "ISAC interrupt %02x", val);
|
||||
|
@ -503,7 +508,7 @@ mISDN_isac_interrupt(dchannel_t *dch, u_char val)
|
|||
if (val & ISACSX_ISTA_CIC)
|
||||
isacsx_cic_irq(dch);
|
||||
if (val & ISACSX_ISTA_ICD) {
|
||||
val = dch->read_reg(dch->inst.data, ISACSX_ISTAD);
|
||||
val = dch->read_reg(dch->inst.privat, ISACSX_ISTAD);
|
||||
if (dch->debug & L1_DEB_ISAC)
|
||||
mISDN_debugprint(&dch->inst, "ISTAD %02x", val);
|
||||
if (val & ISACSX_ISTAD_XDU) {
|
||||
|
@ -527,7 +532,7 @@ mISDN_isac_interrupt(dchannel_t *dch, u_char val)
|
|||
if (val & ISACSX_ISTAD_RFO) {
|
||||
if (dch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&dch->inst, "ISAC RFO");
|
||||
dch->write_reg(dch->inst.data, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
|
||||
dch->write_reg(dch->inst.privat, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
|
||||
}
|
||||
if (val & ISACSX_ISTAD_RME)
|
||||
isacsx_rme_irq(dch);
|
||||
|
@ -550,7 +555,7 @@ mISDN_isac_interrupt(dchannel_t *dch, u_char val)
|
|||
if (dch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&dch->inst, "ISAC SIN interrupt");
|
||||
if (val & 0x01) { /* EXI */
|
||||
val = dch->read_reg(dch->inst.data, ISAC_EXIR);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_EXIR);
|
||||
if (dch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&dch->inst, "ISAC EXIR %02x", val);
|
||||
if (val & 0x80) /* XMR */
|
||||
|
@ -570,69 +575,53 @@ mISDN_isac_interrupt(dchannel_t *dch, u_char val)
|
|||
}
|
||||
|
||||
int
|
||||
mISDN_ISAC_l1hw(mISDNif_t *hif, struct sk_buff *skb)
|
||||
mISDN_ISAC_l1hw(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
dchannel_t *dch;
|
||||
int ret = -EINVAL;
|
||||
channel_t *dch;
|
||||
int ret = 0;
|
||||
mISDN_head_t *hh;
|
||||
u_long flags;
|
||||
|
||||
if (!hif || !skb)
|
||||
return(ret);
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
dch = hif->fdata;
|
||||
ret = 0;
|
||||
dch = container_of(inst, channel_t, inst);
|
||||
if (hh->prim == PH_DATA_REQ) {
|
||||
if (dch->next_skb) {
|
||||
mISDN_debugprint(&dch->inst, " l2l1 next_skb exist this shouldn't happen");
|
||||
return(-EBUSY);
|
||||
}
|
||||
dch->inst.lock(dch->inst.data,0);
|
||||
if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) {
|
||||
test_and_set_bit(FLG_TX_NEXT, &dch->DFlags);
|
||||
dch->next_skb = skb;
|
||||
dch->inst.unlock(dch->inst.data);
|
||||
return(0);
|
||||
} else {
|
||||
dch->tx_len = skb->len;
|
||||
memcpy(dch->tx_buf, skb->data, dch->tx_len);
|
||||
dch->tx_idx = 0;
|
||||
spin_lock_irqsave(inst->hwlock, flags);
|
||||
ret = channel_senddata(dch, hh->dinfo, skb);
|
||||
if (ret > 0) { /* direct TX */
|
||||
isac_fill_fifo(dch);
|
||||
dch->inst.unlock(dch->inst.data);
|
||||
return(if_newhead(&dch->inst.up, PH_DATA_CNF,
|
||||
hh->dinfo, skb));
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(inst->hwlock, flags);
|
||||
return(ret);
|
||||
} else if (hh->prim == (PH_SIGNAL | REQUEST)) {
|
||||
dch->inst.lock(dch->inst.data,0);
|
||||
spin_lock_irqsave(inst->hwlock, flags);
|
||||
if (hh->dinfo == INFO3_P8)
|
||||
ph_command(dch, ISAC_CMD_AR8);
|
||||
else if (hh->dinfo == INFO3_P10)
|
||||
ph_command(dch, ISAC_CMD_AR10);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
dch->inst.unlock(dch->inst.data);
|
||||
spin_unlock_irqrestore(inst->hwlock, flags);
|
||||
} else if (hh->prim == (PH_CONTROL | REQUEST)) {
|
||||
dch->inst.lock(dch->inst.data,0);
|
||||
spin_lock_irqsave(inst->hwlock, flags);
|
||||
if (hh->dinfo == HW_RESET) {
|
||||
if ((dch->ph_state == ISAC_IND_EI) ||
|
||||
(dch->ph_state == ISAC_IND_DR) ||
|
||||
(dch->ph_state == ISAC_IND_RS))
|
||||
if ((dch->state == ISAC_IND_EI) ||
|
||||
(dch->state == ISAC_IND_DR) ||
|
||||
(dch->state == ISAC_IND_RS))
|
||||
ph_command(dch, ISAC_CMD_TIM);
|
||||
else
|
||||
ph_command(dch, ISAC_CMD_RS);
|
||||
} else if (hh->dinfo == HW_POWERUP) {
|
||||
ph_command(dch, ISAC_CMD_TIM);
|
||||
} else if (hh->dinfo == HW_DEACTIVATE) {
|
||||
discard_queue(&dch->rqueue);
|
||||
if (dch->next_skb) {
|
||||
dev_kfree_skb(dch->next_skb);
|
||||
dch->next_skb = NULL;
|
||||
}
|
||||
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);
|
||||
if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
|
||||
dchannel_sched_event(dch, D_CLEARBUSY);
|
||||
test_and_clear_bit(FLG_TX_NEXT, &dch->Flags);
|
||||
test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
|
||||
if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
|
||||
del_timer(&dch->timer);
|
||||
} else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) {
|
||||
u_char tl;
|
||||
if (dch->type & ISAC_TYPE_ISACSX) {
|
||||
|
@ -646,19 +635,19 @@ mISDN_ISAC_l1hw(mISDNif_t *hif, struct sk_buff *skb)
|
|||
if (ISAC_TYPE_IOM1 & dch->type) {
|
||||
/* IOM 1 Mode */
|
||||
if (!tl) {
|
||||
dch->write_reg(dch->inst.data, ISAC_SPCR, 0xa);
|
||||
dch->write_reg(dch->inst.data, ISAC_ADF1, 0x2);
|
||||
dch->write_reg(dch->inst.privat, ISAC_SPCR, 0xa);
|
||||
dch->write_reg(dch->inst.privat, ISAC_ADF1, 0x2);
|
||||
} else {
|
||||
dch->write_reg(dch->inst.data, ISAC_SPCR, tl);
|
||||
dch->write_reg(dch->inst.data, ISAC_ADF1, 0xa);
|
||||
dch->write_reg(dch->inst.privat, ISAC_SPCR, tl);
|
||||
dch->write_reg(dch->inst.privat, ISAC_ADF1, 0xa);
|
||||
}
|
||||
} else {
|
||||
/* IOM 2 Mode */
|
||||
dch->write_reg(dch->inst.data, ISAC_SPCR, tl);
|
||||
dch->write_reg(dch->inst.privat, ISAC_SPCR, tl);
|
||||
if (tl)
|
||||
dch->write_reg(dch->inst.data, ISAC_ADF1, 0x8);
|
||||
dch->write_reg(dch->inst.privat, ISAC_ADF1, 0x8);
|
||||
else
|
||||
dch->write_reg(dch->inst.data, ISAC_ADF1, 0x0);
|
||||
dch->write_reg(dch->inst.privat, ISAC_ADF1, 0x0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -667,8 +656,21 @@ mISDN_ISAC_l1hw(mISDNif_t *hif, struct sk_buff *skb)
|
|||
hh->dinfo);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
dch->inst.unlock(dch->inst.data);
|
||||
} else {
|
||||
spin_unlock_irqrestore(inst->hwlock, flags);
|
||||
} else if (hh->prim == (PH_SIGNAL | INDICATION)) {
|
||||
#if ARCOFI_USE
|
||||
if ((ISAC_TYPE_ARCOFI & dch->type)) {
|
||||
if (hh->dinfo == D_RX_MON1) {
|
||||
arcofi_fsm(dch, ARCOFI_RX_END, skb);
|
||||
} else if (hh->dinfo == D_TX_MON1) {
|
||||
arcofi_fsm(dch, ARCOFI_TX_END, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ret = 0;
|
||||
} else if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS)
|
||||
ret = -EAGAIN;
|
||||
else {
|
||||
if (dch->debug & L1_DEB_WARN)
|
||||
mISDN_debugprint(&dch->inst, "isac_l1hw unknown prim %x",
|
||||
hh->prim);
|
||||
|
@ -680,53 +682,39 @@ mISDN_ISAC_l1hw(mISDNif_t *hif, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
void
|
||||
mISDN_isac_free(dchannel_t *dch) {
|
||||
mISDN_isac_free(channel_t *dch) {
|
||||
isac_chip_t *isac = dch->hw;
|
||||
|
||||
if (dch->dbusytimer.function != NULL) {
|
||||
del_timer(&dch->dbusytimer);
|
||||
dch->dbusytimer.function = NULL;
|
||||
if (dch->timer.function != NULL) {
|
||||
del_timer(&dch->timer);
|
||||
dch->timer.function = NULL;
|
||||
}
|
||||
if (!isac)
|
||||
return;
|
||||
if (isac->mon_rx) {
|
||||
kfree(isac->mon_rx);
|
||||
isac->mon_rx = NULL;
|
||||
}
|
||||
if (isac->mon_tx) {
|
||||
kfree(isac->mon_tx);
|
||||
isac->mon_tx = NULL;
|
||||
}
|
||||
kfree(isac->mon_rx);
|
||||
isac->mon_rx = NULL;
|
||||
kfree(isac->mon_tx);
|
||||
isac->mon_tx = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
dbusy_timer_handler(dchannel_t *dch)
|
||||
dbusy_timer_handler(channel_t *dch)
|
||||
{
|
||||
int rbch, star;
|
||||
u_long flags;
|
||||
|
||||
if (test_bit(FLG_DBUSY_TIMER, &dch->DFlags)) {
|
||||
if (dch->inst.lock(dch->inst.data, 1)) {
|
||||
dch->dbusytimer.expires = jiffies + 1;
|
||||
add_timer(&dch->dbusytimer);
|
||||
return;
|
||||
}
|
||||
rbch = dch->read_reg(dch->inst.data, ISAC_RBCH);
|
||||
star = dch->read_reg(dch->inst.data, ISAC_STAR);
|
||||
if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) {
|
||||
spin_lock_irqsave(dch->inst.hwlock, flags);
|
||||
rbch = dch->read_reg(dch->inst.privat, ISAC_RBCH);
|
||||
star = dch->read_reg(dch->inst.privat, ISAC_STAR);
|
||||
if (dch->debug)
|
||||
mISDN_debugprint(&dch->inst, "D-Channel Busy RBCH %02x STAR %02x",
|
||||
rbch, star);
|
||||
if (rbch & ISAC_RBCH_XAC) { /* D-Channel Busy */
|
||||
test_and_set_bit(FLG_L1_DBUSY, &dch->DFlags);
|
||||
#if 0
|
||||
stptr = dch->stlist;
|
||||
while (stptr != NULL) {
|
||||
stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
#endif
|
||||
test_and_set_bit(FLG_L1_BUSY, &dch->Flags);
|
||||
} else {
|
||||
/* discard frame; reset transceiver */
|
||||
test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags);
|
||||
test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags);
|
||||
if (dch->tx_idx) {
|
||||
dch->tx_idx = 0;
|
||||
} else {
|
||||
|
@ -734,9 +722,9 @@ dbusy_timer_handler(dchannel_t *dch)
|
|||
mISDN_debugprint(&dch->inst, "D-Channel Busy no tx_idx");
|
||||
}
|
||||
/* Transmitter reset */
|
||||
dch->write_reg(dch->inst.data, ISAC_CMDR, 0x01);
|
||||
dch->write_reg(dch->inst.privat, ISAC_CMDR, 0x01);
|
||||
}
|
||||
dch->inst.unlock(dch->inst.data);
|
||||
spin_unlock_irqrestore(dch->inst.hwlock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -745,7 +733,7 @@ static char *ISACVer[] =
|
|||
"2085 V2.3"};
|
||||
|
||||
int
|
||||
mISDN_isac_init(dchannel_t *dch)
|
||||
mISDN_isac_init(channel_t *dch)
|
||||
{
|
||||
isac_chip_t *isac = dch->hw;
|
||||
u_char val;
|
||||
|
@ -753,60 +741,59 @@ mISDN_isac_init(dchannel_t *dch)
|
|||
|
||||
if (!isac)
|
||||
return(-EINVAL);
|
||||
dch->hw_bh = isac_hwbh;
|
||||
isac->mon_tx = NULL;
|
||||
isac->mon_rx = NULL;
|
||||
dch->dbusytimer.function = (void *) dbusy_timer_handler;
|
||||
dch->dbusytimer.data = (long) dch;
|
||||
init_timer(&dch->dbusytimer);
|
||||
dch->timer.function = (void *) dbusy_timer_handler;
|
||||
dch->timer.data = (long) dch;
|
||||
init_timer(&dch->timer);
|
||||
isac->mocr = 0xaa;
|
||||
if (dch->type & ISAC_TYPE_ISACSX) {
|
||||
// clear LDD
|
||||
dch->write_reg(dch->inst.data, ISACSX_TR_CONF0, 0x00);
|
||||
dch->write_reg(dch->inst.privat, ISACSX_TR_CONF0, 0x00);
|
||||
// enable transmitter
|
||||
dch->write_reg(dch->inst.data, ISACSX_TR_CONF2, 0x00);
|
||||
dch->write_reg(dch->inst.privat, ISACSX_TR_CONF2, 0x00);
|
||||
// transparent mode 0, RAC, stop/go
|
||||
dch->write_reg(dch->inst.data, ISACSX_MODED, 0xc9);
|
||||
dch->write_reg(dch->inst.privat, ISACSX_MODED, 0xc9);
|
||||
// all HDLC IRQ unmasked
|
||||
dch->write_reg(dch->inst.data, ISACSX_MASKD, 0x03);
|
||||
dch->write_reg(dch->inst.privat, ISACSX_MASKD, 0x03);
|
||||
// unmask ICD, CID IRQs
|
||||
dch->write_reg(dch->inst.data, ISACSX_MASK, ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
|
||||
dch->write_reg(dch->inst.privat, ISACSX_MASK, ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
|
||||
printk(KERN_INFO "mISDN_isac_init: ISACSX\n");
|
||||
dchannel_sched_event(dch, D_L1STATECHANGE);
|
||||
isac_ph_state_change(dch);
|
||||
ph_command(dch, ISAC_CMD_RS);
|
||||
} else { /* old isac */
|
||||
dch->write_reg(dch->inst.data, ISAC_MASK, 0xff);
|
||||
val = dch->read_reg(dch->inst.data, ISAC_RBCH);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MASK, 0xff);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_RBCH);
|
||||
printk(KERN_INFO "mISDN_isac_init: ISAC version (%x): %s\n", val, ISACVer[(val >> 5) & 3]);
|
||||
dch->type |= ((val >> 5) & 3);
|
||||
if (ISAC_TYPE_IOM1 & dch->type) {
|
||||
/* IOM 1 Mode */
|
||||
dch->write_reg(dch->inst.data, ISAC_ADF2, 0x0);
|
||||
dch->write_reg(dch->inst.data, ISAC_SPCR, 0xa);
|
||||
dch->write_reg(dch->inst.data, ISAC_ADF1, 0x2);
|
||||
dch->write_reg(dch->inst.data, ISAC_STCR, 0x70);
|
||||
dch->write_reg(dch->inst.data, ISAC_MODE, 0xc9);
|
||||
dch->write_reg(dch->inst.privat, ISAC_ADF2, 0x0);
|
||||
dch->write_reg(dch->inst.privat, ISAC_SPCR, 0xa);
|
||||
dch->write_reg(dch->inst.privat, ISAC_ADF1, 0x2);
|
||||
dch->write_reg(dch->inst.privat, ISAC_STCR, 0x70);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MODE, 0xc9);
|
||||
} else {
|
||||
/* IOM 2 Mode */
|
||||
if (!isac->adf2)
|
||||
isac->adf2 = 0x80;
|
||||
dch->write_reg(dch->inst.data, ISAC_ADF2, isac->adf2);
|
||||
dch->write_reg(dch->inst.data, ISAC_SQXR, 0x2f);
|
||||
dch->write_reg(dch->inst.data, ISAC_SPCR, 0x00);
|
||||
dch->write_reg(dch->inst.data, ISAC_STCR, 0x70);
|
||||
dch->write_reg(dch->inst.data, ISAC_MODE, 0xc9);
|
||||
dch->write_reg(dch->inst.data, ISAC_TIMR, 0x00);
|
||||
dch->write_reg(dch->inst.data, ISAC_ADF1, 0x00);
|
||||
dch->write_reg(dch->inst.privat, ISAC_ADF2, isac->adf2);
|
||||
dch->write_reg(dch->inst.privat, ISAC_SQXR, 0x2f);
|
||||
dch->write_reg(dch->inst.privat, ISAC_SPCR, 0x00);
|
||||
dch->write_reg(dch->inst.privat, ISAC_STCR, 0x70);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MODE, 0xc9);
|
||||
dch->write_reg(dch->inst.privat, ISAC_TIMR, 0x00);
|
||||
dch->write_reg(dch->inst.privat, ISAC_ADF1, 0x00);
|
||||
}
|
||||
dchannel_sched_event(dch, D_L1STATECHANGE);
|
||||
isac_ph_state_change(dch);
|
||||
ph_command(dch, ISAC_CMD_RS);
|
||||
dch->write_reg(dch->inst.data, ISAC_MASK, 0x0);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MASK, 0x0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
mISDN_clear_isac(dchannel_t *dch)
|
||||
mISDN_clear_isac(channel_t *dch)
|
||||
{
|
||||
isac_chip_t *isac = dch->hw;
|
||||
u_int val, eval;
|
||||
|
@ -814,22 +801,22 @@ mISDN_clear_isac(dchannel_t *dch)
|
|||
if (!isac)
|
||||
return;
|
||||
/* Disable all IRQ */
|
||||
dch->write_reg(dch->inst.data, ISAC_MASK, 0xFF);
|
||||
val = dch->read_reg(dch->inst.data, ISAC_STAR);
|
||||
dch->write_reg(dch->inst.privat, ISAC_MASK, 0xFF);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_STAR);
|
||||
mISDN_debugprint(&dch->inst, "ISAC STAR %x", val);
|
||||
val = dch->read_reg(dch->inst.data, ISAC_MODE);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_MODE);
|
||||
mISDN_debugprint(&dch->inst, "ISAC MODE %x", val);
|
||||
val = dch->read_reg(dch->inst.data, ISAC_ADF2);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_ADF2);
|
||||
mISDN_debugprint(&dch->inst, "ISAC ADF2 %x", val);
|
||||
val = dch->read_reg(dch->inst.data, ISAC_ISTA);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_ISTA);
|
||||
mISDN_debugprint(&dch->inst, "ISAC ISTA %x", val);
|
||||
if (val & 0x01) {
|
||||
eval = dch->read_reg(dch->inst.data, ISAC_EXIR);
|
||||
eval = dch->read_reg(dch->inst.privat, ISAC_EXIR);
|
||||
mISDN_debugprint(&dch->inst, "ISAC EXIR %x", eval);
|
||||
}
|
||||
val = dch->read_reg(dch->inst.data, ISAC_CIR0);
|
||||
val = dch->read_reg(dch->inst.privat, ISAC_CIR0);
|
||||
mISDN_debugprint(&dch->inst, "ISAC CIR0 %x", val);
|
||||
dch->ph_state = (val >> 2) & 0xf;
|
||||
dch->state = (val >> 2) & 0xf;
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
|
|
|
@ -128,9 +128,9 @@ typedef struct isac_chip {
|
|||
|
||||
/* interface for the isac module */
|
||||
|
||||
extern int mISDN_isac_init(dchannel_t *);
|
||||
extern void mISDN_isac_free(dchannel_t *);
|
||||
extern int mISDN_isac_init(channel_t *);
|
||||
extern void mISDN_isac_free(channel_t *);
|
||||
|
||||
extern void mISDN_isac_interrupt(dchannel_t *, u_char);
|
||||
extern void mISDN_clear_isac(dchannel_t *);
|
||||
extern int mISDN_ISAC_l1hw(mISDNif_t *, struct sk_buff *);
|
||||
extern void mISDN_isac_interrupt(channel_t *, u_char);
|
||||
extern void mISDN_clear_isac(channel_t *);
|
||||
extern int mISDN_ISAC_l1hw(mISDNinstance_t *, struct sk_buff *);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -230,9 +230,9 @@ typedef struct _isar_hw {
|
|||
#define STFAX_ESCAPE 5
|
||||
#define STFAX_SILDET 6
|
||||
|
||||
extern int ISARVersion(bchannel_t *bch, char *s);
|
||||
extern void isar_int_main(bchannel_t *bch);
|
||||
extern int init_isar(bchannel_t *bch);
|
||||
extern void free_isar(bchannel_t *bch);
|
||||
extern int isar_down(mISDNif_t *, struct sk_buff *);
|
||||
extern int isar_load_firmware(bchannel_t *bch, u_char *buf, int size);
|
||||
extern int ISARVersion(channel_t *bch, char *s);
|
||||
extern void isar_int_main(channel_t *bch);
|
||||
extern int init_isar(channel_t *bch);
|
||||
extern void free_isar(channel_t *bch);
|
||||
extern int isar_down(mISDNinstance_t *, struct sk_buff *);
|
||||
extern int isar_load_firmware(channel_t *bch, u_char *buf, int size);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -77,10 +77,10 @@ mISDN_alloc_l3msg(int len, u_char type)
|
|||
|
||||
void mISDN_AddvarIE(struct sk_buff *skb, u_char *ie)
|
||||
{
|
||||
u_char *p, *ps;
|
||||
u16 *ies;
|
||||
int l;
|
||||
Q931_info_t *qi = (Q931_info_t *)skb->data;
|
||||
u_char *p, *ps;
|
||||
ie_info_t *ies;
|
||||
int l;
|
||||
Q931_info_t *qi = (Q931_info_t *)skb->data;
|
||||
|
||||
ies = &qi->bearer_capability;
|
||||
ps = (u_char *) qi;
|
||||
|
@ -96,6 +96,10 @@ void mISDN_AddvarIE(struct sk_buff *skb, u_char *ie)
|
|||
int_error();
|
||||
return;
|
||||
}
|
||||
if (ies->off) { /* already used, no dupes for single octett */
|
||||
int_error();
|
||||
return;
|
||||
}
|
||||
l = 1;
|
||||
} else {
|
||||
if (_mISDN_l3_ie2pos[*ie]<0) {
|
||||
|
@ -103,19 +107,34 @@ void mISDN_AddvarIE(struct sk_buff *skb, u_char *ie)
|
|||
return;
|
||||
}
|
||||
ies += _mISDN_l3_ie2pos[*ie];
|
||||
if (ies->off) {
|
||||
if (ies->repeated)
|
||||
ies = mISDN_get_last_repeated_ie(qi, ies);
|
||||
l = mISDN_get_free_ext_ie(qi);
|
||||
if (l < 0) { // overflow
|
||||
int_error();
|
||||
return;
|
||||
}
|
||||
ies->ridx = l;
|
||||
ies->repeated = 1;
|
||||
ies = &qi->ext[l].ie;
|
||||
ies->cs_flg = 0;
|
||||
qi->ext[l].v.codeset = 0;
|
||||
qi->ext[l].v.val = *ie;
|
||||
}
|
||||
l = ie[1] + 2;
|
||||
}
|
||||
p = skb_put(skb, l);
|
||||
*ies = (u16)(p - ps);
|
||||
ies->off = (u16)(p - ps);
|
||||
memcpy(p, ie, l);
|
||||
}
|
||||
|
||||
void mISDN_AddIE(struct sk_buff *skb, u_char ie, u_char *iep)
|
||||
{
|
||||
u_char *p, *ps;
|
||||
u16 *ies;
|
||||
int l;
|
||||
Q931_info_t *qi = (Q931_info_t *)skb->data;
|
||||
u_char *p, *ps;
|
||||
ie_info_t *ies;
|
||||
int l;
|
||||
Q931_info_t *qi = (Q931_info_t *)skb->data;
|
||||
|
||||
if (ie & 0x80) { /* one octett IE */
|
||||
if (ie == IE_MORE_DATA)
|
||||
|
@ -128,6 +147,10 @@ void mISDN_AddIE(struct sk_buff *skb, u_char ie, u_char *iep)
|
|||
int_error();
|
||||
return;
|
||||
}
|
||||
if (ies->off) { /* already used, no dupes for single octett */
|
||||
int_error();
|
||||
return;
|
||||
}
|
||||
l = 0;
|
||||
} else {
|
||||
if (!iep || !iep[0])
|
||||
|
@ -138,21 +161,55 @@ void mISDN_AddIE(struct sk_buff *skb, u_char ie, u_char *iep)
|
|||
return;
|
||||
}
|
||||
ies += _mISDN_l3_ie2pos[ie];
|
||||
if (ies->off) {
|
||||
if (ies->repeated)
|
||||
ies = mISDN_get_last_repeated_ie(qi, ies);
|
||||
l = mISDN_get_free_ext_ie(qi);
|
||||
if (l < 0) { // overflow
|
||||
int_error();
|
||||
return;
|
||||
}
|
||||
ies->ridx = l;
|
||||
ies->repeated = 1;
|
||||
ies = &qi->ext[l].ie;
|
||||
ies->cs_flg = 0;
|
||||
qi->ext[l].v.codeset = 0;
|
||||
qi->ext[l].v.val = ie;
|
||||
}
|
||||
l = iep[0] + 1;
|
||||
}
|
||||
ps = (u_char *) qi;
|
||||
ps += L3_EXTRA_SIZE;
|
||||
p = skb_put(skb, l+1);
|
||||
*ies = (u16)(p - ps);
|
||||
ies->off = (u16)(p - ps);
|
||||
*p++ = ie;
|
||||
if (l)
|
||||
memcpy(p, iep, l);
|
||||
}
|
||||
|
||||
ie_info_t *mISDN_get_last_repeated_ie(Q931_info_t *qi, ie_info_t *ie)
|
||||
{
|
||||
while(ie->repeated) {
|
||||
ie = &qi->ext[ie->ridx].ie;
|
||||
}
|
||||
return(ie);
|
||||
}
|
||||
|
||||
int mISDN_get_free_ext_ie(Q931_info_t *qi)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (qi->ext[i].ie.off == 0)
|
||||
return(i);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void mISDN_LogL3Msg(struct sk_buff *skb)
|
||||
{
|
||||
u_char *p,*ps, *t, tmp[32];
|
||||
u16 *ies;
|
||||
u_char *p,*ps, *t, tmp[128];
|
||||
ie_info_t *ies;
|
||||
int i,j;
|
||||
Q931_info_t *qi = (Q931_info_t *)skb->data;
|
||||
mISDN_head_t *hh;
|
||||
|
@ -168,18 +225,49 @@ void mISDN_LogL3Msg(struct sk_buff *skb)
|
|||
printk(KERN_DEBUG "L3Msg type(%02x) qi(%p) ies(%p) ps(%p)\n",
|
||||
qi->type, qi, ies, ps);
|
||||
for (i=0;i<32;i++) {
|
||||
if (ies[i]) {
|
||||
p = ps + ies[i];
|
||||
if (ies[i].off) {
|
||||
p = ps + ies[i].off;
|
||||
t = tmp;
|
||||
*t = 0;
|
||||
for (j=0; j<p[1]; j++) {
|
||||
if (j>9) {
|
||||
if (j>40) {
|
||||
sprintf(t, " ...");
|
||||
break;
|
||||
}
|
||||
t += sprintf(t, " %02x", p[j+2]);
|
||||
}
|
||||
printk(KERN_DEBUG "L3Msg ies[%d] off(%d) ie(%02x/%02x) len(%d) %s\n",
|
||||
i, ies[i], _mISDN_l3_pos2ie[i], *p, p[1], tmp);
|
||||
printk(KERN_DEBUG "L3Msg ies[%d] off(%d) rep(%d) ridx(%d) ie(%02x/%02x) len(%d)%s\n",
|
||||
i, ies[i].off, ies[i].repeated, ies[i].ridx, _mISDN_l3_pos2ie[i], *p, p[1], tmp);
|
||||
}
|
||||
}
|
||||
for (i=0;i<8;i++) {
|
||||
if (qi->ext[i].ie.off) {
|
||||
p = ps + qi->ext[i].ie.off;
|
||||
t = tmp;
|
||||
*t = 0;
|
||||
if (qi->ext[i].ie.cs_flg) {
|
||||
for (j=0; j<qi->ext[i].cs.len; j++) {
|
||||
if (j>40) {
|
||||
sprintf(t, " ...");
|
||||
break;
|
||||
}
|
||||
t += sprintf(t, " %02x", p[j]);
|
||||
}
|
||||
printk(KERN_DEBUG "L3Msg ext[%d] off(%d) locked(%d) cs(%d) len(%d)%s\n",
|
||||
i, qi->ext[i].ie.off, qi->ext[i].cs.locked, qi->ext[i].cs.codeset,
|
||||
qi->ext[i].cs.len, tmp);
|
||||
} else {
|
||||
for (j=0; j<p[1]; j++) {
|
||||
if (j>40) {
|
||||
sprintf(t, " ...");
|
||||
break;
|
||||
}
|
||||
t += sprintf(t, " %02x", p[j+2]);
|
||||
}
|
||||
printk(KERN_DEBUG "L3Msg ext[%d] off(%d) rep(%d) ridx(%d) cs(%d) ie(%02x/%02x) len(%d) %s\n",
|
||||
i, qi->ext[i].ie.off, qi->ext[i].ie.repeated, qi->ext[i].ie.ridx,
|
||||
qi->ext[i].v.codeset, qi->ext[i].v.val, *p, p[1], tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,4 +282,6 @@ EXPORT_SYMBOL(mISDN_alloc_l3msg);
|
|||
#endif
|
||||
EXPORT_SYMBOL(mISDN_AddvarIE);
|
||||
EXPORT_SYMBOL(mISDN_AddIE);
|
||||
EXPORT_SYMBOL(mISDN_get_last_repeated_ie);
|
||||
EXPORT_SYMBOL(mISDN_get_free_ext_ie);
|
||||
EXPORT_SYMBOL(mISDN_LogL3Msg);
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
/* $Id$
|
||||
*
|
||||
* mISDN_l1.c common low level stuff for I.430 layer1
|
||||
* mISDN_l1.c common low level stuff for I.430 layer1 TE mode
|
||||
*
|
||||
* Author Karsten Keil (keil@isdn4linux.de)
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
* For changes and modifications please read
|
||||
* ../../../Documentation/isdn/mISDN.cert
|
||||
* This file is released under the GPLv2
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -46,9 +44,11 @@ static mISDNobject_t isdnl1;
|
|||
|
||||
#define TIMER3_VALUE 7000
|
||||
|
||||
#ifdef OBSOLETE
|
||||
static
|
||||
struct Fsm l1fsm_b =
|
||||
{NULL, 0, 0, NULL, NULL};
|
||||
#endif
|
||||
|
||||
static
|
||||
struct Fsm l1fsm_s =
|
||||
|
@ -100,6 +100,7 @@ static char *strL1UState[] =
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifdef OBSOLETE
|
||||
enum {
|
||||
ST_L1_NULL,
|
||||
ST_L1_WAIT_ACT,
|
||||
|
@ -116,7 +117,7 @@ static char *strL1BState[] =
|
|||
"ST_L1_WAIT_DEACT",
|
||||
"ST_L1_ACTIV",
|
||||
};
|
||||
|
||||
#endif
|
||||
enum {
|
||||
EV_PH_ACTIVATE,
|
||||
EV_PH_DEACTIVATE,
|
||||
|
@ -124,7 +125,7 @@ enum {
|
|||
EV_DEACT_CNF,
|
||||
EV_DEACT_IND,
|
||||
EV_POWER_UP,
|
||||
EV_ANYSIG_IND,
|
||||
EV_ANYSIG_IND,
|
||||
EV_INFO2_IND,
|
||||
EV_INFO4_IND,
|
||||
EV_TIMER_DEACT,
|
||||
|
@ -142,7 +143,7 @@ static char *strL1Event[] =
|
|||
"EV_DEACT_CNF",
|
||||
"EV_DEACT_IND",
|
||||
"EV_POWER_UP",
|
||||
"EV_ANYSIG_IND",
|
||||
"EV_ANYSIG_IND",
|
||||
"EV_INFO2_IND",
|
||||
"EV_INFO4_IND",
|
||||
"EV_TIMER_DEACT",
|
||||
|
@ -166,13 +167,13 @@ l1m_debug(struct FsmInst *fi, char *fmt, ...)
|
|||
static int
|
||||
l1up(layer1_t *l1, u_int prim, int dinfo, int len, void *arg)
|
||||
{
|
||||
return(if_link(&l1->inst.up, prim, dinfo, len, arg, 0));
|
||||
return(mISDN_queue_data(&l1->inst, FLG_MSG_UP, prim, dinfo, len, arg, 0));
|
||||
}
|
||||
|
||||
static int
|
||||
l1down(layer1_t *l1, u_int prim, int dinfo, int len, void *arg)
|
||||
{
|
||||
return(if_link(&l1->inst.down, prim, dinfo, len, arg, 0));
|
||||
return(mISDN_queue_data(&l1->inst, FLG_MSG_DOWN, prim, dinfo, len, arg, 0));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -209,8 +210,6 @@ l1_power_up_s(struct FsmInst *fi, int event, void *arg)
|
|||
if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
|
||||
mISDN_FsmChangeState(fi, ST_L1_F4);
|
||||
l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL);
|
||||
mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
|
||||
test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
|
||||
} else
|
||||
mISDN_FsmChangeState(fi, ST_L1_F3);
|
||||
}
|
||||
|
@ -269,11 +268,14 @@ l1_timer3(struct FsmInst *fi, int event, void *arg)
|
|||
layer1_t *l1 = fi->userdata;
|
||||
u_int db = HW_D_NOBLOCKED;
|
||||
|
||||
test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags);
|
||||
test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags);
|
||||
if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
|
||||
if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
|
||||
l1up(l1, PH_CONTROL | INDICATION, 0, 4, &db);
|
||||
l1up(l1, PH_DEACTIVATE | INDICATION, 0, 0, NULL);
|
||||
mISDN_queue_data(&l1->inst, l1->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_DEACTIVATED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
#ifdef mISDN_UINTERFACE
|
||||
if (!test_bit(FLG_L1_UINT, &l1->Flags))
|
||||
|
@ -295,6 +297,9 @@ l1_timer_act(struct FsmInst *fi, int event, void *arg)
|
|||
l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL);
|
||||
else
|
||||
l1up(l1, PH_ACTIVATE | INDICATION, 0, 0, NULL);
|
||||
mISDN_queue_data(&l1->inst, l1->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_ACTIVATED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -309,6 +314,9 @@ l1_timer_deact(struct FsmInst *fi, int event, void *arg)
|
|||
l1up(l1, PH_CONTROL | INDICATION, 0, 4, &db);
|
||||
l1up(l1, PH_DEACTIVATE | INDICATION, 0, 0, NULL);
|
||||
l1down(l1, PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL);
|
||||
mISDN_queue_data(&l1->inst, l1->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_DEACTIVATED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -316,6 +324,8 @@ l1_activate_s(struct FsmInst *fi, int event, void *arg)
|
|||
{
|
||||
layer1_t *l1 = fi->userdata;
|
||||
|
||||
mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
|
||||
test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
|
||||
l1down(l1, PH_CONTROL | REQUEST, HW_RESET, 0, NULL);
|
||||
}
|
||||
|
||||
|
@ -443,7 +453,7 @@ static struct FsmNode L1UFnList[] =
|
|||
#define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode))
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef OBSOLETE
|
||||
static void
|
||||
l1b_activate(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
|
@ -489,26 +499,17 @@ static struct FsmNode L1BFnList[] =
|
|||
};
|
||||
|
||||
#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode))
|
||||
#endif
|
||||
|
||||
static int
|
||||
l1from_up(mISDNif_t *hif, struct sk_buff *skb)
|
||||
l1from_up(layer1_t *l1, struct sk_buff *skb, mISDN_head_t *hh)
|
||||
{
|
||||
layer1_t *l1;
|
||||
mISDN_head_t *hh;
|
||||
int err = 0;
|
||||
|
||||
if (!hif || !hif->fdata || !skb)
|
||||
return(-EINVAL);
|
||||
l1 = hif->fdata;
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
switch(hh->prim) {
|
||||
case (PH_DATA | REQUEST):
|
||||
case (PH_CONTROL | REQUEST):
|
||||
if (l1->inst.down.func)
|
||||
return(l1->inst.down.func(&l1->inst.down,
|
||||
skb));
|
||||
else
|
||||
err = -ENXIO;
|
||||
return(mISDN_queue_down(&l1->inst, 0, skb));
|
||||
break;
|
||||
case (PH_ACTIVATE | REQUEST):
|
||||
if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
|
||||
|
@ -519,10 +520,7 @@ l1from_up(mISDNif_t *hif, struct sk_buff *skb)
|
|||
}
|
||||
break;
|
||||
case (MDL_FINDTEI | REQUEST):
|
||||
if (l1->inst.up.func)
|
||||
return(l1->inst.up.func(&l1->inst.up, skb));
|
||||
else
|
||||
err = -ENXIO;
|
||||
return(mISDN_queue_up(&l1->inst, 0, skb));
|
||||
break;
|
||||
default:
|
||||
if (l1->debug)
|
||||
|
@ -537,28 +535,16 @@ l1from_up(mISDNif_t *hif, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
static int
|
||||
l1from_down(mISDNif_t *hif, struct sk_buff *skb)
|
||||
l1from_down(layer1_t *l1, struct sk_buff *skb, mISDN_head_t *hh)
|
||||
{
|
||||
layer1_t *l1;
|
||||
mISDN_head_t *hh;
|
||||
int err = 0;
|
||||
|
||||
if (!hif || !hif->fdata || !skb)
|
||||
return(-EINVAL);
|
||||
l1 = hif->fdata;
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
if (hh->prim == PH_DATA_IND) {
|
||||
if (test_bit(FLG_L1_ACTTIMER, &l1->Flags))
|
||||
mISDN_FsmEvent(&l1->l1m, EV_TIMER_ACT, NULL);
|
||||
if (l1->inst.up.func)
|
||||
return(l1->inst.up.func(&l1->inst.up, skb));
|
||||
else
|
||||
err = -ENXIO;
|
||||
return(mISDN_queue_up(&l1->inst, 0, skb));
|
||||
} else if (hh->prim == PH_DATA_CNF) {
|
||||
if (l1->inst.up.func)
|
||||
return(l1->inst.up.func(&l1->inst.up, skb));
|
||||
else
|
||||
err = -ENXIO;
|
||||
return(mISDN_queue_up(&l1->inst, 0, skb));
|
||||
} else if (hh->prim == (PH_CONTROL | INDICATION)) {
|
||||
if (hh->dinfo == HW_RESET)
|
||||
mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL);
|
||||
|
@ -570,7 +556,7 @@ l1from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
mISDN_debug(l1->inst.st->id, NULL,
|
||||
"l1from_down ctrl ind %x unhandled", hh->dinfo);
|
||||
} else if (hh->prim == (PH_CONTROL | CONFIRM)) {
|
||||
if (hh->dinfo == HW_DEACTIVATE)
|
||||
if (hh->dinfo == HW_DEACTIVATE)
|
||||
mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL);
|
||||
else if (l1->debug)
|
||||
mISDN_debug(l1->inst.st->id, NULL,
|
||||
|
@ -600,11 +586,67 @@ l1from_down(mISDNif_t *hif, struct sk_buff *skb)
|
|||
return(err);
|
||||
}
|
||||
|
||||
static int
|
||||
l1_shortstatus(layer1_t *l1, struct sk_buff *skb, mISDN_head_t *hh)
|
||||
{
|
||||
u_int temp;
|
||||
|
||||
if (hh->prim == (MGR_SHORTSTATUS | REQUEST)) {
|
||||
temp = hh->dinfo & SSTATUS_ALL;
|
||||
if (temp == SSTATUS_ALL || temp == SSTATUS_L1) {
|
||||
skb_trim(skb, 0);
|
||||
if (hh->dinfo & SSTATUS_BROADCAST_BIT)
|
||||
temp = l1->inst.id | MSG_BROADCAST;
|
||||
else
|
||||
temp = hh->addr | FLG_MSG_TARGET;
|
||||
hh->dinfo = (l1->l1m.state == ST_L1_F7) ?
|
||||
SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED;
|
||||
hh->prim = MGR_SHORTSTATUS | CONFIRM;
|
||||
return(mISDN_queue_message(&l1->inst, temp, skb));
|
||||
}
|
||||
}
|
||||
return(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
l1_function(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
layer1_t *l1 = inst->privat;
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (debug)
|
||||
printk(KERN_DEBUG "%s: addr(%08x) prim(%x)\n", __FUNCTION__, hh->addr, hh->prim);
|
||||
|
||||
if (unlikely((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS))
|
||||
return(l1_shortstatus(l1, skb, hh));
|
||||
|
||||
switch(hh->addr & MSG_DIR_MASK) {
|
||||
case FLG_MSG_DOWN:
|
||||
ret = l1from_up(l1, skb, hh);
|
||||
break;
|
||||
case FLG_MSG_UP:
|
||||
ret = l1from_down(l1, skb, hh);
|
||||
break;
|
||||
case MSG_TO_OWNER:
|
||||
/* FIXME: must be handled depending on type */
|
||||
int_errtxt("not implemented yet");
|
||||
break;
|
||||
default:
|
||||
/* FIXME: must be handled depending on type */
|
||||
int_errtxt("not implemented yet");
|
||||
break;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
release_l1(layer1_t *l1) {
|
||||
mISDNinstance_t *inst = &l1->inst;
|
||||
u_long flags;
|
||||
|
||||
mISDN_FsmDelTimer(&l1->timer, 0);
|
||||
#ifdef OBSOLETE
|
||||
if (inst->up.peer) {
|
||||
inst->up.peer->obj->ctrl(inst->up.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
|
@ -613,15 +655,19 @@ release_l1(layer1_t *l1) {
|
|||
inst->down.peer->obj->ctrl(inst->down.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->down);
|
||||
}
|
||||
#endif
|
||||
spin_lock_irqsave(&isdnl1.lock, flags);
|
||||
list_del(&l1->list);
|
||||
spin_unlock_irqrestore(&isdnl1.lock, flags);
|
||||
isdnl1.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
kfree(l1);
|
||||
}
|
||||
|
||||
static int
|
||||
new_l1(mISDNstack_t *st, mISDN_pid_t *pid) {
|
||||
layer1_t *nl1;
|
||||
int err;
|
||||
layer1_t *nl1;
|
||||
int err;
|
||||
u_long flags;
|
||||
|
||||
if (!st || !pid)
|
||||
return(-EINVAL);
|
||||
|
@ -631,14 +677,14 @@ new_l1(mISDNstack_t *st, mISDN_pid_t *pid) {
|
|||
}
|
||||
memset(nl1, 0, sizeof(layer1_t));
|
||||
memcpy(&nl1->inst.pid, pid, sizeof(mISDN_pid_t));
|
||||
mISDN_init_instance(&nl1->inst, &isdnl1, nl1);
|
||||
mISDN_init_instance(&nl1->inst, &isdnl1, nl1, l1_function);
|
||||
if (!mISDN_SetHandledPID(&isdnl1, &nl1->inst.pid)) {
|
||||
int_error();
|
||||
return(-ENOPROTOOPT);
|
||||
}
|
||||
switch(pid->protocol[1]) {
|
||||
case ISDN_PID_L1_TE_S0:
|
||||
sprintf(nl1->inst.name, "l1TES0 %d", st->id);
|
||||
sprintf(nl1->inst.name, "l1TES0 %x", st->id >> 8);
|
||||
nl1->l1m.fsm = &l1fsm_s;
|
||||
nl1->l1m.state = ST_L1_F3;
|
||||
nl1->Flags = 0;
|
||||
|
@ -655,7 +701,9 @@ new_l1(mISDNstack_t *st, mISDN_pid_t *pid) {
|
|||
nl1->l1m.userint = 0;
|
||||
nl1->l1m.printdebug = l1m_debug;
|
||||
mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer);
|
||||
spin_lock_irqsave(&isdnl1.lock, flags);
|
||||
list_add_tail(&nl1->list, &isdnl1.ilist);
|
||||
spin_unlock_irqrestore(&isdnl1.lock, flags);
|
||||
err = isdnl1.ctrl(st, MGR_REGLAYER | INDICATION, &nl1->inst);
|
||||
if (err) {
|
||||
mISDN_FsmDelTimer(&nl1->timer, 0);
|
||||
|
@ -701,27 +749,31 @@ l1_manager(void *data, u_int prim, void *arg) {
|
|||
mISDNinstance_t *inst = data;
|
||||
layer1_t *l1l;
|
||||
int err = -EINVAL;
|
||||
u_long flags;
|
||||
|
||||
if (debug & 0x10000)
|
||||
printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n",
|
||||
__FUNCTION__, data, prim, arg);
|
||||
if (!data)
|
||||
return(err);
|
||||
spin_lock_irqsave(&isdnl1.lock, flags);
|
||||
list_for_each_entry(l1l, &isdnl1.ilist, list) {
|
||||
if (&l1l->inst == inst) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&isdnl1.lock, flags);
|
||||
if (err && (prim != (MGR_NEWLAYER | REQUEST))) {
|
||||
printk(KERN_WARNING "l1_manager connect no instance\n");
|
||||
return(err);
|
||||
}
|
||||
|
||||
|
||||
switch(prim) {
|
||||
case MGR_NEWLAYER | REQUEST:
|
||||
err = new_l1(data, arg);
|
||||
break;
|
||||
#ifdef OBSOLETE
|
||||
case MGR_CONNECT | REQUEST:
|
||||
err = mISDN_ConnectIF(inst, arg);
|
||||
break;
|
||||
|
@ -733,6 +785,7 @@ l1_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_DISCONNECT | INDICATION:
|
||||
err = mISDN_DisConnectIF(inst, arg);
|
||||
break;
|
||||
#endif
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
case MGR_RELEASE | INDICATION:
|
||||
printk(KERN_DEBUG "release_l1 id %x\n", l1l->inst.st->id);
|
||||
|
@ -762,6 +815,7 @@ int Isdnl1Init(void)
|
|||
isdnl1.name = MName;
|
||||
isdnl1.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0;
|
||||
isdnl1.own_ctrl = l1_manager;
|
||||
spin_lock_init(&isdnl1.lock);
|
||||
INIT_LIST_HEAD(&isdnl1.ilist);
|
||||
#ifdef mISDN_UINTERFACE
|
||||
isdnl1.DPROTO.protocol[1] |= ISDN_PID_L1_TE_U;
|
||||
|
@ -776,18 +830,22 @@ int Isdnl1Init(void)
|
|||
l1fsm_s.strEvent = strL1Event;
|
||||
l1fsm_s.strState = strL1SState;
|
||||
mISDN_FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT);
|
||||
#ifdef OBSOLETE
|
||||
l1fsm_b.state_count = L1B_STATE_COUNT;
|
||||
l1fsm_b.event_count = L1_EVENT_COUNT;
|
||||
l1fsm_b.strEvent = strL1Event;
|
||||
l1fsm_b.strState = strL1BState;
|
||||
mISDN_FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
|
||||
#endif
|
||||
if ((err = mISDN_register(&isdnl1))) {
|
||||
printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
|
||||
#ifdef mISDN_UINTERFACE
|
||||
mISDN_FsmFree(&l1fsm_u);
|
||||
#endif
|
||||
mISDN_FsmFree(&l1fsm_s);
|
||||
#ifdef OBSOLETE
|
||||
mISDN_FsmFree(&l1fsm_b);
|
||||
#endif
|
||||
}
|
||||
return(err);
|
||||
}
|
||||
|
@ -810,6 +868,8 @@ void cleanup_module(void)
|
|||
mISDN_FsmFree(&l1fsm_u);
|
||||
#endif
|
||||
mISDN_FsmFree(&l1fsm_s);
|
||||
#ifdef OBSOLETE
|
||||
mISDN_FsmFree(&l1fsm_b);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "memdbg.h"
|
||||
#endif
|
||||
|
||||
#ifdef OBSOLETE
|
||||
#define D_RCVBUFREADY 0
|
||||
#define D_XMTBUFREADY 1
|
||||
#define D_L1STATECHANGE 2
|
||||
|
@ -32,6 +33,7 @@
|
|||
#define B_XMTBUFREADY 1
|
||||
#define B_BLOCKEDATOMIC 2
|
||||
#define B_DTMFREADY 3
|
||||
#endif
|
||||
|
||||
#define FLG_L1_ACTIVATING 1
|
||||
#define FLG_L1_ACTIVATED 2
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
*
|
||||
* Author Karsten Keil (keil@isdn4linux.de)
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
* For changes and modifications please read
|
||||
* ../../../Documentation/isdn/mISDN.cert
|
||||
* This file is released under the GPLv2
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
|
@ -116,14 +114,11 @@ l2addrsize(layer2_t *l2)
|
|||
static int
|
||||
l2_newid(layer2_t *l2)
|
||||
{
|
||||
u_long flags;
|
||||
int id;
|
||||
|
||||
spin_lock_irqsave(&l2->lock, flags);
|
||||
id = l2->next_id++;
|
||||
if (id == 0x7fff)
|
||||
l2->next_id = 1;
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
id |= (l2->entity << 16);
|
||||
return(id);
|
||||
}
|
||||
|
@ -131,22 +126,20 @@ l2_newid(layer2_t *l2)
|
|||
static int
|
||||
l2up(layer2_t *l2, u_int prim, int dinfo, struct sk_buff *skb)
|
||||
{
|
||||
return(if_newhead(&l2->inst.up, prim, dinfo, skb));
|
||||
return(mISDN_queueup_newhead(&l2->inst, 0, prim, dinfo, skb));
|
||||
}
|
||||
|
||||
static int
|
||||
l2up_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg)
|
||||
{
|
||||
return(if_link(&l2->inst.up, prim, dinfo, len, arg, 0));
|
||||
return(mISDN_queue_data(&l2->inst, FLG_MSG_UP, prim, dinfo, len, arg, 0));
|
||||
}
|
||||
|
||||
static int
|
||||
l2down_skb(layer2_t *l2, struct sk_buff *skb) {
|
||||
mISDNif_t *down = &l2->inst.down;
|
||||
int ret = -ENXIO;
|
||||
int ret;
|
||||
|
||||
if (down->func)
|
||||
ret = down->func(down, skb);
|
||||
ret = mISDN_queue_down(&l2->inst, 0, skb);
|
||||
if (ret && l2->debug)
|
||||
printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret);
|
||||
return(ret);
|
||||
|
@ -189,18 +182,18 @@ l2down_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg)
|
|||
return(err);
|
||||
}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
static int
|
||||
l2_chain_down(mISDNif_t *hif, struct sk_buff *skb) {
|
||||
if (!hif || !hif->fdata)
|
||||
return(-EINVAL);
|
||||
return(l2down_raw(hif->fdata, skb));
|
||||
l2_chain_down(mISDNinstance_t *inst, struct sk_buff *skb) {
|
||||
return(l2down_raw(inst->privat, skb));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
ph_data_confirm(mISDNif_t *up, mISDN_head_t *hh, struct sk_buff *skb) {
|
||||
layer2_t *l2 = up->fdata;
|
||||
struct sk_buff *nskb = skb;
|
||||
mISDNif_t *next = up->clone;
|
||||
ph_data_confirm(mISDNinstance_t *inst, mISDN_head_t *hh, struct sk_buff *skb) {
|
||||
layer2_t *l2 = inst->privat;
|
||||
struct sk_buff *nskb = skb;
|
||||
// mISDNif_t *next = up->clone;
|
||||
int ret = -EAGAIN;
|
||||
|
||||
if (test_bit(FLG_L1_BUSY, &l2->flag)) {
|
||||
|
@ -213,8 +206,10 @@ ph_data_confirm(mISDNif_t *up, mISDN_head_t *hh, struct sk_buff *skb) {
|
|||
}
|
||||
} else
|
||||
l2->down_id = MISDN_ID_NONE;
|
||||
#ifdef FIXME
|
||||
if (next)
|
||||
ret = next->func(next, skb);
|
||||
#endif
|
||||
if (ret) {
|
||||
dev_kfree_skb(skb);
|
||||
ret = 0;
|
||||
|
@ -225,8 +220,10 @@ ph_data_confirm(mISDNif_t *up, mISDN_head_t *hh, struct sk_buff *skb) {
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifdef FIXME
|
||||
if (ret && next)
|
||||
ret = next->func(next, skb);
|
||||
#endif
|
||||
if (!test_and_set_bit(FLG_L1_BUSY, &l2->flag)) {
|
||||
if ((nskb = skb_dequeue(&l2->down_queue))) {
|
||||
l2->down_id = mISDN_HEAD_DINFO(nskb);
|
||||
|
@ -406,7 +403,7 @@ inline int
|
|||
IsSFrame(u_char * data, layer2_t *l2)
|
||||
{
|
||||
register u_char d = *data;
|
||||
|
||||
|
||||
if (!test_bit(FLG_MOD128, &l2->flag))
|
||||
d &= 0xf;
|
||||
return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c));
|
||||
|
@ -530,10 +527,8 @@ legalnr(layer2_t *l2, unsigned int nr)
|
|||
static void
|
||||
setva(layer2_t *l2, unsigned int nr)
|
||||
{
|
||||
u_long flags;
|
||||
struct sk_buff *skb;
|
||||
|
||||
spin_lock_irqsave(&l2->lock, flags);
|
||||
while (l2->va != nr) {
|
||||
l2->va++;
|
||||
if(test_bit(FLG_MOD128, &l2->flag))
|
||||
|
@ -547,7 +542,6 @@ setva(layer2_t *l2, unsigned int nr)
|
|||
}
|
||||
l2->sow = (l2->sow + 1) % l2->window;
|
||||
}
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
while((skb =skb_dequeue(&l2->tmp_queue))) {
|
||||
if (l2up(l2, DL_DATA | CONFIRM, mISDN_HEAD_DINFO(skb), skb))
|
||||
dev_kfree_skb(skb);
|
||||
|
@ -658,7 +652,7 @@ l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg)
|
|||
l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'C');
|
||||
else
|
||||
l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'D');
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -695,7 +689,7 @@ static void
|
|||
l2_go_st3(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
dev_kfree_skb((struct sk_buff *)arg);
|
||||
mISDN_FsmChangeState(fi, ST_L2_3);
|
||||
mISDN_FsmChangeState(fi, ST_L2_3);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -801,7 +795,7 @@ l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg)
|
|||
test_and_set_bit(FLG_L3_INIT, &l2->flag);
|
||||
test_and_clear_bit(FLG_PEND_REL, &l2->flag);
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
l2_l3_reestablish(struct FsmInst *fi, int event, void *arg)
|
||||
|
@ -858,14 +852,11 @@ l2_start_multi(struct FsmInst *fi, int event, void *arg)
|
|||
{
|
||||
layer2_t *l2 = fi->userdata;
|
||||
struct sk_buff *skb = arg;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&l2->lock, flags);
|
||||
l2->vs = 0;
|
||||
l2->va = 0;
|
||||
l2->vr = 0;
|
||||
l2->sow = 0;
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
clear_exception(l2);
|
||||
send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP);
|
||||
mISDN_FsmChangeState(fi, ST_L2_7);
|
||||
|
@ -873,6 +864,10 @@ l2_start_multi(struct FsmInst *fi, int event, void *arg)
|
|||
skb_trim(skb, 0);
|
||||
if (l2up(l2, DL_ESTABLISH | INDICATION, 0, skb))
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -899,7 +894,6 @@ l2_restart_multi(struct FsmInst *fi, int event, void *arg)
|
|||
layer2_t *l2 = fi->userdata;
|
||||
struct sk_buff *skb = arg;
|
||||
int est = 0;
|
||||
u_long flags;
|
||||
|
||||
send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
|
||||
|
||||
|
@ -911,18 +905,20 @@ l2_restart_multi(struct FsmInst *fi, int event, void *arg)
|
|||
}
|
||||
|
||||
clear_exception(l2);
|
||||
spin_lock_irqsave(&l2->lock, flags);
|
||||
l2->vs = 0;
|
||||
l2->va = 0;
|
||||
l2->vr = 0;
|
||||
l2->sow = 0;
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
mISDN_FsmChangeState(fi, ST_L2_7);
|
||||
stop_t200(l2, 3);
|
||||
mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3);
|
||||
|
||||
if (est)
|
||||
if (est) {
|
||||
l2up_create(l2, DL_ESTABLISH | INDICATION, 0, 0, NULL);
|
||||
mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
|
||||
if (skb_queue_len(&l2->i_queue) && cansend(l2))
|
||||
mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
|
||||
|
@ -942,6 +938,10 @@ l2_stop_multi(struct FsmInst *fi, int event, void *arg)
|
|||
discard_queue(&l2->i_queue);
|
||||
freewin(l2);
|
||||
lapb_dl_release_l2l3(l2, INDICATION);
|
||||
|
||||
mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_RELEASED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -950,7 +950,6 @@ l2_connected(struct FsmInst *fi, int event, void *arg)
|
|||
layer2_t *l2 = fi->userdata;
|
||||
struct sk_buff *skb = arg;
|
||||
int pr=-1;
|
||||
u_long flags;
|
||||
|
||||
if (!get_PollFlag(l2, skb)) {
|
||||
l2_mdl_error_ua(fi, event, arg);
|
||||
|
@ -966,12 +965,10 @@ l2_connected(struct FsmInst *fi, int event, void *arg)
|
|||
pr = DL_ESTABLISH | INDICATION;
|
||||
}
|
||||
stop_t200(l2, 5);
|
||||
spin_lock_irqsave(&l2->lock, flags);
|
||||
l2->vr = 0;
|
||||
l2->vs = 0;
|
||||
l2->va = 0;
|
||||
l2->sow = 0;
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
mISDN_FsmChangeState(fi, ST_L2_7);
|
||||
mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4);
|
||||
if (pr != -1)
|
||||
|
@ -979,6 +976,10 @@ l2_connected(struct FsmInst *fi, int event, void *arg)
|
|||
|
||||
if (skb_queue_len(&l2->i_queue) && cansend(l2))
|
||||
mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
|
||||
|
||||
mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -995,6 +996,10 @@ l2_released(struct FsmInst *fi, int event, void *arg)
|
|||
stop_t200(l2, 6);
|
||||
lapb_dl_release_l2l3(l2, CONFIRM);
|
||||
mISDN_FsmChangeState(fi, ST_L2_4);
|
||||
|
||||
mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_RELEASED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1036,6 +1041,9 @@ l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
|
|||
stop_t200(l2, 8);
|
||||
lapb_dl_release_l2l3(l2, CONFIRM);
|
||||
mISDN_FsmChangeState(fi, ST_L2_4);
|
||||
mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_RELEASED,
|
||||
0, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1096,9 +1104,7 @@ static void
|
|||
invoke_retransmission(layer2_t *l2, unsigned int nr)
|
||||
{
|
||||
u_int p1;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&l2->lock, flags);
|
||||
if (l2->vs != nr) {
|
||||
while (l2->vs != nr) {
|
||||
(l2->vs)--;
|
||||
|
@ -1116,10 +1122,8 @@ invoke_retransmission(layer2_t *l2, unsigned int nr)
|
|||
printk(KERN_WARNING "%s: windowar[%d] is NULL\n", __FUNCTION__, p1);
|
||||
l2->windowar[p1] = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
|
||||
} else
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1221,7 +1225,6 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg)
|
|||
struct sk_buff *skb = arg;
|
||||
int PollFlag, i;
|
||||
u_int ns, nr;
|
||||
u_long flags;
|
||||
|
||||
i = l2addrsize(l2);
|
||||
if (test_bit(FLG_MOD128, &l2->flag)) {
|
||||
|
@ -1238,7 +1241,6 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg)
|
|||
if (PollFlag)
|
||||
enquiry_response(l2);
|
||||
} else {
|
||||
spin_lock_irqsave(&l2->lock, flags);
|
||||
if (l2->vr == ns) {
|
||||
l2->vr++;
|
||||
if(test_bit(FLG_MOD128, &l2->flag))
|
||||
|
@ -1246,7 +1248,6 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg)
|
|||
else
|
||||
l2->vr %= 8;
|
||||
test_and_clear_bit(FLG_REJEXC, &l2->flag);
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
if (PollFlag)
|
||||
enquiry_response(l2);
|
||||
else
|
||||
|
@ -1256,7 +1257,6 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg)
|
|||
dev_kfree_skb(skb);
|
||||
} else {
|
||||
/* n(s)!=v(r) */
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
dev_kfree_skb(skb);
|
||||
if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
|
||||
if (PollFlag)
|
||||
|
@ -1410,7 +1410,6 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
|
|||
struct sk_buff *skb, *nskb, *oskb;
|
||||
u_char header[MAX_HEADER_LEN];
|
||||
u_int i, p1;
|
||||
u_long flags;
|
||||
|
||||
if (!cansend(l2))
|
||||
return;
|
||||
|
@ -1419,7 +1418,6 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
|
|||
if (!skb)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&l2->lock, flags);
|
||||
if(test_bit(FLG_MOD128, &l2->flag))
|
||||
p1 = (l2->vs - l2->va) % 128;
|
||||
else
|
||||
|
@ -1440,7 +1438,6 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
|
|||
header[i++] = (l2->vr << 5) | (l2->vs << 1);
|
||||
l2->vs = (l2->vs + 1) % 8;
|
||||
}
|
||||
spin_unlock_irqrestore(&l2->lock, flags);
|
||||
|
||||
nskb = skb_clone(skb, GFP_ATOMIC);
|
||||
p1 = skb_headroom(nskb);
|
||||
|
@ -1609,6 +1606,9 @@ l2_tei_remove(struct FsmInst *fi, int event, void *arg)
|
|||
mISDN_FsmDelTimer(&l2->t203, 19);
|
||||
if (l2up(l2, DL_RELEASE | INDICATION, 0, skb))
|
||||
dev_kfree_skb(skb);
|
||||
mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_RELEASED,
|
||||
0, NULL, 0);
|
||||
mISDN_FsmChangeState(fi, ST_L2_1);
|
||||
}
|
||||
|
||||
|
@ -1617,7 +1617,7 @@ l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
|
|||
{
|
||||
layer2_t *l2 = fi->userdata;
|
||||
struct sk_buff *skb = arg;
|
||||
|
||||
|
||||
discard_queue(&l2->i_queue);
|
||||
discard_queue(&l2->ui_queue);
|
||||
if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
|
||||
|
@ -1667,6 +1667,9 @@ l2_persistant_da(struct FsmInst *fi, int event, void *arg)
|
|||
mISDN_FsmDelTimer(&l2->t203, 19);
|
||||
if (l2up(l2, DL_RELEASE | INDICATION, 0, skb))
|
||||
dev_kfree_skb(skb);
|
||||
mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
|
||||
MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_RELEASED,
|
||||
0, NULL, 0);
|
||||
mISDN_FsmChangeState(fi, ST_L2_4);
|
||||
}
|
||||
|
||||
|
@ -1818,7 +1821,7 @@ ph_data_indication(layer2_t *l2, mISDN_head_t *hh, struct sk_buff *skb) {
|
|||
u_int l;
|
||||
int c = 0;
|
||||
|
||||
|
||||
|
||||
l = l2addrsize(l2);
|
||||
if (skb->len <= l) {
|
||||
mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N');
|
||||
|
@ -1833,15 +1836,24 @@ ph_data_indication(layer2_t *l2, mISDN_head_t *hh, struct sk_buff *skb) {
|
|||
}
|
||||
psapi >>= 2;
|
||||
ptei >>= 1;
|
||||
if ((psapi != l2->sapi) && (psapi != TEI_SAPI))
|
||||
return(ret);
|
||||
if ((psapi != l2->sapi) && (psapi != TEI_SAPI)) {
|
||||
/* not our bussiness */
|
||||
// printk(KERN_DEBUG "%s: sapi %d/%d sapi mismatch\n", __FUNCTION__,
|
||||
// psapi, l2->sapi);
|
||||
dev_kfree_skb(skb);
|
||||
return(0);
|
||||
}
|
||||
if (ptei == GROUP_TEI) {
|
||||
if (psapi == TEI_SAPI) {
|
||||
hh->prim = MDL_UNITDATA | INDICATION;
|
||||
return(l2_tei(l2->tm, skb));
|
||||
}
|
||||
} else if ((ptei != l2->tei) || (psapi == TEI_SAPI)) {
|
||||
return(ret);
|
||||
/* not our bussiness */
|
||||
// printk(KERN_DEBUG "%s: tei %d/%d sapi %d mismatch\n", __FUNCTION__,
|
||||
// ptei, l2->tei, psapi);
|
||||
dev_kfree_skb(skb);
|
||||
return(0);
|
||||
}
|
||||
} else
|
||||
datap += l;
|
||||
|
@ -1880,27 +1892,14 @@ ph_data_indication(layer2_t *l2, mISDN_head_t *hh, struct sk_buff *skb) {
|
|||
}
|
||||
|
||||
static int
|
||||
l2from_down(mISDNif_t *hif, struct sk_buff *askb)
|
||||
l2from_down(layer2_t *l2, struct sk_buff *askb, mISDN_head_t *hh)
|
||||
{
|
||||
layer2_t *l2;
|
||||
int ret = -EINVAL;
|
||||
struct sk_buff *cskb = askb;
|
||||
mISDNif_t *next;
|
||||
mISDN_head_t *hh, sh;
|
||||
|
||||
if (!hif || !askb)
|
||||
return(-EINVAL);
|
||||
l2 = hif->fdata;
|
||||
hh = mISDN_HEAD_P(askb);
|
||||
next = hif->clone;
|
||||
// printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim);
|
||||
if (!l2) {
|
||||
if (next && next->func)
|
||||
ret = next->func(next, askb);
|
||||
return(ret);
|
||||
}
|
||||
if (hh->prim == (PH_DATA | CONFIRM))
|
||||
return(ph_data_confirm(hif, hh, askb));
|
||||
return(ph_data_confirm(&l2->inst, hh, askb));
|
||||
if (hh->prim == (MDL_FINDTEI | REQUEST)) {
|
||||
ret = -ESRCH;
|
||||
if (test_bit(FLG_LAPD, &l2->flag)) {
|
||||
|
@ -1914,10 +1913,13 @@ l2from_down(mISDNif_t *hif, struct sk_buff *askb)
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifdef FIXME
|
||||
if (next && next->func)
|
||||
ret = next->func(next, askb);
|
||||
#endif
|
||||
return(ret);
|
||||
}
|
||||
#ifdef FIXME
|
||||
if (next) {
|
||||
if (next->func) {
|
||||
if (!(cskb = skb_clone(askb, GFP_ATOMIC)))
|
||||
|
@ -1926,6 +1928,7 @@ l2from_down(mISDNif_t *hif, struct sk_buff *askb)
|
|||
sh = *hh;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
switch (hh->prim) {
|
||||
case (PH_DATA_IND):
|
||||
ret = ph_data_indication(l2, hh, cskb);
|
||||
|
@ -1962,26 +1965,21 @@ l2from_down(mISDNif_t *hif, struct sk_buff *askb)
|
|||
dev_kfree_skb(cskb);
|
||||
ret = 0;
|
||||
}
|
||||
#ifdef FIXME
|
||||
if (next && next->func) {
|
||||
*hh = sh;
|
||||
ret = next->func(next, askb);
|
||||
}
|
||||
#endif
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static int
|
||||
l2from_up(mISDNif_t *hif, struct sk_buff *skb) {
|
||||
layer2_t *l2;
|
||||
mISDN_head_t *hh;
|
||||
l2from_up(layer2_t *l2, struct sk_buff *skb, mISDN_head_t *hh) {
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!hif || !skb)
|
||||
return(ret);
|
||||
l2 = hif->fdata;
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
// printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim);
|
||||
if (!l2)
|
||||
return(ret);
|
||||
if (hh->addr & FLG_MSG_CLONED)
|
||||
return(l2down_raw(l2, skb));
|
||||
switch (hh->prim) {
|
||||
case (DL_DATA | REQUEST):
|
||||
ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb);
|
||||
|
@ -2021,7 +2019,7 @@ l2from_up(mISDNif_t *hif, struct sk_buff *skb) {
|
|||
ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, skb);
|
||||
case (MDL_STATUS | REQUEST):
|
||||
l2up_create(l2, MDL_STATUS | CONFIRM, hh->dinfo, 1,
|
||||
(void *)l2->tei);
|
||||
(void *)((u_long)l2->tei));
|
||||
break;
|
||||
default:
|
||||
if (l2->debug)
|
||||
|
@ -2030,6 +2028,68 @@ l2from_up(mISDNif_t *hif, struct sk_buff *skb) {
|
|||
return(ret);
|
||||
}
|
||||
|
||||
static int
|
||||
l2_shortstatus(layer2_t *l2, struct sk_buff *skb, mISDN_head_t *hh)
|
||||
{
|
||||
u_int temp;
|
||||
|
||||
if (hh->prim == (MGR_SHORTSTATUS | REQUEST)) {
|
||||
temp = hh->dinfo & SSTATUS_ALL;
|
||||
if (temp == SSTATUS_ALL || temp == SSTATUS_L2) {
|
||||
skb_trim(skb, 0);
|
||||
if (hh->dinfo & SSTATUS_BROADCAST_BIT)
|
||||
temp = l2->inst.id | MSG_BROADCAST;
|
||||
else
|
||||
temp = hh->addr | FLG_MSG_TARGET;
|
||||
switch(l2->l2m.state) {
|
||||
case ST_L2_7:
|
||||
case ST_L2_8:
|
||||
hh->dinfo = SSTATUS_L2_ESTABLISHED;
|
||||
break;
|
||||
default:
|
||||
hh->dinfo = SSTATUS_L2_RELEASED;
|
||||
}
|
||||
hh->prim = MGR_SHORTSTATUS | CONFIRM;
|
||||
return(mISDN_queue_message(&l2->inst, temp, skb));
|
||||
}
|
||||
}
|
||||
return(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static int
|
||||
l2_function(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
layer2_t *l2 = inst->privat;
|
||||
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (debug)
|
||||
printk(KERN_DEBUG "%s: addr(%08x) prim(%x)\n", __FUNCTION__, hh->addr, hh->prim);
|
||||
if (!l2)
|
||||
return(ret);
|
||||
|
||||
if (unlikely((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS))
|
||||
return(l2_shortstatus(l2, skb, hh));
|
||||
|
||||
switch(hh->addr & MSG_DIR_MASK) {
|
||||
case FLG_MSG_DOWN:
|
||||
ret = l2from_up(l2, skb, hh);
|
||||
break;
|
||||
case FLG_MSG_UP:
|
||||
ret = l2from_down(l2, skb, hh);
|
||||
break;
|
||||
case MSG_TO_OWNER:
|
||||
/* FIXME: must be handled depending on type */
|
||||
int_errtxt("not implemented yet");
|
||||
break;
|
||||
default:
|
||||
/* FIXME: broadcast must be handled depending on type */
|
||||
int_errtxt("not implemented yet");
|
||||
break;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
int
|
||||
tei_l2(layer2_t *l2, struct sk_buff *skb)
|
||||
{
|
||||
|
@ -2077,7 +2137,8 @@ l2m_debug(struct FsmInst *fi, char *fmt, ...)
|
|||
static void
|
||||
release_l2(layer2_t *l2)
|
||||
{
|
||||
mISDNinstance_t *inst = &l2->inst;
|
||||
mISDNinstance_t *inst = &l2->inst;
|
||||
u_long flags;
|
||||
|
||||
mISDN_FsmDelTimer(&l2->t200, 21);
|
||||
mISDN_FsmDelTimer(&l2->t203, 16);
|
||||
|
@ -2087,6 +2148,7 @@ release_l2(layer2_t *l2)
|
|||
ReleaseWin(l2);
|
||||
if (test_bit(FLG_LAPD, &l2->flag))
|
||||
release_tei(l2->tm);
|
||||
#ifdef OBSOLETE
|
||||
if (inst->up.peer) {
|
||||
inst->up.peer->obj->ctrl(inst->up.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
|
@ -2108,18 +2170,22 @@ release_l2(layer2_t *l2)
|
|||
kfree(l2->cloneif);
|
||||
l2->cloneif = NULL;
|
||||
}
|
||||
#endif
|
||||
spin_lock_irqsave(&isdnl2.lock, flags);
|
||||
list_del(&l2->list);
|
||||
spin_unlock_irqrestore(&isdnl2.lock, flags);
|
||||
isdnl2.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
if (l2->entity != MISDN_ENTITY_NONE)
|
||||
isdnl2.ctrl(inst, MGR_DELENTITY | REQUEST, (void *)l2->entity);
|
||||
isdnl2.ctrl(inst, MGR_DELENTITY | REQUEST, (void *)((u_long)l2->entity));
|
||||
kfree(l2);
|
||||
}
|
||||
|
||||
static int
|
||||
new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) {
|
||||
layer2_t *nl2;
|
||||
int err;
|
||||
u_char *p;
|
||||
new_l2(mISDNstack_t *st, mISDN_pid_t *pid) {
|
||||
layer2_t *nl2;
|
||||
int err;
|
||||
u_char *p;
|
||||
u_long flags;
|
||||
|
||||
if (!st || !pid)
|
||||
return(-EINVAL);
|
||||
|
@ -2128,11 +2194,10 @@ new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) {
|
|||
return(-ENOMEM);
|
||||
}
|
||||
memset(nl2, 0, sizeof(layer2_t));
|
||||
spin_lock_init(&nl2->lock);
|
||||
nl2->debug = debug;
|
||||
nl2->next_id = 1;
|
||||
nl2->down_id = MISDN_ID_NONE;
|
||||
mISDN_init_instance(&nl2->inst, &isdnl2, nl2);
|
||||
mISDN_init_instance(&nl2->inst, &isdnl2, nl2, l2_function);
|
||||
nl2->inst.extentions = EXT_INST_CLONE;
|
||||
memcpy(&nl2->inst.pid, pid, sizeof(mISDN_pid_t));
|
||||
if (!mISDN_SetHandledPID(&isdnl2, &nl2->inst.pid)) {
|
||||
|
@ -2141,7 +2206,7 @@ new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) {
|
|||
}
|
||||
switch(pid->protocol[2] & ~ISDN_PID_FEATURE_MASK) {
|
||||
case ISDN_PID_L2_LAPD_NET:
|
||||
sprintf(nl2->inst.name, "lapdn %x", st->id);
|
||||
sprintf(nl2->inst.name, "lapdn %x", st->id>>8);
|
||||
test_and_set_bit(FLG_LAPD, &nl2->flag);
|
||||
test_and_set_bit(FLG_LAPD_NET, &nl2->flag);
|
||||
test_and_set_bit(FLG_FIXED_TEI, &nl2->flag);
|
||||
|
@ -2159,7 +2224,7 @@ new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) {
|
|||
}
|
||||
break;
|
||||
case ISDN_PID_L2_LAPD:
|
||||
sprintf(nl2->inst.name, "lapd %x", st->id);
|
||||
sprintf(nl2->inst.name, "lapd %x", st->id>>8);
|
||||
test_and_set_bit(FLG_LAPD, &nl2->flag);
|
||||
test_and_set_bit(FLG_MOD128, &nl2->flag);
|
||||
test_and_set_bit(FLG_ORIG, &nl2->flag);
|
||||
|
@ -2182,7 +2247,7 @@ new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) {
|
|||
break;
|
||||
case ISDN_PID_L2_B_X75SLP:
|
||||
test_and_set_bit(FLG_LAPB, &nl2->flag);
|
||||
sprintf(nl2->inst.name, "lapb %x", st->id);
|
||||
sprintf(nl2->inst.name, "lapb %x", st->id >> 8);
|
||||
nl2->window = 7;
|
||||
nl2->maxlen = MAX_DATA_SIZE;
|
||||
nl2->T200 = 1000;
|
||||
|
@ -2230,7 +2295,9 @@ new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) {
|
|||
|
||||
mISDN_FsmInitTimer(&nl2->l2m, &nl2->t200);
|
||||
mISDN_FsmInitTimer(&nl2->l2m, &nl2->t203);
|
||||
spin_lock_irqsave(&isdnl2.lock, flags);
|
||||
list_add_tail(&nl2->list, &isdnl2.ilist);
|
||||
spin_unlock_irqrestore(&isdnl2.lock, flags);
|
||||
err = isdnl2.ctrl(&nl2->inst, MGR_NEWENTITY | REQUEST, NULL);
|
||||
if (err) {
|
||||
printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%d)\n",
|
||||
|
@ -2253,16 +2320,14 @@ new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) {
|
|||
stp.down_headerlen = l2headersize(nl2, 0);
|
||||
isdnl2.ctrl(st, MGR_ADDSTPARA | REQUEST, &stp);
|
||||
}
|
||||
if (newl2)
|
||||
*newl2 = nl2;
|
||||
return(err);
|
||||
}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
static int
|
||||
clone_l2(layer2_t *l2, mISDNinstance_t **new_ip) {
|
||||
int err;
|
||||
layer2_t *nl2;
|
||||
mISDNif_t *nif;
|
||||
mISDNstack_t *st;
|
||||
|
||||
if (!l2)
|
||||
|
@ -2272,19 +2337,11 @@ clone_l2(layer2_t *l2, mISDNinstance_t **new_ip) {
|
|||
st = (mISDNstack_t *)*new_ip;
|
||||
if (!st)
|
||||
return(-EINVAL);
|
||||
if (!l2->inst.down.peer)
|
||||
return(-EINVAL);
|
||||
if (!(nif = kmalloc(sizeof(mISDNif_t), GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "clone l2 no if mem\n");
|
||||
return(-ENOMEM);
|
||||
}
|
||||
err = new_l2(st, &l2->inst.pid, &nl2);
|
||||
if (err) {
|
||||
kfree(nif);
|
||||
printk(KERN_ERR "clone l2 failed err(%d)\n", err);
|
||||
return(err);
|
||||
}
|
||||
memset(nif, 0, sizeof(mISDNif_t));
|
||||
nl2->cloneif = nif;
|
||||
nif->func = l2from_down;
|
||||
nif->fdata = nl2;
|
||||
|
@ -2303,6 +2360,7 @@ clone_l2(layer2_t *l2, mISDNinstance_t **new_ip) {
|
|||
*new_ip = &nl2->inst;
|
||||
return(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
l2_status(layer2_t *l2, status_info_l2_t *si)
|
||||
|
@ -2344,7 +2402,7 @@ l2_status(layer2_t *l2, status_info_l2_t *si)
|
|||
}
|
||||
|
||||
static char MName[] = "ISDNL2";
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
MODULE_AUTHOR("Karsten Keil");
|
||||
#ifdef MODULE_LICENSE
|
||||
|
@ -2358,20 +2416,23 @@ l2_manager(void *data, u_int prim, void *arg) {
|
|||
mISDNinstance_t *inst = data;
|
||||
layer2_t *l2l;
|
||||
int err = -EINVAL;
|
||||
u_long flags;
|
||||
|
||||
if (debug & 0x1000)
|
||||
printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", __FUNCTION__,
|
||||
data, prim, arg);
|
||||
if (!data)
|
||||
return(err);
|
||||
spin_lock_irqsave(&isdnl2.lock, flags);
|
||||
list_for_each_entry(l2l, &isdnl2.ilist, list) {
|
||||
if (&l2l->inst == inst) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&isdnl2.lock, flags);
|
||||
if (prim == (MGR_NEWLAYER | REQUEST))
|
||||
return(new_l2(data, arg, NULL));
|
||||
return(new_l2(data, arg));
|
||||
if (err) {
|
||||
if (debug & 0x1)
|
||||
printk(KERN_WARNING "l2_manager prim(%x) l2 no instance\n", prim);
|
||||
|
@ -2379,13 +2440,14 @@ l2_manager(void *data, u_int prim, void *arg) {
|
|||
}
|
||||
switch(prim) {
|
||||
case MGR_NEWENTITY | CONFIRM:
|
||||
l2l->entity = (int)arg;
|
||||
l2l->entity = (u_long)arg & 0xffffffff;
|
||||
break;
|
||||
case MGR_ADDSTPARA | INDICATION:
|
||||
if (((mISDN_stPara_t *)arg)->maxdatalen)
|
||||
l2l->maxlen = ((mISDN_stPara_t *)arg)->maxdatalen;
|
||||
case MGR_CLRSTPARA | INDICATION:
|
||||
break;
|
||||
#ifdef OBSOLETE
|
||||
case MGR_CLONELAYER | REQUEST:
|
||||
return(clone_l2(l2l, arg));
|
||||
case MGR_CONNECT | REQUEST:
|
||||
|
@ -2405,6 +2467,7 @@ l2_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_DISCONNECT | REQUEST:
|
||||
case MGR_DISCONNECT | INDICATION:
|
||||
return(mISDN_DisConnectIF(inst, arg));
|
||||
#endif
|
||||
case MGR_RELEASE | INDICATION:
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
release_l2(l2l);
|
||||
|
@ -2433,6 +2496,7 @@ int Isdnl2_Init(void)
|
|||
ISDN_PID_L2_DF_PTP;
|
||||
isdnl2.BPROTO.protocol[2] = ISDN_PID_L2_B_X75SLP;
|
||||
isdnl2.own_ctrl = l2_manager;
|
||||
spin_lock_init(&isdnl2.lock);
|
||||
INIT_LIST_HEAD(&isdnl2.ilist);
|
||||
l2fsm.state_count = L2_STATE_COUNT;
|
||||
l2fsm.event_count = L2_EVENT_COUNT;
|
||||
|
|
|
@ -47,7 +47,7 @@ typedef struct _layer2 {
|
|||
int T200, N200, T203;
|
||||
int debug;
|
||||
mISDNinstance_t inst;
|
||||
mISDNif_t *cloneif;
|
||||
// mISDNif_t *cloneif;
|
||||
int next_id;
|
||||
u_int down_id;
|
||||
struct sk_buff *windowar[MAX_WINDOW];
|
||||
|
@ -55,7 +55,6 @@ typedef struct _layer2 {
|
|||
struct sk_buff_head ui_queue;
|
||||
struct sk_buff_head down_queue;
|
||||
struct sk_buff_head tmp_queue;
|
||||
spinlock_t lock;
|
||||
} layer2_t;
|
||||
|
||||
/* l2 status_info */
|
||||
|
|
|
@ -88,14 +88,11 @@ l3_debug(layer3_t *l3, char *fmt, ...)
|
|||
static int
|
||||
l3_newid(layer3_t *l3)
|
||||
{
|
||||
u_long flags;
|
||||
int id;
|
||||
|
||||
spin_lock_irqsave(&l3->lock, flags);
|
||||
id = l3->next_id++;
|
||||
if (id == 0x7fff)
|
||||
l3->next_id = 1;
|
||||
spin_unlock_irqrestore(&l3->lock, flags);
|
||||
id |= (l3->entity << 16);
|
||||
return(id);
|
||||
}
|
||||
|
@ -277,7 +274,6 @@ l3_process_t
|
|||
*new_l3_process(layer3_t *l3, int cr, int n303, u_int id)
|
||||
{
|
||||
l3_process_t *p = NULL;
|
||||
u_long flags;
|
||||
|
||||
if (id == MISDN_ID_ANY) {
|
||||
if (l3->entity == MISDN_ENTITY_NONE) {
|
||||
|
@ -285,7 +281,6 @@ l3_process_t
|
|||
__FUNCTION__, l3->id);
|
||||
return (NULL);
|
||||
}
|
||||
spin_lock_irqsave(&l3->lock, flags);
|
||||
if (l3->pid_cnt == 0x7FFF)
|
||||
l3->pid_cnt = 0;
|
||||
while(l3->pid_cnt <= 0x7FFF) {
|
||||
|
@ -295,7 +290,6 @@ l3_process_t
|
|||
if (!p)
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&l3->lock, flags);
|
||||
if (p) {
|
||||
printk(KERN_WARNING "%s: no free process_id for l3(%x) entity(%x)\n",
|
||||
__FUNCTION__, l3->id, l3->entity);
|
||||
|
@ -320,6 +314,7 @@ l3_process_t
|
|||
p->callref = cr;
|
||||
p->n303 = n303;
|
||||
L3InitTimer(p, &p->timer);
|
||||
L3InitTimer(p, &p->aux_timer);
|
||||
list_add_tail(&p->list, &l3->plist);
|
||||
return (p);
|
||||
};
|
||||
|
@ -369,9 +364,9 @@ mISDN_l3up(l3_process_t *l3p, u_int prim, struct sk_buff *skb)
|
|||
return(-EINVAL);
|
||||
l3 = l3p->l3;
|
||||
if (!skb)
|
||||
err = if_link(&l3->inst.up, prim, l3p->id, 0, NULL, 0);
|
||||
err = mISDN_queue_data(&l3->inst, FLG_MSG_UP, prim, l3p->id, 0, NULL, 0);
|
||||
else
|
||||
err = if_newhead(&l3->inst.up, prim, l3p->id, skb);
|
||||
err = mISDN_queueup_newhead(&l3->inst, 0, prim, l3p->id, skb);
|
||||
return(err);
|
||||
}
|
||||
|
||||
|
@ -380,9 +375,9 @@ l3down(layer3_t *l3, u_int prim, int dinfo, struct sk_buff *skb) {
|
|||
int err = -EINVAL;
|
||||
|
||||
if (!skb)
|
||||
err = if_link(&l3->inst.down, prim, dinfo, 0, NULL, 0);
|
||||
err = mISDN_queue_data(&l3->inst, FLG_MSG_DOWN, prim, dinfo, 0, NULL, 0);
|
||||
else
|
||||
err = if_newhead(&l3->inst.down, prim, dinfo, skb);
|
||||
err = mISDN_queuedown_newhead(&l3->inst, 0, prim, dinfo, skb);
|
||||
return(err);
|
||||
}
|
||||
|
||||
|
@ -554,7 +549,6 @@ init_l3(layer3_t *l3)
|
|||
l3->dummy = NULL;
|
||||
l3->entity = MISDN_ENTITY_NONE;
|
||||
l3->next_id = 1;
|
||||
spin_lock_init(&l3->lock);
|
||||
skb_queue_head_init(&l3->squeue);
|
||||
l3->l3m.fsm = &l3fsm;
|
||||
l3->l3m.state = ST_L3_LC_REL;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define L3_DEB_CHARGE 0x08
|
||||
#define L3_DEB_CHECK 0x10
|
||||
#define L3_DEB_SI 0x20
|
||||
#define L3_DEB_MSG 0x80000000
|
||||
|
||||
#define FLG_L2BLOCK 1
|
||||
#define FLG_PTP 2
|
||||
|
@ -45,6 +46,8 @@ typedef struct _l3_process {
|
|||
u_int id;
|
||||
int bc;
|
||||
int err;
|
||||
int aux_state;
|
||||
L3Timer_t aux_timer;
|
||||
} l3_process_t;
|
||||
|
||||
typedef struct _layer3 {
|
||||
|
@ -64,7 +67,6 @@ typedef struct _layer3 {
|
|||
u_long Flag;
|
||||
mISDNinstance_t inst;
|
||||
struct sk_buff_head squeue;
|
||||
spinlock_t lock;
|
||||
int OrigCallRef;
|
||||
} layer3_t;
|
||||
|
||||
|
|
|
@ -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 <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "channel.h"
|
||||
#include "layer1.h"
|
||||
#include "debug.h"
|
||||
#include <linux/isdn_compat.h>
|
||||
|
||||
#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
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -182,7 +182,6 @@ struct _Controller {
|
|||
__u32 addr;
|
||||
int entity;
|
||||
int next_id;
|
||||
spinlock_t id_lock;
|
||||
u_int debug;
|
||||
int maxplci;
|
||||
Plci_t *plcis;
|
||||
|
@ -273,7 +272,6 @@ struct _Ncci {
|
|||
int savedstate;
|
||||
int window;
|
||||
u_long state;
|
||||
spinlock_t conf_lock;
|
||||
ConfQueue_t xmit_skb_handles[CAPI_MAXDATAWINDOW];
|
||||
struct sk_buff *recv_skb_handles[CAPI_MAXDATAWINDOW];
|
||||
struct sk_buff_head squeue;
|
||||
|
@ -315,7 +313,7 @@ int ControllerReleasePlci(Plci_t *);
|
|||
Application_t *getApplication4Id(Controller_t *, __u16);
|
||||
Plci_t *getPlci4Addr(Controller_t *, __u32);
|
||||
int ControllerL4L3(Controller_t *, u_int, int, struct sk_buff *);
|
||||
int ControllerL3L4(mISDNif_t *, struct sk_buff *);
|
||||
int ControllerL3L4(mISDNinstance_t *, struct sk_buff *);
|
||||
PLInst_t *ControllerSelChannel(Controller_t *, u_int);
|
||||
void ControllerAddSSProcess(Controller_t *, SSProcess_t *);
|
||||
SSProcess_t *getSSProcess4Id(Controller_t *, __u16);
|
||||
|
@ -328,7 +326,7 @@ int ControllerNextId(Controller_t *);
|
|||
int ApplicationConstr(Controller_t *, __u16, capi_register_params *);
|
||||
int ApplicationDestr(Application_t *, int);
|
||||
void ApplicationDebug(Application_t *appl, __u32 level, char *fmt, ...);
|
||||
__u16 ApplicationSendMessage(Application_t *appl, struct sk_buff *skb);
|
||||
void ApplicationSendMessage(Application_t *appl, struct sk_buff *skb);
|
||||
void SendCmsg2Application(Application_t *, _cmsg *);
|
||||
void SendCmsgAnswer2Application(Application_t *, _cmsg *, __u16);
|
||||
void AnswerMessage2Application(Application_t *, struct sk_buff *, __u16);
|
||||
|
@ -366,12 +364,15 @@ void AppPlciDelNCCI(Ncci_t *);
|
|||
void AppPlci_l3l4(AppPlci_t *, int, void *);
|
||||
__u16 AppPlciSendMessage(AppPlci_t *, struct sk_buff *);
|
||||
void AppPlciRelease(AppPlci_t *);
|
||||
int AppPlciFacHoldReq(AppPlci_t *, FacReqParm_t *, FacConfParm_t *);
|
||||
int AppPlciFacRetrieveReq(AppPlci_t *, FacReqParm_t *, FacConfParm_t *);
|
||||
int AppPlciFacSuspendReq(AppPlci_t *, FacReqParm_t *, FacConfParm_t *);
|
||||
int AppPlciFacResumeReq(AppPlci_t *, FacReqParm_t *, FacConfParm_t *);
|
||||
void AppPlciGetCmsg(AppPlci_t *, _cmsg *);
|
||||
Ncci_t *getNCCI4addr(AppPlci_t *, __u32, int);
|
||||
int ConnectB3Request(AppPlci_t *, struct sk_buff *);
|
||||
int AppPlcimISDN_SetIF(AppPlci_t *, u_int, void *);
|
||||
void ConnectB3Request(AppPlci_t *, struct sk_buff *);
|
||||
void DisconnectB3Request(AppPlci_t *, struct sk_buff *);
|
||||
int AppPlcimISDN_Active(AppPlci_t *);
|
||||
|
||||
#define GET_NCCI_EXACT 1
|
||||
#define GET_NCCI_ONLY_PLCI 2
|
||||
|
@ -385,7 +386,7 @@ Ncci_t *ncciConstr(AppPlci_t *);
|
|||
void ncciDestr(Ncci_t *);
|
||||
void ncciApplRelease(Ncci_t *);
|
||||
void ncciDelAppPlci(Ncci_t *);
|
||||
__u16 ncciSendMessage(Ncci_t *, struct sk_buff *);
|
||||
void ncciSendMessage(Ncci_t *, struct sk_buff *);
|
||||
int ncci_l3l4(Ncci_t *, mISDN_head_t *, struct sk_buff *);
|
||||
void ncciGetCmsg(Ncci_t *, _cmsg *);
|
||||
int ncci_l3l4_direct(Ncci_t *, mISDN_head_t *, struct sk_buff *);
|
||||
|
@ -399,30 +400,44 @@ SSProcess_t *SSProcessConstr(Application_t *, __u16, __u32);
|
|||
void SSProcessDestr(SSProcess_t *);
|
||||
int Supplementary_l3l4(Controller_t *, __u32, struct sk_buff *);
|
||||
void SupplementaryFacilityReq(Application_t *, _cmsg *);
|
||||
void SendSSNotificationEvent(AppPlci_t *, u16);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// INFOMASK defines (LISTEN commands)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define CAPI_INFOMASK_CAUSE (0x0001)
|
||||
#define CAPI_INFOMASK_DATETIME (0x0002)
|
||||
#define CAPI_INFOMASK_DISPLAY (0x0004)
|
||||
#define CAPI_INFOMASK_USERUSER (0x0008)
|
||||
#define CAPI_INFOMASK_PROGRESS (0x0010)
|
||||
#define CAPI_INFOMASK_FACILITY (0x0020)
|
||||
//#define CAPI_INFOMASK_CHARGE (0x0040)
|
||||
//#define CAPI_INFOMASK_CALLEDPN (0x0080)
|
||||
#define CAPI_INFOMASK_CHANNELID (0x0100)
|
||||
#define CAPI_INFOMASK_EARLYB3 (0x0200)
|
||||
//#define CAPI_INFOMASK_REDIRECT (0x0400)
|
||||
#define CAPI_INFOMASK_CAUSE 0x0001
|
||||
#define CAPI_INFOMASK_DATETIME 0x0002
|
||||
#define CAPI_INFOMASK_DISPLAY 0x0004
|
||||
#define CAPI_INFOMASK_USERUSER 0x0008
|
||||
#define CAPI_INFOMASK_PROGRESS 0x0010
|
||||
#define CAPI_INFOMASK_FACILITY 0x0020
|
||||
#define CAPI_INFOMASK_CHARGE 0x0040
|
||||
#define CAPI_INFOMASK_CALLEDPN 0x0080
|
||||
#define CAPI_INFOMASK_CHANNELID 0x0100
|
||||
#define CAPI_INFOMASK_EARLYB3 0x0200
|
||||
#define CAPI_INFOMASK_REDIRECT 0x0400
|
||||
/* bit 11 reserved */
|
||||
#define CAPI_INFOMASK_COMPLETE 0x1000
|
||||
/* bit 13-31 reserved */
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Supplementary Services
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define SuppServiceHR 0x00000001
|
||||
#define SuppServiceTP 0x00000002
|
||||
#define SuppServiceECT 0x00000004
|
||||
#define SuppService3PTY 0x00000008
|
||||
#define SuppServiceCF 0x00000010
|
||||
#define mISDNSupportedServices (SuppServiceCF | SuppServiceTP)
|
||||
#define SuppServiceCD 0x00000020
|
||||
#define SuppServiceMCID 0x00000040
|
||||
#define SuppServiceCCBS 0x00000080
|
||||
|
||||
#define mISDNSupportedServices (SuppServiceCD | \
|
||||
SuppServiceCF | \
|
||||
SuppServiceTP | \
|
||||
SuppServiceHR)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// structs for Facillity requests
|
||||
|
@ -456,6 +471,12 @@ struct FacReqCFDeactivate {
|
|||
__u8 *ServedUserNumber;
|
||||
};
|
||||
|
||||
struct FacReqCDeflection {
|
||||
__u16 PresentationAllowed;
|
||||
__u8 *DeflectedToNumber;
|
||||
__u8 *DeflectedToSubaddress;
|
||||
};
|
||||
|
||||
#define FacReqCFInterrogateParameters FacReqCFDeactivate
|
||||
|
||||
struct FacReqCFInterrogateNumbers {
|
||||
|
@ -472,6 +493,7 @@ struct FacReqParm {
|
|||
struct FacReqCFDeactivate CFDeactivate;
|
||||
struct FacReqCFInterrogateParameters CFInterrogateParameters;
|
||||
struct FacReqCFInterrogateNumbers CFInterrogateNumbers;
|
||||
struct FacReqCDeflection CDeflection;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
|
|
@ -65,10 +65,8 @@ __mid_kfree(const void *p)
|
|||
_mid_item_t *mid;
|
||||
u_long flags;
|
||||
|
||||
if (!p) {
|
||||
printk(KERN_ERR "zero pointer kfree at %p", __builtin_return_address(0));
|
||||
if (!p)
|
||||
return;
|
||||
}
|
||||
mid = (_mid_item_t *)((u_char *)p - sizeof(_mid_item_t));
|
||||
spin_lock_irqsave(&memdbg_lock, flags);
|
||||
list_del(&mid->head);
|
||||
|
@ -104,10 +102,8 @@ __mid_vfree(const void *p)
|
|||
_mid_item_t *mid;
|
||||
u_long flags;
|
||||
|
||||
if (!p) {
|
||||
printk(KERN_ERR "zero pointer vfree at %p", __builtin_return_address(0));
|
||||
if (!p)
|
||||
return;
|
||||
}
|
||||
mid = (_mid_item_t *)((u_char *)p - sizeof(_mid_item_t));
|
||||
spin_lock_irqsave(&memdbg_lock, flags);
|
||||
list_del(&mid->head);
|
||||
|
|
|
@ -173,9 +173,9 @@ SKB2Application(Ncci_t *ncci, struct sk_buff *skb)
|
|||
static inline int
|
||||
SKB_l4l3(Ncci_t *ncci, struct sk_buff *skb)
|
||||
{
|
||||
if (!ncci->link || !ncci->link->inst.down.func)
|
||||
if (!ncci->link)
|
||||
return(-ENXIO);
|
||||
return(ncci->link->inst.down.func(&ncci->link->inst.down, skb));
|
||||
return(mISDN_queue_down(&ncci->link->inst, 0, skb));
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -438,15 +438,12 @@ ncci_connect_b3_active_ind(struct FsmInst *fi, int event, void *arg)
|
|||
{
|
||||
Ncci_t *ncci = fi->userdata;
|
||||
int i;
|
||||
u_long flags;
|
||||
|
||||
mISDN_FsmChangeState(fi, ST_NCCI_N_ACT);
|
||||
spin_lock_irqsave(&ncci->conf_lock, flags);
|
||||
for (i = 0; i < CAPI_MAXDATAWINDOW; i++) {
|
||||
ncci->xmit_skb_handles[i].PktId = 0;
|
||||
ncci->recv_skb_handles[i] = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
Send2Application(ncci, arg);
|
||||
}
|
||||
|
||||
|
@ -732,7 +729,7 @@ ncciD_disconnect_b3_ind(struct FsmInst *fi, int event, void *arg)
|
|||
skb_pull(skb, CAPIMSG_BASELEN);
|
||||
skb_trim(skb, 0);
|
||||
skb_put(skb, 4);
|
||||
if_newhead(&ncci->link->inst.down, CAPI_DISCONNECT_B3_RESP, 0, skb);
|
||||
mISDN_queuedown_newhead(&ncci->link->inst, 0, CAPI_DISCONNECT_B3_RESP, 0, skb);
|
||||
ncciDestr(ncci);
|
||||
} else
|
||||
SKB2Application(ncci, arg);
|
||||
|
@ -768,7 +765,7 @@ ncciD_appl_release_disc(struct FsmInst *fi, int event, void *arg)
|
|||
capimsg_setu32(parm, 0, ncci->addr);
|
||||
parm[4] = 0;
|
||||
mISDN_FsmChangeState(fi, ST_NCCI_N_4);
|
||||
if_link(&ncci->link->inst.down, CAPI_DISCONNECT_B3_REQ, 0, 5, parm, 0);
|
||||
mISDN_queue_data(&ncci->link->inst, FLG_MSG_DOWN, CAPI_DISCONNECT_B3_REQ, 0, 5, parm, 0);
|
||||
}
|
||||
|
||||
static struct FsmNode fn_ncciD_list[] =
|
||||
|
@ -831,7 +828,6 @@ ncciConstr(AppPlci_t *aplci)
|
|||
ncci->ncci_m.debug = aplci->plci->contr->debug & CAPI_DBG_NCCI_STATE;
|
||||
ncci->ncci_m.userdata = ncci;
|
||||
ncci->ncci_m.printdebug = ncci_debug;
|
||||
spin_lock_init(&ncci->conf_lock);
|
||||
/* unused NCCI */
|
||||
ncci->addr = aplci->addr;
|
||||
ncci->AppPlci = aplci;
|
||||
|
@ -862,7 +858,6 @@ void
|
|||
ncciDestr(Ncci_t *ncci)
|
||||
{
|
||||
int i;
|
||||
u_long flags;
|
||||
|
||||
capidebug(CAPI_DBG_NCCI, "ncciDestr NCCI %x", ncci->addr);
|
||||
|
||||
|
@ -871,13 +866,11 @@ ncciDestr(Ncci_t *ncci)
|
|||
ncci->contr->ctrl->free_ncci(ncci->contr->ctrl, ncci->appl->ApplId, ncci->addr);
|
||||
#endif
|
||||
/* cleanup data queues */
|
||||
spin_lock_irqsave(&ncci->conf_lock, flags);
|
||||
discard_queue(&ncci->squeue);
|
||||
for (i = 0; i < ncci->window; i++) {
|
||||
if (ncci->xmit_skb_handles[i].PktId)
|
||||
ncci->xmit_skb_handles[i].PktId = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
AppPlciDelNCCI(ncci);
|
||||
ncci_free(ncci);
|
||||
}
|
||||
|
@ -945,7 +938,7 @@ ncciDataInd(Ncci_t *ncci, int pr, struct sk_buff *skb)
|
|||
*((__u16*)(nskb->data+6)) = ncci->appl->MsgId++;
|
||||
*((__u32*)(nskb->data+8)) = ncci->addr;
|
||||
if (sizeof(nskb) == 4) {
|
||||
*((__u32*)(nskb->data+12)) = (__u32)(nskb->data + CAPI_B3_DATA_IND_HEADER_SIZE);
|
||||
*((__u32*)(nskb->data+12)) = (__u32)(((u_long)nskb->data + CAPI_B3_DATA_IND_HEADER_SIZE) & 0xffffffff);
|
||||
*((__u64*)(nskb->data+22)) = 0;
|
||||
} else {
|
||||
*((__u32*)(nskb->data+12)) = 0;
|
||||
|
@ -968,7 +961,6 @@ ncciDataReq(Ncci_t *ncci, struct sk_buff *skb)
|
|||
int i, err;
|
||||
__u16 len, capierr = 0;
|
||||
_cmsg *cmsg;
|
||||
u_long flags;
|
||||
|
||||
len = CAPIMSG_LEN(skb->data);
|
||||
if (len != 22 && len != 30) {
|
||||
|
@ -976,13 +968,11 @@ ncciDataReq(Ncci_t *ncci, struct sk_buff *skb)
|
|||
int_error();
|
||||
goto fail;
|
||||
}
|
||||
spin_lock_irqsave(&ncci->conf_lock, flags);
|
||||
for (i = 0; i < ncci->window; i++) {
|
||||
if (ncci->xmit_skb_handles[i].PktId == 0)
|
||||
break;
|
||||
}
|
||||
if (i == ncci->window) {
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
return(CAPI_SENDQUEUEFULL);
|
||||
}
|
||||
mISDN_HEAD_DINFO(skb) = ControllerNextId(ncci->contr);
|
||||
|
@ -996,7 +986,6 @@ ncciDataReq(Ncci_t *ncci, struct sk_buff *skb)
|
|||
if (test_bit(NCCI_STATE_FCTRL, &ncci->state)) {
|
||||
if (test_and_set_bit(NCCI_STATE_BUSY, &ncci->state)) {
|
||||
skb_queue_tail(&ncci->squeue, skb);
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
return(CAPI_NOERROR);
|
||||
}
|
||||
if (skb_queue_len(&ncci->squeue)) {
|
||||
|
@ -1005,7 +994,6 @@ ncciDataReq(Ncci_t *ncci, struct sk_buff *skb)
|
|||
i = -1;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
err = ncciL4L3(ncci, DL_DATA | REQUEST, mISDN_HEAD_DINFO(skb), 0, NULL, skb);
|
||||
if (!err)
|
||||
return(CAPI_NOERROR);
|
||||
|
@ -1013,7 +1001,6 @@ ncciDataReq(Ncci_t *ncci, struct sk_buff *skb)
|
|||
int_error();
|
||||
skb_push(skb, len);
|
||||
capierr = CAPI_MSGBUSY;
|
||||
spin_lock_irqsave(&ncci->conf_lock, flags);
|
||||
if (i == -1) {
|
||||
for (i = 0; i < ncci->window; i++) {
|
||||
if (ncci->xmit_skb_handles[i].PktId == mISDN_HEAD_DINFO(skb))
|
||||
|
@ -1023,10 +1010,8 @@ ncciDataReq(Ncci_t *ncci, struct sk_buff *skb)
|
|||
int_error();
|
||||
else
|
||||
ncci->xmit_skb_handles[i].PktId = 0;
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
} else {
|
||||
ncci->xmit_skb_handles[i].PktId = 0;
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
return(capierr);
|
||||
}
|
||||
fail:
|
||||
|
@ -1054,15 +1039,12 @@ ncciDataConf(Ncci_t *ncci, int pr, struct sk_buff *skb)
|
|||
{
|
||||
int i;
|
||||
_cmsg *cmsg;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&ncci->conf_lock, flags);
|
||||
for (i = 0; i < ncci->window; i++) {
|
||||
if (ncci->xmit_skb_handles[i].PktId == mISDN_HEAD_DINFO(skb))
|
||||
break;
|
||||
}
|
||||
if (i == ncci->window) {
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
int_error();
|
||||
printk(KERN_DEBUG "%s: dinfo(%x)\n", __FUNCTION__, mISDN_HEAD_DINFO(skb));
|
||||
for (i = 0; i < ncci->window; i++)
|
||||
|
@ -1075,7 +1057,6 @@ ncciDataConf(Ncci_t *ncci, int pr, struct sk_buff *skb)
|
|||
|
||||
cmsg = cmsg_alloc();
|
||||
if (!cmsg) {
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
int_error();
|
||||
return(-ENOMEM);
|
||||
}
|
||||
|
@ -1083,14 +1064,11 @@ ncciDataConf(Ncci_t *ncci, int pr, struct sk_buff *skb)
|
|||
capi_cmsg_header(cmsg, ncci->AppPlci->appl->ApplId, CAPI_DATA_B3, CAPI_CONF,
|
||||
ncci->xmit_skb_handles[i].MsgId, ncci->addr);
|
||||
cmsg->DataHandle = ncci->xmit_skb_handles[i].DataHandle;
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
cmsg->Info = 0;
|
||||
Send2Application(ncci, cmsg);
|
||||
if (test_bit(NCCI_STATE_FCTRL, &ncci->state)) {
|
||||
spin_lock_irqsave(&ncci->conf_lock, flags);
|
||||
if (skb_queue_len(&ncci->squeue)) {
|
||||
skb = skb_dequeue(&ncci->squeue);
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
if (ncciL4L3(ncci, DL_DATA | REQUEST, mISDN_HEAD_DINFO(skb),
|
||||
0, NULL, skb)) {
|
||||
int_error();
|
||||
|
@ -1098,7 +1076,6 @@ ncciDataConf(Ncci_t *ncci, int pr, struct sk_buff *skb)
|
|||
}
|
||||
} else {
|
||||
test_and_clear_bit(NCCI_STATE_BUSY, &ncci->state);
|
||||
spin_unlock_irqrestore(&ncci->conf_lock, flags);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
|
@ -1224,7 +1201,7 @@ ncciGetCmsg(Ncci_t *ncci, _cmsg *cmsg)
|
|||
}
|
||||
}
|
||||
|
||||
__u16
|
||||
void
|
||||
ncciSendMessage(Ncci_t *ncci, struct sk_buff *skb)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1237,40 +1214,44 @@ ncciSendMessage(Ncci_t *ncci, struct sk_buff *skb)
|
|||
break;
|
||||
case -EINVAL:
|
||||
case -ENXIO:
|
||||
return(CAPI_MSGBUSY);
|
||||
int_error();
|
||||
break; /* (CAPI_MSGBUSY) */
|
||||
case -EXFULL:
|
||||
return(CAPI_SENDQUEUEFULL);
|
||||
int_error();
|
||||
break; /* (CAPI_SENDQUEUEFULL) */
|
||||
default:
|
||||
int_errtxt("ncci_l4l3_direct return(%d)", ret);
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
return(CAPI_NOERROR);
|
||||
return;
|
||||
}
|
||||
// we're not using the cmsg for DATA_B3 for performance reasons
|
||||
switch (CAPIMSG_CMD(skb->data)) {
|
||||
case CAPI_DATA_B3_REQ:
|
||||
if (ncci->ncci_m.state == ST_NCCI_N_ACT) {
|
||||
return(ncciDataReq(ncci, skb));
|
||||
ret = ncciDataReq(ncci, skb);
|
||||
if (ret)
|
||||
int_error();
|
||||
} else {
|
||||
AnswerMessage2Application(ncci->appl, skb,
|
||||
CapiMessageNotSupportedInCurrentState);
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
return(CAPI_NOERROR);
|
||||
return;
|
||||
case CAPI_DATA_B3_RESP:
|
||||
ncciDataResp(ncci, skb);
|
||||
return(CAPI_NOERROR);
|
||||
return;
|
||||
}
|
||||
cmsg = cmsg_alloc();
|
||||
if (!cmsg) {
|
||||
int_error();
|
||||
return(CAPI_MSGOSRESOURCEERR);
|
||||
return;
|
||||
}
|
||||
capi_message2cmsg(cmsg, skb->data);
|
||||
ncciGetCmsg(ncci, cmsg);
|
||||
dev_kfree_skb(skb);
|
||||
return(CAPI_NOERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1400,10 +1381,9 @@ ncciL4L3(Ncci_t *ncci, u_int prim, int dtyp, int len, void *arg, struct sk_buff
|
|||
capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dtyp(%x) len(%d) skb(%p)",
|
||||
__FUNCTION__, ncci->addr, prim, dtyp, len, skb);
|
||||
if (skb)
|
||||
return(if_newhead(&ncci->link->inst.down, prim, dtyp, skb));
|
||||
return(mISDN_queuedown_newhead(&ncci->link->inst, 0, prim, dtyp, skb));
|
||||
else
|
||||
return(if_link(&ncci->link->inst.down, prim, dtyp,
|
||||
len, arg, 8));
|
||||
return(mISDN_queue_data(&ncci->link->inst, FLG_MSG_DOWN, prim, dtyp, len, arg, 8));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -80,8 +80,11 @@ int plci_l3l4(Plci_t *plci, int pr, struct sk_buff *skb)
|
|||
case CC_SETUP | INDICATION:
|
||||
plciHandleSetupInd(plci, pr, qi);
|
||||
break;
|
||||
/*
|
||||
// no extra treatment for CC_RELEASE_CR | INDICATION !
|
||||
case CC_RELEASE_CR | INDICATION:
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
list_for_each_safe(item, next, &plci->AppPlcis) {
|
||||
aplci = (AppPlci_t *)item;
|
||||
|
|
|
@ -36,18 +36,13 @@
|
|||
#else
|
||||
#include <linux/isapnp.h>
|
||||
#endif
|
||||
#include "dchannel.h"
|
||||
#include "bchannel.h"
|
||||
#include "channel.h"
|
||||
#include "isac.h"
|
||||
#include "isar.h"
|
||||
#include "layer1.h"
|
||||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define SPIN_DEBUG
|
||||
#define LOCK_STATISTIC
|
||||
#include "hw_lock.h"
|
||||
|
||||
extern const char *CardType[];
|
||||
|
||||
const char *Sedlfax_revision = "$Revision$";
|
||||
|
@ -110,7 +105,16 @@ const char *Sedlbauer_Types[] =
|
|||
|
||||
typedef struct _sedl_fax {
|
||||
struct list_head list;
|
||||
void *pdev;
|
||||
union {
|
||||
#if defined(CONFIG_PNP)
|
||||
#ifdef NEW_ISAPNP
|
||||
struct pnp_dev *pnp;
|
||||
#else
|
||||
struct pci_dev *pnp;
|
||||
#endif
|
||||
#endif
|
||||
struct pci_dev *pci;
|
||||
} dev;
|
||||
u_int subtyp;
|
||||
u_int irq;
|
||||
u_int irqcnt;
|
||||
|
@ -118,28 +122,14 @@ typedef struct _sedl_fax {
|
|||
u_int addr;
|
||||
u_int isac;
|
||||
u_int isar;
|
||||
mISDN_HWlock_t lock;
|
||||
spinlock_t lock;
|
||||
isar_reg_t ir;
|
||||
isac_chip_t isac_hw;
|
||||
isar_hw_t isar_hw[2];
|
||||
dchannel_t dch;
|
||||
bchannel_t bch[2];
|
||||
channel_t dch;
|
||||
channel_t bch[2];
|
||||
} sedl_fax;
|
||||
|
||||
static int lock_dev(void *data, int nowait)
|
||||
{
|
||||
register mISDN_HWlock_t *lock = &((sedl_fax *)data)->lock;
|
||||
|
||||
return(lock_HW(lock, nowait));
|
||||
}
|
||||
|
||||
static void unlock_dev(void *data)
|
||||
{
|
||||
register mISDN_HWlock_t *lock = &((sedl_fax *)data)->lock;
|
||||
|
||||
unlock_HW(lock);
|
||||
}
|
||||
|
||||
static inline u_char
|
||||
readreg(unsigned int ale, unsigned int adr, u_char off)
|
||||
{
|
||||
|
@ -202,25 +192,27 @@ WriteISACfifo(void *p, u_char * data, int size)
|
|||
*/
|
||||
|
||||
static u_char
|
||||
ReadISAR(void *p, int mode, u_char offset)
|
||||
ReadISAR(void *p, u_char offset)
|
||||
{
|
||||
if (mode == 0)
|
||||
return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset));
|
||||
else if (mode == 1)
|
||||
byteout(((sedl_fax *)p)->addr, offset);
|
||||
return(bytein(((sedl_fax *)p)->isar));
|
||||
return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISAR(void *p, int mode, u_char offset, u_char value)
|
||||
WriteISAR(void *p, u_char offset, u_char value)
|
||||
{
|
||||
if (mode == 0)
|
||||
writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset, value);
|
||||
else {
|
||||
if (mode == 1)
|
||||
byteout(((sedl_fax *)p)->addr, offset);
|
||||
byteout(((sedl_fax *)p)->isar, value);
|
||||
}
|
||||
writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset, value);
|
||||
}
|
||||
|
||||
static void
|
||||
ReadISARfifo(void *p, u_char * data, int size)
|
||||
{
|
||||
readfifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, ISAR_MBOX, data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISARfifo(void *p, u_char * data, int size)
|
||||
{
|
||||
writefifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, ISAR_MBOX, data, size);
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -266,53 +258,11 @@ static irqreturn_t
|
|||
speedfax_isa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
sedl_fax *sf = dev_id;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&sf->lock.lock, flags);
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.spin_adr = (void *)0x2001;
|
||||
#endif
|
||||
if (test_and_set_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
|
||||
printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
|
||||
__FUNCTION__, sf->lock.state);
|
||||
#ifdef SPIN_DEBUG
|
||||
printk(KERN_ERR "%s: previous lock:%p\n",
|
||||
__FUNCTION__, sf->lock.busy_adr);
|
||||
#endif
|
||||
#ifdef LOCK_STATISTIC
|
||||
sf->lock.irq_fail++;
|
||||
#endif
|
||||
} else {
|
||||
#ifdef LOCK_STATISTIC
|
||||
sf->lock.irq_ok++;
|
||||
#endif
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.busy_adr = speedfax_isa_interrupt;
|
||||
#endif
|
||||
}
|
||||
|
||||
test_and_set_bit(STATE_FLAG_INIRQ, &sf->lock.state);
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
||||
spin_lock(&sf->lock);
|
||||
sf->irqcnt++;
|
||||
do_sedl_interrupt(sf);
|
||||
spin_lock_irqsave(&sf->lock.lock, flags);
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.spin_adr = (void *)0x2002;
|
||||
#endif
|
||||
if (!test_and_clear_bit(STATE_FLAG_INIRQ, &sf->lock.state)) {
|
||||
}
|
||||
if (!test_and_clear_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
|
||||
printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
|
||||
__FUNCTION__, sf->lock.state);
|
||||
}
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.busy_adr = NULL;
|
||||
sf->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
||||
spin_unlock(&sf->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -320,65 +270,38 @@ static irqreturn_t
|
|||
speedfax_pci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
sedl_fax *sf = dev_id;
|
||||
u_long flags;
|
||||
u_char val;
|
||||
|
||||
spin_lock_irqsave(&sf->lock.lock, flags);
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.spin_adr = (void *)0x3001;
|
||||
#endif
|
||||
spin_lock(&sf->lock);
|
||||
val = bytein(sf->cfg + TIGER_AUX_STATUS);
|
||||
if (val & SEDL_TIGER_IRQ_BIT) { /* for us or shared ? */
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
||||
spin_unlock(&sf->lock);
|
||||
return IRQ_NONE; /* shared */
|
||||
}
|
||||
sf->irqcnt++;
|
||||
if (test_and_set_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
|
||||
printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
|
||||
__FUNCTION__, sf->lock.state);
|
||||
#ifdef SPIN_DEBUG
|
||||
printk(KERN_ERR "%s: previous lock:%p\n",
|
||||
__FUNCTION__, sf->lock.busy_adr);
|
||||
#endif
|
||||
#ifdef LOCK_STATISTIC
|
||||
sf->lock.irq_fail++;
|
||||
#endif
|
||||
} else {
|
||||
#ifdef LOCK_STATISTIC
|
||||
sf->lock.irq_ok++;
|
||||
#endif
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.busy_adr = speedfax_pci_interrupt;
|
||||
#endif
|
||||
}
|
||||
|
||||
test_and_set_bit(STATE_FLAG_INIRQ, &sf->lock.state);
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.spin_adr= NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
||||
do_sedl_interrupt(sf);
|
||||
spin_lock_irqsave(&sf->lock.lock, flags);
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.spin_adr = (void *)0x3002;
|
||||
#endif
|
||||
if (!test_and_clear_bit(STATE_FLAG_INIRQ, &sf->lock.state)) {
|
||||
}
|
||||
if (!test_and_clear_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
|
||||
printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
|
||||
__FUNCTION__, sf->lock.state);
|
||||
}
|
||||
#ifdef SPIN_DEBUG
|
||||
sf->lock.busy_adr = NULL;
|
||||
sf->lock.spin_adr = NULL;
|
||||
#endif
|
||||
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
||||
spin_unlock(&sf->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
enable_hwirq(sedl_fax *sf)
|
||||
{
|
||||
WriteISAC(sf, ISAC_MASK, 0);
|
||||
WriteISAR(sf, ISAR_IRQBIT, ISAR_IRQMSK);
|
||||
if (sf->subtyp != SEDL_SPEEDFAX_ISA)
|
||||
byteout(sf->cfg + TIGER_AUX_IRQMASK, SEDL_TIGER_IRQ_BIT);
|
||||
}
|
||||
|
||||
static void
|
||||
disable_hwirq(sedl_fax *sf)
|
||||
{
|
||||
WriteISAC(sf, ISAC_MASK, 0xFF);
|
||||
WriteISAR(sf, ISAR_IRQBIT, 0);
|
||||
if (sf->subtyp != SEDL_SPEEDFAX_ISA)
|
||||
byteout(sf->cfg + TIGER_AUX_IRQMASK, 0);
|
||||
}
|
||||
|
||||
void
|
||||
release_sedlbauer(sedl_fax *sf)
|
||||
{
|
||||
|
@ -386,8 +309,6 @@ release_sedlbauer(sedl_fax *sf)
|
|||
|
||||
if (sf->subtyp == SEDL_SPEEDFAX_ISA)
|
||||
bytecnt = 16;
|
||||
else
|
||||
byteout(sf->cfg + TIGER_AUX_IRQMASK, 0);
|
||||
if (sf->cfg)
|
||||
release_region(sf->cfg, bytecnt);
|
||||
}
|
||||
|
@ -416,6 +337,7 @@ reset_speedfax(sedl_fax *sf)
|
|||
static int init_card(sedl_fax *sf)
|
||||
{
|
||||
int cnt = 3;
|
||||
u_long flags;
|
||||
u_int shared = SA_SHIRQ;
|
||||
void *irq_func = speedfax_pci_interrupt;
|
||||
|
||||
|
@ -423,13 +345,12 @@ static int init_card(sedl_fax *sf)
|
|||
irq_func = speedfax_isa_interrupt;
|
||||
shared = 0;
|
||||
}
|
||||
lock_dev(sf, 0);
|
||||
if (request_irq(sf->irq, irq_func, shared, "speedfax", sf)) {
|
||||
printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
|
||||
sf->irq);
|
||||
unlock_dev(sf);
|
||||
return(-EIO);
|
||||
}
|
||||
spin_lock_irqsave(&sf->lock, flags);
|
||||
while (cnt) {
|
||||
int ret;
|
||||
|
||||
|
@ -440,13 +361,10 @@ static int init_card(sedl_fax *sf)
|
|||
}
|
||||
init_isar(&sf->bch[0]);
|
||||
init_isar(&sf->bch[1]);
|
||||
if (sf->subtyp != SEDL_SPEEDFAX_ISA)
|
||||
byteout(sf->cfg + TIGER_AUX_IRQMASK, SEDL_TIGER_IRQ_BIT);
|
||||
WriteISAC(sf, ISAC_MASK, 0);
|
||||
WriteISAR(sf, 0, ISAR_IRQBIT, ISAR_IRQMSK);
|
||||
enable_hwirq(sf);
|
||||
/* RESET Receiver and Transmitter */
|
||||
WriteISAC(sf, ISAC_CMDR, 0x41);
|
||||
unlock_dev(sf);
|
||||
spin_unlock_irqrestore(&sf->lock, flags);
|
||||
current->state = TASK_UNINTERRUPTIBLE;
|
||||
/* Timeout 10ms */
|
||||
schedule_timeout((10*HZ)/1000);
|
||||
|
@ -459,7 +377,7 @@ static int init_card(sedl_fax *sf)
|
|||
if (cnt == 1) {
|
||||
return (-EIO);
|
||||
} else {
|
||||
lock_dev(sf, 0);
|
||||
spin_lock_irqsave(&sf->lock, flags);
|
||||
reset_speedfax(sf);
|
||||
cnt--;
|
||||
}
|
||||
|
@ -467,7 +385,7 @@ static int init_card(sedl_fax *sf)
|
|||
return(0);
|
||||
}
|
||||
}
|
||||
unlock_dev(sf);
|
||||
spin_unlock_irqrestore(&sf->lock, flags);
|
||||
return(-EIO);
|
||||
}
|
||||
|
||||
|
@ -476,18 +394,28 @@ static int init_card(sedl_fax *sf)
|
|||
#define MODULE_PARM_T "1-4i"
|
||||
static int sedl_cnt;
|
||||
static mISDNobject_t speedfax;
|
||||
static int debug;
|
||||
static u_int protocol[MAX_CARDS];
|
||||
static int layermask[MAX_CARDS];
|
||||
static uint debug;
|
||||
static uint protocol_num;
|
||||
static uint layermask_num;
|
||||
static uint protocol[MAX_CARDS];
|
||||
static uint layermask[MAX_CARDS];
|
||||
|
||||
#ifdef MODULE
|
||||
MODULE_AUTHOR("Karsten Keil");
|
||||
#ifdef MODULE_LICENSE
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
MODULE_PARM(debug, "1i");
|
||||
MODULE_PARM(protocol, MODULE_PARM_T);
|
||||
MODULE_PARM(layermask, MODULE_PARM_T);
|
||||
module_param (debug, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC (debug, "sedlfax debug mask");
|
||||
#ifdef OLD_MODULE_PARAM_ARRAY
|
||||
module_param_array(protocol, uint, protocol_num, S_IRUGO | S_IWUSR);
|
||||
module_param_array(layermask, uint, layermask_num, S_IRUGO | S_IWUSR);
|
||||
#else
|
||||
module_param_array(protocol, uint, &protocol_num, S_IRUGO | S_IWUSR);
|
||||
module_param_array(layermask, uint, &layermask_num, S_IRUGO | S_IWUSR);
|
||||
#endif
|
||||
MODULE_PARM_DESC (protocol, "sedlfax protcol (DSS1 := 2)");
|
||||
MODULE_PARM_DESC(layermask, "sedlfax layer mask");
|
||||
#endif
|
||||
|
||||
static char SpeedfaxName[] = "Speedfax";
|
||||
|
@ -495,7 +423,8 @@ static char SpeedfaxName[] = "Speedfax";
|
|||
int
|
||||
setup_speedfax(sedl_fax *sf)
|
||||
{
|
||||
int bytecnt, ver;
|
||||
int bytecnt, ver;
|
||||
u_long flags;
|
||||
|
||||
bytecnt = (sf->subtyp == SEDL_SPEEDFAX_ISA) ? 16 : 256;
|
||||
if (!request_region(sf->cfg, bytecnt, (sf->subtyp == SEDL_SPEEDFAX_ISA) ? "sedl PnP" : "sedl PCI")) {
|
||||
|
@ -534,19 +463,18 @@ setup_speedfax(sedl_fax *sf)
|
|||
sf->isar_hw[1].reg = &sf->ir;
|
||||
sf->bch[0].hw = &sf->isar_hw[0];
|
||||
sf->bch[1].hw = &sf->isar_hw[1];
|
||||
sf->bch[0].Read_Reg = &ReadISAR;
|
||||
sf->bch[0].Write_Reg = &WriteISAR;
|
||||
sf->bch[1].Read_Reg = &ReadISAR;
|
||||
sf->bch[1].Write_Reg = &WriteISAR;
|
||||
lock_dev(sf, 0);
|
||||
#ifdef SPIN_DEBUG
|
||||
printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &sf->lock.spin_adr, sf->lock.spin_adr);
|
||||
printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &sf->lock.busy_adr, sf->lock.busy_adr);
|
||||
#endif
|
||||
writereg(sf->addr, sf->isar, ISAR_IRQBIT, 0);
|
||||
writereg(sf->addr, sf->isac, ISAC_MASK, 0xFF);
|
||||
sf->bch[0].read_reg = &ReadISAR;
|
||||
sf->bch[0].write_reg = &WriteISAR;
|
||||
sf->bch[0].read_fifo = &ReadISARfifo;
|
||||
sf->bch[0].write_fifo = &WriteISARfifo;
|
||||
sf->bch[1].read_reg = &ReadISAR;
|
||||
sf->bch[1].write_reg = &WriteISAR;
|
||||
sf->bch[1].read_fifo = &ReadISARfifo;
|
||||
sf->bch[1].write_fifo = &WriteISARfifo;
|
||||
spin_lock_irqsave(&sf->lock, flags);
|
||||
disable_hwirq(sf);
|
||||
ver = ISARVersion(&sf->bch[0], "Sedlbauer:");
|
||||
unlock_dev(sf);
|
||||
spin_unlock_irqrestore(&sf->lock, flags);
|
||||
if (ver < 0) {
|
||||
printk(KERN_WARNING
|
||||
"Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
|
||||
|
@ -558,37 +486,33 @@ setup_speedfax(sedl_fax *sf)
|
|||
|
||||
static void
|
||||
release_card(sedl_fax *card) {
|
||||
u_long flags;
|
||||
|
||||
#ifdef LOCK_STATISTIC
|
||||
printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n",
|
||||
card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq);
|
||||
printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
|
||||
card->lock.irq_ok, card->lock.irq_fail);
|
||||
#endif
|
||||
lock_dev(card, 0);
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
disable_hwirq(card);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
free_irq(card->irq, card);
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
free_isar(&card->bch[1]);
|
||||
free_isar(&card->bch[0]);
|
||||
mISDN_isac_free(&card->dch);
|
||||
WriteISAR(card, 0, ISAR_IRQBIT, 0);
|
||||
WriteISAC(card, ISAC_MASK, 0xFF);
|
||||
reset_speedfax(card);
|
||||
WriteISAR(card, 0, ISAR_IRQBIT, 0);
|
||||
WriteISAC(card, ISAC_MASK, 0xFF);
|
||||
release_sedlbauer(card);
|
||||
mISDN_free_bch(&card->bch[1]);
|
||||
mISDN_free_bch(&card->bch[0]);
|
||||
mISDN_free_dch(&card->dch);
|
||||
speedfax.ctrl(card->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &card->dch.inst.up);
|
||||
mISDN_freechannel(&card->bch[1]);
|
||||
mISDN_freechannel(&card->bch[0]);
|
||||
mISDN_freechannel(&card->dch);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
speedfax.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
spin_lock_irqsave(&speedfax.lock, flags);
|
||||
list_del(&card->list);
|
||||
unlock_dev(card);
|
||||
spin_unlock_irqrestore(&speedfax.lock, flags);
|
||||
if (card->subtyp == SEDL_SPEEDFAX_ISA) {
|
||||
pnp_disable_dev(card->pdev);
|
||||
pnp_set_drvdata(card->pdev, NULL);
|
||||
#if defined(CONFIG_PNP)
|
||||
pnp_disable_dev(card->dev.pnp);
|
||||
pnp_set_drvdata(card->dev.pnp, NULL);
|
||||
#endif
|
||||
} else {
|
||||
pci_disable_device(card->pdev);
|
||||
pci_set_drvdata(card->pdev, NULL);
|
||||
pci_disable_device(card->dev.pci);
|
||||
pci_set_drvdata(card->dev.pci, NULL);
|
||||
}
|
||||
kfree(card);
|
||||
sedl_cnt--;
|
||||
|
@ -600,15 +524,18 @@ speedfax_manager(void *data, u_int prim, void *arg) {
|
|||
mISDNinstance_t *inst=data;
|
||||
int channel = -1;
|
||||
struct sk_buff *skb;
|
||||
u_long flags;
|
||||
|
||||
printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n",
|
||||
__FUNCTION__, data, prim, arg);
|
||||
if (debug & MISDN_DEBUG_MANAGER)
|
||||
printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n",
|
||||
__FUNCTION__, data, prim, arg);
|
||||
if (!data) {
|
||||
MGR_HASPROTOCOL_HANDLER(prim,arg,&speedfax)
|
||||
printk(KERN_ERR "speedfax_manager no data prim %x arg %p\n",
|
||||
prim, arg);
|
||||
return(-EINVAL);
|
||||
}
|
||||
spin_lock_irqsave(&speedfax.lock, flags);
|
||||
list_for_each_entry(card, &speedfax.ilist, list) {
|
||||
if (&card->dch.inst == inst) {
|
||||
channel = 2;
|
||||
|
@ -623,44 +550,42 @@ speedfax_manager(void *data, u_int prim, void *arg) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&speedfax.lock, flags);
|
||||
if (channel<0) {
|
||||
printk(KERN_ERR "speedfax_manager no channel data %p prim %x arg %p\n",
|
||||
data, prim, arg);
|
||||
return(-EINVAL);
|
||||
}
|
||||
if (debug & MISDN_DEBUG_MANAGER)
|
||||
printk(KERN_DEBUG "%s: channel %d\n", __FUNCTION__, channel);
|
||||
switch(prim) {
|
||||
case MGR_REGLAYER | CONFIRM:
|
||||
if (channel == 2)
|
||||
dch_set_para(&card->dch, &inst->st->para);
|
||||
mISDN_setpara(&card->dch, &inst->st->para);
|
||||
else
|
||||
bch_set_para(&card->bch[channel], &inst->st->para);
|
||||
mISDN_setpara(&card->bch[channel], &inst->st->para);
|
||||
break;
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
if (channel == 2) {
|
||||
inst->down.fdata = &card->dch;
|
||||
if ((skb = create_link_skb(PH_CONTROL | REQUEST,
|
||||
HW_DEACTIVATE, 0, NULL, 0))) {
|
||||
if (mISDN_ISAC_l1hw(&inst->down, skb))
|
||||
if ((skb = create_link_skb(PH_CONTROL | REQUEST,
|
||||
HW_DEACTIVATE, 0, NULL, 0))) {
|
||||
if (channel == 2) {
|
||||
if (mISDN_ISAC_l1hw(inst, skb))
|
||||
dev_kfree_skb(skb);
|
||||
} else {
|
||||
if (isar_down(inst, skb))
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
} else {
|
||||
inst->down.fdata = &card->bch[channel];
|
||||
if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST,
|
||||
0, 0, NULL, 0))) {
|
||||
if (isar_down(&inst->down, skb))
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
speedfax.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
} else
|
||||
printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__);
|
||||
speedfax.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
break;
|
||||
case MGR_CLRSTPARA | INDICATION:
|
||||
arg = NULL;
|
||||
case MGR_ADDSTPARA | INDICATION:
|
||||
if (channel == 2)
|
||||
dch_set_para(&card->dch, arg);
|
||||
mISDN_setpara(&card->dch, arg);
|
||||
else
|
||||
bch_set_para(&card->bch[channel], arg);
|
||||
mISDN_setpara(&card->bch[channel], arg);
|
||||
break;
|
||||
case MGR_RELEASE | INDICATION:
|
||||
if (channel == 2) {
|
||||
|
@ -669,6 +594,7 @@ speedfax_manager(void *data, u_int prim, void *arg) {
|
|||
speedfax.refcnt--;
|
||||
}
|
||||
break;
|
||||
#ifdef OBSOLETE
|
||||
case MGR_CONNECT | REQUEST:
|
||||
return(mISDN_ConnectIF(inst, arg));
|
||||
case MGR_SETIF | REQUEST:
|
||||
|
@ -680,6 +606,7 @@ speedfax_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_DISCONNECT | REQUEST:
|
||||
case MGR_DISCONNECT | INDICATION:
|
||||
return(mISDN_DisConnectIF(inst, arg));
|
||||
#endif
|
||||
case MGR_LOADFIRM | REQUEST:
|
||||
{
|
||||
struct firm {
|
||||
|
@ -694,20 +621,20 @@ speedfax_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_LOADFIRM | CONFIRM:
|
||||
speedfax.ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL);
|
||||
break;
|
||||
case MGR_SETSTACK | CONFIRM:
|
||||
case MGR_SETSTACK | INDICATION:
|
||||
if ((channel!=2) && (inst->pid.global == 2)) {
|
||||
inst->down.fdata = &card->bch[channel];
|
||||
// inst->down.fdata = &card->bch[channel];
|
||||
if ((skb = create_link_skb(PH_ACTIVATE | REQUEST,
|
||||
0, 0, NULL, 0))) {
|
||||
if (isar_down(&inst->down, skb))
|
||||
if (isar_down(inst, skb))
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
if ((inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) ||
|
||||
(inst->pid.protocol[2] == ISDN_PID_L2_B_TRANSDTMF))
|
||||
if_link(&inst->up, DL_ESTABLISH | INDICATION,
|
||||
mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION,
|
||||
0, 0, NULL, 0);
|
||||
else
|
||||
if_link(&inst->up, PH_ACTIVATE | INDICATION,
|
||||
mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION,
|
||||
0, 0, NULL, 0);
|
||||
}
|
||||
break;
|
||||
|
@ -724,40 +651,55 @@ static int __devinit setup_instance(sedl_fax *card)
|
|||
{
|
||||
int i, err;
|
||||
mISDN_pid_t pid;
|
||||
struct device *dev;
|
||||
u_long flags;
|
||||
|
||||
if (sedl_cnt >= MAX_CARDS) {
|
||||
kfree(card);
|
||||
return(-EINVAL);
|
||||
}
|
||||
if (card->subtyp == SEDL_SPEEDFAX_ISA) {
|
||||
#if defined(CONFIG_PNP)
|
||||
dev = &card->dev.pnp->dev;
|
||||
#else
|
||||
dev = NULL;
|
||||
#endif
|
||||
} else {
|
||||
dev = &card->dev.pci->dev;
|
||||
}
|
||||
spin_lock_irqsave(&speedfax.lock, flags);
|
||||
list_add_tail(&card->list, &speedfax.ilist);
|
||||
spin_unlock_irqrestore(&speedfax.lock, flags);
|
||||
card->dch.debug = debug;
|
||||
lock_HW_init(&card->lock);
|
||||
card->dch.inst.lock = lock_dev;
|
||||
card->dch.inst.unlock = unlock_dev;
|
||||
spin_lock_init(&card->lock);
|
||||
card->dch.inst.hwlock = &card->lock;
|
||||
card->dch.inst.pid.layermask = ISDN_LAYER(0);
|
||||
card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
|
||||
mISDN_init_instance(&card->dch.inst, &speedfax, card);
|
||||
card->dch.inst.class_dev.dev = dev;
|
||||
mISDN_init_instance(&card->dch.inst, &speedfax, card, mISDN_ISAC_l1hw);
|
||||
sprintf(card->dch.inst.name, "SpeedFax%d", sedl_cnt+1);
|
||||
mISDN_set_dchannel_pid(&pid, protocol[sedl_cnt], layermask[sedl_cnt]);
|
||||
mISDN_init_dch(&card->dch);
|
||||
mISDN_initchannel(&card->dch, MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1);
|
||||
for (i=0; i<2; i++) {
|
||||
card->bch[i].channel = i;
|
||||
mISDN_init_instance(&card->bch[i].inst, &speedfax, card);
|
||||
mISDN_init_instance(&card->bch[i].inst, &speedfax, card, isar_down);
|
||||
card->bch[i].inst.pid.layermask = ISDN_LAYER(0);
|
||||
card->bch[i].inst.lock = lock_dev;
|
||||
card->bch[i].inst.unlock = unlock_dev;
|
||||
card->bch[i].inst.hwlock = &card->lock;
|
||||
card->bch[i].debug = debug;
|
||||
card->bch[i].inst.class_dev.dev = dev;
|
||||
sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1);
|
||||
mISDN_init_bch(&card->bch[i]);
|
||||
mISDN_initchannel(&card->bch[i], MSK_INIT_BCHANNEL, MAX_DATA_MEM);
|
||||
}
|
||||
printk(KERN_DEBUG "sfax card %p dch %p bch1 %p bch2 %p\n",
|
||||
card, &card->dch, &card->bch[0], &card->bch[1]);
|
||||
err = setup_speedfax(card);
|
||||
if (err) {
|
||||
mISDN_free_dch(&card->dch);
|
||||
mISDN_free_bch(&card->bch[1]);
|
||||
mISDN_free_bch(&card->bch[0]);
|
||||
mISDN_freechannel(&card->dch);
|
||||
mISDN_freechannel(&card->bch[1]);
|
||||
mISDN_freechannel(&card->bch[0]);
|
||||
spin_lock_irqsave(&speedfax.lock, flags);
|
||||
list_del(&card->list);
|
||||
spin_unlock_irqrestore(&speedfax.lock, flags);
|
||||
kfree(card);
|
||||
return(err);
|
||||
}
|
||||
|
@ -767,6 +709,7 @@ static int __devinit setup_instance(sedl_fax *card)
|
|||
release_card(card);
|
||||
return(err);
|
||||
}
|
||||
speedfax.ctrl(card->dch.inst.st, MGR_STOPSTACK | REQUEST, NULL);
|
||||
for (i=0; i<2; i++) {
|
||||
err = speedfax.ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst);
|
||||
if (err) {
|
||||
|
@ -786,6 +729,7 @@ static int __devinit setup_instance(sedl_fax *card)
|
|||
speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
|
||||
return(err);
|
||||
}
|
||||
speedfax.ctrl(card->dch.inst.st, MGR_STARTSTACK | REQUEST, NULL);
|
||||
printk(KERN_INFO "SpeedFax %d cards installed\n", sedl_cnt);
|
||||
return(0);
|
||||
}
|
||||
|
@ -800,7 +744,7 @@ static int __devinit sedlpci_probe(struct pci_dev *pdev, const struct pci_device
|
|||
return(err);
|
||||
}
|
||||
memset(card, 0, sizeof(sedl_fax));
|
||||
card->pdev = pdev;
|
||||
card->dev.pci = pdev;
|
||||
if (PCI_SUBVENDOR_SPEEDFAX_PYRAMID == pdev->subsystem_vendor)
|
||||
card->subtyp = SEDL_SPEEDFAX_PYRAMID;
|
||||
else
|
||||
|
@ -842,7 +786,7 @@ static int __devinit sedlpnp_probe(struct pci_dev *pdev, const struct isapnp_dev
|
|||
}
|
||||
memset(card, 0, sizeof(sedl_fax));
|
||||
card->subtyp = SEDL_SPEEDFAX_ISA;
|
||||
card->pdev = pdev;
|
||||
card->dev.pnp = pdev;
|
||||
pnp_disable_dev(pdev);
|
||||
err = pnp_activate_dev(pdev);
|
||||
if (err<0) {
|
||||
|
@ -945,6 +889,7 @@ static int __init Speedfax_init(void)
|
|||
#ifdef MODULE
|
||||
speedfax.owner = THIS_MODULE;
|
||||
#endif
|
||||
spin_lock_init(&speedfax.lock);
|
||||
INIT_LIST_HEAD(&speedfax.ilist);
|
||||
speedfax.name = SpeedfaxName;
|
||||
speedfax.own_ctrl = speedfax_manager;
|
||||
|
@ -989,7 +934,9 @@ static int __init Speedfax_init(void)
|
|||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if defined(CONFIG_PNP)
|
||||
out_unregister_pci:
|
||||
#endif
|
||||
pci_unregister_driver(&sedlpci_driver);
|
||||
out:
|
||||
return err;
|
||||
|
|
|
@ -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 <linux/vmalloc.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/in.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "socket.h"
|
||||
#include <linux/isdn_compat.h>
|
||||
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,7 @@
|
|||
#include "asn1_enc.h"
|
||||
#include "dss1.h"
|
||||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define T_ACTIVATE 4000
|
||||
#define T_DEACTIVATE 4000
|
||||
|
@ -27,7 +28,7 @@ encodeInvokeComponentHead(__u8 *p)
|
|||
static void
|
||||
encodeInvokeComponentLength(__u8 *msg, __u8 *p)
|
||||
{
|
||||
msg[3] = p - &msg[5];
|
||||
msg[3] = p - &msg[4];
|
||||
msg[0] = p - &msg[1];
|
||||
}
|
||||
|
||||
|
@ -36,7 +37,6 @@ static int
|
|||
SSProcess_L4L3(SSProcess_t *spc, __u32 prim, struct sk_buff *skb) {
|
||||
int err;
|
||||
|
||||
// FIXME err = ControllerL4L3(contr, prim, contr->addr | DUMMY_CR_FLAG, skb);
|
||||
err = ControllerL4L3(spc->contr, prim, MISDN_ID_DUMMY, skb);
|
||||
if (err)
|
||||
dev_kfree_skb(skb);
|
||||
|
@ -175,6 +175,22 @@ static __inline__ int capiGetFacReqCFInterrogateNumbers(__u8 *p, __u8 *_end, str
|
|||
return(len + 1);
|
||||
}
|
||||
|
||||
static __inline__ int capiGetFacReqCD(__u8 *p, __u8 *_end, struct FacReqCDeflection *CD)
|
||||
{
|
||||
int len = *p++;
|
||||
int ret;
|
||||
__u8 *end = p + len;
|
||||
|
||||
if (end > _end) return -1;
|
||||
|
||||
CAPI_GET(capiGetWord, &CD->PresentationAllowed);
|
||||
CAPI_GET(capiGetStruct, &CD->DeflectedToNumber);
|
||||
CAPI_GET(capiGetStruct, &CD->DeflectedToSubaddress);
|
||||
|
||||
if (p != end) return -1;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
|
||||
static __inline__ int capiGetFacReqParm(__u8 *p, struct FacReqParm *facReqParm)
|
||||
{
|
||||
|
@ -209,6 +225,9 @@ static __inline__ int capiGetFacReqParm(__u8 *p, struct FacReqParm *facReqParm)
|
|||
case 0x000c: // CF Interrogate Numbers
|
||||
CAPI_GET(capiGetFacReqCFInterrogateNumbers, &facReqParm->u.CFInterrogateNumbers);
|
||||
break;
|
||||
case 0x000d: // CD
|
||||
CAPI_GET(capiGetFacReqCD, &facReqParm->u.CDeflection);
|
||||
break;
|
||||
default:
|
||||
return(len + 1);
|
||||
}
|
||||
|
@ -255,7 +274,7 @@ FacCFInterrogateParameters(Application_t *appl, FacReqParm_t *facReqParm, FacCon
|
|||
|
||||
p = encodeInvokeComponentHead(sspc->buf);
|
||||
p += encodeInt(p, sspc->invokeId);
|
||||
p += encodeInt(p, 0x0b); // interrogationDiversion
|
||||
p += encodeInt(p, 11); // interrogationDiversion
|
||||
p += encodeInterrogationDiversion(p, &facReqParm->u.CFInterrogateParameters);
|
||||
encodeInvokeComponentLength(sspc->buf, p);
|
||||
mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
|
||||
|
@ -285,7 +304,7 @@ FacCFInterrogateNumbers(Application_t *appl, FacReqParm_t *facReqParm, FacConfPa
|
|||
|
||||
p = encodeInvokeComponentHead(sspc->buf);
|
||||
p += encodeInt(p, sspc->invokeId);
|
||||
p += encodeInt(p, 0x11); // InterrogateServedUserNumbers
|
||||
p += encodeInt(p, 17); // InterrogateServedUserNumbers
|
||||
encodeInvokeComponentLength(sspc->buf, p);
|
||||
mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
|
||||
SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb);
|
||||
|
@ -311,7 +330,7 @@ FacCFActivate(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facC
|
|||
}
|
||||
p = encodeInvokeComponentHead(sspc->buf);
|
||||
p += encodeInt(p, sspc->invokeId);
|
||||
p += encodeInt(p, 0x07); // activationDiversion
|
||||
p += encodeInt(p, 7); // activationDiversion
|
||||
p += encodeActivationDiversion(p, &facReqParm->u.CFActivate);
|
||||
encodeInvokeComponentLength(sspc->buf, p);
|
||||
mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
|
||||
|
@ -338,7 +357,7 @@ FacCFDeactivate(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *fa
|
|||
}
|
||||
p = encodeInvokeComponentHead(sspc->buf);
|
||||
p += encodeInt(p, sspc->invokeId);
|
||||
p += encodeInt(p, 0x08); // dectivationDiversion
|
||||
p += encodeInt(p, 8); // dectivationDiversion
|
||||
p += encodeDeactivationDiversion(p, &facReqParm->u.CFDeactivate);
|
||||
encodeInvokeComponentLength(sspc->buf, p);
|
||||
mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
|
||||
|
@ -350,6 +369,33 @@ FacCFDeactivate(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *fa
|
|||
return CapiSuccess;
|
||||
}
|
||||
|
||||
static int
|
||||
FacCDeflection(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
|
||||
{
|
||||
SSProcess_t *sspc;
|
||||
struct sk_buff *skb = mISDN_alloc_l3msg(260, MT_FACILITY);
|
||||
__u8 *p;
|
||||
|
||||
if (!skb)
|
||||
return CAPI_MSGOSRESOURCEERR;
|
||||
sspc = SSProcessConstr(aplci->appl, facReqParm->Function, facReqParm->u.CFActivate.Handle);
|
||||
if (!sspc) {
|
||||
kfree_skb(skb);
|
||||
return CAPI_MSGOSRESOURCEERR;
|
||||
}
|
||||
p = encodeInvokeComponentHead(sspc->buf);
|
||||
p += encodeInt(p, sspc->invokeId);
|
||||
p += encodeInt(p, 13); // Calldefection
|
||||
p += encodeInvokeDeflection(p, &facReqParm->u.CDeflection);
|
||||
encodeInvokeComponentLength(sspc->buf, p);
|
||||
mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
|
||||
if (aplci->plci)
|
||||
plciL4L3(aplci->plci, CC_FACILITY | REQUEST, skb);
|
||||
SSProcessAddTimer(sspc, T_ACTIVATE);
|
||||
facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess;
|
||||
return CapiSuccess;
|
||||
}
|
||||
|
||||
void
|
||||
SupplementaryFacilityReq(Application_t *appl, _cmsg *cmsg)
|
||||
{
|
||||
|
@ -373,6 +419,22 @@ SupplementaryFacilityReq(Application_t *appl, _cmsg *cmsg)
|
|||
case 0x0001: // Listen
|
||||
Info = FacListen(appl, &facReqParm, &facConfParm);
|
||||
break;
|
||||
case 0x0002: // Hold
|
||||
aplci = getAppPlci4addr(appl, cmsg->adr.adrPLCI);
|
||||
if (!aplci) {
|
||||
Info = CapiIllContrPlciNcci;
|
||||
break;
|
||||
}
|
||||
Info = AppPlciFacHoldReq(aplci, &facReqParm, &facConfParm);
|
||||
break;
|
||||
case 0x0003: // Retrieve
|
||||
aplci = getAppPlci4addr(appl, cmsg->adr.adrPLCI);
|
||||
if (!aplci) {
|
||||
Info = CapiIllContrPlciNcci;
|
||||
break;
|
||||
}
|
||||
Info = AppPlciFacRetrieveReq(aplci, &facReqParm, &facConfParm);
|
||||
break;
|
||||
case 0x0004: // Suspend
|
||||
aplci = getAppPlci4addr(appl, cmsg->adr.adrPLCI);
|
||||
if (!aplci) {
|
||||
|
@ -409,6 +471,14 @@ SupplementaryFacilityReq(Application_t *appl, _cmsg *cmsg)
|
|||
case 0x000c: // CF Interrogate Numbers
|
||||
Info = FacCFInterrogateNumbers(appl, &facReqParm, &facConfParm);
|
||||
break;
|
||||
case 0x000d: // CD
|
||||
aplci = getAppPlci4addr(appl, cmsg->adr.adrPLCI);
|
||||
if (!aplci) {
|
||||
Info = CapiIllContrPlciNcci;
|
||||
break;
|
||||
}
|
||||
Info = FacCDeflection(aplci, &facReqParm, &facConfParm);
|
||||
break;
|
||||
default:
|
||||
Info = CapiSuccess;
|
||||
facConfParm.u.Info.SupplementaryServiceInfo = CapiSupplementaryServiceNotSupported;
|
||||
|
@ -457,6 +527,40 @@ SendSSFacilityInd(Application_t *appl, __u32 addr, __u8 *para)
|
|||
SendCmsg2Application(appl, cmsg);
|
||||
}
|
||||
|
||||
void
|
||||
SendSSNotificationEvent(AppPlci_t *aplci, u16 event)
|
||||
{
|
||||
__u8 tmp[4], *p;
|
||||
|
||||
if (!aplci->appl)
|
||||
return;
|
||||
switch (event) {
|
||||
case 0x8000: // user hold
|
||||
case 0x8001: // user retrieve
|
||||
if (!(aplci->appl->NotificationMask & SuppServiceHR))
|
||||
return;
|
||||
break;
|
||||
case 0x8002: // user suspended
|
||||
case 0x8003: // user resumed
|
||||
if (!(aplci->appl->NotificationMask & SuppServiceTP))
|
||||
return;
|
||||
break;
|
||||
case 0x8004: // call is diverting
|
||||
case 0x8005: // diversion activated
|
||||
if (!(aplci->appl->NotificationMask & SuppServiceCF))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
int_errtxt("Event %x not known\n", event);
|
||||
return;
|
||||
}
|
||||
p = &tmp[1];
|
||||
p += capiEncodeWord(p, event); // Suspend/Resume Notification
|
||||
*p++ = 0; // empty struct
|
||||
tmp[0] = p - &tmp[1];
|
||||
SendSSFacilityInd(aplci->appl, aplci->addr, tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
SendSSFacilityInd2All(Controller_t *contr, __u32 nMask, __u8 *para)
|
||||
{
|
||||
|
@ -503,10 +607,10 @@ void SSProcessAddTimer(SSProcess_t *sspc, int msec)
|
|||
}
|
||||
|
||||
|
||||
#if 0
|
||||
#ifdef ASN1_DEBUG
|
||||
void printPublicPartyNumber(struct PublicPartyNumber *publicPartyNumber)
|
||||
{
|
||||
printk("(%d) %s\n", publicPartyNumber->publicTypeOfNumber,
|
||||
printk(KERN_DEBUG "(%d) %s\n", publicPartyNumber->publicTypeOfNumber,
|
||||
publicPartyNumber->numberDigits);
|
||||
}
|
||||
|
||||
|
@ -514,7 +618,7 @@ void printPartyNumber(struct PartyNumber *partyNumber)
|
|||
{
|
||||
switch (partyNumber->type) {
|
||||
case 0:
|
||||
printk("unknown %s\n", partyNumber->p.unknown);
|
||||
printk(KERN_DEBUG "unknown %s\n", partyNumber->p.unknown);
|
||||
break;
|
||||
case 1:
|
||||
printPublicPartyNumber(&partyNumber->p.publicPartyNumber);
|
||||
|
@ -541,63 +645,41 @@ void printAddress(struct Address *address)
|
|||
#endif
|
||||
|
||||
static void
|
||||
SSProcessFacility(Controller_t *contr, Q931_info_t *qi)
|
||||
SSProcessSingleFacility(Controller_t *contr, struct asn1_parm *parm)
|
||||
{
|
||||
Application_t *appl;
|
||||
int ie_len;
|
||||
struct asn1_parm parm;
|
||||
SSProcess_t *sspc;
|
||||
__u8 tmp[256];
|
||||
__u8 *p, *end;
|
||||
__u8 *p, tmp[256];
|
||||
|
||||
if (!qi || !qi->facility) {
|
||||
int_error();
|
||||
return;
|
||||
}
|
||||
p = (__u8 *)qi;
|
||||
p += L3_EXTRA_SIZE + qi->facility;
|
||||
p++;
|
||||
ie_len = *p++;
|
||||
end = p + ie_len;
|
||||
// if (end > skb->data + skb->len) {
|
||||
// int_error();
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (*p++ != 0x91) { // Supplementary Service Applications
|
||||
int_error();
|
||||
return;
|
||||
}
|
||||
ParseComponent(&parm, p, end);
|
||||
switch (parm.comp) {
|
||||
switch (parm->comp) {
|
||||
case invoke:
|
||||
#if 0
|
||||
printk("invokeId %d\n", parm.u.inv.invokeId);
|
||||
printk("operationValue %d\n", parm.u.inv.operationValue);
|
||||
#ifdef ASN1_DEBUG
|
||||
printk("invokeId %d\n", parm->u.inv.invokeId);
|
||||
printk("operationValue %d\n", parm->u.inv.operationValue);
|
||||
#endif
|
||||
switch (parm.u.inv.operationValue) {
|
||||
switch (parm->u.inv.operationValue) {
|
||||
case 0x0009:
|
||||
#if 0
|
||||
printk("procedure %d basicService %d\n", parm.c.inv.o.actNot.procedure,
|
||||
parm.c.inv.o.actNot.basicService);
|
||||
printServedUserNr(&parm.c.inv.o.actNot.servedUserNr);
|
||||
printAddress(&parm.c.inv.o.actNot.address);
|
||||
#ifdef ASN1_DEBUG
|
||||
printk("procedure %d basicService %d\n", parm->u.inv.o.actNot.procedure,
|
||||
parm->u.inv.o.actNot.basicService);
|
||||
printServedUserNr(&parm->u.inv.o.actNot.servedUserNr);
|
||||
printAddress(&parm->u.inv.o.actNot.address);
|
||||
#endif
|
||||
p = &tmp[1];
|
||||
p += capiEncodeWord(p, 0x8006);
|
||||
p += capiEncodeFacIndCFNotAct(p, &parm.u.inv.o.actNot);
|
||||
p += capiEncodeFacIndCFNotAct(p, &parm->u.inv.o.actNot);
|
||||
tmp[0] = p - &tmp[1];
|
||||
SendSSFacilityInd2All(contr, SuppServiceCF, tmp);
|
||||
break;
|
||||
case 0x000a:
|
||||
#if 0
|
||||
printk("procedure %d basicService %d\n", parm.c.inv.o.deactNot.procedure,
|
||||
parm.c.inv.o.deactNot.basicService);
|
||||
printServedUserNr(&parm.c.inv.o.deactNot.servedUserNr);
|
||||
#ifdef ASN1_DEBUG
|
||||
printk("procedure %d basicService %d\n", parm->u.inv.o.deactNot.procedure,
|
||||
parm->u.inv.o.deactNot.basicService);
|
||||
printServedUserNr(&parm->u.inv.o.deactNot.servedUserNr);
|
||||
#endif
|
||||
p = &tmp[1];
|
||||
p += capiEncodeWord(p, 0x8007);
|
||||
p += capiEncodeFacIndCFNotDeact(p, &parm.u.inv.o.deactNot);
|
||||
p += capiEncodeFacIndCFNotDeact(p, &parm->u.inv.o.deactNot);
|
||||
tmp[0] = p - &tmp[1];
|
||||
SendSSFacilityInd2All(contr, SuppServiceCF, tmp);
|
||||
break;
|
||||
|
@ -606,7 +688,7 @@ SSProcessFacility(Controller_t *contr, Q931_info_t *qi)
|
|||
}
|
||||
break;
|
||||
case returnResult:
|
||||
sspc = getSSProcess4Id(contr, parm.u.retResult.invokeId);
|
||||
sspc = getSSProcess4Id(contr, parm->u.retResult.invokeId);
|
||||
if (!sspc)
|
||||
return;
|
||||
appl = getApplication4Id(contr, sspc->ApplId);
|
||||
|
@ -623,14 +705,14 @@ SSProcessFacility(Controller_t *contr, Q931_info_t *qi)
|
|||
break;
|
||||
case 0x000b:
|
||||
p += capiEncodeFacIndCFinterParameters(p, 0, sspc->Handle,
|
||||
&parm.u.retResult.o.resultList);
|
||||
&parm->u.retResult.o.resultList);
|
||||
break;
|
||||
case 0x000c:
|
||||
p += capiEncodeFacIndCFinterNumbers(p, 0, sspc->Handle,
|
||||
&parm.u.retResult.o.list);
|
||||
&parm->u.retResult.o.list);
|
||||
break;
|
||||
default:
|
||||
int_error();
|
||||
int_errtxt("returnResult for Function %04x", sspc->Function);
|
||||
break;
|
||||
}
|
||||
tmp[0] = p - &tmp[1];
|
||||
|
@ -638,7 +720,7 @@ SSProcessFacility(Controller_t *contr, Q931_info_t *qi)
|
|||
SSProcessDestr(sspc);
|
||||
break;
|
||||
case returnError:
|
||||
sspc = getSSProcess4Id(contr, parm.u.retResult.invokeId);
|
||||
sspc = getSSProcess4Id(contr, parm->u.retResult.invokeId);
|
||||
if (!sspc)
|
||||
return;
|
||||
appl = getApplication4Id(contr, sspc->ApplId);
|
||||
|
@ -646,28 +728,120 @@ SSProcessFacility(Controller_t *contr, Q931_info_t *qi)
|
|||
return;
|
||||
p = &tmp[1];
|
||||
p += capiEncodeWord(p, sspc->Function);
|
||||
p += capiEncodeFacIndCFact(p, 0x3600 | (parm.u.retError.errorValue & 0xff),
|
||||
p += capiEncodeFacIndCFact(p, 0x3600 | (parm->u.retError.errorValue & 0xff),
|
||||
sspc->Handle);
|
||||
tmp[0] = p - &tmp[1];
|
||||
SendSSFacilityInd(appl, sspc->addr, tmp);
|
||||
SSProcessDestr(sspc);
|
||||
break;
|
||||
case reject:
|
||||
if (parm->u.reject.invokeId == -1) /* ID := NULL */
|
||||
return;
|
||||
if (parm->u.reject.problem != InvokeP) {
|
||||
int_errtxt("reject problem class %d problem %d do not match CAPI errors",
|
||||
parm->u.reject.problem, parm->u.reject.problemValue);
|
||||
/* this is not compatible, but better as ignore */
|
||||
switch (parm->u.reject.problem) {
|
||||
case GeneralP:
|
||||
parm->u.reject.problemValue |= 0x80;
|
||||
break;
|
||||
default:
|
||||
parm->u.reject.problemValue |= (parm->u.reject.problem << 4);
|
||||
}
|
||||
}
|
||||
sspc = getSSProcess4Id(contr, parm->u.reject.invokeId);
|
||||
if (!sspc)
|
||||
return;
|
||||
appl = getApplication4Id(contr, sspc->ApplId);
|
||||
if (!appl)
|
||||
return;
|
||||
p = &tmp[1];
|
||||
p += capiEncodeWord(p, sspc->Function);
|
||||
p += capiEncodeFacIndCFact(p, 0x3700 | (parm->u.reject.problemValue & 0xff),
|
||||
sspc->Handle);
|
||||
tmp[0] = p - &tmp[1];
|
||||
SendSSFacilityInd(appl, sspc->addr, tmp);
|
||||
SSProcessDestr(sspc);
|
||||
break;
|
||||
default:
|
||||
int_error();
|
||||
int_errtxt("component %x not handled", parm->comp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SSProcessFacility(Controller_t *contr, __u8 *p)
|
||||
{
|
||||
int ie_len, l;
|
||||
struct asn1_parm parm;
|
||||
__u8 *end;
|
||||
|
||||
ie_len = *p++;
|
||||
end = p + ie_len;
|
||||
|
||||
p++; // Supplementary Service Applications checked before
|
||||
|
||||
while (p < end) {
|
||||
parm.comp = -1;
|
||||
l = ParseComponent(&parm, p, end);
|
||||
// printk(KERN_DEBUG "ie_len (%d) l(%d) parm.comp %d\n", ie_len, l, parm.comp);
|
||||
if (parm.comp != -1) {
|
||||
SSProcessSingleFacility(contr, &parm);
|
||||
} else {
|
||||
static char logbuf[3*260];
|
||||
|
||||
int_errtxt("component %x not handled", parm.comp);
|
||||
if (ie_len > 260)
|
||||
ie_len = 260;
|
||||
mISDN_QuickHex(logbuf, p, ie_len);
|
||||
printk(KERN_DEBUG "facIE: %s\n", logbuf);
|
||||
}
|
||||
if (l>0)
|
||||
p += l;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Supplementary_l3l4(Controller_t *contr, __u32 prim, struct sk_buff *skb)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int ret = -EINVAL;
|
||||
Q931_info_t *qi;
|
||||
ie_info_t *fac;
|
||||
__u8 *p, *end;
|
||||
|
||||
if (!skb)
|
||||
return(ret);
|
||||
switch (prim) {
|
||||
case CC_FACILITY | INDICATION:
|
||||
if (skb->len)
|
||||
SSProcessFacility(contr, (Q931_info_t *)skb->data);
|
||||
qi = (Q931_info_t *)skb->data;
|
||||
if (skb->len <= L3_EXTRA_SIZE) {
|
||||
int_error();
|
||||
break;
|
||||
}
|
||||
if (!qi->facility.off) {
|
||||
int_error();
|
||||
break;
|
||||
}
|
||||
fac = &qi->facility;
|
||||
while(fac && fac->off) {
|
||||
p = (__u8 *)qi;
|
||||
p += L3_EXTRA_SIZE + fac->off + 1;
|
||||
end = p + *p;
|
||||
if (end > skb->data + skb->len) {
|
||||
int_error();
|
||||
fac = NULL;
|
||||
} else if (p[1] == 0x91) { // Supplementary Service Applications
|
||||
SSProcessFacility(contr, p);
|
||||
} else {
|
||||
int_errtxt("FACILITY but not a Supplementary Service ID %x", p[1]);
|
||||
}
|
||||
if (fac->repeated)
|
||||
fac = &qi->ext[fac->ridx].ie;
|
||||
else
|
||||
fac = NULL;
|
||||
}
|
||||
dev_kfree_skb(skb);
|
||||
ret = 0;
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/* $Id$
|
||||
*
|
||||
* Author Karsten Keil (keil@isdn4linux.de)
|
||||
*
|
||||
* mISDN sysfs common defines
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*
|
||||
*/
|
||||
#include <linux/stringify.h>
|
||||
|
||||
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, \
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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<<mISDN_STACK_INIT)) {
|
||||
/* we want to make st->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<<mISDN_STACK_THREADSTART)) {
|
||||
/* we want to start a new process after abort */
|
||||
err = mISDN_start_stack_thread(st);
|
||||
if (err) {
|
||||
int_errtxt("start_stack_thread:%d", err);
|
||||
return(err);
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
st->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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
/* $Id$
|
||||
*
|
||||
* Linux modular ISDN subsystem, mISDN
|
||||
* X.25/X.31 Layer3 for DTE mode
|
||||
* X.25/X.31 Layer3 for DTE mode
|
||||
*
|
||||
* Author Karsten Keil (kkeil@suse.de)
|
||||
*
|
||||
|
@ -370,7 +370,7 @@ static struct FsmNode PFnList[] =
|
|||
{ST_P0, EV_L3_OUTGOING_CALL, p_p0_outgoing},
|
||||
{ST_P0, EV_L3_CLEARING, p_clearing_req},
|
||||
{ST_P1, EV_L3_READY, p_ready},
|
||||
{ST_P1, EV_L3_OUTGOING_CALL, p_outgoing},
|
||||
{ST_P1, EV_L3_OUTGOING_CALL, p_outgoing},
|
||||
{ST_P1, EV_L2_INCOMING_CALL, p_incoming},
|
||||
{ST_P1, EV_L2_CLEAR, p_clear_ind},
|
||||
{ST_P1, EV_L2_CALL_CNF, p_invalid_pkt},
|
||||
|
@ -493,7 +493,7 @@ d_reset_timeout(struct FsmInst *fi, int event, void *arg)
|
|||
l3c->cause[1] = 51;
|
||||
mISDN_FsmChangeState(fi, ST_D0);
|
||||
if (test_bit(X25_STATE_PERMANENT, &l3c->state))
|
||||
X25_clear_connection(l3c, NULL, 0x3303);
|
||||
X25_clear_connection(l3c, NULL, 0x3303);
|
||||
else
|
||||
mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, NULL);
|
||||
}
|
||||
|
@ -660,7 +660,7 @@ dte_data_ind_d(x25_channel_t *chan, struct sk_buff *skb, u_char gfi, u_char ptyp
|
|||
mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL);
|
||||
} else {
|
||||
int flag = (pr_m & 1) ? CAPI_FLAG_MOREDATA : 0;
|
||||
|
||||
|
||||
if (gfi & X25_GFI_QBIT)
|
||||
flag |= CAPI_FLAG_QUALIFIER;
|
||||
if (gfi & X25_GFI_DBIT)
|
||||
|
@ -755,7 +755,7 @@ dte_data_ind_r(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel, u_c
|
|||
{
|
||||
int ret = X25_ERRCODE_DISCARD;
|
||||
|
||||
|
||||
|
||||
if (ptype == X25_PTYPE_NOTYPE) {
|
||||
if (channel) {
|
||||
if (l3->x25r.state == ST_R1)
|
||||
|
@ -851,19 +851,10 @@ dte_dl_data_ind(x25_l3_t *l3, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
static int
|
||||
dte_from_down(mISDNif_t *hif, struct sk_buff *skb)
|
||||
dte_from_down(x25_l3_t *l3, struct sk_buff *skb, mISDN_head_t *hh)
|
||||
{
|
||||
x25_l3_t *l3;
|
||||
mISDN_head_t *hh;
|
||||
int ret = -EINVAL;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!hif || !hif->fdata || !skb)
|
||||
return(ret);
|
||||
l3 = hif->fdata;
|
||||
if (!l3->inst.up.func) {
|
||||
return(-ENXIO);
|
||||
}
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
if (l3->debug)
|
||||
printk(KERN_DEBUG "%s: prim(%x) dinfo(%x)\n", __FUNCTION__, hh->prim, hh->dinfo);
|
||||
switch(hh->prim) {
|
||||
|
@ -917,7 +908,7 @@ dte_create_channel(x25_l3_t *l3, int typ, u_char flag, __u16 ch, int len, u_char
|
|||
x25_channel_t *l3c;
|
||||
__u16 nch = ch;
|
||||
int ret;
|
||||
|
||||
|
||||
if (typ == X25_CHANNEL_OUTGOING) {
|
||||
if (ch == 0) {
|
||||
/* first search for allready created channels in P1 state */
|
||||
|
@ -1035,38 +1026,13 @@ dte_create_channel(x25_l3_t *l3, int typ, u_char flag, __u16 ch, int len, u_char
|
|||
}
|
||||
|
||||
static int
|
||||
new_l3(mISDNstack_t *st, mISDN_pid_t *pid) {
|
||||
x25_l3_t *n_l3;
|
||||
int err;
|
||||
|
||||
err = new_x25_l3(&n_l3, st, pid, &x25dte_obj, debug);
|
||||
if (err)
|
||||
return(err);
|
||||
|
||||
n_l3->x25r.fsm = &dte_rfsm;
|
||||
n_l3->x25r.state = ST_R0;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int
|
||||
dte_from_up(mISDNif_t *hif, struct sk_buff *skb)
|
||||
dte_from_up(x25_l3_t *l3, struct sk_buff *skb, mISDN_head_t *hh)
|
||||
{
|
||||
x25_l3_t *l3;
|
||||
x25_channel_t *l3c;
|
||||
mISDN_head_t *hh;
|
||||
__u32 addr;
|
||||
__u16 info = 0;
|
||||
int err = 0;
|
||||
|
||||
if (!hif || !hif->fdata || !skb)
|
||||
return(-EINVAL);
|
||||
l3 = hif->fdata;
|
||||
if (!l3->inst.down.func) {
|
||||
return(-ENXIO);
|
||||
}
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
if (l3->debug)
|
||||
printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len);
|
||||
if (skb->len < 4) {
|
||||
printk(KERN_WARNING "%s: skb too short (%d)\n", __FUNCTION__, skb->len);
|
||||
return(-EINVAL);
|
||||
|
@ -1103,7 +1069,7 @@ dte_from_up(mISDNif_t *hif, struct sk_buff *skb)
|
|||
ncpi = (x25_ncpi_t *)skb->data;
|
||||
l3c = dte_create_channel(l3, X25_CHANNEL_OUTGOING, ncpi->Flags,
|
||||
(ncpi->Group<<8) | ncpi->Channel,
|
||||
ncpi->len - 3, &ncpi->Contens[0]);
|
||||
ncpi->len - 3, &ncpi->Contens[0]);
|
||||
}
|
||||
if (l3c)
|
||||
l3c->ncci = addr | (l3c->lchan << 16);
|
||||
|
@ -1166,7 +1132,7 @@ dte_from_up(mISDNif_t *hif, struct sk_buff *skb)
|
|||
info = 0;
|
||||
} else
|
||||
info = 0x2002;
|
||||
|
||||
|
||||
skb_trim(skb, 0);
|
||||
memcpy(skb_put(skb, 2), &info, 2);
|
||||
err = X25sendL4skb(l3c, l3, addr, CAPI_DISCONNECT_B3_CONF, hh->dinfo, skb);
|
||||
|
@ -1235,6 +1201,56 @@ dte_from_up(mISDNif_t *hif, struct sk_buff *skb)
|
|||
return(err);
|
||||
}
|
||||
|
||||
static int
|
||||
dte_function(mISDNinstance_t *inst, struct sk_buff *skb)
|
||||
{
|
||||
x25_l3_t *l3;
|
||||
mISDN_head_t *hh;
|
||||
int ret = -EINVAL;
|
||||
|
||||
l3 = inst->privat;
|
||||
hh = mISDN_HEAD_P(skb);
|
||||
if (l3->debug)
|
||||
printk(KERN_DEBUG "%s: addr(%08x) prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__,
|
||||
hh->addr, hh->prim, hh->dinfo, skb->len);
|
||||
if (debug)
|
||||
printk(KERN_DEBUG "%s: addr(%08x) prim(%x)\n", __FUNCTION__, hh->addr, hh->prim);
|
||||
if (!l3)
|
||||
return(ret);
|
||||
|
||||
switch(hh->addr & MSG_DIR_MASK) {
|
||||
case FLG_MSG_DOWN:
|
||||
ret = dte_from_up(l3, skb, hh);
|
||||
break;
|
||||
case FLG_MSG_UP:
|
||||
case MSG_BROADCAST: // we define broaadcast comes from down below
|
||||
ret = dte_from_down(l3, skb, hh);
|
||||
break;
|
||||
case MSG_TO_OWNER:
|
||||
/* FIXME: must be handled depending on type */
|
||||
int_errtxt("not implemented yet");
|
||||
break;
|
||||
default:
|
||||
/* FIXME: must be handled depending on type */
|
||||
int_errtxt("not implemented yet");
|
||||
break;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static int
|
||||
new_l3(mISDNstack_t *st, mISDN_pid_t *pid) {
|
||||
x25_l3_t *n_l3;
|
||||
int err;
|
||||
|
||||
err = new_x25_l3(&n_l3, st, pid, &x25dte_obj, debug, dte_function);
|
||||
if (err)
|
||||
return(err);
|
||||
n_l3->x25r.fsm = &dte_rfsm;
|
||||
n_l3->x25r.state = ST_R0;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static char MName[] = "X25_DTE";
|
||||
|
||||
|
@ -1251,17 +1267,20 @@ dte_manager(void *data, u_int prim, void *arg) {
|
|||
mISDNinstance_t *inst = data;
|
||||
x25_l3_t *l3_l;
|
||||
int err = -EINVAL;
|
||||
u_long flags;
|
||||
|
||||
if (debug & DEBUG_L3X25_MGR)
|
||||
printk(KERN_DEBUG "l3x25_manager data:%p prim:%x arg:%p\n", data, prim, arg);
|
||||
if (!data)
|
||||
return(err);
|
||||
spin_lock_irqsave(&x25dte_obj.lock, flags);
|
||||
list_for_each_entry(l3_l, &x25dte_obj.ilist, list) {
|
||||
if (&l3_l->inst == inst) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&x25dte_obj.lock, flags);
|
||||
if (prim == (MGR_NEWLAYER | REQUEST))
|
||||
return(new_l3(data, arg));
|
||||
if (err) {
|
||||
|
@ -1270,7 +1289,7 @@ dte_manager(void *data, u_int prim, void *arg) {
|
|||
}
|
||||
switch(prim) {
|
||||
case MGR_NEWENTITY | CONFIRM:
|
||||
l3_l->entity = (int)arg;
|
||||
l3_l->entity = (u_long)arg & 0xffffffff;
|
||||
break;
|
||||
case MGR_ADDSTPARA | INDICATION:
|
||||
{
|
||||
|
@ -1285,6 +1304,7 @@ dte_manager(void *data, u_int prim, void *arg) {
|
|||
}
|
||||
break;
|
||||
case MGR_CLRSTPARA | INDICATION:
|
||||
#ifdef OBSOLETE
|
||||
case MGR_CLONELAYER | REQUEST:
|
||||
break;
|
||||
case MGR_CONNECT | REQUEST:
|
||||
|
@ -1295,6 +1315,7 @@ dte_manager(void *data, u_int prim, void *arg) {
|
|||
case MGR_DISCONNECT | REQUEST:
|
||||
case MGR_DISCONNECT | INDICATION:
|
||||
return(mISDN_DisConnectIF(inst, arg));
|
||||
#endif
|
||||
case MGR_UNREGLAYER | REQUEST:
|
||||
case MGR_RELEASE | INDICATION:
|
||||
if (debug & DEBUG_L3X25_MGR)
|
||||
|
@ -1323,6 +1344,7 @@ x25_dte_init(void)
|
|||
x25dte_obj.name = MName;
|
||||
x25dte_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_X25DTE;
|
||||
x25dte_obj.own_ctrl = dte_manager;
|
||||
spin_lock_init(&x25dte_obj.lock);
|
||||
INIT_LIST_HEAD(&x25dte_obj.ilist);
|
||||
if ((err = mISDN_register(&x25dte_obj))) {
|
||||
printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
|
||||
|
|
|
@ -286,14 +286,11 @@ X25_restart(x25_l3_t *l3)
|
|||
int
|
||||
X25_next_id(x25_l3_t *l3)
|
||||
{
|
||||
u_long flags;
|
||||
int id;
|
||||
|
||||
spin_lock_irqsave(&l3->lock, flags);
|
||||
id = l3->next_id++;
|
||||
if (id == 0x0fff)
|
||||
l3->next_id = 1;
|
||||
spin_unlock_irqrestore(&l3->lock, flags);
|
||||
id |= (l3->entity << 16);
|
||||
return(id);
|
||||
}
|
||||
|
@ -378,7 +375,7 @@ X25_confirmed(x25_channel_t *chan)
|
|||
i = cq->MsgId;
|
||||
/* free entry */
|
||||
cq->PktId = 0;
|
||||
ret = if_newhead(&chan->l3->inst.up, CAPI_DATA_B3_CONF, i, skb);
|
||||
ret = mISDN_queueup_newhead(&chan->l3->inst, 0, CAPI_DATA_B3_CONF, i, skb);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret);
|
||||
dev_kfree_skb(skb);
|
||||
|
@ -453,7 +450,7 @@ X25_receive_data(x25_channel_t *chan, int ps, int flag, struct sk_buff *skb)
|
|||
capimsg_setu16(nskb->data, 10, i);
|
||||
capimsg_setu16(nskb->data, 12, flag);
|
||||
|
||||
if (if_newhead(&chan->l3->inst.up, CAPI_DATA_B3_IND, 0, nskb)) {
|
||||
if (mISDN_queueup_newhead(&chan->l3->inst, 0, CAPI_DATA_B3_IND, 0, nskb)) {
|
||||
chan->recv_handles[i] = 0;
|
||||
return(X25_ERRCODE_DISCARD);
|
||||
}
|
||||
|
@ -539,8 +536,7 @@ void
|
|||
X25_release_channel(x25_channel_t *l3c)
|
||||
{
|
||||
list_del(&l3c->list);
|
||||
if (l3c->ncpi_data)
|
||||
kfree(l3c->ncpi_data);
|
||||
kfree(l3c->ncpi_data);
|
||||
l3c->ncpi_data = NULL;
|
||||
l3c->ncpi_len = 0;
|
||||
discard_queue(&l3c->dataq);
|
||||
|
@ -548,10 +544,7 @@ X25_release_channel(x25_channel_t *l3c)
|
|||
mISDN_FsmDelTimer(&l3c->TD, 2);
|
||||
discard_queue(&l3c->dataq);
|
||||
discard_confq(l3c);
|
||||
if (l3c->confq) {
|
||||
kfree(l3c->confq);
|
||||
l3c->confq = NULL;
|
||||
}
|
||||
kfree(l3c->confq);
|
||||
kfree(l3c);
|
||||
}
|
||||
|
||||
|
@ -560,6 +553,7 @@ X25_release_l3(x25_l3_t *l3) {
|
|||
mISDNinstance_t *inst = &l3->inst;
|
||||
x25_channel_t *ch, *nch;
|
||||
|
||||
#ifdef OBSOLETE
|
||||
if (inst->up.peer) {
|
||||
inst->up.peer->obj->ctrl(inst->up.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->up);
|
||||
|
@ -568,8 +562,12 @@ X25_release_l3(x25_l3_t *l3) {
|
|||
inst->down.peer->obj->ctrl(inst->down.peer,
|
||||
MGR_DISCONNECT | REQUEST, &inst->down);
|
||||
}
|
||||
#endif
|
||||
if (inst->obj) {
|
||||
u_long flags;
|
||||
spin_lock_irqsave(&inst->obj->lock, flags);
|
||||
list_del_init(&l3->list);
|
||||
spin_unlock_irqrestore(&inst->obj->lock, flags);
|
||||
}
|
||||
discard_queue(&l3->downq);
|
||||
list_for_each_entry_safe(ch, nch, &l3->channellist, list)
|
||||
|
@ -577,7 +575,8 @@ X25_release_l3(x25_l3_t *l3) {
|
|||
mISDN_FsmDelTimer(&l3->TR, 3);
|
||||
if (inst->obj) {
|
||||
if (l3->entity != MISDN_ENTITY_NONE)
|
||||
inst->obj->ctrl(inst, MGR_DELENTITY | REQUEST, (void *)l3->entity);
|
||||
inst->obj->ctrl(inst, MGR_DELENTITY | REQUEST,
|
||||
(void *)((u_long)l3->entity));
|
||||
inst->obj->ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
||||
}
|
||||
kfree(l3);
|
||||
|
@ -588,8 +587,7 @@ X25_realloc_ncpi_data(x25_channel_t *l3c, int len, u_char *data)
|
|||
{
|
||||
if (len) {
|
||||
if (len > l3c->ncpi_len) {
|
||||
if (l3c->ncpi_data)
|
||||
kfree(l3c->ncpi_data);
|
||||
kfree(l3c->ncpi_data);
|
||||
l3c->ncpi_data = kmalloc(len, GFP_ATOMIC);
|
||||
if (!l3c->ncpi_data) {
|
||||
l3c->ncpi_len = 0;
|
||||
|
@ -597,7 +595,7 @@ X25_realloc_ncpi_data(x25_channel_t *l3c, int len, u_char *data)
|
|||
}
|
||||
}
|
||||
memcpy(l3c->ncpi_data, data, len);
|
||||
} else if (l3c->ncpi_data) {
|
||||
} else {
|
||||
kfree(l3c->ncpi_data);
|
||||
l3c->ncpi_data = NULL;
|
||||
}
|
||||
|
@ -629,8 +627,7 @@ new_x25_channel(x25_l3_t *l3, x25_channel_t **ch_p, __u16 ch, int dlen, u_char *
|
|||
l3c->confq = kmalloc(l3c->lwin * sizeof(x25_ConfQueue_t), GFP_ATOMIC);
|
||||
if (!l3c->confq) {
|
||||
printk(KERN_ERR "kmalloc confq %d entries failed\n", l3c->lwin);
|
||||
if (l3c->ncpi_data)
|
||||
kfree(l3c->ncpi_data);
|
||||
kfree(l3c->ncpi_data);
|
||||
kfree(l3c);
|
||||
return(-ENOMEM);
|
||||
}
|
||||
|
@ -658,9 +655,10 @@ new_x25_channel(x25_l3_t *l3, x25_channel_t **ch_p, __u16 ch, int dlen, u_char *
|
|||
}
|
||||
|
||||
int
|
||||
new_x25_l3(x25_l3_t **l3_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *obj, int debug) {
|
||||
new_x25_l3(x25_l3_t **l3_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *obj, int debug, if_func_t *function) {
|
||||
x25_l3_t *n_l3;
|
||||
int err;
|
||||
u_long flags;
|
||||
|
||||
if (!st || !pid)
|
||||
return(-EINVAL);
|
||||
|
@ -672,9 +670,8 @@ new_x25_l3(x25_l3_t **l3_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *o
|
|||
INIT_LIST_HEAD(&n_l3->channellist);
|
||||
n_l3->entity = MISDN_ENTITY_NONE;
|
||||
n_l3->next_id = 1;
|
||||
spin_lock_init(&n_l3->lock);
|
||||
memcpy(&n_l3->inst.pid, pid, sizeof(mISDN_pid_t));
|
||||
mISDN_init_instance(&n_l3->inst, obj, n_l3);
|
||||
mISDN_init_instance(&n_l3->inst, obj, n_l3, function);
|
||||
if (!mISDN_SetHandledPID(obj, &n_l3->inst.pid)) {
|
||||
int_error();
|
||||
kfree(n_l3);
|
||||
|
@ -704,8 +701,9 @@ new_x25_l3(x25_l3_t **l3_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *o
|
|||
n_l3->x25r.printdebug = l3m_debug;
|
||||
mISDN_FsmInitTimer(&n_l3->x25r, &n_l3->TR);
|
||||
skb_queue_head_init(&n_l3->downq);
|
||||
|
||||
spin_lock_irqsave(&obj->lock, flags);
|
||||
list_add_tail(&n_l3->list, &obj->ilist);
|
||||
spin_unlock_irqrestore(&obj->lock, flags);
|
||||
err = obj->ctrl(&n_l3->inst, MGR_NEWENTITY | REQUEST, NULL);
|
||||
if (err) {
|
||||
printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%d)\n",
|
||||
|
@ -853,9 +851,7 @@ X25sendL3frame(x25_channel_t *l3c, x25_l3_t *l3, u_char pt, int len, void *arg)
|
|||
mISDN_sethead(DL_DATA_REQ, X25_next_id(l3), skb);
|
||||
|
||||
if (l3->l2l3m.state == ST_LL_ESTAB) {
|
||||
mISDNif_t *down = &l3->inst.down;
|
||||
|
||||
ret = down->func(down, skb);
|
||||
ret = mISDN_queue_message(&l3->inst, FLG_MSG_DOWN, skb);
|
||||
if (ret) {
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
@ -869,15 +865,13 @@ X25sendL3frame(x25_channel_t *l3c, x25_l3_t *l3, u_char pt, int len, void *arg)
|
|||
int
|
||||
X25_l3down(x25_l3_t *l3, u_int prim, u_int dinfo, struct sk_buff *skb)
|
||||
{
|
||||
mISDNif_t *down = &l3->inst.down;
|
||||
int ret;
|
||||
|
||||
if (!skb) {
|
||||
if (!(skb = alloc_stack_skb(0, l3->down_headerlen)))
|
||||
return(-ENOMEM);
|
||||
}
|
||||
mISDN_sethead(prim, dinfo, skb);
|
||||
ret = down->func(down, skb);
|
||||
ret = mISDN_queuedown_newhead(&l3->inst, 0, prim, dinfo, skb);
|
||||
if (ret) {
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
@ -951,7 +945,7 @@ X25sendL4skb(x25_channel_t *l3c, x25_l3_t *l3, __u32 addr, int prim, int dinfo,
|
|||
capimsg_setu32(skb->data, 0, l3c->ncci);
|
||||
else
|
||||
capimsg_setu32(skb->data, 0, addr);
|
||||
return(if_newhead(&l3->inst.up, prim, dinfo, skb));
|
||||
return(mISDN_queueup_newhead(&l3->inst, 0, prim, dinfo, skb));
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -991,7 +985,7 @@ X25sendL4frame(x25_channel_t *l3c, x25_l3_t *l3, int prim, int flags, int len, v
|
|||
dev_kfree_skb(skb);
|
||||
return(-EINVAL);
|
||||
}
|
||||
ret = if_newhead(&l3->inst.up, prim, 0, skb);
|
||||
ret = mISDN_queueup_newhead(&l3->inst, 0, prim, 0, skb);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret);
|
||||
dev_kfree_skb(skb);
|
||||
|
|
|
@ -64,7 +64,6 @@ struct _x25_l3 {
|
|||
x25_B3_cfg_t B3cfg;
|
||||
u_long state;
|
||||
u_int debug;
|
||||
spinlock_t lock;
|
||||
u_char cause[2];
|
||||
};
|
||||
|
||||
|
@ -84,7 +83,6 @@ struct _x25_channel {
|
|||
u_int ncpi_len;
|
||||
u_long state;
|
||||
u_int debug;
|
||||
spinlock_t lock;
|
||||
u_int pr;
|
||||
u_int ps;
|
||||
u_int rps;
|
||||
|
@ -262,7 +260,7 @@ extern void X25_release_channel(x25_channel_t *);
|
|||
extern void X25_release_l3(x25_l3_t *);
|
||||
extern int X25_realloc_ncpi_data(x25_channel_t *, int, u_char *);
|
||||
extern int new_x25_channel(x25_l3_t *, x25_channel_t **, __u16, int, u_char *);
|
||||
extern int new_x25_l3(x25_l3_t **, mISDNstack_t *, mISDN_pid_t *, mISDNobject_t *, int);
|
||||
extern int new_x25_l3(x25_l3_t **, mISDNstack_t *, mISDN_pid_t *, mISDNobject_t *, int, if_func_t *);
|
||||
extern int X25_next_id(x25_l3_t *);
|
||||
extern int X25_add_header(x25_channel_t *, x25_l3_t *, u_char , u_char *, u_char);
|
||||
extern int X25sendL3frame(x25_channel_t *, x25_l3_t *, u_char, int, void *);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#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);
|
||||
}
|
|
@ -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_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -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 <linux/timer.h>
|
||||
#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_ */
|
|
@ -86,9 +86,23 @@ typedef struct wait_queue *wait_queue_head_t;
|
|||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
|
||||
#define OLD_PCI_REGISTER_DRIVER 1
|
||||
#define OLD_MODULE_PARAM_ARRAY
|
||||
#else
|
||||
#undef OLD_PCI_REGISTER_DRIVER
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
|
||||
#define MODULE_MKOBJ_POINTER
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11)
|
||||
#define CLASSDEV_HAS_DEVT
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
|
||||
/* udev sysfs stuff */
|
||||
#define CLASS_WITHOUT_OWNER
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _LINUX_ISDN_COMPAT_H */
|
||||
|
|
|
@ -9,16 +9,6 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
/* primitives for information exchange
|
||||
* generell format
|
||||
* <8 bit reserved>
|
||||
* <4 bit flags>
|
||||
* <4 bit layer>
|
||||
* <8 bit command>
|
||||
* <8 bit subcommand>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* ABI Version 32 bit
|
||||
*
|
||||
|
@ -29,13 +19,29 @@
|
|||
* - changed if any interface is extended but backwards compatible
|
||||
*
|
||||
*/
|
||||
#define MISDN_MAJOR_VERSION 1
|
||||
#define MISDN_MAJOR_VERSION 3
|
||||
#define MISDN_MINOR_VERSION 0
|
||||
#define MISDN_VERSION ((MISDN_MAJOR_VERSION<<16) | MISDN_MINOR_VERSION)
|
||||
|
||||
#define MISDN_REVISION "$Revision$"
|
||||
#define MISDN_DATE "$Date$"
|
||||
|
||||
/* collect some statistics about the message queues */
|
||||
#define MISDN_MSG_STATS
|
||||
|
||||
/* primitives for information exchange
|
||||
* generell format
|
||||
* <8 bit reserved>
|
||||
* <4 bit flags>
|
||||
* <4 bit layer>
|
||||
* <8 bit command>
|
||||
* <8 bit subcommand>
|
||||
*
|
||||
*/
|
||||
|
||||
#define MISDN_CMD_MASK 0xffffff00
|
||||
#define MISDN_SUB_MASK 0x000000ff
|
||||
|
||||
/* SUBCOMMANDS */
|
||||
#define REQUEST 0x80
|
||||
#define CONFIRM 0x81
|
||||
|
@ -61,18 +67,22 @@
|
|||
#define MGR_SETSTACK_NW 0x0f1900
|
||||
#define MGR_ADDSTPARA 0x0f1A00
|
||||
#define MGR_CLRSTPARA 0x0f1B00
|
||||
#define MGR_ADDLAYER 0x0f1C00
|
||||
#define MGR_GETLAYER 0x0f2100
|
||||
#define MGR_GETLAYERID 0x0f2200
|
||||
#define MGR_NEWLAYER 0x0f2300
|
||||
#define MGR_DELLAYER 0x0f2400
|
||||
#define MGR_CLONELAYER 0x0f2500
|
||||
#define MGR_GETIF 0x0f3100
|
||||
#define MGR_CONNECT 0x0f3200
|
||||
#define MGR_DISCONNECT 0x0f3300
|
||||
#define MGR_SETIF 0x0f3400
|
||||
#define MGR_ADDIF 0x0f3500
|
||||
#define MGR_QUEUEIF 0x0f3600
|
||||
//#define MGR_CLONELAYER 0x0f2500
|
||||
//#define MGR_GETIF 0x0f3100
|
||||
//#define MGR_CONNECT 0x0f3200
|
||||
//#define MGR_DISCONNECT 0x0f3300
|
||||
//#define MGR_SETIF 0x0f3400
|
||||
//#define MGR_ADDIF 0x0f3500
|
||||
//#define MGR_QUEUEIF 0x0f3600
|
||||
#define MGR_CTRLREADY 0x0f4100
|
||||
#define MGR_STACKREADY 0x0f4200
|
||||
#define MGR_STOPSTACK 0x0f4300
|
||||
#define MGR_STARTSTACK 0x0f4400
|
||||
#define MGR_RELEASE 0x0f4500
|
||||
#define MGR_GETDEVICE 0x0f5100
|
||||
#define MGR_DELDEVICE 0x0f5200
|
||||
|
@ -85,9 +95,10 @@
|
|||
#define MGR_TIMER 0x0f8800
|
||||
#define MGR_CONTROL 0x0fe100
|
||||
#define MGR_STATUS 0x0fe200
|
||||
#define MGR_HASPROTOCOL 0x0fe300
|
||||
#define MGR_HASPROTOCOL 0x0fe300
|
||||
#define MGR_EVALSTACK 0x0fe400
|
||||
#define MGR_GLOBALOPT 0x0fe500
|
||||
#define MGR_SHORTSTATUS 0x0fe600
|
||||
#define MGR_LOADFIRM 0x0ff000
|
||||
#define MGR_LOGDATA 0x0ff100
|
||||
#define MGR_DEBUGDATA 0x0ff200
|
||||
|
@ -106,12 +117,16 @@
|
|||
#define INFO3_P10 0x130a
|
||||
#define INFO4_P8 0x1408
|
||||
#define INFO4_P10 0x140a
|
||||
#define D_RX_MON0 0x1800
|
||||
#define D_TX_MON0 0x1801
|
||||
#define D_RX_MON1 0x1810
|
||||
#define D_TX_MON1 0x1811
|
||||
#define LOSTFRAMING 0x1f00
|
||||
#define ANYSIGNAL 0x1f01
|
||||
|
||||
/* PH_CONTROL parameter */
|
||||
#define HW_RESET 0x0001
|
||||
#define HW_POWERDOWN 0x0100
|
||||
#define HW_RESET 0x0001
|
||||
#define HW_POWERDOWN 0x0100
|
||||
#define HW_POWERUP 0x0101
|
||||
#define HW_DEACTIVATE 0x0200
|
||||
#define HW_ACTIVATE 0x0201
|
||||
|
@ -147,8 +162,8 @@
|
|||
#define HW_FIRM_START 0xFF10
|
||||
#define HW_FIRM_DATA 0xFF11
|
||||
#define HW_FIRM_END 0xFF12
|
||||
#define HW_D_BLOCKED 0xFF20
|
||||
#define HW_D_NOBLOCKED 0xFF21
|
||||
#define HW_D_BLOCKED 0xFF20
|
||||
#define HW_D_NOBLOCKED 0xFF21
|
||||
#define HW_TESTRX_RAW 0xFF40
|
||||
#define HW_TESTRX_HDLC 0xFF41
|
||||
#define HW_TESTRX_OFF 0xFF4f
|
||||
|
@ -173,6 +188,8 @@
|
|||
#define BF_DISABLE 0x2315
|
||||
#define BF_ACCEPT 0x2316
|
||||
#define BF_REJECT 0x2317
|
||||
#define ECHOCAN_ON 0x2318
|
||||
#define ECHOCAN_OFF 0x2319
|
||||
#define HW_POTS_ON 0x1001
|
||||
#define HW_POTS_OFF 0x1002
|
||||
#define HW_POTS_SETMICVOL 0x1100
|
||||
|
@ -221,7 +238,7 @@
|
|||
#define MPH_DEACTIVATE 0x011000
|
||||
#define MPH_ACTIVATE 0x011100
|
||||
#define MPH_INFORMATION 0x012000
|
||||
|
||||
|
||||
/* layer 2 */
|
||||
#define DL_ESTABLISH 0x020100
|
||||
#define DL_RELEASE 0x020000
|
||||
|
@ -272,6 +289,8 @@
|
|||
|
||||
#define CC_B3_DATA 0x138600
|
||||
|
||||
#define CAPI_MESSAGE_REQUEST 0x040080
|
||||
|
||||
#define LAYER_MASK 0x0F0000
|
||||
#define COMMAND_MASK 0x00FF00
|
||||
#define SUBCOMMAND_MASK 0x0000FF
|
||||
|
@ -319,6 +338,9 @@
|
|||
#define ISDN_PID_L0_NT_UP2 0x00000400
|
||||
#define ISDN_PID_L0_TE_E1 0x00000008
|
||||
#define ISDN_PID_L0_NT_E1 0x00000800
|
||||
#define ISDN_PID_L0_IP_S0 0x00000010
|
||||
#define ISDN_PID_L0_IP_E1 0x00000020
|
||||
#define ISDN_PID_L0_LOOP 0x00000030
|
||||
#define ISDN_PID_L1_TE_S0 0x01000001
|
||||
#define ISDN_PID_L1_NT_S0 0x01000100
|
||||
#define ISDN_PID_L1_TE_U 0x01000002
|
||||
|
@ -327,7 +349,10 @@
|
|||
#define ISDN_PID_L1_NT_UP2 0x01000400
|
||||
#define ISDN_PID_L1_TE_E1 0x01000008
|
||||
#define ISDN_PID_L1_NT_E1 0x01000800
|
||||
#define ISDN_PID_L2_LAPD 0x02000001
|
||||
#define ISDN_PID_L2_LAPD_NET 0x02000002
|
||||
/* Bit 15-0 reserved for CAPI defined protocols */
|
||||
#define ISDN_PID_L0_B_IP 0x40000001
|
||||
#define ISDN_PID_L1_B_64HDLC 0x41000001
|
||||
#define ISDN_PID_L1_B_64TRANS 0x41000002
|
||||
#define ISDN_PID_L1_B_V110_ASYNC 0x41000004
|
||||
|
@ -338,8 +363,6 @@
|
|||
#define ISDN_PID_L1_B_MODEM_ALL 0x41000080
|
||||
#define ISDN_PID_L1_B_MODEM_ASYNC 0x41000100
|
||||
#define ISDN_PID_L1_B_MODEM_HDLC 0x41000200
|
||||
#define ISDN_PID_L2_LAPD 0x02000001
|
||||
#define ISDN_PID_L2_LAPD_NET 0x02000002
|
||||
/* Bit 15-0 reserved for CAPI defined protocols */
|
||||
#define ISDN_PID_L2_B_X75SLP 0x42000001
|
||||
#define ISDN_PID_L2_B_TRANS 0x42000002
|
||||
|
@ -382,6 +405,18 @@
|
|||
#define ISDN_PID_L3_DF_EXTCID 0x00200000
|
||||
#define ISDN_PID_L3_DF_CRLEN2 0x00400000
|
||||
|
||||
|
||||
// short message status
|
||||
#define SSTATUS_ALL 0x0fffffff
|
||||
#define SSTATUS_BROADCAST_BIT 0x10000000
|
||||
#define SSTATUS_L1 0x01ffffff
|
||||
#define SSTATUS_L2 0x02ffffff
|
||||
#define SSTATUS_L1_DEACTIVATED 0x01000000
|
||||
#define SSTATUS_L1_ACTIVATED 0x01000001
|
||||
#define SSTATUS_L2_RELEASED 0x02000000
|
||||
#define SSTATUS_L2_ESTABLISHED 0x02000001
|
||||
|
||||
|
||||
#define mISDN_CORE_DEVICE 0
|
||||
#define mISDN_RAW_DEVICE 128
|
||||
|
||||
|
@ -413,11 +448,14 @@
|
|||
#define MISDN_ID_DUMMY 0xffff0001
|
||||
#define MISDN_ID_GLOBAL 0xffff0002
|
||||
|
||||
#define MAX_LAYER_NR 7
|
||||
#define ISDN_LAYER(n) (1<<n)
|
||||
#define MAX_LAYER_NR 7
|
||||
#define ISDN_LAYER(n) (1<<n)
|
||||
#define LAYER_OUTRANGE(layer) ((layer<0) || (layer>MAX_LAYER_NR))
|
||||
#define mISDN_MAX_IDLEN 16
|
||||
|
||||
/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
|
||||
#define mISDN_MAX_IDLEN 20
|
||||
|
||||
#ifdef OBSOLETE
|
||||
#define IF_NOACTIV 0x00000000
|
||||
#define IF_DOWN 0x01000000
|
||||
#define IF_UP 0x02000000
|
||||
|
@ -432,18 +470,53 @@
|
|||
#define IF_INSTMASK 0x400F0000
|
||||
#define IF_LAYERMASK 0x00F00000
|
||||
#define IF_TYPE(i) ((i)->stat & IF_TYPEMASK)
|
||||
#define CHILD_ID_INC 0x00000100
|
||||
#define CHILD_ID_MAX 0x1000FF00
|
||||
#define CLONE_ID_INC 0x00000100
|
||||
#define CLONE_ID_MAX 0x2000FF00
|
||||
#define INST_ID_INC 0x00010000
|
||||
#define INST_ID_MAX 0x400F0000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* general ID layout for instance and stack id's
|
||||
* 3322 2222 2222 1111 1111 1100 0000 0000
|
||||
* 1098 7654 3210 9876 5432 1098 7654 3210
|
||||
* FFFF FFFF CCCC CCCC SSSS SSSS RRRR LLLL
|
||||
*
|
||||
* L (bit 03-00) Layer ID
|
||||
* R (bit 06-04) reserved (0)
|
||||
* U (bit 07) user device id
|
||||
* S (bit 15-08) Stack ID/controller number
|
||||
* C (bit 23-16) Child/Clone ID
|
||||
* F (bit 31-24) Flags as defined below
|
||||
*
|
||||
*/
|
||||
|
||||
#define FLG_MSG_DOWN 0x01000000
|
||||
#define FLG_MSG_UP 0x02000000
|
||||
#define FLG_MSG_TARGET 0x04000000
|
||||
#define FLG_MSG_CLONED 0x08000000
|
||||
#define FLG_CHILD_STACK 0x10000000
|
||||
#define FLG_CLONE_STACK 0x20000000
|
||||
#define FLG_INSTANCE 0x40000000
|
||||
#define FLG_MSG_TAGGED 0x80000000
|
||||
#define MSG_DIR_MASK 0x03000000
|
||||
#define MSG_BROADCAST 0x03000000
|
||||
#define MSG_TO_OWNER 0x00000000
|
||||
|
||||
#define CHILD_ID_INC 0x00010000
|
||||
#define CHILD_ID_MAX 0x10FF0000
|
||||
#define CHILD_ID_MASK 0x00FF0000
|
||||
#define CLONE_ID_INC 0x00010000
|
||||
#define CLONE_ID_MAX 0x20FF0000
|
||||
#define CLONE_ID_MASK 0x00FF0000
|
||||
#define STACK_ID_INC 0x00000100
|
||||
#define STACK_ID_MAX 0x00007F00
|
||||
#define STACK_ID_MASK 0x30FFFF00
|
||||
#define MASTER_ID_MASK 0x0000FF00
|
||||
#define LAYER_ID_INC 0x00000001
|
||||
#define LAYER_ID_MAX MAX_LAYER_NR
|
||||
#define LAYER_ID_MASK 0x0000000F
|
||||
#define FLG_ID_USER 0x00000080
|
||||
#define INST_ID_MASK 0x70FFFFFF
|
||||
|
||||
#define DUMMY_CR_FLAG 0x7FFFFF00
|
||||
#define CONTROLER_MASK 0x000000FF
|
||||
// #define CONTROLER_MASK 0x0000FF
|
||||
|
||||
/* stack channel values */
|
||||
#define CHANNEL_NUMBER 0x000000FF
|
||||
|
@ -456,16 +529,35 @@
|
|||
#define CHANNEL_EXT_PCM 0x01000000
|
||||
#define CHANNEL_EXT_REV 0x02000000
|
||||
|
||||
/* interface extentions */
|
||||
/* instance/stack extentions */
|
||||
#define EXT_STACK_CLONE 0x00000001
|
||||
#define EXT_INST_CLONE 0x00000100
|
||||
#define EXT_INST_MGR 0x00000200
|
||||
#define EXT_INST_MIDDLE 0x00000400
|
||||
#define EXT_IF_CHAIN 0x00010000
|
||||
#define EXT_IF_EXCLUSIV 0x00020000
|
||||
#define EXT_IF_CREATE 0x00040000
|
||||
#define EXT_IF_SPLIT 0x00080000
|
||||
#define EXT_INST_UNUSED 0x00000800
|
||||
//#define EXT_IF_CHAIN 0x00010000
|
||||
//#define EXT_IF_EXCLUSIV 0x00020000
|
||||
//#define EXT_IF_CREATE 0x00040000
|
||||
//#define EXT_IF_SPLIT 0x00080000
|
||||
|
||||
/* stack status flag */
|
||||
#define mISDN_STACK_ACTION_MASK 0x0000ffff
|
||||
#define mISDN_STACK_COMMAND_MASK 0x000f0000
|
||||
#define mISDN_STACK_STATUS_MASK 0xfff00000
|
||||
/* action bits 0-15 */
|
||||
#define mISDN_STACK_WORK 0
|
||||
#define mISDN_STACK_SETUP 1
|
||||
#define mISDN_STACK_CLEARING 2
|
||||
#define mISDN_STACK_RESTART 3
|
||||
#define mISDN_STACK_WAKEUP 4
|
||||
#define mISDN_STACK_ABORT 15
|
||||
/* status bits 16-31 */
|
||||
#define mISDN_STACK_STOPPED 16
|
||||
#define mISDN_STACK_INIT 17
|
||||
#define mISDN_STACK_THREADSTART 18
|
||||
#define mISDN_STACK_ACTIVE 29
|
||||
#define mISDN_STACK_RUNNING 30
|
||||
#define mISDN_STACK_KILLED 31
|
||||
|
||||
/* special packet type */
|
||||
#define PACKET_NOACK 250
|
||||
|
@ -504,7 +596,7 @@ typedef struct _status_info {
|
|||
typedef struct _logdata {
|
||||
char *head;
|
||||
char *fmt;
|
||||
va_list args;
|
||||
va_list args;
|
||||
} logdata_t;
|
||||
|
||||
typedef struct _moditem {
|
||||
|
@ -533,6 +625,8 @@ typedef struct _stack_info {
|
|||
mISDN_stPara_t para;
|
||||
u_int extentions;
|
||||
u_int mgr;
|
||||
u_int master;
|
||||
u_int clone;
|
||||
int instcnt;
|
||||
int inst[MAX_LAYER_NR +1];
|
||||
int childcnt;
|
||||
|
@ -545,16 +639,19 @@ typedef struct _layer_info {
|
|||
int extentions;
|
||||
u_int id;
|
||||
u_int st;
|
||||
u_int clone;
|
||||
u_int parent;
|
||||
mISDN_pid_t pid;
|
||||
} layer_info_t;
|
||||
|
||||
|
||||
#ifdef OBSOLETE
|
||||
typedef struct _interface_info {
|
||||
int extentions;
|
||||
u_int owner;
|
||||
u_int peer;
|
||||
int stat;
|
||||
} interface_info_t;
|
||||
#endif
|
||||
|
||||
typedef struct _channel_info {
|
||||
u_int channel;
|
||||
|
@ -566,46 +663,76 @@ typedef struct _channel_info {
|
|||
|
||||
/* l3 pointer arrays */
|
||||
|
||||
typedef struct _ie_info {
|
||||
u16 off : 10,
|
||||
ridx : 3,
|
||||
res1 : 1,
|
||||
cs_flg : 1,
|
||||
repeated: 1;
|
||||
} __attribute__((packed)) ie_info_t;
|
||||
|
||||
typedef struct _ie_val {
|
||||
u16 codeset : 3,
|
||||
res1 : 5,
|
||||
val : 8;
|
||||
} __attribute__((packed)) ie_val_t;;
|
||||
|
||||
typedef struct _cs_info {
|
||||
u16 codeset : 3,
|
||||
locked : 1,
|
||||
res1 : 2,
|
||||
len : 10;
|
||||
} __attribute__((packed)) cs_info_t;
|
||||
|
||||
typedef struct _ie_info_ext {
|
||||
ie_info_t ie;
|
||||
union {
|
||||
ie_val_t v;
|
||||
cs_info_t cs;
|
||||
};
|
||||
} __attribute__((packed)) ie_info_ext_t;
|
||||
|
||||
typedef struct _Q931_info {
|
||||
u_char type __attribute__((packed));
|
||||
u_char crlen __attribute__((packed));
|
||||
u16 cr __attribute__((packed));
|
||||
u16 bearer_capability __attribute__((packed));
|
||||
u16 cause __attribute__((packed));
|
||||
u16 call_id __attribute__((packed));
|
||||
u16 call_state __attribute__((packed));
|
||||
u16 channel_id __attribute__((packed));
|
||||
u16 facility __attribute__((packed));
|
||||
u16 progress __attribute__((packed));
|
||||
u16 net_fac __attribute__((packed));
|
||||
u16 notify __attribute__((packed));
|
||||
u16 display __attribute__((packed));
|
||||
u16 date __attribute__((packed));
|
||||
u16 keypad __attribute__((packed));
|
||||
u16 signal __attribute__((packed));
|
||||
u16 info_rate __attribute__((packed));
|
||||
u16 end2end_transit __attribute__((packed));
|
||||
u16 transit_delay_sel __attribute__((packed));
|
||||
u16 pktl_bin_para __attribute__((packed));
|
||||
u16 pktl_window __attribute__((packed));
|
||||
u16 pkt_size __attribute__((packed));
|
||||
u16 closed_userg __attribute__((packed));
|
||||
u16 connected_nr __attribute__((packed));
|
||||
u16 connected_sub __attribute__((packed));
|
||||
u16 calling_nr __attribute__((packed));
|
||||
u16 calling_sub __attribute__((packed));
|
||||
u16 called_nr __attribute__((packed));
|
||||
u16 called_sub __attribute__((packed));
|
||||
u16 redirect_nr __attribute__((packed));
|
||||
u16 transit_net_sel __attribute__((packed));
|
||||
u16 restart_ind __attribute__((packed));
|
||||
u16 llc __attribute__((packed));
|
||||
u16 hlc __attribute__((packed));
|
||||
u16 useruser __attribute__((packed));
|
||||
u16 more_data __attribute__((packed));
|
||||
u16 sending_complete __attribute__((packed));
|
||||
u16 congestion_level __attribute__((packed));
|
||||
u16 fill1 __attribute__((packed));
|
||||
u_char type __attribute__((packed));
|
||||
u_char crlen __attribute__((packed));
|
||||
u16 cr __attribute__((packed));
|
||||
ie_info_t bearer_capability __attribute__((packed));
|
||||
ie_info_t cause __attribute__((packed));
|
||||
ie_info_t call_id __attribute__((packed));
|
||||
ie_info_t call_state __attribute__((packed));
|
||||
ie_info_t channel_id __attribute__((packed));
|
||||
ie_info_t facility __attribute__((packed));
|
||||
ie_info_t progress __attribute__((packed));
|
||||
ie_info_t net_fac __attribute__((packed));
|
||||
ie_info_t notify __attribute__((packed));
|
||||
ie_info_t display __attribute__((packed));
|
||||
ie_info_t date __attribute__((packed));
|
||||
ie_info_t keypad __attribute__((packed));
|
||||
ie_info_t signal __attribute__((packed));
|
||||
ie_info_t info_rate __attribute__((packed));
|
||||
ie_info_t end2end_transit __attribute__((packed));
|
||||
ie_info_t transit_delay_sel __attribute__((packed));
|
||||
ie_info_t pktl_bin_para __attribute__((packed));
|
||||
ie_info_t pktl_window __attribute__((packed));
|
||||
ie_info_t pkt_size __attribute__((packed));
|
||||
ie_info_t closed_userg __attribute__((packed));
|
||||
ie_info_t connected_nr __attribute__((packed));
|
||||
ie_info_t connected_sub __attribute__((packed));
|
||||
ie_info_t calling_nr __attribute__((packed));
|
||||
ie_info_t calling_sub __attribute__((packed));
|
||||
ie_info_t called_nr __attribute__((packed));
|
||||
ie_info_t called_sub __attribute__((packed));
|
||||
ie_info_t redirect_nr __attribute__((packed));
|
||||
ie_info_t transit_net_sel __attribute__((packed));
|
||||
ie_info_t restart_ind __attribute__((packed));
|
||||
ie_info_t llc __attribute__((packed));
|
||||
ie_info_t hlc __attribute__((packed));
|
||||
ie_info_t useruser __attribute__((packed));
|
||||
ie_info_t more_data __attribute__((packed));
|
||||
ie_info_t sending_complete __attribute__((packed));
|
||||
ie_info_t congestion_level __attribute__((packed));
|
||||
ie_info_t fill1 __attribute__((packed));
|
||||
ie_info_ext_t ext[8] __attribute__((packed));
|
||||
} Q931_info_t;
|
||||
|
||||
#define L3_EXTRA_SIZE sizeof(Q931_info_t)
|
||||
|
@ -617,15 +744,11 @@ typedef struct _Q931_info {
|
|||
|
||||
typedef struct _mISDNobject mISDNobject_t;
|
||||
typedef struct _mISDNinstance mISDNinstance_t;
|
||||
typedef struct _mISDNlayer mISDNlayer_t;
|
||||
typedef struct _mISDNstack mISDNstack_t;
|
||||
typedef struct _mISDNport mISDNport_t;
|
||||
typedef struct _mISDNdevice mISDNdevice_t;
|
||||
typedef struct _mISDNif mISDNif_t;
|
||||
typedef int (ctrl_func_t)(void *, u_int, void *);
|
||||
typedef int (if_func_t)(struct _mISDNif *, struct sk_buff *);
|
||||
typedef int (lock_func_t)(void *, int);
|
||||
typedef void (unlock_func_t)(void *);
|
||||
typedef int (if_func_t)(mISDNinstance_t *, struct sk_buff *);
|
||||
|
||||
#define mISDN_HEAD_P(s) ((mISDN_head_t *)&s->cb[0])
|
||||
#define mISDN_HEAD_PRIM(s) ((mISDN_head_t *)&s->cb[0])->prim
|
||||
|
@ -649,16 +772,19 @@ typedef struct _mISDN_headext {
|
|||
struct _mISDNobject {
|
||||
struct list_head list;
|
||||
char *name;
|
||||
int id;
|
||||
u_int id;
|
||||
int refcnt;
|
||||
mISDN_pid_t DPROTO;
|
||||
mISDN_pid_t BPROTO;
|
||||
ctrl_func_t *own_ctrl;
|
||||
ctrl_func_t *ctrl;
|
||||
struct list_head ilist;
|
||||
spinlock_t lock;
|
||||
struct module *owner;
|
||||
struct class_device class_dev;
|
||||
};
|
||||
|
||||
#ifdef OBSOLETE
|
||||
/* the interface between two mISDNinstances */
|
||||
struct _mISDNif {
|
||||
if_func_t *func;
|
||||
|
@ -671,6 +797,7 @@ struct _mISDNif {
|
|||
mISDNinstance_t *owner;
|
||||
mISDNinstance_t *peer;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* a instance of a mISDNobject */
|
||||
struct _mISDNinstance {
|
||||
|
@ -678,16 +805,19 @@ struct _mISDNinstance {
|
|||
char name[mISDN_MAX_IDLEN];
|
||||
int extentions;
|
||||
u_int id;
|
||||
int regcnt;
|
||||
mISDN_pid_t pid;
|
||||
mISDNstack_t *st;
|
||||
mISDNobject_t *obj;
|
||||
void *data;
|
||||
mISDNif_t up;
|
||||
mISDNif_t down;
|
||||
lock_func_t *lock;
|
||||
unlock_func_t *unlock;
|
||||
void *privat;
|
||||
mISDNinstance_t *clone;
|
||||
mISDNinstance_t *parent;
|
||||
if_func_t *function;
|
||||
spinlock_t *hwlock;
|
||||
struct class_device class_dev;
|
||||
};
|
||||
|
||||
#ifdef OBSOLETE
|
||||
/* a list of parallel (horizontal) mISDNinstances in the same layer
|
||||
* normally here is only one instance per layer only if the information
|
||||
* will be splitted here are more instances */
|
||||
|
@ -695,7 +825,7 @@ struct _mISDNlayer {
|
|||
struct list_head list;
|
||||
mISDNinstance_t *inst;
|
||||
};
|
||||
|
||||
#endif
|
||||
/* the STACK; a (vertical) chain of layers */
|
||||
|
||||
struct _mISDNstack {
|
||||
|
@ -704,16 +834,34 @@ struct _mISDNstack {
|
|||
u_int extentions;
|
||||
mISDN_pid_t pid;
|
||||
mISDN_stPara_t para;
|
||||
struct list_head layerlist;
|
||||
u_long status;
|
||||
struct task_struct *thread;
|
||||
struct semaphore *notify;
|
||||
wait_queue_head_t workq;
|
||||
struct sk_buff_head msgq;
|
||||
#ifdef MISDN_MSG_STATS
|
||||
u_int msg_cnt;
|
||||
u_int sleep_cnt;
|
||||
u_int clone_cnt;
|
||||
u_int stopped_cnt;
|
||||
#endif
|
||||
mISDNinstance_t *i_array[MAX_LAYER_NR + 1];
|
||||
struct list_head prereg;
|
||||
mISDNinstance_t *mgr;
|
||||
mISDNstack_t *master;
|
||||
mISDNstack_t *clone;
|
||||
mISDNstack_t *parent;
|
||||
struct list_head childlist;
|
||||
struct class_device class_dev;
|
||||
/* temporary use */
|
||||
mISDN_pid_t new_pid;
|
||||
};
|
||||
|
||||
/* lowlevel read/write struct for the mISDNdevice */
|
||||
struct _mISDNport {
|
||||
wait_queue_head_t procq;
|
||||
spinlock_t lock;
|
||||
mISDNif_t pif;
|
||||
// mISDNif_t pif;
|
||||
u_long Flag;
|
||||
struct sk_buff_head queue;
|
||||
u_int maxqlen;
|
||||
|
@ -735,9 +883,11 @@ struct _mISDNdevice {
|
|||
|
||||
/* common helper functions */
|
||||
extern int mISDN_bprotocol2pid(void *, mISDN_pid_t *);
|
||||
extern int mISDN_SetIF(mISDNinstance_t *, mISDNif_t *, u_int, void *, void *, void *);
|
||||
extern int mISDN_ConnectIF(mISDNinstance_t *, mISDNinstance_t *);
|
||||
extern int mISDN_DisConnectIF(mISDNinstance_t *, mISDNif_t *);
|
||||
// extern int mISDN_SetIF(mISDNinstance_t *, mISDNif_t *, u_int, void *, void *, void *);
|
||||
// extern int mISDN_ConnectIF(mISDNinstance_t *, mISDNinstance_t *);
|
||||
// extern int mISDN_DisConnectIF(mISDNinstance_t *, mISDNif_t *);
|
||||
extern int mISDN_queue_message(mISDNinstance_t *, u_int, struct sk_buff *);
|
||||
|
||||
|
||||
/* global register/unregister functions */
|
||||
|
||||
|
|
|
@ -0,0 +1,544 @@
|
|||
#!/bin/bash
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# misdn-init init script
|
||||
#
|
||||
# Copyright (C) 2005, Nadi Sarrar
|
||||
#
|
||||
# Nadi Sarrar <nadi@beronet.com>
|
||||
#
|
||||
# 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=<number>,<type>[,<option>...]
|
||||
#
|
||||
# <number> count your cards beginning with 1
|
||||
# <type> either 0x1,0x4 or 0x8 for your hfcmulti hardware,
|
||||
# or the name of your card driver module.
|
||||
# <option> ulaw - uLaw (instead of aLaw)
|
||||
# dtmf - enable DTMF detection on all B-channels
|
||||
# pcm_slave - set PCM bus into slave mode
|
||||
#"
|
||||
misdn_cfg_pt2="#
|
||||
# Port settings
|
||||
#
|
||||
# Syntax: <port_type>=<port_number>[,<port_number>...]
|
||||
#
|
||||
# <port_type> te_ptp - TE-Mode, PTP
|
||||
# te_ptmp - TE-Mode, PTMP
|
||||
# te_capi_ptp - TE-Mode (capi), PTP
|
||||
# te_capi_ptmp - TE-Mode (capi), PTMP
|
||||
# nt_ptp - NT-Mode, PTP
|
||||
# nt_ptmp - NT-Mode, PTMP
|
||||
# <port_number> port that should be considered
|
||||
#"
|
||||
misdn_cfg_pt3="#
|
||||
# Port Options
|
||||
#
|
||||
# Syntax: option=<port_number>,<option>[,<option>...]
|
||||
#
|
||||
# <option> master_clock - use master clock for this S/T interface
|
||||
# (only once per chip, only for HFC 8/4)
|
||||
# optical - optical (only HFC-E1)
|
||||
# los - report LOS (only HFC-E1)
|
||||
# ais - report AIS (only HFC-E1)
|
||||
# slip - report SLIP (only HFC-E1)
|
||||
# nocrc4 - turn off crc4 mode use double frame instead
|
||||
# (only HFC-E1)
|
||||
#
|
||||
#option=1,master_clock
|
||||
#option=2,ais,nocrc4
|
||||
#option=3,optical,los,ais,slip
|
||||
|
||||
|
||||
#
|
||||
# General Options for your hfcmulti hardware
|
||||
#
|
||||
# poll=<number>
|
||||
#
|
||||
# Only one poll value must be given for all cards.
|
||||
# Give the number of samples for each fifo process.
|
||||
# By default 128 is used. Decrease to reduce delay, increase to
|
||||
# reduce cpu load. If unsure, don't mess with it!!!
|
||||
# Valid is 32, 64, 128, 256.
|
||||
#
|
||||
# pcm=<number>
|
||||
#
|
||||
# Give the id of the PCM bus. All PCM busses with the same ID
|
||||
# are expected to be connected and have equal slots.
|
||||
# Only one chip of the PCM bus must be master, the others slave.
|
||||
# -1 means no support of PCM bus.
|
||||
#
|
||||
# debug=<number>
|
||||
#
|
||||
# Enable debugging (see hfc_multi.h for debug options).
|
||||
#
|
||||
poll=128
|
||||
#pcm=1
|
||||
debug=0"
|
||||
|
||||
if [ -f ${misdn_init_conf} ]; then
|
||||
cp "${misdn_init_conf}" "${misdn_init_conf}.save" || die "could not backup your existing ${misdn_init_conf}!"
|
||||
echo "[OK] ${misdn_init_conf} already present. backing it up to ${misdn_init_conf}.save"
|
||||
fi
|
||||
echo "${misdn_cfg_pt1}${NL}${cardline}${NL}${misdn_cfg_pt2}${NL}${portline}${NL}${NL}${misdn_cfg_pt3}" > ${misdn_init_conf} || die "could not write to /etc/misdn-init.conf!"
|
||||
#echo "${misdn_cfg_pt1}${NL}${cardline}${NL}${misdn_cfg_pt2}${NL}${portline}${NL}${NL}${misdn_cfg_pt3}" > testconf || die "could not write to /etc/misdn-init.conf!"
|
||||
|
||||
echo "[OK] ${misdn_init_conf} created. It's now safe to run \"/etc/init.d/misdn-init start\""
|
||||
if [ ${portcount} -gt 1 ]; then
|
||||
echo "[ii] make your ports (1-${portcount}) available in asterisk by editing \"/etc/asterisk/misdn.conf\""
|
||||
elif [ ${portcount} -eq 1 ]; then
|
||||
echo "[ii] make your port (1) available in asterisk by editing \"/etc/asterisk/misdn.conf\""
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function check_cfg_file {
|
||||
if [ ! -f ${misdn_init_conf} ]; then
|
||||
echo "[!!] failed to load: ${misdn_init_conf}"
|
||||
echo "run \"/etc/init.d/misdn-init config\" to scan your devices and generate a basic config file."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# MAIN #############
|
||||
|
||||
case "$1" in
|
||||
start|--start)
|
||||
|
||||
remove_preloaded_modules
|
||||
|
||||
check_cfg_file
|
||||
|
||||
$MODPROBE mISDN_core debug=0
|
||||
$MODPROBE mISDN_l1 debug=0
|
||||
$MODPROBE mISDN_l2 debug=0
|
||||
$MODPROBE l3udss1 debug=0
|
||||
$MODPROBE mISDN_dsp debug=0x0 options=0x0
|
||||
$MODPROBE mISDN_capi
|
||||
|
||||
load_card_modules
|
||||
|
||||
sleep 1
|
||||
|
||||
if [ ! -e /dev/mISDN ]; then
|
||||
$MKNOD /dev/mISDN c 46 0
|
||||
echo "[i] creating device node: /dev/mISDN"
|
||||
fi
|
||||
;;
|
||||
|
||||
stop|--stop)
|
||||
|
||||
check_cfg_file
|
||||
|
||||
unload_card_modules
|
||||
|
||||
for mod in $(lsmod | sed -ne '/Module/!{s/\([^ ]*\).*/\1/;p}');
|
||||
do
|
||||
case "${mod}" in
|
||||
mISDN_capi | mISDN_dsp | l3udss1 | mISDN_l2 | mISDN_l1 | mISDN_isac | mISDN_core)
|
||||
eval "${RMMOD} ${mod}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
|
||||
restart|--restart)
|
||||
|
||||
check_cfg_file
|
||||
|
||||
sh $0 stop
|
||||
sleep 2 # some phones will release tei when layer 1 is down
|
||||
sh $0 start
|
||||
;;
|
||||
|
||||
config|--config)
|
||||
|
||||
create_misdn_init_conf
|
||||
|
||||
;;
|
||||
|
||||
scan|--scan)
|
||||
|
||||
create_misdn_init_conf scan
|
||||
|
||||
;;
|
||||
|
||||
help|--help)
|
||||
echo "Usage: $0 {start|stop|restart|config|scan|help}"
|
||||
exit 0
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|config|scan|help}"
|
||||
exit 2
|
||||
;;
|
||||
|
||||
esac
|
||||
|
19
std2kern
19
std2kern
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
KERNELDIR=/usr/src/linux
|
||||
KERNELDIR=/lib/modules/$(uname -r)/build
|
||||
PREPARSER="./preparser"
|
||||
UNIQUE=false
|
||||
VERBOSE=false
|
||||
|
@ -224,6 +224,23 @@ while getopts :dhk:uc:vtidr a ; do
|
|||
done
|
||||
shift `expr $OPTIND - 1`
|
||||
|
||||
echo
|
||||
echo "BE AWARE!!"
|
||||
echo
|
||||
echo "You are just attempting to overwrite your Kernel mISDN Sources"
|
||||
echo "you probably prefer to use make install."
|
||||
echo
|
||||
echo "KERNELDIR=$KERNELDIR"
|
||||
ls -ld $KERNELDIR
|
||||
echo
|
||||
echo "If you still want to patch this Kernel just answer yes:"
|
||||
read i
|
||||
|
||||
if [ ! $i == "yes" ] ; then
|
||||
echo "OK exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$VERSION" -o -z "$PATCHLEVEL" ] ; then
|
||||
if ! [ -f $KERNELDIR/Makefile ] ; then
|
||||
echo "VERSION/PATCHLEVEL not set and no Makefile to read from"
|
||||
|
|
Loading…
Reference in New Issue