mISDN-1_1_9.1/0000755000000000000000000000000011137306107011432 5ustar rootrootmISDN-1_1_9.1/add.config0000644000000000000500000000026411110524073013157 0ustar rootsrc# # Modular ISDN driver # CONFIG_MISDN_DRV=m CONFIG_MISDN_AVM_FRITZ=y CONFIG_MISDN_HFCPCI=y CONFIG_MISDN_SPEEDFAX=y CONFIG_MISDN_W6692=y CONFIG_MISDN_DSP=y CONFIG_MISDN_MEMDEBUG=y mISDN-1_1_9.1/CHANGES0000644000000000000500000000043711135651701012243 0ustar rootsrcmISDN-1-1-2: - added a workaround that fixes a kernel panic when bridging is done after already a few voice frames where transceived on both legs (like when you transfer a call from SIP 2 ISDN) - jollys mail has changed - minor tweaks to misdn-init and to the Kernel-Patch script mISDN-1_1_9.1/config/0002755000000000000500000000000011135651701012513 5ustar rootsrcmISDN-1_1_9.1/config/Makefile0000644000000000000500000000070411110524073014144 0ustar rootsrc all: @echo "Please run 'make install'." install: install -D -m755 mISDN $(INSTALL_PREFIX)/usr/sbin/mISDN for file in $(shell echo *.xsl); do install -D -m644 $${file} $(INSTALL_PREFIX)/usr/lib/mISDN/$${file}; done if [ -d $(INSTALL_PREFIX)/etc/init.d ]; then \ if [ -e $(INSTALL_PREFIX)/etc/init.d/mISDN ]; then rm -rf $(INSTALL_PREFIX)/etc/init.d/mISDN; fi; \ ln -s $(INSTALL_PREFIX)/usr/sbin/mISDN $(INSTALL_PREFIX)/etc/init.d/mISDN; \ fi mISDN-1_1_9.1/config/README.mISDN0000644000000000000500000000505211135651701014304 0ustar rootsrc'mISDN': 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.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 script. --------------------------------------------------------------------------- Requirements: The 'mISDN' script requires you to install the tool 'xsltproc'. To install xsltproc on debian, just type: $ apt-get install xsltproc (as root) On other distros the package name might be libxmtools or likewise. --------------------------------------------------------------------------- Usage: /usr/sbin/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.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: 1) Get and install mISDN: $ wget http://www.misdn.org/downloads/mISDN.tar.gz $ tar xzf mISDN.tar.gz $ cd mISDN* $ make install 2) Let mISDN scan your PCI bus for mISDN compatible hardware and write the results into /etc/mISDN.conf: $ (as root) mISDN config 3) (optional) Edit /etc/mISDN.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' 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' 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 start 60 2 3 4 5 . stop 60 0 1 6 . 5) Run the following to start mISDN: $ (as root) mISDN start --------------------------------------------------------------------------- * Report Bugs: If you experience any bugs or have a feature request, please visit: www.isdn4linux.de/mantis mISDN-1_1_9.1/config/mISDN0000755000000000000500000003775211135651701013367 0ustar rootsrc#!/bin/bash #---------------------------------------------- # # CONFIGURATION: # MISDN_CONF="/etc/mISDN.conf" MISDN_CONF_XSL="/usr/lib/mISDN/mISDN.conf.xsl" # #---------------------------------------------- SELF="${0}" USAGE="Usage: ${SELF} start|stop|restart|config|scan|help" function die { echo "[!!] ${1}" exit 1 } function check_cmd { if ! which "${1}" > /dev/null; then if [ "${2}" = "opt" ]; then return fi if [ "$(id -u)" != "0" ]; then die "$1 not in path, please install and/or be root." else die "$1 not in path, please install." fi exit 1 else local var=$(echo ${1} | tr a-z A-Z) eval "$var=`which ${1}`" fi } function check_misdn_conf { if [ ! -f ${MISDN_CONF} ]; then die "${MISDN_CONF} not found. Please run: ${SELF} config" fi } check_cmd sed check_cmd cut check_cmd cp check_cmd wc check_cmd grep check_cmd xsltproc check_cmd modprobe check_cmd sleep check_cmd lspci check_cmd lsusb opt check_cmd mknod check_cmd chown check_cmd chmod declare -a START_COMMANDS declare -a STOP_COMMANDS declare -a HFCMULTI_card declare -a HFCMULTI_type declare -a HFCMULTI_protocol declare -a HFCMULTI_layermask HFCMULTI_options='' MISDNDSP_options='' L1OIP_options='' AVMFRITZ_protocol='' AVMFRITZ_layermask='' HFCPCI_protocol='' HFCPCI_layermask='' HFCSUSB_protocol='' HFCSUSB_layermask='' HFCSUSB_options='' XHFC_protocol='' XHFC_layermask='' XHFC_options='' L1OIP_type='' L1OIP_protocol='' L1OIP_layermask='' L1OIP_codec='' L1OIP_ip='' L1OIP_port='' L1OIP_localport='' L1OIP_ondemand='' L1OIP_id='' DEVNODE_user='root' DEVNODE_group='root' DEVNODE_mode='0644' declare -a SCAN_card declare -a SCAN_opts declare -a SCAN_num_ports declare -a SCAN_port_opts function parse_config { local CONFIG=$(${XSLTPROC} ${MISDN_CONF_XSL} ${MISDN_CONF}) local t p l line i tmpcmd curr tmpstr extra_modules val local IFS=$'\n' START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install capi" START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install mISDN_core debug=0" START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install mISDN_l1 debug=0" START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install mISDN_l2 debug=0" START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install l3udss1 debug=0" START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install mISDN_capi" for line in ${CONFIG}; do case "${line}" in DEVNODE:mISDN*) tmpstr=$(echo ${line} | ${SED} -n 's/.*user:\([^ ]*\).*/\1/p') if [ ! -z "${tmpstr}" ]; then DEVNODE_user="${tmpstr}" fi tmpstr=$(echo ${line} | ${SED} -n 's/.*group:\([^ ]*\).*/\1/p') if [ ! -z "${tmpstr}" ]; then DEVNODE_group="${tmpstr}" fi tmpstr=$(echo ${line} | ${SED} -n 's/.*mode:\([^ ]*\).*/\1/p') if [ ! -z "${tmpstr}" ]; then DEVNODE_mode="${tmpstr}" fi ;; MODULE:hfcmulti*) HFCMULTI_options=${line:16} ;; MODULE:hfcsusb*) HFCSUSB_options=${line:15} ;; MODULE:xhfc*) XHFC_options=${line:12} ;; MODULE:mISDN_debugtool*) extra_modules[${#extra_modules[@]}]=${line:7} ;; MODULE:mISDN_dsp*) MISDNDSP_options=${line:17} ;; MODULE:l1oip*) L1OIP_options=${line:13} ;; CARD:BN*) curr='hfcmulti' i=${#HFCMULTI_type[@]} let "t = $(echo ${line} | ${SED} -n 's/.*type:\([^,]*\).*/\1/p')" HFCMULTI_type[${i}]=$(printf "0x%x" ${t}) # this is for the BN2E1 card that needs two type numbers t=$(echo ${line} | ${SED} -n 's/.*type:[^,]*,\([^ ]*\).*/\1/p') if [ ! -z "${t}" ]; then let "t = ${t}" HFCMULTI_type[${i}]="${HFCMULTI_type[${i}]},$(printf "0x%x" ${t})" fi HFCMULTI_card[${i}]=$(echo ${line:5} | ${CUT} -d" " -f1) ;; CARD:hfcpci*) curr='hfcpci' ;; CARD:hfcsusb*) curr='hfcsusb' ;; CARD:xhfc*) curr='xhfc' ;; CARD:avmfritz*) curr='avmfritz' ;; CARD:l1oip*) curr='l1oip' ;; PORT*) case "${curr}" in hfcmulti) let "p = $(echo ${line} | ${SED} -n 's/.*protocol:\([^ ]*\).*/\1/p')" HFCMULTI_protocol[${i}]="${HFCMULTI_protocol[${i}]:+"${HFCMULTI_protocol[${i}]},"}$(printf "0x%x" ${p})" let "l = $(echo ${line} | ${SED} -n 's/.*layermask:\([^ ]*\).*/\1/p')" HFCMULTI_layermask[${i}]="${HFCMULTI_layermask[${i}]:+"${HFCMULTI_layermask[${i}]},"}$(printf "0x%x" ${l})" ;; hfcpci) let "p = $(echo ${line} | ${SED} -n 's/.*protocol:\([^ ]*\).*/\1/p')" HFCPCI_protocol="${HFCPCI_protocol:+"${HFCPCI_protocol},"}$(printf "0x%x" ${p})" let "l = $(echo ${line} | ${SED} -n 's/.*layermask:\([^ ]*\).*/\1/p')" HFCPCI_layermask="${HFCPCI_layermask:+"${HFCPCI_layermask},"}$(printf "0x%x" ${l})" ;; hfcsusb) let "p = $(echo ${line} | ${SED} -n 's/.*protocol:\([^ ]*\).*/\1/p')" HFCSUSB_protocol="${HFCSUSB_protocol:+"${HFCSUSB_protocol},"}$(printf "0x%x" ${p})" let "l = $(echo ${line} | ${SED} -n 's/.*layermask:\([^ ]*\).*/\1/p')" HFCSUSB_layermask="${HFCSUSB_layermask:+"${HFCSUSB_layermask},"}$(printf "0x%x" ${l})" ;; xhfc) let "p = $(echo ${line} | ${SED} -n 's/.*protocol:\([^ ]*\).*/\1/p')" XHFC_protocol="${XHFC_protocol:+"${XHFC_protocol},"}$(printf "0x%x" ${p})" let "l = $(echo ${line} | ${SED} -n 's/.*layermask:\([^ ]*\).*/\1/p')" XHFC_layermask="${XHFC_layermask:+"${XHFC_layermask},"}$(printf "0x%x" ${l})" ;; avmfritz) let "p = $(echo ${line} | ${SED} -n 's/.*protocol:\([^ ]*\).*/\1/p')" AVMFRITZ_protocol="${AVMFRITZ_protocol:+"${AVMFRITZ_protocol},"}$(printf "0x%x" ${p})" let "l = $(echo ${line} | ${SED} -n 's/.*layermask:\([^ ]*\).*/\1/p')" AVMFRITZ_layermask="${AVMFRITZ_layermask:+"${AVMFRITZ_layermask},"}$(printf "0x%x" ${l})" ;; l1oip) let "val = $(echo ${line} | ${SED} -n 's/.*type:\([^ ]*\).*/\1/p')" L1OIP_type="${L1OIP_type:+"${L1OIP_type},"}$(printf "0x%x" ${val})" let "val = $(echo ${line} | ${SED} -n 's/.*protocol:\([^ ]*\).*/\1/p')" L1OIP_protocol="${L1OIP_protocol:+"${L1OIP_protocol},"}$(printf "0x%x" ${val})" let "val = $(echo ${line} | ${SED} -n 's/.*layermask:\([^ ]*\).*/\1/p')" L1OIP_layermask="${L1OIP_layermask:+"${L1OIP_layermask},"}$(printf "0x%x" ${val})" val="$(echo ${line} | ${SED} -n 's/.*codec:\([^ ]*\).*/\1/p')" L1OIP_codec="${L1OIP_codec:+"${L1OIP_codec},"}${val}" val="$(echo ${line} | ${SED} -n 's/.*ip:\([^ ]*\).*/\1/p')" L1OIP_ip="${L1OIP_ip:+"${L1OIP_ip},"}${val}" val="$(echo ${line} | ${SED} -n 's/.*port:\([^ ]*\).*/\1/p')" L1OIP_port="${L1OIP_port:+"${L1OIP_port},"}${val}" val="$(echo ${line} | ${SED} -n 's/.*localport:\([^ ]*\).*/\1/p')" L1OIP_localport="${L1OIP_localport:+"${L1OIP_localport},"}${val}" val="$(echo ${line} | ${SED} -n 's/.*ondemand:\([^ ]*\).*/\1/p')" L1OIP_ondemand="${L1OIP_ondemand:+"${L1OIP_ondemand},"}${val}" val="$(echo ${line} | ${SED} -n 's/.*id:\([^ ]*\).*/\1/p')" L1OIP_id="${L1OIP_id:+"${L1OIP_id},"}${val}" ;; esac ;; esac done if [ ! -z "${HFCMULTI_protocol[0]}" ]; then tmpcmd="${MODPROBE} --ignore-install hfcmulti type=${HFCMULTI_type[0]}" i=1 while [ ! -z "${HFCMULTI_type[${i}]}" ]; do tmpcmd="${tmpcmd},${HFCMULTI_type[${i}]}" let "i = ${i} + 1" done tmpcmd="${tmpcmd} protocol=${HFCMULTI_protocol[0]}" i=1 while [ ! -z "${HFCMULTI_protocol[${i}]}" ]; do tmpcmd="${tmpcmd},${HFCMULTI_protocol[${i}]}" let "i = ${i} + 1" done tmpcmd="${tmpcmd} layermask=${HFCMULTI_layermask[0]}" i=1 while [ ! -z "${HFCMULTI_layermask[${i}]}" ]; do tmpcmd="${tmpcmd},${HFCMULTI_layermask[${i}]}" let "i = ${i} + 1" done START_COMMANDS[${#START_COMMANDS[@]}]="${tmpcmd} ${HFCMULTI_options}" fi if [ ! -z "${HFCPCI_protocol}" ]; then START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install hfcpci protocol=${HFCPCI_protocol} layermask=${HFCPCI_layermask}" fi if [ ! -z "${HFCSUSB_protocol}" ]; then START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install hfcsusb protocol=${HFCSUSB_protocol} layermask=${HFCSUSB_layermask} ${HFCSUSB_options}" fi if [ ! -z "${XHFC_protocol}" ]; then START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install xhfc protocol=${XHFC_protocol} layermask=${XHFC_layermask} ${XHFC_options}" fi if [ ! -z "${AVMFRITZ_protocol}" ]; then START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install avmfritz protocol=${AVMFRITZ_protocol} layermask=${AVMFRITZ_layermask}" fi if [ ! -z "${L1OIP_type}" ]; then START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install l1oip type=${L1OIP_type} protocol=${L1OIP_protocol} layermask=${L1OIP_layermask} codec=${L1OIP_codec} ip=${L1OIP_ip} port=${L1OIP_port} localport=${L1OIP_localport} ondemand=${L1OIP_ondemand} id=${L1OIP_id} ${L1OIP_options}" fi START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install mISDN_dsp ${MISDNDSP_options}" i=1 while [ ! -z "${extra_modules[${i}]}" ]; do START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install ${extra_modules[${i}]}" let "i = ${i} + 1" done } function run_start_commands { local i=0 echo "-- Loading mISDN modules --" while [ ! -z "${START_COMMANDS[${i}]}" ]; do echo ">> ${START_COMMANDS[${i}]}" eval "${START_COMMANDS[${i}]}" let "i = ${i} + 1" done } function run_stop_commands { local mod i=0 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 | hfcmulti | hfcpci | hfcsusb | xhfc | avmfritz | l1oip) STOP_COMMANDS[0]="${STOP_COMMANDS[0]:-"${MODPROBE} -r --ignore-remove"} ${mod}" ;; mISDN_debugtool) STOP_COMMANDS[1]="${MODPROBE} -r --ignore-remove mISDN_debugtool" ;; mISDN_core) STOP_COMMANDS[2]="${MODPROBE} -r --ignore-remove mISDN_core" ;; esac done echo "-- Unloading mISDN modules --" for i in `seq 0 1 2`; do if [ ! -z "${STOP_COMMANDS[${i}]}" ]; then echo ">> ${STOP_COMMANDS[${i}]}" eval "${STOP_COMMANDS[${i}]}" fi done } function scan_devices { local skipnext=0 IFS=$'\n' local NL=" " function addcard { SCAN_card[${#SCAN_card[@]}]="${1}" SCAN_opts[${#SCAN_opts[@]}]="${2}" SCAN_num_ports[${#SCAN_num_ports[@]}]="${3}" SCAN_port_opts[${#SCAN_port_opts[@]}]="${4}" } for line in $(${LSPCI} -n -d 0xd161:b410); do addcard "BN4S0" "" 4 'mode="te" link="ptmp"' done for line in $(${LSPCI} -n | ${SED} -n 's/^\(0000:\|\)\([0-9a-f]\{2\}:[0-9a-f]\{2\}.[0-9a-f]\{1\}\)\( Class \| \)[0-9a-f]\{4\}: 1397:\([0-9a-f]\{4\}\).*$/\4 \2/p'); do if [ ${skipnext} -eq 1 ]; then skipnext=0 continue fi case "${line}" in 30b1*) case "${line:5}" in 00*) addcard "BN1E1" "" 1 'mode="nt" link="ptp"' ;; *) if [ $(${LSPCI} -n -s "${line:5:3}" -d 0x1397:30b1 | ${WC} -l) -eq 2 ]; then addcard "BN2E1" "" 2 'mode="nt" link="ptp"' skipnext=1 else addcard "BN1E1" "" 1 'mode="nt" link="ptp"' fi ;; esac ;; 16b8*) addcard "BN8S0" "" 8 'mode="te" link="ptmp"' ;; 08b4*) if ${LSPCI} -n -v -s "${line:5}" | ${GREP} "Subsystem" | ${GREP} "1397:b567" > /dev/null ; then addcard "BN1S0" "" 1 'mode="te" link="ptmp"' elif ${LSPCI} -n -v -s "${line:5}" | ${GREP} "Subsystem" | ${GREP} "1397:b566\|1397:b569" > /dev/null ; then addcard "BN2S0" "" 2 'mode="te" link="ptmp"' else addcard "BN4S0" "" 4 'mode="te" link="ptmp"' fi ;; esac 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" "" 1 'mode="te" link="ptmp"' done for line in $(${LSPCI} -n -d 0x1397:a003); do addcard "xhfc" "" 4 'mode="te" link="ptmp"' done for line in $(${LSPCI} -n | ${GREP} "1244:\(0a00\|0e00\)"); do addcard "avmfritz" "" 1 'mode="te" link="ptmp"' done for line in $(${LSPCI} -n -d 1050:6692); do addcard "w6692pci" "" 1 'mode="te" link="ptmp"' done if [ -e ${LSUSB} ]; then for line in $(${LSUSB} | ${GREP} "0959:2bd0\|0675:1688\|07b0:0007\|0742:200\(7\|8\|9\|A\)\|08e3:0301\|07fa:084\(7\|8\)\|07ba:0006\|0586:0102"); do addcard "hfcsusb" "" 1 'mode="te" link="ptmp"' done fi } function write_mISDN_conf { local NL=" " local TAB=" " local HEADER=" ${TAB}hfcmulti ${TAB}mISDN_dsp ${TAB}mISDN" local FOOTER="" local i=0 j=0 MAIN="" echo "Writing ${MISDN_CONF} for ${#SCAN_card[@]} mISDN compatible device(s):" while [ ! -z "${SCAN_card[${i}]}" ]; do echo ">> ${SCAN_card[${i}]}" MAIN="${MAIN}${NL}${TAB}" j=1 while [ ${j} -le ${SCAN_num_ports[${i}]} ]; do MAIN="${MAIN}${NL}${TAB}${TAB}${j}" let "j = ${j} + 1" done MAIN="${MAIN}${NL}${TAB}" let "i = ${i} + 1" done if [ -f ${MISDN_CONF} ]; then echo "${MISDN_CONF} already present, saving a backup: ${MISDN_CONF}.bak" ${CP} "${MISDN_CONF}" "${MISDN_CONF}.bak" || die "Could not backup your existing ${MISDN_CONF}!" fi echo "${HEADER}${MAIN}${NL}${FOOTER}" > ${MISDN_CONF} } function print_scan_results { local i=0 echo "${#SCAN_card[@]} mISDN compatible device(s) found:" while [ ! -z "${SCAN_card[${i}]}" ]; do echo ">> ${SCAN_card[${i}]}" let "i = ${i} + 1" done } function mk_misdn_dev { if [ ! -e /dev/mISDN ]; then echo "creating device node: /dev/mISDN" ${MKNOD} /dev/mISDN c 46 0 fi ${CHOWN} ${DEVNODE_user}:${DEVNODE_group} /dev/mISDN ${CHMOD} ${DEVNODE_mode} /dev/mISDN } # # MAIN # case "${1}" in start|--start) check_misdn_conf parse_config run_start_commands mk_misdn_dev ;; stop|--stop) run_stop_commands ;; restart|--restart) check_misdn_conf parse_config run_stop_commands ${SLEEP} 2 run_start_commands mk_misdn_dev ;; config|--config) scan_devices write_mISDN_conf ;; scan|--scan) scan_devices print_scan_results ;; help|--help) echo "${USAGE}" exit 0 ;; *) echo "${USAGE}" exit 2 ;; esac mISDN-1_1_9.1/config/mISDN.conf0000644000000000000500000000060511110524073014265 0ustar rootsrc 1 2 3 4 5 6 7 8 mISDN-1_1_9.1/config/mISDN.conf.bnx.xsl0000644000000000000500000001654411135651701015677 0ustar rootsrc no (2**8) + (2**9) + (2**11) (2**11) + (2**12) + (2**13) + (2**18) + (2**19) 4 layermask: 3 0 15 protocol: te nt 34 18 34 + ptp ptmp 0 (-32) (-32) + (2**16) capi: yes no no 4 8 type:1+ ,1+ yes layermask: 3 0 15 protocol: te nt 34 18 34 + ptp ptmp 0 (-32) (-32) + (2**16) + (2**18) + (2**19) + (2**21) + (2**23) capi: yes no no mISDN-1_1_9.1/config/mISDN.conf.hfcmulti.xsl0000644000000000000500000000233111110524073016702 0ustar rootsrc poll= 128 pcm= debug= 0 timer= 0 1 mISDN-1_1_9.1/config/mISDN.conf.hfcsusb.xsl0000644000000000000500000000141311110524073016524 0ustar rootsrc debug= 0 poll= 0 mISDN-1_1_9.1/config/mISDN.conf.inc.xsl0000644000000000000500000000367011110524073015647 0ustar rootsrc 0 0 yes no no 0 0 0 yes no mISDN-1_1_9.1/config/mISDN.conf.l1oip.xsl0000644000000000000500000000714311135651701016127 0ustar rootsrc debug= 0 type: 1 2 3 4 1 layermask: 3 15 protocol: te nt 34 18 34 + ptp ptmp 0 (-32) (-32) codec: 0 ip: 0,0,0,0 port: 0 localport: 0 ondemand: 0 id: 0 mISDN-1_1_9.1/config/mISDN.conf.mISDN_debugtool.xsl0000644000000000000500000000077611110524073020060 0ustar rootsrc PORT= mISDN-1_1_9.1/config/mISDN.conf.mISDN_dsp.xsl0000644000000000000500000000221311110524073016646 0ustar rootsrc debug= 0 options= 0 poll= dtmfthreshold= mISDN-1_1_9.1/config/mISDN.conf.singlepci.xsl0000644000000000000500000000355011110524073017050 0ustar rootsrc layermask: 3 0 15 protocol: te nt 34 18 34 + ptp ptmp 0 (-32) (-32) capi: yes no no mISDN-1_1_9.1/config/mISDN.conf.xhfc.xsl0000644000000000000500000001026111135651701016026 0ustar rootsrc debug= 0 layermask: 3 0 15 protocol: te nt 2 18 34 + ptp ptmp 1024 0 0 + up s0 32 0 0 + yes no 128 0 0 + yes no 256 0 0 + yes no 512 0 0 + yes no 64 0 0 capi: yes no no mISDN-1_1_9.1/config/mISDN.conf.xsl0000644000000000000500000001223611135651701015103 0ustar rootsrc user: root group: root mode: 644 PORT: PORT: PORT: PORT: PORT: PORT: PORT: PORT: mISDN-1_1_9.1/drivers/0002755000000000000500000000000011110524073012716 5ustar rootsrcmISDN-1_1_9.1/drivers/isdn/0002755000000000000500000000000011110524073013653 5ustar rootsrcmISDN-1_1_9.1/drivers/isdn/Config.in.v2.40000644000000000000500000002034711110524073016104 0ustar rootsrc# # ISDN device configuration # # only included if CONFIG_ISDN != n define_bool CONFIG_ISDN_BOOL y if [ "$CONFIG_INET" != "n" ]; then bool ' Support synchronous PPP' CONFIG_ISDN_PPP if [ "$CONFIG_ISDN_PPP" != "n" ]; then bool ' Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ bool ' Support generic MP (RFC 1717)' CONFIG_ISDN_MPP dep_tristate ' Support BSD compression' CONFIG_ISDN_PPP_BSDCOMP $CONFIG_ISDN fi fi bool ' Support audio via ISDN' CONFIG_ISDN_AUDIO if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then bool ' Support AT-Fax Class 1 and 2 commands' CONFIG_ISDN_TTY_FAX fi if [ "$CONFIG_X25" != "n" ]; then bool ' X.25 PLP on top of ISDN' CONFIG_ISDN_X25 fi mainmenu_option next_comment comment 'ISDN feature submodules' dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN dep_tristate 'Support isdn diversion services' CONFIG_ISDN_DIVERSION $CONFIG_ISDN endmenu comment 'low-level hardware drivers' mainmenu_option next_comment comment 'Passive ISDN cards' dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then define_bool CONFIG_ISDN_HISAX y comment ' D-channel protocol features' bool ' HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO if [ "$CONFIG_HISAX_EURO" != "n" ]; then bool ' Support for german chargeinfo' CONFIG_DE_AOC bool ' Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE bool ' Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC bool ' Disable keypad protocol option' CONFIG_HISAX_NO_KEYPAD fi bool ' HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 bool ' HiSax Support for US NI1' CONFIG_HISAX_NI1 int ' Maximum number of cards supported by HiSax' CONFIG_HISAX_MAX_CARDS 8 comment ' HiSax supported cards' if [ "$CONFIG_ISA" != "n" ]; then bool ' Teles 16.0/8.0' CONFIG_HISAX_16_0 bool ' Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 bool ' AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 bool ' ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 bool ' ASUSCOM ISA cards' CONFIG_HISAX_ASUSCOM bool ' TELEINT cards' CONFIG_HISAX_TELEINT bool ' HFC-S based cards' CONFIG_HISAX_HFCS bool ' USR Sportster internal TA' CONFIG_HISAX_SPORTSTER bool ' MIC card' CONFIG_HISAX_MIC bool ' Siemens I-Surf card' CONFIG_HISAX_ISURF bool ' HST Saphir card' CONFIG_HISAX_HSTSAPHIR fi bool ' Teles PCI' CONFIG_HISAX_TELESPCI bool ' Teles S0Box' CONFIG_HISAX_S0BOX bool ' AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI bool ' AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA bool ' Elsa cards' CONFIG_HISAX_ELSA bool ' Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA bool ' Sedlbauer cards' CONFIG_HISAX_SEDLBAUER bool ' NETjet card' CONFIG_HISAX_NETJET bool ' NETspider U card' CONFIG_HISAX_NETJET_U bool ' Niccy PnP/PCI card' CONFIG_HISAX_NICCY bool ' Telekom A4T card' CONFIG_HISAX_BKM_A4T bool ' Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO bool ' Gazel cards' CONFIG_HISAX_GAZEL bool ' HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI bool ' Winbond W6692 based cards' CONFIG_HISAX_W6692 bool ' HFC-S+, HFC-SP, HFC-PCMCIA cards' CONFIG_HISAX_HFC_SX if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then # bool ' TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU bool ' Formula-n enter:now PCI card' CONFIG_HISAX_ENTERNOW_PCI if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then bool ' Am7930' CONFIG_HISAX_AMD7930 fi fi bool ' HiSax debugging' CONFIG_HISAX_DEBUG dep_tristate 'Sedlbauer PCMCIA cards' CONFIG_HISAX_SEDLBAUER_CS $CONFIG_ISDN_DRV_HISAX $CONFIG_PCMCIA dep_tristate 'ELSA PCMCIA MicroLink cards' CONFIG_HISAX_ELSA_CS $CONFIG_ISDN_DRV_HISAX $CONFIG_PCMCIA dep_tristate 'AVM A1 PCMCIA cards' CONFIG_HISAX_AVM_A1_CS $CONFIG_ISDN_DRV_HISAX $CONFIG_PCMCIA $CONFIG_HISAX_AVM_A1_PCMCIA dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_ISDN_DRV_HISAX $CONFIG_EXPERIMENTAL dep_tristate 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_ISDN_DRV_HISAX $CONFIG_EXPERIMENTAL dep_tristate 'Auerswald devices ISDN support' CONFIG_USB_AUERISDN $CONFIG_ISDN_DRV_HISAX fi endmenu ### Active ISDN cards mainmenu_option next_comment comment 'Active ISDN cards' dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN dep_tristate 'Spellcaster support' CONFIG_ISDN_DRV_SC $CONFIG_ISDN dep_tristate 'IBM Active 2000 support' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN bool 'Eicon active card support' CONFIG_ISDN_DRV_EICON if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then if [ "$CONFIG_ISDN_DRV_EICON_OLD" != "y" ]; then dep_tristate ' Build Eicon driver type standalone' CONFIG_ISDN_DRV_EICON_DIVAS $CONFIG_ISDN $CONFIG_PCI fi if [ "$CONFIG_ISDN_DRV_EICON_DIVAS" != "y" ]; then dep_tristate ' Legacy Eicon driver' CONFIG_ISDN_DRV_EICON_OLD $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_EICON_OLD" != "n" ]; then dep_bool ' Eicon PCI DIVA Server BRI/PRI/4BRI support' CONFIG_ISDN_DRV_EICON_PCI $CONFIG_PCI bool ' Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA fi fi fi if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then dep_tristate 'Auvertech TurboPAM support' CONFIG_ISDN_DRV_TPAM $CONFIG_ISDN $CONFIG_PCI fi # CAPI subsystem tristate 'CAPI2.0 support' CONFIG_ISDN_CAPI if [ "$CONFIG_ISDN_CAPI" != "n" ]; then bool ' Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON dep_bool ' CAPI2.0 Middleware support (EXPERIMENTAL)' CONFIG_ISDN_CAPI_MIDDLEWARE $CONFIG_EXPERIMENTAL dep_tristate ' CAPI2.0 /dev/capi support' CONFIG_ISDN_CAPI_CAPI20 $CONFIG_ISDN_CAPI if [ "$CONFIG_ISDN_CAPI_MIDDLEWARE" = "y" ]; then dep_mbool ' CAPI2.0 filesystem support' CONFIG_ISDN_CAPI_CAPIFS_BOOL $CONFIG_ISDN_CAPI_CAPI20 if [ "$CONFIG_ISDN_CAPI_CAPIFS_BOOL" = "y" ]; then define_tristate CONFIG_ISDN_CAPI_CAPIFS $CONFIG_ISDN_CAPI_CAPI20 else define_tristate CONFIG_ISDN_CAPI_CAPIFS n fi fi dep_tristate ' CAPI2.0 capidrv interface support' CONFIG_ISDN_CAPI_CAPIDRV $CONFIG_ISDN_CAPI $CONFIG_ISDN fi # CAPI drivers if [ "$CONFIG_ISDN_CAPI" != "n" ]; then dep_tristate ' AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA $CONFIG_ISDN_CAPI dep_tristate ' AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI $CONFIG_ISDN_CAPI $CONFIG_PCI dep_mbool ' AVM B1 PCI V4 support' CONFIG_ISDN_DRV_AVMB1_B1PCIV4 $CONFIG_ISDN_DRV_AVMB1_B1PCI dep_tristate ' AVM T1/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA $CONFIG_ISDN_CAPI dep_tristate ' AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_ISDN_CAPI dep_tristate ' AVM B1/M1/M2 PCMCIA cs module' CONFIG_ISDN_DRV_AVMB1_AVM_CS $CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_PCMCIA dep_tristate ' AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI $CONFIG_ISDN_CAPI $CONFIG_PCI dep_tristate ' AVM C4/C2 support' CONFIG_ISDN_DRV_AVMB1_C4 $CONFIG_ISDN_CAPI $CONFIG_PCI fi # HYSDN dep_tristate ' Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)' CONFIG_HYSDN m $CONFIG_PROC_FS dep_mbool ' HYSDN CAPI 2.0 support' CONFIG_HYSDN_CAPI $CONFIG_HYSDN $CONFIG_ISDN_CAPI endmenu mainmenu_option next_comment comment 'modular ISDN driver' dep_tristate ' mISDN support' CONFIG_MISDN_DRV $CONFIG_ISDN_CAPI if [ "$CONFIG_MISDN_DRV" != "n" ]; then comment ' mISDN supported cards' bool ' AVM Fritz PCI and ISA PnP cards' CONFIG_MISDN_AVM_FRITZ bool ' Cologne Chip Design HFC PCI cards' CONFIG_MISDN_HFCPCI bool ' Cologne Chip Design HFC multiport cards' CONFIG_MISDN_HFCMULTI bool ' Sedlbauer Speedfax + cards' CONFIG_MISDN_SPEEDFAX bool ' Winbond W6692 cards' CONFIG_MISDN_W6692 comment ' mISDN supported features' bool ' mISDN audio DSP module' CONFIG_MISDN_DSP bool ' mISDN memory leak debug' CONFIG_MISDN_MEMDEBUG fi endmenu mISDN-1_1_9.1/drivers/isdn/Makefile.v2.40000644000000000000500000000304511110524073016003 0ustar rootsrc# Makefile for the kernel ISDN subsystem and device drivers. # The target object and module list name. O_TARGET := vmlinux-obj.o # Objects that export symbols. export-objs := isdn_common.o # Multipart objects. list-multi := isdn.o isdn-objs := isdn_net.o isdn_tty.o isdn_v110.o isdn_common.o # Optional parts of multipart objects. isdn-objs-$(CONFIG_ISDN_PPP) += isdn_ppp.o isdn-objs-$(CONFIG_ISDN_X25) += isdn_concap.o isdn_x25iface.o isdn-objs-$(CONFIG_ISDN_AUDIO) += isdn_audio.o isdn-objs-$(CONFIG_ISDN_TTY_FAX) += isdn_ttyfax.o isdn-objs-$(CONFIG_ISDN_WITH_ABC) += isdn_dwabc.o isdn-objs += $(isdn-objs-y) # Ordering constraints: isdn.o first, rest doesn't matter # Each configuration option enables a list of files. obj-$(CONFIG_ISDN) += isdn.o obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o # Object files in subdirectories mod-subdirs := avmb1 eicon hisax subdir-$(CONFIG_ISDN_DIVERSION) += divert subdir-$(CONFIG_ISDN_HISAX) += hisax subdir-$(CONFIG_ISDN_DRV_ICN) += icn subdir-$(CONFIG_ISDN_DRV_PCBIT) += pcbit subdir-$(CONFIG_ISDN_DRV_SC) += sc subdir-$(CONFIG_ISDN_CAPI) += avmb1 subdir-$(CONFIG_ISDN_DRV_LOOP) += isdnloop subdir-$(CONFIG_ISDN_DRV_ACT2000) += act2000 subdir-$(CONFIG_ISDN_DRV_EICON) += eicon subdir-$(CONFIG_HYSDN) += hysdn subdir-$(CONFIG_ISDN_DRV_TPAM) += tpam subdir-$(CONFIG_MISDN_DRV) += hardware/mISDN obj-y += $(addsuffix /vmlinux-obj.o, $(subdir-y)) # The global Rules.make. include $(TOPDIR)/Rules.make # Link rules for multi-part drivers. isdn.o: $(isdn-objs) $(LD) -r -o $@ $(isdn-objs) mISDN-1_1_9.1/drivers/isdn/hardware/0002755000000000000500000000000011110524073015450 5ustar rootsrcmISDN-1_1_9.1/drivers/isdn/hardware/Kconfig.v2.60000644000000000000500000000035211110524073017443 0ustar rootsrc# # ISDN hardware drivers # comment "CAPI hardware drivers" depends on NET && ISDN && ISDN_CAPI source "drivers/isdn/hardware/avm/Kconfig" source "drivers/isdn/hardware/eicon/Kconfig" source "drivers/isdn/hardware/mISDN/Kconfig" mISDN-1_1_9.1/drivers/isdn/hardware/Makefile.v2.60000644000000000000500000000026211110524073017600 0ustar rootsrc# Makefile for the CAPI hardware drivers # Object files in subdirectories obj-$(CONFIG_CAPI_AVM) += avm/ obj-$(CONFIG_CAPI_EICON) += eicon/ obj-$(CONFIG_MISDN_DRV) += mISDN/ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/0002755000000000000500000000000011137306013016363 5ustar rootsrcmISDN-1_1_9.1/drivers/isdn/hardware/mISDN/Kconfig.v2.60000644000000000000500000000772311135651701020374 0ustar rootsrc# # modularer ISDN driver # menu "Modular ISDN driver" depends on NET && ISDN && ISDN_CAPI!=n config MISDN_DRV tristate "Support modular ISDN driver" help Enable support for the modular ISDN driver. This driver is the successor of the famous HiSax driver. if MISDN_DRV!=n config MISDN_MEMDEBUG bool "Enable memory leak debug for mISDN" help This option is for watching the use of several resources in mISDN. It includes extra code to maintain list of allocated memory and sk_buffs. On module unload you can see not freed resources an their allocation orging and some object specific informations. If unsure, say 'N'. config MISDN_AVM_FRITZ bool "Support for AVM Fritz!Cards" depends on PCI || ISA help Enable support for AVM Fritz!Card PCI and PnP. config MISDN_NETJET bool "Support for NETJet cards" depends on PCI help Enable support for Traverse Technologies' NETJet PCI cards. config MISDN_HFCPCI bool "Support for HFC PCI cards" depends on PCI help Enable support for card with Cologne Chips Design HFC PCI based cards. config MISDN_HFCMULTI bool "Support for HFC multiport cards (HFC-4S/8S/E1)" depends on PCI help Enable support for card with Cologne Chip AG's HFC multiport chip. There are three types of chips that are quite similar, but the interface is different: * HFC-4S (4 S/T interfaces on one chip) * HFC-8S (8 S/T interfaces on one chip) * HFC-E1 (E1 interface for 2Mbit ISDN) if MISDN_HFCMULTI!=n config HFCMULTI_PCIMEM bool "HFC multiport driver with memory mapped IO" depends on PCI help Use memory mapped PCI access rather than IO access. This feature MIGHT be slightly faster, especially when using hardware DTMF detection. Also it may cause trouble with some PCI bridges. If unsure, say 'N'. endif config MISDN_HFCUSB bool "Support for HFC-S USB based TAs" depends on USB && EXPERIMENTAL help 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 help Enable support for Sedlbauer Speedfax+. config MISDN_W6692 bool "Support for Winbond 6692 based cards" depends on PCI help Enable support for Winbond 6692 PCI chip based cards. config MISDN_DSP bool "Digital Audio Processing of transparent data" help Enable support for digital audio processing capability. This module may be used for special applications that require cross connecting of bchannels, conferencing, dtmf decoding 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.eversberg.eu 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.eversberg.eu 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 mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/Makefile0000644000000000000500000000537011135651701020033 0ustar rootsrc# Makefile for the modular ISDN driver # EXTRA_CFLAGS += -ggdb # ifdef MINCLUDES EXTRA_CFLAGS += -I$(MINCLUDES) -g endif ifdef CONFIG_MISDN_MEMDEBUG EXTRA_CFLAGS += -DMISDN_MEMDEBUG endif ifdef CONFIG_MISDN_NETDEV EXTRA_CFLAGS += -DCONFIG_MISDN_NETDEV endif EXTRA_CFLAGS += -DMISDNVERSION=\"$(MISDNVERSION)\" obj-$(CONFIG_MISDN_DRV) += mISDN_core.o obj-$(CONFIG_MISDN_DRV) += mISDN_isac.o obj-$(CONFIG_MISDN_DRV) += mISDN_l1.o obj-$(CONFIG_MISDN_DRV) += mISDN_l2.o obj-$(CONFIG_MISDN_DRV) += mISDN_x25dte.o obj-$(CONFIG_MISDN_DRV) += l3udss1.o obj-$(CONFIG_MISDN_DRV) += mISDN_capi.o obj-$(CONFIG_MISDN_DRV) += mISDN_dtmf.o ifdef CONFIG_MISDN_AVM_FRITZ obj-$(CONFIG_MISDN_DRV) += avmfritz.o endif ifdef CONFIG_MISDN_NETJET obj-$(CONFIG_MISDN_DRV) += netjetpci.o endif ifdef CONFIG_MISDN_HFCPCI obj-$(CONFIG_MISDN_DRV) += hfcpci.o endif 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 obj-$(CONFIG_MISDN_DRV) += w6692pci.o endif 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 # multi objects sedlfax-objs := sedl_fax.o isar.o avmfritz-objs := avm_fritz.o netjetpci-objs := netjet.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 \ channel.o l3helper.o \ sysfs_obj.o sysfs_inst.o sysfs_st.o ifdef CONFIG_MISDN_NETDEV mISDN_core-objs += netdev.o endif ifdef CONFIG_MISDN_MEMDEBUG mISDN_core-objs += memdbg.o endif ifdef CONFIG_MISDN_DEBUGTOOL obj-$(CONFIG_MISDN_DEBUGTOOL) += mISDN_debugtool.o endif mISDN_l1-objs := layer1.o mISDN_l2-objs := layer2.o tei.o l3udss1-objs := layer3.o l3_udss1.o mISDN_capi-objs := capi.o contr.o listen.o appl.o plci.o app_plci.o ncci.o asn1.o \ asn1_aoc.o asn1_comp.o asn1_generic.o asn1_diversion.o \ 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 dsp_cancel.o mISDN_loop-objs := loop.o mISDN_x25dte-objs := x25_dte.o x25_l3.o I4LmISDN-objs := i4l_mISDN.o mISDN_debugtool-objs := debugtool.o mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/Makefile.v2.40000644000000000000500000000357311135651701020526 0ustar rootsrc# Makefile for the modular ISDN driver # # EXTRA_CFLAGS += -S -g # ifdef CONFIG_MISDN_MEMDEBUG EXTRA_CFLAGS += -DMISDN_MEMDEBUG endif EXTRA_CFLAGS += -I ../../avmb1 obj-$(CONFIG_MISDN_DRV) += mISDN_core.o obj-$(CONFIG_MISDN_DRV) += mISDN_isac.o obj-$(CONFIG_MISDN_DRV) += mISDN_l1.o obj-$(CONFIG_MISDN_DRV) += mISDN_l2.o obj-$(CONFIG_MISDN_DRV) += mISDN_x25dte.o obj-$(CONFIG_MISDN_DRV) += l3udss1.o obj-$(CONFIG_MISDN_DRV) += mISDN_capi.o obj-$(CONFIG_MISDN_DRV) += mISDN_dtmf.o ifdef CONFIG_MISDN_AVM_FRITZ obj-$(CONFIG_MISDN_DRV) += avmfritz.o endif ifdef CONFIG_MISDN_HFCPCI obj-$(CONFIG_MISDN_DRV) += hfcpci.o endif ifdef CONFIG_MISDN_SPEEDFAX obj-$(CONFIG_MISDN_DRV) += sedlfax.o endif ifdef CONFIG_MISDN_W6692 obj-$(CONFIG_MISDN_DRV) += w6692pci.o endif ifdef CONFIG_MISDN_HFCMULTI obj-$(CONFIG_MISDN_DRV) += hfcmulti.o endif ifdef CONFIG_MISDN_DSP obj-$(CONFIG_MISDN_DRV) += mISDN_dsp.o endif ifdef CONFIG_I4L_CAPI_LAYER obj-$(CONFIG_MISDN_DRV) += I4LmISDN.o endif # multi objects sedlfax-objs := sedl_fax.o isar.o avmfritz-objs := avm_fritz.o hfcpci-objs := hfc_pci.o w6692pci-objs := w6692.o hfcmulti-objs := hfc_multi.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 ifdef CONFIG_MISDN_MEMDEBUG mISDN_core-objs += memdbg.o endif mISDN_l1-objs := layer1.o mISDN_l2-objs := layer2.o tei.o l3udss1-objs := layer3.o l3_udss1.o mISDN_capi-objs := capi.o contr.o listen.o appl.o plci.o app_plci.o ncci.o asn1.o \ asn1_aoc.o asn1_comp.o asn1_generic.o asn1_diversion.o \ 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_x25dte-objs := x25_dte.o x25_l3.o I4LmISDN-objs := i4l_mISDN.o include Rules.mISDN mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/Makefile.v2.60000644000000000000500000000537011135651701020525 0ustar rootsrc# Makefile for the modular ISDN driver # EXTRA_CFLAGS += -ggdb # ifdef MINCLUDES EXTRA_CFLAGS += -I$(MINCLUDES) -g endif ifdef CONFIG_MISDN_MEMDEBUG EXTRA_CFLAGS += -DMISDN_MEMDEBUG endif ifdef CONFIG_MISDN_NETDEV EXTRA_CFLAGS += -DCONFIG_MISDN_NETDEV endif EXTRA_CFLAGS += -DMISDNVERSION=\"$(MISDNVERSION)\" obj-$(CONFIG_MISDN_DRV) += mISDN_core.o obj-$(CONFIG_MISDN_DRV) += mISDN_isac.o obj-$(CONFIG_MISDN_DRV) += mISDN_l1.o obj-$(CONFIG_MISDN_DRV) += mISDN_l2.o obj-$(CONFIG_MISDN_DRV) += mISDN_x25dte.o obj-$(CONFIG_MISDN_DRV) += l3udss1.o obj-$(CONFIG_MISDN_DRV) += mISDN_capi.o obj-$(CONFIG_MISDN_DRV) += mISDN_dtmf.o ifdef CONFIG_MISDN_AVM_FRITZ obj-$(CONFIG_MISDN_DRV) += avmfritz.o endif ifdef CONFIG_MISDN_NETJET obj-$(CONFIG_MISDN_DRV) += netjetpci.o endif ifdef CONFIG_MISDN_HFCPCI obj-$(CONFIG_MISDN_DRV) += hfcpci.o endif 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 obj-$(CONFIG_MISDN_DRV) += w6692pci.o endif 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 # multi objects sedlfax-objs := sedl_fax.o isar.o avmfritz-objs := avm_fritz.o netjetpci-objs := netjet.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 \ channel.o l3helper.o \ sysfs_obj.o sysfs_inst.o sysfs_st.o ifdef CONFIG_MISDN_NETDEV mISDN_core-objs += netdev.o endif ifdef CONFIG_MISDN_MEMDEBUG mISDN_core-objs += memdbg.o endif ifdef CONFIG_MISDN_DEBUGTOOL obj-$(CONFIG_MISDN_DEBUGTOOL) += mISDN_debugtool.o endif mISDN_l1-objs := layer1.o mISDN_l2-objs := layer2.o tei.o l3udss1-objs := layer3.o l3_udss1.o mISDN_capi-objs := capi.o contr.o listen.o appl.o plci.o app_plci.o ncci.o asn1.o \ asn1_aoc.o asn1_comp.o asn1_generic.o asn1_diversion.o \ 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 dsp_cancel.o mISDN_loop-objs := loop.o mISDN_x25dte-objs := x25_dte.o x25_l3.o I4LmISDN-objs := i4l_mISDN.o mISDN_debugtool-objs := debugtool.o mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/Rules.mISDN.v2.40000755000000000000500000000326511110524073021007 0ustar rootsrc# # local Rules for 2.4 # O_TARGET := vmlinux-obj.o export-objs := core.o isac.o helper.o debug.o fsm.o dchannel.o bchannel.o \ l3helper.o ifdef CONFIG_MISDN_MEMDEBUG export-objs += memdbg.o endif M_OBJS := $(obj-m) include $(TOPDIR)/Rules.make mISDN_core.o: $(mISDN_core-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) mISDN_isac.o: $(mISDN_isac-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) avmfritz.o: $(avmfritz-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) sedlfax.o: $(sedlfax-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) w6692pci.o: $(w6692pci-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) hfcpci.o: $(hfcpci-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) hfcmulti.o: $(hfcmulti-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) mISDN_l1.o: $(mISDN_l1-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) mISDN_l2.o: $(mISDN_l2-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) mISDN_x25dte.o: $(mISDN_x25dte-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) l3udss1.o: $(l3udss1-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) mISDN_capi.o: $(mISDN_capi-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) mISDN_dtmf.o: $(mISDN_dtmf-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) ifdef CONFIG_I4L_CAPI_LAYER I4LmISDN.o: $(I4LmISDN-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) endif mISDN_dsp.o: $(mISDN_dsp-objs) $(RM) $@ $(LD) -r -o $@ $(filter-out $(MODVERFILE) dummy ,$^) mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/app_plci.c0000644000000000000500000017141011135651701020325 0ustar rootsrc/* $Id: app_plci.c,v 1.19 2006/12/06 15:18:07 gkelleter Exp $ * */ #include "m_capi.h" #include "helper.h" #include "debug.h" #include "dss1.h" #define AppPlciDebug(aplci, lev, fmt, args...) \ capidebug(lev, fmt, ## args) #ifndef CAPIUTIL_VERSION #define CAPIUTIL_VERSION 1 #endif static void AppPlciClearOtherApps(AppPlci_t *); static void AppPlciInfoIndMsg(AppPlci_t *, __u32, unsigned char); static void AppPlciInfoIndIE(AppPlci_t *, unsigned char, __u32, Q931_info_t *); static int AppPlciLinkUp(AppPlci_t *); static int AppPlciLinkDown(AppPlci_t *); static u_char BEARER_SPEECH_64K_ALAW[] = {4, 3, 0x80, 0x90, 0xA3}; static u_char BEARER_SPEECH_64K_ULAW[] = {4, 3, 0x80, 0x90, 0xA2}; static u_char BEARER_UNRES_DIGITAL_64K[] = {4, 2, 0x88, 0x90}; static u_char BEARER_RES_DIGITAL_64K[] = {4, 2, 0x89, 0x90}; static u_char BEARER_31AUDIO_64K_ALAW[] = {4, 3, 0x90, 0x90, 0xA3}; static u_char BEARER_31AUDIO_64K_ULAW[] = {4, 3, 0x90, 0x90, 0xA2}; static u_char HLC_TELEPHONY[] = {0x7d, 2, 0x91, 0x81}; static u_char HLC_FACSIMILE[] = {0x7d, 2, 0x91, 0x84}; __u16 q931CIPValue(Q931_info_t *qi) { __u16 CIPValue = 0; u_char *p; if (!qi) return 0; if (!qi->bearer_capability.off) return 0; p = (u_char *)qi; p += L3_EXTRA_SIZE + qi->bearer_capability.off; if (memcmp(p, BEARER_SPEECH_64K_ALAW, 5) == 0 || memcmp(p, BEARER_SPEECH_64K_ULAW, 5) == 0) { CIPValue = 1; } else if (memcmp(p, BEARER_UNRES_DIGITAL_64K, 4) == 0) { CIPValue = 2; } else if (memcmp(p, BEARER_RES_DIGITAL_64K, 4) == 0) { CIPValue = 3; } else if (memcmp(p, BEARER_31AUDIO_64K_ALAW, 5) == 0 || memcmp(p, BEARER_31AUDIO_64K_ULAW, 5) == 0) { CIPValue = 4; } else { CIPValue = 0; } // FIXME: handle duplicated IE's if (!qi->hlc.off) return CIPValue; p = (u_char *)qi; p += L3_EXTRA_SIZE + qi->hlc.off; if ((CIPValue == 1) || (CIPValue == 4)) { if (memcmp(p, HLC_TELEPHONY, 4) == 0) { CIPValue = 16; } else if (memcmp(p, HLC_FACSIMILE, 4) == 0) { CIPValue = 17; } } return CIPValue; } u_int plci_parse_channel_id(u_char *p) { u_int cid = -1; int l; if (p) { p++; capidebug(CAPI_DBG_PLCI_INFO, "%s: l(%d) %x", __FUNCTION__, p[0], p[1]); l = *p++; if (l == 1) { cid = *p; } else if (l == 3) { cid = *p++ << 16; cid |= *p++ << 8; cid |= *p; } } return(cid); } __u16 CIPValue2setup(__u16 CIPValue, struct sk_buff *skb) { switch (CIPValue) { case 16: mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW); mISDN_AddvarIE(skb, HLC_TELEPHONY); break; case 17: mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW); mISDN_AddvarIE(skb, HLC_FACSIMILE); break; case 1: mISDN_AddvarIE(skb, BEARER_SPEECH_64K_ALAW); break; case 2: mISDN_AddvarIE(skb, BEARER_UNRES_DIGITAL_64K); break; case 3: mISDN_AddvarIE(skb, BEARER_RES_DIGITAL_64K); break; case 4: mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW); break; default: return CapiIllMessageParmCoding; } return 0; } __u16 cmsg2setup_req(_cmsg *cmsg, struct sk_buff *skb) { if (CIPValue2setup(cmsg->CIPValue, skb)) goto err; mISDN_AddIE(skb, IE_CALLING_PN, cmsg->CallingPartyNumber); mISDN_AddIE(skb, IE_CALLING_SUB, cmsg->CallingPartySubaddress); mISDN_AddIE(skb, IE_CALLED_PN, cmsg->CalledPartyNumber); mISDN_AddIE(skb, IE_CALLED_SUB, cmsg->CalledPartySubaddress); mISDN_AddIE(skb, IE_BEARER, cmsg->BC); mISDN_AddIE(skb, IE_LLC, cmsg->LLC); mISDN_AddIE(skb, IE_HLC, cmsg->HLC); return 0; err: return CapiIllMessageParmCoding; } __u16 cmsg2info_req(_cmsg *cmsg, struct sk_buff *skb) { mISDN_AddIE(skb, IE_KEYPAD, cmsg->Keypadfacility); mISDN_AddIE(skb, IE_CALLED_PN, cmsg->CalledPartyNumber); return 0; } __u16 cmsg2alerting_req(_cmsg *cmsg, struct sk_buff *skb) { mISDN_AddIE(skb, IE_USER_USER, cmsg->Useruserdata); return 0; } __u16 AppPlciCheckBprotocol(AppPlci_t *aplci, _cmsg *cmsg) { struct capi_ctr *ctrl = aplci->contr->ctrl; u_long sprot; sprot = le32_to_cpu(ctrl->profile.support1); if (!test_bit(cmsg->B1protocol, &sprot)) return CapiB1ProtocolNotSupported; sprot = le32_to_cpu(ctrl->profile.support2); if (!test_bit(cmsg->B2protocol, &sprot)) return CapiB2ProtocolNotSupported; sprot = le32_to_cpu(ctrl->profile.support3); if (!test_bit(cmsg->B3protocol, &sprot)) return CapiB3ProtocolNotSupported; aplci->Bprotocol.B1 = cmsg->B1protocol; aplci->Bprotocol.B2 = cmsg->B2protocol; aplci->Bprotocol.B3 = cmsg->B3protocol; if (cmsg->B1configuration && cmsg->B1configuration[0]) { if (cmsg->B1configuration[0] > 15) { int_errtxt("B1cfg too large(%d)", cmsg->B1configuration[0]); return CapiB1ProtocolParameterNotSupported; } memcpy(&aplci->Bprotocol.B1cfg[0], cmsg->B1configuration, cmsg->B1configuration[0] + 1); } else aplci->Bprotocol.B1cfg[0] = 0; if (cmsg->B2configuration && cmsg->B2configuration[0]) { if (cmsg->B2configuration[0] > 15) { int_errtxt("B2cfg too large(%d)", cmsg->B2configuration[0]); return CapiB2ProtocolParameterNotSupported; } memcpy(&aplci->Bprotocol.B2cfg[0], cmsg->B2configuration, cmsg->B2configuration[0] + 1); } else aplci->Bprotocol.B2cfg[0] = 0; if (cmsg->B3configuration && cmsg->B3configuration[0]) { if (cmsg->B3configuration[0] > 79) { int_errtxt("B3cfg too large(%d)", cmsg->B3configuration[0]); return CapiB3ProtocolParameterNotSupported; } memcpy(&aplci->Bprotocol.B3cfg[0], cmsg->B3configuration, cmsg->B3configuration[0] + 1); } else aplci->Bprotocol.B3cfg[0] = 0; return 0; } // -------------------------------------------------------------------- // PLCI state machine // // Some rules: // * EV_AP_* events come from CAPI Application // * EV_L3_* events come from the ISDN stack // * EV_PI_* events generated in PLCI handling // * messages are send in the routine that handle the event // // -------------------------------------------------------------------- enum { ST_PLCI_P_0, ST_PLCI_P_0_1, ST_PLCI_P_1, ST_PLCI_P_2, ST_PLCI_P_3, ST_PLCI_P_4, ST_PLCI_P_ACT, ST_PLCI_P_HELD, ST_PLCI_P_5, ST_PLCI_P_6, ST_PLCI_P_RES, } const ST_PLCI_COUNT = ST_PLCI_P_RES + 1; static char *str_st_plci[] = { "ST_PLCI_P_0", "ST_PLCI_P_0_1", "ST_PLCI_P_1", "ST_PLCI_P_2", "ST_PLCI_P_3", "ST_PLCI_P_4", "ST_PLCI_P_ACT", "ST_PLCI_P_HELD", "ST_PLCI_P_5", "ST_PLCI_P_6", "ST_PLCI_P_RES", }; enum { EV_AP_CONNECT_REQ, EV_PI_CONNECT_CONF, EV_PI_CONNECT_IND, EV_AP_CONNECT_RESP, EV_PI_CONNECT_ACTIVE_IND, EV_AP_CONNECT_ACTIVE_RESP, EV_AP_ALERT_REQ, EV_AP_INFO_REQ, EV_PI_INFO_IND, EV_PI_FACILITY_IND, EV_AP_SELECT_B_PROTOCOL_REQ, EV_AP_DISCONNECT_REQ, EV_PI_DISCONNECT_IND, EV_AP_DISCONNECT_RESP, EV_AP_HOLD_REQ, EV_AP_RETRIEVE_REQ, EV_PI_HOLD_CONF, EV_PI_RETRIEVE_CONF, EV_AP_SUSPEND_REQ, EV_PI_SUSPEND_CONF, EV_AP_RESUME_REQ, EV_PI_RESUME_CONF, EV_PI_CHANNEL_ERR, EV_L3_SETUP_IND, EV_L3_SETUP_CONF_ERR, EV_L3_SETUP_CONF, EV_L3_SETUP_COMPL_IND, EV_L3_DISCONNECT_IND, EV_L3_RELEASE_IND, EV_L3_RELEASE_PROC_IND, EV_L3_NOTIFY_IND, EV_L3_HOLD_IND, EV_L3_HOLD_ACKNOWLEDGE, EV_L3_HOLD_REJECT, EV_L3_RETRIEVE_IND, EV_L3_RETRIEVE_ACKNOWLEDGE, EV_L3_RETRIEVE_REJECT, EV_L3_SUSPEND_ERR, EV_L3_SUSPEND_CONF, EV_L3_RESUME_ERR, EV_L3_RESUME_CONF, EV_L3_REJECT_IND, EV_PH_CONTROL_IND, EV_AP_RELEASE, } const EV_PLCI_COUNT = EV_AP_RELEASE + 1; static char* str_ev_plci[] = { "EV_AP_CONNECT_REQ", "EV_PI_CONNECT_CONF", "EV_PI_CONNECT_IND", "EV_AP_CONNECT_RESP", "EV_PI_CONNECT_ACTIVE_IND", "EV_AP_CONNECT_ACTIVE_RESP", "EV_AP_ALERT_REQ", "EV_AP_INFO_REQ", "EV_PI_INFO_IND", "EV_PI_FACILITY_IND", "EV_AP_SELECT_B_PROTOCOL_REQ", "EV_AP_DISCONNECT_REQ", "EV_PI_DISCONNECT_IND", "EV_AP_DISCONNECT_RESP", "EV_AP_HOLD_REQ", "EV_AP_RETRIEVE_REQ", "EV_PI_HOLD_CONF", "EV_PI_RETRIEVE_CONF", "EV_AP_SUSPEND_REQ", "EV_PI_SUSPEND_CONF", "EV_AP_RESUME_REQ", "EV_PI_RESUME_CONF", "EV_PI_CHANNEL_ERR", "EV_L3_SETUP_IND", "EV_L3_SETUP_CONF_ERR", "EV_L3_SETUP_CONF", "EV_L3_SETUP_COMPL_IND", "EV_L3_DISCONNECT_IND", "EV_L3_RELEASE_IND", "EV_L3_RELEASE_PROC_IND", "EV_L3_NOTIFY_IND", "EV_L3_HOLD_IND", "EV_L3_HOLD_ACKNOWLEDGE", "EV_L3_HOLD_REJECT", "EV_L3_RETRIEVE_IND", "EV_L3_RETRIEVE_ACKNOWLEDGE", "EV_L3_RETRIEVE_REJECT", "EV_L3_SUSPEND_ERR", "EV_L3_SUSPEND_CONF", "EV_L3_RESUME_ERR", "EV_L3_RESUME_CONF", "EV_L3_REJECT_IND", "EV_PH_CONTROL_IND", "EV_AP_RELEASE", }; static struct Fsm plci_fsm = { 0, 0, 0, 0, 0 }; static void AppPlci_debug(struct FsmInst *fi, char *fmt, ...) { char tmp[128]; char *p = tmp; va_list args; AppPlci_t *aplci = fi->userdata; va_start(args, fmt); p += sprintf(p, "APLCI 0x%x: ", aplci->addr); p += vsprintf(p, fmt, args); *p = 0; AppPlciDebug(aplci, CAPI_DBG_PLCI_STATE, tmp); va_end(args); } static inline void Send2Application(AppPlci_t *aplci, _cmsg *cmsg) { SendCmsg2Application(aplci->appl, cmsg); } static void SendingDelayedMsg(AppPlci_t *aplci) { struct sk_buff *skb; while((skb = skb_dequeue(&aplci->delayedq))) { if (test_bit(APPL_STATE_RELEASE, &aplci->appl->state)) { printk(KERN_WARNING "%s: Application allready released\n", __FUNCTION__); dev_kfree_skb(skb); } else { #ifdef OLDCAPI_DRIVER_INTERFACE aplci->appl->contr->ctrl->handle_capimsg(aplci->appl->contr->ctrl, aplci->appl->ApplId, skb); #else capi_ctr_handle_message(aplci->appl->contr->ctrl, aplci->appl->ApplId, skb); #endif } } test_and_clear_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state); } static void Send2ApplicationDelayed(AppPlci_t *aplci, _cmsg *cmsg) { struct sk_buff *skb; if (test_bit(APPL_STATE_RELEASE, &aplci->appl->state)) { printk(KERN_WARNING "%s: Application allready released\n", __FUNCTION__); cmsg_free(cmsg); return; } if (!(skb = alloc_skb(CAPI_MSG_DEFAULT_LEN, GFP_ATOMIC))) { printk(KERN_WARNING "%s: no mem for %d bytes\n", __FUNCTION__, CAPI_MSG_DEFAULT_LEN); int_error(); cmsg_free(cmsg); return; } capi_cmsg2message(cmsg, skb->data); AppPlciDebug(aplci, CAPI_DBG_APPL_MSG, "%s: len(%d) applid(%x) %s msgnr(%d) addr(%08x)", __FUNCTION__, CAPIMSG_LEN(skb->data), cmsg->ApplId, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Messagenumber, cmsg->adr.adrController); cmsg_free(cmsg); if (CAPI_MSG_DEFAULT_LEN < CAPIMSG_LEN(skb->data)) { printk(KERN_ERR "%s: CAPI_MSG_DEFAULT_LEN overrun (%d/%d)\n", __FUNCTION__, CAPIMSG_LEN(skb->data), CAPI_MSG_DEFAULT_LEN); int_error(); dev_kfree_skb(skb); return; } skb_put(skb, CAPIMSG_LEN(skb->data)); skb_queue_tail(&aplci->delayedq, skb); if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state) && !test_and_set_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state)) SendingDelayedMsg(aplci); } static inline void AppPlciCmsgHeader(AppPlci_t *aplci, _cmsg *cmsg, __u8 cmd, __u8 subcmd) { capi_cmsg_header(cmsg, aplci->appl->ApplId, cmd, subcmd, aplci->appl->MsgId++, aplci->addr); } static void plci_connect_req(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; struct sk_buff *skb; _cmsg *cmsg = arg; __u16 Info = 0; mISDN_FsmChangeState(fi, ST_PLCI_P_0_1); test_and_set_bit(PLCI_STATE_OUTGOING, &plci->state); skb = mISDN_alloc_l3msg(260, MT_SETUP); if (!skb) { Info = CapiNoPlciAvailable; goto answer; } if ((Info = cmsg2setup_req(cmsg, skb))) { goto answer; } if ((Info = AppPlciCheckBprotocol(aplci, cmsg))) { goto answer; } plciNewCrReq(plci); plciL4L3(plci, CC_SETUP | REQUEST, skb); answer: capi_cmsg_answer(cmsg); cmsg->Info = Info; if (cmsg->Info == 0) cmsg->adr.adrPLCI = aplci->addr; mISDN_FsmEvent(fi, EV_PI_CONNECT_CONF, cmsg); } static void plci_connect_conf(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; _cmsg *cmsg = arg; if (cmsg->Info == 0) { Send2Application(aplci, cmsg); mISDN_FsmChangeState(fi, ST_PLCI_P_1); } else { Send2Application(aplci, cmsg); mISDN_FsmChangeState(fi, ST_PLCI_P_0); AppPlciDestr(aplci); } } static void plci_connect_ind(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_PLCI_P_2); Send2Application(fi->userdata, arg); } static void plci_hold_req(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; plciL4L3(plci, CC_HOLD | REQUEST, arg); } static void plci_retrieve_req(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; plciL4L3(plci, CC_RETRIEVE | REQUEST, arg); } static void plci_suspend_req(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; plciL4L3(plci, CC_SUSPEND | REQUEST, arg); } static void plci_resume_req(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; // we already sent CONF with Info = SuppInfo = 0 mISDN_FsmChangeState(fi, ST_PLCI_P_RES); plciNewCrReq(plci); plciL4L3(plci, CC_RESUME | REQUEST, arg); } static void plci_alert_req(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; _cmsg *cmsg = arg; __u16 Info = 0; if (test_and_set_bit(PLCI_STATE_ALERTING, &plci->state)) { Info = 0x0003; // other app is already alerting } else { struct sk_buff *skb = mISDN_alloc_l3msg(260, MT_ALERTING); if (!skb) { int_error(); goto answer; } Info = cmsg2alerting_req(cmsg, skb); if (Info == 0) { plciL4L3(plci, CC_ALERTING | REQUEST, skb); } } answer: capi_cmsg_answer(cmsg); cmsg->Info = Info; Send2Application(aplci, cmsg); } static void plci_connect_resp(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; unsigned char cause[4]; _cmsg *cmsg = arg; struct sk_buff *skb; if (cmsg->Reject == 0) { // accept if (AppPlciCheckBprotocol(aplci, cmsg)) { int_error(); } AppPlciClearOtherApps(aplci); plciL4L3(plci, CC_CONNECT | REQUEST, NULL); mISDN_FsmChangeState(fi, ST_PLCI_P_4); cmsg_free(cmsg); return; } // ignore, reject memcpy(cause, "\x02\x80", 2); // IE CAUSE, location = local switch (cmsg->Reject) { case 2: cause[2] = 0x90; break; // normal call clearing case 3: cause[2] = 0x91; break; // user busy case 4: cause[2] = 0xac; break; // req circuit/channel not avail case 5: cause[2] = 0x9d; break; // fac rejected case 6: cause[2] = 0x86; break; // channel unacceptable case 7: cause[2] = 0xd8; break; // incompatible dest case 8: cause[2] = 0x9b; break; // dest out of order default: if ((cmsg->Reject & 0xff00) == 0x3400) { cause[2] = cmsg->Reject & 0xff; } else { cause[2] = 0x90; break; // normal call clearing } } // FIXME // WHY ??? // if (cmsg->Reject != 1) { // ignore // AppPlciClearOtherApps(aplci); // } // plciDetachAppPlci(plci, aplci); if (plci->nAppl == 1) { int prim; if (test_bit(PLCI_STATE_ALERTING, &plci->state)) { prim = CC_DISCONNECT | REQUEST; skb = mISDN_alloc_l3msg(10, MT_DISCONNECT); } else { // if we already answered, we can't just ignore but must clear actively prim = CC_RELEASE_COMPLETE | REQUEST; skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE); } if (!skb) { plciL4L3(plci, prim, NULL); } else { mISDN_AddIE(skb, IE_CAUSE, cause); plciL4L3(plci, prim, skb); } } cmsg->Command = CAPI_DISCONNECT; cmsg->Subcommand = CAPI_IND; cmsg->Messagenumber = aplci->appl->MsgId++; cmsg->Reject = 0x3400 | cause[2]; if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) cmsg_free(cmsg); } static void plci_connect_active_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; mISDN_FsmChangeState(fi, ST_PLCI_P_ACT); AppPlciLinkUp(aplci); if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) Send2Application(aplci, arg); else Send2ApplicationDelayed(aplci, arg); } static void plci_connect_active_resp(struct FsmInst *fi, int event, void *arg) { cmsg_free(arg); } static void plci_disconnect_req(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; u_char cause[4]; _cmsg *cmsg = arg; mISDN_FsmChangeState(fi, ST_PLCI_P_5); if (!plci) { int_error(); return; } // FIXME handle additional Inf capi_cmsg_answer(cmsg); cmsg->Reason = 0; // disconnect initiated Send2Application(aplci, cmsg); AppPlciLinkDown(aplci); if (!aplci->cause[0]) { // FIXME handle additional Info struct sk_buff *skb; skb = mISDN_alloc_l3msg(10, MT_DISCONNECT); if (!skb) { plciL4L3(plci, CC_DISCONNECT | REQUEST, NULL); } else { memcpy(cause, "\x02\x80\x90", 3); // normal call clearing mISDN_AddIE(skb, IE_CAUSE, cause); plciL4L3(plci, CC_DISCONNECT | REQUEST, skb); } } else { /* release physical link */ // FIXME plciL4L3(plci, CC_RELEASE | REQUEST, NULL); } } static void plci_suspend_conf(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_PLCI_P_5); } static void plci_resume_conf(struct FsmInst *fi, int event, void *arg) { // facility_ind Resume: Reason = 0 AppPlci_t *aplci = fi->userdata; mISDN_FsmChangeState(fi, ST_PLCI_P_ACT); AppPlciLinkUp(aplci); if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) Send2Application(aplci, arg); else Send2ApplicationDelayed(aplci, arg); } static void plci_disconnect_ind(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_PLCI_P_6); Send2Application(fi->userdata, arg); } static void plci_disconnect_resp(struct FsmInst *fi, int event, void *arg) { if (arg) cmsg_free(arg); mISDN_FsmChangeState(fi, ST_PLCI_P_0); AppPlciDestr(fi->userdata); } static void plci_appl_release(struct FsmInst *fi, int event, void *arg) { AppPlciDestr(fi->userdata); } static void plci_appl_release_disc(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; mISDN_FsmChangeState(fi, ST_PLCI_P_5); if (!plci) { int_error(); return; } AppPlciLinkDown(aplci); if (!aplci->cause[0]) { struct sk_buff *skb; skb = mISDN_alloc_l3msg(10, MT_DISCONNECT); if (!skb) { plciL4L3(plci, CC_DISCONNECT | REQUEST, NULL); } else { u_char *cause = "\x02\x80\x9f"; mISDN_AddIE(skb, IE_CAUSE, cause); plciL4L3(plci, CC_DISCONNECT | REQUEST, skb); } } else { /* release physical link */ // FIXME plciL4L3(plci, CC_RELEASE | REQUEST, NULL); } } static void plci_cc_setup_conf(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; _cmsg *cmsg; Q931_info_t *qi = arg; u_char *p; if (aplci->channel == -1) {/* no valid channel set */ mISDN_FsmEvent(fi, EV_PI_CHANNEL_ERR, NULL); return; } CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT_ACTIVE, CAPI_IND); if (qi) { p = (u_char *)qi; p += L3_EXTRA_SIZE; if (qi->connected_nr.off) cmsg->ConnectedNumber = &p[qi->connected_nr.off + 1]; if (qi->connected_sub.off) cmsg->ConnectedSubaddress = &p[qi->connected_sub.off + 1]; if (qi->llc.off) cmsg->LLC = &p[qi->llc.off + 1]; } if (mISDN_FsmEvent(fi, EV_PI_CONNECT_ACTIVE_IND, cmsg)) cmsg_free(cmsg); } static void plci_cc_setup_conf_err(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; _cmsg *cmsg; CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); cmsg->Reason = CapiProtocolErrorLayer3; if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) cmsg_free(cmsg); } static void plci_channel_err(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; _cmsg *cmsg; u_char cause[4]; struct sk_buff *skb; skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE); if (skb) { cause[0] = 2; cause[1] = 0x80; cause[2] = 0x86; /* channel unacceptable */ mISDN_AddIE(skb, IE_CAUSE, cause); plciL4L3(aplci->plci, CC_RELEASE_COMPLETE | REQUEST, skb); } else int_error(); CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); cmsg->Reason = CapiProtocolErrorLayer3; if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) cmsg_free(cmsg); } static void plci_cc_setup_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; _cmsg *cmsg; u_char *p; CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT, CAPI_IND); // FIXME: CW if (qi) { p = (u_char *)qi; p += L3_EXTRA_SIZE; cmsg->CIPValue = q931CIPValue(qi); if (qi->called_nr.off) cmsg->CalledPartyNumber = &p[qi->called_nr.off + 1]; if (qi->called_sub.off) cmsg->CalledPartySubaddress = &p[qi->called_sub.off + 1]; if (qi->calling_nr.off) cmsg->CallingPartyNumber = &p[qi->calling_nr.off + 1]; if (qi->calling_sub.off) cmsg->CallingPartySubaddress = &p[qi->calling_sub.off + 1]; if (qi->bearer_capability.off) cmsg->BC = &p[qi->bearer_capability.off + 1]; if (qi->llc.off) cmsg->LLC = &p[qi->llc.off + 1]; if (qi->hlc.off) cmsg->HLC = &p[qi->hlc.off + 1]; #if CAPIUTIL_VERSION > 1 /* ETS 300 092 Annex B */ if (qi->calling_nr.repeated) { if (qi->ext[qi->calling_nr.ridx].ie.off) cmsg->CallingPartyNumber2 = &p[qi->ext[qi->calling_nr.ridx].ie.off + 1]; else int_error(); } #endif // all else set to default } if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_CONNECT_IND, cmsg)) cmsg_free(cmsg); } static void plci_cc_setup_compl_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; _cmsg *cmsg; CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT_ACTIVE, CAPI_IND); if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_CONNECT_ACTIVE_IND, cmsg)) cmsg_free(cmsg); } static void plci_cc_disconnect_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; u_char *p; if (qi) { p = (u_char *)qi; p += L3_EXTRA_SIZE; if (qi->cause.off) memcpy(aplci->cause, &p[qi->cause.off + 1], 3); } if (aplci->appl->InfoMask & CAPI_INFOMASK_EARLYB3) return; // AppPlciLinkDown(aplci); plciL4L3(aplci->plci, CC_RELEASE | REQUEST, NULL); } static void plci_cc_release_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; u_char *p; _cmsg *cmsg; AppPlciLinkDown(aplci); CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); if (qi) { p = (u_char *)qi; p += L3_EXTRA_SIZE; if (qi->cause.off) cmsg->Reason = 0x3400 | p[qi->cause.off + 3]; else if (aplci->cause[0]) // cause from CC_DISCONNECT IND cmsg->Reason = 0x3400 | aplci->cause[2]; else cmsg->Reason = 0; } else { cmsg->Reason = CapiProtocolErrorLayer1; } if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) cmsg_free(cmsg); } static void plci_cc_notify_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; __u8 *nf; if (!qi || !qi->notify.off) return; nf = (u_char *)qi; nf += L3_EXTRA_SIZE + qi->notify.off + 1; if (nf[0] != 1) // len != 1 return; switch (nf[1]) { case 0xF9: // user hold SendSSNotificationEvent(aplci, 0x8000); break; case 0xFA: // user retrieve SendSSNotificationEvent(aplci, 0x8001); break; case 0x80: // user suspended SendSSNotificationEvent(aplci, 0x8002); break; case 0x81: // user resumed SendSSNotificationEvent(aplci, 0x8003); break; case 0xFB: // call is diverting SendSSNotificationEvent(aplci, 0x8004); break; case 0xE8: // diversion activated SendSSNotificationEvent(aplci, 0x8005); break; default: int_errtxt("unhandled notification %x", nf[1]); } } static void plci_hold_conf(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_PLCI_P_HELD); } static void AppPlci_hold_reply(AppPlci_t *aplci, __u16 SuppServiceReason) { _cmsg *cmsg; __u8 tmp[10], *p; if (aplci->appl) { CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); p = &tmp[1]; p += capiEncodeWord(p, 0x0002); // Hold p += capiEncodeFacIndSuspend(p, SuppServiceReason); tmp[0] = p - &tmp[1]; cmsg->FacilitySelector = 0x0003; cmsg->FacilityIndicationParameter = tmp; Send2Application(aplci, cmsg); } if (SuppServiceReason == CapiSuccess) mISDN_FsmEvent(&aplci->plci_m, EV_PI_HOLD_CONF, NULL); } static void plci_cc_hold_rej(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; u_char *p; __u16 SuppServiceReason; if (qi) { // reject from network if (qi->cause.off) { p = (u_char *)qi; p += L3_EXTRA_SIZE + qi->cause.off; SuppServiceReason = 0x3400 | p[3]; } else SuppServiceReason = CapiProtocolErrorLayer3; } else { // timeout SuppServiceReason = CapiTimeOut; } AppPlci_hold_reply(aplci, SuppServiceReason); } static void plci_cc_hold_ack(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; AppPlci_hold_reply(aplci, CapiSuccess); AppPlciLinkDown(aplci); } static void plci_cc_hold_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; AppPlci_hold_reply(aplci, CapiSuccess); AppPlciLinkDown(aplci); plciL4L3(aplci->plci, CC_HOLD_ACKNOWLEDGE| REQUEST, NULL); } static void plci_retrieve_conf(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; mISDN_FsmChangeState(fi, ST_PLCI_P_ACT); AppPlciLinkUp(aplci); if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) Send2Application(aplci, arg); else Send2ApplicationDelayed(aplci, arg); } static void AppPlci_retrieve_reply(AppPlci_t *aplci, __u16 SuppServiceReason) { _cmsg *cmsg; __u8 tmp[10], *p; if (aplci->appl) { CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); p = &tmp[1]; p += capiEncodeWord(p, 0x0003); // Retrieve p += capiEncodeFacIndSuspend(p, SuppServiceReason); tmp[0] = p - &tmp[1]; cmsg->FacilitySelector = 0x0003; cmsg->FacilityIndicationParameter = tmp; if (SuppServiceReason != CapiSuccess) Send2Application(aplci, cmsg); else if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_RETRIEVE_CONF, cmsg)) cmsg_free(cmsg); } } static void plci_cc_retrieve_rej(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; u_char *p; __u16 SuppServiceReason; if (qi) { // reject from network if (qi->cause.off) { p = (u_char *)qi; p += L3_EXTRA_SIZE + qi->cause.off; SuppServiceReason = 0x3400 | p[3]; } else SuppServiceReason = CapiProtocolErrorLayer3; } else { // timeout SuppServiceReason = CapiTimeOut; } AppPlci_retrieve_reply(aplci, SuppServiceReason); } static void plci_cc_retrieve_ack(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; u_char *ie; if (qi->channel_id.off) { ie = (u_char *)qi; ie += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(ie); AppPlci_retrieve_reply(aplci, CapiSuccess); } else AppPlci_retrieve_reply(aplci, 0x3711); /* resource Error */ } static void plci_cc_retrieve_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; u_char *ie; if (qi->channel_id.off) { ie = (u_char *)qi; ie += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(ie); AppPlci_retrieve_reply(aplci, CapiSuccess); plciL4L3(aplci->plci, CC_RETRIEVE_ACKNOWLEDGE | REQUEST, NULL); } else { AppPlci_retrieve_reply(aplci, 0x3711); /* resource Error */ plciL4L3(aplci->plci, CC_RETRIEVE_REJECT | REQUEST, NULL); } } static void AppPlci_suspend_reply(AppPlci_t *aplci, __u16 SuppServiceReason) { _cmsg *cmsg; __u8 tmp[10], *p; if (aplci->appl) { CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); p = &tmp[1]; p += capiEncodeWord(p, 0x0004); // Suspend p += capiEncodeFacIndSuspend(p, SuppServiceReason); tmp[0] = p - &tmp[1]; cmsg->FacilitySelector = 0x0003; cmsg->FacilityIndicationParameter = tmp; Send2Application(aplci, cmsg); } if (SuppServiceReason == CapiSuccess) mISDN_FsmEvent(&aplci->plci_m, EV_PI_SUSPEND_CONF, NULL); } static void plci_cc_suspend_err(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; u_char *p; __u16 SuppServiceReason; if (qi) { // reject from network if (qi->cause.off) { p = (u_char *)qi; p += L3_EXTRA_SIZE + qi->cause.off; SuppServiceReason = 0x3400 | p[3]; } else SuppServiceReason = CapiProtocolErrorLayer3; } else { // timeout SuppServiceReason = CapiTimeOut; } AppPlci_suspend_reply(aplci, SuppServiceReason); } static void plci_cc_suspend_conf(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; _cmsg *cmsg; AppPlciLinkDown(aplci); AppPlci_suspend_reply(aplci, CapiSuccess); CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) cmsg_free(cmsg); } static void plci_cc_resume_err(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; u_char *p; _cmsg *cmsg; CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND); if (qi) { // reject from network if (qi->cause.off) { p = (u_char *)qi; p += L3_EXTRA_SIZE + qi->cause.off; cmsg->Reason = 0x3400 | p[3]; } else cmsg->Reason = 0; } else { // timeout cmsg->Reason = CapiProtocolErrorLayer1; } if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg)) cmsg_free(cmsg); } static void plci_cc_resume_conf(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Q931_info_t *qi = arg; _cmsg *cmsg; __u8 tmp[10], *p; if (!qi || !qi->channel_id.off) { int_error(); return; } p = (u_char *)qi; p += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(p); CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); p = &tmp[1]; p += capiEncodeWord(p, 0x0005); // Suspend p += capiEncodeFacIndSuspend(p, CapiSuccess); tmp[0] = p - &tmp[1]; cmsg->FacilitySelector = 0x0003; cmsg->FacilityIndicationParameter = tmp; if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_RESUME_CONF, cmsg)) cmsg_free(cmsg); } static void plci_select_b_protocol_req(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; _cmsg *cmsg = arg; __u16 Info; int ret; Info = AppPlciCheckBprotocol(aplci, cmsg); if (Info) goto answer; ret = AppPlciLinkDown(aplci); if (ret) { Info = CapiMessageNotSupportedInCurrentState; goto answer; } ret = AppPlciLinkUp(aplci); if (ret < 0) Info = CapiMessageNotSupportedInCurrentState; else Info = ret; answer: capi_cmsg_answer(cmsg); cmsg->Info = Info; if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) Send2Application(aplci, arg); else Send2ApplicationDelayed(aplci, arg); } static void plci_info_req_overlap(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; Plci_t *plci = aplci->plci; _cmsg *cmsg = arg; __u16 Info = 0; struct sk_buff *skb; skb = mISDN_alloc_l3msg(100, MT_INFORMATION); if (skb) { Info = cmsg2info_req(cmsg, skb); if (Info == CapiSuccess) plciL4L3(plci, CC_INFORMATION | REQUEST, skb); else kfree_skb(skb); } capi_cmsg_answer(cmsg); cmsg->Info = Info; Send2Application(aplci, cmsg); } static void plci_cc_ph_control_ind(struct FsmInst *fi, int event, void *arg) { AppPlci_t *aplci = fi->userdata; int *tt = arg; _cmsg *cmsg; __u8 tmp[2]; if (!arg) return; AppPlciDebug(aplci, CAPI_DBG_PLCI_INFO, "%s: tt(%x)", __FUNCTION__, *tt); if ((*tt & ~DTMF_TONE_MASK) != DTMF_TONE_VAL) return; CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND); tmp[0] = 1; tmp[1] = *tt & DTMF_TONE_MASK; cmsg->FacilitySelector = 0x0001; cmsg->FacilityIndicationParameter = tmp; Send2Application(aplci, cmsg); } static void plci_info_req(struct FsmInst *fi, int event, void *arg) { // FIXME handle INFO CONF if (arg) cmsg_free(arg); } static struct FsmNode fn_plci_list[] = { {ST_PLCI_P_0, EV_AP_CONNECT_REQ, plci_connect_req}, {ST_PLCI_P_0, EV_PI_CONNECT_IND, plci_connect_ind}, {ST_PLCI_P_0, EV_AP_RESUME_REQ, plci_resume_req}, {ST_PLCI_P_0, EV_L3_SETUP_IND, plci_cc_setup_ind}, {ST_PLCI_P_0, EV_AP_RELEASE, plci_appl_release}, {ST_PLCI_P_0_1, EV_PI_CONNECT_CONF, plci_connect_conf}, {ST_PLCI_P_0_1, EV_AP_RELEASE, plci_appl_release}, {ST_PLCI_P_1, EV_PI_CONNECT_ACTIVE_IND, plci_connect_active_ind}, {ST_PLCI_P_1, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, {ST_PLCI_P_1, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, {ST_PLCI_P_1, EV_AP_INFO_REQ, plci_info_req_overlap}, {ST_PLCI_P_1, EV_L3_SETUP_CONF, plci_cc_setup_conf}, {ST_PLCI_P_1, EV_L3_SETUP_CONF_ERR, plci_cc_setup_conf_err}, {ST_PLCI_P_1, EV_L3_DISCONNECT_IND, plci_cc_disconnect_ind}, {ST_PLCI_P_1, EV_L3_RELEASE_PROC_IND, plci_cc_setup_conf_err}, {ST_PLCI_P_1, EV_L3_RELEASE_IND, plci_cc_release_ind}, {ST_PLCI_P_1, EV_L3_REJECT_IND, plci_cc_release_ind}, {ST_PLCI_P_1, EV_PI_CHANNEL_ERR, plci_channel_err}, {ST_PLCI_P_1, EV_AP_RELEASE, plci_appl_release_disc}, {ST_PLCI_P_2, EV_AP_ALERT_REQ, plci_alert_req}, {ST_PLCI_P_2, EV_AP_CONNECT_RESP, plci_connect_resp}, {ST_PLCI_P_2, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, {ST_PLCI_P_2, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, {ST_PLCI_P_2, EV_L3_RELEASE_PROC_IND, plci_cc_release_ind}, {ST_PLCI_P_2, EV_AP_INFO_REQ, plci_info_req}, {ST_PLCI_P_2, EV_L3_RELEASE_IND, plci_cc_release_ind}, {ST_PLCI_P_2, EV_AP_RELEASE, plci_appl_release_disc}, {ST_PLCI_P_4, EV_PI_CONNECT_ACTIVE_IND, plci_connect_active_ind}, {ST_PLCI_P_4, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, {ST_PLCI_P_4, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, {ST_PLCI_P_4, EV_AP_INFO_REQ, plci_info_req}, {ST_PLCI_P_4, EV_L3_SETUP_COMPL_IND, plci_cc_setup_compl_ind}, {ST_PLCI_P_4, EV_L3_RELEASE_IND, plci_cc_release_ind}, {ST_PLCI_P_4, EV_L3_RELEASE_PROC_IND, plci_cc_release_ind}, {ST_PLCI_P_4, EV_PI_CHANNEL_ERR, plci_channel_err}, {ST_PLCI_P_4, EV_AP_RELEASE, plci_appl_release_disc}, {ST_PLCI_P_ACT, EV_AP_CONNECT_ACTIVE_RESP, plci_connect_active_resp}, {ST_PLCI_P_ACT, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, {ST_PLCI_P_ACT, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, {ST_PLCI_P_ACT, EV_AP_INFO_REQ, plci_info_req}, {ST_PLCI_P_ACT, EV_AP_SELECT_B_PROTOCOL_REQ, plci_select_b_protocol_req}, {ST_PLCI_P_ACT, EV_AP_HOLD_REQ, plci_hold_req}, {ST_PLCI_P_ACT, EV_AP_SUSPEND_REQ, plci_suspend_req}, {ST_PLCI_P_ACT, EV_PI_SUSPEND_CONF, plci_suspend_conf}, {ST_PLCI_P_ACT, EV_L3_DISCONNECT_IND, plci_cc_disconnect_ind}, {ST_PLCI_P_ACT, EV_L3_RELEASE_IND, plci_cc_release_ind}, {ST_PLCI_P_ACT, EV_L3_RELEASE_PROC_IND, plci_cc_release_ind}, {ST_PLCI_P_ACT, EV_L3_NOTIFY_IND, plci_cc_notify_ind}, {ST_PLCI_P_ACT, EV_L3_HOLD_IND, plci_cc_hold_ind}, {ST_PLCI_P_ACT, EV_L3_HOLD_ACKNOWLEDGE, plci_cc_hold_ack}, {ST_PLCI_P_ACT, EV_L3_HOLD_REJECT, plci_cc_hold_rej}, {ST_PLCI_P_ACT, EV_PI_HOLD_CONF, plci_hold_conf}, {ST_PLCI_P_ACT, EV_L3_SUSPEND_ERR, plci_cc_suspend_err}, {ST_PLCI_P_ACT, EV_L3_SUSPEND_CONF, plci_cc_suspend_conf}, {ST_PLCI_P_ACT, EV_PH_CONTROL_IND, plci_cc_ph_control_ind}, {ST_PLCI_P_ACT, EV_AP_RELEASE, plci_appl_release_disc}, {ST_PLCI_P_HELD, EV_AP_RETRIEVE_REQ, plci_retrieve_req}, {ST_PLCI_P_HELD, EV_L3_RETRIEVE_ACKNOWLEDGE, plci_cc_retrieve_ack}, {ST_PLCI_P_HELD, EV_L3_RETRIEVE_REJECT, plci_cc_retrieve_rej}, {ST_PLCI_P_HELD, EV_PI_RETRIEVE_CONF, plci_retrieve_conf}, {ST_PLCI_P_HELD, EV_AP_DISCONNECT_REQ, plci_disconnect_req}, {ST_PLCI_P_HELD, EV_AP_INFO_REQ, plci_info_req}, {ST_PLCI_P_HELD, EV_L3_RETRIEVE_IND, plci_cc_retrieve_ind}, {ST_PLCI_P_HELD, EV_L3_DISCONNECT_IND, plci_cc_disconnect_ind}, {ST_PLCI_P_HELD, EV_L3_RELEASE_IND, plci_cc_release_ind}, {ST_PLCI_P_HELD, EV_L3_RELEASE_PROC_IND, plci_cc_release_ind}, {ST_PLCI_P_HELD, EV_L3_NOTIFY_IND, plci_cc_notify_ind}, {ST_PLCI_P_HELD, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, {ST_PLCI_P_HELD, EV_AP_RELEASE, plci_appl_release_disc}, {ST_PLCI_P_5, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, {ST_PLCI_P_5, EV_L3_RELEASE_IND, plci_cc_release_ind}, {ST_PLCI_P_5, EV_L3_RELEASE_PROC_IND, plci_cc_release_ind}, {ST_PLCI_P_5, EV_AP_RELEASE, plci_appl_release}, {ST_PLCI_P_6, EV_AP_DISCONNECT_RESP, plci_disconnect_resp}, {ST_PLCI_P_6, EV_AP_RELEASE, plci_disconnect_resp}, {ST_PLCI_P_RES, EV_PI_RESUME_CONF, plci_resume_conf}, {ST_PLCI_P_RES, EV_PI_DISCONNECT_IND, plci_disconnect_ind}, {ST_PLCI_P_RES, EV_L3_RESUME_ERR, plci_cc_resume_err}, {ST_PLCI_P_RES, EV_L3_RESUME_CONF, plci_cc_resume_conf}, {ST_PLCI_P_RES, EV_AP_RELEASE, plci_appl_release_disc}, }; const int FN_PLCI_COUNT = sizeof(fn_plci_list)/sizeof(struct FsmNode); int AppPlciConstr(AppPlci_t **aplci, Application_t *appl, Plci_t *plci) { AppPlci_t *apl = AppPlci_alloc(); if (!apl) return(-ENOMEM); memset(apl, 0, sizeof(AppPlci_t)); INIT_LIST_HEAD(&apl->head); INIT_LIST_HEAD(&apl->Nccis); apl->addr = plci->addr; apl->appl = appl; apl->plci = plci; apl->contr = plci->contr; apl->plci_m.fsm = &plci_fsm; apl->plci_m.state = ST_PLCI_P_0; apl->plci_m.debug = plci->contr->debug & CAPI_DBG_PLCI_STATE; apl->plci_m.userdata = apl; apl->plci_m.printdebug = AppPlci_debug; apl->channel = -1; skb_queue_head_init(&apl->delayedq); *aplci = apl; return(0); } void AppPlciDestr(AppPlci_t *aplci) { struct list_head *item, *next; if (aplci->plci) { AppPlciDebug(aplci, CAPI_DBG_PLCI, "%s plci state %s", __FUNCTION__, str_st_plci[aplci->plci_m.state]); if (aplci->plci_m.state != ST_PLCI_P_0) { struct sk_buff *skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE); unsigned char cause[] = {2,0x80,0x80| CAUSE_RESOURCES_UNAVAIL}; if (skb) { mISDN_AddIE(skb, IE_CAUSE, cause); plciL4L3(aplci->plci, CC_RELEASE_COMPLETE | REQUEST, skb); } } plciDetachAppPlci(aplci->plci, aplci); } list_for_each_safe(item, next, &aplci->Nccis) { ncciDelAppPlci((Ncci_t *)item); } if (aplci->appl) ApplicationDelAppPlci(aplci->appl, aplci); skb_queue_purge(&aplci->delayedq); AppPlci_free(aplci); } void AppPlciRelease(AppPlci_t *aplci) { struct list_head *item, *next; list_for_each_safe(item, next, &aplci->Nccis) { ncciApplRelease((Ncci_t *)item); } mISDN_FsmEvent(&aplci->plci_m, EV_AP_RELEASE, NULL); } static __inline__ Ncci_t * get_single_NCCI(AppPlci_t *aplci) { struct list_head *item = aplci->Nccis.next; if (item == &aplci->Nccis) return(NULL); if (item->next != &aplci->Nccis) return(NULL); // more as one NCCI return((Ncci_t *)item); } static int PL_l3l4(mISDNinstance_t *inst, struct sk_buff *skb) { AppPlci_t *aplci; Ncci_t *ncci; mISDN_head_t *hh; hh = mISDN_HEAD_P(skb); aplci = inst->privat; if (!aplci) return(-EINVAL); ncci = get_single_NCCI(aplci); capidebug(CAPI_DBG_NCCI_L3, "%s: prim(%x) dinfo (%x) skb(%p) APLCI(%x) ncci(%p)", __FUNCTION__, hh->prim, hh->dinfo, skb, aplci->addr, ncci); if (hh->prim == CAPI_MESSAGE_REQUEST) { if (!ncci) { int_error(); return(-EINVAL); } ncciSendMessage(ncci, skb); return(0); } if (!ncci) { if ((hh->prim != (DL_ESTABLISH | INDICATION)) && (hh->prim != (DL_ESTABLISH | CONFIRM))) { int_error(); return(-ENODEV); } ncci = ncciConstr(aplci); if (!ncci) { int_error(); return(-ENOMEM); } } return(ncci_l3l4(ncci, hh, skb)); } static int PL_l3l4mux(mISDNinstance_t *inst, struct sk_buff *skb) { AppPlci_t *aplci; Ncci_t *ncci; mISDN_head_t *hh; __u32 addr; hh = mISDN_HEAD_P(skb); aplci = inst->privat; if (!aplci) return(-EINVAL); capidebug(CAPI_DBG_NCCI_L3, "%s: prim(%x) dinfo (%x) skb->len(%d)", __FUNCTION__, hh->prim, hh->dinfo, skb->len); if (skb->len < 4) { int_error(); return(-EINVAL); } if (hh->prim == CAPI_MESSAGE_REQUEST) { ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_EXACT); if (!ncci) { int_error(); return(-EINVAL); } ncciSendMessage(ncci, skb); return(0); } addr = CAPIMSG_U32(skb->data, 0); ncci = getNCCI4addr(aplci, addr, GET_NCCI_ONLY_PLCI); if (hh->prim == CAPI_CONNECT_B3_IND) { if (ncci) { int_error(); return(-EBUSY); } ncci = ncciConstr(aplci); if (!ncci) { int_error(); return(-ENOMEM); } addr &= 0xffff0000; addr |= aplci->addr; ncci->addr = addr; capimsg_setu32(skb->data, 0, addr); #ifdef OLDCAPI_DRIVER_INTERFACE ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, addr, ncci->window); #endif } else if (hh->prim == CAPI_CONNECT_B3_CONF) { if (ncci && ((addr & 0xffff0000) != 0)) { if (ncci->addr != addr) { ncci->addr = addr; #ifdef OLDCAPI_DRIVER_INTERFACE ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, addr, ncci->window); #endif } else int_error(); } } if (!ncci) { int_error(); return(-ENODEV); } return(ncci_l3l4_direct(ncci, hh, skb)); } static int AppPlciLinkUp(AppPlci_t *aplci) { mISDN_pid_t pid; mISDN_stPara_t stpara; int retval; if (aplci->channel == -1) {/* no valid channel set */ int_error(); return -EINVAL; } memset(&pid, 0, sizeof(mISDN_pid_t)); pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2) | ISDN_LAYER(3) | ISDN_LAYER(4); if (test_bit(PLCI_STATE_OUTGOING, &aplci->plci->state)) pid.global = 1; // DTE, orginate else pid.global = 2; // DCE, answer if (aplci->Bprotocol.B1 > 23) { int_errtxt("wrong B1 prot %x", aplci->Bprotocol.B1); return 0x3001; } pid.protocol[1] = (1 << aplci->Bprotocol.B1) | ISDN_PID_LAYER(1) | ISDN_PID_BCHANNEL_BIT; retval = mISDN_add_pid_parameter(&pid, 1, &aplci->Bprotocol.B1cfg[0]); if (retval) {/* ressource error */ kfree(pid.pbuf); return 0x1008; } if (aplci->Bprotocol.B2 > 23) { int_errtxt("wrong B2 prot %x", aplci->Bprotocol.B2); kfree(pid.pbuf); return 0x3002; } pid.protocol[2] = (1 << aplci->Bprotocol.B2) | ISDN_PID_LAYER(2) | ISDN_PID_BCHANNEL_BIT; retval = mISDN_add_pid_parameter(&pid, 2, &aplci->Bprotocol.B2cfg[0]); if (retval) {/* ressource error */ kfree(pid.pbuf); return 0x1008; } /* handle DTMF TODO */ if ((pid.protocol[2] == ISDN_PID_L2_B_TRANS) && (pid.protocol[1] == ISDN_PID_L1_B_64TRANS)) pid.protocol[2] = ISDN_PID_L2_B_TRANSDTMF; if (aplci->Bprotocol.B3 > 23) { int_errtxt("wrong B3 prot %x", aplci->Bprotocol.B3); kfree(pid.pbuf); return 0x3003; } pid.protocol[3] = (1 << aplci->Bprotocol.B3) | ISDN_PID_LAYER(3) | ISDN_PID_BCHANNEL_BIT; retval = mISDN_add_pid_parameter(&pid, 3, &aplci->Bprotocol.B3cfg[0]); if (retval) {/* ressource error */ kfree(pid.pbuf); return 0x1008; } capidebug(CAPI_DBG_PLCI, "AppPlciLinkUp B1(%x) B2(%x) B3(%x) global(%d) ch(%x)", pid.protocol[1], pid.protocol[2], pid.protocol[3], pid.global, aplci->channel); capidebug(CAPI_DBG_PLCI, "AppPlciLinkUp B1cfg(%d) B2cfg(%d) B3cfg(%d) maxplen(%d)", aplci->Bprotocol.B1cfg[0], aplci->Bprotocol.B2cfg[0], aplci->Bprotocol.B3cfg[0], pid.maxplen); capidebug(CAPI_DBG_PLCI, "AppPlciLinkUp ch(%d) aplci->contr->linklist(%p)", aplci->channel & 3, aplci->contr->linklist); pid.protocol[4] = ISDN_PID_L4_B_CAPI20; aplci->link = ControllerSelChannel(aplci->contr, aplci->channel); if (!aplci->link) { int_error(); kfree(pid.pbuf); return(-EBUSY); } capidebug(CAPI_DBG_NCCI, "AppPlciLinkUp aplci->link(%p)", aplci->link); memset(&aplci->link->inst.pid, 0, sizeof(mISDN_pid_t)); aplci->link->inst.privat = aplci; aplci->link->inst.pid.layermask = ISDN_LAYER(4); aplci->link->inst.pid.protocol[4] = ISDN_PID_L4_B_CAPI20; if (pid.protocol[3] == ISDN_PID_L3_B_TRANS) { aplci->link->inst.pid.protocol[3] = ISDN_PID_L3_B_TRANS; aplci->link->inst.pid.layermask |= ISDN_LAYER(3); } if (aplci->link->inst.function) int_errtxt("id(%08x) overwrite function (%p)", aplci->link->inst.id, aplci->link->inst.function); if (aplci->Bprotocol.B3 == 0) // transparent aplci->link->inst.function = PL_l3l4; else aplci->link->inst.function = PL_l3l4mux; retval = mISDN_ctrl(aplci->link->st, MGR_ADDLAYER | REQUEST, &aplci->link->inst); if (retval) { printk(KERN_WARNING "%s MGR_ADDLAYER | REQUEST ret(%d)\n", __FUNCTION__, retval); return(retval); } stpara.maxdatalen = aplci->appl->reg_params.datablklen; stpara.up_headerlen = CAPI_B3_DATA_IND_HEADER_SIZE; stpara.down_headerlen = 0; retval = mISDN_ctrl(aplci->link->st, MGR_ADDSTPARA | REQUEST, &stpara); if (retval) { printk(KERN_WARNING "%s MGR_SETSTACK | REQUEST ret(%d)\n", __FUNCTION__, retval); } retval = mISDN_ctrl(aplci->link->st, MGR_SETSTACK | REQUEST, &pid); kfree(pid.pbuf); if (retval) { printk(KERN_WARNING "%s MGR_SETSTACK | REQUEST ret(%d)\n", __FUNCTION__, retval); return(retval); } return(0); } static int ReleaseLink(AppPlci_t *aplci) { int retval = 0; if (aplci->link) { #if 0 if (ncci->ncci_m.state != ST_NCCI_N_0) ncciL4L3(ncci, DL_RELEASE | REQUEST, 0, 0, NULL, NULL); #endif retval = mISDN_ctrl(aplci->link->inst.st, MGR_CLEARSTACK | REQUEST, NULL); if (retval) int_error(); aplci->link = NULL; skb_queue_purge(&aplci->delayedq); test_and_clear_bit(PLCI_STATE_STACKREADY, &aplci->plci->state); } return(retval); } Ncci_t * getNCCI4addr(AppPlci_t *aplci, __u32 addr, int mode) { Ncci_t *ncci = NULL; struct list_head *item; int cnt = 0; list_for_each(item, &aplci->Nccis) { cnt++; ncci = (Ncci_t *)item; if (ncci->addr == addr) return(ncci); if (mode == GET_NCCI_ONLY_PLCI) { if (ncci->addr == (addr & 0xffff)) return(ncci); } } if (!cnt) return(NULL); if (mode != GET_NCCI_PLCI) return(NULL); if (1 == cnt) { if (!(addr & 0xffff0000)) return(ncci); } return(NULL); } void AppPlciDelNCCI(Ncci_t *ncci) { list_del_init(&ncci->head); } int AppPlcimISDN_Active(AppPlci_t *aplci) { if (!aplci) { int_error(); return(-EINVAL); } if (!test_and_set_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state)) { test_and_set_bit(PLCI_STATE_STACKREADY, &aplci->plci->state); SendingDelayedMsg(aplci); } else test_and_set_bit(PLCI_STATE_STACKREADY, &aplci->plci->state); return(0); } void AppPlci_l3l4(AppPlci_t *aplci, int pr, void *arg) { Q931_info_t *qi = arg; u_char *ie; AppPlciDebug(aplci, CAPI_DBG_PLCI_L3, "%s: aplci(%x) pr(%x) arg(%p)", __FUNCTION__, aplci->addr, pr, arg); switch (pr) { case CC_SETUP | INDICATION: if (!qi) return; if (qi->channel_id.off) { ie = (u_char *)qi; ie += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(ie); } mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_IND, arg); if (qi) { AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi); AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); AppPlciInfoIndIE(aplci, IE_CALLED_PN, CAPI_INFOMASK_CALLEDPN, qi); AppPlciInfoIndIE(aplci, IE_COMPLETE, CAPI_INFOMASK_COMPLETE, qi); } break; case CC_TIMEOUT | INDICATION: mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_CONF_ERR, arg); break; case CC_CONNECT | INDICATION: if (qi) { AppPlciInfoIndIE(aplci, IE_DATE, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi); AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); if (qi->channel_id.off) { ie = (u_char *)qi; ie += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(ie); } } mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_CONF, arg); break; case CC_CONNECT_ACKNOWLEDGE | INDICATION: if (qi) { AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); if (qi->channel_id.off) { ie = (u_char *)qi; ie += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(ie); } } mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_COMPL_IND, arg); break; case CC_DISCONNECT | INDICATION: if (qi) { AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_EARLYB3, MT_DISCONNECT); AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi); AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); } mISDN_FsmEvent(&aplci->plci_m, EV_L3_DISCONNECT_IND, arg); break; case CC_RELEASE | INDICATION: if (qi) { AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); } mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_IND, arg); break; case CC_RELEASE_COMPLETE | INDICATION: if (qi) { AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); } mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_IND, arg); break; case CC_RELEASE_CR | INDICATION: mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_PROC_IND, arg); break; case CC_SETUP_ACKNOWLEDGE | INDICATION: if (qi) { AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_SETUP_ACKNOWLEDGE); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi); AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); if (qi->channel_id.off) { ie = (u_char *)qi; ie += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(ie); } } break; case CC_PROCEEDING | INDICATION: if (qi) { AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_CALL_PROCEEDING); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi); AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); if (qi->channel_id.off) { ie = (u_char *)qi; ie += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(ie); } } break; case CC_ALERTING | INDICATION: if (qi) { AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_ALERTING); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi); AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi); AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); if (qi->channel_id.off) { ie = (u_char *)qi; ie += L3_EXTRA_SIZE + qi->channel_id.off; aplci->channel = plci_parse_channel_id(ie); } } break; case CC_PROGRESS | INDICATION: if (qi) { AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_PROGRESS); AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi); AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi); } break; case CC_HOLD | INDICATION: if (qi) AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); if (mISDN_FsmEvent(&aplci->plci_m, EV_L3_HOLD_IND, arg)) { /* no routine reject L3 */ plciL4L3(aplci->plci, CC_HOLD_REJECT | REQUEST, NULL); } break; case CC_HOLD_ACKNOWLEDGE | INDICATION: if (qi) AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); mISDN_FsmEvent(&aplci->plci_m, EV_L3_HOLD_ACKNOWLEDGE, arg); break; case CC_HOLD_REJECT | INDICATION: if (qi) { AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); } mISDN_FsmEvent(&aplci->plci_m, EV_L3_HOLD_REJECT, arg); break; case CC_RETRIEVE | INDICATION: if (qi) { AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); } if (mISDN_FsmEvent(&aplci->plci_m, EV_L3_RETRIEVE_IND, arg)) { /* no routine reject L3 */ plciL4L3(aplci->plci, CC_RETRIEVE_REJECT | REQUEST, NULL); } break; case CC_RETRIEVE_ACKNOWLEDGE | INDICATION: if (qi) { AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi); } mISDN_FsmEvent(&aplci->plci_m, EV_L3_RETRIEVE_ACKNOWLEDGE, arg); break; case CC_RETRIEVE_REJECT | INDICATION: if (qi) { AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi); AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi); } mISDN_FsmEvent(&aplci->plci_m, EV_L3_RETRIEVE_REJECT, arg); break; case CC_SUSPEND_ACKNOWLEDGE | INDICATION: mISDN_FsmEvent(&aplci->plci_m, EV_L3_SUSPEND_CONF, arg); break; case CC_SUSPEND_REJECT | INDICATION: mISDN_FsmEvent(&aplci->plci_m, EV_L3_SUSPEND_ERR, arg); break; case CC_RESUME_ACKNOWLEDGE | INDICATION: mISDN_FsmEvent(&aplci->plci_m, EV_L3_RESUME_CONF, arg); break; case CC_RESUME_REJECT | INDICATION: mISDN_FsmEvent(&aplci->plci_m, EV_L3_RESUME_ERR, arg); break; case CC_NOTIFY | INDICATION: mISDN_FsmEvent(&aplci->plci_m, EV_L3_NOTIFY_IND, arg); break; case PH_CONTROL | INDICATION: /* TOUCH TONE */ mISDN_FsmEvent(&aplci->plci_m, EV_PH_CONTROL_IND, arg); break; default: AppPlciDebug(aplci, CAPI_DBG_WARN, "%s: pr 0x%x not handled", __FUNCTION__, pr); break; } } void AppPlciGetCmsg(AppPlci_t *aplci, _cmsg *cmsg) { int retval = 0; switch (CMSGCMD(cmsg)) { case CAPI_INFO_REQ: retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_INFO_REQ, cmsg); break; case CAPI_ALERT_REQ: retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_ALERT_REQ, cmsg); break; case CAPI_CONNECT_REQ: retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_REQ, cmsg); break; case CAPI_CONNECT_RESP: retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_RESP, cmsg); break; case CAPI_DISCONNECT_REQ: retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_DISCONNECT_REQ, cmsg); break; case CAPI_DISCONNECT_RESP: retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_DISCONNECT_RESP, cmsg); break; case CAPI_CONNECT_ACTIVE_RESP: retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_ACTIVE_RESP, cmsg); break; case CAPI_SELECT_B_PROTOCOL_REQ: retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_SELECT_B_PROTOCOL_REQ, cmsg); break; default: int_error(); retval = -1; } if (retval) { if (cmsg->Command == CAPI_REQ) { capi_cmsg_answer(cmsg); cmsg->Info = CapiMessageNotSupportedInCurrentState; Send2Application(aplci, cmsg); } else cmsg_free(cmsg); } } __u16 AppPlciSendMessage(AppPlci_t *aplci, struct sk_buff *skb) { _cmsg *cmsg; __u16 ret; cmsg = cmsg_alloc(); if (!cmsg) { int_error(); ret = CAPI_REGOSRESOURCEERR; } else { capi_message2cmsg(cmsg, skb->data); AppPlciGetCmsg(aplci, cmsg); dev_kfree_skb(skb); ret = CAPI_NOERROR; } return(ret); } void ConnectB3Request(AppPlci_t *aplci, struct sk_buff *skb) { Ncci_t *ncci = ncciConstr(aplci); int err; if (!ncci) { int_error(); dev_kfree_skb(skb); return; } if (!ncci->link) { int_error(); dev_kfree_skb(skb); return; } err = mISDN_queue_message(&ncci->link->inst, 0, skb); if (err) { int_errtxt("mISDN_queue_message return(%d)", err); dev_kfree_skb(skb); } } void DisconnectB3Request(AppPlci_t *aplci, struct sk_buff *skb) { Ncci_t *ncci; int err; ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_EXACT); if ((!ncci) || (!ncci->link)) { int_error(); if (aplci->appl) AnswerMessage2Application(aplci->appl, skb, CapiIllContrPlciNcci); dev_kfree_skb(skb); return; } if (ncci->link->inst.id == 0) { /* stack is already cleared and so we cannot handle this via the stack */ ncciSendMessage(ncci, skb); return; } err = mISDN_queue_message(&ncci->link->inst, 0, skb); if (err) { int_errtxt("mISDN_queue_message return(%d)", err); dev_kfree_skb(skb); } } static int AppPlciLinkDown(AppPlci_t *aplci) { struct list_head *item, *next; list_for_each_safe(item, next, &aplci->Nccis) { ncciReleaseLink((Ncci_t *)item); } ReleaseLink(aplci); return(0); } int AppPlciFacHoldReq(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) { struct sk_buff *skb; skb = mISDN_alloc_l3msg(20, MT_HOLD); if (!skb) { int_error(); return CapiIllMessageParmCoding; } if (mISDN_FsmEvent(&aplci->plci_m, EV_AP_HOLD_REQ, skb)) { // no routine facConfParm->u.Info.SupplementaryServiceInfo = CapiRequestNotAllowedInThisState; dev_kfree_skb(skb); return CapiMessageNotSupportedInCurrentState; } else { facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; } return CapiSuccess; } int AppPlciFacRetrieveReq(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) { struct sk_buff *skb; skb = mISDN_alloc_l3msg(20, MT_RETRIEVE); if (!skb) { int_error(); return CapiIllMessageParmCoding; } if (mISDN_FsmEvent(&aplci->plci_m, EV_AP_RETRIEVE_REQ, skb)) { // no routine facConfParm->u.Info.SupplementaryServiceInfo = CapiRequestNotAllowedInThisState; dev_kfree_skb(skb); return CapiMessageNotSupportedInCurrentState; } else { facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; } return CapiSuccess; } int AppPlciFacSuspendReq(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) { __u8 *CallIdentity; struct sk_buff *skb; CallIdentity = facReqParm->u.Suspend.CallIdentity; if (CallIdentity && CallIdentity[0] > 8) return CapiIllMessageParmCoding; skb = mISDN_alloc_l3msg(20, MT_SUSPEND); if (!skb) { int_error(); return CapiIllMessageParmCoding; } if (CallIdentity && CallIdentity[0]) mISDN_AddIE(skb, IE_CALL_ID, CallIdentity); if (mISDN_FsmEvent(&aplci->plci_m, EV_AP_SUSPEND_REQ, skb)) { // no routine facConfParm->u.Info.SupplementaryServiceInfo = CapiRequestNotAllowedInThisState; dev_kfree_skb(skb); return CapiMessageNotSupportedInCurrentState; } else { facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; } return CapiSuccess; } int AppPlciFacResumeReq(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) { __u8 *CallIdentity; struct sk_buff *skb; CallIdentity = facReqParm->u.Resume.CallIdentity; if (CallIdentity && CallIdentity[0] > 8) { AppPlciDestr(aplci); return CapiIllMessageParmCoding; } skb = mISDN_alloc_l3msg(20, MT_RESUME); if (!skb) { int_error(); AppPlciDestr(aplci); return CapiIllMessageParmCoding; } if (CallIdentity && CallIdentity[0]) mISDN_AddIE(skb, IE_CALL_ID, CallIdentity); if (mISDN_FsmEvent(&aplci->plci_m, EV_AP_RESUME_REQ, skb)) dev_kfree_skb(skb); facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; return CapiSuccess; } static void AppPlciClearOtherApps(AppPlci_t *aplci) { AppPlci_t *o_aplci; _cmsg *cm; struct list_head *item, *next; if (!aplci->plci) return; if (aplci->plci->nAppl <= 1) return; list_for_each_safe(item, next, &aplci->plci->AppPlcis) { o_aplci = (AppPlci_t *)item; if (o_aplci != aplci) { CMSG_ALLOC(cm); AppPlciCmsgHeader(o_aplci, cm, CAPI_DISCONNECT, CAPI_IND); cm->Reason = 0x3304; // other application got the call mISDN_FsmEvent(&o_aplci->plci_m, EV_PI_DISCONNECT_IND, cm); } } } static void AppPlciInfoIndMsg(AppPlci_t *aplci, __u32 mask, unsigned char mt) { _cmsg *cmsg; if ((!aplci->appl) || (!(aplci->appl->InfoMask & mask))) return; CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_INFO, CAPI_IND); cmsg->InfoNumber = 0x8000 | mt; cmsg->InfoElement = 0; Send2Application(aplci, cmsg); } static void AppPlciInfoIndIE(AppPlci_t *aplci, unsigned char ie, __u32 mask, Q931_info_t *qi) { _cmsg *cmsg; u_char *iep = NULL; ie_info_t *ies; if ((!aplci->appl) || (!(aplci->appl->InfoMask & mask))) return; if (!qi) return; ies = &qi->bearer_capability; if (ie & 0x80) { /* single octett */ if (ie == IE_COMPLETE) { if (!qi->sending_complete.off) return; } else { int_error(); return; } } else { if (mISDN_l3_ie2pos(ie) < 0) return; ies += mISDN_l3_ie2pos(ie); if (!ies->off) return; iep = (u_char *)qi; iep += L3_EXTRA_SIZE + ies->off +1; } CMSG_ALLOC(cmsg); AppPlciCmsgHeader(aplci, cmsg, CAPI_INFO, CAPI_IND); cmsg->InfoNumber = ie; cmsg->InfoElement = iep; if (ie == IE_PROGRESS && aplci->appl->InfoMask & CAPI_INFOMASK_EARLYB3) { if (iep[0] == 0x02 && iep[2] == 0x88) { // in-band information available AppPlciLinkUp(aplci); if (!test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) { Send2ApplicationDelayed(aplci,cmsg); return; } } } Send2Application(aplci, cmsg); } void init_AppPlci(void) { plci_fsm.state_count = ST_PLCI_COUNT; plci_fsm.event_count = EV_PLCI_COUNT; plci_fsm.strEvent = str_ev_plci; plci_fsm.strState = str_st_plci; mISDN_FsmNew(&plci_fsm, fn_plci_list, FN_PLCI_COUNT); } void free_AppPlci(void) { mISDN_FsmFree(&plci_fsm); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/appl.c0000644000000000000500000003134611135651701017475 0ustar rootsrc/* $Id: appl.c,v 1.14 2006/03/06 12:52:07 keil Exp $ * * Applications are owned by the controller and only * handle this controller, multiplexing multiple * controller with one application is done in the higher * driver independ CAPI driver. The application contain * the Listen state machine. * */ #include "m_capi.h" #include "helper.h" #include "debug.h" #include "mISDNManufacturer.h" #define applDebug(appl, lev, fmt, args...) \ capidebug(lev, fmt, ## args) static struct list_head garbage_applications = LIST_HEAD_INIT(garbage_applications); int ApplicationConstr(Controller_t *contr, __u16 ApplId, capi_register_params *rp) { Application_t *appl = kmalloc(sizeof(Application_t), GFP_ATOMIC); if (!appl) { return(-ENOMEM); } memset(appl, 0, sizeof(Application_t)); INIT_LIST_HEAD(&appl->head); appl->contr = contr; appl->maxplci = contr->maxplci; appl->AppPlcis = kmalloc(appl->maxplci * sizeof(AppPlci_t *), GFP_ATOMIC); if (!appl->AppPlcis) { kfree(appl); return(-ENOMEM); } memset(appl->AppPlcis, 0, appl->maxplci * sizeof(AppPlci_t *)); appl->ApplId = ApplId; appl->MsgId = 1; appl->NotificationMask = 0; memcpy(&appl->reg_params, rp, sizeof(capi_register_params)); listenConstr(appl); list_add(&appl->head, &contr->Applications); test_and_set_bit(APPL_STATE_ACTIV, &appl->state); return(0); } /* * Destroy the Application * * depending who initiate this we cannot release imediatly, if * any AppPlci is still in use. * * @who: 0 - a AppPlci is released in state APPL_STATE_RELEASE * 1 - Application is released from CAPI application * 2 - the controller is resetted * 3 - the controller is removed * 4 - the CAPI module will be unload */ int ApplicationDestr(Application_t *appl, int who) { int i, used = 0; AppPlci_t **aplci_p = appl->AppPlcis; if (test_and_set_bit(APPL_STATE_DESTRUCTOR, &appl->state)) { // we are allready in this function return(-EBUSY); } test_and_set_bit(APPL_STATE_RELEASE, &appl->state); test_and_clear_bit(APPL_STATE_ACTIV, &appl->state); listenDestr(appl); if (who > 2) { appl->contr = NULL; } if (aplci_p) { for (i = 0; i < appl->maxplci; i++) { if (*aplci_p) { switch (who) { case 4: AppPlciDestr(*aplci_p); *aplci_p = NULL; break; case 1: case 2: case 3: AppPlciRelease(*aplci_p); case 0: if ((volatile AppPlci_t *)(*aplci_p)) used++; break; } } aplci_p++; } } if (used) { if (who == 3) { list_del_init(&appl->head); list_add(&appl->head, &garbage_applications); } test_and_clear_bit(APPL_STATE_DESTRUCTOR, &appl->state); return(-EBUSY); } list_del_init(&appl->head); appl->maxplci = 0; kfree(appl->AppPlcis); appl->AppPlcis = NULL; kfree(appl); return(0); } AppPlci_t * getAppPlci4addr(Application_t *appl, __u32 addr) { int plci_idx = (addr >> 8) & 0xff; if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) { int_error(); return NULL; } return(appl->AppPlcis[plci_idx - 1]); } static void FacilityReq(Application_t *appl, struct sk_buff *skb) { _cmsg *cmsg; AppPlci_t *aplci; Ncci_t *ncci; cmsg = cmsg_alloc(); if (!cmsg) { int_error(); dev_kfree_skb(skb); return; } capi_message2cmsg(cmsg, skb->data); switch (cmsg->FacilitySelector) { case 0x0000: // Handset case 0x0001: // DTMF aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); if (aplci) { ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_PLCI); if (ncci) { ncciGetCmsg(ncci, cmsg); break; } } SendCmsgAnswer2Application(appl, cmsg, CapiIllContrPlciNcci); break; case 0x0003: // SupplementaryServices SupplementaryFacilityReq(appl, cmsg); break; default: int_error(); SendCmsgAnswer2Application(appl, cmsg, CapiFacilityNotSupported); break; } dev_kfree_skb(skb); } void ApplicationSendMessage(Application_t *appl, struct sk_buff *skb) { Plci_t *plci; AppPlci_t *aplci; __u16 ret; switch (CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data))) { // new NCCI case CAPI_CONNECT_B3_REQ: aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); if (!aplci) { AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci); goto free; } 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: case CAPI_CONNECT_RESP: case CAPI_CONNECT_ACTIVE_RESP: case CAPI_DISCONNECT_REQ: case CAPI_DISCONNECT_RESP: case CAPI_SELECT_B_PROTOCOL_REQ: aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); if (!aplci) { AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci); goto free; } ret = AppPlciSendMessage(aplci, skb); if (ret) { int_error(); } break; case CAPI_CONNECT_REQ: if (ControllerNewPlci(appl->contr, &plci, MISDN_ID_ANY)) { AnswerMessage2Application(appl, skb, CapiNoPlciAvailable); goto free; } aplci = ApplicationNewAppPlci(appl, plci); if (!aplci) { AnswerMessage2Application(appl, skb, CapiNoPlciAvailable); 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 case CAPI_FACILITY_REQ: FacilityReq(appl, skb); break; case CAPI_FACILITY_RESP: goto free; case CAPI_MANUFACTURER_REQ: applManufacturerReq(appl, skb); break; case CAPI_INFO_RESP: goto free; default: applDebug(appl, CAPI_DBG_WARN, "applSendMessage: %#x %#x not handled!", CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)); break; } return; free: dev_kfree_skb(skb); return; } AppPlci_t * ApplicationNewAppPlci(Application_t *appl, Plci_t *plci) { AppPlci_t *aplci; int plci_idx = (plci->addr >> 8) & 0xff; if (test_bit(APPL_STATE_RELEASE, &appl->state)) return(NULL); if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) { int_error(); return(NULL); } if (appl->AppPlcis[plci_idx - 1]) { int_error(); return(NULL); } if (AppPlciConstr(&aplci, appl, plci)) { int_error(); return(NULL); } applDebug(appl, CAPI_DBG_APPL_INFO, "ApplicationNewAppPlci: idx(%d) aplci(%p) appl(%p) plci(%p)", plci_idx, aplci, appl, plci); appl->AppPlcis[plci_idx - 1] = aplci; plciAttachAppPlci(plci, aplci); return(aplci); } void ApplicationDelAppPlci(Application_t *appl, AppPlci_t *aplci) { int plci_idx = (aplci->addr >> 8) & 0xff; if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) { int_error(); return; } if (appl->AppPlcis[plci_idx - 1] != aplci) { int_error(); return; } appl->AppPlcis[plci_idx - 1] = NULL; if (test_bit(APPL_STATE_RELEASE, &appl->state) && !test_bit(APPL_STATE_DESTRUCTOR, &appl->state)) ApplicationDestr(appl, 0); } void SendCmsg2Application(Application_t *appl, _cmsg *cmsg) { struct sk_buff *skb; if (test_bit(APPL_STATE_RELEASE, &appl->state)) { /* Application is released and cannot receive messages * anymore. To avoid stalls in the state machines we * must answer INDICATIONS. */ AppPlci_t *aplci; Ncci_t *ncci; if (CAPI_IND != cmsg->Subcommand) goto free; switch(cmsg->Command) { // for NCCI state machine case CAPI_CONNECT_B3: cmsg->Reject = 2; case CAPI_CONNECT_B3_ACTIVE: case CAPI_DISCONNECT_B3: aplci = getAppPlci4addr(appl, (cmsg->adr.adrNCCI & 0xffff)); if (!aplci) goto free; ncci = getNCCI4addr(aplci, cmsg->adr.adrNCCI, GET_NCCI_EXACT); if (!ncci) { int_error(); goto free; } capi_cmsg_answer(cmsg); ncciGetCmsg(ncci, cmsg); break; // for PLCI state machine case CAPI_CONNECT: cmsg->Reject = 2; case CAPI_CONNECT_ACTIVE: case CAPI_DISCONNECT: aplci = getAppPlci4addr(appl, (cmsg->adr.adrPLCI & 0xffff)); if (!aplci) goto free; capi_cmsg_answer(cmsg); AppPlciGetCmsg(aplci, cmsg); break; case CAPI_FACILITY: case CAPI_MANUFACTURER: case CAPI_INFO: goto free; default: int_error(); goto free; } return; } if (!(skb = alloc_skb(CAPI_MSG_DEFAULT_LEN, GFP_ATOMIC))) { printk(KERN_WARNING "%s: no mem for %d bytes\n", __FUNCTION__, CAPI_MSG_DEFAULT_LEN); int_error(); goto free; } capi_cmsg2message(cmsg, skb->data); applDebug(appl, CAPI_DBG_APPL_MSG, "%s: len(%d) applid(%x) %s msgnr(%d) addr(%08x)", __FUNCTION__, CAPIMSG_LEN(skb->data), cmsg->ApplId, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Messagenumber, cmsg->adr.adrController); if (CAPI_MSG_DEFAULT_LEN < CAPIMSG_LEN(skb->data)) { printk(KERN_ERR "%s: CAPI_MSG_DEFAULT_LEN overrun (%d/%d)\n", __FUNCTION__, CAPIMSG_LEN(skb->data), CAPI_MSG_DEFAULT_LEN); int_error(); dev_kfree_skb(skb); goto free; } skb_put(skb, CAPIMSG_LEN(skb->data)); #ifdef OLDCAPI_DRIVER_INTERFACE appl->contr->ctrl->handle_capimsg(appl->contr->ctrl, cmsg->ApplId, skb); #else capi_ctr_handle_message(appl->contr->ctrl, cmsg->ApplId, skb); #endif free: cmsg_free(cmsg); } void SendCmsgAnswer2Application(Application_t *appl, _cmsg *cmsg, __u16 Info) { capi_cmsg_answer(cmsg); cmsg->Info = Info; SendCmsg2Application(appl, cmsg); } void AnswerMessage2Application(Application_t *appl, struct sk_buff *skb, __u16 Info) { _cmsg *cmsg; CMSG_ALLOC(cmsg); capi_message2cmsg(cmsg, skb->data); SendCmsgAnswer2Application(appl, cmsg, Info); } #define AVM_MANUFACTURER_ID 0x214D5641 /* "AVM!" */ #define CLASS_AVM 0x00 #define FUNCTION_AVM_D2_TRACE 0x01 struct AVMD2Trace { __u8 Length; __u8 data[4]; }; void applManufacturerReqAVM(Application_t *appl, _cmsg *cmsg, struct sk_buff *skb) { struct AVMD2Trace *at; if (cmsg->Class != CLASS_AVM) { applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown class %#x\n", cmsg->Class); cmsg_free(cmsg); dev_kfree_skb(skb); return; } switch (cmsg->Function) { case FUNCTION_AVM_D2_TRACE: at = (struct AVMD2Trace *)cmsg->ManuData; if (!at || at->Length != 4) { int_error(); break; } if (memcmp(at->data, "\200\014\000\000", 4) == 0) { test_and_set_bit(APPL_STATE_D2TRACE, &appl->state); } else if (memcmp(at->data, "\000\000\000\000", 4) == 0) { test_and_clear_bit(APPL_STATE_D2TRACE, &appl->state); } else { int_error(); } break; default: applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown function %#x\n", cmsg->Function); } cmsg_free(cmsg); dev_kfree_skb(skb); } void applManufacturerReqmISDN(Application_t *appl, _cmsg *cmsg, struct sk_buff *skb) { AppPlci_t *aplci; Ncci_t *ncci; switch (cmsg->Class) { case mISDN_MF_CLASS_HANDSET: /* Note normally MANUFATURER messages are only defined for * controller address we extent it here to PLCI/NCCI */ aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data)); if (aplci) { ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_PLCI); if (ncci) { cmsg_free(cmsg); ncciSendMessage(ncci, skb); return; } } SendCmsgAnswer2Application(appl, cmsg, CapiIllContrPlciNcci); break; default: cmsg_free(cmsg); break; } dev_kfree_skb(skb); } void applManufacturerReq(Application_t *appl, struct sk_buff *skb) { _cmsg *cmsg; if (skb->len < 16 + 8) { dev_kfree_skb(skb); return; } cmsg = cmsg_alloc(); if (!cmsg) { int_error(); dev_kfree_skb(skb); return; } capi_message2cmsg(cmsg, skb->data); switch (cmsg->ManuID) { case mISDN_MANUFACTURER_ID: applManufacturerReqmISDN(appl, cmsg, skb); break; case AVM_MANUFACTURER_ID: applManufacturerReqAVM(appl, cmsg, skb); break; default: applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown ManuID %#x\n", cmsg->ManuID); cmsg_free(cmsg); dev_kfree_skb(skb); break; } } void applD2Trace(Application_t *appl, u_char *buf, int len) { _cmsg *cmsg; __u8 manuData[255]; if (!test_bit(APPL_STATE_D2TRACE, &appl->state)) return; CMSG_ALLOC(cmsg); capi_cmsg_header(cmsg, appl->ApplId, CAPI_MANUFACTURER, CAPI_IND, appl->MsgId++, appl->contr->addr); cmsg->ManuID = AVM_MANUFACTURER_ID; cmsg->Class = CLASS_AVM; cmsg->Function = FUNCTION_AVM_D2_TRACE; cmsg->ManuData = (_cstruct) &manuData; manuData[0] = 2 + len; // length manuData[1] = 0x80; manuData[2] = 0x0f; memcpy(&manuData[3], buf, len); SendCmsg2Application(appl, cmsg); } void free_Application(void) { struct list_head *item, *next; int n = 0; if (list_empty(&garbage_applications)) { printk(KERN_DEBUG "%s: no garbage\n", __FUNCTION__); return; } list_for_each_safe(item, next, &garbage_applications) { ApplicationDestr((Application_t *)item, 4); n++; } printk(KERN_WARNING"%s: %d garbage items\n", __FUNCTION__, n); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/arcofi.c0000644000000000000500000000674711135651701020013 0ustar rootsrc/* $Id: arcofi.c,v 1.7 2006/03/06 12:52:07 keil Exp $ * * arcofi.c Ansteuerung ARCOFI 2165 * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ #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(channel_t *dch) { isac_chip_t *isac = dch->hw; if (test_and_set_bit(FLG_ARCOFI_TIMER, &dch->Flags)) { del_timer(&isac->arcofitimer); } init_timer(&isac->arcofitimer); isac->arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ)/1000); add_timer(&isac->arcofitimer); } static void send_arcofi(channel_t *dch) { u_char val; isac_chip_t *isac = dch->hw; add_arcofi_timer(dch); isac->mon_txp = 0; isac->mon_txc = isac->arcofi_list->len; memcpy(isac->mon_tx, isac->arcofi_list->msg, isac->mon_txc); switch(isac->arcofi_bc) { case 0: break; case 1: isac->mon_tx[1] |= 0x40; break; default: break; } isac->mocr &= 0x0f; isac->mocr |= 0xa0; 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.privat, ISAC_MOCR, isac->mocr); } int arcofi_fsm(channel_t *dch, int event, void *data) { isac_chip_t *isac = dch->hw; if (dch->debug & L1_DEB_MONITOR) { mISDN_debugprint(&dch->inst, "arcofi state %d event %d", isac->arcofi_state, event); } if (event == ARCOFI_TIMEOUT) { isac->arcofi_state = ARCOFI_NOP; test_and_set_bit(FLG_ARCOFI_ERROR, &dch->Flags); wake_up(&isac->arcofi_wait); return(1); } switch (isac->arcofi_state) { case ARCOFI_NOP: if (event == ARCOFI_START) { isac->arcofi_list = data; isac->arcofi_state = ARCOFI_TRANSMIT; send_arcofi(dch); } break; case ARCOFI_TRANSMIT: if (event == ARCOFI_TX_END) { if (isac->arcofi_list->receive) { add_arcofi_timer(dch); isac->arcofi_state = ARCOFI_RECEIVE; } else { if (isac->arcofi_list->next) { isac->arcofi_list = isac->arcofi_list->next; send_arcofi(dch); } else { if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->Flags)) { del_timer(&isac->arcofitimer); } isac->arcofi_state = ARCOFI_NOP; wake_up(&isac->arcofi_wait); } } } 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->Flags)) { del_timer(&isac->arcofitimer); } isac->arcofi_state = ARCOFI_NOP; wake_up(&isac->arcofi_wait); } } break; default: mISDN_debugprint(&dch->inst, "Arcofi unknown state %x", isac->arcofi_state); return(2); } return(0); } static void arcofi_timer(channel_t *dch) { arcofi_fsm(dch, ARCOFI_TIMEOUT, NULL); } void clear_arcofi(channel_t *dch) { isac_chip_t *isac = dch->hw; if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->Flags)) { del_timer(&isac->arcofitimer); } } void init_arcofi(channel_t *dch) { isac_chip_t *isac = dch->hw; isac->arcofitimer.function = (void *) arcofi_timer; isac->arcofitimer.data = (long) dch; init_timer(&isac->arcofitimer); dch->type |= ISAC_TYPE_ARCOFI; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/arcofi.h0000644000000000000500000000121111110524073017767 0ustar rootsrc/* $Id: arcofi.h,v 1.2 2006/03/06 12:52:07 keil Exp $ * * arcofi.h Ansteuerung ARCOFI 2165 * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ #define ARCOFI_USE 1 /* states */ #define ARCOFI_NOP 0 #define ARCOFI_TRANSMIT 1 #define ARCOFI_RECEIVE 2 /* events */ #define ARCOFI_START 1 #define ARCOFI_TX_END 2 #define ARCOFI_RX_END 3 #define ARCOFI_TIMEOUT 4 struct arcofi_msg { struct arcofi_msg *next; u_char receive; u_char len; u_char msg[10]; }; extern int arcofi_fsm(channel_t *, int, void *); extern void init_arcofi(channel_t *); extern void clear_arcofi(channel_t *); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1.c0000644000000000000500000000571211135651701017401 0ustar rootsrc/* $Id: asn1.c,v 1.1 2003/11/09 09:12:28 keil Exp $ * */ #include "asn1.h" int ParseTag(u_char *p, u_char *end, int *tag) { *tag = *p; return 1; } int ParseLen(u_char *p, u_char *end, int *len) { int l, i; if (*p == 0x80) { // indefinite *len = -1; return 1; } if (!(*p & 0x80)) { // one byte *len = *p; return 1; } *len = 0; l = *p & ~0x80; p++; for (i = 0; i < l; i++) { *len = (*len << 8) + *p; p++; } return l+1; } int ParseASN1(u_char *p, u_char *end, int level) { int tag, len; int ret; int j; u_char *tag_end, *beg; beg = p; CallASN1(ret, p, end, ParseTag(p, end, &tag)); CallASN1(ret, p, end, ParseLen(p, end, &len)); #ifdef ASN1_DEBUG for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " "); print_asn1msg(PRT_DEBUG_DECODE, "TAG 0x%02x LEN %3d\n", tag, len); #endif if (tag & ASN1_TAG_CONSTRUCTED) { if (len == -1) { // indefinite while (*p) { CallASN1(ret, p, end, ParseASN1(p, end, level + 1)); } p++; if (*p) return -1; p++; } else { tag_end = p + len; while (p < tag_end) { CallASN1(ret, p, end, ParseASN1(p, end, level +1)); } } } else { for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " "); while (len--) { print_asn1msg(PRT_DEBUG_DECODE, "%02x ", *p); p++; } print_asn1msg(PRT_DEBUG_DECODE, "\n"); } for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " "); print_asn1msg(PRT_DEBUG_DECODE, "END (%d)\n", p - beg - 2); return p - beg; } #if 0 #if 0 u_char data[] = {"\xA2\x03\x02\x01\xA3"}; #endif #if 0 // ActNotDiv u_char data[] = {"\xA1\x2C\x02\x01\x7E\x02\x01\x09\x30\x24\x0A" "\x01\x02\x0A\x01\x03\x30\x0C\x80\x0A\x30\x31" "\x33\x30\x31\x34\x34\x37\x37\x30\xA1\x0E\x0A" "\x01\x02\x12\x09\x32\x31\x31\x33\x34\x31\x38\x33\x30"}; #endif #if 0 // ActDiv u_char data[] = {"\xA1\x24\x02\x01\xA1\x02\x01\x07\x30\x1C\x0A" "\x01\x02\x0A\x01\x01\x30\x0C\x80\x0A\x30" "\x31\x33\x30\x31\x34\x34\x37\x37\x30\x80" "\x06\x33\x34\x31\x38\x33\x30"}; #endif #if 0 // DeactNotDiv u_char data[] = {"\xA1\x1E\x02\x01\x08\x02\x01\x0A\x30\x16\x0A" "\x01\x02\x0A\x01\x03\xA1\x0E\x0A\x01\x02\x12" "\x09\x32\x31\x31\x33\x34\x31\x38\x33\x30"}; #endif #if 1 // DeactDiv u_char data[] = {"\xA1\x16\x02\x01\xB1\x02\x01\x08\x30\x0E\x0A" "\x01\x02\x0A\x01\x01\x80\x06\x33\x34\x31\x38\x33\x30"}; #endif #if 0 // AOCE, 0 Einheiten u_char data[] = {"\xA1\x15\x02\x02\x00\xDC\x02\x01\x24\x30\x0C" "\x30\x0A\xA1\x05\x30\x03\x02\x01\x00\x82\x01\x00"}; #endif #if 0 // AOCE, 1 Einheit u_char data[] = {"\xA1\x15\x02\x02\x00\xBC\x02\x01\x24\x30\x0C\x30" "\x0A\xA1\x05\x30\x03\x02\x01\x01\x82\x01\x00"}; #endif #if 0 // AOCD currency u_char data[] = {"\xA1\x1A\x02\x02\x1C\x65\x02\x01\x21\x30\x11\xA1\x0C\x81\x02\x44\x4D\xA2\x06\x81\x01\x18\x82\x01\x01\x82\x01\x00"}; #endif u_char *end = data + 47; #include "asn1_component.h" void main() { struct Aoc chan; #ifdef ASN1_DEBUG ParseASN1(data, end, 0); #endif ParseComponent(&chan, data, end); } #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1.h0000644000000000000500000001553311135651701017410 0ustar rootsrc/* $Id: asn1.h,v 1.3 2006/03/06 12:52:07 keil Exp $ * */ #include #include "helper.h" #ifndef __ASN1_H__ #define __ASN1_H__ typedef enum { invoke = 1, returnResult = 2, returnError = 3, reject = 4, } asn1Component; typedef enum { GeneralP = 0, InvokeP = 1, ReturnResultP= 2, ReturnErrorP = 3, } asn1Problem; struct PublicPartyNumber { int publicTypeOfNumber; char numberDigits[30]; }; struct PartyNumber { int type; union { char unknown[30]; struct PublicPartyNumber publicPartyNumber; } p; }; struct Address { struct PartyNumber partyNumber; char partySubaddress[30]; }; struct ServedUserNr { int all; struct PartyNumber partyNumber; }; struct ActDivNotification { int procedure; int basicService; struct ServedUserNr servedUserNr; struct Address address; }; struct DeactDivNotification { int procedure; int basicService; struct ServedUserNr servedUserNr; }; struct ServedUserNumberList { struct PartyNumber partyNumber[10]; }; struct IntResult { struct ServedUserNr servedUserNr; int procedure; int basicService; struct Address address; }; struct IntResultList { struct IntResult intResult[10]; }; struct asn1Invoke { __u16 invokeId; __u16 operationValue; union { struct ActDivNotification actNot; struct DeactDivNotification deactNot; } o; }; struct asn1ReturnResult { __u16 invokeId; union { struct ServedUserNumberList list; struct IntResultList resultList; } o; }; struct asn1ReturnError { __u16 invokeId; __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(KERN_DEBUG fmt, ## args) #else #define print_asn1msg(dummy, fmt, args...) #endif int ParseASN1(u_char *p, u_char *end, int level); int ParseTag(u_char *p, u_char *end, int *tag); int ParseLen(u_char *p, u_char *end, int *len); #define ASN1_TAG_BOOLEAN (0x01) // is that true? #define ASN1_TAG_INTEGER (0x02) #define ASN1_TAG_BIT_STRING (0x03) #define ASN1_TAG_OCTET_STRING (0x04) #define ASN1_TAG_NULL (0x05) #define ASN1_TAG_OBJECT_IDENTIFIER (0x06) #define ASN1_TAG_ENUM (0x0a) #define ASN1_TAG_SEQUENCE (0x30) #define ASN1_TAG_SET (0x31) #define ASN1_TAG_NUMERIC_STRING (0x12) #define ASN1_TAG_PRINTABLE_STRING (0x13) #define ASN1_TAG_IA5_STRING (0x16) #define ASN1_TAG_UTC_TIME (0x17) #define ASN1_TAG_CONSTRUCTED (0x20) #define ASN1_TAG_CONTEXT_SPECIFIC (0x80) #define ASN1_TAG_EXPLICIT (0x100) #define ASN1_TAG_OPT (0x200) #define ASN1_NOT_TAGGED (0x400) #define CallASN1(ret, p, end, todo) do { \ ret = todo; \ if (ret < 0) { \ int_error(); \ return -1; \ } \ p += ret; \ } while (0) #define INIT \ int tag, len; \ int ret; \ u_char *beg; \ \ print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> %s\n", __FUNCTION__); \ beg = p; \ CallASN1(ret, p, end, ParseTag(p, end, &tag)); \ CallASN1(ret, p, end, ParseLen(p, end, &len)); \ if (len >= 0) { \ if (p + len > end) \ return -1; \ end = p + len; \ } #define XSEQUENCE_1(todo, act_tag, the_tag, arg1) do { \ if (p < end) { \ if (((the_tag) &~ ASN1_TAG_OPT) == ASN1_NOT_TAGGED) { \ if (((u_char)act_tag == *p) || ((act_tag) == ASN1_NOT_TAGGED)) { \ CallASN1(ret, p, end, todo(pc, p, end, arg1)); \ } else { \ if (!((the_tag) & ASN1_TAG_OPT)) { \ print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 1 %s:%d\n", __FUNCTION__, __LINE__); \ return -1; \ } \ } \ } else { \ if ((the_tag) & ASN1_TAG_EXPLICIT) { \ if ((u_char)(((the_tag) & 0xff) | (ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED)) == *p) { \ int xtag, xlen; \ CallASN1(ret, p, end, ParseTag(p, end, &xtag)); \ CallASN1(ret, p, end, ParseLen(p, end, &xlen)); \ CallASN1(ret, p, end, todo(pc, p, end, arg1)); \ } else { \ if (!(the_tag) & ASN1_TAG_OPT) { \ print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 2 %s:%d\n", __FUNCTION__, __LINE__); \ return -1; \ } \ } \ } else { \ if ((u_char)(((the_tag) & 0xff) | (ASN1_TAG_CONTEXT_SPECIFIC | (act_tag & ASN1_TAG_CONSTRUCTED))) == *p) { \ CallASN1(ret, p, end, todo(pc, p, end, arg1)); \ } else { \ if (!(the_tag) & ASN1_TAG_OPT) { \ print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 3 %s:%d\n", __FUNCTION__, __LINE__); \ return -1; \ } \ } \ } \ } \ } else { \ if (!(the_tag) & ASN1_TAG_OPT) { \ print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 4 %s:%d\n", __FUNCTION__, __LINE__); \ return -1; \ } \ } \ } while (0) #define XSEQUENCE_OPT_1(todo, act_tag, the_tag, arg1) \ XSEQUENCE_1(todo, act_tag, (the_tag | ASN1_TAG_OPT), arg1) #define XSEQUENCE(todo, act_tag, the_tag) XSEQUENCE_1(todo, act_tag, the_tag, -1) #define XSEQUENCE_OPT(todo, act_tag, the_tag) XSEQUENCE_OPT_1(todo, act_tag, the_tag, -1) #define XCHOICE_1(todo, act_tag, the_tag, arg1) \ if (act_tag == ASN1_NOT_TAGGED) { \ return todo(pc, beg, end, arg1); \ } \ if (the_tag == ASN1_NOT_TAGGED) { \ if (act_tag == tag) { \ return todo(pc, beg, end, arg1); \ } \ } else { \ if ((the_tag | (0x80 | (act_tag & 0x20))) == tag) { \ return todo(pc, beg, end, arg1); \ } \ } #define XCHOICE(todo, act_tag, the_tag) XCHOICE_1(todo, act_tag, the_tag, -1) #define XCHOICE_DEFAULT do {\ print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 5 %s:%d\n", __FUNCTION__, __LINE__); \ return -1; \ } while (0) #define CHECK_P do { \ if (p >= end) \ return -1; \ } while (0) #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_address.c0000644000000000000500000001554311135651701021111 0ustar rootsrc/* $Id: asn1_address.c,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ #include "asn1.h" #include "asn1_generic.h" #include "asn1_address.h" void buildnumber(char *num, int oc3, int oc3a, char *result, int version, int *provider, int *sondernummer, int *intern, int *local, int dir, int who); // ====================================================================== // Address Types EN 300 196-1 D.3 int ParsePresentationRestricted(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { int ret; ret = ParseNull(pc, p, end, -1); if (ret < 0) return ret; strcpy(str, "(presentation restricted)"); return ret; } int ParseNotAvailInterworking(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { int ret; ret = ParseNull(pc, p, end, -1); if (ret < 0) return ret; strcpy(str, "(not available)"); return ret; } int ParsePresentedAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { INIT; XCHOICE_1(ParseAddressScreened, ASN1_TAG_SEQUENCE, 0, str); XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str); XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str); XCHOICE_1(ParseAddressScreened, ASN1_TAG_NULL, 3, str); XCHOICE_DEFAULT; } int ParsePresentedNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { INIT; XCHOICE_1(ParseNumberScreened, ASN1_TAG_SEQUENCE, 0, str); XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str); XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str); XCHOICE_1(ParseNumberScreened, ASN1_TAG_NULL, 3, str); XCHOICE_DEFAULT; } int ParsePresentedNumberUnscreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { struct PartyNumber partyNumber; INIT; XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 0, &partyNumber); // FIXME EXP XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str); XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str); XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 3, &partyNumber); // FIXME EXP XCHOICE_DEFAULT; } int ParseNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { struct PartyNumber partyNumber; char screeningIndicator[30]; INIT; XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &partyNumber); XSEQUENCE_1(ParseScreeningIndicator, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, screeningIndicator); // str += sprintf(str, "%s", partyNumber); return p - beg; } int ParseAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { struct PartyNumber partyNumber; char partySubaddress[30] = { 0, }; char screeningIndicator[30]; INIT; XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &partyNumber); XSEQUENCE_1(ParseScreeningIndicator, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, screeningIndicator); XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, partySubaddress); // str += sprintf(str, "%s", partyNumber); if (strlen(partySubaddress)) str += sprintf(str, ".%s", partySubaddress); return p - beg; } int ParseAddress(struct asn1_parm *pc, u_char *p, u_char *end, struct Address *address) { INIT; address->partySubaddress[0] = 0; XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &address->partyNumber); XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, address->partySubaddress); return p - beg; } int ParsePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PartyNumber *partyNumber) { INIT; partyNumber->type = 0; XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 0, partyNumber->p.unknown); // unknownPartyNumber partyNumber->type = 1; XCHOICE_1(ParsePublicPartyNumber, ASN1_TAG_SEQUENCE, 1, &partyNumber->p.publicPartyNumber); #if 0 XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 3, str); // dataPartyNumber XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 4, str); // telexPartyNumber XCHOICE_1(ParsePrivatePartyNumber, ASN1_TAG_SEQUENCE, 5, str); XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 8, str); // nationalStandardPartyNumber #endif XCHOICE_DEFAULT; } int ParsePublicPartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PublicPartyNumber *publicPartyNumber) { INIT; XSEQUENCE_1(ParsePublicTypeOfNumber, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &publicPartyNumber->publicTypeOfNumber); XSEQUENCE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, ASN1_NOT_TAGGED, publicPartyNumber->numberDigits); return p - beg; } #if 0 int ParsePrivatePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { int privateTypeOfNumber; char numberDigits[20]; INIT; XSEQUENCE_1(ParsePrivateTypeOfNumber, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &privateTypeOfNumber); XSEQUENCE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, ASN1_NOT_TAGGED, numberDigits); switch (privateTypeOfNumber) { case 0: str += sprintf(str, "(unknown)"); break; case 1: str += sprintf(str, "(regional2)"); break; case 2: str += sprintf(str, "(regional1)"); break; case 3: str += sprintf(str, "(ptn)"); break; case 4: str += sprintf(str, "(local)"); break; case 6: str += sprintf(str, "(abbrev)"); break; } str += sprintf(str, numberDigits); return p - beg; } #endif int ParsePublicTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *publicTypeOfNumber) { return ParseEnum(pc, p, end, publicTypeOfNumber); } #if 0 int ParsePrivateTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *privateTypeOfNumber) { return ParseEnum(pc, p, end, privateTypeOfNumber); } #endif int ParsePartySubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { INIT; XCHOICE_1(ParseUserSpecifiedSubaddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, str); XCHOICE_1(ParseNSAPSubaddress, ASN1_TAG_OCTET_STRING, ASN1_NOT_TAGGED, str); XCHOICE_DEFAULT; } int ParseUserSpecifiedSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { int oddCountIndicator; INIT; XSEQUENCE_1(ParseSubaddressInformation, ASN1_TAG_OCTET_STRING, ASN1_NOT_TAGGED, str); XSEQUENCE_OPT_1(ParseBoolean, ASN1_TAG_BOOLEAN, ASN1_NOT_TAGGED, &oddCountIndicator); return p - beg; } int ParseNSAPSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { return ParseOctetString(pc, p, end, str); } int ParseSubaddressInformation(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { return ParseOctetString(pc, p, end, str); } int ParseScreeningIndicator(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { int ret; int screeningIndicator; ret = ParseEnum(pc, p, end, &screeningIndicator); if (ret < 0) return ret; switch (screeningIndicator) { case 0: sprintf(str, "user provided, not screened"); break; case 1: sprintf(str, "user provided, passed"); break; case 2: sprintf(str, "user provided, failed"); break; case 3: sprintf(str, "network provided"); break; default: sprintf(str, "(%d)", screeningIndicator); break; } return ret; } int ParseNumberDigits(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { return ParseNumericString(pc, p, end, str); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_address.h0000644000000000000500000000330011135651701021102 0ustar rootsrc/* $Id: asn1_address.h,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ // ====================================================================== // Address Types EN 300 196-1 D.3 int ParsePresentedAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParsePresentedNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParsePresentedNumberUnscreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseAddress(struct asn1_parm *pc, u_char *p, u_char *end, struct Address *address); int ParsePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PartyNumber *partyNumber); int ParsePublicPartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PublicPartyNumber *publicPartyNumber); int ParsePrivatePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParsePublicTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *publicTypeOfNumber); int ParsePrivateTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *privateTypeOfNumber); int ParsePartySubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseUserSpecifiedSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseNSAPSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseSubaddressInformation(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseScreeningIndicator(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseNumberDigits(struct asn1_parm *pc, u_char *p, u_char *end, char *str); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_aoc.c0000644000000000000500000001711211135651701020220 0ustar rootsrc/* $Id: asn1_aoc.c,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ #include "asn1.h" #include "asn1_generic.h" #if 0 #include "asn1_address.h" #endif #include "asn1_aoc.h" // ====================================================================== // AOC EN 300 182-1 V1.3.3 #if 0 // AOCDCurrency int ParseAOCDCurrency(struct Channel *chanp, u_char *p, u_char *end, int dummy) { INIT; XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail XCHOICE(ParseAOCDCurrencyInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); XCHOICE_DEFAULT; } #endif // AOCDChargingUnit int ParseAOCDChargingUnit(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail XCHOICE(ParseAOCDChargingUnitInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); XCHOICE_DEFAULT; } #if 0 // AOCECurrency int ParseAOCECurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail XCHOICE(ParseAOCECurrencyInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); XCHOICE_DEFAULT; } #endif // AOCEChargingUnit int ParseAOCEChargingUnit(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail XCHOICE(ParseAOCEChargingUnitInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); XCHOICE_DEFAULT; } #if 0 // AOCDCurrencyInfo int ParseAOCDSpecificCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int typeOfChargingInfo; int billingId; INIT; XSEQUENCE(ParseRecordedCurrency, ASN1_TAG_SEQUENCE, 1); XSEQUENCE_1(ParseTypeOfChargingInfo, ASN1_TAG_ENUM, 2, &typeOfChargingInfo); XSEQUENCE_OPT_1(ParseAOCDBillingId, ASN1_TAG_ENUM, 3, &billingId); return p - beg; } int ParseAOCDCurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XCHOICE(ParseAOCDSpecificCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge XCHOICE_DEFAULT; } #endif // AOCDChargingUnitInfo int ParseAOCDSpecificChargingUnits(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int recordedUnits; int typeOfChargingInfo; int billingId; INIT; XSEQUENCE_1(ParseRecordedUnitsList, ASN1_TAG_SEQUENCE, 1, &recordedUnits); XSEQUENCE_1(ParseTypeOfChargingInfo, ASN1_TAG_ENUM, 2, &typeOfChargingInfo); XSEQUENCE_OPT_1(ParseAOCDBillingId, ASN1_TAG_ENUM, 3, &billingId); // p_L3L4(pc, CC_CHARGE | INDICATION, &recordedUnits); return p - beg; } int ParseAOCDChargingUnitInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XCHOICE(ParseAOCDSpecificChargingUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge XCHOICE_DEFAULT; } #if 0 // RecordedCurrency int ParseRecordedCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { char currency[11]; INIT; XSEQUENCE_1(ParseCurrency, ASN1_TAG_IA5_STRING, 1, currency); XSEQUENCE(ParseAmount, ASN1_TAG_SEQUENCE, 2); return p - beg; } #endif // RecordedUnitsList int ParseRecordedUnitsList(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits) { int i; INIT; XSEQUENCE_1(ParseRecordedUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, recordedUnits); for (i = 0; i < 31; i++) XSEQUENCE_OPT_1(ParseRecordedUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, recordedUnits); return p - beg; } // TypeOfChargingInfo int ParseTypeOfChargingInfo(struct asn1_parm *pc, u_char *p, u_char *end, int *typeOfChargingInfo) { return ParseEnum(pc, p, end, typeOfChargingInfo); } // RecordedUnits int ParseRecordedUnitsChoice(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits) { INIT; XCHOICE_1(ParseNumberOfUnits, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, recordedUnits); XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // not available XCHOICE_DEFAULT; } int ParseRecordedUnits(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits) { int typeOfUnit; INIT; XSEQUENCE_1(ParseRecordedUnitsChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, recordedUnits); XSEQUENCE_OPT_1(ParseTypeOfUnit, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &typeOfUnit); return p - beg; } // AOCDBillingId int ParseAOCDBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId) { return ParseEnum(pc, p, end, billingId); } #if 0 // AOCECurrencyInfo int ParseAOCESpecificCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int billingId; INIT; XSEQUENCE(ParseRecordedCurrency, ASN1_TAG_SEQUENCE, 1); XSEQUENCE_OPT_1(ParseAOCEBillingId, ASN1_TAG_ENUM, 2, &billingId); return p - beg; } int ParseAOCECurrencyInfoChoice(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XCHOICE(ParseAOCESpecificCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge XCHOICE_DEFAULT; } int ParseAOCECurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XSEQUENCE(ParseAOCECurrencyInfoChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED); XSEQUENCE_OPT(ParseChargingAssociation, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED); XCHOICE_DEFAULT; } #endif // AOCEChargingUnitInfo int ParseAOCESpecificChargingUnits(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int recordedUnits; int billingId; INIT; XSEQUENCE_1(ParseRecordedUnitsList, ASN1_TAG_SEQUENCE, 1, &recordedUnits); XSEQUENCE_OPT_1(ParseAOCEBillingId, ASN1_TAG_ENUM, 2, &billingId); // p_L3L4(pc, CC_CHARGE | INDICATION, &recordedUnits); return p - beg; } int ParseAOCEChargingUnitInfoChoice(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XCHOICE(ParseAOCESpecificChargingUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge XCHOICE_DEFAULT; } int ParseAOCEChargingUnitInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; XSEQUENCE(ParseAOCEChargingUnitInfoChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED); XSEQUENCE_OPT(ParseChargingAssociation, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED); return p - beg; } // AOCEBillingId int ParseAOCEBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId) { return ParseEnum(pc, p, end, billingId); } #if 0 // Currency int ParseCurrency(struct asn1_parm *pc, u_char *p, u_char *end, char *currency) { return ParseIA5String(chanp, p, end, currency); } // Amount int ParseAmount(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int amount; int multiplier; INIT; XSEQUENCE_1(ParseCurrencyAmount, ASN1_TAG_INTEGER, 1, &amount); XSEQUENCE_1(ParseMultiplier, ASN1_TAG_INTEGER, 2, &multiplier); return p - beg; } // CurrencyAmount int ParseCurrencyAmount(struct asn1_parm *pc, u_char *p, u_char *end, int *currencyAmount) { return ParseInteger(chanp, p, end, currencyAmount); } // Multiplier int ParseMultiplier(struct asn1_parm *pc, u_char *p, u_char *end, int *multiplier) { return ParseEnum(chanp, p, end, multiplier); } #endif // TypeOfUnit int ParseTypeOfUnit(struct asn1_parm *pc, u_char *p, u_char *end, int *typeOfUnit) { return ParseInteger(pc, p, end, typeOfUnit); } // NumberOfUnits int ParseNumberOfUnits(struct asn1_parm *pc, u_char *p, u_char *end, int *numberOfUnits) { return ParseInteger(pc, p, end, numberOfUnits); } // Charging Association int ParseChargingAssociation(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { // char partyNumber[30]; INIT; // XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 0, partyNumber); XCHOICE(ParseChargeIdentifier, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED); XCHOICE_DEFAULT; } // ChargeIdentifier int ParseChargeIdentifier(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int chargeIdentifier; return ParseInteger(pc, p, end, &chargeIdentifier); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_aoc.h0000644000000000000500000000376611135651701020237 0ustar rootsrc/* $Id: asn1_aoc.h,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ // ====================================================================== // AOC EN 300 182-1 V1.3.3 int ParseAOCDCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy); int ParseAOCDChargingUnit(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); int ParseAOCECurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy); int ParseAOCEChargingUnit(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); int ParseAOCDCurrencyInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); int ParseAOCDChargingUnitInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); int ParseRecordedCurrency(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); int ParseRecordedUnitsList(struct asn1_parm *pc,u_char *p, u_char *end, int *recordedUnits); int ParseTypeOfChargingInfo(struct asn1_parm *pc,u_char *p, u_char *end, int *typeOfChargingInfo); int ParseRecordedUnits(struct asn1_parm *pc,u_char *p, u_char *end, int *recordedUnits); int ParseAOCDBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId); int ParseAOCECurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy); int ParseAOCEChargingUnitInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); int ParseAOCEBillingId(struct asn1_parm *pc,u_char *p, u_char *end, int *billingId); int ParseCurrency(struct asn1_parm *pc,u_char *p, u_char *end, char *currency); int ParseAmount(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); int ParseCurrencyAmount(struct asn1_parm *pc,u_char *p, u_char *end, int *currencyAmount); int ParseMultiplier(struct asn1_parm *pc,u_char *p, u_char *end, int *multiplier); int ParseTypeOfUnit(struct asn1_parm *pc,u_char *p, u_char *end, int *typeOfUnit); int ParseNumberOfUnits(struct asn1_parm *pc,u_char *p, u_char *end, int *numberOfUnits); int ParseChargingAssociation(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); int ParseChargeIdentifier(struct asn1_parm *pc,u_char *p, u_char *end, int dummy); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_basic_service.c0000644000000000000500000000062711135651701022262 0ustar rootsrc/* $Id: asn1_basic_service.c,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ #include "asn1.h" #include "asn1_generic.h" #include "asn1_basic_service.h" // ====================================================================== // Basic Service Elements EN 300 196-1 D.6 int ParseBasicService(struct asn1_parm *pc, u_char *p, u_char *end, int *basicService) { return ParseEnum(pc, p, end, basicService); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_basic_service.h0000644000000000000500000000043211135651701022261 0ustar rootsrc/* $Id: asn1_basic_service.h,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ // ====================================================================== // Basic Service Elements EN 300 196-1 D.6 int ParseBasicService(struct asn1_parm *pc, u_char *p, u_char *end, int *basicService); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_comp.c0000644000000000000500000001613711135651701020422 0ustar rootsrc/* $Id: asn1_comp.c,v 1.3 2006/11/14 12:17:02 crich Exp $ * */ #include "asn1.h" #include "asn1_comp.h" #include "asn1_generic.h" #include "asn1_aoc.h" #include "asn1_diversion.h" // ====================================================================== // Component EN 300 196-1 D.1 int ParseInvokeId(struct asn1_parm *pc, u_char *p, u_char *end, int *invokeId) { return ParseInteger(pc, p, end, invokeId); } int ParseErrorValue(struct asn1_parm *pc, u_char *p, u_char *end, int *errorValue) { return ParseInteger(pc, p, end, errorValue); } int ParseOperationValue(struct asn1_parm *pc, u_char *p, u_char *end, int *operationValue) { return ParseInteger(pc, p, end, operationValue); } int ParseInvokeComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int invokeId, operationValue; INIT; pc->comp = invoke; XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId); // XSEQUENCE_OPT(ParseLinkedId, ASN1_TAG_INTEGER, 0); XSEQUENCE_1(ParseOperationValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &operationValue); pc->u.inv.invokeId = invokeId; pc->u.inv.operationValue = operationValue; switch (operationValue) { #if 0 case 7: XSEQUENCE(ParseARGActivationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; case 8: XSEQUENCE(ParseARGDeactivationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; #endif case 9: XSEQUENCE_1(ParseARGActivationStatusNotificationDiv, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.actNot); break; case 10: XSEQUENCE_1(ParseARGDeactivationStatusNotificationDiv, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.deactNot); break; #if 0 case 11: XSEQUENCE(ParseARGInterrogationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; case 12: XSEQUENCE(ParseARGDiversionInformation, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; case 17: XSEQUENCE(ParseARGInterrogateServedUserNumbers, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; // case 30: XSEQUENCE(ParseChargingRequest, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; // case 31: XSEQUENCE(ParseAOCSCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; // case 32: XSEQUENCE(ParseAOCSSpecialArr, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; case 33: XSEQUENCE(ParseAOCDCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; case 34: XSEQUENCE(ParseAOCDChargingUnit, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; case 35: XSEQUENCE(ParseAOCECurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; case 36: XSEQUENCE(ParseAOCEChargingUnit, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break; #endif default: return -1; } return p - beg; } int ParseReturnResultComponentSequence(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int operationValue; INIT; XSEQUENCE_1(ParseOperationValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &operationValue); switch (operationValue) { case 11: XSEQUENCE(ParseRESInterrogationDiversion, ASN1_TAG_SET, ASN1_NOT_TAGGED); break; case 17: XSEQUENCE(ParseRESInterrogateServedUserNumbers, ASN1_TAG_SET, ASN1_NOT_TAGGED); break; default: return -1; } return p - beg; } int ParseReturnResultComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int invokeId; INIT; pc->comp = returnResult; XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId); XSEQUENCE_OPT(ParseReturnResultComponentSequence, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); pc->u.retResult.invokeId = invokeId; return p - beg; } int ParseReturnErrorComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int invokeId; int errorValue; char error[80]; INIT; pc->comp = returnError; XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId); XSEQUENCE_1(ParseErrorValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &errorValue); pc->u.retError.invokeId = invokeId; pc->u.retError.errorValue = errorValue; switch (errorValue) { case 0: sprintf(error, "not subscribed"); break; case 3: sprintf(error, "not available"); break; case 4: sprintf(error, "not implemented"); break; case 6: sprintf(error, "invalid served user nr"); break; case 7: sprintf(error, "invalid call state"); break; case 8: sprintf(error, "basic service not provided"); break; case 9: sprintf(error, "not incoming call"); break; case 10: sprintf(error, "supplementary service interaction not allowed"); break; case 11: sprintf(error, "resource unavailable"); break; case 12: sprintf(error, "invalid diverted-to nr"); break; case 14: sprintf(error, "special service nr"); break; case 15: sprintf(error, "diversion to served user nr"); break; case 23: sprintf(error, "incoming call accepted"); break; case 24: sprintf(error, "number of diversions exceeded"); break; case 46: sprintf(error, "not activated"); break; case 48: sprintf(error, "request already accepted"); break; default: sprintf(error, "(%d)", errorValue); break; } print_asn1msg(PRT_DEBUG_DECODE, "ReturnError: %s\n", error); 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) { INIT; 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(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; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_comp.h0000644000000000000500000000107711135651701020424 0ustar rootsrc/* $Id: asn1_comp.h,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ #include "asn1.h" int ParseInvokeId(struct asn1_parm *parm, u_char *p, u_char *end, int *invokeId); int ParseOperationValue(struct asn1_parm *parm, u_char *p, u_char *end, int *operationValue); int ParseInvokeComponent(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); int ParseReturnResultComponent(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); int ParseComponent(struct asn1_parm *parm, u_char *p, u_char *end); int XParseComponent(struct asn1_parm *parm, u_char *p, u_char *end); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_diversion.c0000644000000000000500000001645211135651701021466 0ustar rootsrc/* $Id: asn1_diversion.c,v 1.1 2003/11/09 09:12:28 keil Exp $ * */ #include "asn1.h" #include "asn1_generic.h" #include "asn1_address.h" #include "asn1_basic_service.h" #include "asn1_diversion.h" // ====================================================================== // Diversion Supplementary Services ETS 300 207-1 Table 3 #if 0 int ParseARGActivationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int procedure, basicService; struct ServedUserNr servedUserNr; struct Address address; INIT; XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure); XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService); XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &address); XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr); return p - beg; } int ParseARGDeactivationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int procedure, basicService; struct ServedUserNr servedUserNr; INIT; XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure); XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService); XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr); print_asn1msg(PRT_SHOWNUMBERS, "Deactivation Diversion %d (%d), \n", procedure, basicService); return p - beg; } #endif int ParseARGActivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct ActDivNotification *actNot) { INIT; XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &actNot->procedure); XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &actNot->basicService); XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &actNot->address); XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &actNot->servedUserNr); return p - beg; } int ParseARGDeactivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct DeactDivNotification *deactNot) { INIT; XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &deactNot->procedure); XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &deactNot->basicService); XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &deactNot->servedUserNr); return p - beg; } #if 0 int ParseARGInterrogationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int procedure, basicService; struct ServedUserNr servedUserNr; INIT; XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure); XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService); XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr); print_asn1msg(PRT_SHOWNUMBERS, "Interrogation Diversion %d (%d), \n", procedure, basicService); return p - beg; } #endif int ParseRESInterrogationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { print_asn1msg(PRT_SHOWNUMBERS, "Interrogation Diversion Result\n"); return ParseIntResultList(pc, p, end, &pc->u.retResult.o.resultList); } #if 0 int ParseARGInterrogateServedUserNumbers(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { print_asn1msg(PRT_SHOWNUMBERS, "Interrogate Served User Numbers\n"); return 0; } #endif int ParseRESInterrogateServedUserNumbers(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { int ret; ret = ParseServedUserNumberList(pc, p, end, &pc->u.retResult.o.list); if (ret < 0) return ret; print_asn1msg(PRT_SHOWNUMBERS, "Interrogate Served User Numbers:\n"); return ret; } int ParseARGDiversionInformation(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { char diversionReason[20]; int basicService; char servedUserSubaddress[30]; char callingAddress[80]; char originalCalledNr[80]; char lastDivertingNr[80]; char lastDivertingReason[20]; INIT; servedUserSubaddress[0] = 0; callingAddress[0] = 0; originalCalledNr[0] = 0; lastDivertingNr[0] = 0; lastDivertingReason[0] = 0; XSEQUENCE_1(ParseDiversionReason, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, diversionReason); XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService); XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, servedUserSubaddress); XSEQUENCE_OPT_1(ParsePresentedAddressScreened, ASN1_NOT_TAGGED, 0 | ASN1_TAG_EXPLICIT, callingAddress); XSEQUENCE_OPT_1(ParsePresentedNumberUnscreened, ASN1_NOT_TAGGED, 1 | ASN1_TAG_EXPLICIT, originalCalledNr); XSEQUENCE_OPT_1(ParsePresentedNumberUnscreened, ASN1_NOT_TAGGED, 2 | ASN1_TAG_EXPLICIT, lastDivertingNr); XSEQUENCE_OPT_1(ParseDiversionReason, ASN1_TAG_ENUM, 3 | ASN1_TAG_EXPLICIT, lastDivertingReason); // XSEQUENCE_OPT_1(ParseQ931InformationElement, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, userInfo); print_asn1msg(PRT_SHOWNUMBERS, "Diversion Information %s(%d) %s\n" " callingAddress %s originalCalled Nr %s\n" " lastDivertingNr %s lastDiverting Reason %s\n", diversionReason, basicService, servedUserSubaddress, callingAddress, originalCalledNr, lastDivertingNr, lastDivertingReason); return p - beg; } int ParseIntResultList(struct asn1_parm *pc, u_char *p, u_char *end, struct IntResultList *intResultList) { int i; INIT; for (i = 0; i < 10; i++) { intResultList->intResult[i].basicService = -1; XSEQUENCE_OPT_1(ParseIntResult, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &intResultList->intResult[i] ); } return p - beg; } int ParseIntResult(struct asn1_parm *pc, u_char *p, u_char *end, struct IntResult *intResult) { INIT; XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &intResult->servedUserNr); XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &intResult->basicService); XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &intResult->procedure); XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &intResult->address); return p - beg; } int ParseServedUserNrAll(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNr *servedUserNr) { int ret; ret = ParseNull(pc, p, end, 0); if (ret < 0) return ret; servedUserNr->all = 1; return ret; } int ParseServedUserNr(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNr *servedUserNr) { INIT; servedUserNr->all = 0; XCHOICE_1(ParseServedUserNrAll, ASN1_TAG_NULL, ASN1_NOT_TAGGED, servedUserNr); XCHOICE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr->partyNumber); XCHOICE_DEFAULT; } int ParseProcedure(struct asn1_parm *pc, u_char *p, u_char *end, int *procedure) { return ParseEnum(pc, p, end, procedure); } int ParseServedUserNumberList(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNumberList *list) { int i; INIT; for (i = 0; i < 10; i++) { list->partyNumber[i].type = -1; XSEQUENCE_OPT_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &list->partyNumber[i]); } return p - beg; } int ParseDiversionReason(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { int ret; int diversionReason; ret = ParseEnum(pc, p, end, &diversionReason); if (ret < 0) return ret; switch (diversionReason) { case 0: sprintf(str, "unknown"); break; case 1: sprintf(str, "CFU"); break; case 2: sprintf(str, "CFB"); break; case 3: sprintf(str, "CFNR"); break; case 4: sprintf(str, "CD (Alerting)"); break; case 5: sprintf(str, "CD (Immediate)"); break; default: sprintf(str, "(%d)", diversionReason); break; } return ret; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_diversion.h0000644000000000000500000000310711135651701021464 0ustar rootsrc/* $Id: asn1_diversion.h,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ #if 0 int ParseARGActivationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); int ParseARGDeactivationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); #endif int ParseARGActivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct ActDivNotification *actNot); int ParseARGDeactivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct DeactDivNotification *deactNot); int ParseARGInterrogationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); int ParseRESInterrogationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); int ParseARGInterrogateServedUserNumbers(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); int ParseRESInterrogateServedUserNumbers(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); int ParseARGDiversionInformation(struct asn1_parm *parm, u_char *p, u_char *end, int dummy); int ParseIntResult(struct asn1_parm *parm, u_char *p, u_char *end, struct IntResult *intResult); int ParseIntResultList(struct asn1_parm *parm, u_char *p, u_char *end, struct IntResultList *intResultList); int ParseServedUserNr(struct asn1_parm *parm, u_char *p, u_char *end, struct ServedUserNr *servedUserNr); int ParseProcedure(struct asn1_parm *pc, u_char *p, u_char *end, int *procedure); int ParseServedUserNumberList(struct asn1_parm *parm, u_char *p, u_char *end, struct ServedUserNumberList *list); int ParseDiversionReason(struct asn1_parm *parm, u_char *p, u_char *end, char *str); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_enc.c0000644000000000000500000000767511135651701020240 0ustar rootsrc/* $Id: asn1_enc.c,v 1.4 2006/03/06 12:52:07 keil Exp $ * */ #include "m_capi.h" #include "helper.h" #include "asn1_enc.h" int encodeNull(__u8 *dest) { dest[0] = 0x05; // null dest[1] = 0; // length 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; dest[0] = 0x02; // integer dest[1] = 0; // length p = &dest[2]; do { *p++ = i; i >>= 8; } while (i); dest[1] = p - &dest[2]; return p - dest; } int encodeEnum(__u8 *dest, __u32 i) { __u8 *p; dest[0] = 0x0a; // integer dest[1] = 0; // length p = &dest[2]; do { *p++ = i; i >>= 8; } while (i); dest[1] = p - &dest[2]; return p - dest; } int encodeNumberDigits(__u8 *dest, __u8 *nd, __u8 len) { __u8 *p; int i; dest[0] = 0x12; // numeric string dest[1] = 0x0; // length p = &dest[2]; for (i = 0; i < len; i++) *p++ = *nd++; dest[1] = p - &dest[2]; return p - dest; } int encodePublicPartyNumber(__u8 *dest, __u8 *facilityPartyNumber) { __u8 *p; dest[0] = 0x20; // sequence dest[1] = 0; // length p = &dest[2]; p += encodeEnum(p, (facilityPartyNumber[2] & 0x70) >> 4); p += encodeNumberDigits(p, &facilityPartyNumber[4], facilityPartyNumber[0] - 3); dest[1] = p - &dest[2]; return p - dest; } int encodePartyNumber(__u8 *dest, __u8 *facilityPartyNumber) { __u8 *p = dest; p = dest; switch (facilityPartyNumber[1]) { case 0: // unknown p += encodeNumberDigits(p, &facilityPartyNumber[4], facilityPartyNumber[0] - 3); dest[0] &= 0x20; dest[0] |= 0x81; break; case 1: // publicPartyNumber p += encodePublicPartyNumber(p, facilityPartyNumber); dest[0] &= 0x20; dest[0] |= 0x81; break; default: int_error(); return -1; } return p - dest; } int encodeServedUserNumber(__u8 *dest, __u8 *servedUserNumber) { if (servedUserNumber[0]) return encodePartyNumber(dest, servedUserNumber); else return encodeNull(dest); } int encodeAddress(__u8 *dest, __u8 *facilityPartyNumber, __u8 *calledPartySubaddress) { __u8 *p = dest; dest[0] = 0x30; // invoke id tag, integer dest[1] = 0; // length p = &dest[2]; p += encodePartyNumber(p, facilityPartyNumber); #if 0 // FIXME if (calledPartySubaddress[0]) p += encodePartySubaddress(p, calledPartySubaddress); #endif dest[1] = p - &dest[2]; return p - dest; } int encodeActivationDiversion(__u8 *dest, struct FacReqCFActivate *CFActivate) { __u8 *p; dest[0] = 0x30; // sequence dest[1] = 0; // length p = &dest[2]; p += encodeEnum(p, CFActivate->Procedure); p += encodeEnum(p, CFActivate->BasicService); p += encodeAddress(p, CFActivate->ForwardedToNumber, CFActivate->ForwardedToSubaddress); p += encodeServedUserNumber(p, CFActivate->ServedUserNumber); dest[1] = p - &dest[2]; return p - dest; } int encodeDeactivationDiversion(__u8 *dest, struct FacReqCFDeactivate *CFDeactivate) { __u8 *p; dest[0] = 0x30; // sequence dest[1] = 0; // length p = &dest[2]; p += encodeEnum(p, CFDeactivate->Procedure); p += encodeEnum(p, CFDeactivate->BasicService); p += encodeServedUserNumber(p, CFDeactivate->ServedUserNumber); dest[1] = p - &dest[2]; return p - dest; } int encodeInterrogationDiversion(__u8 *dest, struct FacReqCFInterrogateParameters *params) { __u8 *p; dest[0] = 0x30; // sequence dest[1] = 0; // length p = &dest[2]; p += encodeEnum(p, params->Procedure); #if 0 if (basicService == 0) p += encodeNull(p); else #endif p += encodeEnum(p, params->BasicService); p += encodeServedUserNumber(p, params->ServedUserNumber); dest[1] = p - &dest[2]; 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; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_enc.h0000644000000000000500000000157011135651701020231 0ustar rootsrc/* $Id: asn1_enc.h,v 1.1 2006/03/06 12:52:07 keil Exp $ * */ #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); int encodePublicPartyNumber(__u8 *dest, __u8 *facilityPartyNumber); int encodePartyNumber(__u8 *dest, __u8 *facilityPartyNumber); int encodeServedUserNumber(__u8 *dest, __u8 *servedUserNumber); int encodeAddress(__u8 *dest, __u8 *facilityPartyNumber, __u8 *calledPartySubaddress); 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); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_generic.c0000644000000000000500000000366211135651701021077 0ustar rootsrc/* $Id: asn1_generic.c,v 1.1 2003/11/09 09:12:28 keil Exp $ * */ #include "asn1.h" #include "asn1_generic.h" // ====================================================================== // general ASN.1 int ParseBoolean(struct asn1_parm *pc, u_char *p, u_char *end, int *i) { INIT; *i = 0; while (len--) { CHECK_P; *i = (*i >> 8) + *p; p++; } print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> BOOL = %d %#x\n", *i, *i); return p - beg; } int ParseNull(struct asn1_parm *pc, u_char *p, u_char *end, int dummy) { INIT; return p - beg; } int ParseInteger(struct asn1_parm *pc, u_char *p, u_char *end, int *i) { INIT; *i = 0; while (len--) { CHECK_P; *i = (*i << 8) + *p; p++; } print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> INT = %d %#x\n", *i, *i); return p - beg; } int ParseEnum(struct asn1_parm *pc, u_char *p, u_char *end, int *i) { INIT; *i = 0; while (len--) { CHECK_P; *i = (*i << 8) + *p; p++; } print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> ENUM = %d %#x\n", *i, *i); return p - beg; } #if 0 int ParseIA5String(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { INIT; print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> IA5 = "); while (len--) { CHECK_P; print_asn1msg(PRT_DEBUG_DECODE, "%c", *p); *str++ = *p; p++; } print_asn1msg(PRT_DEBUG_DECODE, "\n"); *str = 0; return p - beg; } #endif int ParseNumericString(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { INIT; print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> NumStr = "); while (len--) { CHECK_P; print_asn1msg(PRT_DEBUG_DECODE, "%c", *p); *str++ = *p; p++; } print_asn1msg(PRT_DEBUG_DECODE, "\n"); *str = 0; return p - beg; } int ParseOctetString(struct asn1_parm *pc, u_char *p, u_char *end, char *str) { INIT; print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> Octets = "); while (len--) { CHECK_P; print_asn1msg(PRT_DEBUG_DECODE, " %02x", *p); *str++ = *p; p++; } print_asn1msg(PRT_DEBUG_DECODE, "\n"); *str = 0; return p - beg; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/asn1_generic.h0000644000000000000500000000127711110524073021076 0ustar rootsrc/* $Id: asn1_generic.h,v 1.0 2001/11/02 23:42:26 kkeil Exp $ * */ #include "asn1.h" // ====================================================================== // general ASN.1 int ParseBoolean(struct asn1_parm *pc, u_char *p, u_char *end, int *i); int ParseNull(struct asn1_parm *pc, u_char *p, u_char *end, int dummy); int ParseInteger(struct asn1_parm *pc, u_char *p, u_char *end, int *i); int ParseEnum(struct asn1_parm *pc, u_char *p, u_char *end, int *i); int ParseIA5String(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseNumericString(struct asn1_parm *pc, u_char *p, u_char *end, char *str); int ParseOctetString(struct asn1_parm *pc, u_char *p, u_char *end, char *str); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/avm_fritz.c0000644000000000000500000011413411135651701020537 0ustar rootsrc/* $Id: avm_fritz.c,v 1.43 2007/02/13 10:43:45 crich Exp $ * * fritz_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards * Thanks to AVM, Berlin for informations * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #ifdef NEW_ISAPNP #include #else #include #endif #include #include #include "core.h" #include "channel.h" #include "isac.h" #include "layer1.h" #include "debug.h" static const char *avm_fritz_rev = "$Revision: 1.43 $"; enum { AVM_FRITZ_PCI, AVM_FRITZ_PNP, AVM_FRITZ_PCIV2, }; #ifndef PCI_VENDOR_ID_AVM #define PCI_VENDOR_ID_AVM 0x1244 #endif #ifndef PCI_DEVICE_ID_AVM_FRITZ #define PCI_DEVICE_ID_AVM_FRITZ 0xa00 #endif #ifndef PCI_DEVICE_ID_AVM_A1_V2 #define PCI_DEVICE_ID_AVM_A1_V2 0xe00 #endif #define HDLC_FIFO 0x0 #define HDLC_STATUS 0x4 #define CHIP_WINDOW 0x10 #define CHIP_INDEX 0x4 #define AVM_HDLC_1 0x00 #define AVM_HDLC_2 0x01 #define AVM_ISAC_FIFO 0x02 #define AVM_ISAC_REG_LOW 0x04 #define AVM_ISAC_REG_HIGH 0x06 #define AVM_STATUS0_IRQ_ISAC 0x01 #define AVM_STATUS0_IRQ_HDLC 0x02 #define AVM_STATUS0_IRQ_TIMER 0x04 #define AVM_STATUS0_IRQ_MASK 0x07 #define AVM_STATUS0_RESET 0x01 #define AVM_STATUS0_DIS_TIMER 0x02 #define AVM_STATUS0_RES_TIMER 0x04 #define AVM_STATUS0_ENA_IRQ 0x08 #define AVM_STATUS0_TESTBIT 0x10 #define AVM_STATUS1_INT_SEL 0x0f #define AVM_STATUS1_ENA_IOM 0x80 #define HDLC_MODE_ITF_FLG 0x01 #define HDLC_MODE_TRANS 0x02 #define HDLC_MODE_CCR_7 0x04 #define HDLC_MODE_CCR_16 0x08 #define HDLC_MODE_TESTLOOP 0x80 #define HDLC_INT_XPR 0x80 #define HDLC_INT_XDU 0x40 #define HDLC_INT_RPR 0x20 #define HDLC_INT_MASK 0xE0 #define HDLC_STAT_RME 0x01 #define HDLC_STAT_RDO 0x10 #define HDLC_STAT_CRCVFRRAB 0x0E #define HDLC_STAT_CRCVFR 0x06 #define HDLC_STAT_RML_MASK 0x3f00 #define HDLC_CMD_XRS 0x80 #define HDLC_CMD_XME 0x01 #define HDLC_CMD_RRS 0x20 #define HDLC_CMD_XML_MASK 0x3f00 /* Fritz PCI v2.0 */ #define AVM_HDLC_FIFO_1 0x10 #define AVM_HDLC_FIFO_2 0x18 #define AVM_HDLC_STATUS_1 0x14 #define AVM_HDLC_STATUS_2 0x1c #define AVM_ISACSX_INDEX 0x04 #define AVM_ISACSX_DATA 0x08 /* data struct */ struct hdlc_stat_reg { #ifdef __BIG_ENDIAN u_char fill; u_char mode; u_char xml; u_char cmd; #else u_char cmd; u_char xml; u_char mode; u_char fill; #endif } __attribute__((packed)); typedef struct hdlc_hw { union { u_int ctrl; struct hdlc_stat_reg sr; } ctrl; u_int stat; } hdlc_hw_t; typedef struct _fritzpnppci { struct list_head list; 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; spinlock_t lock; isac_chip_t isac; hdlc_hw_t hdlc[2]; channel_t dch; channel_t bch[2]; u_char ctrlreg; } fritzpnppci; /* Interface functions */ static u_char ReadISAC(void *fc, u_char offset) { register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; register long addr = ((fritzpnppci *)fc)->addr; register u_char val; outb(idx, addr + CHIP_INDEX); val = inb(addr + CHIP_WINDOW + (offset & 0xf)); return (val); } static void WriteISAC(void *fc, u_char offset, u_char value) { register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; register long addr = ((fritzpnppci *)fc)->addr; outb(idx, addr + CHIP_INDEX); outb(value, addr + CHIP_WINDOW + (offset & 0xf)); } static void ReadISACfifo(void *fc, u_char * data, int size) { register long addr = ((fritzpnppci *)fc)->addr; outb(AVM_ISAC_FIFO, addr + CHIP_INDEX); insb(addr + CHIP_WINDOW, data, size); } static void WriteISACfifo(void *fc, u_char * data, int size) { register long addr = ((fritzpnppci *)fc)->addr; outb(AVM_ISAC_FIFO, addr + CHIP_INDEX); outsb(addr + CHIP_WINDOW, data, size); } static unsigned char fcpci2_read_isac(void *fc, unsigned char offset) { register long addr = ((fritzpnppci *)fc)->addr; unsigned char val; outl(offset, addr + AVM_ISACSX_INDEX); val = inl(addr + AVM_ISACSX_DATA); return val; } static void fcpci2_write_isac(void *fc, unsigned char offset, unsigned char value) { register long addr = ((fritzpnppci *)fc)->addr; outl(offset, addr + AVM_ISACSX_INDEX); outl(value, addr + AVM_ISACSX_DATA); } static void fcpci2_read_isac_fifo(void *fc, unsigned char * data, int size) { register long addr = ((fritzpnppci *)fc)->addr; int i; outl(0, addr + AVM_ISACSX_INDEX); for (i = 0; i < size; i++) data[i] = inl(addr + AVM_ISACSX_DATA); } static void fcpci2_write_isac_fifo(void *fc, unsigned char * data, int size) { register long addr = ((fritzpnppci *)fc)->addr; int i; outl(0, addr + AVM_ISACSX_INDEX); for (i = 0; i < size; i++) outl(data[i], addr + AVM_ISACSX_DATA); } static inline channel_t *Sel_BCS(fritzpnppci *fc, int channel) { if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && (fc->bch[0].channel == channel)) return(&fc->bch[0]); else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && (fc->bch[1].channel == channel)) return(&fc->bch[1]); else return(NULL); } static inline void __write_ctrl_pnp(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel, int which) { register u_char idx = channel ? AVM_HDLC_2 : AVM_HDLC_1; outb(idx, fc->addr + CHIP_INDEX); if (which & 4) outb(hdlc->ctrl.sr.mode, fc->addr + CHIP_WINDOW + HDLC_STATUS + 2); if (which & 2) outb(hdlc->ctrl.sr.xml, fc->addr + CHIP_WINDOW + HDLC_STATUS + 1); if (which & 1) outb(hdlc->ctrl.sr.cmd, fc->addr + CHIP_WINDOW + HDLC_STATUS); } static inline void __write_ctrl_pci(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel) { register u_int idx = channel ? AVM_HDLC_2 : AVM_HDLC_1; outl(idx, fc->addr + CHIP_INDEX); outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); } static inline void __write_ctrl_pciv2(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel) { outl(hdlc->ctrl.ctrl, fc->addr + (channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1)); } void 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) mISDN_debugprint(&bch->inst, "hdlc %c wr%x ctrl %x", 'A' + bch->channel, which, hdlc->ctrl.ctrl); switch(fc->type) { case AVM_FRITZ_PCIV2: __write_ctrl_pciv2(fc, hdlc, bch->channel); break; case AVM_FRITZ_PCI: __write_ctrl_pci(fc, hdlc, bch->channel); break; case AVM_FRITZ_PNP: __write_ctrl_pnp(fc, hdlc, bch->channel, which); break; } } static inline u_int __read_status_pnp(u_long addr, u_int channel) { register u_int stat; outb(channel ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); stat = inb(addr + CHIP_WINDOW + HDLC_STATUS); if (stat & HDLC_INT_RPR) stat |= (inb(addr + CHIP_WINDOW + HDLC_STATUS + 1)) << 8; return (stat); } static inline u_int __read_status_pci(u_long addr, u_int channel) { outl(channel ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); return inl(addr + CHIP_WINDOW + HDLC_STATUS); } static inline u_int __read_status_pciv2(u_long addr, u_int channel) { return inl(addr + (channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1)); } static u_int read_status(fritzpnppci *fc, int channel) { switch(fc->type) { case AVM_FRITZ_PCIV2: return(__read_status_pciv2(fc->addr, channel)); case AVM_FRITZ_PCI: return(__read_status_pci(fc->addr, channel)); case AVM_FRITZ_PNP: return(__read_status_pnp(fc->addr, channel)); } /* dummy */ 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(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->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->state = -1; bch->channel = bc; case (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->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->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; test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); break; case (ISDN_PID_L1_B_64HDLC): 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; test_and_set_bit(FLG_HDLC, &bch->Flags); break; default: mISDN_debugprint(&bch->inst, "prot not known %x", protocol); return(-ENOPROTOOPT); } return(0); } static void 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.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_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 = skb_put(bch->rx_skb, count); ptr = (u_int *)p; if (fc->type == AVM_FRITZ_PCIV2) { while (cnt < count) { #ifdef __powerpc__ #ifdef CONFIG_APUS *ptr++ = in_le32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE)); #else *ptr++ = in_be32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE)); #endif /* CONFIG_APUS */ #else *ptr++ = inl(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1)); #endif /* __powerpc__ */ cnt += 4; } } else if (fc->type == AVM_FRITZ_PCI) { outl(idx, fc->addr + CHIP_INDEX); while (cnt < count) { #ifdef __powerpc__ #ifdef CONFIG_APUS *ptr++ = in_le32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE)); #else *ptr++ = in_be32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE)); #endif /* CONFIG_APUS */ #else *ptr++ = inl(fc->addr + CHIP_WINDOW); #endif /* __powerpc__ */ cnt += 4; } } else { outb(idx, fc->addr + CHIP_INDEX); while (cnt < count) { *p++ = inb(fc->addr + CHIP_WINDOW); cnt++; } } if (fc->dch.debug & L1_DEB_HSCX_FIFO) { 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->log); } } #define HDLC_FIFO_SIZE 32 static void hdlc_fill_fifo(channel_t *bch) { fritzpnppci *fc = bch->inst.privat; hdlc_hw_t *hdlc = bch->hw; int count, cnt =0; u_char *p; u_int *ptr; if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) mISDN_debugprint(&bch->inst, "%s", __FUNCTION__); if (!bch->tx_skb) return; count = bch->tx_skb->len - bch->tx_idx; if (count <= 0) return; 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 (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)) mISDN_debugprint(&bch->inst, "%s: %d/%d", __FUNCTION__, count, bch->tx_idx); ptr = (u_int *) p; bch->tx_idx += count; hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count); if (fc->type == AVM_FRITZ_PCIV2) { __write_ctrl_pciv2(fc, hdlc, bch->channel); while (cntaddr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE), *ptr++); #else out_be32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE), *ptr++); #endif /* CONFIG_APUS */ #else outl(*ptr++, fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1)); #endif /* __powerpc__ */ cnt += 4; } } else if (fc->type == AVM_FRITZ_PCI) { __write_ctrl_pci(fc, hdlc, bch->channel); while (cntaddr + CHIP_WINDOW +_IO_BASE), *ptr++); #else out_be32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE), *ptr++); #endif /* CONFIG_APUS */ #else outl(*ptr++, fc->addr + CHIP_WINDOW); #endif /* __powerpc__ */ cnt += 4; } } else { __write_ctrl_pnp(fc, hdlc, bch->channel, 3); while (cntaddr + CHIP_WINDOW); cnt++; } } if (bch->debug & L1_DEB_HSCX_FIFO) { 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->log); } } static void HDLC_irq_xpr(channel_t *bch) { 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_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 { printk(KERN_WARNING "hdlc tx irq TX_NEXT without skb\n"); test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } } else { bch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } } } static void HDLC_irq(channel_t *bch, u_int stat) { int len; struct sk_buff *skb; hdlc_hw_t *hdlc = bch->hw; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat); if (stat & HDLC_INT_RPR) { if (stat & HDLC_STAT_RDO) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "RDO"); else mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat); hdlc->ctrl.sr.xml = 0; hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; write_ctrl(bch, 1); hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; write_ctrl(bch, 1); 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 (!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) || 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; } 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); 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) { 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; hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; write_ctrl(bch, 1); hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; HDLC_irq_xpr(bch); return; } else if (stat & HDLC_INT_XPR) HDLC_irq_xpr(bch); } static inline void HDLC_irq_main(fritzpnppci *fc) { u_int stat; channel_t *bch; stat = read_status(fc, 0); if (stat & HDLC_INT_MASK) { if (!(bch = Sel_BCS(fc, 0))) { if (fc->bch[0].debug) mISDN_debugprint(&fc->bch[0].inst, "hdlc spurious channel 0 IRQ"); } else HDLC_irq(bch, stat); } stat = read_status(fc, 1); if (stat & HDLC_INT_MASK) { if (!(bch = Sel_BCS(fc, 1))) { if (fc->bch[1].debug) mISDN_debugprint(&fc->bch[1].inst, "hdlc spurious channel 1 IRQ"); } else HDLC_irq(bch, stat); } } static irqreturn_t avm_fritz_interrupt(int intno, void *dev_id, struct pt_regs *regs) { fritzpnppci *fc = dev_id; u_char val; u_char sval; 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 */ spin_unlock(&fc->lock); return IRQ_NONE; } fc->irqcnt++; if (!(sval & AVM_STATUS0_IRQ_ISAC)) { val = ReadISAC(fc, ISAC_ISTA); mISDN_isac_interrupt(&fc->dch, val); } if (!(sval & AVM_STATUS0_IRQ_HDLC)) { HDLC_irq_main(fc); } if (fc->type == AVM_FRITZ_PNP) { WriteISAC(fc, ISAC_MASK, 0xFF); WriteISAC(fc, ISAC_MASK, 0x0); } spin_unlock(&fc->lock); return IRQ_HANDLED; } static irqreturn_t avm_fritzv2_interrupt(int intno, void *dev_id, struct pt_regs *regs) { fritzpnppci *fc = dev_id; u_char val; u_char sval; 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 */ spin_unlock(&fc->lock); return IRQ_NONE; } fc->irqcnt++; if (sval & AVM_STATUS0_IRQ_HDLC) { HDLC_irq_main(fc); } if (sval & AVM_STATUS0_IRQ_ISAC) { val = fcpci2_read_isac(fc, ISACSX_ISTA); mISDN_isac_interrupt(&fc->dch, val); } if (sval & AVM_STATUS0_IRQ_TIMER) { if (fc->dch.debug & L1_DEB_INTSTAT) mISDN_debugprint(&fc->dch.inst, "Fc2 timer irq"); outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); udelay(1); outb(fc->ctrlreg, fc->addr + 2); } spin_unlock(&fc->lock); return IRQ_HANDLED; } static int hdlc_down(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *bch; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; 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); ret = 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]); spin_unlock_irqrestore(inst->hwlock, flags); } 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)))) { 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; } 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); if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } test_and_clear_bit(FLG_ACTIVE, &bch->Flags); spin_unlock_irqrestore(inst->hwlock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) if (!mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, 0, skb)) return(0); } else { printk(KERN_WARNING "hdlc_down unknown prim(%x)\n", hh->prim); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } static void inithdlc(fritzpnppci *fc) { modehdlc(&fc->bch[0], 0, -1); modehdlc(&fc->bch[1], 1, -1); } void clear_pending_hdlc_ints(fritzpnppci *fc) { u_int val; val = read_status(fc, 0); mISDN_debugprint(&fc->dch.inst, "HDLC 1 STA %x", val); val = read_status(fc, 1); mISDN_debugprint(&fc->dch.inst, "HDLC 2 STA %x", val); } static void reset_avmpcipnp(fritzpnppci *fc) { switch (fc->type) { case AVM_FRITZ_PNP: case AVM_FRITZ_PCI: fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER; break; case AVM_FRITZ_PCIV2: fc->ctrlreg = AVM_STATUS0_RESET; break; } printk(KERN_INFO "AVM PCI/PnP: reset\n"); 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; disable_hwirq(fc); outb(AVM_STATUS1_ENA_IOM, fc->addr + 3); break; case AVM_FRITZ_PCIV2: fc->ctrlreg = 0; disable_hwirq(fc); break; } mdelay(1); printk(KERN_INFO "AVM PCI/PnP: S0/S1 %x/%x\n", inb(fc->addr + 2), inb(fc->addr + 3)); } 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) { shared = 0; id = "AVM Fritz!PnP"; } reset_avmpcipnp(fc); /* disable IRQ */ if (fc->type == AVM_FRITZ_PCIV2) { if (request_irq(fc->irq, (void *)avm_fritzv2_interrupt, shared, id, fc)) { printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", fc->irq); return(-EIO); } } else { if (request_irq(fc->irq, (void *)avm_fritz_interrupt, shared, id, fc)) { printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", fc->irq); return(-EIO); } } 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); WriteISAC(fc, ISAC_MASK, 0); enable_hwirq(fc); /* RESET Receiver and Transmitter */ WriteISAC(fc, ISAC_CMDR, 0x41); spin_unlock_irqrestore(&fc->lock, flags); /* Timeout 10ms */ current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); printk(KERN_INFO "AVM Fritz!PCI: IRQ %d count %d\n", fc->irq, fc->irqcnt); if (!fc->irqcnt) { printk(KERN_WARNING "AVM Fritz!PCI: IRQ(%d) getting no interrupts during init %d\n", fc->irq, 4 - cnt); if (cnt == 1) { return (-EIO); } else { reset_avmpcipnp(fc); cnt--; } } else { return(0); } } return(-EIO); } #define MAX_CARDS 4 static int fritz_cnt; static u_int protocol[MAX_CARDS]; static int layermask[MAX_CARDS]; static mISDNobject_t fritz; static int debug; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #define MODULE_PARM_T "1-4i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #ifdef OLD_MODULE_PARAM_ARRAY static int num_protocol=0,num_layermask=0; module_param_array(protocol, uint, num_protocol, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, num_layermask, S_IRUGO | S_IWUSR); #else module_param_array(protocol, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, NULL, S_IRUGO | S_IWUSR); #endif #endif #endif int setup_fritz(fritzpnppci *fc) { u_int val, ver; if (!request_region(fc->addr, 32, (fc->type == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) { printk(KERN_WARNING "mISDN: %s config port %x-%x already in use\n", "AVM Fritz!PCI", fc->addr, fc->addr + 31); return(-EIO); } switch (fc->type) { case AVM_FRITZ_PCI: val = inl(fc->addr); printk(KERN_INFO "AVM PCI: stat %#x\n", val); printk(KERN_INFO "AVM PCI: Class %X Rev %d\n", val & 0xff, (val>>8) & 0xff); outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf); fc->dch.read_reg = &ReadISAC; fc->dch.write_reg = &WriteISAC; fc->dch.read_fifo = &ReadISACfifo; fc->dch.write_fifo = &WriteISACfifo; fc->dch.type = ISAC_TYPE_ISAC; break; case AVM_FRITZ_PCIV2: val = inl(fc->addr); printk(KERN_INFO "AVM PCI V2: stat %#x\n", val); printk(KERN_INFO "AVM PCI V2: Class %X Rev %d\n", val & 0xff, (val>>8) & 0xff); ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf); fc->dch.read_reg = &fcpci2_read_isac; fc->dch.write_reg = &fcpci2_write_isac; fc->dch.read_fifo = &fcpci2_read_isac_fifo; fc->dch.write_fifo = &fcpci2_write_isac_fifo; fc->dch.type = ISAC_TYPE_ISACSX; break; case AVM_FRITZ_PNP: val = inb(fc->addr); ver = inb(fc->addr + 1); printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver); outb(AVM_HDLC_1, fc->addr + CHIP_INDEX); ver = inb(fc->addr + CHIP_WINDOW + 7); printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf); fc->dch.read_reg = &ReadISAC; fc->dch.write_reg = &WriteISAC; fc->dch.read_fifo = &ReadISACfifo; fc->dch.write_fifo = &WriteISACfifo; fc->dch.type = ISAC_TYPE_ISAC; break; default: release_region(fc->addr, 32); printk(KERN_WARNING "AVM unknown type %d\n", fc->type); return(-ENODEV); } printk(KERN_INFO "mISDN: %s config irq:%d base:0x%X\n", (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : (fc->type == AVM_FRITZ_PCIV2) ? "AVM Fritz!PCIv2" : "AVM Fritz!PnP", fc->irq, fc->addr); fc->dch.hw = &fc->isac; return(0); } static void release_card(fritzpnppci *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_freechannel(&card->bch[1]); mISDN_freechannel(&card->bch[0]); mISDN_freechannel(&card->dch); spin_unlock_irqrestore(&card->lock, flags); mISDN_ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); spin_lock_irqsave(&fritz.lock, flags); list_del(&card->list); spin_unlock_irqrestore(&fritz.lock, flags); if (card->type == AVM_FRITZ_PNP) { #if defined(CONFIG_PNP) pnp_disable_dev(card->dev.pnp); pnp_set_drvdata(card->dev.pnp, NULL); #endif } else { pci_disable_device(card->dev.pci); pci_set_drvdata(card->dev.pci, NULL); } kfree(card); } static int 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) printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n", __FUNCTION__, data, prim, arg); if (!data) { MGR_HASPROTOCOL_HANDLER(prim,arg,&fritz) printk(KERN_ERR "%s: no data prim %x arg %p\n", __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; break; } if (&card->bch[0].inst == inst) { channel = 0; break; } if (&card->bch[1].inst == inst) { channel = 1; 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); return(-EINVAL); } switch(prim) { case MGR_REGLAYER | CONFIRM: if (channel == 2) mISDN_setpara(&card->dch, &inst->st->para); else mISDN_setpara(&card->bch[channel], &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: 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 printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: if (channel == 2) mISDN_setpara(&card->dch, arg); else mISDN_setpara(&card->bch[channel], arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { release_card(card); } else { fritz.refcnt--; } break; case MGR_SETSTACK | INDICATION: if ((channel!=2) && (inst->pid.global == 2)) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (hdlc_down(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); } 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; spin_lock_init(&card->lock); card->dch.inst.hwlock = &card->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->dch.inst.class_dev.parent = dev; #else card->dch.inst.class_dev.dev = dev; #endif 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_ISAC_l1hw); sprintf(card->dch.inst.name, "Fritz%d", fritz_cnt+1); mISDN_set_dchannel_pid(&pid, protocol[fritz_cnt], layermask[fritz_cnt]); 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, hdlc_down); card->bch[i].inst.pid.layermask = ISDN_LAYER(0); card->bch[i].inst.hwlock = &card->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->bch[i].inst.class_dev.parent = dev; #else card->bch[i].inst.class_dev.dev = dev; #endif card->bch[i].debug = debug; sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1); 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_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); } fritz_cnt++; err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst); if (err) { release_card(card); return(err); } for (i=0; i<2; i++) { err = mISDN_ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst); if (err) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } } err = mISDN_ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid); if (err) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } err = init_card(card); if (err) { mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } mISDN_ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL); printk(KERN_INFO "fritz %d cards installed\n", fritz_cnt); return(0); } static int __devinit fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int err = -ENOMEM; fritzpnppci *card; if (!(card = kmalloc(sizeof(fritzpnppci), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for fritzcard\n"); return(err); } memset(card, 0, sizeof(fritzpnppci)); if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) card->type = AVM_FRITZ_PCIV2; else card->type = AVM_FRITZ_PCI; card->dev.pci = pdev; err = pci_enable_device(pdev); if (err) { kfree(card); return(err); } printk(KERN_INFO "mISDN_fcpcipnp: found adapter %s at %s\n", (char *) ent->driver_data, pci_name(pdev)); card->addr = pci_resource_start(pdev, 1); card->irq = pdev->irq; pci_set_drvdata(pdev, card); err = setup_instance(card); if (err) pci_set_drvdata(pdev, NULL); return(err); } #if defined(CONFIG_PNP) #ifdef NEW_ISAPNP static int __devinit fritzpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) #else static int __devinit fritzpnp_probe(struct pci_dev *pdev, const struct isapnp_device_id *dev_id) #endif { int err; fritzpnppci *card; if (!pdev) return(-ENODEV); if (!(card = kmalloc(sizeof(fritzpnppci), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for fritzcard\n"); return(-ENOMEM); } memset(card, 0, sizeof(fritzpnppci)); card->type = AVM_FRITZ_PNP; card->dev.pnp = pdev; pnp_disable_dev(pdev); err = pnp_activate_dev(pdev); if (err<0) { printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __FUNCTION__, (char *)dev_id->driver_data, err); kfree(card); return(err); } card->addr = pnp_port_start(pdev, 0); card->irq = pnp_irq(pdev, 0); printk(KERN_INFO "mISDN_fcpcipnp: found adapter %s at IO %#x irq %d\n", (char *)dev_id->driver_data, card->addr, card->irq); pnp_set_drvdata(pdev, card); err = setup_instance(card); if (err) pnp_set_drvdata(pdev, NULL); return(err); } #endif /* CONFIG_PNP */ static void __devexit fritz_remove_pci(struct pci_dev *pdev) { fritzpnppci *card = pci_get_drvdata(pdev); if (card) mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); else printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); } #if defined(CONFIG_PNP) #ifdef NEW_ISAPNP static void __devexit fritz_remove_pnp(struct pnp_dev *pdev) #else static void __devexit fritz_remove_pnp(struct pci_dev *pdev) #endif { fritzpnppci *card = pnp_get_drvdata(pdev); if (card) mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); else printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); } #endif /* CONFIG_PNP */ static struct pci_device_id fcpci_ids[] __devinitdata = { { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1 , PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) "Fritz!Card PCI" }, { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) "Fritz!Card PCI v2" }, { } }; MODULE_DEVICE_TABLE(pci, fcpci_ids); static struct pci_driver fcpci_driver = { name: "fcpci", probe: fritzpci_probe, remove: __devexit_p(fritz_remove_pci), id_table: fcpci_ids, }; #if defined(CONFIG_PNP) #ifdef NEW_ISAPNP static struct pnp_device_id fcpnp_ids[] __devinitdata = { { .id = "AVM0900", .driver_data = (unsigned long) "Fritz!Card PnP", }, }; static struct pnp_driver fcpnp_driver = { #else static struct isapnp_device_id fcpnp_ids[] __devinitdata = { { ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), (unsigned long) "Fritz!Card PnP" }, { } }; MODULE_DEVICE_TABLE(isapnp, fcpnp_ids); static struct isapnp_driver fcpnp_driver = { #endif name: "fcpnp", probe: fritzpnp_probe, remove: __devexit_p(fritz_remove_pnp), id_table: fcpnp_ids, }; #endif /* CONFIG_PNP */ static char FritzName[] = "AVM Fritz"; static int __init Fritz_init(void) { int err; #ifdef OLD_PCI_REGISTER_DRIVER int pci_nr_found; #endif printk(KERN_INFO "AVM Fritz PCI/PnP driver Rev. %s\n", mISDN_getrev(avm_fritz_rev)); #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; fritz.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; fritz.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; fritz.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS; if ((err = mISDN_register(&fritz))) { printk(KERN_ERR "Can't register Fritz PCI error(%d)\n", err); return(err); } err = pci_register_driver(&fcpci_driver); if (err < 0) goto out; #ifdef OLD_PCI_REGISTER_DRIVER pci_nr_found = err; #endif #if defined(CONFIG_PNP) err = pnp_register_driver(&fcpnp_driver); if (err < 0) goto out_unregister_pci; #endif #if !defined(CONFIG_HOTPLUG) || defined(MODULE) #ifdef OLD_PCI_REGISTER_DRIVER if (pci_nr_found + err == 0) { err = -ENODEV; goto out_unregister_isapnp; } #endif #endif mISDN_module_register(THIS_MODULE); return 0; #if !defined(CONFIG_HOTPLUG) || defined(MODULE) #ifdef OLD_PCI_REGISTER_DRIVER out_unregister_isapnp: #if defined(CONFIG_PNP) pnp_unregister_driver(&fcpnp_driver); #endif #endif #endif #if defined(CONFIG_PNP) out_unregister_pci: #endif pci_unregister_driver(&fcpci_driver); out: return err; } static void __exit Fritz_cleanup(void) { fritzpnppci *card, *next; int err; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&fritz))) { printk(KERN_ERR "Can't unregister Fritz PCI error(%d)\n", err); } list_for_each_entry_safe(card, next, &fritz.ilist, list) { printk(KERN_ERR "Fritz PCI card struct not empty refs %d\n", fritz.refcnt); release_card(card); } #if defined(CONFIG_PNP) pnp_unregister_driver(&fcpnp_driver); #endif pci_unregister_driver(&fcpci_driver); } module_init(Fritz_init); module_exit(Fritz_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/capi.c0000644000000000000500000002441211135651701017451 0ustar rootsrc/* $Id: capi.c,v 1.21 2006/12/21 15:25:06 nadi Exp $ * */ #include #include "core.h" #include "m_capi.h" #include "helper.h" #include "debug.h" static char *capi_revision = "$Revision: 1.21 $"; static int debug = 0; static mISDNobject_t capi_obj; static char MName[] = "mISDN Capi 2.0"; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #endif #endif static char deb_buf[256]; void capidebug(int level, char *fmt, ...) { va_list args; if (debug & level) { va_start(args, fmt); vsprintf(deb_buf, fmt, args); printk(KERN_DEBUG "%s\n", deb_buf); va_end(args); } } #ifdef OLDCAPI_DRIVER_INTERFACE struct capi_driver_interface *cdrv_if; #endif struct kmem_cache *mISDN_cmsg_cp; struct kmem_cache *mISDN_AppPlci_cp; struct kmem_cache *mISDN_ncci_cp; struct kmem_cache *mISDN_sspc_cp; #ifdef MISDN_KMEM_DEBUG static struct list_head mISDN_kmem_garbage = LIST_HEAD_INIT(mISDN_kmem_garbage); _cmsg * _kd_cmsg_alloc(char *fn, int line) { _kd_cmsg_t *ki = kmem_cache_alloc(mISDN_cmsg_cp, GFP_ATOMIC); if (!ki) return(NULL); ki->kdi.typ = KM_DBG_TYP_CM; INIT_LIST_HEAD(&ki->kdi.head); ki->kdi.line = line; ki->kdi.file = fn; list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage); return(&ki->cm); } void cmsg_free(_cmsg *cm) { km_dbg_item_t *kdi; if (!cm) { int_errtxt("zero pointer free at %p", __builtin_return_address(0)); return; } kdi = KDB_GET_KDI(cm); list_del(&kdi->head); kmem_cache_free(mISDN_cmsg_cp, kdi); } AppPlci_t * _kd_AppPlci_alloc(char *fn, int line) { _kd_AppPlci_t *ki = kmem_cache_alloc(mISDN_AppPlci_cp, GFP_ATOMIC); if (!ki) return(NULL); ki->kdi.typ = KM_DBG_TYP_AP; INIT_LIST_HEAD(&ki->kdi.head); ki->kdi.line = line; ki->kdi.file = fn; list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage); return(&ki->ap); } void AppPlci_free(AppPlci_t *ap) { km_dbg_item_t *kdi; if (!ap) { int_errtxt("zero pointer free at %p", __builtin_return_address(0)); return; } kdi = KDB_GET_KDI(ap); list_del(&kdi->head); kmem_cache_free(mISDN_AppPlci_cp, kdi); } Ncci_t * _kd_ncci_alloc(char *fn, int line) { _kd_Ncci_t *ki = kmem_cache_alloc(mISDN_ncci_cp, GFP_ATOMIC); if (!ki) return(NULL); ki->kdi.typ = KM_DBG_TYP_NI; INIT_LIST_HEAD(&ki->kdi.head); ki->kdi.line = line; ki->kdi.file = fn; list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage); return(&ki->ni); } void ncci_free(Ncci_t *ni) { km_dbg_item_t *kdi; if (!ni) { int_errtxt("zero pointer free at %p", __builtin_return_address(0)); return; } kdi = KDB_GET_KDI(ni); list_del(&kdi->head); kmem_cache_free(mISDN_ncci_cp, kdi); } SSProcess_t * _kd_SSProcess_alloc(char *fn, int line) { _kd_SSProcess_t *ki = kmem_cache_alloc(mISDN_sspc_cp, GFP_ATOMIC); if (!ki) return(NULL); ki->kdi.typ = KM_DBG_TYP_SP; INIT_LIST_HEAD(&ki->kdi.head); ki->kdi.line = line; ki->kdi.file = fn; list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage); return(&ki->sp); } void SSProcess_free(SSProcess_t *sp) { km_dbg_item_t *kdi; if (!sp) { int_errtxt("zero pointer free at %p", __builtin_return_address(0)); return; } kdi = KDB_GET_KDI(sp); list_del(&kdi->head); kmem_cache_free(mISDN_sspc_cp, kdi); } static void free_garbage(void) { struct list_head *item, *next; _kd_all_t *kda; list_for_each_safe(item, next, &mISDN_kmem_garbage) { kda = (_kd_all_t *)item; printk(KERN_DEBUG "garbage item found (%p <- %p -> %p) type%ld allocated at %s:%d\n", kda->kdi.head.prev, item, kda->kdi.head.next, kda->kdi.typ, kda->kdi.file, kda->kdi.line); list_del(item); switch(kda->kdi.typ) { case KM_DBG_TYP_CM: printk(KERN_DEBUG "cmsg cmd(%x,%x) appl(%x) addr(%x) nr(%d)\n", kda->a.cm.Command, kda->a.cm.Subcommand, kda->a.cm.ApplId, kda->a.cm.adr.adrController, kda->a.cm.Messagenumber); kmem_cache_free(mISDN_cmsg_cp, item); break; case KM_DBG_TYP_AP: printk(KERN_DEBUG "AppPlci: PLCI(%x) m.state(%x) appl(%p)\n", kda->a.ap.addr, kda->a.ap.plci_m.state, kda->a.ap.appl); kmem_cache_free(mISDN_AppPlci_cp, item); break; case KM_DBG_TYP_NI: printk(KERN_DEBUG "Ncci: NCCI(%x) state(%lx) m.state(%x) aplci(%p)\n", kda->a.ni.addr, kda->a.ni.state, kda->a.ni.ncci_m.state, kda->a.ni.AppPlci); kmem_cache_free(mISDN_ncci_cp, item); break; case KM_DBG_TYP_SP: printk(KERN_DEBUG "SSPc: addr(%x) id(%x) apid(%x) func(%x)\n", kda->a.sp.addr, kda->a.sp.invokeId, kda->a.sp.ApplId, kda->a.sp.Function); kmem_cache_free(mISDN_sspc_cp, item); break; default: printk(KERN_DEBUG "unknown garbage item(%p) type %ld\n", item, kda->kdi.typ); break; } } } #endif static void CapiCachesFree(void) { #ifdef MISDN_KMEM_DEBUG free_garbage(); #endif if (mISDN_cmsg_cp) { kmem_cache_destroy(mISDN_cmsg_cp); mISDN_cmsg_cp = NULL; } if (mISDN_AppPlci_cp) { kmem_cache_destroy(mISDN_AppPlci_cp); mISDN_AppPlci_cp = NULL; } if (mISDN_ncci_cp) { kmem_cache_destroy(mISDN_ncci_cp); mISDN_ncci_cp = NULL; } if (mISDN_sspc_cp) { kmem_cache_destroy(mISDN_sspc_cp); mISDN_sspc_cp = NULL; } } static int CapiNew(void) { mISDN_cmsg_cp = NULL; mISDN_AppPlci_cp = NULL; mISDN_ncci_cp = NULL; mISDN_sspc_cp = NULL; mISDN_cmsg_cp = kmem_cache_create("mISDN_cmesg", #ifdef MISDN_KMEM_DEBUG sizeof(_kd_cmsg_t), #else sizeof(_cmsg), #endif 0, 0, NULL #ifdef MISDN_COMPAT_KMEMCACHE , NULL #endif ); if (!mISDN_cmsg_cp) { CapiCachesFree(); return(-ENOMEM); } mISDN_AppPlci_cp = kmem_cache_create("mISDN_AppPlci", #ifdef MISDN_KMEM_DEBUG sizeof(_kd_AppPlci_t), #else sizeof(AppPlci_t), #endif 0, 0, NULL #ifdef MISDN_COMPAT_KMEMCACHE , NULL #endif ); if (!mISDN_AppPlci_cp) { CapiCachesFree(); return(-ENOMEM); } mISDN_ncci_cp = kmem_cache_create("mISDN_Ncci", #ifdef MISDN_KMEM_DEBUG sizeof(_kd_Ncci_t), #else sizeof(Ncci_t), #endif 0, 0, NULL #ifdef MISDN_COMPAT_KMEMCACHE , NULL #endif ); if (!mISDN_ncci_cp) { CapiCachesFree(); return(-ENOMEM); } mISDN_sspc_cp = kmem_cache_create("mISDN_SSProc", #ifdef MISDN_KMEM_DEBUG sizeof(_kd_SSProcess_t), #else sizeof(SSProcess_t), #endif 0, 0, NULL #ifdef MISDN_COMPAT_KMEMCACHE , NULL #endif ); if (!mISDN_sspc_cp) { CapiCachesFree(); return(-ENOMEM); } #ifdef OLDCAPI_DRIVER_INTERFACE cdrv_if = attach_capi_driver(&mISDN_driver); if (!cdrv_if) { CapiCachesFree(); printk(KERN_ERR "mISDN: failed to attach capi_driver\n"); return -EIO; } #endif init_listen(); init_AppPlci(); init_ncci(); return 0; } static int capi20_manager(void *data, u_int prim, void *arg) { mISDNinstance_t *inst = data; 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++; break; } list_for_each_entry(plink, &ctrl->linklist, list) { if (&plink->inst == inst) { found++; break; } } if (found) break; plink = NULL; } 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) ctrl->debug = debug; return(ret); } if (!ctrl) { if (CAPI_DBG_WARN & debug) printk(KERN_WARNING "capi20_manager setif no instance\n"); return(-EINVAL); } switch(prim) { case MGR_NEWENTITY | CONFIRM: ctrl->entity = (u_long)arg & 0xffffffff; break; #ifdef FIXME case MGR_CONNECT | REQUEST: return(mISDN_ConnectIF(inst, arg)); case MGR_SETIF | INDICATION: case MGR_SETIF | REQUEST: if (&ctrl->inst == inst) return(mISDN_SetIF(inst, arg, prim, NULL, ControllerL3L4, ctrl)); else return(AppPlcimISDN_SetIF(inst->data, prim, 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); ControllerDestr(ctrl); break; case MGR_UNREGLAYER | REQUEST: if (plink) { plink->inst.function = NULL; mISDN_ctrl(&plink->inst, MGR_UNREGLAYER | REQUEST, NULL); } break; case MGR_CTRLREADY | INDICATION: if (CAPI_DBG_INFO & debug) printk(KERN_DEBUG "ctrl %x ready\n", ctrl->inst.st->id); ControllerRun(ctrl); break; default: if (CAPI_DBG_WARN & debug) printk(KERN_WARNING "capi20_manager prim %x not handled\n", prim); return(-EINVAL); } return(0); } int Capi20Init(void) { int err; printk(KERN_INFO "%s driver file version %s\n", MName, mISDN_getrev(capi_revision)); #ifdef MODULE capi_obj.owner = THIS_MODULE; #endif capi_obj.name = MName; capi_obj.DPROTO.protocol[4] = ISDN_PID_L4_CAPI20; 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); if ((err = mISDN_register(&capi_obj))) { printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); #ifdef OLDCAPI_DRIVER_INTERFACE detach_capi_driver(&mISDN_driver); #endif CapiCachesFree(); free_listen(); free_AppPlci(); free_ncci(); free_Application(); } else mISDN_module_register(THIS_MODULE); return(err); } #ifdef MODULE static void Capi20cleanup(void) { int err; Controller_t *contr, *next; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&capi_obj))) { printk(KERN_ERR "Can't unregister CAPI20 error(%d)\n", err); } if (!list_empty(&capi_obj.ilist)) { printk(KERN_WARNING "mISDN controller list not empty\n"); list_for_each_entry_safe(contr, next, &capi_obj.ilist, list) ControllerDestr(contr); } #ifdef OLDCAPI_DRIVER_INTERFACE detach_capi_driver(&mISDN_driver); #endif free_Application(); CapiCachesFree(); free_listen(); free_AppPlci(); free_ncci(); } module_init(Capi20Init); module_exit(Capi20cleanup); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/capi_enc.c0000644000000000000500000001152711110524073020273 0ustar rootsrc/* $Id: capi_enc.c,v 1.3 2003/11/21 22:29:41 keil Exp $ * */ #include "m_capi.h" #include "asn1.h" int capiEncodeWord(__u8 *p, __u16 i) { *p++ = i; *p++ = i >> 8; return 2; } int capiEncodeDWord(__u8 *p, __u32 i) { *p++ = i; *p++ = i >> 8; *p++ = i >> 16; *p++ = i >> 24; return 4; } int capiEncodeFacilityPartyNumber(__u8 *dest, struct PartyNumber *partyNumber) { __u8 *p; p = &dest[1]; switch (partyNumber->type) { case 0: // unknown *p++ = 0; *p++ = 0; *p++ = 0; strcpy(p, partyNumber->p.unknown); p += strlen(partyNumber->p.unknown); break; case 1: // publicPartyNumber *p++ = 1; *p++ = partyNumber->p.publicPartyNumber.publicTypeOfNumber << 4; *p++ = 0; strcpy(p, partyNumber->p.publicPartyNumber.numberDigits); p += strlen(partyNumber->p.publicPartyNumber.numberDigits); break; default: int_error(); } dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacilityPartyNumber2(__u8 *dest, struct ServedUserNr *servedUserNr) { if (servedUserNr->all) { *dest++ = 0; // empty struct; return 1; } return capiEncodeFacilityPartyNumber(dest, &servedUserNr->partyNumber); } int capiEncodeServedUserNumbers(__u8 *dest, struct ServedUserNumberList *list) { __u8 *p; int i; p = &dest[1]; for (i = 0; i < 10; i++) { if (list->partyNumber[i].type >= 0) p += capiEncodeFacilityPartyNumber(p, &list->partyNumber[i]); } dest[0] = p - &dest[1]; return p - dest; } int capiEncodeInterrogateResponse(__u8 *dest, struct IntResult *intResult) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, intResult->procedure); p += capiEncodeWord(p, intResult->basicService); p += capiEncodeFacilityPartyNumber2(p, &intResult->servedUserNr); p += capiEncodeFacilityPartyNumber(p, &intResult->address.partyNumber); *p++ = 0; // subaddress dest[0] = p - &dest[1]; return p - dest; } int capiEncodeInterrogateResponseList(__u8 *dest, struct IntResultList *list) { __u8 *p; int i; p = &dest[1]; for (i = 0; i < 10; i++) { if (list->intResult[i].basicService >= 0) p += capiEncodeInterrogateResponse(p, &list->intResult[i]); } dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacIndCFact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, SupplementaryServiceReason); p += capiEncodeDWord(p, Handle); dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacIndCFdeact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, SupplementaryServiceReason); p += capiEncodeDWord(p, Handle); dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacIndCFinterParameters(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, struct IntResultList *list) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, SupplementaryServiceReason); p += capiEncodeDWord(p, Handle); p += capiEncodeInterrogateResponseList(p, list); dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacIndCFinterNumbers(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, struct ServedUserNumberList *list) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, SupplementaryServiceReason); p += capiEncodeDWord(p, Handle); p += capiEncodeServedUserNumbers(p, list); dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacIndCFNotAct(__u8 *dest, struct ActDivNotification *actNot) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, actNot->procedure); p += capiEncodeWord(p, actNot->basicService); p += capiEncodeFacilityPartyNumber2(p, &actNot->servedUserNr); p += capiEncodeFacilityPartyNumber(p, &actNot->address.partyNumber); *p++ = 0; // sub dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacIndCFNotDeact(__u8 *dest, struct DeactDivNotification *deactNot) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, deactNot->procedure); p += capiEncodeWord(p, deactNot->basicService); p += capiEncodeFacilityPartyNumber2(p, &deactNot->servedUserNr); dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacConfStruct(__u8 *dest, struct FacConfParm *facConfParm) { __u8 *p; p = &dest[1]; switch (facConfParm->Function) { case 0x0000: p += capiEncodeWord(p, facConfParm->u.GetSupportedServices.SupplementaryServiceInfo); p += capiEncodeDWord(p, facConfParm->u.GetSupportedServices.SupportedServices); break; default: p += capiEncodeWord(p, facConfParm->u.Info.SupplementaryServiceInfo); } dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacConfParm(__u8 *dest, struct FacConfParm *facConfParm) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, facConfParm->Function); p += capiEncodeFacConfStruct(p, facConfParm); dest[0] = p - &dest[1]; return p - dest; } int capiEncodeFacIndSuspend(__u8 *dest, __u16 SupplementaryServiceReason) { __u8 *p; p = &dest[1]; p += capiEncodeWord(p, SupplementaryServiceReason); dest[0] = p - &dest[1]; return p - dest; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/channel.c0000644000000000000500000000336011135651701020144 0ustar rootsrc/* $Id: channel.c,v 1.2 2006/03/06 12:58:31 keil Exp $ * * Author (c) Karsten Keil * * This file is released under the GPLv2 * */ #include #include "channel.h" #include "layer1.h" int mISDN_initchannel(channel_t *ch, ulong prop, int maxlen) { ch->log = kmalloc(MAX_LOG_SPACE, GFP_ATOMIC); if (!ch->log) { printk(KERN_WARNING "mISDN: No memory for channel log\n"); return(-ENOMEM); } ch->Flags = prop; ch->maxlen = maxlen; ch->hw = NULL; ch->rx_skb = NULL; ch->tx_skb = NULL; ch->tx_idx = 0; ch->next_skb = NULL; return(0); } int mISDN_freechannel(channel_t *ch) { if (ch->tx_skb) { dev_kfree_skb(ch->tx_skb); ch->tx_skb = NULL; } if (ch->rx_skb) { dev_kfree_skb(ch->rx_skb); ch->rx_skb = NULL; } if (ch->next_skb) { dev_kfree_skb(ch->next_skb); ch->next_skb = NULL; } kfree(ch->log); ch->log = NULL; return(0); } /* need called with HW lock */ int mISDN_setpara(channel_t *ch, mISDN_stPara_t *stp) { if (!stp) { // clear parameters ch->maxlen = 0; ch->up_headerlen = 0; return(0); } if (stp->up_headerlen) ch->up_headerlen = stp->up_headerlen; if (stp->maxdatalen) { if (ch->maxlen < stp->maxdatalen) { if (ch->rx_skb) { struct sk_buff *skb; skb = alloc_skb(stp->maxdatalen + ch->up_headerlen, GFP_ATOMIC); if (!skb) { int_errtxt("no skb for %d+%d", stp->maxdatalen, ch->up_headerlen); return(-ENOMEM); } skb_reserve(skb, ch->up_headerlen); memcpy(skb_put(skb, ch->rx_skb->len), ch->rx_skb->data, ch->rx_skb->len); dev_kfree_skb(ch->rx_skb); ch->rx_skb = skb; } } ch->maxlen = stp->maxdatalen; } return(0); } EXPORT_SYMBOL(mISDN_initchannel); EXPORT_SYMBOL(mISDN_freechannel); EXPORT_SYMBOL(mISDN_setpara); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/channel.h0000644000000000000500000001055111135651701020151 0ustar rootsrc/* $Id: channel.h,v 1.4 2006/09/07 13:02:34 crich Exp $ * * Basic declarations for a mISDN HW channel * * Author (c) Karsten Keil * * This file is released under the GPLv2 * */ #ifndef MISDN_CHANNEL_H #define MISDN_CHANNEL_H #include #include #include #include #include "helper.h" #ifdef MISDN_MEMDEBUG #include "memdbg.h" #endif #include "core.h" #define MAX_DFRAME_LEN_L1 300 #define MAX_MON_FRAME 32 #define MAX_LOG_SPACE 2048 #define MISDN_COPY_SIZE 32 /* channel->Flags bit field */ #define FLG_TX_BUSY 0 // tx_buf in use #define FLG_TX_NEXT 1 // next_skb in use #define FLG_L1_BUSY 2 // L1 is permanent busy #define FLG_USED 5 // channel is in use #define FLG_ACTIVE 6 // channel is activated #define FLG_BUSY_TIMER 7 /* channel type */ #define FLG_DCHANNEL 8 // channel is D-channel #define FLG_BCHANNEL 9 // channel is B-channel #define FLG_ECHANNEL 10 // channel is E-channel #define FLG_TRANSPARENT 12 // channel use transparent data #define FLG_HDLC 13 // channel use hdlc data #define FLG_L2DATA 14 // channel use L2 DATA primitivs #define FLG_ORIGIN 15 // channel is on origin site /* channel specific stuff */ /* arcofi specific */ #define FLG_ARCOFI_TIMER 16 #define FLG_ARCOFI_ERROR 17 /* isar specific */ #define FLG_INITIALIZED 16 #define FLG_DLEETX 17 #define FLG_LASTDLE 18 #define FLG_FIRST 19 #define FLG_LASTDATA 20 #define FLG_NMD_DATA 21 #define FLG_FTI_RUN 22 #define FLG_LL_OK 23 #define FLG_LL_CONN 24 #define FLG_DTMFSEND 25 #define MSK_INIT_DCHANNEL ((1<Flags) ? DL_DATA : PH_DATA; if (!skb) { err = mISDN_queue_data(&ch->inst, FLG_MSG_UP, pr, dinfo, 0, NULL, ch->up_headerlen); } else { #ifdef CONFIG_MISDN_NETDEV misdn_log_frame(ch->inst.st, skb->data, skb->len, FLG_MSG_UP); #endif if (ch->Flags & MSK_INIT_DCHANNEL) mISDN_dt_new_frame(ch->inst.st, D_RX, skb, 1); 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) { #ifdef DEBUG_NEXT_SKB_EXISTS printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", __FUNCTION__, skb->len, ch->next_skb->len); #endif return(-EBUSY); } if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { test_and_set_bit(FLG_TX_NEXT, &ch->Flags); #ifdef CONFIG_MISDN_NETDEV misdn_log_frame(ch->inst.st, skb->data, skb->len, FLG_MSG_DOWN); #endif if (ch->Flags & MSK_INIT_DCHANNEL) mISDN_dt_new_frame(ch->inst.st, D_TX, skb, 1); ch->next_skb = skb; return(0); } else { /* write to fifo */ ch->tx_skb = skb; ch->tx_idx = 0; #ifdef CONFIG_MISDN_NETDEV misdn_log_frame(ch->inst.st, skb->data, skb->len, FLG_MSG_DOWN); #endif if (ch->Flags & MSK_INIT_DCHANNEL) mISDN_dt_new_frame(ch->inst.st, D_TX, skb, 1); queue_ch_frame(ch, CONFIRM, di, NULL); return(skb->len); } } #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/contr.c0000644000000000000500000004772211135651701017673 0ustar rootsrc/* $Id: contr.c,v 1.30 2006/09/14 15:51:46 gkelleter Exp $ * */ #include #include #include "m_capi.h" #include "helper.h" #include "debug.h" #define contrDebug(contr, lev, fmt, args...) \ if (contr->debug & lev) capidebug(lev, fmt, ## args) void ControllerDestr(Controller_t *contr) { mISDNinstance_t *inst = &contr->inst; struct list_head *item, *next; u_long flags; spin_lock_irqsave(&contr->list_lock, flags); list_for_each_safe(item, next, &contr->Applications) { ApplicationDestr(list_entry(item, Application_t, head), 3); } if (contr->plcis) { Plci_t *plci = contr->plcis; int i; for (i = 0; i < contr->maxplci; i++) { AppPlci_t *aplci; if (test_bit(PLCI_STATE_ACTIV, &plci->state)) { if (plci->nAppl) { printk(KERN_ERR "%s: PLCI(%x) still busy (%d)\n", __FUNCTION__, plci->addr, plci->nAppl); list_for_each_safe(item, next, &plci->AppPlcis) { aplci = (AppPlci_t *)item; aplci->contr = NULL; plciDetachAppPlci(plci, aplci); AppPlciDestr(aplci); } } } plci++; } kfree(contr->plcis); contr->plcis = NULL; } list_for_each_safe(item, next, &contr->SSProcesse) { SSProcessDestr(list_entry(item, SSProcess_t, head)); } #ifdef OLDCAPI_DRIVER_INTERFACE if (contr->ctrl) cdrv_if->detach_ctr(contr->ctrl); #else if (contr->ctrl) { detach_capi_ctr(contr->ctrl); kfree(contr->ctrl); } #endif contr->ctrl = NULL; #ifdef FIXME if (inst->up.peer) { mISDN_ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); } if (inst->down.peer) { mISDN_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) mISDN_ctrl(inst, MGR_DELENTITY | REQUEST, (void *)((u_long)contr->entity)); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); list_del(&contr->list); spin_unlock_irqrestore(&contr->list_lock, flags); kfree(contr); } void ControllerRun(Controller_t *contr) { PLInst_t *plink; int ret; if (contr->inst.st && contr->inst.st->mgr) sprintf(contr->ctrl->manu, "mISDN CAPI controller %s", contr->inst.st->mgr->name); else sprintf(contr->ctrl->manu, "mISDN CAPI"); strncpy(contr->ctrl->serial, "0002", CAPI_SERIAL_LEN); contr->ctrl->version.majorversion = 2; contr->ctrl->version.minorversion = 0; contr->ctrl->version.majormanuversion = 1; contr->ctrl->version.minormanuversion = 0; memset(&contr->ctrl->profile, 0, sizeof(struct capi_profile)); contr->ctrl->profile.ncontroller = cpu_to_le16(1); contr->ctrl->profile.nbchannel = cpu_to_le16(contr->nr_bc); contrDebug(contr, CAPI_DBG_INFO, "%s: %s version(%s)", __FUNCTION__, contr->ctrl->manu, contr->ctrl->serial); // FIXME ret = mISDN_ctrl(contr->inst.st, MGR_GLOBALOPT | REQUEST, &contr->ctrl->profile.goptions); if (ret) { /* Fallback on error, minimum set */ contr->ctrl->profile.goptions = GLOBALOPT_INTERNAL_CTRL; } /* add options we allways know about FIXME: DTMF */ contr->ctrl->profile.goptions |= GLOBALOPT_DTMF | GLOBALOPT_SUPPLEMENTARY_SERVICE; if (contr->nr_bc) { mISDN_pid_t pidmask; memset(&pidmask, 0, sizeof(mISDN_pid_t)); pidmask.protocol[1] = 0x03ff; pidmask.protocol[2] = 0x1fff; pidmask.protocol[3] = 0x00ff; if (list_empty(&contr->linklist)) { int_error(); ret = -EINVAL; } else { plink = list_entry(contr->linklist.next, PLInst_t, list); ret = mISDN_ctrl(plink->st, MGR_EVALSTACK | REQUEST, &pidmask); } if (ret) { /* Fallback on error, minimum set */ int_error(); contr->ctrl->profile.support1 = 3; // HDLC, TRANS contr->ctrl->profile.support2 = 3; // X75SLP, TRANS contr->ctrl->profile.support3 = 1; // TRANS } else { contr->ctrl->profile.support1 = pidmask.protocol[1]; contr->ctrl->profile.support2 = pidmask.protocol[2]; contr->ctrl->profile.support3 = pidmask.protocol[3]; } } contrDebug(contr, CAPI_DBG_INFO, "%s: GLOBAL(%08X) B1(%08X) B2(%08X) B3(%08X)", __FUNCTION__, contr->ctrl->profile.goptions, contr->ctrl->profile.support1, contr->ctrl->profile.support2, contr->ctrl->profile.support3); cpu_to_le32s(&contr->ctrl->profile.goptions); cpu_to_le32s(&contr->ctrl->profile.support1); cpu_to_le32s(&contr->ctrl->profile.support2); cpu_to_le32s(&contr->ctrl->profile.support3); #ifdef OLDCAPI_DRIVER_INTERFACE contr->ctrl->ready(contr->ctrl); #else capi_ctr_ready(contr->ctrl); #endif } Application_t *getApplication4Id(Controller_t *contr, __u16 ApplId) { struct list_head *item; Application_t *ap = NULL; list_for_each(item, &contr->Applications) { ap = (Application_t *)item; if (ap->ApplId == ApplId) break; ap = NULL; } return(ap); } Plci_t *getPlci4Addr(Controller_t *contr, __u32 addr) { int i = (addr >> 8) & 0xff; if ((i < 1) || (i > contr->maxplci)) { int_error(); return(NULL); } return(&contr->plcis[i - 1]); } static void RegisterApplication(struct capi_ctr *ctrl, __u16 ApplId, capi_register_params *rp) { Controller_t *contr = ctrl->driverdata; Application_t *appl; u_long flags; int ret; contrDebug(contr, CAPI_DBG_APPL, "%s: ApplId(%x)", __FUNCTION__, ApplId); appl = getApplication4Id(contr, ApplId); if (appl) { int_error(); return; } spin_lock_irqsave(&contr->list_lock, flags); ret = ApplicationConstr(contr, ApplId, rp); spin_unlock_irqrestore(&contr->list_lock, flags); if (ret) { int_error(); return; } #ifdef OLDCAPI_DRIVER_INTERFACE contr->ctrl->appl_registered(contr->ctrl, ApplId); #endif } static void ReleaseApplication(struct capi_ctr *ctrl, __u16 ApplId) { Controller_t *contr = ctrl->driverdata; Application_t *appl; u_long flags; contrDebug(contr, CAPI_DBG_APPL, "%s: ApplId(%x) caller:%lx", __FUNCTION__, ApplId, __builtin_return_address(0)); spin_lock_irqsave(&contr->list_lock, flags); appl = getApplication4Id(contr, ApplId); if (!appl) { spin_unlock_irqrestore(&contr->list_lock, flags); int_error(); return; } ApplicationDestr(appl, 1); spin_unlock_irqrestore(&contr->list_lock, flags); #ifdef OLDCAPI_DRIVER_INTERFACE contr->ctrl->appl_released(contr->ctrl, ApplId); #endif } #ifdef OLDCAPI_DRIVER_INTERFACE static void #else static u16 #endif SendMessage(struct capi_ctr *ctrl, struct sk_buff *skb) { Controller_t *contr = ctrl->driverdata; Application_t *appl; int ApplId; 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; 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 } static int LoadFirmware(struct capi_ctr *ctrl, capiloaddata *data) { Controller_t *contr = ctrl->driverdata; struct firm { int len; void *data; } firm; int retval; firm.len = data->firmware.len; if (data->firmware.user) { firm.data = vmalloc(data->firmware.len); if (!firm.data) return(-ENOMEM); retval = copy_from_user(firm.data, data->firmware.data, data->firmware.len); if (retval) { vfree(firm.data); return(retval); } } else firm.data = data; mISDN_ctrl(contr->inst.st, MGR_LOADFIRM | REQUEST, &firm); if (data->firmware.user) vfree(firm.data); return(0); } static char * procinfo(struct capi_ctr *ctrl) { Controller_t *contr = ctrl->driverdata; if (CAPI_DBG_INFO & contr->debug) printk(KERN_DEBUG "%s\n", __FUNCTION__); if (!contr) return ""; sprintf(contr->infobuf, "-"); return contr->infobuf; } static int read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) { int len = 0; len += sprintf(page+len, "mISDN_read_proc\n"); if (off+count >= len) *eof = 1; if (len < off) return 0; *start = page + off; return ((count < len-off) ? count : len-off); }; static void ResetController(struct capi_ctr *ctrl) { Controller_t *contr = ctrl->driverdata; struct list_head *item, *next; u_long flags; spin_lock_irqsave(&contr->list_lock, flags); list_for_each_safe(item, next, &contr->Applications) { ApplicationDestr((Application_t *)item, 2); } list_for_each_safe(item, next, &contr->SSProcesse) { SSProcessDestr((SSProcess_t *)item); } spin_unlock_irqrestore(&contr->list_lock, flags); #ifdef OLDCAPI_DRIVER_INTERFACE contr->ctrl->reseted(contr->ctrl); #else capi_ctr_reseted(contr->ctrl); #endif } #ifdef OLDCAPI_DRIVER_INTERFACE static void Remove_Controller(struct capi_ctr *ctrl) { Controller_t *contr = ctrl->driverdata; if (CAPI_DBG_INFO & contr->debug) printk(KERN_DEBUG "%s\n", __FUNCTION__); } struct capi_driver mISDN_driver = { "mISDN", "0.01", LoadFirmware, ResetController, Remove_Controller, RegisterApplication, ReleaseApplication, SendMessage, procinfo, read_proc, 0, 0, }; #endif void ControllerD2Trace(Controller_t *contr, u_char *buf, int len) { struct list_head *item; list_for_each(item, &contr->Applications) { applD2Trace((Application_t *)item, buf, len); } } static __inline__ Plci_t * getPlci4L3id(Controller_t *contr, u_int l3id) { Plci_t *plci = contr->plcis; int i; for (i = 0; i < contr->maxplci; i++) { if (test_bit(PLCI_STATE_ACTIV, &plci->state) && (plci->l3id == l3id)) return(plci); plci++; } return(NULL); } int ControllerNewPlci(Controller_t *contr, Plci_t **plci_p, u_int l3id) { int i; Plci_t *plci = contr->plcis; for (i = 0; i < contr->maxplci; i++) { if (!test_and_set_bit(PLCI_STATE_ACTIV, &plci->state)) break; plci++; } if (i == contr->maxplci) { contrDebug(contr, CAPI_DBG_PLCI, "%s: no free PLCI", __FUNCTION__); return(-EBUSY); //FIXME } *plci_p = plci; if (l3id == MISDN_ID_ANY) { if (contr->entity == MISDN_ENTITY_NONE) { printk(KERN_ERR "mISDN %s: no ENTITY id\n", __FUNCTION__); test_and_clear_bit(PLCI_STATE_ACTIV, &plci->state); return(-EINVAL); //FIXME } plci->l3id = (contr->entity << 16) | plci->addr; } else { plci = getPlci4L3id(contr, l3id); if (plci) { printk(KERN_WARNING "mISDN %s: PLCI(%x) allready has l3id(%x)\n", __FUNCTION__, plci->addr, l3id); test_and_clear_bit(PLCI_STATE_ACTIV, &(*plci_p)->state); return(-EBUSY); } plci = *plci_p; plci->l3id = l3id; } contrDebug(contr, CAPI_DBG_PLCI, "%s: PLCI(%x) plci(%p,%d) id(%x)", __FUNCTION__, plci->addr, plci, sizeof(*plci), plci->l3id); return(0); } int ControllerReleasePlci(Plci_t *plci) { if (!plci->contr) { int_error(); return(-EINVAL); } if (plci->nAppl) { contrDebug(plci->contr, CAPI_DBG_PLCI, "%s: PLCI(%x) still has %d Applications", __FUNCTION__, plci->addr, plci->nAppl); return(-EBUSY); } if (!list_empty(&plci->AppPlcis)) { int_errtxt("PLCI(%x) AppPlcis list not empty", plci->addr); return(-EBUSY); } test_and_clear_bit(PLCI_STATE_ALERTING, &plci->state); test_and_clear_bit(PLCI_STATE_OUTGOING, &plci->state); plci->l3id = MISDN_ID_NONE; if (!test_and_clear_bit(PLCI_STATE_ACTIV, &plci->state)) int_errtxt("PLCI(%x) was not activ", plci->addr); return(0); } void ControllerAddSSProcess(Controller_t *contr, SSProcess_t *sp) { u_long flags; INIT_LIST_HEAD(&sp->head); sp->contr = contr; sp->addr = contr->addr; spin_lock_irqsave(&contr->list_lock, flags); contr->LastInvokeId++; sp->invokeId = contr->LastInvokeId; list_add(&sp->head, &contr->SSProcesse); spin_unlock_irqrestore(&contr->list_lock, flags); } SSProcess_t *getSSProcess4Id(Controller_t *contr, __u16 id) { struct list_head *item; SSProcess_t *sp = NULL; list_for_each(item, &contr->SSProcesse) { sp = (SSProcess_t *)item; if (sp->invokeId == id) break; sp = NULL; } return(sp); } static int Controller_function(mISDNinstance_t *inst, struct sk_buff *skb) { Controller_t *contr; Plci_t *plci; int ret = -EINVAL; mISDN_head_t *hh; hh = mISDN_HEAD_P(skb); contr = inst->privat; contrDebug(contr, CAPI_DBG_CONTR_INFO, "%s: prim(%x) id(%x)", __FUNCTION__, hh->prim, hh->dinfo); 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))) { contrDebug(contr, CAPI_DBG_WARN, "%s: unknown plci prim(%x) id(%x)", __FUNCTION__, hh->prim, hh->dinfo); return(-ENODEV); } contrDebug(contr, CAPI_DBG_PLCI, "%s: PLCI(%x) plci(%p)", __FUNCTION__, plci->addr, plci); ret = plci_l3l4(plci, hh->prim, skb); } return(ret); } int ControllerL4L3(Controller_t *contr, u_int prim, int dinfo, struct sk_buff *skb) { return(mISDN_queuedown_newhead(&contr->inst, 0, prim, dinfo, skb)); } void ControllerPutStatus(Controller_t *contr, char *msg) { contrDebug(contr, CAPI_DBG_CONTR, "%s: %s", __FUNCTION__, msg); } int ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *ocapi) { struct list_head *head; Controller_t *contr; int retval; mISDNstack_t *cst; PLInst_t *plink; u_long flags; if (!st) return(-EINVAL); if (list_empty(&st->childlist)) { if ((st->id & FLG_CLONE_STACK) && (st->childlist.prev != &st->childlist)) { head = st->childlist.prev; } else { printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n", __FUNCTION__, st->id, st->childlist.prev, &st->childlist, st->childlist.next); return(-EINVAL); } } else head = &st->childlist; if (!pid) return(-EINVAL); contr = kmalloc(sizeof(Controller_t), GFP_KERNEL); if (!contr) return(-ENOMEM); memset(contr, 0, sizeof(Controller_t)); INIT_LIST_HEAD(&contr->Applications); INIT_LIST_HEAD(&contr->SSProcesse); INIT_LIST_HEAD(&contr->linklist); spin_lock_init(&contr->list_lock); contr->next_id = 1; memcpy(&contr->inst.pid, pid, sizeof(mISDN_pid_t)); #ifndef OLDCAPI_DRIVER_INTERFACE if (!(contr->ctrl = kmalloc(sizeof(struct capi_ctr), GFP_KERNEL))) { printk(KERN_ERR "no mem for contr->ctrl\n"); int_error(); ControllerDestr(contr); return -ENOMEM; } memset(contr->ctrl, 0, sizeof(struct capi_ctr)); #endif list_for_each_entry(cst, head, list) contr->nr_bc++; if (!contr->nr_bc) { printk(KERN_ERR "no bchannels\n"); ControllerDestr(contr); return(-EINVAL); // FIXME } if (contr->nr_bc <= 2) contr->maxplci = CAPI_MAXPLCI_BRI; else if (contr->nr_bc <= 8) contr->maxplci = contr->nr_bc * 2 + 4; else contr->maxplci = CAPI_MAXPLCI_PRI; contr->plcis = kmalloc(contr->maxplci*sizeof(Plci_t), GFP_KERNEL); if (!contr->plcis) { printk(KERN_ERR "no mem for contr->plcis\n"); int_error(); contr->maxplci = 0; ControllerDestr(contr); return -ENOMEM; } 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); return(-ENOPROTOOPT); } list_for_each_entry(cst, head, list) { if (!(plink = kmalloc(sizeof(PLInst_t), GFP_KERNEL))) { printk(KERN_ERR "no mem for PLinst\n"); int_error(); ControllerDestr(contr); return -ENOMEM; } memset(plink, 0, sizeof(PLInst_t)); plink->st = cst; plink->inst.st = cst; mISDN_init_instance(&plink->inst, ocapi, plink, NULL); plink->inst.pid.layermask |= ISDN_LAYER(4); // 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 = mISDN_ctrl(&contr->inst, MGR_NEWENTITY | REQUEST, NULL); if (retval) { printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%d)\n", __FUNCTION__, retval); } retval = 0; #ifdef OLDCAPI_DRIVER_INTERFACE { char tmp[10]; 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", contr->addr); contr->ctrl->driver_name = "mISDN"; contr->ctrl->driverdata = contr; contr->ctrl->register_appl = RegisterApplication; contr->ctrl->release_appl = ReleaseApplication; contr->ctrl->send_message = SendMessage; contr->ctrl->load_firmware = LoadFirmware; contr->ctrl->reset_ctr = ResetController; contr->ctrl->procinfo = procinfo; contr->ctrl->ctr_read_proc = read_proc; 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); mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &contr->inst); // contr->inst.up.stat = IF_DOWN; *contr_p = contr; } else { ControllerDestr(contr); } return retval; } PLInst_t * ControllerSelChannel(Controller_t *contr, u_int channel) { mISDNstack_t *cst; PLInst_t *plink; channel_info_t ci; int ret; if (list_empty(&contr->linklist)) { int_errtxt("no linklist for controller(%x)", contr->addr); return(NULL); } ci.channel = channel; ci.st.p = NULL; ret = mISDN_ctrl(contr->inst.st, MGR_SELCHANNEL | REQUEST, &ci); if (ret) { int_errtxt("MGR_SELCHANNEL ret(%d)", ret); return(NULL); } cst = ci.st.p; list_for_each_entry(plink, &contr->linklist, list) { if (cst == plink->st) return(plink); } return(NULL); } int ControllerNextId(Controller_t *contr) { int id; id = contr->next_id++; if (id == 0x7fff) contr->next_id = 1; id |= (contr->entity << 16); return(id); } #if 0 static void d2_listener(struct IsdnCardState *cs, u_char *buf, int len) { Controller_t *contr = cs->contr; if (!contr) { int_error(); return; } ControllerD2Trace(contr, buf, len); } #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/core.c0000644000000000000500000004743111135651701017473 0ustar rootsrc/* $Id: core.c,v 1.40 2007/02/13 10:43:45 crich Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include #include #include "core.h" #ifdef CONFIG_KMOD #include #endif #ifdef CONFIG_SMP #include #endif static char *mISDN_core_revision = "$Revision: 1.40 $"; static char *mISDN_core_version = MISDNVERSION ; static void (*dt_new_frame) (mISDNstack_t *stack, enum mISDN_dt_type type, struct sk_buff *skb, int duplicate_skb) = NULL; LIST_HEAD(mISDN_objectlist); static rwlock_t mISDN_objects_lock = RW_LOCK_UNLOCKED; LIST_HEAD(mISDN_modulelist); static rwlock_t mISDN_modules_lock = RW_LOCK_UNLOCKED; struct modulelist { struct list_head list; struct module *module; }; int core_debug; static u_char entityarray[MISDN_MAX_ENTITY/8]; static spinlock_t entity_lock = SPIN_LOCK_UNLOCKED; static uint debug; static int obj_id; static int dt_enabled = 0; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #else module_param (debug, uint, S_IRUGO | S_IWUSR); #endif MODULE_PARM_DESC (debug, "mISDN core debug mask"); #endif typedef struct _mISDN_thread { /* thread */ struct task_struct *thread; wait_queue_head_t waitq; struct semaphore *notify; u_long Flags; struct sk_buff_head workq; } mISDN_thread_t; #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; static moditem_t modlist[] = { {"mISDN_l1", ISDN_PID_L1_TE_S0}, {"mISDN_l2", ISDN_PID_L2_LAPD}, {"mISDN_l2", ISDN_PID_L2_B_X75SLP}, {"l3udss1", ISDN_PID_L3_DSS1USER}, {"mISDN_dtmf", ISDN_PID_L2_B_TRANSDTMF}, {NULL, ISDN_PID_NONE} }; /* * kernel thread to do work which cannot be done *in interrupt context */ static int mISDNd(void *data) { mISDN_thread_t *hkt = data; #ifdef CONFIG_SMP lock_kernel(); #endif MAKEDAEMON("mISDNd"); sigfillset(¤t->blocked); hkt->thread = current; #ifdef CONFIG_SMP unlock_kernel(); #endif printk(KERN_DEBUG "mISDNd: kernel daemon started (current:%p)\n", current); test_and_set_bit(mISDN_TFLAGS_STARTED, &hkt->Flags); for (;;) { int err; struct sk_buff *skb; mISDN_headext_t *hhe; if (test_and_clear_bit(mISDN_TFLAGS_RMMOD, &hkt->Flags)) break; if (hkt->notify != NULL) up(hkt->notify); 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))) { test_and_set_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags); err = -EINVAL; hhe=mISDN_HEADEXT_P(skb); switch (hhe->addr) { case MGR_FUNCTION: err = hhe->func.ctrl(hhe->data[0], hhe->prim, skb->len ? skb->data : NULL); if (err) { printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) failed err(%d)\n", hhe->addr, hhe->prim, err); } else { if (debug) printk(KERN_DEBUG "mISDNd: addr(%x) prim(%x) success\n", hhe->addr, hhe->prim); err--; /* to free skb */ } break; #ifdef FIXME case MGR_QUEUEIF: err = hhe->func.iff(hhe->data[0], skb); if (err) { printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) failed err(%d)\n", hhe->addr, hhe->prim, err); } break; #endif default: int_error(); printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) unknown\n", hhe->addr, hhe->prim); err = -EINVAL; break; } if (err) kfree_skb(skb); test_and_clear_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags); } if (test_and_clear_bit(mISDN_TFLAGS_TEST, &hkt->Flags)) printk(KERN_DEBUG "mISDNd: test event done\n"); } printk(KERN_DEBUG "mISDNd: daemon exit now (current:%p)\n", current); test_and_clear_bit(mISDN_TFLAGS_STARTED, &hkt->Flags); test_and_clear_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags); discard_queue(&hkt->workq); hkt->thread = NULL; if (hkt->notify != NULL) up(hkt->notify); return(0); } mISDNobject_t * get_object(int id) { mISDNobject_t *obj; read_lock(&mISDN_objects_lock); list_for_each_entry(obj, &mISDN_objectlist, list) if (obj->id == id) { read_unlock(&mISDN_objects_lock); return(obj); } read_unlock(&mISDN_objects_lock); return(NULL); } static mISDNobject_t * find_object(int protocol) { mISDNobject_t *obj; int err; read_lock(&mISDN_objects_lock); list_for_each_entry(obj, &mISDN_objectlist, list) { err = obj->own_ctrl(NULL, MGR_HASPROTOCOL | REQUEST, &protocol); if (!err) goto unlock; if (err != -ENOPROTOOPT) { if (0 == mISDN_HasProtocol(obj, protocol)) goto unlock; } } obj = NULL; unlock: read_unlock(&mISDN_objects_lock); return(obj); } static mISDNobject_t * find_object_module(int protocol) { #ifdef CONFIG_KMOD int err; #endif moditem_t *m = modlist; mISDNobject_t *obj; while (m->name != NULL) { if (m->protocol == protocol) { #ifdef CONFIG_KMOD if (debug) printk(KERN_DEBUG "find_object_module %s - trying to load\n", m->name); err=request_module(m->name); if (debug) printk(KERN_DEBUG "find_object_module: request_module(%s) returns(%d)\n", m->name, err); #else printk(KERN_WARNING "not possible to autoload %s please try to load manually\n", m->name); #endif if ((obj = find_object(protocol))) return(obj); } m++; } if (debug) printk(KERN_DEBUG "%s: no module for protocol %x found\n", __FUNCTION__, protocol); return(NULL); } #ifdef FIXME static int dummy_if(mISDNif_t *hif, struct sk_buff *skb) { if (!skb) { printk(KERN_WARNING "%s: hif(%p) without skb\n", __FUNCTION__, hif); return(-EINVAL); } if (debug & DEBUG_DUMMY_FUNC) { mISDN_head_t *hh = mISDN_HEAD_P(skb); printk(KERN_DEBUG "%s: hif(%p) skb(%p) len(%d) prim(%x)\n", __FUNCTION__, hif, skb, skb->len, hh->prim); } dev_kfree_skb_any(skb); return(0); } #endif mISDNinstance_t * get_next_instance(mISDNstack_t *st, mISDN_pid_t *pid) { int err; mISDNinstance_t *next; int layer, proto; mISDNobject_t *obj; layer = mISDN_get_lowlayer(pid->layermask); proto = pid->protocol[layer]; next = get_instance(st, layer, proto); if (!next) { obj = find_object(proto); if (!obj) obj = find_object_module(proto); if (!obj) { if (debug) printk(KERN_WARNING "%s: no object found\n", __FUNCTION__); return(NULL); } err = obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, pid); if (err) { printk(KERN_WARNING "%s: newlayer err(%d)\n", __FUNCTION__, err); return(NULL); } next = get_instance(st, layer, proto); } return(next); } static int sel_channel(mISDNstack_t *st, channel_info_t *ci) { int err = -EINVAL; if (!ci) return(err); if (debug) printk(KERN_DEBUG "%s: st(%p) st->mgr(%p)\n", __FUNCTION__, st, st->mgr); if (st->mgr) { if (st->mgr->obj && st->mgr->obj->own_ctrl) { err = st->mgr->obj->own_ctrl(st->mgr, MGR_SELCHANNEL | REQUEST, ci); if (debug) printk(KERN_DEBUG "%s: MGR_SELCHANNEL(%d)\n", __FUNCTION__, err); } else int_error(); } else { printk(KERN_WARNING "%s: no mgr st(%p)\n", __FUNCTION__, st); } if (err) { mISDNstack_t *cst; u_int nr = 0; ci->st.p = NULL; if (!(ci->channel & (~CHANNEL_NUMBER))) { /* only number is set */ struct list_head *head; if (list_empty(&st->childlist)) { if ((st->id & FLG_CLONE_STACK) && (st->childlist.prev != &st->childlist)) { head = st->childlist.prev; } else { printk(KERN_WARNING "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n", __FUNCTION__, st->id, st->childlist.prev, &st->childlist, st->childlist.next); return(err); } } else head = &st->childlist; list_for_each_entry(cst, head, list) { nr++; if (nr == (ci->channel & 3)) { ci->st.p = cst; return(0); } } } } return(err); } #ifdef FIXME static int disconnect_if(mISDNinstance_t *inst, u_int prim, mISDNif_t *hif) { int err = 0; if (hif) { hif->stat = IF_NOACTIV; hif->func = dummy_if; hif->peer = NULL; hif->fdata = NULL; } if (inst) err = inst->obj->own_ctrl(inst, prim, hif); return(err); } static int add_if(mISDNinstance_t *inst, u_int prim, mISDNif_t *hif) { mISDNif_t *myif; if (!inst) return(-EINVAL); if (!hif) return(-EINVAL); if (hif->stat & IF_UP) { myif = &inst->down; } else if (hif->stat & IF_DOWN) { myif = &inst->up; } else return(-EINVAL); while(myif->clone) myif = myif->clone; myif->clone = hif; hif->predecessor = myif; inst->obj->own_ctrl(inst, prim, hif); return(0); } #endif static char tmpbuf[4096]; static int debugout(mISDNinstance_t *inst, logdata_t *log) { char *p = tmpbuf; if (log->head && *log->head) p += sprintf(p,"%s ", log->head); else p += sprintf(p,"%s ", inst->obj->name); p += vsprintf(p, log->fmt, log->args); printk(KERN_DEBUG "%s\n", tmpbuf); return(0); } static int get_hdevice(mISDNdevice_t **dev, int *typ) { if (!dev) 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) { mISDN_headext_t *hhe = mISDN_HEADEXT_P(skb); hhe->addr = prim; skb_queue_tail(&mISDN_thread.workq, skb); wake_up_interruptible(&mISDN_thread.waitq); return(0); } #endif static int set_stack_req(mISDNstack_t *st, mISDN_pid_t *pid) { struct sk_buff *skb; 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); hhe = mISDN_HEADEXT_P(skb); hhe->prim = MGR_SETSTACK_NW | REQUEST; hhe->addr = MGR_FUNCTION; hhe->data[0] = st; npid = (mISDN_pid_t *)skb_put(skb, sizeof(mISDN_pid_t)); if (pid->maxplen) pbuf = skb_put(skb, pid->maxplen); err = copy_pid(npid, pid, pbuf); if (err) // FIXME error handling int_errtxt("copy_pid error %d", err); hhe->func.ctrl = mISDN_ctrl; skb_queue_tail(&mISDN_thread.workq, skb); wake_up_interruptible(&mISDN_thread.waitq); return(0); } static int queue_ctrl_ready(mISDNstack_t *st, void *arg) { struct sk_buff *skb; mISDN_headext_t *hhe; if (!(skb = alloc_skb(4, GFP_ATOMIC))) return(-ENOMEM); if (arg) /* maybe changed for future enhancements */ return(-EINVAL); hhe = mISDN_HEADEXT_P(skb); hhe->prim = MGR_CTRLREADY | INDICATION; hhe->addr = MGR_FUNCTION; hhe->data[0] = st; hhe->func.ctrl = do_for_all_layers; skb_queue_tail(&mISDN_thread.workq, skb); wake_up_interruptible(&mISDN_thread.waitq); return(0); } int mISDN_alloc_entity(int *entity) { u_long flags; spin_lock_irqsave(&entity_lock, flags); *entity = 1; while(*entity < MISDN_MAX_ENTITY) { if (!test_and_set_bit(*entity, (u_long *)&entityarray[0])) break; (*entity)++; } spin_unlock_irqrestore(&entity_lock, flags); if (*entity == MISDN_MAX_ENTITY) return(-EBUSY); return(0); } int mISDN_delete_entity(int entity) { u_long flags; int ret = 0; spin_lock_irqsave(&entity_lock, flags); if (!test_and_clear_bit(entity, (u_long *)&entityarray[0])) { printk(KERN_WARNING "mISDN: del_entity(%d) but entity not allocated\n", entity); ret = -ENODEV; } spin_unlock_irqrestore(&entity_lock, flags); return(ret); } static int new_entity(mISDNinstance_t *inst) { int entity; int ret; if (!inst) return(-EINVAL); ret = mISDN_alloc_entity(&entity); if (ret) { 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 *)((u_long)entity)); if (ret) mISDN_delete_entity(entity); return(ret); } int mISDN_ctrl(void *data, u_int prim, void *arg) { mISDNstack_t *st = data; switch(prim) { case MGR_NEWSTACK | REQUEST: if (!(st = new_stack(data, arg))) return(-EINVAL); return(0); case MGR_NEWENTITY | REQUEST: return(new_entity(data)); case MGR_DELENTITY | REQUEST: case MGR_DELENTITY | INDICATION: return(mISDN_delete_entity((u_long)arg & 0xffffffff)); case MGR_REGLAYER | INDICATION: return(register_layer(st, arg)); case MGR_REGLAYER | REQUEST: if (!register_layer(st, arg)) { mISDNinstance_t *inst = arg; return(inst->obj->own_ctrl(arg, MGR_REGLAYER | CONFIRM, NULL)); } 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 */ #ifdef CONFIG_MISDN_NETDEV misdn_netdev_addstack(st); #endif return(set_stack_req(st, arg)); case MGR_SETSTACK_NW | REQUEST: return(set_stack(st, arg)); case MGR_CLEARSTACK | REQUEST: 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; case MGR_DEBUGDATA | REQUEST: return(debugout(data, arg)); default: if (debug) printk(KERN_WARNING "manager prim %x not handled\n", prim); break; } return(-EINVAL); } void mISDN_dt_set_callback(void (*new_frame) (mISDNstack_t *stack, enum mISDN_dt_type type, struct sk_buff *skb, int duplicate_skb)) { dt_new_frame = new_frame; } void mISDN_dt_unset_callback(void) { dt_new_frame = NULL; } void mISDN_dt_enable(void) { dt_enabled = dt_new_frame ? 1 : 0; } void mISDN_dt_disable(void) { dt_enabled = 0; } void mISDN_dt_new_frame(mISDNstack_t *stack, enum mISDN_dt_type type, struct sk_buff *skb, int duplicate_skb) { if (dt_enabled) dt_new_frame(stack, type, skb, duplicate_skb); else if (!duplicate_skb && skb) kfree_skb(skb); } void mISDN_module_register(struct module *module) { struct modulelist *ml = kmalloc(sizeof(struct modulelist), GFP_KERNEL); if (!ml) { printk(KERN_DEBUG "mISDN_register_module: kmalloc failed!\n"); return; } ml->module = module; write_lock(&mISDN_modules_lock); list_add(&ml->list, &mISDN_modulelist); write_unlock(&mISDN_modules_lock); if (debug) printk(KERN_DEBUG "mISDN_register_module(%s)\n", module->name); } void mISDN_module_unregister(struct module *module) { struct modulelist *ml, *mi; write_lock(&mISDN_modules_lock); list_for_each_entry_safe(ml, mi, &mISDN_modulelist, list) if (ml->module == module) { list_del(&ml->list); kfree(ml); write_unlock(&mISDN_modules_lock); if (debug) printk(KERN_DEBUG "mISDN_unregister_module(%s)\n", module->name); return; } write_unlock(&mISDN_modules_lock); } void mISDN_inc_usage(void) { struct modulelist *ml; read_lock(&mISDN_modules_lock); list_for_each_entry(ml, &mISDN_modulelist, list) try_module_get(ml->module); read_unlock(&mISDN_modules_lock); } void mISDN_dec_usage(void) { struct modulelist *ml; read_lock(&mISDN_modules_lock); list_for_each_entry(ml, &mISDN_modulelist, list) { if (module_refcount(ml->module) > 0) module_put(ml->module); } read_unlock(&mISDN_modules_lock); } int mISDN_register(mISDNobject_t *obj) { u_long flags; int retval=0; if (!obj) return(-EINVAL); write_lock_irqsave(&mISDN_objects_lock, flags); obj->id = obj_id++; list_add_tail(&obj->list, &mISDN_objectlist); write_unlock_irqrestore(&mISDN_objects_lock, flags); // register_prop if (debug) printk(KERN_DEBUG "mISDN_register %s id %x\n", obj->name, obj->id); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "mISDN_register: obj(%p)\n", obj); #ifndef SYSFS_SUPPORT_2_6_24 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); } #endif return(retval); } int mISDN_unregister(mISDNobject_t *obj) { u_long flags; if (!obj) return(-EINVAL); if (debug) printk(KERN_DEBUG "mISDN_unregister %s %d refs\n", obj->name, obj->refcnt); if (obj->DPROTO.protocol[0]) { if (debug) printk(KERN_DEBUG "mISDN_unregister stacks(%s)\n", obj->name); release_stacks(obj); } else 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); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) device_unregister(&obj->class_dev); #else class_device_unregister(&obj->class_dev); #endif return(0); } int mISDNInit(void) { struct semaphore sem; int err; init_MUTEX_LOCKED(&sem); printk(KERN_INFO "Modular ISDN Stack core version (%s) revision (%s)\n", mISDN_core_version, mISDN_core_revision); core_debug = debug; #ifdef MISDN_MEMDEBUG err = __mid_init(); if (err) return(err); #endif err = mISDN_sysfs_init(); if (err) goto sysfs_fail; err = init_mISDNdev(debug); if (err) goto dev_fail; #ifdef CONFIG_MISDN_NETDEV misdn_netdev_init(); #endif init_waitqueue_head(&mISDN_thread.waitq); skb_queue_head_init(&mISDN_thread.workq); mISDN_thread.notify = &sem; kernel_thread(mISDNd, (void *)&mISDN_thread, 0); down(&sem); mISDN_thread.notify = NULL; 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) { struct semaphore sem; init_MUTEX_LOCKED(&sem); free_mISDNdev(); if (!list_empty(&mISDN_objectlist)) { printk(KERN_WARNING "mISDNcore mISDN_objects not empty\n"); } check_stacklist(); if (!list_empty(&mISDN_modulelist)) printk(KERN_WARNING "mISDNcore mISDN_modulelist not empty\n"); if (mISDN_thread.thread) { /* abort mISDNd kernel thread */ mISDN_thread.notify = &sem; test_and_set_bit(mISDN_TFLAGS_RMMOD, &mISDN_thread.Flags); wake_up_interruptible(&mISDN_thread.waitq); down(&sem); mISDN_thread.notify = NULL; } #ifdef MISDN_MEMDEBUG __mid_cleanup(); #endif #ifdef CONFIG_MISDN_NETDEV misdn_netdev_exit(); #endif mISDN_sysfs_cleanup(); printk(KERN_DEBUG "mISDNcore unloaded\n"); } module_init(mISDNInit); module_exit(mISDN_cleanup); EXPORT_SYMBOL(mISDN_module_register); EXPORT_SYMBOL(mISDN_module_unregister); EXPORT_SYMBOL(mISDN_inc_usage); EXPORT_SYMBOL(mISDN_dec_usage); EXPORT_SYMBOL(mISDN_ctrl); EXPORT_SYMBOL(mISDN_register); EXPORT_SYMBOL(mISDN_unregister); EXPORT_SYMBOL(mISDN_dt_set_callback); EXPORT_SYMBOL(mISDN_dt_unset_callback); EXPORT_SYMBOL(mISDN_dt_enable); EXPORT_SYMBOL(mISDN_dt_disable); EXPORT_SYMBOL(mISDN_dt_new_frame); #ifdef CONFIG_MISDN_NETDEV EXPORT_SYMBOL(misdn_log_frame); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/core.h0000644000000000000500000000775711135651701017507 0ustar rootsrc/* $Id: core.h,v 1.19 2006/12/21 15:25:06 nadi Exp $ * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include #include #include "helper.h" #ifdef MISDN_MEMDEBUG #include "memdbg.h" #endif #define mISDN_MAJOR 46 #define mISDN_MINOR_CORE 0 #define mISDN_MINOR_RAW_MIN 128 #define mISDN_MINOR_RAW_MAX 255 /* debugging */ #define DEBUG_CORE_FUNC 0x0001 //#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 #define DEBUG_SYSFS 0x4000 #define DEBUG_THREADS 0x8000 /* 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 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 *, int); extern int evaluate_stack_pids(mISDNstack_t *, mISDN_pid_t *); extern mISDNinstance_t *getlayer4lay(mISDNstack_t *, int); extern mISDNinstance_t *get_instance(mISDNstack_t *, int, int); /* from sysfs_obj.c */ extern int mISDN_register_sysfs_obj(mISDNobject_t *); extern int mISDN_sysfs_init(void); extern void mISDN_sysfs_cleanup(void); /* 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); extern mISDNinstance_t *get_instance4id(u_int); extern int mISDN_alloc_entity(int *); extern int mISDN_delete_entity(int); extern void mISDN_module_register(struct module *); extern void mISDN_module_unregister(struct module *); extern void mISDN_inc_usage(void); extern void mISDN_dec_usage(void); /* debugtool helpers from core.c */ extern void mISDN_dt_set_callback(void (*new_frame) (mISDNstack_t *stack, enum mISDN_dt_type type, struct sk_buff *skb, int duplicate_skb)); extern void mISDN_dt_unset_callback(void); extern void mISDN_dt_enable(void); extern void mISDN_dt_disable(void); extern void mISDN_dt_new_frame(mISDNstack_t *stack, enum mISDN_dt_type type, struct sk_buff *skb, int duplicate_skb); #ifdef CONFIG_MISDN_NETDEV /* from netdev_main.c */ void misdn_log_frame(mISDNstack_t *, /* Stack for which to log */ unsigned char *, /* frame to log */ int, /* frame len */ int ); /* direction (0=rx,1=tx) */ int misdn_netdev_addstack(mISDNstack_t *); /* create new netdevice by stack */ int misdn_netdev_init(void); /* initialize netdevices */ void misdn_netdev_exit(void); /* exit netdeivces */ #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/debug.c0000644000000000000500000000330211135651701017616 0ustar rootsrc/* $Id: debug.c,v 1.7 2006/03/23 13:11:43 keil Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include #include #include "debug.h" #define mISDN_STATUS_BUFSIZE 4096 static char tmpbuf[mISDN_STATUS_BUFSIZE]; void vmISDN_debug(int id, char *head, char *fmt, va_list args) { /* if head == NULL the fmt contains the full info */ char *p = tmpbuf; p += sprintf(p,"%d ", id); if (head) p += sprintf(p, "%s ", head); p += vsprintf(p, fmt, args); printk(KERN_DEBUG "%s\n", tmpbuf); } void mISDN_debug(int id, char *head, char *fmt, ...) { va_list args; va_start(args, fmt); vmISDN_debug(id, head, fmt, args); va_end(args); } void mISDN_debugprint(mISDNinstance_t *inst, char *fmt, ...) { logdata_t log; va_start(log.args, fmt); log.head = inst->name; log.fmt = fmt; mISDN_ctrl(inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } char * mISDN_getrev(const char *revision) { char *rev; char *p; if ((p = strchr(revision, ':'))) { rev = p + 2; p = strchr(rev, '$'); if (p) *--p = 0; } else rev = "???"; return rev; } int mISDN_QuickHex(char *txt, u_char * p, int cnt) { register int i; register char *t = txt; register u_char w; for (i = 0; i < cnt; i++) { *t++ = ' '; w = (p[i] >> 4) & 0x0f; if (w < 10) *t++ = '0' + w; else *t++ = 'A' - 10 + w; w = p[i] & 0x0f; if (w < 10) *t++ = '0' + w; else *t++ = 'A' - 10 + w; } *t++ = 0; return (t - txt); } EXPORT_SYMBOL(vmISDN_debug); EXPORT_SYMBOL(mISDN_debug); EXPORT_SYMBOL(mISDN_getrev); EXPORT_SYMBOL(mISDN_debugprint); EXPORT_SYMBOL(mISDN_QuickHex); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/debug.h0000644000000000000500000000102111110524073017611 0ustar rootsrc/* $Id: debug.h,v 1.3 2006/03/06 12:52:07 keil Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * 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 mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/debugtool.c0000644000000000000500000001513611135651701020524 0ustar rootsrc/* * debugtool.c: Debug-Tool for mISDN * * Copyright (C) 2007, Nadi Sarrar * * Nadi Sarrar * * 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., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * */ #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include #define DT_VERSION 1 #define MODULE_NAME "mISDN_debugtool" #define ADDR INADDR_LOOPBACK static int PORT = 50501; static int dt_enabled = 0; static struct task_struct *thread; static struct sk_buff_head skb_q; static DECLARE_WAIT_QUEUE_HEAD(skb_q_wait); static void dt_new_frame (mISDNstack_t *stack, enum mISDN_dt_type type, struct sk_buff *skb, int duplicate_skb) { struct sk_buff *dup; mISDN_dt_header_t *hdr; dup = skb ? (duplicate_skb ? skb_copy(skb, GFP_ATOMIC) : skb) : alloc_skb(0, GFP_ATOMIC); if (!dup) return; hdr = (mISDN_dt_header_t *)dup->cb; memset(hdr, 0, sizeof(mISDN_dt_header_t)); hdr->version = DT_VERSION; hdr->type = type; hdr->stack_id = stack->id; hdr->stack_protocol = stack->pid.modparm_protocol; hdr->time = current_kernel_time(); hdr->plength = skb ? skb->len : 0; skb_queue_tail(&skb_q, dup); wake_up_interruptible(&skb_q_wait); } static inline int dt_send_buf (struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len) { struct msghdr msg; struct iovec iov; mm_segment_t oldfs; int size = 0; if (!sock->sk) return 0; iov.iov_base = buf; iov.iov_len = len; msg.msg_flags = 0; msg.msg_name = addr; msg.msg_namelen = sizeof(struct sockaddr_in); msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; oldfs = get_fs(); set_fs(KERNEL_DS); size = sock_sendmsg(sock, &msg, len); set_fs(oldfs); return size; } static inline void dt_send_skb (struct socket *sock, struct sockaddr_in *addr, const struct sk_buff *skb) { if (skb->len) { unsigned char buf[sizeof(mISDN_dt_header_t) + skb->len]; memcpy(buf, skb->cb, sizeof(mISDN_dt_header_t)); memcpy(buf + sizeof(mISDN_dt_header_t), skb->data, skb->len); dt_send_buf(sock, addr, buf, sizeof(buf)); } else dt_send_buf(sock, addr, (unsigned char *)skb->cb, sizeof(mISDN_dt_header_t)); } static void dt_run (void) { int ret; struct sk_buff *skb; struct socket *sock; struct sockaddr_in addr; lock_kernel(); current->flags |= PF_NOFREEZE; daemonize(MODULE_NAME); allow_signal(SIGKILL); unlock_kernel(); /* init socket */ ret = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); if (ret < 0) { printk(KERN_INFO "%s: sock_create failed! (%d)\n", __FUNCTION__, -ret); sock = NULL; return; } memset(&addr, 0, sizeof(struct sockaddr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(ADDR); addr.sin_port = htons(PORT); ret = sock->ops->connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr), 0); if (ret < 0) { printk(KERN_INFO "%s: Could not connect to socket (%d)\n", __FUNCTION__, -ret); sock_release(sock); sock = NULL; return; } printk(KERN_INFO MODULE_NAME ": Using destination port %d.\n", PORT); for (;;) { wait_event_interruptible(skb_q_wait, signal_pending(current) || !skb_queue_empty(&skb_q)); if (signal_pending(current)) break; skb = skb_dequeue(&skb_q); dt_send_skb(sock, &addr, skb); kfree_skb(skb); } printk(KERN_INFO MODULE_NAME ": Leaving thread successfully.\n"); sock_release(sock); } /* sysfs */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static void dt_class_release (struct device *dev) {} #else static void dt_class_release (struct class_device *dev) {} #endif static struct class dt_class = { .name = "mISDN-debugtool", #ifndef CLASS_WITHOUT_OWNER .owner = THIS_MODULE, #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) .dev_release = &dt_class_release, #else .release = &dt_class_release, #endif }; static ssize_t attr_show_enabled (struct class *class, char *buf) { return sprintf(buf, "%d\n", dt_enabled); } static ssize_t attr_store_enabled (struct class *class, const char *buf, size_t count) { if (count > 0 && *buf == '1') { mISDN_dt_enable(); dt_enabled = 1; } else { mISDN_dt_disable(); dt_enabled = 0; } return count; } static CLASS_ATTR(enabled, 0644, attr_show_enabled, attr_store_enabled); module_param(PORT, int, S_IRUGO); int __init dt_init(void) { /* init queue */ skb_queue_head_init(&skb_q); /* init sysfs */ if (class_register(&dt_class) || class_create_file(&dt_class, &class_attr_enabled)) { printk(KERN_INFO MODULE_NAME "%s: Failed to initialize sysfs!\n", __FUNCTION__); return -EPERM; } /* start thread */ thread = kthread_run((void *)dt_run, NULL, MODULE_NAME); if (IS_ERR(thread)) { printk(KERN_INFO MODULE_NAME "%s: Could not start kernel thread!\n", __FUNCTION__); class_unregister(&dt_class); return -ENOMEM; } /* register with mISDN */ mISDN_module_register(THIS_MODULE); mISDN_dt_set_callback(dt_new_frame); printk(KERN_INFO MODULE_NAME ": module loaded\n"); return 0; } void __exit dt_exit(void) { int ret; mISDN_dt_disable(); mISDN_dt_unset_callback(); mISDN_module_unregister(THIS_MODULE); if (thread) { lock_kernel(); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) ret = kill_pid(find_pid_ns(thread->pid, &init_pid_ns), SIGKILL, 1); #else ret = kill_proc(thread->pid, SIGKILL, 1); #endif unlock_kernel(); if (ret < 0) printk(KERN_INFO MODULE_NAME ": Unknown error (%d) while trying to terminate kernel thread!\n", -ret); wake_up_interruptible(&skb_q_wait); } class_unregister(&dt_class); skb_queue_purge(&skb_q); printk(KERN_INFO MODULE_NAME ": module unloaded\n"); } module_init(dt_init); module_exit(dt_exit); MODULE_DESCRIPTION("Debug-Tool for mISDN"); MODULE_AUTHOR("Nadi Sarrar "); MODULE_LICENSE("GPL"); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp.h0000644000000000000500000001724211135651701017333 0ustar rootsrc/* $Id: dsp.h,v 1.13 2007/03/27 15:06:29 jolly Exp $ * * Audio support data for ISDN4Linux. * * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ #define DEBUG_DSP_MGR 0x0001 #define DEBUG_DSP_CORE 0x0002 #define DEBUG_DSP_DTMF 0x0004 #define DEBUG_DSP_DTMFCOEFF 0x0008 #define DEBUG_DSP_CMX 0x0010 #define DEBUG_DSP_TONE 0x0020 #define DEBUG_DSP_BLOWFISH 0x0040 #define DEBUG_DSP_DELAY 0x0080 /* options may be: * * bit 0 = use ulaw instead of alaw * bit 1 = enable hfc hardware accelleration for all channels * */ #define DSP_OPT_ULAW (1<<0) #define DSP_OPT_NOHARDWARE (1<<1) #define FEAT_STATE_INIT 1 #define FEAT_STATE_WAIT 2 #define FEAT_STATE_RECEIVED 3 #include #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. */ //#define AGGRESSIVE_SUPPRESSOR //#include "dsp_mec2.h" //#include "dsp_kb1ec.h" #include "dsp_mg2ec.h" /* * uncomment this one to cancel echo more aggressive */ //#define AGGRESSIVE_SUPPRESSOR extern int dsp_options; extern int dsp_debug; extern int dsp_poll; extern int dsp_tics; /*************** * audio stuff * ***************/ extern s32 dsp_audio_alaw_to_s32[256]; extern s32 dsp_audio_ulaw_to_s32[256]; extern s32 *dsp_audio_law_to_s32; extern u8 dsp_audio_s16_to_law[65536]; extern u8 dsp_audio_alaw_to_ulaw[256]; extern u8 dsp_audio_mix_law[65536]; extern u8 dsp_audio_seven2law[128]; extern u8 dsp_audio_law2seven[256]; extern void dsp_audio_generate_law_tables(void); extern void dsp_audio_generate_s2law_table(void); 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 dsp_silence; /************* * cmx stuff * *************/ #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: * * each conference has a unique number, given by user space. * the conferences are linked in a chain. * each conference has members linked in a chain. * each dsplayer points to a member, each member points to a dsplayer. */ /* all members within a conference (this is linked 1:1 with the dsp) */ struct _dsp; typedef struct _conf_member { struct list_head list; struct _dsp *dsp; } conf_member_t; /* the list of all conferences */ typedef struct _conference { struct list_head list; u32 id; /* all cmx stacks with the same ID are connected */ struct list_head mlist; int software; /* conf is processed by software */ int hardware; /* conf is processed by hardware */ } conference_t; extern mISDNobject_t dsp_obj; /************** * DTMF stuff * **************/ #define DSP_DTMF_NPOINTS 102 #define ECHOCAN_BUFLEN 4*128 typedef struct _dtmf_t { int treshold; /* above this is dtmf (square of) */ int software; /* dtmf uses software decoding */ int hardware; /* dtmf uses hardware decoding */ int size; /* number of bytes in buffer */ signed short buffer[DSP_DTMF_NPOINTS]; /* buffers one full dtmf frame */ u8 lastwhat, lastdigit; int count; u8 digits[16]; /* just the dtmf result */ } dtmf_t; /**************** * cancel stuff * ****************/ /*************** * tones stuff * ***************/ typedef struct _tone_t { int software; /* tones are generated by software */ int hardware; /* tones are generated by hardware */ int tone; void *pattern; int count; int index; struct timer_list tl; } tone_t; /***************** * general stuff * *****************/ #define DELAY_CHECK 8000 struct dsp_features { int hfc_id; /* unique id to identify the chip (or -1) */ int hfc_dtmf; /* set if HFCmulti card supports dtmf */ int hfc_loops; /* set if card supports tone loops */ int hfc_echocanhw; /* set if card supports echocancelation*/ 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 { struct list_head list; mISDNinstance_t inst; int b_active; int echo; /* echo is done by software */ int rx_disabled; int tx_mix; tone_t tone; dtmf_t dtmf; int tx_volume, rx_volume; /* conference stuff */ u32 conf_id; conference_t *conf; conf_member_t *member; /* while we're waiting for the hw */ u32 queue_conf_id; /* buffer stuff */ 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]; /* hardware stuff */ struct dsp_features features; /* features */ struct timer_list feature_tl; spinlock_t feature_lock; int feature_state; int pcm_slot_rx; /* current PCM slot (or -1) */ int pcm_bank_rx; int pcm_slot_tx; int pcm_bank_tx; int hfc_conf; /* unique id of current conference (or -1) */ /* encryption stuff */ int bf_enable; u32 bf_p[18]; u32 bf_s[1024]; int bf_crypt_pos; u8 bf_data_in[9]; u8 bf_crypt_out[9]; int bf_decrypt_in_pos; int bf_decrypt_out_pos; u8 bf_crypt_inring[16]; u8 bf_data_out[9]; int bf_sync; /* echo cancellation stuff */ int queue_cancel[3]; int cancel_enable; int cancel_hardware; /*we are using hw echo canc*/ 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 */ extern void dsp_change_volume(struct sk_buff *skb, int volume); extern struct list_head Conf_list; 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); extern void dsp_dtmf_goertzel_init(dsp_t *dsp); extern u8 *dsp_dtmf_goertzel_decode(dsp_t *dsp, u8 *data, int len, int fmt); extern int dsp_tone(dsp_t *dsp, int tone); extern void dsp_tone_copy(dsp_t *dsp, u8 *data, int len); extern void dsp_tone_timeout(void *arg); extern void dsp_bf_encrypt(dsp_t *dsp, u8 *data, int len); 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); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_arith.h0000644000000000000500000001703311135651701020520 0ustar rootsrc#ifndef _ZAPTEL_ARITH_H #define _ZAPTEL_ARITH_H /* * Handy add/subtract functions to operate on chunks of shorts. * Feel free to add customizations for additional architectures * */ #ifdef CONFIG_ZAPTEL_MMX #ifdef ZT_CHUNKSIZE static inline void __ACSS(volatile short *dst, const short *src) { __asm__ __volatile__ ( "movq 0(%0), %%mm0;\n" "movq 0(%1), %%mm1;\n" "movq 8(%0), %%mm2;\n" "movq 8(%1), %%mm3;\n" "paddsw %%mm1, %%mm0;\n" "paddsw %%mm3, %%mm2;\n" "movq %%mm0, 0(%0);\n" "movq %%mm2, 8(%0);\n" : "=r" (dst) : "r" (src), "0" (dst) : "memory" #if CLOBBERMMX , "%mm0", "%mm1", "%mm2", "%mm3" #endif ); } static inline void __SCSS(volatile short *dst, const short *src) { __asm__ __volatile__ ( "movq 0(%0), %%mm0;\n" "movq 0(%1), %%mm1;\n" "movq 8(%0), %%mm2;\n" "movq 8(%1), %%mm3;\n" "psubsw %%mm1, %%mm0;\n" "psubsw %%mm3, %%mm2;\n" "movq %%mm0, 0(%0);\n" "movq %%mm2, 8(%0);\n" : "=r" (dst) : "r" (src), "0" (dst) : "memory" #if CLOBBERMMX , "%mm0", "%mm1", "%mm2", "%mm3" #endif ); } #if (ZT_CHUNKSIZE == 8) #define ACSS(a,b) __ACSS(a,b) #define SCSS(a,b) __SCSS(a,b) #elif (ZT_CHUNKSIZE > 8) static inline void ACSS(volatile short *dst, const short *src) { int x; for (x=0;x>= 4; /* Clear our accumulator, mm4 */ /* For every set of eight... Load 16 coefficients into four registers... Shift each word right 16 to make them shorts... Pack the resulting shorts into two registers... With the coefficients now in mm0 and mm2, load the history into mm1 and mm3... Multiply/add mm1 into mm0, and mm3 into mm2... Add mm2 into mm0 (without saturation, alas). Now we have two half-results. Accumulate in mm4 (again, without saturation, alas) */ __asm__ ( "pxor %%mm4, %%mm4;\n" "mov %1, %%edi;\n" "mov %2, %%esi;\n" "mov %3, %%ecx;\n" "1:" "movq 0(%%edi), %%mm0;\n" "movq 8(%%edi), %%mm1;\n" "movq 16(%%edi), %%mm2;\n" "movq 24(%%edi), %%mm3;\n" /* can't use 4/5 since 4 is the accumulator for us */ "movq 32(%%edi), %%mm6;\n" "movq 40(%%edi), %%mm7;\n" "psrad $16, %%mm0;\n" "psrad $16, %%mm1;\n" "psrad $16, %%mm2;\n" "psrad $16, %%mm3;\n" "psrad $16, %%mm6;\n" "psrad $16, %%mm7;\n" "packssdw %%mm1, %%mm0;\n" "packssdw %%mm3, %%mm2;\n" "packssdw %%mm7, %%mm6;\n" "movq 0(%%esi), %%mm1;\n" "movq 8(%%esi), %%mm3;\n" "movq 16(%%esi), %%mm7;\n" "pmaddwd %%mm1, %%mm0;\n" "pmaddwd %%mm3, %%mm2;\n" "pmaddwd %%mm7, %%mm6;\n" "paddd %%mm6, %%mm4;\n" "paddd %%mm2, %%mm4;\n" "paddd %%mm0, %%mm4;\n" /* Come back and do for the last few bytes */ "movq 48(%%edi), %%mm6;\n" "movq 56(%%edi), %%mm7;\n" "psrad $16, %%mm6;\n" "psrad $16, %%mm7;\n" "packssdw %%mm7, %%mm6;\n" "movq 24(%%esi), %%mm7;\n" "pmaddwd %%mm7, %%mm6;\n" "paddd %%mm6, %%mm4;\n" "add $64, %%edi;\n" "add $32, %%esi;\n" "dec %%ecx;\n" "jnz 1b;\n" "movq %%mm4, %%mm0;\n" "psrlq $32, %%mm0;\n" "paddd %%mm0, %%mm4;\n" "movd %%mm4, %0;\n" : "=r" (sum) : "r" (coeffs), "r" (hist), "r" (len) : "%ecx", "%edi", "%esi" ); return sum; } static inline void UPDATE(volatile int *taps, const short *history, const int nsuppr, const int ntaps) { int i; int correction; for (i=0;i>= 4; /* First, load up taps, */ __asm__ ( "pxor %%mm4, %%mm4;\n" "mov %0, %%edi;\n" "mov %1, %%esi;\n" "mov %3, %%ecx;\n" "1:" "jnz 1b;\n" "movq %%mm4, %%mm0;\n" "psrlq $32, %%mm0;\n" "paddd %%mm0, %%mm4;\n" "movd %%mm4, %0;\n" : "=r" (taps), "=r" (taps_short) : "r" (history), "r" (nsuppr), "r" (ntaps), "0" (taps) : "%ecx", "%edi", "%esi" ); #endif #if 1 for (i=0;i> 16; } #endif } static inline int CONVOLVE2(const short *coeffs, const short *hist, int len) { int sum; /* Divide length by 16 */ len >>= 4; /* Clear our accumulator, mm4 */ /* For every set of eight... Load in eight coefficients and eight historic samples, multliply add and accumulate the result */ __asm__ ( "pxor %%mm4, %%mm4;\n" "mov %1, %%edi;\n" "mov %2, %%esi;\n" "mov %3, %%ecx;\n" "1:" "movq 0(%%edi), %%mm0;\n" "movq 8(%%edi), %%mm2;\n" "movq 0(%%esi), %%mm1;\n" "movq 8(%%esi), %%mm3;\n" "pmaddwd %%mm1, %%mm0;\n" "pmaddwd %%mm3, %%mm2;\n" "paddd %%mm2, %%mm4;\n" "paddd %%mm0, %%mm4;\n" "movq 16(%%edi), %%mm0;\n" "movq 24(%%edi), %%mm2;\n" "movq 16(%%esi), %%mm1;\n" "movq 24(%%esi), %%mm3;\n" "pmaddwd %%mm1, %%mm0;\n" "pmaddwd %%mm3, %%mm2;\n" "paddd %%mm2, %%mm4;\n" "paddd %%mm0, %%mm4;\n" "add $32, %%edi;\n" "add $32, %%esi;\n" "dec %%ecx;\n" "jnz 1b;\n" "movq %%mm4, %%mm0;\n" "psrlq $32, %%mm0;\n" "paddd %%mm0, %%mm4;\n" "movd %%mm4, %0;\n" : "=r" (sum) : "r" (coeffs), "r" (hist), "r" (len) : "%ecx", "%edi", "%esi" ); return sum; } static inline short MAX16(const short *y, int len, int *pos) { int k; short max = 0; int bestpos = 0; for (k=0;k 32767) sum = 32767; else if (sum < -32768) sum = -32768; dst[x] = sum; } } static inline void SCSS(short *dst, short *src) { int x,sum; /* Add src to dst with saturation, storing in dst */ for (x=0;x 32767) sum = 32767; else if (sum < -32768) sum = -32768; dst[x] = sum; } } #endif /* ZT_CHUNKSIZE */ static inline int CONVOLVE(const int *coeffs, const short *hist, int len) { int x; int sum = 0; for (x=0;x> 16) * hist[x]; return sum; } static inline int CONVOLVE2(const short *coeffs, const short *hist, int len) { int x; int sum = 0; for (x=0;x> 16; } } static inline short MAX16(const short *y, int len, int *pos) { int k; short max = 0; int bestpos = 0; for (k=0;k signed 16-bit */ s32 dsp_audio_ulaw_to_s32[256]; /* alaw[unsigned char] -> signed 16-bit */ s32 dsp_audio_alaw_to_s32[256]; s32 *dsp_audio_law_to_s32; /* signed 16-bit -> law */ u8 dsp_audio_s16_to_law[65536]; /* alaw -> ulaw */ u8 dsp_audio_alaw_to_ulaw[256]; /* ulaw -> alaw */ u8 dsp_audio_ulaw_to_alaw[256]; u8 dsp_silence; /***************************************************** * generate table for conversion of s16 to alaw/ulaw * *****************************************************/ #define AMI_MASK 0x55 static inline unsigned char linear2alaw (short int linear) { int mask; int seg; int pcm_val; static int seg_end[8] = { 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF }; pcm_val = linear; if (pcm_val >= 0) { /* Sign (7th) bit = 1 */ mask = AMI_MASK | 0x80; } else { /* Sign bit = 0 */ mask = AMI_MASK; pcm_val = -pcm_val; } /* Convert the scaled magnitude to segment number. */ for (seg = 0; seg < 8; seg++) { if (pcm_val <= seg_end[seg]) break; } /* Combine the sign, segment, and quantization bits. */ return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; } static inline short int alaw2linear (unsigned char alaw) { int i; int seg; alaw ^= AMI_MASK; i = ((alaw & 0x0F) << 4) + 8 /* rounding error */; seg = (((int) alaw & 0x70) >> 4); if (seg) i = (i + 0x100) << (seg - 1); return (short int) ((alaw & 0x80) ? i : -i); } static inline short int ulaw2linear(unsigned char ulaw) { short mu, e, f, y; static short etab[] = {0,132,396,924,1980,4092,8316,16764}; mu = 255 - ulaw; e = (mu & 0x70) / 16; f = mu & 0x0f; y = f * (1 << (e + 3)); y += etab[e]; if (mu & 0x80) y = -y; return y; } #define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */ static unsigned char linear2ulaw(short sample) { static int exp_lut[256] = { 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }; int sign, exponent, mantissa; unsigned char ulawbyte; /* Get the sample into sign-magnitude. */ sign = (sample >> 8) & 0x80; /* set aside the sign */ if (sign != 0) sample = -sample; /* get magnitude */ /* Convert from 16 bit linear to ulaw. */ sample = sample + BIAS; exponent = exp_lut[(sample >> 7) & 0xFF]; mantissa = (sample >> (exponent + 3)) & 0x0F; ulawbyte = ~(sign | (exponent << 4) | mantissa); return ulawbyte; } static int reverse_bits(int i) { int z,j; z = 0; for (j = 0; j < 8; j++) { if ((i & (1 << j)) != 0) { z |= 1 << (7 - j); } } return z; } void dsp_audio_generate_law_tables(void) { int i; for (i = 0; i < 256; i++) { dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i)); } for (i = 0; i < 256; i++) { dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i)); } for (i = 0; i < 256; i++) { dsp_audio_alaw_to_ulaw[i] = linear2ulaw(dsp_audio_alaw_to_s32[i]); dsp_audio_ulaw_to_alaw[i] = linear2alaw(dsp_audio_ulaw_to_s32[i]); } } void dsp_audio_generate_s2law_table(void) { int i; if (dsp_options & DSP_OPT_ULAW) { /* generating ulaw-table */ for (i = -32768; i < 32768; i++) { dsp_audio_s16_to_law[i & 0xffff] = reverse_bits(linear2ulaw(i)); } } else { /* generating alaw-table */ for (i = -32768; i < 32768; i++) { dsp_audio_s16_to_law[i & 0xffff] = reverse_bits(linear2alaw(i)); } } } /* * the seven bit sample is the number of every second alaw-sample ordered by * aplitude. 0x00 is negative, 0x7f is positive amplitude. */ u8 dsp_audio_seven2law[128]; u8 dsp_audio_law2seven[256]; /******************************************************************** * generate table for conversion law from/to 7-bit alaw-like sample * ********************************************************************/ void dsp_audio_generate_seven(void) { int i, j, k; u8 spl; u8 sorted_alaw[256]; /* generate alaw table, sorted by the linear value */ for (i = 0; i < 256; i++) { j = 0; for (k = 0; k < 256; k++) { if (dsp_audio_alaw_to_s32[k] < dsp_audio_alaw_to_s32[i]) { j++; } } sorted_alaw[j] = i; } /* generate tabels */ for (i = 0; i < 256; i++) { /* spl is the source: the law-sample (converted to alaw) */ spl = i; if (dsp_options & DSP_OPT_ULAW) spl = dsp_audio_ulaw_to_alaw[i]; /* find the 7-bit-sample */ for (j = 0; j < 256; j++) { if (sorted_alaw[j] == spl) break; } /* write 7-bit audio value */ dsp_audio_law2seven[i] = j >> 1; } for (i = 0; i < 128; i++) { spl = sorted_alaw[i << 1]; if (dsp_options & DSP_OPT_ULAW) spl = dsp_audio_alaw_to_ulaw[spl]; dsp_audio_seven2law[i] = spl; } } /* mix 2*law -> law */ u8 dsp_audio_mix_law[65536]; /****************************************************** * generate mix table to mix two law samples into one * ******************************************************/ void dsp_audio_generate_mix_table(void) { int i, j; s32 sample; i = 0; while(i < 256) { j = 0; while(j < 256) { sample = dsp_audio_law_to_s32[i]; sample += dsp_audio_law_to_s32[j]; if (sample > 32767) sample = 32767; if (sample < -32768) sample = -32768; dsp_audio_mix_law[(i<<8)|j] = dsp_audio_s16_to_law[sample & 0xffff]; j++; } i++; } } /************************************* * generate different volume changes * *************************************/ static u8 dsp_audio_reduce8[256]; static u8 dsp_audio_reduce7[256]; static u8 dsp_audio_reduce6[256]; static u8 dsp_audio_reduce5[256]; static u8 dsp_audio_reduce4[256]; static u8 dsp_audio_reduce3[256]; static u8 dsp_audio_reduce2[256]; static u8 dsp_audio_reduce1[256]; static u8 dsp_audio_increase1[256]; static u8 dsp_audio_increase2[256]; static u8 dsp_audio_increase3[256]; static u8 dsp_audio_increase4[256]; static u8 dsp_audio_increase5[256]; static u8 dsp_audio_increase6[256]; static u8 dsp_audio_increase7[256]; static u8 dsp_audio_increase8[256]; static u8 *dsp_audio_volume_change[16] = { dsp_audio_reduce8, dsp_audio_reduce7, dsp_audio_reduce6, dsp_audio_reduce5, dsp_audio_reduce4, dsp_audio_reduce3, dsp_audio_reduce2, dsp_audio_reduce1, dsp_audio_increase1, dsp_audio_increase2, dsp_audio_increase3, dsp_audio_increase4, dsp_audio_increase5, dsp_audio_increase6, dsp_audio_increase7, dsp_audio_increase8, }; void dsp_audio_generate_volume_changes(void) { register s32 sample; int i; int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 }; int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; i = 0; while(i < 256) { dsp_audio_reduce8[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[7] / num[7] ) & 0xffff]; dsp_audio_reduce7[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff]; dsp_audio_reduce6[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff]; dsp_audio_reduce5[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff]; dsp_audio_reduce4[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff]; dsp_audio_reduce3[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff]; dsp_audio_reduce2[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff]; dsp_audio_reduce1[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff]; sample = dsp_audio_law_to_s32[i] * num[0] / denum[0]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; sample = dsp_audio_law_to_s32[i] * num[1] / denum[1]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; sample = dsp_audio_law_to_s32[i] * num[2] / denum[2]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; sample = dsp_audio_law_to_s32[i] * num[3] / denum[3]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; sample = dsp_audio_law_to_s32[i] * num[4] / denum[4]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; sample = dsp_audio_law_to_s32[i] * num[5] / denum[5]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; sample = dsp_audio_law_to_s32[i] * num[6] / denum[6]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; sample = dsp_audio_law_to_s32[i] * num[7] / denum[7]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff]; i++; } } /************************************** * change the volume of the given skb * **************************************/ /* this is a helper function for changing volume of skb. the range may be * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8 */ void dsp_change_volume(struct sk_buff *skb, int volume) { u8 *volume_change; int i, ii; u8 *p; int shift; if (volume == 0) return; /* get correct conversion table */ if (volume < 0) { shift = volume + 8; if (shift < 0) shift = 0; } else { shift = volume + 7; if (shift > 15) shift = 15; } volume_change = dsp_audio_volume_change[shift]; i = 0; ii = skb->len; p = skb->data; /* change volume */ while(i < ii) { *p = volume_change[*p]; p++; i++; } } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_biquad.h0000644000000000000500000000377611135651701020667 0ustar rootsrc/* * 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 * * 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 ------------------------------------------------------------*/ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_blowfish.c0000644000000000000500000005722511135651701021230 0ustar rootsrc/* $Id: dsp_blowfish.c,v 1.5 2007/03/27 15:06:29 jolly Exp $ * * Blowfish encryption/decryption for mISDN_dsp. * * Copyright Andreas Eversberg (jolly@eversberg.eu) * * 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" /* * how to encode a sample stream to 64-bit blocks that will be encryped * * first of all, data is collected until a block of 9 samples are received. * of course, a packet may have much more than 9 sample, but is may have * not excacly the multiple of 9 samples. if there is a rest, the next * received data will complete the block. * * the block is then converted to 9 uLAW samples without the least sigificant * bit. the result is a 7-bit encoded sample. * * the samples will be reoganised to form 8 bytes of data: * (5(6) means: encoded sample no. 5, bit 6) * * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6) * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5) * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4) * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3) * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2) * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0) * * the missing bit 0 of the last byte is filled with some * random noise, to fill all 8 bytes. * * the 8 bytes will be encrypted using blowfish. * * the result will be converted into 9 bytes. the bit 7 is used for * checksumme (CS) for sync (0, 1) and for the last bit: * (5(6) means: crypted byte 5, bit 6) * * 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) * 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2) * 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3) * 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4) * 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5) * CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6) * CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7) * CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0) * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) * * the checksum is used to detect transmission errors and frame drops. * * synchronisation of received block is done by shifting the upper bit of each * byte (bit 7) to a shift register. if the rigister has the first five bits * (10000), this is used to find the sync. only if sync has been found, the * current block of 9 received bytes are decrypted. before that the check * sum is calculated. if it is incorrect the block is dropped. * this will avoid loud noise due to corrupt encrypted data. * * if the last block is corrupt, the current decoded block is repeated * until a valid block has been received. */ /* some blowfish parts are taken from the crypto-api for faster implementation */ struct bf_ctx { u32 p[18]; u32 s[1024]; }; static const u32 bf_pbox[16 + 2] = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, }; static const u32 bf_sbox[256 * 4] = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, }; /* * Round loop unrolling macros, S is a pointer to a S-Box array * organized in 4 unsigned longs at a row. */ #define GET32_3(x) (((x) & 0xff)) #define GET32_2(x) (((x) >> (8)) & (0xff)) #define GET32_1(x) (((x) >> (16)) & (0xff)) #define GET32_0(x) (((x) >> (24)) & (0xff)) #define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \ S[512 + GET32_2(x)]) + S[768 + GET32_3(x)]) #define EROUND(a, b, n) b ^= P[n]; a ^= bf_F (b) #define DROUND(a, b, n) a ^= bf_F (b); b ^= P[n] /* * encrypt isdn data frame * every block with 9 samples is encrypted */ void dsp_bf_encrypt(dsp_t *dsp, u8 *data, int len) { int i = 0, j = dsp->bf_crypt_pos; u8 *bf_data_in = dsp->bf_data_in; u8 *bf_crypt_out = dsp->bf_crypt_out; u32 *P = dsp->bf_p; u32 *S = dsp->bf_s; u32 yl, yr; u32 cs; u8 nibble; while(i < len) { /* collect a block of 9 samples */ if (j < 9) { bf_data_in[j] = *data; *data++ = bf_crypt_out[j++]; i++; continue; } j = 0; /* transcode 9 samples xlaw to 8 bytes */ yl = dsp_audio_law2seven[bf_data_in[0]]; yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]]; yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]]; yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]]; yr = nibble = dsp_audio_law2seven[bf_data_in[4]]; yl = (yl<<4) | (nibble>>3); yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]]; yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]]; yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]]; yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]]; yr = (yr<<1) | (bf_data_in[0] & 1); /* fill unused bit with random noise of audio input */ /* encrypt */ EROUND(yr, yl, 0); EROUND(yl, yr, 1); EROUND(yr, yl, 2); EROUND(yl, yr, 3); EROUND(yr, yl, 4); EROUND(yl, yr, 5); EROUND(yr, yl, 6); EROUND(yl, yr, 7); EROUND(yr, yl, 8); EROUND(yl, yr, 9); EROUND(yr, yl, 10); EROUND(yl, yr, 11); EROUND(yr, yl, 12); EROUND(yl, yr, 13); EROUND(yr, yl, 14); EROUND(yl, yr, 15); yl ^= P[16]; yr ^= P[17]; /* calculate 3-bit checksumme */ cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) ^ (yr>>28) ^ (yr>>31); /* transcode 8 crypted bytes to 9 data bytes with sync * and checksum information */ bf_crypt_out[0] = (yl>>25) | 0x80; bf_crypt_out[1] = (yl>>18) & 0x7f; bf_crypt_out[2] = (yl>>11) & 0x7f; bf_crypt_out[3] = (yl>>4) & 0x7f; bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07); bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80); bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80); bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7); bf_crypt_out[8] = yr; } /* write current count */ dsp->bf_crypt_pos = j; } /* * decrypt isdn data frame * every block with 9 bytes is decrypted */ void dsp_bf_decrypt(dsp_t *dsp, u8 *data, int len) { int i = 0; u8 j = dsp->bf_decrypt_in_pos; u8 k = dsp->bf_decrypt_out_pos; u8 *bf_crypt_inring = dsp->bf_crypt_inring; u8 *bf_data_out = dsp->bf_data_out; u16 sync = dsp->bf_sync; u32 *P = dsp->bf_p; u32 *S = dsp->bf_s; u32 yl, yr; u8 nibble; u8 cs, cs0,cs1,cs2; while(i < len) { /* shift upper bit and rotate data to buffer ring * send current decrypted data */ sync = (sync<<1) | ((*data)>>7); bf_crypt_inring[j++ & 15] = *data; *data++ = bf_data_out[k++]; i++; if (k == 9) k = 0; /* repeat if no sync has been found */ /* check if not in sync */ if ((sync&0x1f0) != 0x100) continue; j -= 9; /* transcode receive data to 64 bit block of encrypted data */ yl = bf_crypt_inring[j++ & 15]; yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ yr = nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ yl = (yl<<4) | (nibble>>3); cs2 = bf_crypt_inring[j++ & 15]; yr = (yr<<7) | (cs2 & 0x7f); cs1 = bf_crypt_inring[j++ & 15]; yr = (yr<<7) | (cs1 & 0x7f); cs0 = bf_crypt_inring[j++ & 15]; yr = (yr<<7) | (cs0 & 0x7f); yr = (yr<<8) | bf_crypt_inring[j++ & 15]; /* calculate 3-bit checksumme */ cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) ^ (yr>>28) ^ (yr>>31); /* check if frame is valid */ if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) { if (dsp_debug & DEBUG_DSP_BLOWFISH) printk(KERN_DEBUG "DSP BLOWFISH: received corrupt frame, checksumme is not correct\n"); continue; } /* decrypt */ yr ^= P[17]; yl ^= P[16]; DROUND(yl, yr, 15); DROUND(yr, yl, 14); DROUND(yl, yr, 13); DROUND(yr, yl, 12); DROUND(yl, yr, 11); DROUND(yr, yl, 10); DROUND(yl, yr, 9); DROUND(yr, yl, 8); DROUND(yl, yr, 7); DROUND(yr, yl, 6); DROUND(yl, yr, 5); DROUND(yr, yl, 4); DROUND(yl, yr, 3); DROUND(yr, yl, 2); DROUND(yl, yr, 1); DROUND(yr, yl, 0); /* transcode 8 crypted bytes to 9 sample bytes */ bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f]; bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f]; bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f]; bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f]; bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) | ((yr>>29) & 0x07)]; bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f]; bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f]; bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f]; bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f]; k = 0; /* start with new decoded frame */ } /* write current count and sync */ dsp->bf_decrypt_in_pos = j; dsp->bf_decrypt_out_pos = k; dsp->bf_sync = sync; } /* used to encrypt S and P boxes */ static inline void encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src) { u32 yl = src[0]; u32 yr = src[1]; EROUND(yr, yl, 0); EROUND(yl, yr, 1); EROUND(yr, yl, 2); EROUND(yl, yr, 3); EROUND(yr, yl, 4); EROUND(yl, yr, 5); EROUND(yr, yl, 6); EROUND(yl, yr, 7); EROUND(yr, yl, 8); EROUND(yl, yr, 9); EROUND(yr, yl, 10); EROUND(yl, yr, 11); EROUND(yr, yl, 12); EROUND(yl, yr, 13); EROUND(yr, yl, 14); EROUND(yl, yr, 15); yl ^= P[16]; yr ^= P[17]; dst[0] = yr; dst[1] = yl; } /* * initialize the dsp for encryption and decryption using the same key * Calculates the blowfish S and P boxes for encryption and decryption. * The margin of keylen must be 4-56 bytes. * returns 0 if ok. */ int dsp_bf_init(dsp_t *dsp, const u8 *key, uint keylen) { short i, j, count; u32 data[2], temp; u32 *P = (u32 *)dsp->bf_p; u32 *S = (u32 *)dsp->bf_s; if (keylen<4 || keylen>56) return(1); /* Set dsp states */ i = 0; while(i < 9) { dsp->bf_crypt_out[i] = 0xff; dsp->bf_data_out[i] = dsp_silence; i++; } dsp->bf_crypt_pos = 0; dsp->bf_decrypt_in_pos = 0; dsp->bf_decrypt_out_pos = 0; dsp->bf_sync = 0x1ff; dsp->bf_enable = 1; /* Copy the initialization s-boxes */ for (i = 0, count = 0; i < 256; i++) for (j = 0; j < 4; j++, count++) S[count] = bf_sbox[count]; /* Set the p-boxes */ for (i = 0; i < 16 + 2; i++) P[i] = bf_pbox[i]; /* Actual subkey generation */ for (j = 0, i = 0; i < 16 + 2; i++) { temp = (((u32 )key[j] << 24) | ((u32 )key[(j + 1) % keylen] << 16) | ((u32 )key[(j + 2) % keylen] << 8) | ((u32 )key[(j + 3) % keylen])); P[i] = P[i] ^ temp; j = (j + 4) % keylen; } data[0] = 0x00000000; data[1] = 0x00000000; for (i = 0; i < 16 + 2; i += 2) { encrypt_block(P, S, data, data); P[i] = data[0]; P[i + 1] = data[1]; } for (i = 0; i < 4; i++) { for (j = 0, count = i * 256; j < 256; j += 2, count += 2) { encrypt_block(P, S, data, data); S[count] = data[0]; S[count + 1] = data[1]; } } return(0); } /* turn encryption off */ void dsp_bf_cleanup(dsp_t *dsp) { dsp->bf_enable = 0; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_cmx.c0000644000000000000500000012305311135651702020174 0ustar rootsrc/* $Id: dsp_cmx.c,v 1.15 2007/03/27 15:06:29 jolly Exp $ * * Audio crossconnecting/conferrencing (hardware level). * * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ /* * The process of adding and removing parties to/from a conference: * * There is a chain of conference_t which has one or more members in a chain * of conf_member_t. * * After a party is added, the conference is checked for hardware capability. * Also if a party is removed, the conference is checked again. * * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect * 1-n = hardware-conference. The n will give the conference number. * * Depending on the change after removal or insertion of a party, hardware * commands are given. * * The current solution is stored within the conference_t entry. */ /* HOW THE CMX WORKS: * * There are 3 types of interaction: One member is alone, in this case only * 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. * * Features of CMX are: * - Crossconnecting or even conference, if more than two members are together. * - 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 2 buffers: * * * RX-Buffer * R W * | | * ----------------+-------------+------------------- * * The rx-buffer is a ring buffer used to store the received data for each * 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 * R W * | | * -----------------+--------+----------------------- * * The tx-buffer is a ring buffer to queue the transmit data from user space * until it will be mixed or sent. There are two pointers, R and W. If the write * pointer W would reach or overrun R, the buffer would overrun. In this case * (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 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. * The procedure of transmit data to card is cmx_send. * * * Interaction with other features: * * DTMF: * DTMF decoding is done before the data is crossconnected. * * Volume change: * Changing rx-volume is done before the data is crossconnected. The tx-volume * must be changed whenever data is transmitted to the card by the cmx. * * Tones: * If a tone is enabled, it will be processed whenever data is transmitted to * the card. It will replace the tx-data from the user space. * If tones are generated by hardware, this conference member is removed for * this time. * * Disable rx-data: * If cmx is realized in hardware, rx data will be disabled if requested by * the upper layer. If dtmf decoding is done by software and enabled, rx data * will not be diabled but blocked to the upper layer. * * HFC conference engine: * If it is possible to realize all features using hardware, hardware will be * used if not forbidden by control command. Disabling rx-data provides * absolutely traffic free audio processing. (except for the quick 1-frame * upload of a tone loop, only once for a new tone) * */ // delay.h is required for hw_lock.h #include #include #include "layer1.h" #include "helper.h" #include "debug.h" #include "dsp.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 */ LIST_HEAD(Conf_list); /* * debug cmx memory structure */ void dsp_cmx_debug(dsp_t *dsp) { 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) { 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); if (dsp == odsp) printk(" *this*"); printk("\n"); } printk(KERN_DEBUG "-----Current Conf:\n"); 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"); } /* * search conference */ static conference_t *dsp_cmx_search_conf(u32 id) { conference_t *conf; if (!id) { printk(KERN_WARNING "%s: conference ID is 0.\n", __FUNCTION__); return(NULL); } /* search conference */ list_for_each_entry(conf, &Conf_list, list) if (conf->id == id) return(conf); return(NULL); } /* * add member to conference */ static int dsp_cmx_add_conf_member(dsp_t *dsp, conference_t *conf) { conf_member_t *member; if (!conf || !dsp) { printk(KERN_WARNING "%s: conf or dsp is 0.\n", __FUNCTION__); return(-EINVAL); } if (dsp->member) { printk(KERN_WARNING "%s: dsp is already member in a conf.\n", __FUNCTION__); return(-EINVAL); } if (dsp->conf) { printk(KERN_WARNING "%s: dsp is already in a conf.\n", __FUNCTION__); return(-EINVAL); } if (!(member = kmalloc(sizeof(conf_member_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc conf_member_t failed\n"); return(-ENOMEM); } memset(member, 0, sizeof(conf_member_t)); member->dsp = dsp; /* 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); dsp->conf = conf; dsp->member = member; return(0); } /* * del member from conference */ int dsp_cmx_del_conf_member(dsp_t *dsp) { conf_member_t *member; if (!dsp) { printk(KERN_WARNING "%s: dsp is 0.\n", __FUNCTION__); return(-EINVAL); } if (!dsp->conf) { printk(KERN_WARNING "%s: dsp is not in a conf.\n", __FUNCTION__); return(-EINVAL); } if (list_empty(&dsp->conf->mlist)) { printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", __FUNCTION__); return(-EINVAL); } /* find us in conf */ list_for_each_entry(member, &dsp->conf->mlist, list) { if (member->dsp == dsp) { list_del(&member->list); dsp->conf = NULL; dsp->member = NULL; kfree(member); return(0); } } printk(KERN_WARNING "%s: dsp is not present in its own conf_meber list.\n", __FUNCTION__); return(-EINVAL); } /* * new conference */ static conference_t *dsp_cmx_new_conf(u32 id) { conference_t *conf; if (!id) { printk(KERN_WARNING "%s: id is 0.\n", __FUNCTION__); return(NULL); } if (!(conf = kmalloc(sizeof(conference_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc conference_t failed\n"); return(NULL); } memset(conf, 0, sizeof(conference_t)); INIT_LIST_HEAD(&conf->mlist); conf->id = id; list_add_tail(&conf->list, &Conf_list); return(conf); } /* * del conference */ int dsp_cmx_del_conf(conference_t *conf) { if (!conf) { printk(KERN_WARNING "%s: conf is null.\n", __FUNCTION__); return(-EINVAL); } if (!list_empty(&conf->mlist)) { printk(KERN_WARNING "%s: conf not empty.\n", __FUNCTION__); return(-EINVAL); } list_del(&conf->list); kfree(conf); return(0); } /* * send HW message to hfc card */ static void dsp_cmx_hw_message(dsp_t *dsp, u32 message, u32 param1, u32 param2, u32 param3, u32 param4) { struct sk_buff *nskb; u32 param[4]; param[0] = param1; param[1] = param2; param[2] = param3; param[3] = param4; nskb = create_link_skb(PH_CONTROL | REQUEST, message, sizeof(param), param, 0); if (!nskb) { printk(KERN_ERR "%s: No mem for skb.\n", __FUNCTION__); return; } /* unlocking is not required, because we don't expect a response */ if (mISDN_queue_down(&dsp->inst, 0, nskb)) dev_kfree_skb(nskb); } /* * do hardware update and set the software/hardware flag * * either a conference or a dsp instance can be given * if only dsp instance is given, the instance is not associated with a conf * and therefore removed. if a conference is given, the dsp is expected to * be member of that conference. */ 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; /* dsp gets updated (no conf) */ //printk("-----1\n"); if (!conf) { //printk("-----2\n"); if (!dsp) return; //printk("-----3\n"); if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s checking dsp %s\n", __FUNCTION__, dsp->inst.name); //printk("-----a\n"); one_member: /* remove HFC conference if enabled */ if (dsp->hfc_conf >= 0) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s removing %s from HFC conf %d because dsp is split\n", __FUNCTION__, dsp->inst.name, dsp->hfc_conf); dsp_cmx_hw_message(dsp, HW_CONF_SPLIT, 0, 0, 0, 0); dsp->hfc_conf = -1; } if (!dsp->echo) { /* NO ECHO: remove PCM slot if assigned */ if (dsp->pcm_slot_tx>=0 || dsp->pcm_slot_rx>=0) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s removing %s from PCM slot %d (TX) %d (RX) because dsp is split (no echo)\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx, dsp->pcm_slot_rx); dsp_cmx_hw_message(dsp, HW_PCM_DISC, 0, 0, 0, 0); dsp->pcm_slot_tx = -1; dsp->pcm_bank_tx = -1; dsp->pcm_slot_rx = -1; dsp->pcm_bank_rx = -1; } return; } /* ECHO: already echo */ if (dsp->pcm_slot_tx>=0 && dsp->pcm_slot_rx<0 && dsp->pcm_bank_tx==2 && dsp->pcm_bank_rx==2) return; /* ECHO: if slot already assigned */ if (dsp->pcm_slot_tx>=0) { dsp->pcm_slot_rx = dsp->pcm_slot_tx; dsp->pcm_bank_tx = 2; /* loop */ dsp->pcm_bank_rx = 2; if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s refresh %s for echo using slot %d\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx); dsp_cmx_hw_message(dsp, HW_PCM_CONN, dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); return; } /* ECHO: find slot */ dsp->pcm_slot_tx = -1; dsp->pcm_slot_rx = -1; memset(freeslots, 1, sizeof(freeslots)); list_for_each_entry(finddsp, &dsp_obj.ilist, list) { if (finddsp->features.pcm_id==dsp->features.pcm_id) { if (finddsp->pcm_slot_rx>=0 && finddsp->pcm_slot_rxpcm_slot_tx] = 0; if (finddsp->pcm_slot_tx>=0 && finddsp->pcm_slot_txpcm_slot_rx] = 0; } } i = 0; ii = dsp->features.pcm_slots; while(i < ii) { if (freeslots[i]) break; i++; } if (i == ii) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s no slot available for echo\n", __FUNCTION__); /* no more slots available */ return; } /* assign free slot */ dsp->pcm_slot_tx = i; dsp->pcm_slot_rx = i; dsp->pcm_bank_tx = 2; /* loop */ dsp->pcm_bank_rx = 2; if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s assign echo for %s using slot %d\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx); dsp_cmx_hw_message(dsp, HW_PCM_CONN, dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); return; } //printk("-----4\n"); /* conf gets updated (all members) */ if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s checking conference %d\n", __FUNCTION__, conf->id); //printk("-----5\n"); if (list_empty(&conf->mlist)) { printk(KERN_ERR "%s: conference whithout members\n", __FUNCTION__); return; } member = list_entry(conf->mlist.next, conf_member_t, list); same_hfc = member->dsp->features.hfc_id; same_pcm = member->dsp->features.pcm_id; /* check all members in our conference */ list_for_each_entry(member, &conf->mlist, list) { /* check if member uses mixing */ if (member->dsp->tx_mix) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s dsp %s cannot form a conf, because tx_mix is turned on\n", __FUNCTION__, member->dsp->inst.name); conf_software: // printk(KERN_NOTICE "****** SOFTWARE CONFERENCE\n"); list_for_each_entry(member, &conf->mlist, list) { dsp = member->dsp; /* remove HFC conference if enabled */ if (dsp->hfc_conf >= 0) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s removing %s from HFC conf %d because not possible with hardware\n", __FUNCTION__, dsp->inst.name, dsp->hfc_conf); dsp_cmx_hw_message(dsp, HW_CONF_SPLIT, 0, 0, 0, 0); dsp->hfc_conf = -1; } /* remove PCM slot if assigned */ if (dsp->pcm_slot_tx>=0 || dsp->pcm_slot_rx>=0) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s removing %s from PCM slot %d (TX) slot %d (RX) because not possible with hardware\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx, dsp->pcm_slot_rx); dsp_cmx_hw_message(dsp, HW_PCM_DISC, 0, 0, 0, 0); dsp->pcm_slot_tx = -1; dsp->pcm_bank_tx = -1; dsp->pcm_slot_rx = -1; dsp->pcm_bank_rx = -1; } } conf->hardware = 0; conf->software = 1; return; } /* check if member has echo turned on */ if (member->dsp->echo) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s dsp %s cannot form a conf, because echo is turned on\n", __FUNCTION__, member->dsp->inst.name); goto conf_software; } /* check if member has tx_mix turned on */ if (member->dsp->tx_mix) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s dsp %s cannot form a conf, because tx_mix is turned on\n", __FUNCTION__, member->dsp->inst.name); goto conf_software; } /* check if member changes volume at an not suppoted level */ if (member->dsp->tx_volume) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s dsp %s cannot form a conf, because tx_volume is changed\n", __FUNCTION__, member->dsp->inst.name); goto conf_software; } if (member->dsp->rx_volume) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s dsp %s cannot form a conf, because rx_volume is changed\n", __FUNCTION__, member->dsp->inst.name); goto conf_software; } /* check if encryption is enabled */ if (member->dsp->bf_enable) { if (dsp_debug & DEBUG_DSP_CMX) 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) printk(KERN_DEBUG "%s dsp %s cannot form a conf, because dsp has no PCM bus\n", __FUNCTION__, member->dsp->inst.name); goto conf_software; } /* check if relations are on the same PCM bus */ if (member->dsp->features.pcm_id != same_pcm) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s dsp %s cannot form a conf, because dsp is on a different PCM bus than the first dsp\n", __FUNCTION__, member->dsp->inst.name); goto conf_software; } /* determine if members are on the same hfc chip */ if (same_hfc != member->dsp->features.hfc_id) same_hfc = -1; /* if there are members already in a conference */ if (current_conf<0 && member->dsp->hfc_conf>=0) current_conf = member->dsp->hfc_conf; /* if any member is not in a conference */ if (member->dsp->hfc_conf < 0) all_conf = 0; memb++; } /* if no member, this is an error */ if (memb < 1) return; /* one member */ if (memb == 1) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s conf %d cannot form a HW conference, because dsp is alone\n", __FUNCTION__, conf->id); conf->hardware = 0; conf->software = 1; member = list_entry(conf->mlist.next, conf_member_t, list); dsp = member->dsp; goto one_member; } /* ok, now we are sure that all members are on the same pcm. * now we will see if we have only two members, so we can do * crossconnections, which don't have any limitations. */ /* if we have only two members */ if (memb == 2) { member = list_entry(conf->mlist.next, conf_member_t, list); nextm = list_entry(member->list.next, conf_member_t, list); /* remove HFC conference if enabled */ if (member->dsp->hfc_conf >= 0) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s removing %s from HFC conf %d because two parties require only a PCM slot\n", __FUNCTION__, member->dsp->inst.name, member->dsp->hfc_conf); dsp_cmx_hw_message(member->dsp, HW_CONF_SPLIT, 0, 0, 0, 0); member->dsp->hfc_conf = -1; } if (nextm->dsp->hfc_conf >= 0) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s removing %s from HFC conf %d because two parties require only a PCM slot\n", __FUNCTION__, nextm->dsp->inst.name, nextm->dsp->hfc_conf); dsp_cmx_hw_message(nextm->dsp, HW_CONF_SPLIT, 0, 0, 0, 0); nextm->dsp->hfc_conf = -1; } /* if members have two banks (and not on the same chip) */ if (member->dsp->features.pcm_banks>1 && nextm->dsp->features.pcm_banks>1 && member->dsp->features.hfc_id!=nextm->dsp->features.hfc_id) { /* if both members have same slots with crossed banks */ if (member->dsp->pcm_slot_tx>=0 && member->dsp->pcm_slot_rx>=0 && nextm->dsp->pcm_slot_tx>=0 && nextm->dsp->pcm_slot_rx>=0 && nextm->dsp->pcm_slot_tx==member->dsp->pcm_slot_rx && nextm->dsp->pcm_slot_rx==member->dsp->pcm_slot_tx && nextm->dsp->pcm_slot_tx==member->dsp->pcm_slot_tx && member->dsp->pcm_bank_tx!=member->dsp->pcm_bank_rx && nextm->dsp->pcm_bank_tx!=nextm->dsp->pcm_bank_rx) { /* all members have same slot */ if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s dsp %s & %s stay joined on PCM slot %d bank %d (TX) bank %d (RX) (on different chips)\n", __FUNCTION__, member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, member->dsp->pcm_bank_rx); conf->hardware = 0; conf->software = 1; return; } /* find a new slot */ memset(freeslots, 1, sizeof(freeslots)); list_for_each_entry(dsp, &dsp_obj.ilist, list) { if (dsp!=member->dsp && dsp!=nextm->dsp && member->dsp->features.pcm_id==dsp->features.pcm_id) { if (dsp->pcm_slot_rx>=0 && dsp->pcm_slot_rxpcm_slot_tx] = 0; if (dsp->pcm_slot_tx>=0 && dsp->pcm_slot_txpcm_slot_rx] = 0; } } i = 0; ii = member->dsp->features.pcm_slots; while(i < ii) { if (freeslots[i]) break; i++; } if (i == ii) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s no slot available for %s & %s\n", __FUNCTION__, member->dsp->inst.name, nextm->dsp->inst.name); /* no more slots available */ goto conf_software; } /* assign free slot */ member->dsp->pcm_slot_tx = i; member->dsp->pcm_slot_rx = i; nextm->dsp->pcm_slot_tx = i; nextm->dsp->pcm_slot_rx = i; member->dsp->pcm_bank_rx = 0; member->dsp->pcm_bank_tx = 1; nextm->dsp->pcm_bank_rx = 1; nextm->dsp->pcm_bank_tx = 0; if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s adding %s & %s to new PCM slot %d (TX and RX on different chips) because both members have not same slots\n", __FUNCTION__, member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx); dsp_cmx_hw_message(member->dsp, HW_PCM_CONN, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); dsp_cmx_hw_message(nextm->dsp, HW_PCM_CONN, nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); conf->hardware = 1; conf->software = 0; return; /* if members have one bank (or on the same chip) */ } else { /* if both members have different crossed slots */ if (member->dsp->pcm_slot_tx>=0 && member->dsp->pcm_slot_rx>=0 && nextm->dsp->pcm_slot_tx>=0 && nextm->dsp->pcm_slot_rx>=0 && nextm->dsp->pcm_slot_tx==member->dsp->pcm_slot_rx && nextm->dsp->pcm_slot_rx==member->dsp->pcm_slot_tx && member->dsp->pcm_slot_tx!=member->dsp->pcm_slot_rx && member->dsp->pcm_bank_tx==0 && member->dsp->pcm_bank_rx==0 && nextm->dsp->pcm_bank_tx==0 && nextm->dsp->pcm_bank_rx==0) { /* all members have same slot */ if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s dsp %s & %s stay joined on PCM slot %d (TX) %d (RX) on same chip or one bank PCM)\n", __FUNCTION__, member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx, member->dsp->pcm_slot_rx); conf->hardware = 0; conf->software = 1; return; } /* find two new slot */ memset(freeslots, 1, sizeof(freeslots)); list_for_each_entry(dsp, &dsp_obj.ilist, list) { if (dsp!=member->dsp && dsp!=nextm->dsp && member->dsp->features.pcm_id==dsp->features.pcm_id) { if (dsp->pcm_slot_rx>=0 && dsp->pcm_slot_rxpcm_slot_tx] = 0; if (dsp->pcm_slot_tx>=0 && dsp->pcm_slot_txpcm_slot_rx] = 0; } } i1 = 0; ii = member->dsp->features.pcm_slots; while(i1 < ii) { if (freeslots[i1]) break; i1++; } if (i1 == ii) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s no slot available for %s & %s\n", __FUNCTION__, member->dsp->inst.name, nextm->dsp->inst.name); /* no more slots available */ goto conf_software; } i2 = i1+1; while(i2 < ii) { if (freeslots[i2]) break; i2++; } if (i2 == ii) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s no slot available for %s & %s\n", __FUNCTION__, member->dsp->inst.name, nextm->dsp->inst.name); /* no more slots available */ goto conf_software; } /* assign free slots */ member->dsp->pcm_slot_tx = i1; member->dsp->pcm_slot_rx = i2; nextm->dsp->pcm_slot_tx = i2; nextm->dsp->pcm_slot_rx = i1; member->dsp->pcm_bank_rx = 0; member->dsp->pcm_bank_tx = 0; nextm->dsp->pcm_bank_rx = 0; nextm->dsp->pcm_bank_tx = 0; if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s adding %s & %s to new PCM slot %d (TX) %d (RX) on same chip or one bank PCM, because both members have not crossed slots\n", __FUNCTION__, member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx, member->dsp->pcm_slot_rx); dsp_cmx_hw_message(member->dsp, HW_PCM_CONN, member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); dsp_cmx_hw_message(nextm->dsp, HW_PCM_CONN, nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); conf->hardware = 1; conf->software = 0; return; } } /* if we have more than two, we may check if we have a conference * unit available on the chip. also all members must be on the same */ /* if not the same HFC chip */ if (same_hfc < 0) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s conference %d cannot be formed, because members are on different chips or not on HFC chip\n", __FUNCTION__, conf->id); goto conf_software; } /* if all members already have the same conference */ if (all_conf) return; /* if there is an existing conference, but not all members have joined */ if (current_conf >= 0) { join_members: list_for_each_entry(member, &conf->mlist, list) { /* join to current conference */ if (member->dsp->hfc_conf == current_conf) { continue; } /* get a free timeslot first */ memset(freeslots, 1, sizeof(freeslots)); list_for_each_entry(dsp, &dsp_obj.ilist, list) { /* not checking current member, because * slot will be overwritten. */ if (dsp!=member->dsp /* dsp must be on the same PCM */ && member->dsp->features.pcm_id==dsp->features.pcm_id) { /* dsp must be on a slot */ if (dsp->pcm_slot_tx>=0 && dsp->pcm_slot_txpcm_slot_tx] = 0; if (dsp->pcm_slot_rx>=0 && dsp->pcm_slot_rxpcm_slot_rx] = 0; } } i = 0; ii = member->dsp->features.pcm_slots; while(i < ii) { if (freeslots[i]) break; i++; } if (i == ii) { /* no more slots available */ if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s conference %d cannot be formed, because no slot free\n", __FUNCTION__, conf->id); goto conf_software; } if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s changing dsp %s to HW conference %d slot %d\n", __FUNCTION__, member->dsp->inst.name, current_conf, i); /* assign free slot & set PCM & join conf */ member->dsp->pcm_slot_tx = i; member->dsp->pcm_slot_rx = i; member->dsp->pcm_bank_tx = 2; /* loop */ member->dsp->pcm_bank_rx = 2; member->dsp->hfc_conf = current_conf; dsp_cmx_hw_message(member->dsp, HW_PCM_CONN, i, 2, i, 2); dsp_cmx_hw_message(member->dsp, HW_CONF_JOIN, current_conf, 0, 0, 0); } return; } /* no member is in a conference yet, so we find a free one */ memset(freeunits, 1, sizeof(freeunits)); list_for_each_entry(dsp, &dsp_obj.ilist, list) { /* dsp must be on the same chip */ if (dsp->features.hfc_id==same_hfc /* dsp must have joined a HW conference */ && dsp->hfc_conf>=0 /* slot must be within range */ && dsp->hfc_conf<8) freeunits[dsp->hfc_conf] = 0; } i = 0; ii = 8; while(i < ii) { if (freeunits[i]) break; i++; } if (i == ii) { /* no more conferences available */ if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s conference %d cannot be formed, because no conference number free\n", __FUNCTION__, conf->id); goto conf_software; } /* join all members */ current_conf = i; goto join_members; } /* * conf_id != 0: join or change conference * conf_id == 0: split from conference if not already */ int dsp_cmx_conf(dsp_t *dsp, u32 conf_id) { int err; conference_t *conf; /* if conference doesn't change */ if (dsp->conf_id == conf_id) return(0); spin_lock(&dsp->feature_lock); if (dsp->feature_state != FEAT_STATE_RECEIVED) { dsp->queue_conf_id=conf_id; spin_unlock(&dsp->feature_lock); return 0; } spin_unlock(&dsp->feature_lock); /* first remove us from current conf */ if (dsp->conf_id) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "removing us from conference %d\n", dsp->conf->id); /* remove us from conf */ conf = dsp->conf; err = dsp_cmx_del_conf_member(dsp); if (err) return(err); dsp->conf_id = 0; /* update hardware */ dsp_cmx_hardware(NULL, dsp); /* conf now empty? */ if (list_empty(&conf->mlist)) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "conference is empty, so we remove it.\n"); err = dsp_cmx_del_conf(conf); if (err) return(err); } else { /* update members left on conf */ dsp_cmx_hardware(conf, NULL); } } /* if split */ if (!conf_id) return(0); /* now add us to conf */ if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "searching conference %d\n", conf_id); conf = dsp_cmx_search_conf(conf_id); if (!conf) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "conference doesn't exist yet, creating.\n"); /* the conference doesn't exist, so we create */ conf = dsp_cmx_new_conf(conf_id); if (!conf) return(-EINVAL); } /* add conference member */ err = dsp_cmx_add_conf_member(dsp, conf); if (err) return(err); dsp->conf_id = conf_id; /* if we are alone, we do nothing! */ if (list_empty(&conf->mlist)) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "we are alone in this conference, so exit.\n"); /* update hardware */ dsp_cmx_hardware(NULL, dsp); return(0); } /* update members on conf */ dsp_cmx_hardware(conf, NULL); return(0); } /* * audio data is received from card */ void dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb) { // s32 *c; u8 *d, *p; int len = skb->len; 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; #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 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; } /* 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->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 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; u8 *d, *p, *q, *o_q; struct sk_buff *nskb; 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; #if 1 /* If we have 2 members and we are connected to pcm_slot, it looks like we're bridged on the pcm, so why should we send anything ? */ if ( members==2 && (dsp->features.pcm_id>=0) && (dsp->pcm_slot_tx>=0) && (dsp->pcm_slot_rx>=0) ) { return; } #endif #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; } 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->tx_R; /* tx-pointers */ tt = dsp->tx_W; r = dsp->rx_R; /* rx-pointers */ rr = (r + len) & CMX_BUFF_MASK; /* PROCESS TONES/TX-DATA ONLY */ if (dsp->tone.tone && dsp->tone.software) { /* -> copy tone */ dsp_tone_copy(dsp, d, len); 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) { /* -> send tx-data and continue when not enough */ while(r!=rr && t!=tt) { *d++ = p[t]; /* write tx_buff */ t = (t+1) & CMX_BUFF_MASK; r = (r+1) & CMX_BUFF_MASK; } if(r == rr) { dsp->tx_R = t; goto send_packet; } } /* 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 */ while(r!=rr && t!=tt) { *d++ = p[t]; /* write tx_buff */ t = (t+1) & CMX_BUFF_MASK; r = (r+1) & CMX_BUFF_MASK; } if(r != rr) 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 */ while(r!=rr && t!=tt) { *d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]]; t = (t+1) & CMX_BUFF_MASK; r = (r+1) & CMX_BUFF_MASK; } while(r != rr) { *d++ = q[r]; /* echo */ r = (r+1) & CMX_BUFF_MASK; } } dsp->tx_R = t; goto send_packet; } /* PROCESS DATA (two members) */ #ifdef CMX_CONF_DEBUG if (0) { #else 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_q = other->rx_buff; /* received data */ o_rr = (other->rx_R + len) & CMX_BUFF_MASK; /* end of rx-pointer */ o_r = (o_rr - rr + r) & CMX_BUFF_MASK; /* start rx-pointer at current read position*/ /* -> 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(o_r!=o_rr && t!=tt) { *d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]]; t = (t+1) & CMX_BUFF_MASK; o_r = (o_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[q[r]] + dsp_audio_law_to_s32[o_q[o_r]]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; *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[(q[r]<<8)|o_q[o_r]]; r = (r+1) & CMX_BUFF_MASK; o_r = (o_r+1) & CMX_BUFF_MASK; } } dsp->tx_R = t; goto send_packet; } /* 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++ - dsp_audio_law_to_s32[q[r]]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* conf-rx+tx */ r = (r+1) & CMX_BUFF_MASK; t = (t+1) & CMX_BUFF_MASK; } while(r != rr) { sample = *c++ - dsp_audio_law_to_s32[q[r]]; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* conf-rx */ r = (r+1) & CMX_BUFF_MASK; } /* -> if echo is enabled */ } else { /* -> encode conf-data, if tx-data is available, mix */ while(r!=rr && t!=tt) { sample = dsp_audio_law_to_s32[p[t]] + *c++; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* conf(echo)+tx */ t = (t+1) & CMX_BUFF_MASK; r = (r+1) & CMX_BUFF_MASK; } while(r != rr) { sample = *c++; if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; *d++ = dsp_audio_s16_to_law[sample & 0xffff]; /* conf(echo) */ r = (r+1) & CMX_BUFF_MASK; } } 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->conf_id) 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 */ if (member->dsp->conf_id) 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); } /* * audio data is transmitted from upper layer to the dsp */ void dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb) { u_int w, ww; u8 *d, *p; int space, l; /* check if we have sompen */ l = skb->len; if (l < 1) return; /* 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; if (space <= 0) space += CMX_BUFF_SIZE; /* write-pointer should not overrun nor reach read pointer */ if (space-1 < skb->len) /* write to the space we have left */ ww = (ww - 1) & CMX_BUFF_MASK; else /* write until all byte are copied */ ww = (w + skb->len) & CMX_BUFF_MASK; 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", (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->inst.name); #endif /* copy transmit data to tx-buffer */ while(w != ww) { p[w]= *d++; w = (w+1) & CMX_BUFF_MASK; } return; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_core.c0000644000000000000500000007524211135651702020343 0ustar rootsrc/* $Id: dsp_core.c,v 1.29 2007/03/27 15:06:29 jolly Exp $ * * Author Andreas Eversberg (jolly@eversberg.eu) * Based on source code structure by * Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * For changes and modifications please read * ../../../Documentation/isdn/mISDN.cert * * Thanks to Karsten Keil (great drivers) * Cologne Chip (great chips) * * This module does: * Real-time tone generation * DTMF detection * Real-time cross-connection and conferrence * Compensate jitter due to system load and hardware fault. * All features are done in kernel space and will be realized * using hardware, if available and supported by chip set. * Blowfish encryption/decryption */ /* STRUCTURE: * * The dsp module provides layer 2 for b-channels (64kbit). It provides * transparent audio forwarding with special digital signal processing: * * - (1) generation of tones * - (2) detection of dtmf tones * - (3) crossconnecting and conferences * - (4) echo generation for delay test * - (5) volume control * - (6) disable receive data * - (7) echo cancelation * - (8) encryption/decryption * * Look: * TX RX * ------upper layer------ * | ^ * | |(6) * v | * +-----+-------------+-----+ * |(3)(4) | * | CMX | * | | * | +-------------+ * | | ^ * | | | * |+---------+| +----+----+ * ||(1) || |(5) | * || || | | * || Tones || |RX Volume| * || || | | * || || | | * |+----+----+| +----+----+ * +-----+-----+ ^ * | | * v | * +----+----+ +----+----+ * |(5) | |(2) | * | | | | * |TX Volume| | DTMF | * | | | | * | | | | * +----+----+ +----+----+ * | ^ * | | * v | * +----+-------------+----+ * |(7) | * | | * | Echo Cancellation | * | | * | | * +----+-------------+----+ * | ^ * | | * v | * +----+----+ +----+----+ * |(8) | |(8) | * | | | | * | Encrypt | | Decrypt | * | | | | * | | | | * +----+----+ +----+----+ * | ^ * | | * v | * ------card layer------ * TX RX * * Above you can see the logical data flow. If software is used to do the * process, it is actually the real data flow. If hardware is used, data * may not flow, but hardware commands to the card, to provide the data flow * as shown. * * NOTE: The channel must be activated in order to make dsp work, even if * no data flow to the upper layer is intended. Activation can be done * after and before controlling the setting using PH_CONTROL requests. * * DTMF: Will be detected by hardware if possible. It is done before CMX * processing. * * Tones: Will be generated via software if endless looped audio fifos are * not supported by hardware. Tones will override all data from CMX. * It is not required to join a conference to use tones at any time. * * CMX: Is transparent when not used. When it is used, it will do * crossconnections and conferences via software if not possible through * hardware. If hardware capability is available, hardware is used. * * Echo: Is generated by CMX and is used to check performane of hard and * software CMX. * * The CMX has special functions for conferences with one, two and more * members. It will allow different types of data flow. Receive and transmit * 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 * usefull if an answering machine records audio. Not sending and receiving is * usefull during most states of the call. If supported by hardware, tones * will be played without cpu load. Small PBXs and NT-Mode applications will * not need expensive hardware when processing calls. * * * LOCKING: * * When data is received from upper or lower layer (card), the complete dsp * module is locked by a global lock. When data is ready to be transmitted * to a different layer, the module is unlocked. It is not allowed to hold a * lock outside own layer. * Reasons: Multiple threads must not process cmx at the same time, if threads * serve instances, that are connected in same conference. * PH_CONTROL must not change any settings, join or split conference members * during process of data. * * * TRANSMISSION: * TBD There are three things that need to receive data from card: - software DTMF decoder - software cmx (if conference exists) - upper layer, if rx-data not disabled Whenever dtmf decoder is turned on or off, software cmx changes, rx-data is disabled or enabled, or card becomes activated, then rx-data is disabled or enabled using a special command to the card. There are three things that need to transmit data to card: - software tone generation (part of cmx) - software cmx - upper layer, if tx-data is written to tx-buffer */ const char *dsp_revision = "$Revision: 1.29 $"; #include #include #include #include "core.h" #include "layer1.h" #include "helper.h" #include "debug.h" #include "dsp.h" static char DSPName[] = "DSP"; mISDNobject_t dsp_obj; static int debug = 0; int dsp_debug; static int options = 0; int dsp_options; static int poll = 0; int dsp_poll, dsp_tics; int dtmfthreshold=100L; #ifdef MODULE MODULE_AUTHOR("Andreas Eversberg"); #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); MODULE_PARM(options, "1i"); MODULE_PARM(poll, "1i"); MODULE_PARM(dtmfthreshold, "1i"); #else module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(options, uint, S_IRUGO | S_IWUSR); module_param(poll, uint, S_IRUGO | S_IWUSR); module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR); #endif #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #endif /* * special message process for DL_CONTROL | REQUEST */ static int dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb) { struct sk_buff *nskb; int ret = 0; int cont; u8 *data; int len; if (skb->len < sizeof(int)) { printk(KERN_ERR "%s: PH_CONTROL message too short\n", __FUNCTION__); } cont = *((int *)skb->data); len = skb->len - sizeof(int); data = skb->data + sizeof(int); switch (cont) { case DTMF_TONE_START: /* turn on DTMF */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: start dtmf\n", __FUNCTION__); #if 0 if (len == sizeof(int)) { printk(KERN_NOTICE "changing DTMF Threshold to %d\n",*((int*)data)); dsp->dtmf.treshold=(*(int*)data)*10000; } #endif dsp_dtmf_goertzel_init(dsp); /* checking for hardware capability */ if (dsp->features.hfc_dtmf) { dsp->dtmf.hardware = 1; dsp->dtmf.software = 0; } else { dsp->dtmf.hardware = 0; dsp->dtmf.software = 1; } break; case DTMF_TONE_STOP: /* turn off DTMF */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: stop dtmf\n", __FUNCTION__); dsp->dtmf.hardware = 0; dsp->dtmf.software = 0; break; case CMX_CONF_JOIN: /* join / update conference */ if (len != sizeof(int)) { ret = -EINVAL; break; } if (*((u32 *)data) == 0) goto conf_split; if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: join conference %d\n", __FUNCTION__, *((u32 *)data)); ret = dsp_cmx_conf(dsp, *((u32 *)data)); if (dsp_debug & DEBUG_DSP_CMX) dsp_cmx_debug(dsp); break; case CMX_CONF_SPLIT: /* remove from conference */ conf_split: if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: release conference\n", __FUNCTION__); ret = dsp_cmx_conf(dsp, 0); if (dsp_debug & DEBUG_DSP_CMX) dsp_cmx_debug(dsp); break; case TONE_PATT_ON: /* play tone */ if (len != sizeof(int)) { ret = -EINVAL; break; } if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: turn tone 0x%x on\n", __FUNCTION__, *((int *)skb->data)); ret = dsp_tone(dsp, *((int *)data)); if (!ret) dsp_cmx_hardware(dsp->conf, dsp); if (!dsp->tone.tone) goto tone_off; break; case TONE_PATT_OFF: /* stop tone */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: turn tone off\n", __FUNCTION__); dsp_tone(dsp, 0); dsp_cmx_hardware(dsp->conf, dsp); /* reset tx buffers (user space data) */ tone_off: dsp->tx_R = dsp->tx_W = 0; break; case VOL_CHANGE_TX: /* change volume */ if (len != sizeof(int)) { ret = -EINVAL; break; } dsp->tx_volume = *((int *)data); if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume); dsp_cmx_hardware(dsp->conf, dsp); break; case VOL_CHANGE_RX: /* change volume */ if (len != sizeof(int)) { ret = -EINVAL; break; } dsp->rx_volume = *((int *)data); if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: change rx volume to %d\n", __FUNCTION__, dsp->tx_volume); dsp_cmx_hardware(dsp->conf, dsp); break; case CMX_ECHO_ON: /* enable echo */ dsp->echo = 1; /* soft echo */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: enable cmx-echo\n", __FUNCTION__); dsp_cmx_hardware(dsp->conf, dsp); if (dsp_debug & DEBUG_DSP_CMX) dsp_cmx_debug(dsp); break; case CMX_ECHO_OFF: /* disable echo */ dsp->echo = 0; if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: disable cmx-echo\n", __FUNCTION__); dsp_cmx_hardware(dsp->conf, dsp); if (dsp_debug & DEBUG_DSP_CMX) dsp_cmx_debug(dsp); break; case CMX_RECEIVE_ON: /* enable receive to user space */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: enable receive to user space\n", __FUNCTION__); dsp->rx_disabled = 0; dsp_cmx_hardware(dsp->conf, dsp); break; case CMX_RECEIVE_OFF: /* disable receive to user space */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: disable receive to user space\n", __FUNCTION__); dsp->rx_disabled = 1; dsp_cmx_hardware(dsp->conf, dsp); break; case CMX_MIX_ON: /* enable mixing of transmit data with conference members */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: enable mixing of tx-data with conf mebers\n", __FUNCTION__); dsp->tx_mix = 1; dsp_cmx_hardware(dsp->conf, dsp); if (dsp_debug & DEBUG_DSP_CMX) dsp_cmx_debug(dsp); break; case CMX_MIX_OFF: /* disable mixing of transmit data with conference members */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: disable mixing of tx-data with conf mebers\n", __FUNCTION__); dsp->tx_mix = 0; dsp_cmx_hardware(dsp->conf, dsp); 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; break; } if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: turn blowfish on (key not shown)\n", __FUNCTION__); ret = dsp_bf_init(dsp, (u8*)data, len); /* set new cont */ if (!ret) cont = BF_ACCEPT; else cont = BF_REJECT; /* send indication if it worked to set it */ nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0); if (mISDN_queue_up(&dsp->inst, 0, nskb)) dev_kfree_skb(nskb); if (!ret) dsp_cmx_hardware(dsp->conf, dsp); break; case BF_DISABLE: /* turn blowfish off */ if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: turn blowfish off\n", __FUNCTION__); dsp_bf_cleanup(dsp); dsp_cmx_hardware(dsp->conf, dsp); break; default: if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", __FUNCTION__, cont); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } /* * messages from upper layers */ static int dsp_from_up(mISDNinstance_t *inst, struct sk_buff *skb) { dsp_t *dsp; mISDN_head_t *hh; int ret = 0; u_long flags; if (!skb) return(-EINVAL); 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: if (skb->len < 1) return(-EINVAL); if (!dsp->conf_id) { /* PROCESS TONES/TX-DATA ONLY */ if (dsp->tone.tone) { /* -> copy tone */ dsp_tone_copy(dsp, skb->data, skb->len); } if (dsp->tx_volume) dsp_change_volume(skb, dsp->tx_volume); /* cancel echo */ if (dsp->cancel_enable) dsp_cancel_tx(dsp, skb->data, skb->len); /* crypt */ if (dsp->bf_enable) dsp_bf_encrypt(dsp, skb->data, skb->len); /* send packet */ if (mISDN_queue_down(&dsp->inst, 0, skb)) { dev_kfree_skb(skb); printk(KERN_ERR "%s: failed to send tx-packet\n", __FUNCTION__); return (-EIO); } } else { if (dsp->features.pcm_id>=0) { if (dsp_debug) printk("Not sending Data to CMX -- > returning because of HW bridge\n"); dev_kfree_skb(skb); break; } /* 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); } spin_unlock_irqrestore(&dsp_obj.lock, flags); dev_kfree_skb(skb); } break; case PH_CONTROL | REQUEST: spin_lock_irqsave(&dsp_obj.lock, flags); ret = dsp_control_req(dsp, hh, skb); 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); if (dsp->dtmf.hardware || dsp->dtmf.software) dsp_dtmf_goertzel_init(dsp); hh->prim = PH_ACTIVATE | REQUEST; 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); dsp->tone.tone = dsp->tone.hardware = dsp->tone.software = 0; if (timer_pending(&dsp->tone.tl)) del_timer(&dsp->tone.tl); hh->prim = PH_DEACTIVATE | REQUEST; 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; } return(ret); } /* * messages from lower layers */ static int dsp_from_down(mISDNinstance_t *inst, struct sk_buff *skb) { dsp_t *dsp; mISDN_head_t *hh; int ret = 0; u8 *digits; int cont; struct sk_buff *nskb; u_long flags; if (!skb) return(-EINVAL); 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) 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); if (digits) while(*digits) { if (dsp_debug & DEBUG_DSP_DTMF) 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); if (mISDN_queue_up(&dsp->inst, 0, nskb)) dev_kfree_skb(nskb); digits++; } } /* change volume if requested */ if (dsp->rx_volume) dsp_change_volume(skb, dsp->rx_volume); if (dsp->conf_id) { /* 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); } spin_unlock_irqrestore(&dsp_obj.lock, flags); } if (dsp->rx_disabled) { /* if receive is not allowed */ dev_kfree_skb(skb); break; } hh->prim = DL_DATA | INDICATION; ret = mISDN_queue_up(&dsp->inst, 0, skb); break; case PH_CONTROL | INDICATION: 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) { case HW_HFC_COEFF: /* getting coefficients */ 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; } 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; case VOL_CHANGE_TX: /* change volume */ if (skb->len != sizeof(int)) { ret = -EINVAL; break; } dsp->tx_volume = *((int *)skb->data); if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume); printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume); dsp_cmx_hardware(dsp->conf, dsp); break; default: if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: ctrl ind %x unhandled %s\n", __FUNCTION__, hh->dinfo, dsp->inst.name); ret = -EINVAL; } break; case PH_ACTIVATE | CONFIRM: 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->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); 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; ret = mISDN_queue_up(&dsp->inst, 0, skb); break; case PH_DEACTIVATE | CONFIRM: 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; 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; } 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); } /* * desroy DSP instances */ static void release_dsp(dsp_t *dsp) { mISDNinstance_t *inst = &dsp->inst; conference_t *conf; u_long flags; 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); if (dsp_debug & DEBUG_DSP_MGR) printk(KERN_DEBUG "%s: removing conferences %s\n", __FUNCTION__, dsp->inst.name); conf = dsp->conf; if (conf) { dsp_cmx_del_conf_member(dsp); if (!list_empty(&conf->mlist)) { dsp_cmx_del_conf(conf); } } 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); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); vfree(dsp); if (dsp_debug & DEBUG_DSP_MGR) printk(KERN_DEBUG "%s: dsp instance released\n", __FUNCTION__); } /* * 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); spin_lock(&dsp->feature_lock); dsp->feature_state = FEAT_STATE_WAIT; spin_unlock(&dsp->feature_lock); 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 hfc_echocanhw:%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.hfc_echocanhw, dsp->features.pcm_id, dsp->features.pcm_slots, dsp->features.pcm_banks); spin_lock(&dsp->feature_lock); dsp->feature_state = FEAT_STATE_RECEIVED; spin_unlock(&dsp->feature_lock); if (dsp->queue_conf_id) { /*work on queued conf id*/ dsp_cmx_conf(dsp, dsp->queue_conf_id ); if (dsp_debug & DEBUG_DSP_CMX) dsp_cmx_debug(dsp); } if (dsp->queue_cancel[2]) { dsp_cancel_init(dsp, dsp->queue_cancel[0], dsp->queue_cancel[1], dsp->queue_cancel[2] ); } break; } } /* * create new DSP instances */ static int new_dsp(mISDNstack_t *st, mISDN_pid_t *pid) { 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__); if (!st || !pid) return(-EINVAL); if (!(ndsp = vmalloc(sizeof(dsp_t)))) { printk(KERN_ERR "%s: vmalloc dsp_t failed\n", __FUNCTION__); return(-ENOMEM); } memset(ndsp, 0, sizeof(dsp_t)); memcpy(&ndsp->inst.pid, pid, sizeof(mISDN_pid_t)); mISDN_init_instance(&ndsp->inst, &dsp_obj, ndsp, dsp_function); if (!mISDN_SetHandledPID(&dsp_obj, &ndsp->inst.pid)) { int_error(); err = -ENOPROTOOPT; free_mem: vfree(ndsp); return(err); } sprintf(ndsp->inst.name, "DSP_S%x/C%x", (st->id&0xff00)>>8, (st->id&0xff0000)>>16); /* set frame size to start */ ndsp->features.hfc_id = -1; /* current PCM id */ ndsp->features.pcm_id = -1; /* current PCM id */ ndsp->pcm_slot_rx = -1; /* current CPM slot */ ndsp->pcm_slot_tx = -1; ndsp->pcm_bank_rx = -1; ndsp->pcm_bank_tx = -1; ndsp->hfc_conf = -1; /* current conference number */ /* set tone timer */ ndsp->tone.tl.function = (void *)dsp_tone_timeout; ndsp->tone.tl.data = (long) ndsp; init_timer(&ndsp->tone.tl); /* set dsp feture timer */ ndsp->feature_tl.function = (void *)dsp_feat; ndsp->feature_tl.data = (long) ndsp; ndsp->feature_state = FEAT_STATE_INIT; if (dtmfthreshold < 20 || dtmfthreshold> 500) { dtmfthreshold=200; } ndsp->dtmf.treshold=dtmfthreshold*10000; spin_lock_init(&ndsp->feature_lock); 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 = mISDN_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); 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); return(err); } /* * 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; 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); } switch(prim) { 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); break; case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: ret = mISDN_SetIF(inst, arg, prim, dsp_from_up, dsp_from_down, dspl); break; case MGR_DISCONNECT | REQUEST: 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) printk(KERN_DEBUG "%s: release_dsp id %x\n", __FUNCTION__, dspl->inst.st->id); release_dsp(dspl); break; default: printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim); ret = -EINVAL; break; } return(ret); } /* * initialize DSP object */ static int dsp_init(void) { int err; /* copy variables */ dsp_options = options; dsp_debug = debug; /* display revision */ printk(KERN_INFO "mISDN_dsp: Audio DSP Rev. %s (debug=0x%x) EchoCancellor %s dtmfthreshold(%d)\n", mISDN_getrev(dsp_revision), debug, EC_TYPE, dtmfthreshold); /* set packet size */ 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); } /* fill mISDN object (dsp_obj) */ memset(&dsp_obj, 0, sizeof(dsp_obj)); #ifdef MODULE #ifdef SET_MODULE_OWNER SET_MODULE_OWNER(&dsp_obj); #endif #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 */ dsp_audio_generate_law_tables(); 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(); dsp_audio_generate_mix_table(); if (dsp_options & DSP_OPT_ULAW) dsp_audio_generate_ulaw_samples(); dsp_audio_generate_volume_changes(); /* 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); mISDN_module_register(THIS_MODULE); return(0); } /* * cleanup DSP object during module removal */ static void dsp_cleanup(void) { dsp_t *dspl, *nd; int err; mISDN_module_unregister(THIS_MODULE); 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__); if ((err = mISDN_unregister(&dsp_obj))) { printk(KERN_ERR "mISDN_dsp: Can't unregister Audio DSP error(%d)\n", err); } if (!list_empty(&dsp_obj.ilist)) { printk(KERN_WARNING "mISDN_dsp: Audio DSP object inst list not empty.\n"); list_for_each_entry_safe(dspl, nd, &dsp_obj.ilist, list) release_dsp(dspl); } if (!list_empty(&Conf_list)) { printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not all memory freed.\n"); } } #ifdef MODULE module_init(dsp_init); module_exit(dsp_cleanup); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_dtmf.c0000644000000000000500000001434311135651702020340 0ustar rootsrc/* $Id: dsp_dtmf.c,v 1.7 2007/03/27 15:06:29 jolly Exp $ * * DTMF decoder. * * Copyright by Andreas Eversberg (jolly@eversberg.eu) * based on different decoders such as ISDN4Linux * * 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" #define NCOEFF 8 /* number of frequencies to be analyzed */ /* For DTMF recognition: * 2 * cos(2 * PI * k / N) precalculated for all k */ static u64 cos2pik[NCOEFF] = { /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 }; /* digit matrix */ static char dtmf_matrix[4][4] = { {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} }; /* dtmf detection using goertzel algorithm * init function */ void dsp_dtmf_goertzel_init(dsp_t *dsp) { dsp->dtmf.size = 0; dsp->dtmf.lastwhat = '\0'; dsp->dtmf.lastdigit = '\0'; dsp->dtmf.count = 0; } /************************************************************* * calculate the coefficients of the given sample and decode * *************************************************************/ /* the given sample is decoded. if the sample is not long enough for a * complete frame, the decoding is finished and continued with the next * call of this function. * * the algorithm is very good for detection with a minimum of errors. i * tested it allot. it even works with very short tones (40ms). the only * disadvantage is, that it doesn't work good with different volumes of both * tones. this will happen, if accoustically coupled dialers are used. * it sometimes detects tones during speach, which is normal for decoders. * use sequences to given commands during calls. * * dtmf - points to a structure of the current dtmf state * spl and len - the sample * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder */ u8 *dsp_dtmf_goertzel_decode(dsp_t *dsp, u8 *data, int len, int fmt) { u8 what; int size; signed short *buf; s32 sk, sk1, sk2; int k, n, i; s32 *hfccoeff; s32 result[NCOEFF], tresh, treshl; int lowgroup, highgroup; s64 cos2pik_; dsp->dtmf.digits[0] = '\0'; /* note: the function will loop until the buffer are not enough samples * left to decode a full frame */ again: /* convert samples */ size = dsp->dtmf.size; buf = dsp->dtmf.buffer; switch(fmt) { case 0: /* alaw */ case 1: /* ulaw */ while(size 0) printk(KERN_ERR "%s: coefficients have invalid size. (is=%d < must=%d)\n", __FUNCTION__, len, 64); return(dsp->dtmf.digits); } hfccoeff = (s32 *)data; for (k = 0; k < NCOEFF; k++) { sk2 = (*hfccoeff++)>>4; sk = (*hfccoeff++)>>4; if (sk>32767 || sk<-32767 || sk2>32767 || sk2<-32767) printk(KERN_WARNING "DTMF-Detection overflow\n"); /* compute |X(k)|**2 */ result[k] = (sk * sk) - (((cos2pik[k] * sk) >> 15) * sk2) + (sk2 * sk2); } data += 64; len -= 64; goto coefficients; break; } dsp->dtmf.size = size; if (size < DSP_DTMF_NPOINTS) return(dsp->dtmf.digits); dsp->dtmf.size = 0; /* now we have a full buffer of signed long samples - we do goertzel */ for (k = 0; k < NCOEFF; k++) { sk = sk1 = sk2 = 0; buf = dsp->dtmf.buffer; cos2pik_ = cos2pik[k]; for (n = 0; n < DSP_DTMF_NPOINTS; n++) { sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++); sk2 = sk1; sk1 = sk; } sk>>=8; sk2>>=8; if (sk>32767 || sk<-32767 || sk2>32767 || sk2<-32767) printk(KERN_WARNING "DTMF-Detection overflow\n"); /* compute |X(k)|**2 */ result[k] = (sk * sk) - (((cos2pik[k] * sk) >> 15) * sk2) + (sk2 * sk2); } /* our (squared) coefficients have been calculated, we need to process * them. */ coefficients: tresh = 0; for (i = 0; i < NCOEFF; i++) { if (result[i] < 0) result[i] = 0; if (result[i] > dsp->dtmf.treshold) { if (result[i] > tresh) tresh = result[i]; } } if (tresh == 0) { what = 0; goto storedigit; } if (dsp_debug & DEBUG_DSP_DTMFCOEFF) printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", result[0]/10000, result[1]/10000, result[2]/10000, result[3]/10000, result[4]/10000, result[5]/10000, result[6]/10000, result[7]/10000, tresh/10000, result[0]/(tresh/100), result[1]/(tresh/100), result[2]/(tresh/100), result[3]/(tresh/100), result[4]/(tresh/100), result[5]/(tresh/100), result[6]/(tresh/100), result[7]/(tresh/100)); /* calc digit (lowgroup/highgroup) */ lowgroup = highgroup = -1; treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ tresh = tresh >> 2; /* touchtones must match within 6 dB */ for (i = 0; i < NCOEFF; i++) { if (result[i] < treshl) continue; /* ignore */ if (result[i] < tresh) { lowgroup = highgroup = -1; break; /* noise inbetween */ } /* good level found. This is allowed only one time per group */ if (i < NCOEFF/2) { /* lowgroup*/ if (lowgroup >= 0) { // Bad. Another tone found. */ lowgroup = -1; break; } else lowgroup = i; } else { /* higroup */ if (highgroup >= 0) { // Bad. Another tone found. */ highgroup = -1; break; } else highgroup = i-(NCOEFF/2); } } /* get digit or null */ what = 0; if (lowgroup>=0 && highgroup>=0) what = dtmf_matrix[lowgroup][highgroup]; storedigit: if (what && (dsp_debug & DEBUG_DSP_DTMF)) printk(KERN_DEBUG "DTMF what: %c\n", what); if (dsp->dtmf.lastwhat!=what) dsp->dtmf.count = 0; /* the tone (or no tone) must remain 3 times without change */ if (dsp->dtmf.count == 2) { if (dsp->dtmf.lastdigit!=what) { dsp->dtmf.lastdigit = what; if (what) { if (dsp_debug & DEBUG_DSP_DTMF) printk(KERN_DEBUG "DTMF digit: %c\n", what); if ((strlen(dsp->dtmf.digits)+1) dtmf.digits)) { dsp->dtmf.digits[strlen(dsp->dtmf.digits)+1] = '\0'; dsp->dtmf.digits[strlen(dsp->dtmf.digits)] = what; } } } } else dsp->dtmf.count++; dsp->dtmf.lastwhat = what; goto again; } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_kb1ec.h0000644000000000000500000004303111135651702020374 0ustar rootsrc/* * ECHO_CAN_KB1 * * by Kris Boutilier * * Based upon mech2.h * * Copyright (C) 2002, Digium, Inc. * * This program is free software and may be used and * distributed according to the terms of the GNU * General Public License, incorporated herein by * reference. * * Additional background on the techniques used in this code can be found in: * * Messerschmitt, David; Hedberg, David; Cole, Christopher; Haoui, Amine; * Winship, Peter; "Digital Voice Echo Canceller with a TMS32020," * in Digital Signal Processing Applications with the TMS320 Family, * pp. 415-437, Texas Instruments, Inc., 1986. * * A pdf of which is available by searching on the document title at http://www.ti.com/ * */ #ifndef _MARK2_ECHO_H #define _MARK2_ECHO_H #define EC_TYPE "KB1" #ifdef __KERNEL__ #include #include #define MALLOC(a) kmalloc((a), GFP_ATOMIC) #define FREE(a) kfree(a) #else #include #include #include #include #include #define MALLOC(a) malloc(a) #define FREE(a) free(a) #endif /* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */ /* #define MEC2_STATS 4000 */ /* Uncomment to generate per-sample statistics - this will severely degrade system performance and audio quality */ /* #define MEC2_STATS_DETAILED */ /* Get optimized routines for math */ #include "dsp_arith.h" /* Bring in definitions for the various constants and thresholds */ #include "dsp_kb1ec_const.h" #ifndef NULL #define NULL 0 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE (!FALSE) #endif /* Generic circular buffer definition */ typedef struct { /* Pointer to the relative 'start' of the buffer */ int idx_d; /* The absolute size of the buffer */ int size_d; /* The actual sample - twice as large as we need, however we do store values at idx_d and idx_d+size_d */ short *buf_d; } echo_can_cb_s; /* Echo canceller definition */ struct echo_can_state { /* an arbitrary ID for this echo can - this really should be settable from the calling channel... */ int id; /* absolute time - aka. sample number index - essentially the number of samples since this can was init'ed */ int i_d; /* Pre-computed constants */ /* ---------------------- */ /* Number of filter coefficents */ int N_d; /* Rate of adaptation of filter */ int beta2_i; /* Accumulators for power computations */ /* ----------------------------------- */ /* reference signal power estimate - aka. Average absolute value of y(k) */ int Ly_i; /* ... */ int Lu_i; /* Accumulators for signal detectors */ /* --------------------------------- */ /* Power estimate of the recent past of the near-end hybrid signal - aka. Short-time average of: 2 x |s(i)| */ int s_tilde_i; /* Power estimate of the recent past of the far-end receive signal - aka. Short-time average of: |y(i)| */ int y_tilde_i; /* Near end speech detection counter - stores Hangover counter time remaining, in samples */ int HCNTR_d; /* Circular buffers and coefficients */ /* --------------------------------- */ /* ... */ int *a_i; /* ... */ short *a_s; /* Reference samples of far-end receive signal */ echo_can_cb_s y_s; /* Reference samples of near-end signal */ echo_can_cb_s s_s; /* Reference samples of near-end signal minus echo estimate */ echo_can_cb_s u_s; /* Reference samples of far-end receive signal used to calculate short-time average */ echo_can_cb_s y_tilde_s; /* Peak far-end receive signal */ /* --------------------------- */ /* Highest y_tilde value in the sample buffer */ short max_y_tilde; /* Index of the sample containing the max_y_tilde value */ int max_y_tilde_pos; #ifdef MEC2_STATS /* Storage for performance statistics */ int cntr_nearend_speech_frames; int cntr_residualcorrected_frames; int cntr_residualcorrected_framesskipped; int cntr_coeff_updates; int cntr_coeff_missedupdates; int avg_Lu_i_toolow; int avg_Lu_i_ok; #endif }; static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where) { cb->buf_d = (short *)where; cb->idx_d = 0; cb->size_d = len; } static inline void add_cc_s(echo_can_cb_s *cb, short newval) { /* Can't use modulus because N+M isn't a power of two (generally) */ cb->idx_d--; if (cb->idx_d < (int)0) /* Whoops - the pointer to the 'start' wrapped around so reset it to the top of the buffer */ cb->idx_d += cb->size_d; /* Load two copies into memory */ cb->buf_d[cb->idx_d] = newval; cb->buf_d[cb->idx_d + cb->size_d] = newval; } static inline short get_cc_s(echo_can_cb_s *cb, int pos) { /* Load two copies into memory */ return cb->buf_d[cb->idx_d + pos]; } static inline void init_cc(struct echo_can_state *ec, int N, int maxy, int maxu) { void *ptr = ec; unsigned long tmp; /* Double-word align past end of state */ ptr += sizeof(struct echo_can_state); tmp = (unsigned long)ptr; tmp += 3; tmp &= ~3L; ptr = (void *)tmp; /* Reset parameters */ ec->N_d = N; ec->beta2_i = DEFAULT_BETA1_I; /* Allocate coefficient memory */ ec->a_i = ptr; ptr += (sizeof(int) * ec->N_d); ec->a_s = ptr; ptr += (sizeof(short) * ec->N_d); /* Reset Y circular buffer (short version) */ init_cb_s(&ec->y_s, maxy, ptr); ptr += (sizeof(short) * (maxy) * 2); /* Reset Sigma circular buffer (short version for FIR filter) */ init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr); ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2); init_cb_s(&ec->u_s, maxu, ptr); ptr += (sizeof(short) * maxu * 2); /* Allocate a buffer for the reference signal power computation */ init_cb_s(&ec->y_tilde_s, ec->N_d, ptr); /* Reset the absolute time index */ ec->i_d = (int)0; /* Reset the power computations (for y and u) */ ec->Ly_i = DEFAULT_CUTOFF_I; ec->Lu_i = DEFAULT_CUTOFF_I; #ifdef MEC2_STATS /* set the identity */ ec->id = (int)&ptr; /* Reset performance stats */ ec->cntr_nearend_speech_frames = (int)0; ec->cntr_residualcorrected_frames = (int)0; ec->cntr_residualcorrected_framesskipped = (int)0; ec->cntr_coeff_updates = (int)0; ec->cntr_coeff_missedupdates = (int)0; ec->avg_Lu_i_toolow = (int)0; ec->avg_Lu_i_ok = (int)0; #endif /* Reset the near-end speech detector */ ec->s_tilde_i = (int)0; ec->y_tilde_i = (int)0; ec->HCNTR_d = (int)0; } static inline void echo_can_free(struct echo_can_state *ec) { FREE(ec); } static inline short echo_can_update(struct echo_can_state *ec, short iref, short isig) { /* Declare local variables that are used more than once */ /* ... */ int k; /* ... */ int rs; /* ... */ short u; /* ... */ int Py_i; /* ... */ int two_beta_i; /* flow A on pg. 428 */ /* eq. (16): high-pass filter the input to generate the next value; * push the current value into the circular buffer * * sdc_im1_d = sdc_d; * sdc_d = sig; * s_i_d = sdc_d; * s_d = s_i_d; * s_i_d = (float)(1.0 - gamma_d) * s_i_d * + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d); */ /* Update the Far-end receive signal circular buffers and accumulators */ /* ------------------------------------------------------------------- */ /* Delete the oldest sample from the power estimate accumulator */ ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I; /* Add the new sample to the power estimate accumulator */ ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I; /* Push a copy of the new sample into its circular buffer */ add_cc_s(&ec->y_s, iref); /* eq. (2): compute r in fixed-point */ rs = CONVOLVE2(ec->a_s, ec->y_s.buf_d + ec->y_s.idx_d, ec->N_d); rs >>= 15; /* eq. (3): compute the output value (see figure 3) and the error * note: the error is the same as the output signal when near-end * speech is not present */ u = isig - rs; /* Push a copy of the output value sample into its circular buffer */ add_cc_s(&ec->u_s, u); /* Update the Near-end hybrid signal circular buffers and accumulators */ /* ------------------------------------------------------------------- */ /* Delete the oldest sample from the power estimate accumulator */ ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 )); /* Add the new sample to the power estimate accumulator */ ec->s_tilde_i += abs(isig); /* Push a copy of the new sample into it's circular buffer */ add_cc_s(&ec->s_s, isig); /* Push a copy of the current short-time average of the far-end receive signal into it's circular buffer */ add_cc_s(&ec->y_tilde_s, ec->y_tilde_i); /* flow B on pg. 428 */ /* If the hangover timer isn't running then compute the new convergence factor, otherwise set Py_i to 32768 */ if (!ec->HCNTR_d) { Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I); Py_i >>= 15; } else { Py_i = (1 << 15); } #if 0 /* Vary rate of adaptation depending on position in the file * Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech * has begun of the file to allow the echo cancellor to estimate the * channel accurately * Still needs conversion! */ if (ec->start_speech_d != 0 ){ if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){ ec->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - ec->start_speech_d))); } } else { ec->beta2_d = DEFAULT_BETA1; } #endif /* Fixed point, inverted */ ec->beta2_i = DEFAULT_BETA1_I; /* Fixed point version, inverted */ two_beta_i = (ec->beta2_i * Py_i) >> 15; if (!two_beta_i) two_beta_i++; /* Update the Suppressed signal power estimate accumulator */ /* ------------------------------------------------------- */ /* Delete the oldest sample from the power estimate accumulator */ ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ; /* Add the new sample to the power estimate accumulator */ ec->Lu_i += abs(u); /* Update the Far-end reference signal power estimate accumulator */ /* -------------------------------------------------------------- */ /* eq. (10): update power estimate of the reference */ /* Delete the oldest sample from the power estimate accumulator */ ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ; /* Add the new sample to the power estimate accumulator */ ec->Ly_i += abs(iref); if (ec->Ly_i < DEFAULT_CUTOFF_I) ec->Ly_i = DEFAULT_CUTOFF_I; /* Update the Peak far-end receive signal detected */ /* ----------------------------------------------- */ if (ec->y_tilde_i > ec->max_y_tilde) { /* New highest y_tilde with full life */ ec->max_y_tilde = ec->y_tilde_i; ec->max_y_tilde_pos = ec->N_d - 1; } else if (--ec->max_y_tilde_pos < 0) { /* Time to find new max y tilde... */ ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos); } /* Determine if near end speech was detected in this sample */ /* -------------------------------------------------------- */ if (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde) && (ec->max_y_tilde > 0)) { /* Then start the Hangover counter */ ec->HCNTR_d = DEFAULT_HANGT; #ifdef MEC2_STATS_DETAILED printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde); #endif #ifdef MEC2_STATS ++ec->cntr_nearend_speech_frames; #endif } else if (ec->HCNTR_d > (int)0) { /* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */ #ifdef MEC2_STATS ++ec->cntr_nearend_speech_frames; #endif ec->HCNTR_d--; } /* Update coefficients if no near-end speech in this sample (ie. HCNTR_d = 0) * and we have enough signal to bother trying to update. * -------------------------------------------------------------------------- */ if (!ec->HCNTR_d && /* no near-end speech present */ !(ec->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */ if (ec->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */ /* so loop over all the filter coefficients */ #ifdef MEC2_STATS_DETAILED printk( KERN_INFO "updating coefficients with: ec->Lu_i %9d\n", ec->Lu_i); #endif #ifdef MEC2_STATS ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i; ++ec->cntr_coeff_updates; #endif for (k=0; k < ec->N_d; k++) { /* eq. (7): compute an expectation over M_d samples */ int grad2; grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d, ec->y_s.buf_d + ec->y_s.idx_d + k, DEFAULT_M); /* eq. (7): update the coefficient */ ec->a_i[k] += grad2 / two_beta_i; ec->a_s[k] = ec->a_i[k] >> 16; } } else { #ifdef MEC2_STATS_DETAILED printk( KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I); #endif #ifdef MEC2_STATS ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i; ++ec->cntr_coeff_missedupdates; #endif } } /* paragraph below eq. (15): if no near-end speech in the sample and * the reference signal power estimate > cutoff threshold * then perform residual error suppression */ #ifdef MEC2_STATS_DETAILED if (ec->HCNTR_d == 0) printk( KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); #endif #ifndef NO_ECHO_SUPPRESSOR #ifdef AGGRESSIVE_SUPPRESSOR if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) { for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) { u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); } #ifdef MEC2_STATS_DETAILED printk( KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); #endif #ifdef MEC2_STATS ++ec->cntr_residualcorrected_frames; #endif } #else if (ec->HCNTR_d == 0) { if ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I) { for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) { u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); } #ifdef MEC2_STATS_DETAILED printk( KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); #endif #ifdef MEC2_STATS ++ec->cntr_residualcorrected_frames; #endif } #ifdef MEC2_STATS else { ++ec->cntr_residualcorrected_framesskipped; } #endif } #endif #endif #if 0 /* This will generate a non-linear supression factor, once converted */ if ((ec->HCNTR_d == 0) && ((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) && (ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) { suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(ec->Lu_d/ec->Ly_d) - SUPPR_CEIL / (float)(SUPPR_FLOOR - SUPPR_CEIL); u_suppr = pow(10.0, (suppr_factor) * RES_SUPR_FACTOR / 10.0) * u_suppr; } #endif #ifdef MEC2_STATS /* Periodically dump performance stats */ if ((ec->i_d % MEC2_STATS) == 0) { /* make sure to avoid div0's! */ if (ec->cntr_coeff_missedupdates > 0) ec->avg_Lu_i_toolow = (int)(ec->avg_Lu_i_toolow / ec->cntr_coeff_missedupdates); else ec->avg_Lu_i_toolow = -1; if (ec->cntr_coeff_updates > 0) ec->avg_Lu_i_ok = (ec->avg_Lu_i_ok / ec->cntr_coeff_updates); else ec->avg_Lu_i_ok = -1; printk( KERN_INFO "%d: Near end speech: %5d Residuals corrected/skipped: %5d/%5d Coefficients updated ok/low sig: %3d/%3d Lu_i avg ok/low sig %6d/%5d\n", ec->id, ec->cntr_nearend_speech_frames, ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped, ec->cntr_coeff_updates, ec->cntr_coeff_missedupdates, ec->avg_Lu_i_ok, ec->avg_Lu_i_toolow); ec->cntr_nearend_speech_frames = 0; ec->cntr_residualcorrected_frames = 0; ec->cntr_residualcorrected_framesskipped = 0; ec->cntr_coeff_updates = 0; ec->cntr_coeff_missedupdates = 0; ec->avg_Lu_i_ok = 0; ec->avg_Lu_i_toolow = 0; } #endif /* Increment the sample index and return the corrected sample */ ec->i_d++; return u; } static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) { struct echo_can_state *ec; int maxy; int maxu; maxy = len + DEFAULT_M; maxu = DEFAULT_M; if (maxy < (1 << DEFAULT_ALPHA_YT_I)) maxy = (1 << DEFAULT_ALPHA_YT_I); if (maxy < (1 << DEFAULT_SIGMA_LY_I)) maxy = (1 << DEFAULT_SIGMA_LY_I); if (maxu < (1 << DEFAULT_SIGMA_LU_I)) maxu = (1 << DEFAULT_SIGMA_LU_I); ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) + 4 + /* align */ sizeof(int) * len + /* a_i */ sizeof(short) * len + /* a_s */ 2 * sizeof(short) * (maxy) + /* y_s */ 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ 2 * sizeof(short) * (maxu) + /* u_s */ 2 * sizeof(short) * len); /* y_tilde_s */ if (ec) { memset(ec, 0, sizeof(struct echo_can_state) + 4 + /* align */ sizeof(int) * len + /* a_i */ sizeof(short) * len + /* a_s */ 2 * sizeof(short) * (maxy) + /* y_s */ 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ 2 * sizeof(short) * (maxu) + /* u_s */ 2 * sizeof(short) * len); /* y_tilde_s */ init_cc(ec, len, maxy, maxu); } return ec; } static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) { /* Set the hangover counter to the length of the can to * avoid adjustments occuring immediately after initial forced training */ ec->HCNTR_d = ec->N_d << 1; if (pos >= ec->N_d) return 1; ec->a_i[pos] = val << 17; ec->a_s[pos] = val << 1; if (++pos >= ec->N_d) return 1; return 0; } #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_ecdis.h0000644000000000000500000000733111110524073020472 0ustar rootsrc/* * SpanDSP - a series of DSP components for telephony * * ec_disable_detector.h - A detector which should eventually meet the * G.164/G.165 requirements for detecting the * 2100Hz echo cancellor disable tone. * * Written by Steve Underwood * * Copyright (C) 2001 Steve Underwood * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "dsp_biquad.h" typedef struct { biquad2_state_t notch; int notch_level; int channel_level; int tone_present; int tone_cycle_duration; int good_cycles; int hit; } echo_can_disable_detector_state_t; #define FALSE 0 #define TRUE (!FALSE) static inline void echo_can_disable_detector_init (echo_can_disable_detector_state_t *det) { /* Elliptic notch */ /* This is actually centred at 2095Hz, but gets the balance we want, due to the asymmetric walls of the notch */ biquad2_init (&det->notch, (int32_t) (-0.7600000*32768.0), (int32_t) (-0.1183852*32768.0), (int32_t) (-0.5104039*32768.0), (int32_t) ( 0.1567596*32768.0), (int32_t) ( 1.0000000*32768.0)); det->channel_level = 0; det->notch_level = 0; det->tone_present = FALSE; det->tone_cycle_duration = 0; det->good_cycles = 0; det->hit = 0; } /*- End of function --------------------------------------------------------*/ static inline int echo_can_disable_detector_update (echo_can_disable_detector_state_t *det, int16_t amp) { int16_t notched; notched = biquad2 (&det->notch, amp); /* Estimate the overall energy in the channel, and the energy in the notch (i.e. overall channel energy - tone energy => noise). Use abs instead of multiply for speed (is it really faster?). Damp the overall energy a little more for a stable result. Damp the notch energy a little less, so we don't damp out the blip every time the phase reverses */ det->channel_level += ((abs(amp) - det->channel_level) >> 5); det->notch_level += ((abs(notched) - det->notch_level) >> 4); if (det->channel_level > 280) { /* There is adequate energy in the channel. Is it mostly at 2100Hz? */ if (det->notch_level*6 < det->channel_level) { /* The notch says yes, so we have the tone. */ if (!det->tone_present) { /* Do we get a kick every 450+-25ms? */ if (det->tone_cycle_duration >= 425*8 && det->tone_cycle_duration <= 475*8) { det->good_cycles++; if (det->good_cycles > 2) det->hit = TRUE; } det->tone_cycle_duration = 0; } det->tone_present = TRUE; } else { det->tone_present = FALSE; } det->tone_cycle_duration++; } else { det->tone_present = FALSE; det->tone_cycle_duration = 0; det->good_cycles = 0; } return det->hit; } /*- End of function --------------------------------------------------------*/ /*- End of file ------------------------------------------------------------*/ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_mec2.h0000644000000000000500000002547511135651702020251 0ustar rootsrc/* * Mark's Second Echo Canceller * * Copyright (C) 2002, Digium, Inc. * * This program is free software and may be used and * distributed according to the terms of the GNU * General Public License, incorporated herein by * reference. * */ #ifndef _MARK2_ECHO_H #define _MARK2_ECHO_H #define EC_TYPE "MARK2" #ifdef __KERNEL__ #include #include #define MALLOC(a) kmalloc((a), GFP_ATOMIC) #define FREE(a) kfree(a) #else #include #include #include #include #include #define MALLOC(a) malloc(a) #define FREE(a) free(a) #endif /* Get optimized routines for math */ #include "dsp_arith.h" #ifndef NULL #define NULL 0 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE (!FALSE) #endif #include "dsp_mec2_const.h" /* Circular buffer definition */ typedef struct { int idx_d; int size_d; short *buf_d; /* Twice as large as we need */ } echo_can_cb_s; // class definition // struct echo_can_state { /* Echo canceller definition */ /* absolute time */ int i_d; /* pre-computed constants */ int N_d; int beta2_i; // declare accumulators for power computations // int Ly_i; int Lu_i; // declare an accumulator for the near-end signal detector // int s_tilde_i; int HCNTR_d; // circular buffers and coefficients // int *a_i; short *a_s; echo_can_cb_s y_s; echo_can_cb_s s_s; echo_can_cb_s u_s; echo_can_cb_s y_tilde_s; int y_tilde_i; /* Max memory */ short max_y_tilde; int max_y_tilde_pos; }; static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where) { cb->buf_d = (short *)where; cb->idx_d = 0; cb->size_d = len; } static inline void add_cc_s(echo_can_cb_s *cb, short newval) { /* Can't use modulus because N+M isn't a power of two (generally) */ cb->idx_d--; if (cb->idx_d < (int)0) {cb->idx_d += cb->size_d;} /* Load two copies into memory */ cb->buf_d[cb->idx_d] = newval; cb->buf_d[cb->idx_d + cb->size_d] = newval; } static inline short get_cc_s(echo_can_cb_s *cb, int pos) { /* Load two copies into memory */ return cb->buf_d[cb->idx_d + pos]; } static inline void init_cc(struct echo_can_state *ec, int N, int maxy, int maxu) { void *ptr = ec; unsigned long tmp; /* double-word align past end of state */ ptr += sizeof(struct echo_can_state); tmp = (unsigned long)ptr; tmp += 3; tmp &= ~3L; ptr = (void *)tmp; // reset parameters // ec->N_d = N; ec->beta2_i = DEFAULT_BETA1_I; // allocate coefficient memory // ec->a_i = ptr; ptr += (sizeof(int) * ec->N_d); ec->a_s = ptr; ptr += (sizeof(short) * ec->N_d); /* Reset Y circular buffer (short version) */ init_cb_s(&ec->y_s, maxy, ptr); ptr += (sizeof(short) * (maxy) * 2); /* Reset Sig circular buffer (short version for FIR filter) */ init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr); ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2); init_cb_s(&ec->u_s, maxu, ptr); ptr += (sizeof(short) * maxu * 2); // allocate a buffer for the reference signal power computation // init_cb_s(&ec->y_tilde_s, ec->N_d, ptr); // reset absolute time // ec->i_d = (int)0; // reset the power computations (for y and u) // ec->Ly_i = DEFAULT_CUTOFF_I; ec->Lu_i = DEFAULT_CUTOFF_I; // reset the near-end speech detector // ec->s_tilde_i = 0; ec->y_tilde_i = 0; ec->HCNTR_d = (int)0; // exit gracefully // } static inline void echo_can_free(struct echo_can_state *ec) { FREE(ec); } static inline short echo_can_update(struct echo_can_state *ec, short iref, short isig) { /* declare local variables that are used more than once */ int k; int rs; short u; int Py_i; int two_beta_i; /*************************************************************************** // // flow A on pg. 428 // ***************************************************************************/ /* eq. (16): high-pass filter the input to generate the next value; // push the current value into the circular buffer // // sdc_im1_d = sdc_d; // sdc_d = sig; // s_i_d = sdc_d; // s_d = s_i_d; // s_i_d = (float)(1.0 - gamma_d) * s_i_d + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d); */ /* Delete last sample from power estimate */ ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I; /* push the reference data onto the circular buffer */ add_cc_s(&ec->y_s, iref); /* eq. (2): compute r in fixed-point */ rs = CONVOLVE2(ec->a_s, ec->y_s.buf_d + ec->y_s.idx_d, ec->N_d); rs >>= 15; /* eq. (3): compute the output value (see figure 3) and the error // note: the error is the same as the output signal when near-end // speech is not present */ u = isig - rs; add_cc_s(&ec->u_s, u); /* Delete oldest part of received s_tilde */ ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 )); /* push the signal on the circular buffer, too */ add_cc_s(&ec->s_s, isig); ec->s_tilde_i += abs(isig); ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_YT_I; /* Add to our list of recent y_tilde's */ add_cc_s(&ec->y_tilde_s, ec->y_tilde_i); /**************************************************************************** // // flow B on pg. 428 // ****************************************************************************/ /* compute the new convergence factor */ if (!ec->HCNTR_d) { Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I); Py_i >>= 15; } else { Py_i = (1 << 15); } #if 0 printf("Py: %e, Py_i: %e\n", Py, Py_i * AMPL_SCALE_1); #endif /* Vary rate of adaptation depending on position in the file // Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech // has begun of the file to allow the echo cancellor to estimate the // channel accurately */ #if 0 if (ec->start_speech_d != 0 ){ if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){ ec->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - ec->start_speech_d))); } } else {ec->beta2_d = DEFAULT_BETA1;} #endif ec->beta2_i = DEFAULT_BETA1_I; /* Fixed point, inverted */ two_beta_i = (ec->beta2_i * Py_i) >> 15; /* Fixed point version, inverted */ if (!two_beta_i) two_beta_i++; /* Update Lu_i (Suppressed power estimate) */ ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ; ec->Lu_i += abs(u); /* eq. (10): update power estimate of the reference */ ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ; ec->Ly_i += abs(iref); if (ec->Ly_i < DEFAULT_CUTOFF_I) ec->Ly_i = DEFAULT_CUTOFF_I; #if 0 printf("Float: %e, Int: %e\n", ec->Ly_d, (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * AMPL_SCALE_1); #endif if (ec->y_tilde_i > ec->max_y_tilde) { /* New highest y_tilde with full life */ ec->max_y_tilde = ec->y_tilde_i; ec->max_y_tilde_pos = ec->N_d - 1; } else if (--ec->max_y_tilde_pos < 0) { /* Time to find new max y tilde... */ ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos); } if ((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde) { ec->HCNTR_d = DEFAULT_HANGT; } else if (ec->HCNTR_d > (int)0) { ec->HCNTR_d--; } /* update coefficients if no near-end speech and we have enough signal * to bother trying to update. */ if (!ec->HCNTR_d && !(ec->i_d % DEFAULT_M) && (ec->Lu_i > MIN_UPDATE_THRESH_I)) { // loop over all filter coefficients // for (k=0; kN_d; k++) { // eq. (7): compute an expectation over M_d samples // int grad2; grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d, ec->y_s.buf_d + ec->y_s.idx_d + k, DEFAULT_M); // eq. (7): update the coefficient // ec->a_i[k] += grad2 / two_beta_i; ec->a_s[k] = ec->a_i[k] >> 16; } } /* paragraph below eq. (15): if no near-end speech, // check for residual error suppression */ #ifndef NO_ECHO_SUPPRESSOR #ifdef AGGRESSIVE_SUPPRESSOR #ifdef AGGRESSIVE_TIMELIMIT /* This allows the aggressive suppressor to turn off after set amount of time */ if (ec->i_d > AGGRESSIVE_TIMELIMIT ) { if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) { u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); } } else { #endif if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) { u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); } #ifdef AGGRESSIVE_TIMELIMIT } #endif #else if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) { u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); } #endif #endif #if 0 if ((ec->HCNTR_d == 0) && ((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) && (ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) { suppr_factor = (10/(float)(SUPPR_FLOOR-SUPPR_CEIL))*log(ec->Lu_d/ec->Ly_d) - SUPPR_CEIL/(float)(SUPPR_FLOOR - SUPPR_CEIL); u_suppr = pow(10.0,(suppr_factor)*RES_SUPR_FACTOR/10.0)*u_suppr; } #endif ec->i_d++; return u; } static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) { struct echo_can_state *ec; int maxy; int maxu; maxy = len + DEFAULT_M; maxu = DEFAULT_M; if (maxy < (1 << DEFAULT_ALPHA_YT_I)) maxy = (1 << DEFAULT_ALPHA_YT_I); if (maxy < (1 << DEFAULT_SIGMA_LY_I)) maxy = (1 << DEFAULT_SIGMA_LY_I); if (maxu < (1 << DEFAULT_SIGMA_LU_I)) maxu = (1 << DEFAULT_SIGMA_LU_I); ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) + 4 + /* align */ sizeof(int) * len + /* a_i */ sizeof(short) * len + /* a_s */ 2 * sizeof(short) * (maxy) + /* y_s */ 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ 2 * sizeof(short) * (maxu) + /* u_s */ 2 * sizeof(short) * len); /* y_tilde_s */ if (ec) { memset(ec, 0, sizeof(struct echo_can_state) + 4 + /* align */ sizeof(int) * len + /* a_i */ sizeof(short) * len + /* a_s */ 2 * sizeof(short) * (maxy) + /* y_s */ 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ 2 * sizeof(short) * (maxu) + /* u_s */ 2 * sizeof(short) * len); /* y_tilde_s */ init_cc(ec, len, maxy, maxu); } return ec; } static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) { /* Reset hang counter to avoid adjustments after initial forced training */ ec->HCNTR_d = ec->N_d << 1; if (pos >= ec->N_d) return 1; ec->a_i[pos] = val << 17; ec->a_s[pos] = val << 1; if (++pos >= ec->N_d) return 1; return 0; } #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_mg2ec.h0000644000000000000500000005075711135651702020421 0ustar rootsrc/* * ECHO_CAN_MG2 * * by Michael Gernoth * * Based upon kb1ec.h and mec2.h * * Copyright (C) 2002, Digium, Inc. * * This program is free software and may be used and * distributed according to the terms of the GNU * General Public License, incorporated herein by * reference. * * Additional background on the techniques used in this code can be found in: * * Messerschmitt, David; Hedberg, David; Cole, Christopher; Haoui, Amine; * Winship, Peter; "Digital Voice Echo Canceller with a TMS32020," * in Digital Signal Processing Applications with the TMS320 Family, * pp. 415-437, Texas Instruments, Inc., 1986. * * A pdf of which is available by searching on the document title at http://www.ti.com/ * */ #ifndef _MG2_ECHO_H #define _MG2_ECHO_H #define EC_TYPE "MG2" #ifdef __KERNEL__ #include #include #define MALLOC(a) kmalloc((a), GFP_ATOMIC) #define FREE(a) kfree(a) #else #include #include #include #include #include #define MALLOC(a) malloc(a) #define FREE(a) free(a) #endif #define ABS(a) abs(a!=-32768?a:-32767) #define RESTORE_COEFFS {\ int x;\ memcpy(ec->a_i, ec->c_i, ec->N_d*sizeof(int));\ for (x=0;xN_d;x++) {\ ec->a_s[x] = ec->a_i[x] >> 16;\ }\ ec->backup = BACKUP;\ } /* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */ /* #define MEC2_STATS 4000 */ /* Uncomment to generate per-sample statistics - this will severely degrade system performance and audio quality */ /* #define MEC2_STATS_DETAILED */ /* Uncomment to generate per-call DC bias offset messages */ /* #define MEC2_DCBIAS_MESSAGE */ /* Get optimized routines for math */ #include "dsp_arith.h" /* Bring in definitions for the various constants and thresholds */ #include "dsp_mg2ec_const.h" #define DC_NORMALIZE #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; int lastcount; int backup; #ifdef DC_NORMALIZE int dc_estimate; #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); /* 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) { #if defined(DC_NORMALIZE) && defined(MEC2_DCBIAS_MESSAGE) printk("EC: DC bias calculated: %d V\n", ec->dc_estimate >> 15); #endif FREE(ec); } #ifdef DC_NORMALIZE static short inline dc_removal(int *dc_estimate, short samp) { *dc_estimate += ((((int)samp << 15) - *dc_estimate) >> 9); return samp - (*dc_estimate >> 15); } #endif 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; #ifdef DC_NORMALIZE isig = dc_removal(&ec->dc_estimate, isig); #endif /* 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; if (ec->lastsig == isig) { ec->lastcount++; } else { ec->lastcount = 0; ec->lastsig = isig; } if (isig == 0) { u = 0; } else if (ec->lastcount > 255) { /* We have seen the same input-signal more than 255 times, * we should pass it through uncancelled, as we are likely on hold */ 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 mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dtmf.c0000644000000000000500000005002211135651702017464 0ustar rootsrc/* $Id: dtmf.c,v 1.18 2007/02/13 10:43:45 crich Exp $ * * Linux ISDN subsystem, DTMF tone module * * Author Karsten Keil (kkeil@suse.de) * * based on I4L isdn_audio code * Copyright 2003 by Karsten Keil (kkeil@suse.de) * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at) * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ #include #include "core.h" #include "layer1.h" #include "helper.h" #include "debug.h" #define DTMF_NPOINTS 205 /* Number of samples for DTMF recognition */ typedef struct _dtmf { struct list_head list; u_long Flags; int debug; char last; int idx; int buf[DTMF_NPOINTS]; mISDNinstance_t inst; } dtmf_t; #define FLG_DTMF_ULAW 1 #define FLG_DTMF_ACTIV 2 static u_int debug = 0; #define DEBUG_DTMF_MGR 0x001 #define DEBUG_DTMF_TONE 0x010 #define DEBUG_DTMF_CTRL 0x020 #define DEBUG_DTMF_DETECT 0x100 #define DEBUG_DTMF_KOEFF 0x200 static mISDNobject_t dtmf_obj; static char *mISDN_dtmf_revision = "$Revision: 1.18 $"; /* * Misc. lookup-tables. */ /* ulaw -> signed 16-bit */ static short isdn_audio_ulaw_to_s16[] = { 0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84, 0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84, 0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84, 0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84, 0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804, 0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004, 0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444, 0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844, 0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64, 0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64, 0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74, 0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74, 0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc, 0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c, 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0, 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000, 0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c, 0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c, 0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c, 0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c, 0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc, 0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc, 0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc, 0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc, 0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c, 0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c, 0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c, 0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c, 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084, 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 }; /* alaw -> signed 16-bit */ static short isdn_audio_alaw_to_s16[] = { 0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4, 0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74, 0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4, 0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64, 0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4, 0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4, 0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4, 0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4, 0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64, 0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34, 0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844, 0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24, 0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64, 0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4, 0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964, 0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4, 0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24, 0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94, 0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924, 0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94, 0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24, 0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14, 0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24, 0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14, 0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4, 0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54, 0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4, 0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64, 0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4, 0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4, 0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4, 0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4 }; /* alaw -> ulaw */ static char isdn_audio_alaw_to_ulaw[] = { 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 }; /* ulaw -> alaw */ static char isdn_audio_ulaw_to_alaw[] = { 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a }; #define NCOEFF 8 /* number of frequencies to be analyzed */ #define DTMF_TRESH 4000 /* above this is dtmf */ #define SILENCE_TRESH 200 /* below this is silence */ #define AMP_BITS 9 /* bits per sample, reduced to avoid overflow */ #define LOGRP 0 #define HIGRP 1 /* For DTMF recognition: * 2 * cos(2 * PI * k / N) precalculated for all k */ static int cos2pik[NCOEFF] = { 55813, 53604, 51193, 48591, 38114, 33057, 25889, 18332 }; static char dtmf_matrix[4][4] = { {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} }; static inline void isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n) { #ifdef __i386__ unsigned long d0, d1, d2, d3; __asm__ __volatile__( "cld\n" "1:\tlodsb\n\t" "xlatb\n\t" "stosb\n\t" "loop 1b\n\t" : "=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3) : "0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff) : "memory", "ax"); #else while (n--) *buff = table[*(unsigned char *)buff], buff++; #endif } void isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len) { isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len); } void isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len) { isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len); } /* * Goertzel algorithm. * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/ * for more info. */ static void isdn_audio_goertzel(dtmf_t *dtmf) { int sk[NCOEFF], sk1[NCOEFF], sk2[NCOEFF]; register int sample; int k, n; int thresh, silence; int lgrp,hgrp; char what; memset(sk, 0, NCOEFF*sizeof(int)); memset(sk1, 0, NCOEFF*sizeof(int)); memset(sk2, 0, NCOEFF*sizeof(int)); for (n = 0; n < DTMF_NPOINTS; n++) { sample = dtmf->buf[n]; for (k = 0; k < NCOEFF; k++) sk[k] = sample + ((cos2pik[k] * sk1[k]) >> 15) - sk2[k]; memcpy(sk2, sk1, NCOEFF*sizeof(int)); memcpy(sk1, sk, NCOEFF*sizeof(int)); } thresh = silence = 0; lgrp = hgrp = -1; for (k = 0; k < NCOEFF; k++) { sk[k] >>= 1; sk2[k] >>= 1; /* compute |X(k)|**2 */ /* report overflows. This should not happen. */ /* Comment this out if desired */ if (sk[k] < -32768 || sk[k] > 32767) printk(KERN_DEBUG "dtmf goertzel overflow, sk[%d]=%d\n", k, sk[k]); if (sk2[k] < -32768 || sk2[k] > 32767) printk(KERN_DEBUG "isdn_audio: dtmf goertzel overflow, sk2[%d]=%d\n", k, sk2[k]); sk1[k] = ((sk[k] * sk[k]) >> AMP_BITS) - ((((cos2pik[k] * sk[k]) >> 15) * sk2[k]) >> AMP_BITS) + ((sk2[k] * sk2[k]) >> AMP_BITS); if (sk1[k] > DTMF_TRESH) { if (sk1[k] > thresh) thresh = sk1[k]; } else if (sk1[k] < SILENCE_TRESH) silence++; } if (dtmf->debug & DEBUG_DTMF_KOEFF) printk(KERN_DEBUG "DTMF koeff(%d,%d,%d,%d,%d,%d,%d,%d) range(%d-%d)\n", sk1[0], sk1[1], sk1[2], sk1[3], sk1[4], sk1[5], sk1[6], sk1[7], SILENCE_TRESH, DTMF_TRESH); if (silence == NCOEFF) what = ' '; else { if (thresh > 0) { thresh = thresh >> 4; /* touchtones must match within 12 dB */ for (k = 0; k < NCOEFF; k++) { if (sk1[k] < thresh) continue; /* ignore */ /* good level found. This is allowed only one time per group */ if (k < NCOEFF / 2) { /* lowgroup*/ if (lgrp >= 0) { // Bad. Another tone found. */ lgrp = -1; break; } else lgrp = k; } else { /* higroup */ if (hgrp >= 0) { // Bad. Another tone found. */ hgrp = -1; break; } else hgrp = k - NCOEFF/2; } } if ((lgrp >= 0) && (hgrp >= 0)) { what = dtmf_matrix[lgrp][hgrp]; if (dtmf->last != ' ' && dtmf->last != '.') dtmf->last = what; /* min. 1 non-DTMF between DTMF */ } else what = '.'; } else what = '.'; } if (dtmf->debug & DEBUG_DTMF_DETECT) printk(KERN_DEBUG "DTMF: last(%c) what(%c)\n", dtmf->last, what); if ((what != dtmf->last) && (what != ' ') && (what != '.')) { if (dtmf->debug & DEBUG_DTMF_TONE) printk(KERN_DEBUG "DTMF: tone='%c'\n", what); k = what | DTMF_TONE_VAL; mISDN_queue_data(&dtmf->inst, FLG_MSG_UP, PH_CONTROL | INDICATION, 0, sizeof(int), &k, 0); } dtmf->last = what; } /* * Decode audio stream into signed u16 * start detection if enough data was sampled */ static void isdn_audio_calc_dtmf(dtmf_t *dtmf, struct sk_buff *skb) { int len = skb->len; u_char *p = skb->data; int i; int c; while (len) { c = DTMF_NPOINTS - dtmf->idx; if (c > len) c = len; if (c <= 0) break; for (i = 0; i < c; i++) { if (test_bit(FLG_DTMF_ULAW, &dtmf->Flags)) dtmf->buf[dtmf->idx++] = isdn_audio_ulaw_to_s16[*p++] >> (15 - AMP_BITS); else dtmf->buf[dtmf->idx++] = isdn_audio_alaw_to_s16[*p++] >> (15 - AMP_BITS); } if (dtmf->idx == DTMF_NPOINTS) { isdn_audio_goertzel(dtmf); dtmf->idx = 0; } len -= c; } } static void dtmf_reset(dtmf_t *dtmf) { dtmf->last = ' '; dtmf->idx = 0; } #ifdef OBSOLETE static int dtmf_from_up(mISDNinstance_t *inst, struct sk_buff *skb) { dtmf_t *dtmf; mISDN_head_t *hh; int *data; int err = 0; dtmf = inst->privat; hh = mISDN_HEAD_P(skb); switch(hh->prim) { case (PH_CONTROL | REQUEST): if ((hh->dinfo == 0) && (skb->len >= sizeof(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); break; } else if (*data == DTMF_TONE_STOP) { test_and_clear_bit(FLG_DTMF_ACTIV, &dtmf->Flags); dtmf_reset(dtmf); break; } } /* Fall trough in case of not handled function */ default: return(mISDN_queue_down(inst, 0, skb)); } if (!err) dev_kfree_skb(skb); return(err); } #endif static int dtmf_function(mISDNinstance_t *inst, struct sk_buff *skb) { dtmf_t *dtmf; mISDN_head_t *hh; dtmf = inst->privat; hh = mISDN_HEAD_P(skb); switch(hh->prim) { case (PH_DATA | CONFIRM): hh->prim = DL_DATA | CONFIRM; break; case (DL_DATA_IND): case (PH_DATA_IND): if (test_bit(FLG_DTMF_ACTIV, &dtmf->Flags)) 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; case (PH_ACTIVATE | INDICATION): hh->prim = DL_ESTABLISH | INDICATION; break; case (PH_DEACTIVATE | CONFIRM): hh->prim = DL_RELEASE | CONFIRM; break; case (PH_DEACTIVATE | INDICATION): hh->prim = DL_RELEASE | INDICATION; break; } 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; spin_lock_irqsave(&dtmf_obj.lock, flags); list_del(&dtmf->list); spin_unlock_irqrestore(&dtmf_obj.lock, flags); mISDN_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; u_long flags; if (!st || !pid) return(-EINVAL); if (!(n_dtmf = kmalloc(sizeof(dtmf_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc dtmf_t failed\n"); return(-ENOMEM); } 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, 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 = mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &n_dtmf->inst); if (err) { list_del(&n_dtmf->list); kfree(n_dtmf); } return(err); } #if 0 static int dtmf_status(dtmf_t *dtmf, status_info_dtmf_t *si) { if (!si) return(-EINVAL); memset(si, 0, sizeof(status_info_dtmf_t)); si->len = sizeof(status_info_dtmf_t) - 2*sizeof(int); si->typ = STATUS_INFO_L1; si->protocol = dtmf->inst.pid.protocol[1]; if (test_bit(FLG_L1_ACTIVATED, &dtmf->Flags)) si->status = 1; si->state = dtmf->dtmfm.state; si->Flags = dtmf->Flags; si->T3 = TIMER3_VALUE; si->debug = dtmf->delay; si->debug = dtmf->debug; return(0); } #endif static char MName[] = "DTMF"; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #else module_param (debug, uint, S_IRUGO | S_IWUSR); #endif MODULE_PARM_DESC (debug, "dtmf debug mask"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #endif static int 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) { printk(KERN_WARNING "dtmf_manager prim(%x) no instance\n", prim); return(ret); } switch(prim) { case MGR_CLRSTPARA | INDICATION: break; #ifdef OBSOLETE case MGR_CLONELAYER | REQUEST: break; case MGR_CONNECT | REQUEST: return(mISDN_ConnectIF(inst, arg)); case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: return(mISDN_SetIF(inst, arg, prim, dtmf_from_up, dtmf_from_down, dtmf_l)); 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) printk(KERN_DEBUG "release_dtmf id %x\n", dtmf_l->inst.st->id); release_dtmf(dtmf_l); break; // case MGR_STATUS | REQUEST: // return(dtmf_status(dtmf_l, arg)); default: if (debug & DEBUG_DTMF_MGR) printk(KERN_WARNING "dtmf_manager prim %x not handled\n", prim); return(-EINVAL); } return(0); } static int dtmf_init(void) { int err; printk(KERN_INFO "DTMF modul version %s\n", mISDN_getrev(mISDN_dtmf_revision)); #ifdef MODULE dtmf_obj.owner = THIS_MODULE; #endif 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); } else mISDN_module_register(THIS_MODULE); return(err); } static void dtmf_cleanup(void) { int err; dtmf_t *dtmf, *nd; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&dtmf_obj))) { printk(KERN_ERR "Can't unregister DTMF error(%d)\n", err); } if (!list_empty(&dtmf_obj.ilist)) { printk(KERN_WARNING "dtmf inst list not empty\n"); list_for_each_entry_safe(dtmf, nd, &dtmf_obj.ilist, list) release_dtmf(dtmf); } } module_init(dtmf_init); module_exit(dtmf_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_kb1ec_const.h0000644000000000000500000000516611110524073021602 0ustar rootsrc/* 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 */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/faxl3.c0000644000000000000500000014525211135651702017561 0ustar rootsrc/* $Id: faxl3.c,v 1.8 2007/02/13 10:43:45 crich Exp $ * * Linux ISDN subsystem, Fax Layer 3 * * Author Karsten Keil (kkeil@suse.de) * * Copyright 2003 by Karsten Keil (kkeil@suse.de) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ #include #include "layer1.h" #include "m_capi.h" #include "helper.h" #include "debug.h" static int ttt=180; typedef struct _faxl3 { struct list_head list; spinlock_t lock; u_long state; int debug; mISDNinstance_t inst; u16 options; u16 format; u8 stationID[24]; u8 headline[64]; u8 DIS[12]; u8 CIS[24]; // only max 20 are used u8 NSF[12]; u8 DTC[12]; u8 DCS[12]; u8 CIG[24]; u8 NSC[12]; u_int peer_rate_mask; u_int own_rate_mask; int current_rate_idx; int current_mod; int current_rate; int pending_mod; int pending_rate; int result; struct FsmInst main; struct FsmTimer deltimer; struct FsmTimer timer1; struct FsmInst mod; struct FsmTimer modtimer; struct sk_buff_head downq; struct sk_buff_head dataq; struct sk_buff_head pageq; struct sk_buff_head saveq; struct sk_buff *data_skb; struct sk_buff *page_skb; int entity; int next_id; int maxdatalen; int up_headerlen; int down_headerlen; u32 ncci; // SFFHEADER int pages; u32 offset_lpage; u32 offset_dend; int current_page; // SFF page header u8 page_vres; u8 page_hres; u8 page_code; u8 page_rsv1; u16 page_llen; u16 page_plen; u32 page_oprv; u32 page_onxt; u8 lasttyp; int lastlen; int line_cnt; int page_retry; } faxl3_t; #define FAXL3_STATE_OUTGOING 1 #define FAXL3_STATE_SENT_TIS 2 #define FAXL3_STATE_CAPICONNECT 3 #define FAXL3_STATE_GOT_DIS 8 #define FAXL3_STATE_GOT_CIS 9 #define FAXL3_STATE_GOT_NSF 10 #define FAXL3_STATE_SFFHEADER 16 #define FAXL3_STATE_PAGEHEADER 17 #define FAXL3_STATE_NEWPAGE 18 #define FAXL3_STATE_LASTPAGE 19 #define FAXL3_STATE_CONTINUE 20 #define FAXL3_STATE_HAVEDATA 21 #define FAXL3_STATE_DATABUSY 24 #define FAXL3_STATE_DATALAST 25 #define FAXL3_STATE_DATAREADY 26 #define FAXL3_RESULT_NONE 0 #define FAXL3_RESULT_CFR 1 #define FAXL3_RESULT_FTT 2 #define FAXL3_RESULT_MCF 3 #define FAXL3_RESULT_RTP 4 #define FAXL3_RESULT_RTN 5 static char logbuf[2000]; static int debug = 0; #define DEBUG_FAXL3_FUNC 0x0001 #define DEBUG_FAXL3_MGR 0x0010 #define DEBUG_FAXL3_CFG 0x0020 #define DEBUG_FAXL3_MSG 0x0100 #define DEBUG_FAXL3_SIG 0x0200 #define DEBUG_FAXL3_PAGEPREPARE 0x1000 static mISDNobject_t faxl3_obj; static char *mISDN_faxl3_revision = "$Revision: 1.8 $"; static u_char FaxModulation[] = {24,48,72,74,96,98,122,146}; static u_char FaxModulationTrain[] = {24,48,72,73,96,97,121,145}; static int FaxModulationBaud[] = {2400,4800,7200,7200,9600,9600,12000,14400}; #define MAX_FAXMODULATION_INDEX 7 #define FAXMODULATION_MASK 0xff #define FAXMODM_UNDEF 0x00 #define FAXMODM_V27 0x03 #define FAXMODM_V27_V29 0x17 #define FAXMODM_V27_V29_V33 0x17 //We don't have V.33 definition yet #define FAXMODM_V27_V29_V33_V17 0xff static u_int FaxModulationRates[16] = { FAXMODM_UNDEF, FAXMODM_UNDEF, FAXMODM_V27, FAXMODM_V27_V29, FAXMODM_UNDEF, FAXMODM_UNDEF, FAXMODM_UNDEF, FAXMODM_V27_V29_V33, FAXMODM_UNDEF, FAXMODM_UNDEF, FAXMODM_UNDEF, FAXMODM_V27_V29_V33_V17, FAXMODM_UNDEF, FAXMODM_UNDEF, FAXMODM_UNDEF, FAXMODM_UNDEF }; static u8 FaxModulationRates_DCS[8] = { 0x0, 0x2, 0x3, 0xb, 0x1, 0x9, 0xa, 0x8 }; #define Dxx_TYPE_DIS 0 #define Dxx_TYPE_DTC 1 #define Dxx_TYPE_DCS 2 static void l3m_debug(struct FsmInst *fi, char *fmt, ...); static int send_hdlc_data(faxl3_t *fl3, u8 adr, u8 hcf, u8 fcf, u8 *para, int len); static int sendL4frame(faxl3_t *fl3, int prim, int di, int len, void *arg, struct sk_buff *skb); static int send_capi_msg_ncpi(faxl3_t *fl3, int prim, u16 Info); static int prepare_page_data(faxl3_t *fl3); static int copy_page_data4retry(faxl3_t *fl3); static struct Fsm faxl3fsm = {NULL, 0, 0, NULL, NULL}; enum { ST_L3_IDLE, ST_L3_WAIT_RECVDIS, ST_L3_RECV_DIS, ST_L3_WAIT_SENDDCS, ST_L3_SEND_DCS, ST_L3_WAIT_SENDTRAIN, ST_L3_SEND_TRAIN, ST_L3_WAIT_TRAINSTATE, ST_L3_RECV_TRAINSTATE, ST_L3_WAIT_SENDPAGE, ST_L3_SEND_PAGE, ST_L3_WAIT_PAGESTATE, ST_L3_RECV_PAGESTATE, ST_L3_WAIT_SENDEOP, ST_L3_SEND_EOP, ST_L3_WAIT_RECVMCF, ST_L3_RECV_MCF, ST_L3_WAIT_SENDDCN, ST_L3_SEND_DCN, ST_L3_CLEARING, }; #define FAXL3_STATE_COUNT (ST_L3_CLEARING+1) static char *strfaxl3State[] = { "ST_L3_IDLE", "ST_L3_WAIT_RECVDIS", "ST_L3_RECV_DIS", "ST_L3_WAIT_SENDDCS", "ST_L3_SEND_DCS", "ST_L3_WAIT_SENDTRAIN", "ST_L3_SEND_TRAIN", "ST_L3_WAIT_TRAINSTATE", "ST_L3_RECV_TRAINSTATE", "ST_L3_WAIT_SENDPAGE", "ST_L3_SEND_PAGE", "ST_L3_WAIT_PAGESTATE", "ST_L3_RECV_PAGESTATE", "ST_L3_WAIT_SENDEOP", "ST_L3_SEND_EOP", "ST_L3_WAIT_RECVMCF", "ST_L3_RECV_MCF", "ST_L3_WAIT_SENDDCN", "ST_L3_SEND_DCN", "ST_L3_CLEARING", }; enum { EV_CALL_OUT, EV_MODEM_ACTIV, EV_MODEM_IDLE, EV_MODEM_ERROR, EV_DATA, EV_NEXT_DATA, EV_DELAYTIMER, EV_CLEARING, }; #define FAXL3_EVENT_COUNT (EV_CLEARING + 1) static char *strfaxl3Event[] = { "EV_CALL_OUT", "EV_MODEM_ACTIV", "EV_MODEM_IDLE", "EV_MODEM_ERROR", "EV_DATA", "EV_NEXT_DATA", "EV_DELAYTIMER", "EV_CLEARING", }; static struct Fsm modfsm = {NULL, 0, 0, NULL, NULL}; enum { ST_MOD_NULL, ST_MOD_IDLE, ST_MOD_WAITCONNECT, ST_MOD_CONNECTED, ST_MOD_WAITDISCONNECT, }; #define MOD_STATE_COUNT (ST_MOD_WAITDISCONNECT + 1) static char *strmodState[] = { "ST_MOD_NULL", "ST_MOD_IDLE", "ST_MOD_WAITCONNECT", "ST_MOD_CONNECTED", "ST_MOD_WAITDISCONNECT", }; enum { EV_MOD_READY, EV_MOD_NEW, EV_MOD_CONNECT, EV_MOD_DISCONNECT, EV_MOD_NOCARRIER, EV_MOD_ERROR, EV_MOD_TIMEOUT, }; #define MOD_EVENT_COUNT (EV_MOD_TIMEOUT + 1) static char *strmodEvent[] = { "EV_MOD_READY", "EV_MOD_NEW", "EV_MOD_CONNECT", "EV_MOD_DISCONNECT", "EV_MOD_NOCARRIER", "EV_MOD_ERROR", "EV_MOD_TIMEOUT", }; static int data_next_id(faxl3_t *fl3) { u_long flags; int id; spin_lock_irqsave(&fl3->lock, flags); id = fl3->next_id++; if (id == 0x0fff) fl3->next_id = 1; spin_unlock_irqrestore(&fl3->lock, flags); id |= (fl3->entity << 16); return(id); } static void print_hexdata(faxl3_t *fl3, char *head, int len, char *data) { char *t = logbuf; t += sprintf(logbuf, "%s", head); if (len > 650) len = 650; mISDN_QuickHex(t, data, len); printk(KERN_DEBUG "%s\n", logbuf); } static char *rate_1[16] = { "undef", "undef", "2400,4800 V.27ter", "9600,7200,4800,2400 V.27ter/V.29", "undef", "undef", "undef", "14400,12000,9600,7200,4800,2400 V.27ter/V.29/V.33", "undef", "undef", "undef", "14400,12000,9600,7200,4800,2400 V.27ter/V.29/V.33/V.17", "undef", "undef", "undef", "undef" }; static char *rate_2[16] = { "2400(V.27ter)", "9600(V.29)", "4800(V.27ter)", "7200(V.29)", "14400(V.33)", "undef", "12000(V.33)", "undef", "14400(V.17)", "9600(V.17)", "12000(V.17)", "7200(V.17)", "undef", "undef", "undef", "undef" }; static char *pwidth_1[4] = { "1728/A4", "1728/A4 2048/B4", "1728/A4 2048/B4 2432/A3", "undef" }; static char *pwidth_2[4] = { "1728 A4", "2048 B4", "2432 A3", "undef" }; static char *plength[4] = { "297/A4", "364/B4", "unlimited", "undef" }; static char *minrowtime_1[8] = { "20ms", "5ms", "10ms", "20ms*", "40ms", "40ms*", "10ms*", "0ms" }; static char *minrowtime_2[8] = { "20ms", "5ms", "10ms", " ", "40ms", " ", " ", "0ms" }; static void print_Dxx(faxl3_t *fl3, int typ) { char *ts; u8 *p, v1,v2,v3; switch (typ) { case Dxx_TYPE_DIS: ts = "DIS"; p = fl3->DIS; break; case Dxx_TYPE_DTC: ts = "DTC"; p = fl3->DTC; break; case Dxx_TYPE_DCS: ts = "DCS"; p = fl3->DCS; break; default: int_error(); return; } /* OK byte one is only for group 1/2 compatibility */ printk(KERN_DEBUG "%s: byte1 %02X\n", ts, *p); v1 = (p[1] >> 2) & 0xf; printk(KERN_DEBUG "%s:%s%s %s%s%s\n", ts, (test_bit(8, (u_long *)p) && (typ != Dxx_TYPE_DCS)) ? " SendG3" : "", (test_bit(9, (u_long *)p)) ? " RecvG3" : "", (typ == Dxx_TYPE_DCS) ? rate_2[v1] : rate_1[v1], (test_bit(14, (u_long *)p)) ? " 7,7Row/mm" : "", (test_bit(15, (u_long *)p)) ? " 2-Dim" : ""); v1 = p[2] & 3; v2 = (p[2] >> 2) & 3; v3 = (p[2] >> 4) & 7; printk(KERN_DEBUG "%s: width(%s) plength(%s) MinRow(%s)\n", ts, (typ == Dxx_TYPE_DCS) ? pwidth_2[v1] : pwidth_1[v1], plength[v2], (typ == Dxx_TYPE_DCS) ? minrowtime_2[v3] : minrowtime_1[v3]); if (!test_bit(23, (u_long *)p)) return; if (typ == Dxx_TYPE_DCS) printk(KERN_DEBUG "%s:%s%s%s BS(%s)%s\n", ts, (test_bit(24, (u_long *)p)) ? " 2400" : "", (test_bit(25, (u_long *)p)) ? " uncompressed" : "", (test_bit(26, (u_long *)p)) ? " ECM" : "", (test_bit(27, (u_long *)p)) ? "64" : "256", (test_bit(30, (u_long *)p)) ? " MMR" : ""); else printk(KERN_DEBUG "%s:%s%s%s%s\n", ts, (test_bit(24, (u_long *)p)) ? " 2400" : "", (test_bit(25, (u_long *)p)) ? " uncompressed" : "", (test_bit(26, (u_long *)p)) ? " ECM" : "", (test_bit(30, (u_long *)p)) ? " MMR" : ""); if (!test_bit(31, (u_long *)p)) return; /* byte is reseved */ if (!test_bit(39, (u_long *)p)) return; // TODO if (!test_bit(47, (u_long *)p)) return; // TODO if (!test_bit(55, (u_long *)p)) return; // TODO if (!test_bit(63, (u_long *)p)) return; // TODO } static u8 calc_dtcrate(faxl3_t *fl3) { if ((FAXMODM_V27_V29_V33_V17 & fl3->own_rate_mask) == FAXMODM_V27_V29_V33_V17) return(11); if ((FAXMODM_V27_V29_V33 & fl3->own_rate_mask) == FAXMODM_V27_V29_V33) return(7); if ((FAXMODM_V27_V29 & fl3->own_rate_mask) == FAXMODM_V27_V29) return(3); if ((FAXMODM_V27& fl3->own_rate_mask) == FAXMODM_V27) return(2); return(0); } static u8 calc_dcsrate(faxl3_t *fl3) { if ((fl3->current_rate_idx > MAX_FAXMODULATION_INDEX) || (fl3->current_rate_idx < 0)) { int_errtxt("current_rate_idx(%d)", fl3->current_rate_idx); return(0xf); } return(FaxModulationRates_DCS[fl3->current_rate_idx]); } static void fill_Dxx(faxl3_t *fl3, int typ) { u8 *p, v1,v2,v3; switch (typ) { case Dxx_TYPE_DIS: p = fl3->DIS; break; case Dxx_TYPE_DTC: p = fl3->DTC; break; case Dxx_TYPE_DCS: p = fl3->DCS; break; default: int_error(); return; } memset(p, 0, 12); // clear all bits /* OK byte one is only for group 1/2 compatibility, skipped */ if (typ == Dxx_TYPE_DCS) v1 = calc_dcsrate(fl3); else v1 = calc_dtcrate(fl3); p[1] = v1 << 2; if (typ == Dxx_TYPE_DCS) test_and_set_bit(9, (u_long *)p); else test_and_set_bit(8, (u_long *)p); if (fl3->options & 1) test_and_set_bit(14, (u_long *)p); // TODO: calc test_and_set_bit(14, (u_long *)p); v1 = 0; // A4, TODO: calc v2 = 2; // unlimited, TODO: calc v3 = 7; // 0 ms, TODO: calc p[2] = v1 | (v2 << 2) | (v3 << 4); test_and_set_bit(23, (u_long *)p); // next byte exist p[3] = 0; // TODO: calc } static int send_Dxx(faxl3_t *fl3, int typ, int last) { u8 *p, fcf, hdlc_cf = last ? 0x13 : 3; int len; switch (typ) { case Dxx_TYPE_DIS: p = fl3->DIS; fcf = 80; break; case Dxx_TYPE_DTC: p = fl3->DTC; fcf = 0x81; break; case Dxx_TYPE_DCS: p = fl3->DCS; fcf = 0x83; break; default: int_error(); return(-EINVAL); } if (!test_bit(23, (u_long *)p)) len = 3; else if (!test_bit(31, (u_long *)p)) len = 4; else if (!test_bit(39, (u_long *)p)) len = 5; else if (!test_bit(47, (u_long *)p)) len = 6; else if (!test_bit(55, (u_long *)p)) len = 7; else if (!test_bit(63, (u_long *)p)) len = 8; else len = 9; return(send_hdlc_data(fl3, 0xff, hdlc_cf, fcf, p, len)); } static int send_char20(faxl3_t *fl3, u8 *p, int fcf, int last) { u8 buf[20], *s, hdlc_cf = last ? 0x13 : 3; int len, i; memset(buf, ' ', 20); len = strlen(p); if (len > 20) len = 20; s = buf; for (i=len; i>0; i--) *s++ = p[i-1]; return(send_hdlc_data(fl3, 0xff, hdlc_cf, fcf, buf, 20)); } static int is_valid_rate_idx(faxl3_t *fl3, int ridx) { if ((ridx > MAX_FAXMODULATION_INDEX) || (ridx < 0)) return(0); if (((1<own_rate_mask & fl3->peer_rate_mask) == 0) return(0); else return(1); } static int fallback_rate(faxl3_t *fl3) { fl3->current_rate_idx--; while ((fl3->current_rate_idx >= 0) && !is_valid_rate_idx(fl3, fl3->current_rate_idx)) { fl3->current_rate_idx--; } return(is_valid_rate_idx(fl3, fl3->current_rate_idx)); } static int calc_max_rate(faxl3_t *fl3) { int i; for (i = MAX_FAXMODULATION_INDEX; i >= 0; i--) { if (is_valid_rate_idx(fl3, i)) return(i); } return(-1); } static int send_data_down(faxl3_t *fl3, struct sk_buff *skb) { int ret = 0; mISDNif_t *down = &fl3->inst.down; if (test_and_set_bit(FAXL3_STATE_DATABUSY, &fl3->state)) { skb_queue_tail(&fl3->downq, skb); } else { mISDN_sethead(PH_DATA_REQ, data_next_id(fl3), skb); ret = down->func(down, skb); if (ret) { int_errtxt("down: error(%d)", ret); } } return(ret); } static int send_hdlc_data(faxl3_t *fl3, u8 adr, u8 hcf, u8 fcf, u8 *para, int len) { struct sk_buff *skb; u_char *p; int ret; if (!(skb = alloc_stack_skb(3 + len, 1))) return(-ENOMEM); p = skb_put(skb, 3); *p++ = adr; *p++ = hcf; *p++ = fcf; if (len) memcpy(skb_put(skb, len), para, len); ret = send_data_down(fl3, skb); if (ret) dev_kfree_skb(skb); return(ret); } static void mod_init(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; int err; err = if_link(&fl3->inst.down, PH_ACTIVATE | REQUEST, 0, 0, NULL, 0); if (err) { int_error(); return; } } static void set_new_modulation(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; int err; if ((fl3->pending_mod < 0) || (fl3->pending_rate <0)) { if (event == EV_MOD_READY) mISDN_FsmChangeState(fi, ST_MOD_IDLE); else int_error(); return; } mISDN_FsmChangeState(fi, ST_MOD_WAITCONNECT); err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, fl3->pending_mod, sizeof(int), &fl3->pending_rate, 0); if (err) { int_error(); return; } } static void mod_activ(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; mISDN_FsmChangeState(fi, ST_MOD_CONNECTED); fl3->current_mod = fl3->pending_mod; fl3->pending_mod = -1; fl3->current_rate = fl3->pending_rate; fl3->pending_rate = -1; mISDN_FsmEvent(&fl3->main, EV_MODEM_ACTIV, NULL); } static void mod_disconnect(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_MOD_WAITDISCONNECT); } static void mod_error(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; mISDN_FsmChangeState(fi, ST_MOD_IDLE); mISDN_FsmEvent(&fl3->main, EV_MODEM_ERROR, NULL); } static void mod_nocarrier(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; mISDN_FsmChangeState(fi, ST_MOD_IDLE); mISDN_FsmEvent(&fl3->main, EV_MODEM_IDLE, NULL); } static struct FsmNode ModFnList[] = { {ST_MOD_NULL, EV_MOD_NEW, mod_init}, {ST_MOD_NULL, EV_MOD_READY, set_new_modulation}, {ST_MOD_IDLE, EV_MOD_READY, set_new_modulation}, {ST_MOD_IDLE, EV_MOD_NEW, set_new_modulation}, {ST_MOD_WAITCONNECT, EV_MOD_CONNECT, mod_activ}, {ST_MOD_WAITCONNECT, EV_MOD_ERROR, mod_error}, {ST_MOD_CONNECTED, EV_MOD_NOCARRIER, mod_nocarrier}, {ST_MOD_CONNECTED, EV_MOD_DISCONNECT, mod_disconnect}, {ST_MOD_WAITDISCONNECT, EV_MOD_NOCARRIER, mod_nocarrier}, }; #define MOD_FN_COUNT (sizeof(ModFnList)/sizeof(struct FsmNode)) static void l3m_callout(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_L3_WAIT_RECVDIS); } static void l3m_activ_dis(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_L3_RECV_DIS); } static int get_Dxx(u8 *dst, u8 *src, int len) { if (len < 3) { int_errtxt("Dxx too short %d", len); return(-1); } if (len > 12) len = 12; // normally max 9 bytes memcpy(dst, src, len); return(0); } static int get_CHAR20(u8 *dst, u8 *src, int len) { int i; if (len <= 0) { int_errtxt("string too short %d", len); return(-1); } if (len > 20) { int_errtxt("string too big (%d) rest ignored", len); len = 20; } for (i = 20; i > len; i--) dst[20-i] = ' '; for (; i > 0; i--) dst[20-i] = src[i-1]; dst[20] = 0; return(0); } static int get_Nxx(u8 *dst, u8 *src, int len) { if (len < 2) { int_errtxt("Nxx too short %d", len); return(-1); } if (len > 12) { int_errtxt("Nxx too big (%d) ignored", len); return(-2); } memcpy(dst, src, len); return(0); } static void init_newpage(faxl3_t *fl3) { fl3->page_retry = 0; fl3->line_cnt = 0; fl3->result = 0; discard_queue(&fl3->pageq); discard_queue(&fl3->saveq); test_and_clear_bit(FAXL3_STATE_NEWPAGE, &fl3->state); } static void l3m_receive_dis(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; struct sk_buff *skb = arg; u8 end, *p = skb->data; if (skb->len < 3) { int_errtxt("HDLC too short %d", skb->len); return; } if (*p != 0xff) { int_errtxt("HDLC addr not FF (%02X)", *p); } p++; if (*p == 0x03) { end = 0; } else if (*p == 0x13) { end = 1; } else { int_errtxt("wrong HDLC CTRL (%02X)", *p); } p++; skb_pull(skb, 3); switch(*p) { case 0x80: // DIS if (0 == get_Dxx(fl3->DIS, p+1, skb->len)) test_and_set_bit(FAXL3_STATE_GOT_DIS, &fl3->state); break; case 0x40: // CIS if (0 == get_CHAR20(fl3->CIS, p+1, skb->len)) test_and_set_bit(FAXL3_STATE_GOT_CIS, &fl3->state); break; case 0x20: // NSF if (0 == get_Nxx(fl3->NSF, p+1, skb->len)) test_and_set_bit(FAXL3_STATE_GOT_NSF, &fl3->state); break; default: int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len); break; } } static void l3m_finish_dis(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; if (fl3->debug & DEBUG_FAXL3_SIG) { if (test_bit(FAXL3_STATE_GOT_DIS, &fl3->state)) print_Dxx(fl3, Dxx_TYPE_DIS); if (test_bit(FAXL3_STATE_GOT_CIS, &fl3->state)) printk(KERN_DEBUG "CIS: %s\n", fl3->CIS); if (test_bit(FAXL3_STATE_GOT_NSF, &fl3->state)) print_hexdata(fl3, "NSF: ", 12, fl3->NSF); } fl3->peer_rate_mask = FaxModulationRates[(fl3->DIS[1]>>2) &0xf]; fl3->current_rate_idx = calc_max_rate(fl3); if (fl3->current_rate_idx > 0) { fill_Dxx(fl3, Dxx_TYPE_DCS); if (fl3->debug & DEBUG_FAXL3_SIG) print_Dxx(fl3, Dxx_TYPE_DCS); fl3->pending_mod = HW_MOD_FTH; fl3->pending_rate = 3; mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCS); mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); } else // ABORT mISDN_FsmChangeState(fi, ST_L3_IDLE); } static void l3m_send_dcs(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; mISDN_FsmChangeState(fi, ST_L3_SEND_DCS); if (!test_and_set_bit(FAXL3_STATE_SENT_TIS, &fl3->state)) send_char20(fl3, &fl3->stationID[1], 0x43, 0); send_Dxx(fl3, Dxx_TYPE_DCS, 1); } static void l3m_send_lastdata(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; int err; if (test_and_clear_bit(FAXL3_STATE_DATALAST, &fl3->state)) { err = 0; err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, HW_MOD_LASTDATA, sizeof(int), &err, 0); if (err) { int_error(); return; } } } static void l3m_finish_dcs(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDTRAIN); /* wait 75 ms */ mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2); } static void l3m_setup_train(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) { fl3->pending_mod = HW_MOD_FTM; fl3->pending_rate = FaxModulationTrain[fl3->current_rate_idx]; mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); } else // ABORT mISDN_FsmChangeState(fi, ST_L3_IDLE); } static void l3m_send_train(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; struct sk_buff *skb; int len, ret; mISDN_FsmChangeState(fi, ST_L3_SEND_TRAIN); len = 3*(FaxModulationBaud[fl3->current_rate_idx]/16); // 1,5 sec if (!(skb = alloc_stack_skb(len, 1))) return; memset(skb_put(skb, len), 0, len); test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state); ret = send_data_down(fl3, skb); if (ret) { dev_kfree_skb(skb); } } static void l3m_finish_train(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; fl3->pending_mod = HW_MOD_FRH; fl3->pending_rate = 3; mISDN_FsmChangeState(fi, ST_L3_WAIT_TRAINSTATE); mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); } static void l3m_activ_trainstate(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_L3_RECV_TRAINSTATE); } static void l3m_receive_trainstate(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; struct sk_buff *skb = arg; u8 end, *p = skb->data; if (skb->len < 3) { int_errtxt("HDLC too short %d", skb->len); return; } if (*p != 0xff) { int_errtxt("HDLC addr not FF (%02X)", *p); } p++; if (*p == 0x03) { end = 0; } else if (*p == 0x13) { end = 1; } else { int_errtxt("wrong HDLC CTRL (%02X)", *p); } p++; skb_pull(skb, 3); switch(*p) { case 0x84: // CFR fl3->result = FAXL3_RESULT_CFR; printk(KERN_DEBUG "training successfull\n"); if (!test_and_set_bit(FAXL3_STATE_CAPICONNECT, &fl3->state)) send_capi_msg_ncpi(fl3, CAPI_CONNECT_B3_ACTIVE_IND, 0); break; case 0x44: // FTT fl3->result = FAXL3_RESULT_FTT; printk(KERN_DEBUG "training failed\n"); break; default: fl3->result = FAXL3_RESULT_NONE; int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len); break; } } static void l3m_finish_trainstate(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; if (fl3->result == FAXL3_RESULT_FTT) { fl3->pending_mod = HW_MOD_FTH; fl3->pending_rate = 3; if (fallback_rate(fl3)) { fill_Dxx(fl3, Dxx_TYPE_DCS); mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCS); } else { mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCN); } } else { mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDPAGE); mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2); } } static void l3m_setup_sendpage(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) { fl3->pending_mod = HW_MOD_FTM; fl3->pending_rate = FaxModulation[fl3->current_rate_idx]; mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); } else // ABORT mISDN_FsmChangeState(fi, ST_L3_IDLE); } static void l3m_ready_sendpage(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; struct sk_buff *skb; int ret; mISDN_FsmChangeState(fi, ST_L3_SEND_PAGE); if (!(skb = alloc_stack_skb(4000, 1))) return; // memset(skb_put(skb, 1000), 0xff, 1000); // memset(skb_put(skb, 1000), 0, 1000); // memset(skb_put(skb, 100), 0xff, 100); memset(skb_put(skb, ttt), 0, ttt); test_and_set_bit(FAXL3_STATE_DATAREADY, &fl3->state); ret = send_data_down(fl3, skb); if (ret) { dev_kfree_skb(skb); } } static void l3m_send_next_pagedata(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; struct sk_buff *skb; int err; start: if (test_bit(FAXL3_STATE_DATALAST, &fl3->state)) { if (skb_queue_empty(&fl3->pageq)) { err = 0; err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, HW_MOD_LASTDATA, sizeof(int), &err, 0); if (err) { int_error(); return; } test_and_clear_bit(FAXL3_STATE_DATALAST, &fl3->state); } else { while((skb = skb_dequeue(&fl3->pageq))) send_data_down(fl3, skb); } } else { if (!fl3->page_retry) { prepare_page_data(fl3); } else if (skb_queue_empty(&fl3->pageq)) { test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state); goto start; } if ((skb = skb_dequeue(&fl3->pageq))) send_data_down(fl3, skb); } } static void l3m_finish_page(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; test_and_clear_bit(FAXL3_STATE_DATAREADY, &fl3->state); fl3->pending_mod = HW_MOD_FTH; fl3->pending_rate = 3; mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDEOP); mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); } static void l3m_send_endofpage(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; u8 fcf; mISDN_FsmChangeState(fi, ST_L3_SEND_EOP); if (test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) fcf = 0x2f; // EOP else fcf = 0x4f; // MPS send_hdlc_data(fl3, 0xff, 0x13, fcf, NULL, 0); } static void l3m_finish_eop(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; fl3->pending_mod = HW_MOD_FRH; fl3->pending_rate = 3; mISDN_FsmChangeState(fi, ST_L3_WAIT_RECVMCF); mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); } static void l3m_activ_mcf(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_L3_RECV_MCF); } static void l3m_receive_mcf(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; struct sk_buff *skb = arg; u8 end, *p = skb->data; if (skb->len < 3) { int_errtxt("HDLC too short %d", skb->len); return; } if (*p != 0xff) { int_errtxt("HDLC addr not FF (%02X)", *p); } p++; if (*p == 0x03) { end = 0; } else if (*p == 0x13) { end = 1; } else { int_errtxt("wrong HDLC CTRL (%02X)", *p); } p++; skb_pull(skb, 3); switch(*p) { case 0x8C: // MCF fl3->result = FAXL3_RESULT_MCF; printk(KERN_DEBUG "got MCF\n"); break; case 0xCC: // RTP fl3->result = FAXL3_RESULT_RTP; printk(KERN_DEBUG "got RTP\n"); break; case 0x4C: // RTN fl3->result = FAXL3_RESULT_RTN; printk(KERN_DEBUG "got RTN\n"); break; default: fl3->result = FAXL3_RESULT_NONE; int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len); break; } } static void l3m_finish_mcf(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; int newstate = ST_L3_WAIT_SENDDCN; if (fl3->result == FAXL3_RESULT_RTN) { fl3->page_retry++; if ((fl3->page_retry < 5) && fallback_rate(fl3)) { newstate = ST_L3_WAIT_SENDDCS; fill_Dxx(fl3, Dxx_TYPE_DCS); copy_page_data4retry(fl3); } } else if (fl3->result == FAXL3_RESULT_MCF) { if (!test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) { init_newpage(fl3); prepare_page_data(fl3); mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDPAGE); mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2); return; } } else if (fl3->result == FAXL3_RESULT_RTP) { if (!test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) { init_newpage(fl3); prepare_page_data(fl3); newstate = ST_L3_WAIT_SENDDCS; fill_Dxx(fl3, Dxx_TYPE_DCS); } } else { int_errtxt("unhandled result %d abort", fl3->result); } fl3->pending_mod = HW_MOD_FTH; fl3->pending_rate = 3; mISDN_FsmChangeState(fi, newstate); mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); } static void l3m_send_dcn(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; send_hdlc_data(fl3, 0xff, 0x13, 0xfb, NULL, 0); } static void l3m_finish_dcn(struct FsmInst *fi, int event, void *arg) { faxl3_t *fl3 = fi->userdata; send_capi_msg_ncpi(fl3, CAPI_DISCONNECT_B3_IND, 0); mISDN_FsmChangeState(fi, ST_L3_CLEARING); } static struct FsmNode FaxL3FnList[] = { {ST_L3_IDLE, EV_CALL_OUT, l3m_callout}, {ST_L3_WAIT_RECVDIS, EV_MODEM_ACTIV, l3m_activ_dis}, {ST_L3_RECV_DIS, EV_DATA, l3m_receive_dis}, {ST_L3_RECV_DIS, EV_MODEM_IDLE, l3m_finish_dis}, {ST_L3_WAIT_SENDDCS, EV_MODEM_ACTIV, l3m_send_dcs}, {ST_L3_SEND_DCS, EV_NEXT_DATA, l3m_send_lastdata}, {ST_L3_SEND_DCS, EV_MODEM_IDLE, l3m_finish_dcs}, {ST_L3_WAIT_SENDTRAIN, EV_DELAYTIMER, l3m_setup_train}, {ST_L3_WAIT_SENDTRAIN, EV_MODEM_ACTIV, l3m_send_train}, {ST_L3_SEND_TRAIN, EV_MODEM_IDLE, l3m_finish_train}, {ST_L3_SEND_TRAIN, EV_NEXT_DATA, l3m_send_lastdata}, {ST_L3_WAIT_TRAINSTATE, EV_MODEM_ACTIV, l3m_activ_trainstate}, {ST_L3_RECV_TRAINSTATE, EV_DATA, l3m_receive_trainstate}, {ST_L3_RECV_TRAINSTATE, EV_MODEM_IDLE, l3m_finish_trainstate}, {ST_L3_WAIT_SENDPAGE, EV_DELAYTIMER, l3m_setup_sendpage}, {ST_L3_WAIT_SENDPAGE, EV_MODEM_ACTIV, l3m_ready_sendpage}, {ST_L3_SEND_PAGE, EV_NEXT_DATA, l3m_send_next_pagedata}, {ST_L3_SEND_PAGE, EV_MODEM_IDLE, l3m_finish_page}, {ST_L3_WAIT_SENDEOP, EV_MODEM_ACTIV, l3m_send_endofpage}, {ST_L3_SEND_EOP, EV_NEXT_DATA, l3m_send_lastdata}, {ST_L3_SEND_EOP, EV_MODEM_IDLE, l3m_finish_eop}, {ST_L3_WAIT_RECVMCF, EV_MODEM_ACTIV, l3m_activ_mcf}, {ST_L3_RECV_MCF, EV_DATA, l3m_receive_mcf}, {ST_L3_RECV_MCF, EV_MODEM_IDLE, l3m_finish_mcf}, {ST_L3_WAIT_SENDDCN, EV_MODEM_ACTIV, l3m_send_dcn}, {ST_L3_SEND_DCN, EV_NEXT_DATA, l3m_send_lastdata}, {ST_L3_SEND_DCN, EV_MODEM_IDLE, l3m_finish_dcn}, }; #define FAXL3_FN_COUNT (sizeof(FaxL3FnList)/sizeof(struct FsmNode)) static int data_b3_conf(faxl3_t *fl3, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); u8 buf[4]; hh++; capimsg_setu16(buf, 0, hh->prim); // datahandle hh--; capimsg_setu16(buf, 2, 0); // Info sendL4frame(fl3, CAPI_DATA_B3_CONF, hh->dinfo, 4, buf, NULL); dev_kfree_skb(skb); return(0); } static int copy_page_data4retry(faxl3_t *fl3) { struct sk_buff_head tmpq; struct sk_buff *skb, *nskb; int err = 0; skb_queue_head_init(&tmpq); discard_queue(&fl3->pageq); while((skb = skb_dequeue(&fl3->saveq))) { nskb = skb_clone(skb, GFP_ATOMIC); skb_queue_tail(&fl3->pageq, skb); if (!nskb) { int_error(); err = -ENOMEM; } else skb_queue_tail(&tmpq, nskb); } if (err) { discard_queue(&tmpq); } else { while((skb = skb_dequeue(&tmpq))) skb_queue_tail(&fl3->saveq, skb); } return(err); } #define PAGE_SKB_LEN 1024 static int collect_page_data(faxl3_t *fl3, int len, u8 *buf, int flush) { int l = len; struct sk_buff *skb; if (!fl3->page_skb) { fl3->page_skb = alloc_stack_skb(PAGE_SKB_LEN, 1); if (!fl3->page_skb) return(-ENOMEM); } if ((fl3->page_skb->len + len) >= PAGE_SKB_LEN) { l = PAGE_SKB_LEN - fl3->page_skb->len; memcpy(skb_put(fl3->page_skb, l), buf, l); buf += l; skb = skb_clone(fl3->page_skb, GFP_ATOMIC); if (!skb) { int_error(); } else { // for resend pages skb_queue_tail(&fl3->saveq, skb); } skb_queue_tail(&fl3->pageq, fl3->page_skb); fl3->page_skb = alloc_stack_skb(PAGE_SKB_LEN, 1); if (!fl3->page_skb) { int_error(); return(-ENOMEM); } l = len - l; } if (l) { memcpy(skb_put(fl3->page_skb, l), buf, l); } if (flush) { skb = skb_clone(fl3->page_skb, GFP_ATOMIC); if (!skb) { int_error(); } else { // for resend pages skb_queue_tail(&fl3->saveq, skb); } skb_queue_tail(&fl3->pageq, fl3->page_skb); fl3->page_skb = NULL; } return(0); } static int fill_empty_lines(faxl3_t *fl3, int cnt) { u8 buf[4], *p; int l,ret = 0; if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) printk(KERN_DEBUG "%s %d\n", __FUNCTION__, cnt); p = buf; if (fl3->page_llen == 1728) { *p++ = 0x00; *p++ = 0x40; *p++ = 0xd9; *p++ = 0xa4; l = 4; } else { int_error(); return(-EINVAL); } while(cnt) { ret = collect_page_data(fl3, l, buf, 0); fl3->line_cnt++; cnt--; } return(ret); } static int fill_rtc(faxl3_t *fl3) { u8 buf[8] = {0, 0x08, 0x80,}; int cnt = 3, ret = 0; if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) printk(KERN_DEBUG "%s\n", __FUNCTION__); while(cnt) { ret = collect_page_data(fl3, 3, buf, 0); cnt--; } memset(buf, 0 ,8); ret = collect_page_data(fl3, 8, buf, 1); return(ret); } static int fill_line(faxl3_t *fl3, int cnt1, u8 *data1, int cnt2, u8 *data2) { u8 eol[2] = {0x00, 0x80}; int ret = 0; if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) printk(KERN_DEBUG "%s: %d/%d\n", __FUNCTION__, cnt1, cnt2); ret = collect_page_data(fl3, 2, eol, 0); if (cnt1) ret = collect_page_data(fl3, cnt1, data1, 0); if (cnt2) ret = collect_page_data(fl3, cnt2, data2, 0); fl3->line_cnt++; return(ret); } static int next_data_skb(faxl3_t *fl3) { struct sk_buff *skb; int cnt = 3, ret = 0; skb = skb_dequeue(&fl3->dataq); if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) printk(KERN_DEBUG "%s: %p/%p %d %d %lx\n", __FUNCTION__, fl3->data_skb, skb, fl3->lasttyp, fl3->lastlen, fl3->state); if (!skb) { if (fl3->data_skb && (fl3->data_skb->len == 0)) { data_b3_conf(fl3, fl3->data_skb); fl3->data_skb = NULL; test_and_clear_bit(FAXL3_STATE_HAVEDATA, &fl3->state); } return(-EAGAIN); } if (fl3->data_skb) { if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE) printk(KERN_DEBUG "%s: len(%d) hl(%d)\n", __FUNCTION__, fl3->data_skb->len, skb_headroom(skb)); if (fl3->data_skb->len) { if (fl3->data_skb->len <= skb_headroom(skb)) { memcpy(skb_push(skb, fl3->data_skb->len), fl3->data_skb->data, fl3->data_skb->len); skb_pull(fl3->data_skb, fl3->data_skb->len); } if (test_and_clear_bit(FAXL3_STATE_CONTINUE, &fl3->state)) { cnt = fl3->lastlen - fl3->data_skb->len; fill_line(fl3, fl3->data_skb->len, fl3->data_skb->data, cnt, skb->data); skb_pull(fl3->data_skb, fl3->data_skb->len); skb_pull(skb, cnt); } if (fl3->data_skb->len) { int_error(); return(-EINVAL); } } data_b3_conf(fl3, fl3->data_skb); } fl3->data_skb = skb; return(ret); } static int prepare_page_data(faxl3_t *fl3) { u32 tmp32; u16 tmp16; u8 ver, len8; if (!fl3->data_skb) { fl3->data_skb = skb_dequeue(&fl3->dataq); if (!fl3->data_skb) return(-EAGAIN); } if (test_bit(FAXL3_STATE_CONTINUE, &fl3->state)) { if (next_data_skb(fl3)) return(-EAGAIN); } if (!test_and_set_bit(FAXL3_STATE_SFFHEADER, &fl3->state)) { if (fl3->data_skb->len < 0x14) { int_error(); return(-EINVAL); } tmp32 = CAPIMSG_U32(fl3->data_skb->data, 0); ver = CAPIMSG_U8(fl3->data_skb->data, 4); len8 = CAPIMSG_U8(fl3->data_skb->data, 5); tmp16 = CAPIMSG_U16(fl3->data_skb->data, 6); printk(KERN_DEBUG "SFFHEADER(%x,%x,%x,%x)\n", tmp32, ver, len8, tmp16); if (tmp32 != 0x66666653) { // SFFF int_error(); return(-EINVAL); } if (ver != 1) { // only ver 1 supported int_error(); return(-EINVAL); } fl3->pages = CAPIMSG_U16(fl3->data_skb->data, 8); tmp16 = CAPIMSG_U16(fl3->data_skb->data, 10); fl3->offset_lpage = CAPIMSG_U32(fl3->data_skb->data, 12); fl3->offset_dend = CAPIMSG_U32(fl3->data_skb->data, 16); printk(KERN_DEBUG "SFFHEADER pages %d ofP(%x) o_lpage(%x) o_dend(%x)\n", fl3->pages, tmp16, fl3->offset_lpage, fl3->offset_dend); if (tmp16 != 0x14) { int_error(); return(-EINVAL); } skb_pull(fl3->data_skb, 0x14); } if (fl3->data_skb->len < 2) { if (next_data_skb(fl3)) return(-EAGAIN); } while (fl3->data_skb->len > 1) { fl3->lasttyp = CAPIMSG_U8(fl3->data_skb->data, 0); fl3->lastlen = 0; if (fl3->lasttyp == 255) { int_error(); return(-EINVAL); } else if (fl3->lasttyp == 254) { // page header len8 = CAPIMSG_U8(fl3->data_skb->data, 1); printk(KERN_DEBUG "current page end: %d lines\n", fl3->line_cnt); if (len8 == 0) { // doc end printk(KERN_DEBUG "SFF doc end found\n"); test_and_set_bit(FAXL3_STATE_LASTPAGE, &fl3->state); test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state); skb_pull(fl3->data_skb, 2); fill_rtc(fl3); // TODO clean up skb return(0); } if (test_and_set_bit(FAXL3_STATE_NEWPAGE, &fl3->state)) { // next page fill_rtc(fl3); test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state); return(0); } if (fl3->data_skb->len < (2 + len8)) { if (next_data_skb(fl3)) return(-EAGAIN); } printk(KERN_DEBUG "SFF page header len %d\n", len8); if (len8 < 10) { int_error(); return(-EINVAL); } fl3->page_vres = CAPIMSG_U8(fl3->data_skb->data, 2); fl3->page_hres = CAPIMSG_U8(fl3->data_skb->data, 3); fl3->page_code = CAPIMSG_U8(fl3->data_skb->data, 4); fl3->page_rsv1 = CAPIMSG_U8(fl3->data_skb->data, 5); fl3->page_llen = CAPIMSG_U16(fl3->data_skb->data, 6); fl3->page_plen = CAPIMSG_U16(fl3->data_skb->data, 8); fl3->page_oprv = CAPIMSG_U32(fl3->data_skb->data, 10); fl3->page_onxt = CAPIMSG_U32(fl3->data_skb->data, 14); skb_pull(fl3->data_skb, len8 +2); printk(KERN_DEBUG "SFF page header: vres(%x) hres(%x) code(%x) resrv(%x)\n", fl3->page_vres, fl3->page_hres, fl3->page_code, fl3->page_rsv1); printk(KERN_DEBUG "SFF page header: llen(%d) plen(%d) op(%x) on(%x)\n", fl3->page_llen, fl3->page_plen, fl3->page_oprv, fl3->page_onxt); continue; } else if (fl3->lasttyp == 0) { if (fl3->data_skb->len < 3) { if (next_data_skb(fl3)) return(-EAGAIN); } fl3->lastlen = CAPIMSG_U16(fl3->data_skb->data, 1); skb_pull(fl3->data_skb, 3); } else if (fl3->lasttyp < 216) { fl3->lastlen = fl3->lasttyp; skb_pull(fl3->data_skb, 1); } else if (fl3->lasttyp < 253) { // white lines skb_pull(fl3->data_skb, 1); fill_empty_lines(fl3, fl3->lasttyp - 216); continue; } if (fl3->data_skb->len < fl3->lastlen) { test_and_set_bit(FAXL3_STATE_CONTINUE, &fl3->state); break; } fill_line(fl3, fl3->lastlen, fl3->data_skb->data, 0, NULL); skb_pull(fl3->data_skb, fl3->lastlen); } return(0); } static u16 data_b3_req(faxl3_t *fl3, struct sk_buff *skb) { __u16 size; mISDN_head_t *hh = mISDN_HEAD_P(skb); if (skb->len < 10) { int_errtxt("skb too short"); return(0x2007); } size = CAPIMSG_U16(skb->data, 4); /* we save DataHandle and Flags in a area after normal mISDN_HEAD */ hh++; hh->prim = CAPIMSG_U16(skb->data, 6); hh->dinfo = CAPIMSG_U16(skb->data, 8); /* the data begins behind the header, we don't use Data32/Data64 here */ if ((skb->len - size) == 18) skb_pull(skb, 18); else if ((skb->len - size) == 10) // old format skb_pull(skb, 10); else { int_errtxt("skb data_b3 header len mismatch len %d", skb->len - size); return(0x2007); } if (test_and_set_bit(FAXL3_STATE_HAVEDATA, &fl3->state)) { skb_queue_tail(&fl3->dataq, skb); } else { fl3->data_skb = skb; prepare_page_data(fl3); } return(0); } static int data_b3_resp(faxl3_t *faxl3, u_int di, struct sk_buff *skb) { dev_kfree_skb(skb); return(0); } static int connect_b3_req(faxl3_t *fl3, u_int di, u32 addr, struct sk_buff *skb) { u16 info = 0; print_hexdata(fl3, "NCPI: ", skb->len, skb->data); fl3->pending_mod = HW_MOD_FRH; fl3->pending_rate = 3; mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL); mISDN_FsmEvent(&fl3->main, EV_CALL_OUT, NULL); fl3->ncci = 0x10000 | addr; skb_push(skb, 4); sendL4frame(fl3, CAPI_CONNECT_B3_CONF, di, 2, &info, skb); return(0); } static int sendL4frame(faxl3_t *fl3, int prim, int di, int len, void *arg, struct sk_buff *skb) { u_char *p; int ret; if (!skb) { skb = alloc_stack_skb(len + 20, fl3->up_headerlen); if (!skb) return(-ENOMEM); } else { skb_trim(skb, 0); } capimsg_setu32(skb_put(skb, 4), 0, fl3->ncci); switch(prim) { case CAPI_CONNECT_B3_CONF: capimsg_setu16(skb_put(skb, 2), 0, *((u16 *)arg)); break; case CAPI_DISCONNECT_B3_IND: // capimsg_setu16(skb_put(skb, 2), 0, flags & 0xffff); case CAPI_CONNECT_B3_IND: case CAPI_RESET_B3_IND: case CAPI_CONNECT_B3_ACTIVE_IND: case CAPI_DATA_B3_CONF: if (len) { p = skb_put(skb, len); memcpy(p, arg, len); } else { p = skb_put(skb, 1); *p = 0; } break; default: int_error(); dev_kfree_skb(skb); return(-EINVAL); } ret = if_newhead(&fl3->inst.up, prim, di, skb); if (ret) { printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret); dev_kfree_skb(skb); } return(ret); } static int send_capi_msg_ncpi(faxl3_t *fl3, int prim, u16 Info) { u8 ncpi[36], *p, off=0, len = 0; u16 lastmod = 0; memset(ncpi, 0, 36); switch(prim) { case CAPI_CONNECT_B3_ACTIVE_IND: if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) lastmod = FaxModulationBaud[fl3->current_rate_idx]; p = fl3->CIS; break; case CAPI_DISCONNECT_B3_IND: if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) lastmod = FaxModulationBaud[fl3->current_rate_idx]; p = fl3->CIS; capimsg_setu16(ncpi, 0, Info); off = 2; break; default: int_error(); return(-EINVAL); } capimsg_setu16(ncpi, off+1, lastmod); capimsg_setu16(ncpi, off+3, 0x8000); // FIXME no ECM capimsg_setu16(ncpi, off+5, 0); capimsg_setu16(ncpi, off+7, 0); len = strlen(p); if (len > 20) len = 20; capimsg_setu8(ncpi, off+9, len); if (len) memcpy(&ncpi[off+10], p, len); len += 9; // 8*u16 + lenfield capimsg_setu8(ncpi, off, len); return(sendL4frame(fl3, prim, 0, len + off, ncpi, NULL)); } static int faxl3_from_up(mISDNif_t *hif, struct sk_buff *skb) { faxl3_t *faxl3; mISDN_head_t *hh; __u32 addr; __u16 info = 0; int err = 0; if (!hif || !hif->fdata || !skb) return(-EINVAL); faxl3 = hif->fdata; if (!faxl3->inst.down.func) { return(-ENXIO); } hh = mISDN_HEAD_P(skb); if (faxl3->debug & DEBUG_FAXL3_FUNC) 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); } else { addr = CAPIMSG_U32(skb->data, 0); skb_pull(skb, 4); } if (faxl3->debug & DEBUG_FAXL3_FUNC) printk(KERN_DEBUG "%s: addr(%x)\n", __FUNCTION__, addr); switch(hh->prim) { case CAPI_DATA_B3_REQ: info = data_b3_req(faxl3, skb); if (info) { } else err = 0; break; case CAPI_DATA_B3_RESP: return(data_b3_resp(faxl3, hh->dinfo, skb)); case CAPI_CONNECT_B3_REQ: return(connect_b3_req(faxl3, hh->dinfo, addr, skb)); case CAPI_RESET_B3_REQ: break; case CAPI_DISCONNECT_B3_REQ: break; case CAPI_CONNECT_B3_RESP: if (skb->len <= 2) { printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP skb too short (%d)\n", __FUNCTION__, skb->len); skb_push(skb, 4); return(-EINVAL); } info = CAPIMSG_U16(skb->data, 0); skb_pull(skb, 2); if (info == 0) ; if (skb->len <= 4) { // default NCPI } else { } dev_kfree_skb(skb); err = 0; break; case CAPI_CONNECT_B3_ACTIVE_RESP: // nothing to do dev_kfree_skb(skb); err = 0; break; case CAPI_RESET_B3_RESP: dev_kfree_skb(skb); err = 0; break; case CAPI_DISCONNECT_B3_RESP: dev_kfree_skb(skb); err = 0; break; default: printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n", __FUNCTION__, hh->prim, hh->dinfo); err = -EINVAL; } return(err); } static void ph_status_ind(faxl3_t *fl3, int status) { switch(status) { case HW_MOD_READY: mISDN_FsmEvent(&fl3->mod, EV_MOD_READY, NULL); break; case HW_MOD_CONNECT: mISDN_FsmEvent(&fl3->mod, EV_MOD_CONNECT, NULL); break; case HW_MOD_OK: case HW_MOD_NOCARR: mISDN_FsmEvent(&fl3->mod, EV_MOD_NOCARRIER, NULL); break; default: int_errtxt("unhandled status(%x)", status); break; } } static void ph_data_cnf(faxl3_t *fl3, int status) { struct sk_buff *skb ; mISDNif_t *down = &fl3->inst.down; int ret; if (!test_bit(FAXL3_STATE_DATABUSY, &fl3->state)) { int_errtxt("PH_DATA | CONFIRM without DATABUSY"); return; } skb = skb_dequeue(&fl3->downq); if ((skb == NULL) && test_bit(FAXL3_STATE_DATAREADY, &fl3->state)) { skb = skb_dequeue(&fl3->pageq); } if (skb) { mISDN_sethead(PH_DATA_REQ, data_next_id(fl3), skb); ret = down->func(down, skb); if (ret) { dev_kfree_skb(skb); int_errtxt("down: error(%d)", ret); return; } } else { test_and_clear_bit(FAXL3_STATE_DATABUSY, &fl3->state); mISDN_FsmEvent(&fl3->main, EV_NEXT_DATA, NULL); } } static int faxl3_from_down(mISDNif_t *hif, struct sk_buff *skb) { faxl3_t *faxl3; mISDN_head_t *hh; int err = 0; if (!hif || !hif->fdata || !skb) return(-EINVAL); faxl3 = hif->fdata; if (!faxl3->inst.up.func) { return(-ENXIO); } hh = mISDN_HEAD_P(skb); if (faxl3->debug & DEBUG_FAXL3_FUNC) printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len); switch(hh->prim) { case PH_ACTIVATE | INDICATION: case PH_ACTIVATE | CONFIRM: break; case PH_STATUS | INDICATION: ph_status_ind(faxl3, hh->dinfo); break; case PH_DATA | INDICATION: mISDN_FsmEvent(&faxl3->main, EV_DATA, skb); break; case PH_DATA | CONFIRM: ph_data_cnf(faxl3, hh->dinfo); break; default: printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n", __FUNCTION__, hh->prim, hh->dinfo); err = -EINVAL; break; } if (!err) dev_kfree_skb(skb); return(err); } static char MName[] = "FAXL3"; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(ttt, uint, S_IRUGO | S_IWUSR); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #endif static void l3m_debug(struct FsmInst *fi, char *fmt, ...) { faxl3_t *fl3 = fi->userdata; logdata_t log; va_start(log.args, fmt); log.fmt = fmt; log.head = fl3->inst.name; mISDN_ctrl(&fl3->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } static void release_faxl3(faxl3_t *faxl3) { mISDNinstance_t *inst = &faxl3->inst; if (inst->up.peer) { mISDN_ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); } if (inst->down.peer) { mISDN_ctrl(inst->down.peer, MGR_DISCONNECT | REQUEST, &inst->down); } list_del(&faxl3->list); discard_queue(&faxl3->downq); discard_queue(&faxl3->dataq); discard_queue(&faxl3->pageq); discard_queue(&faxl3->saveq); mISDN_FsmDelTimer(&faxl3->deltimer, 99); mISDN_FsmDelTimer(&faxl3->timer1, 99); mISDN_FsmDelTimer(&faxl3->modtimer, 99); if (faxl3->entity != MISDN_ENTITY_NONE) mISDN_ctrl(inst, MGR_DELENTITY | REQUEST, (void *)faxl3->entity); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); kfree(faxl3); } static int new_faxl3(mISDNstack_t *st, mISDN_pid_t *pid) { faxl3_t *n_faxl3; u8 *p; int err; if (!st || !pid) return(-EINVAL); if (!(n_faxl3 = kmalloc(sizeof(faxl3_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc faxl3_t failed\n"); return(-ENOMEM); } memset(n_faxl3, 0, sizeof(faxl3_t)); n_faxl3->entity = MISDN_ENTITY_NONE; n_faxl3->next_id = 1; spin_lock_init(&n_faxl3->lock); memcpy(&n_faxl3->inst.pid, pid, sizeof(mISDN_pid_t)); if (n_faxl3->inst.pid.global == 1) test_and_set_bit(FAXL3_STATE_OUTGOING, &n_faxl3->state); p = n_faxl3->inst.pid.param[3]; if (p) { if (*p < 6) { int_errtxt("B3cfg too shoort(%d)", *p); } else { n_faxl3->options = CAPIMSG_U16(p, 1); n_faxl3->format = CAPIMSG_U16(p, 3); p += 5; if (*p && (*p <= 20)) memcpy(n_faxl3->stationID, p, *p + 1); p += (*p +1); if (*p && (*p <= 62)) memcpy(n_faxl3->headline, p, *p + 1); } } if (debug & DEBUG_FAXL3_CFG) { printk(KERN_DEBUG "%s opt(%x) fmt(%x) id=%s head=%s\n", test_bit(FAXL3_STATE_OUTGOING, &n_faxl3->state) ? "out" : "in", n_faxl3->options, n_faxl3->format, &n_faxl3->stationID[1], &n_faxl3->headline[1]); if (n_faxl3->inst.pid.param[1]) { p = n_faxl3->inst.pid.param[1]; printk(KERN_DEBUG "B1 param len %d rate %d\n", *p, (*p > 1) ? *((u16 *)(p+1)) : -1); } } mISDN_init_instance(&n_faxl3->inst, &faxl3_obj, n_faxl3); if (!mISDN_SetHandledPID(&faxl3_obj, &n_faxl3->inst.pid)) { int_error(); kfree(n_faxl3); return(-ENOPROTOOPT); } n_faxl3->own_rate_mask = FAXMODM_V27_V29_V33_V17; n_faxl3->current_rate_idx = -1; n_faxl3->current_mod = -1; n_faxl3->current_rate = -1; n_faxl3->pending_mod = -1; n_faxl3->pending_rate = -1; n_faxl3->debug = debug; n_faxl3->main.fsm = &faxl3fsm; n_faxl3->main.state = ST_L3_IDLE; n_faxl3->main.debug = debug; n_faxl3->main.userdata = n_faxl3; n_faxl3->main.userint = 0; n_faxl3->main.printdebug = l3m_debug; mISDN_FsmInitTimer(&n_faxl3->main, &n_faxl3->deltimer); mISDN_FsmInitTimer(&n_faxl3->main, &n_faxl3->timer1); n_faxl3->mod.fsm = &modfsm; n_faxl3->mod.state = ST_MOD_NULL; n_faxl3->mod.debug = debug; n_faxl3->mod.userdata = n_faxl3; n_faxl3->mod.userint = 0; n_faxl3->mod.printdebug = l3m_debug; mISDN_FsmInitTimer(&n_faxl3->mod, &n_faxl3->modtimer); skb_queue_head_init(&n_faxl3->downq); skb_queue_head_init(&n_faxl3->dataq); skb_queue_head_init(&n_faxl3->pageq); skb_queue_head_init(&n_faxl3->saveq); list_add_tail(&n_faxl3->list, &faxl3_obj.ilist); n_faxl3->entity = MISDN_ENTITY_NONE; err = mISDN_ctrl(&n_faxl3->inst, MGR_NEWENTITY | REQUEST, NULL); if (err) { printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%d)\n", __FUNCTION__, err); } err = mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &n_faxl3->inst); if (err) { list_del(&n_faxl3->list); kfree(n_faxl3); } else { if (st->para.maxdatalen) n_faxl3->maxdatalen = st->para.maxdatalen; if (st->para.up_headerlen) n_faxl3->up_headerlen = st->para.up_headerlen; if (st->para.down_headerlen) n_faxl3->down_headerlen = st->para.down_headerlen; if (debug) printk(KERN_DEBUG "%s:mlen(%d) hup(%d) hdown(%d)\n", __FUNCTION__, n_faxl3->maxdatalen, n_faxl3->up_headerlen, n_faxl3->down_headerlen); } return(err); } static int faxl3_manager(void *data, u_int prim, void *arg) { mISDNinstance_t *inst = data; faxl3_t *faxl3_l; int err = -EINVAL; if (debug & DEBUG_FAXL3_MGR) printk(KERN_DEBUG "faxl3_manager data:%p prim:%x arg:%p\n", data, prim, arg); if (!data) return(err); list_for_each_entry(faxl3_l, &faxl3_obj.ilist, list) { if (&faxl3_l->inst == inst) { err = 0; break; } } if (prim == (MGR_NEWLAYER | REQUEST)) return(new_faxl3(data, arg)); if (err) { printk(KERN_WARNING "faxl3_manager prim(%x) no instance\n", prim); return(err); } switch(prim) { case MGR_CLRSTPARA | INDICATION: case MGR_CLONELAYER | REQUEST: break; case MGR_ADDSTPARA | INDICATION: { mISDN_stPara_t *stp = arg; if (stp->down_headerlen) faxl3_l->down_headerlen = stp->down_headerlen; if (stp->up_headerlen) faxl3_l->up_headerlen = stp->up_headerlen; printk(KERN_DEBUG "MGR_ADDSTPARA: (%d/%d/%d)\n", stp->maxdatalen, stp->down_headerlen, stp->up_headerlen); } break; case MGR_CONNECT | REQUEST: return(mISDN_ConnectIF(inst, arg)); case MGR_NEWENTITY | CONFIRM: faxl3_l->entity = (int)arg; break; case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: return(mISDN_SetIF(inst, arg, prim, faxl3_from_up, faxl3_from_down, faxl3_l)); case MGR_DISCONNECT | REQUEST: case MGR_DISCONNECT | INDICATION: return(mISDN_DisConnectIF(inst, arg)); case MGR_UNREGLAYER | REQUEST: case MGR_RELEASE | INDICATION: if (debug & DEBUG_FAXL3_MGR) printk(KERN_DEBUG "release_faxl3 id %x\n", faxl3_l->inst.st->id); release_faxl3(faxl3_l); break; default: if (debug & DEBUG_FAXL3_MGR) printk(KERN_WARNING "faxl3_manager prim %x not handled\n", prim); return(-EINVAL); } return(0); } static int faxl3_init(void) { int err; printk(KERN_INFO "%s modul version %s\n", MName, mISDN_getrev(mISDN_faxl3_revision)); #ifdef MODULE faxl3_obj.owner = THIS_MODULE; #endif faxl3_obj.name = MName; faxl3_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_T30; faxl3_obj.own_ctrl = faxl3_manager; INIT_LIST_HEAD(&faxl3_obj.ilist); if ((err = mISDN_register(&faxl3_obj))) { printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); return(err); } faxl3fsm.state_count = FAXL3_STATE_COUNT; faxl3fsm.event_count = FAXL3_EVENT_COUNT; faxl3fsm.strEvent = strfaxl3Event; faxl3fsm.strState = strfaxl3State; mISDN_FsmNew(&faxl3fsm, FaxL3FnList, FAXL3_FN_COUNT); modfsm.state_count = MOD_STATE_COUNT; modfsm.event_count = MOD_EVENT_COUNT; modfsm.strEvent = strmodEvent; modfsm.strState = strmodState; mISDN_FsmNew(&modfsm, ModFnList, MOD_FN_COUNT); return(err); } static void faxl3_cleanup(void) { faxl3_t *l3, *nl3; int err; if ((err = mISDN_unregister(&faxl3_obj))) { printk(KERN_ERR "Can't unregister DTMF error(%d)\n", err); } if(!list_empty(&faxl3_obj.ilist)) { printk(KERN_WARNING "faxl3 inst list not empty\n"); list_for_each_entry_safe(l3, nl3, &faxl3_obj.ilist, list) release_faxl3(l3); } mISDN_FsmFree(&faxl3fsm); mISDN_FsmFree(&modfsm); } module_init(faxl3_init); module_exit(faxl3_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/fsm.c0000644000000000000500000001005211135651702017316 0ustar rootsrc/* $Id: fsm.c,v 1.5 2007/03/21 13:26:56 crich Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * Thanks to Jan den Ouden * Fritz Elfert * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include #include #ifdef NEED_JIFFIES_INCLUDE #include #endif #include #include "fsm.h" #define FSM_TIMER_DEBUG 0 void mISDN_FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount) { int i; fsm->jumpmatrix = (FSMFNPTR *) kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL); memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count); for (i = 0; i < fncount; i++) if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) { printk(KERN_ERR "mISDN_FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n", i,(long)fnlist[i].state,(long)fsm->state_count, (long)fnlist[i].event,(long)fsm->event_count); } else fsm->jumpmatrix[fsm->state_count * fnlist[i].event + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; } void mISDN_FsmFree(struct Fsm *fsm) { kfree((void *) fsm->jumpmatrix); } int mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg) { FSMFNPTR r; if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) { printk(KERN_ERR "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", (long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count); return(1); } r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; if (r) { if (fi->debug) fi->printdebug(fi, "State %s Event %s", fi->fsm->strState[fi->state], fi->fsm->strEvent[event]); r(fi, event, arg); return (0); } else { if (fi->debug) fi->printdebug(fi, "State %s Event %s no action", fi->fsm->strState[fi->state], fi->fsm->strEvent[event]); return (!0); } } void mISDN_FsmChangeState(struct FsmInst *fi, int newstate) { fi->state = newstate; if (fi->debug) fi->printdebug(fi, "ChangeState %s", fi->fsm->strState[newstate]); } static void FsmExpireTimer(struct FsmTimer *ft) { #if FSM_TIMER_DEBUG if (ft->fi->debug) ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); #endif mISDN_FsmEvent(ft->fi, ft->event, ft->arg); } void mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) { ft->fi = fi; ft->tl.function = (void *) FsmExpireTimer; ft->tl.data = (long) ft; #if FSM_TIMER_DEBUG if (ft->fi->debug) ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); #endif init_timer(&ft->tl); } void mISDN_FsmDelTimer(struct FsmTimer *ft, int where) { #if FSM_TIMER_DEBUG if (ft->fi->debug) ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d", (long) ft, where); #endif del_timer(&ft->tl); } int mISDN_FsmAddTimer(struct FsmTimer *ft, int millisec, int event, void *arg, int where) { #if FSM_TIMER_DEBUG if (ft->fi->debug) ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d", (long) ft, millisec, where); #endif if (timer_pending(&ft->tl)) { if (ft->fi->debug) { printk(KERN_WARNING "mISDN_FsmAddTimer: timer already active!\n"); ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer already active!"); } return -1; } init_timer(&ft->tl); ft->event = event; ft->arg = arg; ft->tl.expires = jiffies + (millisec * HZ) / 1000; add_timer(&ft->tl); return 0; } void mISDN_FsmRestartTimer(struct FsmTimer *ft, int millisec, int event, void *arg, int where) { #if FSM_TIMER_DEBUG if (ft->fi->debug) ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d", (long) ft, millisec, where); #endif if (timer_pending(&ft->tl)) del_timer(&ft->tl); init_timer(&ft->tl); ft->event = event; ft->arg = arg; ft->tl.expires = jiffies + (millisec * HZ) / 1000; add_timer(&ft->tl); } EXPORT_SYMBOL(mISDN_FsmNew); EXPORT_SYMBOL(mISDN_FsmFree); EXPORT_SYMBOL(mISDN_FsmEvent); EXPORT_SYMBOL(mISDN_FsmChangeState); EXPORT_SYMBOL(mISDN_FsmInitTimer); EXPORT_SYMBOL(mISDN_FsmAddTimer); EXPORT_SYMBOL(mISDN_FsmRestartTimer); EXPORT_SYMBOL(mISDN_FsmDelTimer); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_mec2_const.h0000644000000000000500000000133511110524073021435 0ustar rootsrc/* 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 */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/helper.c0000644000000000000500000002076611135651702020025 0ustar rootsrc/* $Id: helper.c,v 1.16 2006/08/07 23:35:59 keil Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include "helper.h" #undef DEBUG_LM #undef DEBUG_IF void mISDN_set_dchannel_pid(mISDN_pid_t *pid, int protocol, int layermask) { if (!layermask) layermask = ISDN_LAYER(0)| ISDN_LAYER(1) | ISDN_LAYER(2) | ISDN_LAYER(3) | ISDN_LAYER(4); memset(pid, 0, sizeof(mISDN_pid_t)); pid->layermask = layermask; pid->modparm_protocol = protocol; if (layermask & ISDN_LAYER(0)) pid->protocol[0] = ISDN_PID_L0_TE_S0; if (layermask & ISDN_LAYER(1)) pid->protocol[1] = ISDN_PID_L1_TE_S0; if (layermask & ISDN_LAYER(2)) { pid->protocol[2] = ISDN_PID_L2_LAPD; if (protocol & 0x20) pid->protocol[2] |= ISDN_PID_L2_DF_PTP; } if (layermask & ISDN_LAYER(3)) { if ((protocol & 0xf) == 2) pid->protocol[3] = ISDN_PID_L3_DSS1USER; if (protocol & 0x20) pid->protocol[3] |= ISDN_PID_L3_DF_PTP; } if (layermask & ISDN_LAYER(4)) pid->protocol[4] = ISDN_PID_L4_CAPI20; } int mISDN_add_pid_parameter(mISDN_pid_t *pid, int layer, u_char *para) { u16 l; if (para == NULL || *para == 0) { pid->param[layer] = 0; return 0; } l = 1 + *para; /* including length itself */ if (!pid->pbuf) { pid->maxplen = l + 1; /* pbuf[0] is never used */ if (l < 63) pid->maxplen = 64; pid->pbuf = kzalloc(pid->maxplen, GFP_ATOMIC); pid->pidx = 1; if (!pid->pbuf) { pid->maxplen = 0; return -ENOMEM; } } else if ((pid->pidx + l) > pid->maxplen) { u_char *tbuf; tbuf = kmalloc(pid->pidx + l, GFP_ATOMIC); if (!tbuf) return -ENOMEM; memcpy(tbuf, pid->pbuf, pid->pidx); kfree(pid->pbuf); pid->pbuf = tbuf; pid->maxplen = pid->pidx + l; } pid->param[layer] = pid->pidx; memcpy(&pid->pbuf[pid->pidx], para, l); pid->pidx += l; return 0; } int mISDN_bprotocol2pid(void *bp, mISDN_pid_t *pid) { u8 *p = bp; u16 *w = bp; int i, ret; p += 6; for (i=1; i<=3; i++) { if (*w > 23) { int_errtxt("L%d pid %x\n",i,*w); return -EINVAL; } pid->protocol[i] = (1 <<*w) | ISDN_PID_LAYER(i) | ISDN_PID_BCHANNEL_BIT; ret = mISDN_add_pid_parameter(pid, i, p); if (ret) return ret; w++; p += *p; p++; } pid->global = 0; if (*p == 2) { // len of 1 word p++; w = (u16 *)p; pid->global = *w; } return 0; } int mISDN_HasProtocol(mISDNobject_t *obj, u_int protocol) { int layer; u_int pmask; if (!obj) { int_error(); return(-EINVAL); } layer = (protocol & ISDN_PID_LAYER_MASK)>>24; if (layer > MAX_LAYER_NR) { int_errtxt("layer %d", layer); return(-EINVAL); } if (protocol & ISDN_PID_BCHANNEL_BIT) pmask = obj->BPROTO.protocol[layer]; else pmask = obj->DPROTO.protocol[layer]; if (pmask == ISDN_PID_ANY) return(-EPROTO); if (protocol == (protocol & pmask)) return(0); else return(-ENOPROTOOPT); } int mISDN_SetHandledPID(mISDNobject_t *obj, mISDN_pid_t *pid) { int layer; int ret = 0; mISDN_pid_t sav; if (!obj || !pid) { int_error(); return(0); } #ifdef DEBUG_LM printk(KERN_DEBUG "%s: %s LM(%x)\n", __FUNCTION__, obj->name, pid->layermask); #endif memcpy(&sav, pid, sizeof(mISDN_pid_t)); memset(pid, 0, sizeof(mISDN_pid_t)); pid->pbuf = sav.pbuf; pid->maxplen = sav.maxplen; pid->pidx = sav.pidx; pid->global = sav.global; if (!sav.layermask) { printk(KERN_WARNING "%s: no layermask in pid\n", __FUNCTION__); return(0); } for (layer=0; layer<=MAX_LAYER_NR; layer++) { if (!(ISDN_LAYER(layer) & sav.layermask)) { if (ret) break; else continue; } if (0 == mISDN_HasProtocol(obj, sav.protocol[layer])) { ret++; pid->protocol[layer] = sav.protocol[layer]; pid->param[layer] = sav.param[layer]; pid->layermask |= ISDN_LAYER(layer); } else break; } return(ret); } void mISDN_RemoveUsedPID(mISDN_pid_t *pid, mISDN_pid_t *used) { int layer; if (!used || !pid) { int_error(); return; } if (!used->layermask) return; for (layer=0; layer<=MAX_LAYER_NR; layer++) { if (!(ISDN_LAYER(layer) & used->layermask)) continue; pid->protocol[layer] = ISDN_PID_NONE; pid->param[layer] = 0; pid->layermask &= ~(ISDN_LAYER(layer)); } } int mISDN_layermask2layer(int layermask) { switch(layermask) { case ISDN_LAYER(0): return(0); case ISDN_LAYER(1): return(1); case ISDN_LAYER(2): return(2); case ISDN_LAYER(3): return(3); case ISDN_LAYER(4): return(4); case ISDN_LAYER(5): return(5); case ISDN_LAYER(6): return(6); case ISDN_LAYER(7): return(7); case 0: return(-1); } return(-2); } int mISDN_get_protocol(mISDNstack_t *st, int layer) { if (!st){ int_error(); return(-EINVAL); } if (LAYER_OUTRANGE(layer)) { int_errtxt("L%d st(%x)", layer, st->id); return(-EINVAL); } return(st->pid.protocol[layer]); } int mISDN_get_lowlayer(int layermask) { int layer; if (!layermask) return(0); for (layer=0; layer <= MAX_LAYER_NR; layer++) if (layermask & ISDN_LAYER(layer)) return(layer); return(0); } int mISDN_get_down_layer(int layermask) { int downlayer = 1; if (layermask>255 || (layermask & 1)) { int_errtxt("lmask %x out of range", layermask); return(0); } while(downlayer <= MAX_LAYER_NR) { if (ISDN_LAYER(downlayer) & layermask) break; downlayer++; } downlayer--; return(downlayer); } int mISDN_get_up_layer(int layermask) { int uplayer = MAX_LAYER_NR; if (layermask>=128) { int_errtxt("lmask %x out of range", layermask); return(0); } while(uplayer>=0) { if (ISDN_LAYER(uplayer) & layermask) break; uplayer--; } uplayer++; return(uplayer); } #ifdef OBSOLETE int mISDN_SetIF(mISDNinstance_t *owner, mISDNif_t *hif, u_int prim, void *upfunc, void *downfunc, void *data) { mISDNif_t *own_hif; if (!owner) { int_error(); return(-EINVAL); } if (!hif) { int_error(); return(-EINVAL); } if (IF_TYPE(hif) == IF_UP) { hif->func = upfunc; own_hif = &owner->up; #ifdef DEBUG_IF printk(KERN_DEBUG "%s: IF_UP: func:%p(%p)\n", __FUNCTION__, hif->func, owner->data); #endif } else if (IF_TYPE(hif) == IF_DOWN) { hif->func = downfunc; own_hif = &owner->down; #ifdef DEBUG_IF printk(KERN_DEBUG "%s: IF_DOWN: func:%p(%p)\n", __FUNCTION__, hif->func, owner->data); #endif } else { int_errtxt("stat(%x)", hif->stat); return(-EINVAL); } hif->peer = owner; hif->fdata = data; if ((prim & SUBCOMMAND_MASK) == REQUEST) { if (own_hif->stat == IF_NOACTIV) { if (IF_TYPE(hif) == IF_UP) own_hif->stat = IF_DOWN; else own_hif->stat = IF_UP; own_hif->owner = owner; return(hif->owner->obj->own_ctrl(hif->owner, MGR_SETIF | INDICATION, own_hif)); } else { int_errtxt("REQ own stat(%x)", own_hif->stat); return(-EBUSY); } } return(0); } int mISDN_ConnectIF(mISDNinstance_t *owner, mISDNinstance_t *peer) { mISDNif_t *hif; if (!owner) { int_error(); return(-EINVAL); } if (!peer) { int_error(); return(-EINVAL); } if (owner->pid.layermask < peer->pid.layermask) { hif = &owner->up; hif->owner = owner; hif->stat = IF_DOWN; } else if (owner->pid.layermask > peer->pid.layermask) { hif = &owner->down; hif->owner = owner; hif->stat = IF_UP; } else { int_errtxt("OLM == PLM: %x", owner->pid.layermask); return(-EINVAL); } return(peer->obj->own_ctrl(peer, MGR_SETIF | REQUEST, hif)); } int mISDN_DisConnectIF(mISDNinstance_t *inst, mISDNif_t *hif) { if (hif) { if (hif->clone) { if (hif->clone->owner) hif->clone->owner->obj->ctrl(hif->clone->owner, MGR_DISCONNECT | REQUEST, hif->clone); } if (inst->up.peer) { if (inst->up.peer == hif->owner) inst->up.peer->obj->ctrl(inst->up.peer, MGR_DISCONNECT | INDICATION, &inst->up); } if (inst->down.peer) { if (inst->down.peer == hif->owner) inst->down.peer->obj->ctrl(inst->down.peer, MGR_DISCONNECT | INDICATION, &inst->down); } } return(0); } #endif void mISDN_init_instance(mISDNinstance_t *inst, mISDNobject_t *obj, void *data, if_func_t *function) { if (!obj) { int_error(); return; } INIT_LIST_HEAD(&inst->list); inst->obj = obj; inst->privat = data; inst->function = function; } EXPORT_SYMBOL(mISDN_set_dchannel_pid); EXPORT_SYMBOL(mISDN_get_lowlayer); EXPORT_SYMBOL(mISDN_get_up_layer); EXPORT_SYMBOL(mISDN_get_down_layer); EXPORT_SYMBOL(mISDN_layermask2layer); EXPORT_SYMBOL(mISDN_get_protocol); 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_add_pid_parameter); EXPORT_SYMBOL(mISDN_bprotocol2pid); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/hfc_multi.c0000644000000000000500000040402411135651702020511 0ustar rootsrc/* * hfc_multi.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards * * Author Andreas Eversberg (jolly@eversberg.eu) * ported to mqueue mechanism: * Peter Sprenger (sprengermoving-bytes.de) * * inspired by existing hfc-pci driver: * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) * Copyright 2001 by Karsten Keil (keil@isdn4linux.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * Thanks to Cologne Chip AG for this great controller! */ /* module parameters: * type: Value 1 = HFC-E1 (1 port) 0x01 Value 4 = HFC-4S (4 ports) 0x04 Value 8 = HFC-8S (8 ports) 0x08 Bit 8 = uLaw (instead of aLaw) Bit 9 = Enable DTMF detection on all B-channels Bit 10 = spare Bit 11 = Set PCM bus into slave mode. Bit 12 = Ignore missing frame clock on PCM bus. Bit 13 = Use direct RX clock for PCM sync rather than PLL. (E1 only) Bit 14 = Use external ram (128K) Bit 15 = Use external ram (512K) Bit 16 = Use 64 timeslots instead of 32 Bit 17 = Use 128 timeslots instead of anything else Bit 18 = Use crystal clock for PCM and E1, for autarc clocking. Bit 19 = Send the Watchdog a Signal (Dual E1 with Watchdog) * protocol: NOTE: Must be given for all ports, not for the number of cards. HFC-4S/HFC-8S/HFC-E1 bits: Bit 0-3 = protocol Bit 4 = NT-Mode Bit 5 = PTP (instead of multipoint) HFC-4S/HFC-8S only bits: Bit 16 = Use master clock for this S/T interface (ony once per chip). Bit 17 = transmitter line setup (non capacitive mode) DONT CARE! Bit 18 = Disable E-channel. (No E-channel processing) HFC-E1 only bits: Bit 16 = interface: 0=copper, 1=optical Bit 17 = reserved (later for 32 B-channels transparent mode) Bit 18 = Report LOS Bit 19 = Report AIS Bit 20 = Report SLIP Bit 21-22 = elastic jitter buffer (1-3), Use 0 for default. Bit 23 = Turn off CRC-4 Multiframe Mode, use double frame mode instead. (all other bits are reserved and shall be 0) * layermask: NOTE: Must be given for all ports, not for the number of cards. mask of layers to be used for D-channel stack * debug: NOTE: only one debug value must be given for all cards enable debugging (see hfc_multi.h for debug options) * poll: NOTE: 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 8, 16, 32, 64, 128, 256. * pcm: NOTE: only one pcm value must be given for all cards 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 using register map (never use this, it will flood your system log) */ //#define HFC_REGISTER_MAP #include #include #include #include "core.h" #include "channel.h" #include "layer1.h" #include "dsp.h" #include "debug.h" #include //#warning //#define IRQCOUNT_DEBUG #include "hfc_multi.h" #ifdef ECHOPREP #include "gaintab.h" #endif //#warning #define bugtest {} #if 0 #define bugtest \ if (hc->irq) free_irq(hc->irq, hc); \ hc->irq = 0; \ if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, SA_SHIRQ, "HFC-multi", hc)) { \ printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", hc->pci_dev->irq); \ hc->irq = hc->pci_dev->irq; } #endif static void ph_state_change(channel_t *ch); extern const char *CardType[]; static const char *hfcmulti_revision = "$Revision: 1.68 $"; static int HFC_cnt; static mISDNobject_t HFCM_obj; static char HFCName[] = "HFC_multi"; extern void ztdummy_extern_interrupt(void); static void (* hfc_interrupt)(void); extern void ztdummy_register_interrupt(void); static void (* register_interrupt)(void); /* table entry in the PCI devices list */ typedef struct { int vendor_id; int vendor_sub; int device_id; int device_sub; char *vendor_name; char *card_name; int type; int ports; int clock2; int leds; int opticalsupport; } PCI_ENTRY; static int poll_timer = 6; /* default = 128 samples = 16ms */ /* number of POLL_TIMER interrupts for G2 timeout (min 120ms) */ static int nt_t1_count[] = { 480, 240, 120, 60, 30, 15, 8, 4 }; #define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ #define CLKDEL_NT 0x0c /* CLKDEL in NT mode (0x60 MUST not be included!) */ static u_char silence = 0xff; /* silence by LAW */ /* enable 32 bit fifo access (PC usage) */ #define FIFO_32BIT_ACCESS #define VENDOR_CCD "Cologne Chip AG" #define CCAG_VID 0x1397 // Cologne Chip Vendor ID #define HFC4S_ID 0x08B4 #define HFC8S_ID 0x16B8 #define HFCE1_ID 0x30B1 static const PCI_ENTRY id_list[] = { #if 0 {CCAG_VID, 0xffffffff, HFC4S_ID, 0xffffffff, VENDOR_CCD, "HFC-4S CCAG Eval", 4, 1, 2}, {CCAG_VID, 0xffffffff, HFC8S_ID, 0xffffffff, VENDOR_CCD, "HFC-8S CCAG Eval", 8, 1, 0}, {CCAG_VID, 0xffffffff, HFCE1_ID, 0xffffffff, VENDOR_CCD, "HFC-E1 CCAG Eval", 1, 0, 1}, /* E1 only supports single clock */ #endif {CCAG_VID, CCAG_VID, HFC4S_ID, 0x08B4, VENDOR_CCD, "HFC-4S CCAG Eval (old)", 0, 4, 0, 0, 0}, {CCAG_VID, CCAG_VID, HFC8S_ID, 0x16B8, VENDOR_CCD, "HFC-8S CCAG Eval (old)", 0, 8, 0, 0, 0}, {CCAG_VID, CCAG_VID, HFCE1_ID, 0x30B1, VENDOR_CCD, "HFC-E1 CCAG Eval (old)", 1, 1, 0, 0, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB520, VENDOR_CCD, "HFC-4S IOB4ST", 0, 4, 1, 2, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB620, VENDOR_CCD, "HFC-4S", 0, 4, 1, 2, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB550, VENDOR_CCD, "HFC-4S (junghanns 2.0)", 0, 4, 1, 2, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB560, VENDOR_CCD, "HFC-4S Beronet Card", 0, 4, 1, 2, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB568, VENDOR_CCD, "HFC-4S Beronet Card (mini PCI)", 0, 4, 1, 2, 0}, {0xD161, 0xD161, 0xB410, 0xB410, VENDOR_CCD, "HFC-4S Digium Card", 0, 4, 0, 2, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB540, VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 0, 4, 1, 2, 0}, {CCAG_VID, CCAG_VID, HFC8S_ID, 0xB521, VENDOR_CCD, "HFC-8S IOB4ST Recording", 0, 8, 1, 0, 0}, {CCAG_VID, CCAG_VID, HFC8S_ID, 0xB522, VENDOR_CCD, "HFC-8S IOB8ST", 0, 8, 1, 0, 0}, {CCAG_VID, CCAG_VID, HFC8S_ID, 0xB552, VENDOR_CCD, "HFC-8S", 0, 8, 1, 0, 0}, {CCAG_VID, CCAG_VID, HFC8S_ID, 0xB55B, VENDOR_CCD, "HFC-8S-Junghans", 0, 8, 1, 0, 0}, {CCAG_VID, CCAG_VID, HFC8S_ID, 0xB622, VENDOR_CCD, "HFC-8S", 0, 8, 1, 0, 0}, {CCAG_VID, CCAG_VID, HFC8S_ID, 0xB562, VENDOR_CCD, "HFC-8S Beronet Card", 0, 8, 1, 0, 0}, {CCAG_VID, CCAG_VID, HFC8S_ID, 0xB56B, VENDOR_CCD, "HFC-8S Beronet Card (+)", 0, 8, 1, 8, 0}, {CCAG_VID, CCAG_VID, HFCE1_ID, 0xB523, VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0}, /* E1 only supports single clock */ {CCAG_VID, CCAG_VID, HFCE1_ID, 0xC523, VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0}, /* E1 only supports single clock */ {CCAG_VID, CCAG_VID, HFCE1_ID, 0xB56A, VENDOR_CCD, "HFC-E1 Beronet Card (mini PCI)", 1, 1, 0, 1, 0}, /* E1 only supports single clock */ {CCAG_VID, CCAG_VID, HFCE1_ID, 0xB563, VENDOR_CCD, "HFC-E1 Beronet Card", 1, 1, 0, 1, 0}, /* E1 only supports single clock */ {CCAG_VID, CCAG_VID, HFCE1_ID, 0xB565, VENDOR_CCD, "HFC-E1+ Beronet Card (Dual)", 1, 1, 0, 1, 0}, /* E1 only supports single clock */ {CCAG_VID, CCAG_VID, HFCE1_ID, 0xB564, VENDOR_CCD, "HFC-E1 Beronet Card (Dual)", 1, 1, 0, 1, 0}, /* E1 only supports single clock */ {CCAG_VID, CCAG_VID, HFCE1_ID, 0xB553, VENDOR_CCD, "HFC-E1 Junghanns Card", 1, 1, 0, 1, 0}, /* E1 only supports single clock */ {0x10B5, CCAG_VID, 0x9030, 0x3136, VENDOR_CCD, "HFC-4S PCIBridgeEval", 0, 4, 0, 0, 0}, // PLX PCI-Bridge {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB566, VENDOR_CCD, "HFC-2S Beronet Card", 0, 2, 1, 3, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB569, VENDOR_CCD, "HFC-2S Beronet Card (mini PCI)", 0, 2, 1, 3, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB761, VENDOR_CCD, "HFC-2S Beronet Card PCIe", 0, 2, 1, 3, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB762, VENDOR_CCD, "HFC-4S Beronet Card PCIe", 0, 4, 1, 2, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xB567, VENDOR_CCD, "HFC-1S Beronet Card (mini PCI)", 0, 1, 1, 3, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xE888, VENDOR_CCD, "HFC-4S OpenVox Card", 0, 4, 1, 2, 0}, {CCAG_VID, CCAG_VID, HFC4S_ID, 0xE884, VENDOR_CCD, "HFC-2S OpenVox Card", 0, 2, 1, 2, 0}, {0, 0, 0, 0, NULL, NULL, 0, 0, 0, 0}, }; /* * debugtool helper */ static void dt_newstate(mISDNstack_t *stack, u_int state, char *message) { int mlen = message ? strlen(message) + 1 : 0; struct sk_buff *skb = alloc_skb(mlen + 4, GFP_ATOMIC); if (!skb) return; *(unsigned int *)skb_put(skb, 4) = state; if (message) memcpy(skb_put(skb, mlen), message, mlen); mISDN_dt_new_frame(stack, NEWSTATE, skb, 0); } /****************/ /* module stuff */ /****************/ /* NOTE: MAX_PORTS must be 8*MAX_CARDS */ #define MAX_CARDS 16 #define MAX_PORTS 128 static u_int type[MAX_CARDS]; static BYTE allocated[MAX_CARDS]; // remember if card is found static int pcm[MAX_PORTS]; static u_int protocol[MAX_PORTS]; static int layermask[MAX_PORTS]; static int debug; static int poll; static int timer; #ifdef MODULE MODULE_AUTHOR("Andreas Eversberg"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); MODULE_PARM(poll, "1i"); MODULE_PARM(timer, "1i"); #define MODULE_PARM_T "1-128i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); MODULE_PARM(type, MODULE_PARM_T); MODULE_PARM(pcm, MODULE_PARM_T); #else module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(poll, uint, S_IRUGO | S_IWUSR); module_param(timer, uint, S_IRUGO | S_IWUSR); #ifdef OLD_MODULE_PARAM_ARRAY static int num_type=0, num_pcm=0, num_protocol=0, num_layermask=0; module_param_array(type, uint, num_type, S_IRUGO | S_IWUSR); module_param_array(pcm, uint, num_pcm, S_IRUGO | S_IWUSR); module_param_array(protocol, uint, num_protocol, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, num_layermask, S_IRUGO | S_IWUSR); #else module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(protocol, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, NULL, S_IRUGO | S_IWUSR); #endif #endif #endif #define HFCM_FIX_IRQS static void enable_hwirq(hfc_multi_t *hc) { hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN; HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); } static void disable_hwirq(hfc_multi_t *hc) { hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN); HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); } #ifndef CONFIG_HFCMULTI_PCIMEM #define B410P_CARD #endif #define NUM_EC 2 #define MAX_TDM_CHAN 32 #ifdef B410P_CARD inline void enablepcibridge(hfc_multi_t *c) { HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /*was _io before*/ } inline void disablepcibridge(hfc_multi_t *c) { HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /*was _io before*/ } inline unsigned char readpcibridge(hfc_multi_t *c, unsigned char address) { unsigned short cipv; unsigned char data; // slow down a PCI read access by 1 PCI clock cycle HFC_outb(c, R_CTRL, 0x4); /*was _io before*/ if (address == 0) cipv=0x4000; else cipv=0x5800; // select local bridge port address by writing to CIP port //data = HFC_inb(c, cipv); /*was _io before*/ outw(cipv, c->pci_iobase + 4); data = inb(c->pci_iobase); // restore R_CTRL for normal PCI read cycle speed HFC_outb(c, R_CTRL, 0x0); /*was _io before*/ return data; } inline void writepcibridge(hfc_multi_t *hc, unsigned char address, unsigned char data) { unsigned short cipv; unsigned int datav; if (address == 0) cipv=0x4000; else cipv=0x5800; // select local bridge port address by writing to CIP port outw(cipv, hc->pci_iobase + 4); // define a 32 bit dword with 4 identical bytes for write sequence datav=data | ( (__u32) data <<8) | ( (__u32) data <<16) | ( (__u32) data <<24); // write this 32 bit dword to the bridge data port // this will initiate a write sequence of up to 4 writes to the same address on the local bus // interface // the number of write accesses is undefined but >=1 and depends on the next PCI transaction // during write sequence on the local bus outl(datav, hc->pci_iobase); } inline void cpld_set_reg(hfc_multi_t *hc, unsigned char reg) { /* Do data pin read low byte */ HFC_outb(hc, R_GPIO_OUT1, reg); } inline void cpld_write_reg(hfc_multi_t *hc, unsigned char reg, unsigned char val) { cpld_set_reg(hc, reg); enablepcibridge(hc); writepcibridge(hc, 1, val); disablepcibridge(hc); return; } inline unsigned char cpld_read_reg(hfc_multi_t *hc, unsigned char reg) { unsigned char bytein; cpld_set_reg(hc, reg); /* Do data pin read low byte */ HFC_outb(hc, R_GPIO_OUT1, reg); enablepcibridge(hc); bytein = readpcibridge(hc, 1); disablepcibridge(hc); return bytein; } inline void vpm_write_address(hfc_multi_t *hc, unsigned short addr) { cpld_write_reg(hc, 0, 0xff & addr); cpld_write_reg(hc, 1, 0x01 & (addr >> 8)); } inline unsigned short vpm_read_address(hfc_multi_t *c) { unsigned short addr; unsigned short highbit; addr = cpld_read_reg(c, 0); highbit = cpld_read_reg(c, 1); addr = addr | (highbit << 8); return addr & 0x1ff; } inline unsigned char vpm_in(hfc_multi_t *c, int which, unsigned short addr) { unsigned char res; vpm_write_address(c, addr); if (!which) cpld_set_reg(c, 2); else cpld_set_reg(c, 3); enablepcibridge(c); res = readpcibridge(c, 1); disablepcibridge(c); cpld_set_reg(c, 0); return res; } inline void vpm_out(hfc_multi_t *c, int which, unsigned short addr, unsigned char data) { vpm_write_address(c, addr); enablepcibridge(c); if (!which) cpld_set_reg(c, 2); else cpld_set_reg(c, 3); writepcibridge(c, 1, data); cpld_set_reg(c, 0); disablepcibridge(c); { unsigned char regin; regin = vpm_in(c, which, addr); if (regin != data) printk("Wrote 0x%x to register 0x%x but got back 0x%x\n", data, addr, regin); } return; } void vpm_init(hfc_multi_t *wc) { unsigned char reg; unsigned int mask; unsigned int i, x, y; unsigned int ver; for (x=0;x> (i << 3)) & 0xff); /* Setup convergence rate */ printk("VPM: A-law mode\n"); reg = 0x00 | 0x10 | 0x01; vpm_out(wc,x,0x20,reg); printk("VPM reg 0x20 is %x\n", reg); //vpm_out(wc,x,0x20,(0x00 | 0x08 | 0x20 | 0x10)); vpm_out(wc, x, 0x24, 0x02); reg = vpm_in(wc, x, 0x24); printk("NLP Thresh is set to %d (0x%x)\n", reg, reg); /* Initialize echo cans */ for (i = 0 ; i < MAX_TDM_CHAN; i++) { if (mask & (0x00000001 << i)) vpm_out(wc,x,i,0x00); } udelay(10000); /* Put in bypass mode */ for (i = 0 ; i < MAX_TDM_CHAN ; i++) { if (mask & (0x00000001 << i)) { vpm_out(wc,x,i,0x01); } } /* Enable bypass */ for (i = 0 ; i < MAX_TDM_CHAN ; i++) { if (mask & (0x00000001 << i)) vpm_out(wc,x,0x78 + i,0x01); } } } void vpm_check(hfc_multi_t *hctmp) { unsigned char gpi2; gpi2 = HFC_inb(hctmp, R_GPI_IN2); if ((gpi2 & 0x3) != 0x3) { printk("Got interrupt 0x%x from VPM!\n", gpi2); } } /* * Interface to enable/disable the HW Echocan * * these functions are called within a spin_lock_irqsave on * the channel instance lock, so we are not disturbed by irqs * * we can later easily change the interface to make other * things configurable, for now we configure the taps * */ void vpm_echocan_on(hfc_multi_t *hc, int ch, int taps) { unsigned int timeslot; unsigned int unit; channel_t *bch = hc->chan[ch].ch; #ifdef TXADJ int txadj = -4; #endif if (hc->chan[ch].protocol != ISDN_PID_L1_B_64TRANS) return; if (!bch) return; #ifdef TXADJ skb = create_link_skb(PH_CONTROL | INDICATION, VOL_CHANGE_TX, sizeof(int), &txadj, 0); if (mISDN_queue_up(&bch->inst, 0, skb)) dev_kfree_skb(skb); #endif timeslot = ((ch/4)*8) + ((ch%4)*4) + 1; unit = ch % 4; printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", taps, timeslot); vpm_out(hc, unit, timeslot, 0x7e); } void vpm_echocan_off(hfc_multi_t *hc, int ch) { unsigned int timeslot; unsigned int unit; channel_t *bch = hc->chan[ch].ch; struct sk_buff *skb; int txadj = 0; if (hc->chan[ch].protocol != ISDN_PID_L1_B_64TRANS) return; if (!bch) return; skb = create_link_skb(PH_CONTROL | INDICATION, VOL_CHANGE_TX, sizeof(int), &txadj, 0); if (mISDN_queue_up(&bch->inst, 0, skb)) dev_kfree_skb(skb); timeslot = ((ch/4)*8) + ((ch%4)*4) + 1; unit = ch % 4; printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", timeslot); /*FILLME*/ vpm_out(hc, unit, timeslot, 0x01); } #endif /* B410_CARD */ /******************************************/ /* free hardware resources used by driver */ /******************************************/ static void release_io_hfcmulti(hfc_multi_t *hc) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); /* soft reset also masks all interrupts */ hc->hw.r_cirm |= V_SRES; HFC_outb(hc, R_CIRM, hc->hw.r_cirm); udelay(1000); hc->hw.r_cirm &= ~V_SRES; HFC_outb(hc, R_CIRM, hc->hw.r_cirm); udelay(1000); /* instead of 'wait' that may cause locking */ /* disable memory mapped ports / io ports */ pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); #ifdef CONFIG_HFCMULTI_PCIMEM if (hc->pci_membase) iounmap((void *)hc->pci_membase); if (hc->plx_membase) iounmap((void *)hc->plx_membase); #else if (hc->pci_iobase) release_region(hc->pci_iobase, 8); #endif #if 1 if (hc->pci_dev) { pci_disable_device(hc->pci_dev); pci_set_drvdata(hc->pci_dev, NULL); } #endif if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: done\n", __FUNCTION__); } /****************************************************************************/ /* function called to reset the HFC chip. A complete software reset of chip */ /* and fifos is done. All configuration of the chip is done. */ /****************************************************************************/ static int init_chip(hfc_multi_t *hc) { u_long flags, val, val2 = 0, rev; int cnt = 0; int i, err = 0; u_char r_conf_en,rval; spin_lock_irqsave(&hc->lock, flags); /* reset all registers */ memset(&hc->hw, 0, sizeof(hfcmulti_hw_t)); /* revision check */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); val = HFC_inb(hc, R_CHIP_ID)>>4; if(val!=0x8 && val!=0xc && val!=0xe) { printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n",(u_int)val); err = -EIO; goto out; } rev = HFC_inb(hc, R_CHIP_RV); printk(KERN_INFO "HFC_multi: resetting HFC with chip ID=0x%lx revision=%ld%s\n", val, rev, (rev==0)?" (old FIFO handling)":""); if (rev == 0) { test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); printk(KERN_WARNING "HFC_multi: NOTE: Your chip is revision 0, ask Cologne Chip for update. Newer chips have a better FIFO handling. Old chips still work but may have slightly lower HDLC transmit performance.\n"); } if (rev > 1) { printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't consider chip revision = %ld. The chip / bridge may not work.\n", rev); } /* set s-ram size */ hc->Flen = 0x10; hc->Zmin = 0x80; hc->Zlen = 384; hc->DTMFbase = 0x1000; if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n", __FUNCTION__); hc->hw.r_ctrl |= V_EXT_RAM; hc->hw.r_ram_sz = 1; hc->Flen = 0x20; hc->Zmin = 0xc0; hc->Zlen = 1856; hc->DTMFbase = 0x2000; } if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n", __FUNCTION__); hc->hw.r_ctrl |= V_EXT_RAM; hc->hw.r_ram_sz = 2; hc->Flen = 0x20; hc->Zmin = 0xc0; hc->Zlen = 8000; hc->DTMFbase = 0x2000; } /* we only want the real Z2 read-pointer for revision > 0 */ if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) hc->hw.r_ram_sz |= V_FZ_MD; /* soft reset */ HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); HFC_outb(hc, R_FIFO_MD, 0); hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR; HFC_outb(hc, R_CIRM, hc->hw.r_cirm); udelay(100); hc->hw.r_cirm = 0; HFC_outb(hc, R_CIRM, hc->hw.r_cirm); udelay(100); HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); /* set pcm mode & reset */ if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: setting PCM into slave mode\n", __FUNCTION__); } else { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: setting PCM into master mode\n", __FUNCTION__); hc->hw.r_pcm_md0 |= V_PCM_MD; } // RAM access test HFC_outb(hc, R_RAM_ADDR0, 0); HFC_outb(hc, R_RAM_ADDR1, 0); HFC_outb(hc, R_RAM_ADDR2, 0); for(i=0;i<256;i++) { HFC_outb(hc, R_RAM_ADDR0,i); HFC_outb(hc, R_RAM_DATA,((i*3)&0xff)); //udelay(5); //HFC_outb(hc, R_RAM_DATA,((i*3)&0xff)); } for(i=0;i<256;i++) { HFC_outb(hc, R_RAM_ADDR0,i); HFC_inb(hc, R_RAM_DATA); rval=HFC_inb(hc, R_INT_DATA); if(rval!=((i*3)&0xff)) { printk(KERN_DEBUG "addr:%x val:%x should:%x\n",i,rval,(i*3)&0xff); err++; } } if (err) { printk(KERN_DEBUG "aborting.1 - %d RAM access errors\n",err); err = -EIO; goto out; } #ifdef B410P_CARD if (test_bit(HFC_CHIP_DIGICARD,&hc->chip)) { HFC_outb(hc, R_BRG_PCM_CFG, 0x2); HFC_outb(hc, R_PCM_MD0, (0x9<<4) | 0x1); HFC_outb(hc, R_PCM_MD1, 0); printk(KERN_NOTICE "Setting GPIOs\n"); HFC_outb(hc, R_GPIO_SEL, 0x30); HFC_outb(hc, R_GPIO_EN1, 0x3); udelay(1000); printk(KERN_NOTICE "calling vpm_init\n"); vpm_init(hc); } else #endif { HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90); if (hc->slots == 32) HFC_outb(hc, R_PCM_MD1, 0x00); if (hc->slots == 64) HFC_outb(hc, R_PCM_MD1, 0x10); if (hc->slots == 128) HFC_outb(hc, R_PCM_MD1, 0x20); HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0); HFC_outb(hc, R_PCM_MD2, 0x00); HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); } i = 0; while (i < 256) { HFC_outb_(hc, R_SLOT, i); HFC_outb_(hc, A_SL_CFG, 0); HFC_outb_(hc, A_CONF, 0); hc->slot_owner[i] = -1; i++; } /* set clock speed */ if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: setting double clock\n", __FUNCTION__); HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); } /* check if R_F0_CNT counts */ val = HFC_inb(hc, R_F0_CNTL); val += HFC_inb(hc, R_F0_CNTH) << 8; if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "HFC_multi F0_CNT %ld after status ok\n", val); set_current_state(TASK_UNINTERRUPTIBLE); while (cnt < 50) { /* max 50 ms */ spin_unlock_irqrestore(&hc->lock, flags); schedule_timeout((HZ*10)/1000); /* Timeout 10ms */ spin_lock_irqsave(&hc->lock, flags); cnt+=10; val2 = HFC_inb(hc, R_F0_CNTL); val2 += HFC_inb(hc, R_F0_CNTH) << 8; if (val2 >= val+4) /* wait 4 pulses */ break; } if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "HFC_multi F0_CNT %ld after %dms\n", val2, cnt); if (val2 < val+4) { printk(KERN_ERR "HFC_multi ERROR 125us pulse not counting.\n"); if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { printk(KERN_ERR "HFC_multi This happens in PCM slave mode without connected master.\n"); } if (test_bit(HFC_CHIP_DIGICARD, &hc->chip)) { printk(KERN_ERR "HFC_multi ingoring PCM clock for digicard.\n"); } else { if (!test_bit(HFC_CHIP_CLOCK_IGNORE, &hc->chip) ){ err = -EIO; goto out; } } } /* set up timer */ HFC_outb(hc, R_TI_WD, poll_timer); hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; /* set up 125us interrupt, only if function pointer is available and module parameter timer is set */ if (timer!=0 && hfc_interrupt && register_interrupt) { /* only one chip should use this interrupt */ timer = 0; hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK; /* deactivate other interrupts in ztdummy */ register_interrupt(); } /* set E1 state machine IRQ */ if (hc->type == 1) hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; /* set DTMF detection */ if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: enabling DTMF detection for all B-channel\n", __FUNCTION__); hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; if (test_bit(HFC_CHIP_ULAW, &hc->chip)) hc->hw.r_dtmf |= V_ULAW_SEL; HFC_outb(hc, R_DTMF_N, 102-1); hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; } /* conference engine */ if (test_bit(HFC_CHIP_ULAW, &hc->chip)) r_conf_en = V_CONF_EN | V_ULAW; else r_conf_en = V_CONF_EN; HFC_outb(hc, R_CONF_EN, r_conf_en); /* setting leds */ switch(hc->leds) { case 1: /* HFC-E1 OEM */ if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) HFC_outb(hc, R_GPIO_SEL, 0x32); else HFC_outb(hc, R_GPIO_SEL, 0x30); HFC_outb(hc, R_GPIO_EN1, 0x0f); HFC_outb(hc, R_GPIO_OUT1, 0x00); HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); break; case 2: /* HFC-4S OEM */ case 3: HFC_outb(hc, R_GPIO_SEL, 0xf0); HFC_outb(hc, R_GPIO_EN1, 0xff); HFC_outb(hc, R_GPIO_OUT1, 0x00); break; } /* set master clock */ if (hc->masterclk >= 0) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: setting ST master clock to port %d (0..%d)\n", __FUNCTION__, hc->masterclk, hc->ports-1); hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC; HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); } /* setting misc irq */ HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n", hc->hw.r_irqmsk_misc); if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: done\n", __FUNCTION__); out: spin_unlock_irqrestore(&hc->lock, flags); return(err); } /************************/ /* control the watchdog */ /************************/ static void hfcmulti_watchdog(hfc_multi_t *hc) { hc->wdcount++; if (hc->wdcount > 10 ) { hc->wdcount=0; hc->wdbyte = hc->wdbyte==V_GPIO_OUT2?V_GPIO_OUT3:V_GPIO_OUT2; /** printk("Sending Watchdog Kill %x\n",hc->wdbyte); **/ HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte); } } /***************/ /* output leds */ /***************/ static void hfcmulti_leds(hfc_multi_t *hc) { int i, state, active; channel_t *dch; int led[4]; hc->ledcount += poll; if (hc->ledcount > 4096) hc->ledcount -= 4096; switch(hc->leds) { case 1: /* HFC-E1 OEM */ /* 2 red blinking: LOS 1 red: AIS left green: PH_ACTIVATE right green flashing: FIFO activity */ i = HFC_inb(hc, R_GPI_IN0) & 0xf0; if (!(i & 0x40)) { /* LOS */ if (hc->e1_switch != i) { hc->e1_switch = i; hc->hw.r_tx0 &= ~V_OUT_EN; HFC_outb(hc, R_TX0, hc->hw.r_tx0); } if (hc->ledcount & 512) led[0] = led[1] = 1; else led[0] = led[1] = 0; led[2] = led[3] = 0; } else if (!(i & 0x80)) { /* AIS */ if (hc->e1_switch != i) { hc->e1_switch = i; hc->hw.r_tx0 |= V_OUT_EN; hc->hw.r_tx1 |= V_AIS_OUT; HFC_outb(hc, R_TX0, hc->hw.r_tx0); HFC_outb(hc, R_TX1, hc->hw.r_tx1); } if (hc->ledcount & 512) led[2] = led[3] = 1; else led[2] = led[3] = 0; led[0] = led[1] = 0; } else { if (hc->e1_switch != i) { /* reset LOS/AIS */ hc->e1_switch = i; hc->hw.r_tx0 |= V_OUT_EN; hc->hw.r_tx1 &= ~V_AIS_OUT; HFC_outb(hc, R_TX0, hc->hw.r_tx0); HFC_outb(hc, R_TX1, hc->hw.r_tx1); } if (HFC_inb_(hc, R_RX_STA0) & V_SIG_LOS) { if (hc->ledcount>>11) led[0] = led[1] = 1; /* both red blinking */ else led[0] = led[1] = 0; } else if (HFC_inb_(hc, R_RX_STA0) & V_AIS) { led[0] = led[1] = 1; /* both red */ } else { led[0] = led[1] = 0; /* no red */ } state = 0; active = 1; dch = hc->chan[16].ch; if (dch && test_bit(FLG_DCHANNEL, &dch->Flags)) state = dch->state; if (state == active) { led[2] = 1; /* left green */ if (hc->activity[0]) { led[3] = 1; /* right green */ hc->activity[0] = 0; } else led[3] = 0; /* no right green */ } else led[2] = led[3] = 0; /* no green */ } HFC_outb(hc, R_GPIO_OUT1, (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xf); /* leds are inverted */ break; case 2: /* HFC-4S OEM */ /* red blinking = PH_DEACTIVATE red steady = PH_ACTIVATE green flashing = fifo activity */ i = 0; while(i < 4) { state = 0; active = -1; dch = hc->chan[(i<<2)|2].ch; if (dch && test_bit(FLG_DCHANNEL, &dch->Flags)) { state = dch->state; active = test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)?3:7; } if (state) { if (state==active) { if (hc->activity[i]) { led[i] = 1; /* led green */ hc->activity[i] = 0; } else led[i] = 2; /* led red */ } else if (hc->ledcount>>11) led[i] = 2; /* led red */ else led[i] = 0; /* led off */ } else led[i] = 0; /* led off */ i++; } #ifdef B410P_CARD if (test_bit(HFC_CHIP_DIGICARD, &hc->chip)) { int leds=0; for (i=0; i<4; i++) { if (led[i]==1) { /*green*/ leds |=( 0x2 <<(i*2)); } else if (led[i]==2) { /*red*/ leds |=( 0x1 <<(i*2)); } } vpm_out(hc, 0, 0x1a8+3,leds); } else #endif { HFC_outb(hc, R_GPIO_EN1, ((led[0]>0)<<0) | ((led[1]>0)<<1) | ((led[2]>0)<<2) | ((led[3]>0)<<3)); HFC_outb(hc, R_GPIO_OUT1, ((led[0]&1)<<0) | ((led[1]&1)<<1) | ((led[2]&1)<<2) | ((led[3]&1)<<3)); } break; case 3: /* red blinking = PH_DEACTIVATE red steady = PH_ACTIVATE green flashing = fifo activity */ for(i=0;i<2;i++) { state = 0; active = -1; dch = hc->chan[(i<<2)|2].ch; if (dch && test_bit(FLG_DCHANNEL, &dch->Flags)) { state = dch->state; active = test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)?3:7; } if (state) { if (state==active) { if (hc->activity[i]) { led[i] = 1; /* led green */ hc->activity[i] = 0; } else led[i] = 2; /* led red */ } else if (hc->ledcount>>11) led[i] = 2; /* led red */ else led[i] = 0; /* led off */ } else led[i] = 0; /* led off */ } // printk("leds %d %d\n", led[0], led[1]); //LEDME HFC_outb(hc, R_GPIO_EN1, ((led[0]>0)<<2) | ((led[1]>0)<<3) ); HFC_outb(hc, R_GPIO_OUT1, ((led[0]&1)<<2) | ((led[1]&1)<<3) ); break; case 8: { unsigned long led=0; int off=0; if (hc->ledcount>2048) off=1; for (i=0;i<8;i++) { state = 0; active = -1; dch = hc->chan[(i<<2)|2].ch; if (dch && test_bit(FLG_DCHANNEL, &dch->Flags)) { state = dch->state; active = test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)?3:7; } if (state) { if (state!=active && off) led |= 1<pci_iobase + 4); outl(leddw, hc->pci_iobase); HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); #endif } break; } } /**************************/ /* read dtmf coefficients */ /**************************/ static void hfcmulti_dtmf(hfc_multi_t *hc) { signed long coeff[16]; DWORD mantissa; int co, ch; channel_t *bch = NULL; BYTE exponent; int dtmf = 0; int addr; WORD w_float; struct sk_buff *skb; if (debug & DEBUG_HFCMULTI_DTMF) printk(KERN_DEBUG "%s: dtmf detection irq\n", __FUNCTION__); ch = 0; while(ch < 32) { // only process enabled B-channels bch = hc->chan[ch].ch; if ((!bch) || !test_bit(FLG_BCHANNEL, &bch->Flags)) { ch++; continue; } if (!hc->created[hc->chan[ch].port]) { ch++; continue; } if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) { ch++; continue; } if (debug & DEBUG_HFCMULTI_DTMF) printk(KERN_DEBUG "%s: dtmf channel %d:", __FUNCTION__, ch); dtmf = 1; co = 0; while(co < 8) { // read W(n-1) coefficient addr = hc->DTMFbase + ((co<<7) | (ch<<2)); HFC_outb_(hc, R_RAM_ADDR0, addr); HFC_outb_(hc, R_RAM_ADDR1, addr>>8); HFC_outb_(hc, R_RAM_ADDR2, (addr>>16) | V_ADDR_INC); w_float = HFC_inb_(hc, R_RAM_DATA); #ifdef CONFIG_HFCMULTI_PCIMEM w_float |= (HFC_inb_(hc, R_RAM_DATA) << 8); #else w_float |= (HFC_getb(hc) << 8); #endif if (debug & DEBUG_HFCMULTI_DTMF) printk(" %04x", w_float); // decode float (see chip doc) mantissa = w_float & 0x0fff; if (w_float & 0x8000) mantissa |= 0xfffff000; exponent = (w_float>>12) & 0x7; if (exponent) { mantissa ^= 0x1000; mantissa <<= (exponent-1); } // store coefficient coeff[co<<1] = mantissa; // read W(n) coefficient w_float = HFC_inb_(hc, R_RAM_DATA); #ifdef CONFIG_HFCMULTI_PCIMEM w_float |= (HFC_inb_(hc, R_RAM_DATA) << 8); #else w_float |= (HFC_getb(hc) << 8); #endif if (debug & DEBUG_HFCMULTI_DTMF) printk(" %04x", w_float); // decode float (see chip doc) mantissa = w_float & 0x0fff; if (w_float & 0x8000) mantissa |= 0xfffff000; exponent = (w_float>>12) & 0x7; if (exponent) { mantissa ^= 0x1000; mantissa <<= (exponent-1); } // store coefficient coeff[(co<<1)|1] = mantissa; co++; } skb = create_link_skb(PH_CONTROL | INDICATION, HW_HFC_COEFF, sizeof(coeff), coeff, 0); if (!skb) { printk(KERN_WARNING "%s: No memory for skb\n", __FUNCTION__); ch++; continue; } if (debug & DEBUG_HFCMULTI_DTMF) { printk("\n"); printk("%s: DTMF ready %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx len=%d\n", __FUNCTION__, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4], coeff[5], coeff[6], coeff[7], skb->len); } #ifdef FIXME // TODO changed if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) && bch->dev) hif = &bch->dev->rport.pif; else hif = &bch->inst.up; #endif if (mISDN_queue_up(&bch->inst, 0, skb)) dev_kfree_skb(skb); ch++; } // restart DTMF processing hc->dtmf = dtmf; if (dtmf) HFC_outb_(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); } #ifdef CONFIG_HFCMULTI_PCIMEM /*******************/ /* write fifo data */ /*******************/ void write_fifo_data(hfc_multi_t *hc,BYTE *dest,int len) { int i, remain; remain = len; #ifdef FIFO_32BIT_ACCESS for (i = 0; i < len/4; i++) { HFC_outl_(hc, A_FIFO_DATA0, *((DWORD *)dest)); remain -= 4; dest += 4; } #endif #ifdef FIFO_16BIT_ACCESS for (i = 0; i < len/2; i++) { HFC_outw_(hc, A_FIFO_DATA0, *((WORD *)dest)); remain -= 2; dest += 2; } #endif for (i = 0; i < remain; i++) HFC_outb_(hc, A_FIFO_DATA0, *dest++); } #endif #ifndef CONFIG_HFCMULTI_PCIMEM /*******************/ /* write fifo data */ /*******************/ void write_fifo_data(hfc_multi_t *hc, BYTE *dest, int len) { int i, remain; remain = len; HFC_set(hc, A_FIFO_DATA0); #ifdef FIFO_32BIT_ACCESS for (i = 0; i < len/4; i++) { HFC_putl(hc, *((DWORD *)dest)); remain -= 4; dest += 4; } #endif #ifdef FIFO_16BIT_ACCESS for(i = 0; i < len/2; i++) { HFC_putw(hc, *((WORD *)dest)); remain -= 2; dest += 2; } #endif for (i = 0; i < remain; i++) HFC_putb(hc, *dest++); } #endif /*********************************/ /* fill fifo as much as possible */ /*********************************/ static void hfcmulti_tx(hfc_multi_t *hc, int ch, channel_t *chan) { int i, ii, temp, len; int Zspace, z1, z2; int Fspace, f1, f2; BYTE *d; int txpending, slot_tx; f1 = HFC_inb_(hc, A_F1); f2 = HFC_inb_(hc, A_F2); //printk(KERN_DEBUG "txf12:%x %x\n",f1,f2); /* get skb, fifo & mode */ txpending = hc->chan[ch].txpending; slot_tx = hc->chan[ch].slot_tx; len = chan->tx_skb ? chan->tx_skb->len : 0; if ((!len) && txpending != 1) return; /* no data */ //printk("debug: data: len = %d, txpending = %d!!!!\n", *len, txpending); /* lets see how much data we will have left in buffer */ if (test_bit(HFC_CHIP_DIGICARD, &hc->chip) && (hc->chan[ch].protocol == ISDN_PID_L1_B_64TRANS) && (hc->chan[ch].slot_rx < 0) && (hc->chan[ch].bank_rx == 0) && (hc->chan[ch].slot_tx < 0) && (hc->chan[ch].bank_tx == 0)) { HFC_outb_(hc, R_FIFO, 0x20 | (ch << 1)); } else HFC_outb_(hc, R_FIFO, ch << 1); HFC_wait_(hc); if (txpending == 2) { /* reset fifo */ HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait_(hc); HFC_outb(hc, A_SUBCH_CFG, 0); txpending = 1; } next_frame: if (test_bit(FLG_HDLC, &chan->Flags)) { f1 = HFC_inb_(hc, A_F1); f2 = HFC_inb_(hc, A_F2); while (f2 != (temp = HFC_inb_(hc, A_F2))) { if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: reread f2 because %d!=%d\n", __FUNCTION__, temp, f2); f2 = temp; /* repeat until F2 is equal */ } Fspace = f2 - f1 - 1; if (Fspace < 0) Fspace += hc->Flen; /* Old FIFO handling doesn't give us the current Z2 read * pointer, so we cannot send the next frame before the fifo * is empty. It makes no difference except for a slightly * lower performance. */ if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { if (f1 != f2) Fspace = 0; else Fspace = 1; } /* one frame only for ST D-channels, to allow resending */ if (hc->type != 1 && test_bit(FLG_DCHANNEL, &chan->Flags)) { if (f1 != f2) Fspace = 0; } /* F-counter full condition */ if (Fspace == 0) return; } z1 = HFC_inw_(hc, A_Z1) - hc->Zmin; z2 = HFC_inw_(hc, A_Z2) - hc->Zmin; while (z2 != (temp = (HFC_inw_(hc, A_Z2) - hc->Zmin))) { if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: reread z2 because %d!=%d\n", __FUNCTION__, temp, z2); z2 = temp; /* repeat unti Z2 is equal */ } Zspace = z2 - z1 - 1; if (Zspace < 0) Zspace += hc->Zlen; /* buffer too full, there must be at least one more byte for 0-volume */ if (Zspace < 4) /* just to be safe */ return; /* if no data */ if (!len) { if (z1 == z2) { /* empty */ /* if done with FIFO audio data during PCM connection */ if (!test_bit(FLG_HDLC, &chan->Flags) && txpending && slot_tx >= 0) { if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: reconnecting PCM due to no more FIFO data: channel %d slot_tx %d\n", __FUNCTION__, ch, slot_tx); /* connect slot */ HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF); HFC_outb_(hc, R_FIFO, ch<<1 | 1); HFC_wait_(hc); HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF); HFC_outb_(hc, R_FIFO, ch<<1); HFC_wait_(hc); } txpending = hc->chan[ch].txpending = 0; } return; /* no data */ } /* if audio data */ if (!test_bit(FLG_HDLC, &chan->Flags) && !txpending && slot_tx >= 0) { if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: disconnecting PCM due to FIFO data: channel %d slot_tx %d\n", __FUNCTION__, ch, slot_tx); /* disconnect slot */ HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); HFC_outb_(hc, R_FIFO, ch<<1 | 1); HFC_wait_(hc); HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); HFC_outb_(hc, R_FIFO, ch<<1); HFC_wait_(hc); } txpending = hc->chan[ch].txpending = 1; /* show activity */ hc->activity[hc->chan[ch].port] = 1; /* fill fifo to what we have left */ i = chan->tx_idx; ii = len; d = chan->tx_skb->data + i; if (ii - i > Zspace) ii = Zspace + i; if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: fifo(%d) has %d bytes space left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", __FUNCTION__, ch, Zspace, z1, z2, ii-i, len-i, test_bit(FLG_HDLC, &chan->Flags) ? "HDLC":"TRANS"); /* Have to prep the audio data */ write_fifo_data(hc, d, ii - i); chan->tx_idx = ii; /* if not all data has been written */ if (ii != len) { /* NOTE: fifo is started by the calling function */ return; } /* if all data has been written */ if (test_bit(FLG_HDLC, &chan->Flags)) { /* increment f-counter */ HFC_outb_(hc, R_INC_RES_FIFO, V_INC_F); HFC_wait_(hc); } // check for next frame if (chan->tx_skb) { dev_kfree_skb(chan->tx_skb); } if (test_bit(FLG_TX_NEXT, &chan->Flags)) { chan->tx_skb = chan->next_skb; if (chan->tx_skb) { mISDN_head_t *hh = mISDN_HEAD_P(chan->tx_skb); chan->next_skb = NULL; test_and_clear_bit(FLG_TX_NEXT, &chan->Flags); chan->tx_idx = 0; len = chan->tx_skb->len; queue_ch_frame(chan, CONFIRM, hh->dinfo, NULL); goto next_frame; } else { test_and_clear_bit(FLG_TX_NEXT, &chan->Flags); printk(KERN_WARNING "%s: tx irq TX_NEXT without skb (dch ch=%d)\n", __FUNCTION__, ch); } } else chan->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &chan->Flags); chan->tx_idx = 0; /* now we have no more data, so in case of transparent, * we set the last byte in fifo to 'silence' in case we will get * no more data at all. this prevents sending an undefined value. */ if (!test_bit(FLG_HDLC, &chan->Flags)) HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); } #ifdef CONFIG_HFCMULTI_PCIMEM /******************/ /* read fifo data */ /******************/ void read_fifo_data(hfc_multi_t *hc, BYTE *dest, int len) { int i, remain; remain = len; #ifdef FIFO_32BIT_ACCESS for(i = 0; i < len/4; i++) { *((DWORD *)dest) = HFC_inl_(hc, A_FIFO_DATA0); remain -= 4; dest += 4; } #endif #ifdef FIFO_16BIT_ACCESS for(i = 0; i < len/2; i++) { *((WORD *)dest) = HFC_inw_(hc, A_FIFO_DATA0); remain -= 2; dest += 2; } #endif for(i = 0; i < remain; i++) *dest++ = HFC_inb_(hc, A_FIFO_DATA0); } #endif #ifndef CONFIG_HFCMULTI_PCIMEM /******************/ /* read fifo data */ /******************/ void read_fifo_data(hfc_multi_t *hc, BYTE *dest, int len) { int i, remain; remain = len; HFC_set(hc, A_FIFO_DATA0); #ifdef FIFO_32BIT_ACCESS for(i = 0; i < len/4; i++) { *((DWORD *)dest) = HFC_getl(hc); remain -= 4; dest += 4; } #endif #ifdef FIFO_16BIT_ACCESS for(i = 0; i < len/2; i++) { *((WORD *)dest) = HFC_getw(hc); remain -= 2; dest += 2; } #endif for(i = 0;i < remain; i++) *dest++ = HFC_getb(hc); } #endif static void hfcmulti_rx(hfc_multi_t *hc, int ch, channel_t *chan) { int temp; int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ struct sk_buff *skb; /* lets see how much data we received */ if (test_bit(HFC_CHIP_DIGICARD, &hc->chip) && (hc->chan[ch].protocol == ISDN_PID_L1_B_64TRANS) && (hc->chan[ch].slot_rx < 0) && (hc->chan[ch].bank_rx == 0) && (hc->chan[ch].slot_tx < 0) && (hc->chan[ch].bank_tx == 0)) { HFC_outb_(hc, R_FIFO, 0x20 | (ch<<1) | 1); } else HFC_outb_(hc, R_FIFO, (ch<<1)|1); HFC_wait_(hc); next_frame: if (test_bit(FLG_HDLC, &chan->Flags)) { f1 = HFC_inb_(hc, A_F1); while (f1 != (temp = HFC_inb_(hc, A_F1))) { if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: reread f1 because %d!=%d\n", __FUNCTION__, temp, f1); f1 = temp; /* repeat until F1 is equal */ } f2 = HFC_inb_(hc, A_F2); } //if(f1!=f2) printk(KERN_DEBUG "got a chan:%d framef12:%x %x!!!!\n",ch,f1,f2); //printk(KERN_DEBUG "got a chan:%d framef12:%x %x!!!!\n",ch,f1,f2); z1 = HFC_inw_(hc, A_Z1) - hc->Zmin; while(z1 != (temp = (HFC_inw_(hc, A_Z1) - hc->Zmin))) { if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: reread z2 because %d!=%d\n", __FUNCTION__, temp, z2); z1 = temp; /* repeat until Z1 is equal */ } z2 = HFC_inw_(hc, A_Z2) - hc->Zmin; Zsize = z1 - z2; if (test_bit(FLG_HDLC, &chan->Flags) && f1 != f2) /* complete hdlc frame */ Zsize++; if (Zsize < 0) Zsize += hc->Zlen; /* if buffer is empty */ if (Zsize <= 0) return; if (!chan->rx_skb) { chan->rx_skb = alloc_stack_skb(chan->maxlen + 3, chan->up_headerlen); if (!chan->rx_skb) { printk(KERN_DEBUG "%s: No mem for rx_skb\n", __FUNCTION__); return; } } /* show activity */ hc->activity[hc->chan[ch].port] = 1; /* empty fifo with what we have */ if (test_bit(FLG_HDLC, &chan->Flags)) { if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: fifo(%d) reading %d bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) got=%d\n", __FUNCTION__, ch, Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", f1, f2, Zsize + chan->rx_skb->len); /* HDLC */ if ((Zsize + chan->rx_skb->len) > (chan->maxlen + 3)) { if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: hdlc-frame too large.\n", __FUNCTION__); skb_trim(chan->rx_skb, 0); HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait_(hc); return; } read_fifo_data(hc, skb_put(chan->rx_skb, Zsize), Zsize); if (f1 != f2) { /* increment Z2,F2-counter */ HFC_outb_(hc, R_INC_RES_FIFO, V_INC_F); HFC_wait_(hc); /* check size */ if (chan->rx_skb->len < 4) { if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: Frame below minimum size\n", __FUNCTION__); skb_trim(chan->rx_skb, 0); goto next_frame; } /* there is at least one complete frame, check crc */ if (chan->rx_skb->data[chan->rx_skb->len - 1]) { printk(KERN_WARNING "%s: CRC-error\n", __FUNCTION__); mISDN_dt_new_frame(chan->inst.st, CRC_ERR, NULL, 0); skb_trim(chan->rx_skb, 0); goto next_frame; } /* only send dchannel if in active state */ if (test_bit(FLG_DCHANNEL, &chan->Flags) && hc->type == 1 && hc->chan[ch].e1_state != 1) { skb_trim(chan->rx_skb, 0); goto next_frame; } skb_trim(chan->rx_skb, chan->rx_skb->len - 3); if (chan->rx_skb->len < MISDN_COPY_SIZE) { skb = alloc_stack_skb(chan->rx_skb->len, chan->up_headerlen); if (skb) { memcpy(skb_put(skb, chan->rx_skb->len), chan->rx_skb->data, chan->rx_skb->len); skb_trim(chan->rx_skb, 0); } else { skb = chan->rx_skb; chan->rx_skb = NULL; } } else { skb = chan->rx_skb; chan->rx_skb = NULL; } if (debug & DEBUG_HFCMULTI_FIFO) { temp = 0; while(temp < skb->len) printk("%02x ", skb->data[temp++]); printk("\n"); } queue_ch_frame(chan, INDICATION, MISDN_ID_ANY, skb); goto next_frame; } /* there is an incomplete frame */ } else { /* transparent */ if (Zsize > skb_tailroom(chan->rx_skb)) Zsize = skb_tailroom(chan->rx_skb); if (Zsize < MISDN_COPY_SIZE) { skb = alloc_stack_skb(Zsize, chan->up_headerlen); if (!skb) { skb = chan->rx_skb; chan->rx_skb = NULL; } } else { skb = chan->rx_skb; chan->rx_skb = NULL; } if (debug & DEBUG_HFCMULTI_FIFO) printk(KERN_DEBUG "%s: fifo(%d) reading %d bytes (z1=%04x, z2=%04x) TRANS\n", __FUNCTION__, ch, Zsize, z1, z2); read_fifo_data(hc, skb_put(skb, Zsize), Zsize); queue_ch_frame(chan, INDICATION, MISDN_ID_ANY, skb); } } /*********************/ /* Interrupt handler */ /*********************/ static void signal_state_up(channel_t *dch, int dinfo, char *msg) { struct sk_buff *skb; if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: %s\n", __FUNCTION__,msg); skb = create_link_skb(PH_CONTROL | INDICATION, dinfo, 0, NULL, 0); if(!skb) return; if (mISDN_queue_up(&dch->inst, 0, skb)) dev_kfree_skb(skb); } static inline void handle_timer_irq(hfc_multi_t *hc) { int ch, temp; channel_t *chan; ch = 0; while(ch < 32) { chan = hc->chan[ch].ch; if (chan && hc->created[hc->chan[ch].port]) { hfcmulti_tx(hc, ch, chan); /* fifo is started when switching to rx-fifo */ hfcmulti_rx(hc, ch, chan); if (test_bit(FLG_DCHANNEL, &chan->Flags) && hc->chan[ch].nt_timer > -1) { if (!(--hc->chan[ch].nt_timer)) { ph_state_change(chan); if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: nt_timer at state %x\n", __FUNCTION__, chan->state); } } } ch++; } if (hc->type == 1 && hc->created[0]) { chan = hc->chan[16].ch; if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[16].cfg)) { if (debug & DEBUG_HFCMULTI_SYNC) printk(KERN_DEBUG "%s: (id=%d) E1 got LOS\n", __FUNCTION__, hc->id); /* LOS */ temp = HFC_inb_(hc, R_RX_STA0) & V_SIG_LOS; if (!temp && hc->chan[16].los) signal_state_up(chan, HW_LOS, "LOS detected"); if (temp && !hc->chan[16].los) signal_state_up(chan, HW_LOS_OFF, "LOS gone"); hc->chan[16].los = temp; } if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[16].cfg)) { if (debug & DEBUG_HFCMULTI_SYNC) printk(KERN_DEBUG "%s: (id=%d) E1 got AIS\n", __FUNCTION__, hc->id); /* AIS */ temp = HFC_inb_(hc, R_RX_STA0) & V_AIS; if (!temp && hc->chan[16].ais) signal_state_up(chan, HW_AIS, "AIS detected"); if (temp && !hc->chan[16].ais) signal_state_up(chan, HW_AIS_OFF, "AIS gone"); hc->chan[16].ais = temp; } if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[16].cfg)) { if (debug & DEBUG_HFCMULTI_SYNC) printk(KERN_DEBUG "%s: (id=%d) E1 got SLIP (RX)\n", __FUNCTION__, hc->id); /* SLIP */ temp = HFC_inb_(hc, R_SLIP) & V_FOSLIP_RX; if (!temp && hc->chan[16].slip_rx) signal_state_up(chan, HW_SLIP_RX, " bit SLIP detected RX"); hc->chan[16].slip_rx = temp; temp = HFC_inb_(hc, R_SLIP) & V_FOSLIP_TX; if (!temp && hc->chan[16].slip_tx) signal_state_up(chan, HW_SLIP_TX, " bit SLIP detected TX"); hc->chan[16].slip_tx = temp; } temp = HFC_inb_(hc, R_JATT_DIR); switch(hc->chan[16].sync) { case 0: if ((temp & 0x60) == 0x60) { if (debug & DEBUG_HFCMULTI_SYNC) printk(KERN_DEBUG "%s: (id=%d) E1 now in clock sync\n", __FUNCTION__, hc->id); HFC_outb(hc, R_RX_OFF, hc->chan[16].jitter | V_RX_INIT); HFC_outb(hc, R_TX_OFF, hc->chan[16].jitter | V_RX_INIT); hc->chan[16].sync = 1; goto check_framesync; } break; case 1: if ((temp & 0x60) != 0x60) { if (debug & DEBUG_HFCMULTI_SYNC) printk(KERN_DEBUG "%s: (id=%d) E1 lost clock sync\n", __FUNCTION__, hc->id); hc->chan[16].sync = 0; break; } check_framesync: temp = HFC_inb_(hc, R_RX_STA0); if (temp == 0x27) { if (debug & DEBUG_HFCMULTI_SYNC) printk(KERN_DEBUG "%s: (id=%d) E1 now in frame sync\n", __FUNCTION__, hc->id); hc->chan[16].sync = 2; } break; case 2: if ((temp & 0x60) != 0x60) { if (debug & DEBUG_HFCMULTI_SYNC) printk(KERN_DEBUG "%s: (id=%d) E1 lost clock & frame sync\n", __FUNCTION__, hc->id); hc->chan[16].sync = 0; break; } temp = HFC_inb_(hc, R_RX_STA0); if (temp != 0x27) { if (debug & DEBUG_HFCMULTI_SYNC) printk(KERN_DEBUG "%s: (id=%d) E1 lost frame sync\n", __FUNCTION__, hc->id); hc->chan[16].sync = 1; } break; } } if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) hfcmulti_watchdog(hc); if (hc->leds) hfcmulti_leds(hc); } static irqreturn_t hfcmulti_interrupt(int intno, void *dev_id, struct pt_regs *regs) { #ifdef IRQCOUNT_DEBUG static int iq1=0,iq2=0,iq3=0,iq4=0,iq5=0,iq6=0,iqcnt=0; #endif static int count=0; hfc_multi_t *hc = dev_id; channel_t *chan; u_char r_irq_statech, status, r_irq_misc, r_irq_oview, r_irq_fifo_bl; //u_char bl1,bl2; #ifdef CONFIG_PLX_PCI_BRIDGE u_short *plx_acc,wval; #endif int ch,i,j; #ifdef CONFIG_PLX_PCI_BRIDGE plx_acc=(u_short*)(hc->plx_membase+0x4c); wval=*plx_acc; if(!(wval&0x04)) { if(wval&0x20) { //printk(KERN_WARNING "NO irq LINTI1:%x\n",wval); printk(KERN_WARNING "got irq LINTI2\n"); } return(IRQ_NONE); } #endif spin_lock(&hc->lock); if (!hc) { printk(KERN_WARNING "HFC-multi: Spurious interrupt!\n"); irq_notforus: #ifdef CONFIG_PLX_PCI_BRIDGE //plx_acc=(u_short*)(hc->plx_membase+0x4c); //*plx_acc=0xc00; // clear LINTI1 & LINTI2 //*plx_acc=0xc41; #endif spin_unlock(&hc->lock); return(IRQ_NONE); } status = HFC_inb_(hc, R_STATUS); r_irq_statech = HFC_inb_(hc, R_IRQ_STATECH); #ifdef IRQCOUNT_DEBUG if (r_irq_statech) iq1++; if (status&V_DTMF_STA) iq2++; if (status&V_LOST_STA) iq3++; if (status&V_EXT_IRQSTA) iq4++; if (status&V_MISC_IRQSTA) iq5++; if (status&V_FR_IRQSTA) iq6++; if (iqcnt++ > 5000) { printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n",iq1,iq2,iq3,iq4,iq5,iq6); iqcnt=0; } #endif if (!r_irq_statech && !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | V_MISC_IRQSTA | V_FR_IRQSTA))) { /* irq is not for us */ //if(status) printk(KERN_WARNING "nofus:%x\n",status); goto irq_notforus; } hc->irqcnt++; if (r_irq_statech) { if (hc->type != 1) { /* state machine */ ch = 0; while(ch < 32) { chan = hc->chan[ch].ch; if (chan && test_bit(FLG_DCHANNEL, &chan->Flags)) { if (r_irq_statech & 1) { HFC_outb_(hc, R_ST_SEL, hc->chan[ch].port); chan->state = HFC_inb(hc, A_ST_RD_STATE) & 0x0f; if (chan->state == (test_bit(HFC_CFG_NTMODE, &hc->chan[chan->channel].cfg) ? 3: 7)) { HFC_outb_(hc, R_FIFO, (ch<<1) | 1); HFC_wait_(hc); HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait_(hc); chan->tx_idx=0; } ph_state_change(chan); if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: S/T newstate %x port %d\n", __FUNCTION__, chan->state, hc->chan[ch].port); dt_newstate(chan->inst.st, chan->state, "S/T"); } r_irq_statech >>= 1; } ch++; } } } if (status & V_EXT_IRQSTA) { /* external IRQ */ } if (status & V_LOST_STA) { /* LOST IRQ */ HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ } if (status & V_MISC_IRQSTA) { /* misc IRQ */ r_irq_misc = HFC_inb_(hc, R_IRQ_MISC); if (r_irq_misc & V_STA_IRQ) { if (hc->type == 1) { /* state machine */ chan = hc->chan[16].ch; chan->state = HFC_inb_(hc, R_E1_RD_STA) & 0x7; ph_state_change(chan); if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: E1 newstate %x\n", __FUNCTION__, chan->state); dt_newstate(chan->inst.st, chan->state, "E1"); } } if (r_irq_misc & V_TI_IRQ) handle_timer_irq(hc); if (r_irq_misc & V_DTMF_IRQ) { /* -> DTMF IRQ */ hfcmulti_dtmf(hc); } if (r_irq_misc & V_IRQ_PROC) { /* IRQ every 125us */ count++; /* generate 1kHz signal */ if(count==8) { if (hfc_interrupt) hfc_interrupt(); count = 0; } } } if (status & V_FR_IRQSTA) { /* FIFO IRQ */ r_irq_oview = HFC_inb_(hc, R_IRQ_OVIEW); //if(r_irq_oview) printk(KERN_DEBUG "OV:%x\n",r_irq_oview); i = 0; while(i < 8) { if (r_irq_oview & (1 << i)) { r_irq_fifo_bl = HFC_inb_(hc, R_IRQ_FIFO_BL0 + i); //r_irq_fifo_bl = HFC_inb_(hc, R_INT_DATA); //if(r_irq_fifo_bl) printk(KERN_DEBUG "BL%d:%x\n",i,r_irq_fifo_bl); //bl1 = HFC_inb_(hc, R_IRQ_FIFO_BL0); //bl2 = HFC_inb_(hc, R_IRQ_FIFO_BL0); //printk(KERN_DEBUG "zero:%x :%x\n",bl1,bl2); r_irq_fifo_bl = HFC_inb_(hc, R_IRQ_FIFO_BL0 + i); j = 0; while(j < 8) { ch = (i<<2) + (j>>1); if (ch >= 16) { if (ch == 16) printk("Shouldn't be servicing high FIFOs. Continuing.\n"); continue; } chan = hc->chan[ch].ch; if (r_irq_fifo_bl & (1 << j)) { if (chan && hc->created[hc->chan[ch].port] && test_bit(FLG_ACTIVE, &chan->Flags)) { //printk(KERN_DEBUG "txchan:%d\n",ch); hfcmulti_tx(hc, ch, chan); /* start fifo */ HFC_outb_(hc, R_FIFO, 0); HFC_wait_(hc); } } j++; if (r_irq_fifo_bl & (1 << j)) { if (chan && hc->created[hc->chan[ch].port] && test_bit(FLG_ACTIVE, &chan->Flags)) { hfcmulti_rx(hc, ch, chan); //printk(KERN_DEBUG "rxchan:%d\n",ch); } } j++; } } i++; } } #ifdef CONFIG_PLX_PCI_BRIDGE //plx_acc=(u_short*)(hc->plx_membase+0x4c); //*plx_acc=0xc00; // clear LINTI1 & LINTI2 //*plx_acc=0xc41; #endif spin_unlock(&hc->lock); return(IRQ_HANDLED); } /********************************************************************/ /* timer callback for D-chan busy resolution. Currently no function */ /********************************************************************/ static void hfcmulti_dbusy_timer(hfc_multi_t *hc) { } /***************************************************************/ /* activate/deactivate hardware for selected channels and mode */ /***************************************************************/ /* configure B-channel with the given protocol * ch eqals to the HFC-channel (0-31) * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 for S/T, 1-31 for E1) * the hdlc interrupts will be set/unset * */ static int mode_hfcmulti(hfc_multi_t *hc, int ch, int protocol, int slot_tx, int bank_tx, int slot_rx, int bank_rx) { int flow_tx = 0, flow_rx = 0, routing = 0; int oslot_tx = hc->chan[ch].slot_tx; int oslot_rx = hc->chan[ch].slot_rx; int conf = hc->chan[ch].conf; if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: channel %d protocol %x slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, ch, protocol, slot_tx, bank_tx, slot_rx, bank_rx); if (oslot_tx>=0 && slot_tx!=oslot_tx) { /* remove from slot */ if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", __FUNCTION__, oslot_tx); if (hc->slot_owner[oslot_tx<<1] == ch) { HFC_outb(hc, R_SLOT, oslot_tx<<1); HFC_outb(hc, A_SL_CFG, 0); HFC_outb(hc, A_CONF, 0); hc->slot_owner[oslot_tx<<1] = -1; } else { if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: we are not owner of this slot anymore, channel %d is.\n", __FUNCTION__, hc->slot_owner[oslot_tx<<1]); } } if (oslot_rx>=0 && slot_rx!=oslot_rx) { /* remove from slot */ if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: remove from slot %d (RX)\n", __FUNCTION__, oslot_rx); if (hc->slot_owner[(oslot_rx<<1)|1] == ch) { HFC_outb(hc, R_SLOT, (oslot_rx<<1) | V_SL_DIR); HFC_outb(hc, A_SL_CFG, 0); hc->slot_owner[(oslot_rx<<1)|1] = -1; } else { if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: we are not owner of this slot anymore, channel %d is.\n", __FUNCTION__, hc->slot_owner[(oslot_rx<<1)|1]); } } if (slot_tx < 0) { flow_tx = 0x80; /* FIFO->ST */ /* disable pcm slot */ hc->chan[ch].slot_tx = -1; hc->chan[ch].bank_tx = 0; } else { /* set pcm slot */ if (hc->chan[ch].txpending) flow_tx = 0x80; /* FIFO->ST */ else flow_tx = 0xc0; /* PCM->ST */ /* put on slot */ routing = bank_tx?0xc0:0x80; if (conf>=0 || bank_tx>1) routing = 0x40; /* loop */ if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: put to slot %d bank %d flow %02x routing %02x conf %d (TX)\n", __FUNCTION__, slot_tx, bank_tx, flow_tx, routing, conf); HFC_outb(hc, R_SLOT, slot_tx<<1); HFC_outb(hc, A_SL_CFG, (ch<<1) | routing); HFC_outb(hc, A_CONF, (conf<0)?0:(conf|V_CONF_SL)); hc->slot_owner[slot_tx<<1] = ch; hc->chan[ch].slot_tx = slot_tx; hc->chan[ch].bank_tx = bank_tx; } if (slot_rx < 0) { /* disable pcm slot */ flow_rx = 0x80; /* ST->FIFO */ hc->chan[ch].slot_rx = -1; hc->chan[ch].bank_rx = 0; } else { /* set pcm slot */ if (hc->chan[ch].txpending) flow_rx = 0x80; /* ST->FIFO */ else flow_rx = 0xc0; /* ST->(FIFO,PCM) */ /* put on slot */ routing = bank_rx?0x80:0xc0; /* reversed */ if (conf >= 0 || bank_rx > 1) routing = 0x40; /* loop */ if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: put to slot %d bank %d flow %02x routing %02x conf %d (RX)\n", __FUNCTION__, slot_rx, bank_rx, flow_rx, routing, conf); HFC_outb(hc, R_SLOT, (slot_rx<<1) | V_SL_DIR); HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing); hc->slot_owner[(slot_rx<<1)|1] = ch; hc->chan[ch].slot_rx = slot_rx; hc->chan[ch].bank_rx = bank_rx; } switch (protocol) { case (ISDN_PID_NONE): /* disable TX fifo */ HFC_outb(hc, R_FIFO, ch << 1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); HFC_outb(hc, A_SUBCH_CFG, 0); HFC_outb(hc, A_IRQ_MSK, 0); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); /* disable RX fifo */ HFC_outb(hc, R_FIFO, (ch<<1)|1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); HFC_outb(hc, A_SUBCH_CFG, 0); HFC_outb(hc, A_IRQ_MSK, 0); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); if (hc->type == 1) { } else if ((ch & 0x3) < 2) { hc->hw.a_st_ctrl0[hc->chan[ch].port] &= ((ch & 0x3) == 0)? ~V_B1_EN: ~V_B2_EN; HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]); } if (test_bit(FLG_BCHANNEL, &hc->chan[ch].ch->Flags)) { test_and_clear_bit(FLG_HDLC, &hc->chan[ch].ch->Flags); test_and_clear_bit(FLG_TRANSPARENT, &hc->chan[ch].ch->Flags); } break; case (ISDN_PID_L1_B_64TRANS): /* B-channel */ #ifdef B410P_CARD if (test_bit(HFC_CHIP_DIGICARD, &hc->chip) && (hc->chan[ch].slot_rx < 0) && (hc->chan[ch].bank_rx == 0) && (hc->chan[ch].slot_tx < 0) && (hc->chan[ch].bank_tx == 0)) { printk("Setting B-channel %d to echo cancelable state on PCM slot %d\n", ch, ((ch/4)*8) + ((ch%4)*4) + 1); printk("Enabling pass through for channel\n"); vpm_out(hc, ch, ((ch/4)*8) + ((ch%4)*4) + 1, 0x01); /* rx path */ /* S/T -> PCM */ HFC_outb(hc, R_FIFO, (ch << 1)); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); HFC_outb(hc, R_SLOT, (((ch/4)*8) + ((ch%4)*4) + 1) << 1); HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1)); /* PCM -> FIFO */ HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); HFC_outb(hc, A_SUBCH_CFG, 0); HFC_outb(hc, A_IRQ_MSK, 0); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); HFC_outb(hc, R_SLOT, ((((ch/4)*8) + ((ch%4)*4) + 1) << 1) | 1); HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1); /* tx path */ /* PCM -> S/T */ HFC_outb(hc, R_FIFO, (ch << 1) | 1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); HFC_outb(hc, R_SLOT, ((((ch/4)*8) + ((ch%4)*4)) << 1) | 1); HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1); /* FIFO -> PCM */ HFC_outb(hc, R_FIFO, 0x20 | (ch << 1)); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); HFC_outb(hc, A_SUBCH_CFG, 0); HFC_outb(hc, A_IRQ_MSK, 0); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); /* tx silence */ HFC_outb(hc, R_SLOT, (((ch/4)*8) + ((ch%4)*4)) << 1); HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); } else #endif { /* enable TX fifo */ HFC_outb(hc, R_FIFO, ch<<1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_HDLC_TRP | V_IFF); HFC_outb(hc, A_SUBCH_CFG, 0); HFC_outb(hc, A_IRQ_MSK, 0); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); /* tx silence */ /* enable RX fifo */ HFC_outb(hc, R_FIFO, (ch<<1)|1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP); HFC_outb(hc, A_SUBCH_CFG, 0); HFC_outb(hc, A_IRQ_MSK, 0); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); } if (hc->type != 1) { hc->hw.a_st_ctrl0[hc->chan[ch].port] |= ((ch&0x3)==0)?V_B1_EN:V_B2_EN; HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]); } if (test_bit(FLG_BCHANNEL, &hc->chan[ch].ch->Flags)) test_and_set_bit(FLG_TRANSPARENT, &hc->chan[ch].ch->Flags); break; case (ISDN_PID_L1_B_64HDLC): /* B-channel */ case (ISDN_PID_L1_TE_E1): /* D-channel E1 */ case (ISDN_PID_L1_NT_E1): /* enable TX fifo */ HFC_outb(hc, R_FIFO, ch<<1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); HFC_outb(hc, A_SUBCH_CFG, 0); HFC_outb(hc, A_IRQ_MSK, V_IRQ); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); /* enable RX fifo */ HFC_outb(hc, R_FIFO, (ch<<1)|1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); HFC_outb(hc, A_SUBCH_CFG, 0); HFC_outb(hc, A_IRQ_MSK, V_IRQ); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); if (hc->type == 1) { } else { hc->hw.a_st_ctrl0[hc->chan[ch].port] |= ((ch&0x3)==0)?V_B1_EN:V_B2_EN; HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]); } if (test_bit(FLG_BCHANNEL, &hc->chan[ch].ch->Flags)) test_and_set_bit(FLG_HDLC, &hc->chan[ch].ch->Flags); break; case (ISDN_PID_L1_TE_S0): /* D-channel S0 */ case (ISDN_PID_L1_NT_S0): /* enable TX fifo */ HFC_outb(hc, R_FIFO, ch<<1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); HFC_outb(hc, A_SUBCH_CFG, 2); HFC_outb(hc, A_IRQ_MSK, V_IRQ); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); /* enable RX fifo */ HFC_outb(hc, R_FIFO, (ch<<1)|1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); HFC_outb(hc, A_SUBCH_CFG, 2); HFC_outb(hc, A_IRQ_MSK, V_IRQ); HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); break; default: printk(KERN_DEBUG "%s: protocol not known %x\n", __FUNCTION__, protocol); hc->chan[ch].protocol = ISDN_PID_NONE; return(-ENOPROTOOPT); } hc->chan[ch].protocol = protocol; return(0); } /**************************/ /* connect/disconnect PCM */ /**************************/ static void hfcmulti_pcm(hfc_multi_t *hc, int ch, int slot_tx, int bank_tx, int slot_rx, int bank_rx) { if (slot_rx<0 || slot_rx<0 || bank_tx<0 || bank_rx<0) { /* disable PCM */ mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); return; } /* enable pcm */ mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, slot_rx, bank_rx); } /**************************/ /* set/disable conference */ /**************************/ static void hfcmulti_conf(hfc_multi_t *hc, int ch, int num) { if (num>=0 && num<=7) hc->chan[ch].conf = num; else hc->chan[ch].conf = -1; mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, hc->chan[ch].bank_rx); } /***************************/ /* set/disable sample loop */ /***************************/ // NOTE: this function is experimental and therefore disabled static void hfcmulti_splloop(hfc_multi_t *hc, int ch, u_char *data, int len) { channel_t *bch = hc->chan[ch].ch; /* flush pending TX data */ if (bch->next_skb) { test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } bch->tx_idx = 0; /* prevent overflow */ if (len > hc->Zlen-1) len = hc->Zlen-1; /* select fifo */ HFC_outb_(hc, R_FIFO, ch<<1); HFC_wait_(hc); /* reset fifo */ HFC_outb(hc, A_SUBCH_CFG, 0); udelay(500); HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait_(hc); udelay(500); /* if off */ if (len <= 0) { HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); if (hc->chan[ch].slot_tx>=0) { if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: connecting PCM due to no more TONE: channel %d slot_tx %d\n", __FUNCTION__, ch, hc->chan[ch].slot_tx); /* connect slot */ HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF); HFC_outb(hc, R_FIFO, ch<<1 | 1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF); } hc->chan[ch].txpending = 0; return; } /* loop fifo */ /* set mode */ hc->chan[ch].txpending = 2; //printk("len=%d %02x %02x %02x\n", len, data[0], data[1], data[2]); /* write loop data */ write_fifo_data(hc,data,len); udelay(500); HFC_outb(hc, A_SUBCH_CFG, V_LOOP_FIFO); udelay(500); /* disconnect slot */ if (hc->chan[ch].slot_tx>=0) { if (debug & DEBUG_HFCMULTI_MODE) printk(KERN_DEBUG "%s: disconnecting PCM due to TONE: channel %d slot_tx %d\n", __FUNCTION__, ch, hc->chan[ch].slot_tx); HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); HFC_outb(hc, R_FIFO, ch<<1 | 1); HFC_wait(hc); HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); HFC_outb(hc, R_FIFO, ch<<1); HFC_wait(hc); } else { /* change fifo */ HFC_outb(hc, R_FIFO, ch<<1); HFC_wait(hc); } //udelay(300); } /******************************/ /* Layer2 -> Layer 1 Transfer */ /******************************/ static int handle_dmsg(channel_t *ch, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); hfc_multi_t *hc = ch->inst.privat; int slot_tx, slot_rx, bank_tx, bank_rx; int ret = -EAGAIN; u_long flags; if (hh->prim == (PH_SIGNAL | REQUEST)) { spin_lock_irqsave(ch->inst.hwlock, flags); switch (hh->dinfo) { case INFO3_P8: case INFO3_P10: break; default: printk(KERN_DEBUG "%s: unknown PH_SIGNAL info %x\n", __FUNCTION__, hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(ch->inst.hwlock, flags); } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(ch->inst.hwlock, flags); ret = 0; switch (hh->dinfo) { case HW_RESET: /* start activation */ if (hc->type == 1) { HFC_outb(hc, R_E1_WR_STA, V_E1_LD_STA | 0); udelay(6); /* wait at least 5,21us */ HFC_outb(hc, R_E1_WR_STA, 0); } else { HFC_outb(hc, R_ST_SEL, hc->chan[ch->channel].port); HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* G1 */ udelay(6); /* wait at least 5,21us */ HFC_outb(hc, A_ST_WR_STATE, 3); HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT*3)); /* activate */ } spin_unlock_irqrestore(ch->inst.hwlock, flags); skb_trim(skb, 0); return(mISDN_queueup_newhead(&ch->inst, 0, PH_CONTROL | INDICATION,HW_POWERUP, skb)); case HW_DEACTIVATE: if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_DEACTIVATE\n", __FUNCTION__); goto hw_deactivate; /* after lock */ case HW_PCM_CONN: /* connect interface to pcm timeslot (0..N) */ if (skb->len < 4*sizeof(u_long)) { printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__); break; } slot_tx = ((int *)skb->data)[0]; bank_tx = ((int *)skb->data)[1]; slot_rx = ((int *)skb->data)[2]; bank_rx = ((int *)skb->data)[3]; if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_INFO "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); if (slot_tx <= hc->slots && bank_tx <=2 && slot_rx <= hc->slots && bank_rx <= 2) hfcmulti_pcm(hc, ch->channel, slot_tx, bank_tx, slot_rx, bank_rx); else printk(KERN_WARNING "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX) out of range\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); break; case HW_PCM_DISC: /* release interface from pcm timeslot */ if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_INFO "%s: HW_PCM_DISC\n", __FUNCTION__); hfcmulti_pcm(hc, ch->channel, -1, -1, -1, -1); break; case HW_POWERUP: HFC_outb(hc, R_ST_SEL, hc->chan[ch->channel].port); HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */ udelay(6); /* wait at least 5,21us */ HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */ break; default: printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(ch->inst.hwlock, flags); } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { if (test_bit(HFC_CFG_NTMODE, &hc->chan[ch->channel].cfg)) { if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: PH_ACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[ch->channel].port, hc->ports-1); spin_lock_irqsave(ch->inst.hwlock, flags); /* start activation */ if (hc->type == 1) { //chanannel_sched_event(chan, D_L1STATECHANGE); ph_state_change(ch); if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: E1 report state %x \n", __FUNCTION__, ch->state); } else { HFC_outb(hc, R_ST_SEL, hc->chan[ch->channel].port); HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); /* G1 */ udelay(6); /* wait at least 5,21us */ HFC_outb(hc, A_ST_WR_STATE, 1); HFC_outb(hc, A_ST_WR_STATE, 1 | (V_ST_ACT*3)); /* activate */ ch->state = 1; } spin_unlock_irqrestore(ch->inst.hwlock, flags); ret = 0; } else { if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: PH_ACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[ch->channel].port, hc->ports-1); ret = -EINVAL; } } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { if (test_bit(HFC_CFG_NTMODE, &hc->chan[ch->channel].cfg)) { if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: PH_DEACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[ch->channel].port, hc->ports-1); spin_lock_irqsave(ch->inst.hwlock, flags); hw_deactivate: //ch->state = 0; ch->state = 1; /* start deactivation */ if (hc->type == 1) { if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: PH_DEACTIVATE no BRI\n", __FUNCTION__); } else { HFC_outb(hc, R_ST_SEL, hc->chan[ch->channel].port); HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); /* deactivate */ } if (ch->next_skb) { dev_kfree_skb(ch->next_skb); ch->next_skb = NULL; } if (ch->rx_skb) { dev_kfree_skb(ch->rx_skb); ch->rx_skb = NULL; } ch->tx_idx = 0; if (ch->tx_skb) { dev_kfree_skb(ch->tx_skb); ch->tx_skb = NULL; } test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); if (test_and_clear_bit(FLG_BUSY_TIMER, &ch->Flags)) del_timer(&ch->timer); spin_unlock_irqrestore(ch->inst.hwlock, flags); ret = 0; } else { if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: PH_DEACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[ch->channel].port, hc->ports-1); ret = -EINVAL; } } else if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS) { u_int temp = hh->dinfo & SSTATUS_ALL; // remove SSTATUS_BROADCAST_BIT if (((hc->type == 1) || test_bit(HFC_CFG_NTMODE, &hc->chan[ch->channel].cfg)) && (temp == SSTATUS_ALL || temp == SSTATUS_L1)) { if (hh->dinfo & SSTATUS_BROADCAST_BIT) temp = ch->inst.id | MSG_BROADCAST; else temp = hh->addr | FLG_MSG_TARGET; skb_trim(skb, 0); hh->dinfo = test_bit(FLG_ACTIVE, &ch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED; hh->prim = MGR_SHORTSTATUS | CONFIRM; return(mISDN_queue_message(&ch->inst, temp, skb)); } ret = -EOPNOTSUPP; } if (!ret) dev_kfree_skb(skb); return(ret); } static int handle_bmsg(channel_t *ch, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); hfc_multi_t *hc = ch->inst.privat; u_long flags, num; int slot_tx, slot_rx, bank_tx, bank_rx; int ret = -EAGAIN; struct dsp_features *features; int taps; if ((hh->prim == (PH_ACTIVATE | REQUEST)) || (hh->prim == (DL_ESTABLISH | REQUEST))) { /* activate B-channel if not already activated */ if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", __FUNCTION__, ch->channel); if (!test_and_set_bit(FLG_ACTIVE, &ch->Flags)) { spin_lock_irqsave(ch->inst.hwlock, flags); if (ch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) test_and_set_bit(FLG_L2DATA, &ch->Flags); ret = mode_hfcmulti(hc, ch->channel, ch->inst.pid.protocol[1], hc->chan[ch->channel].slot_tx, hc->chan[ch->channel].bank_tx, hc->chan[ch->channel].slot_rx, hc->chan[ch->channel].bank_rx); if (!ret) { if (ch->inst.pid.protocol[1] == ISDN_PID_L1_B_64TRANS && !hc->dtmf) { /* start decoder */ hc->dtmf = 1; if (debug & DEBUG_HFCMULTI_DTMF) printk(KERN_DEBUG "%s: start dtmf decoder\n", __FUNCTION__); HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); } } spin_unlock_irqrestore(ch->inst.hwlock, flags); } else ret = 0; skb_trim(skb, 0); return(mISDN_queueup_newhead(&ch->inst, 0, hh->prim | CONFIRM, ret, skb)); } if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || (hh->prim == (DL_RELEASE | REQUEST)) || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: PH_DEACTIVATE ch %d (0..32)\n", __FUNCTION__, ch->channel); /* deactivate B-channel if not already deactivated */ spin_lock_irqsave(ch->inst.hwlock, flags); if (ch->next_skb) { test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); dev_kfree_skb(ch->next_skb); ch->next_skb = NULL; } if (ch->tx_skb) { dev_kfree_skb(ch->tx_skb); ch->tx_skb = NULL; } ch->tx_idx = 0; if (ch->rx_skb) { dev_kfree_skb(ch->rx_skb); ch->rx_skb = NULL; } test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); hc->chan[ch->channel].slot_tx = -1; hc->chan[ch->channel].slot_rx = -1; hc->chan[ch->channel].conf = -1; mode_hfcmulti(hc, ch->channel, ISDN_PID_NONE, hc->chan[ch->channel].slot_tx, hc->chan[ch->channel].bank_tx, hc->chan[ch->channel].slot_rx, hc->chan[ch->channel].bank_rx); test_and_clear_bit(FLG_L2DATA, &ch->Flags); test_and_clear_bit(FLG_ACTIVE, &ch->Flags); spin_unlock_irqrestore(ch->inst.hwlock, flags); skb_trim(skb, 0); ret = 0; if (hh->prim != (PH_CONTROL | REQUEST)) return(mISDN_queueup_newhead(&ch->inst, 0, hh->prim | CONFIRM, ret, skb)); } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(ch->inst.hwlock, flags); switch (hh->dinfo) { case HW_FEATURES: /* fill features structure */ #warning this is dangerous, the skb should never used to transfer a pointer please use a message if (skb->len != sizeof(void *)) { printk(KERN_WARNING "%s: HW_FEATURES lacks parameters\n", __FUNCTION__); break; } if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_FEATURE request\n", __FUNCTION__); features = *((struct dsp_features **)skb->data); features->hfc_id = hc->id; if (test_bit(HFC_CHIP_DTMF, &hc->chip)) features->hfc_dtmf = 1; features->hfc_loops = 0; features->pcm_id = hc->pcm; features->pcm_slots = hc->slots; features->pcm_banks = 2; if (test_bit(HFC_CHIP_DIGICARD, &hc->chip)) features->hfc_echocanhw=1; ret = 0; break; case HW_PCM_CONN: /* connect interface to pcm timeslot (0..N) */ if (skb->len < 4*sizeof(s32)) { printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__); break; } slot_tx = ((s32 *)skb->data)[0]; bank_tx = ((s32 *)skb->data)[1]; slot_rx = ((s32 *)skb->data)[2]; bank_rx = ((s32 *)skb->data)[3]; if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); if (slot_tx <= hc->slots && bank_tx <= 2 && slot_rx <= hc->slots && bank_rx <= 2) hfcmulti_pcm(hc, ch->channel, slot_tx, bank_tx, slot_rx, bank_rx); else printk(KERN_WARNING "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX) out of range\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); ret = 0; break; case HW_PCM_DISC: /* release interface from pcm timeslot */ if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_PCM_DISC\n", __FUNCTION__); hfcmulti_pcm(hc, ch->channel, -1, -1, -1, -1); ret = 0; break; case HW_CONF_JOIN: /* join conference (0..7) */ if (skb->len < sizeof(u32)) { printk(KERN_WARNING "%s: HW_CONF_JOIN lacks parameters\n", __FUNCTION__); break; } num = ((u32 *)skb->data)[0]; if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_CONF_JOIN conf %ld\n", __FUNCTION__, num); if (num <= 7) { hfcmulti_conf(hc, ch->channel, num); ret = 0; } else printk(KERN_WARNING "%s: HW_CONF_JOIN conf %ld out of range\n", __FUNCTION__, num); break; case HW_CONF_SPLIT: /* split conference */ if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_CONF_SPLIT\n", __FUNCTION__); hfcmulti_conf(hc, ch->channel, -1); ret = 0; break; case HW_SPL_LOOP_ON: /* set sample loop */ if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_SPL_LOOP_ON (len = %d)\n", __FUNCTION__, skb->len); hfcmulti_splloop(hc, ch->channel, skb->data, skb->len); ret = 0; break; case HW_SPL_LOOP_OFF: /* set silence */ if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_SPL_LOOP_OFF\n", __FUNCTION__); hfcmulti_splloop(hc, ch->channel, NULL, 0); ret = 0; break; case HW_ECHOCAN_ON: if (skb->len < sizeof(u32)) { printk(KERN_WARNING "%s: HW_ECHOCAN_ON lacks parameters\n", __FUNCTION__); } taps = ((u32 *)skb->data)[0]; #ifdef B410P_CARD vpm_echocan_on(hc, ch->channel, taps); #endif ret=0; break; case HW_ECHOCAN_OFF: #ifdef B410P_CARD vpm_echocan_off(hc, ch->channel); #endif ret=0; break; default: if (debug) printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(ch->inst.hwlock, flags); } if (!ret) dev_kfree_skb(skb); return(ret); } /* * message transfer from layer 1 to hardware. */ static int hfcmulti_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *chan = container_of(inst, channel_t, inst); hfc_multi_t *hc; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; hc = chan->inst.privat; if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(chan, hh->dinfo, skb); if (ret > 0) { /* direct TX */ hfcmulti_tx(hc, chan->channel, chan); /* start fifo */ HFC_outb(hc, R_FIFO, 0); HFC_wait(hc); ret = 0; } spin_unlock_irqrestore(inst->hwlock, flags); return(ret); } if (test_bit(FLG_DCHANNEL, &chan->Flags)) { ret = handle_dmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (test_bit(FLG_BCHANNEL, &chan->Flags)) { ret = handle_bmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } /***************************/ /* handle D-channel events */ /***************************/ /* handle state change event */ static void ph_state_change(channel_t *dch) { hfc_multi_t *hc = dch->inst.privat; u_int prim = PH_SIGNAL | INDICATION; u_int para = 0; int ch; if (!dch) { printk(KERN_WARNING "%s: ERROR given dch is NULL\n", __FUNCTION__); return; } ch = dch->channel; if (hc->type == 1) { if (!test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) { if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: E1 TE newstate %x\n", __FUNCTION__, dch->state); dt_newstate(dch->inst.st, dch->state, "E1 TE"); } else { if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: E1 NT newstate %x\n", __FUNCTION__, dch->state); dt_newstate(dch->inst.st, dch->state, "E1 NT"); } switch (dch->state) { case (1): prim = PH_ACTIVATE | INDICATION; para = 0; test_and_set_bit(FLG_ACTIVE, &dch->Flags); break; default: if (hc->chan[ch].e1_state != 1) return; prim = PH_DEACTIVATE | INDICATION; para = 0; test_and_clear_bit(FLG_ACTIVE, &dch->Flags); } hc->chan[ch].e1_state = dch->state; } else { if (!test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) { if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: S/T TE newstate %x\n", __FUNCTION__, dch->state); dt_newstate(dch->inst.st, dch->state, "S/T TE"); switch (dch->state) { case (0): prim = PH_CONTROL | INDICATION; para = HW_RESET; break; case (3): prim = PH_CONTROL | INDICATION; para = HW_DEACTIVATE; break; case (5): case (8): para = ANYSIGNAL; break; case (6): para = INFO2; break; case (7): para = INFO4_P8; break; default: return; } } else { if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: S/T NT newstate %x\n", __FUNCTION__, dch->state); dt_newstate(dch->inst.st, dch->state, "S/T NT"); switch (dch->state) { case (2): if (hc->chan[ch].nt_timer == 0) { hc->chan[ch].nt_timer = -1; HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); HFC_outb(hc, A_ST_WR_STATE, 4 | V_ST_LD_STA); /* G4 */ udelay(6); /* wait at least 5,21us */ HFC_outb(hc, A_ST_WR_STATE, 4); dch->state = 4; } else { /* one extra count for the next event */ hc->chan[ch].nt_timer = nt_t1_count[poll_timer] + 1; HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); HFC_outb(hc, A_ST_WR_STATE, 2 | V_SET_G2_G3); /* allow G2 -> G3 transition */ } return; case (1): prim = PH_DEACTIVATE | INDICATION; para = 0; hc->chan[ch].nt_timer = -1; test_and_clear_bit(FLG_ACTIVE, &dch->Flags); break; case (4): hc->chan[ch].nt_timer = -1; return; case (3): prim = PH_ACTIVATE | INDICATION; para = 0; hc->chan[ch].nt_timer = -1; test_and_set_bit(FLG_ACTIVE, &dch->Flags); break; default: return; } } } mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0); if ((hc->type == 1) || test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, MGR_SHORTSTATUS | INDICATION, test_bit(FLG_ACTIVE, &dch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED, 0, NULL, 0); } /*************************************/ /* called for card mode init message */ /*************************************/ static void hfcmulti_initmode(hfc_multi_t *hc) { int nt_mode; BYTE r_sci_msk, a_st_wr_state, r_e1_wr_sta; int i, port; channel_t *dch; // u_long flags; if (debug & DEBUG_HFCMULTI_INIT) printk("%s: entered\n", __FUNCTION__); if (hc->type == 1) { nt_mode = test_bit(HFC_CFG_NTMODE, &hc->chan[16].cfg); hc->chan[16].slot_tx = -1; hc->chan[16].slot_rx = -1; hc->chan[16].conf = -1; mode_hfcmulti(hc, 16, nt_mode?ISDN_PID_L1_NT_E1:ISDN_PID_L1_TE_E1, -1, 0, -1, 0); hc->chan[16].ch->timer.function = (void *) hfcmulti_dbusy_timer; hc->chan[16].ch->timer.data = (long) &hc->chan[16].ch; init_timer(&hc->chan[16].ch->timer); i = 0; while (i < 30) { hc->chan[i+1+(i>=15)].slot_tx = -1; hc->chan[i+1+(i>=15)].slot_rx = -1; hc->chan[i+1+(i>=15)].conf = -1; mode_hfcmulti(hc, i+1+(i>=15), ISDN_PID_NONE, -1, 0, -1, 0); i++; } } else { i = 0; while (i < hc->ports) { nt_mode = test_bit(HFC_CFG_NTMODE, &hc->chan[(i<<2)+2].cfg); hc->chan[(i<<2)+2].slot_tx = -1; hc->chan[(i<<2)+2].slot_rx = -1; hc->chan[(i<<2)+2].conf = -1; mode_hfcmulti(hc, (i<<2)+2, nt_mode?ISDN_PID_L1_NT_S0:ISDN_PID_L1_TE_S0, -1, 0, -1, 0); hc->chan[(i<<2)+2].ch->timer.function = (void *) hfcmulti_dbusy_timer; hc->chan[(i<<2)+2].ch->timer.data = (long) &hc->chan[(i<<2)+2].ch; init_timer(&hc->chan[(i<<2)+2].ch->timer); hc->chan[i<<2].slot_tx = -1; hc->chan[i<<2].slot_rx = -1; hc->chan[i<<2].conf = -1; mode_hfcmulti(hc, i<<2, ISDN_PID_NONE, -1, 0, -1, 0); hc->chan[(i<<2)+1].slot_tx = -1; hc->chan[(i<<2)+1].slot_rx = -1; hc->chan[(i<<2)+1].conf = -1; mode_hfcmulti(hc, (i<<2)+1, ISDN_PID_NONE, -1, 0, -1, 0); i++; } } /* set up interface */ if (hc->type != 1) { /* ST */ r_sci_msk = 0; i = 0; while(i < 32) { dch = hc->chan[i].ch; if (!dch || !test_bit(FLG_DCHANNEL, &dch->Flags)) { i++; continue; } port = hc->chan[i].port; /* select interface */ HFC_outb(hc, R_ST_SEL, port); if (test_bit(HFC_CFG_NTMODE, &hc->chan[i].cfg)) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: ST port %d is NT-mode\n", __FUNCTION__, port); /* clock delay */ HFC_outb(hc, A_ST_CLK_DLY, CLKDEL_NT | 0x60); a_st_wr_state = 1; /* G1 */ hc->hw.a_st_ctrl0[port] = V_ST_MD; } else { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: ST port %d is TE-mode\n", __FUNCTION__, port); /* clock delay */ HFC_outb(hc, A_ST_CLK_DLY, CLKDEL_TE); a_st_wr_state = 2; /* F2 */ hc->hw.a_st_ctrl0[port] = 0; } if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) { hc->hw.a_st_ctrl0[port] |= V_TX_LI; } /* line setup */ HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[port]); /* disable E-channel */ if (test_bit(HFC_CFG_NTMODE, &hc->chan[i].cfg) || test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); /* enable B-channel receive */ HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); /* state machine setup */ HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); udelay(6); /* wait at least 5,21us */ HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); r_sci_msk |= 1 << port; i++; } /* state machine interrupts */ HFC_outb(hc, R_SCI_MSK, r_sci_msk); } else { /* E1 */ if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[16].cfg)) { HFC_outb(hc, R_LOS0, 255); /* 2 ms */ HFC_outb(hc, R_LOS1, 255); /* 512 ms */ } if (test_bit(HFC_CFG_OPTICAL, &hc->chan[16].cfg)) { HFC_outb(hc, R_RX0, 0); hc->hw.r_tx0 = 0 | V_OUT_EN; } else { HFC_outb(hc, R_RX0, 1); hc->hw.r_tx0 = 1 | V_OUT_EN; } hc->hw.r_tx1 = V_ATX | V_NTRI; HFC_outb(hc, R_TX0, hc->hw.r_tx0); HFC_outb(hc, R_TX1, hc->hw.r_tx1); HFC_outb(hc, R_TX_FR0, 0x00); HFC_outb(hc, R_TX_FR1, 0xf8); if (test_bit(HFC_CFG_CRC4, &hc->chan[16].cfg)) HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); if (test_bit(HFC_CFG_CRC4, &hc->chan[16].cfg)) HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { /* SLAVE (clock master) */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: E1 port is clock master (clock from PCM)\n", __FUNCTION__); HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC); } else { if (test_bit(HFC_CHIP_CRYSTAL_CLOCK, &hc->chip)) { /* MASTER (clock master) */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: E1 port is clock master (clock from crystal)\n", __FUNCTION__); HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC | V_JATT_OFF); } else { /* MASTER (clock slave) */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: E1 port is clock slave (clock to PCM)\n", __FUNCTION__); HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); } } if (test_bit(HFC_CFG_NTMODE, &hc->chan[(i<<2)+2].cfg)) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: E1 port is NT-mode\n", __FUNCTION__); r_e1_wr_sta = 0; /* G0 */ }else { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: E1 port is TE-mode\n", __FUNCTION__); r_e1_wr_sta = 0; /* F0 */ } HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX | V_IPATS0 | V_IPATS1 | V_IPATS2); } else { HFC_outb(hc, R_SYNC_OUT, V_IPATS0 | V_IPATS1 | V_IPATS2); } HFC_outb(hc, R_PWM_MD, V_PWM0_MD); HFC_outb(hc, R_PWM0, 0x50); HFC_outb(hc, R_PWM1, 0xff); /* state machine setup */ HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); udelay(6); /* wait at least 5,21us */ HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); } if (debug & DEBUG_HFCMULTI_INIT) printk("%s: done\n", __FUNCTION__); } /***********************/ /* initialize the card */ /***********************/ /* start timer irq, wait some time and check if we have interrupts. * if not, reset chip and try again. */ static int init_card(hfc_multi_t *hc) { int cnt = 1; /* as long as there is no trouble */ int err = -EIO; u_long flags; #ifdef CONFIG_PLX_PCI_BRIDGE u_short *plx_acc; #endif if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); spin_lock_irqsave(&hc->lock, flags); /* set interrupts but let global interrupt disabled*/ hc->hw.r_irq_ctrl = V_FIFO_IRQ; disable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); if (request_irq(hc->pci_dev->irq, (void *)hfcmulti_interrupt, SA_SHIRQ, "HFC-multi", hc)) { printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", hc->pci_dev->irq); return(-EIO); } hc->irq = hc->pci_dev->irq; #ifdef CONFIG_PLX_PCI_BRIDGE plx_acc=(u_short*)(hc->plx_membase+0x4c); *plx_acc=0x41; // enable PCI & LINT1 irq #endif if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: IRQ %d count %d\n", __FUNCTION__, hc->irq, hc->irqcnt); while (cnt) { if ((err = init_chip(hc))) { goto error; } /* Finally enable IRQ output * this is only allowed, if an IRQ routine is allready * established for this HFC, so don't do that earlier */ spin_lock_irqsave(&hc->lock, flags); enable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); //printk(KERN_DEBUG "no master irq set!!!\n"); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ /* turn IRQ off until chip is completely initialized */ spin_lock_irqsave(&hc->lock, flags); disable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: IRQ %d count %d\n", __FUNCTION__, hc->irq, hc->irqcnt); if (hc->irqcnt) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: done\n", __FUNCTION__); return(0); } printk(KERN_WARNING "HFC PCI: IRQ(%d) getting no interrupts during init (try %d)\n", hc->irq, cnt); if (test_bit(HFC_CHIP_CLOCK_IGNORE, &hc->chip) || test_bit(HFC_CHIP_DIGICARD, &hc->chip)) { printk(KERN_WARNING "HFC PCI: Ignoring Clock so we go on here\n"); return 0; } #ifdef CONFIG_PLX_PCI_BRIDGE plx_acc=(u_short*)(hc->plx_membase+0x4c); *plx_acc=0x00; // disable PCI & LINT1 irq #endif cnt--; err = -EIO; } error: if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_WARNING "%s: free irq %d\n", __FUNCTION__, hc->irq); if (hc->irq) { free_irq(hc->irq, hc); hc->irq = 0; } if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: done (err=%d)\n", __FUNCTION__, err); return(err); } /*********************************************************/ /* select free channel and return OK(0), -EBUSY, -EINVAL */ /*********************************************************/ static int SelFreeBChannel(hfc_multi_t *hc, int ch, channel_info_t *ci) { channel_t *bch; hfc_multi_t *hfc; mISDNstack_t *bst; struct list_head *head; int cnr; int port = hc->chan[ch].port; if (port < 0 || port >= hc->ports) { printk(KERN_WARNING "%s: port(%d) out of range", __FUNCTION__, port); return(-EINVAL); } if (!ci) return(-EINVAL); ci->st.p = NULL; cnr=0; bst = hc->chan[ch].ch->inst.st; if (list_empty(&bst->childlist)) { if ((bst->id & FLG_CLONE_STACK) && (bst->childlist.prev != &bst->childlist)) { head = bst->childlist.prev; } else { printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n", __FUNCTION__, bst->id, bst->childlist.prev, &bst->childlist, bst->childlist.next); return(-EINVAL); } } else head = &bst->childlist; list_for_each_entry(bst, head, list) { if (cnr == ((hc->type == 1) ? 30: 2)) /* 30 or 2 B-channels */ { printk(KERN_WARNING "%s: fatal error: more b-stacks than ports", __FUNCTION__); return(-EINVAL); } if(!bst->mgr) { int_errtxt("no mgr st(%p)", bst); return(-EINVAL); } hfc = bst->mgr->privat; if (!hfc) { int_errtxt("no mgr->data st(%p)", bst); return(-EINVAL); } if (hc->type == 1) bch = hc->chan[cnr + 1 + (cnr>=15)].ch; else bch = hc->chan[(port<<2) + cnr].ch; if (!(ci->channel & (~CHANNEL_NUMBER))) { /* only number is set */ if ((ci->channel & 0x3) == (cnr + 1)) { if (test_bit(FLG_ACTIVE, &bch->Flags)) return(-EBUSY); ci->st.p = bst; return(0); } } if ((ci->channel & (~CHANNEL_NUMBER)) == 0x00a18300) { if (!test_bit(FLG_ACTIVE, &bch->Flags)) { ci->st.p = bst; return(0); } } cnr++; } return(-EBUSY); } /*********************************/ /* find pci device and set it up */ /*********************************/ static int setup_pci(hfc_multi_t *hc, struct pci_dev *pdev, int id_idx) { int i; printk(KERN_INFO "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", id_list[id_idx].vendor_name, id_list[id_idx].card_name, (id_list[id_idx].clock2)?"double":"normal"); /* go into 0-state (we might already be due to zero-filled-object */ for (i = 0; i < 32; i++) { if (hc->chan[i].ch && test_bit(FLG_DCHANNEL, &hc->chan[i].ch->Flags)) hc->chan[i].ch->state = 0; } hc->pci_dev = pdev; if (id_list[id_idx].clock2) test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); #if 1 if (id_list[id_idx].device_id == 0xB410) test_and_set_bit(HFC_CHIP_DIGICARD, &hc->chip); #endif if (hc->pci_dev->irq <= 0) { printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); return (-EIO); } if (pci_enable_device(hc->pci_dev)) { printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); return (-EIO); } hc->leds = id_list[id_idx].leds; hc->opticalsupport = id_list[id_idx].opticalsupport; #ifdef CONFIG_HFCMULTI_PCIMEM hc->pci_membase = NULL; hc->plx_membase = NULL; #ifdef CONFIG_PLX_PCI_BRIDGE hc->plx_origmembase = get_pcibase(hc->pci_dev, 0); // MEMBASE 1 is PLX PCI Bridge if (!hc->plx_origmembase) { printk(KERN_WARNING "HFC-multi: No IO-Memory for PCI PLX bridge found\n"); pci_disable_device(hc->pci_dev); return (-EIO); } if (!(hc->plx_membase = ioremap(hc->plx_origmembase, 128))) { printk(KERN_WARNING "HFC-multi: failed to remap plx address space. (internal error)\n"); hc->plx_membase = NULL; pci_disable_device(hc->pci_dev); return (-EIO); } printk(KERN_WARNING "HFC-multi: plx_membase:%#x plx_origmembase:%#x\n",(u_int) hc->plx_membase, (u_int)hc->plx_origmembase); hc->pci_origmembase = get_pcibase(hc->pci_dev, 2); // MEMBASE 1 is PLX PCI Bridge if (!hc->pci_origmembase) { printk(KERN_WARNING "HFC-multi: No IO-Memory for PCI card found\n"); pci_disable_device(hc->pci_dev); return (-EIO); } if (!(hc->pci_membase = ioremap(hc->pci_origmembase, 0x400))) { printk(KERN_WARNING "HFC-multi: failed to remap io address space. (internal error)\n"); hc->pci_membase = NULL; pci_disable_device(hc->pci_dev); return (-EIO); } printk(KERN_INFO "%s: defined at MEMBASE %#x (%#x) IRQ %d HZ %d leds-type %d\n", hc->name, (u_int) hc->pci_membase, (u_int) hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); #else // CONFIG_PLX_PCI_BRIDGE hc->pci_origmembase = get_pcibase(hc->pci_dev, 1); if (!hc->pci_origmembase) { printk(KERN_WARNING "HFC-multi: No IO-Memory for PCI card found\n"); pci_disable_device(hc->pci_dev); return (-EIO); } if (!(hc->pci_membase = ioremap(hc->pci_origmembase, 256))) { printk(KERN_WARNING "HFC-multi: failed to remap io address space. (internal error)\n"); hc->pci_membase = NULL; pci_disable_device(hc->pci_dev); return (-EIO); } printk(KERN_INFO "%s: defined at MEMBASE %#x (%#x) IRQ %d HZ %d leds-type %d\n", hc->name, (u_int) hc->pci_membase, (u_int) hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); #endif // CONFIG_PLX_PCI_BRIDGE #else hc->pci_iobase = (u_int) get_pcibase(hc->pci_dev, 0); if (!hc->pci_iobase) { printk(KERN_WARNING "HFC-multi: No IO for PCI card found\n"); pci_disable_device(hc->pci_dev); return (-EIO); } if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { printk(KERN_WARNING "HFC-multi: failed to rquest address space at 0x%04x (internal error)\n", hc->pci_iobase); hc->pci_iobase = 0; pci_disable_device(hc->pci_dev); return (-EIO); } printk(KERN_INFO "%s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", hc->name, (u_int) hc->pci_iobase, hc->pci_dev->irq, HZ, hc->leds); pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); #endif pci_set_drvdata(hc->pci_dev, hc); /* At this point the needed PCI config is done */ /* fifos are still not enabled */ return (0); } static void release_ports_hw(hfc_multi_t *hc) { u_long flags; spin_lock_irqsave(&hc->lock, flags); /*first we disable all the hw stuff*/ disable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); udelay(1000); /* disable D-channels & B-channels */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: disable all channels (d and b)\n", __FUNCTION__); /* dimm leds */ if (hc->leds) hfcmulti_leds(hc); #if 0 if (hc->type == 1) { hc->chan[16].slot_tx = -1; hc->chan[16].slot_rx = -1; hc->chan[16].conf = -1; mode_hfcmulti(hc, 16, ISDN_PID_NONE, -1, 0, -1, 0);//d i = 0; while(i < 30) { hc->chan[i+1+(i>=15)].slot_tx = -1; hc->chan[i+1+(i>=15)].slot_rx = -1; hc->chan[i+1+(i>=15)].conf = -1; mode_hfcmulti(hc, i+1+(i>=15), ISDN_PID_NONE, -1, 0, -1, 0); //b i++; } } else { i = 0; while(i < hc->ports) { if (hc->created[i]) { hc->chan[(i<<2)+2].slot_tx = -1; hc->chan[(i<<2)+2].slot_rx = -1; hc->chan[(i<<2)+2].conf = -1; mode_hfcmulti(hc, (i<<2)+2, ISDN_PID_NONE, -1, 0, -1, 0); //d hc->chan[i<<2].slot_tx = -1; hc->chan[i<<2].slot_rx = -1; hc->chan[i<<2].conf = -1; mode_hfcmulti(hc, i<<2, ISDN_PID_NONE, -1, 0, -1, 0); //b1 hc->chan[(i<<2)+1].slot_tx = -1; hc->chan[(i<<2)+1].slot_rx = -1; hc->chan[(i<<2)+1].conf = -1; mode_hfcmulti(hc, (i<<2)+1, ISDN_PID_NONE, -1, 0, -1, 0); //b2 } i++; } } #endif release_io_hfcmulti(hc); if (hc->irq) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_WARNING "%s: free irq %d\n", __FUNCTION__, hc->irq); free_irq(hc->irq, hc); hc->irq = 0; } udelay(1000); /*now we finish off the lists and stuff*/ /* remove us from list and delete */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_WARNING "%s: remove instance from list\n", __FUNCTION__); #if 1 u_long flags2; spin_lock_irqsave(&HFCM_obj.lock,flags2); list_del(&hc->list); spin_unlock_irqrestore(&HFCM_obj.lock,flags2); #endif if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_WARNING "%s: delete instance\n", __FUNCTION__); kfree(hc); hc=NULL; HFC_cnt--; if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_WARNING "%s: card successfully removed\n", __FUNCTION__); } /*************************** * remove port from stack * ***************************/ static void release_port(hfc_multi_t *hc, int port) { int i = 0; u_long flags; if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); if (port >= hc->ports) { printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", __FUNCTION__, port); return; } spin_lock_irqsave(&hc->lock, flags); if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: releasing port=%d\n", __FUNCTION__, port); if ( !hc->created[port]) { printk(KERN_WARNING "%s: ERROR given stack is not used by card (port=%d).\n", __FUNCTION__, port); spin_unlock_irqrestore(&hc->lock,flags); return; } for (i=0;i<32;i++) { if (hc->chan[i].ch && test_bit(FLG_DCHANNEL, &hc->chan[i].ch->Flags) && hc->chan[i].ch->timer.function != NULL ) { del_timer(&hc->chan[i].ch->timer); hc->chan[i].ch->timer.function = NULL; } } /* free channels */ i = 0; while(i < 32) { if (hc->chan[i].ch) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: free port %d %c-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, test_bit(FLG_DCHANNEL, &hc->chan[i].ch->Flags) ? 'D' : 'B', i); mISDN_freechannel(hc->chan[i].ch); spin_unlock_irqrestore(&hc->lock,flags); if (test_bit(FLG_DCHANNEL, &hc->chan[i].ch->Flags)) mISDN_ctrl(&hc->chan[i].ch->inst, MGR_UNREGLAYER | REQUEST, NULL); spin_lock_irqsave(&hc->lock,flags); kfree(hc->chan[i].ch); hc->chan[i].ch = NULL; } i++; } hc->created[port]=0; spin_unlock_irqrestore(&hc->lock,flags); } static int HFC_manager(void *data, u_int prim, void *arg) { hfc_multi_t *hc; mISDNinstance_t *inst = data; struct sk_buff *skb; channel_t *chan = NULL; int ch = -1; int i; u_long flags; if (!data) { MGR_HASPROTOCOL_HANDLER(prim,arg,&HFCM_obj) printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); return(-EINVAL); } /* find channel and card */ spin_lock_irqsave(&HFCM_obj.lock, flags); list_for_each_entry(hc, &HFCM_obj.ilist, list) { i = 0; while(i < 32) { //printk(KERN_DEBUG "comparing (D-channel) card=%08x inst=%08x with inst=%08x\n", hc, &hc->dch[i].inst, inst); if ((hc->chan[i].ch) && (&hc->chan[i].ch->inst == inst)) { ch = i; chan = hc->chan[i].ch; break; } i++; } if (ch >= 0) break; } spin_unlock_irqrestore(&HFCM_obj.lock, flags); if (ch < 0) { printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return(-EINVAL); } if (debug & DEBUG_HFCMULTI_MGR) printk(KERN_DEBUG "%s: channel %d (0..31) data %p prim %x arg %p\n", __FUNCTION__, ch, data, prim, arg); switch(prim) { case MGR_REGLAYER | CONFIRM: bugtest if (debug & DEBUG_HFCMULTI_MGR) printk(KERN_DEBUG "%s: MGR_REGLAYER\n", __FUNCTION__); mISDN_setpara(chan, &inst->st->para); bugtest break; case MGR_UNREGLAYER | REQUEST: bugtest if (debug & DEBUG_HFCMULTI_MGR) printk(KERN_DEBUG "%s: MGR_UNREGLAYER\n", __FUNCTION__); i = test_bit(FLG_DCHANNEL, &chan->Flags) ? HW_DEACTIVATE : 0; if ((skb = create_link_skb(PH_CONTROL | REQUEST, i, 0, NULL, 0))) { if (hfcmulti_l2l1(inst, skb)) dev_kfree_skb(skb); } mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); bugtest break; case MGR_CLRSTPARA | INDICATION: arg = NULL; // fall through case MGR_ADDSTPARA | INDICATION: if (debug & DEBUG_HFCMULTI_MGR) printk(KERN_DEBUG "%s: MGR_***STPARA\n", __FUNCTION__); mISDN_setpara(chan, arg); break; case MGR_RELEASE | INDICATION: if (debug & DEBUG_HFCMULTI_MGR) printk(KERN_DEBUG "%s: MGR_RELEASE = remove port from mISDN\n", __FUNCTION__); break; #ifdef FIXME case MGR_CONNECT | REQUEST: if (debug & DEBUG_HFCMULTI_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_HFCMULTI_MGR) printk(KERN_DEBUG "%s: MGR_SETIF\n", __FUNCTION__); if (dch) return(mISDN_SetIF(inst, arg, prim, hfcmulti_l1hw, NULL, dch)); if (bch) return(mISDN_SetIF(inst, arg, prim, hfcmulti_l2l1, NULL, bch)); break; case MGR_DISCONNECT | REQUEST: case MGR_DISCONNECT | INDICATION: if (debug & DEBUG_HFCMULTI_MGR) printk(KERN_DEBUG "%s: MGR_DISCONNECT\n", __FUNCTION__); return(mISDN_DisConnectIF(inst, arg)); #endif case MGR_SELCHANNEL | REQUEST: if (debug & DEBUG_HFCMULTI_MGR) printk(KERN_DEBUG "%s: MGR_SELCHANNEL\n", __FUNCTION__); if (!test_bit(FLG_DCHANNEL, &chan->Flags)) { printk(KERN_WARNING "%s(MGR_SELCHANNEL|REQUEST): selchannel not dinst\n", __FUNCTION__); return(-EINVAL); } return(SelFreeBChannel(hc, ch, arg)); case MGR_SETSTACK | INDICATION: bugtest if (debug & DEBUG_HFCMULTI_MGR) printk(KERN_DEBUG "%s: MGR_SETSTACK\n", __FUNCTION__); if (test_bit(FLG_BCHANNEL, &chan->Flags) && inst->pid.global==2) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (hfcmulti_l2l1(inst, skb)) dev_kfree_skb(skb); } if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } bugtest 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); } static int find_idlist_entry(int vendor,int subvendor, int device, int subdevice) { int cnt; cnt=0; while(id_list[cnt].vendor_id) { if(id_list[cnt].vendor_id==vendor && id_list[cnt].vendor_sub==subvendor && id_list[cnt].device_id==device && id_list[cnt].device_sub==subdevice) return(cnt); cnt++; } return(-1); } static int __devinit hfcpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int i, ret_err=0; static int HFC_idx=0, HFC_port_idx=0; int port_idx; int bchperport, card_type, pt; int ch, ch2; int id_idx; // index to id_list int hfc_type; // chip type int hfc_ports; // chip ports hfc_multi_t *hc; mISDN_pid_t pid, pids[8]; mISDNstack_t *dst = NULL; /* make gcc happy */ channel_t *chan; u_long flags; u_char dips=0, pmj=0; // dip settings, port mode Jumpers id_idx = find_idlist_entry(ent->vendor,ent->subvendor,ent->device,ent->subdevice); if (id_idx == -1) { if (ent->vendor == CCAG_VID) if (ent->device == HFC4S_ID || ent->device == HFC8S_ID || ent->device == HFCE1_ID) printk( KERN_ERR "unknown HFC multiport controller (vendor:%x device:%x subvendor:%x subdevice:%x) Please contact the driver maintainer for support.\n", ent->vendor,ent->device,ent->subvendor,ent->subdevice); return (-ENODEV); } //hfc_type=id_list[id_idx].type; hfc_ports=id_list[id_idx].ports; /* check card type */ switch (ent->device) { case HFCE1_ID: bchperport = 30; card_type=1; hfc_type=0x1; break; case HFC4S_ID: bchperport = 2; card_type=0; hfc_type=0x4; break; case 0xB410: bchperport = 2; card_type=0; hfc_type=0x4; break; case HFC8S_ID: bchperport = 2; card_type=0; hfc_type=0x8; break; default: printk(KERN_ERR "Card type(%d) not supported.\n", type[HFC_idx] & 0xff); ret_err = -EINVAL; goto free_object; } port_idx=HFC_port_idx; if(HFC_idx == -1) { printk( KERN_ERR "HFC-MULTI: Card '%s' found, but not given by module's options, ignoring...\n", id_list[id_idx].card_name); return (-ENODEV); } if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: Registering chip type %d (0x%x)\n", __FUNCTION__, type[HFC_idx] & 0xff, type[HFC_idx]); /* allocate card+fifo structure */ //#warning //void *davor=kmalloc(8, GFP_ATOMIC); if (!(hc = kmalloc(sizeof(hfc_multi_t), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for HFC-Multi card\n"); ret_err = -ENOMEM; goto free_object; } //void *danach=kmalloc(8, GFP_ATOMIC); memset(hc, 0, sizeof(hfc_multi_t)); //hc->davor=davor; //hc->danach=danach; hc->idx = HFC_idx; hc->id = HFC_idx + 1; hc->pcm = pcm[HFC_idx]; /* set chip specific features */ hc->masterclk = -1; hc->type = card_type; hc->ports = hfc_ports; // printk( KERN_NOTICE "type:%d ports:%d HFC_idx:%d port_idx:%d\n",card_type, hfc_ports, HFC_idx, port_idx); if (type[HFC_idx] & 0x100) { test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); silence = 0xff; /* ulaw silence */ } else silence = 0x2a; /* alaw silence */ if (type[HFC_idx] & 0x200) test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); // if ((type[HFC_idx]&0x400) && hc->type==4) // test_and_set_bit(HFC_CHIP_LEDS, &hc->chip); if (type[HFC_idx] & 0x800) test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); if (type[HFC_idx] & 0x1000) test_and_set_bit(HFC_CHIP_CLOCK_IGNORE, &hc->chip); if (type[HFC_idx] & 0x2000) test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip); if (type[HFC_idx] & 0x4000) test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); if (type[HFC_idx] & 0x8000) test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); hc->slots = 32; if (type[HFC_idx] & 0x10000) hc->slots = 64; if (type[HFC_idx] & 0x20000) hc->slots = 128; if (type[HFC_idx] & 0x40000) test_and_set_bit(HFC_CHIP_CRYSTAL_CLOCK, &hc->chip); if (type[HFC_idx] & 0x80000) { test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip); hc->wdcount=0; hc->wdbyte=V_GPIO_OUT2; printk(KERN_NOTICE "Watchdog enabled\n"); } if (hc->type == 1) sprintf(hc->name, "HFC-E1#%d", HFC_idx+1); else sprintf(hc->name, "HFC-%dS#%d", hc->ports, HFC_idx+1); if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); spin_lock_irqsave(&HFCM_obj.lock, flags); list_add_tail(&hc->list, &HFCM_obj.ilist); spin_unlock_irqrestore(&HFCM_obj.lock, flags); if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); spin_lock_init(&hc->lock); pt = 0; while (pt < hc->ports) { if (port_idx >= MAX_PORTS) { printk(KERN_ERR "Invalid HFC type.\n"); ret_err = -EINVAL; goto free_channels; } if (protocol[port_idx] == 0) { printk(KERN_ERR "Not enough 'protocol' values given.\n"); ret_err = -EINVAL; goto free_channels; } if (hc->type == 1) ch = 16; else ch = (pt<<2)+2; if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: Registering D-channel, card(%d) ch(%d) port(%d) protocol(%x)\n", __FUNCTION__, HFC_idx+1, ch, pt+1, protocol[port_idx]); hc->chan[ch].port = pt; hc->chan[ch].nt_timer = -1; chan = kmalloc(sizeof(channel_t), GFP_ATOMIC); if (!chan) { ret_err = -ENOMEM; goto free_channels; } memset(chan, 0, sizeof(channel_t)); chan->channel = ch; //chan->debug = debug; chan->inst.obj = &HFCM_obj; chan->inst.hwlock = &hc->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) chan->inst.class_dev.parent = &pdev->dev; #else chan->inst.class_dev.dev = &pdev->dev; #endif mISDN_init_instance(&chan->inst, &HFCM_obj, hc, hfcmulti_l2l1); chan->inst.pid.layermask = ISDN_LAYER(0); sprintf(chan->inst.name, "HFCm%d/%d", HFC_idx+1, pt+1); ret_err = mISDN_initchannel(chan, MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); if (ret_err) goto free_channels; hc->chan[ch].ch = chan; i=0; while(i < bchperport) { if (hc->type == 1) ch2 = i + 1 + (i>=15); else ch2 = (pt<<2)+i; if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: Registering B-channel, card(%d) ch(%d) port(%d) channel(%d)\n", __FUNCTION__, HFC_idx+1, ch2, pt+1, i); hc->chan[ch2].port = pt; chan = kmalloc(sizeof(channel_t), GFP_ATOMIC); if (!chan) { ret_err = -ENOMEM; goto free_channels; } memset(chan, 0, sizeof(channel_t)); chan->channel = ch2; mISDN_init_instance(&chan->inst, &HFCM_obj, hc, hfcmulti_l2l1); chan->inst.pid.layermask = ISDN_LAYER(0); chan->inst.hwlock = &hc->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) chan->inst.class_dev.parent = &pdev->dev; #else chan->inst.class_dev.dev = &pdev->dev; #endif //bch->debug = debug; sprintf(chan->inst.name, "%s B%d", hc->chan[ch].ch->inst.name, i+1); ret_err = mISDN_initchannel(chan, MSK_INIT_BCHANNEL, MAX_DATA_MEM); if (ret_err) { kfree(chan); goto free_channels; } hc->chan[ch2].ch = chan; #ifdef FIXME // TODO if (chan->dev) { chan->dev->wport.pif.func = hfcmulti_l2l1; chan->dev->wport.pif.fdata = chan; } #endif i++; } chan = hc->chan[ch].ch; /* set D-channel */ mISDN_set_dchannel_pid(&pid, protocol[port_idx], layermask[port_idx]); /* set PRI */ if (hc->type == 1) { if (layermask[port_idx] & ISDN_LAYER(2)) { pid.protocol[2] |= ISDN_PID_L2_DF_PTP; } if (layermask[port_idx] & ISDN_LAYER(3)) { pid.protocol[3] |= ISDN_PID_L3_DF_PTP; pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID; pid.protocol[3] |= ISDN_PID_L3_DF_CRLEN2; } } /* set protocol type */ if (protocol[port_idx] & 0x10) { /* NT-mode */ chan->inst.pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_NT_E1:ISDN_PID_L0_NT_S0; chan->inst.pid.protocol[1] = (hc->type==1)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0; pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_NT_E1:ISDN_PID_L0_NT_S0; pid.protocol[1] = (hc->type==1)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0; chan->inst.pid.layermask |= ISDN_LAYER(1); pid.layermask |= ISDN_LAYER(1); if (layermask[port_idx] & ISDN_LAYER(2)) pid.protocol[2] = ISDN_PID_L2_LAPD_NET; test_and_set_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg); } else { /* TE-mode */ chan->inst.pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_TE_E1:ISDN_PID_L0_TE_S0; pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_TE_E1:ISDN_PID_L0_TE_S0; if (hc->type == 1) { /* own E1 for E1 */ chan->inst.pid.protocol[1] = ISDN_PID_L1_TE_E1; pid.protocol[1] = ISDN_PID_L1_TE_E1; chan->inst.pid.layermask |= ISDN_LAYER(1); pid.layermask |= ISDN_LAYER(1); } } if (hc->type != 1) { /* S/T */ /* set master clock */ if (protocol[port_idx] & 0x10000) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL set master clock: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); if (test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) { printk(KERN_ERR "Error: Master clock for port(%d) of card(%d) is only possible with TE-mode\n", pt+1, HFC_idx+1); ret_err = -EINVAL; goto free_channels; } if (hc->masterclk >= 0) { printk(KERN_ERR "Error: Master clock for port(%d) of card(%d) already defined for port(%d)\n", pt+1, HFC_idx+1, hc->masterclk+1); ret_err = -EINVAL; goto free_channels; } hc->masterclk = pt; } /* set transmitter line to non capacitive */ if (protocol[port_idx] & 0x20000) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL set non capacitive transmitter: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); test_and_set_bit(HFC_CFG_NONCAP_TX, &hc->chan[ch].cfg); } /* disable E-channel */ if (protocol[port_idx] & 0x40000) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL disable E-channel: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); test_and_set_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[ch].cfg); } /* register E-channel */ if (protocol[port_idx] & 0x80000) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL register E-channel: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); test_and_set_bit(HFC_CFG_REG_ECHANNEL, &hc->chan[ch].cfg); } } else { /* E1 */ /* set optical line type */ if (protocol[port_idx] & 0x10000) { if (!id_list[id_idx].opticalsupport) { printk(KERN_INFO "This board has no optical support\n"); } else { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL set optical interfacs: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); test_and_set_bit(HFC_CFG_OPTICAL, &hc->chan[ch].cfg); } } /* set LOS report */ if (protocol[port_idx] & 0x40000) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL set LOS report: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); test_and_set_bit(HFC_CFG_REPORT_LOS, &hc->chan[ch].cfg); } /* set AIS report */ if (protocol[port_idx] & 0x80000) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL set AIS report: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); test_and_set_bit(HFC_CFG_REPORT_AIS, &hc->chan[ch].cfg); } /* set SLIP report */ if (protocol[port_idx] & 0x100000) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL set SLIP report: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); test_and_set_bit(HFC_CFG_REPORT_SLIP, &hc->chan[ch].cfg); } /* set elastic jitter buffer */ if (protocol[port_idx] & 0x600000) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL set elastic buffer to %d: card(%d) port(%d)\n", __FUNCTION__, hc->chan[ch].jitter, HFC_idx+1, pt); hc->chan[ch].jitter = (protocol[port_idx]>>21) & 0x3; } else hc->chan[ch].jitter = 2; /* default */ /* set CRC-4 Mode */ if (! (protocol[port_idx] & 0x800000)) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PROTOCOL turn on CRC4 report: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); test_and_set_bit(HFC_CFG_CRC4, &hc->chan[ch].cfg); printk(KERN_DEBUG "%s: PROTOCOL turn on CRC4 report: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); } else { printk(KERN_DEBUG "%s: PROTOCOL turn off CRC4 report: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt); } } memcpy(&pids[pt], &pid, sizeof(pid)); pt++; port_idx++; } /* run card setup */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: Setting up card(%d)\n", __FUNCTION__, HFC_idx+1); if ((ret_err = setup_pci(hc,pdev,id_idx))) { goto free_channels; } if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: Initializing card(%d)\n", __FUNCTION__, HFC_idx+1); if ((ret_err = init_card(hc))) { if (debug & DEBUG_HFCMULTI_INIT) { printk(KERN_DEBUG "%s: do release_io_hfcmulti\n", __FUNCTION__); release_io_hfcmulti(hc); } goto free_channels; } if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: Init modes card(%d)\n", __FUNCTION__, HFC_idx+1); hfcmulti_initmode(hc); /* check if Port Jumper config matches module param 'protocol' */ if (!hc->type && hc->ports<=4) { // Dip Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + GPI 19/20/23 (R_GPI_IN2)) dips = ((HFC_inb(hc, R_GPIO_IN1) >> 5) & 0x7) | (HFC_inb(hc, R_GPI_IN2) & 0x98); // Port mode (TE/NT) jumpers pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf); if (test_bit(HFC_CHIP_DIGICARD, &hc->chip)) pmj = ~pmj & 0xf; printk(KERN_INFO "%s: DIPs(0x%x) jumpers(0x%x)\n", __FUNCTION__, dips, pmj); pt = 0; while(pt < hc->type) { chan = hc->chan[(pt<<2)+2].ch; // check for protocol param mismatch if (((pmj & (1 << pt)) && (chan->inst.pid.protocol[0] == ISDN_PID_L0_TE_S0)) || ((!(pmj & (1 << pt))) && (chan->inst.pid.protocol[0] == ISDN_PID_L0_NT_S0))) { printk ("%s: protocol WARNING: port %i is jumpered for %s mode!\n", __FUNCTION__, pt, (pmj & (1 << pt)?"NT":"TE") ); } pt++; } } /* add stacks */ pt = 0; while(pt < hc->ports) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: Adding d-stack: card(%d) port(%d)\n", __FUNCTION__, HFC_idx+1, pt+1); if (hc->type == 1) chan = hc->chan[16].ch; else chan = hc->chan[(pt<<2)+2].ch; if ((ret_err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &chan->inst))) { int i=0; printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", ret_err); free_release: /* all ports, hc is free */ for (i=0; iports; i++) release_port(hc, i); goto free_object; } /* indicate that this stack is created */ hc->created[pt] = 1; dst = chan->inst.st; i = 0; while(i < bchperport) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: Adding b-stack: card(%d) port(%d) B-channel(%d)\n", __FUNCTION__, HFC_idx+1, pt+1, i+1); if (hc->type == 1) chan = hc->chan[i + 1 + (i>=15)].ch; else chan = hc->chan[(pt<<2) + i].ch; if ((ret_err = mISDN_ctrl(dst, MGR_NEWSTACK | REQUEST, &chan->inst))) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", ret_err); free_delstack: mISDN_ctrl(dst, MGR_DELSTACK | REQUEST, NULL); goto free_release; } i++; } if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: (before MGR_SETSTACK REQUEST) layermask=0x%x\n", __FUNCTION__, pids[pt].layermask); if ((ret_err = mISDN_ctrl(dst, MGR_SETSTACK | REQUEST, &pids[pt]))) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", ret_err); goto free_delstack; } if (debug & DEBUG_HFCMULTI_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 */ mISDN_ctrl(dst, MGR_CTRLREADY | INDICATION, NULL); pt++; } /* now turning on irq */ spin_lock_irqsave(&hc->lock, flags); enable_hwirq(hc); /* we are on air! */ allocated[HFC_idx] = 1; HFC_cnt++; spin_unlock_irqrestore(&hc->lock, flags); HFC_idx++; HFC_port_idx+=hc->ports; return(0); /* if an error ocurred */ free_channels: i = 0; while(i < 32) { if (hc->chan[i].ch) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: free %c-channel %d (1..32)\n", __FUNCTION__, test_bit(FLG_DCHANNEL, &hc->chan[i].ch->Flags) ? 'D' : 'B', i); mISDN_freechannel(hc->chan[i].ch); kfree(hc->chan[i].ch); hc->chan[i].ch = NULL; } i++; } if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: before REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); spin_lock_irqsave(&HFCM_obj.lock, flags); list_del(&hc->list); spin_unlock_irqrestore(&HFCM_obj.lock, flags); if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: after REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); kfree(hc); free_object: return(ret_err); } static void __devexit hfc_remove_pci(struct pci_dev *pdev) { int i,ch; hfc_multi_t *card = pci_get_drvdata(pdev); if (debug) printk( KERN_INFO "removing hfc_multi card vendor:%x device:%x subvendor:%x subdevice:%x\n", pdev->vendor,pdev->device,pdev->subsystem_vendor,pdev->subsystem_device); if (card) { #if 1 for(i=0;iports;i++) { // type is also number of d-channel if(card->created[i]) { if (card->type == 1) ch = 16; else ch = (i*4)+2; // if created elete stack if (card->chan[ch].ch && test_bit(FLG_DCHANNEL, &card->chan[ch].ch->Flags)) mISDN_ctrl(card->chan[ch].ch->inst.st, MGR_DELSTACK | REQUEST, NULL); } } #endif // relase all ports allocated[card->idx] = 0; } else { if (debug) printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); } } static struct pci_device_id hfmultipci_ids[] __devinitdata = { /** Cards with HFC-4S Chip**/ { CCAG_VID, 0x08B4 , CCAG_VID, 0xB567, 0, 0, 0 }, //BN1S mini PCI { CCAG_VID, 0x08B4 , CCAG_VID, 0xB566, 0, 0, 0 }, //BN2S { CCAG_VID, 0x08B4 , CCAG_VID, 0xB761, 0, 0, 0 }, //BN2S PCIe { CCAG_VID, 0x08B4 , CCAG_VID, 0xB762, 0, 0, 0 }, //BN4S PCIe { CCAG_VID, 0x08B4 , CCAG_VID, 0xB569, 0, 0, 0 }, //BN2S mini PCI { CCAG_VID, 0x08B4 , CCAG_VID, 0xB560, 0, 0, 0 }, //BN4S { CCAG_VID, 0x08B4 , CCAG_VID, 0xB568, 0, 0, 0 }, //BN4S mini PCI { CCAG_VID, 0x08B4 , CCAG_VID, 0x08B4, 0, 0, 0 }, //Old Eval { CCAG_VID, 0x08B4 , CCAG_VID, 0xB520, 0, 0, 0 }, //IOB4ST { CCAG_VID, 0x08B4 , CCAG_VID, 0xB620, 0, 0, 0 }, //4S { 0xD161, 0xB410 , 0xD161, 0xB410, 0, 0, 0 }, //4S - Digium { CCAG_VID, 0x08B4 , CCAG_VID, 0xB540, 0, 0, 0 }, //4S Swyx { CCAG_VID, 0x08B4 , CCAG_VID, 0xB550, 0, 0, 0 }, //4S junghanns 2.0 { CCAG_VID, 0x08B4 , CCAG_VID, 0xE884, 0, 0, 0 }, //2S OpenVox B200P { CCAG_VID, 0x08B4 , CCAG_VID, 0xE888, 0, 0, 0 }, //4S OpenVox B400P /** Cards with HFC-8S Chip**/ { CCAG_VID, 0x16B8 , CCAG_VID, 0xB562, 0, 0, 0 }, //BN8S { CCAG_VID, 0x16B8 , CCAG_VID, 0xB56B, 0, 0, 0 }, //BN8S+ { CCAG_VID, 0x16B8 , CCAG_VID, 0x16B8, 0, 0, 0 }, //old Eval { CCAG_VID, 0x16B8 , CCAG_VID, 0xB521, 0, 0, 0 }, //IOB8ST Recording { CCAG_VID, 0x16B8 , CCAG_VID, 0xB522, 0, 0, 0 }, //IOB8ST { CCAG_VID, 0x16B8 , CCAG_VID, 0xB552, 0, 0, 0 }, //IOB8ST { CCAG_VID, 0x16B8 , CCAG_VID, 0xB55B, 0, 0, 0 }, //IOB8ST - Junghans { CCAG_VID, 0x16B8 , CCAG_VID, 0xB622, 0, 0, 0 }, //8S /** Cards with HFC-E1 Chip**/ { CCAG_VID, 0x30B1 , CCAG_VID, 0xB563, 0, 0, 0 }, //BNE1 { CCAG_VID, 0x30B1 , CCAG_VID, 0xB56A, 0, 0, 0 }, //BNE1 mini PCI { CCAG_VID, 0x30B1 , CCAG_VID, 0xB565, 0, 0, 0 }, //BNE1 + (Dual) { CCAG_VID, 0x30B1 , CCAG_VID, 0xB564, 0, 0, 0 }, //BNE1 (Dual) { CCAG_VID, 0x30B1 , CCAG_VID, 0xB553, 0, 0, 0 }, //Junghanns E1 { CCAG_VID, 0x30B1 , CCAG_VID, 0x30B1, 0, 0, 0 }, //Old Eval { CCAG_VID, 0x30B1 , CCAG_VID, 0xB523, 0, 0, 0 }, //IOB1E1 { CCAG_VID, 0x30B1 , CCAG_VID, 0xC523, 0, 0, 0 }, //E1 #if 0 { CCAG_VID, 0x08B4 , PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { CCAG_VID, 0x16B8 , PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { CCAG_VID, 0x30B1 , PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, #endif { 0x10B5, 0x9030 , CCAG_VID, 0x3136 , 0, 0, 0 }, // PLX PCI Bridge { 0x10B5, 0x9030 , PCI_ANY_ID, PCI_ANY_ID , 0, 0, 0 }, // PLX PCI Bridge {0, } }; MODULE_DEVICE_TABLE(pci, hfmultipci_ids); static struct pci_driver hfcmultipci_driver = { name: "hfc_multi", probe: hfcpci_probe, remove: __devexit_p(hfc_remove_pci), id_table: hfmultipci_ids, }; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) static void HFCmulti_cleanup(void) { #else static void __exit HFCmulti_cleanup(void) { #endif hfc_multi_t *hc,*next; int err; mISDN_module_unregister(THIS_MODULE); /* unload interrupt function symbol*/ if (hfc_interrupt) { symbol_put(ztdummy_extern_interrupt); } if (register_interrupt) { symbol_put(ztdummy_register_interrupt); } /* unregister mISDN object */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: entered (refcnt = %d HFC_cnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt, HFC_cnt); if ((err = mISDN_unregister(&HFCM_obj))) { printk(KERN_ERR "Can't unregister HFC-Multi cards error(%d)\n", err); } /* remove remaining devices, but this should never happen */ if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: now checking ilist (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: done (refcnt = %d HFC_cnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt, HFC_cnt); list_for_each_entry_safe(hc, next, &HFCM_obj.ilist, list) { if (debug) printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", HFCM_obj.refcnt); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) int i; for (i=0;iports;i++) { release_port(hc, i); } #endif release_ports_hw(hc); /* all ports, hc is free */ udelay(1000); } /* get rid of all devices of this driver */ pci_unregister_driver(&hfcmultipci_driver); } static int __init HFCmulti_init(void) { int err, i; char tmpstr[64]; #if !defined(CONFIG_HOTPLUG) || !defined(MODULE) #error "CONFIG_HOTPLUG and MODULE are not defined." #endif if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: init entered\n", __FUNCTION__); #ifdef __BIG_ENDIAN #error "not running on big endian machines now" #endif strcpy(tmpstr, hfcmulti_revision); /* get interrupt function pointer */ hfc_interrupt = symbol_get(ztdummy_extern_interrupt); register_interrupt = symbol_get(ztdummy_register_interrupt); printk(KERN_INFO "mISDN: HFC-multi driver Rev. %s\n", mISDN_getrev(tmpstr)); switch(poll) { case 0: poll_timer = 6; poll = 128; break; /* wenn dieses break nochmal verschwindet, gibt es heisse ohren :-) */ case 8: poll_timer = 2; break; case 16: poll_timer = 3; break; case 32: poll_timer = 4; break; case 64: poll_timer = 5; break; case 128: poll_timer = 6; break; case 256: poll_timer = 7; break; default: printk(KERN_ERR "%s: Wrong poll value (%d).\n", __FUNCTION__, poll); err = -EINVAL; return(err); } memset(&HFCM_obj, 0, sizeof(HFCM_obj)); #ifdef MODULE HFCM_obj.owner = THIS_MODULE; #endif spin_lock_init(&HFCM_obj.lock); INIT_LIST_HEAD(&HFCM_obj.ilist); HFCM_obj.name = HFCName; HFCM_obj.own_ctrl = HFC_manager; HFCM_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0 | ISDN_PID_L0_TE_E1 | ISDN_PID_L0_NT_E1; HFCM_obj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0 | ISDN_PID_L1_TE_E1 | ISDN_PID_L1_NT_E1; HFCM_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; HFCM_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: registering HFCM_obj\n", __FUNCTION__); if ((err = mISDN_register(&HFCM_obj))) { printk(KERN_ERR "Can't register HFC-Multi cards error(%d)\n", err); return(err); } if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: new mISDN object (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt); for(i=0;i0 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) */ 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 txpending; /* if there is currently data in the FIFO 0=no, 1=yes, 2=splloop */ int e1_state; /* keep track of last state */ }; typedef struct hfc_chan hfc_chan_t; struct hfcmulti_hw { 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; }; typedef struct hfcmulti_hw hfcmulti_hw_t; /* for each stack these flags are used (cfg) */ #define HFC_CFG_NTMODE 0 /* NT-mode */ #define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ #define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ #define HFC_CFG_REG_ECHANNEL 3 /* register E-channel */ #define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */ #define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */ #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 */ #define HFC_CHIP_REVISION0 2 /* old fifo handling */ #define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ #define HFC_CHIP_CLOCK_IGNORE 4 /* ignore missing PCM clock */ #define HFC_CHIP_RX_SYNC 5 /* ignore missing PCM clock */ #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 */ #define HFC_CHIP_WATCHDOG 10 /* wether we should send signals to the watchdog */ #define HFC_CHIP_DIGICARD 11 /* wether we have a b410p with echocan in hw */ struct hfc_multi { struct list_head list; 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; int ports; u_int irq; /* irq used by card */ u_int irqcnt; struct pci_dev *pci_dev; #ifdef CONFIG_HFCMULTI_PCIMEM 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 hfcmulti_hw_t hw; /* remember data of write-only-registers */ u_char e1_switch; /* last state of the switches */ u_long chip; /* chip configuration */ int masterclk;/* port that provides master clock -1=off */ int dtmf; /* flag that dtmf is currently in process */ int Flen; /* F-buffer size */ int Zlen; /* Z-buffer size (not maximum) */ int Zmin; /* Z-buffer offset */ int DTMFbase;/* base address of DTMF coefficients */ u_int slots; /* number of PCM slots */ u_int leds; /* type of leds */ u_int ledcount; /* used to animate leds */ int opticalsupport; /* has the e1 board an optical Interface*/ u_long wdcount; /* every 500 ms we need to send the watchdog a signal */ u_char wdbyte; /* watchdog toggle byte*/ u_int activity[8]; /* if there is any action on this port (will be cleared after showing led-states) */ spinlock_t lock; /* the lock */ /* the channel index is counted from 0, regardless where the channel * is located on the hfc-channel. * the bch->channel is equvalent to the hfc-channel */ hfc_chan_t chan[32]; u_char created[8]; /* what port is created */ signed char slot_owner[256]; /* owner channel of slot */ }; typedef struct hfc_multi hfc_multi_t; /*********************************************\ |* REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 *| \*********************************************/ /* write only registers */ #define R_CIRM 0x00 #define R_CTRL 0x01 #define R_BRG_PCM_CFG 0x02 #define R_RAM_ADDR0 0x08 #define R_RAM_ADDR1 0x09 #define R_RAM_ADDR2 0x0A #define R_FIRST_FIFO 0x0B #define R_RAM_SZ 0x0C #define R_FIFO_MD 0x0D #define R_INC_RES_FIFO 0x0E #define R_FSM_IDX 0x0F #define R_FIFO 0x0F #define R_SLOT 0x10 #define R_IRQMSK_MISC 0x11 #define R_SCI_MSK 0x12 #define R_IRQ_CTRL 0x13 #define R_PCM_MD0 0x14 #define R_PCM_MD1 0x15 #define R_PCM_MD2 0x15 #define R_SH0H 0x15 #define R_SH1H 0x15 #define R_SH0L 0x15 #define R_SH1L 0x15 #define R_SL_SEL0 0x15 #define R_SL_SEL1 0x15 #define R_SL_SEL2 0x15 #define R_SL_SEL3 0x15 #define R_SL_SEL4 0x15 #define R_SL_SEL5 0x15 #define R_SL_SEL6 0x15 #define R_SL_SEL7 0x15 #define R_ST_SEL 0x16 #define R_ST_SYNC 0x17 #define R_CONF_EN 0x18 #define R_TI_WD 0x1A #define R_BERT_WD_MD 0x1B #define R_DTMF 0x1C #define R_DTMF_N 0x1D #define R_E1_WR_STA 0x20 #define R_E1_RD_STA 0x20 #define R_LOS0 0x22 #define R_LOS1 0x23 #define R_RX0 0x24 #define R_RX_FR0 0x25 #define R_RX_FR1 0x26 #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 */ #define A_ST_RD_STATE 0x30 #define A_ST_WR_STATE 0x30 #define R_RX_OFF 0x30 #define A_ST_CTRL0 0x31 #define R_SYNC_OUT 0x31 #define A_ST_CTRL1 0x32 #define A_ST_CTRL2 0x33 #define A_ST_SQ_WR 0x34 #define R_TX_OFF 0x34 #define R_SYNC_CTRL 0x35 #define A_ST_CLK_DLY 0x37 #define R_PWM0 0x38 #define R_PWM1 0x39 #define A_ST_B1_TX 0x3C #define A_ST_B2_TX 0x3D #define A_ST_D_TX 0x3E #define R_GPIO_OUT0 0x40 #define R_GPIO_OUT1 0x41 #define R_GPIO_EN0 0x42 #define R_GPIO_EN1 0x43 #define R_GPIO_SEL 0x44 #define R_BRG_CTRL 0x45 #define R_PWM_MD 0x46 #define R_BRG_MD 0x47 #define R_BRG_TIM0 0x48 #define R_BRG_TIM1 0x49 #define R_BRG_TIM2 0x4A #define R_BRG_TIM3 0x4B #define R_BRG_TIM_SEL01 0x4C #define R_BRG_TIM_SEL23 0x4D #define R_BRG_TIM_SEL45 0x4E #define R_BRG_TIM_SEL67 0x4F #define A_SL_CFG 0xD0 #define A_CONF 0xD1 #define A_CH_MSK 0xF4 #define A_CON_HDLC 0xFA #define A_SUBCH_CFG 0xFB #define A_CHANNEL 0xFC #define A_FIFO_SEQ 0xFD #define A_IRQ_MSK 0xFF /* read only registers */ #define A_Z12 0x04 #define A_Z1L 0x04 #define A_Z1 0x04 #define A_Z1H 0x05 #define A_Z2L 0x06 #define A_Z2 0x06 #define A_Z2H 0x07 #define A_F1 0x0C #define A_F12 0x0C #define A_F2 0x0D #define R_IRQ_OVIEW 0x10 #define R_IRQ_MISC 0x11 #define R_IRQ_STATECH 0x12 #define R_CONF_OFLOW 0x14 #define R_RAM_USE 0x15 #define R_CHIP_ID 0x16 #define R_BERT_STA 0x17 #define R_F0_CNTL 0x18 #define R_F0_CNTH 0x19 #define R_BERT_EC 0x1A #define R_BERT_ECL 0x1A #define R_BERT_ECH 0x1B #define R_STATUS 0x1C #define R_CHIP_RV 0x1F #define R_STATE 0x20 #define R_RX_STA0 0x24 #define R_RX_STA1 0x25 #define R_RX_STA2 0x26 #define R_RX_STA3 0x27 #define R_JATT_DIR 0x2b /* undocumented */ #define R_SLIP 0x2c #define A_ST_RD_STA 0x30 #define R_FAS_EC 0x30 #define R_FAS_ECL 0x30 #define R_FAS_ECH 0x31 #define R_VIO_EC 0x32 #define R_VIO_ECL 0x32 #define R_VIO_ECH 0x33 #define A_ST_SQ_RD 0x34 #define R_CRC_EC 0x34 #define R_CRC_ECL 0x34 #define R_CRC_ECH 0x35 #define R_E_EC 0x36 #define R_E_ECL 0x36 #define R_E_ECH 0x37 #define R_SA6_SA13_EC 0x38 #define R_SA6_SA13_ECL 0x38 #define R_SA6_SA13_ECH 0x39 #define R_SA6_SA23_EC 0x3A #define R_SA6_SA23_ECL 0x3A #define R_SA6_SA23_ECH 0x3B #define A_ST_B1_RX 0x3C #define A_ST_B2_RX 0x3D #define A_ST_D_RX 0x3E #define A_ST_E_RX 0x3F #define R_GPIO_IN0 0x40 #define R_GPIO_IN1 0x41 #define R_GPI_IN0 0x44 #define R_GPI_IN1 0x45 #define R_GPI_IN2 0x46 #define R_GPI_IN3 0x47 #define R_INT_DATA 0x88 #define R_IRQ_FIFO_BL0 0xC8 #define R_IRQ_FIFO_BL1 0xC9 #define R_IRQ_FIFO_BL2 0xCA #define R_IRQ_FIFO_BL3 0xCB #define R_IRQ_FIFO_BL4 0xCC #define R_IRQ_FIFO_BL5 0xCD #define R_IRQ_FIFO_BL6 0xCE #define R_IRQ_FIFO_BL7 0xCF /* read and write registers */ #define A_FIFO_DATA0 0x80 #define A_FIFO_DATA1 0x80 #define A_FIFO_DATA2 0x80 #define A_FIFO_DATA0_NOINC 0x84 #define A_FIFO_DATA1_NOINC 0x84 #define A_FIFO_DATA2_NOINC 0x84 #define R_RAM_DATA 0xC0 /****************************************\ |* BIT SETTING FOR HFC-4S/8S AND HFC-E1 *| \****************************************/ /* chapter 2: universal bus interface */ /* R_CIRM */ #define V_IRQ_SEL 0x01 #define V_SRES 0x08 #define V_HFCRES 0x10 #define V_PCMRES 0x20 #define V_STRES 0x40 #define V_ETRES 0x40 #define V_RLD_EPR 0x80 /* R_CTRL */ #define V_FIFO_LPRIO 0x02 #define V_SLOW_RD 0x04 #define V_EXT_RAM 0x08 #define V_CLK_OFF 0x20 #define V_ST_CLK 0x40 /* R_RAM_ADDR0 */ #define V_RAM_ADDR2 0x01 #define V_ADDR_RES 0x40 #define V_ADDR_INC 0x80 /* R_RAM_SZ */ #define V_RAM_SZ 0x01 #define V_PWM0_16KHZ 0x10 #define V_PWM1_16KHZ 0x20 #define V_FZ_MD 0x80 /* R_CHIP_ID */ #define V_PNP_IRQ 0x01 #define V_CHIP_ID 0x10 /* chapter 3: data flow */ /* R_FIRST_FIFO */ #define V_FIRST_FIRO_DIR 0x01 #define V_FIRST_FIFO_NUM 0x02 /* R_FIFO_MD */ #define V_FIFO_MD 0x01 #define V_CSM_MD 0x04 #define V_FSM_MD 0x08 #define V_FIFO_SZ 0x10 /* R_FIFO */ #define V_FIFO_DIR 0x01 #define V_FIFO_NUM 0x02 #define V_REV 0x80 /* R_SLOT */ #define V_SL_DIR 0x01 #define V_SL_NUM 0x02 /* A_SL_CFG */ #define V_CH_DIR 0x01 #define V_CH_SEL 0x02 #define V_ROUTING 0x40 /* A_CON_HDLC */ #define V_IFF 0x01 #define V_HDLC_TRP 0x02 #define V_TRP_IRQ 0x04 #define V_DATA_FLOW 0x20 /* A_SUBCH_CFG */ #define V_BIT_CNT 0x01 #define V_START_BIT 0x08 #define V_LOOP_FIFO 0x40 #define V_INV_DATA 0x80 /* A_CHANNEL */ #define V_CH_DIR0 0x01 #define V_CH_NUM0 0x02 /* A_FIFO_SEQ */ #define V_NEXT_FIFO_DIR 0x01 #define V_NEXT_FIFO_NUM 0x02 #define V_SEQ_END 0x40 /* chapter 4: FIFO handling and HDLC controller */ /* R_INC_RES_FIFO */ #define V_INC_F 0x01 #define V_RES_F 0x02 #define V_RES_LOST 0x04 /* chapter 5: S/T interface */ /* R_SCI_MSK */ #define V_SCI_MSK_ST0 0x01 #define V_SCI_MSK_ST1 0x02 #define V_SCI_MSK_ST2 0x04 #define V_SCI_MSK_ST3 0x08 #define V_SCI_MSK_ST4 0x10 #define V_SCI_MSK_ST5 0x20 #define V_SCI_MSK_ST6 0x40 #define V_SCI_MSK_ST7 0x80 /* R_ST_SEL */ #define V_ST_SEL 0x01 #define V_MULT_ST 0x08 /* R_ST_SYNC */ #define V_SYNC_SEL 0x01 #define V_AUTO_SYNC 0x08 /* A_ST_WR_STA */ #define V_ST_SET_STA 0x01 #define V_ST_LD_STA 0x10 #define V_ST_ACT 0x20 #define V_SET_G2_G3 0x80 /* A_ST_CTRL0 */ #define V_B1_EN 0x01 #define V_B2_EN 0x02 #define V_ST_MD 0x04 #define V_D_PRIO 0x08 #define V_SQ_EN 0x10 #define V_96KHZ 0x20 #define V_TX_LI 0x40 #define V_ST_STOP 0x80 /* A_ST_CTRL1 */ #define V_G2_G3_EN 0x01 #define V_D_HI 0x04 #define V_E_IGNO 0x08 #define V_E_LO 0x10 #define V_B12_SWAP 0x80 /* A_ST_CTRL2 */ #define V_B1_RX_EN 0x01 #define V_B2_RX_EN 0x02 #define V_ST_TRIS 0x40 /* A_ST_CLK_DLY */ #define V_ST_CK_DLY 0x01 #define V_ST_SMPL 0x10 /* A_ST_D_TX */ #define V_ST_D_TX 0x40 /* R_IRQ_STATECH */ #define V_SCI_ST0 0x01 #define V_SCI_ST1 0x02 #define V_SCI_ST2 0x04 #define V_SCI_ST3 0x08 #define V_SCI_ST4 0x10 #define V_SCI_ST5 0x20 #define V_SCI_ST6 0x40 #define V_SCI_ST7 0x80 /* A_ST_RD_STA */ #define V_ST_STA 0x01 #define V_FR_SYNC_ST 0x10 #define V_TI2_EXP 0x20 #define V_INFO0 0x40 #define V_G2_G3 0x80 /* A_ST_SQ_RD */ #define V_ST_SQ 0x01 #define V_MF_RX_RDY 0x10 #define V_MF_TX_RDY 0x80 /* A_ST_D_RX */ #define V_ST_D_RX 0x40 /* A_ST_E_RX */ #define V_ST_E_RX 0x40 /* chapter 5: E1 interface */ /* R_E1_WR_STA */ /* R_E1_RD_STA */ #define V_E1_SET_STA 0x01 #define V_E1_LD_STA 0x10 /* R_RX0 */ #define V_RX_CODE 0x01 #define V_RX_FBAUD 0x04 #define V_RX_CMI 0x08 #define V_RX_INV_CMI 0x10 #define V_RX_INV_CLK 0x20 #define V_RX_INV_DATA 0x40 #define V_AIS_ITU 0x80 /* R_RX_FR0 */ #define V_NO_INSYNC 0x01 #define V_AUTO_RESYNC 0x02 #define V_AUTO_RECO 0x04 #define V_SWORD_COND 0x08 #define V_SYNC_LOSS 0x10 #define V_XCRC_SYNC 0x20 #define V_MF_RESYNC 0x40 #define V_RESYNC 0x80 /* R_RX_FR1 */ #define V_RX_MF 0x01 #define V_RX_MF_SYNC 0x02 #define V_RX_SL0_RAM 0x04 #define V_ERR_SIM 0x20 #define V_RES_NMF 0x40 /* R_TX0 */ #define V_TX_CODE 0x01 #define V_TX_FBAUD 0x04 #define V_TX_CMI_CODE 0x08 #define V_TX_INV_CMI_CODE 0x10 #define V_TX_INV_CLK 0x20 #define V_TX_INV_DATA 0x40 #define V_OUT_EN 0x80 /* R_TX1 */ #define V_INV_CLK 0x01 #define V_EXCHG_DATA_LI 0x02 #define V_AIS_OUT 0x04 #define V_ATX 0x20 #define V_NTRI 0x40 #define V_AUTO_ERR_RES 0x80 /* R_TX_FR0 */ #define V_TRP_FAS 0x01 #define V_TRP_NFAS 0x02 #define V_TRP_RAL 0x04 #define V_TRP_SA 0x08 /* R_TX_FR1 */ #define V_TX_FAS 0x01 #define V_TX_NFAS 0x02 #define V_TX_RAL 0x04 #define V_TX_SA 0x08 /* R_TX_FR2 */ #define V_TX_MF 0x01 #define V_TRP_SL0 0x02 #define V_TX_SL0_RAM 0x04 #define V_TX_E 0x10 #define V_NEG_E 0x20 #define V_XS12_ON 0x40 #define V_XS15_ON 0x80 /* R_RX_OFF */ #define V_RX_SZ 0x01 #define V_RX_INIT 0x04 /* R_SYNC_OUT */ #define V_SYNC_E1_RX 0x01 #define V_IPATS0 0x20 #define V_IPATS1 0x40 #define V_IPATS2 0x80 /* R_TX_OFF */ #define V_TX_SZ 0x01 #define V_TX_INIT 0x04 /* R_SYNC_CTRL */ #define V_EXT_CLK_SYNC 0x01 #define V_SYNC_OFFS 0x02 #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_OFF 0x80 /* R_STATE */ #define V_E1_STA 0x01 #define V_ALT_FR_RX 0x40 #define V_ALT_FR_TX 0x80 /* R_RX_STA0 */ #define V_RX_STA 0x01 #define V_FR_SYNC_E1 0x04 #define V_SIG_LOS 0x08 #define V_MFA_STA 0x10 #define V_AIS 0x40 #define V_NO_MF_SYNC 0x80 /* R_RX_STA1 */ #define V_SI_FAS 0x01 #define V_SI_NFAS 0x02 #define V_A 0x04 #define V_CRC_OK 0x08 #define V_TX_E1 0x10 #define V_TX_E2 0x20 #define V_RX_E1 0x40 #define V_RX_E2 0x80 /* R_SLIP */ #define V_SLIP_RX 0x01 #define V_FOSLIP_RX 0x08 #define V_SLIP_TX 0x10 #define V_FOSLIP_TX 0x80 /* chapter 6: PCM interface */ /* R_PCM_MD0 */ #define V_PCM_MD 0x01 #define V_C4_POL 0x02 #define V_F0_NEG 0x04 #define V_F0_LEN 0x08 #define V_PCM_ADDR 0x10 /* R_SL_SEL0 */ #define V_SL_SEL0 0x01 #define V_SH_SEL0 0x80 /* R_SL_SEL1 */ #define V_SL_SEL1 0x01 #define V_SH_SEL1 0x80 /* R_SL_SEL2 */ #define V_SL_SEL2 0x01 #define V_SH_SEL2 0x80 /* R_SL_SEL3 */ #define V_SL_SEL3 0x01 #define V_SH_SEL3 0x80 /* R_SL_SEL4 */ #define V_SL_SEL4 0x01 #define V_SH_SEL4 0x80 /* R_SL_SEL5 */ #define V_SL_SEL5 0x01 #define V_SH_SEL5 0x80 /* R_SL_SEL6 */ #define V_SL_SEL6 0x01 #define V_SH_SEL6 0x80 /* R_SL_SEL7 */ #define V_SL_SEL7 0x01 #define V_SH_SEL7 0x80 /* R_PCM_MD1 */ #define V_ODEC_CON 0x01 #define V_PLL_ADJ 0x04 #define V_PCM_DR 0x10 #define V_PCM_LOOP 0x40 /* R_PCM_MD2 */ #define V_SYNC_PLL 0x02 #define V_SYCN_SRC 0x04 #define V_SYNC_OUT 0x08 #define V_ICR_FR_TIME 0x40 #define V_EN_PLL 0x80 /* chapter 7: pulse width modulation */ /* R_PWM_MD */ #define V_EXT_IRQ_EN 0x08 #define V_PWM0_MD 0x10 #define V_PWM1_MD 0x40 /* chapter 8: multiparty audio conferences */ /* R_CONF_EN */ #define V_CONF_EN 0x01 #define V_ULAW 0x80 /* A_CONF */ #define V_CONF_NUM 0x01 #define V_NOISE_SUPPR 0x08 #define V_ATT_LEV 0x20 #define V_CONF_SL 0x80 /* R_CONF_OFLOW */ #define V_CONF_OFLOW0 0x01 #define V_CONF_OFLOW1 0x02 #define V_CONF_OFLOW2 0x04 #define V_CONF_OFLOW3 0x08 #define V_CONF_OFLOW4 0x10 #define V_CONF_OFLOW5 0x20 #define V_CONF_OFLOW6 0x40 #define V_CONF_OFLOW7 0x80 /* chapter 9: DTMF contoller */ /* R_DTMF0 */ #define V_DTMF_EN 0x01 #define V_HARM_SEL 0x02 #define V_DTMF_RX_CH 0x04 #define V_DTMF_STOP 0x08 #define V_CHBL_SEL 0x10 #define V_RST_DTMF 0x40 #define V_ULAW_SEL 0x80 /* chapter 10: BERT */ /* R_BERT_WD_MD */ #define V_PAT_SEQ 0x01 #define V_BERT_ERR 0x08 #define V_AUTO_WD_RES 0x20 #define V_WD_RES 0x80 /* R_BERT_STA */ #define V_BERT_SYNC_SRC 0x01 #define V_BERT_SYNC 0x10 #define V_BERT_INV_DATA 0x20 /* chapter 11: auxiliary interface */ /* R_BRG_PCM_CFG */ #define V_BRG_EN 0x01 #define V_BRG_MD 0x02 #define V_PCM_CLK 0x20 #define V_ADDR_WRDLY 0x40 /* R_BRG_CTRL */ #define V_BRG_CS 0x01 #define V_BRG_ADDR 0x08 #define V_BRG_CS_SRC 0x80 /* R_BRG_MD */ #define V_BRG_MD0 0x01 #define V_BRG_MD1 0x02 #define V_BRG_MD2 0x04 #define V_BRG_MD3 0x08 #define V_BRG_MD4 0x10 #define V_BRG_MD5 0x20 #define V_BRG_MD6 0x40 #define V_BRG_MD7 0x80 /* R_BRG_TIM0 */ #define V_BRG_TIM0_IDLE 0x01 #define V_BRG_TIM0_CLK 0x10 /* R_BRG_TIM1 */ #define V_BRG_TIM1_IDLE 0x01 #define V_BRG_TIM1_CLK 0x10 /* R_BRG_TIM2 */ #define V_BRG_TIM2_IDLE 0x01 #define V_BRG_TIM2_CLK 0x10 /* R_BRG_TIM3 */ #define V_BRG_TIM3_IDLE 0x01 #define V_BRG_TIM3_CLK 0x10 /* R_BRG_TIM_SEL01 */ #define V_BRG_WR_SEL0 0x01 #define V_BRG_RD_SEL0 0x04 #define V_BRG_WR_SEL1 0x10 #define V_BRG_RD_SEL1 0x40 /* R_BRG_TIM_SEL23 */ #define V_BRG_WR_SEL2 0x01 #define V_BRG_RD_SEL2 0x04 #define V_BRG_WR_SEL3 0x10 #define V_BRG_RD_SEL3 0x40 /* R_BRG_TIM_SEL45 */ #define V_BRG_WR_SEL4 0x01 #define V_BRG_RD_SEL4 0x04 #define V_BRG_WR_SEL5 0x10 #define V_BRG_RD_SEL5 0x40 /* R_BRG_TIM_SEL67 */ #define V_BRG_WR_SEL6 0x01 #define V_BRG_RD_SEL6 0x04 #define V_BRG_WR_SEL7 0x10 #define V_BRG_RD_SEL7 0x40 /* chapter 12: clock, reset, interrupt, timer and watchdog */ /* R_IRQMSK_MISC */ #define V_STA_IRQMSK 0x01 #define V_TI_IRQMSK 0x02 #define V_PROC_IRQMSK 0x04 #define V_DTMF_IRQMSK 0x08 #define V_IRQ1S_MSK 0x10 #define V_SA6_IRQMSK 0x20 #define V_RX_EOMF_MSK 0x40 #define V_TX_EOMF_MSK 0x80 /* R_IRQ_CTRL */ #define V_FIFO_IRQ 0x01 #define V_GLOB_IRQ_EN 0x08 #define V_IRQ_POL 0x10 /* R_TI_WD */ #define V_EV_TS 0x01 #define V_WD_TS 0x10 /* A_IRQ_MSK */ #define V_IRQ 0x01 #define V_BERT_EN 0x02 #define V_MIX_IRQ 0x04 /* R_IRQ_OVIEW */ #define V_IRQ_FIFO_BL0 0x01 #define V_IRQ_FIFO_BL1 0x02 #define V_IRQ_FIFO_BL2 0x04 #define V_IRQ_FIFO_BL3 0x08 #define V_IRQ_FIFO_BL4 0x10 #define V_IRQ_FIFO_BL5 0x20 #define V_IRQ_FIFO_BL6 0x40 #define V_IRQ_FIFO_BL7 0x80 /* R_IRQ_MISC */ #define V_STA_IRQ 0x01 #define V_TI_IRQ 0x02 #define V_IRQ_PROC 0x04 #define V_DTMF_IRQ 0x08 #define V_IRQ1S 0x10 #define V_SA6_IRQ 0x20 #define V_RX_EOMF 0x40 #define V_TX_EOMF 0x80 /* R_STATUS */ #define V_BUSY 0x01 #define V_PROC 0x02 #define V_DTMF_STA 0x04 #define V_LOST_STA 0x08 #define V_SYNC_IN 0x10 #define V_EXT_IRQSTA 0x20 #define V_MISC_IRQSTA 0x40 #define V_FR_IRQSTA 0x80 /* R_IRQ_FIFO_BL0 */ #define V_IRQ_FIFO0_TX 0x01 #define V_IRQ_FIFO0_RX 0x02 #define V_IRQ_FIFO1_TX 0x04 #define V_IRQ_FIFO1_RX 0x08 #define V_IRQ_FIFO2_TX 0x10 #define V_IRQ_FIFO2_RX 0x20 #define V_IRQ_FIFO3_TX 0x40 #define V_IRQ_FIFO3_RX 0x80 /* R_IRQ_FIFO_BL1 */ #define V_IRQ_FIFO4_TX 0x01 #define V_IRQ_FIFO4_RX 0x02 #define V_IRQ_FIFO5_TX 0x04 #define V_IRQ_FIFO5_RX 0x08 #define V_IRQ_FIFO6_TX 0x10 #define V_IRQ_FIFO6_RX 0x20 #define V_IRQ_FIFO7_TX 0x40 #define V_IRQ_FIFO7_RX 0x80 /* R_IRQ_FIFO_BL2 */ #define V_IRQ_FIFO8_TX 0x01 #define V_IRQ_FIFO8_RX 0x02 #define V_IRQ_FIFO9_TX 0x04 #define V_IRQ_FIFO9_RX 0x08 #define V_IRQ_FIFO10_TX 0x10 #define V_IRQ_FIFO10_RX 0x20 #define V_IRQ_FIFO11_TX 0x40 #define V_IRQ_FIFO11_RX 0x80 /* R_IRQ_FIFO_BL3 */ #define V_IRQ_FIFO12_TX 0x01 #define V_IRQ_FIFO12_RX 0x02 #define V_IRQ_FIFO13_TX 0x04 #define V_IRQ_FIFO13_RX 0x08 #define V_IRQ_FIFO14_TX 0x10 #define V_IRQ_FIFO14_RX 0x20 #define V_IRQ_FIFO15_TX 0x40 #define V_IRQ_FIFO15_RX 0x80 /* R_IRQ_FIFO_BL4 */ #define V_IRQ_FIFO16_TX 0x01 #define V_IRQ_FIFO16_RX 0x02 #define V_IRQ_FIFO17_TX 0x04 #define V_IRQ_FIFO17_RX 0x08 #define V_IRQ_FIFO18_TX 0x10 #define V_IRQ_FIFO18_RX 0x20 #define V_IRQ_FIFO19_TX 0x40 #define V_IRQ_FIFO19_RX 0x80 /* R_IRQ_FIFO_BL5 */ #define V_IRQ_FIFO20_TX 0x01 #define V_IRQ_FIFO20_RX 0x02 #define V_IRQ_FIFO21_TX 0x04 #define V_IRQ_FIFO21_RX 0x08 #define V_IRQ_FIFO22_TX 0x10 #define V_IRQ_FIFO22_RX 0x20 #define V_IRQ_FIFO23_TX 0x40 #define V_IRQ_FIFO23_RX 0x80 /* R_IRQ_FIFO_BL6 */ #define V_IRQ_FIFO24_TX 0x01 #define V_IRQ_FIFO24_RX 0x02 #define V_IRQ_FIFO25_TX 0x04 #define V_IRQ_FIFO25_RX 0x08 #define V_IRQ_FIFO26_TX 0x10 #define V_IRQ_FIFO26_RX 0x20 #define V_IRQ_FIFO27_TX 0x40 #define V_IRQ_FIFO27_RX 0x80 /* R_IRQ_FIFO_BL7 */ #define V_IRQ_FIFO28_TX 0x01 #define V_IRQ_FIFO28_RX 0x02 #define V_IRQ_FIFO29_TX 0x04 #define V_IRQ_FIFO29_RX 0x08 #define V_IRQ_FIFO30_TX 0x10 #define V_IRQ_FIFO30_RX 0x20 #define V_IRQ_FIFO31_TX 0x40 #define V_IRQ_FIFO31_RX 0x80 /* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ /* R_GPIO_OUT0 */ #define V_GPIO_OUT0 0x01 #define V_GPIO_OUT1 0x02 #define V_GPIO_OUT2 0x04 #define V_GPIO_OUT3 0x08 #define V_GPIO_OUT4 0x10 #define V_GPIO_OUT5 0x20 #define V_GPIO_OUT6 0x40 #define V_GPIO_OUT7 0x80 /* R_GPIO_OUT1 */ #define V_GPIO_OUT8 0x01 #define V_GPIO_OUT9 0x02 #define V_GPIO_OUT10 0x04 #define V_GPIO_OUT11 0x08 #define V_GPIO_OUT12 0x10 #define V_GPIO_OUT13 0x20 #define V_GPIO_OUT14 0x40 #define V_GPIO_OUT15 0x80 /* R_GPIO_EN0 */ #define V_GPIO_EN0 0x01 #define V_GPIO_EN1 0x02 #define V_GPIO_EN2 0x04 #define V_GPIO_EN3 0x08 #define V_GPIO_EN4 0x10 #define V_GPIO_EN5 0x20 #define V_GPIO_EN6 0x40 #define V_GPIO_EN7 0x80 /* R_GPIO_EN1 */ #define V_GPIO_EN8 0x01 #define V_GPIO_EN9 0x02 #define V_GPIO_EN10 0x04 #define V_GPIO_EN11 0x08 #define V_GPIO_EN12 0x10 #define V_GPIO_EN13 0x20 #define V_GPIO_EN14 0x40 #define V_GPIO_EN15 0x80 /* R_GPIO_SEL */ #define V_GPIO_SEL0 0x01 #define V_GPIO_SEL1 0x02 #define V_GPIO_SEL2 0x04 #define V_GPIO_SEL3 0x08 #define V_GPIO_SEL4 0x10 #define V_GPIO_SEL5 0x20 #define V_GPIO_SEL6 0x40 #define V_GPIO_SEL7 0x80 /* R_GPIO_IN0 */ #define V_GPIO_IN0 0x01 #define V_GPIO_IN1 0x02 #define V_GPIO_IN2 0x04 #define V_GPIO_IN3 0x08 #define V_GPIO_IN4 0x10 #define V_GPIO_IN5 0x20 #define V_GPIO_IN6 0x40 #define V_GPIO_IN7 0x80 /* R_GPIO_IN1 */ #define V_GPIO_IN8 0x01 #define V_GPIO_IN9 0x02 #define V_GPIO_IN10 0x04 #define V_GPIO_IN11 0x08 #define V_GPIO_IN12 0x10 #define V_GPIO_IN13 0x20 #define V_GPIO_IN14 0x40 #define V_GPIO_IN15 0x80 /* R_GPI_IN0 */ #define V_GPI_IN0 0x01 #define V_GPI_IN1 0x02 #define V_GPI_IN2 0x04 #define V_GPI_IN3 0x08 #define V_GPI_IN4 0x10 #define V_GPI_IN5 0x20 #define V_GPI_IN6 0x40 #define V_GPI_IN7 0x80 /* R_GPI_IN1 */ #define V_GPI_IN8 0x01 #define V_GPI_IN9 0x02 #define V_GPI_IN10 0x04 #define V_GPI_IN11 0x08 #define V_GPI_IN12 0x10 #define V_GPI_IN13 0x20 #define V_GPI_IN14 0x40 #define V_GPI_IN15 0x80 /* R_GPI_IN2 */ #define V_GPI_IN16 0x01 #define V_GPI_IN17 0x02 #define V_GPI_IN18 0x04 #define V_GPI_IN19 0x08 #define V_GPI_IN20 0x10 #define V_GPI_IN21 0x20 #define V_GPI_IN22 0x40 #define V_GPI_IN23 0x80 /* R_GPI_IN3 */ #define V_GPI_IN24 0x01 #define V_GPI_IN25 0x02 #define V_GPI_IN26 0x04 #define V_GPI_IN27 0x08 #define V_GPI_IN28 0x10 #define V_GPI_IN29 0x20 #define V_GPI_IN30 0x40 #define V_GPI_IN31 0x80 /* map of all registers, used for debugging */ #ifdef HFC_REGISTER_MAP struct hfc_register_names { char *name; u_char reg; } hfc_register_names[] = { /* write registers */ {"R_CIRM", 0x00}, {"R_CTRL", 0x01}, {"R_BRG_PCM_CFG ", 0x02}, {"R_RAM_ADDR0", 0x08}, {"R_RAM_ADDR1", 0x09}, {"R_RAM_ADDR2", 0x0A}, {"R_FIRST_FIFO", 0x0B}, {"R_RAM_SZ", 0x0C}, {"R_FIFO_MD", 0x0D}, {"R_INC_RES_FIFO", 0x0E}, {"R_FIFO / R_FSM_IDX", 0x0F}, {"R_SLOT", 0x10}, {"R_IRQMSK_MISC", 0x11}, {"R_SCI_MSK", 0x12}, {"R_IRQ_CTRL", 0x13}, {"R_PCM_MD0", 0x14}, {"R_0x15", 0x15}, {"R_ST_SEL", 0x16}, {"R_ST_SYNC", 0x17}, {"R_CONF_EN", 0x18}, {"R_TI_WD", 0x1A}, {"R_BERT_WD_MD", 0x1B}, {"R_DTMF", 0x1C}, {"R_DTMF_N", 0x1D}, {"R_E1_XX_STA", 0x20}, {"R_LOS0", 0x22}, {"R_LOS1", 0x23}, {"R_RX0", 0x24}, {"R_RX_FR0", 0x25}, {"R_RX_FR1", 0x26}, {"R_TX0", 0x28}, {"R_TX1", 0x29}, {"R_TX_FR0", 0x2C}, {"R_TX_FR1", 0x2D}, {"R_TX_FR2", 0x2E}, {"R_JATT_ATT", 0x2F}, {"A_ST_xx_STA/R_RX_OFF",0x30}, {"A_ST_CTRL0", 0x31}, {"R_SYNC_OUT", 0x31}, {"A_ST_CTRL1", 0x32}, {"A_ST_CTRL2", 0x33}, {"A_ST_SQ_WR", 0x34}, {"R_TX_OFF", 0x34}, {"R_SYNC_CTRL", 0x35}, {"A_ST_CLK_DLY", 0x37}, {"R_PWM0", 0x38}, {"R_PWM1", 0x39}, {"A_ST_B1_TX", 0x3C}, {"A_ST_B2_TX", 0x3D}, {"A_ST_D_TX", 0x3E}, {"R_GPIO_OUT0", 0x40}, {"R_GPIO_OUT1", 0x41}, {"R_GPIO_EN0", 0x42}, {"R_GPIO_EN1", 0x43}, {"R_GPIO_SEL", 0x44}, {"R_BRG_CTRL", 0x45}, {"R_PWM_MD", 0x46}, {"R_BRG_MD", 0x47}, {"R_BRG_TIM0", 0x48}, {"R_BRG_TIM1", 0x49}, {"R_BRG_TIM2", 0x4A}, {"R_BRG_TIM3", 0x4B}, {"R_BRG_TIM_SEL01", 0x4C}, {"R_BRG_TIM_SEL23", 0x4D}, {"R_BRG_TIM_SEL45", 0x4E}, {"R_BRG_TIM_SEL67", 0x4F}, {"A_FIFO_DATA0-2", 0x80}, {"A_FIFO_DATA0-2_NOINC",0x84}, {"R_RAM_DATA", 0xC0}, {"A_SL_CFG", 0xD0}, {"A_CONF", 0xD1}, {"A_CH_MSK", 0xF4}, {"A_CON_HDLC", 0xFA}, {"A_SUBCH_CFG", 0xFB}, {"A_CHANNEL", 0xFC}, {"A_FIFO_SEQ", 0xFD}, {"A_IRQ_MSK", 0xFF}, {NULL, 0}, /* read registers */ {"A_Z1", 0x04}, {"A_Z1H", 0x05}, {"A_Z2", 0x06}, {"A_Z2H", 0x07}, {"A_F1", 0x0C}, {"A_F2", 0x0D}, {"R_IRQ_OVIEW", 0x10}, {"R_IRQ_MISC", 0x11}, {"R_IRQ_STATECH", 0x12}, {"R_CONF_OFLOW", 0x14}, {"R_RAM_USE", 0x15}, {"R_CHIP_ID", 0x16}, {"R_BERT_STA", 0x17}, {"R_F0_CNTL", 0x18}, {"R_F0_CNTH", 0x19}, {"R_BERT_ECL", 0x1A}, {"R_BERT_ECH", 0x1B}, {"R_STATUS", 0x1C}, {"R_CHIP_RV", 0x1F}, {"R_STATE", 0x20}, {"R_RX_STA0", 0x24}, {"R_RX_STA1", 0x25}, {"R_RX_STA2", 0x26}, {"R_RX_STA3", 0x27}, {"R_JATT_DIR", 0x2b}, {"R_SLIP", 0x2c}, {"A_ST_RD_STA", 0x30}, {"R_FAS_ECL", 0x30}, {"R_FAS_ECH", 0x31}, {"R_VIO_ECL", 0x32}, {"R_VIO_ECH", 0x33}, {"R_CRC_ECL / A_ST_SQ_RD",0x34}, {"R_CRC_ECH", 0x35}, {"R_E_ECL", 0x36}, {"R_E_ECH", 0x37}, {"R_SA6_SA13_ECL", 0x38}, {"R_SA6_SA13_ECH", 0x39}, {"R_SA6_SA23_ECL", 0x3A}, {"R_SA6_SA23_ECH", 0x3B}, {"A_ST_B1_RX", 0x3C}, {"A_ST_B2_RX", 0x3D}, {"A_ST_D_RX", 0x3E}, {"A_ST_E_RX", 0x3F}, {"R_GPIO_IN0", 0x40}, {"R_GPIO_IN1", 0x41}, {"R_GPI_IN0", 0x44}, {"R_GPI_IN1", 0x45}, {"R_GPI_IN2", 0x46}, {"R_GPI_IN3", 0x47}, {"A_FIFO_DATA0-2", 0x80}, {"A_FIFO_DATA0-2_NOINC",0x84}, {"R_INT_DATA", 0x88}, {"R_RAM_DATA", 0xC0}, {"R_IRQ_FIFO_BL0", 0xC8}, {"R_IRQ_FIFO_BL1", 0xC9}, {"R_IRQ_FIFO_BL2", 0xCA}, {"R_IRQ_FIFO_BL3", 0xCB}, {"R_IRQ_FIFO_BL4", 0xCC}, {"R_IRQ_FIFO_BL5", 0xCD}, {"R_IRQ_FIFO_BL6", 0xCE}, {"R_IRQ_FIFO_BL7", 0xCF}, }; #endif /* HFC_REGISTER_MAP */ /* 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)*ADDR_MULT))) = c) #define HFC_inl(a,b) (*((volatile u_long *)((a->pci_membase)+((b)*ADDR_MULT)))) #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) /* macros */ #ifndef HFC_REGISTER_MAP /* usage: HFC_outX(card,register,value); */ #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)*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*ADDR_MULT)))) & V_BUSY) #else /* HFC_REGISTER_MAP */ #define HFC_outb(a,b,c) _HFC_outb(a, b, c, __FUNCTION__, __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; i = -1; while(hfc_register_names[++i].name) { if (hfc_register_names[i].reg == b) strcat(regname, hfc_register_names[i].name); } if (regname[0] == '\0') strcpy(regname, "register"); bits[7] = '0'+(!!(c&1)); bits[6] = '0'+(!!(c&2)); bits[5] = '0'+(!!(c&4)); bits[4] = '0'+(!!(c&8)); bits[3] = '0'+(!!(c&16)); bits[2] = '0'+(!!(c&32)); bits[1] = '0'+(!!(c&64)); bits[0] = '0'+(!!(c&128)); printk(KERN_DEBUG "HFC_outb(\"%s\", %02x=%s, 0x%02x=%s); in %s() line %d\n", a->name, b, regname, c, bits, function, line); return(*(((volatile u_char *)a->pci_membase)+b) = c); } #define HFC_inb(a,b) _HFC_inb(a, b, __FUNCTION__, __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)*ADDR_MULT)); int i; i = 0; while(hfc_register_names[i++].name) ; while(hfc_register_names[++i].name) { if (hfc_register_names[i].reg == b) strcat(regname, hfc_register_names[i].name); } if (regname[0] == '\0') strcpy(regname, "register"); bits[7] = '0'+(!!(c&1)); bits[6] = '0'+(!!(c&2)); bits[5] = '0'+(!!(c&4)); bits[4] = '0'+(!!(c&8)); bits[3] = '0'+(!!(c&16)); bits[2] = '0'+(!!(c&32)); bits[1] = '0'+(!!(c&64)); bits[0] = '0'+(!!(c&128)); printk(KERN_DEBUG "HFC_inb(\"%s\", %02x=%s) = 0x%02x=%s; in %s() line %d\n", a->name, b, regname, c, bits, function, line); return(c); } #define HFC_inw(a,b) _HFC_inw(a, b, __FUNCTION__, __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)*ADDR_MULT))); int i; i = 0; while(hfc_register_names[i++].name) ; while(hfc_register_names[++i].name) { if (hfc_register_names[i].reg == b) strcat(regname, hfc_register_names[i].name); } if (regname[0] == '\0') strcpy(regname, "register"); printk(KERN_DEBUG "HFC_inw(\"%s\", %02x=%s) = 0x%04x; in %s() line %d\n", a->name, b, regname, c, function, line); return(c); } #define HFC_wait(a) _HFC_wait(a, __FUNCTION__, __LINE__) static void _HFC_wait(hfc_multi_t *a, char *function, int line) { printk(KERN_DEBUG "HFC_wait(\"%s\"); in %s() line %d\n", a->name, function, line); while((*(((volatile u_char *)a->pci_membase)+R_STATUS)) & V_BUSY); } #endif /* else HFC_REGISTER_MAP */ #else /* CONFIG_HFCMULTI_PCIMEM */ /* ACCESS TO PCI IO REGISTERS */ #ifdef HFC_REGISTER_MAP #error Please use "HFC_REGISTER_MAP" debugging only in conjuction with PCIMEM access. #endif /* usage: HFC_outX(card,register,value); */ static inline void HFC_outb(hfc_multi_t *a, unsigned short b, u_char c) { outw(b,(a->pci_iobase)+4); outb(c,a->pci_iobase); } static inline void HFC_outl(hfc_multi_t *a, unsigned short b, u_long c) { outw(b,(a->pci_iobase)+4); outl(c,a->pci_iobase); } static inline void HFC_outw(hfc_multi_t *a, unsigned short b, u_long c) { outw(b,(a->pci_iobase)+4); outw(c,a->pci_iobase); } /* usage: value=HFC_inX(card,register); */ static inline u_char HFC_inb(hfc_multi_t *a, unsigned short b) { outw(b,(a->pci_iobase)+4); return (inb((volatile u_int)a->pci_iobase)); } static inline u_short HFC_inw(hfc_multi_t *a, unsigned short b) { outw(b,(a->pci_iobase)+4); return (inw((volatile u_int)a->pci_iobase)); } static inline u_long HFC_inl(hfc_multi_t *a, unsigned short b) { outw(b,(a->pci_iobase)+4); return (inl((volatile u_int)a->pci_iobase)); } /* usage: HFC_wait(card); */ static inline void HFC_wait(hfc_multi_t *a) { outb(R_STATUS,(a->pci_iobase)+4); while(inb((volatile u_int)a->pci_iobase) & V_BUSY); } /* usage: HFC_set(card,register); */ #define HFC_set(a, b) outb(b,(a->pci_iobase)+4) /* 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) #define HFC_inl_(a,b) HFC_inl(a,b) #define HFC_inw_(a,b) HFC_inw(a,b) #define HFC_outb_(a,b,c) HFC_outb(a,b,c) #define HFC_inb_(a,b) HFC_inb(a,b) #define HFC_wait_(a) HFC_wait(a) #endif /* else CONFIG_HFCMULTI_PCIMEM */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/hfc_pci.c0000644000000000000500000021136711135651702020140 0ustar rootsrc/* $Id: hfc_pci.c,v 1.49 2006/12/21 15:25:06 nadi Exp $ * hfc_pci.c low level driver for CCD's hfc-pci based cards * * Author Werner Cornelius (werner@isdn4linux.de) * based on existing driver for CCD hfc ISA cards * type approval valid for HFC-S PCI A based card * * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) * Copyright 2001 by Karsten Keil (keil@isdn4linux.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include "core.h" #include "channel.h" #include "hfc_pci.h" #include "layer1.h" #include "debug.h" #include #define HFC_INFO(txt) printk(KERN_DEBUG txt) extern const char *CardType[]; static const char *hfcpci_revision = "$Revision: 1.49 $"; /* table entry in the PCI devices list */ typedef struct { int vendor_id; int device_id; char *vendor_name; char *card_name; } PCI_ENTRY; #define NT_T1_COUNT 20 /* number of 3.125ms interrupts for G2 timeout */ #define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ #define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ #ifndef PCI_VENDOR_ID_CCD #define PCI_VENDOR_ID_CCD 0x1397 #define PCI_DEVICE_ID_CCD_2BD0 0x2BD0 #define PCI_DEVICE_ID_CCD_B000 0xB000 #define PCI_DEVICE_ID_CCD_B006 0xB006 #define PCI_DEVICE_ID_CCD_B007 0xB007 #define PCI_DEVICE_ID_CCD_B008 0xB008 #define PCI_DEVICE_ID_CCD_B009 0xB009 #define PCI_DEVICE_ID_CCD_B00A 0xB00A #define PCI_DEVICE_ID_CCD_B00B 0xB00B #define PCI_DEVICE_ID_CCD_B00C 0xB00C #define PCI_DEVICE_ID_CCD_B100 0xB100 #define PCI_VENDOR_ID_ASUSTEK 0x1043 #define PCI_DEVICE_ID_ASUSTEK_0675 0x0675 #define PCI_VENDOR_ID_BERKOM 0x0871 #define PCI_DEVICE_ID_BERKOM_A1T 0xFFA1 #define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xFFA2 #define PCI_VENDOR_ID_ANIGMA 0x1051 #define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100 #define PCI_VENDOR_ID_ZOLTRIX 0x15b0 #define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2BD0 #define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070 #define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071 #define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072 #define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073 #define PCI_VENDOR_ID_ABOCOM 0x13D1 #define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1 #endif #ifndef PCI_VENDOR_ID_SITECOM #define PCI_VENDOR_ID_SITECOM 0x182D #define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069 #endif /* new device IDs, obsolete when include/linux/pci_ids.h will be updated */ #ifndef PCI_DEVICE_ID_CCD_B700 #define PCI_DEVICE_ID_CCD_B700 0xb700 #endif #ifndef PCI_DEVICE_ID_CCD_B701 #define PCI_DEVICE_ID_CCD_B701 0xb701 #endif static const PCI_ENTRY id_list[] = { {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, "CCD/Billion/Asuscom", "2BD0"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, "Billion", "B000"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, "Billion", "B006"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, "Billion", "B007"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, "Billion", "B008"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, "Billion", "B009"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, "Billion", "B00A"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, "Billion", "B00B"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, "Billion", "B00C"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, "Seyeon", "B100"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B700, "Primux II S0", "B700"}, {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701, "Primux II S0 NT", "B701"}, {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, "Abocom/Magitek", "2BD1"}, {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, "Asuscom/Askey", "675"}, {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, "German telekom", "T-Concept"}, {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, "German telekom", "A1T"}, {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, "Motorola MC145575", "MC145575"}, {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, "Zoltrix", "2BD0"}, {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"}, {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,"Digi International", "Digi DataFire Micro V (Europe)"}, {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,"Digi International", "Digi DataFire Micro V IOM2 (North America)"}, {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,"Digi International", "Digi DataFire Micro V (North America)"}, {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2,"Sitecom Connectivity", "DC-105 ISDN TA"}, {0, 0, NULL, NULL}, }; struct hfcPCI_hw { unsigned char cirm; unsigned char ctmt; unsigned char clkdel; unsigned char states; unsigned char conn; unsigned char mst_m; unsigned char int_m1; unsigned char int_m2; unsigned char sctrl; unsigned char sctrl_r; unsigned char sctrl_e; unsigned char trm; unsigned char fifo_en; unsigned char bswapped; unsigned char nt_mode; int nt_timer; struct pci_dev *dev; unsigned char *pci_io; /* start of PCI IO memory */ dma_addr_t dmahandle; void *fifos; /* FIFO memory */ int last_bfifo_cnt[2]; /* marker saving last b-fifo frame count */ struct timer_list timer; }; typedef struct hfcPCI_hw hfcPCI_hw_t; #define SPIN_DEBUG #define HFC_CFG_MASTER 1 #define HFC_CFG_SLAVE 2 #define HFC_CFG_PCM 3 #define HFC_CFG_2HFC 4 #define HFC_CFG_SLAVEHFC 5 #define HFC_CFG_NEG_F0 6 #define HFC_CFG_SW_DD_DU 7 typedef struct _hfc_pci { struct list_head list; u_char subtyp; u_char chanlimit; u_long cfg; u_int irq; u_int irqcnt; hfcPCI_hw_t hw; spinlock_t lock; channel_t dch; channel_t bch[2]; } hfc_pci_t; /* Interface functions */ static void enable_hwirq(hfc_pci_t *hc) { hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE; Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); } static void disable_hwirq(hfc_pci_t *hc) { hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE); Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); } /******************************************/ /* free hardware resources used by driver */ /******************************************/ void release_io_hfcpci(hfc_pci_t *hc) { hc->hw.int_m2 = 0; /* interrupt output off ! */ disable_hwirq(hc); Write_hfc(hc, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ mdelay(10); /* Timeout 10ms */ hc->hw.cirm = 0; /* Reset Off */ Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); pci_write_config_word(hc->hw.dev, PCI_COMMAND, 0); /* disable memory mapped ports + busmaster */ del_timer(&hc->hw.timer); pci_free_consistent(hc->hw.dev, 0x8000, hc->hw.fifos, hc->hw.dmahandle); iounmap((void *)hc->hw.pci_io); } /********************************************************************************/ /* function called to reset the HFC PCI chip. A complete software reset of chip */ /* and fifos is done. */ /********************************************************************************/ static void reset_hfcpci(hfc_pci_t *hc) { u_char val; int cnt = 0; HFC_INFO("reset_hfcpci: entered\n"); val = Read_hfc(hc, HFCPCI_CHIP_ID); printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val); pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ disable_hwirq(hc); pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */ val = Read_hfc(hc, HFCPCI_STATUS); printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val); hc->hw.cirm = HFCPCI_RESET; /* Reset On */ Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); set_current_state(TASK_UNINTERRUPTIBLE); mdelay(10); /* Timeout 10ms */ hc->hw.cirm = 0; /* Reset Off */ Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); val = Read_hfc(hc, HFCPCI_STATUS); printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val); while (cnt < 50000) { /* max 50000 us */ udelay(5); cnt += 5; val = Read_hfc(hc, HFCPCI_STATUS); if (!(val & 2)) break; } printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt); hc->hw.fifo_en = 0x30; /* only D fifos enabled */ hc->hw.bswapped = 0; /* no exchange */ hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ hc->hw.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ hc->hw.sctrl_r = 0; hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE; /* S/T Auto awake */ hc->hw.mst_m = 0; if (test_bit(HFC_CFG_MASTER, &hc->cfg)) hc->hw.mst_m |= HFCPCI_MASTER; /* HFC Master Mode */ if (test_bit(HFC_CFG_NEG_F0, &hc->cfg)) hc->hw.mst_m |= HFCPCI_F0_NEGATIV; if (hc->hw.nt_mode) { hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */ hc->hw.sctrl |= SCTRL_MODE_NT; /* NT-MODE */ hc->hw.states = 1; /* G1 */ } else { hc->hw.clkdel = CLKDEL_TE; /* ST-Bit delay for TE-Mode */ hc->hw.states = 2; /* F2 */ } Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel); Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER; Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); /* Clear already pending ints */ if (Read_hfc(hc, HFCPCI_INT_S1)); Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states); udelay(10); Write_hfc(hc, HFCPCI_STATES, hc->hw.states); Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); /* Init GCI/IOM2 in master mode */ /* Slots 0 and 1 are set for B-chan 1 and 2 */ /* D- and monitor/CI channel are not enabled */ /* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */ /* STIO2 is used as data input, B1+B2 from IOM->ST */ /* ST B-channel send disabled -> continous 1s */ /* The IOM slots are always enabled */ if (test_bit(HFC_CFG_PCM, &hc->cfg)) { /* set data flow directions: connect B1,B2: HFC to/from PCM */ hc->hw.conn = 0x09; } else { hc->hw.conn = 0x36; /* set data flow directions */ if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { Write_hfc(hc, HFCPCI_B1_SSL, 0xC0); Write_hfc(hc, HFCPCI_B2_SSL, 0xC1); Write_hfc(hc, HFCPCI_B1_RSL, 0xC0); Write_hfc(hc, HFCPCI_B2_RSL, 0xC1); } else { Write_hfc(hc, HFCPCI_B1_SSL, 0x80); Write_hfc(hc, HFCPCI_B2_SSL, 0x81); Write_hfc(hc, HFCPCI_B1_RSL, 0x80); Write_hfc(hc, HFCPCI_B2_RSL, 0x81); } } Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); val = Read_hfc(hc, HFCPCI_INT_S2); } /***************************************************/ /* Timer function called when kernel timer expires */ /***************************************************/ static void hfcpci_Timer(hfc_pci_t *hc) { hc->hw.timer.expires = jiffies + 75; /* WD RESET */ /* WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80); add_timer(&hc->hw.timer); */ } /************************************************/ /* select a b-channel entry matching and active */ /************************************************/ static channel_t * Sel_BCS(hfc_pci_t *hc, int channel) { if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) && (hc->bch[0].channel & channel)) return (&hc->bch[0]); else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) && (hc->bch[1].channel & channel)) return (&hc->bch[1]); else return (NULL); } /***************************************/ /* clear the desired B-channel rx fifo */ /***************************************/ static void hfcpci_clear_fifo_rx(hfc_pci_t *hc, int fifo) { u_char fifo_state; bzfifo_type *bzr; if (fifo) { bzr = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2; fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX; } else { bzr = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b1; fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX; } if (fifo_state) hc->hw.fifo_en ^= fifo_state; Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); hc->hw.last_bfifo_cnt[fifo] = 0; bzr->f1 = MAX_B_FRAMES; bzr->f2 = bzr->f1; /* init F pointers to remain constant */ bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16(le16_to_cpu(bzr->za[MAX_B_FRAMES].z1)); if (fifo_state) hc->hw.fifo_en |= fifo_state; Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); } /***************************************/ /* clear the desired B-channel tx fifo */ /***************************************/ static void hfcpci_clear_fifo_tx(hfc_pci_t *hc, int fifo) { u_char fifo_state; bzfifo_type *bzt; if (fifo) { bzt = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b2; fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX; } else { bzt = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b1; fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX; } if (fifo_state) hc->hw.fifo_en ^= fifo_state; Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); if (hc->bch[fifo].debug & L1_DEB_HSCX) mISDN_debugprint(&hc->bch[fifo].inst, "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x) state(%x)", fifo, bzt->f1, bzt->f2, le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), le16_to_cpu(bzt->za[MAX_B_FRAMES].z2), fifo_state); bzt->f2 = MAX_B_FRAMES; bzt->f1 = bzt->f2; /* init F pointers to remain constant */ bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(le16_to_cpu(bzt->za[MAX_B_FRAMES].z1 - 1)); if (fifo_state) hc->hw.fifo_en |= fifo_state; Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); if (hc->bch[fifo].debug & L1_DEB_HSCX) mISDN_debugprint(&hc->bch[fifo].inst, "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)", fifo, bzt->f1, bzt->f2, le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), le16_to_cpu(bzt->za[MAX_B_FRAMES].z2)); } /*********************************************/ /* read a complete B-frame out of the buffer */ /*********************************************/ static void hfcpci_empty_fifo(channel_t *bch, bzfifo_type * bz, u_char * bdata, int count) { u_char *ptr, *ptr1, new_f2; int total, maxlen, new_z2; z_type *zp; if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) mISDN_debugprint(&bch->inst, "hfcpci_empty_fifo"); zp = &bz->za[bz->f2]; /* point to Z-Regs */ new_z2 = le16_to_cpu(zp->z2) + count; /* new position in fifo */ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z2 -= B_FIFO_SIZE; /* buffer wrap */ new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; if ((count > MAX_DATA_SIZE + 3) || (count < 4) || (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count); #ifdef ERROR_STATISTIC bch->err_inv++; #endif bz->za[new_f2].z2 = cpu_to_le16(new_z2); bz->f2 = new_f2; /* next buffer */ } else if (!(bch->rx_skb = alloc_stack_skb(count - 3, bch->up_headerlen))) printk(KERN_WARNING "HFCPCI: receive out of memory\n"); else { total = count; count -= 3; ptr = skb_put(bch->rx_skb, count); if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL) maxlen = count; /* complete transfer */ else maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(zp->z2); /* maximum */ ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); /* start of data */ memcpy(ptr, ptr1, maxlen); /* copy data */ count -= maxlen; if (count) { /* rest remaining */ ptr += maxlen; ptr1 = bdata; /* start of buffer */ memcpy(ptr, ptr1, count); /* rest */ } bz->za[new_f2].z2 = cpu_to_le16(new_z2); bz->f2 = new_f2; /* next buffer */ queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, bch->rx_skb); bch->rx_skb = NULL; } } /*******************************/ /* D-channel receive procedure */ /*******************************/ static int receive_dmsg(hfc_pci_t *hc) { channel_t *dch = &hc->dch; int maxlen; int rcnt, total; int count = 5; u_char *ptr, *ptr1; dfifo_type *df; z_type *zp; df = &((fifo_area *) (hc->hw.fifos))->d_chan.d_rx; while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { zp = &df->za[df->f2 & D_FREG_MASK]; rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); if (rcnt < 0) rcnt += D_FIFO_SIZE; rcnt++; if (dch->debug & L1_DEB_ISAC) mISDN_debugprint(&dch->inst, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", df->f1, df->f2, le16_to_cpu(zp->z1), le16_to_cpu(zp->z2), rcnt); if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || (df->data[le16_to_cpu(zp->z1)])) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[le16_to_cpu(zp->z1)]); #ifdef ERROR_STATISTIC cs->err_rx++; #endif df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((zp->z2 + rcnt) & (D_FIFO_SIZE - 1)); } else if ((dch->rx_skb = alloc_stack_skb(rcnt - 3, dch->up_headerlen))) { total = rcnt; rcnt -= 3; ptr = skb_put(dch->rx_skb, rcnt); if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE) maxlen = rcnt; /* complete transfer */ else maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2); /* maximum */ ptr1 = df->data + le16_to_cpu(zp->z2); /* start of data */ memcpy(ptr, ptr1, maxlen); /* copy data */ rcnt -= maxlen; if (rcnt) { /* rest remaining */ ptr += maxlen; ptr1 = df->data; /* start of buffer */ memcpy(ptr, ptr1, rcnt); /* rest */ } df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1)); if (dch->debug & L1_DEB_ISAC_FIFO) { char *t = dch->log; count = dch->rx_skb->len; ptr = dch->rx_skb->data; t += sprintf(t, "hfcD_empty_fifo cnt %d", count); mISDN_QuickHex(t, ptr, count); mISDN_debugprint(&dch->inst, dch->log); } mISDN_queueup_newhead(&dch->inst, 0, PH_DATA_IND, MISDN_ID_ANY, dch->rx_skb); dch->rx_skb = NULL; } else printk(KERN_WARNING "HFC-PCI: D receive out of memory\n"); } return (1); } /*******************************************************************************/ /* check for transparent receive data and read max one threshold size if avail */ /*******************************************************************************/ int hfcpci_empty_fifo_trans(channel_t *bch, bzfifo_type * bz, u_char * bdata) { unsigned short *z1r, *z2r; int new_z2, fcnt, maxlen; u_char *ptr, *ptr1; z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ z2r = z1r + 1; if (!(fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r))) return (0); /* no data avail */ if (fcnt <= 0) fcnt += B_FIFO_SIZE; /* bytes actually buffered */ if (fcnt > HFCPCI_BTRANS_THRESHOLD) fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ new_z2 = le16_to_cpu(*z2r) + fcnt; /* new position in fifo */ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z2 -= B_FIFO_SIZE; /* buffer wrap */ if (!(bch->rx_skb = alloc_stack_skb(fcnt, bch->up_headerlen))) printk(KERN_WARNING "HFCPCI: receive out of memory\n"); else { ptr = skb_put(bch->rx_skb, fcnt); if (le16_to_cpu(*z2r) + fcnt <= B_FIFO_SIZE + B_SUB_VAL) maxlen = fcnt; /* complete transfer */ else maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r); /* maximum */ ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL); /* start of data */ memcpy(ptr, ptr1, maxlen); /* copy data */ fcnt -= maxlen; if (fcnt) { /* rest remaining */ ptr += maxlen; ptr1 = bdata; /* start of buffer */ memcpy(ptr, ptr1, fcnt); /* rest */ } queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, bch->rx_skb); bch->rx_skb = NULL; } *z2r = cpu_to_le16(new_z2); /* new position */ return (1); } /* hfcpci_empty_fifo_trans */ /**********************************/ /* B-channel main receive routine */ /**********************************/ void main_rec_hfcpci(channel_t *bch) { hfc_pci_t *hc = bch->hw; int rcnt, real_fifo; int receive, count = 5; bzfifo_type *bz; u_char *bdata; z_type *zp; if ((bch->channel & 2) && (!hc->hw.bswapped)) { bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2; bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b2; real_fifo = 1; } else { bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b1; bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b1; real_fifo = 0; } Begin: count--; if (bz->f1 != bz->f2) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "hfcpci rec ch(%x) f1(%d) f2(%d)", bch->channel, bz->f1, bz->f2); zp = &bz->za[bz->f2]; rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); if (rcnt < 0) rcnt += B_FIFO_SIZE; rcnt++; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)", bch->channel, le16_to_cpu(zp->z1), le16_to_cpu(zp->z2), rcnt); hfcpci_empty_fifo(bch, bz, bdata, rcnt); rcnt = bz->f1 - bz->f2; if (rcnt < 0) rcnt += MAX_B_FRAMES + 1; if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) { rcnt = 0; hfcpci_clear_fifo_rx(hc, real_fifo); } hc->hw.last_bfifo_cnt[real_fifo] = rcnt; if (rcnt > 1) receive = 1; else receive = 0; } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) receive = hfcpci_empty_fifo_trans(bch, bz, bdata); else receive = 0; if (count && receive) goto Begin; return; } /**************************/ /* D-channel send routine */ /**************************/ static void hfcpci_fill_dfifo(hfc_pci_t *hc) { channel_t *dch = &hc->dch; int fcnt; int count, new_z1, maxlen; dfifo_type *df; u_char *src, *dst, new_f1; if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) mISDN_debugprint(&dch->inst, "hfcpci_fill_dfifo"); if (!dch->tx_skb) return; count = dch->tx_skb->len - dch->tx_idx; if (count <= 0) return; df = &((fifo_area *) (hc->hw.fifos))->d_chan.d_tx; if (dch->debug & L1_DEB_ISAC_FIFO) mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)", df->f1, df->f2, le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1)); fcnt = df->f1 - df->f2; /* frame count actually buffered */ if (fcnt < 0) fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ if (fcnt > (MAX_D_FRAMES - 1)) { if (dch->debug & L1_DEB_ISAC) mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo more as 14 frames"); #ifdef ERROR_STATISTIC cs->err_tx++; #endif return; } /* now determine free bytes in FIFO buffer */ maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1; if (maxlen <= 0) maxlen += D_FIFO_SIZE; /* count now contains available bytes */ if (dch->debug & L1_DEB_ISAC) mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo count(%ld/%d)", count, maxlen); if (count > maxlen) { if (dch->debug & L1_DEB_ISAC) mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo no fifo mem"); return; } new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) & (D_FIFO_SIZE - 1); new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); src = dch->tx_skb->data + dch->tx_idx; /* source pointer */ dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); /* end fifo */ if (maxlen > count) maxlen = count; /* limit size */ memcpy(dst, src, maxlen); /* first copy */ count -= maxlen; /* remaining bytes */ if (count) { dst = df->data; /* start of buffer */ src += maxlen; /* new position */ memcpy(dst, src, count); } df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); /* for next buffer */ df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); /* new pos actual buffer */ df->f1 = new_f1; /* next frame */ if (dch->debug & L1_DEB_ISAC_FIFO) { char *t = dch->log; count = dch->tx_skb->len - dch->tx_idx; src = dch->tx_skb->data + dch->tx_idx; t += sprintf(t, "hfcD_fill_fifo cnt %d", count); mISDN_QuickHex(t, src, count); mISDN_debugprint(&dch->inst, dch->log); } dch->tx_idx = dch->tx_skb->len; return; } /**************************/ /* B-channel send routine */ /**************************/ static void hfcpci_fill_fifo(channel_t *bch) { hfc_pci_t *hc = bch->hw; int maxlen, fcnt; int count, new_z1; bzfifo_type *bz; u_char *bdata; u_char new_f1, *src, *dst; unsigned short *z1t, *z2t; if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) mISDN_debugprint(&bch->inst, "%s", __FUNCTION__); if ((!bch->tx_skb) || bch->tx_skb->len <= 0) return; count = bch->tx_skb->len - bch->tx_idx; if ((bch->channel & 2) && (!hc->hw.bswapped)) { bz = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b2; bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.txdat_b2; } else { bz = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b1; bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.txdat_b1; } if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { z1t = &bz->za[MAX_B_FRAMES].z1; z2t = z1t + 1; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo_trans ch(%x) cnt(%d) z1(%x) z2(%x)", bch->channel, count, le16_to_cpu(*z1t), le16_to_cpu(*z2t)); fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); if (fcnt <= 0) fcnt += B_FIFO_SIZE; /* fcnt contains available bytes in fifo */ fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send */ next_t_frame: if (fcnt < (2 * HFCPCI_BTRANS_THRESHOLD)) { count = bch->tx_skb->len - bch->tx_idx; if (count >= B_FIFO_SIZE - fcnt) count = B_FIFO_SIZE - fcnt -1; if (count <= 0) return; /* data is suitable for fifo */ new_z1 = le16_to_cpu(*z1t) + count; /* new buffer Position */ if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z1 -= B_FIFO_SIZE; /* buffer wrap */ src = bch->tx_skb->data + bch->tx_idx; /* source pointer */ dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); /* end of fifo */ if (bch->debug & L1_DEB_HSCX_FIFO) mISDN_debugprint(&bch->inst, "hfcpci_FFt fcnt(%d) maxl(%d) nz1(%x) dst(%p)", fcnt, maxlen, new_z1, dst); fcnt += count; bch->tx_idx += count; if (maxlen > count) maxlen = count; /* limit size */ memcpy(dst, src, maxlen); /* first copy */ count -= maxlen; /* remaining bytes */ if (count) { dst = bdata; /* start of buffer */ src += maxlen; /* new position */ memcpy(dst, src, count); } *z1t = cpu_to_le16(new_z1); /* now send data */ if (bch->tx_idx < bch->tx_skb->len) return; dev_kfree_skb(bch->tx_skb); bch->tx_idx = 0; 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); goto next_t_frame; } else { test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n"); } } bch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } return; } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)", __FUNCTION__, bch->channel, bz->f1, bz->f2, bz->za[bz->f1].z1); fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ if (fcnt < 0) fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ if (fcnt > (MAX_B_FRAMES - 1)) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "hfcpci_fill_Bfifo more as 14 frames"); return; } /* now determine free bytes in FIFO buffer */ maxlen = le16_to_cpu(bz->za[bz->f2].z2) - le16_to_cpu(bz->za[bz->f1].z1) - 1; if (maxlen <= 0) maxlen += B_FIFO_SIZE; /* count now contains available bytes */ if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo ch(%x) count(%ld/%d),%lx", bch->channel, count, maxlen, current->state); if (maxlen < count) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo no fifo mem"); return; } new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count; /* new buffer Position */ if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z1 -= B_FIFO_SIZE; /* buffer wrap */ new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); src = bch->tx_skb->data + bch->tx_idx; /* source pointer */ dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL); maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1); /* end fifo */ if (maxlen > count) maxlen = count; /* limit size */ memcpy(dst, src, maxlen); /* first copy */ count -= maxlen; /* remaining bytes */ if (count) { dst = bdata; /* start of buffer */ src += maxlen; /* new position */ memcpy(dst, src, count); } bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */ bz->f1 = new_f1; /* next frame */ dev_kfree_skb(bch->tx_skb); bch->tx_idx = 0; 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); } else { test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n"); } } else { bch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } } /***************************/ /* handle L1 state changes */ /***************************/ static void ph_state_change(channel_t *dch) { hfc_pci_t *hc = dch->inst.privat; u_int prim = PH_SIGNAL | INDICATION; u_int para = 0; if (!hc->hw.nt_mode) { if (dch->debug) printk(KERN_DEBUG "%s: TE newstate %x\n", __FUNCTION__, dch->state); switch (dch->state) { case (0): prim = PH_CONTROL | INDICATION; para = HW_RESET; break; case (3): prim = PH_CONTROL | INDICATION; para = HW_DEACTIVATE; break; case (5): case (8): para = ANYSIGNAL; break; case (6): para = INFO2; break; case (7): para = INFO4_P8; break; default: return; } } else { if (dch->debug) printk(KERN_DEBUG "%s: NT newstate %x\n", __FUNCTION__, dch->state); switch (dch->state) { case (2): if (hc->hw.nt_timer < 0) { hc->hw.nt_timer = 0; hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); /* Clear already pending ints */ if (Read_hfc(hc, HFCPCI_INT_S1)); Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE); udelay(10); Write_hfc(hc, HFCPCI_STATES, 4); dch->state = 4; } else { hc->hw.int_m1 |= HFCPCI_INTS_TIMER; Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; hc->hw.ctmt |= HFCPCI_TIM3_125; Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); hc->hw.nt_timer = NT_T1_COUNT; Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); /* allow G2 -> G3 transition */ } return; case (1): prim = PH_DEACTIVATE | INDICATION; para = 0; hc->hw.nt_timer = 0; hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); test_and_clear_bit(FLG_ACTIVE, &dch->Flags); break; case (4): hc->hw.nt_timer = 0; hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); return; case (3): prim = PH_ACTIVATE | INDICATION; para = 0; hc->hw.nt_timer = 0; hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); test_and_set_bit(FLG_ACTIVE, &dch->Flags); break; default: return; } mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, MGR_SHORTSTATUS | INDICATION, test_bit(FLG_ACTIVE, &dch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED, 0, NULL, 0); } mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0); } /*********************/ /* Interrupt handler */ /*********************/ static inline void tx_irq(channel_t *chan) { if (chan->tx_skb && chan->tx_idx < chan->tx_skb->len) { if (test_bit(FLG_DCHANNEL, &chan->Flags)) hfcpci_fill_dfifo(chan->hw); if (test_bit(FLG_BCHANNEL, &chan->Flags)) hfcpci_fill_fifo(chan); } else { if (chan->tx_skb) dev_kfree_skb(chan->tx_skb); chan->tx_idx = 0; if (test_bit(FLG_TX_NEXT, &chan->Flags)) { chan->tx_skb = chan->next_skb; if (chan->tx_skb) { mISDN_head_t *hh = mISDN_HEAD_P(chan->tx_skb); chan->next_skb = NULL; test_and_clear_bit(FLG_TX_NEXT, &chan->Flags); queue_ch_frame(chan, CONFIRM, hh->dinfo, NULL); if (test_bit(FLG_DCHANNEL, &chan->Flags)) hfcpci_fill_dfifo(chan->hw); if (test_bit(FLG_BCHANNEL, &chan->Flags)) hfcpci_fill_fifo(chan); } else { printk(KERN_WARNING "hfc tx irq TX_NEXT without skb\n"); test_and_clear_bit(FLG_TX_NEXT, &chan->Flags); test_and_clear_bit(FLG_TX_BUSY, &chan->Flags); } } else { test_and_clear_bit(FLG_TX_BUSY, &chan->Flags); chan->tx_skb = NULL; } } } static irqreturn_t hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs) { hfc_pci_t *hc = dev_id; u_char exval; channel_t *bch; u_char val, stat; spin_lock(&hc->lock); if (!(hc->hw.int_m2 & 0x08)) { spin_unlock(&hc->lock); return IRQ_NONE; /* not initialised */ } if (HFCPCI_ANYINT & (stat = Read_hfc(hc, HFCPCI_STATUS))) { val = Read_hfc(hc, HFCPCI_INT_S1); if (hc->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&hc->dch.inst, "HFC-PCI: stat(%02x) s1(%02x)", stat, val); } else { /* shared */ spin_unlock(&hc->lock); return IRQ_NONE; } hc->irqcnt++; if (hc->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&hc->dch.inst, "HFC-PCI irq %x", val); val &= hc->hw.int_m1; if (val & 0x40) { /* state machine irq */ exval = Read_hfc(hc, HFCPCI_STATES) & 0xf; if (hc->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&hc->dch.inst, "ph_state chg %d->%d", hc->dch.state, exval); hc->dch.state = exval; ph_state_change(&hc->dch); val &= ~0x40; } if (val & 0x80) { /* timer irq */ if (hc->hw.nt_mode) { if ((--hc->hw.nt_timer) < 0) ph_state_change(&hc->dch); } val &= ~0x80; Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); } if (val & 0x08) { if (!(bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1))) { if (hc->dch.debug) mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x08 IRQ"); } else main_rec_hfcpci(bch); } if (val & 0x10) { // if (hc->logecho) // receive_emsg(hc); // else if (!(bch = Sel_BCS(hc, 2))) { if (hc->dch.debug) mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x10 IRQ"); } else main_rec_hfcpci(bch); } if (val & 0x01) { if (!(bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1))) { if (hc->dch.debug) mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x01 IRQ"); } else tx_irq(bch); } if (val & 0x02) { if (!(bch = Sel_BCS(hc, 2))) { if (hc->dch.debug) mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x02 IRQ"); } else tx_irq(bch); } if (val & 0x20) { /* receive dframe */ receive_dmsg(hc); } if (val & 0x04) { /* dframe transmitted */ if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags)) del_timer(&hc->dch.timer); tx_irq(&hc->dch); } spin_unlock(&hc->lock); return IRQ_HANDLED; } /********************************************************************/ /* timer callback for D-chan busy resolution. Currently no function */ /********************************************************************/ static void hfcpci_dbusy_timer(hfc_pci_t *hc) { } /***************************************************************/ /* activate/deactivate hardware for selected channels and mode */ /***************************************************************/ static int mode_hfcpci(channel_t *bch, int bc, int protocol) { hfc_pci_t *hc = bch->hw; int fifo2; u_char rx_slot = 0, tx_slot = 0, pcm_mode; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "HFCPCI bchannel protocol %x-->%x ch %x-->%x", bch->state, protocol, bch->channel, bc); fifo2 = bc; pcm_mode = (bc>>24) & 0xff; if (pcm_mode) { /* PCM SLOT USE */ if (!test_bit(HFC_CFG_PCM, &hc->cfg)) printk(KERN_WARNING "%s: pcm channel id without HFC_CFG_PCM\n", __FUNCTION__); rx_slot = (bc>>8) & 0xff; tx_slot = (bc>>16) & 0xff; bc = bc & 0xff; } else if (test_bit(HFC_CFG_PCM, &hc->cfg) && (protocol > ISDN_PID_NONE)) printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n", __FUNCTION__); if (hc->chanlimit > 1) { hc->hw.bswapped = 0; /* B1 and B2 normal mode */ hc->hw.sctrl_e &= ~0x80; } else { if (bc & 2) { if (protocol != ISDN_PID_NONE) { hc->hw.bswapped = 1; /* B1 and B2 exchanged */ hc->hw.sctrl_e |= 0x80; } else { hc->hw.bswapped = 0; /* B1 and B2 normal mode */ hc->hw.sctrl_e &= ~0x80; } fifo2 = 1; } else { hc->hw.bswapped = 0; /* B1 and B2 normal mode */ hc->hw.sctrl_e &= ~0x80; } } switch (protocol) { case (-1): /* used for init */ bch->state = -1; bch->channel = bc; case (ISDN_PID_NONE): if (bch->state == ISDN_PID_NONE) { return(0); } if (bc & 2) { hc->hw.sctrl &= ~SCTRL_B2_ENA; hc->hw.sctrl_r &= ~SCTRL_B2_ENA; } else { hc->hw.sctrl &= ~SCTRL_B1_ENA; hc->hw.sctrl_r &= ~SCTRL_B1_ENA; } if (fifo2 & 2) { hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2; hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); } else { hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1; hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); } #ifdef REVERSE_BITORDER if (bch->channel & 2) hc->hw.cirm &= 0x7f; else hc->hw.cirm &= 0xbf; #endif bch->state = ISDN_PID_NONE; bch->channel = bc; test_and_clear_bit(FLG_HDLC, &bch->Flags); test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); break; case (ISDN_PID_L1_B_64TRANS): bch->state = protocol; bch->channel = bc; hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0); hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0); if (bc & 2) { hc->hw.sctrl |= SCTRL_B2_ENA; hc->hw.sctrl_r |= SCTRL_B2_ENA; #ifdef REVERSE_BITORDER hc->hw.cirm |= 0x80; #endif } else { hc->hw.sctrl |= SCTRL_B1_ENA; hc->hw.sctrl_r |= SCTRL_B1_ENA; #ifdef REVERSE_BITORDER hc->hw.cirm |= 0x40; #endif } if (fifo2 & 2) { hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); hc->hw.ctmt |= 2; hc->hw.conn &= ~0x18; } else { hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); hc->hw.ctmt |= 1; hc->hw.conn &= ~0x03; } test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); break; case (ISDN_PID_L1_B_64HDLC): bch->state = protocol; bch->channel = bc; hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0); hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0); if (bc & 2) { hc->hw.sctrl |= SCTRL_B2_ENA; hc->hw.sctrl_r |= SCTRL_B2_ENA; } else { hc->hw.sctrl |= SCTRL_B1_ENA; hc->hw.sctrl_r |= SCTRL_B1_ENA; } if (fifo2 & 2) { hc->hw.last_bfifo_cnt[1] = 0; hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC); hc->hw.ctmt &= ~2; hc->hw.conn &= ~0x18; } else { hc->hw.last_bfifo_cnt[0] = 0; hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC); hc->hw.ctmt &= ~1; hc->hw.conn &= ~0x03; } test_and_set_bit(FLG_HDLC, &bch->Flags); break; default: mISDN_debugprint(&bch->inst, "prot not known %x", protocol); return(-ENOPROTOOPT); } if (test_bit(HFC_CFG_PCM, &hc->cfg)) { if ((protocol == ISDN_PID_NONE) || (protocol == -1)) { /* init case */ rx_slot = 0; tx_slot = 0; } else { if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { rx_slot |= 0xC0; tx_slot |= 0xC0; } else { rx_slot |= 0x80; tx_slot |= 0x80; } } if (bc & 2) { hc->hw.conn &= 0xc7; hc->hw.conn |= 0x08; printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n", __FUNCTION__, tx_slot); printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n", __FUNCTION__, rx_slot); Write_hfc(hc, HFCPCI_B2_SSL, tx_slot); Write_hfc(hc, HFCPCI_B2_RSL, rx_slot); } else { hc->hw.conn &= 0xf8; hc->hw.conn |= 0x01; printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n", __FUNCTION__, tx_slot); printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n", __FUNCTION__, rx_slot); Write_hfc(hc, HFCPCI_B1_SSL, tx_slot); Write_hfc(hc, HFCPCI_B1_RSL, rx_slot); } } Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); #ifdef REVERSE_BITORDER Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); #endif return(0); } static int set_hfcpci_rxtest(channel_t *bch, int protocol, struct sk_buff *skb) { hfc_pci_t *hc = bch->hw; int *chan = (int *)skb->data; if (skb->len <4) { mISDN_debugprint(&bch->inst, "HFCPCI rxtest no channel parameter"); return(-EINVAL); } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x", bch->state, protocol, bch->channel, *chan); if (bch->channel != *chan) { mISDN_debugprint(&bch->inst, "HFCPCI rxtest wrong channel parameter %x/%x", bch->channel, *chan); return(-EINVAL); } switch (protocol) { case (ISDN_PID_L1_B_64TRANS): bch->state = protocol; hfcpci_clear_fifo_rx(hc, (*chan & 2)?1:0); if (*chan & 2) { hc->hw.sctrl_r |= SCTRL_B2_ENA; hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; hc->hw.int_m1 |= HFCPCI_INTS_B2REC; hc->hw.ctmt |= 2; hc->hw.conn &= ~0x18; #ifdef REVERSE_BITORDER hc->hw.cirm |= 0x80; #endif } else { hc->hw.sctrl_r |= SCTRL_B1_ENA; hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; hc->hw.int_m1 |= HFCPCI_INTS_B1REC; hc->hw.ctmt |= 1; hc->hw.conn &= ~0x03; #ifdef REVERSE_BITORDER hc->hw.cirm |= 0x40; #endif } break; case (ISDN_PID_L1_B_64HDLC): bch->state = protocol; hfcpci_clear_fifo_rx(hc, (*chan & 2)?1:0); if (*chan & 2) { hc->hw.sctrl_r |= SCTRL_B2_ENA; hc->hw.last_bfifo_cnt[1] = 0; hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; hc->hw.int_m1 |= HFCPCI_INTS_B2REC; hc->hw.ctmt &= ~2; hc->hw.conn &= ~0x18; } else { hc->hw.sctrl_r |= SCTRL_B1_ENA; hc->hw.last_bfifo_cnt[0] = 0; hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; hc->hw.int_m1 |= HFCPCI_INTS_B1REC; hc->hw.ctmt &= ~1; hc->hw.conn &= ~0x03; } break; default: mISDN_debugprint(&bch->inst, "prot not known %x", protocol); return(-ENOPROTOOPT); } Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); #ifdef REVERSE_BITORDER Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); #endif return(0); } /*************************************/ /* Layer 1 D-channel hardware access */ /*************************************/ static int hfc_dmsg(channel_t *dch, struct sk_buff *skb) { hfc_pci_t *hc = dch->hw; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if (hh->prim == (PH_SIGNAL | REQUEST)) { spin_lock_irqsave(dch->inst.hwlock, flags); if ((hh->dinfo == INFO3_P8) || (hh->dinfo == INFO3_P10)) { if (test_bit(HFC_CFG_MASTER, &hc->cfg)) hc->hw.mst_m |= HFCPCI_MASTER; Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); } else ret = -EINVAL; spin_unlock_irqrestore(dch->inst.hwlock, flags); } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(dch->inst.hwlock, flags); if (hh->dinfo == HW_RESET) { Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); /* HFC ST 3 */ udelay(6); Write_hfc(hc, HFCPCI_STATES, 3); /* HFC ST 2 */ if (test_bit(HFC_CFG_MASTER, &hc->cfg)) hc->hw.mst_m |= HFCPCI_MASTER; Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); spin_unlock_irqrestore(dch->inst.hwlock, flags); skb_trim(skb, 0); return(mISDN_queueup_newhead(&dch->inst, 0, PH_CONTROL | INDICATION, HW_POWERUP, skb)); // l1_msg(hc, HW_POWERUP | CONFIRM, NULL); } else if (hh->dinfo == HW_DEACTIVATE) { hc->hw.mst_m &= ~HFCPCI_MASTER; Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); if (dch->next_skb) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) del_timer(&dch->timer); } else if (hh->dinfo == HW_POWERUP) { Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION); } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) { u_char slot; if (1 & hh->dinfo) { if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) slot = 0xC0; else slot = 0x80; printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", __FUNCTION__, slot); Write_hfc(hc, HFCPCI_B1_SSL, slot); Write_hfc(hc, HFCPCI_B1_RSL, slot); hc->hw.conn = (hc->hw.conn & ~7) | 1; Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); } if (2 & hh->dinfo) { if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) slot = 0xC1; else slot = 0x81; printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", __FUNCTION__, slot); Write_hfc(hc, HFCPCI_B2_SSL, slot); Write_hfc(hc, HFCPCI_B2_RSL, slot); hc->hw.conn = (hc->hw.conn & ~0x38) | 0x08; Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); } if (3 & hh->dinfo) hc->hw.trm |= 0x80; /* enable IOM-loop */ else hc->hw.trm &= 0x7f; /* disable IOM-loop */ Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: unknown ctrl %x", __FUNCTION__, hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(dch->inst.hwlock, flags); } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { if (hc->hw.nt_mode) { spin_lock_irqsave(dch->inst.hwlock, flags); Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0); /* G0 */ udelay(6); Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1); /* G1 */ udelay(6); if (test_bit(HFC_CFG_MASTER, &hc->cfg)) hc->hw.mst_m |= HFCPCI_MASTER; Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); udelay(6); Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION | 1); spin_unlock_irqrestore(dch->inst.hwlock, flags); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: PH_ACTIVATE none NT mode", __FUNCTION__); ret = -EINVAL; } } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { if (hc->hw.nt_mode) { spin_lock_irqsave(dch->inst.hwlock, flags); hc->hw.mst_m &= ~HFCPCI_MASTER; Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); if (dch->next_skb) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) del_timer(&dch->timer); #ifdef FIXME if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) dchannel_sched_event(&hc->dch, D_CLEARBUSY); #endif spin_unlock_irqrestore(dch->inst.hwlock, flags); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: PH_DEACTIVATE none NT mode", __FUNCTION__); ret = -EINVAL; } } else if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS) { u_int temp = hh->dinfo & SSTATUS_ALL; if (hc->hw.nt_mode && /* if TE mode ignore */ (temp == SSTATUS_ALL || temp == SSTATUS_L1)) { if (hh->dinfo & SSTATUS_BROADCAST_BIT) temp = dch->inst.id | MSG_BROADCAST; else temp = hh->addr | FLG_MSG_TARGET; skb_trim(skb, 0); hh->dinfo = test_bit(FLG_ACTIVE, &dch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED; hh->prim = MGR_SHORTSTATUS | CONFIRM; return(mISDN_queue_message(&dch->inst, temp, skb)); } ret = -EOPNOTSUPP; } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: unknown prim %x", __FUNCTION__, hh->prim); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } /*************************************/ /* Layer 1 B-channel hardware access */ /*************************************/ static int hfc_bmsg(channel_t *bch, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); int ret = -EINVAL; u_long flags; if ((hh->prim == (PH_ACTIVATE | REQUEST)) || (hh->prim == (DL_ESTABLISH | REQUEST))) { if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { spin_lock_irqsave(bch->inst.hwlock, flags); ret = mode_hfcpci(bch, bch->channel, bch->inst.pid.protocol[1]); if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) test_and_set_bit(FLG_L2DATA, &bch->Flags); spin_unlock_irqrestore(bch->inst.hwlock, flags); } else ret = 0; skb_trim(skb, 0); return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb)); } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || (hh->prim == (DL_RELEASE | REQUEST)) || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { spin_lock_irqsave(bch->inst.hwlock, flags); if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; } bch->tx_idx = 0; if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); mode_hfcpci(bch, bch->channel, ISDN_PID_NONE); test_and_clear_bit(FLG_L2DATA, &bch->Flags); test_and_clear_bit(FLG_ACTIVE, &bch->Flags); spin_unlock_irqrestore(bch->inst.hwlock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb)) return(0); ret = 0; } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(bch->inst.hwlock, flags); if (hh->dinfo == HW_TESTRX_RAW) { ret = set_hfcpci_rxtest(bch, ISDN_PID_L1_B_64TRANS, skb); } else if (hh->dinfo == HW_TESTRX_HDLC) { ret = set_hfcpci_rxtest(bch, ISDN_PID_L1_B_64HDLC, skb); } else if (hh->dinfo == HW_TESTRX_OFF) { mode_hfcpci(bch, bch->channel, ISDN_PID_NONE); ret = 0; } else ret = -EINVAL; spin_unlock_irqrestore(bch->inst.hwlock, flags); if (!ret) { skb_trim(skb, 0); if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, hh->dinfo, skb)) return(0); } } else { printk(KERN_WARNING "%s: unknown prim(%x)\n", __FUNCTION__, hh->prim); ret = -EAGAIN; } if (!ret) dev_kfree_skb(skb); return(ret); } /******************************/ /* Layer2 -> Layer 1 Transfer */ /******************************/ static int hfcpci_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *chan = container_of(inst, channel_t, inst); int ret = -EINVAL; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == PH_DATA_REQ) || (hh->prim == (DL_DATA | REQUEST))) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(chan, hh->dinfo, skb); if (ret > 0) { /* direct TX */ if (test_bit(FLG_DCHANNEL, &chan->Flags)) hfcpci_fill_dfifo(chan->hw); if (test_bit(FLG_BCHANNEL, &chan->Flags)) hfcpci_fill_fifo(chan); ret = 0; } spin_unlock_irqrestore(inst->hwlock, flags); return(ret); } if (test_bit(FLG_DCHANNEL, &chan->Flags)) { ret = hfc_dmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (test_bit(FLG_BCHANNEL, &chan->Flags)) { ret = hfc_bmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } /********************************/ /* called for card init message */ /********************************/ void inithfcpci(hfc_pci_t *hc) { HFC_INFO("inithfcpci: entered\n"); hc->dch.timer.function = (void *) hfcpci_dbusy_timer; hc->dch.timer.data = (long) &hc->dch; init_timer(&hc->dch.timer); hc->chanlimit = 2; mode_hfcpci(&hc->bch[0], 1, -1); mode_hfcpci(&hc->bch[1], 2, -1); } static int init_card(hfc_pci_t *hc) { int cnt = 3; u_long flags; HFC_INFO("init_card: entered\n"); spin_lock_irqsave(&hc->lock, flags); disable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); if (request_irq(hc->irq, (void *)hfcpci_interrupt, SA_SHIRQ, "HFC PCI", hc)) { printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", hc->irq); return(-EIO); } spin_lock_irqsave(&hc->lock, flags); while (cnt) { inithfcpci(hc); /* Finally enable IRQ output * this is only allowed, if an IRQ routine is allready * established for this HFC, so don't do that earlier */ enable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); /* Timeout 80ms */ current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((80*HZ)/1000); printk(KERN_INFO "HFC PCI: IRQ %d count %d\n", hc->irq, hc->irqcnt); /* now switch timer interrupt off */ spin_lock_irqsave(&hc->lock, flags); hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); /* reinit mode reg */ Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); if (!hc->irqcnt) { printk(KERN_WARNING "HFC PCI: IRQ(%d) getting no interrupts during init %d\n", hc->irq, 4 - cnt); if (cnt == 1) { spin_unlock_irqrestore(&hc->lock, flags); return (-EIO); } else { reset_hfcpci(hc); cnt--; } } else { spin_unlock_irqrestore(&hc->lock, flags); return(0); } } spin_unlock_irqrestore(&hc->lock, flags); return(-EIO); } static int SelFreeBChannel(hfc_pci_t *hc, channel_info_t *ci) { channel_t *bch; hfc_pci_t *hfc; mISDNstack_t *bst; u_int cnr; struct list_head *head; if (!ci) return(-EINVAL); ci->st.p = NULL; cnr=0; bst = hc->dch.inst.st; if (list_empty(&bst->childlist)) { if ((bst->id & FLG_CLONE_STACK) && (bst->childlist.prev != &bst->childlist)) { head = bst->childlist.prev; } else { printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n", __FUNCTION__, bst->id, bst->childlist.prev, &bst->childlist, bst->childlist.next); return(-EINVAL); } } else head = &bst->childlist; list_for_each_entry(bst, head, list) { if (!bst->mgr) { int_errtxt("no mgr st(%p)", bst); return(-EINVAL); } bch = container_of(bst->mgr, channel_t, inst); hfc = bst->mgr->privat; if (!hfc) { int_errtxt("no mgr->data st(%p)", bst); return(-EINVAL); } bch = &hfc->bch[cnr & 1]; if (!(ci->channel & (~CHANNEL_NUMBER))) { /* only number is set */ if ((ci->channel & 0x3) == (cnr + 1)) { if (test_bit(FLG_ACTIVE, &bch->Flags)) return(-EBUSY); bch->channel = (cnr & 1) ? 2 : 1; ci->st.p = bst; return(0); } } else if ((ci->channel & (~CHANNEL_NUMBER)) == 0x00a18300) { if (!test_bit(FLG_ACTIVE, &bch->Flags)) { ci->st.p = bst; bch->channel = (cnr & 1) ? 2 : 1; bch->channel |= CHANNEL_EXT_PCM; bch->channel |= (ci->channel & 0x1f) << 16; bch->channel |= (ci->channel & 0x1f) << 8; ci->st.p = bst; return(0); } } cnr++; } return(-EBUSY); } #define MAX_CARDS 8 static int HFC_cnt; static uint protocol[MAX_CARDS]; static uint layermask[MAX_CARDS]; static uint debug; static mISDNobject_t HFC_obj; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #define MODULE_PARM_T "1-4i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #else module_param (debug, uint, 0); MODULE_PARM_DESC (debug, "hfcpci debug mask"); #ifdef OLD_MODULE_PARAM_ARRAY static int protocol_cnt; module_param_array(protocol, uint, protocol_cnt, 0); #else module_param_array(protocol, uint, NULL, 0); #endif MODULE_PARM_DESC (protocol, "hfcpci protcol (DSS1 := 2)"); /* short description of protocol * protocol=[,p2,p3...] * * Values: * the value has following structure * D-channel protocol id * Flags for special features * Spare (set to 0) * * D-channel protocol ids * 1 1TR6 (not released yet) * 2 DSS1 * * Feature Flags * bit 4 0x0010 Net side stack (NT mode) * bit 5 0x0020 point to point line * bit 6 0x0040 PCM slave mode * bit 7 0x0080 use negativ frame pulse * bit 8 0x0100 use setting from the previous HFC driver and add channels to * the previous stack, used for the second chip in 2 chip setups * bit 9 0x0200 switch DD/DU interface * bit 10 - 15 reserved */ #ifdef OLD_MODULE_PARAM_ARRAY static int layermask_cnt; module_param_array(layermask, uint, layermask_cnt, 0); #else module_param_array(layermask, uint, NULL, 0); #endif MODULE_PARM_DESC(layermask, "hfcpci layer mask"); #endif #endif static char HFCName[] = "HFC_PCI"; /* this variable is used as card index when more than one cards are present */ static struct pci_dev *dev_hfcpci = NULL; static int setup_hfcpci(hfc_pci_t *hc) { char tmp[64]; int i=0; struct pci_dev *tmp_hfcpci = NULL; void *buffer; strcpy(tmp, hfcpci_revision); printk(KERN_INFO "mISDN: HFC-PCI driver Rev. %s\n", mISDN_getrev(tmp)); hc->hw.cirm = 0; hc->dch.state = 0; while (id_list[i].vendor_id) { tmp_hfcpci = pci_find_device(id_list[i].vendor_id, id_list[i].device_id, dev_hfcpci); i++; if (tmp_hfcpci) { if (pci_enable_device(tmp_hfcpci)) continue; pci_set_master(tmp_hfcpci); break; } } if (tmp_hfcpci) { i--; dev_hfcpci = tmp_hfcpci; /* old device */ hc->hw.dev = dev_hfcpci; hc->irq = dev_hfcpci->irq; if (!hc->irq) { printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); return (1); } hc->hw.pci_io = (char *) get_pcibase(dev_hfcpci, 1); printk(KERN_INFO "mISDN: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name); } else { printk(KERN_WARNING "HFC-PCI: No more PCI cards found\n"); return (1); } if (!hc->hw.pci_io) { printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); return (1); } /* Allocate memory for FIFOS */ /* the memory needs to be on a 32k boundary within the first 4G */ pci_set_dma_mask(dev_hfcpci, 0xFFFF8000); buffer = pci_alloc_consistent(dev_hfcpci, 0x8000, &hc->hw.dmahandle); /* We silently assume the address is okay if nonzero */ if(!buffer) { printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n"); return 1; } hc->hw.fifos = buffer; pci_write_config_dword(hc->hw.dev, 0x80, hc->hw.dmahandle); hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256); printk(KERN_INFO "HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n", (u_long) hc->hw.pci_io, (u_long) hc->hw.fifos, (u_long) virt_to_bus(hc->hw.fifos), hc->irq, HZ); pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ hc->hw.int_m2 = 0; disable_hwirq(hc); hc->hw.int_m1 = 0; Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); /* At this point the needed PCI config is done */ /* fifos are still not enabled */ hc->hw.timer.function = (void *) hfcpci_Timer; hc->hw.timer.data = (long) hc; init_timer(&hc->hw.timer); reset_hfcpci(hc); return (0); } static void release_card(hfc_pci_t *hc) { u_long flags; free_irq(hc->irq, hc); spin_lock_irqsave(&hc->lock, flags); mode_hfcpci(&hc->bch[0], 1, ISDN_PID_NONE); mode_hfcpci(&hc->bch[1], 2, ISDN_PID_NONE); if (hc->dch.timer.function != NULL) { del_timer(&hc->dch.timer); hc->dch.timer.function = NULL; } release_io_hfcpci(hc); mISDN_freechannel(&hc->bch[1]); mISDN_freechannel(&hc->bch[0]); mISDN_freechannel(&hc->dch); spin_unlock_irqrestore(&hc->lock, flags); mISDN_ctrl(&hc->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); spin_lock_irqsave(&HFC_obj.lock, flags); list_del(&hc->list); spin_unlock_irqrestore(&HFC_obj.lock, flags); kfree(hc); } static int HFC_manager(void *data, u_int prim, void *arg) { hfc_pci_t *card; mISDNinstance_t *inst = data; struct sk_buff *skb; int channel = -1; u_long flags; if (!data) { MGR_HASPROTOCOL_HANDLER(prim,arg,&HFC_obj) printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); return(-EINVAL); } spin_lock_irqsave(&HFC_obj.lock, flags); list_for_each_entry(card, &HFC_obj.ilist, list) { if (&card->dch.inst == inst) { channel = 2; break; } if (&card->bch[0].inst == inst) { channel = 0; break; } if (&card->bch[1].inst == inst) { inst = &card->bch[1].inst; channel = 1; break; } } spin_unlock_irqrestore(&HFC_obj.lock, flags); if (channel<0) { printk(KERN_ERR "%s: no channel data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return(-EINVAL); } switch(prim) { case MGR_REGLAYER | CONFIRM: if (channel == 2) mISDN_setpara(&card->dch, &inst->st->para); else mISDN_setpara(&card->bch[channel], &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { if (hfcpci_l2l1(inst, skb)) dev_kfree_skb(skb); } mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: if (channel == 2) mISDN_setpara(&card->dch, arg); else mISDN_setpara(&card->bch[channel], arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { release_card(card); } else { HFC_obj.refcnt--; } break; #ifdef FIXME 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, HFCD_l1hw, NULL, &card->dch)); else return(mISDN_SetIF(inst, arg, prim, hfcpci_l2l1, NULL, &card->bch[channel])); case MGR_DISCONNECT | REQUEST: case MGR_DISCONNECT | INDICATION: return(mISDN_DisConnectIF(inst, arg)); #endif case MGR_SELCHANNEL | REQUEST: if (channel != 2) { printk(KERN_WARNING "%s: selchannel not dinst\n", __FUNCTION__); return(-EINVAL); } return(SelFreeBChannel(card, arg)); case MGR_SETSTACK | INDICATION: if ((channel!=2) && (inst->pid.global == 2)) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (hfcpci_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); } static int __init HFC_init(void) { int err,i; hfc_pci_t *card, *prev; mISDN_pid_t pid; mISDNstack_t *dst; u_long flags; #ifdef MODULE HFC_obj.owner = THIS_MODULE; #endif spin_lock_init(&HFC_obj.lock); INIT_LIST_HEAD(&HFC_obj.ilist); HFC_obj.name = HFCName; HFC_obj.own_ctrl = HFC_manager; HFC_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0; HFC_obj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0; HFC_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; HFC_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; if ((err = mISDN_register(&HFC_obj))) { printk(KERN_ERR "Can't register HFC PCI error(%d)\n", err); return(err); } while (HFC_cnt < MAX_CARDS) { if (!(card = kmalloc(sizeof(hfc_pci_t), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for HFCcard\n"); mISDN_unregister(&HFC_obj); return(-ENOMEM); } memset(card, 0, sizeof(hfc_pci_t)); spin_lock_irqsave(&HFC_obj.lock, flags); list_add_tail(&card->list, &HFC_obj.ilist); spin_unlock_irqrestore(&HFC_obj.lock, flags); card->dch.debug = debug; spin_lock_init(&card->lock); card->dch.inst.hwlock = &card->lock; mISDN_init_instance(&card->dch.inst, &HFC_obj, card, hfcpci_l2l1); card->dch.inst.pid.layermask = ISDN_LAYER(0); sprintf(card->dch.inst.name, "HFC%d", HFC_cnt+1); err = mISDN_initchannel(&card->dch, MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); card->dch.hw = card; if (err) { mISDN_unregister(&HFC_obj); return(err); } for (i=0; i<2; i++) { card->bch[i].channel = i + 1; mISDN_init_instance(&card->bch[i].inst, &HFC_obj, card, hfcpci_l2l1); card->bch[i].inst.pid.layermask = ISDN_LAYER(0); card->bch[i].inst.hwlock = &card->lock; card->bch[i].debug = debug; sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1); mISDN_initchannel(&card->bch[i], MSK_INIT_BCHANNEL, MAX_DATA_MEM); card->bch[i].hw = card; #ifdef FIXME if (card->bch[i].dev) { card->bch[i].dev->wport.pif.func = hfcpci_l2l1; card->bch[i].dev->wport.pif.fdata = &card->bch[i]; } #endif } if (protocol[HFC_cnt] == 0x100) { if (card->list.prev == &HFC_obj.ilist) prev = NULL; else prev = list_entry(card->list.prev, hfc_pci_t, list); if (!prev) { int_errtxt("card(%d) no previous HFC", HFC_cnt); if (!HFC_cnt) mISDN_unregister(&HFC_obj); else err = 0; return(err); } i = HFC_cnt - 1; test_and_set_bit(HFC_CFG_2HFC, &prev->cfg); test_and_set_bit(HFC_CFG_2HFC, &card->cfg); test_and_set_bit(HFC_CFG_SLAVEHFC, &card->cfg); } else { prev = NULL; i = HFC_cnt; } mISDN_set_dchannel_pid(&pid, protocol[i], layermask[i]); test_and_set_bit(HFC_CFG_MASTER, &card->cfg); if (protocol[i] & 0x10) { card->dch.inst.pid.protocol[0] = ISDN_PID_L0_NT_S0; card->dch.inst.pid.protocol[1] = ISDN_PID_L1_NT_S0; pid.protocol[0] = ISDN_PID_L0_NT_S0; pid.protocol[1] = ISDN_PID_L1_NT_S0; card->dch.inst.pid.layermask |= ISDN_LAYER(1); pid.layermask |= ISDN_LAYER(1); if (layermask[i] & ISDN_LAYER(2)) pid.protocol[2] = ISDN_PID_L2_LAPD_NET; card->hw.nt_mode = 1; } else { card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; card->hw.nt_mode = 0; } if (protocol[i] & 0x40) { if (pid.layermask & ISDN_LAYER(3)) pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID; test_and_set_bit(HFC_CFG_PCM, &card->cfg); test_and_set_bit(HFC_CFG_SLAVE, &card->cfg); test_and_clear_bit(HFC_CFG_MASTER, &card->cfg); } if (protocol[i] & 0x80) { test_and_set_bit(HFC_CFG_NEG_F0, &card->cfg); } if (protocol[i] & 0x200) { test_and_set_bit(HFC_CFG_SW_DD_DU, &card->cfg); } printk(KERN_DEBUG "HFC card %p dch %p bch1 %p bch2 %p\n", card, &card->dch, &card->bch[0], &card->bch[1]); if (setup_hfcpci(card)) { err = 0; mISDN_freechannel(&card->dch); mISDN_freechannel(&card->bch[1]); mISDN_freechannel(&card->bch[0]); spin_lock_irqsave(&HFC_obj.lock, flags); list_del(&card->list); spin_unlock_irqrestore(&HFC_obj.lock, flags); kfree(card); if (!HFC_cnt) { mISDN_unregister(&HFC_obj); err = -ENODEV; } else printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); return(err); } // card->dch.inst.class_dev.dev = &card->hw.dev->dev; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->dch.inst.class_dev.parent = &card->hw.dev->dev; #else card->dch.inst.class_dev.dev = &card->hw.dev->dev; #endif HFC_cnt++; if (prev) { dst = prev->dch.inst.st; } else { if ((err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst))) { printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", err); release_card(card); if (!HFC_cnt) mISDN_unregister(&HFC_obj); else err = 0; return(err); } dst = card->dch.inst.st; } mISDN_ctrl(dst, MGR_STOPSTACK | REQUEST, NULL); for (i = 0; i < 2; i++) { //card->bch[i].inst.class_dev.dev = &card->hw.dev->dev; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->bch[i].inst.class_dev.parent = &card->hw.dev->dev; #else card->bch[i].inst.class_dev.dev = &card->hw.dev->dev; #endif if ((err = mISDN_ctrl(dst, MGR_NEWSTACK | REQUEST, &card->bch[i].inst))) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); if (!HFC_cnt) mISDN_unregister(&HFC_obj); else err = 0; return(err); } } if (protocol[HFC_cnt] != 0x100) { /* next not second HFC */ if ((err = mISDN_ctrl(dst, MGR_SETSTACK | REQUEST, &pid))) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); mISDN_ctrl(dst, MGR_DELSTACK | REQUEST, NULL); if (!HFC_cnt) mISDN_unregister(&HFC_obj); else err = 0; return(err); } } if ((err = init_card(card))) { mISDN_ctrl(dst, MGR_DELSTACK | REQUEST, NULL); if (!HFC_cnt) mISDN_unregister(&HFC_obj); else err = 0; return(err); } mISDN_ctrl(dst, MGR_STARTSTACK | REQUEST, NULL); mISDN_ctrl(dst, MGR_CTRLREADY | INDICATION, NULL); } mISDN_module_register(THIS_MODULE); printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); return(0); } #ifdef MODULE static void __exit HFC_cleanup(void) { hfc_pci_t *card, *next; int err; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&HFC_obj))) { printk(KERN_ERR "Can't unregister HFC PCI error(%d)\n", err); } list_for_each_entry_safe(card, next, &HFC_obj.ilist, list) { printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", HFC_obj.refcnt); release_card(card); } return; } module_init(HFC_init); module_exit(HFC_cleanup); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/hfc_pci.h0000644000000000000500000001507011135651702020136 0ustar rootsrc/* $Id: hfc_pci.h,v 1.3 2003/06/21 21:39:54 kkeil Exp $ * * specific defines for CCD's HFC 2BDS0 PCI chips * * Author Werner Cornelius (werner@isdn4linux.de) * * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /*********************************************/ /* thresholds for transparent B-channel mode */ /* change mask and threshold simultaneously */ /*********************************************/ #define HFCPCI_BTRANS_THRESHOLD 128 #define HFCPCI_BTRANS_THRESMASK 0x00 /* defines for PCI config */ #define PCI_ENA_MEMIO 0x02 #define PCI_ENA_MASTER 0x04 /* GCI/IOM bus monitor registers */ #define HCFPCI_C_I 0x08 #define HFCPCI_TRxR 0x0C #define HFCPCI_MON1_D 0x28 #define HFCPCI_MON2_D 0x2C /* GCI/IOM bus timeslot registers */ #define HFCPCI_B1_SSL 0x80 #define HFCPCI_B2_SSL 0x84 #define HFCPCI_AUX1_SSL 0x88 #define HFCPCI_AUX2_SSL 0x8C #define HFCPCI_B1_RSL 0x90 #define HFCPCI_B2_RSL 0x94 #define HFCPCI_AUX1_RSL 0x98 #define HFCPCI_AUX2_RSL 0x9C /* GCI/IOM bus data registers */ #define HFCPCI_B1_D 0xA0 #define HFCPCI_B2_D 0xA4 #define HFCPCI_AUX1_D 0xA8 #define HFCPCI_AUX2_D 0xAC /* GCI/IOM bus configuration registers */ #define HFCPCI_MST_EMOD 0xB4 #define HFCPCI_MST_MODE 0xB8 #define HFCPCI_CONNECT 0xBC /* Interrupt and status registers */ #define HFCPCI_FIFO_EN 0x44 #define HFCPCI_TRM 0x48 #define HFCPCI_B_MODE 0x4C #define HFCPCI_CHIP_ID 0x58 #define HFCPCI_CIRM 0x60 #define HFCPCI_CTMT 0x64 #define HFCPCI_INT_M1 0x68 #define HFCPCI_INT_M2 0x6C #define HFCPCI_INT_S1 0x78 #define HFCPCI_INT_S2 0x7C #define HFCPCI_STATUS 0x70 /* S/T section registers */ #define HFCPCI_STATES 0xC0 #define HFCPCI_SCTRL 0xC4 #define HFCPCI_SCTRL_E 0xC8 #define HFCPCI_SCTRL_R 0xCC #define HFCPCI_SQ 0xD0 #define HFCPCI_CLKDEL 0xDC #define HFCPCI_B1_REC 0xF0 #define HFCPCI_B1_SEND 0xF0 #define HFCPCI_B2_REC 0xF4 #define HFCPCI_B2_SEND 0xF4 #define HFCPCI_D_REC 0xF8 #define HFCPCI_D_SEND 0xF8 #define HFCPCI_E_REC 0xFC /* bits in status register (READ) */ #define HFCPCI_PCI_PROC 0x02 #define HFCPCI_NBUSY 0x04 #define HFCPCI_TIMER_ELAP 0x10 #define HFCPCI_STATINT 0x20 #define HFCPCI_FRAMEINT 0x40 #define HFCPCI_ANYINT 0x80 /* bits in CTMT (Write) */ #define HFCPCI_CLTIMER 0x80 #define HFCPCI_TIM3_125 0x04 #define HFCPCI_TIM25 0x10 #define HFCPCI_TIM50 0x14 #define HFCPCI_TIM400 0x18 #define HFCPCI_TIM800 0x1C #define HFCPCI_AUTO_TIMER 0x20 #define HFCPCI_TRANSB2 0x02 #define HFCPCI_TRANSB1 0x01 /* bits in CIRM (Write) */ #define HFCPCI_AUX_MSK 0x07 #define HFCPCI_RESET 0x08 #define HFCPCI_B1_REV 0x40 #define HFCPCI_B2_REV 0x80 /* bits in INT_M1 and INT_S1 */ #define HFCPCI_INTS_B1TRANS 0x01 #define HFCPCI_INTS_B2TRANS 0x02 #define HFCPCI_INTS_DTRANS 0x04 #define HFCPCI_INTS_B1REC 0x08 #define HFCPCI_INTS_B2REC 0x10 #define HFCPCI_INTS_DREC 0x20 #define HFCPCI_INTS_L1STATE 0x40 #define HFCPCI_INTS_TIMER 0x80 /* bits in INT_M2 */ #define HFCPCI_PROC_TRANS 0x01 #define HFCPCI_GCI_I_CHG 0x02 #define HFCPCI_GCI_MON_REC 0x04 #define HFCPCI_IRQ_ENABLE 0x08 #define HFCPCI_PMESEL 0x80 /* bits in STATES */ #define HFCPCI_STATE_MSK 0x0F #define HFCPCI_LOAD_STATE 0x10 #define HFCPCI_ACTIVATE 0x20 #define HFCPCI_DO_ACTION 0x40 #define HFCPCI_NT_G2_G3 0x80 /* bits in HFCD_MST_MODE */ #define HFCPCI_MASTER 0x01 #define HFCPCI_SLAVE 0x00 #define HFCPCI_F0IO_POSITIV 0x02 #define HFCPCI_F0_NEGATIV 0x04 #define HFCPCI_F0_2C4 0x08 /* remaining bits are for codecs control */ /* bits in HFCD_SCTRL */ #define SCTRL_B1_ENA 0x01 #define SCTRL_B2_ENA 0x02 #define SCTRL_MODE_TE 0x00 #define SCTRL_MODE_NT 0x04 #define SCTRL_LOW_PRIO 0x08 #define SCTRL_SQ_ENA 0x10 #define SCTRL_TEST 0x20 #define SCTRL_NONE_CAP 0x40 #define SCTRL_PWR_DOWN 0x80 /* bits in SCTRL_E */ #define HFCPCI_AUTO_AWAKE 0x01 #define HFCPCI_DBIT_1 0x04 #define HFCPCI_IGNORE_COL 0x08 #define HFCPCI_CHG_B1_B2 0x80 /****************************/ /* bits in FIFO_EN register */ /****************************/ #define HFCPCI_FIFOEN_B1 0x03 #define HFCPCI_FIFOEN_B2 0x0C #define HFCPCI_FIFOEN_DTX 0x10 #define HFCPCI_FIFOEN_B1TX 0x01 #define HFCPCI_FIFOEN_B1RX 0x02 #define HFCPCI_FIFOEN_B2TX 0x04 #define HFCPCI_FIFOEN_B2RX 0x08 /***********************************/ /* definitions of fifo memory area */ /***********************************/ #define MAX_D_FRAMES 15 #define MAX_B_FRAMES 31 #define B_SUB_VAL 0x200 #define B_FIFO_SIZE (0x2000 - B_SUB_VAL) #define D_FIFO_SIZE 512 #define D_FREG_MASK 0xF typedef struct { unsigned short z1; /* Z1 pointer 16 Bit */ unsigned short z2; /* Z2 pointer 16 Bit */ } z_type; typedef struct { u_char data[D_FIFO_SIZE]; /* FIFO data space */ u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */ u_char f1,f2; /* f pointers */ u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */ z_type za[MAX_D_FRAMES+1]; /* mask index with D_FREG_MASK for access */ u_char fill3[0x4000-0x2100]; /* align 16K */ } dfifo_type; typedef struct { z_type za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ u_char f1,f2; /* f pointers */ u_char fill[0x2100-0x2082]; /* alignment */ } bzfifo_type; typedef union { struct { dfifo_type d_tx; /* D-send channel */ dfifo_type d_rx; /* D-receive channel */ } d_chan; struct { u_char fill1[0x200]; u_char txdat_b1[B_FIFO_SIZE]; bzfifo_type txbz_b1; bzfifo_type txbz_b2; u_char txdat_b2[B_FIFO_SIZE]; u_char fill2[D_FIFO_SIZE]; u_char rxdat_b1[B_FIFO_SIZE]; bzfifo_type rxbz_b1; bzfifo_type rxbz_b2; u_char rxdat_b2[B_FIFO_SIZE]; } b_chans; u_char fill[32768]; } fifo_area; #define Write_hfc(a,b,c) (*(((u_char *)a->hw.pci_io)+b) = c) #define Read_hfc(a,b) (*(((u_char *)a->hw.pci_io)+b)) mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/hfcs_mini.c0000755000000000000500000014030011135651702020473 0ustar rootsrc/* $Id: hfcs_mini.c,v 1.10 2007/02/13 10:43:45 crich Exp $ * * mISDN driver for Colognechip HFC-S mini Evaluation Card * * Authors : Martin Bachem, Joerg Ciesielski * Contact : info@colognechip.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ******************************************************************************* * * MODULE PARAMETERS: * (NOTE: layermask and protocol must be given for all ports, * not for the number of cards.) * * - protocol=[,p2,p3...] * Values: * D-channel protocol id * Flags for special features * Spare (set to 0) * * D-channel protocol ids * - 1 1TR6 (not released yet) * - 2 DSS1 * * Feature Flags * 0x0010 Net side stack (NT mode) * 0x0020 PCI mode (0=master, 1=slave) * 0x0040 not in use * 0x0080 B channel loop (for layer1 tests) * * - layermask=[,l2,l3...] (32bit): * mask of layers to be used for D-channel stack * * - debug: * enable debugging (see hfcs_mini.h for debug options) * */ #include #include #include #include "core.h" #include "layer1.h" #include "debug.h" #include "hfcs_mini.h" #include "hfcsmcc.h" #if HFCBRIDGE == BRIDGE_HFCPCI #include #endif static const char hfcsmini_rev[] = "$Revision: 1.10 $"; #define MAX_CARDS 8 static int card_cnt; static u_int protocol[MAX_CARDS]; static int layermask[MAX_CARDS]; static mISDNobject_t hw_mISDNObj; static int debug = 0; #ifdef MODULE MODULE_LICENSE("GPL"); #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #define MODULE_PARM_T "1-4i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #ifdef OLD_MODULE_PARAM_ARRAY static int num_protocol=0, num_layermask=0; module_param_array(protocol, uint, num_protocol, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, num_layermask, S_IRUGO | S_IWUSR); #else module_param_array(protocol, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, NULL, S_IRUGO | S_IWUSR); #endif #endif #endif #if HFCBRIDGE == BRIDGE_HFCPCI static inline void hfcsmini_sel_reg(hfcsmini_hw * hw, __u8 reg_addr) { outb(6, hw->iobase + 3); /* A0 = 1, reset = 1 */ outb(reg_addr, hw->iobase + 1); /* write register number */ outb(4, hw->iobase + 3); /* A0 = 0, reset = 1 */ } static inline __u8 read_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr) { register u_char ret; #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_lock_irq(&hw->rlock); #endif hfcsmini_sel_reg(hw, reg_addr); ret = inb(hw->iobase + 1); #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_unlock_irq(&hw->rlock); #endif return(ret); } /* read register in already spin-locked irq context */ static inline __u8 read_hfcsmini_irq(hfcsmini_hw * hw, __u8 reg_addr) { register u_char ret; hfcsmini_sel_reg(hw, reg_addr); ret = inb(hw->iobase + 1); return(ret); } static inline __u8 read_hfcsmini_stable(hfcsmini_hw * hw, __u8 reg_addr) { register u_char in1, in2; #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_lock_irq(&hw->rlock); #endif hfcsmini_sel_reg(hw, reg_addr); in1 = inb(hw->iobase + 1); // loop until 2 equal accesses while((in2=inb(hw->iobase + 1))!=in1) in1=in2; #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_unlock_irq(&hw->rlock); #endif return(in1); } static inline void write_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr, __u8 value) { #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_lock_irq(&hw->rlock); #endif hfcsmini_sel_reg(hw, reg_addr); outb(value, hw->iobase + 1); #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_unlock_irq(&hw->rlock); #endif } #endif static void hfcsmini_ph_command(channel_t * dch, u_char command) { hfcsmini_hw *hw = dch->hw; if (dch->debug) mISDN_debugprint(&dch->inst, "%s command(%i) channel(%i)", __FUNCTION__, command, dch->channel); switch (command) { case HFC_L1_ACTIVATE_TE: if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) { mISDN_debugprint(&dch->inst, "HFC_L1_ACTIVATE_TE channel(%i) command(%i)", dch->channel, command); } write_hfcsmini(hw, R_ST_WR_STA, (M_ST_LD_STA | (M1_ST_SET_STA*4))); udelay(125); /* to be sure INFO1 signals are sent */ write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_SET_STA * 4)); break; case HFC_L1_FORCE_DEACTIVATE_TE: write_hfcsmini(hw, R_ST_WR_STA, (M_ST_LD_STA | (M1_ST_SET_STA*3))); udelay(7); /* wait at least 5,21 us */ write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_SET_STA*3)); break; case HFC_L1_ACTIVATE_NT: if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "HFC_L1_ACTIVATE_NT channel(%i)"); write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_ACT | M_SET_G2_G3)); break; case HFC_L1_DEACTIVATE_NT: if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "HFC_L1_DEACTIVATE_NT channel(%i)"); write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_ACT * 2)); break; case HFC_L1_TESTLOOP_B1: break; case HFC_L1_TESTLOOP_B2: break; } } /*********************************/ /* S0 state change event handler */ /*********************************/ static void s0_new_state(channel_t * dch) { u_int prim = PH_SIGNAL | INDICATION; u_int para = 0; hfcsmini_hw *hw = dch->hw; if (hw->portmode & PORT_MODE_TE) { if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "%s: TE %d", __FUNCTION__, dch->state); switch (dch->state) { case (0): prim = PH_CONTROL | INDICATION; para = HW_RESET; break; case (3): prim = PH_CONTROL | INDICATION; para = HW_DEACTIVATE; break; case (6): para = INFO2; break; case (7): para = INFO4_P8; break; case (5): case (8): para = ANYSIGNAL; break; default: return; } if (dch->state== 7) test_and_set_bit(FLG_ACTIVE, &dch->Flags); else test_and_clear_bit(FLG_ACTIVE, &dch->Flags); } // PORT_MODE_TE if (hw->portmode & PORT_MODE_NT) { if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "%s: NT %d", __FUNCTION__, dch->state); switch (dch->state) { case (1): hw->nt_timer = 0; hw->portmode &= ~NT_TIMER; prim = PH_DEACTIVATE | INDICATION; test_and_clear_bit(FLG_ACTIVE, &dch->Flags); para = 0; break; case (2): if (hw->nt_timer < 0) { hw->nt_timer = 0; hw->portmode &= ~NT_TIMER; hfcsmini_ph_command(dch, HFC_L1_DEACTIVATE_NT); } else { hw->nt_timer = NT_T1_COUNT; hw->portmode |= NT_TIMER; write_hfcsmini(hw, R_ST_WR_STA, M_SET_G2_G3); } return; case (3): hw->nt_timer = 0; hw->portmode &= ~NT_TIMER; prim = PH_ACTIVATE | INDICATION; test_and_set_bit(FLG_ACTIVE, &dch->Flags); para = 0; break; case (4): hw->nt_timer = 0; hw->portmode &= ~NT_TIMER; return; default: break; } } // PORT_MODE_NT mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0); } /*************************************/ /* Layer 1 D-channel hardware access */ /*************************************/ static int handle_dmsg(channel_t *dch, struct sk_buff *skb) { int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); hfcsmini_hw *hw = dch->hw; u_long flags; if (hh->prim == (PH_SIGNAL | REQUEST)) { ret = -EINVAL; } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(&hw->rlock, flags); if (hh->dinfo == HW_RESET) { if (dch->state != 0) hfcsmini_ph_command(dch, HFC_L1_ACTIVATE_TE); spin_unlock_irqrestore(&hw->rlock, flags); skb_trim(skb, 0); return(mISDN_queueup_newhead(&dch->inst, 0, PH_CONTROL | INDICATION,HW_POWERUP, skb)); } else if (hh->dinfo == HW_DEACTIVATE) { if (dch->next_skb) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); #ifdef FIXME if (test_and_clear_bit(FLG_L1_DBUSY, &dch->Flags)) dchannel_sched_event(dch, D_CLEARBUSY); #endif } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) { if (1 & hh->dinfo) hfcsmini_ph_command(dch, HFC_L1_TESTLOOP_B1); if (2 & hh->dinfo) hfcsmini_ph_command(dch, HFC_L1_TESTLOOP_B2); } else if (hh->dinfo == HW_POWERUP) { hfcsmini_ph_command(dch, HFC_L1_FORCE_DEACTIVATE_TE); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "hfcsmini_l1hw unknown ctrl %x", hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(&hw->rlock, flags); } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { spin_lock_irqsave(&hw->rlock, flags); if (hw->portmode & PORT_MODE_NT) { hfcsmini_ph_command(dch, HFC_L1_ACTIVATE_NT); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: PH_ACTIVATE none NT mode", __FUNCTION__); ret = -EINVAL; } spin_unlock_irqrestore(&hw->rlock, flags); } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { spin_lock_irqsave(&hw->rlock, flags); if (hw->portmode & PORT_MODE_NT) { hfcsmini_ph_command(dch, HFC_L1_DEACTIVATE_NT); if (test_and_clear_bit(FLG_TX_NEXT, &dch->Flags)) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } if (dch->tx_skb) { dev_kfree_skb(dch->tx_skb); dch->tx_skb = NULL; } dch->tx_idx = 0; if (dch->rx_skb) { dev_kfree_skb(dch->rx_skb); dch->rx_skb = NULL; } test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); test_and_clear_bit(FLG_ACTIVE, &dch->Flags); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: PH_DEACTIVATE none NT mode", __FUNCTION__); ret = -EINVAL; } spin_unlock_irqrestore(&hw->rlock, flags); } else if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS) { u_int temp = hh->dinfo & SSTATUS_ALL; // remove SSTATUS_BROADCAST_BIT if ((hw->portmode & PORT_MODE_NT) && (temp == SSTATUS_ALL || temp == SSTATUS_L1)) { if (hh->dinfo & SSTATUS_BROADCAST_BIT) temp = dch->inst.id | MSG_BROADCAST; else temp = hh->addr | FLG_MSG_TARGET; skb_trim(skb, 0); hh->dinfo = test_bit(FLG_ACTIVE, &dch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED; hh->prim = MGR_SHORTSTATUS | CONFIRM; return(mISDN_queue_message(&dch->inst, temp, skb)); } ret = -EOPNOTSUPP; } else { printk(KERN_WARNING "%s %s: unknown prim(%x)\n", hw->card_name, __FUNCTION__, hh->prim); ret = -EAGAIN; } if (!ret) dev_kfree_skb(skb); return (ret); } /*************************************/ /* Layer 1 B-channel hardware access */ /*************************************/ static int handle_bmsg(channel_t *bch, struct sk_buff *skb) { hfcsmini_hw *hw = bch->hw; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == (PH_ACTIVATE | REQUEST)) || (hh->prim == (DL_ESTABLISH | REQUEST))) { if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { spin_lock_irqsave(&hw->rlock, flags); ret = setup_channel(hw, bch->channel, bch->inst.pid.protocol[1]); if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) test_and_set_bit(FLG_L2DATA, &bch->Flags); spin_unlock_irqrestore(&hw->rlock, flags); } #ifdef FIXME if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif skb_trim(skb, 0); return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb)); } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || (hh->prim == (DL_RELEASE | REQUEST)) || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { spin_lock_irqsave(&hw->rlock, flags); if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; } bch->tx_idx = 0; if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } test_and_clear_bit(FLG_L2DATA, &bch->Flags); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); setup_channel(hw, bch->channel, ISDN_PID_NONE); test_and_clear_bit(FLG_ACTIVE, &bch->Flags); spin_unlock_irqrestore(&hw->rlock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) { #ifdef FIXME if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb)) return(0); } } else if (hh->prim == (PH_CONTROL | REQUEST)) { // do not handle PH_CONTROL | REQUEST ?? } else { printk(KERN_WARNING "%s %s: unknown prim(%x)\n", hw->card_name, __FUNCTION__, hh->prim); ret = -EAGAIN; } if (!ret) dev_kfree_skb(skb); return (ret); } /******************************/ /* Layer2 -> Layer 1 Transfer */ /******************************/ static int hfcsmini_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *chan = container_of(inst, channel_t, inst); hfcsmini_hw *hw = chan->hw; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(chan, hh->dinfo, skb); if (ret > 0) { /* direct TX */ tasklet_schedule(&hw->tasklet); ret = 0; } spin_unlock_irqrestore(inst->hwlock, flags); return(ret); } if (test_bit(FLG_DCHANNEL, &chan->Flags)) { ret = handle_dmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (test_bit(FLG_BCHANNEL, &chan->Flags)) { ret = handle_bmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } static int hfcsmini_manager(void *data, u_int prim, void *arg) { hfcsmini_hw *hw = NULL; mISDNinstance_t *inst = data; struct sk_buff *skb; int channel = -1; int i; channel_t *chan = NULL; u_long flags; if (!data) { MGR_HASPROTOCOL_HANDLER(prim, arg, &hw_mISDNObj) printk(KERN_ERR "%s %s: no data prim %x arg %p\n", hw->card_name, __FUNCTION__, prim, arg); return (-EINVAL); } spin_lock_irqsave(&hw_mISDNObj.lock, flags); /* find channel and card */ list_for_each_entry(hw, &hw_mISDNObj.ilist, list) { i = 0; while (i < MAX_CHAN) { if (hw->chan[i].Flags && &hw->chan[i].inst == inst) { channel = i; chan = &hw->chan[i]; break; } i++; } if (channel >= 0) break; } spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); if (channel < 0) { printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return (-EINVAL); } switch (prim) { case MGR_REGLAYER | CONFIRM: mISDN_setpara(chan, &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { if (hfcsmini_l2l1(inst, skb)) dev_kfree_skb(skb); } else printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: mISDN_setpara(chan, arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { release_card(hw); } else { hw_mISDNObj.refcnt--; } break; case MGR_SETSTACK | INDICATION: if ((channel != 2) && (inst->pid.global == 2)) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (hfcsmini_l2l1(inst, skb)) dev_kfree_skb(skb); } if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } break; case MGR_GLOBALOPT | REQUEST: if (arg) { // FIXME: detect cards with HEADSET u_int *gopt = arg; *gopt = GLOBALOPT_INTERNAL_CTRL | GLOBALOPT_EXTERNAL_EQUIPMENT | GLOBALOPT_HANDSET; } else return (-EINVAL); break; case MGR_SELCHANNEL | REQUEST: // no special procedure return (-EINVAL); PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); default: printk(KERN_WARNING "%s %s: prim %x not handled\n", hw->card_name, __FUNCTION__, prim); return (-EINVAL); } return (0); } /***********************************/ /* check if new buffer for channel */ /* is waitinng is transmitt queue */ /***********************************/ int next_tx_frame(hfcsmini_hw * hw, __u8 channel) { channel_t *ch = &hw->chan[channel]; if (ch->tx_skb) dev_kfree_skb(ch->tx_skb); if (test_and_clear_bit(FLG_TX_NEXT, &ch->Flags)) { ch->tx_skb = ch->next_skb; if (ch->tx_skb) { mISDN_head_t *hh = mISDN_HEAD_P(ch->tx_skb); ch->next_skb = NULL; test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); ch->tx_idx = 0; queue_ch_frame(ch, CONFIRM, hh->dinfo, NULL); return (1); } else { printk(KERN_WARNING "%s channel(%i) TX_NEXT without skb\n", hw->card_name, channel); test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); } } else ch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); return (0); } static inline void hfcsmini_waitbusy(hfcsmini_hw *hw) { while (read_hfcsmini(hw, R_STATUS) & M_BUSY); } static inline void hfcsmini_selfifo(hfcsmini_hw *hw, __u8 fifo) { write_hfcsmini(hw, R_FIFO, fifo); hfcsmini_waitbusy(hw); } static inline void hfcsmini_inc_f(hfcsmini_hw *hw) { write_hfcsmini(hw, A_INC_RES_FIFO, M_INC_F); hfcsmini_waitbusy(hw); } static inline void hfcsmini_resetfifo(hfcsmini_hw *hw) { write_hfcsmini(hw, A_INC_RES_FIFO, M_RES_FIFO); hfcsmini_waitbusy(hw); } /**************************/ /* fill fifo with TX data */ /**************************/ void hfcsmini_write_fifo(hfcsmini_hw *hw, __u8 channel) { __u8 fcnt, tcnt, i; __u8 free; __u8 f1, f2; __u8 fstat; __u8 *data; int remain; channel_t *ch = &hw->chan[channel]; send_buffer: if (!ch->tx_skb) return; remain = ch->tx_skb->len - ch->tx_idx; if (remain <= 0) return; hfcsmini_selfifo(hw, (channel * 2)); free = (hw->max_z - (read_hfcsmini_stable(hw, A_USAGE))); tcnt = (free >= remain) ? remain : free; fstat = read_hfcsmini(hw, R_ST_RD_STA); f1 = read_hfcsmini_stable(hw, A_F1); f2 = read_hfcsmini(hw, A_F2); fcnt = 0x07 - ((f1 - f2) & 0x07); /* free frame count in tx fifo */ if (debug & DEBUG_HFC_FIFO) { mISDN_debugprint(&ch->inst, "%s channel(%i) len(%i) idx(%i) f1(%i) " "f2(%i) fcnt(%i) tcnt(%i) free(%i) fstat(%i)", __FUNCTION__, channel, ch->tx_skb->len, ch->tx_idx, f1, f2, fcnt, tcnt, free, fstat); } if (free && fcnt && tcnt) { data = ch->tx_skb->data + ch->tx_idx; ch->tx_idx += tcnt; if (debug & DEBUG_HFC_FIFO) { printk(KERN_DEBUG "%s channel(%i) writing: ", hw->card_name, channel); } i = tcnt; /* write data to Fifo */ while (i--) { if (debug & DEBUG_HFC_FIFO) printk("%02x ", *data); write_hfcsmini(hw, A_FIFO_DATA, *data++); } if (debug & DEBUG_HFC_FIFO) printk("\n"); if (ch->tx_idx == ch->tx_skb->len) { if (test_bit(FLG_HDLC, &ch->Flags)) { /* terminate frame */ hfcsmini_inc_f(hw); } else { hfcsmini_selfifo(hw, (channel * 2)); } if (debug & DEBUG_HFC_BTRACE) mISDN_debugprint(&ch->inst, "TX frame channel(%i) completed", channel); if (next_tx_frame(hw, channel)) { if (debug & DEBUG_HFC_BTRACE) mISDN_debugprint(&ch->inst, "channel(%i) has next_tx_frame", channel); if ((free - tcnt) > 8) { if (debug & DEBUG_HFC_BTRACE) mISDN_debugprint(&ch->inst, "channel(%i) continue B-TX immediatetly", channel); goto send_buffer; } } } else { /* tx buffer not complete, but fifo filled to maximum */ hfcsmini_selfifo(hw, (channel * 2)); } } } /****************************/ /* read RX data out of fifo */ /****************************/ void hfcsmini_read_fifo(hfcsmini_hw *hw, __u8 channel) { __u8 f1 = 0, f2 = 0, z1, z2; __u8 fstat = 0; int i; int rcnt; /* read rcnt bytes out of fifo */ __u8 *data; /* new data pointer */ struct sk_buff *skb; /* data buffer for upper layer */ channel_t *ch = &hw->chan[channel]; receive_buffer: hfcsmini_selfifo(hw, (channel * 2) + 1); if (test_bit(FLG_HDLC, &ch->Flags)) { /* hdlc rcnt */ f1 = read_hfcsmini_stable(hw, A_F1); f2 = read_hfcsmini(hw, A_F2); z1 = read_hfcsmini_stable(hw, A_Z1); z2 = read_hfcsmini(hw, A_Z2); fstat = read_hfcsmini(hw, R_ST_RD_STA); rcnt = (z1 - z2) & hw->max_z; if (f1 != f2) rcnt++; } else { /* transparent rcnt */ rcnt = read_hfcsmini_stable(hw, A_USAGE) - 1; f1=f2=z1=z2=0; } if (debug & DEBUG_HFC_FIFO) { if (ch->rx_skb) i = ch->rx_skb->len; else i = 0; mISDN_debugprint(&ch->inst, "reading %i bytes channel(%i) " "irq_cnt(%i) fstat(%i) idx(%i) f1(%i) f2(%i) z1(%i) z2(%i)", rcnt, channel, hw->irq_cnt, fstat, i, f1, f2, z1, z2); } if (rcnt > 0) { if (!ch->rx_skb) { ch->rx_skb = alloc_stack_skb(ch->maxlen + 3, ch->up_headerlen); if (!ch->rx_skb) { printk(KERN_DEBUG "%s: No mem for rx_skb\n", __FUNCTION__); return; } } data = skb_put(ch->rx_skb, rcnt); /* read data from FIFO*/ while (rcnt--) *data++ = read_hfcsmini(hw, A_FIFO_DATA); } else return; if (test_bit(FLG_HDLC, &ch->Flags)) { if (f1 != f2) { hfcsmini_inc_f(hw); /* check minimum frame size */ if (ch->rx_skb->len < 4) { if (debug & DEBUG_HFC_FIFO_ERR) mISDN_debugprint(&ch->inst, "%s: frame in channel(%i) < minimum size", __FUNCTION__, channel); goto read_exit; } /* check crc */ if (ch->rx_skb->data[ch->rx_skb->len - 1]) { if (debug & DEBUG_HFC_FIFO_ERR) mISDN_debugprint(&ch->inst, "%s: channel(%i) CRC-error", __FUNCTION__, channel); goto read_exit; } /* remove cksum */ skb_trim(ch->rx_skb, ch->rx_skb->len - 3); if (ch->rx_skb->len < MISDN_COPY_SIZE) { skb = alloc_stack_skb(ch->rx_skb->len, ch->up_headerlen); if (skb) { memcpy(skb_put(skb, ch->rx_skb->len), ch->rx_skb->data, ch->rx_skb->len); skb_trim(ch->rx_skb, 0); } else { skb = ch->rx_skb; ch->rx_skb = NULL; } } else { skb = ch->rx_skb; ch->rx_skb = NULL; } if ((ch->debug) && (debug & DEBUG_HFC_DTRACE)) { mISDN_debugprint(&ch->inst, "channel(%i) new RX len(%i): ", channel, skb->len); i = 0; printk(" "); while (i < skb->len) printk("%02x ", skb->data[i++]); printk("\n"); } queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, skb); read_exit: if (ch->rx_skb) skb_trim(ch->rx_skb, 0); if (read_hfcsmini_stable(hw, A_USAGE) > 8) { if (debug & DEBUG_HFC_FIFO) mISDN_debugprint(&ch->inst, "%s: channel(%i) continue hfcsmini_read_fifo", __FUNCTION__, channel); goto receive_buffer; } return; } else { hfcsmini_selfifo(hw, (channel * 2) + 1); } } else { /* transparent data */ hfcsmini_selfifo(hw, (channel * 2) + 1); if (ch->rx_skb->len >= 128) { /* deliver transparent data to layer2 */ queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, ch->rx_skb); ch->rx_skb = NULL; } } } /*************************************/ /* bottom half handler for interrupt */ /*************************************/ static void hfcsmini_bh_handler(unsigned long ul_hw) { hfcsmini_hw *hw = (hfcsmini_hw *) ul_hw; reg_r_st_rd_sta state; int i; /* Timer Int */ if (hw->misc_irq.bit.v_ti_irq) { hw->misc_irq.bit.v_ti_irq = 0; /* add Fifo-Fill info into int_s1 bitfield */ hw->fifo_irq.reg |= ((read_hfcsmini(hw, R_FILL) ^ FIFO_MASK_TX) & hw->fifomask); /* Handle TX Fifos */ for (i = 0; i < hw->max_fifo; i++) { if ((1 << (i * 2)) & (hw->fifo_irq.reg)) { hw->fifo_irq.reg &= ~(1 << (i * 2)); if (test_bit(FLG_TX_BUSY, &hw->chan[i].Flags)) hfcsmini_write_fifo(hw, i); } } /* handle NT Timer */ if ((hw->portmode & PORT_MODE_NT) && (hw->portmode & NT_TIMER)) if ((--hw->nt_timer) < 0) s0_new_state(&hw->chan[2]); } /* Handle RX Fifos */ for (i = 0; i < hw->max_fifo; i++) { if ((1 << (i * 2 + 1)) & (hw->fifo_irq.reg)) { hw->fifo_irq.reg &= ~(1 << (i * 2 + 1)); hfcsmini_read_fifo(hw, i); } } /* state machine IRQ */ if (hw->misc_irq.bit.v_st_irq) { hw->misc_irq.bit.v_st_irq = 0; state.reg = read_hfcsmini(hw, R_ST_RD_STA); /* mISDN_debugprint(&dch->inst, "new_l1_state(0x%02x)", state.bit.v_st_sta); */ if (state.bit.v_st_sta != hw->chan[2].state) { hw->chan[2].state = state.bit.v_st_sta; s0_new_state(&hw->chan[2]); } } return; } /*********************/ /* Interrupt handler */ /*********************/ static irqreturn_t hfcsmini_interrupt(int intno, void *dev_id, struct pt_regs *regs) { __u8 fifo_irq, misc_irq; hfcsmini_hw *hw = dev_id; spin_lock(&hw->rlock); if (!(hw->misc_irqmsk.bit.v_irq_en)) { if (!(hw->testirq)) printk(KERN_INFO "%s %s GLOBAL INTERRUPT DISABLED\n", hw->card_name, __FUNCTION__); spin_unlock(&hw->rlock); return IRQ_NONE; } fifo_irq = read_hfcsmini_irq(hw, R_FIFO_IRQ) & hw->fifo_irqmsk.reg; misc_irq = read_hfcsmini_irq(hw, R_MISC_IRQ) & hw->misc_irqmsk.reg; if (!fifo_irq && !misc_irq) { spin_unlock(&hw->rlock); return IRQ_NONE; /* other hardware interrupted */ } hw->irq_cnt++; hw->fifo_irq.reg |= fifo_irq; hw->misc_irq.reg |= misc_irq; /* queue bottom half */ if (!(hw->testirq)) { tasklet_schedule(&hw->tasklet); } spin_unlock(&hw->rlock); return IRQ_HANDLED; } /*************************************/ /* free memory for all used channels */ /*************************************/ void release_channels(hfcsmini_hw * hw) { int i = 0; while (i < MAX_CHAN) { if (hw->chan[i].Flags) { if (debug & DEBUG_HFC_INIT) printk(KERN_DEBUG "%s %s: free channel %d\n", hw->card_name, __FUNCTION__, i); mISDN_freechannel(&hw->chan[i]); mISDN_ctrl(&hw->chan[i].inst, MGR_UNREGLAYER | REQUEST, NULL); } i++; } } /******************************************/ /* Setup Fifo using HDLC_PAR and CON_HDLC */ /******************************************/ void setup_fifo(hfcsmini_hw * hw, int fifo, __u8 hdlcreg, __u8 con_reg, __u8 irq_enable, __u8 enable) { if (enable) /* mark fifo to be 'in use' */ hw->fifomask |= (1 << fifo); else hw->fifomask &= ~(1 << fifo); if (irq_enable) hw->fifo_irqmsk.reg |= (1 << fifo); else hw->fifo_irqmsk.reg &= ~(1 << fifo); write_hfcsmini(hw, R_FIFO_IRQMSK, hw->fifo_irqmsk.reg); hfcsmini_selfifo(hw, fifo); write_hfcsmini(hw, A_HDLC_PAR, hdlcreg); write_hfcsmini(hw, A_CON_HDLC, con_reg); hfcsmini_resetfifo(hw); } /*************************************************/ /* Setup ST interface, enable/disable B-Channels */ /*************************************************/ void setup_st(hfcsmini_hw * hw, __u8 bc, __u8 enable) { if (!((bc == 0) || (bc == 1))) { printk(KERN_INFO "%s %s: ERROR: bc(%i) unvalid!\n", hw->card_name, __FUNCTION__, bc); return; } if (bc) { hw->st_ctrl0.bit.v_b2_en = (enable?1:0); hw->st_ctrl2.bit.v_b2_rx_en = (enable?1:0); } else { hw->st_ctrl0.bit.v_b1_en = (enable?1:0); hw->st_ctrl2.bit.v_b1_rx_en = (enable?1:0); } write_hfcsmini(hw, R_ST_CTRL0, hw->st_ctrl0.reg); write_hfcsmini(hw, R_ST_CTRL2, hw->st_ctrl2.reg); if (debug & DEBUG_HFC_MODE) { printk(KERN_INFO "%s %s: bc(%i) %s, R_ST_CTRL0(0x%02x) R_ST_CTRL2(0x%02x)\n", hw->card_name, __FUNCTION__, bc, enable?"enable":"disable", hw->st_ctrl0.reg, hw->st_ctrl2.reg); } } /*********************************************/ /* (dis-) connect D/B-Channel using protocol */ /*********************************************/ int setup_channel(hfcsmini_hw *hw, __u8 channel, int protocol) { if (test_bit(FLG_BCHANNEL, &hw->chan[channel].Flags)) { if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "channel(%i) protocol %x-->%x", channel, hw->chan[channel].state, protocol); switch (protocol) { case (-1): /* used for init */ hw->chan[channel].state = -1; hw->chan[channel].channel = channel; /* fall trough */ case (ISDN_PID_NONE): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "ISDN_PID_NONE"); if (hw->chan[channel].state == ISDN_PID_NONE) return (0); /* already in idle state */ hw->chan[channel].state = ISDN_PID_NONE; /* B-TX */ setup_fifo(hw, (channel << 1), 0, 0, FIFO_IRQ_OFF, FIFO_DISABLE); /* B-RX */ setup_fifo(hw, (channel << 1) + 1, 0, 0, FIFO_IRQ_OFF, FIFO_DISABLE); setup_st(hw, channel, 0); test_and_clear_bit(FLG_HDLC, &hw->chan[channel].Flags); test_and_clear_bit(FLG_TRANSPARENT, &hw->chan[channel].Flags); break; case (ISDN_PID_L1_B_64TRANS): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "ISDN_PID_L1_B_64TRANS"); /* B-TX */ setup_fifo(hw, (channel << 1), HDLC_PAR_BCH, CON_HDLC_B_TRANS, FIFO_IRQ_OFF, FIFO_ENABLE); /* B-RX */ setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_BCH, CON_HDLC_B_TRANS, FIFO_IRQ_OFF, FIFO_ENABLE); setup_st(hw, channel, 1); hw->chan[channel].state = ISDN_PID_L1_B_64TRANS; test_and_set_bit(FLG_TRANSPARENT, &hw->chan[channel].Flags); break; case (ISDN_PID_L1_B_64HDLC): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "ISDN_PID_L1_B_64HDLC"); /* B-TX */ setup_fifo(hw, (channel << 1), HDLC_PAR_BCH, CON_HDLC_B_HDLC, FIFO_IRQ_OFF, FIFO_ENABLE); /* B-RX */ setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_BCH, CON_HDLC_B_HDLC, FIFO_IRQ_ON, FIFO_ENABLE); setup_st(hw, channel, 1); hw->chan[channel].state = ISDN_PID_L1_B_64HDLC; test_and_set_bit(FLG_HDLC, &hw->chan[channel].Flags); break; default: mISDN_debugprint(&hw->chan[channel].inst, "prot not known %x", protocol); return (-ENOPROTOOPT); } return (0); } if (test_bit(FLG_DCHANNEL, &hw->chan[channel].Flags)) { if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "D channel(%i) protocol(%i)",channel, protocol); /* init the D-channel fifos */ /* D-TX */ setup_fifo(hw, (channel << 1), HDLC_PAR_DCH, CON_HDLC_D_HDLC, FIFO_IRQ_OFF, FIFO_ENABLE); /* D-RX */ setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_DCH, CON_HDLC_D_HDLC, FIFO_IRQ_ON, FIFO_DISABLE); return (0); } printk(KERN_INFO "%s %s ERROR: channel(%i) is NEITHER B nor D !!!\n", hw->card_name, __FUNCTION__, channel); return (-1); } /*****************************************************/ /* register ISDN stack for one HFC-S mini instance */ /* - register all ports and channels */ /* - set param_idx */ /* */ /* channel mapping in mISDN in hw->chan[] */ /* 0=B1, 1=B2, 2=D, 3=PCM */ /*****************************************************/ int init_mISDN_channels(hfcsmini_hw * hw) { int err; int ch; int b; mISDN_pid_t pid; u_long flags; /* clear PCM */ memset(&hw->chan[3], 0, sizeof(channel_t)); /* init D channels */ ch = 2; if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: Registering D-channel, card(%d) protocol(%x)\n", hw->card_name, __FUNCTION__, hw->cardnum, hw->dpid); memset(&hw->chan[ch], 0, sizeof(channel_t)); hw->chan[ch].channel = ch; hw->chan[ch].debug = debug; hw->chan[ch].inst.obj = &hw_mISDNObj; hw->chan[ch].inst.hwlock = &hw->mlock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) hw->chan[ch].inst.class_dev.parent = &hw->pdev->dev; #else hw->chan[ch].inst.class_dev.dev = &hw->pdev->dev; #endif mISDN_init_instance(&hw->chan[ch].inst, &hw_mISDNObj, hw, hfcsmini_l2l1); hw->chan[ch].inst.pid.layermask = ISDN_LAYER(0); sprintf(hw->chan[ch].inst.name, "%s", hw->card_name); err = mISDN_initchannel(&hw->chan[ch], MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); if (err) goto free_channels; hw->chan[ch].hw = hw; /* init B channels */ for (b = 0; b < 2; b++) { if (debug & DEBUG_HFC_INIT) printk(KERN_DEBUG "%s %s: Registering B-channel, card(%d) " "ch(%d)\n", hw->card_name, __FUNCTION__, hw->cardnum, b); memset(&hw->chan[b], 0, sizeof(channel_t)); hw->chan[b].channel = b; hw->chan[b].debug = debug; mISDN_init_instance(&hw->chan[b].inst, &hw_mISDNObj, hw, hfcsmini_l2l1); hw->chan[b].inst.pid.layermask = ISDN_LAYER(0); hw->chan[b].inst.hwlock = &hw->mlock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) hw->chan[b].inst.class_dev.parent = &hw->pdev->dev; #else hw->chan[b].inst.class_dev.dev = &hw->pdev->dev; #endif sprintf(hw->chan[b].inst.name, "%s B%d", hw->chan[ch].inst.name, b + 1); if (mISDN_initchannel(&hw->chan[b], MSK_INIT_BCHANNEL, MAX_DATA_MEM)) { err = -ENOMEM; goto free_channels; } hw->chan[b].hw = hw; } mISDN_set_dchannel_pid(&pid, hw->dpid, layermask[hw->param_idx]); /* set protocol for NT/TE */ if (hw->portmode & PORT_MODE_NT) { /* NT-mode */ hw->portmode |= NT_TIMER; hw->nt_timer = 0; hw->chan[ch].inst.pid.protocol[0] = ISDN_PID_L0_NT_S0; hw->chan[ch].inst.pid.protocol[1] = ISDN_PID_L1_NT_S0; pid.protocol[0] = ISDN_PID_L0_NT_S0; pid.protocol[1] = ISDN_PID_L1_NT_S0; hw->chan[ch].inst.pid.layermask |= ISDN_LAYER(1); pid.layermask |= ISDN_LAYER(1); if (layermask[hw->param_idx] & ISDN_LAYER(2)) pid.protocol[2] = ISDN_PID_L2_LAPD_NET; } else { /* TE-mode */ hw->portmode |= PORT_MODE_TE; hw->chan[ch].inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; pid.protocol[0] = ISDN_PID_L0_TE_S0; } if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: registering Stack\n", hw->card_name, __FUNCTION__); /* register stack */ err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &hw->chan[ch].inst); if (err) { printk(KERN_ERR "%s %s: MGR_NEWSTACK | REQUEST err(%d)\n", hw->card_name, __FUNCTION__, err); goto free_channels; } hw->chan[ch].state = 0; for (b = 0; b < 2; b++) { err = mISDN_ctrl(hw->chan[ch].inst.st, MGR_NEWSTACK | REQUEST, &hw->chan[b].inst); if (err) { printk(KERN_ERR "%s %s: MGR_ADDSTACK bchan error %d\n", hw->card_name, __FUNCTION__, err); goto free_stack; } } err = mISDN_ctrl(hw->chan[ch].inst.st, MGR_SETSTACK | REQUEST, &pid); if (err) { printk(KERN_ERR "%s %s: MGR_SETSTACK REQUEST dch err(%d)\n", hw->card_name, __FUNCTION__, err); mISDN_ctrl(hw->chan[ch].inst.st, MGR_DELSTACK | REQUEST, NULL); goto free_stack; } setup_channel(hw, hw->chan[ch].channel, -1); for (b = 0; b < 2; b++) { setup_channel(hw, b, -1); } /* delay some time */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ mISDN_ctrl(hw->chan[ch].inst.st, MGR_CTRLREADY | INDICATION, NULL); return (0); free_stack: mISDN_ctrl(hw->chan[ch].inst.st, MGR_DELSTACK | REQUEST, NULL); free_channels: spin_lock_irqsave(&hw_mISDNObj.lock, flags); release_channels(hw); list_del(&hw->list); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); return (err); } /********************************/ /* parse module paramaters like */ /* NE/TE and S0/Up port mode */ /********************************/ void parse_module_params(hfcsmini_hw * hw) { /* D-Channel protocol: (2=DSS1) */ hw->dpid = (protocol[hw->param_idx] & 0x0F); if (hw->dpid == 0) { printk(KERN_INFO "%s %s: WARNING: wrong value for protocol[%i], " "assuming 0x02 (DSS1)...\n", hw->card_name, __FUNCTION__, hw->param_idx); hw->dpid = 0x02; } /* Line Interface TE or NT */ if (protocol[hw->param_idx] & 0x10) hw->portmode |= PORT_MODE_NT; else hw->portmode |= PORT_MODE_TE; /* Line Interface in S0 or Up mode */ if (!(protocol[hw->param_idx] & 0x40)) hw->portmode |= PORT_MODE_BUS_MASTER; /* link B-channel loop */ if (protocol[hw->param_idx] & 0x80) hw->portmode |= PORT_MODE_LOOP; if (debug & DEBUG_HFC_INIT) printk ("%s %s: protocol[%i]=0x%02x, dpid=%d,%s bus-mode:%s %s\n", hw->card_name, __FUNCTION__, hw->param_idx, protocol[hw->param_idx], hw->dpid, (hw->portmode & PORT_MODE_TE)?"TE":"NT", (hw->portmode & PORT_MODE_BUS_MASTER)?"MASTER":"SLAVE", (hw->portmode & PORT_MODE_LOOP)?"B-LOOP":"" ); } /*****************************************/ /* initialise the HFC-S mini ISDN Chip */ /* return 0 on success. */ /*****************************************/ int init_hfcsmini(hfcsmini_hw * hw) { int err = 0; reg_r_fifo_thres threshold; #if HFCBRIDGE == BRIDGE_HFCPCI err = init_pci_bridge(hw); if (err) return(-ENODEV); #endif hw->chip_id.reg = read_hfcsmini(hw, R_CHIP_ID); if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s ChipID: 0x%x\n", hw->card_name, __FUNCTION__, hw->chip_id.bit.v_chip_id); switch (hw->chip_id.bit.v_chip_id) { case CHIP_ID_HFCSMINI: hw->max_fifo = 4; hw->ti.reg = 5; /* 8 ms timer interval */ hw->max_z = 0x7F; break; default: err = -ENODEV; } if (err) { if (debug & DEBUG_HFC_INIT) printk(KERN_ERR "%s %s: unkown Chip ID 0x%x\n", hw->card_name, __FUNCTION__, hw->chip_id.bit.v_chip_id); return (err); } /* reset card */ write_hfcsmini(hw, R_CIRM, M_SRES); /* Reset On */ udelay(10); write_hfcsmini(hw, R_CIRM, 0); /* Reset Off */ /* wait until fifo controller init sequence is finished */ hfcsmini_waitbusy(hw); /* reset D-Channel S/T controller */ write_hfcsmini(hw, R_ST_CTRL1, M_D_RES); if (hw->portmode & PORT_MODE_TE) { /* TE mode */ hw->st_ctrl0.reg = 0; write_hfcsmini(hw, R_ST_CLK_DLY, (M1_ST_CLK_DLY* 0xF)); write_hfcsmini(hw, R_ST_CTRL1, 0); } else { /* NT mode */ hw->st_ctrl0.reg = 4; write_hfcsmini(hw, R_ST_CLK_DLY, ((M1_ST_SMPL * 0x6) | (M1_ST_CLK_DLY*0xC))); write_hfcsmini(hw, R_ST_CTRL1, M_E_IGNO); } hw->st_ctrl2.reg = 0; write_hfcsmini(hw, R_ST_CTRL0, hw->st_ctrl0.reg); write_hfcsmini(hw, R_ST_CTRL2, hw->st_ctrl2.reg); /* HFC Master/Slave Mode */ if (hw->portmode & PORT_MODE_BUS_MASTER) hw->pcm_md0.bit.v_pcm_md = 1; else hw->pcm_md0.bit.v_pcm_md = 0; write_hfcsmini(hw, R_PCM_MD0, hw->pcm_md0.reg); write_hfcsmini(hw, R_PCM_MD1, 0); write_hfcsmini(hw, R_PCM_MD2, 0); /* setup threshold register */ threshold.bit.v_thres_tx = (HFCSMINI_TX_THRESHOLD / 8); threshold.bit.v_thres_rx = (HFCSMINI_RX_THRESHOLD / 8); write_hfcsmini(hw, R_FIFO_THRES, threshold.reg); /* test timer irq */ enable_interrupts(hw); mdelay(((1 << hw->ti.reg)+1)*2); hw->testirq = 0; if (hw->irq_cnt) { printk(KERN_INFO "%s %s: test IRQ OK, irq_cnt %i\n", hw->card_name, __FUNCTION__, hw->irq_cnt); disable_interrupts(hw); return (0); } else { if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: ERROR getting IRQ (irq_cnt %i)\n", hw->card_name, __FUNCTION__, hw->irq_cnt); disable_interrupts(hw); free_irq(hw->irq, hw); return (-EIO); } } /*****************************************************/ /* disable all interrupts by disabling M_GLOB_IRQ_EN */ /*****************************************************/ void disable_interrupts(hfcsmini_hw * hw) { u_long flags; if (debug & DEBUG_HFC_IRQ) printk(KERN_INFO "%s %s\n", hw->card_name, __FUNCTION__); spin_lock_irqsave(&hw->mlock, flags); hw->fifo_irqmsk.reg = 0; hw->misc_irqmsk.reg = 0; write_hfcsmini(hw, R_FIFO_IRQMSK, hw->fifo_irqmsk.reg); write_hfcsmini(hw, R_MISC_IRQMSK, hw->misc_irqmsk.reg); spin_unlock_irqrestore(&hw->mlock, flags); } /******************************************/ /* start interrupt and set interrupt mask */ /******************************************/ void enable_interrupts(hfcsmini_hw * hw) { u_long flags; if (debug & DEBUG_HFC_IRQ) printk(KERN_INFO "%s %s\n", hw->card_name, __FUNCTION__); spin_lock_irqsave(&hw->mlock, flags); hw->fifo_irq.reg = 0; hw->misc_irq.reg = 0; write_hfcsmini(hw, R_TI, hw->ti.reg); /* D-RX and D-TX interrupts enable */ hw->fifo_irqmsk.bit.v_fifo2_tx_irqmsk = 1; hw->fifo_irqmsk.bit.v_fifo2_rx_irqmsk = 1; /* clear pending ints */ if (read_hfcsmini(hw, R_FIFO_IRQ)); if (read_hfcsmini(hw, R_MISC_IRQ)); /* Finally enable IRQ output */ hw->misc_irqmsk.bit.v_st_irqmsk = 1; /* enable L1-state change irq */ hw->misc_irqmsk.bit.v_ti_irqmsk = 1; /* enable timer irq */ hw->misc_irqmsk.bit.v_irq_en = 1; /* IRQ global enable */ write_hfcsmini(hw, R_MISC_IRQMSK, hw->misc_irqmsk.reg); spin_unlock_irqrestore(&hw->mlock, flags); return; } /**************************************/ /* initialise the HFC-S mini hardware */ /* return 0 on success. */ /**************************************/ static int __devinit setup_instance(hfcsmini_hw * hw) { int err; hfcsmini_hw *previous_hw; u_long flags; if (debug & DEBUG_HFC_INIT) printk(KERN_WARNING "%s %s\n", hw->card_name, __FUNCTION__); spin_lock_init(&hw->mlock); spin_lock_init(&hw->rlock); tasklet_init(&hw->tasklet, hfcsmini_bh_handler, (unsigned long) hw); /* search previous instances to index protocol[] array */ list_for_each_entry(previous_hw, &hw_mISDNObj.ilist, list) hw->param_idx++; /* add this instance to hardware list */ spin_lock_irqsave(&hw_mISDNObj.lock, flags); list_add_tail(&hw->list, &hw_mISDNObj.ilist); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); /* init interrupt engine */ hw->testirq = 1; if (debug & DEBUG_HFC_INIT) printk(KERN_WARNING "%s %s: requesting IRQ %d\n", hw->card_name, __FUNCTION__, hw->irq); if (request_irq(hw->irq, (void*)hfcsmini_interrupt, SA_SHIRQ, "HFC-S mini", hw)) { printk(KERN_WARNING "%s %s: couldn't get interrupt %d\n", hw->card_name, __FUNCTION__, hw->irq); hw->irq = 0; err = -EIO; goto out; } parse_module_params(hw); err = init_hfcsmini(hw); if (err) goto out; /* register all channels at ISDN procol stack */ err = init_mISDN_channels(hw); if (err) goto out; /* delay some time to have mISDN initialazed complete */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ /* Clear already pending ints */ if (read_hfcsmini(hw, R_FIFO_IRQ)); enable_interrupts(hw); /* enable state machine */ write_hfcsmini(hw, R_ST_RD_STA, 0x0); return(0); out: return (err); } #if HFCBRIDGE == BRIDGE_HFCPCI /***********************/ /* PCI Bridge ID List */ /***********************/ static struct pci_device_id hfcsmini_ids[] = { {.vendor = PCI_VENDOR_ID_CCD, .device = 0xA001, .subvendor = PCI_VENDOR_ID_CCD, .subdevice = 0xFFFF, .driver_data = (unsigned long) &((hfcsmini_param) {0xFF, "HFC-S mini Evaluation Board"}), }, {} }; /******************************/ /* initialise the PCI Bridge */ /* return 0 on success. */ /******************************/ int init_pci_bridge(hfcsmini_hw * hw) { outb(0x58, hw->iobase + 4); /* ID-register of bridge */ if ((inb(hw->iobase) & 0xf0) != 0x30) { printk(KERN_INFO "%s %s: chip ID for PCI bridge invalid\n", hw->card_name, __FUNCTION__); release_region(hw->iobase, 8); return(-EIO); } outb(0x60, hw->iobase + 4); /* CIRM register of bridge */ outb(0x07, hw->iobase); /* 15 PCI clocks aux access */ /* reset sequence */ outb(2, hw->iobase + 3); /* A0 = 1, reset = 0 (active) */ udelay(10); outb(6, hw->iobase + 3); /* A0 = 1, reset = 1 (inactive) */ outb(0, hw->iobase + 1); /* write dummy register number */ /* wait until reset sequence finished, can be redefined after schematic review */ mdelay(300); return (0); } /************************/ /* release single card */ /************************/ static void release_card(hfcsmini_hw * hw) { u_long flags; disable_interrupts(hw); free_irq(hw->irq, hw); /* wait for pending tasklet to finish */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ spin_lock_irqsave(&hw_mISDNObj.lock, flags); release_channels(hw); list_del(&hw->list); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); kfree(hw); } /*****************************************/ /* PCI hotplug interface: probe new card */ /*****************************************/ static int __devinit hfcsmini_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { hfcsmini_param *driver_data = (hfcsmini_param *) ent->driver_data; hfcsmini_hw *hw; int err = -ENOMEM; if (!(hw = kmalloc(sizeof(hfcsmini_hw), GFP_ATOMIC))) { printk(KERN_ERR "%s %s: No kmem for HFC-S mini card\n", hw->card_name, __FUNCTION__); return (err); } memset(hw, 0, sizeof(hfcsmini_hw)); hw->pdev = pdev; err = pci_enable_device(pdev); if (err) goto out; hw->cardnum = card_cnt; sprintf(hw->card_name, "%s_%d", DRIVER_NAME, hw->cardnum); printk(KERN_INFO "%s %s: adapter '%s' found on PCI bus %02x dev %02x\n", hw->card_name, __FUNCTION__, driver_data->device_name, pdev->bus->number, pdev->devfn); hw->driver_data = *driver_data; hw->irq = pdev->irq; hw->iobase = (u_int) get_pcibase(pdev, 0); if (!hw->iobase) { printk(KERN_WARNING "%s no IO for PCI card found\n", hw->card_name); return(-EIO); } if (!request_region(hw->iobase, 8, "hfcmulti")) { printk(KERN_WARNING "%s failed to request " "address space at 0x%04x\n", hw->card_name, hw->iobase); } printk(KERN_INFO "%s defined at IOBASE 0x%#x IRQ %d HZ %d\n", hw->card_name, (u_int) hw->iobase, hw->irq, HZ); /* enable IO */ pci_write_config_word(pdev, PCI_COMMAND, 0x01); pci_set_drvdata(pdev, hw); err = setup_instance(hw); if (!err) { card_cnt++; return (0); } else { goto out; } out: kfree(hw); return (err); }; /**************************************/ /* PCI hotplug interface: remove card */ /**************************************/ static void __devexit hfcsmini_pci_remove(struct pci_dev *pdev) { hfcsmini_hw *hw = pci_get_drvdata(pdev); printk(KERN_INFO "%s %s: removing card\n", hw->card_name, __FUNCTION__); release_card(hw); card_cnt--; pci_disable_device(pdev); return; }; /*****************************/ /* Module PCI driver exports */ /*****************************/ static struct pci_driver hfcsmini_driver = { name:DRIVER_NAME, probe:hfcsmini_pci_probe, remove:__devexit_p(hfcsmini_pci_remove), id_table:hfcsmini_ids, }; MODULE_DEVICE_TABLE(pci, hfcsmini_ids); #endif /***************/ /* Module init */ /***************/ static int __init hfcsmini_init(void) { int err; printk(KERN_INFO "HFC-S mini: %s driver Rev. %s (debug=%i)\n", __FUNCTION__, mISDN_getrev(hfcsmini_rev), debug); #ifdef MODULE hw_mISDNObj.owner = THIS_MODULE; #endif INIT_LIST_HEAD(&hw_mISDNObj.ilist); spin_lock_init(&hw_mISDNObj.lock); hw_mISDNObj.name = DRIVER_NAME; hw_mISDNObj.own_ctrl = hfcsmini_manager; hw_mISDNObj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0; hw_mISDNObj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0; hw_mISDNObj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; hw_mISDNObj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; card_cnt = 0; if ((err = mISDN_register(&hw_mISDNObj))) { printk(KERN_ERR "HFC-S mini: can't register HFC-S mini, error(%d)\n", err); goto out; } #if HFCBRIDGE == BRIDGE_HFCPCI err = pci_register_driver(&hfcsmini_driver); if (err < 0) { goto out; } #if !defined(CONFIG_HOTPLUG) if (err == 0) { err = -ENODEV; pci_unregister_driver(&hfcsmini_driver); goto out; } #endif #endif mISDN_module_register(THIS_MODULE); printk(KERN_INFO "HFC-S mini: %d cards installed\n", card_cnt); return 0; out: return (err); } static void __exit hfcsmini_cleanup(void) { int err; mISDN_module_unregister(THIS_MODULE); #if HFCBRIDGE == BRIDGE_HFCPCI pci_unregister_driver(&hfcsmini_driver); #endif if ((err = mISDN_unregister(&hw_mISDNObj))) { printk(KERN_ERR "HFC-S mini: can't unregister HFC-S mini, error(%d)\n", err); } printk(KERN_INFO "%s: driver removed\n", __FUNCTION__); } module_init(hfcsmini_init); module_exit(hfcsmini_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/hfcs_mini.h0000755000000000000500000001333011135651702020502 0ustar rootsrc/* $Id: hfcs_mini.h,v 1.2 2006/03/06 12:58:31 keil Exp $ * * 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__ */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_tones.c0000644000000000000500000003511711110524073020531 0ustar rootsrc/* $Id: dsp_tones.c,v 1.8 2007/03/27 15:06:29 jolly Exp $ * * Audio support data for ISDN4Linux. * * Copyright Andreas Eversberg (jolly@eversberg.eu) * * 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" #define DATA_S sample_silence #define SIZE_S &sizeof_silence #define DATA_GA sample_german_all #define SIZE_GA &sizeof_german_all #define DATA_GO sample_german_old #define SIZE_GO &sizeof_german_old #define DATA_DT sample_american_dialtone #define SIZE_DT &sizeof_american_dialtone #define DATA_RI sample_american_ringing #define SIZE_RI &sizeof_american_ringing #define DATA_BU sample_american_busy #define SIZE_BU &sizeof_american_busy #define DATA_S1 sample_special1 #define SIZE_S1 &sizeof_special1 #define DATA_S2 sample_special2 #define SIZE_S2 &sizeof_special2 #define DATA_S3 sample_special3 #define SIZE_S3 &sizeof_special3 /***************/ /* tones loops */ /***************/ /* all tones are alaw encoded */ /* the last sample+1 is in phase with the first sample. the error is low */ static u8 sample_german_all[]= { 0x80,0xab,0x81,0x6d,0xfd,0xdd,0x5d,0x9d, 0x4d,0xd1,0x89,0x88,0xd0,0x4c,0x9c,0x5c, 0xdc,0xfc,0x6c, 0x80,0xab,0x81,0x6d,0xfd,0xdd,0x5d,0x9d, 0x4d,0xd1,0x89,0x88,0xd0,0x4c,0x9c,0x5c, 0xdc,0xfc,0x6c, 0x80,0xab,0x81,0x6d,0xfd,0xdd,0x5d,0x9d, 0x4d,0xd1,0x89,0x88,0xd0,0x4c,0x9c,0x5c, 0xdc,0xfc,0x6c, 0x80,0xab,0x81,0x6d,0xfd,0xdd,0x5d,0x9d, 0x4d,0xd1,0x89,0x88,0xd0,0x4c,0x9c,0x5c, 0xdc,0xfc,0x6c, }; static u32 sizeof_german_all = sizeof(sample_german_all); static u8 sample_german_old[]= { 0xec,0x68,0xe1,0x6d,0x6d,0x91,0x51,0xed, 0x6d,0x01,0x1e,0x10,0x0c,0x90,0x60,0x70, 0x8c, 0xec,0x68,0xe1,0x6d,0x6d,0x91,0x51,0xed, 0x6d,0x01,0x1e,0x10,0x0c,0x90,0x60,0x70, 0x8c, 0xec,0x68,0xe1,0x6d,0x6d,0x91,0x51,0xed, 0x6d,0x01,0x1e,0x10,0x0c,0x90,0x60,0x70, 0x8c, 0xec,0x68,0xe1,0x6d,0x6d,0x91,0x51,0xed, 0x6d,0x01,0x1e,0x10,0x0c,0x90,0x60,0x70, 0x8c, }; static u32 sizeof_german_old = sizeof(sample_german_old); static u8 sample_american_dialtone[]= { 0x2a,0x18,0x90,0x6c,0x4c,0xbc,0x4c,0x6c, 0x10,0x58,0x32,0xb9,0x31,0x2d,0x8d,0x0d, 0x8d,0x2d,0x31,0x99,0x0f,0x28,0x60,0xf0, 0xd0,0x50,0xd0,0x30,0x60,0x08,0x8e,0x67, 0x09,0x19,0x21,0xe1,0xd9,0xb9,0x29,0x67, 0x83,0x02,0xce,0xbe,0xee,0x1a,0x1b,0xef, 0xbf,0xcf,0x03,0x82,0x66,0x28,0xb8,0xd8, 0xe0,0x20,0x18,0x08,0x66,0x8f,0x09,0x61, 0x31,0xd1,0x51,0xd1,0xf1,0x61,0x29,0x0e, 0x98,0x30,0x2c,0x8c,0x0c,0x8c,0x2c,0x30, 0xb8,0x33,0x59,0x11,0x6d,0x4d,0xbd,0x4d, 0x6d,0x91,0x19, }; static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); static u8 sample_american_ringing[]= { 0x2a,0xe0,0xac,0x0c,0xbc,0x4c,0x8c,0x90, 0x48,0xc7,0xc1,0xed,0xcd,0x4d,0xcd,0xed, 0xc1,0xb7,0x08,0x30,0xec,0xcc,0xcc,0x8c, 0x10,0x58,0x1a,0x99,0x71,0xed,0x8d,0x8d, 0x2d,0x41,0x89,0x9e,0x20,0x70,0x2c,0xec, 0x2c,0x70,0x20,0x86,0x77,0xe1,0x31,0x11, 0xd1,0xf1,0x81,0x09,0xa3,0x56,0x58,0x00, 0x40,0xc0,0x60,0x38,0x46,0x43,0x57,0x39, 0xd9,0x59,0x99,0xc9,0x77,0x2f,0x2e,0xc6, 0xd6,0x28,0xd6,0x36,0x26,0x2e,0x8a,0xa3, 0x43,0x63,0x4b,0x4a,0x62,0x42,0xa2,0x8b, 0x2f,0x27,0x37,0xd7,0x29,0xd7,0xc7,0x2f, 0x2e,0x76,0xc8,0x98,0x58,0xd8,0x38,0x56, 0x42,0x47,0x39,0x61,0xc1,0x41,0x01,0x59, 0x57,0xa2,0x08,0x80,0xf0,0xd0,0x10,0x30, 0xe0,0x76,0x87,0x21,0x71,0x2d,0xed,0x2d, 0x71,0x21,0x9f,0x88,0x40,0x2c,0x8c,0x8c, 0xec,0x70,0x98,0x1b,0x59,0x11,0x8d,0xcd, 0xcd,0xed,0x31,0x09,0xb6,0xc0,0xec,0xcc, 0x4c,0xcc,0xec,0xc0,0xc6,0x49,0x91,0x8d, 0x4d,0xbd,0x0d,0xad,0xe1, }; static u32 sizeof_american_ringing = sizeof(sample_american_ringing); static u8 sample_american_busy[]= { 0x2a,0x00,0x6c,0x4c,0x4c,0x6c,0xb0,0x66, 0x99,0x11,0x6d,0x8d,0x2d,0x41,0xd7,0x96, 0x60,0xf0,0x70,0x40,0x58,0xf6,0x53,0x57, 0x09,0x89,0xd7,0x5f,0xe3,0x2a,0xe3,0x5f, 0xd7,0x89,0x09,0x57,0x53,0xf6,0x58,0x40, 0x70,0xf0,0x60,0x96,0xd7,0x41,0x2d,0x8d, 0x6d,0x11,0x99,0x66,0xb0,0x6c,0x4c,0x4c, 0x6c,0x00,0x2a,0x01,0x6d,0x4d,0x4d,0x6d, 0xb1,0x67,0x98,0x10,0x6c,0x8c,0x2c,0x40, 0xd6,0x97,0x61,0xf1,0x71,0x41,0x59,0xf7, 0x52,0x56,0x08,0x88,0xd6,0x5e,0xe2,0x2a, 0xe2,0x5e,0xd6,0x88,0x08,0x56,0x52,0xf7, 0x59,0x41,0x71,0xf1,0x61,0x97,0xd6,0x40, 0x2c,0x8c,0x6c,0x10,0x98,0x67,0xb1,0x6d, 0x4d,0x4d,0x6d,0x01, }; static u32 sizeof_american_busy = sizeof(sample_american_busy); static u8 sample_special1[]= { 0x2a,0x2c,0xbc,0x6c,0xd6,0x71,0xbd,0x0d, 0xd9,0x80,0xcc,0x4c,0x40,0x39,0x0d,0xbd, 0x11,0x86,0xec,0xbc,0xec,0x0e,0x51,0xbd, 0x8d,0x89,0x30,0x4c,0xcc,0xe0,0xe1,0xcd, 0x4d,0x31,0x88,0x8c,0xbc,0x50,0x0f,0xed, 0xbd,0xed,0x87,0x10,0xbc,0x0c,0x38,0x41, 0x4d,0xcd,0x81,0xd8,0x0c,0xbc,0x70,0xd7, 0x6d,0xbd,0x2d, }; static u32 sizeof_special1 = sizeof(sample_special1); static u8 sample_special2[]= { 0x2a,0xcc,0x8c,0xd7,0x4d,0x2d,0x18,0xbc, 0x10,0xc1,0xbd,0xc1,0x10,0xbc,0x18,0x2d, 0x4d,0xd7,0x8c,0xcc,0x2a,0xcd,0x8d,0xd6, 0x4c,0x2c,0x19,0xbd,0x11,0xc0,0xbc,0xc0, 0x11,0xbd,0x19,0x2c,0x4c,0xd6,0x8d,0xcd, 0x2a,0xcc,0x8c,0xd7,0x4d,0x2d,0x18,0xbc, 0x10,0xc1,0xbd,0xc1,0x10,0xbc,0x18,0x2d, 0x4d,0xd7,0x8c,0xcc,0x2a,0xcd,0x8d,0xd6, 0x4c,0x2c,0x19,0xbd,0x11,0xc0,0xbc,0xc0, 0x11,0xbd,0x19,0x2c,0x4c,0xd6,0x8d,0xcd, }; static u32 sizeof_special2 = sizeof(sample_special2); static u8 sample_special3[]= { 0x2a,0xbc,0x18,0xcd,0x11,0x2c,0x8c,0xc1, 0x4d,0xd6,0xbc,0xd6,0x4d,0xc1,0x8c,0x2c, 0x11,0xcd,0x18,0xbc,0x2a,0xbd,0x19,0xcc, 0x10,0x2d,0x8d,0xc0,0x4c,0xd7,0xbd,0xd7, 0x4c,0xc0,0x8d,0x2d,0x10,0xcc,0x19,0xbd, 0x2a,0xbc,0x18,0xcd,0x11,0x2c,0x8c,0xc1, 0x4d,0xd6,0xbc,0xd6,0x4d,0xc1,0x8c,0x2c, 0x11,0xcd,0x18,0xbc,0x2a,0xbd,0x19,0xcc, 0x10,0x2d,0x8d,0xc0,0x4c,0xd7,0xbd,0xd7, 0x4c,0xc0,0x8d,0x2d,0x10,0xcc,0x19,0xbd, }; static u32 sizeof_special3 = sizeof(sample_special3); static u8 sample_silence[]= { 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, }; static u32 sizeof_silence = sizeof(sample_silence); struct tones_samples { u32 *len; u8 *data; }; static struct tones_samples samples[] = { {&sizeof_german_all, sample_german_all}, {&sizeof_german_old, sample_german_old}, {&sizeof_american_dialtone, sample_american_dialtone}, {&sizeof_american_ringing, sample_american_ringing}, {&sizeof_american_busy, sample_american_busy}, {&sizeof_special1, sample_special1}, {&sizeof_special2, sample_special2}, {&sizeof_special3, sample_special3}, {NULL, NULL}, }; /*********************************** * generate ulaw from alaw samples * ***********************************/ void dsp_audio_generate_ulaw_samples(void) { int i,j; i = 0; while(samples[i].len) { j = 0; while(j < (*samples[i].len)) { samples[i].data[j] = dsp_audio_alaw_to_ulaw[samples[i].data[j]]; j++; } i++; } } /**************************** * tone sequence definition * ****************************/ struct pattern { int tone; u8 *data[10]; u32 *siz[10]; u32 seq[10]; } pattern[] = { {TONE_GERMAN_DIALTONE, {DATA_GA,0,0,0,0,0,0,0,0,0}, {SIZE_GA,0,0,0,0,0,0,0,0,0}, {1900,0,0,0,0,0,0,0,0,0}}, {TONE_GERMAN_OLDDIALTONE, {DATA_GO,0,0,0,0,0,0,0,0,0}, {SIZE_GO,0,0,0,0,0,0,0,0,0}, {1998,0,0,0,0,0,0,0,0,0}}, {TONE_AMERICAN_DIALTONE, {DATA_DT,0,0,0,0,0,0,0,0,0}, {SIZE_DT,0,0,0,0,0,0,0,0,0}, {8000,0,0,0,0,0,0,0,0,0}}, {TONE_GERMAN_DIALPBX, {DATA_GA,DATA_S,DATA_GA,DATA_S,DATA_GA,DATA_S,0,0,0,0}, {SIZE_GA,SIZE_S,SIZE_GA,SIZE_S,SIZE_GA,SIZE_S,0,0,0,0}, {2000,2000,2000,2000,2000,12000,0,0,0,0}}, {TONE_GERMAN_OLDDIALPBX, {DATA_GO,DATA_S,DATA_GO,DATA_S,DATA_GO,DATA_S,0,0,0,0}, {SIZE_GO,SIZE_S,SIZE_GO,SIZE_S,SIZE_GO,SIZE_S,0,0,0,0}, {2000,2000,2000,2000,2000,12000,0,0,0,0}}, {TONE_AMERICAN_DIALPBX, {DATA_DT,DATA_S,DATA_DT,DATA_S,DATA_DT,DATA_S,0,0,0,0}, {SIZE_DT,SIZE_S,SIZE_DT,SIZE_S,SIZE_DT,SIZE_S,0,0,0,0}, {2000,2000,2000,2000,2000,12000,0,0,0,0}}, {TONE_GERMAN_RINGING, {DATA_GA,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_GA,SIZE_S,0,0,0,0,0,0,0,0}, {8000,32000,0,0,0,0,0,0,0,0}}, {TONE_GERMAN_OLDRINGING, {DATA_GO,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_GO,SIZE_S,0,0,0,0,0,0,0,0}, {8000,40000,0,0,0,0,0,0,0,0}}, {TONE_AMERICAN_RINGING, {DATA_RI,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_RI,SIZE_S,0,0,0,0,0,0,0,0}, {8000,32000,0,0,0,0,0,0,0,0}}, {TONE_GERMAN_RINGPBX, {DATA_GA,DATA_S,DATA_GA,DATA_S,0,0,0,0,0,0}, {SIZE_GA,SIZE_S,SIZE_GA,SIZE_S,0,0,0,0,0,0}, {4000,4000,4000,28000,0,0,0,0,0,0}}, {TONE_GERMAN_OLDRINGPBX, {DATA_GO,DATA_S,DATA_GO,DATA_S,0,0,0,0,0,0}, {SIZE_GO,SIZE_S,SIZE_GO,SIZE_S,0,0,0,0,0,0}, {4000,4000,4000,28000,0,0,0,0,0,0}}, {TONE_AMERICAN_RINGPBX, {DATA_RI,DATA_S,DATA_RI,DATA_S,0,0,0,0,0,0}, {SIZE_RI,SIZE_S,SIZE_RI,SIZE_S,0,0,0,0,0,0}, {4000,4000,4000,28000,0,0,0,0,0,0}}, {TONE_GERMAN_BUSY, {DATA_GA,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_GA,SIZE_S,0,0,0,0,0,0,0,0}, {4000,4000,0,0,0,0,0,0,0,0}}, {TONE_GERMAN_OLDBUSY, {DATA_GO,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_GO,SIZE_S,0,0,0,0,0,0,0,0}, {1000,5000,0,0,0,0,0,0,0,0}}, {TONE_AMERICAN_BUSY, {DATA_BU,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_BU,SIZE_S,0,0,0,0,0,0,0,0}, {4000,4000,0,0,0,0,0,0,0,0}}, {TONE_GERMAN_HANGUP, {DATA_GA,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_GA,SIZE_S,0,0,0,0,0,0,0,0}, {4000,4000,0,0,0,0,0,0,0,0}}, {TONE_GERMAN_OLDHANGUP, {DATA_GO,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_GO,SIZE_S,0,0,0,0,0,0,0,0}, {1000,5000,0,0,0,0,0,0,0,0}}, {TONE_AMERICAN_HANGUP, {DATA_DT,0,0,0,0,0,0,0,0,0}, {SIZE_DT,0,0,0,0,0,0,0,0,0}, {8000,0,0,0,0,0,0,0,0,0}}, {TONE_SPECIAL_INFO, {DATA_S1,DATA_S2,DATA_S3,DATA_S,0,0,0,0,0,0}, {SIZE_S1,SIZE_S2,SIZE_S3,SIZE_S,0,0,0,0,0,0}, {2666,2666,2666,8002,0,0,0,0,0,0}}, {TONE_GERMAN_GASSENBESETZT, {DATA_GA,DATA_S,0,0,0,0,0,0,0,0}, {SIZE_GA,SIZE_S,0,0,0,0,0,0,0,0}, {2000,2000,0,0,0,0,0,0,0,0}}, {TONE_GERMAN_AUFSCHALTTON, {DATA_GO,DATA_S,DATA_GO,DATA_S,0,0,0,0,0,0}, {SIZE_GO,SIZE_S,SIZE_GO,SIZE_S,0,0,0,0,0,0}, {1000,5000,1000,17000,0,0,0,0,0,0}}, {0, {0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0}}, }; /****************** * copy tone data * ******************/ /* an sk_buff is generated from the number of samples needed. * the count will be changed and may begin from 0 each pattern period. * the clue is to precalculate the pointers and legths to use only one * memcpy per function call, or two memcpy if the tone sequence changes. * * pattern - the type of the pattern * count - the sample from the beginning of the pattern (phase) * len - the number of bytes * * return - the sk_buff with the sample * * if tones has finished (e.g. knocking tone), dsp->tones is turned off */ void dsp_tone_copy(dsp_t *dsp, u8 *data, int len) { int index, count, start, num; struct pattern *pat; tone_t *tone = &dsp->tone; /* if we have no tone, we copy silence */ if (!tone->tone) { memset(data, dsp_silence, len); return; } /* process pattern */ pat = (struct pattern *)tone->pattern; /* points to the current pattern */ index = tone->index; /* gives current sequence index */ count = tone->count; /* gives current sample */ /* copy sample */ while(len) { /* find sample to start with */ while(42) { /* warp arround */ if (!pat->seq[index]) { count = 0; index = 0; } /* check if we are currently playing this tone */ if (count < pat->seq[index]) { break; } if (dsp_debug & DEBUG_DSP_TONE) printk(KERN_DEBUG "%s: reaching next sequence (index=%d)\n", __FUNCTION__, index); count -= pat->seq[index]; index++; } /* calculate start and number of samples */ start = count % (*(pat->siz[index])); num = len; if (num+count > pat->seq[index]) num = pat->seq[index] - count; if (num+start > (*(pat->siz[index]))) num = (*(pat->siz[index])) - start; /* copy memory */ memcpy(data, pat->data[index]+start, num); /* reduce length */ data += num; count += num; len -= num; } tone->index = index; tone->count = count; /* return sk_buff */ return; } /******************************* * send HW message to hfc card * *******************************/ static void dsp_tone_hw_message(dsp_t *dsp, u8 *sample, int len) { struct sk_buff *nskb; nskb = create_link_skb(PH_CONTROL | REQUEST, (len)?HW_SPL_LOOP_ON:HW_SPL_LOOP_OFF, len, sample, 0); if (!nskb) { printk(KERN_ERR "%s: No mem for skb.\n", __FUNCTION__); return; } /* unlocking is not required, because we don't expect a response */ if (mISDN_queue_down(&dsp->inst, 0, nskb)) dev_kfree_skb(nskb); } /***************** * timer expires * *****************/ void dsp_tone_timeout(void *arg) { dsp_t *dsp = arg; tone_t *tone = &dsp->tone; struct pattern *pat = (struct pattern *)tone->pattern; int index = tone->index; if (!tone->tone) return; index++; if (!pat->seq[index]) index = 0; tone->index = index; /* set next tone */ if (pat->data[index] == DATA_S) dsp_tone_hw_message(dsp, 0, 0); else dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); /* set timer */ init_timer(&tone->tl); tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; add_timer(&tone->tl); } /******************** * set/release tone * ********************/ /* * tones are relaized by streaming or by special loop commands if supported * by hardware. when hardware is used, the patterns will be controlled by * timers. */ int dsp_tone(dsp_t *dsp, int tone) { struct pattern *pat; int i; tone_t *tonet = &dsp->tone; tonet->software = 0; tonet->hardware = 0; /* we turn off the tone */ if (!tone) { if (dsp->features.hfc_loops) if (timer_pending(&tonet->tl)) del_timer(&tonet->tl); if (dsp->features.hfc_loops) dsp_tone_hw_message(dsp, NULL, 0); tonet->tone = 0; return(0); } pat = NULL; i = 0; while(pattern[i].tone) { if (pattern[i].tone == tone) { pat = &pattern[i]; break; } i++; } if (!pat) { printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); return(-EINVAL); } if (dsp_debug & DEBUG_DSP_TONE) printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", __FUNCTION__, tone, 0); tonet->tone = tone; tonet->pattern = pat; tonet->index = 0; tonet->count = 0; if (dsp->features.hfc_loops) { tonet->hardware = 1; /* set first tone */ dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); /* set timer */ if (timer_pending(&tonet->tl)) del_timer(&tonet->tl); init_timer(&tonet->tl); tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; add_timer(&tonet->tl); } else { tonet->software = 1; } return(0); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dss1.h0000644000000000000500000001144111110524073017404 0ustar rootsrc/* $Id: dss1.h,v 1.5 2006/12/27 18:50:50 jolly Exp $ * * DSS1 (Euro) D-channel protocol defines * * This file is (c) under GNU PUBLIC LICENSE * */ #ifndef l3dss1_process #define T302 15000 #define T303 4000 #define T304 30000 #define T305 30000 #define T308 4000 /* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */ /* This makes some tests easier and quicker */ #define T309 40000 /* T310 can be between 30-120 Seconds. We use 120 Seconds so the user can hear the inband messages */ #define T310 120000 #define T313 4000 #define T318 4000 #define T319 4000 #define N303 1 #define T_CTRL 180000 #define THOLD 4000 #define TRETRIEVE 4000 /* private TIMER events */ #define CC_T302 0x030201 #define CC_T303 0x030301 #define CC_T304 0x030401 #define CC_T305 0x030501 #define CC_T308_1 0x030801 #define CC_T308_2 0x030802 #define CC_T309 0x030901 #define CC_T310 0x031001 #define CC_T313 0x031301 #define CC_T318 0x031801 #define CC_T319 0x031901 #define CC_TCTRL 0x031f01 #define CC_THOLD 0x03a001 #define CC_TRETRIEVE 0x03a101 /* * Message-Types */ #define MT_ALERTING 0x01 #define MT_CALL_PROCEEDING 0x02 #define MT_CONNECT 0x07 #define MT_CONNECT_ACKNOWLEDGE 0x0f #define MT_PROGRESS 0x03 #define MT_SETUP 0x05 #define MT_SETUP_ACKNOWLEDGE 0x0d #define MT_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 #define MT_SUSPEND 0x25 #define MT_SUSPEND_ACKNOWLEDGE 0x2d #define MT_SUSPEND_REJECT 0x21 #define MT_USER_INFORMATION 0x20 #define MT_DISCONNECT 0x45 #define MT_RELEASE 0x4d #define MT_RELEASE_COMPLETE 0x5a #define MT_RESTART 0x46 #define MT_RESTART_ACKNOWLEDGE 0x4e #define MT_SEGMENT 0x60 #define MT_CONGESTION_CONTROL 0x79 #define MT_INFORMATION 0x7b #define MT_FACILITY 0x62 #define MT_NOTIFY 0x6e #define MT_STATUS 0x7d #define MT_STATUS_ENQUIRY 0x75 #define IE_SEGMENT 0x00 #define IE_BEARER 0x04 #define IE_CAUSE 0x08 #define IE_CALL_ID 0x10 #define IE_CALL_STATE 0x14 #define IE_CHANNEL_ID 0x18 #define IE_FACILITY 0x1c #define IE_PROGRESS 0x1e #define IE_NET_FAC 0x20 #define IE_NOTIFY 0x27 #define IE_DISPLAY 0x28 #define IE_DATE 0x29 #define IE_KEYPAD 0x2c #define IE_SIGNAL 0x34 #define IE_INFORATE 0x40 #define IE_E2E_TDELAY 0x42 #define IE_TDELAY_SEL 0x43 #define IE_PACK_BINPARA 0x44 #define IE_PACK_WINSIZE 0x45 #define IE_PACK_SIZE 0x46 #define IE_CUG 0x47 #define IE_REV_CHARGE 0x4a #define IE_CONNECT_PN 0x4c #define IE_CONNECT_SUB 0x4d #define IE_CALLING_PN 0x6c #define IE_CALLING_SUB 0x6d #define IE_CALLED_PN 0x70 #define IE_CALLED_SUB 0x71 #define IE_REDIR_NR 0x74 #define IE_REDIR_DN 0x76 #define IE_TRANS_SEL 0x78 #define IE_RESTART_IND 0x79 #define IE_LLC 0x7c #define IE_HLC 0x7d #define IE_USER_USER 0x7e #define IE_ESCAPE 0x7f #define IE_SHIFT 0x90 #define IE_MORE_DATA 0xa0 #define IE_COMPLETE 0xa1 #define IE_CONGESTION 0xb0 #define IE_COMPR_REQ 0x01 #define IE_REPEAT 0xd0 #define IE_MANDATORY 0x0100 /* mandatory not in every case */ #define IE_MANDATORY_1 0x0200 #define ERR_IE_COMPREHENSION 1 #define ERR_IE_UNRECOGNIZED -1 #define ERR_IE_LENGTH -2 #define ERR_IE_SEQUENCE -3 #define CAUSE_LOC_USER 0 #define CAUSE_NORMAL_CLEARING 16 #define CAUSE_CALL_REJECTED 21 #define CAUSE_INVALID_NUMBER 28 #define CAUSE_STATUS_RESPONSE 30 #define CAUSE_NORMALUNSPECIFIED 31 #define CAUSE_TEMPORARY_FAILURE 41 #define CAUSE_RESOURCES_UNAVAIL 47 #define CAUSE_INVALID_CALLREF 81 #define CAUSE_MANDATORY_IE_MISS 96 #define CAUSE_MT_NOTIMPLEMENTED 97 #define CAUSE_IE_NOTIMPLEMENTED 99 #define CAUSE_INVALID_CONTENTS 100 #define CAUSE_NOTCOMPAT_STATE 101 #define CAUSE_TIMER_EXPIRED 102 #define CAUSE_PROTOCOL_ERROR 111 #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 */ typedef struct { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */ ulong ll_id; /* remebered ll id */ u_char remote_operation; /* handled remote operation, 0 = not active */ int proc; /* rememered procedure */ ulong remote_result; /* result of remote operation for statcallb */ char uus1_data[35]; /* data send during alerting or disconnect */ } dss1_proc_priv; /* l3dss1 specific data in protocol stack */ typedef struct { unsigned char last_invoke_id; /* last used value for invoking */ unsigned char invoke_used[32]; /* 256 bits for 256 values */ } dss1_stk_priv; #endif /* only l3dss1_process */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/hfcs_usb.c0000644000000000000500000017415411135651702020343 0ustar rootsrc/* hfcs_usb.c * mISDN driver for Colognechip HFC-S USB chip * * Author * Martin Bachem (info@colognechip.com, m.bachem@gmx.de) * - based on HiSax hfc_usb.c driver by Peter Sprenger * - based on a mISDN skel driver by Karten Keil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include "core.h" #include "channel.h" #include "layer1.h" #include "debug.h" #include "hfcs_usb.h" #define DRIVER_NAME "mISDN_hfcsusb" const char *hfcsusb_rev = "Revision: 1.32, 2007-08-21 (mISDN_1_1)"; #define MAX_CARDS 8 static int hfcsusb_cnt; static u_int protocol[MAX_CARDS] = {2,2,2,2,2,2,2,2}; static int layermask[MAX_CARDS]; static mISDNobject_t hw_mISDNObj; static int debug = 0; static int poll = 128; #ifdef MODULE #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); MODULE_PARM(poll, "1i"); #define MODULE_PARM_T "1-4i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #else module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(poll, uint, S_IRUGO | S_IWUSR); #ifdef OLD_MODULE_PARAM_ARRAY static int num_protocol=0, num_layermask=0; module_param_array(protocol, uint, num_protocol, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, num_layermask, S_IRUGO | S_IWUSR); #else module_param_array(protocol, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, NULL, S_IRUGO | S_IWUSR); #endif #endif #endif struct _hfcsusb_t; /* forward definition */ /* structure defining input+output fifos (interrupt/bulk mode) */ struct usb_fifo; /* forward definition */ typedef struct iso_urb_struct { struct urb *purb; __u8 buffer[ISO_BUFFER_SIZE]; /* buffer incoming/outgoing USB URB data */ struct usb_fifo *owner_fifo; /* pointer to owner fifo */ } iso_urb_struct; typedef struct usb_fifo { int fifonum; /* fifo index attached to this structure */ int active; /* fifo is currently active */ struct _hfcsusb_t *card; /* pointer to main structure */ int pipe; /* address of endpoint */ __u8 usb_packet_maxlen; /* maximum length for usb transfer */ unsigned int max_size; /* maximum size of receive/send packet */ __u8 intervall; /* interrupt interval */ struct urb *urb; /* transfer structure for usb routines */ __u8 buffer[128]; /* buffer USB INT OUT URB data */ int bit_line; /* how much bits are in the fifo? */ volatile __u8 usb_transfer_mode; /* switched between ISO and INT */ iso_urb_struct iso[2]; /* need two urbs to have one always for pending */ __u8 ch_idx; /* link BChannel Fifos to chan[ch_idx] */ int last_urblen; /* remember length of last packet */ } usb_fifo; typedef struct _hfcsusb_t { struct list_head list; channel_t chan[4]; /* B1,B2,D,(PCM) */ struct usb_device *dev; /* our device */ struct usb_interface *intf; /* used interface */ int if_used; /* used interface number */ int alt_used; /* used alternate config */ int cfg_used; /* configuration index used */ int vend_idx; /* index in hfcsusb_idtab */ int packet_size; int iso_packet_size; int disc_flag; /* 1 if device was disonnected to avoid some USB actions */ usb_fifo fifos[HFCUSB_NUM_FIFOS]; /* structure holding all fifo data */ /* control pipe background handling */ ctrl_buft ctrl_buff[HFC_CTRL_BUFSIZE]; /* buffer holding queued data */ volatile int ctrl_in_idx, ctrl_out_idx, ctrl_cnt; /* input/output pointer + count */ struct urb *ctrl_urb; /* transfer structure for control channel */ struct usb_ctrlrequest ctrl_write; /* buffer for control write request */ struct usb_ctrlrequest ctrl_read; /* same for read request */ int ctrl_paksize; /* control pipe packet size */ int ctrl_in_pipe, ctrl_out_pipe; /* handles for control pipe */ spinlock_t ctrl_lock; /* queueing ctrl urbs needs to be locked */ spinlock_t lock; volatile __u8 threshold_mask; /* threshold in fifo flow control */ __u8 old_led_state, led_state; __u8 portmode; /* TE ?, NT ?, NT Timer runnning? */ int nt_timer; } hfcsusb_t; /* private vendor specific data */ typedef struct { __u8 led_scheme; // led display scheme signed short led_bits[8]; // array of 8 possible LED bitmask settings char *vend_name; // device name } hfcsusb_vdata; /* supported devices */ static struct usb_device_id hfcsusb_idtab[] = { { USB_DEVICE(0x0959, 0x2bd0), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_OFF, {4, 0, 2, 1}, "ISDN USB TA (Cologne Chip HFC-S USB based)"}), }, { USB_DEVICE(0x0675, 0x1688), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {1, 2, 0, 0}, "DrayTek miniVigor 128 USB ISDN TA"}), }, { USB_DEVICE(0x07b0, 0x0007), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {0x80, -64, -32, -16}, "Billion tiny USB ISDN TA 128"}), }, { USB_DEVICE(0x0742, 0x2008), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {4, 0, 2, 1}, "Stollmann USB TA"}), }, { USB_DEVICE(0x0742, 0x2009), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {4, 0, 2, 1}, "Aceex USB ISDN TA"}), }, { USB_DEVICE(0x0742, 0x200A), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {4, 0, 2, 1}, "OEM USB ISDN TA"}), }, { USB_DEVICE(0x08e3, 0x0301), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {2, 0, 1, 4}, "Olitec USB RNIS"}), }, { USB_DEVICE(0x07fa, 0x0846), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {0x80, -64, -32, -16}, "Bewan Modem RNIS USB"}), }, { USB_DEVICE(0x07fa, 0x0847), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {0x80, -64, -32, -16}, "Djinn Numeris USB"}), }, { USB_DEVICE(0x07b0, 0x0006), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {0x80, -64, -32, -16}, "Twister ISDN TA"}), }, { USB_DEVICE(0x0586, 0x0102), .driver_info = (unsigned long) &((hfcsusb_vdata) {LED_SCHEME1, {0x88, -64, -32, -16}, "ZyXEL OMNI.NET USB II"}), }, { } }; MODULE_DEVICE_TABLE(usb, hfcsusb_idtab); /* some function prototypes */ static int hfcsusb_l2l1(mISDNinstance_t *inst, struct sk_buff *skb); static int setup_bchannel(channel_t * bch, int protocol); static void hfcsusb_ph_command(hfcsusb_t * card, u_char command); static void release_card(hfcsusb_t * card); /* start next background transfer for control channel */ static void ctrl_start_transfer(hfcsusb_t * card) { if (card->ctrl_cnt) { card->ctrl_urb->pipe = card->ctrl_out_pipe; card->ctrl_urb->setup_packet = (u_char *) & card->ctrl_write; card->ctrl_urb->transfer_buffer = NULL; card->ctrl_urb->transfer_buffer_length = 0; card->ctrl_write.wIndex = cpu_to_le16(card->ctrl_buff[card->ctrl_out_idx].hfcs_reg); card->ctrl_write.wValue = cpu_to_le16(card->ctrl_buff[card->ctrl_out_idx].reg_val); usb_submit_urb(card->ctrl_urb, GFP_ATOMIC); /* start transfer */ } } /* * queue a control transfer request * to write HFC-S USB register * return 0 on success. */ static int queued_Write_hfc(hfcsusb_t * card, __u8 reg, __u8 val) { ctrl_buft *buf; spin_lock(&card->ctrl_lock); if (card->ctrl_cnt >= HFC_CTRL_BUFSIZE) return (1); /* no space left */ buf = &card->ctrl_buff[card->ctrl_in_idx]; /* pointer to new index */ buf->hfcs_reg = reg; buf->reg_val = val; if (++card->ctrl_in_idx >= HFC_CTRL_BUFSIZE) card->ctrl_in_idx = 0; /* pointer wrap */ if (++card->ctrl_cnt == 1) ctrl_start_transfer(card); spin_unlock(&card->ctrl_lock); return (0); } /* control completion routine handling background control cmds */ static void #ifdef OLD_IRQ_CALL ctrl_complete(struct urb *urb, struct pt_regs *regs) #else ctrl_complete(struct urb *urb) #endif { hfcsusb_t *card = (hfcsusb_t *) urb->context; ctrl_buft *buf; urb->dev = card->dev; if (card->ctrl_cnt) { buf = &card->ctrl_buff[card->ctrl_out_idx]; card->ctrl_cnt--; /* decrement actual count */ if (++card->ctrl_out_idx >= HFC_CTRL_BUFSIZE) card->ctrl_out_idx = 0; /* pointer wrap */ ctrl_start_transfer(card); /* start next transfer */ } } /* write led data to auxport & invert if necessary */ static void write_led(hfcsusb_t * card, __u8 led_state) { if (led_state != card->old_led_state) { card->old_led_state = led_state; queued_Write_hfc(card, HFCUSB_P_DATA, led_state); } } /* handle LED bits */ static void set_led_bit(hfcsusb_t * card, signed short led_bits, int unset) { if (unset) { if (led_bits < 0) card->led_state |= abs(led_bits); else card->led_state &= ~led_bits; } else { if (led_bits < 0) card->led_state &= ~abs(led_bits); else card->led_state |= led_bits; } } /* handle LED requests */ static void handle_led(hfcsusb_t * card, int event) { hfcsusb_vdata *driver_info = (hfcsusb_vdata *) hfcsusb_idtab[card->vend_idx].driver_info; if (driver_info->led_scheme == LED_OFF) { return; } switch (event) { case LED_POWER_ON: set_led_bit(card, driver_info->led_bits[0], 0); set_led_bit(card, driver_info->led_bits[1], 1); set_led_bit(card, driver_info->led_bits[2], 1); set_led_bit(card, driver_info->led_bits[3], 1); break; case LED_POWER_OFF: set_led_bit(card, driver_info->led_bits[0], 1); set_led_bit(card, driver_info->led_bits[1], 1); set_led_bit(card, driver_info->led_bits[2], 1); set_led_bit(card, driver_info->led_bits[3], 1); break; case LED_S0_ON: set_led_bit(card, driver_info->led_bits[1], 0); break; case LED_S0_OFF: set_led_bit(card, driver_info->led_bits[1], 1); break; case LED_B1_ON: set_led_bit(card, driver_info->led_bits[2], 0); break; case LED_B1_OFF: set_led_bit(card, driver_info->led_bits[2], 1); break; case LED_B2_ON: set_led_bit(card, driver_info->led_bits[3], 0); break; case LED_B2_OFF: set_led_bit(card, driver_info->led_bits[3], 1); break; } write_led(card, card->led_state); } #define HFC_MAX_TE_LAYER1_STATE 8 #define HFC_MAX_NT_LAYER1_STATE 4 const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = { "TE F0 - Reset", "TE F1 - Reset", "TE F2 - Sensing", "TE F3 - Deactivated", "TE F4 - Awaiting signal", "TE F5 - Identifying input", "TE F6 - Synchronized", "TE F7 - Activated" "TE F8 - Lost framing", }; const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = { "NT G0 - Reset", "NT G1 - Deactive", "NT G2 - Pending activation", "NT G3 - Active", "NT G4 - Pending deactivation", }; static void hfc_dt_state(mISDNstack_t *stack, u_int state, u_int te) { int mlen; char * message = NULL; struct sk_buff *skb; if ((te) && (state <= HFC_MAX_TE_LAYER1_STATE)) message = (char *)HFC_TE_LAYER1_STATES[state]; else if ((!te) && (state <= HFC_MAX_NT_LAYER1_STATE)) message = (char *)HFC_NT_LAYER1_STATES[state]; mlen = message ? strlen(message) + 1 : 0; skb = alloc_skb(mlen + 4, GFP_ATOMIC); if (!skb) return; *(unsigned int *)skb_put(skb, 4) = state; if (message) memcpy(skb_put(skb, mlen), message, mlen); mISDN_dt_new_frame(stack, NEWSTATE, skb, 0); } /* S0 state change event handler */ static void S0_new_state(channel_t * dch) { u_int prim = PH_SIGNAL | INDICATION; u_int para = 0; hfcsusb_t *card = dch->inst.privat; if (card->portmode & PORT_MODE_TE) { if (dch->debug) mISDN_debugprint(&card->chan[D].inst, "%s: TE %d", __FUNCTION__, dch->state); switch (dch->state) { case (0): prim = PH_CONTROL | INDICATION; para = HW_RESET; break; case (3): prim = PH_CONTROL | INDICATION; para = HW_DEACTIVATE; handle_led(card, LED_S0_OFF); break; case (5): case (8): para = ANYSIGNAL; break; case (6): para = INFO2; break; case (7): para = INFO4_P8; handle_led(card, LED_S0_ON); break; default: return; } if (dch->state== 7) test_and_set_bit(FLG_ACTIVE, &dch->Flags); else test_and_clear_bit(FLG_ACTIVE, &dch->Flags); } else { if (dch->debug) mISDN_debugprint(&card->chan[D].inst, "%s: NT %d", __FUNCTION__, dch->state); switch (dch->state) { case (1): test_and_clear_bit(FLG_ACTIVE, &dch->Flags); card->nt_timer = 0; card->portmode &= ~NT_ACTIVATION_TIMER; prim = PH_DEACTIVATE | INDICATION; para = 0; handle_led(card, LED_S0_OFF); break; case (2): if (card->nt_timer < 0) { card->nt_timer = 0; card->portmode &= ~NT_ACTIVATION_TIMER; hfcsusb_ph_command(dch->hw, HFC_L1_DEACTIVATE_NT); } else { card->portmode |= NT_ACTIVATION_TIMER; card->nt_timer = NT_T1_COUNT; /* allow G2 -> G3 transition */ queued_Write_hfc(card, HFCUSB_STATES, 2 | HFCUSB_NT_G2_G3); } return; case (3): test_and_set_bit(FLG_ACTIVE, &dch->Flags); card->nt_timer = 0; card->portmode &= ~NT_ACTIVATION_TIMER; prim = PH_ACTIVATE | CONFIRM; para = 0; handle_led(card, LED_S0_ON); break; case (4): card->nt_timer = 0; card->portmode &= ~NT_ACTIVATION_TIMER; return; default: break; } mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, MGR_SHORTSTATUS | INDICATION, test_bit(FLG_ACTIVE, &dch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED, 0, NULL, 0); } mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0); hfc_dt_state(dch->inst.st, dch->state, card->portmode & PORT_MODE_TE); } /* trigger S0 state changes */ static void state_handler(hfcsusb_t * card, __u8 new_l1_state) { if (new_l1_state == card->chan[D].state || new_l1_state < 1 || new_l1_state > 8) return; card->chan[D].state = new_l1_state; S0_new_state(&card->chan[D]); } /* * B-channel setup routine, setup the selected B-channel mode for a given * protocol * It also maybe change the B-channel timeslot to match the allocated slot * * basic protocol values * -1 used for first time setup during init * ISDN_PID_NONE unused channel, idle mode (disconnected) * ISDN_PID_L1_B_64TRANS 64 kBit transparent * ISDN_PID_L1_B_64HDLC 64 kBit HDLC framing * * if the hardware supports more protocols, they should be handled too */ static int setup_bchannel(channel_t * bch, int protocol) { __u8 conhdlc, sctrl, sctrl_r; /* conatainer for new register vals */ hfcsusb_t *card = bch->inst.privat; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "protocol %x-->%x channel(%d)", bch->state, protocol, bch->channel); /* setup val for CON_HDLC */ conhdlc = 0; if (protocol > ISDN_PID_NONE) conhdlc = 8; /* enable FIFO */ switch (protocol) { case (-1): /* used for init */ bch->state = -1; /* fall trough */ case (ISDN_PID_NONE): if (bch->state == ISDN_PID_NONE) return (0); /* already in idle state */ 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): conhdlc |= 2; bch->state = protocol; set_bit(FLG_TRANSPARENT, &bch->Flags); break; case (ISDN_PID_L1_B_64HDLC): bch->state = protocol; set_bit(FLG_HDLC, &bch->Flags); break; default: mISDN_debugprint(&bch->inst, "prot not known %x", protocol); return (-ENOPROTOOPT); } if (protocol >= ISDN_PID_NONE) { /* printk ("HFCS-USB: %s: HFCUSB_FIFO(0x%x) HFCUSB_CON_HDLC(0x%x)\n", __FUNCTION__, (bch->channel)?2:0, conhdlc); */ /* set FIFO to transmit register */ queued_Write_hfc(card, HFCUSB_FIFO, (bch->channel)?2:0); queued_Write_hfc(card, HFCUSB_CON_HDLC, conhdlc); /* reset fifo */ queued_Write_hfc(card, HFCUSB_INC_RES_F, 2); /* printk ("HFCS-USB: %s: HFCUSB_FIFO(0x%x) HFCUSB_CON_HDLC(0x%x)\n", __FUNCTION__, (bch->channel)?2:0, conhdlc); */ /* set FIFO to receive register */ queued_Write_hfc(card, HFCUSB_FIFO, ((bch->channel)?3:1)); queued_Write_hfc(card, HFCUSB_CON_HDLC, conhdlc); /* reset fifo */ queued_Write_hfc(card, HFCUSB_INC_RES_F, 2); sctrl = 0x40 + ((card->portmode & PORT_MODE_TE)?0x00:0x04); sctrl_r = 0x0; if (card->chan[B1].state) { sctrl |= ((card->chan[B1].channel)?2:1); sctrl_r |= ((card->chan[B1].channel)?2:1); } if (card->chan[B2].state) { sctrl |= ((card->chan[B2].channel)?2:1); sctrl_r |= ((card->chan[B2].channel)?2:1); } /* printk ("HFCS-USB: %s: HFCUSB_SCTRL(0x%x) HFCUSB_SCTRL_R(0x%x)\n", __FUNCTION__, sctrl, sctrl_r); */ queued_Write_hfc(card, HFCUSB_SCTRL, sctrl); queued_Write_hfc(card, HFCUSB_SCTRL_R, sctrl_r); if (protocol > ISDN_PID_NONE) { handle_led(card, ((bch->channel)?LED_B2_ON:LED_B1_ON)); } else { handle_led(card, ((bch->channel)?LED_B2_OFF:LED_B1_OFF)); } } return (0); } static void hfcsusb_ph_command(hfcsusb_t * card, u_char command) { if (card->chan[D].debug & L1_DEB_ISAC) mISDN_debugprint(&card->chan[D].inst, "hfcsusb_ph_command %x", command); switch (command) { case HFC_L1_ACTIVATE_TE: /* force sending sending INFO1 */ queued_Write_hfc(card, HFCUSB_STATES, 0x14); /* start l1 activation */ queued_Write_hfc(card, HFCUSB_STATES, 0x04); break; case HFC_L1_FORCE_DEACTIVATE_TE: queued_Write_hfc(card, HFCUSB_STATES, 0x10); queued_Write_hfc(card, HFCUSB_STATES, 0x03); break; case HFC_L1_ACTIVATE_NT: if (card->chan[D].state == 3) { mISDN_queue_data(&card->chan[D].inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } else { queued_Write_hfc(card, HFCUSB_STATES, HFCUSB_ACTIVATE | HFCUSB_DO_ACTION | HFCUSB_NT_G2_G3); } break; case HFC_L1_DEACTIVATE_NT: queued_Write_hfc(card, HFCUSB_STATES, HFCUSB_DO_ACTION); break; } } /* Layer 1 D-channel hardware access */ static int handle_dmsg(channel_t *dch, struct sk_buff *skb) { int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); hfcsusb_t *hw = dch->hw; if (hh->prim == (PH_SIGNAL | REQUEST)) { ret = -EINVAL; } else if (hh->prim == (PH_CONTROL | REQUEST)) { if (hh->dinfo == HW_RESET) { if (dch->state != 0) hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_TE); skb_trim(skb, 0); return(mISDN_queueup_newhead(&dch->inst, 0, PH_CONTROL | INDICATION,HW_POWERUP, skb)); } else if (hh->dinfo == HW_DEACTIVATE) { if (dch->next_skb) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); #ifdef FIXME if (test_and_clear_bit(FLG_L1_DBUSY, &dch->Flags)) dchannel_sched_event(dch, D_CLEARBUSY); #endif } else if (hh->dinfo == HW_POWERUP) { hfcsusb_ph_command(hw, HFC_L1_FORCE_DEACTIVATE_TE); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "hfcsusb_l1hw unknown ctrl %x", hh->dinfo); ret = -EINVAL; } } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { if (hw->portmode & PORT_MODE_NT) { hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_NT); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: PH_ACTIVATE none NT mode", __FUNCTION__); ret = -EINVAL; } } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { if (hw->portmode & PORT_MODE_NT) { hfcsusb_ph_command(hw, HFC_L1_DEACTIVATE_NT); if (test_and_clear_bit(FLG_TX_NEXT, &dch->Flags)) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } if (dch->tx_skb) { dev_kfree_skb(dch->tx_skb); dch->tx_skb = NULL; } dch->tx_idx = 0; if (dch->rx_skb) { dev_kfree_skb(dch->rx_skb); dch->rx_skb = NULL; } test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); test_and_clear_bit(FLG_ACTIVE, &dch->Flags); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: PH_DEACTIVATE none NT mode", __FUNCTION__); ret = -EINVAL; } } else if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS) { u_int temp = hh->dinfo & SSTATUS_ALL; // remove SSTATUS_BROADCAST_BIT if ((hw->portmode & PORT_MODE_NT) && (temp == SSTATUS_ALL || temp == SSTATUS_L1)) { if (hh->dinfo & SSTATUS_BROADCAST_BIT) temp = dch->inst.id | MSG_BROADCAST; else temp = hh->addr | FLG_MSG_TARGET; skb_trim(skb, 0); hh->dinfo = test_bit(FLG_ACTIVE, &dch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED; hh->prim = MGR_SHORTSTATUS | CONFIRM; return(mISDN_queue_message(&dch->inst, temp, skb)); } ret = -EOPNOTSUPP; } else { printk(KERN_WARNING "%s %s: unknown prim(%x)\n", dch->inst.name, __FUNCTION__, hh->prim); ret = -EAGAIN; } if (!ret) dev_kfree_skb(skb); return (ret); } /* Layer 1 B-channel hardware access */ static int handle_bmsg(channel_t *bch, struct sk_buff *skb) { int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); hfcsusb_t *hw = bch->hw; u_long flags; if ((hh->prim == (PH_ACTIVATE | REQUEST)) || (hh->prim == (DL_ESTABLISH | REQUEST))) { spin_lock_irqsave(&hw->lock, flags); if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { ret = setup_bchannel(bch, bch->inst.pid.protocol[1]); if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) test_and_set_bit(FLG_L2DATA, &bch->Flags); } spin_unlock_irqrestore(&hw->lock, flags); #ifdef FIXME if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif skb_trim(skb, 0); return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb)); } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || (hh->prim == (DL_RELEASE | REQUEST)) || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { spin_lock_irqsave(&hw->lock, flags); if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; } bch->tx_idx = 0; if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } test_and_clear_bit(FLG_L2DATA, &bch->Flags); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); setup_bchannel(bch, ISDN_PID_NONE); test_and_clear_bit(FLG_ACTIVE, &bch->Flags); spin_unlock_irqrestore(&hw->lock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) { #ifdef FIXME if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb)) return(0); } } else if (hh->prim == (PH_CONTROL | REQUEST)) { // do not handle PH_CONTROL | REQUEST ?? } else { printk(KERN_WARNING "%s %s: unknown prim(%x)\n", bch->inst.name, __FUNCTION__, hh->prim); ret = -EAGAIN; } if (!ret) dev_kfree_skb(skb); return (ret); } /* Layer2 -> Layer 1 Transfer */ static int hfcsusb_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *chan = container_of(inst, channel_t, inst); int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; int i; if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(chan, hh->dinfo, skb); if (ret > 0) { if (!(chan)) { printk (KERN_INFO "HFC-S USB: CRITICAL ERROR! chan is NULL pointer!\n"); spin_unlock_irqrestore(inst->hwlock, flags); return(-EINVAL); } if (!(chan->tx_skb)) { printk (KERN_INFO "HFC-S USB: CRITICAL ERROR! channel_senddata returned %d without chan->tx_skb\n", ret); spin_unlock_irqrestore(inst->hwlock, flags); return(-EINVAL); } /* channel data debug: */ if ((chan->debug) && (debug & DEBUG_HFC_FIFO)) { mISDN_debugprint(&chan->inst, "new TX channel(%i) len(%i): ", chan->channel, chan->tx_skb->len); i = 0; printk(" "); while (i < chan->tx_skb->len) printk("%02x ", chan->tx_skb->data[i++]); printk("\n"); } /* data gets transmitted later in USB ISO OUT traffic */ ret = 0; } spin_unlock_irqrestore(inst->hwlock, flags); return(ret); } if (test_bit(FLG_DCHANNEL, &chan->Flags)) { ret = handle_dmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (test_bit(FLG_BCHANNEL, &chan->Flags)) { ret = handle_bmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } static int hfcsusb_manager(void *data, u_int prim, void *arg) { hfcsusb_t *hw = NULL; mISDNinstance_t *inst = data; struct sk_buff *skb; int channel = -1; int i; channel_t *chan = NULL; u_long flags; if (!data) { MGR_HASPROTOCOL_HANDLER(prim, arg, &hw_mISDNObj) printk(KERN_ERR "%s %s: no data prim %x arg %p\n", hw->chan[D].inst.name, __FUNCTION__, prim, arg); return (-EINVAL); } spin_lock_irqsave(&hw_mISDNObj.lock, flags); /* find channel and card */ list_for_each_entry(hw, &hw_mISDNObj.ilist, list) { i = 0; while (i < MAX_CHAN) { if (hw->chan[i].Flags && &hw->chan[i].inst == inst) { channel = i; chan = &hw->chan[i]; break; } i++; } if (channel >= 0) break; } spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); if (channel < 0) { printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return (-EINVAL); } switch (prim) { case MGR_REGLAYER | CONFIRM: mISDN_setpara(chan, &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { if (hfcsusb_l2l1(inst, skb)) dev_kfree_skb(skb); } else printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: mISDN_setpara(chan, arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { release_card(hw); } else { hw_mISDNObj.refcnt--; } break; case MGR_SETSTACK | INDICATION: if ((channel != 2) && (inst->pid.global == 2)) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (hfcsusb_l2l1(inst, skb)) dev_kfree_skb(skb); } if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } break; case MGR_GLOBALOPT | REQUEST: if (arg) { // FIXME: detect cards with HEADSET u_int *gopt = arg; *gopt = GLOBALOPT_INTERNAL_CTRL | GLOBALOPT_EXTERNAL_EQUIPMENT | GLOBALOPT_HANDSET; } else return (-EINVAL); break; case MGR_SELCHANNEL | REQUEST: // no special procedure return (-EINVAL); PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); default: printk(KERN_WARNING "%s %s: prim %x not handled\n", hw->chan[D].inst.name, __FUNCTION__, prim); return (-EINVAL); } return (0); } /* collect data from interrupt or isochron in */ static void collect_rx_frame(usb_fifo * fifo, __u8 * data, unsigned int len, int finish) { hfcsusb_t *card = fifo->card; channel_t *ch = &card->chan[fifo->ch_idx]; struct sk_buff *skb; /* data buffer for upper layer */ int fifon; int i; if (!len) return; fifon = fifo->fifonum; if (!ch->rx_skb) { ch->rx_skb = alloc_stack_skb(ch->maxlen + 3, ch->up_headerlen); if (!ch->rx_skb) { if (ch->debug) printk(KERN_DEBUG "%s: No mem for rx_skb\n", __FUNCTION__); return; } skb_trim(ch->rx_skb, 0); } if (fifon == HFCUSB_D_RX) { /* D-Channel SKK range check */ if ((ch->rx_skb->len + len) >= MAX_DFRAME_LEN_L1) { if (ch->debug) printk(KERN_DEBUG "%s: sbk mem exceeded for fifo(%d) HFCUSB_D_RX\n", __FUNCTION__, fifon); skb_trim(ch->rx_skb, 0); return; } } else { /* B-Channel SKB range check */ if ((ch->rx_skb->len + len) >= (MAX_BCH_SIZE + 3)) { if (ch->debug) printk(KERN_DEBUG "%s: sbk mem exceeded for fifo(%d) HFCUSB_B_RX\n", __FUNCTION__, fifon); skb_trim(ch->rx_skb, 0); return; } } // printk ("skb_put: len(%d) new_len(%d)", ch->rx_skb->len, len); memcpy(skb_put(ch->rx_skb, len), data, len); if (test_bit(FLG_HDLC, &ch->Flags)) { /* we have a complete hdlc packet */ if (finish) { if ((ch->rx_skb->len > 3) && (!(ch->rx_skb->data[ch->rx_skb->len - 1]))) { if ((ch->debug) && (debug & DEBUG_HFC_FIFO)) { mISDN_debugprint(&ch->inst, "fifon(%i) new RX len(%i): ", fifon, ch->rx_skb->len); i = 0; printk(" "); while (i < ch->rx_skb->len) printk("%02x ", ch->rx_skb->data[i++]); printk("\n"); } /* remove CRC & status */ skb_trim(ch->rx_skb, ch->rx_skb->len - 3); if (ch->rx_skb->len < MISDN_COPY_SIZE) { skb = alloc_stack_skb(ch->rx_skb->len, ch->up_headerlen); if (skb) { memcpy(skb_put(skb, ch->rx_skb->len), ch->rx_skb->data, ch->rx_skb->len); skb_trim(ch->rx_skb, 0); } else { skb = ch->rx_skb; ch->rx_skb = NULL; } } else { skb = ch->rx_skb; ch->rx_skb = NULL; } queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, skb); } else { if (ch->debug) { printk ("HFC-S USB: CRC or minlen ERROR fifon(%i) RX len(%i): ", fifon, ch->rx_skb->len); i = 0; printk(" "); while (i < ch->rx_skb->len) printk("%02x ", ch->rx_skb->data[i++]); printk("\n"); } skb_trim(ch->rx_skb, 0); } } } else { if (finish || ch->rx_skb->len >= poll) { /* deliver transparent data to layer2 */ queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, ch->rx_skb); ch->rx_skb = NULL; } } } void fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *buf, int num_packets, int packet_size, int interval, usb_complete_t complete, void *context) { int k; usb_fill_bulk_urb(urb, dev, pipe, buf, packet_size * num_packets, complete, context); urb->number_of_packets = num_packets; urb->transfer_flags = URB_ISO_ASAP; urb->actual_length = 0; urb->interval = interval; for (k = 0; k < num_packets; k++) { urb->iso_frame_desc[k].offset = packet_size * k; urb->iso_frame_desc[k].length = packet_size; urb->iso_frame_desc[k].actual_length = 0; } } /* receive completion routine for all ISO tx fifos */ static void #ifdef OLD_IRQ_CALL rx_iso_complete(struct urb *urb, struct pt_regs *regs) #else rx_iso_complete(struct urb *urb) #endif { iso_urb_struct *context_iso_urb = (iso_urb_struct *) urb->context; usb_fifo *fifo = context_iso_urb->owner_fifo; hfcsusb_t *card = fifo->card; channel_t *ch = &card->chan[fifo->ch_idx]; int k, len, errcode, offset, num_isoc_packets, fifon, maxlen, status, iso_status; __u8 *buf; static __u8 eof[8]; fifon = fifo->fifonum; status = urb->status; /* * ISO transfer only partially completed, * look at individual frame status for details */ if (status == -EXDEV) { if (ch->debug) printk(KERN_INFO "HFC-S USB: rx_iso_complete with -EXDEV " "urb->status %d, fifonum %d\n", status, fifon); // clear status, so go on with ISO transfers status = 0; } if (fifo->active && !status) { num_isoc_packets = iso_packets[fifon]; maxlen = fifo->usb_packet_maxlen; for (k = 0; k < num_isoc_packets; ++k) { len = urb->iso_frame_desc[k].actual_length; offset = urb->iso_frame_desc[k].offset; buf = context_iso_urb->buffer + offset; iso_status = urb->iso_frame_desc[k].status; if (ch->debug && iso_status && !card->disc_flag) printk(KERN_INFO "HFC-S USB: rx_iso_complete " "ISO packet %i, status: %i\n", k, iso_status); /* USB data log for every ISO in: if (fifon == 1) { printk ("(%d/%d) len(%d) ", k, num_isoc_packets-1, len); for (i=0; ilast_urblen != maxlen) { /* * save fifo fill-level threshold bits to * use them later in TX ISO URB completions */ card->threshold_mask = buf[1]; /* care for L1 state only at D-Channel data */ if (fifon == HFCUSB_D_RX) state_handler(card, buf[0] >> 4); eof[fifon] = buf[0] & 1; if (len > 2) collect_rx_frame(fifo, buf + 2, len - 2, (len < maxlen) ? eof[fifon] : 0); } else { collect_rx_frame(fifo, buf, len, (len < maxlen) ? eof[fifon] : 0); } fifo->last_urblen = len; } fill_isoc_urb(urb, fifo->card->dev, fifo->pipe, context_iso_urb->buffer, num_isoc_packets, fifo->usb_packet_maxlen, fifo->intervall, (usb_complete_t)rx_iso_complete, urb->context); errcode = usb_submit_urb(urb, GFP_ATOMIC); if (errcode < 0) { if (ch->debug) printk(KERN_INFO "HFC-S USB: error submitting ISO URB: %d\n", errcode); } } else { if (status && !card->disc_flag) { if (ch->debug) printk(KERN_INFO "HFC-S USB: rx_iso_complete : " "urb->status %d, fifonum %d\n", status, fifon); } } } /* receive completion routine for all interrupt rx fifos */ static void #ifdef OLD_IRQ_CALL rx_int_complete(struct urb *urb, struct pt_regs *regs) #else rx_int_complete(struct urb *urb) #endif { int len; int status; __u8 *buf, maxlen, fifon; usb_fifo *fifo = (usb_fifo *) urb->context; hfcsusb_t *card = fifo->card; channel_t *ch = &card->chan[fifo->ch_idx]; static __u8 eof[8]; urb->dev = card->dev; /* security init */ fifon = fifo->fifonum; if ((!fifo->active) || (urb->status)) { if (ch->debug) printk(KERN_INFO "HFC-S USB: RX-Fifo %i is going down (%i)\n", fifon, urb->status); fifo->urb->interval = 0; /* cancel automatic rescheduling */ return; } len = urb->actual_length; buf = fifo->buffer; maxlen = fifo->usb_packet_maxlen; /* USB data log for every INT in: if (fifon == 1) { printk ("fifon %d len %d: ", fifon, len); for (i=0; ilast_urblen != fifo->usb_packet_maxlen) { /* the threshold mask is in the 2nd status byte */ card->threshold_mask = buf[1]; /* handle S0 state */ state_handler(card, buf[0] >> 4); eof[fifon] = buf[0] & 1; /* if we have more than the 2 status bytes -> collect data */ if (len > 2) collect_rx_frame(fifo, buf + 2, urb->actual_length - 2, (len < maxlen) ? eof[fifon] : 0); } else { collect_rx_frame(fifo, buf, urb->actual_length, (len < maxlen) ? eof[fifon] : 0); } fifo->last_urblen = urb->actual_length; status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { if (ch->debug) printk(KERN_INFO "HFC-S USB: error resubmitting URN at rx_int_complete...\n"); } } /* * check if new buffer for channel * is waitinng is transmitt queue */ int next_tx_frame(hfcsusb_t * hw, __u8 channel) { int i; channel_t *ch = &hw->chan[channel]; if (ch->tx_skb) dev_kfree_skb(ch->tx_skb); if (test_and_clear_bit(FLG_TX_NEXT, &ch->Flags)) { ch->tx_skb = ch->next_skb; if (ch->tx_skb) { mISDN_head_t *hh = mISDN_HEAD_P(ch->tx_skb); ch->next_skb = NULL; test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); ch->tx_idx = 0; /* channel data debug: */ if ((ch->debug) && (debug & DEBUG_HFC_FIFO)) { mISDN_debugprint(&ch->inst, "new TX channel(%i) len(%i): ", ch->channel, ch->tx_skb->len); i = 0; printk(" "); while (i < ch->tx_skb->len) printk("%02x ", ch->tx_skb->data[i++]); printk(" (TX_NEXT)\n"); } queue_ch_frame(ch, CONFIRM, hh->dinfo, NULL); return (1); } else { if (ch->debug) printk(KERN_WARNING "%s channel(%i) TX_NEXT without skb\n", ch->inst.name, channel); test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); } } else ch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); return (0); } /* transmit completion routine for all ISO tx fifos */ static void #ifdef OLD_IRQ_CALL tx_iso_complete(struct urb *urb, struct pt_regs *regs) #else tx_iso_complete(struct urb *urb) #endif { iso_urb_struct *context_iso_urb = (iso_urb_struct *) urb->context; usb_fifo *fifo = context_iso_urb->owner_fifo; hfcsusb_t *card = fifo->card; channel_t *ch = &card->chan[fifo->ch_idx]; int k, tx_offset, num_isoc_packets, sink, remain, current_len, errcode; int frame_complete, fifon, status; __u8 threshbit; fifon = fifo->fifonum; status = urb->status; tx_offset = 0; /* * ISO transfer only partially completed, * look at individual frame status for details */ if (status == -EXDEV) { if (ch->debug) printk(KERN_INFO "HFC-S USB: tx_iso_complete with -EXDEV, " "urb->status %d, fifonum %d\n", status, fifon); if (ch->debug) { for (k = 0; k < iso_packets[fifon]; ++k) { errcode = urb->iso_frame_desc[k].status; printk(KERN_INFO "HFC-S USB: tx_iso_complete with -EXDEV, " "ISO packet %i, status: %i\n", k, errcode); } } // clear status, so go on with ISO transfers status = 0; } if (fifo->active && !status) { /* is FifoFull-threshold set for our channel? */ threshbit = (card->threshold_mask & (1 << fifon)); num_isoc_packets = iso_packets[fifon]; /* predict dataflow to avoid fifo overflow */ if (fifon >= HFCUSB_D_TX) { sink = (threshbit) ? SINK_DMIN : SINK_DMAX; } else { sink = (threshbit) ? SINK_MIN : SINK_MAX; } fill_isoc_urb(urb, fifo->card->dev, fifo->pipe, context_iso_urb->buffer, num_isoc_packets, fifo->usb_packet_maxlen, fifo->intervall, (usb_complete_t)tx_iso_complete, urb->context); memset(context_iso_urb->buffer, 0, sizeof(context_iso_urb->buffer)); frame_complete = 0; /* Generate next Iso Packets */ for (k = 0; k < num_isoc_packets; ++k) { if (ch->tx_skb) { remain = ch->tx_skb->len - ch->tx_idx; } else { remain = 0; } if (remain > 0) { /* we lower data margin every msec */ fifo->bit_line -= sink; current_len = (0 - fifo->bit_line) / 8; /* maximum 15 byte for every ISO packet makes our life easier */ if (current_len > 14) current_len = 14; current_len = (remain <= current_len) ? remain : current_len; /* how much bit do we put on the line? */ fifo->bit_line += current_len * 8; context_iso_urb->buffer[tx_offset] = 0; if (current_len == remain) { if (test_bit(FLG_HDLC, &ch->Flags)) { /* here frame completion */ context_iso_urb->buffer[tx_offset] = 1; /* add 2 byte flags and 16bit CRC at end of ISDN frame */ fifo->bit_line += 32; } frame_complete = 1; } /* copy tx data to iso-urb buffer */ memcpy(context_iso_urb->buffer + tx_offset + 1, (ch->tx_skb->data + ch->tx_idx), current_len); ch->tx_idx += current_len; /* define packet delimeters within the URB buffer */ urb->iso_frame_desc[k].offset = tx_offset; urb->iso_frame_desc[k].length = current_len + 1; #if 0 // USB data log for every ISO out if ((fifon == HFCUSB_D_TX) && ch->debug) { printk ("D ISO TX (%d/%d) offset(%d) len(%d) ", k, num_isoc_packets-1, urb->iso_frame_desc[k].offset, urb->iso_frame_desc[k].length); for (i=urb->iso_frame_desc[k].offset; i<(urb->iso_frame_desc[k].offset + urb->iso_frame_desc[k].length); i++) printk ("%x ", context_iso_urb->buffer[i]); printk (" skb->len(%i) tx-idx(%d)\n", ch->tx_skb->len, ch->tx_idx); } #endif tx_offset += (current_len + 1); } else { urb->iso_frame_desc[k].offset = tx_offset++; urb->iso_frame_desc[k].length = 1; fifo->bit_line -= sink; /* we lower data margin every msec */ if (fifo->bit_line < BITLINE_INF) { fifo->bit_line = BITLINE_INF; } } if (frame_complete) { next_tx_frame(card, fifo->ch_idx); frame_complete = 0; } } errcode = usb_submit_urb(urb, GFP_ATOMIC); if (errcode < 0) { if (ch->debug) printk(KERN_INFO "HFC-S USB: error submitting ISO URB: %d \n", errcode); } /* * abuse DChannel tx iso completion to trigger NT mode state changes * tx_iso_complete is assumed to be called every fifo->intervall ms */ if ((fifon == HFCUSB_D_TX) && (card->portmode & PORT_MODE_NT) && (card->portmode & NT_ACTIVATION_TIMER)) { if ((--card->nt_timer) < 0) S0_new_state(&card->chan[D]); } } else { if (status && !card->disc_flag) { if (ch->debug) printk(KERN_INFO "HFC-S USB: tx_iso_complete : urb->status %s (%i), fifonum=%d\n", symbolic(urb_errlist, status), status, fifon); } } } /* * allocs urbs and start isoc transfer with two pending urbs to avoid * gaps in the transfer chain */ static int start_isoc_chain(usb_fifo * fifo, int num_packets_per_urb, usb_complete_t complete, int packet_size) { int i, k, errcode; if (debug) printk(KERN_INFO "HFC-S USB: starting ISO-chain for Fifo %i\n", fifo->fifonum); /* allocate Memory for Iso out Urbs */ for (i = 0; i < 2; i++) { if (!(fifo->iso[i].purb)) { fifo->iso[i].purb = usb_alloc_urb(num_packets_per_urb, GFP_KERNEL); if (!(fifo->iso[i].purb)) { printk(KERN_INFO "alloc urb for fifo %i failed!!!", fifo->fifonum); } fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo; /* Init the first iso */ if (ISO_BUFFER_SIZE >= (fifo->usb_packet_maxlen * num_packets_per_urb)) { fill_isoc_urb(fifo->iso[i].purb, fifo->card->dev, fifo->pipe, fifo->iso[i].buffer, num_packets_per_urb, fifo->usb_packet_maxlen, fifo->intervall, complete, &fifo->iso[i]); memset(fifo->iso[i].buffer, 0, sizeof(fifo->iso[i].buffer)); /* defining packet delimeters in fifo->buffer */ for (k = 0; k < num_packets_per_urb; k++) { fifo->iso[i].purb-> iso_frame_desc[k].offset = k * packet_size; fifo->iso[i].purb-> iso_frame_desc[k].length = packet_size; } } else { printk(KERN_INFO "HFC-S USB: ISO Buffer size to small!\n"); } } fifo->bit_line = BITLINE_INF; errcode = usb_submit_urb(fifo->iso[i].purb, GFP_KERNEL); fifo->active = (errcode >= 0) ? 1 : 0; if (errcode < 0) { printk(KERN_INFO "HFC-S USB: %s URB nr:%d\n", symbolic(urb_errlist, errcode), i); }; } return (fifo->active); } /* stops running iso chain and frees their pending urbs */ static void stop_isoc_chain(usb_fifo * fifo) { int i; for (i = 0; i < 2; i++) { if (fifo->iso[i].purb) { if (debug) printk(KERN_INFO "HFC-S USB: %s for fifo %i.%i\n", __FUNCTION__, fifo->fifonum, i); usb_kill_urb(fifo->iso[i].purb); usb_free_urb(fifo->iso[i].purb); fifo->iso[i].purb = NULL; } } if (fifo->urb) { usb_kill_urb(fifo->urb); usb_free_urb(fifo->urb); fifo->urb = NULL; } fifo->active = 0; } /* start the interrupt transfer for the given fifo */ static void start_int_fifo(usb_fifo * fifo) { int errcode; if (debug) printk(KERN_INFO "HFC-S USB: starting intr IN fifo:%d\n", fifo->fifonum); if (!fifo->urb) { fifo->urb = usb_alloc_urb(0, GFP_KERNEL); if (!fifo->urb) return; } usb_fill_int_urb(fifo->urb, fifo->card->dev, fifo->pipe, fifo->buffer, fifo->usb_packet_maxlen, (usb_complete_t)rx_int_complete, fifo, fifo->intervall); fifo->active = 1; /* must be marked active */ errcode = usb_submit_urb(fifo->urb, GFP_KERNEL); if (errcode) { printk(KERN_INFO "HFC-S USB: submit URB error(start_int_info): status:%i\n", errcode); fifo->active = 0; } } /* Hardware Initialization */ int setup_hfcsusb(hfcsusb_t * card) { usb_fifo *fifo; int i, err; u_char b; /* check the chip id */ if (read_usb(card, HFCUSB_CHIP_ID, &b) != 1) { printk(KERN_INFO "HFC-USB: cannot read chip id\n"); return (1); } if (b != HFCUSB_CHIPID) { printk(KERN_INFO "HFC-S USB: Invalid chip id 0x%02x\n", b); return (1); } /* first set the needed config, interface and alternate */ err = usb_set_interface(card->dev, card->if_used, card->alt_used); /* do Chip reset */ write_usb(card, HFCUSB_CIRM, 8); /* aux = output, reset off */ write_usb(card, HFCUSB_CIRM, 0x10); /* set USB_SIZE to match the the wMaxPacketSize for INT or BULK transfers */ write_usb(card, HFCUSB_USB_SIZE, (card->packet_size / 8) | ((card->packet_size / 8) << 4)); /* set USB_SIZE_I to match the the wMaxPacketSize for ISO transfers */ write_usb(card, HFCUSB_USB_SIZE_I, card->iso_packet_size); /* enable PCM/GCI master mode */ write_usb(card, HFCUSB_MST_MODE1, 0); /* set default values */ write_usb(card, HFCUSB_MST_MODE0, 1); /* enable master mode */ /* init the fifos */ write_usb(card, HFCUSB_F_THRES, (HFCUSB_TX_THRESHOLD / 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4)); fifo = card->fifos; for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { write_usb(card, HFCUSB_FIFO, i); /* select the desired fifo */ fifo[i].max_size = (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN; fifo[i].last_urblen = 0; /* set 2 bit for D- & E-channel */ write_usb(card, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2)); /* enable all fifos */ if (i == HFCUSB_D_TX) { // enable Interframe Fill for DChannel TX in TE Mode write_usb(card, HFCUSB_CON_HDLC, (card->portmode & PORT_MODE_NT) ? 0x08 : 0x09); // write_usb(card, HFCUSB_CON_HDLC, 0x08); } else { write_usb(card, HFCUSB_CON_HDLC, 0x08); } write_usb(card, HFCUSB_INC_RES_F, 2); /* reset the fifo */ } if (card->portmode & PORT_MODE_NT) { write_usb(card, HFCUSB_SCTRL, 0x44); /* disable B transmitters + capacitive mode, enable NT mode */ write_usb(card, HFCUSB_SCTRL_E, 0x09); write_usb(card, HFCUSB_CLKDEL, CLKDEL_NT); /* clock delay value */ write_usb(card, HFCUSB_STATES, 1 | 0x10); /* set deactivated mode */ write_usb(card, HFCUSB_STATES, 1); /* enable state machine */ } else { write_usb(card, HFCUSB_SCTRL, 0x40); /* disable B transmitters + capacitive mode, enable TE mode */ write_usb(card, HFCUSB_SCTRL_E, 0x00); write_usb(card, HFCUSB_CLKDEL, CLKDEL_TE); /* clock delay value */ write_usb(card, HFCUSB_STATES, 3 | 0x10); /* set deactivated mode */ write_usb(card, HFCUSB_STATES, 3); /* enable state machine */ } write_usb(card, HFCUSB_SCTRL_R, 0); /* disable both B receivers */ card->disc_flag = 0; card->led_state = 0; card->old_led_state = 0; /* init the background machinery for control requests */ card->ctrl_read.bRequestType = 0xc0; card->ctrl_read.bRequest = 1; card->ctrl_read.wLength = cpu_to_le16(1); card->ctrl_write.bRequestType = 0x40; card->ctrl_write.bRequest = 0; card->ctrl_write.wLength = 0; usb_fill_control_urb(card->ctrl_urb, card->dev, card->ctrl_out_pipe, (u_char *) & card->ctrl_write, NULL, 0, (usb_complete_t)ctrl_complete, card); /* Init All Fifos */ for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { card->fifos[i].iso[0].purb = NULL; card->fifos[i].iso[1].purb = NULL; card->fifos[i].active = 0; } /* 3 (+1) INT IN + 3 ISO OUT */ if (card->cfg_used == CNF_3INT3ISO || card->cfg_used == CNF_4INT3ISO) { start_int_fifo(card->fifos + HFCUSB_D_RX); /* if (card->fifos[HFCUSB_PCM_RX].pipe) start_int_fifo(card->fifos + HFCUSB_PCM_RX); */ start_int_fifo(card->fifos + HFCUSB_B1_RX); start_int_fifo(card->fifos + HFCUSB_B2_RX); } /* 3 (+1) ISO IN + 3 ISO OUT */ if (card->cfg_used == CNF_3ISO3ISO || card->cfg_used == CNF_4ISO3ISO) { start_isoc_chain(card->fifos + HFCUSB_D_RX, ISOC_PACKETS_D, (usb_complete_t)rx_iso_complete, 16); /* if (card->fifos[HFCUSB_PCM_RX].pipe) start_isoc_chain(card->fifos + HFCUSB_PCM_RX, ISOC_PACKETS_D, rx_iso_complete, 16); */ start_isoc_chain(card->fifos + HFCUSB_B1_RX, ISOC_PACKETS_B, (usb_complete_t)rx_iso_complete, 16); start_isoc_chain(card->fifos + HFCUSB_B2_RX, ISOC_PACKETS_B, (usb_complete_t)rx_iso_complete, 16); } start_isoc_chain(card->fifos + HFCUSB_D_TX, ISOC_PACKETS_D, (usb_complete_t)tx_iso_complete, 1); start_isoc_chain(card->fifos + HFCUSB_B1_TX, ISOC_PACKETS_B, (usb_complete_t)tx_iso_complete, 1); start_isoc_chain(card->fifos + HFCUSB_B2_TX, ISOC_PACKETS_B, (usb_complete_t)tx_iso_complete, 1); #ifdef TEST_LEDS __u8 t; t = 0; /* __u8 s; for (s=0; s<10; s++) { for (t=0; t<8; t++) { queued_Write_hfc(card, HFCUSB_P_DATA, 1 << t); printk ("TEST LED: P_DATA: 0x%x\n", 1<led_state); schedule_timeout(2 * HZ); } #endif handle_led(card, LED_POWER_ON); return (0); } static void release_card(hfcsusb_t * card) { int i; u_long flags; if (debug & 0x10000) printk(KERN_DEBUG "%s\n", __FUNCTION__); setup_bchannel(&card->chan[B1], ISDN_PID_NONE); setup_bchannel(&card->chan[B2], ISDN_PID_NONE); mISDN_freechannel(&card->chan[B1]); mISDN_freechannel(&card->chan[B2]); mISDN_freechannel(&card->chan[D]); mISDN_ctrl(&card->chan[D].inst, MGR_UNREGLAYER | REQUEST, NULL); spin_lock_irqsave(&hw_mISDNObj.lock, flags); list_del(&card->list); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ /* tell all fifos to terminate */ for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { if (card->fifos[i].usb_transfer_mode == USB_ISOC) { if (card->fifos[i].active > 0) { stop_isoc_chain(&card->fifos[i]); } } else { if (card->fifos[i].active > 0) { card->fifos[i].active = 0; } if (card->fifos[i].urb) { usb_kill_urb(card->fifos[i].urb); usb_free_urb(card->fifos[i].urb); card->fifos[i].urb = NULL; } } card->fifos[i].active = 0; } /* wait for all URBS to terminate */ if (card->ctrl_urb) { usb_kill_urb(card->ctrl_urb); usb_free_urb(card->ctrl_urb); card->ctrl_urb = NULL; } hfcsusb_cnt--; if (card->intf) usb_set_intfdata(card->intf, NULL); kfree(card); } static int setup_instance(hfcsusb_t * card) { int i, err; mISDN_pid_t pid; u_long flags; spin_lock_irqsave(&hw_mISDNObj.lock, flags); list_add_tail(&card->list, &hw_mISDNObj.ilist); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); card->chan[D].debug = debug; spin_lock_init(&card->ctrl_lock); spin_lock_init(&card->lock); /* link card->fifos[] to card->chan[] */ card->fifos[HFCUSB_D_RX].ch_idx = D; card->fifos[HFCUSB_D_TX].ch_idx = D; card->fifos[HFCUSB_B1_RX].ch_idx = B1; card->fifos[HFCUSB_B1_TX].ch_idx = B1; card->fifos[HFCUSB_B2_RX].ch_idx = B2; card->fifos[HFCUSB_B2_TX].ch_idx = B2; card->fifos[HFCUSB_PCM_RX].ch_idx = PCM; card->fifos[HFCUSB_PCM_TX].ch_idx = PCM; card->chan[D].channel = D; card->chan[D].state = 0; card->chan[D].inst.hwlock = &card->lock; card->chan[D].inst.pid.layermask = ISDN_LAYER(0); card->chan[D].inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->chan[D].inst.class_dev.parent = &card->dev->dev; #else card->chan[D].inst.class_dev.dev = &card->dev->dev; #endif mISDN_init_instance(&card->chan[D].inst, &hw_mISDNObj, card, hfcsusb_l2l1); sprintf(card->chan[D].inst.name, "hfcsusb_%d", hfcsusb_cnt + 1); mISDN_set_dchannel_pid(&pid, protocol[hfcsusb_cnt], layermask[hfcsusb_cnt]); mISDN_initchannel(&card->chan[D], MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); card->chan[D].hw = card; card->portmode = 0; for (i = B1; i <= B2; i++) { card->chan[i].channel = i; mISDN_init_instance(&card->chan[i].inst, &hw_mISDNObj, card, hfcsusb_l2l1); card->chan[i].inst.pid.layermask = ISDN_LAYER(0); card->chan[i].inst.hwlock = &card->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->chan[i].inst.class_dev.parent = &card->dev->dev; #else card->chan[i].inst.class_dev.dev = &card->dev->dev; #endif card->chan[i].debug = debug; sprintf(card->chan[i].inst.name, "%s B%d", card->chan[D].inst.name, i + 1); mISDN_initchannel(&card->chan[i], MSK_INIT_BCHANNEL, MAX_DATA_MEM); card->chan[i].hw = card; #ifdef FIXME if (card->chan[i].dev) { card->chan[i].dev->wport.pif.func = hfcsusb_l2l1; card->chan[i].dev->wport.pif.fdata = &card->chan[i]; } #endif } card->chan[PCM].channel = PCM; if (protocol[hfcsusb_cnt] & 0x10) { // NT Mode if (card->chan[D].debug) printk (KERN_INFO "%s wants NT Mode\n", card->chan[D].inst.name); card->chan[D].inst.pid.protocol[0] = ISDN_PID_L0_NT_S0; card->chan[D].inst.pid.protocol[1] = ISDN_PID_L1_NT_S0; pid.protocol[0] = ISDN_PID_L0_NT_S0; pid.protocol[1] = ISDN_PID_L1_NT_S0; card->chan[D].inst.pid.layermask |= ISDN_LAYER(1); pid.layermask |= ISDN_LAYER(1); if (layermask[i] & ISDN_LAYER(2)) pid.protocol[2] = ISDN_PID_L2_LAPD_NET; /* select NT mode with activated NT Timer (T1) */ card->portmode |= (PORT_MODE_NT | NT_ACTIVATION_TIMER); } else { if (card->chan[D].debug) printk (KERN_INFO "%s wants TE Mode\n", card->chan[D].inst.name); // TE Mode card->chan[D].inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; card->portmode |= PORT_MODE_TE; } if (debug) printk(KERN_DEBUG "hfcsusb card %p dch %p bch1 %p bch2 %p\n", card, &card->chan[D], &card->chan[B1], &card->chan[B2]); err = setup_hfcsusb(card); if (err) { mISDN_freechannel(&card->chan[D]); mISDN_freechannel(&card->chan[B2]); mISDN_freechannel(&card->chan[B1]); spin_lock_irqsave(&hw_mISDNObj.lock, flags); list_del(&card->list); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); kfree(card); return (err); } hfcsusb_cnt++; err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->chan[D].inst); if (err) { release_card(card); return (err); } for (i = B1; i <= B2; i++) { err = mISDN_ctrl(card->chan[D].inst.st, MGR_NEWSTACK | REQUEST, &card->chan[i].inst); if (err) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); mISDN_ctrl(card->chan[D].inst.st, MGR_DELSTACK | REQUEST, NULL); return (err); } setup_bchannel(&card->chan[i], -1); } if (debug) printk(KERN_DEBUG "%s lm %x\n", __FUNCTION__, pid.layermask); err = mISDN_ctrl(card->chan[D].inst.st, MGR_SETSTACK | REQUEST, &pid); if (err) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); mISDN_ctrl(card->chan[D].inst.st, MGR_DELSTACK | REQUEST, NULL); return (err); } mISDN_ctrl(card->chan[D].inst.st, MGR_CTRLREADY | INDICATION, NULL); usb_set_intfdata(card->intf, card); return (0); } /* function called to probe a new plugged device */ static int hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); hfcsusb_t *card; struct usb_host_interface *iface = intf->cur_altsetting; struct usb_host_interface *iface_used = NULL; struct usb_host_endpoint *ep; int ifnum = iface->desc.bInterfaceNumber; int i, idx, alt_idx, probe_alt_setting, vend_idx, cfg_used, *vcf, attr, cfg_found, ep_addr; int cmptbl[16], small_match, iso_packet_size, packet_size, alt_used = 0; hfcsusb_vdata *driver_info; vend_idx = 0xffff; for (i = 0; hfcsusb_idtab[i].idVendor; i++) { if ((le16_to_cpu(dev->descriptor.idVendor) == hfcsusb_idtab[i].idVendor) && (le16_to_cpu(dev->descriptor.idProduct) == hfcsusb_idtab[i].idProduct)) { vend_idx = i; continue; } } printk(KERN_INFO "HFC-S USB: probing interface(%d) actalt(%d) minor(%d) vend_idx(%d)\n", ifnum, iface->desc.bAlternateSetting, intf->minor, vend_idx); if (vend_idx == 0xffff) { printk(KERN_WARNING "HFC-S USB: no valid vendor found in USB descriptor\n"); return (-EIO); } /* if vendor and product ID is OK, start probing alternate settings */ alt_idx = 0; small_match = 0xffff; /* default settings */ iso_packet_size = 16; packet_size = 64; while (alt_idx < intf->num_altsetting) { iface = intf->altsetting + alt_idx; probe_alt_setting = iface->desc.bAlternateSetting; cfg_used = 0; /* check for config EOL element */ while (validconf[cfg_used][0]) { cfg_found = 1; vcf = validconf[cfg_used]; /* first endpoint descriptor */ ep = iface->endpoint; memcpy(cmptbl, vcf, 16 * sizeof(int)); /* check for all endpoints in this alternate setting */ for (i = 0; i < iface->desc.bNumEndpoints; i++) { ep_addr = ep->desc.bEndpointAddress; /* get endpoint base */ idx = ((ep_addr & 0x7f) - 1) * 2; if (ep_addr & 0x80) idx++; attr = ep->desc.bmAttributes; if (cmptbl[idx] == EP_NUL) { cfg_found = 0; } if (attr == USB_ENDPOINT_XFER_INT && cmptbl[idx] == EP_INT) cmptbl[idx] = EP_NUL; if (attr == USB_ENDPOINT_XFER_BULK && cmptbl[idx] == EP_BLK) cmptbl[idx] = EP_NUL; if (attr == USB_ENDPOINT_XFER_ISOC && cmptbl[idx] == EP_ISO) cmptbl[idx] = EP_NUL; /* check if all INT endpoints match minimum interval */ if (attr == USB_ENDPOINT_XFER_INT && ep->desc.bInterval < vcf[17]) { cfg_found = 0; } ep++; } for (i = 0; i < 16; i++) { /* all entries must be EP_NOP or EP_NUL for a valid config */ if (cmptbl[i] != EP_NOP && cmptbl[i] != EP_NUL) cfg_found = 0; } if (cfg_found) { if (cfg_used < small_match) { small_match = cfg_used; alt_used = probe_alt_setting; iface_used = iface; } } cfg_used++; } alt_idx++; } /* (alt_idx < intf->num_altsetting) */ /* not found a valid USB Ta Endpoint config */ if (small_match == 0xffff) { printk(KERN_WARNING "HFC-S USB: no valid endpoint found in USB descriptor\n"); return (-EIO); } iface = iface_used; card = kzalloc(sizeof(hfcsusb_t), GFP_KERNEL); if (!card) return (-ENOMEM); /* got no mem */ ep = iface->endpoint; vcf = validconf[small_match]; for (i = 0; i < iface->desc.bNumEndpoints; i++) { usb_fifo *f; ep_addr = ep->desc.bEndpointAddress; /* get endpoint base */ idx = ((ep_addr & 0x7f) - 1) * 2; if (ep_addr & 0x80) idx++; f = &card->fifos[idx & 7]; /* init Endpoints */ if (vcf[idx] == EP_NOP || vcf[idx] == EP_NUL) { ep++; continue; } switch (ep->desc.bmAttributes) { case USB_ENDPOINT_XFER_INT: f->pipe = usb_rcvintpipe(dev, ep->desc.bEndpointAddress); f->usb_transfer_mode = USB_INT; packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); break; case USB_ENDPOINT_XFER_BULK: if (ep_addr & 0x80) f->pipe = usb_rcvbulkpipe(dev, ep->desc.bEndpointAddress); else f->pipe = usb_sndbulkpipe(dev, ep->desc.bEndpointAddress); f->usb_transfer_mode = USB_BULK; packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); break; case USB_ENDPOINT_XFER_ISOC: if (ep_addr & 0x80) f->pipe = usb_rcvisocpipe(dev, ep->desc.bEndpointAddress); else f->pipe = usb_sndisocpipe(dev, ep->desc.bEndpointAddress); f->usb_transfer_mode = USB_ISOC; iso_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); break; default: f->pipe = 0; } /* switch attribute */ if (f->pipe) { f->fifonum = idx & 7; f->card = card; f->usb_packet_maxlen = le16_to_cpu(ep->desc.wMaxPacketSize); f->intervall = ep->desc.bInterval; } ep++; } card->dev = dev; /* save device */ card->if_used = ifnum; /* save used interface */ card->alt_used = alt_used; /* and alternate config */ card->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */ card->cfg_used = vcf[16]; /* store used config */ card->vend_idx = vend_idx; /* store found vendor */ card->packet_size = packet_size; card->iso_packet_size = iso_packet_size; /* create the control pipes needed for register access */ card->ctrl_in_pipe = usb_rcvctrlpipe(card->dev, 0); card->ctrl_out_pipe = usb_sndctrlpipe(card->dev, 0); card->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); driver_info = (hfcsusb_vdata *) hfcsusb_idtab[vend_idx].driver_info; printk(KERN_INFO "HFC-S USB: detected \"%s\" (%s, if=%d alt=%d)\n", driver_info->vend_name, conf_str[small_match], ifnum, alt_used); card->intf = intf; if (setup_instance(card)) { return (-EIO); } return (0); } /* function called when an active device is removed */ static void hfcsusb_disconnect(struct usb_interface *intf) { hfcsusb_t *card = usb_get_intfdata(intf); printk(KERN_INFO "HFC-S USB: device disconnect\n"); if (!card) { if (debug & 0x10000) printk(KERN_DEBUG "%s : NO CONTEXT!\n", __FUNCTION__); return; } if (debug & 0x10000) printk(KERN_DEBUG "%s\n", __FUNCTION__); card->disc_flag = 1; mISDN_ctrl(card->chan[D].inst.st, MGR_DELSTACK | REQUEST, NULL); // release_card(card); usb_set_intfdata(intf, NULL); } static struct usb_driver hfcsusb_drv = { .name = DRIVER_NAME, .id_table = hfcsusb_idtab, .probe = hfcsusb_probe, .disconnect = hfcsusb_disconnect, }; static int __init hfcsusb_init(void) { int err; // debug = 0xFFFF; printk(KERN_INFO "hfcsusb driver Rev. %s (debug=%i)\n", mISDN_getrev(hfcsusb_rev), debug); #ifdef MODULE hw_mISDNObj.owner = THIS_MODULE; #endif spin_lock_init(&hw_mISDNObj.lock); INIT_LIST_HEAD(&hw_mISDNObj.ilist); hw_mISDNObj.name = DRIVER_NAME; hw_mISDNObj.own_ctrl = hfcsusb_manager; hw_mISDNObj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0; hw_mISDNObj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0; hw_mISDNObj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; hw_mISDNObj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; if ((err = mISDN_register(&hw_mISDNObj))) { printk(KERN_ERR "Can't register hfcsusb error(%d)\n", err); return (err); } if (usb_register(&hfcsusb_drv)) { printk(KERN_INFO "hfcsusb: Unable to register hfcsusb module at usb stack\n"); goto out; } mISDN_module_register(THIS_MODULE); return 0; out: mISDN_unregister(&hw_mISDNObj); return err; } static void __exit hfcsusb_cleanup(void) { int err; hfcsusb_t *card, *next; mISDN_module_unregister(THIS_MODULE); if (debug & 0x10000) printk(KERN_DEBUG "%s\n", __FUNCTION__); list_for_each_entry_safe(card, next, &hw_mISDNObj.ilist, list) { handle_led(card, LED_POWER_OFF); } if ((err = mISDN_unregister(&hw_mISDNObj))) { printk(KERN_ERR "Can't unregister hfcsusb error(%d)\n", err); } /* unregister Hardware */ usb_deregister(&hfcsusb_drv); /* release our driver */ } module_init(hfcsusb_init); module_exit(hfcsusb_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/hfcs_usb.h0000644000000000000500000001655611135651702020351 0ustar rootsrc/* * hfcs_usb.h, HFC-S USB mISDN driver */ #ifndef __HFCS_USB_H__ #define __HFCS_USB_H__ #define DRIVER_AUTHOR "Martin Bachem " #define DRIVER_DESC "HFC-S USB based mISDN driver" /* 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 HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */ #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 /* 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 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 MAX_BCH_SIZE 2048 /* allowed B-channel packet size */ #define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */ #define HFCUSB_TX_THRESHOLD 64 /* threshold for fifo report bit tx */ #define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */ #define HFCUSB_CIRM 0x00 /* cirm register index */ #define HFCUSB_USB_SIZE 0x07 /* int length register */ #define HFCUSB_USB_SIZE_I 0x06 /* iso length register */ #define HFCUSB_F_CROSS 0x0b /* bit order register */ #define HFCUSB_CLKDEL 0x37 /* bit delay register */ #define HFCUSB_CON_HDLC 0xfa /* channel connect register */ #define HFCUSB_HDLC_PAR 0xfb #define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */ #define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */ #define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */ #define HFCUSB_F_THRES 0x0c /* threshold register */ #define HFCUSB_FIFO 0x0f /* fifo select register */ #define HFCUSB_F_USAGE 0x1a /* fifo usage register */ #define HFCUSB_MST_MODE0 0x14 #define HFCUSB_MST_MODE1 0x15 #define HFCUSB_P_DATA 0x1f #define HFCUSB_INC_RES_F 0x0e #define HFCUSB_STATES 0x30 #define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */ /* fifo registers */ #define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */ #define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */ #define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */ #define HFCUSB_B2_TX 2 #define HFCUSB_B2_RX 3 #define HFCUSB_D_TX 4 #define HFCUSB_D_RX 5 #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 */ #define USB_INT 0 #define USB_BULK 1 #define USB_ISOC 2 #define ISOC_PACKETS_D 8 #define ISOC_PACKETS_B 8 #define ISO_BUFFER_SIZE 128 /* defines how much ISO packets are handled in one URB */ static int iso_packets[8] = { ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D }; /* Fifo flow Control for TX ISO */ #define SINK_MAX 68 #define SINK_MIN 48 #define SINK_DMIN 12 #define SINK_DMAX 18 #define BITLINE_INF (-64*8) /* HFC-S USB register access by Control-URSs */ #define write_usb(a,b,c)usb_control_msg((a)->dev,(a)->ctrl_out_pipe,0,0x40,(c),(b),0,0,HFC_CTRL_TIMEOUT) #define read_usb(a,b,c) usb_control_msg((a)->dev,(a)->ctrl_in_pipe,1,0xC0,0,(b),(c),1,HFC_CTRL_TIMEOUT) #define HFC_CTRL_BUFSIZE 32 typedef struct { __u8 hfcs_reg; /* register number */ __u8 reg_val; /* value to be written (or read) */ } ctrl_buft; /* * URB error codes * Used to represent a list of values and their respective symbolic names */ struct hfcusb_symbolic_list { const int num; const char *name; }; static struct hfcusb_symbolic_list urb_errlist[] = { {-ENOMEM, "No memory for allocation of internal structures"}, {-ENOSPC, "The host controller's bandwidth is already consumed"}, {-ENOENT, "URB was canceled by unlink_urb"}, {-EXDEV, "ISO transfer only partially completed"}, {-EAGAIN, "Too match scheduled for the future"}, {-ENXIO, "URB already queued"}, {-EFBIG, "Too much ISO frames requested"}, {-ENOSR, "Buffer error (overrun)"}, {-EPIPE, "Specified endpoint is stalled (device not responding)"}, {-EOVERFLOW, "Babble (bad cable?)"}, {-EPROTO, "Bit-stuff error (bad cable?)"}, {-EILSEQ, "CRC/Timeout"}, {-ETIMEDOUT, "NAK (device does not respond)"}, {-ESHUTDOWN, "Device unplugged"}, {-1, NULL} }; static inline const char * symbolic(struct hfcusb_symbolic_list list[], const int num) { int i; for (i = 0; list[i].name != NULL; i++) if (list[i].num == num) return (list[i].name); return ""; } /* USB descriptor need to contain one of the following EndPoint combination: */ #define CNF_4INT3ISO 1 // 4 INT IN, 3 ISO OUT #define CNF_3INT3ISO 2 // 3 INT IN, 3 ISO OUT #define CNF_4ISO3ISO 3 // 4 ISO IN, 3 ISO OUT #define CNF_3ISO3ISO 4 // 3 ISO IN, 3 ISO OUT #define EP_NUL 1 // Endpoint at this position not allowed #define EP_NOP 2 // all type of endpoints allowed at this position #define EP_ISO 3 // Isochron endpoint mandatory at this position #define EP_BLK 4 // Bulk endpoint mandatory at this position #define EP_INT 5 // Interrupt endpoint mandatory at this position /* * List of all supported enpoints configiration sets, used to find the * best matching endpoint configuration within a devices' USB descriptor. * We need at least 3 RX endpoints, and 3 TX endpoints, either * INT-in and ISO-out, or ISO-in and ISO-out) * with 4 RX endpoints even E-Channel logging is possible */ static int validconf[][19] = { // INT in, ISO out config {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, CNF_4INT3ISO, 2, 1}, {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, CNF_3INT3ISO, 2, 0}, // ISO in, ISO out config {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO, CNF_4ISO3ISO, 2, 1}, {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL, CNF_3ISO3ISO, 2, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // EOL element }; /* string description of chosen config */ char *conf_str[] = { "4 Interrupt IN + 3 Isochron OUT", "3 Interrupt IN + 3 Isochron OUT", "4 Isochron IN + 3 Isochron OUT", "3 Isochron IN + 3 Isochron OUT" }; #define LED_OFF 0 // no LED support #define LED_SCHEME1 1 // LED standard scheme #define LED_SCHEME2 2 // not used yet... #define LED_POWER_ON 1 #define LED_POWER_OFF 2 #define LED_S0_ON 3 #define LED_S0_OFF 4 #define LED_B1_ON 5 #define LED_B1_OFF 6 #define LED_B1_DATA 7 #define LED_B2_ON 8 #define LED_B2_OFF 9 #define LED_B2_DATA 10 #define LED_NORMAL 0 // LEDs are normal #define LED_INVERTED 1 // LEDs are inverted /* time in ms to perform a Flashing LED when B-Channel has traffic */ #define LED_TIME 250 #endif /* __HFCS_USB_H__ */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/fsm.h0000644000000000000500000000226111110524073017317 0ustar rootsrc/* $Id: fsm.h,v 1.2 2004/01/30 23:46:37 keil Exp $ * * This file is (c) under GNU PUBLIC LICENSE * */ #ifndef _MISDN_FSM_H #define _MISDN_FSM_H #include /* Statemachine */ struct FsmInst; typedef void (* FSMFNPTR)(struct FsmInst *, int, void *); struct Fsm { FSMFNPTR *jumpmatrix; int state_count, event_count; char **strEvent, **strState; }; struct FsmInst { struct Fsm *fsm; int state; int debug; void *userdata; int userint; void (*printdebug) (struct FsmInst *, char *, ...); }; struct FsmNode { int state, event; void (*routine) (struct FsmInst *, int, void *); }; struct FsmTimer { struct FsmInst *fi; struct timer_list tl; int event; void *arg; }; extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); extern void mISDN_FsmFree(struct Fsm *); extern int mISDN_FsmEvent(struct FsmInst *, int , void *); extern void mISDN_FsmChangeState(struct FsmInst *, int); extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *); extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int); extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int); extern void mISDN_FsmDelTimer(struct FsmTimer *, int); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/l1oip.c0000644000000000000500000010737511135651702017574 0ustar rootsrc/* * l1oip.c low level driver for tunneling layer 1 over IP * * NOTE: It is not compatible with TDMoIP nor "ISDN over IP". * * Author Andreas Eversberg (jolly@eversberg.eu) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* module parameters: * type: Value 1 = BRI Value 2 = PRI Value 3 = BRI (multi channel frame) Value 4 = PRI (multi channel frame) A multi channel frame reduces overhead to a single frame for all b-channels, but increases delay. * codec: Value 0 = aLaw transparent Value 1 = uLaw transparent (instead of aLaw) Value 2 = aLaw to TADPCM Value 3 = uLaw to TADPCM * protocol: Bit 0-3 = protocol Bit 4 = NT-Mode Bit 5 = PTP (instead of multipoint) * layermask: NOTE: Must be given for all ports, not for the number of cards. mask of layers to be used for D-channel stack * limit: limitation of bchannels to control bandwidth (1...29) * ip: binary representation of remote ip address (127.0.0.0 -> 0x7f000001) If not given, no remote address is set. * port: port number If not given or 0, port 931 is used. * id: mandatory value to identify frames. This value must be equal on both peers and should be random. * debug: NOTE: only one debug value must be given for all cards enable debugging (see l1oip.h for debug options) Special PH_CONTROL messages: dinfo = L1OIP_SETPEER data bytes 0-3 : IP address in network order (MSB first) data bytes 4-5 : local port in network order data bytes 6-7 : remote port in network order dinfo = L1OIP_UNSETPEER * Use l1oipctrl for setting or removin ip address L1oIP-Protocol -------------- The Layer 1 over IP protocol tunnels frames and audio streams over IP. It will be directly attached to the layer 2 or interconnected to layer 1 of a different stack. It also provides layer 1 control and keeps dynamic IP connectivity up. Frame structure: +---------------------------------------------------------------+ | ID | +---------------+---------------+-------------------------------+ | Coding |B| Channel | Time Base / Layer 1 message | +---------------+---------------+-------------------------------+ | Channel Map | +---------------------------------------------------------------+ | | . Data . . . The "ID" should be a random number. It makes shure that missrouted frames get dropped due to wrong id. Also it provides simple security agains DOS attacs. The "Coding" byte defines the data format. It can be 0 HDLC-data 1 TADPCM (table ADPCM) 2 A-law 3 u-law The "B"-Flag shows the interface type: 0 BRI 1 PRI The "Channel" will give the timeslot or channel number. 0 Layer 1 message 1-2 B-Channel for BRI interface 1-15 B-Channel 1-15 for PRI interface 16 D-Channel for PRI and BRI interface 17-31 B-Channel 17-31 for PRI interface 127 B-Channels as given by "Channel Map" The "Time Base" is used to rearange packets and to detect packet loss. The 16 bits are sent in network order (MSB first) and count 1/8000 th of a second. This causes a wrap arround each 8,192 seconds. There is no requirement for the initial "Time Base", but 0 should be used for the first packet. The "Channel Map" are 4 bytes in network order (MSB first). They only exist, if the Channel Map was selected with the Channel value. Bits 1-31 represent the existance of data for each channel Bit 0 is not used and shall be 0. The length of each channel data is defined by the total number of bytes divided by the number of bits set in the Channel Map. The coding and length must be equal for all existing channels. NOTE: D-Channel data must be sent via seperate frame, because length and coding are differen. Also packet mode data should be sent in a seperate frame. The total length of data is defined by the maximum packet size (without header). Validity check at the receiver: Packets will be dropped if - the length is less than 4 bytes. - there is no data in the packet. - the Coding is not supported. False coding should produce a warning once. - the B-flag does not equal the expected interface type. - the channel is out of range. - the channel does not exists by interface. A warning should be produced once. - the channel map is selected, but length is less than 8 bytes. - the channel map's bit 0 is set if channel map is selected. - the channel map is completely 0, but the packet has data anyway. - the length of data is not a multiple of the channels indicated by channel map. - the data exceeds the maximum frame length of the ISDN driver. - the layer 1 message is unknown. Layer 1 Message: This is a special type of frame. In this case the "Time Base" contains two bytes with the message. The first byte (MSB) contains the sequence number and the second byte the message. The sequence number is used to detect the reception of a message. If the message is received, the new sequence number is acknowledged using message 0 (keepalive) If the keepalive is received with the sequence number last sent, the next message can be sent with incremented sequence number. If no message is to be sent, the sequence number is not incremented and the last sequence number is repeated. The keepalive is sent every 10 seconds. If a message is about to be sent, the message is repeated every second until the keepalive is received with the incremented sequence number. 0,x IP link keepalive. X is a sequence to detect packet loss. 1,1 Activate layer 1 1,0 Deactivate layer 1 2,1 AIS on (alarm on the remote interface) 2,0 AIS off 3,1 Maintainance blocked 3,0 Maintainance unblocked 16,x Application specific information. 32,0 Announce new IP 32,1 Acknowledge new IP IP Announcement: One or both sides may have dynamic IP address. A simple trick is used to get the remote IP if it changes. It is assumed, that both sides don't change their IP at the same time. If IP changes, the peer must announce it's new IP address. A layer 1 message with the new IP address (4 extra bytes) and the "peer's password" (more extra bytes). The transmission interval is one second. The remote peer will receive the new IP address with the password. If the password matches, the new IP will be used. The passwort is used to prevent "take over" connections. An acknowledge will be generated, by the remote peer to make the local peer stop sending "Announces". The initial value will be given by application. It is only required for one peer to give the initial IP address. After an IP address is given, it will be announced. */ #include #include #include "dchannel.h" #include "bchannel.h" #include "layer1.h" #include "dsp.h" #include "debug.h" #include #include "l1oip.h" //static void ph_state_change(dchannel_t *dch); extern const char *CardType[]; static const char *l1oip_revision = "$Revision: 1.8 $"; static int l1oip_cnt; static mISDNobject_t l1oip_obj; static char l1oipName[] = "Layer1oIP"; /****************/ /* module stuff */ /****************/ #define MAX_CARDS 16 static u_int type[MAX_CARDS]; static u_int codec[MAX_CARDS]; static u_int protocol[MAX_CARDS]; static int layermask[MAX_CARDS]; static int debug; #ifdef MODULE MODULE_AUTHOR("Andreas Eversberg"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(codec, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(protocol, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, NULL, S_IRUGO | S_IWUSR); module_param(debug, uint, S_IRUGO | S_IWUSR); #endif /********************/ /* D-channel access */ /********************/ locking bedenken /* message transfer from layer 2 */ static int l1oip_dchannel(mISDNinstance_t *inst, struct sk_buff *skb) { dchannel_t *dch = container_of(inst, dchannel_t, inst); l1oip_t *hc; int ret = 0; mISDN_head_t *hh; u_long flags; hh = mISDN_HEAD_P(skb); hc = dch->inst.privat; if (hh->prim == PH_DATA_REQ) { /* check oversize */ if (skb->len <= 0) { printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__); return(-EINVAL); } if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > MAX_L1OIP_LEN) { printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__); return(-EINVAL); } /* check for pending next_skb */ spin_lock_irqsave(inst->hwlock, flags); if (dch->next_skb) { printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", __FUNCTION__, skb->len, dch->next_skb->len); spin_unlock_irqrestore(inst->hwlock, flags); return(-EBUSY); } if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) { test_and_set_bit(FLG_TX_NEXT, &dch->DFlags); dch->next_skb = skb; spin_unlock_irqrestore(inst->hwlock, flags); return(0); } /* send/queue frame */ l1oip_tx(hc, 16, skb, CODEC_L1OIP_DATA); spin_unlock_irqrestore(inst->hwlock, flags); skb_trim(skb, 0); return(mISDN_queueup_newhead(inst, 0, PH_DATA_CNF,hh->dinfo, skb)); } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(inst->hwlock, flags); switch (hh->dinfo) { case L1OIP_SETPEER: lkkllk return(mISDN_queueup_newhead(inst, 0, PH_CONTROL | INDICATION, L1OIP_SETPEER, skb)); break; case L1OIP_UNSETPEER: lkkllk return(mISDN_queueup_newhead(inst, 0, PH_CONTROL | INDICATION, L1OIP_UNSETPEER, skb)); break; default: printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(inst->hwlock, flags); } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) { if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: PH_ACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->pri?30:2); spin_lock_irqsave(inst->hwlock, flags); /* start activation */ if (pri) { //dchannel_sched_event(dch, D_L1STATECHANGE); ph_state_change(dch); if (debug & DEBUG_L1OIP_STATE) printk(KERN_DEBUG "%s: E1 report state %x \n", __FUNCTION__, dch->ph_state); } else { HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port); HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); /* G1 */ udelay(6); /* wait at least 5,21us */ HFC_outb(hc, A_ST_WR_STATE, 1); HFC_outb(hc, A_ST_WR_STATE, 1 | (V_ST_ACT*3)); /* activate */ dch->ph_state = 1; } spin_unlock_irqrestore(inst->hwlock, flags); } else { if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: PH_ACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->pri?30:2); ret = -EINVAL; } } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) { if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: PH_DEACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->pri?30:2); spin_lock_irqsave(inst->hwlock, flags); hw_deactivate: /* after lock */ dch->ph_state = 0; /* start deactivation */ if (hc->pri) { if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: PH_DEACTIVATE no BRI\n", __FUNCTION__); } else { HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port); HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); /* deactivate */ } if (dch->next_skb) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } dch->tx_idx = dch->tx_len = hc->chan[dch->channel].rx_idx = 0; test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags); test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags); if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags)) del_timer(&dch->dbusytimer); spin_unlock_irqrestore(inst->hwlock, flags); } else { if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: PH_DEACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->pri?30:2); ret = -EINVAL; } } else if (hh->prim == MGR_SHORTSTATUS) { if(hh->dinfo==SSTATUS_ALL || hh->dinfo==SSTATUS_L1) { int new_addr; if(hh->dinfo&SSTATUS_BROADCAST_BIT) new_addr= dch->inst.id | MSG_BROADCAST; else new_addr=hh->addr | FLG_MSG_TARGET; return(mISDN_queueup_newhead(inst, new_addr, MGR_SHORTSTATUS,(dch->l1_up) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED, skb)); } } else { if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: unknown prim %x\n", __FUNCTION__, hh->prim); ret = -EINVAL; } if (!ret) { // printk("1\n"); dev_kfree_skb(skb); // printk("2\n"); } return(ret); } /******************************/ /* Layer2 -> Layer 1 Transfer */ /******************************/ /* messages from layer 2 to layer 1 are processed here. */ static int l1oip_bchannel(mISDNinstance_t *inst, struct sk_buff *skb) { u_long flags, num; int slot_tx, slot_rx, bank_tx, bank_rx; bchannel_t *bch = container_of(inst, bchannel_t, inst); int ret = -EINVAL; mISDN_head_t *hh; hfc_multi_t *hc; struct dsp_features *features; hh = mISDN_HEAD_P(skb); hc = bch->inst.privat; if ((hh->prim == PH_DATA_REQ) || (hh->prim == (DL_DATA | REQUEST))) { if (skb->len <= 0) { printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__); return(-EINVAL); } if (skb->len > MAX_DATA_MEM) { printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__); return(-EINVAL); } /* check for pending next_skb */ spin_lock_irqsave(inst->hwlock, flags); if (bch->next_skb) { printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", __FUNCTION__, skb->len, bch->next_skb->len); spin_unlock_irqrestore(inst->hwlock, flags); return(-EBUSY); } /* if we have currently a pending tx skb */ if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) { test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag); bch->next_skb = skb; spin_unlock_irqrestore(inst->hwlock, flags); return(0); } /* write to fifo */ bch->tx_len = skb->len; memcpy(bch->tx_buf, skb->data, bch->tx_len); bch->tx_idx = 0; hfcmulti_tx(hc, bch->channel, NULL, bch); /* start fifo */ HFC_outb_(hc, R_FIFO, 0); HFC_wait_(hc); spin_unlock_irqrestore(inst->hwlock, flags); #ifdef FIXME // TODO changed if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) && bch->dev) hif = &bch->dev->rport.pif; else hif = &bch->inst.up; #endif skb_trim(skb, 0); return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, hh->dinfo, skb)); } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) || (hh->prim == (DL_ESTABLISH | REQUEST))) { /* activate B-channel if not already activated */ if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel); if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag)) ret = 0; else { spin_lock_irqsave(inst->hwlock, flags); ret = mode_hfcmulti(hc, bch->channel, bch->inst.pid.protocol[1], hc->chan[bch->channel].slot_tx, hc->chan[bch->channel].bank_tx, hc->chan[bch->channel].slot_rx, hc->chan[bch->channel].bank_rx); if (!ret) { bch->protocol = bch->inst.pid.protocol[1]; if (bch->protocol==ISDN_PID_L1_B_64TRANS && !hc->dtmf) { /* start decoder */ hc->dtmf = 1; if (debug & DEBUG_L1OIP_DTMF) printk(KERN_DEBUG "%s: start dtmf decoder\n", __FUNCTION__); HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); } } spin_unlock_irqrestore(inst->hwlock, flags); } #ifdef FIXME // TODO changed if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif skb_trim(skb, 0); return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, ret, skb)); } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || (hh->prim == (DL_RELEASE | REQUEST)) || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: PH_DEACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel); /* deactivate B-channel if not already deactivated */ spin_lock_irqsave(inst->hwlock, flags); if (bch->next_skb) { test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag); dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } bch->tx_idx = bch->tx_len = bch->rx_idx = 0; test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag); hc->chan[bch->channel].slot_tx = -1; hc->chan[bch->channel].slot_rx = -1; hc->chan[bch->channel].conf = -1; mode_hfcmulti(hc, bch->channel, ISDN_PID_NONE, hc->chan[bch->channel].slot_tx, hc->chan[bch->channel].bank_tx, hc->chan[bch->channel].slot_rx, hc->chan[bch->channel].bank_rx); bch->protocol = ISDN_PID_NONE; test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag); spin_unlock_irqrestore(inst->hwlock, flags); skb_trim(skb, 0); //printk("5\n"); if (hh->prim != (PH_CONTROL | REQUEST)) { #ifdef FIXME // TODO changed if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, ret, skb)); //printk("6\n"); } //printk("7\n"); ret = 0; } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(inst->hwlock, flags); switch (hh->dinfo) { /* fill features structure */ case HW_FEATURES: if (skb->len != sizeof(void *)) { printk(KERN_WARNING "%s: HW_FEATURES lacks parameters\n", __FUNCTION__); break; } if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: HW_FEATURE request\n", __FUNCTION__); features = *((struct dsp_features **)skb->data); features->hfc_id = hc->id; if (test_bit(HFC_CHIP_DTMF, &hc->chip)) features->hfc_dtmf = 1; features->hfc_loops = 0; features->pcm_id = hc->pcm; features->pcm_slots = hc->slots; features->pcm_banks = 2; ret = 0; break; /* connect interface to pcm timeslot (0..N) */ case HW_PCM_CONN: if (skb->len < 4*sizeof(u_long)) { printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__); break; } slot_tx = ((int *)skb->data)[0]; bank_tx = ((int *)skb->data)[1]; slot_rx = ((int *)skb->data)[2]; bank_rx = ((int *)skb->data)[3]; if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); if (slot_tx<=hc->slots && bank_tx<=2 && slot_rx<=hc->slots && bank_rx<=2) hfcmulti_pcm(hc, bch->channel, slot_tx, bank_tx, slot_rx, bank_rx); else printk(KERN_WARNING "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX) out of range\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx); ret = 0; break; /* release interface from pcm timeslot */ case HW_PCM_DISC: if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: HW_PCM_DISC\n", __FUNCTION__); hfcmulti_pcm(hc, bch->channel, -1, -1, -1, -1); ret = 0; break; /* join conference (0..7) */ case HW_CONF_JOIN: if (skb->len < sizeof(u_long)) { printk(KERN_WARNING "%s: HW_CONF_JOIN lacks parameters\n", __FUNCTION__); break; } num = ((u_long *)skb->data)[0]; if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: HW_CONF_JOIN conf %ld\n", __FUNCTION__, num); if (num <= 7) { hfcmulti_conf(hc, bch->channel, num); ret = 0; } else printk(KERN_WARNING "%s: HW_CONF_JOIN conf %ld out of range\n", __FUNCTION__, num); break; /* split conference */ case HW_CONF_SPLIT: if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: HW_CONF_SPLIT\n", __FUNCTION__); hfcmulti_conf(hc, bch->channel, -1); ret = 0; break; /* set sample loop */ case HW_SPL_LOOP_ON: if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: HW_SPL_LOOP_ON (len = %d)\n", __FUNCTION__, skb->len); hfcmulti_splloop(hc, bch->channel, skb->data, skb->len); ret = 0; break; /* set silence */ case HW_SPL_LOOP_OFF: if (debug & DEBUG_L1OIP_MSG) printk(KERN_DEBUG "%s: HW_SPL_LOOP_OFF\n", __FUNCTION__); hfcmulti_splloop(hc, bch->channel, NULL, 0); ret = 0; break; default: printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(inst->hwlock, flags); } else { printk(KERN_WARNING "%s: unknown prim(%x)\n", __FUNCTION__, hh->prim); ret = -EINVAL; } if (!ret) { // printk("3\n"); dev_kfree_skb(skb); // printk("4\n"); } return(ret); } /* MGR stuff */ static int l1oip_manager(void *data, u_int prim, void *arg) { hfc_multi_t *hc; mISDNinstance_t *inst = data; struct sk_buff *skb; dchannel_t *dch = NULL; bchannel_t *bch = NULL; int ch; int i; u_long flags; if (!data) { MGR_HASPROTOCOL_HANDLER(prim,arg,&HFCM_obj) printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); return(-EINVAL); } /* find channel and card */ spin_lock_irqsave(&HFCM_obj.lock, flags); if (hc->dch[i]) if (&hc->dch[i]->inst == inst) { dch = hc->dch[i]; ch = dch->channel; break; } list_for_each_entry(hc, &HFCM_obj.ilist, list) { i = 0; while(i < 30) { //printk(KERN_DEBUG "comparing (D-channel) card=%08x inst=%08x with inst=%08x\n", hc, &hc->dch[i].inst, inst); if (hc->bch[i]) if (&hc->bch[i]->inst == inst) { bch = hc->bch[i]; ch = dch->channel; goto found; } i++; } } spin_unlock_irqrestore(&HFCM_obj.lock, flags); printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return(-EINVAL); found: spin_unlock_irqrestore(&HFCM_obj.lock, flags); if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: channel %d (0..31) data %p prim %x arg %p\n", __FUNCTION__, ch, data, prim, arg); switch(prim) { case MGR_REGLAYER | CONFIRM: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_REGLAYER\n", __FUNCTION__); if (dch) dch_set_para(dch, &inst->st->para); if (bch) bch_set_para(bch, &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_UNREGLAYER\n", __FUNCTION__); if (dch) { if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { if (hfcmulti_l1hw(inst, skb)) dev_kfree_skb(skb); } } else if (bch) { if ((skb = create_link_skb(PH_CONTROL | REQUEST, 0, 0, NULL, 0))) { if (hfcmulti_l2l1(inst, skb)) dev_kfree_skb(skb); } } mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; // fall through case MGR_ADDSTPARA | INDICATION: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_***STPARA\n", __FUNCTION__); if (dch) dch_set_para(dch, arg); if (bch) bch_set_para(bch, arg); break; case MGR_RELEASE | INDICATION: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_RELEASE = remove port from mISDN\n", __FUNCTION__); if (dch) { release_network(hc); release_card(hc); } break; #ifdef FIXME case MGR_CONNECT | REQUEST: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_CONNECT\n", __FUNCTION__); return(mISDN_ConnectIF(inst, arg)); case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_SETIF\n", __FUNCTION__); if (dch) return(mISDN_SetIF(inst, arg, prim, hfcmulti_l1hw, NULL, dch)); if (bch) return(mISDN_SetIF(inst, arg, prim, hfcmulti_l2l1, NULL, bch)); break; case MGR_DISCONNECT | REQUEST: case MGR_DISCONNECT | INDICATION: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_DISCONNECT\n", __FUNCTION__); return(mISDN_DisConnectIF(inst, arg)); #endif case MGR_SELCHANNEL | REQUEST: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_SELCHANNEL\n", __FUNCTION__); if (!dch) { printk(KERN_WARNING "%s(MGR_SELCHANNEL|REQUEST): selchannel not dinst\n", __FUNCTION__); return(-EINVAL); } return(SelFreeBChannel(hc, ch, arg)); case MGR_SETSTACK | INDICATION: if (debug & DEBUG_L1OIP_MGR) printk(KERN_DEBUG "%s: MGR_SETSTACK\n", __FUNCTION__); if (bch && inst->pid.global==2) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (hfcmulti_l2l1(inst, skb)) dev_kfree_skb(skb); } if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } break; PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST); default: printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim); return(-EINVAL); } return(0); } /************************** * remove card from stack * **************************/ static void release_card(hfc_multi_t *hc) { int i = 0; u_long flags; if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: entered\n", __FUNCTION__); if (hc->dch) { if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: free port %d D-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, i); mISDN_free_dch(hc->chan[i].dch); mISDN_ctrl(&hc->chan[i].dch->inst, MGR_UNREGLAYER | REQUEST, NULL); kfree(hc->chan[i].dch); hc->chan[i].dch = NULL; } // if (hc->chan[i].rx_buf) { // kfree(hc->chan[i].rx_buf); // hc->chan[i].rx_buf = NULL; // } i = 0; while(i < 30) { if (hc->bch[i]) { if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: free port %d B-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, hc->bch[i].channel); mISDN_free_bch(hc->bch[i]); kfree(hc->bch[i]); hc->bch[i] = NULL; } i++; } /* remove us from list and delete */ if (debug & DEBUG_L1OIP_INIT) printk(KERN_WARNING "%s: remove instance from list\n", __FUNCTION__); spin_lock_irqsave(&HFCM_obj.lock, flags); list_del(&hc->list); spin_unlock_irqrestore(&HFCM_obj.lock, flags); if (debug & DEBUG_L1OIP_INIT) printk(KERN_WARNING "%s: delete instance\n", __FUNCTION__); kfree(hc); HFC_cnt--; if (debug & DEBUG_L1OIP_INIT) printk(KERN_WARNING "%s: card successfully removed\n", __FUNCTION__); } static void __exit l1oip_cleanup(void) { l1oip_t *hc,*next; int err; /* unregister mISDN object */ if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: entered (refcnt = %d l1oip_cnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt, l1oip_cnt); if ((err = mISDN_unregister(&l1oip_obj))) { printk(KERN_ERR "Can't unregister L1oIP error(%d)\n", err); } /* remove remaining devices, but this should never happen */ if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: now checking ilist (refcnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt); list_for_each_entry_safe(hc, next, &l1oip_obj.ilist, list) { printk(KERN_ERR "L1oIP devices struct not empty refs %d\n", l1oip_obj.refcnt); release_network(hc); release_card(hc); } if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: done (refcnt = %d l1oip_cnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt, l1oip_cnt); } static int __init l1oip_init(void) { int err, i; char tmpstr[64]; #if !defined(CONFIG_HOTPLUG) || !defined(MODULE) #error "CONFIG_HOTPLUG and MODULE are not defined." #endif if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: init entered\n", __FUNCTION__); #ifdef __BIG_ENDIAN #error "not running on big endian machines now" #endif strcpy(tmpstr, l1oip_revision); printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n", mISDN_getrev(tmpstr)); memset(&l1oip_obj, 0, sizeof(l1oip_obj)); #ifdef MODULE l1oip_obj.owner = THIS_MODULE; #endif spin_lock_init(&l1oip_obj.lock); INIT_LIST_HEAD(&l1oip_obj.ilist); l1oip_obj.name = l1oipName; l1oip_obj.own_ctrl = l1oip_manager; l1oip_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0 | ISDN_PID_L0_TE_E1 | ISDN_PID_L0_NT_E1; l1oip_obj.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0 | ISDN_PID_L1_NT_S0 | ISDN_PID_L1_TE_E1 | ISDN_PID_L1_NT_E1; l1oip_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; l1oip_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: registering l1oip_obj\n", __FUNCTION__); if ((err = mISDN_register(&l1oip_obj))) { printk(KERN_ERR "Can't register L1oIP error(%d)\n", err); return(err); } if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: new mISDN object (refcnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt); l1oip_cnt = 0; /* check card type */ switch (type[l1oip_cnt] & 0xff) { case 1: pri = 0; multichannel = 0; break; case 2: pri = 1; multichannel = 0; break; case 3: pri = 0; multichannel = 1; break; case 4: pri = 1; multichannel = 1; break; case 0: printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt); return(0); default: printk(KERN_ERR "Card type(%d) not supported.\n", type[HFC_idx] & 0xff); ret_err = -EINVAL; goto free_object; } /* allocate card+fifo structure */ if (!(hc = kmalloc(sizeof(l1oip_t), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for L1-over-IP driver.\n"); ret_err = -ENOMEM; goto free_object; } memset(hc, 0, sizeof(hfc_multi_t)); hc->idx = l1oip_cnt; hc->pri = pri; hc->multichannel = multichannel; hc->limit = limit[l1oip_cnt]; if (hc->pri) sprintf(hc->name, "L1oIP-E1#%d", HFC_cnt+1); else sprintf(hc->name, "L1oIP-S0#%d", HFC_cnt+1); if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); spin_lock_irqsave(&HFCM_obj.lock, flags); list_add_tail(&hc->list, &HFCM_obj.ilist); spin_unlock_irqrestore(&HFCM_obj.lock, flags); if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__); spin_lock_init(&hc->lock); /* check codec */ switch (codec[l1oip_cnt]) { case 0: break; case 1: hc->ulaw = 1; break; case 2: hc->tadpcm = 1; break; case 3: hc->ulaw = 1; hc->tadpcm = 1; break; default: printk(KERN_ERR "Codec(%d) not supported.\n", codec[l1oip_cnt]); ret_err = -EINVAL; goto free_channels; } if (id[l1oip_cnt] == 0) { printk(KERN_ERR "No 'id' value given. Please use 32 bit randmom number 0x...\n"); ret_err = -EINVAL; goto free_channels; } /* check protocol */ if (protocol[l1oip_cnt] == 0) { printk(KERN_ERR "No 'protocol' value given.\n"); ret_err = -EINVAL; goto free_channels; } if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: Registering D-channel, card(%d) protocol(%x)\n", __FUNCTION__, l1oip_cnt+1, protocol[l1oip_cnt]); dch = kmalloc(sizeof(dchannel_t), GFP_ATOMIC); if (!dch) { ret_err = -ENOMEM; goto free_channels; } memset(dch, 0, sizeof(dchannel_t)); if (hc->pri) dch->channel = 16; //dch->debug = debug; dch->inst.obj = &l1oip_obj; dch->inst.hwlock = &hc->lock; dch->inst.class_dev.dev = &pdev->dev; mISDN_init_instance(&dch->inst, &l1oip_obj, hc, l1oip_dchannel); dch->inst.pid.layermask = ISDN_LAYER(0); sprintf(dch->inst.name, "L1OIP%d", l1oip_cnt); // if (!(hc->chan[ch].rx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) { // ret_err = -ENOMEM; // goto free_channels; // } if (mISDN_init_dch(dch)) { ret_err = -ENOMEM; goto free_channels; } hc->dch = dch; i=0; while(i < ((hc->pri)?30:2)) { if (hc->pri) ch = i + 1 + (i>=15); else ch = i + 1; if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: Registering B-channel, card(%d) channel(%d)\n", __FUNCTION__, l1oip_cnt, ch); bch = kmalloc(sizeof(bchannel_t), GFP_ATOMIC); if (!bch) { ret_err = -ENOMEM; goto free_channels; } memset(bch, 0, sizeof(bchannel_t)); bch->channel = ch; mISDN_init_instance(&bch->inst, &l1oip_obj, hc, l1oip_bchannel); bch->inst.pid.layermask = ISDN_LAYER(0); bch->inst.hwlock = &hc->lock; bch->inst.class_dev.dev = &pdev->dev; //bch->debug = debug; sprintf(bch->inst.name, "%s B%d", dch->inst.name, i+1); if (mISDN_init_bch(bch)) { kfree(bch); ret_err = -ENOMEM; goto free_channels; } hc->bch[i] = bch; #ifdef FIXME // TODO if (bch->dev) { bch->dev->wport.pif.func = l1oip_bchannel; bch->dev->wport.pif.fdata = bch; } #endif i++; } /* set D-channel */ mISDN_set_dchannel_pid(&pid, protocol[l1oip_cnt], layermask[l1oip_cnt]); /* set PRI */ if (hc->pri == 1) { if (layermask[l1oip_cnt] & ISDN_LAYER(2)) { pid.protocol[2] |= ISDN_PID_L2_DF_PTP; } if (layermask[l1oip_cnt] & ISDN_LAYER(3)) { pid.protocol[3] |= ISDN_PID_L3_DF_PTP; pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID; pid.protocol[3] |= ISDN_PID_L3_DF_CRLEN2; } } /* set protocol type */ dch->inst.pid.protocol[0] = (hc->pri)?ISDN_PID_L0_IP_E1:ISDN_PID_L0_IP_S0; pid.protocol[0] = (hc->pri)?ISDN_PID_L0_IP_E1:ISDN_PID_L0_IP_S0; if (protocol[l1oip_cnt] & 0x10) { /* NT-mode */ dch->inst.pid.protocol[1] = (hc->pri)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0; pid.protocol[1] = (hc->pri)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0; if (layermask[l1oip_cnt] & ISDN_LAYER(2)) pid.protocol[2] = ISDN_PID_L2_LAPD_NET; } else { /* TE-mode */ dch->inst.pid.protocol[1] = (hc->pri)?ISDN_PID_L1_TE_E1:ISDN_PID_L1_TE_S0; pid.protocol[1] = (hc->pri)?ISDN_PID_L1_TE_E1:ISDN_PID_L1_TE_S0; } dch->inst.pid.layermask |= ISDN_LAYER(1); pid.layermask |= ISDN_LAYER(1); /* run card setup */ if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: Setting up network(%d)\n", __FUNCTION__, l1oip_cnt+1); if ((ret_err = setup_network(hc))) { goto free_channels; } /* add stacks */ if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: Adding d-stack: card(%d)\n", __FUNCTION__, l1oip_cnt+1); if ((ret_err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &dch->inst))) { printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", ret_err); free_release: release_network(hc); goto free_object; } dst = dch->inst.st; i=0; while(i < ((hc->pri)?30:2)) { bch = hc->bch; if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: Adding b-stack: card(%d) B-channel(%d)\n", __FUNCTION__, l1oip_cnt+1, bch->channel); if ((ret_err = mISDN_ctrl(dst, MGR_NEWSTACK | REQUEST, &bch->inst))) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", ret_err); free_delstack: mISDN_ctrl(dst, MGR_DELSTACK | REQUEST, NULL); goto free_release; } bch->st = bch->inst.st; i++; } if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: (before MGR_SETSTACK REQUEST) layermask=0x%x\n", __FUNCTION__, pids[pt].layermask); if ((ret_err = mISDN_ctrl(dst, MGR_SETSTACK | REQUEST, &pids[pt]))) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", ret_err); goto free_delstack; } if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: (after MGR_SETSTACK REQUEST)\n", __FUNCTION__); /* delay some time */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ /* tell stack, that we are ready */ mISDN_ctrl(dst, MGR_CTRLREADY | INDICATION, NULL); HFC_cnt++; goto next_card; /* if an error ocurred */ free_channels: if (hc->dch) { if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: free D-channel %d (1..32)\n", __FUNCTION__, i); mISDN_free_dch(hc->dch); kfree(hc->dch); hc->dch = NULL; } // if (hc->rx_buf) { // kfree(hc->rx_buf); // hc->rx_buf = NULL; // } i = 0; while(i < 30) { if (hc->bch[i]) { if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: free B-channel %d (1..32)\n", __FUNCTION__, hc->bch[i].channel); mISDN_free_bch(hc->bch[i]); kfree(hc->bch[i]); hc->bch[i] = NULL; } i++; } if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: before REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt); spin_lock_irqsave(&l1oip_obj.lock, flags); list_del(&hc->list); spin_unlock_irqrestore(&l1oip_obj.lock, flags); if (debug & DEBUG_L1OIP_INIT) printk(KERN_DEBUG "%s: after REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, l1oip_obj.refcnt); kfree(hc); free_object: l1oip_cleanup(); return(ret_err); } #ifdef MODULE module_init(l1oip_init); module_exit(l1oip_cleanup); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/helper.h0000644000000000000500000002002611110524073020010 0ustar rootsrc/* $Id: helper.h,v 1.16 2006/08/07 23:35:59 keil Exp $ * * Basic declarations, defines and prototypes * * This file is released under the GPLv2 * */ #ifndef _mISDN_HELPER_H #define _mISDN_HELPER_H #include #ifdef MISDN_MEMDEBUG #include "memdbg.h" #endif /* shortcut to report errors locations, sometime also used for debugging !FIXME! */ #define int_error() \ printk(KERN_ERR "mISDN: INTERNAL ERROR in %s:%d\n", \ __FILE__, __LINE__) /* shortcut to report errors locations with an additional message text */ #define int_errtxt(fmt, arg...) \ printk(KERN_ERR "mISDN: INTERNAL ERROR in %s:%d " fmt "\n", \ __FILE__, __LINE__, ## arg) /* cleanup SKB queues, return count of dropped packets */ static inline int discard_queue(struct sk_buff_head *q) { struct sk_buff *skb; int ret=0; while ((skb = skb_dequeue(q))) { dev_kfree_skb(skb); ret++; } return(ret); } /* allocate a SKB for DATA packets in the mISDN stack with enough headroom * the MEMDEBUG version is for debugging memory leaks in the mISDN stack */ #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(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(u_int size, u_int reserve) { struct sk_buff *skb; if (!(skb = alloc_skb(size + reserve, GFP_ATOMIC))) #endif printk(KERN_WARNING "%s(%d,%d): no skb size\n", __FUNCTION__, size, reserve); else skb_reserve(skb, reserve); return(skb); } /* * mISDN_set_dchannel_pid(mISDN_pid_t *pid, int protocol, int layermask) * * set default values for the D-channel protocol ID struct * * layermask - bitmask which layers should be set (default 0,1,2,3,4) * protocol - bitmask for special L2/L3 option (from protocol module parameter of L0 modules) */ extern void mISDN_set_dchannel_pid(mISDN_pid_t *, int, int); /* * int mISDN_get_lowlayer(int layermask) * * get the lowest layer number of a layermask * e.g layermask=0x0c returns 2 */ extern int mISDN_get_lowlayer(int); /* * int mISDN_get_up_layer(int layermask) * * get the next higher layer number, which is not part of the given layermask */ extern int mISDN_get_up_layer(int); /* * int mISDN_get_down_layer(int layermask) * * get the next lower layer number, which is not part of the given layermask */ extern int mISDN_get_down_layer(int); /* * int mISDN_layermask2layer(int layermask) * * translate bit position in layermask into the layernumber * only valid if only one bit in the mask was set */ extern int mISDN_layermask2layer(int); /* * int mISDN_get_protocol(mISDNstack_t *st, int layer) * * get the protocol value of layer in stack */ extern int mISDN_get_protocol(mISDNstack_t *, int); /* * int mISDN_HasProtocol(mISDNobject_t *obj, u_int protocol) * * test if given object can handle protocol * * return 0 if yes * */ extern int mISDN_HasProtocol(mISDNobject_t *, u_int); /* * int mISDN_SetHandledPID(mISDNobject_t *obj, mISDN_pid_t *pid) * * returns the layermask of the supported protocols of object * from the protocol ID struct */ extern int mISDN_SetHandledPID(mISDNobject_t *, mISDN_pid_t *); /* * mISDN_RemoveUsedPID(mISDN_pid_t *pid, mISDN_pid_t *used) * * remove the protocol values from struct which are also in the * struct */ extern void mISDN_RemoveUsedPID(mISDN_pid_t *, mISDN_pid_t *); /* * mISDN_init_instance(mISDNinstance_t *inst, mISDNobject_t *obj, void *data) * * initialisize the mISDNinstance_t struct */ extern void mISDN_init_instance(mISDNinstance_t *, mISDNobject_t *, void *, if_func_t *); /* returns the member count of a list */ static inline int count_list_member(struct list_head *head) { int cnt = 0; struct list_head *m; list_for_each(m, head) cnt++; return(cnt); } /* same as mISDN_HasProtocol, but for a pointer */ static inline int mISDN_HasProtocolP(mISDNobject_t *obj, int *PP) { if (!PP) { int_error(); return(-EINVAL); } return(mISDN_HasProtocol(obj, *PP)); } /* set primitiv and dinfo field of a internal (SKB) mISDN message */ static inline void mISDN_sethead(u_int prim, int dinfo, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); hh->prim = prim; hh->dinfo = dinfo; } #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 mISDN_queueup_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_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, 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, 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, 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 printk(KERN_WARNING "%s: no skb size %d+%d\n", __FUNCTION__, len, reserve); return(NULL); } else skb_reserve(skb, reserve); if (len) memcpy(skb_put(skb, len), dp, len); hh = mISDN_HEAD_P(skb); hh->prim = prim; hh->dinfo = dinfo; hh->len = len; return(skb); } /* allocate a SKB for a mISDN message with enough headroom * fill mesage data into this SKB and send it trough the interface * the MEMDEBUG version is for debugging memory leaks in the mISDN stack */ #ifdef MISDN_MEMDEBUG #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_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, dp, reserve, fn, line))) #else static inline int 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, dp, reserve))) #endif return(-ENOMEM); err = mISDN_queue_message(inst, aflag, skb); if (err) kfree_skb(skb); return(err); } /* L3 data struct helper functions */ extern signed int mISDN_l3_ie2pos(u_char); extern unsigned char mISDN_l3_pos2ie(int); extern void mISDN_initQ931_info(Q931_info_t *); #ifdef MISDN_MEMDEBUG #define mISDN_alloc_l3msg(a, b) __mid_alloc_l3msg(a, b, __FILE__, __LINE__) extern struct sk_buff *__mid_alloc_l3msg(int, u_char, char *, int); #else 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 *); extern int mISDN_add_pid_parameter(mISDN_pid_t *, int, u_char *); /* manager default handler helper macros */ #define PRIM_NOT_HANDLED(p) case p: break #define MGR_HASPROTOCOL_HANDLER(p,a,o) \ if ((MGR_HASPROTOCOL | REQUEST) == p) {\ if (a) {\ int prot = *((int *)a);\ return(mISDN_HasProtocol(o, prot));\ } else \ return(-EINVAL);\ } #endif /* _mISDN_HELPER_H */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/hfcsmcc.h0000755000000000000500000010702511135651702020156 0ustar rootsrc/*___________________________________________________________________________________*/ /* */ /* (C) Copyright Cologne Chip AG, 2005 */ /*___________________________________________________________________________________*/ /* */ /* */ /* File name: hfcsmcc.h */ /* File content: This file contains the HFC-S mini register definitions. */ /* Creation date: 24.10.2005 10:45 */ /* Creator: Genero 3.2 */ /* Data base: HFC XML 1.6 for HFC-S mini and HFC-S USB (unreleased) */ /* Address range: 0x00 - 0xFC */ /* */ /* The information presented can not be considered as assured characteristics. */ /* Data can change without notice. Please check version numbers in case of doubt. */ /* */ /* For further information or questions please contact support@CologneChip.com */ /* */ /* */ /*___________________________________________________________________________________*/ /* */ /* WARNING: This file has been generated automatically and should not be */ /* changed to maintain compatibility with later versions. */ /*___________________________________________________________________________________*/ /* */ #ifndef _HFCSMCC_H_ #define _HFCSMCC_H_ typedef unsigned char BYTE; typedef BYTE REGWORD; // maximum register length (standard) typedef BYTE REGADDR; // address width /*___________________________________________________________________________________*/ /* */ /* The following definitions are only used for multi-register access and can be */ /* switched off. Define MULTIREG below if you want to use multi-register accesses. */ /*___________________________________________________________________________________*/ /* */ #define MULTIREG /*___________________________________________________________________________________*/ #ifdef MULTIREG typedef unsigned short REGWORD16; // multi-register access, 2 registers typedef unsigned int REGWORD32; // multi-register access, 4 registers #endif typedef enum {no=0, yes} REGBOOL; typedef enum { // register and bitmap access modes: writeonly=0, // write only readonly, // read only readwrite, // read/write // following modes only for mixed mode registers: readwrite_write, // read/write and write only readwrite_read, // read/write and read only write_read, // write only and read only readwrite_write_read // read/write, write only and read only } ACCESSMODE; /*___________________________________________________________________________________*/ /* */ /* common chip information: */ /*___________________________________________________________________________________*/ #define CHIP_NAME "HFC-S mini" #define CHIP_TITLE "ISDN HDLC FIFO controller with S/T interface and integrated FIFOs" #define CHIP_MANUFACTURER "Cologne Chip" #define CHIP_ID 0x05 #define CHIP_REGISTER_COUNT 71 #define CHIP_DATABASE "Version HFC-XMLHFC XML 1.6 for HFC-S mini and HFC-S USB (unreleased) -GeneroGenero 3.2 " /*___________________________________________________________________________________*/ /* */ /* Begin of HFC-S mini register definitions. */ /*___________________________________________________________________________________*/ /* */ #define R_CIRM 0x00 // register access #define M_SRES 0x08 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD reserved_28:3; REGWORD v_sres:1; REGWORD reserved_29:4; } bit_r_cirm; // register and bitmap data typedef union {REGWORD reg; bit_r_cirm bit;} reg_r_cirm; // register and bitmap access #define A_Z1 0x04 // register access #define M_Z1 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_z1:8; } bit_a_z1; // register and bitmap data typedef union {REGWORD reg; bit_a_z1 bit;} reg_a_z1; // register and bitmap access #define A_Z2 0x06 // register access #define M_Z2 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_z2:8; } bit_a_z2; // register and bitmap data typedef union {REGWORD reg; bit_a_z2 bit;} reg_a_z2; // register and bitmap access #define R_RAM_ADDR0 0x08 // register access #define M_RAM_ADDR0 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_ram_addr0:8; } bit_r_ram_addr0; // register and bitmap data typedef union {REGWORD reg; bit_r_ram_addr0 bit;} reg_r_ram_addr0; // register and bitmap access #define R_RAM_ADDR1 0x09 // register access #define M_RAM_ADDR1 0x07 // bitmap mask (3bit) #define M1_RAM_ADDR1 0x01 #define M_ADDR_RES 0x40 // bitmap mask (1bit) #define M_ADDR_INC 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_ram_addr1:3; REGWORD reserved_0:3; REGWORD v_addr_res:1; REGWORD v_addr_inc:1; } bit_r_ram_addr1; // register and bitmap data typedef union {REGWORD reg; bit_r_ram_addr1 bit;} reg_r_ram_addr1; // register and bitmap access #define R_FIFO_REV 0x0B // register access #define M_FIFO0_TX_REV 0x01 // bitmap mask (1bit) #define M_FIFO0_RX_REV 0x02 // bitmap mask (1bit) #define M_FIFO1_TX_REV 0x04 // bitmap mask (1bit) #define M_FIFO1_RX_REV 0x08 // bitmap mask (1bit) #define M_FIFO2_TX_REV 0x10 // bitmap mask (1bit) #define M_FIFO2_RX_REV 0x20 // bitmap mask (1bit) #define M_FIFO3_TX_REV 0x40 // bitmap mask (1bit) #define M_FIFO3_RX_REV 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo0_tx_rev:1; REGWORD v_fifo0_rx_rev:1; REGWORD v_fifo1_tx_rev:1; REGWORD v_fifo1_rx_rev:1; REGWORD v_fifo2_tx_rev:1; REGWORD v_fifo2_rx_rev:1; REGWORD v_fifo3_tx_rev:1; REGWORD v_fifo3_rx_rev:1; } bit_r_fifo_rev; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_rev bit;} reg_r_fifo_rev; // register and bitmap access #define A_F1 0x0C // register access #define M_F1 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_f1:8; } bit_a_f1; // register and bitmap data typedef union {REGWORD reg; bit_a_f1 bit;} reg_a_f1; // register and bitmap access #define R_FIFO_THRES 0x0C // register access #define M_THRES_TX 0x0F // bitmap mask (4bit) #define M1_THRES_TX 0x01 #define M_THRES_RX 0xF0 // bitmap mask (4bit) #define M1_THRES_RX 0x10 typedef struct // bitmap construction { REGWORD v_thres_tx:4; REGWORD v_thres_rx:4; } bit_r_fifo_thres; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_thres bit;} reg_r_fifo_thres; // register and bitmap access #define A_F2 0x0D // register access #define M_F2 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_f2:8; } bit_a_f2; // register and bitmap data typedef union {REGWORD reg; bit_a_f2 bit;} reg_a_f2; // register and bitmap access #define R_DF_MD 0x0D // register access #define M_CSM 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD reserved_1:7; REGWORD v_csm:1; } bit_r_df_md; // register and bitmap data typedef union {REGWORD reg; bit_r_df_md bit;} reg_r_df_md; // register and bitmap access #define A_INC_RES_FIFO 0x0E // register access #define M_INC_F 0x01 // bitmap mask (1bit) #define M_RES_FIFO 0x02 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_inc_f:1; REGWORD v_res_fifo:1; REGWORD reserved_2:6; } bit_a_inc_res_fifo; // register and bitmap data typedef union {REGWORD reg; bit_a_inc_res_fifo bit;} reg_a_inc_res_fifo; // register and bitmap access #define R_FIFO 0x0F // register access #define M_FIFO_DIR 0x01 // bitmap mask (1bit) #define M_FIFO_NUM 0x06 // bitmap mask (2bit) #define M1_FIFO_NUM 0x02 typedef struct // bitmap construction { REGWORD v_fifo_dir:1; REGWORD v_fifo_num:2; REGWORD reserved_3:5; } bit_r_fifo; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo bit;} reg_r_fifo; // register and bitmap access #define R_FIFO_IRQ 0x10 // register access #define M_FIFO0_TX_IRQ 0x01 // bitmap mask (1bit) #define M_FIFO0_RX_IRQ 0x02 // bitmap mask (1bit) #define M_FIFO1_TX_IRQ 0x04 // bitmap mask (1bit) #define M_FIFO1_RX_IRQ 0x08 // bitmap mask (1bit) #define M_FIFO2_TX_IRQ 0x10 // bitmap mask (1bit) #define M_FIFO2_RX_IRQ 0x20 // bitmap mask (1bit) #define M_FIFO3_TX_IRQ 0x40 // bitmap mask (1bit) #define M_FIFO3_RX_IRQ 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo0_tx_irq:1; REGWORD v_fifo0_rx_irq:1; REGWORD v_fifo1_tx_irq:1; REGWORD v_fifo1_rx_irq:1; REGWORD v_fifo2_tx_irq:1; REGWORD v_fifo2_rx_irq:1; REGWORD v_fifo3_tx_irq:1; REGWORD v_fifo3_rx_irq:1; } bit_r_fifo_irq; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_irq bit;} reg_r_fifo_irq; // register and bitmap access #define R_MISC_IRQ 0x11 // register access #define M_ST_IRQ 0x01 // bitmap mask (1bit) #define M_TI_IRQ 0x02 // bitmap mask (1bit) #define M_PROC_IRQ 0x04 // bitmap mask (1bit) #define M_CI_IRQ 0x08 // bitmap mask (1bit) #define M_MON_RX_IRQ 0x10 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_st_irq:1; REGWORD v_ti_irq:1; REGWORD v_proc_irq:1; REGWORD v_ci_irq:1; REGWORD v_mon_rx_irq:1; REGWORD reserved_31:3; } bit_r_misc_irq; // register and bitmap data typedef union {REGWORD reg; bit_r_misc_irq bit;} reg_r_misc_irq; // register and bitmap access #define R_PCM_MD0 0x14 // register access #define M_PCM_MD 0x01 // bitmap mask (1bit) #define M_C4_POL 0x02 // bitmap mask (1bit) #define M_F0_NEG 0x04 // bitmap mask (1bit) #define M_F0_LEN 0x08 // bitmap mask (1bit) #define M_SL_CODECA 0x30 // bitmap mask (2bit) #define M1_SL_CODECA 0x10 #define M_SL_CODECB 0xC0 // bitmap mask (2bit) #define M1_SL_CODECB 0x40 typedef struct // bitmap construction { REGWORD v_pcm_md:1; REGWORD v_c4_pol:1; REGWORD v_f0_neg:1; REGWORD v_f0_len:1; REGWORD v_sl_codeca:2; REGWORD v_sl_codecb:2; } bit_r_pcm_md0; // register and bitmap data typedef union {REGWORD reg; bit_r_pcm_md0 bit;} reg_r_pcm_md0; // register and bitmap access #define R_PCM_MD1 0x15 // register access #define M_AUX1_MIR 0x01 // bitmap mask (1bit) #define M_AUX2_MIR 0x02 // bitmap mask (1bit) #define M_PLL_ADJ 0x0C // bitmap mask (2bit) #define M1_PLL_ADJ 0x04 #define M_PCM_DR 0x30 // bitmap mask (2bit) #define M1_PCM_DR 0x10 #define M_PCM_LOOP 0x40 // bitmap mask (1bit) #define M_GCI_EN 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_aux1_mir:1; REGWORD v_aux2_mir:1; REGWORD v_pll_adj:2; REGWORD v_pcm_dr:2; REGWORD v_pcm_loop:1; REGWORD v_gci_en:1; } bit_r_pcm_md1; // register and bitmap data typedef union {REGWORD reg; bit_r_pcm_md1 bit;} reg_r_pcm_md1; // register and bitmap access #define R_PCM_MD2 0x16 // register access #define M_OKI_CODECA 0x01 // bitmap mask (1bit) #define M_OKI_CODECB 0x02 // bitmap mask (1bit) #define M_SYNC_SRC 0x04 // bitmap mask (1bit) #define M_SYNC_OUT 0x08 // bitmap mask (1bit) #define M_SL_BL 0x30 // bitmap mask (2bit) #define M1_SL_BL 0x10 #define M_PLL_ICR 0x40 // bitmap mask (1bit) #define M_PLL_MAN 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_oki_codeca:1; REGWORD v_oki_codecb:1; REGWORD v_sync_src:1; REGWORD v_sync_out:1; REGWORD v_sl_bl:2; REGWORD v_pll_icr:1; REGWORD v_pll_man:1; } bit_r_pcm_md2; // register and bitmap data typedef union {REGWORD reg; bit_r_pcm_md2 bit;} reg_r_pcm_md2; // register and bitmap access #define R_CHIP_ID 0x16 // register access #define M_CHIP_ID 0xF0 // bitmap mask (4bit) #define M1_CHIP_ID 0x10 typedef struct // bitmap construction { REGWORD reserved_22:4; REGWORD v_chip_id:4; } bit_r_chip_id; // register and bitmap data typedef union {REGWORD reg; bit_r_chip_id bit;} reg_r_chip_id; // register and bitmap access #define R_F0_CNTL 0x18 // register access #define M_F0_CNTL 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_f0_cntl:8; } bit_r_f0_cntl; // register and bitmap data typedef union {REGWORD reg; bit_r_f0_cntl bit;} reg_r_f0_cntl; // register and bitmap access #define R_F0_CNTH 0x19 // register access #define M_F0_CNTH 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_f0_cnth:8; } bit_r_f0_cnth; // register and bitmap data typedef union {REGWORD reg; bit_r_f0_cnth bit;} reg_r_f0_cnth; // register and bitmap access #define A_USAGE 0x1A // register access #define M_USAGE 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_usage:8; } bit_a_usage; // register and bitmap data typedef union {REGWORD reg; bit_a_usage bit;} reg_a_usage; // register and bitmap access #define R_FIFO_IRQMSK 0x1A // register access #define M_FIFO0_TX_IRQMSK 0x01 // bitmap mask (1bit) #define M_FIFO0_RX_IRQMSK 0x02 // bitmap mask (1bit) #define M_FIFO1_TX_IRQMSK 0x04 // bitmap mask (1bit) #define M_FIFO1_RX_IRQMSK 0x08 // bitmap mask (1bit) #define M_FIFO2_TX_IRQMSK 0x10 // bitmap mask (1bit) #define M_FIFO2_RX_IRQMSK 0x20 // bitmap mask (1bit) #define M_FIFO3_TX_IRQMSK 0x40 // bitmap mask (1bit) #define M_FIFO3_RX_IRQMSK 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo0_tx_irqmsk:1; REGWORD v_fifo0_rx_irqmsk:1; REGWORD v_fifo1_tx_irqmsk:1; REGWORD v_fifo1_rx_irqmsk:1; REGWORD v_fifo2_tx_irqmsk:1; REGWORD v_fifo2_rx_irqmsk:1; REGWORD v_fifo3_tx_irqmsk:1; REGWORD v_fifo3_rx_irqmsk:1; } bit_r_fifo_irqmsk; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_irqmsk bit;} reg_r_fifo_irqmsk; // register and bitmap access #define R_FILL 0x1B // register access #define M_FIFO0_TX_FILL 0x01 // bitmap mask (1bit) #define M_FIFO0_RX_FILL 0x02 // bitmap mask (1bit) #define M_FIFO1_TX_FILL 0x04 // bitmap mask (1bit) #define M_FIFO1_RX_FILL 0x08 // bitmap mask (1bit) #define M_FIFO2_TX_FILL 0x10 // bitmap mask (1bit) #define M_FIFO2_RX_FILL 0x20 // bitmap mask (1bit) #define M_FIFO3_TX_FILL 0x40 // bitmap mask (1bit) #define M_FIFO3_RX_FILL 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo0_tx_fill:1; REGWORD v_fifo0_rx_fill:1; REGWORD v_fifo1_tx_fill:1; REGWORD v_fifo1_rx_fill:1; REGWORD v_fifo2_tx_fill:1; REGWORD v_fifo2_rx_fill:1; REGWORD v_fifo3_tx_fill:1; REGWORD v_fifo3_rx_fill:1; } bit_r_fill; // register and bitmap data typedef union {REGWORD reg; bit_r_fill bit;} reg_r_fill; // register and bitmap access #define R_MISC_IRQMSK 0x1B // register access #define M_ST_IRQMSK 0x01 // bitmap mask (1bit) #define M_TI_IRQMSK 0x02 // bitmap mask (1bit) #define M_PROC_IRQMSK 0x04 // bitmap mask (1bit) #define M_CI_IRQMSK 0x08 // bitmap mask (1bit) #define M_MON_IRQMSK 0x10 // bitmap mask (1bit) #define M_IRQ_REV 0x40 // bitmap mask (1bit) #define M_IRQ_EN 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_st_irqmsk:1; REGWORD v_ti_irqmsk:1; REGWORD v_proc_irqmsk:1; REGWORD v_ci_irqmsk:1; REGWORD v_mon_irqmsk:1; REGWORD reserved_4:1; REGWORD v_irq_rev:1; REGWORD v_irq_en:1; } bit_r_misc_irqmsk; // register and bitmap data typedef union {REGWORD reg; bit_r_misc_irqmsk bit;} reg_r_misc_irqmsk; // register and bitmap access #define R_TI 0x1C // register access #define M_EV_TS 0x0F // bitmap mask (4bit) #define M1_EV_TS 0x01 typedef struct // bitmap construction { REGWORD v_ev_ts:4; REGWORD reserved_30:4; } bit_r_ti; // register and bitmap data typedef union {REGWORD reg; bit_r_ti bit;} reg_r_ti; // register and bitmap access #define R_STATUS 0x1C // register access #define M_BUSY 0x01 // bitmap mask (1bit) #define M_PROC 0x02 // bitmap mask (1bit) #define M_AWAKE_IN 0x08 // bitmap mask (1bit) #define M_SYNC_IN 0x10 // bitmap mask (1bit) #define M_MISC_IRQSTA 0x40 // bitmap mask (1bit) #define M_FIFO_IRQSTA 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_busy:1; REGWORD v_proc:1; REGWORD reserved_32:1; REGWORD v_awake_in:1; REGWORD v_sync_in:1; REGWORD reserved_33:1; REGWORD v_misc_irqsta:1; REGWORD v_fifo_irqsta:1; } bit_r_status; // register and bitmap data typedef union {REGWORD reg; bit_r_status bit;} reg_r_status; // register and bitmap access #define R_B1_TX_SL 0x20 // register access #define M_B1_TX_SL 0x1F // bitmap mask (5bit) #define M1_B1_TX_SL 0x01 #define M_B1_TX_ROUT 0xC0 // bitmap mask (2bit) #define M1_B1_TX_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_b1_tx_sl:5; REGWORD reserved_5:1; REGWORD v_b1_tx_rout:2; } bit_r_b1_tx_sl; // register and bitmap data typedef union {REGWORD reg; bit_r_b1_tx_sl bit;} reg_r_b1_tx_sl; // register and bitmap access #define R_B2_TX_SL 0x21 // register access #define M_B2_TX_SL 0x1F // bitmap mask (5bit) #define M1_B2_TX_SL 0x01 #define M_B2_TX_ROUT 0xC0 // bitmap mask (2bit) #define M1_B2_TX_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_b2_tx_sl:5; REGWORD reserved_6:1; REGWORD v_b2_tx_rout:2; } bit_r_b2_tx_sl; // register and bitmap data typedef union {REGWORD reg; bit_r_b2_tx_sl bit;} reg_r_b2_tx_sl; // register and bitmap access #define R_AUX1_TX_SL 0x22 // register access #define M_AUX1_TX_SL 0x1F // bitmap mask (5bit) #define M1_AUX1_TX_SL 0x01 #define M_AUX1_TX_ROUT 0xC0 // bitmap mask (2bit) #define M1_AUX1_TX_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_aux1_tx_sl:5; REGWORD reserved_7:1; REGWORD v_aux1_tx_rout:2; } bit_r_aux1_tx_sl; // register and bitmap data typedef union {REGWORD reg; bit_r_aux1_tx_sl bit;} reg_r_aux1_tx_sl; // register and bitmap access #define R_AUX2_TX_SL 0x23 // register access #define M_AUX2_TX_SL 0x1F // bitmap mask (5bit) #define M1_AUX2_TX_SL 0x01 #define M_AUX2_TX_ROUT 0xC0 // bitmap mask (2bit) #define M1_AUX2_TX_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_aux2_tx_sl:5; REGWORD reserved_8:1; REGWORD v_aux2_tx_rout:2; } bit_r_aux2_tx_sl; // register and bitmap data typedef union {REGWORD reg; bit_r_aux2_tx_sl bit;} reg_r_aux2_tx_sl; // register and bitmap access #define R_B1_RX_SL 0x24 // register access #define M_B1_RX_SL 0x1F // bitmap mask (5bit) #define M1_B1_RX_SL 0x01 #define M_B1_RX_ROUT 0xC0 // bitmap mask (2bit) #define M1_B1_RX_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_b1_rx_sl:5; REGWORD reserved_9:1; REGWORD v_b1_rx_rout:2; } bit_r_b1_rx_sl; // register and bitmap data typedef union {REGWORD reg; bit_r_b1_rx_sl bit;} reg_r_b1_rx_sl; // register and bitmap access #define R_B2_RX_SL 0x25 // register access #define M_B2_RX_SL 0x1F // bitmap mask (5bit) #define M1_B2_RX_SL 0x01 #define M_B2_RX_ROUT 0xC0 // bitmap mask (2bit) #define M1_B2_RX_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_b2_rx_sl:5; REGWORD reserved_10:1; REGWORD v_b2_rx_rout:2; } bit_r_b2_rx_sl; // register and bitmap data typedef union {REGWORD reg; bit_r_b2_rx_sl bit;} reg_r_b2_rx_sl; // register and bitmap access #define R_AUX1_RX_SL 0x26 // register access #define M_AUX1_RX_SL 0x1F // bitmap mask (5bit) #define M1_AUX1_RX_SL 0x01 #define M_AUX1_RX_ROUT 0xC0 // bitmap mask (2bit) #define M1_AUX1_RX_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_aux1_rx_sl:5; REGWORD reserved_11:1; REGWORD v_aux1_rx_rout:2; } bit_r_aux1_rx_sl; // register and bitmap data typedef union {REGWORD reg; bit_r_aux1_rx_sl bit;} reg_r_aux1_rx_sl; // register and bitmap access #define R_AUX2_RX_SL 0x27 // register access #define M_AUX2_RX_SL 0x1F // bitmap mask (5bit) #define M1_AUX2_RX_SL 0x01 #define M_AUX2_RX_ROUT 0xC0 // bitmap mask (2bit) #define M1_AUX2_RX_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_aux2_rx_sl:5; REGWORD reserved_12:1; REGWORD v_aux2_rx_rout:2; } bit_r_aux2_rx_sl; // register and bitmap data typedef union {REGWORD reg; bit_r_aux2_rx_sl bit;} reg_r_aux2_rx_sl; // register and bitmap access #define R_CI_RX 0x28 // register access #define M_GCI_I 0x0F // bitmap mask (4bit) #define M1_GCI_I 0x01 typedef struct // bitmap construction { REGWORD v_gci_i:4; REGWORD reserved_23:4; } bit_r_ci_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_ci_rx bit;} reg_r_ci_rx; // register and bitmap access #define R_CI_TX 0x28 // register access #define M_GCI_C 0x0F // bitmap mask (4bit) #define M1_GCI_C 0x01 typedef struct // bitmap construction { REGWORD v_gci_c:4; REGWORD reserved_13:4; } bit_r_ci_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_ci_tx bit;} reg_r_ci_tx; // register and bitmap access #define R_PCM_GCI_STA 0x29 // register access #define M_MON_RXR 0x01 // bitmap mask (1bit) #define M_MON_TXR 0x02 // bitmap mask (1bit) #define M_STIO2_IN 0x40 // bitmap mask (1bit) #define M_STIO1_IN 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_mon_rxr:1; REGWORD v_mon_txr:1; REGWORD reserved_24:4; REGWORD v_stio2_in:1; REGWORD v_stio1_in:1; } bit_r_pcm_gci_sta; // register and bitmap data typedef union {REGWORD reg; bit_r_pcm_gci_sta bit;} reg_r_pcm_gci_sta; // register and bitmap access #define R_MON1_RX 0x2A // register access #define M_MON1_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_mon1_rx:8; } bit_r_mon1_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_mon1_rx bit;} reg_r_mon1_rx; // register and bitmap access #define R_MON1_TX 0x2A // register access #define M_MON1_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_mon1_tx:8; } bit_r_mon1_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_mon1_tx bit;} reg_r_mon1_tx; // register and bitmap access #define R_MON2_RX 0x2B // register access #define M_MON2_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_mon2_rx:8; } bit_r_mon2_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_mon2_rx bit;} reg_r_mon2_rx; // register and bitmap access #define R_MON2_TX 0x2B // register access #define M_MON2_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_mon2_tx:8; } bit_r_mon2_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_mon2_tx bit;} reg_r_mon2_tx; // register and bitmap access #define R_B1_RX 0x2C // register access #define M_B1_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_b1_rx:8; } bit_r_b1_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_b1_rx bit;} reg_r_b1_rx; // register and bitmap access #define R_B1_TX 0x2C // register access #define M_B1_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_b1_tx:8; } bit_r_b1_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_b1_tx bit;} reg_r_b1_tx; // register and bitmap access #define R_B2_RX 0x2D // register access #define M_B2_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_b2_rx:8; } bit_r_b2_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_b2_rx bit;} reg_r_b2_rx; // register and bitmap access #define R_B2_TX 0x2D // register access #define M_B2_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_b2_tx:8; } bit_r_b2_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_b2_tx bit;} reg_r_b2_tx; // register and bitmap access #define R_AUX1_RX 0x2E // register access #define M_AUX1_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_aux1_rx:8; } bit_r_aux1_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_aux1_rx bit;} reg_r_aux1_rx; // register and bitmap access #define R_AUX1_TX 0x2E // register access #define M_AUX1_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_aux1_tx:8; } bit_r_aux1_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_aux1_tx bit;} reg_r_aux1_tx; // register and bitmap access #define R_AUX2_RX 0x2F // register access #define M_AUX2_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_aux2_rx:8; } bit_r_aux2_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_aux2_rx bit;} reg_r_aux2_rx; // register and bitmap access #define R_AUX2_TX 0x2F // register access #define M_AUX2_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_aux2_tx:8; } bit_r_aux2_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_aux2_tx bit;} reg_r_aux2_tx; // register and bitmap access #define R_ST_RD_STA 0x30 // register access #define M_ST_STA 0x0F // bitmap mask (4bit) #define M1_ST_STA 0x01 #define M_FR_SYNC 0x10 // bitmap mask (1bit) #define M_T2_EXP 0x20 // bitmap mask (1bit) #define M_INFO0 0x40 // bitmap mask (1bit) #define M_G2_G3 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_st_sta:4; REGWORD v_fr_sync:1; REGWORD v_t2_exp:1; REGWORD v_info0:1; REGWORD v_g2_g3:1; } bit_r_st_rd_sta; // register and bitmap data typedef union {REGWORD reg; bit_r_st_rd_sta bit;} reg_r_st_rd_sta; // register and bitmap access #define R_ST_WR_STA 0x30 // register access #define M_ST_SET_STA 0x0F // bitmap mask (4bit) #define M1_ST_SET_STA 0x01 #define M_ST_LD_STA 0x10 // bitmap mask (1bit) #define M_ST_ACT 0x60 // bitmap mask (2bit) #define M1_ST_ACT 0x20 #define M_SET_G2_G3 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_st_set_sta:4; REGWORD v_st_ld_sta:1; REGWORD v_st_act:2; REGWORD v_set_g2_g3:1; } bit_r_st_wr_sta; // register and bitmap data typedef union {REGWORD reg; bit_r_st_wr_sta bit;} reg_r_st_wr_sta; // register and bitmap access #define R_ST_CTRL0 0x31 // register access #define M_B1_EN 0x01 // bitmap mask (1bit) #define M_B2_EN 0x02 // bitmap mask (1bit) #define M_ST_MD 0x04 // bitmap mask (1bit) #define M_D_PRIO 0x08 // bitmap mask (1bit) #define M_SQ_EN 0x10 // bitmap mask (1bit) #define M_96KHZ 0x20 // bitmap mask (1bit) #define M_TX_LI 0x40 // bitmap mask (1bit) #define M_ST_STOP 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_b1_en:1; REGWORD v_b2_en:1; REGWORD v_st_md:1; REGWORD v_d_prio:1; REGWORD v_sq_en:1; REGWORD v_96khz:1; REGWORD v_tx_li:1; REGWORD v_st_stop:1; } bit_r_st_ctrl0; // register and bitmap data typedef union {REGWORD reg; bit_r_st_ctrl0 bit;} reg_r_st_ctrl0; // register and bitmap access #define R_ST_CTRL1 0x32 // register access #define M_G2_G3_EN 0x01 // bitmap mask (1bit) #define M_D_RES 0x04 // bitmap mask (1bit) #define M_E_IGNO 0x08 // bitmap mask (1bit) #define M_E_LO 0x10 // bitmap mask (1bit) #define M_B12_SWAP 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_g2_g3_en:1; REGWORD reserved_14:1; REGWORD v_d_res:1; REGWORD v_e_igno:1; REGWORD v_e_lo:1; REGWORD reserved_15:2; REGWORD v_b12_swap:1; } bit_r_st_ctrl1; // register and bitmap data typedef union {REGWORD reg; bit_r_st_ctrl1 bit;} reg_r_st_ctrl1; // register and bitmap access #define R_ST_CTRL2 0x33 // register access #define M_B1_RX_EN 0x01 // bitmap mask (1bit) #define M_B2_RX_EN 0x02 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_b1_rx_en:1; REGWORD v_b2_rx_en:1; REGWORD reserved_16:6; } bit_r_st_ctrl2; // register and bitmap data typedef union {REGWORD reg; bit_r_st_ctrl2 bit;} reg_r_st_ctrl2; // register and bitmap access #define R_ST_SQ_RD 0x34 // register access #define M_ST_SQ_RD 0x0F // bitmap mask (4bit) #define M1_ST_SQ_RD 0x01 #define M_MF_RX_RDY 0x10 // bitmap mask (1bit) #define M_MF_TX_RDY 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_st_sq_rd:4; REGWORD v_mf_rx_rdy:1; REGWORD reserved_25:2; REGWORD v_mf_tx_rdy:1; } bit_r_st_sq_rd; // register and bitmap data typedef union {REGWORD reg; bit_r_st_sq_rd bit;} reg_r_st_sq_rd; // register and bitmap access #define R_ST_SQ_WR 0x34 // register access #define M_ST_SQ_WR 0x0F // bitmap mask (4bit) #define M1_ST_SQ_WR 0x01 typedef struct // bitmap construction { REGWORD v_st_sq_wr:4; REGWORD reserved_17:4; } bit_r_st_sq_wr; // register and bitmap data typedef union {REGWORD reg; bit_r_st_sq_wr bit;} reg_r_st_sq_wr; // register and bitmap access #define R_ST_CLK_DLY 0x37 // register access #define M_ST_CLK_DLY 0x0F // bitmap mask (4bit) #define M1_ST_CLK_DLY 0x01 #define M_ST_SMPL 0x70 // bitmap mask (3bit) #define M1_ST_SMPL 0x10 typedef struct // bitmap construction { REGWORD v_st_clk_dly:4; REGWORD v_st_smpl:3; REGWORD reserved_18:1; } bit_r_st_clk_dly; // register and bitmap data typedef union {REGWORD reg; bit_r_st_clk_dly bit;} reg_r_st_clk_dly; // register and bitmap access #define R_ST_B1_RX 0x3C // register access #define M_ST_B1_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_st_b1_rx:8; } bit_r_st_b1_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_st_b1_rx bit;} reg_r_st_b1_rx; // register and bitmap access #define R_ST_B1_TX 0x3C // register access #define M_ST_B1_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_st_b1_tx:8; } bit_r_st_b1_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_st_b1_tx bit;} reg_r_st_b1_tx; // register and bitmap access #define R_ST_B2_RX 0x3D // register access #define M_ST_B2_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_st_b2_rx:8; } bit_r_st_b2_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_st_b2_rx bit;} reg_r_st_b2_rx; // register and bitmap access #define R_ST_B2_TX 0x3D // register access #define M_ST_B2_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_st_b2_tx:8; } bit_r_st_b2_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_st_b2_tx bit;} reg_r_st_b2_tx; // register and bitmap access #define R_ST_D_RX 0x3E // register access #define M_ST_D_RX 0xC0 // bitmap mask (2bit) #define M1_ST_D_RX 0x40 typedef struct // bitmap construction { REGWORD reserved_26:6; REGWORD v_st_d_rx:2; } bit_r_st_d_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_st_d_rx bit;} reg_r_st_d_rx; // register and bitmap access #define R_ST_D_TX 0x3E // register access #define M_ST_D_TX 0xC0 // bitmap mask (2bit) #define M1_ST_D_TX 0x40 typedef struct // bitmap construction { REGWORD reserved_19:6; REGWORD v_st_d_tx:2; } bit_r_st_d_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_st_d_tx bit;} reg_r_st_d_tx; // register and bitmap access #define R_ST_E_RX 0x3F // register access #define M_ST_E_RX 0xC0 // bitmap mask (2bit) #define M1_ST_E_RX 0x40 typedef struct // bitmap construction { REGWORD reserved_27:6; REGWORD v_st_e_rx:2; } bit_r_st_e_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_st_e_rx bit;} reg_r_st_e_rx; // register and bitmap access #define A_FIFO_DATA 0x80 // register access #define M_FIFO_DATA 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_fifo_data:8; } bit_a_fifo_data; // register and bitmap data typedef union {REGWORD reg; bit_a_fifo_data bit;} reg_a_fifo_data; // register and bitmap access #define A_FIFO_DATA_NOINC 0x84 // register access #define M_FIFO_DATA_NOINC 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_fifo_data_noinc:8; } bit_a_fifo_data_noinc; // register and bitmap data typedef union {REGWORD reg; bit_a_fifo_data_noinc bit;} reg_a_fifo_data_noinc; // register and bitmap access #define R_RAM_DATA 0xC0 // register access #define M_RAM_DATA 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_ram_data:8; } bit_r_ram_data; // register and bitmap data typedef union {REGWORD reg; bit_r_ram_data bit;} reg_r_ram_data; // register and bitmap access #define A_CH_MSK 0xF4 // register access #define M_CH_MSK 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_ch_msk:8; } bit_a_ch_msk; // register and bitmap data typedef union {REGWORD reg; bit_a_ch_msk bit;} reg_a_ch_msk; // register and bitmap access #define A_CON_HDLC 0xFA // register access #define M_IFF 0x01 // bitmap mask (1bit) #define M_HDLC_TRP 0x02 // bitmap mask (1bit) #define M_TRP_IRQ 0x0C // bitmap mask (2bit) #define M1_TRP_IRQ 0x04 #define M_DATA_FLOW 0xE0 // bitmap mask (3bit) #define M1_DATA_FLOW 0x20 typedef struct // bitmap construction { REGWORD v_iff:1; REGWORD v_hdlc_trp:1; REGWORD v_trp_irq:2; REGWORD reserved_20:1; REGWORD v_data_flow:3; } bit_a_con_hdlc; // register and bitmap data typedef union {REGWORD reg; bit_a_con_hdlc bit;} reg_a_con_hdlc; // register and bitmap access #define A_HDLC_PAR 0xFB // register access #define M_BIT_CNT 0x07 // bitmap mask (3bit) #define M1_BIT_CNT 0x01 #define M_START_BIT 0x38 // bitmap mask (3bit) #define M1_START_BIT 0x08 #define M_LOOP_FIFO 0x40 // bitmap mask (1bit) #define M_INV_DATA 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_bit_cnt:3; REGWORD v_start_bit:3; REGWORD v_loop_fifo:1; REGWORD v_inv_data:1; } bit_a_hdlc_par; // register and bitmap data typedef union {REGWORD reg; bit_a_hdlc_par bit;} reg_a_hdlc_par; // register and bitmap access #define A_CHANNEL 0xFC // register access #define M_CH_DIR 0x01 // bitmap mask (1bit) #define M_CH_NUM 0x06 // bitmap mask (2bit) #define M1_CH_NUM 0x02 typedef struct // bitmap construction { REGWORD v_ch_dir:1; REGWORD v_ch_num:2; REGWORD reserved_21:5; } bit_a_channel; // register and bitmap data typedef union {REGWORD reg; bit_a_channel bit;} reg_a_channel; // register and bitmap access #endif /* _HFCSMCC_H_ */ /*___________________________________________________________________________________*/ /* */ /* End of HFC-S mini register definitions. */ /* */ /* Total number of registers processed: 71 of 71 */ /* Total number of bitmaps processed : 209 */ /* */ /*___________________________________________________________________________________*/ /* */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/i4l_mISDN.c0000644000000000000500000011034411135651702020220 0ustar rootsrc/* $Id: i4l_mISDN.c,v 1.12 2007/02/13 10:43:45 crich Exp $ * * interface for old I4L hardware drivers to the CAPI driver * * Copyright (C) 2003 Karsten Keil (kkeil@suse.de) * * Author Karsten Keil (kkeil@suse.de) * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include #include #include #include "fsm.h" #include "helper.h" #include "dss1.h" #include "debug.h" static char *i4lcapi_revision = "$Revision: 1.12 $"; /* data struct */ typedef struct _i4l_channel i4l_channel_t; typedef struct _i4l_capi i4l_capi_t; struct _i4l_channel { mISDNinstance_t inst; i4l_capi_t *drv; int nr; u_int Flags; int cause_loc; int cause_val; u_int l4id; struct FsmInst i4lm; struct sk_buff_head sendq; struct sk_buff_head ackq; }; struct _i4l_capi { i4l_capi_t *prev; i4l_capi_t *next; isdn_if *interface; mISDNinstance_t inst; mISDN_pid_t pid; int idx; int locks; int debug; int nr_ch; i4l_channel_t *ch; }; #define I4L_FLG_LOCK 0 #define I4L_FLG_L1TRANS 1 #define I4L_FLG_L1HDLC 2 #define I4L_FLG_LAYER1 3 #define I4L_FLG_BREADY 4 #define I4L_FLG_BCONN 5 #define I4L_FLG_HANGUP 6 static struct Fsm i4lfsm_s = {NULL, 0, 0, NULL, NULL}; enum { ST_NULL, ST_ICALL, ST_OCALL, ST_PROCEED, ST_ALERT, ST_WAITDCONN, ST_ACTIVD, ST_BREADY, ST_ACTIVB, ST_HANGUP, }; #define STATE_COUNT (ST_HANGUP+1) static char *strI4LState[] = { "ST_NULL", "ST_ICALL", "ST_OCALL", "ST_PROCEED", "ST_ALERT", "ST_WAITDCONN", "ST_ACTIVD", "ST_BREADY", "ST_ACTIVB", "ST_HANGUP", }; enum { EV_I4L_ICALL, EV_I4L_DCONN, EV_I4L_BCONN, EV_I4L_DHUP, EV_I4L_BHUP, EV_I4L_L1ERR, EV_STACKREADY, EV_CAPI_OCALL, EV_CAPI_ALERT, EV_CAPI_PROCEED, EV_CAPI_DCONNECT, EV_CAPI_ESTABLISHB, EV_CAPI_RELEASEB, EV_CAPI_DISCONNECT, EV_CAPI_RELEASE, }; #define EVENT_COUNT (EV_CAPI_RELEASE + 1) static char *strI4LEvent[] = { "EV_I4L_ICALL", "EV_I4L_DCONN", "EV_I4L_BCONN", "EV_I4L_DHUP", "EV_I4L_BHUP", "EV_I4L_L1ERR", "EV_STACKREADY", "EV_CAPI_OCALL", "EV_CAPI_ALERT", "EV_CAPI_PROCEED", "EV_CAPI_DCONNECT", "EV_CAPI_ESTABLISHB", "EV_CAPI_RELEASEB", "EV_CAPI_DISCONNECT", "EV_CAPI_RELEASE", }; static void i4lm_debug(struct FsmInst *fi, char *fmt, ...) { i4l_channel_t *ch = fi->userdata; logdata_t log; va_start(log.args, fmt); log.fmt = fmt; log.head = ch->inst.name; ch->inst.obj->ctrl(&ch->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } #define MAX_CARDS 8 static i4l_capi_t *drvmap[MAX_CARDS]; static mISDNobject_t I4Lcapi; static int debug; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif module_param(debug, uint, S_IRUGO | S_IWUSR); #endif static void i4l_lock_drv(i4l_capi_t *ic) { isdn_ctrl cmd; cmd.driver = ic->idx; cmd.arg = 0; cmd.command = ISDN_CMD_LOCK; ic->interface->command(&cmd); ic->locks++; } static void i4l_unlock_drv(i4l_capi_t *ic) { isdn_ctrl cmd; cmd.driver = ic->idx; cmd.arg = 0; cmd.command = ISDN_CMD_UNLOCK; ic->interface->command(&cmd); ic->locks--; } static int i4l_cmd(i4l_capi_t *ic, int arg, int cmd) { isdn_ctrl ctrl; ctrl.driver = ic->idx; ctrl.arg = arg; ctrl.command = cmd; return(ic->interface->command(&ctrl)); } static void init_channel(i4l_capi_t *ic, int nr) { i4l_channel_t *ch; ch = ic->ch + nr; memset(ch, 0, sizeof(i4l_channel_t)); ch->nr = nr; ch->drv = ic; ch->i4lm.debug = debug & 0x8; ch->i4lm.userdata = ch; ch->i4lm.userint = 0; ch->i4lm.printdebug = i4lm_debug; ch->i4lm.fsm = &i4lfsm_s; ch->i4lm.state = ST_NULL; skb_queue_head_init(&ch->sendq); skb_queue_head_init(&ch->ackq); ch->inst.obj = &I4Lcapi; ch->inst.data = ch; ch->inst.pid.layermask = ISDN_LAYER(0); ch->inst.up.owner = &ch->inst; ch->inst.down.owner = &ch->inst; mISDN_ctrl(NULL, MGR_DISCONNECT | REQUEST, &ch->inst.down); sprintf(ch->inst.name, "%s B%d", ic->inst.name, nr+1); } static void reset_channel(i4l_channel_t *ch) { ch->cause_loc = 0; ch->cause_val = 0; ch->l4id = 0; skb_queue_purge(&ch->sendq); skb_queue_purge(&ch->ackq); if (test_and_clear_bit(I4L_FLG_LOCK, &ch->Flags)) i4l_unlock_drv(ch->drv); ch->Flags = 0; } static void release_card(int idx) { i4l_capi_t *ic = drvmap[idx]; i4l_channel_t *ch; int i; mISDNinstance_t *inst; if (!ic) return; drvmap[idx] = NULL; ch = ic->ch; for (i=0; inr_ch; i++) { inst = &ch->inst; if (inst->up.peer) inst->up.peer->obj->ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); reset_channel(ch); ch++; } inst = &ic->inst; if (inst->up.peer) { inst->up.peer->obj->ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); } REMOVE_FROM_LISTBASE(ic, ((i4l_capi_t *)I4Lcapi.ilist)); while (ic->locks > 0) i4l_unlock_drv(ic); kfree(ic->ch); ic->ch = NULL; kfree(ic); I4Lcapi.refcnt--; } static int sendup(i4l_channel_t *ch, int Dchannel, int prim, struct sk_buff *skb) { int ret; mISDN_headext_t *hhe; mISDNinstance_t *I; if (!skb) { skb = alloc_skb(8, GFP_ATOMIC); if (!skb) return(-ENOMEM); } hhe = mISDN_HEADEXT_P(skb); hhe->prim = prim; hhe->dinfo = ch->l4id; if (ch->drv->debug & 0x4) mISDN_LogL3Msg(skb); if (Dchannel) I = &ch->drv->inst; else I = &ch->inst; if (!I->up.func) { int_error(); dev_kfree_skb(skb); return(-EUNATCH); } if (in_interrupt()) { hhe->func.iff = I->up.func; hhe->data[0] = &I->up; ret = I->obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb); } else ret = I->up.func(&I->up, skb); if (ret) dev_kfree_skb(skb); return(ret); } static int sendqueued(i4l_channel_t *ch) { struct sk_buff *skb, *s_skb; int len, ret; if (!test_bit(I4L_FLG_BCONN, &ch->Flags)) { if (ch->drv->debug & 0x40) printk(KERN_DEBUG "%s: bc%d not ready\n", __FUNCTION__, ch->nr); return(0); } while ((skb = skb_dequeue(&ch->sendq))) { s_skb = skb_clone(skb, GFP_ATOMIC); len = s_skb->len; skb_queue_tail(&ch->ackq, skb); ret = ch->drv->interface->writebuf_skb(ch->drv->idx, ch->nr, 1, s_skb); if (ch->drv->debug & 0x800) printk(KERN_DEBUG "bc%d sent skb(%p) %d(%d)\n", ch->nr, skb, ret, len); if (ret == len) { continue; } else if (ret > 0) { skb_queue_head(&ch->sendq, s_skb); break; } else { skb_unlink(skb); skb_queue_head(&ch->sendq, skb); if (!ret) dev_kfree_skb(s_skb); break; } } return(0); } static u_char * EncodeASyncParams(u_char * p, u_char si2) { // 7c 06 88 90 21 42 00 bb p[0] = 0; p[1] = 0x40; // Intermediate rate: 16 kbit/s jj 2000.02.19 p[2] = 0x80; if (si2 & 32) // 7 data bits p[2] += 16; else // 8 data bits p[2] += 24; if (si2 & 16) // 2 stop bits p[2] += 96; else // 1 stop bit p[2] += 32; if (si2 & 8) // even parity p[2] += 2; else // no parity p[2] += 3; switch (si2 & 0x07) { case 0: p[0] = 66; // 1200 bit/s break; case 1: p[0] = 88; // 1200/75 bit/s break; case 2: p[0] = 87; // 75/1200 bit/s break; case 3: p[0] = 67; // 2400 bit/s break; case 4: p[0] = 69; // 4800 bit/s break; case 5: p[0] = 72; // 9600 bit/s break; case 6: p[0] = 73; // 14400 bit/s break; case 7: p[0] = 75; // 19200 bit/s break; } return p + 3; } static u_char EncodeSyncParams(u_char si2, u_char ai) { switch (si2) { case 0: return ai + 2; // 1200 bit/s case 1: return ai + 24; // 1200/75 bit/s case 2: return ai + 23; // 75/1200 bit/s case 3: return ai + 3; // 2400 bit/s case 4: return ai + 5; // 4800 bit/s case 5: return ai + 8; // 9600 bit/s case 6: return ai + 9; // 14400 bit/s case 7: return ai + 11; // 19200 bit/s case 8: return ai + 14; // 48000 bit/s case 9: return ai + 15; // 56000 bit/s case 15: return ai + 40; // negotiate bit/s default: break; } return ai; } static u_char DecodeASyncParams(u_char si2, u_char * p) { u_char info; switch (p[5]) { case 66: // 1200 bit/s break; // si2 don't change case 88: // 1200/75 bit/s si2 += 1; break; case 87: // 75/1200 bit/s si2 += 2; break; case 67: // 2400 bit/s si2 += 3; break; case 69: // 4800 bit/s si2 += 4; break; case 72: // 9600 bit/s si2 += 5; break; case 73: // 14400 bit/s si2 += 6; break; case 75: // 19200 bit/s si2 += 7; break; } info = p[7] & 0x7f; if ((info & 16) && (!(info & 8))) // 7 data bits si2 += 32; // else 8 data bits if ((info & 96) == 96) // 2 stop bits si2 += 16; // else 1 stop bit if ((info & 2) && (!(info & 1))) // even parity si2 += 8; // else no parity return si2; } static u_char DecodeSyncParams(u_char si2, u_char info) { switch (info & 0x7f) { case 40: // bit/s negotiation failed ai := 165 not 175! return si2 + 15; case 15: // 56000 bit/s failed, ai := 0 not 169 ! return si2 + 9; case 14: // 48000 bit/s return si2 + 8; case 11: // 19200 bit/s return si2 + 7; case 9: // 14400 bit/s return si2 + 6; case 8: // 9600 bit/s return si2 + 5; case 5: // 4800 bit/s return si2 + 4; case 3: // 2400 bit/s return si2 + 3; case 23: // 75/1200 bit/s return si2 + 2; case 24: // 1200/75 bit/s return si2 + 1; default: // 1200 bit/s return si2; } } static u_char DecodeSI2(u_char *p) { if (p) { switch (p[4] & 0x0f) { case 0x01: if (p[1] == 0x04) // sync. Bitratenadaption return DecodeSyncParams(160, p[5]); // V.110/X.30 else if (p[1] == 0x06) // async. Bitratenadaption return DecodeASyncParams(192, p); // V.110/X.30 break; case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption if (p[1] > 3) return DecodeSyncParams(176, p[5]); // V.120 break; } } return 0; } static void i4l_l1err(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; sendup(ch, 1, DL_RELEASE | INDICATION, NULL); reset_channel(ch); mISDN_FsmChangeState(fi, ST_NULL); } static void i4l_dhup(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; struct sk_buff *skb; u_char tmp[8]; skb = mISDN_alloc_l3msg(8, MT_RELEASE); if (!skb) return; tmp[0] = IE_CAUSE; tmp[1] = 2; if (ch->cause_val) { tmp[2] = ch->cause_loc; tmp[3] = ch->cause_val; } else { tmp[2] = 0x80; tmp[3] = 0x9f; /* normal, unspecified */ } mISDN_AddvarIE(skb, tmp); sendup(ch, 1, CC_RELEASE | INDICATION, skb); reset_channel(ch); mISDN_FsmChangeState(fi, ST_NULL); } static void i4l_icall(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; setup_parm *setup = arg; u_char tmp[36], *p; struct sk_buff *skb; int i,j; test_and_set_bit(I4L_FLG_LOCK, &ch->Flags); i4l_lock_drv(ch->drv); if ((skb = alloc_skb(sizeof(int *) + 8, GFP_ATOMIC))) { int **idp = (int **)skb_put(skb, sizeof(idp)); mISDN_head_t *hh = mISDN_HEAD_P(skb); *idp = &ch->l4id; hh->prim = CC_NEW_CR | INDICATION; i = ch->drv->inst.up.func(&ch->drv->inst.up, skb); if (i) { int_error(); dev_kfree_skb(skb); return; } if (ch->drv->debug & 0x2) printk(KERN_DEBUG "%s: l4id(%x) ch(%p)->nr %d\n", __FUNCTION__, ch->l4id, ch, ch->nr); } else return; skb = mISDN_alloc_l3msg(260, MT_SETUP); if (!skb) return; p = tmp; switch (setup->si1) { case 1: /* Telephony */ *p++ = IE_BEARER; *p++ = 0x3; /* Length */ *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */ *p++ = 0x90; /* Circuit-Mode 64kbps */ *p++ = 0xa3; /* A-Law Audio */ break; case 5: /* Datatransmission 64k, BTX */ case 7: /* Datatransmission 64k */ default: *p++ = IE_BEARER; *p++ = 0x2; /* Length */ *p++ = 0x88; /* Coding CCITT, unrestr. dig. Info.*/ *p++ = 0x90; /* Circuit-Mode 64kbps */ break; } mISDN_AddvarIE(skb, tmp); tmp[0] = IE_CHANNEL_ID; tmp[1] = 1; tmp[2] = 0x85 + ch->nr; mISDN_AddvarIE(skb, tmp); if (setup->phone[0]) { i = 1; if (setup->plan) { tmp[i++] = setup->plan; if (!(setup->plan & 0x80)) tmp[i++] = setup->screen; } else tmp[i++] = 0x81; j = 0; while (setup->phone[j]) { if (setup->phone[j] == '.') /* subaddress */ break; tmp[i++] = setup->phone[j++]; } tmp[0] = i-1; mISDN_AddIE(skb, IE_CALLING_PN, tmp); if (setup->phone[j] == '.') { i = 1; tmp[i++] = 0x80; j++; while (setup->phone[j]) tmp[i++] = setup->phone[j++]; tmp[0] = i-1; mISDN_AddIE(skb, IE_CALLING_SUB, tmp); } } if (setup->eazmsn[0]) { i = 1; tmp[i++] = 0x81; j = 0; while (setup->eazmsn[j]) { if (setup->eazmsn[j] == '.') /* subaddress */ break; tmp[i++] = setup->eazmsn[j++]; } tmp[0] = i-1; mISDN_AddIE(skb, IE_CALLED_PN, tmp); if (setup->eazmsn[j] == '.') { i = 1; tmp[i++] = 0x80; j++; while (setup->eazmsn[j]) tmp[i++] = setup->eazmsn[j++]; tmp[0] = i-1; mISDN_AddIE(skb, IE_CALLED_SUB, tmp); } } p = tmp; *p++ = IE_LLC; if ((setup->si2 >= 160) && (setup->si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 *p++ = 0x04; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21; *p++ = EncodeSyncParams(setup->si2 - 160, 0x80); test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); } else if ((setup->si2 >= 176) && (setup->si2 <= 191)) { // sync. Bitratenadaption, V.120 *p++ = 0x05; *p++ = 0x88; *p++ = 0x90; *p++ = 0x28; *p++ = EncodeSyncParams(setup->si2 - 176, 0); *p++ = 0x82; test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); } else if (setup->si2 >= 192) { // async. Bitratenadaption, V.110/X.30 *p++ = 0x06; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21; p = EncodeASyncParams(p, setup->si2 - 192); test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); } else { switch(setup->si1) { case 1: *p++ = 0x3; *p++ = 0x90; *p++ = 0x90; *p++ = 0xa3; test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); break; case 5: case 7: default: *p++ = 0x2; *p++ = 0x88; *p++ = 0x90; test_and_set_bit(I4L_FLG_L1HDLC, &ch->Flags); break; } } mISDN_AddvarIE(skb, tmp); mISDN_FsmChangeState(fi, ST_ICALL); sendup(ch, 1, CC_SETUP | INDICATION, skb); } static void i4l_dconn_out(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; struct sk_buff *skb; u_char tmp[4]; skb = mISDN_alloc_l3msg(4, MT_CONNECT); if (!skb) return; tmp[0] = IE_CHANNEL_ID; tmp[1] = 1; tmp[2] = 0x85 + ch->nr; mISDN_AddvarIE(skb, tmp); sendup(ch, 1, CC_CONNECT | INDICATION, skb); mISDN_FsmChangeState(fi, ST_ACTIVD); } static void i4l_dconn_in(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; sendup(ch, 1, CC_CONNECT_ACKNOWLEDGE | INDICATION, NULL); mISDN_FsmChangeState(fi, ST_ACTIVD); } static void i4l_bconn_notready(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; test_and_set_bit(I4L_FLG_BCONN, &ch->Flags); } static void i4l_bconn(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; int prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_ACTIVATE : DL_ESTABLISH; sendup(ch, 0, prim | INDICATION, NULL); test_and_set_bit(I4L_FLG_BCONN, &ch->Flags); mISDN_FsmChangeState(fi, ST_ACTIVB); if (skb_queue_len(&ch->sendq)) sendqueued(ch); } static void i4l_bhup(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; int prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_DEACTIVATE : DL_RELEASE; mISDN_FsmChangeState(fi, ST_ACTIVD); sendup(ch, 0, prim | INDICATION, NULL); } static void stackready(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; mISDN_FsmChangeState(fi, ST_BREADY); test_and_set_bit(I4L_FLG_BREADY, &ch->Flags); if (test_bit(I4L_FLG_BCONN, &ch->Flags)) mISDN_FsmEvent(&ch->i4lm, EV_I4L_BCONN, NULL); } static void capi_ocall(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; struct sk_buff *skb = arg; Q931_info_t *qi = (Q931_info_t *)skb->data; u_char *p, *ps = skb->data; isdn_ctrl ctrl; int i,l; mISDN_FsmChangeState(fi, ST_OCALL); test_and_set_bit(I4L_FLG_LOCK, &ch->Flags); i4l_lock_drv(ch->drv); ps += L3_EXTRA_SIZE; ctrl.parm.setup.si1 = 0; ctrl.parm.setup.si2 = 0; if (qi->bearer_capability) { p = ps + qi->bearer_capability; if ((p[1] > 1) && (p[1] < 11)) { switch (p[2] & 0x7f) { case 0x00: /* Speech */ case 0x10: /* 3.1 Khz audio */ ctrl.parm.setup.si1 = 1; break; case 0x08: /* Unrestricted digital information */ ctrl.parm.setup.si1 = 7; if (qi->llc) ctrl.parm.setup.si2 = DecodeSI2(ps + qi->llc); break; case 0x09: /* Restricted digital information */ ctrl.parm.setup.si1 = 2; break; case 0x11: /* Unrestr. digital information with * tones/announcements ( or 7 kHz audio) */ ctrl.parm.setup.si1 = 3; break; case 0x18: /* Video */ ctrl.parm.setup.si1 = 4; break; } switch (p[3] & 0x7f) { case 0x40: /* packed mode */ ctrl.parm.setup.si1 = 8; break; } } } if ((ctrl.parm.setup.si1 == 7) && (ctrl.parm.setup.si2 < 160)) test_and_set_bit(I4L_FLG_L1HDLC, &ch->Flags); else test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags); i = 0; if (qi->calling_nr) { p = ps + qi->calling_nr + 1; l = *p++; ctrl.parm.setup.plan = *p; l--; if (!(*p & 0x80)) { p++; ctrl.parm.setup.screen = *p; l--; } else ctrl.parm.setup.screen = 0; p++; while(icalling_sub) { p = ps + qi->calling_sub + 1; l = *p++; l--; p++; if (l>0) ctrl.parm.setup.eazmsn[i++] = '.'; while(l>0) { ctrl.parm.setup.eazmsn[i++] = *p++; l--; } ctrl.parm.setup.eazmsn[i] = 0; } i = 0; if (qi->called_nr) { p = ps + qi->called_nr + 1; l = *p++; p++; l--; while(icalled_sub) { p = ps + qi->called_sub + 1; l = *p++; l--; p++; if (l>0) ctrl.parm.setup.phone[i++] = '.'; while(l>0) { ctrl.parm.setup.phone[i++] = *p++; l--; } ctrl.parm.setup.phone[i] = 0; } if (test_bit(I4L_FLG_L1TRANS, &ch->Flags)) { i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_TRANS << 8), ISDN_CMD_SETL2); i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3); } else { i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_HDLC << 8), ISDN_CMD_SETL2); i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3); } if (ch->drv->debug & 0x4) printk(KERN_DEBUG "ocall from %s, si(%d/%d) -> %s\n", ctrl.parm.setup.eazmsn, ctrl.parm.setup.si1, ctrl.parm.setup.si2, ctrl.parm.setup.phone); ctrl.driver = ch->drv->idx; ctrl.arg = ch->nr; ctrl.command = ISDN_CMD_DIAL; ch->drv->interface->command(&ctrl); dev_kfree_skb(skb); } static void capi_alert(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; struct sk_buff *skb = arg; mISDN_FsmChangeState(fi, ST_ALERT); i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ALERT); if (skb) dev_kfree_skb(skb); } static void capi_connect(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; struct sk_buff *skb = arg; if (test_bit(I4L_FLG_L1TRANS, &ch->Flags)) { i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_TRANS << 8), ISDN_CMD_SETL2); i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3); } else { i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_HDLC << 8), ISDN_CMD_SETL2); i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3); } mISDN_FsmChangeState(fi, ST_WAITDCONN); i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ACCEPTD); if (skb) dev_kfree_skb(skb); } static void capi_disconnect(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; struct sk_buff *skb = arg; mISDN_FsmChangeState(fi, ST_HANGUP); test_and_set_bit(I4L_FLG_HANGUP, &ch->Flags); i4l_cmd(ch->drv, ch->nr, ISDN_CMD_HANGUP); if (skb) dev_kfree_skb(skb); } static void capi_release(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; struct sk_buff *skb = arg; if (!test_and_clear_bit(I4L_FLG_HANGUP, &ch->Flags)) i4l_cmd(ch->drv, ch->nr, ISDN_CMD_HANGUP); if (skb) dev_kfree_skb(skb); reset_channel(ch); mISDN_FsmChangeState(fi, ST_NULL); } static void capi_establishb(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ACCEPTB); } static void capi_releaseb(struct FsmInst *fi, int event, void *arg) { i4l_channel_t *ch = fi->userdata; test_and_clear_bit(I4L_FLG_BREADY, &ch->Flags); mISDN_FsmChangeState(fi, ST_ACTIVD); } static int Dchannel_i4l(mISDNif_t *hif, struct sk_buff *skb) { int i, ret = -EINVAL; mISDN_head_t *hh; i4l_capi_t *ic; i4l_channel_t *ch; if (!hif || !skb) return(ret); ic = hif->fdata; hh = mISDN_HEAD_P(skb); if (ic->debug & 0x2) printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", __FUNCTION__, hh->prim, hh->dinfo); if (!ic) return(ret); if ((DL_ESTABLISH | REQUEST) == hh->prim) { // FIXME dev_kfree_skb(skb); return(0); } ch = ic->ch; for (i=0; i < ic->nr_ch; i++) { if (ch->l4id == hh->dinfo) break; ch++; } if (i == ic->nr_ch) ch = NULL; if ((CC_NEW_CR | REQUEST) == hh->prim) { if (ch) { printk(KERN_WARNING "%s: ch%x in use\n", __FUNCTION__, ch->nr); ret = -EBUSY; } else { ch = ic->ch; for (i=0; i < ic->nr_ch; i++) { if (ch->l4id == 0) break; ch++; } if (i == ic->nr_ch) { ret = -EBUSY; } else { ch->l4id = hh->dinfo; ret = 0; dev_kfree_skb(skb); } } return(ret); } if (!ch) { printk(KERN_WARNING "%s: no channel prim(%x) id(%x)\n", __FUNCTION__, hh->prim, hh->dinfo); return(ret); } if (ch->drv->debug & 0x4) mISDN_LogL3Msg(skb); switch(hh->prim) { case CC_SETUP | REQUEST: ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_OCALL, skb); break; case CC_ALERTING | REQUEST: ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_ALERT, skb); break; case CC_CONNECT | REQUEST: ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_DCONNECT, skb); break; case CC_DISCONNECT | REQUEST: case CC_RELEASE | REQUEST: ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_DISCONNECT, skb); break; case CC_RELEASE_COMPLETE | REQUEST: ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_RELEASE, skb); break; default: if (debug) printk(KERN_DEBUG "%s: ch%x prim(%x) id(%x) not handled\n", __FUNCTION__, ch->nr, hh->prim, hh->dinfo); break; } return(ret); } static int Bchannel_i4l(mISDNif_t *hif, struct sk_buff *skb) { i4l_channel_t *ch; int ret = -EINVAL; mISDN_head_t *hh; if (!hif || !skb) return(ret); ch = hif->fdata; hh = mISDN_HEAD_P(skb); if (ch->drv->debug & 0x20) printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); switch(hh->prim) { case PH_ACTIVATE | REQUEST: case DL_ESTABLISH | REQUEST: mISDN_FsmEvent(&ch->i4lm, EV_CAPI_ESTABLISHB, NULL); skb_trim(skb, 0); ret = if_newhead(&ch->inst.up, hh->prim | CONFIRM, 0, skb); break; case PH_DEACTIVATE | REQUEST: case DL_RELEASE | REQUEST: mISDN_FsmEvent(&ch->i4lm, EV_CAPI_RELEASEB, NULL); skb_trim(skb, 0); ret = if_newhead(&ch->inst.up, hh->prim | CONFIRM, 0, skb); break; case PH_DATA | REQUEST: case DL_DATA | REQUEST: skb_queue_tail(&ch->sendq, skb); ret = sendqueued(ch); break; default: if (debug) printk(KERN_DEBUG "%s: ch%x prim(%x) id(%x) not handled\n", __FUNCTION__, ch->nr, hh->prim, hh->dinfo); break; } return(ret); } /* * Receive a packet from B-Channel. (Called from low-level-module) */ static void I4Lcapi_receive_skb_callback(int drvidx, int channel, struct sk_buff *skb) { i4l_capi_t *ic = drvmap[drvidx]; i4l_channel_t *ch; mISDN_headext_t *hhe = mISDN_HEADEXT_P(skb); int ret; if (!ic) { int_error(); return; } ch = ic->ch + channel; if (!test_bit(I4L_FLG_BREADY, &ch->Flags)) { if (ic->debug & 0x10) printk(KERN_WARNING "I4Lcapi_receive_skb_callback: bc%d/%d not ready\n", channel, ch->nr); dev_kfree_skb(skb); return; } hhe->prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_DATA_IND : DL_DATA_IND; if (!ch->inst.up.func) { dev_kfree_skb(skb); int_error(); return; } if (in_interrupt()) { hhe->func.iff = ch->inst.up.func; hhe->data[0] = &ch->inst.up; ret = ch->inst.obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb); } else ret = ch->inst.up.func(&ch->inst.up, skb); if (ret) dev_kfree_skb(skb); } static int i4l_stat_run(i4l_capi_t *ic) { int err; err = mISDN_ctrl(ic->inst.st, MGR_SETSTACK | REQUEST, &ic->pid); if (err) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); mISDN_ctrl(ic->inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } return(0); } static int i4l_sent_pkt(i4l_capi_t *drv, isdn_ctrl *c) { i4l_channel_t *ch = drv->ch; struct sk_buff *skb; int ret; mISDN_headext_t *hhe; if (c->arg < 0) return -1; ch += c->arg; skb = skb_dequeue(&ch->ackq); if (!skb) { int_error(); return(-1); } if (drv->debug & 0x800) printk(KERN_DEBUG "bc%ld ack skb(%p)\n", c->arg, skb); if (skb_queue_len(&ch->sendq)) sendqueued(ch); skb_trim(skb, 0); hhe = mISDN_HEADEXT_P(skb); hhe->prim |= CONFIRM; if (in_interrupt()) { hhe->func.iff = ch->inst.up.func; hhe->data[0] = &ch->inst.up; ret = ch->inst.obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb); } else ret = ch->inst.up.func(&ch->inst.up, skb); if (ret) dev_kfree_skb(skb); return(ret); } #define I4L_LOGBUF_SIZE 256 static char logbuf[I4L_LOGBUF_SIZE]; static int i4l_stavail(i4l_capi_t *drv, isdn_ctrl *c) { int len = c->arg; if (drv->interface->readstat) { while(len>0) { if (len < I4L_LOGBUF_SIZE) { drv->interface->readstat(logbuf, len, 0, drv->idx, 0); logbuf[len] = 0; } else { drv->interface->readstat(logbuf, I4L_LOGBUF_SIZE - 1, 0, drv->idx, 0); logbuf[I4L_LOGBUF_SIZE] = 0; } if (drv->debug & 0x1) printk(KERN_DEBUG "%s", logbuf); len -= (I4L_LOGBUF_SIZE -1); } } return(0); } static int I4Lcapi_status_callback(isdn_ctrl *c) { i4l_capi_t *drv = drvmap[c->driver]; i4l_channel_t *ch; int i, ret = -1; if (!drv) return(-1); if (c->command == ISDN_STAT_BSENT) return(i4l_sent_pkt(drv, c)); if (c->command == ISDN_STAT_STAVAIL) return(i4l_stavail(drv, c)); ch = drv->ch; if (drv->debug & 0x8) printk(KERN_DEBUG "drv%d cmd(%d) arg(%ld)\n", c->driver, c->command, c->arg); switch (c->command) { case ISDN_STAT_RUN: ret = i4l_stat_run(drv); break; case ISDN_STAT_STOP: // FIXME ret = 0; break; case ISDN_STAT_ICALL: if (c->arg < 0) return -1; ch += c->arg; ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_ICALL, &c->parm.setup); break; case ISDN_STAT_CINF: if (c->arg < 0) return -1; // FIXME ret = 0; break; case ISDN_STAT_CAUSE: if (c->arg < 0) return -1; ch += c->arg; if ((c->parm.num[0] == 'E') || (c->parm.num[0] == 'L')) i = 1; else i = 0; sscanf(&c->parm.num[i], "%2X%2X", &ch->cause_loc, &ch->cause_val); ch->cause_loc |= 0x80; ch->cause_val |= 0x80; if (drv->debug & 0x1) printk(KERN_DEBUG "isdn: ch%ld cause: %s %02x%02x\n", c->arg, c->parm.num, ch->cause_loc, ch->cause_val); ret = 0; break; case ISDN_STAT_DISPLAY: // FIXME ret = 0; break; case ISDN_STAT_DCONN: if (c->arg < 0) return -1; ch += c->arg; ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_DCONN, NULL); break; case ISDN_STAT_DHUP: if (c->arg < 0) return -1; ch += c->arg; ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_DHUP, NULL); break; case ISDN_STAT_BCONN: if (c->arg < 0) return -1; ch += c->arg; ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_BCONN, NULL); break; case ISDN_STAT_BHUP: if (c->arg < 0) return -1; ch += c->arg; ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_BHUP, NULL); break; case ISDN_STAT_NODCH: case ISDN_STAT_L1ERR: if (c->arg < 0) return -1; ch += c->arg; ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_L1ERR, NULL); break; case ISDN_STAT_ADDCH: case ISDN_STAT_DISCH: // FIXME ret = 0; break; case ISDN_STAT_UNLOAD: ret = mISDN_ctrl(drv->inst.st, MGR_DELSTACK | REQUEST, NULL); MOD_DEC_USE_COUNT; break; case CAPI_PUT_MESSAGE: // FIXME break; case ISDN_STAT_FAXIND: // FIXME break; case ISDN_STAT_AUDIO: // FIXME break; case ISDN_STAT_PROT: case ISDN_STAT_REDIR: // FIXME break; default: break; } return(ret); } static int I4Lcapi_manager(void *data, u_int prim, void *arg) { i4l_capi_t *card = I4Lcapi.ilist; mISDNinstance_t *inst = data; i4l_channel_t *channel = NULL; int nr_ch = -2; if (debug & 0x100) printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", __FUNCTION__, data, prim, arg); if (prim == (MGR_HASPROTOCOL | REQUEST)) return(mISDN_HasProtocolP(&I4Lcapi, arg)); if (!data) { printk(KERN_ERR "I4Lcapi_manager no data prim %x arg %p\n", prim, arg); return(-EINVAL); } while(card) { if (&card->inst == inst) { nr_ch = -1; break; } channel = card->ch; for (nr_ch = 0; nr_ch < card->nr_ch; nr_ch++) { if (&channel->inst == inst) break; channel++; } if (nr_ch != card->nr_ch) break; card = card->next; channel = NULL; nr_ch = -2; } if (nr_ch == -2) { printk(KERN_ERR "I4Lcapi_manager no channel data %p prim %x arg %p\n", data, prim, arg); return(-EINVAL); } switch(prim) { case MGR_REGLAYER | CONFIRM: break; case MGR_UNREGLAYER | REQUEST: mISDN_ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_RELEASE | INDICATION: if (nr_ch == -1) { release_card(card->idx); } else { I4Lcapi.refcnt--; } break; case MGR_CONNECT | REQUEST: return(mISDN_ConnectIF(inst, arg)); case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: if (nr_ch == -1) return(mISDN_SetIF(inst, arg, prim, Dchannel_i4l, NULL, card)); else return(mISDN_SetIF(inst, arg, prim, Bchannel_i4l, NULL, channel)); case MGR_DISCONNECT | REQUEST: case MGR_DISCONNECT | INDICATION: return(mISDN_DisConnectIF(inst, arg)); 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); mISDN_FsmEvent(&channel->i4lm, EV_STACKREADY, NULL); } break; default: if (debug) printk(KERN_DEBUG "I4Lcapi_manager prim %x not handled\n", prim); return(-EINVAL); } return(0); } static i4l_capi_reg_t I4Lcapireg; static int I4Lcapi_register(isdn_if *iif) { int drvidx = 0; int i, err; i4l_channel_t *ch; if (!iif->writebuf_skb) { printk(KERN_ERR "I4Lcapi_register: No write routine given.\n"); return 0; } for (drvidx=0; drvidxch = kmalloc(iif->channels * sizeof(i4l_channel_t), GFP_KERNEL); if (!drvmap[drvidx]->ch) { printk(KERN_ERR "I4Lcapi_register: no memory for i4l_channel_t\n"); kfree(drvmap[drvidx]); drvmap[drvidx] = NULL; return(0); } drvmap[drvidx]->idx = drvidx; drvmap[drvidx]->interface = iif; drvmap[drvidx]->nr_ch = iif->channels; iif->channels = drvidx; iif->rcvcallb_skb = I4Lcapi_receive_skb_callback; iif->statcallb = I4Lcapi_status_callback; APPEND_TO_LIST(drvmap[drvidx], ((i4l_capi_t *)I4Lcapi.ilist)); drvmap[drvidx]->debug = debug; drvmap[drvidx]->inst.pid.layermask = ISDN_LAYER(0) | ISDN_LAYER(1) | ISDN_LAYER(2) | ISDN_LAYER(3); drvmap[drvidx]->inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; drvmap[drvidx]->inst.pid.protocol[1] = ISDN_PID_L1_TE_S0; drvmap[drvidx]->inst.pid.protocol[2] = ISDN_PID_L2_LAPD; drvmap[drvidx]->inst.pid.protocol[3] = ISDN_PID_L3_DSS1USER; mISDN_init_instance(&drvmap[drvidx]->inst, &I4Lcapi, drvmap[drvidx]); sprintf(drvmap[drvidx]->inst.name, "Fritz%d", drvidx+1); mISDN_set_dchannel_pid(&drvmap[drvidx]->pid, 2, 0); for (i=0; i < drvmap[drvidx]->nr_ch; i++) { init_channel(drvmap[drvidx], i); } err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &drvmap[drvidx]->inst); if (err) { release_card(drvidx); return(err); } ch = drvmap[drvidx]->ch; for (i=0; i < drvmap[drvidx]->nr_ch; i++) { err = mISDN_ctrl(drvmap[drvidx]->inst.st, MGR_NEWSTACK | REQUEST, &ch->inst); if (err) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); mISDN_ctrl(drvmap[drvidx]->inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } ch++; } MOD_INC_USE_COUNT; return(1); } static struct FsmNode I4LFnList[] = { {ST_NULL, EV_I4L_ICALL, i4l_icall}, {ST_NULL, EV_CAPI_OCALL, capi_ocall}, {ST_ICALL, EV_I4L_DHUP, i4l_dhup}, {ST_ICALL, EV_I4L_L1ERR, i4l_l1err}, {ST_ICALL, EV_CAPI_ALERT, capi_alert}, {ST_ICALL, EV_CAPI_DCONNECT, capi_connect}, {ST_ICALL, EV_CAPI_DISCONNECT, capi_disconnect}, {ST_ICALL, EV_CAPI_RELEASE, capi_release}, {ST_OCALL, EV_I4L_DHUP, i4l_dhup}, {ST_OCALL, EV_I4L_L1ERR, i4l_l1err}, {ST_OCALL, EV_CAPI_DISCONNECT, capi_disconnect}, {ST_OCALL, EV_CAPI_RELEASE, capi_release}, {ST_OCALL, EV_I4L_DCONN, i4l_dconn_out}, {ST_PROCEED, EV_I4L_DHUP, i4l_dhup}, {ST_PROCEED, EV_I4L_L1ERR, i4l_l1err}, {ST_PROCEED, EV_CAPI_ALERT, capi_alert}, {ST_PROCEED, EV_CAPI_DCONNECT, capi_connect}, {ST_PROCEED, EV_CAPI_DISCONNECT, capi_disconnect}, {ST_PROCEED, EV_CAPI_RELEASE, capi_release}, {ST_ALERT, EV_I4L_DHUP, i4l_dhup}, {ST_ALERT, EV_I4L_L1ERR, i4l_l1err}, {ST_ALERT, EV_CAPI_DCONNECT, capi_connect}, {ST_ALERT, EV_CAPI_DISCONNECT, capi_disconnect}, {ST_ALERT, EV_CAPI_RELEASE, capi_release}, {ST_WAITDCONN, EV_I4L_DCONN, i4l_dconn_in}, {ST_WAITDCONN, EV_CAPI_DISCONNECT, capi_disconnect}, {ST_WAITDCONN, EV_CAPI_RELEASE, capi_release}, {ST_WAITDCONN, EV_I4L_DHUP, i4l_dhup}, {ST_ACTIVD, EV_I4L_DHUP, i4l_dhup}, {ST_ACTIVD, EV_I4L_L1ERR, i4l_l1err}, {ST_ACTIVD, EV_CAPI_DISCONNECT, capi_disconnect}, {ST_ACTIVD, EV_CAPI_RELEASE, capi_release}, {ST_ACTIVD, EV_I4L_BCONN, i4l_bconn_notready}, {ST_ACTIVD, EV_STACKREADY, stackready}, {ST_BREADY, EV_CAPI_ESTABLISHB, capi_establishb}, {ST_BREADY, EV_I4L_BCONN, i4l_bconn}, {ST_BREADY, EV_I4L_DHUP, i4l_dhup}, {ST_BREADY, EV_I4L_BHUP, i4l_bhup}, {ST_BREADY, EV_I4L_L1ERR, i4l_l1err}, {ST_BREADY, EV_CAPI_RELEASEB, capi_releaseb}, {ST_BREADY, EV_CAPI_DISCONNECT, capi_disconnect}, {ST_BREADY, EV_CAPI_RELEASE, capi_release}, {ST_ACTIVB, EV_I4L_DHUP, i4l_dhup}, {ST_ACTIVB, EV_I4L_BHUP, i4l_bhup}, {ST_ACTIVB, EV_I4L_L1ERR, i4l_l1err}, {ST_ACTIVB, EV_CAPI_DISCONNECT, capi_disconnect}, {ST_ACTIVB, EV_CAPI_RELEASE, capi_release}, {ST_ACTIVB, EV_CAPI_RELEASEB, capi_releaseb}, {ST_HANGUP, EV_I4L_DHUP, i4l_dhup}, {ST_HANGUP, EV_I4L_L1ERR, i4l_l1err}, {ST_HANGUP, EV_CAPI_RELEASE, capi_release}, }; #define I4L_FN_COUNT (sizeof(I4LFnList)/sizeof(struct FsmNode)) static char *I4L_capi_name = "I4L CAPI"; int I4Lcapi_init(void) { int err; printk(KERN_INFO "I4L CAPI interface modul version %s\n", mISDN_getrev(i4lcapi_revision)); #ifdef MODULE I4Lcapi.owner = THIS_MODULE; #endif I4Lcapi.name = I4L_capi_name; I4Lcapi.own_ctrl = I4Lcapi_manager; I4Lcapi.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; I4Lcapi.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0; I4Lcapi.DPROTO.protocol[2] = ISDN_PID_L2_LAPD | ISDN_PID_L2_DF_PTP; I4Lcapi.DPROTO.protocol[3] = ISDN_PID_L3_DSS1USER | ISDN_PID_L3_DF_PTP; I4Lcapi.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; I4Lcapi.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS; I4Lcapi.prev = NULL; I4Lcapi.next = NULL; i4lfsm_s.state_count = STATE_COUNT; i4lfsm_s.event_count = EVENT_COUNT; i4lfsm_s.strEvent = strI4LEvent; i4lfsm_s.strState = strI4LState; mISDN_FsmNew(&i4lfsm_s, I4LFnList, I4L_FN_COUNT); if ((err = mISDN_register(&I4Lcapi))) { printk(KERN_ERR "Can't register I4L CAPI error(%d)\n", err); mISDN_FsmFree(&i4lfsm_s); return(err); } I4Lcapireg.register_func = I4Lcapi_register; strcpy(I4Lcapireg.name, "I4L CAPI"); err = register_i4lcapi(&I4Lcapireg); printk(KERN_INFO "registered I4L CAPI %s err(%d)\n", i4lcapi_revision, err); return(0); } #ifdef MODULE void I4Lcapi_cleanup(void) { int err; if ((err = mISDN_unregister(&I4Lcapi))) { printk(KERN_ERR "Can't unregister I4Lcapi error(%d)\n", err); return; } while(I4Lcapi.ilist) { printk(KERN_ERR "I4Lcapi card struct not empty refs %d\n", I4Lcapi.refcnt); release_card(((i4l_capi_t *)I4Lcapi.ilist)->idx); } mISDN_FsmFree(&i4lfsm_s); unregister_i4lcapi(); return; } module_init(I4Lcapi_init); module_exit(I4Lcapi_cleanup); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/l3_udss1.c0000644000000000000500000023056611135651702020204 0ustar rootsrc/* $Id: l3_udss1.c,v 1.47 2007/01/10 12:56:45 crich Exp $ * * EURO/DSS1 D-channel protocol * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * For changes and modifications please read * ../../../Documentation/isdn/mISDN.cert * * Thanks to Jan den Ouden * Fritz Elfert * */ #include #include "core.h" #include "layer3.h" #include "helper.h" #include "debug.h" #include "dss1.h" static int debug = 0; static mISDNobject_t u_dss1; const char *dss1_revision = "$Revision: 1.47 $"; static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1}; static int dss1man(l3_process_t *, u_int, void *); static int parseQ931(struct sk_buff *skb) { Q931_info_t *qi; int l, codeset, maincodeset; int len, iep, pos = 0, cnt = 0, eidx = -1; u16 cr; ie_info_t *ie, *old; u_char t, *p = skb->data; if (skb->len < 3) return(-1); p++; l = (*p++) & 0xf; if (l>2) return(-2); if (l) cr = *p++; else cr = 0; if (l == 2) { cr <<= 8; cr |= *p++; } else if (l == 1) if (cr & 0x80) { cr |= 0x8000; cr &= 0xFF7F; } t = *p; if ((u_long)p & 1) pos = 1; else pos = 0; skb_pull(skb, (p - skb->data) - pos); len = skb->len; p = skb->data; if (skb_headroom(skb) < (int)L3_EXTRA_SIZE) { int_error(); return(-3); } qi = (Q931_info_t *)skb_push(skb, L3_EXTRA_SIZE); mISDN_initQ931_info(qi); qi->type = t; qi->crlen = l; qi->cr = cr; pos++; codeset = maincodeset = 0; ie = &qi->bearer_capability; while (pos < len) { if ((p[pos] & 0xf0) == 0x90) { codeset = p[pos] & 0x07; if (!(p[pos] & 0x08)) maincodeset = codeset; if (eidx >= 0) { qi->ext[eidx].cs.len = pos - qi->ext[eidx].ie.off; eidx = -1; } pos++; continue; } if (codeset == 0) { if (p[pos] & 0x80) { /* single octett IE */ if (p[pos] == IE_MORE_DATA) qi->more_data.off = pos; else if (p[pos] == IE_COMPLETE) { qi->sending_complete.off = pos; } else if ((p[pos] & 0xf0) == IE_CONGESTION) qi->congestion_level.off = pos; else { printk("parseQ931: Unknown Single Oct IE [%x]\n",p[pos]); } cnt++; pos++; } else { t = p[pos]; iep = mISDN_l3_ie2pos(t); if ((pos+1) >= len) return(-4); l = p[pos+1]; if ((pos+l+1) >= len) return(-5); if (iep>=0) { if (!ie[iep].off) { /* IE not detected before */ ie[iep].off = pos; } else { /* IE is repeated */ old = &ie[iep]; if (old->repeated) old = mISDN_get_last_repeated_ie(qi, old); if (!old) { int_error(); return(-6); } eidx = mISDN_get_free_ext_ie(qi); if (eidx < 0) { int_error(); return(-7); } old->ridx = eidx; old->repeated = 1; qi->ext[eidx].ie.off = pos; qi->ext[eidx].v.codeset = 0; qi->ext[eidx].v.val = t; eidx = -1; } } else { int i; for (i=0; comp_required[i] > 0; i++) { if ( p[pos] == comp_required[i] && l==1 ) { qi->comprehension_required.off = pos; } } if (!qi->comprehension_required.off) printk(" ie not handled ie [%x] l [%x]\n", p[pos],l); } pos += l + 2; cnt++; } } else { /* codeset != 0 */ if (eidx < 0) { eidx = mISDN_get_free_ext_ie(qi); if (eidx < 0) { int_error(); return(-8); } qi->ext[eidx].cs.codeset = codeset; qi->ext[eidx].ie.off = pos; qi->ext[eidx].ie.cs_flg = 1; if (codeset == maincodeset) { /* locked shift */ qi->ext[eidx].cs.locked = 1; } } if (p[pos] & 0x80) { /* single octett IE */ cnt++; pos++; } else { if ((pos+1) >= len) return(-4); l = p[pos+1]; if ((pos+l+1) >= len) return(-5); pos += l + 2; cnt++; } if (qi->ext[eidx].cs.locked == 0) {/* single IE codeset shift */ qi->ext[eidx].cs.len = pos - qi->ext[eidx].ie.off; eidx = -1; } } codeset = maincodeset; } if (eidx >= 0) qi->ext[eidx].cs.len = pos - qi->ext[eidx].ie.off; return(cnt); } static int calc_msg_len(Q931_info_t *qi) { int i, cnt = 0; u_char *buf = (u_char *)qi; ie_info_t *ie; buf += L3_EXTRA_SIZE; if (qi->more_data.off) cnt++; if (qi->sending_complete.off) cnt++; if (qi->congestion_level.off) cnt++; ie = &qi->bearer_capability; while (ie <= &qi->comprehension_required) { if (ie->off) cnt += buf[ie->off + 1] + 2; ie++; } for (i = 0; i < 8; i++) { if (qi->ext[i].ie.off) { if (qi->ext[i].ie.cs_flg == 1) { /* other codset info chunk */ cnt++; /* codeset shift IE */ cnt += qi->ext[i].cs.len; } else { /* repeated IE */ cnt += buf[qi->ext[i].ie.off + 1] + 2; } } } return(cnt); } static int compose_msg(struct sk_buff *skb, Q931_info_t *qi) { int i, l, ri; u_char *p, *buf = (u_char *)qi; ie_info_t *ie; buf += L3_EXTRA_SIZE; if (qi->more_data.off) { p = skb_put(skb, 1); *p = buf[qi->more_data.off]; } if (qi->sending_complete.off) { p = skb_put(skb, 1); *p = buf[qi->sending_complete.off]; } if (qi->congestion_level.off) { p = skb_put(skb, 1); *p = buf[qi->congestion_level.off]; } ie = &qi->bearer_capability; for (i=0; i<33; i++) { if (ie[i].off) { l = buf[ie[i].off + 1] +1; p = skb_put(skb, l + 1); *p++ = mISDN_l3_pos2ie(i); memcpy(p, &buf[ie[i].off + 1], l); if (ie[i].repeated) { ri = ie[i].ridx; while(ri >= 0) { l = buf[qi->ext[ri].ie.off + 1] +1; p = skb_put(skb, l + 1); if (mISDN_l3_pos2ie(i) != qi->ext[ri].v.val) int_error(); *p++ = qi->ext[ri].v.val; memcpy(p, &buf[qi->ext[ri].ie.off + 1], l); if (qi->ext[ri].ie.repeated) ri = qi->ext[ri].ie.ridx; else ri = -1; } } } } for (i=0; i<8; i++) { /* handle other codeset elements */ if (qi->ext[i].ie.cs_flg == 1) { p = skb_put(skb, 1); /* shift codeset IE */ if (qi->ext[i].cs.locked == 1) *p = 0x90 | qi->ext[i].cs.codeset; else /* non-locking shift */ *p = 0x98 | qi->ext[i].cs.codeset; p = skb_put(skb, qi->ext[i].cs.len); memcpy(p, &buf[qi->ext[i].ie.off], qi->ext[i].cs.len); } } return(0); } static struct sk_buff *MsgStart(l3_process_t *pc, u_char mt, int len) { struct sk_buff *skb; int lx = 4; u_char *p; if (test_bit(FLG_CRLEN2, &pc->l3->Flag)) lx++; if (pc->callref == -1) /* dummy cr */ lx = 3; if (!(skb = alloc_stack_skb(len + lx, pc->l3->down_headerlen))) return(NULL); p = skb_put(skb, lx); *p++ = 8; if (lx == 3) *p++ = 0; else if (lx == 5) { *p++ = 2; *p++ = (pc->callref >> 8) ^ 0x80; *p++ = pc->callref & 0xff; } else { *p++ = 1; *p = pc->callref & 0xff; if (!(pc->callref & 0x8000)) *p |= 0x80; p++; } *p = mt; return(skb); } static int SendMsg(l3_process_t *pc, struct sk_buff *skb, int state) { int l; int ret; struct sk_buff *nskb; Q931_info_t *qi; if (!skb) return(-EINVAL); qi = (Q931_info_t *)skb->data; l = calc_msg_len(qi); if (!(nskb = MsgStart(pc, qi->type, l))) { kfree_skb(skb); return(-ENOMEM); } if (l) compose_msg(nskb, qi); kfree_skb(skb); if (state != -1) newl3state(pc, state); if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, nskb))) kfree_skb(nskb); return(ret); } static int l3dss1_message(l3_process_t *pc, u_char mt) { struct sk_buff *skb; int ret; if (!(skb = MsgStart(pc, mt, 0))) return(-ENOMEM); if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb))) kfree_skb(skb); return(ret); } static void l3dss1_message_cause(l3_process_t *pc, u_char mt, u_char cause) { struct sk_buff *skb; u_char *p; int ret; if (!(skb = MsgStart(pc, mt, 4))) return; p = skb_put(skb, 4); *p++ = IE_CAUSE; *p++ = 0x2; *p++ = 0x80 | CAUSE_LOC_USER; *p++ = 0x80 | cause; if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb))) kfree_skb(skb); } static void l3dss1_status_send(l3_process_t *pc, u_char cause) { struct sk_buff *skb; u_char *p; int ret; if (!(skb = MsgStart(pc, MT_STATUS, 7))) return; p = skb_put(skb, 7); *p++ = IE_CAUSE; *p++ = 2; *p++ = 0x80 | CAUSE_LOC_USER; *p++ = 0x80 | cause; *p++ = IE_CALL_STATE; *p++ = 1; *p++ = pc->state & 0x3f; if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb))) kfree_skb(skb); } static void l3dss1_msg_without_setup(l3_process_t *pc, u_char cause) { /* This routine is called if here was no SETUP made (checks in dss1up and in * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code * MT_STATUS_ENQUIRE in the NULL state is handled too */ switch (cause) { case 81: /* invalid callreference */ case 88: /* incomp destination */ case 96: /* mandory IE missing */ case 100: /* invalid IE contents */ case 101: /* incompatible Callstate */ l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); break; default: printk(KERN_ERR "mISDN l3dss1_msg_without_setup wrong cause %d\n", cause); } release_l3_process(pc); } static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_REDIR_DN, IE_HLC, IE_USER_USER, -1}; static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_REDIR_DN, IE_HLC, -1}; static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL, IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1}; static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1}; static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLED_PN, -1}; static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, IE_REDIR_DN, -1}; static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS | IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; /* a RELEASE_COMPLETE with errors don't require special actions static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1}; */ static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_DISPLAY, -1}; static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER | IE_MANDATORY, IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN, IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR, IE_LLC, IE_HLC, IE_USER_USER, -1}; static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1}; static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1}; static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_FACILITY, IE_DISPLAY, -1}; static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_HOLD[] = {IE_DISPLAY, -1}; static int ie_HOLD_ACKNOWLEDGE[] = {IE_DISPLAY, -1}; static int ie_HOLD_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_RETRIEVE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_DISPLAY, -1}; static int ie_RETRIEVE_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_DISPLAY, -1}; static int ie_RETRIEVE_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; /* not used * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY, * IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1}; * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1}; * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND | * IE_MANDATORY, -1}; */ static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1}; static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1}; struct ie_len { int ie; int len; }; static struct ie_len max_ie_len[] = { {IE_SEGMENT, 4}, {IE_BEARER, 12}, {IE_CAUSE, 32}, {IE_CALL_ID, 10}, {IE_CALL_STATE, 3}, {IE_CHANNEL_ID, 34}, {IE_FACILITY, 255}, {IE_PROGRESS, 4}, {IE_NET_FAC, 255}, {IE_NOTIFY, 255}, /* 3-* Q.932 Section 9 */ {IE_DISPLAY, 82}, {IE_DATE, 8}, {IE_KEYPAD, 34}, {IE_SIGNAL, 3}, {IE_INFORATE, 6}, {IE_E2E_TDELAY, 11}, {IE_TDELAY_SEL, 5}, {IE_PACK_BINPARA, 3}, {IE_PACK_WINSIZE, 4}, {IE_PACK_SIZE, 4}, {IE_CUG, 7}, {IE_REV_CHARGE, 3}, {IE_CALLING_PN, 24}, {IE_CALLING_SUB, 23}, {IE_CALLED_PN, 24}, {IE_CALLED_SUB, 23}, {IE_REDIR_NR, 255}, {IE_REDIR_DN, 255}, {IE_TRANS_SEL, 255}, {IE_RESTART_IND, 3}, {IE_LLC, 18}, {IE_HLC, 5}, {IE_USER_USER, 131}, {-1,0}, }; static int getmax_ie_len(u_char ie) { int i = 0; while (max_ie_len[i].ie != -1) { if (max_ie_len[i].ie == ie) return(max_ie_len[i].len); i++; } return(255); } static int ie_in_set(l3_process_t *pc, u_char ie, int *checklist) { int ret = 1; while (*checklist != -1) { if ((*checklist & 0xff) == ie) { if (ie & 0x80) return(-ret); else return(ret); } ret++; checklist++; } return(0); } static int check_infoelements(l3_process_t *pc, struct sk_buff *skb, int *checklist) { Q931_info_t *qi = (Q931_info_t *)skb->data; int *cl = checklist; u_char *p, ie; ie_info_t *iep; int i, l, newpos, oldpos; int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0; p = skb->data; p += L3_EXTRA_SIZE; iep = &qi->bearer_capability; oldpos = -1; for (i=0; i<33; i++) { if (iep[i].off) { ie = mISDN_l3_pos2ie(i); if ((newpos = ie_in_set(pc, ie, cl))) { if (newpos > 0) { if (newpos < oldpos) err_seq++; else oldpos = newpos; } else { printk(KERN_NOTICE "ie_in_set returned <0 [%d] ie:[%x]\n",newpos,ie); } } else { if (debug) printk(KERN_NOTICE "Found ie in set which we do not support ie [%x]\n",ie); if (ie_in_set(pc, ie, comp_required)) err_compr++; else err_ureg++; } l = p[iep[i].off +1]; if (l > getmax_ie_len(ie)) err_len++; } } if (qi->comprehension_required.off) { if ( ! (p[qi->comprehension_required.off +2] &0xf) ) { err_compr++; } } if (err_compr | err_ureg | err_len | err_seq) { if (pc->l3->debug & L3_DEB_CHECK) l3_debug(pc->l3, "check IE MT(%x) %d/%d/%d/%d", qi->type, err_compr, err_ureg, err_len, err_seq); if (err_compr) return(ERR_IE_COMPREHENSION); if (err_ureg) return(ERR_IE_UNRECOGNIZED); if (err_len) return(ERR_IE_LENGTH); if (err_seq) return(ERR_IE_SEQUENCE); } return(0); } /* verify if a message type exists and contain no IE error */ static int l3dss1_check_messagetype_validity(l3_process_t *pc, int mt, void *arg) { switch (mt) { case MT_ALERTING: case MT_CALL_PROCEEDING: case MT_CONNECT: case MT_CONNECT_ACKNOWLEDGE: case MT_DISCONNECT: case MT_INFORMATION: case MT_FACILITY: case MT_NOTIFY: case MT_PROGRESS: case MT_RELEASE: case MT_RELEASE_COMPLETE: case MT_SETUP: case MT_SETUP_ACKNOWLEDGE: case MT_RESUME_ACKNOWLEDGE: case MT_RESUME_REJECT: case MT_SUSPEND_ACKNOWLEDGE: case MT_SUSPEND_REJECT: case MT_USER_INFORMATION: case MT_RESTART: case MT_RESTART_ACKNOWLEDGE: case MT_CONGESTION_CONTROL: case MT_STATUS: case MT_STATUS_ENQUIRY: case MT_HOLD: case MT_HOLD_ACKNOWLEDGE: case MT_HOLD_REJECT: case MT_RETRIEVE: case MT_RETRIEVE_ACKNOWLEDGE: case MT_RETRIEVE_REJECT: if (pc->l3->debug & L3_DEB_CHECK) l3_debug(pc->l3, "l3dss1_check_messagetype_validity mt(%x) OK", mt); break; case MT_RESUME: /* RESUME only in user->net */ case MT_SUSPEND: /* SUSPEND only in user->net */ default: if (pc->l3->debug & (L3_DEB_CHECK | L3_DEB_WARN)) l3_debug(pc->l3, "l3dss1_check_messagetype_validity mt(%x) fail", mt); l3dss1_status_send(pc, CAUSE_MT_NOTIMPLEMENTED); return(1); } return(0); } static void l3dss1_std_ie_err(l3_process_t *pc, int ret) { if (pc->l3->debug & L3_DEB_CHECK) l3_debug(pc->l3, "check_infoelements ret %d", ret); switch(ret) { case 0: break; case ERR_IE_COMPREHENSION: l3dss1_status_send(pc, CAUSE_MANDATORY_IE_MISS); break; case ERR_IE_UNRECOGNIZED: l3dss1_status_send(pc, CAUSE_IE_NOTIMPLEMENTED); break; case ERR_IE_LENGTH: l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); break; case ERR_IE_SEQUENCE: default: break; } } static int l3dss1_get_channel_id(l3_process_t *pc, struct sk_buff *skb) { Q931_info_t *qi = (Q931_info_t *)skb->data; u_char *p; if (qi->channel_id.off) { p = skb->data; p += L3_EXTRA_SIZE + qi->channel_id.off; p++; if (test_bit(FLG_EXTCID, &pc->l3->Flag)) { if (*p != 1) { pc->bc = 1; pc->real_bc=p[3]; return (0); } } if (*p != 1) { /* len for BRI = 1 */ if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "wrong chid len %d", *p); return (-2); } p++; if (*p & 0x60) { /* only base rate interface */ if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "wrong chid %x", *p); return (-3); } pc->bc = *p & 3; pc->real_bc=pc->bc; } else return(-1); return(0); } static int l3dss1_get_cause(l3_process_t *pc, struct sk_buff *skb) { Q931_info_t *qi = (Q931_info_t *)skb->data; u_char l; u_char *p; if (qi->cause.off) { p = skb->data; p += L3_EXTRA_SIZE + qi->cause.off; p++; l = *p++; if (l>30) { return(-30); } if (l) l--; else { return(-2); } if (l && !(*p & 0x80)) { l--; p++; /* skip recommendation */ } p++; if (l) { if (!(*p & 0x80)) { return(-3); } pc->err = *p & 0x7F; } else { return(-4); } } else return(-1); return(0); } static void l3dss1_release_req(l3_process_t *pc, u_char pr, void *arg) { StopAllL3Timer(pc); if (arg) { SendMsg(pc, arg, 19); } else { newl3state(pc, 19); l3dss1_message(pc, MT_RELEASE); } L3AddTimer(&pc->timer, T308, CC_T308_1); } static void l3dss1_setup_req(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = skb_clone(arg, GFP_ATOMIC); if (!SendMsg(pc, arg, 1)) { L3DelTimer(&pc->timer); L3AddTimer(&pc->timer, T303, CC_T303); pc->t303skb = skb; } else dev_kfree_skb(skb); } static void l3dss1_disconnect_req(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; Q931_info_t *qi; u_char *p; StopAllL3Timer(pc); if (arg) { qi = (Q931_info_t *)skb->data; if (!qi->cause.off) { qi->cause.off = skb->len - L3_EXTRA_SIZE; p = skb_put(skb, 4); *p++ = IE_CAUSE; *p++ = 2; *p++ = 0x80 | CAUSE_LOC_USER; *p++ = 0x80 | CAUSE_NORMALUNSPECIFIED; pc->cause=CAUSE_NORMALUNSPECIFIED; } else { p=skb->data; p += L3_EXTRA_SIZE + qi->cause.off; pc->cause = (*(p+3) & 0x7f); } SendMsg(pc, arg, 11); } else { newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_NORMALUNSPECIFIED); pc->cause=CAUSE_NORMALUNSPECIFIED; } L3AddTimer(&pc->timer, T305, CC_T305); } static void l3dss1_connect_req(l3_process_t *pc, u_char pr, void *arg) { if (!pc->bc) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "D-chan connect for waiting call"); l3dss1_disconnect_req(pc, pr, NULL); return; } if (arg) { SendMsg(pc, arg, 8); } else { newl3state(pc, 8); l3dss1_message(pc, MT_CONNECT); } L3DelTimer(&pc->timer); L3AddTimer(&pc->timer, T313, CC_T313); } static void l3dss1_release_cmpl_req(l3_process_t *pc, u_char pr, void *arg) { StopAllL3Timer(pc); if (arg) { SendMsg(pc, arg, 0); } else { newl3state(pc, 0); l3dss1_message(pc, MT_RELEASE_COMPLETE); } mISDN_l3up(pc, CC_RELEASE_COMPLETE | CONFIRM, NULL); release_l3_process(pc); } static void l3dss1_alert_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) { SendMsg(pc, arg, 7); } else { newl3state(pc, 7); l3dss1_message(pc, MT_ALERTING); } L3DelTimer(&pc->timer); } static void l3dss1_proceed_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) { SendMsg(pc, arg, 9); } else { newl3state(pc, 9); l3dss1_message(pc, MT_CALL_PROCEEDING); } L3DelTimer(&pc->timer); } static void l3dss1_setup_ack_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) { SendMsg(pc, arg, 25); } else { newl3state(pc, 25); l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE); } L3DelTimer(&pc->timer); L3AddTimer(&pc->timer, T302, CC_T302); } static void l3dss1_suspend_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) { SendMsg(pc, arg, 15); } else { newl3state(pc, 15); l3dss1_message(pc, MT_SUSPEND); } L3AddTimer(&pc->timer, T319, CC_T319); } static void l3dss1_resume_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) { SendMsg(pc, arg, 17); } else { newl3state(pc, 17); l3dss1_message(pc, MT_RESUME); } L3AddTimer(&pc->timer, T318, CC_T318); } static void l3dss1_status_enq_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) dev_kfree_skb(arg); l3dss1_message(pc, MT_STATUS_ENQUIRY); } static void l3dss1_information_req(l3_process_t *pc, u_char pr, void *arg) { if (pc->state == 2) { L3DelTimer(&pc->timer); L3AddTimer(&pc->timer, T304, CC_T304); } if (arg) { SendMsg(pc, arg, -1); } } static void l3dss1_notify_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) { SendMsg(pc, arg, -1); } } static void l3dss1_progress_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) { SendMsg(pc, arg, -1); } } static void l3dss1_facility_req(l3_process_t *pc, u_char pr, void *arg) { if (arg) { SendMsg(pc, arg, -1); } } static void l3dss1_restart_req(l3_process_t *pc, u_char pr, void *arg) { pc->callref=0; if (arg) { SendMsg(pc, arg, -1); } pc->callref=-1; } static void l3dss1_release_cmpl(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; StopAllL3Timer(pc); newl3state(pc, 0); if (mISDN_l3up(pc, CC_RELEASE_COMPLETE | INDICATION, skb)) dev_kfree_skb(skb); release_l3_process(pc); } static void l3dss1_alerting(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; ret = check_infoelements(pc, skb, ie_ALERTING); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } L3DelTimer(&pc->timer); /* T304 */ if (pc->t303skb) { dev_kfree_skb(pc->t303skb); pc->t303skb = NULL; } newl3state(pc, 4); if (ret) l3dss1_std_ie_err(pc, ret); if (mISDN_l3up(pc, CC_ALERTING | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_call_proc(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; u_char cause; if (!(ret = l3dss1_get_channel_id(pc, skb))) { if ((0 == pc->bc) || (3 == pc->bc)) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup answer with wrong chid %x", pc->bc); l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); return; } } else if (1 == pc->state) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup answer wrong chid (ret %d)", ret); if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); return; } /* Now we are on none mandatory IEs */ ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } L3DelTimer(&pc->timer); if (pc->t303skb) { dev_kfree_skb(pc->t303skb); pc->t303skb = NULL; } newl3state(pc, 3); L3AddTimer(&pc->timer, T310, CC_T310); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); if (mISDN_l3up(pc, CC_PROCEEDING | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_connect(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; ret = check_infoelements(pc, skb, ie_CONNECT); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } L3DelTimer(&pc->timer); /* T310 */ if (pc->t303skb) { dev_kfree_skb(pc->t303skb); pc->t303skb = NULL; } l3dss1_message(pc, MT_CONNECT_ACKNOWLEDGE); newl3state(pc, 10); if (ret) l3dss1_std_ie_err(pc, ret); if (mISDN_l3up(pc, CC_CONNECT | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_connect_ack(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } newl3state(pc, 10); L3DelTimer(&pc->timer); if (pc->t303skb) { dev_kfree_skb(pc->t303skb); pc->t303skb = NULL; } if (ret) l3dss1_std_ie_err(pc, ret); if (mISDN_l3up(pc, CC_CONNECT_ACKNOWLEDGE | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_disconnect(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; u_char cause = 0; StopAllL3Timer(pc); if ((ret = l3dss1_get_cause(pc, skb))) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "DISC get_cause ret(%d)", ret); if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; } else if (pc->state == 7) /* Call Received*/ cause = pc->err; ret = check_infoelements(pc, skb, ie_DISCONNECT); if (ERR_IE_COMPREHENSION == ret) cause = CAUSE_MANDATORY_IE_MISS; else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret)) cause = CAUSE_IE_NOTIMPLEMENTED; ret = pc->state; if (cause) newl3state(pc, 19); else newl3state(pc, 12); if (11 != ret) { if (mISDN_l3up(pc, CC_DISCONNECT | INDICATION, skb)) dev_kfree_skb(skb); } else if (!cause) { l3dss1_release_req(pc, pr, NULL); dev_kfree_skb(skb); } else dev_kfree_skb(skb); if (cause) { l3dss1_message_cause(pc, MT_RELEASE, cause); L3AddTimer(&pc->timer, T308, CC_T308_1); } } static void l3dss1_setup_ack(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; u_char cause; if (!(ret = l3dss1_get_channel_id(pc, skb))) { if ((0 == pc->bc) || (3 == pc->bc)) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup answer with wrong chid %x", pc->bc); l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); dev_kfree_skb(skb); return; } } else { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup answer wrong chid (ret %d)", ret); if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); dev_kfree_skb(skb); return; } /* Now we are on none mandatory IEs */ ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } L3DelTimer(&pc->timer); if (pc->t303skb) { dev_kfree_skb(pc->t303skb); pc->t303skb = NULL; } newl3state(pc, 2); L3AddTimer(&pc->timer, T304, CC_T304); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); if (mISDN_l3up(pc, CC_SETUP_ACKNOWLEDGE | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_setup(l3_process_t *pc, u_char pr, void *arg) { u_char *p, cause, bc2 = 0; int bcfound = 0; struct sk_buff *skb = arg; Q931_info_t *qi = (Q931_info_t *)skb->data; int err = 0; /* * Bearer Capabilities */ /* only the first occurence 'll be detected ! */ p = skb->data; if (qi->bearer_capability.off) { p += L3_EXTRA_SIZE + qi->bearer_capability.off; p++; if ((p[0] < 2) || (p[0] > 11)) err = 1; else { bc2 = p[2] & 0x7f; switch (p[1] & 0x7f) { case 0x00: /* Speech */ case 0x10: /* 3.1 Khz audio */ case 0x08: /* Unrestricted digital information */ case 0x09: /* Restricted digital information */ case 0x11: /* Unrestr. digital information with * tones/announcements ( or 7 kHz audio */ case 0x18: /* Video */ break; default: err = 2; break; } switch (p[2] & 0x7f) { case 0x40: /* packed mode */ case 0x10: /* 64 kbit */ case 0x11: /* 2*64 kbit */ case 0x13: /* 384 kbit */ case 0x15: /* 1536 kbit */ case 0x17: /* 1920 kbit */ break; default: err = 3; break; } } if (err) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup with wrong bearer(l=%d:%x,%x)", p[0], p[1], p[2]); l3dss1_msg_without_setup(pc, CAUSE_INVALID_CONTENTS); dev_kfree_skb(skb); return; } } else { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup without bearer capabilities"); /* ETS 300-104 1.3.3 */ l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS); dev_kfree_skb(skb); return; } /* * Channel Identification */ if (!(err = l3dss1_get_channel_id(pc, skb))) { if (pc->bc) { if ((3 == pc->bc) && (0x10 == bc2)) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup with wrong chid %x", pc->bc); l3dss1_msg_without_setup(pc, CAUSE_INVALID_CONTENTS); dev_kfree_skb(skb); return; } bcfound++; } else { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup without bchannel, call waiting"); bcfound++; } } else { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "setup with wrong chid ret %d", err); if (err == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_msg_without_setup(pc, cause); dev_kfree_skb(skb); return; } /* Now we are on none mandatory IEs */ err = check_infoelements(pc, skb, ie_SETUP); if (ERR_IE_COMPREHENSION == err) { l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS); dev_kfree_skb(skb); return; } newl3state(pc, 6); L3DelTimer(&pc->timer); L3AddTimer(&pc->timer, T_CTRL, CC_TCTRL); if (err) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, err); // already done // err = mISDN_l3up(pc, CC_NEW_CR | INDICATION, NULL); if (mISDN_l3up(pc, CC_SETUP | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_reset(l3_process_t *pc, u_char pr, void *arg) { release_l3_process(pc); } static void l3dss1_release(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret, cause=0; StopAllL3Timer(pc); if ((ret = l3dss1_get_cause(pc, skb))) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "REL get_cause ret(%d)", ret); if ((ret == -1) && (pc->state != 11)) cause = CAUSE_MANDATORY_IE_MISS; else if (ret != -1) cause = CAUSE_INVALID_CONTENTS; } ret = check_infoelements(pc, skb, ie_RELEASE); if (ERR_IE_COMPREHENSION == ret) cause = CAUSE_MANDATORY_IE_MISS; else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause)) cause = CAUSE_IE_NOTIMPLEMENTED; if (cause) l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); else l3dss1_message(pc, MT_RELEASE_COMPLETE); if (mISDN_l3up(pc, CC_RELEASE | INDICATION, skb)) dev_kfree_skb(skb); newl3state(pc, 0); release_l3_process(pc); } static void l3dss1_progress(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; Q931_info_t *qi = (Q931_info_t *)skb->data; int err = 0; u_char *p, cause = CAUSE_INVALID_CONTENTS; if (qi->progress.off) { p = skb->data; p += L3_EXTRA_SIZE + qi->progress.off; p++; if (p[0] != 2) { err = 1; } else if (!(p[1] & 0x70)) { switch (p[1]) { case 0x80: case 0x81: case 0x82: case 0x84: case 0x85: case 0x87: case 0x8a: switch (p[2]) { case 0x81: case 0x82: case 0x83: case 0x84: case 0x88: break; default: err = 2; break; } break; default: err = 3; break; } } } else { cause = CAUSE_MANDATORY_IE_MISS; err = 4; } if (err) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "progress error %d", err); l3dss1_status_send(pc, cause); dev_kfree_skb(skb); return; } /* Now we are on none mandatory IEs */ err = check_infoelements(pc, skb, ie_PROGRESS); if (err) l3dss1_std_ie_err(pc, err); /* * clear T310 if running (should be cleared by a Progress * Message, according to ETSI). * */ L3DelTimer(&pc->timer); if (pc->t303skb) { dev_kfree_skb(pc->t303skb); pc->t303skb = NULL; } if (ERR_IE_COMPREHENSION != err) { if (mISDN_l3up(pc, CC_PROGRESS | INDICATION, skb)) dev_kfree_skb(skb); } else dev_kfree_skb(skb); } static void l3dss1_notify(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; Q931_info_t *qi = (Q931_info_t *)skb->data; int err = 0; u_char *p, cause = CAUSE_INVALID_CONTENTS; if (qi->notify.off) { p = skb->data; p += L3_EXTRA_SIZE + qi->notify.off; p++; if (p[0] != 1) { err = 1; #if 0 } else { switch (p[1]) { case 0x80: case 0x81: case 0x82: break; default: err = 2; break; } #endif } } else { cause = CAUSE_MANDATORY_IE_MISS; err = 3; } if (err) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "notify error %d", err); l3dss1_status_send(pc, cause); dev_kfree_skb(skb); return; } /* Now we are on none mandatory IEs */ err = check_infoelements(pc, skb, ie_NOTIFY); if (err) l3dss1_std_ie_err(pc, err); if (ERR_IE_COMPREHENSION != err) { if (mISDN_l3up(pc, CC_NOTIFY | INDICATION, skb)) dev_kfree_skb(skb); } else dev_kfree_skb(skb); } static void l3dss1_status_enq(l3_process_t *pc, u_char pr, void *arg) { int ret; struct sk_buff *skb = arg; ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY); l3dss1_std_ie_err(pc, ret); l3dss1_status_send(pc, CAUSE_STATUS_RESPONSE); if (mISDN_l3up(pc, CC_STATUS_ENQUIRY | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_information(l3_process_t *pc, u_char pr, void *arg) { int ret; struct sk_buff *skb = arg; ret = check_infoelements(pc, skb, ie_INFORMATION); if (ret) l3dss1_std_ie_err(pc, ret); if (pc->state == 25) { /* overlap receiving */ L3DelTimer(&pc->timer); L3AddTimer(&pc->timer, T302, CC_T302); } if (mISDN_l3up(pc, CC_INFORMATION | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_release_ind(l3_process_t *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int err, callState = -1; Q931_info_t *qi = (Q931_info_t *)skb->data; if (pc->state == 19) { /* ETS 300-102 5.3.5 */ newl3state(pc, 0); err = mISDN_l3up(pc, CC_RELEASE | INDICATION, skb); release_l3_process(pc); } else { if (qi->call_state.off) { p = skb->data; p += L3_EXTRA_SIZE + qi->call_state.off; p++; if (1 == *p++) callState = *p; } if (callState == 0) { /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 * set down layer 3 without sending any message */ newl3state(pc, 0); err = mISDN_l3up(pc, CC_RELEASE | INDICATION, skb); release_l3_process(pc); } else { err = mISDN_l3up(pc, CC_RELEASE | INDICATION, skb); } } if (err) dev_kfree_skb(skb); } static void l3dss1_restart(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; L3DelTimer(&pc->timer); mISDN_l3up(pc, CC_RELEASE | INDICATION, NULL); release_l3_process(pc); if (skb) dev_kfree_skb(skb); } static void l3dss1_status(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; Q931_info_t *qi = (Q931_info_t *)skb->data; int ret = 0; u_char *p, cause = 0, callState = 0xff; if ((ret = l3dss1_get_cause(pc, skb))) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "STATUS get_cause ret(%d)", ret); if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; } if (qi->call_state.off) { p = skb->data; p += L3_EXTRA_SIZE + qi->call_state.off; p++; if (1 == *p++) { callState = *p; if (!ie_in_set(pc, callState, l3_valid_states)) cause = CAUSE_INVALID_CONTENTS; } else cause = CAUSE_INVALID_CONTENTS; } else cause = CAUSE_MANDATORY_IE_MISS; if (!cause) { /* no error before */ ret = check_infoelements(pc, skb, ie_STATUS); if (ERR_IE_COMPREHENSION == ret) cause = CAUSE_MANDATORY_IE_MISS; else if (ERR_IE_UNRECOGNIZED == ret) cause = CAUSE_IE_NOTIMPLEMENTED; } if (cause) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "STATUS error(%d/%d)", ret, cause); l3dss1_status_send(pc, cause); if (cause != CAUSE_IE_NOTIMPLEMENTED) { dev_kfree_skb(skb); return; } } if (qi->cause.off) cause = pc->err & 0x7f; if ((cause == CAUSE_PROTOCOL_ERROR) && (callState == 0)) { /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... * if received MT_STATUS with cause == 111 and call * state == 0, then we must set down layer 3 */ newl3state(pc, 0); ret = mISDN_l3up(pc, CC_STATUS| INDICATION, skb); release_l3_process(pc); } else ret = mISDN_l3up(pc, CC_STATUS | INDICATION, skb); if (ret) dev_kfree_skb(skb); } static void l3dss1_facility(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; Q931_info_t *qi = (Q931_info_t *)skb->data; int ret; ret = check_infoelements(pc, skb, ie_FACILITY); l3dss1_std_ie_err(pc, ret); if (!qi->facility.off) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "FACILITY without IE_FACILITY"); dev_kfree_skb(skb); return; } if (mISDN_l3up(pc, CC_FACILITY | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_suspend_ack(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; L3DelTimer(&pc->timer); newl3state(pc, 0); /* We don't handle suspend_ack for IE errors now */ if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE))) if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "SUSPACK check ie(%d)",ret); if (mISDN_l3up(pc, CC_SUSPEND_ACKNOWLEDGE | INDICATION, skb)) dev_kfree_skb(skb); release_l3_process(pc); } static void l3dss1_suspend_rej(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; u_char cause; if ((ret = l3dss1_get_cause(pc, skb))) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "SUSP_REJ get_cause err(%d)", ret); if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); dev_kfree_skb(skb); return; } ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } L3DelTimer(&pc->timer); if (mISDN_l3up(pc, CC_SUSPEND_REJECT | INDICATION, skb)) dev_kfree_skb(skb); newl3state(pc, 10); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_resume_ack(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; if (!(ret = l3dss1_get_channel_id(pc, skb))) { if ((0 == pc->bc) || (3 == pc->bc)) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "resume ack with wrong chid %x", pc->bc); l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); dev_kfree_skb(skb); return; } } else if (1 == pc->state) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "resume ack without chid err(%d)", ret); l3dss1_status_send(pc, CAUSE_MANDATORY_IE_MISS); dev_kfree_skb(skb); return; } ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } L3DelTimer(&pc->timer); if (mISDN_l3up(pc, CC_RESUME_ACKNOWLEDGE | INDICATION, skb)) dev_kfree_skb(skb); newl3state(pc, 10); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_resume_rej(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; u_char cause; if ((ret = l3dss1_get_cause(pc, skb))) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "RES_REJ get_cause err(%d)", ret); if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); dev_kfree_skb(skb); return; } ret = check_infoelements(pc, skb, ie_RESUME_REJECT); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } L3DelTimer(&pc->timer); if (mISDN_l3up(pc, CC_RESUME_REJECT | INDICATION, skb)) dev_kfree_skb(skb); newl3state(pc, 0); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); release_l3_process(pc); } static void l3dss1_global_restart(l3_process_t *pc, u_char pr, void *arg) { u_char *p=NULL, *cp=NULL, ri, ch = 0, chan = 0, chan_len=0; struct sk_buff *skb = arg, *nskb=NULL; Q931_info_t *qi = (Q931_info_t *)skb->data; l3_process_t *up, *n; int i=0; // newl3state(pc, 2); L3DelTimer(&pc->timer); if (qi->restart_ind.off) { p = skb->data; p += L3_EXTRA_SIZE + qi->restart_ind.off; p++; ri = p[1]; if (pc->l3->debug) l3_debug(pc->l3, "Restart %x", ri); } else { l3_debug(pc->l3, "Restart without restart IE"); ri = 0x86; } if (qi->channel_id.off) { p = skb->data; p += L3_EXTRA_SIZE + qi->channel_id.off; cp=p; p++; chan_len=p[0]; if (chan_len==1) { chan = p[1] & 0x3; ch = p[1]; } else { chan = p[3]; ch = p[1]; } if (pc->l3->debug) l3_debug(pc->l3, "Restart for channel %d", chan); } list_for_each_entry_safe(up, n, &pc->l3->plist, list) { if ((ri & 7) == 7) dss1man(up, CC_RESTART | REQUEST, NULL); else if (up->real_bc == chan) { mISDN_l3up(up, CC_RESTART | REQUEST, NULL); l3_debug(up->l3, "Resetting channel\n"); release_l3_process(up); } } dev_kfree_skb(skb); switch(chan_len) { case 0: nskb = MsgStart(pc, MT_RESTART_ACKNOWLEDGE, 3); p = skb_put(nskb, 3); break; case 1: nskb = MsgStart(pc, MT_RESTART_ACKNOWLEDGE, 6); p = skb_put(nskb, 6); if (chan) { *p++ = IE_CHANNEL_ID; *p++ = 1; *p++ = ch | 0x80; } break; case 3: nskb = MsgStart(pc, MT_RESTART_ACKNOWLEDGE, 8); p = skb_put(nskb, 8); /*copy channel ie*/ for (i=0; i<5; i++) *p++ = *cp++; break; } *p++ = IE_RESTART_IND; *p++ = 1; *p++ = ri; if (l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, nskb)) kfree_skb(nskb); } static void l3dss1_restart_ack_up(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; printk(KERN_NOTICE "Restart Acknowledge to upper layer\n"); if (mISDN_l3up(pc, CC_RESTART | CONFIRM, skb)) dev_kfree_skb(skb); } static void l3dss1_restart_ack(l3_process_t *pc, u_char pr, void *arg) { printk(KERN_NOTICE "Restart Acknowledge\n"); } static void l3dss1_dummy(l3_process_t *pc, u_char pr, void *arg) { } static void l3dss1_hold_req(l3_process_t *pc, u_char pr, void *arg) { if (!test_bit(FLG_PTP, &pc->l3->Flag)) { if ((pc->state & VALID_HOLD_STATES_PTMP) == 0) { /* not a valid HOLD state for PtMP */ return; } } switch(pc->aux_state) { case AUX_IDLE: break; default: int_errtxt("RETRIEVE_REQ in wrong aux state %d\n", pc->aux_state); case AUX_HOLD_IND: /* maybe collition, ignored */ return; } if (arg) SendMsg(pc, arg, -1); else l3dss1_message(pc, MT_HOLD); pc->aux_state = AUX_HOLD_REQ; L3AddTimer(&pc->aux_timer, THOLD, CC_THOLD); } static void l3dss1_hold_ack_req(l3_process_t *pc, u_char pr, void *arg) { switch(pc->aux_state) { case AUX_HOLD_IND: break; default: int_errtxt("HOLD_ACK in wrong aux state %d\n", pc->aux_state); return; } if (arg) SendMsg(pc, arg, -1); else l3dss1_message(pc, MT_HOLD_ACKNOWLEDGE); pc->aux_state = AUX_CALL_HELD; } static void l3dss1_hold_rej_req(l3_process_t *pc, u_char pr, void *arg) { switch(pc->aux_state) { case AUX_HOLD_IND: break; default: int_errtxt("HOLD_REJ in wrong aux state %d\n", pc->aux_state); return; } if (arg) SendMsg(pc, arg, -1); else l3dss1_message_cause(pc, MT_HOLD_REJECT, CAUSE_RESOURCES_UNAVAIL); // FIXME pc->aux_state = AUX_IDLE; } static void l3dss1_hold_ind(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; ret = check_infoelements(pc, skb, ie_HOLD); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } if (test_bit(FLG_PTP, &pc->l3->Flag)) { if ((pc->state & VALID_HOLD_STATES_PTP) == 0) { /* not a valid HOLD state for PtP */ l3dss1_message_cause(pc, MT_HOLD_REJECT, CAUSE_NOTCOMPAT_STATE); dev_kfree_skb(skb); return; } } else { if ((pc->state & VALID_HOLD_STATES_PTMP) == 0) { /* not a valid HOLD state for PtMP */ l3dss1_message_cause(pc, MT_HOLD_REJECT, CAUSE_NOTCOMPAT_STATE); dev_kfree_skb(skb); return; } } switch(pc->aux_state) { case AUX_HOLD_REQ: L3DelTimer(&pc->aux_timer); case AUX_IDLE: if (mISDN_l3up(pc, CC_HOLD | INDICATION, skb)) dev_kfree_skb(skb); else { pc->aux_state = AUX_HOLD_IND; } break; default: l3dss1_message_cause(pc, MT_HOLD_REJECT, CAUSE_NOTCOMPAT_STATE); dev_kfree_skb(skb); return; } if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_hold_rej(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; u_char cause; if ((ret = l3dss1_get_cause(pc, skb))) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "HOLD_REJ get_cause err(%d)", ret); if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); dev_kfree_skb(skb); return; } ret = check_infoelements(pc, skb, ie_HOLD_REJECT); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } switch(pc->aux_state) { case AUX_HOLD_REQ: L3DelTimer(&pc->aux_timer); break; default: int_errtxt("HOLD_REJ in wrong aux state %d\n", pc->aux_state); } pc->aux_state = AUX_IDLE; if (mISDN_l3up(pc, CC_HOLD_REJECT | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_hold_ignore(l3_process_t *pc, u_char pr, void *arg) { dev_kfree_skb(arg); } static void l3dss1_hold_req_ignore(l3_process_t *pc, u_char pr, void *arg) { } static void l3dss1_hold_ack(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; ret = check_infoelements(pc, skb, ie_HOLD_ACKNOWLEDGE); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } switch(pc->aux_state) { case AUX_HOLD_REQ: L3DelTimer(&pc->aux_timer); if (mISDN_l3up(pc, CC_HOLD_ACKNOWLEDGE | INDICATION, skb)) dev_kfree_skb(skb); pc->aux_state = AUX_CALL_HELD; break; default: int_errtxt("HOLD_ACK in wrong aux state %d\n", pc->aux_state); dev_kfree_skb(skb); } if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_retrieve_req(l3_process_t *pc, u_char pr, void *arg) { if (!test_bit(FLG_PTP, &pc->l3->Flag)) { if ((pc->state & (VALID_HOLD_STATES_PTMP | SBIT(12))) == 0) { /* not a valid RETRIEVE state for PtMP */ return; } } switch(pc->aux_state) { case AUX_CALL_HELD: break; default: int_errtxt("RETRIEVE_REQ in wrong aux state %d\n", pc->aux_state); case AUX_RETRIEVE_IND: /* maybe collition, ignored */ return; } if (arg) { SendMsg(pc, arg, -1); } else { newl3state(pc, -1); l3dss1_message(pc, MT_RETRIEVE); } pc->aux_state = AUX_RETRIEVE_REQ; L3AddTimer(&pc->aux_timer, TRETRIEVE, CC_TRETRIEVE); } static void l3dss1_retrieve_ack_req(l3_process_t *pc, u_char pr, void *arg) { switch(pc->aux_state) { case AUX_RETRIEVE_IND: break; default: int_errtxt("HOLD_REJ in wrong aux state %d\n", pc->aux_state); return; } if (arg) SendMsg(pc, arg, -1); else l3dss1_message(pc, MT_RETRIEVE_ACKNOWLEDGE); pc->aux_state = AUX_IDLE; } static void l3dss1_retrieve_rej_req(l3_process_t *pc, u_char pr, void *arg) { switch(pc->aux_state) { case AUX_RETRIEVE_IND: break; default: int_errtxt("HOLD_REJ in wrong aux state %d\n", pc->aux_state); return; } if (arg) SendMsg(pc, arg, -1); else l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_RESOURCES_UNAVAIL); // FIXME pc->aux_state = AUX_CALL_HELD; } static void l3dss1_retrieve_ind(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; if (test_bit(FLG_PTP, &pc->l3->Flag)) { if ((pc->state & (VALID_HOLD_STATES_PTP | SBIT(12))) == 0) { /* not a valid RETRIEVE state for PtP */ l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_NOTCOMPAT_STATE); dev_kfree_skb(skb); return; } } else { if ((pc->state & (VALID_HOLD_STATES_PTMP | SBIT(12))) == 0) { /* not a valid RETRIEVE state for PtMP */ l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_NOTCOMPAT_STATE); dev_kfree_skb(skb); return; } } ret = check_infoelements(pc, skb, ie_RETRIEVE); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } switch(pc->aux_state) { case AUX_RETRIEVE_REQ: L3DelTimer(&pc->aux_timer); case AUX_CALL_HELD: if (!(ret = l3dss1_get_channel_id(pc, skb))) { if ((0 == pc->bc) || (3 == pc->bc)) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "RETRIEVE with wrong chid %x", pc->bc); l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_INVALID_CONTENTS); dev_kfree_skb(skb); return; } } if (mISDN_l3up(pc, CC_RETRIEVE | INDICATION, skb)) dev_kfree_skb(skb); else { pc->aux_state = AUX_RETRIEVE_IND; } break; default: l3dss1_message_cause(pc, MT_RETRIEVE_REJECT, CAUSE_NOTCOMPAT_STATE); dev_kfree_skb(skb); return; } if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_retrieve_ack(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; ret = check_infoelements(pc, skb, ie_RETRIEVE_ACKNOWLEDGE); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } switch(pc->aux_state) { case AUX_RETRIEVE_REQ: L3DelTimer(&pc->aux_timer); if (!(ret = l3dss1_get_channel_id(pc, skb))) { if ((0 == pc->bc) || (3 == pc->bc)) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "RETRIEVE ACK with wrong chid %x", pc->bc); l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS); dev_kfree_skb(skb); return; } } if (mISDN_l3up(pc, CC_RETRIEVE_ACKNOWLEDGE | INDICATION, skb)) dev_kfree_skb(skb); pc->aux_state = AUX_IDLE; break; default: int_errtxt("RETRIEVE_ACK in wrong aux state %d\n", pc->aux_state); } if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); } static void l3dss1_retrieve_rej(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; int ret; u_char cause; if ((ret = l3dss1_get_cause(pc, skb))) { if (pc->l3->debug & L3_DEB_WARN) l3_debug(pc->l3, "RETRIEVE_REJ get_cause err(%d)", ret); if (ret == -1) cause = CAUSE_MANDATORY_IE_MISS; else cause = CAUSE_INVALID_CONTENTS; l3dss1_status_send(pc, cause); dev_kfree_skb(skb); return; } ret = check_infoelements(pc, skb, ie_RETRIEVE_REJECT); if (ERR_IE_COMPREHENSION == ret) { l3dss1_std_ie_err(pc, ret); dev_kfree_skb(skb); return; } switch(pc->aux_state) { case AUX_RETRIEVE_REQ: L3DelTimer(&pc->aux_timer); pc->aux_state = AUX_CALL_HELD; break; default: int_errtxt("RETRIEVE_REJ in wrong aux state %d\n", pc->aux_state); } pc->aux_state = AUX_IDLE; if (mISDN_l3up(pc, CC_RETRIEVE_REJECT | INDICATION, skb)) dev_kfree_skb(skb); } static void l3dss1_thold(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->aux_timer); #if 0 pc->cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ #endif mISDN_l3up(pc, CC_HOLD_REJECT | INDICATION, NULL); pc->aux_state = AUX_IDLE; } static void l3dss1_tretrieve(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->aux_timer); #if 0 pc->cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ #endif mISDN_l3up(pc, CC_RETRIEVE_REJECT | INDICATION, NULL); pc->aux_state = AUX_CALL_HELD; } static void l3dss1_t302(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_INVALID_NUMBER); mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); } static void l3dss1_t303(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); if (pc->n303 > 0) { pc->n303--; if (pc->t303skb) { struct sk_buff *skb; if (pc->n303 > 0) { skb = skb_clone(pc->t303skb, GFP_ATOMIC); } else { skb = pc->t303skb; pc->t303skb = NULL; } if (skb) SendMsg(pc, skb, -1); } L3AddTimer(&pc->timer, T303, CC_T303); return; } if (pc->t303skb) kfree_skb(pc->t303skb); pc->t303skb = NULL; l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, CAUSE_TIMER_EXPIRED); mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); release_l3_process(pc); } static void l3dss1_t304(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); } static void l3dss1_t305(l3_process_t *pc, u_char pr, void *arg) { int cause; L3DelTimer(&pc->timer); if (pc->cause != NO_CAUSE) { printk(KERN_NOTICE "Using buffered cause %x\n",pc->cause); cause = pc->cause; } else { cause=CAUSE_NORMAL_CLEARING; } newl3state(pc, 19); l3dss1_message_cause(pc, MT_RELEASE, cause); L3AddTimer(&pc->timer, T308, CC_T308_1); } static void l3dss1_t310(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); } static void l3dss1_t313(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); newl3state(pc, 11); l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED); mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); } static void l3dss1_t308_1(l3_process_t *pc, u_char pr, void *arg) { newl3state(pc, 19); L3DelTimer(&pc->timer); l3dss1_message(pc, MT_RELEASE); L3AddTimer(&pc->timer, T308, CC_T308_2); } static void l3dss1_t308_2(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL); release_l3_process(pc); } static void l3dss1_t318(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); #if 0 pc->cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ #endif mISDN_l3up(pc, CC_RESUME_REJECT | INDICATION, NULL); newl3state(pc, 19); l3dss1_message(pc, MT_RELEASE); L3AddTimer(&pc->timer, T308, CC_T308_1); } static void l3dss1_t319(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); #if 0 pc->cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ #endif mISDN_l3up(pc, CC_SUSPEND_REJECT | INDICATION, NULL); newl3state(pc, 10); } static void l3dss1_dl_reset(l3_process_t *pc, u_char pr, void *arg) { struct sk_buff *nskb, *skb = alloc_skb(L3_EXTRA_SIZE + 10, GFP_ATOMIC); Q931_info_t *qi; u_char *p; if (!skb) return; qi = (Q931_info_t *)skb_put(skb, L3_EXTRA_SIZE); mISDN_initQ931_info(qi); qi->type = MT_DISCONNECT; qi->cause.off = 1; p = skb_put(skb, 5); p++; *p++ = IE_CAUSE; *p++ = 2; *p++ = 0x80 | CAUSE_LOC_USER; *p++ = 0x80 | CAUSE_TEMPORARY_FAILURE; nskb = skb_clone(skb, GFP_ATOMIC); l3dss1_disconnect_req(pc, pr, skb); if (nskb) { if (mISDN_l3up(pc, CC_DISCONNECT | REQUEST, nskb)) dev_kfree_skb(nskb); } } static void l3dss1_dl_release(l3_process_t *pc, u_char pr, void *arg) { newl3state(pc, 0); #if 0 pc->cause = 0x1b; /* Destination out of order */ pc->para.loc = 0; #endif release_l3_process(pc); } static void l3dss1_dl_reestablish(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); L3AddTimer(&pc->timer, T309, CC_T309); l3_msg(pc->l3, DL_ESTABLISH | REQUEST, 0, 0, NULL); } static void l3dss1_dl_reest_status(l3_process_t *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); l3dss1_status_send(pc, CAUSE_NORMALUNSPECIFIED); } /* *INDENT-OFF* */ static struct stateentry downstatelist[] = { {SBIT(0), CC_SETUP | REQUEST, l3dss1_setup_req}, {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(25), CC_INFORMATION | REQUEST, l3dss1_information_req}, {ALL_STATES, CC_NOTIFY | REQUEST, l3dss1_notify_req}, {SBIT(10), CC_PROGRESS | REQUEST, l3dss1_progress_req}, {SBIT(0), CC_RESUME | REQUEST, l3dss1_resume_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25), CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, {SBIT(6) | SBIT(11) | SBIT(12), CC_RELEASE | REQUEST, l3dss1_release_req}, {ALL_STATES, CC_RESTART | REQUEST, l3dss1_restart}, {SBIT(6) | SBIT(25), CC_SETUP | RESPONSE, l3dss1_release_cmpl_req}, {ALL_STATES, CC_RELEASE_COMPLETE | REQUEST, l3dss1_release_cmpl_req}, {SBIT(6) | SBIT(25), CC_PROCEEDING | REQUEST, l3dss1_proceed_req}, {SBIT(6), CC_SETUP_ACKNOWLEDGE | REQUEST, l3dss1_setup_ack_req}, {SBIT(25), CC_SETUP_ACKNOWLEDGE | REQUEST, l3dss1_dummy}, {SBIT(6) | SBIT(9) | SBIT(25), CC_ALERTING | REQUEST, l3dss1_alert_req}, {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25), CC_CONNECT | REQUEST, l3dss1_connect_req}, {SBIT(10), CC_SUSPEND | REQUEST, l3dss1_suspend_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), CC_HOLD | REQUEST, l3dss1_hold_req}, {ALL_STATES, CC_HOLD | REQUEST, l3dss1_hold_req_ignore}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), CC_HOLD_ACKNOWLEDGE | REQUEST, l3dss1_hold_ack_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), CC_HOLD_REJECT | REQUEST, l3dss1_hold_rej_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), CC_RETRIEVE | REQUEST, l3dss1_retrieve_req}, {ALL_STATES, CC_RETRIEVE| REQUEST, l3dss1_hold_req_ignore}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), CC_RETRIEVE_ACKNOWLEDGE | REQUEST, l3dss1_retrieve_ack_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), CC_RETRIEVE_REJECT | REQUEST, l3dss1_retrieve_rej_req}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), CC_HOLD | REQUEST, l3dss1_hold_req}, {ALL_STATES, CC_FACILITY | REQUEST, l3dss1_facility_req}, {ALL_STATES, CC_STATUS_ENQUIRY | REQUEST, l3dss1_status_enq_req}, }; #define DOWNSLLEN \ (sizeof(downstatelist) / sizeof(struct stateentry)) static struct stateentry datastatelist[] = { {ALL_STATES, MT_STATUS_ENQUIRY, l3dss1_status_enq}, {ALL_STATES, MT_FACILITY, l3dss1_facility}, {SBIT(19), MT_STATUS, l3dss1_release_ind}, {ALL_STATES, MT_STATUS, l3dss1_status}, {SBIT(0), MT_SETUP, l3dss1_setup}, {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), MT_SETUP, l3dss1_dummy}, {SBIT(1) | SBIT(2), MT_CALL_PROCEEDING, l3dss1_call_proc}, {SBIT(1), MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, {SBIT(2) | SBIT(3), MT_ALERTING, l3dss1_alerting}, {SBIT(2) | SBIT(3) | SBIT(4), MT_PROGRESS, l3dss1_progress}, {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), MT_INFORMATION, l3dss1_information}, {ALL_STATES, MT_NOTIFY, l3dss1_notify}, {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25), MT_RELEASE, l3dss1_release}, {SBIT(19), MT_RELEASE, l3dss1_release_ind}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25), MT_DISCONNECT, l3dss1_disconnect}, {SBIT(19), MT_DISCONNECT, l3dss1_dummy}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), MT_CONNECT, l3dss1_connect}, {SBIT(8), MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, {SBIT(15), MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack}, {SBIT(15), MT_SUSPEND_REJECT, l3dss1_suspend_rej}, {SBIT(17), MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack}, {SBIT(17), MT_RESUME_REJECT, l3dss1_resume_rej}, {SBIT(12) | SBIT(19), MT_HOLD, l3dss1_hold_ignore}, {ALL_STATES, MT_HOLD, l3dss1_hold_ind}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), MT_HOLD_REJECT, l3dss1_hold_rej}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10), MT_HOLD_ACKNOWLEDGE, l3dss1_hold_ack}, {ALL_STATES, MT_RETRIEVE, l3dss1_retrieve_ind}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), MT_RETRIEVE_REJECT, l3dss1_retrieve_rej}, {SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(12), MT_RETRIEVE_ACKNOWLEDGE, l3dss1_retrieve_ack}, }; #define DATASLLEN \ (sizeof(datastatelist) / sizeof(struct stateentry)) static struct stateentry globalmes_list[] = { {ALL_STATES, MT_STATUS, l3dss1_status}, {SBIT(0), MT_RESTART, l3dss1_global_restart}, {SBIT(0), MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack_up}, {SBIT(1), MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack}, }; #define GLOBALM_LEN \ (sizeof(globalmes_list) / sizeof(struct stateentry)) static struct stateentry manstatelist[] = { {SBIT(2), DL_ESTABLISH | INDICATION, l3dss1_dl_reset}, {SBIT(10), DL_ESTABLISH | CONFIRM, l3dss1_dl_reest_status}, {SBIT(10), DL_RELEASE | INDICATION, l3dss1_dl_reestablish}, {ALL_STATES, DL_RELEASE | INDICATION, l3dss1_dl_release}, {SBIT(25), CC_T302, l3dss1_t302}, {SBIT(1), CC_T303, l3dss1_t303}, {SBIT(2), CC_T304, l3dss1_t304}, {SBIT(3), CC_T310, l3dss1_t310}, {SBIT(8), CC_T313, l3dss1_t313}, {SBIT(11), CC_T305, l3dss1_t305}, {SBIT(15), CC_T319, l3dss1_t319}, {SBIT(17), CC_T318, l3dss1_t318}, {SBIT(19), CC_T308_1, l3dss1_t308_1}, {SBIT(19), CC_T308_2, l3dss1_t308_2}, {SBIT(10), CC_T309, l3dss1_dl_release}, {SBIT(6), CC_TCTRL, l3dss1_reset}, {ALL_STATES, CC_THOLD, l3dss1_thold}, {ALL_STATES, CC_TRETRIEVE, l3dss1_tretrieve}, {ALL_STATES, CC_RESTART | REQUEST, l3dss1_restart}, }; #define MANSLLEN \ (sizeof(manstatelist) / sizeof(struct stateentry)) /* *INDENT-ON* */ static void global_handler(layer3_t *l3, u_int mt, struct sk_buff *skb) { u_int i; l3_process_t *proc = l3->global; proc->callref = skb->data[2]; /* cr flag */ for (i = 0; i < GLOBALM_LEN; i++) if ((mt == globalmes_list[i].primitive) && ((1 << proc->state) & globalmes_list[i].state)) break; if (i == GLOBALM_LEN) { if (l3->debug & L3_DEB_STATE) { l3_debug(l3, "dss1 global state %d mt %x unhandled", proc->state, mt); } l3dss1_status_send(proc, CAUSE_INVALID_CALLREF); dev_kfree_skb(skb); } else { if (l3->debug & L3_DEB_STATE) { l3_debug(l3, "dss1 global %d mt %x", proc->state, mt); } globalmes_list[i].rout(proc, mt, skb); } } static int dss1_fromdown(layer3_t *l3, struct sk_buff *skb, mISDN_head_t *hh) { u_int i; int cause, callState, ret = -EINVAL; char *ptr; l3_process_t *proc; Q931_info_t *qi; switch (hh->prim) { case (DL_DATA | INDICATION): case (DL_UNITDATA | INDICATION): break; case (DL_ESTABLISH | CONFIRM): case (DL_ESTABLISH | INDICATION): case (DL_RELEASE | INDICATION): case (DL_RELEASE | CONFIRM): l3_msg(l3, hh->prim, hh->dinfo, 0, NULL); dev_kfree_skb(skb); return(0); break; case (DL_DATA | CONFIRM): case (DL_UNITDATA | CONFIRM): dev_kfree_skb(skb); return(0); break; default: printk(KERN_WARNING "%s: unknown pr=%04x dinfo=%x\n", __FUNCTION__, hh->prim, hh->dinfo); return(-EINVAL); } if (skb->len < 3) { l3_debug(l3, "dss1up frame too short(%d)", skb->len); dev_kfree_skb(skb); return(0); } if (skb->data[0] != PROTO_DIS_EURO) { if (l3->debug & L3_DEB_PROTERR) { l3_debug(l3, "dss1up%sunexpected discriminator %x message len %d", (hh->prim == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", skb->data[0], skb->len); } dev_kfree_skb(skb); return(0); } ret = parseQ931(skb); if (ret < 0) { if (l3->debug & L3_DEB_PROTERR) l3_debug(l3, "dss1up: parse IE error %d", ret); printk(KERN_WARNING "dss1up: parse IE error %d\n", ret); dev_kfree_skb(skb); return(0); } if (l3->debug & L3_DEB_MSG) mISDN_LogL3Msg(skb); qi = (Q931_info_t *)skb->data; ptr = skb->data; ptr += L3_EXTRA_SIZE; if (l3->debug & L3_DEB_STATE) l3_debug(l3, "dss1up cr %d", qi->cr); if (qi->crlen == 0) { /* Dummy Callref */ switch(qi->type) { case MT_FACILITY: l3dss1_facility(l3->dummy, hh->prim, skb); return(0); default: if (l3->debug & L3_DEB_WARN) l3_debug(l3, "dss1up dummy Callref (no facility msg or ie)"); break; } return(0); } else if ((qi->cr & 0x7fff) == 0) { /* Global CallRef */ if (l3->debug & L3_DEB_STATE) l3_debug(l3, "dss1up Global CallRef"); global_handler(l3, qi->type, skb); return(0); } else if (!(proc = getl3proc(l3, qi->cr))) { /* No transaction process exist, that means no call with * this callreference is active */ if (qi->type == MT_SETUP) { /* Setup creates a new l3 process */ if (qi->cr & 0x8000) { /* Setup with wrong CREF flag */ if (l3->debug & L3_DEB_STATE) l3_debug(l3, "dss1up wrong CRef flag"); dev_kfree_skb(skb); return(0); } if (!(proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) { /* May be to answer with RELEASE_COMPLETE and * CAUSE 0x2f "Resource unavailable", but this * need a new_l3_process too ... arghh */ dev_kfree_skb(skb); return(0); } /* register this ID in L4 */ ret = mISDN_l3up(proc, CC_NEW_CR | INDICATION, NULL); if (ret) { printk(KERN_WARNING "dss1up: cannot register ID(%x)\n", proc->id); dev_kfree_skb(skb); release_l3_process(proc); return(0); } } else if (qi->type == MT_STATUS) { cause = 0; if (qi->cause.off) { if (ptr[qi->cause.off +1] >= 2) cause = ptr[qi->cause.off + 3] & 0x7f; else cause = ptr[qi->cause.off + 2] & 0x7f; } callState = 0; if (qi->call_state.off) { callState = ptr[qi->call_state.off + 2]; } /* ETS 300-104 part 2.4.1 * if setup has not been made and a message type * MT_STATUS is received with call state == 0, * we must send nothing */ if (callState != 0) { /* ETS 300-104 part 2.4.2 * if setup has not been made and a message type * MT_STATUS is received with call state != 0, * we must send MT_RELEASE_COMPLETE cause 101 */ if ((proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) { l3dss1_msg_without_setup(proc, CAUSE_NOTCOMPAT_STATE); } } dev_kfree_skb(skb); return(0); } else if (qi->type == MT_RELEASE_COMPLETE) { dev_kfree_skb(skb); return(0); } else { /* ETS 300-104 part 2 * if setup has not been made and a message type * (except MT_SETUP and RELEASE_COMPLETE) is received, * we must send MT_RELEASE_COMPLETE cause 81 */ printk("We got Message with Invalid Callref\n"); if ((proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) { l3dss1_msg_without_setup(proc, CAUSE_INVALID_CALLREF); } dev_kfree_skb(skb); return(0); } } if (l3dss1_check_messagetype_validity(proc, qi->type, skb)) { dev_kfree_skb(skb); return(0); } for (i = 0; i < DATASLLEN; i++) if ((qi->type == datastatelist[i].primitive) && ((1 << proc->state) & datastatelist[i].state)) break; if (i == DATASLLEN) { if (l3->debug & L3_DEB_STATE) { l3_debug(l3, "dss1up%sstate %d mt %#x unhandled", (hh->prim == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", proc->state, qi->type); } if ((MT_RELEASE_COMPLETE != qi->type) && (MT_RELEASE != qi->type)) { l3dss1_status_send(proc, CAUSE_NOTCOMPAT_STATE); } dev_kfree_skb(skb); } else { if (l3->debug & L3_DEB_STATE) { l3_debug(l3, "dss1up%sstate %d mt %x", (hh->prim == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", proc->state, qi->type); } datastatelist[i].rout(proc, hh->prim, skb); } return(0); } static int dss1_fromup(layer3_t *l3, struct sk_buff *skb, mISDN_head_t *hh) { u_int i; int cr, ret = -EINVAL; l3_process_t *proc; if ((DL_ESTABLISH | REQUEST) == hh->prim) { l3_msg(l3, hh->prim, 0, 0, NULL); dev_kfree_skb(skb); return(0); } proc = getl3proc4id(l3, hh->dinfo); if ((CC_NEW_CR | REQUEST) == hh->prim) { if (proc) { printk(KERN_WARNING "%s: proc(%x) allready exist\n", __FUNCTION__, hh->dinfo); ret = -EBUSY; } else { cr = newcallref(l3); cr |= 0x8000; ret = -ENOMEM; if ((proc = new_l3_process(l3, cr, N303, hh->dinfo))) { ret = 0; dev_kfree_skb(skb); } } return(ret); } if ((l3->debug & L3_DEB_MSG) && skb->len) mISDN_LogL3Msg(skb); if (!proc && hh->dinfo == MISDN_ID_DUMMY) { if (hh->prim == (CC_FACILITY | REQUEST)) { l3dss1_facility_req(l3->dummy, hh->prim, skb->len ? skb : NULL); ret = 0; } return(ret); } if (!proc && hh->dinfo == MISDN_ID_GLOBAL) { if (hh->prim == (CC_RESTART | REQUEST)) { if (l3->debug) printk(KERN_NOTICE "Global Restart Req\n"); l3dss1_restart_req(l3->dummy, hh->prim, skb->len ? skb : NULL); ret = 0; } return(ret); } if (!proc) { if(debug) printk(KERN_ERR "mISDN dss1 fromup without proc pr=%04x dinfo(%x)\n", hh->prim, hh->dinfo); return(-EINVAL); } for (i = 0; i < DOWNSLLEN; i++) if ((hh->prim == downstatelist[i].primitive) && ((1 << proc->state) & downstatelist[i].state)) break; if (i == DOWNSLLEN) { if (l3->debug & L3_DEB_STATE) { l3_debug(l3, "dss1down state %d prim %#x unhandled", proc->state, hh->prim); } dev_kfree_skb(skb); } else { if (l3->debug & L3_DEB_STATE) { l3_debug(l3, "dss1down state %d prim %#x para len %d", proc->state, hh->prim, skb->len); } if (skb->len) downstatelist[i].rout(proc, hh->prim, skb); else { downstatelist[i].rout(proc, hh->prim, NULL); dev_kfree_skb(skb); } } return(0); } static int dss1man(l3_process_t *proc, u_int pr, void *arg) { u_int i; if (!proc) { printk(KERN_ERR "mISDN dss1man without proc pr=%04x\n", pr); return(-EINVAL); } for (i = 0; i < MANSLLEN; i++) if ((pr == manstatelist[i].primitive) && ((1 << proc->state) & manstatelist[i].state)) break; if (i == MANSLLEN) { if (proc->l3->debug & L3_DEB_STATE) { l3_debug(proc->l3, "cr %d dss1man state %d prim %#x unhandled", proc->callref & 0x7fff, proc->state, pr); } } else { if (proc->l3->debug & L3_DEB_STATE) { l3_debug(proc->l3, "cr %d dss1man state %d prim %#x", proc->callref & 0x7fff, proc->state, pr); } manstatelist[i].rout(proc, pr, arg); } return(0); } static int dss1_function(mISDNinstance_t *inst, struct sk_buff *skb) { layer3_t *l3; int ret = -EINVAL; mISDN_head_t *hh; l3 = inst->privat; hh = mISDN_HEAD_P(skb); 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 = dss1_fromup(l3, skb, hh); break; case FLG_MSG_UP: ret = dss1_fromdown(l3, 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 */ if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS) { ret = -EOPNOTSUPP; break; } int_errtxt("not implemented yet"); break; } return(ret); } static void release_udss1(layer3_t *l3) { mISDNinstance_t *inst = &l3->inst; u_long flags; if (debug) printk(KERN_DEBUG "release_udss1 refcnt %d l3(%p) inst(%p)\n", u_dss1.refcnt, l3, inst); release_l3(l3); #ifdef FIXME 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); } #endif spin_lock_irqsave(&u_dss1.lock, flags); list_del(&l3->list); spin_unlock_irqrestore(&u_dss1.lock, flags); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); if (l3->entity != MISDN_ENTITY_NONE) mISDN_ctrl(inst, MGR_DELENTITY | REQUEST, (void *)((u_long)l3->entity)); kfree(l3); } static int new_udss1(mISDNstack_t *st, mISDN_pid_t *pid) { layer3_t *nl3; int err; u_long flags; if (!st || !pid) return(-EINVAL); if (!(nl3 = kmalloc(sizeof(layer3_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc layer3 failed\n"); return(-ENOMEM); } memset(nl3, 0, sizeof(layer3_t)); memcpy(&nl3->inst.pid, pid, sizeof(mISDN_pid_t)); nl3->debug = debug; mISDN_init_instance(&nl3->inst, &u_dss1, nl3, dss1_function); if (!mISDN_SetHandledPID(&u_dss1, &nl3->inst.pid)) { int_error(); return(-ENOPROTOOPT); } if ((pid->protocol[3] & ~ISDN_PID_FEATURE_MASK) != ISDN_PID_L3_DSS1USER) { printk(KERN_ERR "udss1 create failed prt %x\n", pid->protocol[3]); kfree(nl3); return(-ENOPROTOOPT); } init_l3(nl3); if (pid->protocol[3] & ISDN_PID_L3_DF_PTP) test_and_set_bit(FLG_PTP, &nl3->Flag); if (pid->protocol[3] & ISDN_PID_L3_DF_EXTCID) test_and_set_bit(FLG_EXTCID, &nl3->Flag); if (pid->protocol[3] & ISDN_PID_L3_DF_CRLEN2) test_and_set_bit(FLG_CRLEN2, &nl3->Flag); if (!(nl3->global = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) { printk(KERN_ERR "mISDN can't get memory for dss1 global CR\n"); release_l3(nl3); kfree(nl3); return(-ENOMEM); } nl3->global->state = 0; nl3->global->callref = 0; nl3->global->id = MISDN_ID_GLOBAL; INIT_LIST_HEAD(&nl3->global->list); nl3->global->n303 = N303; nl3->global->l3 = nl3; nl3->global->t303skb = NULL; L3InitTimer(nl3->global, &nl3->global->timer); L3InitTimer(nl3->global, &nl3->global->aux_timer); if (!(nl3->dummy = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) { printk(KERN_ERR "mISDN can't get memory for dss1 dummy CR\n"); release_l3(nl3); kfree(nl3); return(-ENOMEM); } nl3->dummy->state = 0; nl3->dummy->callref = -1; nl3->dummy->id = MISDN_ID_DUMMY; INIT_LIST_HEAD(&nl3->dummy->list); nl3->dummy->n303 = N303; nl3->dummy->l3 = nl3; nl3->dummy->t303skb = NULL; L3InitTimer(nl3->dummy, &nl3->dummy->timer); L3InitTimer(nl3->dummy, &nl3->dummy->aux_timer); sprintf(nl3->inst.name, "DSS1 %x", st->id >> 8); nl3->p_mgr = dss1man; spin_lock_irqsave(&u_dss1.lock, flags); list_add_tail(&nl3->list, &u_dss1.ilist); spin_unlock_irqrestore(&u_dss1.lock, flags); err = mISDN_ctrl(&nl3->inst, MGR_NEWENTITY | REQUEST, NULL); if (err) { printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%d)\n", __FUNCTION__, err); } err = mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &nl3->inst); if (err) { release_l3(nl3); list_del(&nl3->list); kfree(nl3); } else { mISDN_stPara_t stp; if (st->para.down_headerlen) nl3->down_headerlen = st->para.down_headerlen; stp.maxdatalen = 0; stp.up_headerlen = L3_EXTRA_SIZE; stp.down_headerlen = 0; mISDN_ctrl(st, MGR_ADDSTPARA | REQUEST, &stp); } return(err); } static char MName[] = "UDSS1"; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #endif #endif static int udss1_manager(void *data, u_int prim, void *arg) { mISDNinstance_t *inst = data; layer3_t *l3l; u_long flags; if (debug & MISDN_DEBUG_MANAGER) printk(KERN_DEBUG "udss1_manager data:%p prim:%x arg:%p\n", data, prim, arg); if (!data) return(-EINVAL); spin_lock_irqsave(&u_dss1.lock, flags); list_for_each_entry(l3l, &u_dss1.ilist, list) { if (&l3l->inst == inst) break; } if (&l3l->list == &u_dss1.ilist) l3l = NULL; spin_unlock_irqrestore(&u_dss1.lock, flags); if (prim == (MGR_NEWLAYER | REQUEST)) return(new_udss1(data, arg)); if (!l3l) { if (debug & 0x1) printk(KERN_WARNING "udss1_manager prim(%x) no instance\n", prim); return(-EINVAL); } switch(prim) { case MGR_NEWENTITY | CONFIRM: l3l->entity = (u_long)arg & 0xffffffff; break; case MGR_ADDSTPARA | INDICATION: l3l->down_headerlen = ((mISDN_stPara_t *)arg)->down_headerlen; case MGR_CLRSTPARA | INDICATION: break; #ifdef FIXME case MGR_CONNECT | REQUEST: return(mISDN_ConnectIF(inst, arg)); case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: return(mISDN_SetIF(inst, arg, prim, dss1_fromup, dss1_fromdown, l3l)); case MGR_DISCONNECT | REQUEST: case MGR_DISCONNECT | INDICATION: return(mISDN_DisConnectIF(inst, arg)); #endif case MGR_RELEASE | INDICATION: case MGR_UNREGLAYER | REQUEST: if (debug & MISDN_DEBUG_MANAGER) printk(KERN_DEBUG "release_udss1 id %x\n", l3l->inst.st->id); release_udss1(l3l); break; PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); default: if (debug & 0x1) printk(KERN_WARNING "udss1 prim %x not handled\n", prim); return(-EINVAL); } return(0); } int UDSS1Init(void) { int err; char tmp[32]; strcpy(tmp, dss1_revision); printk(KERN_INFO "mISDN: DSS1 Rev. %s\n", mISDN_getrev(tmp)); #ifdef MODULE u_dss1.owner = THIS_MODULE; #endif spin_lock_init(&u_dss1.lock); INIT_LIST_HEAD(&u_dss1.ilist); u_dss1.name = MName; u_dss1.DPROTO.protocol[3] = ISDN_PID_L3_DSS1USER | ISDN_PID_L3_DF_PTP | ISDN_PID_L3_DF_EXTCID | ISDN_PID_L3_DF_CRLEN2; u_dss1.own_ctrl = udss1_manager; mISDNl3New(); if ((err = mISDN_register(&u_dss1))) { printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); mISDNl3Free(); } else mISDN_module_register(THIS_MODULE); return(err); } #ifdef MODULE void UDSS1_cleanup(void) { int err; layer3_t *l3, *next; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&u_dss1))) { printk(KERN_ERR "Can't unregister User DSS1 error(%d)\n", err); } if (!list_empty(&u_dss1.ilist)) { printk(KERN_WARNING "mISDNl3 u_dss1 list not empty\n"); list_for_each_entry_safe(l3, next, &u_dss1.ilist, list) release_udss1(l3); } mISDNl3Free(); } module_init(UDSS1Init); module_exit(UDSS1_cleanup); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/layer1.c0000644000000000000500000005304711135651702017741 0ustar rootsrc/* $Id: layer1.c,v 1.20 2007/02/13 10:43:45 crich Exp $ * * mISDN_l1.c common low level stuff for I.430 layer1 TE mode * * Author Karsten Keil (keil@isdn4linux.de) * * This file is released under the GPLv2 * */ static char *l1_revision = "$Revision: 1.20 $"; #include #include "core.h" #include "layer1.h" #include "helper.h" #include "debug.h" typedef struct _layer1 { struct list_head list; u_long Flags; struct FsmInst l1m; struct FsmTimer timer; int debug; int delay; mISDNinstance_t inst; } layer1_t; /* l1 status_info */ typedef struct _status_info_l1 { int len; int typ; int protocol; int status; int state; u_long Flags; int T3; int delay; int debug; } status_info_l1_t; static int debug = 0; 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 = {NULL, 0, 0, NULL, NULL}; enum { ST_L1_F2, ST_L1_F3, ST_L1_F4, ST_L1_F5, ST_L1_F6, ST_L1_F7, ST_L1_F8, }; #define L1S_STATE_COUNT (ST_L1_F8+1) static char *strL1SState[] = { "ST_L1_F2", "ST_L1_F3", "ST_L1_F4", "ST_L1_F5", "ST_L1_F6", "ST_L1_F7", "ST_L1_F8", }; #ifdef mISDN_UINTERFACE static struct Fsm l1fsm_u = {NULL, 0, 0, NULL, NULL}; enum { ST_L1_RESET, ST_L1_DEACT, ST_L1_SYNC2, ST_L1_TRANS, }; #define L1U_STATE_COUNT (ST_L1_TRANS+1) static char *strL1UState[] = { "ST_L1_RESET", "ST_L1_DEACT", "ST_L1_SYNC2", "ST_L1_TRANS", }; #endif #ifdef OBSOLETE enum { ST_L1_NULL, ST_L1_WAIT_ACT, ST_L1_WAIT_DEACT, ST_L1_ACTIV, }; #define L1B_STATE_COUNT (ST_L1_ACTIV+1) static char *strL1BState[] = { "ST_L1_NULL", "ST_L1_WAIT_ACT", "ST_L1_WAIT_DEACT", "ST_L1_ACTIV", }; #endif enum { EV_PH_ACTIVATE, EV_PH_DEACTIVATE, EV_RESET_IND, EV_DEACT_CNF, EV_DEACT_IND, EV_POWER_UP, EV_ANYSIG_IND, EV_INFO2_IND, EV_INFO4_IND, EV_TIMER_DEACT, EV_TIMER_ACT, EV_TIMER3, }; #define L1_EVENT_COUNT (EV_TIMER3 + 1) static char *strL1Event[] = { "EV_PH_ACTIVATE", "EV_PH_DEACTIVATE", "EV_RESET_IND", "EV_DEACT_CNF", "EV_DEACT_IND", "EV_POWER_UP", "EV_ANYSIG_IND", "EV_INFO2_IND", "EV_INFO4_IND", "EV_TIMER_DEACT", "EV_TIMER_ACT", "EV_TIMER3", }; static void l1m_debug(struct FsmInst *fi, char *fmt, ...) { layer1_t *l1 = fi->userdata; logdata_t log; va_start(log.args, fmt); log.fmt = fmt; log.head = l1->inst.name; mISDN_ctrl(&l1->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } static int l1up(layer1_t *l1, u_int prim, int dinfo, int len, void *arg) { 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(mISDN_queue_data(&l1->inst, FLG_MSG_DOWN, prim, dinfo, len, arg, 0)); } static void l1_reset(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_L1_F3); } static void l1_deact_cnf(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; mISDN_FsmChangeState(fi, ST_L1_F3); if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL); } static void l1_deact_req_s(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; mISDN_FsmChangeState(fi, ST_L1_F3); mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2); test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); } static void l1_power_up_s(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { mISDN_FsmChangeState(fi, ST_L1_F4); l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL); } else mISDN_FsmChangeState(fi, ST_L1_F3); } static void l1_go_F5(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_L1_F5); } static void l1_go_F8(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_L1_F8); } static void l1_info2_ind(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; #ifdef mISDN_UINTERFACE if (test_bit(FLG_L1_UINT, &l1->Flags)) mISDN_FsmChangeState(fi, ST_L1_SYNC2); else #endif mISDN_FsmChangeState(fi, ST_L1_F6); l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL); } static void l1_info4_ind(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; #ifdef mISDN_UINTERFACE if (test_bit(FLG_L1_UINT, &l1->Flags)) mISDN_FsmChangeState(fi, ST_L1_TRANS); else #endif mISDN_FsmChangeState(fi, ST_L1_F7); l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL); if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags)) mISDN_FsmDelTimer(&l1->timer, 4); if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags)) mISDN_FsmDelTimer(&l1->timer, 3); mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2); test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags); } } static void 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); 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)) #endif if (l1->l1m.state != ST_L1_F6) { mISDN_FsmChangeState(fi, ST_L1_F3); l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL); } } static void l1_timer_act(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags); test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags); if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) 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 l1_timer_deact(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; u_int db = HW_D_NOBLOCKED; test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags); test_and_clear_bit(FLG_L1_ACTIVATED, &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); 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 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); } static void l1_activate_no(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; u_int db = HW_D_NOBLOCKED; if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) && (!test_bit(FLG_L1_T3RUN, &l1->Flags))) { 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); } } static struct FsmNode L1SFnList[] = { {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, {ST_L1_F3, EV_RESET_IND, l1_reset}, {ST_L1_F4, EV_RESET_IND, l1_reset}, {ST_L1_F5, EV_RESET_IND, l1_reset}, {ST_L1_F6, EV_RESET_IND, l1_reset}, {ST_L1_F7, EV_RESET_IND, l1_reset}, {ST_L1_F8, EV_RESET_IND, l1_reset}, {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, {ST_L1_F4, EV_ANYSIG_IND,l1_go_F5}, {ST_L1_F6, EV_ANYSIG_IND,l1_go_F8}, {ST_L1_F7, EV_ANYSIG_IND,l1_go_F8}, {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, {ST_L1_F3, EV_TIMER3, l1_timer3}, {ST_L1_F4, EV_TIMER3, l1_timer3}, {ST_L1_F5, EV_TIMER3, l1_timer3}, {ST_L1_F6, EV_TIMER3, l1_timer3}, {ST_L1_F8, EV_TIMER3, l1_timer3}, {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, }; #define L1S_FN_COUNT (sizeof(L1SFnList)/sizeof(struct FsmNode)) #ifdef mISDN_UINTERFACE static void l1_deact_req_u(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; mISDN_FsmChangeState(fi, ST_L1_RESET); mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2); test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL); } static void l1_power_up_u(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); } static void l1_info0_ind(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_L1_DEACT); } static void l1_activate_u(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; l1down(l1, PH_SIGNAL | REQUEST, INFO1, 0, NULL); } static struct FsmNode L1UFnList[] = { {ST_L1_RESET, EV_DEACT_IND, l1_deact_req_u}, {ST_L1_DEACT, EV_DEACT_IND, l1_deact_req_u}, {ST_L1_SYNC2, EV_DEACT_IND, l1_deact_req_u}, {ST_L1_TRANS, EV_DEACT_IND, l1_deact_req_u}, {ST_L1_DEACT, EV_PH_ACTIVATE, l1_activate_u}, {ST_L1_DEACT, EV_POWER_UP, l1_power_up_u}, {ST_L1_DEACT, EV_INFO2_IND, l1_info2_ind}, {ST_L1_TRANS, EV_INFO2_IND, l1_info2_ind}, {ST_L1_RESET, EV_DEACT_CNF, l1_info0_ind}, {ST_L1_DEACT, EV_INFO4_IND, l1_info4_ind}, {ST_L1_SYNC2, EV_INFO4_IND, l1_info4_ind}, {ST_L1_RESET, EV_INFO4_IND, l1_info4_ind}, {ST_L1_DEACT, EV_TIMER3, l1_timer3}, {ST_L1_SYNC2, EV_TIMER3, l1_timer3}, {ST_L1_TRANS, EV_TIMER_ACT, l1_timer_act}, {ST_L1_DEACT, EV_TIMER_DEACT, l1_timer_deact}, {ST_L1_SYNC2, EV_TIMER_DEACT, l1_timer_deact}, {ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact}, }; #define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode)) #endif #ifdef OBSOLETE static void l1b_activate(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; mISDN_FsmChangeState(fi, ST_L1_WAIT_ACT); mISDN_FsmRestartTimer(&l1->timer, l1->delay, EV_TIMER_ACT, NULL, 2); } static void l1b_deactivate(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; mISDN_FsmChangeState(fi, ST_L1_WAIT_DEACT); mISDN_FsmRestartTimer(&l1->timer, 10, EV_TIMER_DEACT, NULL, 2); } static void l1b_timer_act(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; mISDN_FsmChangeState(fi, ST_L1_ACTIV); l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL); } static void l1b_timer_deact(struct FsmInst *fi, int event, void *arg) { layer1_t *l1 = fi->userdata; mISDN_FsmChangeState(fi, ST_L1_NULL); l1up(l1, PH_DEACTIVATE | CONFIRM, 0, 0, NULL); } static struct FsmNode L1BFnList[] = { {ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate}, {ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act}, {ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate}, {ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact}, }; #define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode)) #endif static int l1from_up(layer1_t *l1, struct sk_buff *skb, mISDN_head_t *hh) { int err = 0; switch(hh->prim) { case (PH_DATA | REQUEST): case (PH_CONTROL | REQUEST): return(mISDN_queue_down(&l1->inst, 0, skb)); break; case (PH_ACTIVATE | REQUEST): if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL); else { test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags); mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); } break; case (MDL_FINDTEI | REQUEST): return(mISDN_queue_up(&l1->inst, 0, skb)); break; default: if (l1->debug) mISDN_debug(l1->inst.st->id, NULL, "l1from_up msg %x unhandled", hh->prim); err = -EINVAL; break; } if (!err) dev_kfree_skb(skb); return(err); } static int l1from_down(layer1_t *l1, struct sk_buff *skb, mISDN_head_t *hh) { int err = 0; if (hh->prim == PH_DATA_IND) { if (test_bit(FLG_L1_ACTTIMER, &l1->Flags)) mISDN_FsmEvent(&l1->l1m, EV_TIMER_ACT, NULL); return(mISDN_queue_up(&l1->inst, 0, skb)); } else if (hh->prim == PH_DATA_CNF) { 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); else if (hh->dinfo == HW_DEACTIVATE) mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); else if (hh->dinfo == HW_POWERUP) mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); else if (l1->debug) 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) mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); else if (l1->debug) mISDN_debug(l1->inst.st->id, NULL, "l1from_down ctrl cnf %x unhandled", hh->dinfo); } else if (hh->prim == (PH_SIGNAL | INDICATION)) { if (hh->dinfo == ANYSIGNAL) mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); else if (hh->dinfo == LOSTFRAMING) mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); else if (hh->dinfo == INFO2) mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); else if (hh->dinfo == INFO4_P8) mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); else if (hh->dinfo == INFO4_P10) mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); else if (l1->debug) mISDN_debug(l1->inst.st->id, NULL, "l1from_down sig %x unhandled", hh->dinfo); } else { if (l1->debug) mISDN_debug(l1->inst.st->id, NULL, "l1from_down msg %x unhandled", hh->prim); err = -EINVAL; } if (!err) dev_kfree_skb(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); } if (inst->down.peer) { 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); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); kfree(l1); } static int new_l1(mISDNstack_t *st, mISDN_pid_t *pid) { layer1_t *nl1; int err; u_long flags; if (!st || !pid) return(-EINVAL); if (!(nl1 = kmalloc(sizeof(layer1_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc layer1_t failed\n"); return(-ENOMEM); } memset(nl1, 0, sizeof(layer1_t)); memcpy(&nl1->inst.pid, pid, sizeof(mISDN_pid_t)); 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 %x", st->id >> 8); nl1->l1m.fsm = &l1fsm_s; nl1->l1m.state = ST_L1_F3; nl1->Flags = 0; break; default: printk(KERN_ERR "layer1 create failed prt %x\n", pid->protocol[1]); kfree(nl1); return(-ENOPROTOOPT); } nl1->debug = debug; nl1->l1m.debug = debug; nl1->l1m.userdata = nl1; 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 = mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &nl1->inst); if (err) { mISDN_FsmDelTimer(&nl1->timer, 0); list_del(&nl1->list); kfree(nl1); } return(err); } static int l1_status(layer1_t *l1, status_info_l1_t *si) { if (!si) return(-EINVAL); memset(si, 0, sizeof(status_info_l1_t)); si->len = sizeof(status_info_l1_t) - 2*sizeof(int); si->typ = STATUS_INFO_L1; si->protocol = l1->inst.pid.protocol[1]; if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) si->status = 1; si->state = l1->l1m.state; si->Flags = l1->Flags; si->T3 = TIMER3_VALUE; si->debug = l1->delay; si->debug = l1->debug; return(0); } static char MName[] = "ISDNL1"; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #endif #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #define Isdnl1Init init_module #endif static int 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))) { if (debug) 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; case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: err = mISDN_SetIF(inst, arg, prim, l1from_up, l1from_down, l1l); break; case MGR_DISCONNECT | REQUEST: 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); release_l1(l1l); break; case MGR_STATUS | REQUEST: err = l1_status(l1l, arg); break; PRIM_NOT_HANDLED(MGR_CTRLREADY|INDICATION); PRIM_NOT_HANDLED(MGR_ADDSTPARA|INDICATION); PRIM_NOT_HANDLED(MGR_SETSTACK|INDICATION); default: printk(KERN_WARNING "l1_manager prim %x not handled\n", prim); err = -EINVAL; break; } return(err); } int Isdnl1Init(void) { int err; printk(KERN_INFO "ISDN L1 driver version %s\n", mISDN_getrev(l1_revision)); #ifdef MODULE isdnl1.owner = THIS_MODULE; #endif 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; l1fsm_u.state_count = L1U_STATE_COUNT; l1fsm_u.event_count = L1_EVENT_COUNT; l1fsm_u.strEvent = strL1Event; l1fsm_u.strState = strL1UState; mISDN_FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT); #endif l1fsm_s.state_count = L1S_STATE_COUNT; l1fsm_s.event_count = L1_EVENT_COUNT; 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 } else mISDN_module_register(THIS_MODULE); return(err); } #ifdef MODULE void cleanup_module(void) { int err; layer1_t *l1, *nl1; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&isdnl1))) { printk(KERN_ERR "Can't unregister ISDN layer 1 error(%d)\n", err); } if(!list_empty(&isdnl1.ilist)) { printk(KERN_WARNING "mISDNl1 inst list not empty\n"); list_for_each_entry_safe(l1, nl1, &isdnl1.ilist, list) release_l1(l1); } #ifdef mISDN_UINTERFACE mISDN_FsmFree(&l1fsm_u); #endif mISDN_FsmFree(&l1fsm_s); #ifdef OBSOLETE mISDN_FsmFree(&l1fsm_b); #endif } #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/layer2.c0000644000000000000500000016545611135651702017752 0ustar rootsrc/* $Id: layer2.c,v 1.32 2006/12/21 15:25:06 nadi Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * This file is released under the GPLv2 * */ #include #include "core.h" #include "layer2.h" #include "helper.h" #include "debug.h" static char *l2_revision = "$Revision: 1.32 $"; static void l2m_debug(struct FsmInst *fi, char *fmt, ...); static int debug = 0; static mISDNobject_t isdnl2; static struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; enum { ST_L2_1, ST_L2_2, ST_L2_3, ST_L2_4, ST_L2_5, ST_L2_6, ST_L2_7, ST_L2_8, }; #define L2_STATE_COUNT (ST_L2_8+1) static char *strL2State[] = { "ST_L2_1", "ST_L2_2", "ST_L2_3", "ST_L2_4", "ST_L2_5", "ST_L2_6", "ST_L2_7", "ST_L2_8", }; enum { EV_L2_UI, EV_L2_SABME, EV_L2_DISC, EV_L2_DM, EV_L2_UA, EV_L2_FRMR, EV_L2_SUPER, EV_L2_I, EV_L2_DL_DATA, EV_L2_ACK_PULL, EV_L2_DL_UNITDATA, EV_L2_DL_ESTABLISH_REQ, EV_L2_DL_RELEASE_REQ, EV_L2_MDL_ASSIGN, EV_L2_MDL_REMOVE, EV_L2_MDL_ERROR, EV_L1_DEACTIVATE, EV_L2_T200, EV_L2_T203, EV_L2_SET_OWN_BUSY, EV_L2_CLEAR_OWN_BUSY, EV_L2_FRAME_ERROR, }; #define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1) static char *strL2Event[] = { "EV_L2_UI", "EV_L2_SABME", "EV_L2_DISC", "EV_L2_DM", "EV_L2_UA", "EV_L2_FRMR", "EV_L2_SUPER", "EV_L2_I", "EV_L2_DL_DATA", "EV_L2_ACK_PULL", "EV_L2_DL_UNITDATA", "EV_L2_DL_ESTABLISH_REQ", "EV_L2_DL_RELEASE_REQ", "EV_L2_MDL_ASSIGN", "EV_L2_MDL_REMOVE", "EV_L2_MDL_ERROR", "EV_L1_DEACTIVATE", "EV_L2_T200", "EV_L2_T203", "EV_L2_SET_OWN_BUSY", "EV_L2_CLEAR_OWN_BUSY", "EV_L2_FRAME_ERROR", }; inline u_int l2headersize(layer2_t *l2, int ui) { return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1)); } inline u_int l2addrsize(layer2_t *l2) { return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); } static int l2_newid(layer2_t *l2) { int id; id = l2->next_id++; if (id == 0x7fff) l2->next_id = 1; id |= (l2->entity << 16); return(id); } static int l2up(layer2_t *l2, u_int prim, int dinfo, struct sk_buff *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(mISDN_queue_data(&l2->inst, FLG_MSG_UP, prim, dinfo, len, arg, 0)); } static int l2down_skb(layer2_t *l2, struct sk_buff *skb) { int ret; ret = mISDN_queue_down(&l2->inst, 0, skb); if (ret && l2->debug) printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret); return(ret); } static int l2down_raw(layer2_t *l2, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); if (hh->prim == PH_DATA_REQ) { if (test_and_set_bit(FLG_L1_BUSY, &l2->flag)) { skb_queue_tail(&l2->down_queue, skb); return(0); } l2->down_id = mISDN_HEAD_DINFO(skb); } return(l2down_skb(l2, skb)); } static int l2down(layer2_t *l2, u_int prim, int dinfo, struct sk_buff *skb) { mISDN_sethead(prim, dinfo, skb); return(l2down_raw(l2, skb)); } static int l2down_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg) { struct sk_buff *skb; int err; skb = create_link_skb(prim, dinfo, len, arg, 0); if (!skb) return(-ENOMEM); err = l2down_raw(l2, skb); if (err) dev_kfree_skb(skb); return(err); } #ifdef OBSOLETE static int l2_chain_down(mISDNinstance_t *inst, struct sk_buff *skb) { return(l2down_raw(inst->privat, skb)); } #endif static int 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)) { if (hh->dinfo == l2->down_id) { if ((nskb = skb_dequeue(&l2->down_queue))) { l2->down_id = mISDN_HEAD_DINFO(nskb); if (l2down_skb(l2, nskb)) { dev_kfree_skb(nskb); l2->down_id = MISDN_ID_NONE; } } 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; } if (l2->down_id == MISDN_ID_NONE) { test_and_clear_bit(FLG_L1_BUSY, &l2->flag); mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); } } } #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); if (l2down_skb(l2, nskb)) { dev_kfree_skb(nskb); l2->down_id = MISDN_ID_NONE; test_and_clear_bit(FLG_L1_BUSY, &l2->flag); } } else test_and_clear_bit(FLG_L1_BUSY, &l2->flag); } return(ret); } static int l2mgr(layer2_t *l2, u_int prim, void *arg) { long c = (long)arg; printk(KERN_WARNING "l2mgr: addr:%x prim %x %c\n", l2->inst.id, prim, (char)c); if (test_bit(FLG_LAPD, &l2->flag) && !test_bit(FLG_FIXED_TEI, &l2->flag)) { struct sk_buff *skb; switch(c) { case 'C': case 'D': case 'G': case 'H': skb = create_link_skb(prim, 0, 0, NULL, 0); if (!skb) break; if (l2_tei(l2->tm, skb)) dev_kfree_skb(skb); break; } } return(0); } static void set_peer_busy(layer2_t *l2) { test_and_set_bit(FLG_PEER_BUSY, &l2->flag); if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) test_and_set_bit(FLG_L2BLOCK, &l2->flag); } static void clear_peer_busy(layer2_t *l2) { if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) test_and_clear_bit(FLG_L2BLOCK, &l2->flag); } static void InitWin(layer2_t *l2) { int i; for (i = 0; i < MAX_WINDOW; i++) l2->windowar[i] = NULL; } static int freewin(layer2_t *l2) { int i, cnt = 0; for (i = 0; i < MAX_WINDOW; i++) { if (l2->windowar[i]) { cnt++; dev_kfree_skb(l2->windowar[i]); l2->windowar[i] = NULL; } } return cnt; } static void ReleaseWin(layer2_t *l2) { int cnt; if((cnt = freewin(l2))) printk(KERN_WARNING "isdnl2 freed %d skbuffs in release\n", cnt); } inline unsigned int cansend(layer2_t *l2) { unsigned int p1; if(test_bit(FLG_MOD128, &l2->flag)) p1 = (l2->vs - l2->va) % 128; else p1 = (l2->vs - l2->va) % 8; return ((p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag)); } inline void clear_exception(layer2_t *l2) { test_and_clear_bit(FLG_ACK_PEND, &l2->flag); test_and_clear_bit(FLG_REJEXC, &l2->flag); test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); clear_peer_busy(l2); } static int sethdraddr(layer2_t *l2, u_char *header, int rsp) { u_char *ptr = header; int crbit = rsp; if (test_bit(FLG_LAPD, &l2->flag)) { if (test_bit(FLG_LAPD_NET, &l2->flag)) crbit = !crbit; *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0); *ptr++ = (l2->tei << 1) | 1; return (2); } else { if (test_bit(FLG_ORIG, &l2->flag)) crbit = !crbit; if (crbit) *ptr++ = l2->addr.B; else *ptr++ = l2->addr.A; return (1); } } inline static void enqueue_super(layer2_t *l2, struct sk_buff *skb) { if (l2down(l2, PH_DATA | REQUEST, l2_newid(l2), skb)) dev_kfree_skb(skb); } inline static void enqueue_ui(layer2_t *l2, struct sk_buff *skb) { if (l2down(l2, PH_DATA | REQUEST, mISDN_HEAD_DINFO(skb), skb)) dev_kfree_skb(skb); } inline int IsUI(u_char * data) { return ((data[0] & 0xef) == UI); } inline int IsUA(u_char * data) { return ((data[0] & 0xef) == UA); } inline int IsDM(u_char * data) { return ((data[0] & 0xef) == DM); } inline int IsDISC(u_char * data) { return ((data[0] & 0xef) == DISC); } inline int IsRR(u_char * data, layer2_t *l2) { if (test_bit(FLG_MOD128, &l2->flag)) return (data[0] == RR); else return ((data[0] & 0xf) == 1); } 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)); } inline int IsSABME(u_char * data, layer2_t *l2) { u_char d = data[0] & ~0x10; return (test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM); } inline int IsREJ(u_char * data, layer2_t *l2) { return (test_bit(FLG_MOD128, &l2->flag) ? data[0] == REJ : (data[0] & 0xf) == REJ); } inline int IsFRMR(u_char * data) { return ((data[0] & 0xef) == FRMR); } inline int IsRNR(u_char * data, layer2_t *l2) { return (test_bit(FLG_MOD128, &l2->flag) ? data[0] == RNR : (data[0] & 0xf) == RNR); } int iframe_error(layer2_t *l2, struct sk_buff *skb) { u_int i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1); int rsp = *skb->data & 0x2; if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; if (rsp) return 'L'; if (skb->len < i) return 'N'; if ((skb->len - i) > l2->maxlen) return 'O'; return 0; } int super_error(layer2_t *l2, struct sk_buff *skb) { if (skb->len != l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1)) return 'N'; return 0; } int unnum_error(layer2_t *l2, struct sk_buff *skb, int wantrsp) { int rsp = (*skb->data & 0x2) >> 1; if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; if (rsp != wantrsp) return 'L'; if (skb->len != l2addrsize(l2) + 1) return 'N'; return 0; } int UI_error(layer2_t *l2, struct sk_buff *skb) { int rsp = *skb->data & 0x2; if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; if (rsp) return 'L'; if (skb->len > l2->maxlen + l2addrsize(l2) + 1) return 'O'; return 0; } int FRMR_error(layer2_t *l2, struct sk_buff *skb) { u_int headers = l2addrsize(l2) + 1; u_char *datap = skb->data + headers; int rsp = *skb->data & 0x2; if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; if (!rsp) return 'L'; if (test_bit(FLG_MOD128, &l2->flag)) { if (skb->len < headers + 5) return 'N'; else if (l2->debug) l2m_debug(&l2->l2m, "FRMR information %2x %2x %2x %2x %2x", datap[0], datap[1], datap[2], datap[3], datap[4]); } else { if (skb->len < headers + 3) return 'N'; else if (l2->debug) l2m_debug(&l2->l2m, "FRMR information %2x %2x %2x", datap[0], datap[1], datap[2]); } return 0; } static unsigned int legalnr(layer2_t *l2, unsigned int nr) { if(test_bit(FLG_MOD128, &l2->flag)) return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); else return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); } static void setva(layer2_t *l2, unsigned int nr) { struct sk_buff *skb; while (l2->va != nr) { l2->va++; if(test_bit(FLG_MOD128, &l2->flag)) l2->va %= 128; else l2->va %= 8; if (l2->windowar[l2->sow]) { skb_trim(l2->windowar[l2->sow], 0); skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]); l2->windowar[l2->sow] = NULL; } l2->sow = (l2->sow + 1) % l2->window; } while((skb =skb_dequeue(&l2->tmp_queue))) { if (l2up(l2, DL_DATA | CONFIRM, mISDN_HEAD_DINFO(skb), skb)) dev_kfree_skb(skb); } } static void send_uframe(layer2_t *l2, struct sk_buff *skb, u_char cmd, u_char cr) { u_char tmp[MAX_HEADER_LEN]; int i; i = sethdraddr(l2, tmp, cr); tmp[i++] = cmd; if (skb) skb_trim(skb, 0); else if (!(skb = alloc_skb(i, GFP_ATOMIC))) { printk(KERN_WARNING "%s: can't alloc skbuff\n", __FUNCTION__); return; } memcpy(skb_put(skb, i), tmp, i); enqueue_super(l2, skb); } inline u_char get_PollFlag(layer2_t *l2, struct sk_buff * skb) { return (skb->data[l2addrsize(l2)] & 0x10); } inline u_char get_PollFlagFree(layer2_t *l2, struct sk_buff *skb) { u_char PF; PF = get_PollFlag(l2, skb); dev_kfree_skb(skb); return (PF); } inline void start_t200(layer2_t *l2, int i) { mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); test_and_set_bit(FLG_T200_RUN, &l2->flag); } inline void restart_t200(layer2_t *l2, int i) { mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); test_and_set_bit(FLG_T200_RUN, &l2->flag); } inline void stop_t200(layer2_t *l2, int i) { if(test_and_clear_bit(FLG_T200_RUN, &l2->flag)) mISDN_FsmDelTimer(&l2->t200, i); } inline void st5_dl_release_l2l3(layer2_t *l2) { int pr; if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) { pr = DL_RELEASE | CONFIRM; } else { pr = DL_RELEASE | INDICATION; } l2up_create(l2, pr, 0, 0, NULL); } inline void lapb_dl_release_l2l3(layer2_t *l2, int f) { if (test_bit(FLG_LAPB, &l2->flag)) l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); l2up_create(l2, DL_RELEASE | f, 0, 0, NULL); } static void establishlink(struct FsmInst *fi) { layer2_t *l2 = fi->userdata; u_char cmd; clear_exception(l2); l2->rc = 0; cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10; send_uframe(l2, NULL, cmd, CMD); mISDN_FsmDelTimer(&l2->t203, 1); restart_t200(l2, 1); test_and_clear_bit(FLG_PEND_REL, &l2->flag); freewin(l2); mISDN_FsmChangeState(fi, ST_L2_5); } static void l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; layer2_t *l2 = fi->userdata; if (get_PollFlagFree(l2, skb)) l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'C'); else l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'D'); } static void l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; layer2_t *l2 = fi->userdata; if (get_PollFlagFree(l2, skb)) l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'B'); else { l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'E'); establishlink(fi); test_and_clear_bit(FLG_L3_INIT, &l2->flag); } } static void l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; layer2_t *l2 = fi->userdata; if (get_PollFlagFree(l2, skb)) l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'B'); else { l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'E'); } establishlink(fi); test_and_clear_bit(FLG_L3_INIT, &l2->flag); } 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); } static void l2_mdl_assign(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; mISDN_head_t *hh; mISDN_FsmChangeState(fi, ST_L2_3); skb_trim(skb, 0); hh = mISDN_HEAD_P(skb); hh->prim = MDL_ASSIGN | INDICATION; hh->dinfo = 0; if (l2_tei(l2->tm, skb)) dev_kfree_skb(skb); } static void l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; skb_queue_tail(&l2->ui_queue, skb); mISDN_FsmChangeState(fi, ST_L2_2); if ((skb = create_link_skb(MDL_ASSIGN | INDICATION, 0, 0, NULL, 0))) { if (l2_tei(l2->tm, skb)) dev_kfree_skb(skb); } } static void l2_queue_ui(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; skb_queue_tail(&l2->ui_queue, skb); } static void tx_ui(layer2_t *l2) { struct sk_buff *skb; u_char header[MAX_HEADER_LEN]; int i; i = sethdraddr(l2, header, CMD); if (test_bit(FLG_LAPD_NET, &l2->flag)) header[1] = 0xff; /* tei 127 */ header[i++] = UI; while ((skb = skb_dequeue(&l2->ui_queue))) { memcpy(skb_push(skb, i), header, i); enqueue_ui(l2, skb); } } static void l2_send_ui(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; skb_queue_tail(&l2->ui_queue, skb); tx_ui(l2); } static void l2_got_ui(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; skb_pull(skb, l2headersize(l2, 1)); /* * in states 1-3 for broadcast */ if (l2up(l2, DL_UNITDATA | INDICATION, mISDN_HEAD_DINFO(skb), skb)) dev_kfree_skb(skb); } static void l2_establish(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; layer2_t *l2 = fi->userdata; if (!test_bit(FLG_LAPD_NET, &l2->flag)) { establishlink(fi); test_and_set_bit(FLG_L3_INIT, &l2->flag); } dev_kfree_skb(skb); } static void l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; layer2_t *l2 = fi->userdata; discard_queue(&l2->i_queue); 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) { struct sk_buff *skb = arg; layer2_t *l2 = fi->userdata; discard_queue(&l2->i_queue); establishlink(fi); test_and_set_bit(FLG_L3_INIT, &l2->flag); dev_kfree_skb(skb); } static void l2_release(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; skb_trim(skb, 0); if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb)) dev_kfree_skb(skb); } static void l2_pend_rel(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; layer2_t *l2 = fi->userdata; test_and_set_bit(FLG_PEND_REL, &l2->flag); dev_kfree_skb(skb); } static void l2_disconnect(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; discard_queue(&l2->i_queue); freewin(l2); mISDN_FsmChangeState(fi, ST_L2_6); l2->rc = 0; send_uframe(l2, NULL, DISC | 0x10, CMD); mISDN_FsmDelTimer(&l2->t203, 1); restart_t200(l2, 2); if (skb) dev_kfree_skb(skb); } static void l2_start_multi(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; l2->vs = 0; l2->va = 0; l2->vr = 0; l2->sow = 0; clear_exception(l2); send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); mISDN_FsmChangeState(fi, ST_L2_7); mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); 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 l2_send_UA(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); } static void l2_send_DM(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP); } static void l2_restart_multi(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; int est = 0; send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'F'); if (l2->vs != l2->va) { discard_queue(&l2->i_queue); est = 1; } clear_exception(l2); l2->vs = 0; l2->va = 0; l2->vr = 0; l2->sow = 0; mISDN_FsmChangeState(fi, ST_L2_7); stop_t200(l2, 3); mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); 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); } static void l2_stop_multi(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; mISDN_FsmChangeState(fi, ST_L2_4); mISDN_FsmDelTimer(&l2->t203, 3); stop_t200(l2, 4); send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); 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 l2_connected(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; int pr=-1; if (!get_PollFlag(l2, skb)) { l2_mdl_error_ua(fi, event, arg); return; } dev_kfree_skb(skb); if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) l2_disconnect(fi, event, NULL); if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) { pr = DL_ESTABLISH | CONFIRM; } else if (l2->vs != l2->va) { discard_queue(&l2->i_queue); pr = DL_ESTABLISH | INDICATION; } stop_t200(l2, 5); l2->vr = 0; l2->vs = 0; l2->va = 0; l2->sow = 0; mISDN_FsmChangeState(fi, ST_L2_7); mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4); if (pr != -1) l2up_create(l2, pr, 0, 0, NULL); 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 l2_released(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; if (!get_PollFlag(l2, skb)) { l2_mdl_error_ua(fi, event, arg); return; } dev_kfree_skb(skb); 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 l2_reestablish(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; if (!get_PollFlagFree(l2, skb)) { establishlink(fi); test_and_set_bit(FLG_L3_INIT, &l2->flag); } } static void l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; if (get_PollFlagFree(l2, skb)) { stop_t200(l2, 7); if (!test_bit(FLG_L3_INIT, &l2->flag)) discard_queue(&l2->i_queue); if (test_bit(FLG_LAPB, &l2->flag)) l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); st5_dl_release_l2l3(l2); mISDN_FsmChangeState(fi, ST_L2_4); } } static void l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; if (get_PollFlagFree(l2, skb)) { 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); } } void enquiry_cr(layer2_t *l2, u_char typ, u_char cr, u_char pf) { struct sk_buff *skb; u_char tmp[MAX_HEADER_LEN]; int i; i = sethdraddr(l2, tmp, cr); if (test_bit(FLG_MOD128, &l2->flag)) { tmp[i++] = typ; tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); } else tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); if (!(skb = alloc_skb(i, GFP_ATOMIC))) { printk(KERN_WARNING "isdnl2 can't alloc sbbuff for enquiry_cr\n"); return; } memcpy(skb_put(skb, i), tmp, i); enqueue_super(l2, skb); } inline void enquiry_response(layer2_t *l2) { if (test_bit(FLG_OWN_BUSY, &l2->flag)) enquiry_cr(l2, RNR, RSP, 1); else enquiry_cr(l2, RR, RSP, 1); test_and_clear_bit(FLG_ACK_PEND, &l2->flag); } inline void transmit_enquiry(layer2_t *l2) { if (test_bit(FLG_OWN_BUSY, &l2->flag)) enquiry_cr(l2, RNR, CMD, 1); else enquiry_cr(l2, RR, CMD, 1); test_and_clear_bit(FLG_ACK_PEND, &l2->flag); start_t200(l2, 9); } static void nrerrorrecovery(struct FsmInst *fi) { layer2_t *l2 = fi->userdata; l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'J'); establishlink(fi); test_and_clear_bit(FLG_L3_INIT, &l2->flag); } static void invoke_retransmission(layer2_t *l2, unsigned int nr) { u_int p1; if (l2->vs != nr) { while (l2->vs != nr) { (l2->vs)--; if(test_bit(FLG_MOD128, &l2->flag)) { l2->vs %= 128; p1 = (l2->vs - l2->va) % 128; } else { l2->vs %= 8; p1 = (l2->vs - l2->va) % 8; } p1 = (p1 + l2->sow) % l2->window; if (l2->windowar[p1]) skb_queue_head(&l2->i_queue, l2->windowar[p1]); else printk(KERN_WARNING "%s: windowar[%d] is NULL\n", __FUNCTION__, p1); l2->windowar[p1] = NULL; } mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); } } static void l2_st7_got_super(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; int PollFlag, rsp, typ = RR; unsigned int nr; rsp = *skb->data & 0x2; if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; skb_pull(skb, l2addrsize(l2)); if (IsRNR(skb->data, l2)) { set_peer_busy(l2); typ = RNR; } else clear_peer_busy(l2); if (IsREJ(skb->data, l2)) typ = REJ; if (test_bit(FLG_MOD128, &l2->flag)) { PollFlag = (skb->data[1] & 0x1) == 0x1; nr = skb->data[1] >> 1; } else { PollFlag = (skb->data[0] & 0x10); nr = (skb->data[0] >> 5) & 0x7; } dev_kfree_skb(skb); if (PollFlag) { if (rsp) l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'A'); else enquiry_response(l2); } if (legalnr(l2, nr)) { if (typ == REJ) { setva(l2, nr); invoke_retransmission(l2, nr); stop_t200(l2, 10); if (mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 6)) l2m_debug(&l2->l2m, "Restart T203 ST7 REJ"); } else if ((nr == l2->vs) && (typ == RR)) { setva(l2, nr); stop_t200(l2, 11); mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 7); } else if ((l2->va != nr) || (typ == RNR)) { setva(l2, nr); if (typ != RR) mISDN_FsmDelTimer(&l2->t203, 9); restart_t200(l2, 12); } if (skb_queue_len(&l2->i_queue) && (typ == RR)) { mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); } } else nrerrorrecovery(fi); } static void l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; if (!test_bit(FLG_L3_INIT, &l2->flag)) skb_queue_tail(&l2->i_queue, skb); else dev_kfree_skb(skb); } static void l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; skb_queue_tail(&l2->i_queue, skb); mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); } static void l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; skb_queue_tail(&l2->i_queue, skb); } static void l2_got_iframe(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; int PollFlag, i; u_int ns, nr; i = l2addrsize(l2); if (test_bit(FLG_MOD128, &l2->flag)) { PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); ns = skb->data[i] >> 1; nr = (skb->data[i + 1] >> 1) & 0x7f; } else { PollFlag = (skb->data[i] & 0x10); ns = (skb->data[i] >> 1) & 0x7; nr = (skb->data[i] >> 5) & 0x7; } if (test_bit(FLG_OWN_BUSY, &l2->flag)) { dev_kfree_skb(skb); if (PollFlag) enquiry_response(l2); } else { if (l2->vr == ns) { l2->vr++; if(test_bit(FLG_MOD128, &l2->flag)) l2->vr %= 128; else l2->vr %= 8; test_and_clear_bit(FLG_REJEXC, &l2->flag); if (PollFlag) enquiry_response(l2); else test_and_set_bit(FLG_ACK_PEND, &l2->flag); skb_pull(skb, l2headersize(l2, 0)); if (l2up(l2, DL_DATA | INDICATION, mISDN_HEAD_DINFO(skb), skb)) dev_kfree_skb(skb); } else { /* n(s)!=v(r) */ dev_kfree_skb(skb); if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { if (PollFlag) enquiry_response(l2); } else { enquiry_cr(l2, REJ, RSP, PollFlag); test_and_clear_bit(FLG_ACK_PEND, &l2->flag); } } } if (legalnr(l2, nr)) { if (!test_bit(FLG_PEER_BUSY, &l2->flag) && (fi->state == ST_L2_7)) { if (nr == l2->vs) { stop_t200(l2, 13); mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 7); } else if (nr != l2->va) restart_t200(l2, 14); } setva(l2, nr); } else { nrerrorrecovery(fi); return; } if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7)) mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag)) enquiry_cr(l2, RR, RSP, 0); } static void l2_got_tei(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; mISDN_head_t *hh = mISDN_HEAD_P(skb); l2->tei = hh->dinfo; dev_kfree_skb(skb); if (fi->state == ST_L2_3) { establishlink(fi); test_and_set_bit(FLG_L3_INIT, &l2->flag); } else mISDN_FsmChangeState(fi, ST_L2_4); if (skb_queue_len(&l2->ui_queue)) tx_ui(l2); } static void l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; if (test_bit(FLG_LAPD, &l2->flag) && test_bit(FLG_DCHAN_BUSY, &l2->flag)) { mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); } else if (l2->rc == l2->N200) { mISDN_FsmChangeState(fi, ST_L2_4); test_and_clear_bit(FLG_T200_RUN, &l2->flag); discard_queue(&l2->i_queue); l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'G'); if (test_bit(FLG_LAPB, &l2->flag)) l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); st5_dl_release_l2l3(l2); } else { l2->rc++; mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10, CMD); } } static void l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; if (test_bit(FLG_LAPD, &l2->flag) && test_bit(FLG_DCHAN_BUSY, &l2->flag)) { mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); } else if (l2->rc == l2->N200) { mISDN_FsmChangeState(fi, ST_L2_4); test_and_clear_bit(FLG_T200_RUN, &l2->flag); l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'H'); lapb_dl_release_l2l3(l2, CONFIRM); } else { l2->rc++; mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); send_uframe(l2, NULL, DISC | 0x10, CMD); } } static void l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; if (test_bit(FLG_LAPD, &l2->flag) && test_bit(FLG_DCHAN_BUSY, &l2->flag)) { mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); return; } test_and_clear_bit(FLG_T200_RUN, &l2->flag); l2->rc = 0; mISDN_FsmChangeState(fi, ST_L2_8); transmit_enquiry(l2); l2->rc++; } static void l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; if (test_bit(FLG_LAPD, &l2->flag) && test_bit(FLG_DCHAN_BUSY, &l2->flag)) { mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); return; } test_and_clear_bit(FLG_T200_RUN, &l2->flag); if (l2->rc == l2->N200) { l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'I'); establishlink(fi); test_and_clear_bit(FLG_L3_INIT, &l2->flag); } else { transmit_enquiry(l2); l2->rc++; } } static void l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; if (test_bit(FLG_LAPD, &l2->flag) && test_bit(FLG_DCHAN_BUSY, &l2->flag)) { mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9); return; } mISDN_FsmChangeState(fi, ST_L2_8); transmit_enquiry(l2); l2->rc = 0; } static void l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb, *nskb, *oskb; u_char header[MAX_HEADER_LEN]; u_int i, p1; if (!cansend(l2)) return; skb = skb_dequeue(&l2->i_queue); if (!skb) return; if(test_bit(FLG_MOD128, &l2->flag)) p1 = (l2->vs - l2->va) % 128; else p1 = (l2->vs - l2->va) % 8; p1 = (p1 + l2->sow) % l2->window; if (l2->windowar[p1]) { printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", p1); dev_kfree_skb(l2->windowar[p1]); } l2->windowar[p1] = skb; i = sethdraddr(l2, header, CMD); if (test_bit(FLG_MOD128, &l2->flag)) { header[i++] = l2->vs << 1; header[i++] = l2->vr << 1; l2->vs = (l2->vs + 1) % 128; } else { header[i++] = (l2->vr << 5) | (l2->vs << 1); l2->vs = (l2->vs + 1) % 8; } nskb = skb_clone(skb, GFP_ATOMIC); p1 = skb_headroom(nskb); if (p1 >= i) memcpy(skb_push(nskb, i), header, i); else { printk(KERN_WARNING "isdnl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); oskb = nskb; nskb = alloc_skb(oskb->len + i, GFP_ATOMIC); if (!nskb) { dev_kfree_skb(oskb); printk(KERN_WARNING "%s: no skb mem\n", __FUNCTION__); return; } memcpy(skb_put(nskb, i), header, i); memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len); dev_kfree_skb(oskb); } l2down(l2, PH_DATA_REQ, mISDN_HEAD_DINFO(skb), nskb); test_and_clear_bit(FLG_ACK_PEND, &l2->flag); if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { mISDN_FsmDelTimer(&l2->t203, 13); mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11); } } static void l2_st8_got_super(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; int PollFlag, rsp, rnr = 0; unsigned int nr; rsp = *skb->data & 0x2; if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; skb_pull(skb, l2addrsize(l2)); if (IsRNR(skb->data, l2)) { set_peer_busy(l2); rnr = 1; } else clear_peer_busy(l2); if (test_bit(FLG_MOD128, &l2->flag)) { PollFlag = (skb->data[1] & 0x1) == 0x1; nr = skb->data[1] >> 1; } else { PollFlag = (skb->data[0] & 0x10); nr = (skb->data[0] >> 5) & 0x7; } dev_kfree_skb(skb); if (rsp && PollFlag) { if (legalnr(l2, nr)) { if (rnr) { restart_t200(l2, 15); } else { stop_t200(l2, 16); mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 5); setva(l2, nr); } invoke_retransmission(l2, nr); mISDN_FsmChangeState(fi, ST_L2_7); if (skb_queue_len(&l2->i_queue) && cansend(l2)) mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); } else nrerrorrecovery(fi); } else { if (!rsp && PollFlag) enquiry_response(l2); if (legalnr(l2, nr)) { setva(l2, nr); } else nrerrorrecovery(fi); } } static void l2_got_FRMR(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; skb_pull(skb, l2addrsize(l2) + 1); if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ (IsUA(skb->data) && (fi->state == ST_L2_7))) { l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'K'); establishlink(fi); test_and_clear_bit(FLG_L3_INIT, &l2->flag); } dev_kfree_skb(skb); } static void l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; discard_queue(&l2->ui_queue); l2->tei = -1; mISDN_FsmChangeState(fi, ST_L2_1); dev_kfree_skb(skb); } static void l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; discard_queue(&l2->ui_queue); l2->tei = -1; skb_trim(skb, 0); if (l2up(l2, DL_RELEASE | INDICATION, 0, skb)) dev_kfree_skb(skb); mISDN_FsmChangeState(fi, ST_L2_1); } static void l2_st5_tei_remove(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); freewin(l2); l2->tei = -1; stop_t200(l2, 17); st5_dl_release_l2l3(l2); mISDN_FsmChangeState(fi, ST_L2_1); dev_kfree_skb(skb); } static void l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; discard_queue(&l2->ui_queue); l2->tei = -1; stop_t200(l2, 18); if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb)) dev_kfree_skb(skb); mISDN_FsmChangeState(fi, ST_L2_1); } static void l2_tei_remove(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); freewin(l2); l2->tei = -1; stop_t200(l2, 17); 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); } static void 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)) if (!l2up(l2, DL_RELEASE | INDICATION, 0, skb)) return; dev_kfree_skb(skb); } static void l2_st5_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); freewin(l2); stop_t200(l2, 19); st5_dl_release_l2l3(l2); mISDN_FsmChangeState(fi, ST_L2_4); dev_kfree_skb(skb); } static void l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; discard_queue(&l2->ui_queue); stop_t200(l2, 20); if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb)) dev_kfree_skb(skb); mISDN_FsmChangeState(fi, ST_L2_4); } static void l2_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); freewin(l2); stop_t200(l2, 19); 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); } static void l2_set_own_busy(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; if(!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) { enquiry_cr(l2, RNR, RSP, 0); test_and_clear_bit(FLG_ACK_PEND, &l2->flag); } if (skb) dev_kfree_skb(skb); } static void l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; struct sk_buff *skb = arg; if(!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) { enquiry_cr(l2, RR, RSP, 0); test_and_clear_bit(FLG_ACK_PEND, &l2->flag); } if (skb) dev_kfree_skb(skb); } static void l2_frame_error(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; l2mgr(l2, MDL_ERROR | INDICATION, arg); } static void l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) { layer2_t *l2 = fi->userdata; l2mgr(l2, MDL_ERROR | INDICATION, arg); establishlink(fi); test_and_clear_bit(FLG_L3_INIT, &l2->flag); } static struct FsmNode L2FnList[] = { {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign}, {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui}, {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui}, {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui}, {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui}, {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui}, {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui}, {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui}, {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, {ST_L2_4, EV_L2_SABME, l2_start_multi}, {ST_L2_5, EV_L2_SABME, l2_send_UA}, {ST_L2_6, EV_L2_SABME, l2_send_DM}, {ST_L2_7, EV_L2_SABME, l2_restart_multi}, {ST_L2_8, EV_L2_SABME, l2_restart_multi}, {ST_L2_4, EV_L2_DISC, l2_send_DM}, {ST_L2_5, EV_L2_DISC, l2_send_DM}, {ST_L2_6, EV_L2_DISC, l2_send_UA}, {ST_L2_7, EV_L2_DISC, l2_stop_multi}, {ST_L2_8, EV_L2_DISC, l2_stop_multi}, {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, {ST_L2_5, EV_L2_UA, l2_connected}, {ST_L2_6, EV_L2_UA, l2_released}, {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, {ST_L2_4, EV_L2_DM, l2_reestablish}, {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, {ST_L2_1, EV_L2_UI, l2_got_ui}, {ST_L2_2, EV_L2_UI, l2_got_ui}, {ST_L2_3, EV_L2_UI, l2_got_ui}, {ST_L2_4, EV_L2_UI, l2_got_ui}, {ST_L2_5, EV_L2_UI, l2_got_ui}, {ST_L2_6, EV_L2_UI, l2_got_ui}, {ST_L2_7, EV_L2_UI, l2_got_ui}, {ST_L2_8, EV_L2_UI, l2_got_ui}, {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, {ST_L2_7, EV_L2_I, l2_got_iframe}, {ST_L2_8, EV_L2_I, l2_got_iframe}, {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, }; #define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) static int ph_data_indication(layer2_t *l2, mISDN_head_t *hh, struct sk_buff *skb) { u_char *datap = skb->data; int ret = -EINVAL; int psapi, ptei; u_int l; int c = 0; l = l2addrsize(l2); if (skb->len <= l) { mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); return(ret); } if (test_bit(FLG_LAPD, &l2->flag)) { psapi = *datap++; ptei = *datap++; if ((psapi & 1) || !(ptei & 1)) { printk(KERN_WARNING "l2 D-channel addr:%x frame wrong EA0/EA1\n", l2->inst.id); return(ret); } psapi >>= 2; ptei >>= 1; 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)) { /* 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; if (!(*datap & 1)) { /* I-Frame */ if(!(c = iframe_error(l2, skb))) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb); } else if (IsSFrame(datap, l2)) { /* S-Frame */ if(!(c = super_error(l2, skb))) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb); } else if (IsUI(datap)) { if(!(c = UI_error(l2, skb))) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); } else if (IsSABME(datap, l2)) { if(!(c = unnum_error(l2, skb, CMD))) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); } else if (IsUA(datap)) { if(!(c = unnum_error(l2, skb, RSP))) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb); } else if (IsDISC(datap)) { if(!(c = unnum_error(l2, skb, CMD))) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb); } else if (IsDM(datap)) { if(!(c = unnum_error(l2, skb, RSP))) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb); } else if (IsFRMR(datap)) { if(!(c = FRMR_error(l2, skb))) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb); } else { c = 'L'; } if (c) { printk(KERN_WARNING "l2 D-channel frame error %c\n",c); mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c); } return(ret); } static int l2from_down(layer2_t *l2, struct sk_buff *askb, mISDN_head_t *hh) { int ret = -EINVAL; struct sk_buff *cskb = askb; // printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); if (hh->prim == (PH_DATA | CONFIRM)) return(ph_data_confirm(&l2->inst, hh, askb)); if (hh->prim == (MDL_FINDTEI | REQUEST)) { ret = -ESRCH; if (test_bit(FLG_LAPD, &l2->flag)) { if (l2->tei == hh->dinfo) { void *p = ((u_char *)askb->data); teimgr_t **tp = p; if (tp) { *tp = l2->tm; dev_kfree_skb(askb); return(0); } } } #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))) return(next->func(next, askb)); else sh = *hh; } } #endif switch (hh->prim) { case (PH_DATA_IND): ret = ph_data_indication(l2, hh, cskb); break; case (PH_CONTROL | INDICATION): if (hh->dinfo == HW_D_BLOCKED) test_and_set_bit(FLG_DCHAN_BUSY, &l2->flag); else if (hh->dinfo == HW_D_NOBLOCKED) test_and_clear_bit(FLG_DCHAN_BUSY, &l2->flag); else break; break; case (PH_ACTIVATE | CONFIRM): case (PH_ACTIVATE | INDICATION): test_and_set_bit(FLG_L1_ACTIV, &l2->flag); if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_ESTABLISH_REQ, cskb); break; case (PH_DEACTIVATE | INDICATION): case (PH_DEACTIVATE | CONFIRM): test_and_clear_bit(FLG_L1_ACTIV, &l2->flag); ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, cskb); if (ret) dev_kfree_skb(cskb); ret = 0; break; default: if (l2->debug) l2m_debug(&l2->l2m, "l2 unknown pr %x", hh->prim); ret = -EINVAL; break; } if (ret) { 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(layer2_t *l2, struct sk_buff *skb, mISDN_head_t *hh) { int ret = -EINVAL; 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); break; case (DL_UNITDATA | REQUEST): ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb); break; case (DL_ESTABLISH | REQUEST): if (test_bit(FLG_L1_ACTIV, &l2->flag)) { if (test_bit(FLG_LAPD, &l2->flag) || test_bit(FLG_ORIG, &l2->flag)) { ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_ESTABLISH_REQ, skb); } } else { if (test_bit(FLG_LAPD, &l2->flag) || test_bit(FLG_ORIG, &l2->flag)) { test_and_set_bit(FLG_ESTAB_PEND, &l2->flag); } ret = l2down(l2, PH_ACTIVATE | REQUEST, 0, skb); } break; case (DL_RELEASE | REQUEST): if (test_bit(FLG_LAPB, &l2->flag)) l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL); ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, skb); break; case (MDL_ASSIGN | REQUEST): ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, skb); break; case (MDL_REMOVE | REQUEST): ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, skb); break; case (MDL_ERROR | RESPONSE): ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, skb); case (MDL_STATUS | REQUEST): l2up_create(l2, MDL_STATUS | CONFIRM, hh->dinfo, 1, (void *)((u_long)l2->tei)); break; default: if (l2->debug) l2m_debug(&l2->l2m, "l2 unknown pr %04x", hh->prim); } 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) { mISDN_head_t *hh; int ret = -EINVAL; if (!l2 || !skb) return(ret); hh = mISDN_HEAD_P(skb); if (l2->debug) printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); switch(hh->prim) { case (MDL_UNITDATA | REQUEST): ret = l2down(l2, PH_DATA_REQ, l2_newid(l2), skb); break; case (MDL_ASSIGN | REQUEST): ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, skb); break; case (MDL_REMOVE | REQUEST): ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, skb); break; case (MDL_ERROR | RESPONSE): ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, skb); break; case (MDL_ERROR | REQUEST): /* ETS 300-125 5.3.2.1 Test: TC13010*/ printk(KERN_NOTICE "MDL_ERROR|REQ (tei_l2)\n"); ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, skb); break; case (MDL_FINDTEI | REQUEST): ret = l2down_skb(l2, skb); break; } return(ret); } static void l2m_debug(struct FsmInst *fi, char *fmt, ...) { layer2_t *l2 = fi->userdata; logdata_t log; va_start(log.args, fmt); log.fmt = fmt; log.head = l2->inst.name; mISDN_ctrl(&l2->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } static void release_l2(layer2_t *l2) { mISDNinstance_t *inst = &l2->inst; u_long flags; mISDN_FsmDelTimer(&l2->t200, 21); mISDN_FsmDelTimer(&l2->t203, 16); discard_queue(&l2->i_queue); discard_queue(&l2->ui_queue); discard_queue(&l2->down_queue); 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); } if (inst->down.peer) { inst->down.peer->obj->ctrl(inst->down.peer, MGR_DISCONNECT | REQUEST, &inst->down); } if (l2->cloneif) { printk(KERN_DEBUG "%s: cloneif(%p) owner(%p) peer(%p)\n", __FUNCTION__, l2->cloneif, l2->cloneif->owner, l2->cloneif->peer); if (l2->cloneif->predecessor) l2->cloneif->predecessor->clone = l2->cloneif->clone; if (l2->cloneif->clone) l2->cloneif->clone->predecessor = l2->cloneif->predecessor; if (l2->cloneif->owner && (l2->cloneif->owner->up.clone == l2->cloneif)) l2->cloneif->owner->up.clone = l2->cloneif->clone; kfree(l2->cloneif); l2->cloneif = NULL; } #endif spin_lock_irqsave(&isdnl2.lock, flags); list_del(&l2->list); spin_unlock_irqrestore(&isdnl2.lock, flags); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); if (l2->entity != MISDN_ENTITY_NONE) mISDN_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 *nl2; int err; u_char *p; u_long flags; if (!st || !pid) return(-EINVAL); if (!(nl2 = kmalloc(sizeof(layer2_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc layer2 failed\n"); return(-ENOMEM); } memset(nl2, 0, sizeof(layer2_t)); nl2->debug = debug; nl2->next_id = 1; nl2->down_id = MISDN_ID_NONE; 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)) { int_error(); return(-ENOPROTOOPT); } switch(pid->protocol[2] & ~ISDN_PID_FEATURE_MASK) { case ISDN_PID_L2_LAPD_NET: 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); test_and_set_bit(FLG_MOD128, &nl2->flag); nl2->sapi = 0; nl2->tei = 88; nl2->maxlen = MAX_DFRAME_LEN; nl2->window = 1; nl2->T200 = 1000; nl2->N200 = 3; nl2->T203 = 10000; if (create_teimgr(nl2)) { kfree(nl2); return(-EINVAL); } break; case ISDN_PID_L2_LAPD: 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); nl2->sapi = 0; nl2->tei = -1; if (pid->protocol[2] & ISDN_PID_L2_DF_PTP) { test_and_set_bit(FLG_PTP, &nl2->flag); test_and_set_bit(FLG_FIXED_TEI, &nl2->flag); nl2->tei = 0; } nl2->maxlen = MAX_DFRAME_LEN; if (pid->protocol[3] & ISDN_PID_L3_DF_CRLEN2) { if (debug) printk("layer2: Windowsize 7\n"); nl2->window = 7; } else { if (debug) printk("layer2: Windowsize 1\n"); nl2->window = 1; } nl2->T200 = 1000; nl2->N200 = 3; nl2->T203 = 10000; if (create_teimgr(nl2)) { kfree(nl2); return(-EINVAL); } break; case ISDN_PID_L2_B_X75SLP: test_and_set_bit(FLG_LAPB, &nl2->flag); sprintf(nl2->inst.name, "lapb %x", st->id >> 8); nl2->window = 7; nl2->maxlen = MAX_DATA_SIZE; nl2->T200 = 1000; nl2->N200 = 4; nl2->T203 = 5000; nl2->addr.A = 3; nl2->addr.B = 1; if (nl2->inst.pid.global == 1) test_and_set_bit(FLG_ORIG, &nl2->flag); if (pid->param[2] && pid->pbuf) { p = pid->pbuf + pid->param[2]; if (*p>=4) { p++; nl2->addr.A = *p++; nl2->addr.B = *p++; if (*p++ == 128) test_and_set_bit(FLG_MOD128, &nl2->flag); nl2->window = *p++; if (nl2->window > 7) nl2->window = 7; } } break; default: printk(KERN_ERR "layer2 create failed prt %x\n", pid->protocol[2]); kfree(nl2); return(-ENOPROTOOPT); } skb_queue_head_init(&nl2->i_queue); skb_queue_head_init(&nl2->ui_queue); skb_queue_head_init(&nl2->down_queue); skb_queue_head_init(&nl2->tmp_queue); InitWin(nl2); nl2->l2m.fsm = &l2fsm; if (test_bit(FLG_LAPB, &nl2->flag) || test_bit(FLG_PTP, &nl2->flag) || test_bit(FLG_LAPD_NET, &nl2->flag)) nl2->l2m.state = ST_L2_4; else nl2->l2m.state = ST_L2_1; nl2->l2m.debug = debug; nl2->l2m.userdata = nl2; nl2->l2m.userint = 0; nl2->l2m.printdebug = l2m_debug; 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 = mISDN_ctrl(&nl2->inst, MGR_NEWENTITY | REQUEST, NULL); if (err) { printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%d)\n", __FUNCTION__, err); } err = mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &nl2->inst); if (err) { mISDN_FsmDelTimer(&nl2->t200, 0); mISDN_FsmDelTimer(&nl2->t203, 0); list_del(&nl2->list); kfree(nl2); nl2 = NULL; } else { mISDN_stPara_t stp; if (st->para.maxdatalen) nl2->maxlen = st->para.maxdatalen; stp.maxdatalen = 0; stp.up_headerlen = 0; stp.down_headerlen = l2headersize(nl2, 0); mISDN_ctrl(st, MGR_ADDSTPARA | REQUEST, &stp); } return(err); } #ifdef OBSOLETE static int clone_l2(layer2_t *l2, mISDNinstance_t **new_ip) { int err; layer2_t *nl2; mISDNstack_t *st; if (!l2) return(-EINVAL); if (!new_ip) return(-EINVAL); st = (mISDNstack_t *)*new_ip; if (!st) return(-EINVAL); err = new_l2(st, &l2->inst.pid, &nl2); if (err) { printk(KERN_ERR "clone l2 failed err(%d)\n", err); return(err); } nl2->cloneif = nif; nif->func = l2from_down; nif->fdata = nl2; nif->owner = l2->inst.down.peer; nif->peer = &nl2->inst; nif->stat = IF_DOWN; nif->predecessor = &nif->owner->up; while(nif->predecessor->clone) nif->predecessor = nif->predecessor->clone; nif->predecessor->clone = nif; nl2->inst.down.owner = &nl2->inst; nl2->inst.down.peer = &l2->inst; nl2->inst.down.func = l2_chain_down; nl2->inst.down.fdata = l2; nl2->inst.down.stat = IF_UP; *new_ip = &nl2->inst; return(err); } #endif static int l2_status(layer2_t *l2, status_info_l2_t *si) { if (!si) return(-EINVAL); memset(si, 0, sizeof(status_info_l2_t)); si->len = sizeof(status_info_l2_t) - 2*sizeof(int); si->typ = STATUS_INFO_L2; si->protocol = l2->inst.pid.protocol[2]; si->state = l2->l2m.state; si->sapi = l2->sapi; si->tei = l2->tei; si->addr = l2->addr; si->maxlen = l2->maxlen; si->flag = l2->flag; si->vs = l2->vs; si->va = l2->va; si->vr = l2->vr; si->rc = l2->rc; si->window = l2->window; si->sow = l2->sow; si->T200 = l2->T200; si->N200 = l2->N200; si->T203 = l2->T203; si->len_i_queue = skb_queue_len(&l2->i_queue); si->len_ui_queue = skb_queue_len(&l2->ui_queue); si->len_d_queue = skb_queue_len(&l2->down_queue); si->debug = l2->debug; if (l2->tm) { si->tei_state = l2->tm->tei_m.state; si->tei_ri = l2->tm->ri; si->T202 = l2->tm->T202; si->N202 = l2->tm->N202; si->tei_debug = l2->tm->debug; } return(0); } static char MName[] = "ISDNL2"; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #endif #endif static int 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)); if (err) { if (debug & 0x1) printk(KERN_WARNING "l2_manager prim(%x) l2 no instance\n", prim); return(err); } switch(prim) { case MGR_NEWENTITY | CONFIRM: 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: return(mISDN_ConnectIF(inst, arg)); case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: return(mISDN_SetIF(inst, arg, prim, l2from_up, l2from_down, l2l)); case MGR_ADDIF | REQUEST: if (arg) { mISDNif_t *hif = arg; if (hif->stat & IF_UP) { hif->fdata = l2l; hif->func = l2_chain_down; } } break; 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); break; case MGR_STATUS | REQUEST: return(l2_status(l2l, arg)); default: if (debug & 0x1) printk(KERN_WARNING "l2_manager prim %x not handled\n", prim); return(-EINVAL); } return(0); } int Isdnl2_Init(void) { int err; printk(KERN_INFO "ISDN L2 driver version %s\n", mISDN_getrev(l2_revision)); #ifdef MODULE isdnl2.owner = THIS_MODULE; #endif isdnl2.name = MName; isdnl2.DPROTO.protocol[2] = ISDN_PID_L2_LAPD | ISDN_PID_L2_LAPD_NET | 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; l2fsm.strEvent = strL2Event; l2fsm.strState = strL2State; mISDN_FsmNew(&l2fsm, L2FnList, L2_FN_COUNT); TEIInit(); if ((err = mISDN_register(&isdnl2))) { printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); mISDN_FsmFree(&l2fsm); } else mISDN_module_register(THIS_MODULE); return(err); } void Isdnl2_cleanup(void) { int err; layer2_t *l2, *nl2; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&isdnl2))) { printk(KERN_ERR "Can't unregister ISDN layer 2 error(%d)\n", err); } if(!list_empty(&isdnl2.ilist)) { printk(KERN_WARNING "mISDNl2 l2 list not empty\n"); list_for_each_entry_safe(l2, nl2, &isdnl2.ilist, list) release_l2(l2); } TEIFree(); mISDN_FsmFree(&l2fsm); } module_init(Isdnl2_Init); module_exit(Isdnl2_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/layer3.c0000644000000000000500000003077311135651702017744 0ustar rootsrc/* $Id: layer3.c,v 1.20 2006/05/29 16:46:10 crich Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * For changes and modifications please read * ../../../Documentation/isdn/mISDN.cert * * Thanks to Jan den Ouden * Fritz Elfert * */ #include "layer3.h" #include "helper.h" #include "dss1.h" const char *l3_revision = "$Revision: 1.20 $"; static struct Fsm l3fsm = {NULL, 0, 0, NULL, NULL}; enum { ST_L3_LC_REL, ST_L3_LC_ESTAB_WAIT, ST_L3_LC_REL_DELAY, ST_L3_LC_REL_WAIT, ST_L3_LC_ESTAB, }; #define L3_STATE_COUNT (ST_L3_LC_ESTAB+1) static char *strL3State[] = { "ST_L3_LC_REL", "ST_L3_LC_ESTAB_WAIT", "ST_L3_LC_REL_DELAY", "ST_L3_LC_REL_WAIT", "ST_L3_LC_ESTAB", }; enum { EV_ESTABLISH_REQ, EV_ESTABLISH_IND, EV_ESTABLISH_CNF, EV_RELEASE_REQ, EV_RELEASE_CNF, EV_RELEASE_IND, EV_TIMEOUT, }; #define L3_EVENT_COUNT (EV_TIMEOUT+1) static char *strL3Event[] = { "EV_ESTABLISH_REQ", "EV_ESTABLISH_IND", "EV_ESTABLISH_CNF", "EV_RELEASE_REQ", "EV_RELEASE_CNF", "EV_RELEASE_IND", "EV_TIMEOUT", }; static void l3m_debug(struct FsmInst *fi, char *fmt, ...) { layer3_t *l3 = fi->userdata; logdata_t log; va_start(log.args, fmt); log.fmt = fmt; log.head = l3->inst.name; mISDN_ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } void l3_debug(layer3_t *l3, char *fmt, ...) { logdata_t log; va_start(log.args, fmt); log.fmt = fmt; log.head = l3->inst.name; mISDN_ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } static int l3_newid(layer3_t *l3) { int id; id = l3->next_id++; if (id == 0x7fff) l3->next_id = 1; id |= (l3->entity << 16); return(id); } u_char * findie(u_char * p, int size, u_char ie, int wanted_set) { int l, codeset, maincodeset; u_char *pend = p + size; /* skip protocol discriminator, callref and message type */ p++; l = (*p++) & 0xf; p += l; p++; codeset = 0; maincodeset = 0; /* while there are bytes left... */ while (p < pend) { if ((*p & 0xf0) == 0x90) { codeset = *p & 0x07; if (!(*p & 0x08)) maincodeset = codeset; } if (codeset == wanted_set) { if (*p == ie) { /* improved length check (Werner Cornelius) */ if (!(*p & 0x80)) { if ((pend - p) < 2) return(NULL); if (*(p+1) > (pend - (p+2))) return(NULL); p++; /* points to len */ } return (p); } else if ((*p > ie) && !(*p & 0x80)) return (NULL); } if (!(*p & 0x80)) { p++; l = *p; p += l; codeset = maincodeset; } p++; } return (NULL); } int getcallref(u_char * p) { int l, cr = 0; p++; /* prot discr */ if (*p & 0xfe) /* wrong callref BRI only 1 octet*/ return(-2); l = 0xf & *p++; /* callref length */ if (!l) /* dummy CallRef */ return(-1); cr = *p++; return (cr); } int newcallref(layer3_t *l3) { int max = 127; if (test_bit(FLG_CRLEN2, &l3->Flag)) max = 32767; if (l3->OrigCallRef >= max) l3->OrigCallRef = 1; else l3->OrigCallRef++; return (l3->OrigCallRef); } void newl3state(l3_process_t *pc, int state) { if (pc->l3->debug & L3_DEB_STATE) l3m_debug(&pc->l3->l3m, "newstate cr %d %d --> %d", pc->callref & 0x7F, pc->state, state); pc->state = state; } static void L3ExpireTimer(L3Timer_t *t) { t->pc->l3->p_mgr(t->pc, t->event, NULL); } void L3InitTimer(l3_process_t *pc, L3Timer_t *t) { t->pc = pc; t->tl.function = (void *) L3ExpireTimer; t->tl.data = (long) t; init_timer(&t->tl); } void L3DelTimer(L3Timer_t *t) { del_timer(&t->tl); } int L3AddTimer(L3Timer_t *t, int millisec, int event) { if (timer_pending(&t->tl)) { printk(KERN_WARNING "L3AddTimer: timer already active!\n"); return -1; } init_timer(&t->tl); t->event = event; t->tl.expires = jiffies + (millisec * HZ) / 1000; add_timer(&t->tl); return 0; } void StopAllL3Timer(l3_process_t *pc) { L3DelTimer(&pc->timer); if (pc->t303skb) { dev_kfree_skb(pc->t303skb); pc->t303skb = NULL; } } /* static void no_l3_proto(struct PStack *st, int pr, void *arg) { struct sk_buff *skb = arg; mISDN_putstatus(st->l1.hardware, "L3", "no D protocol"); if (skb) { dev_kfree_skb(skb); } } static int no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic) { printk(KERN_WARNING "mISDN: no specific protocol handler for proto %lu\n",ic->arg & 0xFF); return(-1); } */ l3_process_t *getl3proc(layer3_t *l3, int cr) { l3_process_t *p; list_for_each_entry(p, &l3->plist, list) if (p->callref == cr) return (p); return (NULL); } l3_process_t *getl3proc4id(layer3_t *l3, u_int id) { l3_process_t *p; list_for_each_entry(p, &l3->plist, list) if (p->id == id) return (p); return (NULL); } l3_process_t *new_l3_process(layer3_t *l3, int cr, int n303, u_int id) { l3_process_t *p = NULL; if (id == MISDN_ID_ANY) { if (l3->entity == MISDN_ENTITY_NONE) { printk(KERN_WARNING "%s: no entity allocated for l3(%x)\n", __FUNCTION__, l3->id); return (NULL); } if (l3->pid_cnt == 0x7FFF) l3->pid_cnt = 0; while(l3->pid_cnt <= 0x7FFF) { l3->pid_cnt++; id = l3->pid_cnt | (l3->entity << 16); p = getl3proc4id(l3, id); if (!p) break; } if (p) { printk(KERN_WARNING "%s: no free process_id for l3(%x) entity(%x)\n", __FUNCTION__, l3->id, l3->entity); return (NULL); } } else { /* id from other entity */ p = getl3proc4id(l3, id); if (p) { printk(KERN_WARNING "%s: process_id(%x) allready in use in l3(%x)\n", __FUNCTION__, id, l3->id); return (NULL); } } if (!(p = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) { printk(KERN_ERR "mISDN can't get memory for cr %d\n", cr); return (NULL); } memset(p, 0, sizeof(l3_process_t)); p->cause=NO_CAUSE; p->l3 = l3; p->id = id; p->callref = cr; p->n303 = n303; L3InitTimer(p, &p->timer); L3InitTimer(p, &p->aux_timer); list_add_tail(&p->list, &l3->plist); return (p); }; void release_l3_process(l3_process_t *p) { layer3_t *l3; if (!p) return; l3 = p->l3; mISDN_l3up(p, CC_RELEASE_CR | INDICATION, NULL); list_del(&p->list); StopAllL3Timer(p); kfree(p); if (list_empty(&l3->plist) && !test_bit(FLG_PTP, &l3->Flag)) { if (l3->debug) l3_debug(l3, "release_l3_process: last process"); if (!skb_queue_len(&l3->squeue)) { if (l3->debug) l3_debug(l3, "release_l3_process: release link"); mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL); } else { if (l3->debug) l3_debug(l3, "release_l3_process: not release link"); } } }; static void l3ml3p(layer3_t *l3, int pr) { l3_process_t *p, *np; list_for_each_entry_safe(p, np, &l3->plist, list) l3->p_mgr(p, pr, NULL); } int mISDN_l3up(l3_process_t *l3p, u_int prim, struct sk_buff *skb) { layer3_t *l3; int err = -EINVAL; if (!l3p) return(-EINVAL); l3 = l3p->l3; if (!skb) err = mISDN_queue_data(&l3->inst, FLG_MSG_UP, prim, l3p->id, 0, NULL, 0); else err = mISDN_queueup_newhead(&l3->inst, 0, prim, l3p->id, skb); return(err); } static int l3down(layer3_t *l3, u_int prim, int dinfo, struct sk_buff *skb) { int err = -EINVAL; if (!skb) err = mISDN_queue_data(&l3->inst, FLG_MSG_DOWN, prim, dinfo, 0, NULL, 0); else err = mISDN_queuedown_newhead(&l3->inst, 0, prim, dinfo, skb); return(err); } #define DREL_TIMER_VALUE 40000 static void lc_activate(struct FsmInst *fi, int event, void *arg) { layer3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT); l3down(l3, DL_ESTABLISH | REQUEST, 0, NULL); } static void lc_connect(struct FsmInst *fi, int event, void *arg) { layer3_t *l3 = fi->userdata; struct sk_buff *skb; int dequeued = 0; mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB); while ((skb = skb_dequeue(&l3->squeue))) { if (l3down(l3, DL_DATA | REQUEST, l3_newid(l3), skb)) dev_kfree_skb(skb); dequeued++; } if (list_empty(&l3->plist) && dequeued) { if (l3->debug) l3m_debug(fi, "lc_connect: release link"); mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL); } else l3ml3p(l3, DL_ESTABLISH | INDICATION); } static void lc_connected(struct FsmInst *fi, int event, void *arg) { layer3_t *l3 = fi->userdata; struct sk_buff *skb; int dequeued = 0; mISDN_FsmDelTimer(&l3->l3m_timer, 51); mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB); while ((skb = skb_dequeue(&l3->squeue))) { if (l3down(l3, DL_DATA | REQUEST, l3_newid(l3), skb)) dev_kfree_skb(skb); dequeued++; } if (list_empty(&l3->plist) && dequeued) { if (l3->debug) l3m_debug(fi, "lc_connected: release link"); mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL); } else l3ml3p(l3, DL_ESTABLISH | CONFIRM); } static void lc_start_delay(struct FsmInst *fi, int event, void *arg) { layer3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_L3_LC_REL_DELAY); mISDN_FsmAddTimer(&l3->l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50); } static void lc_release_req(struct FsmInst *fi, int event, void *arg) { layer3_t *l3 = fi->userdata; if (test_bit(FLG_L2BLOCK, &l3->Flag)) { if (l3->debug) l3m_debug(fi, "lc_release_req: l2 blocked"); /* restart release timer */ mISDN_FsmAddTimer(&l3->l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51); } else { mISDN_FsmChangeState(fi, ST_L3_LC_REL_WAIT); l3down(l3, DL_RELEASE | REQUEST, 0, NULL); } } static void lc_release_ind(struct FsmInst *fi, int event, void *arg) { layer3_t *l3 = fi->userdata; mISDN_FsmDelTimer(&l3->l3m_timer, 52); mISDN_FsmChangeState(fi, ST_L3_LC_REL); discard_queue(&l3->squeue); l3ml3p(l3, DL_RELEASE | INDICATION); } static void lc_release_cnf(struct FsmInst *fi, int event, void *arg) { layer3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_L3_LC_REL); discard_queue(&l3->squeue); l3ml3p(l3, DL_RELEASE | CONFIRM); } /* *INDENT-OFF* */ static struct FsmNode L3FnList[] = { {ST_L3_LC_REL, EV_ESTABLISH_REQ, lc_activate}, {ST_L3_LC_REL, EV_ESTABLISH_IND, lc_connect}, {ST_L3_LC_REL, EV_ESTABLISH_CNF, lc_connect}, {ST_L3_LC_ESTAB_WAIT, EV_ESTABLISH_CNF, lc_connected}, {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_start_delay}, {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_IND, lc_release_ind}, {ST_L3_LC_ESTAB, EV_RELEASE_IND, lc_release_ind}, {ST_L3_LC_ESTAB, EV_RELEASE_REQ, lc_start_delay}, {ST_L3_LC_REL_DELAY, EV_RELEASE_IND, lc_release_ind}, {ST_L3_LC_REL_DELAY, EV_ESTABLISH_REQ, lc_connected}, {ST_L3_LC_REL_DELAY, EV_TIMEOUT, lc_release_req}, {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_cnf}, {ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate}, }; /* *INDENT-ON* */ #define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode)) int l3_msg(layer3_t *l3, u_int pr, int dinfo, int len, void *arg) { switch (pr) { case (DL_DATA | REQUEST): if (l3->l3m.state == ST_L3_LC_ESTAB) { return(l3down(l3, pr, l3_newid(l3), arg)); } else { struct sk_buff *skb = arg; // printk(KERN_DEBUG "%s: queue skb %p len(%d)\n", // __FUNCTION__, skb, skb->len); skb_queue_tail(&l3->squeue, skb); mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_REQ, NULL); } break; case (DL_ESTABLISH | REQUEST): mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_REQ, NULL); break; case (DL_ESTABLISH | CONFIRM): mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_CNF, NULL); break; case (DL_ESTABLISH | INDICATION): mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_IND, NULL); break; case (DL_RELEASE | INDICATION): mISDN_FsmEvent(&l3->l3m, EV_RELEASE_IND, NULL); break; case (DL_RELEASE | CONFIRM): mISDN_FsmEvent(&l3->l3m, EV_RELEASE_CNF, NULL); break; case (DL_RELEASE | REQUEST): mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL); break; } return(0); } void init_l3(layer3_t *l3) { INIT_LIST_HEAD(&l3->plist); l3->global = NULL; l3->dummy = NULL; l3->entity = MISDN_ENTITY_NONE; l3->next_id = 1; skb_queue_head_init(&l3->squeue); l3->l3m.fsm = &l3fsm; l3->l3m.state = ST_L3_LC_REL; l3->l3m.debug = l3->debug; l3->l3m.userdata = l3; l3->l3m.userint = 0; l3->l3m.printdebug = l3m_debug; mISDN_FsmInitTimer(&l3->l3m, &l3->l3m_timer); } void release_l3(layer3_t *l3) { l3_process_t *p, *np; if (l3->l3m.debug) printk(KERN_DEBUG "release_l3(%p) plist(%s) global(%p) dummy(%p)\n", l3, list_empty(&l3->plist) ? "no" : "yes", l3->global, l3->dummy); list_for_each_entry_safe(p, np, &l3->plist, list) release_l3_process(p); if (l3->global) { StopAllL3Timer(l3->global); kfree(l3->global); l3->global = NULL; } if (l3->dummy) { StopAllL3Timer(l3->dummy); kfree(l3->dummy); l3->dummy = NULL; } mISDN_FsmDelTimer(&l3->l3m_timer, 54); discard_queue(&l3->squeue); } void mISDNl3New(void) { l3fsm.state_count = L3_STATE_COUNT; l3fsm.event_count = L3_EVENT_COUNT; l3fsm.strEvent = strL3Event; l3fsm.strState = strL3State; mISDN_FsmNew(&l3fsm, L3FnList, L3_FN_COUNT); } void mISDNl3Free(void) { mISDN_FsmFree(&l3fsm); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/loop.c0000644000000000000500000003772011135651702017515 0ustar rootsrc/* * loop.c loop driver for looped bchannel pairs * * Author Andreas Eversberg (jolly@eversberg.eu) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* module parameters: * interfaces: Number of loop interfaces. Default is 1. */ #include #include #include "channel.h" #include "layer1.h" #include "debug.h" #include #include "loop.h" static const char *loop_revision = "$Revision: 1.8 $"; 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_param(interfaces, uint, S_IRUGO | S_IWUSR); module_param(debug, uint, S_IRUGO | S_IWUSR); #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); } } mISDN_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 = mISDN_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 = mISDN_ctrl(dst, MGR_NEWSTACK | REQUEST, &bch->inst))) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", ret_err); free_delstack: mISDN_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 = mISDN_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 */ mISDN_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 mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/loop.h0000644000000000000500000000071411135651702017513 0ustar rootsrc/* * 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; mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/m_capi.h0000755000000000000500000004623411135651702020004 0ustar rootsrc/* $Id: m_capi.h,v 1.13 2006/03/06 12:52:07 keil Exp $ * * Rewritten CAPI Layer (Layer4 in mISDN) * * The CAPI layer knows following basic Objects * * - Controller_t : the contoller instance * - Application_t : applications object * - Plci_t : PLCI object * - AppPlci_t : per application PLCI object * - Ncci_t : NCCI object * * For Supplementary Services * - SSProcess_t : a process handling a service request * * The controller is a Layer4 (D-channel) stack instance of * mISDN. * * Applications are owned by the controller and only * handle this controller, multiplexing multiple * controller with one application is done in the higher * driver independ CAPI driver. The application contain * the Listen state machine. * * Plcis are owned by the controller and are static (allocated * together with the controller). They maybe in use or inactiv. * Currently 8 PLCIs are available on a BRI (2 B-channel) controller * and 40 on a PRI (30 B-channel). They have a list of the associated * application PLCIs. * * AppPlcis are owned by the application and are * instance of the PLCI per application. They contain the * CAPI2.0 PCLI state machine * * Nccis are owned by the application Plcis. In the first version * this driver supports only one NCCI per PLCI. * * */ #ifndef __mISDN_CAPI_H__ #define __mISDN_CAPI_H__ #include #include #include #include #include #ifdef OLDCAPI_DRIVER_INTERFACE #include "../avmb1/capiutil.h" #include "../avmb1/capicmd.h" #include "../avmb1/capilli.h" #else #include #include #include #include #endif #include "asn1.h" #include "fsm.h" #ifdef MISDN_MEMDEBUG #include "memdbg.h" #define MISDN_KMEM_DEBUG 1 #endif // --------------------------------------------------------------------------- // common stuff // debuging levels and functions // --------------------------------------------------------------------------- #define CAPI_DBG_WARN 0x00000001 #define CAPI_DBG_INFO 0x00000004 #define CAPI_DBG_APPL 0x00000010 #define CAPI_DBG_APPL_INFO 0x00000040 #define CAPI_DBG_APPL_MSG 0x00000080 #define CAPI_DBG_LISTEN 0x00000100 #define CAPI_DBG_LISTEN_STATE 0x00000200 #define CAPI_DBG_LISTEN_INFO 0x00000400 #define CAPI_DBG_CONTR 0x00010000 #define CAPI_DBG_CONTR_INFO 0x00040000 #define CAPI_DBG_CONTR_MSG 0x00080000 #define CAPI_DBG_PLCI 0x00100000 #define CAPI_DBG_PLCI_STATE 0x00200000 #define CAPI_DBG_PLCI_INFO 0x00400000 #define CAPI_DBG_PLCI_L3 0x00800000 #define CAPI_DBG_NCCI 0x01000000 #define CAPI_DBG_NCCI_STATE 0x02000000 #define CAPI_DBG_NCCI_INFO 0x04000000 #define CAPI_DBG_NCCI_L3 0x08000000 void capidebug(int, char *, ...); #ifdef OLDCAPI_DRIVER_INTERFACE extern struct capi_driver_interface *cdrv_if; extern struct capi_driver mISDN_driver; #endif // --------------------------------------------------------------------------- // Init/Exit functions // --------------------------------------------------------------------------- void init_listen(void); void init_AppPlci(void); void init_ncci(void); void free_Application(void); void free_listen(void); void free_AppPlci(void); void free_ncci(void); // --------------------------------------------------------------------------- // More CAPI defines // --------------------------------------------------------------------------- /* we implement 64 bit extentions */ #define CAPI_B3_DATA_IND_HEADER_SIZE 30 #define CAPI_MSG_DEFAULT_LEN 256 #define CAPIMSG_REQ_DATAHANDLE(m) (m[18] | (m[19]<<8)) #define CAPIMSG_RESP_DATAHANDLE(m) (m[12] | (m[13]<<8)) #define CMSGCMD(cmsg) CAPICMD((cmsg)->Command, (cmsg)->Subcommand) #define CAPI_MAXPLCI_BRI 8 #define CAPI_MAXPLCI_PRI 40 __u16 q931CIPValue(Q931_info_t *); // --------------------------------------------------------------------------- // Basic CAPI types // --------------------------------------------------------------------------- typedef struct _Controller Controller_t; typedef struct _Application Application_t; typedef struct _Ncci Ncci_t; typedef struct _Plci Plci_t; typedef struct _AppPlci AppPlci_t; typedef struct _SSProcess SSProcess_t; // some helper types typedef struct _ConfQueue ConfQueue_t; typedef struct _PLInst PLInst_t; // Facility types typedef struct FacReqParm FacReqParm_t; typedef struct FacConfParm FacConfParm_t; // --------------------------------------------------------------------------- // Helper structs // --------------------------------------------------------------------------- struct _PLInst { struct list_head list; u_int state; mISDNstack_t *st; mISDNinstance_t inst; }; struct _ConfQueue { __u32 PktId; __u16 DataHandle; __u16 MsgId; }; struct Bprotocol { __u16 B1; __u16 B2; __u16 B3; __u8 B1cfg[16]; __u8 B2cfg[16]; __u8 B3cfg[80]; }; // --------------------------------------------------------------------------- // struct Controller // --------------------------------------------------------------------------- struct _Controller { struct list_head list; mISDNinstance_t inst; int nr_bc; struct list_head linklist; struct capi_ctr *ctrl; __u32 addr; int entity; int next_id; u_int debug; int maxplci; Plci_t *plcis; struct list_head Applications; struct list_head SSProcesse; spinlock_t list_lock; __u32 NotificationMask; __u16 LastInvokeId; char infobuf[128]; }; // --------------------------------------------------------------------------- // struct Application // --------------------------------------------------------------------------- struct _Application { struct list_head head; Controller_t *contr; __u16 ApplId; __u16 MsgId; __u32 InfoMask; __u32 CIPmask; __u32 CIPmask2; __u32 NotificationMask; u_long state; struct FsmInst listen_m; int maxplci; AppPlci_t **AppPlcis; capi_register_params reg_params; }; #define APPL_STATE_ACTIV 1 #define APPL_STATE_RELEASE 2 #define APPL_STATE_LISTEN 3 #define APPL_STATE_DESTRUCTOR 4 #define APPL_STATE_D2TRACE 8 // --------------------------------------------------------------------------- // struct Plci // --------------------------------------------------------------------------- struct _Plci { Controller_t *contr; __u32 addr; __u32 l3id; u_long state; int nAppl; struct list_head AppPlcis; }; #define PLCI_STATE_ACTIV 1 #define PLCI_STATE_ALERTING 2 #define PLCI_STATE_OUTGOING 3 #define PLCI_STATE_STACKREADY 4 #define PLCI_STATE_SENDDELAYED 5 // --------------------------------------------------------------------------- // struct AppPlci // --------------------------------------------------------------------------- struct _AppPlci { struct list_head head; __u32 addr; Plci_t *plci; Application_t *appl; Controller_t *contr; PLInst_t *link; struct sk_buff_head delayedq; struct list_head Nccis; struct FsmInst plci_m; u_char cause[4]; int channel; struct Bprotocol Bprotocol; }; // --------------------------------------------------------------------------- // struct Ncci // --------------------------------------------------------------------------- struct _Ncci { struct list_head head; __u32 addr; PLInst_t *link; Controller_t *contr; AppPlci_t *AppPlci; Application_t *appl; struct FsmInst ncci_m; int savedstate; int window; u_long state; ConfQueue_t xmit_skb_handles[CAPI_MAXDATAWINDOW]; struct sk_buff *recv_skb_handles[CAPI_MAXDATAWINDOW]; struct sk_buff_head squeue; }; #define NCCI_STATE_FCTRL 1 #define NCCI_STATE_BUSY 2 #define NCCI_STATE_L3TRANS 3 #define NCCI_STATE_APPLRELEASED 4 // --------------------------------------------------------------------------- // struct SSProcess_t // --------------------------------------------------------------------------- struct _SSProcess { struct list_head head; __u16 invokeId; __u16 Function; __u32 Handle; __u32 addr; __u16 ApplId; Controller_t *contr; struct timer_list tl; __u8 buf[128]; }; // --------------------------------------------------------------------------- // FUNCTION prototypes // // Controller prototypes // --------------------------------------------------------------------------- int ControllerConstr(Controller_t **, mISDNstack_t *, mISDN_pid_t *, mISDNobject_t *); void ControllerDestr(Controller_t *); void ControllerRun(Controller_t *); void ControllerDebug(Controller_t *, __u32, char *, ...); int ControllerNewPlci(Controller_t *, Plci_t **, u_int); 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(mISDNinstance_t *, struct sk_buff *); PLInst_t *ControllerSelChannel(Controller_t *, u_int); void ControllerAddSSProcess(Controller_t *, SSProcess_t *); SSProcess_t *getSSProcess4Id(Controller_t *, __u16); int ControllerNextId(Controller_t *); // --------------------------------------------------------------------------- // Application prototypes // --------------------------------------------------------------------------- int ApplicationConstr(Controller_t *, __u16, capi_register_params *); int ApplicationDestr(Application_t *, int); void ApplicationDebug(Application_t *appl, __u32 level, char *fmt, ...); 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); void applManufacturerReq(Application_t *appl, struct sk_buff *skb); void applD2Trace(Application_t *appl, u_char *buf, int len); AppPlci_t *ApplicationNewAppPlci(Application_t *, Plci_t *); AppPlci_t *getAppPlci4addr(Application_t *, __u32); void ApplicationDelAppPlci(Application_t *, AppPlci_t *); void listenConstr(Application_t *); void listenDestr(Application_t *); __u16 listenSendMessage(Application_t *, struct sk_buff *); int listenHandle(Application_t *, __u16); // --------------------------------------------------------------------------- // PLCI prototypes // --------------------------------------------------------------------------- void plciInit(Controller_t *); void plciDebug(Plci_t *, __u32, char *, ...); int plci_l3l4(Plci_t *, int, struct sk_buff *); void plciAttachAppPlci(Plci_t *, AppPlci_t *); void plciDetachAppPlci(Plci_t *, AppPlci_t *); void plciNewCrInd(Plci_t *, void *); void plciNewCrReq(Plci_t *); int plciL4L3(Plci_t *, __u32, struct sk_buff *); // --------------------------------------------------------------------------- // AppPLCI prototypes // --------------------------------------------------------------------------- int AppPlciConstr(AppPlci_t **, Application_t *, Plci_t *); void AppPlciDestr(AppPlci_t *); 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); 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 #define GET_NCCI_PLCI 3 // --------------------------------------------------------------------------- // NCCI prototypes // --------------------------------------------------------------------------- Ncci_t *ncciConstr(AppPlci_t *); void ncciDestr(Ncci_t *); void ncciApplRelease(Ncci_t *); void ncciDelAppPlci(Ncci_t *); 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 *); void ncciReleaseLink(Ncci_t *); // --------------------------------------------------------------------------- // SSProcess prototypes // --------------------------------------------------------------------------- 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 /* 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 SuppServiceCD 0x00000020 #define SuppServiceMCID 0x00000040 #define SuppServiceCCBS 0x00000080 #define mISDNSupportedServices (SuppServiceCD | \ SuppServiceCF | \ SuppServiceTP | \ SuppServiceHR) // --------------------------------------------------------------------------- // structs for Facillity requests // --------------------------------------------------------------------------- struct FacReqListen { __u32 NotificationMask; }; struct FacReqSuspend { __u8 *CallIdentity; }; struct FacReqResume { __u8 *CallIdentity; }; struct FacReqCFActivate { __u32 Handle; __u16 Procedure; __u16 BasicService; __u8 *ServedUserNumber; __u8 *ForwardedToNumber; __u8 *ForwardedToSubaddress; }; struct FacReqCFDeactivate { __u32 Handle; __u16 Procedure; __u16 BasicService; __u8 *ServedUserNumber; }; struct FacReqCDeflection { __u16 PresentationAllowed; __u8 *DeflectedToNumber; __u8 *DeflectedToSubaddress; }; #define FacReqCFInterrogateParameters FacReqCFDeactivate struct FacReqCFInterrogateNumbers { __u32 Handle; }; struct FacReqParm { __u16 Function; union { struct FacReqListen Listen; struct FacReqSuspend Suspend; struct FacReqResume Resume; struct FacReqCFActivate CFActivate; struct FacReqCFDeactivate CFDeactivate; struct FacReqCFInterrogateParameters CFInterrogateParameters; struct FacReqCFInterrogateNumbers CFInterrogateNumbers; struct FacReqCDeflection CDeflection; } u; }; // --------------------------------------------------------------------------- // structs for Facillity confirms // --------------------------------------------------------------------------- struct FacConfGetSupportedServices { __u16 SupplementaryServiceInfo; __u32 SupportedServices; }; struct FacConfInfo { __u16 SupplementaryServiceInfo; }; struct FacConfParm { __u16 Function; union { struct FacConfGetSupportedServices GetSupportedServices; struct FacConfInfo Info; } u; }; int capiEncodeWord(__u8 *dest, __u16 i); int capiEncodeDWord(__u8 *dest, __u32 i); int capiEncodeFacIndCFact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle); int capiEncodeFacIndCFdeact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle); int capiEncodeFacIndCFNotAct(__u8 *dest, struct ActDivNotification *actNot); int capiEncodeFacIndCFNotDeact(__u8 *dest, struct DeactDivNotification *deactNot); int capiEncodeFacIndCFinterParameters(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, struct IntResultList *intResultList); int capiEncodeFacIndCFinterNumbers(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, struct ServedUserNumberList *list); int capiEncodeFacConfParm(__u8 *dest, struct FacConfParm *facConfParm); int capiEncodeFacIndSuspend(__u8 *dest, __u16 SupplementaryServiceReason); // --------------------------------------------------------------------------- // mISDN kmem cache managment functions // --------------------------------------------------------------------------- /* kmem caches */ extern struct kmem_cache *mISDN_cmsg_cp; extern struct kmem_cache *mISDN_AppPlci_cp; extern struct kmem_cache *mISDN_ncci_cp; extern struct kmem_cache *mISDN_sspc_cp; #ifdef MISDN_KMEM_DEBUG typedef struct _kd_cmsg _kd_cmsg_t; typedef struct _kd_Ncci _kd_Ncci_t; typedef struct _kd_AppPlci _kd_AppPlci_t; typedef struct _kd_SSProcess _kd_SSProcess_t; typedef struct _kd_all _kd_all_t; typedef struct __km_dbg_item { struct list_head head; long typ; char *file; u_int line; } km_dbg_item_t; struct _kd_cmsg { km_dbg_item_t kdi; _cmsg cm; }; struct _kd_AppPlci { km_dbg_item_t kdi; AppPlci_t ap; }; struct _kd_Ncci { km_dbg_item_t kdi; Ncci_t ni; }; struct _kd_SSProcess { km_dbg_item_t kdi; SSProcess_t sp; }; struct _kd_all { km_dbg_item_t kdi; union { _cmsg cm; AppPlci_t ap; Ncci_t ni; SSProcess_t sp; } a; }; #define KDB_GET_KDI(kd) ((km_dbg_item_t *)(((u_char *)kd) - sizeof(km_dbg_item_t))) #define KDB_GET_KDALL(kd) ((_kd_all_t *)(((u_char *)kd) - sizeof(km_dbg_item_t))) #define KM_DBG_TYP_CM 1 #define KM_DBG_TYP_AP 2 #define KM_DBG_TYP_NI 3 #define KM_DBG_TYP_SP 4 #define cmsg_alloc() _kd_cmsg_alloc(__FILE__, __LINE__) extern _cmsg *_kd_cmsg_alloc(char *, int); extern void cmsg_free(_cmsg *cm); #define AppPlci_alloc() _kd_AppPlci_alloc(__FILE__, __LINE__) extern AppPlci_t *_kd_AppPlci_alloc(char *, int); extern void AppPlci_free(AppPlci_t *ap); #define ncci_alloc() _kd_ncci_alloc(__FILE__, __LINE__) extern Ncci_t *_kd_ncci_alloc(char *, int); extern void ncci_free(Ncci_t *ni); #define SSProcess_alloc() _kd_SSProcess_alloc(__FILE__, __LINE__) extern SSProcess_t *_kd_SSProcess_alloc(char *, int); extern void SSProcess_free(SSProcess_t *sp); #else /* ! MISDN_KMEM_DEBUG */ static __inline__ _cmsg *cmsg_alloc(void) { return(kmem_cache_alloc(mISDN_cmsg_cp, GFP_ATOMIC)); } static __inline__ void cmsg_free(_cmsg *cm) { kmem_cache_free(mISDN_cmsg_cp, cm); } static __inline__ AppPlci_t *AppPlci_alloc(void) { return(kmem_cache_alloc(mISDN_AppPlci_cp, GFP_ATOMIC)); } static __inline__ void AppPlci_free(AppPlci_t *ap) { kmem_cache_free(mISDN_AppPlci_cp, ap); } static __inline__ Ncci_t *ncci_alloc(void) { return(kmem_cache_alloc(mISDN_ncci_cp, GFP_ATOMIC)); } static __inline__ void ncci_free(Ncci_t *ni) { kmem_cache_free(mISDN_ncci_cp, ni); } static __inline__ SSProcess_t *SSProcess_alloc(void) { return(kmem_cache_alloc(mISDN_sspc_cp, GFP_ATOMIC)); } static __inline__ void SSProcess_free(SSProcess_t *sp) { kmem_cache_free(mISDN_sspc_cp, sp); } #endif /* MISDN_KMEM_DEBUG */ // cmsg_alloc with error handling for void functions #define CMSG_ALLOC(cm) if (!(cm = cmsg_alloc())) {int_error();return;} #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/memdbg.c0000644000000000000500000001556511135651702020002 0ustar rootsrc#include #include #include #include #include #include #include #ifdef CONFIG_KMOD #include #endif static struct list_head mISDN_memdbg_list = LIST_HEAD_INIT(mISDN_memdbg_list); static struct list_head mISDN_skbdbg_list = LIST_HEAD_INIT(mISDN_skbdbg_list); static spinlock_t memdbg_lock = SPIN_LOCK_UNLOCKED; static spinlock_t skbdbg_lock = SPIN_LOCK_UNLOCKED; static kmem_cache_t *mid_sitem_cache; #define MAX_FILE_STRLEN (64 - 3*sizeof(u_int) - sizeof(struct list_head)) #define MID_ITEM_TYP_KMALLOC 1 #define MID_ITEM_TYP_VMALLOC 2 typedef struct _mid_item { struct list_head head; u_int typ; u_int size; u_int line; char file[MAX_FILE_STRLEN]; } _mid_item_t; typedef struct _mid_sitem { struct list_head head; struct sk_buff *skb; unsigned int size; int line; char file[MAX_FILE_STRLEN]; } _mid_sitem_t; void * __mid_kmalloc(size_t size, int ord, char *fn, int line) { _mid_item_t *mid; u_long flags; mid = kmalloc(size + sizeof(_mid_item_t), ord); if (mid) { INIT_LIST_HEAD(&mid->head); mid->typ = MID_ITEM_TYP_KMALLOC; mid->size = size; mid->line = line; memcpy(mid->file, fn, MAX_FILE_STRLEN); mid->file[MAX_FILE_STRLEN-1] = 0; spin_lock_irqsave(&memdbg_lock, flags); list_add_tail(&mid->head, &mISDN_memdbg_list); spin_unlock_irqrestore(&memdbg_lock, flags); return((void *)&mid->file[MAX_FILE_STRLEN]); } else return(NULL); } void __mid_kfree(const void *p) { _mid_item_t *mid; u_long flags; if (!p) return; mid = (_mid_item_t *)((u_char *)p - sizeof(_mid_item_t)); spin_lock_irqsave(&memdbg_lock, flags); list_del(&mid->head); spin_unlock_irqrestore(&memdbg_lock, flags); kfree(mid); } void * __mid_vmalloc(size_t size, char *fn, int line) { _mid_item_t *mid; u_long flags; mid = vmalloc(size + sizeof(_mid_item_t)); if (mid) { INIT_LIST_HEAD(&mid->head); mid->typ = MID_ITEM_TYP_VMALLOC; mid->size = size; mid->line = line; memcpy(mid->file, fn, MAX_FILE_STRLEN); mid->file[MAX_FILE_STRLEN-1] = 0; spin_lock_irqsave(&memdbg_lock, flags); list_add_tail(&mid->head, &mISDN_memdbg_list); spin_unlock_irqrestore(&memdbg_lock, flags); return((void *)&mid->file[MAX_FILE_STRLEN]); } else return(NULL); } void __mid_vfree(const void *p) { _mid_item_t *mid; u_long flags; if (!p) return; mid = (_mid_item_t *)((u_char *)p - sizeof(_mid_item_t)); spin_lock_irqsave(&memdbg_lock, flags); list_del(&mid->head); spin_unlock_irqrestore(&memdbg_lock, flags); vfree(mid); } static void __mid_skb_destructor(struct sk_buff *skb) { struct list_head *item; _mid_sitem_t *sid; u_long flags; spin_lock_irqsave(&skbdbg_lock, flags); list_for_each(item, &mISDN_skbdbg_list) { sid = (_mid_sitem_t *)item; if (sid->skb == skb) { list_del(&sid->head); spin_unlock_irqrestore(&skbdbg_lock, flags); kmem_cache_free(mid_sitem_cache, sid); return; } } spin_unlock_irqrestore(&skbdbg_lock, flags); printk(KERN_DEBUG "%s: item(%p) not in list\n", __FUNCTION__, skb); } static __inline__ void __mid_sitem_setup(struct sk_buff *skb, unsigned int size, char *fn, int line) { _mid_sitem_t *sid; u_long flags; sid = kmem_cache_alloc(mid_sitem_cache, GFP_ATOMIC); if (!sid) { printk(KERN_DEBUG "%s: no memory for sitem skb %p %s:%d\n", __FUNCTION__, skb, fn, line); return; } INIT_LIST_HEAD(&sid->head); sid->skb = skb; sid->size = size; sid->line = line; memcpy(sid->file, fn, MAX_FILE_STRLEN); sid->file[MAX_FILE_STRLEN-1] = 0; skb->destructor = __mid_skb_destructor; spin_lock_irqsave(&skbdbg_lock, flags); list_add_tail(&sid->head, &mISDN_skbdbg_list); spin_unlock_irqrestore(&skbdbg_lock, flags); } struct sk_buff * __mid_alloc_skb(unsigned int size, int gfp_mask, char *fn, int line) { struct sk_buff *skb = alloc_skb(size, gfp_mask); if (!skb) return(NULL); __mid_sitem_setup(skb, size, fn, line); return(skb); } struct sk_buff * __mid_dev_alloc_skb(unsigned int size, char *fn, int line) { struct sk_buff *skb = dev_alloc_skb(size); if (!skb) return(NULL); __mid_sitem_setup(skb, size, fn, line); return(skb); } struct sk_buff *__mid_skb_clone(struct sk_buff *skb, int gfp_mask, char *fn, int line) { struct sk_buff *nskb = skb_clone(skb, gfp_mask); if (!nskb) return(NULL); __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line); return(nskb); } struct sk_buff *__mid_skb_copy(struct sk_buff *skb, int gfp_mask, char *fn, int line) { struct sk_buff *nskb = skb_copy(skb, gfp_mask); if (!nskb) return(NULL); __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line); return(nskb); } struct sk_buff *__mid_skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom, char *fn, int line) { struct sk_buff *nskb = skb_realloc_headroom(skb, headroom); if (!nskb || (nskb == skb)) return(nskb); __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line); return(nskb); } void __mid_cleanup(void) { struct list_head *item, *next; _mid_item_t *mid; _mid_sitem_t *sid; mISDN_head_t *hh; int n = 0; u_long flags; spin_lock_irqsave(&memdbg_lock, flags); list_for_each_safe(item, next, &mISDN_memdbg_list) { mid = (_mid_item_t *)item; switch(mid->typ) { case MID_ITEM_TYP_KMALLOC: printk(KERN_ERR "not freed kmalloc size(%d) from %s:%d\n", mid->size, mid->file, mid->line); kfree(mid); break; case MID_ITEM_TYP_VMALLOC: printk(KERN_ERR "not freed vmalloc size(%d) from %s:%d\n", mid->size, mid->file, mid->line); vfree(mid); break; default: printk(KERN_ERR "unknown mid->typ(%d) size(%d) from %s:%d\n", mid->typ, mid->size, mid->file, mid->line); break; } n++; } spin_unlock_irqrestore(&memdbg_lock, flags); printk(KERN_DEBUG "%s: %d kmalloc item(s) freed\n", __FUNCTION__, n); n = 0; spin_lock_irqsave(&skbdbg_lock, flags); list_for_each_safe(item, next, &mISDN_skbdbg_list) { sid = (_mid_sitem_t *)item; hh = mISDN_HEAD_P(sid->skb); printk(KERN_ERR "not freed skb(%p) size(%d) prim(%x) dinfo(%x) allocated at %s:%d\n", sid->skb, sid->size, hh->prim, hh->dinfo, sid->file, sid->line); /*maybe the skb is still aktiv */ sid->skb->destructor = NULL; list_del(&sid->head); kmem_cache_free(mid_sitem_cache, sid); n++; } spin_unlock_irqrestore(&skbdbg_lock, flags); if (mid_sitem_cache) kmem_cache_destroy(mid_sitem_cache); printk(KERN_DEBUG "%s: %d sk_buff item(s) freed\n", __FUNCTION__, n); } int __mid_init(void) { mid_sitem_cache = kmem_cache_create("mISDN_skbdbg", sizeof(_mid_sitem_t), 0, 0, NULL, NULL); if (!mid_sitem_cache) return(-ENOMEM); return(0); } #ifdef MODULE EXPORT_SYMBOL(__mid_kmalloc); EXPORT_SYMBOL(__mid_kfree); EXPORT_SYMBOL(__mid_vmalloc); EXPORT_SYMBOL(__mid_vfree); EXPORT_SYMBOL(__mid_alloc_skb); EXPORT_SYMBOL(__mid_dev_alloc_skb); EXPORT_SYMBOL(__mid_skb_clone); EXPORT_SYMBOL(__mid_skb_copy); EXPORT_SYMBOL(__mid_skb_realloc_headroom); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/memdbg.h0000644000000000000500000000267311135651702020003 0ustar rootsrc#ifndef MEMDBG_H #define MEMDBG_H #ifdef MISDN_MEMDEBUG #include #include #undef kmalloc #undef kfree #undef vmalloc #undef vfree #undef alloc_skb #undef dev_alloc_skb #undef skb_clone #undef skb_copy #undef skb_realloc_headroom #define kmalloc(a, b) __mid_kmalloc(a, b, __FILE__, __LINE__) #define kfree(a) __mid_kfree(a) #define vmalloc(s) __mid_vmalloc(s, __FILE__, __LINE__) #define vfree(p) __mid_vfree(p) #define alloc_skb(a, b) __mid_alloc_skb(a, b, __FILE__, __LINE__) #define dev_alloc_skb(a) __mid_dev_alloc_skb(a, __FILE__, __LINE__) #define skb_clone(a, b) __mid_skb_clone(a, b, __FILE__, __LINE__) #define skb_copy(a, b) __mid_skb_copy(a, b, __FILE__, __LINE__) #define skb_realloc_headroom(a, b) __mid_skb_realloc_headroom(a, b, __FILE__, __LINE__) extern void *__mid_kmalloc(size_t, int, char *, int); extern void __mid_kfree(const void *); extern void *__mid_vmalloc(size_t, char *, int); extern void __mid_vfree(const void *); extern void __mid_cleanup(void); extern int __mid_init(void); extern struct sk_buff *__mid_alloc_skb(unsigned int,int, char *, int); extern struct sk_buff *__mid_dev_alloc_skb(unsigned int,char *, int); extern struct sk_buff *__mid_skb_clone(struct sk_buff *, int, char *, int); extern struct sk_buff *__mid_skb_copy(struct sk_buff *, int, char *, int); extern struct sk_buff *__mid_skb_realloc_headroom(struct sk_buff *, unsigned int, char *, int); #endif #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/isac.c0000644000000000000500000005541711110524073017457 0ustar rootsrc/* $Id: isac.c,v 1.18 2006/12/21 15:25:06 nadi Exp $ * * isac.c ISAC specific routines * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE */ #include #include "core.h" #include "channel.h" #include "isac.h" #include "arcofi.h" #include "layer1.h" #include "helper.h" #include "debug.h" #ifdef CONFIG_KMOD #include #endif #define DBUSY_TIMER_VALUE 80 #define ARCOFI_USE 1 const char *isac_revision = "$Revision: 1.18 $"; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif EXPORT_SYMBOL(mISDN_isac_init); EXPORT_SYMBOL(mISDN_isac_free); EXPORT_SYMBOL(mISDN_isac_interrupt); EXPORT_SYMBOL(mISDN_clear_isac); EXPORT_SYMBOL(mISDN_ISAC_l1hw); #endif static inline void 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.privat, ISACSX_CIX0, (command << 4) | 0xE); else dch->write_reg(dch->inst.privat, ISAC_CIX0, (command << 2) | 3); } static void isac_ph_state_change(channel_t *dch) { u_int prim = PH_SIGNAL | INDICATION; u_int para = 0; switch (dch->state) { case (ISAC_IND_RS): case (ISAC_IND_EI): ph_command(dch, ISAC_CMD_DUI); prim = PH_CONTROL | INDICATION; para = HW_RESET; break; case (ISAC_IND_DID): prim = PH_CONTROL | CONFIRM; para = HW_DEACTIVATE; break; case (ISAC_IND_DR): prim = PH_CONTROL | INDICATION; para = HW_DEACTIVATE; break; case (ISAC_IND_PU): prim = PH_CONTROL | INDICATION; para = HW_POWERUP; break; case (ISAC_IND_RSY): para = ANYSIGNAL; break; case (ISAC_IND_ARD): para = INFO2; break; case (ISAC_IND_AI8): para = INFO4_P8; break; case (ISAC_IND_AI10): para = INFO4_P10; break; default: return; } mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0); } #ifdef OBSOLETE static void isac_hwbh(channel_t *dch) { if (dch->debug) printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event); if (test_and_clear_bit(D_L1STATECHANGE, &dch->event)) isac_new_ph(dch); #if ARCOFI_USE if (!(ISAC_TYPE_ARCOFI & dch->type)) return; if (test_and_clear_bit(D_RX_MON1, &dch->event)) arcofi_fsm(dch, ARCOFI_RX_END, NULL); if (test_and_clear_bit(D_TX_MON1, &dch->event)) arcofi_fsm(dch, ARCOFI_TX_END, NULL); #endif } #endif void isac_empty_fifo(channel_t *dch, int count) { u_char *ptr; if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) mISDN_debugprint(&dch->inst, "isac_empty_fifo"); 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.privat, ISAC_CMDR, 0x80); return; } } if ((dch->rx_skb->len + count) >= MAX_DFRAME_LEN_L1) { 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.privat, ISAC_CMDR, 0x80); return; } ptr = skb_put(dch->rx_skb, count); 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->log; t += sprintf(t, "isac_empty_fifo cnt %d", count); mISDN_QuickHex(t, ptr, count); mISDN_debugprint(&dch->inst, dch->log); } } static void 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"); if (!dch->tx_skb) return; count = dch->tx_skb->len - dch->tx_idx; if (count <= 0) return; more = 0; if (count > 32) { more = !0; count = 32; } ptr = dch->tx_skb->data + dch->tx_idx; dch->tx_idx += count; 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->timer); } 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->log; t += sprintf(t, "isac_fill_fifo cnt %d", count); mISDN_QuickHex(t, ptr, count); mISDN_debugprint(&dch->inst, dch->log); } } static void isac_rme_irq(channel_t *dch) { u_char val; u_int count; val = dch->read_reg(dch->inst.privat, ISAC_RSTA); if ((val & 0x70) != 0x20) { if (val & 0x40) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC RDO"); #ifdef ERROR_STATISTIC dch->err_rx++; #endif } if (!(val & 0x20)) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC CRC error"); #ifdef ERROR_STATISTIC dch->err_crc++; #endif } 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.privat, ISAC_RBCL) & 0x1f; if (count == 0) count = 32; isac_empty_fifo(dch, count); 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; } static void isac_xpr_irq(channel_t *dch) { 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 (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); } else { printk(KERN_WARNING "isac tx irq TX_NEXT without skb\n"); test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); } } else { dch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); } } } static void isac_retransmit(channel_t *dch) { 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_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); } 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(channel_t *dch) { u_char val; isac_chip_t *isac = dch->hw; 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 if (val & 0x08) { if (!isac->mon_rx) { if (!(isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC MON RX out of memory!"); isac->mocr &= 0xf0; isac->mocr |= 0x0a; dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr); goto afterMONR0; } else isac->mon_rxp = 0; } if (isac->mon_rxp >= MAX_MON_FRAME) { isac->mocr &= 0xf0; isac->mocr |= 0x0a; 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.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.privat, ISAC_MOCR, isac->mocr); } } afterMONR0: if (val & 0x80) { if (!isac->mon_rx) { if (!(isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC MON RX out of memory!"); isac->mocr &= 0x0f; isac->mocr |= 0xa0; dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr); goto afterMONR1; } else isac->mon_rxp = 0; } if (isac->mon_rxp >= MAX_MON_FRAME) { isac->mocr &= 0x0f; isac->mocr |= 0xa0; 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.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.privat, ISAC_MOCR, isac->mocr); } afterMONR1: if (val & 0x04) { isac->mocr &= 0xf0; dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr); isac->mocr |= 0x0a; 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.privat, ISAC_MOCR, isac->mocr); isac->mocr |= 0xa0; 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.privat, ISAC_MOCR, isac->mocr); isac->mocr |= 0x0a; dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr); if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) 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)) { mISDN_queue_data(&dch->inst, 0, PH_SIGNAL | INDICATION, D_TX_MON0, 0, NULL, 0); goto AfterMOX0; } 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]); } AfterMOX0: if (val & 0x20) { if ((!isac->mon_tx) || (isac->mon_txc && (isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) { isac->mocr &= 0x0f; dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr); isac->mocr |= 0xa0; dch->write_reg(dch->inst.privat, ISAC_MOCR, isac->mocr); if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) 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)) { mISDN_queue_data(&dch->inst, 0, PH_SIGNAL | INDICATION, D_TX_MON1, 0, NULL, 0); goto AfterMOX1; } 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]); } AfterMOX1: val = 0; /* dummy to avoid warning */ #endif } static void isac_cisq_irq(channel_t *dch) { unsigned char val; 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->state, (val >> 2) & 0xf); dch->state = (val >> 2) & 0xf; isac_ph_state_change(dch); } if (val & 1) { 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(channel_t *dch) { unsigned char val; 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->state, val >> 4); dch->state = val >> 4; isac_ph_state_change(dch); } } static void isacsx_rme_irq(channel_t *dch) { int count; unsigned char val; val = dch->read_reg(dch->inst.privat, ISACSX_RSTAD); if ((val & (ISACSX_RSTAD_VFR | ISACSX_RSTAD_RDO | ISACSX_RSTAD_CRC | ISACSX_RSTAD_RAB)) != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "RSTAD %#x, dropped", val); #ifdef ERROR_STATISTIC if (val & ISACSX_RSTAD_CRC) dch->err_rx++; else dch->err_crc++; #endif 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.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); 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; } void mISDN_isac_interrupt(channel_t *dch, u_char val) { if (dch->debug & L1_DEB_ISAC) mISDN_debugprint(&dch->inst, "ISAC interrupt %02x", val); if (dch->type & ISAC_TYPE_ISACSX) { if (val & ISACSX_ISTA_CIC) isacsx_cic_irq(dch); if (val & ISACSX_ISTA_ICD) { 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) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC XDU"); #ifdef ERROR_STATISTIC dch->err_tx++; #endif isac_retransmit(dch); } if (val & ISACSX_ISTAD_XMR) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC XMR"); #ifdef ERROR_STATISTIC dch->err_tx++; #endif isac_retransmit(dch); } if (val & ISACSX_ISTAD_XPR) isac_xpr_irq(dch); if (val & ISACSX_ISTAD_RFO) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC RFO"); dch->write_reg(dch->inst.privat, ISACSX_CMDRD, ISACSX_CMDRD_RMC); } if (val & ISACSX_ISTAD_RME) isacsx_rme_irq(dch); if (val & ISACSX_ISTAD_RPF) isac_empty_fifo(dch, 0x20); } } else { if (val & 0x80) /* RME */ isac_rme_irq(dch); if (val & 0x40) /* RPF */ isac_empty_fifo(dch, 32); if (val & 0x10) /* XPR */ isac_xpr_irq(dch); if (val & 0x04) /* CISQ */ isac_cisq_irq(dch); if (val & 0x20) /* RSC - never */ if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC RSC interrupt"); if (val & 0x02) /* SIN - never */ if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC SIN interrupt"); if (val & 0x01) { /* EXI */ 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 */ mISDN_debugprint(&dch->inst, "ISAC XMR"); if (val & 0x40) { /* XDU */ if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "ISAC XDU"); #ifdef ERROR_STATISTIC dch->err_tx++; #endif isac_retransmit(dch); } if (val & 0x04) /* MOS */ isac_mos_irq(dch); } } } int mISDN_ISAC_l1hw(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *dch; int ret = 0; mISDN_head_t *hh; u_long flags; hh = mISDN_HEAD_P(skb); dch = container_of(inst, channel_t, inst); if (hh->prim == PH_DATA_REQ) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(dch, hh->dinfo, skb); if (ret > 0) { /* direct TX */ isac_fill_fifo(dch); ret = 0; } spin_unlock_irqrestore(inst->hwlock, flags); return(ret); } else if (hh->prim == (PH_SIGNAL | REQUEST)) { 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; spin_unlock_irqrestore(inst->hwlock, flags); } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(inst->hwlock, flags); if (hh->dinfo == HW_RESET) { 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) { if (dch->next_skb) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); 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) { /* TODO */ } else { tl = 0; if (1 & hh->dinfo) tl |= 0x0c; if (2 & hh->dinfo) tl |= 0x3; if (ISAC_TYPE_IOM1 & dch->type) { /* IOM 1 Mode */ if (!tl) { dch->write_reg(dch->inst.privat, ISAC_SPCR, 0xa); dch->write_reg(dch->inst.privat, ISAC_ADF1, 0x2); } else { 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.privat, ISAC_SPCR, tl); if (tl) dch->write_reg(dch->inst.privat, ISAC_ADF1, 0x8); else dch->write_reg(dch->inst.privat, ISAC_ADF1, 0x0); } } } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "isac_l1hw unknown ctrl %x", hh->dinfo); ret = -EINVAL; } 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); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } void mISDN_isac_free(channel_t *dch) { isac_chip_t *isac = dch->hw; if (dch->timer.function != NULL) { del_timer(&dch->timer); dch->timer.function = NULL; } if (!isac) return; kfree(isac->mon_rx); isac->mon_rx = NULL; kfree(isac->mon_tx); isac->mon_tx = NULL; } static void dbusy_timer_handler(channel_t *dch) { int rbch, star; u_long flags; 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_BUSY, &dch->Flags); } else { /* discard frame; reset transceiver */ test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags); if (dch->tx_idx) { dch->tx_idx = 0; } else { printk(KERN_WARNING "mISDN: ISAC D-Channel Busy no tx_idx\n"); mISDN_debugprint(&dch->inst, "D-Channel Busy no tx_idx"); } /* Transmitter reset */ dch->write_reg(dch->inst.privat, ISAC_CMDR, 0x01); } spin_unlock_irqrestore(dch->inst.hwlock, flags); } } static char *ISACVer[] = {"2086/2186 V1.1", "2085 B1", "2085 B2", "2085 V2.3"}; int mISDN_isac_init(channel_t *dch) { isac_chip_t *isac = dch->hw; u_char val; if (!isac) return(-EINVAL); isac->mon_tx = NULL; isac->mon_rx = NULL; 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.privat, ISACSX_TR_CONF0, 0x00); // enable transmitter dch->write_reg(dch->inst.privat, ISACSX_TR_CONF2, 0x00); // transparent mode 0, RAC, stop/go dch->write_reg(dch->inst.privat, ISACSX_MODED, 0xc9); // all HDLC IRQ unmasked dch->write_reg(dch->inst.privat, ISACSX_MASKD, 0x03); // unmask ICD, CID IRQs dch->write_reg(dch->inst.privat, ISACSX_MASK, ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC)); printk(KERN_INFO "mISDN_isac_init: ISACSX\n"); isac_ph_state_change(dch); ph_command(dch, ISAC_CMD_RS); } else { /* old isac */ 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.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.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); } isac_ph_state_change(dch); ph_command(dch, ISAC_CMD_RS); dch->write_reg(dch->inst.privat, ISAC_MASK, 0x0); } return 0; } void mISDN_clear_isac(channel_t *dch) { isac_chip_t *isac = dch->hw; u_int val, eval; if (!isac) return; /* Disable all IRQ */ 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.privat, ISAC_MODE); mISDN_debugprint(&dch->inst, "ISAC MODE %x", val); val = dch->read_reg(dch->inst.privat, ISAC_ADF2); mISDN_debugprint(&dch->inst, "ISAC ADF2 %x", val); 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.privat, ISAC_EXIR); mISDN_debugprint(&dch->inst, "ISAC EXIR %x", eval); } val = dch->read_reg(dch->inst.privat, ISAC_CIR0); mISDN_debugprint(&dch->inst, "ISAC CIR0 %x", val); dch->state = (val >> 2) & 0xf; } #ifdef MODULE static int isac_mod_init(void) { printk(KERN_INFO "ISAC module %s\n", isac_revision); mISDN_module_register(THIS_MODULE); return(0); } static void isac_mod_cleanup(void) { mISDN_module_unregister(THIS_MODULE); printk(KERN_INFO "ISAC module unloaded\n"); } module_init(isac_mod_init); module_exit(isac_mod_cleanup); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/isac.h0000644000000000000500000000607111110524073017454 0ustar rootsrc/* $Id: isac.h,v 1.5 2006/03/06 12:52:07 keil Exp $ * * isac.h ISAC specific defines * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ /* privat isac data */ typedef struct isac_chip { u_char *mon_tx; u_char *mon_rx; int mon_txp; int mon_txc; int mon_rxp; struct arcofi_msg *arcofi_list; struct timer_list arcofitimer; wait_queue_head_t arcofi_wait; u_char arcofi_bc; u_char arcofi_state; u_char mocr; u_char adf2; } isac_chip_t; #define ISAC_TYPE_ISAC 0x0010 #define ISAC_TYPE_IPAC 0x0020 #define ISAC_TYPE_ISACSX 0x0040 #define ISAC_TYPE_IPACSX 0x0080 #define ISAC_TYPE_IOM1 0x0100 #define ISAC_TYPE_ARCOFI 0x1000 /* All Registers original Siemens Spec */ #define ISAC_MASK 0x20 #define ISAC_ISTA 0x20 #define ISAC_STAR 0x21 #define ISAC_CMDR 0x21 #define ISAC_EXIR 0x24 #define ISAC_ADF2 0x39 #define ISAC_SPCR 0x30 #define ISAC_ADF1 0x38 #define ISAC_CIR0 0x31 #define ISAC_CIX0 0x31 #define ISAC_CIR1 0x33 #define ISAC_CIX1 0x33 #define ISAC_STCR 0x37 #define ISAC_MODE 0x22 #define ISAC_RSTA 0x27 #define ISAC_RBCL 0x25 #define ISAC_RBCH 0x2A #define ISAC_TIMR 0x23 #define ISAC_SQXR 0x3b #define ISAC_MOSR 0x3a #define ISAC_MOCR 0x3a #define ISAC_MOR0 0x32 #define ISAC_MOX0 0x32 #define ISAC_MOR1 0x34 #define ISAC_MOX1 0x34 #define ISAC_RBCH_XAC 0x80 #define ISAC_CMD_TIM 0x0 #define ISAC_CMD_RS 0x1 #define ISAC_CMD_SCZ 0x4 #define ISAC_CMD_SSZ 0x2 #define ISAC_CMD_AR8 0x8 #define ISAC_CMD_AR10 0x9 #define ISAC_CMD_ARL 0xA #define ISAC_CMD_DUI 0xF #define ISAC_IND_RS 0x1 #define ISAC_IND_PU 0x7 #define ISAC_IND_DR 0x0 #define ISAC_IND_SD 0x2 #define ISAC_IND_DIS 0x3 #define ISAC_IND_EI 0x6 #define ISAC_IND_RSY 0x4 #define ISAC_IND_ARD 0x8 #define ISAC_IND_TI 0xA #define ISAC_IND_ATI 0xB #define ISAC_IND_AI8 0xC #define ISAC_IND_AI10 0xD #define ISAC_IND_DID 0xF /* the new ISACX */ #define ISACSX_MASK 0x60 #define ISACSX_ISTA 0x60 #define ISACSX_ISTA_ICD 0x01 #define ISACSX_ISTA_CIC 0x10 #define ISACSX_MASKD 0x20 #define ISACSX_ISTAD 0x20 #define ISACSX_ISTAD_XDU 0x04 #define ISACSX_ISTAD_XMR 0x08 #define ISACSX_ISTAD_XPR 0x10 #define ISACSX_ISTAD_RFO 0x20 #define ISACSX_ISTAD_RPF 0x40 #define ISACSX_ISTAD_RME 0x80 #define ISACSX_CMDRD 0x21 #define ISACSX_CMDRD_XRES 0x01 #define ISACSX_CMDRD_XME 0x02 #define ISACSX_CMDRD_XTF 0x08 #define ISACSX_CMDRD_RRES 0x40 #define ISACSX_CMDRD_RMC 0x80 #define ISACSX_MODED 0x22 #define ISACSX_RBCLD 0x26 #define ISACSX_RSTAD 0x28 #define ISACSX_RSTAD_RAB 0x10 #define ISACSX_RSTAD_CRC 0x20 #define ISACSX_RSTAD_RDO 0x40 #define ISACSX_RSTAD_VFR 0x80 #define ISACSX_CIR0 0x2e #define ISACSX_CIR0_CIC0 0x08 #define ISACSX_CIX0 0x2e #define ISACSX_TR_CONF0 0x30 #define ISACSX_TR_CONF2 0x32 /* interface for the isac module */ extern int mISDN_isac_init(channel_t *); extern void mISDN_isac_free(channel_t *); 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 *); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/isar.c0000644000000000000500000013652211110524073017473 0ustar rootsrc/* $Id: isar.c,v 1.22 2006/06/27 13:24:07 keil Exp $ * * isar.c ISAR (Siemens PSB 7110) specific routines * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include "layer1.h" #include "channel.h" #include "isar.h" #include "debug.h" #define DBG_LOADFIRM 0 #define DUMP_MBOXFRAME 2 #define MIN(a,b) ((aread_reg(bch->inst.privat, ISAR_HIA) & 1) && timeout) { udelay(1); timeout--; } if (!timeout) printk(KERN_WARNING "mISDN: ISAR waitforHIA timeout\n"); return(timeout); } int sendmsg(channel_t *bch, u_char his, u_char creg, u_char len, u_char *msg) { int i; if (!waitforHIA(bch, 4000)) return(0); #if DUMP_MBOXFRAME if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "sendmsg(%02x,%02x,%d)", his, creg, len); #endif bch->write_reg(bch->inst.privat, ISAR_CTRL_H, creg); bch->write_reg(bch->inst.privat, ISAR_CTRL_L, len); bch->write_reg(bch->inst.privat, ISAR_WADR, 0); if (msg && len) { bch->write_fifo(bch->inst.privat, msg, len); #if DUMP_MBOXFRAME>1 if (bch->debug & L1_DEB_HSCX_FIFO) { char *t; i = len; while (i>0) { t = bch->log; t += sprintf(t, "sendmbox cnt %d", len); mISDN_QuickHex(t, &msg[len-i], (i>64) ? 64:i); mISDN_debugprint(&bch->inst, bch->log); i -= 64; } } #endif } bch->write_reg(bch->inst.privat, ISAR_HIS, his); waitforHIA(bch, 10000); return(1); } /* Call only with IRQ disabled !!! */ inline void rcv_mbox(channel_t *bch, isar_reg_t *ireg, u_char *msg) { int i; bch->write_reg(bch->inst.privat, ISAR_RADR, 0); if (msg && ireg->clsb) { bch->read_fifo(bch->inst.privat, msg, ireg->clsb); #if DUMP_MBOXFRAME>1 if (bch->debug & L1_DEB_HSCX_FIFO) { char *t; i = ireg->clsb; while (i>0) { t = bch->log; t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb); mISDN_QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i); mISDN_debugprint(&bch->inst, bch->log); i -= 64; } } #endif } bch->write_reg(bch->inst.privat, ISAR_IIA, 0); } /* Call only with IRQ disabled !!! */ inline void get_irq_infos(channel_t *bch, isar_reg_t *ireg) { ireg->iis = bch->read_reg(bch->inst.privat, ISAR_IIS); ireg->cmsb = bch->read_reg(bch->inst.privat, ISAR_CTRL_H); ireg->clsb = bch->read_reg(bch->inst.privat, ISAR_CTRL_L); #if DUMP_MBOXFRAME if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "rcv_mbox(%02x,%02x,%d)", ireg->iis, ireg->cmsb, ireg->clsb); #endif } int waitrecmsg(channel_t *bch, u_char *len, u_char *msg, int maxdelay) { int timeout = 0; isar_hw_t *ih = bch->hw; while((!(bch->read_reg(bch->inst.privat, ISAR_IRQBIT) & ISAR_IRQSTA)) && (timeout++ < maxdelay)) udelay(1); if (timeout >= maxdelay) { printk(KERN_WARNING"isar recmsg IRQSTA timeout\n"); return(0); } get_irq_infos(bch, ih->reg); rcv_mbox(bch, ih->reg, msg); *len = ih->reg->clsb; return(1); } int ISARVersion(channel_t *bch, char *s) { int ver; u_char msg[] = ISAR_MSG_HWVER; u_char tmp[64]; u_char len; isar_hw_t *ih = bch->hw; int debug; // bch->cardmsg(bch->inst.privat, CARD_RESET, NULL); /* disable ISAR IRQ */ bch->write_reg(bch->inst.privat, ISAR_IRQBIT, 0); debug = bch->debug; bch->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO); if (!sendmsg(bch, ISAR_HIS_VNR, 0, 3, msg)) return(-1); if (!waitrecmsg(bch, &len, tmp, 100000)) return(-2); bch->debug = debug; if (ih->reg->iis == ISAR_IIS_VNR) { if (len == 1) { ver = tmp[0] & 0xf; printk(KERN_INFO "%s ISAR version %d\n", s, ver); return(ver); } return(-3); } return(-4); } int isar_load_firmware(channel_t *bch, u_char *buf, int size) { int ret, cnt, debug; u_char len, nom, noc; u_short sadr, left, *sp; u_char *p = buf; u_char *msg, *tmpmsg, *mp, tmp[64]; isar_hw_t *ih = bch->hw; u_long flags; struct {u_short sadr; u_short len; u_short d_key; } *blk_head; spin_lock_irqsave(bch->inst.hwlock, flags); #define BLK_HEAD_SIZE 6 if (1 != (ret = ISARVersion(bch, "Testing"))) { printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret); spin_unlock_irqrestore(bch->inst.hwlock, flags); return(1); } debug = bch->debug; #if DBG_LOADFIRM<2 bch->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO); #endif printk(KERN_DEBUG"isar_load_firmware buf %#lx\n", (u_long)buf); printk(KERN_DEBUG"isar_load_firmware size: %d\n", size); cnt = 0; /* disable ISAR IRQ */ bch->write_reg(bch->inst.privat, ISAR_IRQBIT, 0); if (!(msg = kmalloc(256, GFP_ATOMIC))) { printk(KERN_ERR"isar_load_firmware no buffer\n"); spin_unlock_irqrestore(bch->inst.hwlock, flags); return (1); } while (cnt < size) { blk_head = (void *)p; #ifdef __BIG_ENDIAN sadr = (blk_head->sadr & 0xff)*256 + blk_head->sadr/256; blk_head->sadr = sadr; sadr = (blk_head->len & 0xff)*256 + blk_head->len/256; blk_head->len = sadr; sadr = (blk_head->d_key & 0xff)*256 + blk_head->d_key/256; blk_head->d_key = sadr; #endif /* __BIG_ENDIAN */ cnt += BLK_HEAD_SIZE; p += BLK_HEAD_SIZE; printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n", blk_head->sadr, blk_head->len, blk_head->d_key & 0xff); sadr = blk_head->sadr; left = blk_head->len; if (cnt+left > size) { printk(KERN_ERR"isar: firmware size error have %d need %d bytes\n", size, cnt+left); ret = 1;goto reterror; } if (!sendmsg(bch, ISAR_HIS_DKEY, blk_head->d_key & 0xff, 0, NULL)) { printk(KERN_ERR"isar sendmsg dkey failed\n"); ret = 1;goto reterror; } if (!waitrecmsg(bch, &len, tmp, 100000)) { printk(KERN_ERR"isar waitrecmsg dkey failed\n"); ret = 1;goto reterror; } if ((ih->reg->iis != ISAR_IIS_DKEY) || ih->reg->cmsb || len) { printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n", ih->reg->iis, ih->reg->cmsb, len); ret = 1;goto reterror; } while (left>0) { noc = MIN(126, left); nom = 2*noc; mp = msg; *mp++ = sadr / 256; *mp++ = sadr % 256; left -= noc; *mp++ = noc; tmpmsg = p; p += nom; cnt += nom; nom += 3; sp = (u_short *)tmpmsg; #if DBG_LOADFIRM printk(KERN_DEBUG"isar: load %3d words at %04x\n", noc, sadr); #endif sadr += noc; while(noc) { #ifdef __BIG_ENDIAN *mp++ = *sp % 256; *mp++ = *sp / 256; #else *mp++ = *sp / 256; *mp++ = *sp % 256; #endif /* __BIG_ENDIAN */ sp++; noc--; } if (!sendmsg(bch, ISAR_HIS_FIRM, 0, nom, msg)) { printk(KERN_ERR"isar sendmsg prog failed\n"); ret = 1;goto reterror; } if (!waitrecmsg(bch, &len, tmp, 100000)) { printk(KERN_ERR"isar waitrecmsg prog failed\n"); ret = 1;goto reterror; } if ((ih->reg->iis != ISAR_IIS_FIRM) || ih->reg->cmsb || len) { printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n", ih->reg->iis, ih->reg->cmsb, len); ret = 1;goto reterror; } } printk(KERN_DEBUG"isar firmware block %5d words loaded\n", blk_head->len); } /* 10ms delay */ cnt = 10; while (cnt--) udelay(1000); msg[0] = 0xff; msg[1] = 0xfe; ih->reg->bstat = 0; if (!sendmsg(bch, ISAR_HIS_STDSP, 0, 2, msg)) { printk(KERN_ERR"isar sendmsg start dsp failed\n"); ret = 1;goto reterror; } if (!waitrecmsg(bch, &len, tmp, 100000)) { printk(KERN_ERR"isar waitrecmsg start dsp failed\n"); ret = 1;goto reterror; } if ((ih->reg->iis != ISAR_IIS_STDSP) || ih->reg->cmsb || len) { printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n", ih->reg->iis, ih->reg->cmsb, len); ret = 1;goto reterror; } else printk(KERN_DEBUG"isar start dsp success\n"); /* NORMAL mode entered */ /* Enable IRQs of ISAR */ bch->write_reg(bch->inst.privat, ISAR_IRQBIT, ISAR_IRQSTA); spin_unlock_irqrestore(bch->inst.hwlock, flags); cnt = 1000; /* max 1s */ while ((!ih->reg->bstat) && cnt) { mdelay(1); cnt--; } if (!cnt) { printk(KERN_ERR"isar no general status event received\n"); ret = 1; goto reterrflg; } else { printk(KERN_DEBUG"isar general status event %x\n", ih->reg->bstat); } /* 10ms delay */ cnt = 10; while (cnt--) mdelay(1); ih->reg->iis = 0; spin_lock_irqsave(bch->inst.hwlock, flags); if (!sendmsg(bch, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) { printk(KERN_ERR"isar sendmsg self tst failed\n"); ret = 1;goto reterror; } spin_unlock_irqrestore(bch->inst.hwlock, flags); cnt = 10000; /* max 100 ms */ while ((ih->reg->iis != ISAR_IIS_DIAG) && cnt) { udelay(10); cnt--; } mdelay(1); if (!cnt) { printk(KERN_ERR"isar no self tst response\n"); ret = 1;goto reterrflg; } if ((ih->reg->cmsb == ISAR_CTRL_STST) && (ih->reg->clsb == 1) && (ih->reg->par[0] == 0)) { printk(KERN_DEBUG"isar selftest OK\n"); } else { printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n", ih->reg->cmsb, ih->reg->clsb, ih->reg->par[0]); ret = 1;goto reterrflg; } spin_lock_irqsave(bch->inst.hwlock, flags); ih->reg->iis = 0; if (!sendmsg(bch, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) { printk(KERN_ERR"isar RQST SVN failed\n"); ret = 1;goto reterror; } spin_unlock_irqrestore(bch->inst.hwlock, flags); cnt = 30000; /* max 300 ms */ while ((ih->reg->iis != ISAR_IIS_DIAG) && cnt) { udelay(10); cnt--; } mdelay(1); if (!cnt) { printk(KERN_ERR"isar no SVN response\n"); ret = 1;goto reterrflg; } else { if ((ih->reg->cmsb == ISAR_CTRL_SWVER) && (ih->reg->clsb == 1)) printk(KERN_DEBUG"isar software version %#x\n", ih->reg->par[0]); else { printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n", ih->reg->cmsb, ih->reg->clsb, cnt); ret = 1;goto reterrflg; } } bch->debug = debug; spin_lock_irqsave(bch->inst.hwlock, flags); isar_setup(bch); spin_unlock_irqrestore(bch->inst.hwlock, flags); bch->inst.obj->own_ctrl(&bch->inst, MGR_LOADFIRM | CONFIRM, NULL); ret = 0; reterrflg: spin_lock_irqsave(bch->inst.hwlock, flags); reterror: bch->debug = debug; if (ret) /* disable ISAR IRQ */ bch->write_reg(bch->inst.privat, ISAR_IRQBIT, 0); spin_unlock_irqrestore(bch->inst.hwlock, flags); kfree(msg); return(ret); } #ifdef OBSOLETE #define B_LL_READY 8 #define B_LL_NOCARRIER 9 #define B_LL_CONNECT 10 #define B_LL_OK 11 #define B_LL_FCERROR 12 #define B_TOUCH_TONE 13 #endif static inline void deliver_status(channel_t *bch, int status) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "HL->LL FAXIND %x", status); mISDN_queue_data(&bch->inst, FLG_MSG_UP, PH_STATUS | INDICATION, status, 0, NULL, 0); } #ifdef OBSOLETE static void isar_bh(channel_t *bch) { int tt; if (test_and_clear_bit(B_LL_READY, &bch->event)) deliver_status(bch, HW_MOD_READY); if (test_and_clear_bit(B_LL_NOCARRIER, &bch->event)) deliver_status(bch, HW_MOD_NOCARR); if (test_and_clear_bit(B_LL_CONNECT, &bch->event)) deliver_status(bch, HW_MOD_CONNECT); if (test_and_clear_bit(B_LL_OK, &bch->event)) deliver_status(bch, HW_MOD_OK); if (test_and_clear_bit(B_LL_FCERROR, &bch->event)) deliver_status(bch, HW_MOD_FCERROR); if (test_and_clear_bit(B_TOUCH_TONE, &bch->event)) { tt = bch->conmsg[0] | 0x30; if (tt == 0x3e) tt = '*'; else if (tt == 0x3f) tt = '#'; else if (tt > '9') tt += 7; tt |= DTMF_TONE_VAL; mISDN_queue_data(&bch->inst, FLG_MSG_UP, PH_CONTROL | INDICATION, 0, sizeof(int), &tt, 0); } } #endif static inline void isar_rcv_frame(channel_t *bch) { u_char *ptr; struct sk_buff *skb; isar_hw_t *ih = bch->hw; if (!ih->reg->clsb) { mISDN_debugprint(&bch->inst, "isar zero len frame"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); return; } switch (bch->state) { case ISDN_PID_NONE: mISDN_debugprint(&bch->inst, "isar protocol 0 spurious IIS_RDATA %x/%x/%x", ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); printk(KERN_WARNING"isar protocol 0 spurious IIS_RDATA %x/%x/%x\n", ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; case ISDN_PID_L1_B_64TRANS: case ISDN_PID_L2_B_TRANSDTMF: case ISDN_PID_L1_B_MODEM_ASYNC: if (!bch->rx_skb) { bch->rx_skb = alloc_stack_skb(ih->reg->clsb, bch->up_headerlen); if (unlikely(!bch->rx_skb)) { printk(KERN_WARNING "mISDN: skb out of memory\n"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; } } rcv_mbox(bch, ih->reg, (u_char *)skb_put(bch->rx_skb, ih->reg->clsb)); queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, bch->rx_skb); bch->rx_skb = NULL; break; case ISDN_PID_L1_B_64HDLC: if (!bch->rx_skb) { bch->rx_skb = alloc_stack_skb(bch->maxlen + 2, bch->up_headerlen); if (unlikely(!bch->rx_skb)) { printk(KERN_WARNING "mISDN: skb out of memory\n"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; } } if ((bch->rx_skb->len + ih->reg->clsb) > (bch->maxlen + 2)) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar_rcv_frame: incoming packet too large"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); skb_trim(bch->rx_skb, 0); break; } if (ih->reg->cmsb & HDLC_ERROR) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar frame error %x len %d", ih->reg->cmsb, ih->reg->clsb); #ifdef ERROR_STATISTIC if (ih->reg->cmsb & HDLC_ERR_RER) bch->err_inv++; if (ih->reg->cmsb & HDLC_ERR_CER) bch->err_crc++; #endif skb_trim(bch->rx_skb, 0); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; } if (ih->reg->cmsb & HDLC_FSD) skb_trim(bch->rx_skb, 0); ptr = skb_put(bch->rx_skb, ih->reg->clsb); rcv_mbox(bch, ih->reg, ptr); if (ih->reg->cmsb & HDLC_FED) { if (bch->rx_skb->len < 3) { /* last 2 bytes are the FCS */ if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar frame to short %d", bch->rx_skb->len); skb_trim(bch->rx_skb, 0); break; } skb_trim(bch->rx_skb, bch->rx_skb->len - 2); 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; } queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, skb); } break; case ISDN_PID_L1_B_T30FAX: if (ih->state != STFAX_ACTIV) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar_rcv_frame: not ACTIV"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); if (bch->rx_skb) skb_trim(bch->rx_skb, 0); break; } if (ih->cmd == PCTRL_CMD_FRM) { if (!bch->rx_skb) { bch->rx_skb = alloc_stack_skb(ih->reg->clsb, bch->up_headerlen); if (unlikely(!bch->rx_skb)) { printk(KERN_WARNING "mISDN: skb out of memory\n"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; } } rcv_mbox(bch, ih->reg, skb_put(bch->rx_skb, ih->reg->clsb)); if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "isar_rcv_frame: %d", bch->rx_skb->len); if (ih->reg->cmsb & SART_NMD) { /* ABORT */ if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar_rcv_frame: no more data"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); ih->state = STFAX_ESCAPE; // set_skb_flag(skb, DF_NOMOREDATA); } queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, bch->rx_skb); bch->rx_skb = NULL; if (ih->reg->cmsb & SART_NMD) deliver_status(bch, HW_MOD_NOCARR); break; } if (ih->cmd != PCTRL_CMD_FRH) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar_rcv_frame: unknown fax mode %x", ih->cmd); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); if (bch->rx_skb) skb_trim(bch->rx_skb, 0); break; } /* PCTRL_CMD_FRH */ if (!bch->rx_skb) { bch->rx_skb = alloc_stack_skb(bch->maxlen + 2, bch->up_headerlen); if (unlikely(!bch->rx_skb)) { printk(KERN_WARNING "mISDN: skb out of memory\n"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; } } if ((bch->rx_skb->len + ih->reg->clsb) > (bch->maxlen + 2)) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar_rcv_frame: incoming packet too large"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); skb_trim(bch->rx_skb, 0); break; } else if (ih->reg->cmsb & HDLC_ERROR) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar frame error %x len %d", ih->reg->cmsb, ih->reg->clsb); skb_trim(bch->rx_skb, 0); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; } if (ih->reg->cmsb & HDLC_FSD) skb_trim(bch->rx_skb, 0); ptr = skb_put(bch->rx_skb, ih->reg->clsb); rcv_mbox(bch, ih->reg, ptr); if (ih->reg->cmsb & HDLC_FED) { if (bch->rx_skb->len < 3) { /* last 2 bytes are the FCS */ if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar frame to short %d", bch->rx_skb->len); skb_trim(bch->rx_skb, 0); break; } skb_trim(bch->rx_skb, bch->rx_skb->len - 2); 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; } queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, skb); } if (ih->reg->cmsb & SART_NMD) { /* ABORT */ if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar_rcv_frame: no more data"); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); if (bch->rx_skb) skb_trim(bch->rx_skb, 0); sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); ih->state = STFAX_ESCAPE; deliver_status(bch, HW_MOD_NOCARR); } break; default: printk(KERN_ERR"isar_rcv_frame protocol (%x)error\n", bch->state); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; } } void isar_fill_fifo(channel_t *bch) { isar_hw_t *ih = bch->hw; int count; u_char msb; u_char *ptr; if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) mISDN_debugprint(&bch->inst, "%s", __FUNCTION__); if (!bch->tx_skb) return; count = bch->tx_skb->len - bch->tx_idx; if (count <= 0) return; if (!(ih->reg->bstat & (ih->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2))) return; if (count > ih->mml) { msb = 0; count = ih->mml; } else { msb = HDLC_FED; } ptr = bch->tx_skb->data + bch->tx_idx; if (!bch->tx_idx) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "frame start"); if ((bch->state == ISDN_PID_L1_B_T30FAX) && (ih->cmd == PCTRL_CMD_FTH)) { if (count > 1) { if ((ptr[0]== 0xff) && (ptr[1] == 0x13)) { /* last frame */ test_and_set_bit(FLG_LASTDATA, &bch->Flags); if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "set LASTDATA"); if (msb == HDLC_FED) test_and_set_bit(FLG_DLEETX, &bch->Flags); } } } msb |= HDLC_FST; } bch->tx_idx += count; switch (bch->state) { case ISDN_PID_NONE: printk(KERN_ERR "%s: wrong protocol 0\n", __FUNCTION__); break; case ISDN_PID_L1_B_64TRANS: case ISDN_PID_L2_B_TRANSDTMF: case ISDN_PID_L1_B_MODEM_ASYNC: sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, 0, count, ptr); break; case ISDN_PID_L1_B_64HDLC: sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, msb, count, ptr); break; case ISDN_PID_L1_B_T30FAX: if (ih->state != STFAX_ACTIV) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "%s: not ACTIV", __FUNCTION__); } else if (ih->cmd == PCTRL_CMD_FTH) { sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, msb, count, ptr); } else if (ih->cmd == PCTRL_CMD_FTM) { sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, 0, count, ptr); } else { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "%s: not FTH/FTM", __FUNCTION__); } break; default: if (bch->debug) mISDN_debugprint(&bch->inst, "%s: protocol(%x) error", __FUNCTION__, bch->state); printk(KERN_ERR "%s: protocol(%x) error\n", __FUNCTION__, bch->state); break; } } inline channel_t *sel_bch_isar(channel_t *bch, u_char dpath) { if ((!dpath) || (dpath == 3)) return(NULL); if (((isar_hw_t *)bch[0].hw)->dpath == dpath) return(&bch[0]); if (((isar_hw_t *)bch[1].hw)->dpath == dpath) return(&bch[1]); return(NULL); } inline void send_frames(channel_t *bch) { isar_hw_t *ih = bch->hw; if (bch->tx_skb && (bch->tx_skb->len > bch->tx_idx)) { isar_fill_fifo(bch); } else { if (bch->state == ISDN_PID_L1_B_T30FAX) { if (ih->cmd == PCTRL_CMD_FTH) { if (test_bit(FLG_LASTDATA, &bch->Flags)) { printk(KERN_WARNING "set NMD_DATA\n"); test_and_set_bit(FLG_NMD_DATA, &bch->Flags); } } else if (ih->cmd == PCTRL_CMD_FTM) { if (test_bit(FLG_DLEETX, &bch->Flags)) { test_and_set_bit(FLG_LASTDATA, &bch->Flags); test_and_set_bit(FLG_NMD_DATA, &bch->Flags); } } } if (bch->tx_skb) dev_kfree_skb(bch->tx_skb); bch->tx_idx = 0; 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); isar_fill_fifo(bch); } else { printk(KERN_WARNING "hdlc tx irq TX_NEXT without skb\n"); test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } } else { bch->tx_skb = NULL; if (test_and_clear_bit(FLG_DLEETX, &bch->Flags)) { if (test_and_clear_bit(FLG_LASTDATA, &bch->Flags)) { if (test_and_clear_bit(FLG_NMD_DATA, &bch->Flags)) { u_char dummy = 0; sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA, 0x01, 1, &dummy); } test_and_set_bit(FLG_LL_OK, &bch->Flags); } else { deliver_status(bch, HW_MOD_CONNECT); } } test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); // bch_sched_event(bch, B_XMTBUFREADY); } } } inline void check_send(channel_t *bch, u_char rdm) { channel_t *bc; if (rdm & BSTAT_RDM1) { if ((bc = sel_bch_isar(bch, 1))) { if (test_bit(FLG_ACTIVE, &bc->Flags)) { send_frames(bc); } } } if (rdm & BSTAT_RDM2) { if ((bc = sel_bch_isar(bch, 2))) { if (test_bit(FLG_ACTIVE, &bc->Flags)) { send_frames(bc); } } } } const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", "300", "600", "1200", "2400", "4800", "7200", "9600nt", "9600t", "12000", "14400", "WRONG"}; const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"}; static void isar_pump_status_rsp(channel_t *bch, isar_reg_t *ireg) { isar_hw_t *ih = bch->hw; u_char ril = ireg->par[0]; u_char rim; if (!test_and_clear_bit(ISAR_RATE_REQ, &ireg->Flags)) return; if (ril > 14) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "wrong pstrsp ril=%d",ril); ril = 15; } switch(ireg->par[1]) { case 0: rim = 0; break; case 0x20: rim = 2; break; case 0x40: rim = 3; break; case 0x41: rim = 4; break; case 0x51: rim = 5; break; case 0x61: rim = 6; break; case 0x71: rim = 7; break; case 0x82: rim = 8; break; case 0x92: rim = 9; break; case 0xa2: rim = 10; break; default: rim = 1; break; } sprintf(ih->conmsg,"%s %s", dmril[ril], dmrim[rim]); if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump strsp %s %s", ih->conmsg); } static void isar_pump_statev_modem(channel_t *bch, u_char devt) { isar_hw_t *ih = bch->hw; u_char dps = SET_DPS(ih->dpath); switch(devt) { case PSEV_10MS_TIMER: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev TIMER"); break; case PSEV_CON_ON: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev CONNECT"); deliver_status(bch, HW_MOD_CONNECT); break; case PSEV_CON_OFF: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev NO CONNECT"); sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); deliver_status(bch, HW_MOD_NOCARR); break; case PSEV_V24_OFF: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev V24 OFF"); break; case PSEV_CTS_ON: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev CTS ON"); break; case PSEV_CTS_OFF: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev CTS OFF"); break; case PSEV_DCD_ON: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev CARRIER ON"); test_and_set_bit(ISAR_RATE_REQ, &ih->reg->Flags); sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); break; case PSEV_DCD_OFF: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev CARRIER OFF"); break; case PSEV_DSR_ON: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev DSR ON"); break; case PSEV_DSR_OFF: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev DSR_OFF"); break; case PSEV_REM_RET: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev REMOTE RETRAIN"); break; case PSEV_REM_REN: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev REMOTE RENEGOTIATE"); break; case PSEV_GSTN_CLR: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev GSTN CLEAR", devt); break; default: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "unknown pump stev %x", devt); break; } } static void isar_pump_statev_fax(channel_t *bch, u_char devt) { isar_hw_t *ih = bch->hw; u_char dps = SET_DPS(ih->dpath); u_char p1; switch(devt) { case PSEV_10MS_TIMER: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev TIMER"); break; case PSEV_RSP_READY: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev RSP_READY"); ih->state = STFAX_READY; deliver_status(bch, HW_MOD_READY); // if (test_bit(BC_FLG_ORIG, &bch->Flags)) { // isar_pump_cmd(bch, HW_MOD_FRH, 3); // } else { // isar_pump_cmd(bch, HW_MOD_FTH, 3); // } break; case PSEV_LINE_TX_H: if (ih->state == STFAX_LINE) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev LINE_TX_H"); ih->state = STFAX_CONT; sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); } else { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "pump stev LINE_TX_H wrong st %x", ih->state); } break; case PSEV_LINE_RX_H: if (ih->state == STFAX_LINE) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev LINE_RX_H"); ih->state = STFAX_CONT; sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); } else { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "pump stev LINE_RX_H wrong st %x", ih->state); } break; case PSEV_LINE_TX_B: if (ih->state == STFAX_LINE) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev LINE_TX_B"); ih->state = STFAX_CONT; sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); } else { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "pump stev LINE_TX_B wrong st %x", ih->state); } break; case PSEV_LINE_RX_B: if (ih->state == STFAX_LINE) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev LINE_RX_B"); ih->state = STFAX_CONT; sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL); } else { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "pump stev LINE_RX_B wrong st %x", ih->state); } break; case PSEV_RSP_CONN: if (ih->state == STFAX_CONT) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev RSP_CONN"); ih->state = STFAX_ACTIV; test_and_set_bit(ISAR_RATE_REQ, &ih->reg->Flags); sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); if (ih->cmd == PCTRL_CMD_FTH) { int delay = (ih->mod == 3) ? 1000 : 200; /* 1s (200 ms) Flags before data */ if (test_and_set_bit(FLG_FTI_RUN, &bch->Flags)) del_timer(&ih->ftimer); ih->ftimer.expires = jiffies + ((delay * HZ)/1000); test_and_set_bit(FLG_LL_CONN, &bch->Flags); add_timer(&ih->ftimer); } else { deliver_status(bch, HW_MOD_CONNECT); } } else { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "pump stev RSP_CONN wrong st %x", ih->state); } break; case PSEV_FLAGS_DET: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev FLAGS_DET"); break; case PSEV_RSP_DISC: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev RSP_DISC state(%d)", ih->state); if (ih->state == STFAX_ESCAPE) { p1 = 5; switch(ih->newcmd) { case 0: ih->state = STFAX_READY; break; case PCTRL_CMD_FTM: p1 = 2; case PCTRL_CMD_FTH: sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_SILON, 1, &p1); ih->state = STFAX_SILDET; break; case PCTRL_CMD_FRH: case PCTRL_CMD_FRM: p1 = ih->mod = ih->newmod; ih->newmod = 0; ih->cmd = ih->newcmd; ih->newcmd = 0; sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, ih->cmd, 1, &p1); ih->state = STFAX_LINE; ih->try_mod = 3; break; default: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "RSP_DISC unknown newcmd %x", ih->newcmd); break; } } else if (ih->state == STFAX_ACTIV) { if (test_and_clear_bit(FLG_LL_OK, &bch->Flags)) { deliver_status(bch, HW_MOD_OK); } else if (ih->cmd == PCTRL_CMD_FRM) { deliver_status(bch, HW_MOD_NOCARR); } else { deliver_status(bch, HW_MOD_FCERROR); } ih->state = STFAX_READY; } else if (ih->state != STFAX_SILDET) { // ignore in STFAX_SILDET ih->state = STFAX_READY; deliver_status(bch, HW_MOD_FCERROR); } break; case PSEV_RSP_SILDET: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev RSP_SILDET"); if (ih->state == STFAX_SILDET) { p1 = ih->mod = ih->newmod; ih->newmod = 0; ih->cmd = ih->newcmd; ih->newcmd = 0; sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, ih->cmd, 1, &p1); ih->state = STFAX_LINE; ih->try_mod = 3; } break; case PSEV_RSP_SILOFF: if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev RSP_SILOFF"); break; case PSEV_RSP_FCERR: if (ih->state == STFAX_LINE) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev RSP_FCERR try %d", ih->try_mod); if (ih->try_mod--) { sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, ih->cmd, 1, &ih->mod); break; } } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "pump stev RSP_FCERR"); ih->state = STFAX_ESCAPE; sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL); deliver_status(bch, HW_MOD_FCERROR); break; default: break; } } static char debbuf[128]; void isar_int_main(channel_t *bch) { isar_hw_t *ih = bch->hw; channel_t *bc; get_irq_infos(bch, ih->reg); switch (ih->reg->iis & ISAR_IIS_MSCMSD) { case ISAR_IIS_RDATA: if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) { isar_rcv_frame(bc); } else { mISDN_debugprint(&bch->inst, "isar spurious IIS_RDATA %x/%x/%x", ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); } break; case ISAR_IIS_GSTEV: bch->write_reg(bch->inst.privat, ISAR_IIA, 0); ih->reg->bstat |= ih->reg->cmsb; check_send(bch, ih->reg->cmsb); break; case ISAR_IIS_BSTEV: #ifdef ERROR_STATISTIC if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) { if (ih->reg->cmsb == BSTEV_TBO) bc->err_tx++; if (ih->reg->cmsb == BSTEV_RBO) bc->err_rdo++; } #endif if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "Buffer STEV dpath%d msb(%x)", ih->reg->iis>>6, ih->reg->cmsb); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); break; case ISAR_IIS_PSTEV: if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) { rcv_mbox(bc, ih->reg, (u_char *)ih->reg->par); if (bc->state == ISDN_PID_L1_B_MODEM_ASYNC) { isar_pump_statev_modem(bc, ih->reg->cmsb); } else if (bc->state == ISDN_PID_L1_B_T30FAX) { isar_pump_statev_fax(bc, ih->reg->cmsb); } else if (bc->state == ISDN_PID_L2_B_TRANSDTMF) { int tt; tt = ih->reg->cmsb | 0x30; if (tt == 0x3e) tt = '*'; else if (tt == 0x3f) tt = '#'; else if (tt > '9') tt += 7; tt |= DTMF_TONE_VAL; mISDN_queue_data(&bch->inst, FLG_MSG_UP, PH_CONTROL | INDICATION, 0, sizeof(int), &tt, 0); } else { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "isar IIS_PSTEV pmode %d stat %x", bc->state, ih->reg->cmsb); } } else { mISDN_debugprint(&bch->inst, "isar spurious IIS_PSTEV %x/%x/%x", ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); } break; case ISAR_IIS_PSTRSP: if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) { rcv_mbox(bc, ih->reg, (u_char *)ih->reg->par); isar_pump_status_rsp(bc, ih->reg); } else { mISDN_debugprint(&bch->inst, "isar spurious IIS_PSTRSP %x/%x/%x", ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); bch->write_reg(bch->inst.privat, ISAR_IIA, 0); } break; case ISAR_IIS_DIAG: case ISAR_IIS_BSTRSP: case ISAR_IIS_IOM2RSP: rcv_mbox(bch, ih->reg, (u_char *)ih->reg->par); if ((bch->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO)) == L1_DEB_HSCX) { u_char *tp=debbuf; tp += sprintf(debbuf, "msg iis(%x) msb(%x)", ih->reg->iis, ih->reg->cmsb); mISDN_QuickHex(tp, (u_char *)ih->reg->par, ih->reg->clsb); mISDN_debugprint(&bch->inst, debbuf); } break; case ISAR_IIS_INVMSG: rcv_mbox(bch, ih->reg, debbuf); if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "invalid msg his:%x", ih->reg->cmsb); break; default: rcv_mbox(bch, ih->reg, debbuf); if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "unhandled msg iis(%x) ctrl(%x/%x)", ih->reg->iis, ih->reg->cmsb, ih->reg->clsb); break; } } static void ftimer_handler(channel_t *bch) { if (bch->debug) mISDN_debugprint(&bch->inst, "ftimer flags %04x", bch->Flags); test_and_clear_bit(FLG_FTI_RUN, &bch->Flags); if (test_and_clear_bit(FLG_LL_CONN, &bch->Flags)) { deliver_status(bch, HW_MOD_CONNECT); } } static void setup_pump(channel_t *bch) { isar_hw_t *ih = bch->hw; u_char dps = SET_DPS(ih->dpath); u_char ctrl, param[6]; switch (bch->state) { case ISDN_PID_NONE: case ISDN_PID_L1_B_64TRANS: case ISDN_PID_L1_B_64HDLC: sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL); break; case ISDN_PID_L2_B_TRANSDTMF: if (test_bit(FLG_DTMFSEND, &bch->Flags)) { param[0] = 5; /* TOA 5 db */ sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_DTMF_TRANS, 1, param); } else { param[0] = 40; /* REL -46 dbm */ sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_DTMF, 1, param); } case ISDN_PID_L1_B_MODEM_ASYNC: ctrl = PMOD_DATAMODEM; if (test_bit(FLG_ORIGIN, &bch->Flags)) { ctrl |= PCTRL_ORIG; param[5] = PV32P6_CTN; } else { param[5] = PV32P6_ATN; } param[0] = 6; /* 6 db */ param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B | PV32P2_V22C | PV32P2_V21 | PV32P2_BEL; param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B; param[3] = PV32P4_UT144; param[4] = PV32P5_UT144; sendmsg(bch, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param); break; case ISDN_PID_L1_B_T30FAX: ctrl = PMOD_FAX; if (test_bit(FLG_ORIGIN, &bch->Flags)) { ctrl |= PCTRL_ORIG; param[1] = PFAXP2_CTN; } else { param[1] = PFAXP2_ATN; } param[0] = 6; /* 6 db */ sendmsg(bch, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param); ih->state = STFAX_NULL; ih->newcmd = 0; ih->newmod = 0; test_and_set_bit(FLG_FTI_RUN, &bch->Flags); break; } udelay(1000); sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); udelay(1000); } static void setup_sart(channel_t *bch) { isar_hw_t *ih = bch->hw; u_char dps = SET_DPS(ih->dpath); u_char ctrl, param[2]; switch (bch->state) { case ISDN_PID_NONE: sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL); break; case ISDN_PID_L1_B_64TRANS: case ISDN_PID_L2_B_TRANSDTMF: sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0"); break; case ISDN_PID_L1_B_64HDLC: case ISDN_PID_L1_B_T30FAX: param[0] = 0; sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, param); break; case ISDN_PID_L1_B_MODEM_ASYNC: ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; param[0] = S_P1_CHS_8; param[1] = S_P2_BFT_DEF; sendmsg(bch, dps | ISAR_HIS_SARTCFG, ctrl, 2, param); break; } udelay(1000); sendmsg(bch, dps | ISAR_HIS_BSTREQ, 0, 0, NULL); udelay(1000); } static void setup_iom2(channel_t *bch) { isar_hw_t *ih = bch->hw; u_char dps = SET_DPS(ih->dpath); u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD,0,0,0,0}; if (bch->channel) msg[1] = msg[3] = 1; switch (bch->state) { case ISDN_PID_NONE: cmsb = 0; /* dummy slot */ msg[1] = msg[3] = ih->dpath + 2; break; case ISDN_PID_L1_B_64TRANS: case ISDN_PID_L1_B_64HDLC: break; case ISDN_PID_L1_B_MODEM_ASYNC: case ISDN_PID_L1_B_T30FAX: cmsb |= IOM_CTRL_RCV; case ISDN_PID_L2_B_TRANSDTMF: if (test_bit(FLG_DTMFSEND, &bch->Flags)) cmsb |= IOM_CTRL_RCV; cmsb |= IOM_CTRL_ALAW; break; } sendmsg(bch, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg); udelay(1000); sendmsg(bch, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL); udelay(1000); } static int modeisar(channel_t *bch, int channel, u_int bprotocol, u_char *param) { isar_hw_t *ih = bch->hw; /* Here we are selecting the best datapath for requested protocol */ if(bch->state == ISDN_PID_NONE) { /* New Setup */ bch->channel = channel; switch (bprotocol) { case ISDN_PID_NONE: /* init */ if (!ih->dpath) /* no init for dpath 0 */ return(0); break; case ISDN_PID_L1_B_64TRANS: case ISDN_PID_L1_B_64HDLC: /* best is datapath 2 */ if (!test_and_set_bit(ISAR_DP2_USE, &ih->reg->Flags)) ih->dpath = 2; else if (!test_and_set_bit(ISAR_DP1_USE, &ih->reg->Flags)) ih->dpath = 1; else { printk(KERN_WARNING"isar modeisar both pathes in use\n"); return(-EINVAL); } break; case ISDN_PID_L1_B_MODEM_ASYNC: case ISDN_PID_L1_B_T30FAX: case ISDN_PID_L2_B_TRANSDTMF: /* only datapath 1 */ if (!test_and_set_bit(ISAR_DP1_USE, &ih->reg->Flags)) ih->dpath = 1; else { printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n"); mISDN_debugprint(&bch->inst, "isar modeisar analog funktions only with DP1"); return(-EBUSY); } break; } } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "isar dp%d protocol %x->%x ichan %d", ih->dpath, bch->state, bprotocol, channel); bch->state = bprotocol; setup_pump(bch); setup_iom2(bch); setup_sart(bch); if (bch->state == ISDN_PID_NONE) { /* Clear resources */ if (ih->dpath == 1) test_and_clear_bit(ISAR_DP1_USE, &ih->reg->Flags); else if (ih->dpath == 2) test_and_clear_bit(ISAR_DP2_USE, &ih->reg->Flags); ih->dpath = 0; } return(0); } static void isar_pump_cmd(channel_t *bch, int cmd, u_char para) { isar_hw_t *ih = bch->hw; u_char dps = SET_DPS(ih->dpath); u_char ctrl = 0, nom = 0, p1 = 0; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "isar_pump_cmd %x/%x state(%x)", cmd, para, ih->state); switch(cmd) { case HW_MOD_FTM: if (ih->state == STFAX_READY) { p1 = para; ctrl = PCTRL_CMD_FTM; nom = 1; ih->state = STFAX_LINE; ih->cmd = ctrl; ih->mod = para; ih->newmod = 0; ih->newcmd = 0; ih->try_mod = 3; } else if ((ih->state == STFAX_ACTIV) && (ih->cmd == PCTRL_CMD_FTM) && (ih->mod == para)) { deliver_status(bch, HW_MOD_CONNECT); } else { ih->newmod = para; ih->newcmd = PCTRL_CMD_FTM; nom = 0; ctrl = PCTRL_CMD_ESC; ih->state = STFAX_ESCAPE; } break; case HW_MOD_FTH: if (ih->state == STFAX_READY) { p1 = para; ctrl = PCTRL_CMD_FTH; nom = 1; ih->state = STFAX_LINE; ih->cmd = ctrl; ih->mod = para; ih->newmod = 0; ih->newcmd = 0; ih->try_mod = 3; } else if ((ih->state == STFAX_ACTIV) && (ih->cmd == PCTRL_CMD_FTH) && (ih->mod == para)) { deliver_status(bch, HW_MOD_CONNECT); } else { ih->newmod = para; ih->newcmd = PCTRL_CMD_FTH; nom = 0; ctrl = PCTRL_CMD_ESC; ih->state = STFAX_ESCAPE; } break; case HW_MOD_FRM: if (ih->state == STFAX_READY) { p1 = para; ctrl = PCTRL_CMD_FRM; nom = 1; ih->state = STFAX_LINE; ih->cmd = ctrl; ih->mod = para; ih->newmod = 0; ih->newcmd = 0; ih->try_mod = 3; } else if ((ih->state == STFAX_ACTIV) && (ih->cmd == PCTRL_CMD_FRM) && (ih->mod == para)) { deliver_status(bch, HW_MOD_CONNECT); } else { ih->newmod = para; ih->newcmd = PCTRL_CMD_FRM; nom = 0; ctrl = PCTRL_CMD_ESC; ih->state = STFAX_ESCAPE; } break; case HW_MOD_FRH: if (ih->state == STFAX_READY) { p1 = para; ctrl = PCTRL_CMD_FRH; nom = 1; ih->state = STFAX_LINE; ih->cmd = ctrl; ih->mod = para; ih->newmod = 0; ih->newcmd = 0; ih->try_mod = 3; } else if ((ih->state == STFAX_ACTIV) && (ih->cmd == PCTRL_CMD_FRH) && (ih->mod == para)) { deliver_status(bch, HW_MOD_CONNECT); } else { ih->newmod = para; ih->newcmd = PCTRL_CMD_FRH; nom = 0; ctrl = PCTRL_CMD_ESC; ih->state = STFAX_ESCAPE; } break; case PCTRL_CMD_TDTMF: p1 = para; nom = 1; ctrl = PCTRL_CMD_TDTMF; break; } if (ctrl) sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1); } void isar_setup(channel_t *bch) { u_char msg; int i; /* Dpath 1, 2 */ msg = 61; for (i=0; i<2; i++) { isar_hw_t *ih = bch[i].hw; /* Buffer Config */ sendmsg(bch, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | ISAR_HIS_P12CFG, 4, 1, &msg); ih->mml = msg; bch[i].state = 0; ih->dpath = i + 1; modeisar(&bch[i], i, 0, NULL); } } int isar_down(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *bch = container_of(inst, channel_t, inst); int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == PH_DATA_REQ) || (hh->prim == (DL_DATA | REQUEST))) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(bch, hh->dinfo, skb); if (ret > 0) { /* direct TX */ isar_fill_fifo(bch); ret = 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)) { u_int bp = bch->inst.pid.protocol[1]; if (bch->inst.pid.global == 1) test_and_set_bit(FLG_ORIGIN, &bch->Flags); if ((bp == ISDN_PID_L1_B_64TRANS) && (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANSDTMF)) bp = ISDN_PID_L2_B_TRANSDTMF; spin_lock_irqsave(inst->hwlock, flags); ret = modeisar(bch, bch->channel, bp, NULL); spin_unlock_irqrestore(inst->hwlock, flags); } 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)))) { 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; } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; } bch->tx_idx = 0; if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); test_and_clear_bit(FLG_L2DATA, &bch->Flags); modeisar(bch, bch->channel, 0, NULL); test_and_clear_bit(FLG_ACTIVE, &bch->Flags); spin_unlock_irqrestore(inst->hwlock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) if (!mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, 0, skb)) return(0); } else if (hh->prim == (PH_CONTROL | REQUEST)) { int *val; int len; val = (int *)skb->data; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "PH_CONTROL | REQUEST %x/%x", hh->dinfo, *val); if ((hh->dinfo == 0) && ((*val & ~DTMF_TONE_MASK) == DTMF_TONE_VAL)) { if (bch->state == ISDN_PID_L2_B_TRANSDTMF) { char tt = *val & DTMF_TONE_MASK; if (tt == '*') tt = 0x1e; else if (tt == '#') tt = 0x1f; else if (tt > '9') tt -= 7; tt &= 0x1f; spin_lock_irqsave(inst->hwlock, flags); isar_pump_cmd(bch, PCTRL_CMD_TDTMF, tt); spin_unlock_irqrestore(inst->hwlock, flags); skb_trim(skb, 0); if (!mISDN_queueup_newhead(inst, 0, PH_CONTROL | CONFIRM, 0, skb)) return(0); } else { printk(KERN_WARNING "isar_down TOUCH_TONE_SEND wrong protocol %x\n", bch->state); return(-EINVAL); } } else if ((hh->dinfo == HW_MOD_FRM) || (hh->dinfo == HW_MOD_FRH) || (hh->dinfo == HW_MOD_FTM) || (hh->dinfo == HW_MOD_FTH)) { u_int i; for (i=0; i i) && test_bit(FLG_INITIALIZED, &bch->Flags)) { printk(KERN_WARNING "isar: new mod\n"); isar_pump_cmd(bch, hh->dinfo, *val); ret = 0; } else { int_errtxt("wrong modulation"); /* wrong modulation or not activ */ // TODO ret = -EINVAL; } } else if (hh->dinfo == HW_MOD_LASTDATA) { test_and_set_bit(FLG_DLEETX, &bch->Flags); } else if (hh->dinfo == HW_FIRM_START) { firmwaresize = *val; if (!(firmware = vmalloc(firmwaresize))) { firmwaresize = 0; return(-ENOMEM); } fw_p = firmware; skb_trim(skb, 0); if(!mISDN_queueup_newhead(inst, 0, PH_CONTROL | CONFIRM, 0, skb)) return(0); } else if (hh->dinfo == HW_FIRM_DATA) { len = *val++; if (!fw_p) return(-EINVAL); memcpy(fw_p, val, len); fw_p += len; skb_trim(skb, 0); if(!mISDN_queueup_newhead(inst, 0, PH_CONTROL | CONFIRM, 0, skb)) return(0); } else if (hh->dinfo == HW_FIRM_END) { if (!fw_p) return(-EINVAL); len = (fw_p - firmware) & 0xffffffff; if (len == firmwaresize) ret = isar_load_firmware(bch, firmware, firmwaresize); else { printk(KERN_WARNING "wrong firmware size %d/%d\n", len, firmwaresize); ret = -EINVAL; } vfree(firmware); fw_p = firmware = NULL; firmwaresize = 0; skb_trim(skb, 0); if(!mISDN_queueup_newhead(inst, 0, PH_CONTROL | CONFIRM, 0, skb)) return(0); } else { printk(KERN_WARNING "isar_down unknown (PH_CONTROL | REQUEST) %x\n", hh->dinfo); ret = -EINVAL; } } else { printk(KERN_WARNING "isar_down unknown prim(%x)\n", hh->prim); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } void free_isar(channel_t *bch) { isar_hw_t *ih = bch->hw; modeisar(bch, bch->channel, 0, NULL); del_timer(&ih->ftimer); test_and_clear_bit(FLG_INITIALIZED, &bch->Flags); } int init_isar(channel_t *bch) { isar_hw_t *ih = bch->hw; printk(KERN_INFO "mISDN: ISAR driver Rev. %s\n", mISDN_getrev(ISAR_revision)); ih->ftimer.function = (void *) ftimer_handler; ih->ftimer.data = (long) bch; init_timer(&ih->ftimer); test_and_set_bit(FLG_INITIALIZED, &bch->Flags); return (0); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/isar.h0000644000000000000500000001256511110524073017500 0ustar rootsrc/* $Id: isar.h,v 1.4 2006/03/06 12:52:07 keil Exp $ * * isar.h ISAR (Siemens PSB 7110) specific defines * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * */ typedef struct _isar_reg { unsigned long Flags; volatile u_char bstat; volatile u_char iis; volatile u_char cmsb; volatile u_char clsb; volatile u_char par[8]; } isar_reg_t; typedef struct _isar_hw { int dpath; int mml; u_char state; u_char cmd; u_char mod; u_char newcmd; u_char newmod; char try_mod; struct timer_list ftimer; u_char conmsg[16]; isar_reg_t *reg; } isar_hw_t; #define ISAR_IRQMSK 0x04 #define ISAR_IRQSTA 0x04 #define ISAR_IRQBIT 0x75 #define ISAR_CTRL_H 0x61 #define ISAR_CTRL_L 0x60 #define ISAR_IIS 0x58 #define ISAR_IIA 0x58 #define ISAR_HIS 0x50 #define ISAR_HIA 0x50 #define ISAR_MBOX 0x4c #define ISAR_WADR 0x4a #define ISAR_RADR 0x48 #define ISAR_HIS_VNR 0x14 #define ISAR_HIS_DKEY 0x02 #define ISAR_HIS_FIRM 0x1e #define ISAR_HIS_STDSP 0x08 #define ISAR_HIS_DIAG 0x05 #define ISAR_HIS_P0CFG 0x3c #define ISAR_HIS_P12CFG 0x24 #define ISAR_HIS_SARTCFG 0x25 #define ISAR_HIS_PUMPCFG 0x26 #define ISAR_HIS_PUMPCTRL 0x2a #define ISAR_HIS_IOM2CFG 0x27 #define ISAR_HIS_IOM2REQ 0x07 #define ISAR_HIS_IOM2CTRL 0x2b #define ISAR_HIS_BSTREQ 0x0c #define ISAR_HIS_PSTREQ 0x0e #define ISAR_HIS_SDATA 0x20 #define ISAR_HIS_DPS1 0x40 #define ISAR_HIS_DPS2 0x80 #define SET_DPS(x) ((x<<6) & 0xc0) #define ISAR_IIS_MSCMSD 0x3f #define ISAR_IIS_VNR 0x15 #define ISAR_IIS_DKEY 0x03 #define ISAR_IIS_FIRM 0x1f #define ISAR_IIS_STDSP 0x09 #define ISAR_IIS_DIAG 0x25 #define ISAR_IIS_GSTEV 0x00 #define ISAR_IIS_BSTEV 0x28 #define ISAR_IIS_BSTRSP 0x2c #define ISAR_IIS_PSTRSP 0x2e #define ISAR_IIS_PSTEV 0x2a #define ISAR_IIS_IOM2RSP 0x27 #define ISAR_IIS_RDATA 0x20 #define ISAR_IIS_INVMSG 0x3f #define ISAR_CTRL_SWVER 0x10 #define ISAR_CTRL_STST 0x40 #define ISAR_MSG_HWVER {0x20, 0, 1} #define ISAR_DP1_USE 1 #define ISAR_DP2_USE 2 #define ISAR_RATE_REQ 3 #define PMOD_DISABLE 0 #define PMOD_FAX 1 #define PMOD_DATAMODEM 2 #define PMOD_HALFDUPLEX 3 #define PMOD_V110 4 #define PMOD_DTMF 5 #define PMOD_DTMF_TRANS 6 #define PMOD_BYPASS 7 #define PCTRL_ORIG 0x80 #define PV32P2_V23R 0x40 #define PV32P2_V22A 0x20 #define PV32P2_V22B 0x10 #define PV32P2_V22C 0x08 #define PV32P2_V21 0x02 #define PV32P2_BEL 0x01 // LSB MSB in ISAR doc wrong !!! Arghhh #define PV32P3_AMOD 0x80 #define PV32P3_V32B 0x02 #define PV32P3_V23B 0x01 #define PV32P4_48 0x11 #define PV32P5_48 0x05 #define PV32P4_UT48 0x11 #define PV32P5_UT48 0x0d #define PV32P4_96 0x11 #define PV32P5_96 0x03 #define PV32P4_UT96 0x11 #define PV32P5_UT96 0x0f #define PV32P4_B96 0x91 #define PV32P5_B96 0x0b #define PV32P4_UTB96 0xd1 #define PV32P5_UTB96 0x0f #define PV32P4_120 0xb1 #define PV32P5_120 0x09 #define PV32P4_UT120 0xf1 #define PV32P5_UT120 0x0f #define PV32P4_144 0x99 #define PV32P5_144 0x09 #define PV32P4_UT144 0xf9 #define PV32P5_UT144 0x0f #define PV32P6_CTN 0x01 #define PV32P6_ATN 0x02 #define PFAXP2_CTN 0x01 #define PFAXP2_ATN 0x04 #define PSEV_10MS_TIMER 0x02 #define PSEV_CON_ON 0x18 #define PSEV_CON_OFF 0x19 #define PSEV_V24_OFF 0x20 #define PSEV_CTS_ON 0x21 #define PSEV_CTS_OFF 0x22 #define PSEV_DCD_ON 0x23 #define PSEV_DCD_OFF 0x24 #define PSEV_DSR_ON 0x25 #define PSEV_DSR_OFF 0x26 #define PSEV_REM_RET 0xcc #define PSEV_REM_REN 0xcd #define PSEV_GSTN_CLR 0xd4 #define PSEV_RSP_READY 0xbc #define PSEV_LINE_TX_H 0xb3 #define PSEV_LINE_TX_B 0xb2 #define PSEV_LINE_RX_H 0xb1 #define PSEV_LINE_RX_B 0xb0 #define PSEV_RSP_CONN 0xb5 #define PSEV_RSP_DISC 0xb7 #define PSEV_RSP_FCERR 0xb9 #define PSEV_RSP_SILDET 0xbe #define PSEV_RSP_SILOFF 0xab #define PSEV_FLAGS_DET 0xba #define PCTRL_CMD_TDTMF 0x5a #define PCTRL_CMD_FTH 0xa7 #define PCTRL_CMD_FRH 0xa5 #define PCTRL_CMD_FTM 0xa8 #define PCTRL_CMD_FRM 0xa6 #define PCTRL_CMD_SILON 0xac #define PCTRL_CMD_CONT 0xa2 #define PCTRL_CMD_ESC 0xa4 #define PCTRL_CMD_SILOFF 0xab #define PCTRL_CMD_HALT 0xa9 #define PCTRL_LOC_RET 0xcf #define PCTRL_LOC_REN 0xce #define SMODE_DISABLE 0 #define SMODE_V14 2 #define SMODE_HDLC 3 #define SMODE_BINARY 4 #define SMODE_FSK_V14 5 #define SCTRL_HDMC_BOTH 0x00 #define SCTRL_HDMC_DTX 0x80 #define SCTRL_HDMC_DRX 0x40 #define S_P1_OVSP 0x40 #define S_P1_SNP 0x20 #define S_P1_EOP 0x10 #define S_P1_EDP 0x08 #define S_P1_NSB 0x04 #define S_P1_CHS_8 0x03 #define S_P1_CHS_7 0x02 #define S_P1_CHS_6 0x01 #define S_P1_CHS_5 0x00 #define S_P2_BFT_DEF 0x10 #define IOM_CTRL_ENA 0x80 #define IOM_CTRL_NOPCM 0x00 #define IOM_CTRL_ALAW 0x02 #define IOM_CTRL_ULAW 0x04 #define IOM_CTRL_RCV 0x01 #define IOM_P1_TXD 0x10 #define HDLC_FED 0x40 #define HDLC_FSD 0x20 #define HDLC_FST 0x20 #define HDLC_ERROR 0x1c #define HDLC_ERR_FAD 0x10 #define HDLC_ERR_RER 0x08 #define HDLC_ERR_CER 0x04 #define SART_NMD 0x01 #define BSTAT_RDM0 0x1 #define BSTAT_RDM1 0x2 #define BSTAT_RDM2 0x4 #define BSTAT_RDM3 0x8 #define BSTEV_TBO 0x1f #define BSTEV_RBO 0x2f /* FAX State Machine */ #define STFAX_NULL 0 #define STFAX_READY 1 #define STFAX_LINE 2 #define STFAX_CONT 3 #define STFAX_ACTIV 4 #define STFAX_ESCAPE 5 #define STFAX_SILDET 6 extern int ISARVersion(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); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/ncci.c0000644000000000000500000011164111135651702017453 0ustar rootsrc/* $Id: ncci.c,v 1.29 2006/09/14 15:51:46 gkelleter Exp $ * */ #include "m_capi.h" #include "helper.h" #include "debug.h" #include "dss1.h" #include "mISDNManufacturer.h" static int ncciL4L3(Ncci_t *, u_int, int, int, void *, struct sk_buff *); static char logbuf[8000]; void log_skbdata(struct sk_buff *skb) { char *t = logbuf; t += sprintf(t, "skbdata(%d):", skb->len); mISDN_QuickHex(t, skb->data, skb->len); printk(KERN_DEBUG "%s\n", logbuf); } // -------------------------------------------------------------------- // NCCI state machine // // Some rules: // * EV_AP_* events come from CAPI Application // * EV_DL_* events come from the ISDN stack // * EV_NC_* events generated in NCCI handling // * messages are send in the routine that handle the event // // -------------------------------------------------------------------- enum { ST_NCCI_N_0, ST_NCCI_N_0_1, ST_NCCI_N_1, ST_NCCI_N_2, ST_NCCI_N_ACT, ST_NCCI_N_3, ST_NCCI_N_4, ST_NCCI_N_5, } const ST_NCCI_COUNT = ST_NCCI_N_5 + 1; static char *str_st_ncci[] = { "ST_NCCI_N_0", "ST_NCCI_N_0_1", "ST_NCCI_N_1", "ST_NCCI_N_2", "ST_NCCI_N_ACT", "ST_NCCI_N_3", "ST_NCCI_N_4", "ST_NCCI_N_5", }; enum { EV_AP_CONNECT_B3_REQ, EV_NC_CONNECT_B3_CONF, EV_NC_CONNECT_B3_IND, EV_AP_CONNECT_B3_RESP, EV_NC_CONNECT_B3_ACTIVE_IND, EV_AP_CONNECT_B3_ACTIVE_RESP, EV_AP_RESET_B3_REQ, EV_NC_RESET_B3_IND, EV_NC_RESET_B3_CONF, EV_AP_RESET_B3_RESP, EV_NC_CONNECT_B3_T90_ACTIVE_IND, EV_AP_DISCONNECT_B3_REQ, EV_NC_DISCONNECT_B3_IND, EV_NC_DISCONNECT_B3_CONF, EV_AP_DISCONNECT_B3_RESP, EV_AP_FACILITY_REQ, EV_AP_MANUFACTURER_REQ, EV_DL_ESTABLISH_IND, EV_DL_ESTABLISH_CONF, EV_DL_RELEASE_IND, EV_DL_RELEASE_CONF, EV_DL_DOWN_IND, EV_NC_LINKDOWN, EV_AP_RELEASE, } const EV_NCCI_COUNT = EV_AP_RELEASE + 1; static char* str_ev_ncci[] = { "EV_AP_CONNECT_B3_REQ", "EV_NC_CONNECT_B3_CONF", "EV_NC_CONNECT_B3_IND", "EV_AP_CONNECT_B3_RESP", "EV_NC_CONNECT_B3_ACTIVE_IND", "EV_AP_CONNECT_B3_ACTIVE_RESP", "EV_AP_RESET_B3_REQ", "EV_NC_RESET_B3_IND", "EV_NC_RESET_B3_CONF", "EV_AP_RESET_B3_RESP", "EV_NC_CONNECT_B3_T90_ACTIVE_IND", "EV_AP_DISCONNECT_B3_REQ", "EV_NC_DISCONNECT_B3_IND", "EV_NC_DISCONNECT_B3_CONF", "EV_AP_DISCONNECT_B3_RESP", "EV_AP_FACILITY_REQ", "EV_AP_MANUFACTURER_REQ", "EV_DL_ESTABLISH_IND", "EV_DL_ESTABLISH_CONF", "EV_DL_RELEASE_IND", "EV_DL_RELEASE_CONF", "EV_DL_DOWN_IND", "EV_NC_LINKDOWN", "EV_AP_RELEASE", }; static struct Fsm ncci_fsm = { 0, 0, 0, 0, 0 }; static struct Fsm ncciD_fsm = { 0, 0, 0, 0, 0 }; static int select_NCCIaddr(Ncci_t *ncci) { __u32 addr; Ncci_t *test; if (!ncci->AppPlci) return(-EINVAL); addr = 0x00010000 | ncci->AppPlci->addr; while (addr < 0x00ffffff) { /* OK not more as 255 NCCI */ test = getNCCI4addr(ncci->AppPlci, addr, GET_NCCI_EXACT); if (!test) { ncci->addr = addr; #ifdef OLDCAPI_DRIVER_INTERFACE ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, ncci->addr, ncci->window); #endif return(0); } addr += 0x00010000; } ncci->addr = ncci->AppPlci->addr; return(-EBUSY); } static void ncci_debug(struct FsmInst *fi, char *fmt, ...) { char tmp[128]; char *p = tmp; va_list args; Ncci_t *ncci = fi->userdata; if (!ncci->ncci_m.debug) return; va_start(args, fmt); p += sprintf(p, "NCCI 0x%x: ", ncci->addr); p += vsprintf(p, fmt, args); *p++ = '\n'; *p = 0; printk(KERN_DEBUG "%s", tmp); va_end(args); } static inline void SKB2Application(Ncci_t *ncci, struct sk_buff *skb) { if (!test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) { #ifdef OLDCAPI_DRIVER_INTERFACE ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, skb); #else capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, skb); #endif } } static inline int SKB_l4l3(Ncci_t *ncci, struct sk_buff *skb) { if (!ncci->link) return(-ENXIO); return(mISDN_queue_down(&ncci->link->inst, 0, skb)); } static inline void Send2Application(Ncci_t *ncci, _cmsg *cmsg) { SendCmsg2Application(ncci->appl, cmsg); } static inline void ncciCmsgHeader(Ncci_t *ncci, _cmsg *cmsg, __u8 cmd, __u8 subcmd) { capi_cmsg_header(cmsg, ncci->appl->ApplId, cmd, subcmd, ncci->appl->MsgId++, ncci->addr); } static void ncci_connect_b3_req(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg = arg; // FIXME if (!ncci->appl) { cmsg_free(cmsg); return; } mISDN_FsmChangeState(fi, ST_NCCI_N_0_1); capi_cmsg_answer(cmsg); // TODO: NCPI handling /* We need a real addr now */ if (0xffff0000 & ncci->addr) { int_error(); cmsg->Info = CapiNoNcciAvailable; ncci->addr = ncci->AppPlci->addr; } else { cmsg->Info = 0; if (select_NCCIaddr(ncci)) { int_error(); cmsg->Info = CapiNoNcciAvailable; } } cmsg->adr.adrNCCI = ncci->addr; ncci_debug(fi, "ncci_connect_b3_req NCCI %x cmsg->Info(%x)", ncci->addr, cmsg->Info); if (mISDN_FsmEvent(fi, EV_NC_CONNECT_B3_CONF, cmsg)) cmsg_free(cmsg); } static void ncci_connect_b3_ind(struct FsmInst *fi, int event, void *arg) { // from DL_ESTABLISH mISDN_FsmChangeState(fi, ST_NCCI_N_1); Send2Application(fi->userdata, arg); } static void ncci_connect_b3_resp(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg = arg; // FIXME if (!ncci->appl) { cmsg_free(cmsg); return; } if (cmsg->Info == 0) { mISDN_FsmChangeState(fi, ST_NCCI_N_2); ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3_ACTIVE, CAPI_IND); event = EV_NC_CONNECT_B3_ACTIVE_IND; } else { mISDN_FsmChangeState(fi, ST_NCCI_N_4); cmsg->Info = 0; ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); event = EV_NC_DISCONNECT_B3_IND; } if (mISDN_FsmEvent(&ncci->ncci_m, event, cmsg)) cmsg_free(cmsg); } static void ncci_connect_b3_conf(struct FsmInst *fi, int event, void *arg) { _cmsg *cmsg = arg; if (cmsg->Info == 0) { mISDN_FsmChangeState(fi, ST_NCCI_N_2); Send2Application(fi->userdata, cmsg); ncciL4L3(fi->userdata, DL_ESTABLISH | REQUEST, 0, 0, NULL, NULL); } else { mISDN_FsmChangeState(fi, ST_NCCI_N_0); Send2Application(fi->userdata, cmsg); ncciDestr(fi->userdata); } } static void ncci_disconnect_b3_req(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg = arg; __u16 Info = 0; if (ncci->appl) { //FIXME /* TODO: handle NCPI and wait for all DATA_B3_REQ confirmed on * related protocols (voice, T30) */ capi_cmsg_answer(cmsg); cmsg->Info = Info; if (mISDN_FsmEvent(fi, EV_NC_DISCONNECT_B3_CONF, cmsg)) cmsg_free(cmsg); } else { cmsg_free(cmsg); mISDN_FsmChangeState(fi, ST_NCCI_N_4); } ncciL4L3(ncci, DL_RELEASE | REQUEST, 0, 0, NULL, NULL); } static void ncci_disconnect_b3_conf(struct FsmInst *fi, int event, void *arg) { _cmsg *cmsg = arg; if (cmsg->Info == 0) { mISDN_FsmChangeState(fi, ST_NCCI_N_4); } Send2Application(fi->userdata, cmsg); } static void ncci_disconnect_b3_ind(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; mISDN_FsmChangeState(fi, ST_NCCI_N_5); if (ncci->appl) { // FIXME Send2Application(ncci, arg); } else { cmsg_free(arg); mISDN_FsmChangeState(fi, ST_NCCI_N_0); ncciDestr(ncci); } } static void ncci_disconnect_b3_resp(struct FsmInst *fi, int event, void *arg) { if (arg) cmsg_free(arg); mISDN_FsmChangeState(fi, ST_NCCI_N_0); ncciDestr(fi->userdata); } static void ncci_facility_req(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg = arg; u_char *p = cmsg->FacilityRequestParameter; u16 func; int op; if (!ncci->appl) return; capi_cmsg_answer(cmsg); cmsg->Info = CAPI_NOERROR; if (cmsg->FacilitySelector == 0) { // Handset int err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_ON, 0, NULL, NULL); if (err) cmsg->Info = CapiFacilityNotSupported; } else if (cmsg->FacilitySelector != 1) { // not DTMF cmsg->Info = CapiIllMessageParmCoding; } else if (p && p[0]) { func = CAPIMSG_U16(p, 1); ncci_debug(fi, "%s: p %02x %02x %02x func(%x)", __FUNCTION__, p[0], p[1], p[2], func); switch (func) { case 1: op = DTMF_TONE_START; ncciL4L3(ncci, PH_CONTROL | REQUEST, 0, sizeof(int), &op, NULL); break; case 2: op = DTMF_TONE_STOP; ncciL4L3(ncci, PH_CONTROL | REQUEST, 0, sizeof(int), &op, NULL); break; default: cmsg->Info = CapiFacilityNotSupported; break; } } else cmsg->Info = CapiIllMessageParmCoding; Send2Application(ncci, cmsg); } static void ncci_manufacturer_req(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg = arg; int err, op; struct _manu_conf_para { u8 len; u16 Info; u16 vol; } mcp = {2, CAPI_NOERROR,0}; struct _manu_req_para { u8 len; u16 vol; } __attribute__((packed)) *mrp; if (!ncci->appl) return; mrp = (struct _manu_req_para *)cmsg->ManuData; capi_cmsg_answer(cmsg); if (cmsg->Class == mISDN_MF_CLASS_HANDSET) { // Handset switch(cmsg->Function) { case mISDN_MF_HANDSET_ENABLE: err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_ON, 0, NULL, NULL); if (err) mcp.Info = CapiFacilityNotSupported; break; case mISDN_MF_HANDSET_DISABLE: err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_OFF, 0, NULL, NULL); if (err) mcp.Info = CapiFacilityNotSupported; break; case mISDN_MF_HANDSET_SETMICVOLUME: case mISDN_MF_HANDSET_SETSPKVOLUME: if (!mrp || mrp->len != 2) { mcp.Info = CapiIllMessageParmCoding; break; } op = (cmsg->Function == mISDN_MF_HANDSET_SETSPKVOLUME) ? HW_POTS_SETSPKVOL : HW_POTS_SETMICVOL; err = ncciL4L3(ncci, PH_CONTROL | REQUEST, op, 2, &mrp->vol, NULL); if (err == -ENODEV) mcp.Info = CapiFacilityNotSupported; else if (err) mcp.Info = CapiIllMessageParmCoding; break; /* not handled yet */ case mISDN_MF_HANDSET_GETMICVOLUME: case mISDN_MF_HANDSET_GETSPKVOLUME: default: mcp.Info = CapiFacilityNotSupported; break; } } else mcp.Info = CapiIllMessageParmCoding; cmsg->ManuData = (_cstruct)&mcp; Send2Application(ncci, cmsg); } static void ncci_connect_b3_active_ind(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; int i; mISDN_FsmChangeState(fi, ST_NCCI_N_ACT); for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { ncci->xmit_skb_handles[i].PktId = 0; ncci->recv_skb_handles[i] = 0; } Send2Application(ncci, arg); } static void ncci_connect_b3_active_resp(struct FsmInst *fi, int event, void *arg) { cmsg_free(arg); } static void ncci_n0_dl_establish_ind_conf(struct FsmInst *fi, int event, void *arg) { _cmsg *cmsg; Ncci_t *ncci = fi->userdata; if (!ncci->appl) return; if (!(0xffff0000 & ncci->addr)) { if (select_NCCIaddr(ncci)) { int_error(); return; } } else { int_error(); return; } CMSG_ALLOC(cmsg); ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3, CAPI_IND); if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_CONNECT_B3_IND, cmsg)) cmsg_free(cmsg); } static void ncci_dl_establish_conf(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg; if (!ncci->appl) return; CMSG_ALLOC(cmsg); ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3_ACTIVE, CAPI_IND); if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_CONNECT_B3_ACTIVE_IND, cmsg)) cmsg_free(cmsg); } static void ncci_dl_release_ind_conf(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg; CMSG_ALLOC(cmsg); ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg)) cmsg_free(cmsg); } static void ncci_linkdown(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg; CMSG_ALLOC(cmsg); ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg)) cmsg_free(cmsg); } static void ncci_dl_down_ind(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg; CMSG_ALLOC(cmsg); ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); cmsg->Reason_B3 = CapiProtocolErrorLayer1; if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg)) cmsg_free(cmsg); } static void ncci_appl_release(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_NCCI_N_0); ncciDestr(fi->userdata); } static void ncci_appl_release_disc(struct FsmInst *fi, int event, void *arg) { ncciL4L3(fi->userdata, DL_RELEASE | REQUEST, 0, 0, NULL, NULL); } static struct FsmNode fn_ncci_list[] = { {ST_NCCI_N_0, EV_AP_CONNECT_B3_REQ, ncci_connect_b3_req}, {ST_NCCI_N_0, EV_NC_CONNECT_B3_IND, ncci_connect_b3_ind}, {ST_NCCI_N_0, EV_DL_ESTABLISH_CONF, ncci_n0_dl_establish_ind_conf}, {ST_NCCI_N_0, EV_DL_ESTABLISH_IND, ncci_n0_dl_establish_ind_conf}, {ST_NCCI_N_0, EV_AP_RELEASE, ncci_appl_release}, {ST_NCCI_N_0_1, EV_NC_CONNECT_B3_CONF, ncci_connect_b3_conf}, {ST_NCCI_N_0_1, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, {ST_NCCI_N_0_1, EV_AP_RELEASE, ncci_appl_release}, {ST_NCCI_N_1, EV_AP_CONNECT_B3_RESP, ncci_connect_b3_resp}, {ST_NCCI_N_1, EV_AP_DISCONNECT_B3_REQ, ncci_disconnect_b3_req}, {ST_NCCI_N_1, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, {ST_NCCI_N_1, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, {ST_NCCI_N_1, EV_AP_RELEASE, ncci_appl_release_disc}, {ST_NCCI_N_1, EV_NC_LINKDOWN, ncci_linkdown}, {ST_NCCI_N_2, EV_NC_CONNECT_B3_ACTIVE_IND, ncci_connect_b3_active_ind}, {ST_NCCI_N_2, EV_AP_DISCONNECT_B3_REQ, ncci_disconnect_b3_req}, {ST_NCCI_N_2, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, {ST_NCCI_N_2, EV_DL_ESTABLISH_CONF, ncci_dl_establish_conf}, {ST_NCCI_N_2, EV_DL_RELEASE_IND, ncci_dl_release_ind_conf}, {ST_NCCI_N_2, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, {ST_NCCI_N_2, EV_AP_RELEASE, ncci_appl_release_disc}, {ST_NCCI_N_2, EV_NC_LINKDOWN, ncci_linkdown}, #if 0 {ST_NCCI_N_3, EV_NC_RESET_B3_IND, ncci_reset_b3_ind}, {ST_NCCI_N_3, EV_DL_DOWN_IND, ncci_dl_down_ind}, {ST_NCCI_N_3, EV_AP_DISCONNECT_B3_REQ, ncci_disconnect_b3_req}, {ST_NCCI_N_3, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, {ST_NCCI_N_3, EV_AP_RELEASE, ncci_appl_release_disc}, {ST_NCCI_N_3, EV_NC_LINKDOWN, ncci_linkdown}, #endif {ST_NCCI_N_ACT, EV_AP_CONNECT_B3_ACTIVE_RESP, ncci_connect_b3_active_resp}, {ST_NCCI_N_ACT, EV_AP_DISCONNECT_B3_REQ, ncci_disconnect_b3_req}, {ST_NCCI_N_ACT, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, {ST_NCCI_N_ACT, EV_DL_RELEASE_IND, ncci_dl_release_ind_conf}, {ST_NCCI_N_ACT, EV_DL_RELEASE_CONF, ncci_dl_release_ind_conf}, {ST_NCCI_N_ACT, EV_DL_DOWN_IND, ncci_dl_down_ind}, {ST_NCCI_N_ACT, EV_AP_FACILITY_REQ, ncci_facility_req}, {ST_NCCI_N_ACT, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, {ST_NCCI_N_ACT, EV_AP_RELEASE, ncci_appl_release_disc}, {ST_NCCI_N_ACT, EV_NC_LINKDOWN, ncci_linkdown}, #if 0 {ST_NCCI_N_ACT, EV_AP_RESET_B3_REQ, ncci_reset_b3_req}, {ST_NCCI_N_ACT, EV_NC_RESET_B3_IND, ncci_reset_b3_ind}, {ST_NCCI_N_ACT, EV_NC_CONNECT_B3_T90_ACTIVE_IND,ncci_connect_b3_t90_active_ind}, #endif {ST_NCCI_N_4, EV_NC_DISCONNECT_B3_CONF, ncci_disconnect_b3_conf}, {ST_NCCI_N_4, EV_NC_DISCONNECT_B3_IND, ncci_disconnect_b3_ind}, {ST_NCCI_N_4, EV_DL_RELEASE_CONF, ncci_dl_release_ind_conf}, {ST_NCCI_N_4, EV_DL_DOWN_IND, ncci_dl_down_ind}, {ST_NCCI_N_4, EV_AP_MANUFACTURER_REQ, ncci_manufacturer_req}, {ST_NCCI_N_4, EV_NC_LINKDOWN, ncci_linkdown}, {ST_NCCI_N_5, EV_AP_DISCONNECT_B3_RESP, ncci_disconnect_b3_resp}, {ST_NCCI_N_5, EV_AP_RELEASE, ncci_appl_release}, }; const int FN_NCCI_COUNT = sizeof(fn_ncci_list)/sizeof(struct FsmNode); static void ncciD_connect_b3_req(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_NCCI_N_0_1); if (SKB_l4l3(fi->userdata, arg)) dev_kfree_skb(arg); } static void ncciD_connect_b3_conf(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; __u16 info = CAPIMSG_U16(skb->data, 12); if (info == 0) mISDN_FsmChangeState(fi, ST_NCCI_N_2); else mISDN_FsmChangeState(fi, ST_NCCI_N_0); SKB2Application(fi->userdata, skb); if (info != 0) ncciDestr(fi->userdata); } static void ncciD_connect_b3_ind(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_NCCI_N_1); SKB2Application(fi->userdata, arg); } static void ncciD_connect_b3_resp(struct FsmInst *fi, int event, void *arg) { struct sk_buff *skb = arg; __u16 rej = CAPIMSG_U16(skb->data, 4); if (rej) mISDN_FsmChangeState(fi, ST_NCCI_N_4); else mISDN_FsmChangeState(fi, ST_NCCI_N_2); if (SKB_l4l3(fi->userdata, arg)) dev_kfree_skb(arg); } static void ncciD_connect_b3_active_ind(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_NCCI_N_ACT); SKB2Application(fi->userdata, arg); } static void ncciD_connect_b3_active_resp(struct FsmInst *fi, int event, void *arg) { if (SKB_l4l3(fi->userdata, arg)) dev_kfree_skb(arg); } static void ncciD_reset_b3_ind(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_NCCI_N_ACT); SKB2Application(fi->userdata, arg); } static void ncciD_reset_b3_resp(struct FsmInst *fi, int event, void *arg) { if (SKB_l4l3(fi->userdata, arg)) dev_kfree_skb(arg); } static void ncciD_reset_b3_conf(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_NCCI_N_3); SKB2Application(fi->userdata, arg); } static void ncciD_reset_b3_req(struct FsmInst *fi, int event, void *arg) { if (SKB_l4l3(fi->userdata, arg)) dev_kfree_skb(arg); } static void ncciD_disconnect_b3_req(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; ncci->savedstate = fi->state; mISDN_FsmChangeState(fi, ST_NCCI_N_4); if (SKB_l4l3(fi->userdata, arg)) dev_kfree_skb(arg); } static void ncciD_disconnect_b3_conf(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; struct sk_buff *skb = arg; __u16 info = CAPIMSG_U16(skb->data, 12); if (ncci->ncci_m.debug) log_skbdata(skb); if (test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) return; if (info != 0) mISDN_FsmChangeState(fi, ncci->savedstate); SKB2Application(ncci, skb); } static void ncciD_disconnect_b3_ind(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; struct sk_buff *skb = arg; mISDN_FsmChangeState(fi, ST_NCCI_N_5); if (test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) { skb_pull(skb, CAPIMSG_BASELEN); skb_trim(skb, 0); skb_put(skb, 4); mISDN_queuedown_newhead(&ncci->link->inst, 0, CAPI_DISCONNECT_B3_RESP, 0, skb); ncciDestr(ncci); } else SKB2Application(ncci, arg); } static void ncciD_disconnect_b3_resp(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_NCCI_N_0); if (SKB_l4l3(fi->userdata, arg)) dev_kfree_skb(arg); ncciDestr(fi->userdata); } static void ncciD_linkdown(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; _cmsg *cmsg; CMSG_ALLOC(cmsg); ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND); mISDN_FsmChangeState(fi, ST_NCCI_N_5); Send2Application(ncci, cmsg); } static void ncciD_appl_release_disc(struct FsmInst *fi, int event, void *arg) { Ncci_t *ncci = fi->userdata; u_char parm[5]; capimsg_setu32(parm, 0, ncci->addr); parm[4] = 0; mISDN_FsmChangeState(fi, ST_NCCI_N_4); mISDN_queue_data(&ncci->link->inst, FLG_MSG_DOWN, CAPI_DISCONNECT_B3_REQ, 0, 5, parm, 0); } static struct FsmNode fn_ncciD_list[] = { {ST_NCCI_N_0, EV_AP_CONNECT_B3_REQ, ncciD_connect_b3_req}, {ST_NCCI_N_0, EV_NC_CONNECT_B3_IND, ncciD_connect_b3_ind}, {ST_NCCI_N_0, EV_AP_RELEASE, ncci_appl_release}, {ST_NCCI_N_0_1, EV_NC_CONNECT_B3_CONF, ncciD_connect_b3_conf}, {ST_NCCI_N_0_1, EV_AP_RELEASE, ncci_appl_release}, {ST_NCCI_N_1, EV_AP_CONNECT_B3_RESP, ncciD_connect_b3_resp}, {ST_NCCI_N_1, EV_AP_DISCONNECT_B3_REQ, ncciD_disconnect_b3_req}, {ST_NCCI_N_1, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, {ST_NCCI_N_1, EV_AP_RELEASE, ncciD_appl_release_disc}, {ST_NCCI_N_1, EV_NC_LINKDOWN, ncciD_linkdown}, {ST_NCCI_N_2, EV_NC_CONNECT_B3_ACTIVE_IND, ncciD_connect_b3_active_ind}, {ST_NCCI_N_2, EV_AP_DISCONNECT_B3_REQ, ncciD_disconnect_b3_req}, {ST_NCCI_N_2, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, {ST_NCCI_N_2, EV_AP_RELEASE, ncciD_appl_release_disc}, {ST_NCCI_N_2, EV_NC_LINKDOWN, ncciD_linkdown}, {ST_NCCI_N_3, EV_NC_RESET_B3_IND, ncciD_reset_b3_ind}, {ST_NCCI_N_3, EV_AP_DISCONNECT_B3_REQ, ncciD_disconnect_b3_req}, {ST_NCCI_N_3, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, {ST_NCCI_N_3, EV_AP_RELEASE, ncciD_appl_release_disc}, {ST_NCCI_N_3, EV_NC_LINKDOWN, ncciD_linkdown}, {ST_NCCI_N_ACT, EV_AP_CONNECT_B3_ACTIVE_RESP, ncciD_connect_b3_active_resp}, {ST_NCCI_N_ACT, EV_AP_DISCONNECT_B3_REQ, ncciD_disconnect_b3_req}, {ST_NCCI_N_ACT, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, {ST_NCCI_N_ACT, EV_AP_RELEASE, ncciD_appl_release_disc}, {ST_NCCI_N_ACT, EV_NC_LINKDOWN, ncciD_linkdown}, {ST_NCCI_N_ACT, EV_AP_RESET_B3_REQ, ncciD_reset_b3_req}, {ST_NCCI_N_ACT, EV_NC_RESET_B3_IND, ncciD_reset_b3_ind}, {ST_NCCI_N_ACT, EV_NC_RESET_B3_CONF, ncciD_reset_b3_conf}, {ST_NCCI_N_ACT, EV_AP_RESET_B3_RESP, ncciD_reset_b3_resp}, //{ST_NCCI_N_ACT, EV_NC_CONNECT_B3_T90_ACTIVE_IND,ncciD_connect_b3_t90_active_ind}, {ST_NCCI_N_4, EV_NC_DISCONNECT_B3_CONF, ncciD_disconnect_b3_conf}, {ST_NCCI_N_4, EV_NC_DISCONNECT_B3_IND, ncciD_disconnect_b3_ind}, {ST_NCCI_N_4, EV_NC_LINKDOWN, ncciD_linkdown}, {ST_NCCI_N_5, EV_AP_DISCONNECT_B3_RESP, ncciD_disconnect_b3_resp}, {ST_NCCI_N_5, EV_AP_RELEASE, ncci_appl_release}, }; const int FN_NCCID_COUNT = sizeof(fn_ncciD_list)/sizeof(struct FsmNode); Ncci_t * ncciConstr(AppPlci_t *aplci) { Ncci_t *ncci = ncci_alloc(); if (!ncci) return(NULL); memset(ncci, 0, sizeof(Ncci_t)); ncci->ncci_m.state = ST_NCCI_N_0; ncci->ncci_m.debug = aplci->plci->contr->debug & CAPI_DBG_NCCI_STATE; ncci->ncci_m.userdata = ncci; ncci->ncci_m.printdebug = ncci_debug; /* unused NCCI */ ncci->addr = aplci->addr; ncci->AppPlci = aplci; ncci->link = aplci->link; ncci->contr = aplci->contr; ncci->appl = aplci->appl; ncci->window = aplci->appl->reg_params.datablkcnt; if (aplci->Bprotocol.B2 != 0) /* X.75 has own flowctrl */ test_and_set_bit(NCCI_STATE_FCTRL, &ncci->state); if (aplci->Bprotocol.B3 == 0) { test_and_set_bit(NCCI_STATE_L3TRANS, &ncci->state); ncci->ncci_m.fsm = &ncci_fsm; } else ncci->ncci_m.fsm = &ncciD_fsm; skb_queue_head_init(&ncci->squeue); if (ncci->window > CAPI_MAXDATAWINDOW) { ncci->window = CAPI_MAXDATAWINDOW; } INIT_LIST_HEAD(&ncci->head); list_add(&ncci->head, &aplci->Nccis); if (ncci->ncci_m.debug) printk(KERN_DEBUG "%s: ncci(%p) NCCI(%x) debug (%x/%x)\n", __FUNCTION__, ncci, ncci->addr, aplci->plci->contr->debug, CAPI_DBG_NCCI_STATE); return(ncci); } void ncciDestr(Ncci_t *ncci) { int i; capidebug(CAPI_DBG_NCCI, "ncciDestr NCCI %x", ncci->addr); #ifdef OLDCAPI_DRIVER_INTERFACE if (!test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) ncci->contr->ctrl->free_ncci(ncci->contr->ctrl, ncci->appl->ApplId, ncci->addr); #endif /* cleanup data queues */ discard_queue(&ncci->squeue); for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { if (ncci->xmit_skb_handles[i].PktId) ncci->xmit_skb_handles[i].PktId = 0; } AppPlciDelNCCI(ncci); ncci_free(ncci); } void ncciApplRelease(Ncci_t *ncci) { test_and_set_bit(NCCI_STATE_APPLRELEASED, &ncci->state); mISDN_FsmEvent(&ncci->ncci_m, EV_AP_RELEASE, NULL); } void ncciDelAppPlci(Ncci_t *ncci) { printk(KERN_DEBUG "%s: ncci(%p) NCCI(%x)\n", __FUNCTION__, ncci, ncci->addr); ncci->AppPlci = NULL; /* maybe we should release the NCCI here */ } void ncciReleaseLink(Ncci_t *ncci) { /* this is normal shutdown on speech and other transparent protocols */ mISDN_FsmEvent(&ncci->ncci_m, EV_NC_LINKDOWN, NULL); } void ncciDataInd(Ncci_t *ncci, int pr, struct sk_buff *skb) { struct sk_buff *nskb; int i; for (i = 0; i < ncci->window; i++) { if (ncci->recv_skb_handles[i] == 0) break; } if (i == ncci->window) { // FIXME: trigger flow control if supported by L2 protocol printk(KERN_DEBUG "%s: frame %d dropped\n", __FUNCTION__, skb->len); dev_kfree_skb(skb); return; } if (skb_headroom(skb) < CAPI_B3_DATA_IND_HEADER_SIZE) { capidebug(CAPI_DBG_NCCI_L3, "%s: only %d bytes headroom, need %d", __FUNCTION__, skb_headroom(skb), CAPI_B3_DATA_IND_HEADER_SIZE); nskb = skb_realloc_headroom(skb, CAPI_B3_DATA_IND_HEADER_SIZE); dev_kfree_skb(skb); if (!nskb) { int_error(); return; } } else { nskb = skb; } ncci->recv_skb_handles[i] = nskb; skb_push(nskb, CAPI_B3_DATA_IND_HEADER_SIZE); CAPIMSG_SETLEN(nskb->data, CAPI_B3_DATA_IND_HEADER_SIZE); CAPIMSG_SETAPPID(nskb->data, ncci->appl->ApplId); CAPIMSG_SETCOMMAND(nskb->data, CAPI_DATA_B3); CAPIMSG_SETSUBCOMMAND(nskb->data, CAPI_IND); CAPIMSG_SETMSGID(nskb->data, ncci->appl->MsgId++); CAPIMSG_SETCONTROL(nskb->data, ncci->addr); if (sizeof(nskb) == 4) { capimsg_setu32(nskb->data, 12, (__u32)(((u_long)nskb->data + CAPI_B3_DATA_IND_HEADER_SIZE) & 0xffffffff)); *((__u64*)(nskb->data+22)) = cpu_to_le64(0); } else { capimsg_setu32(nskb->data, 12, 0); *((__u64*)(nskb->data+22)) = cpu_to_le64((__u64)nskb->data + CAPI_B3_DATA_IND_HEADER_SIZE); } CAPIMSG_SETDATALEN(nskb->data, nskb->len - CAPI_B3_DATA_IND_HEADER_SIZE); capimsg_setu16(nskb->data, 18, i); // FIXME FLAGS capimsg_setu16(nskb->data, 20, 0); #ifdef OLDCAPI_DRIVER_INTERFACE ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, nskb); #else capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, nskb); #endif } __u16 ncciDataReq(Ncci_t *ncci, struct sk_buff *skb) { int i, err; __u16 len, capierr = 0; _cmsg *cmsg; len = CAPIMSG_LEN(skb->data); if (len != 22 && len != 30) { capierr = CapiIllMessageParmCoding; int_error(); goto fail; } for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { /* try reserving a slot atomically (use an invalid Id) */ if (cmpxchg(&ncci->xmit_skb_handles[i].PktId, 0, MISDN_ID_DUMMY) == 0) break; } if (i == CAPI_MAXDATAWINDOW) { return(CAPI_SENDQUEUEFULL); } mISDN_HEAD_DINFO(skb) = ControllerNextId(ncci->contr); ncci->xmit_skb_handles[i].PktId = mISDN_HEAD_DINFO(skb); ncci->xmit_skb_handles[i].DataHandle = CAPIMSG_REQ_DATAHANDLE(skb->data); ncci->xmit_skb_handles[i].MsgId = CAPIMSG_MSGID(skb->data); /* the data begins behind the header, we don't use Data32/Data64 here */ skb_pull(skb, len); if (test_bit(NCCI_STATE_FCTRL, &ncci->state)) { if (test_and_set_bit(NCCI_STATE_BUSY, &ncci->state)) { skb_queue_tail(&ncci->squeue, skb); return(CAPI_NOERROR); } if (skb_queue_len(&ncci->squeue)) { skb_queue_tail(&ncci->squeue, skb); skb = skb_dequeue(&ncci->squeue); i = -1; } } err = ncciL4L3(ncci, DL_DATA | REQUEST, mISDN_HEAD_DINFO(skb), 0, NULL, skb); if (!err) return(CAPI_NOERROR); int_error(); skb_push(skb, len); capierr = CAPI_MSGBUSY; if (i == -1) { for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { if (ncci->xmit_skb_handles[i].PktId == mISDN_HEAD_DINFO(skb)) break; } if (i == CAPI_MAXDATAWINDOW) int_error(); else ncci->xmit_skb_handles[i].PktId = 0; } else { ncci->xmit_skb_handles[i].PktId = 0; return(capierr); } fail: cmsg = cmsg_alloc(); if (!cmsg) { int_error(); if (capierr != CAPI_MSGBUSY) return(CAPI_MSGOSRESOURCEERR); /* we can not do error handling on a skb from the queue here */ dev_kfree_skb(skb); return(CAPI_NOERROR); } capi_cmsg_header(cmsg, ncci->AppPlci->appl->ApplId, CAPI_DATA_B3, CAPI_CONF, CAPIMSG_MSGID(skb->data), ncci->addr); /* illegal len (too short) ??? */ cmsg->DataHandle = CAPIMSG_REQ_DATAHANDLE(skb->data); cmsg->Info = capierr; Send2Application(ncci, cmsg); dev_kfree_skb(skb); return(CAPI_NOERROR); } int ncciDataConf(Ncci_t *ncci, int pr, struct sk_buff *skb) { int i; _cmsg *cmsg; for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { if (ncci->xmit_skb_handles[i].PktId == mISDN_HEAD_DINFO(skb)) break; } if (i == CAPI_MAXDATAWINDOW) { int_error(); printk(KERN_DEBUG "%s: dinfo(%x)\n", __FUNCTION__, mISDN_HEAD_DINFO(skb)); for (i = 0; i < CAPI_MAXDATAWINDOW; i++) printk(KERN_DEBUG "%s: PktId[%d] %x\n", __FUNCTION__, i, ncci->xmit_skb_handles[i].PktId); return(-EINVAL); } capidebug(CAPI_DBG_NCCI_L3, "%s: entry %d/%d handle (%x)", __FUNCTION__, i, CAPI_MAXDATAWINDOW, ncci->xmit_skb_handles[i].DataHandle); cmsg = cmsg_alloc(); if (!cmsg) { int_error(); ncci->xmit_skb_handles[i].PktId = 0; return(-ENOMEM); } dev_kfree_skb(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; cmsg->Info = 0; ncci->xmit_skb_handles[i].PktId = 0; Send2Application(ncci, cmsg); if (test_bit(NCCI_STATE_FCTRL, &ncci->state)) { if (skb_queue_len(&ncci->squeue)) { skb = skb_dequeue(&ncci->squeue); if (ncciL4L3(ncci, DL_DATA | REQUEST, mISDN_HEAD_DINFO(skb), 0, NULL, skb)) { int_error(); dev_kfree_skb(skb); } } else { test_and_clear_bit(NCCI_STATE_BUSY, &ncci->state); } } return(0); } void ncciDataResp(Ncci_t *ncci, struct sk_buff *skb) { // FIXME: incoming flow control doesn't work yet int i; i = CAPIMSG_RESP_DATAHANDLE(skb->data); if (i < 0 || i > ncci->window) { int_error(); return; } if (!ncci->recv_skb_handles[i]) { int_error(); return; } ncci->recv_skb_handles[i] = 0; dev_kfree_skb(skb); } int ncci_l4l3_direct(Ncci_t *ncci, struct sk_buff *skb) { mISDN_head_t *hh; int ret; hh = mISDN_HEAD_P(skb); if (ncci->ncci_m.debug) log_skbdata(skb); hh->prim = CAPIMSG_CMD(skb->data); hh->dinfo = CAPIMSG_MSGID(skb->data); skb_pull(skb, CAPIMSG_BASELEN); if (ncci->ncci_m.debug) log_skbdata(skb); switch (hh->prim) { case CAPI_DATA_B3_REQ: case CAPI_DATA_B3_RESP: case CAPI_FACILITY_REQ: case CAPI_FACILITY_RESP: case CAPI_MANUFACTURER_REQ: case CAPI_MANUFACTURER_RESP: return(SKB_l4l3(ncci, skb)); case CAPI_CONNECT_B3_REQ: ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_REQ, skb); break; case CAPI_CONNECT_B3_RESP: ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_RESP, skb); break; case CAPI_CONNECT_B3_ACTIVE_RESP: ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_ACTIVE_RESP, skb); break; case CAPI_DISCONNECT_B3_REQ: ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_REQ, skb); break; case CAPI_DISCONNECT_B3_RESP: ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_RESP, skb); break; case CAPI_RESET_B3_REQ: ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_RESET_B3_REQ, skb); break; case CAPI_RESET_B3_RESP: ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_RESET_B3_RESP, skb); break; default: int_error(); ret = -1; } if (ret) { int_error(); dev_kfree_skb(skb); } return(0); } void ncciGetCmsg(Ncci_t *ncci, _cmsg *cmsg) { int retval = 0; if (!test_bit(NCCI_STATE_L3TRANS, &ncci->state)) { int_error(); cmsg_free(cmsg); return; } switch (CMSGCMD(cmsg)) { case CAPI_CONNECT_B3_REQ: retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_REQ, cmsg); break; case CAPI_CONNECT_B3_RESP: retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_RESP, cmsg); break; case CAPI_CONNECT_B3_ACTIVE_RESP: retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_ACTIVE_RESP, cmsg); break; case CAPI_DISCONNECT_B3_REQ: retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_REQ, cmsg); break; case CAPI_DISCONNECT_B3_RESP: retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_RESP, cmsg); break; case CAPI_FACILITY_REQ: retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_FACILITY_REQ, cmsg); break; case CAPI_MANUFACTURER_REQ: retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_MANUFACTURER_REQ, cmsg); break; default: int_error(); retval = -1; } if (retval) { if (cmsg->Command == CAPI_REQ) { capi_cmsg_answer(cmsg); cmsg->Info = CapiMessageNotSupportedInCurrentState; Send2Application(ncci, cmsg); } else cmsg_free(cmsg); } } void ncciSendMessage(Ncci_t *ncci, struct sk_buff *skb) { int ret; _cmsg *cmsg; if (!test_bit(NCCI_STATE_L3TRANS, &ncci->state)) { ret = ncci_l4l3_direct(ncci, skb); switch(ret) { case 0: break; case -EINVAL: case -ENXIO: int_error(); break; /* (CAPI_MSGBUSY) */ case -EXFULL: int_error(); break; /* (CAPI_SENDQUEUEFULL) */ default: int_errtxt("ncci_l4l3_direct return(%d)", ret); dev_kfree_skb(skb); break; } 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) { ret = ncciDataReq(ncci, skb); if (ret) int_error(); } else { AnswerMessage2Application(ncci->appl, skb, CapiMessageNotSupportedInCurrentState); dev_kfree_skb(skb); } return; case CAPI_DATA_B3_RESP: ncciDataResp(ncci, skb); return; } cmsg = cmsg_alloc(); if (!cmsg) { int_error(); return; } capi_message2cmsg(cmsg, skb->data); ncciGetCmsg(ncci, cmsg); dev_kfree_skb(skb); return; } int ncci_l3l4_direct(Ncci_t *ncci, mISDN_head_t *hh, struct sk_buff *skb) { __u16 msgnr; int event; capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dinfo (%x) skb(%p) s(%x)", __FUNCTION__, ncci->addr, hh->prim, hh->dinfo, skb, ncci->state); if (ncci->ncci_m.debug) log_skbdata(skb); switch (hh->prim & 0xFF) { case CAPI_IND: msgnr = ncci->appl->MsgId++; break; case CAPI_CONF: msgnr = hh->dinfo & 0xffff; break; default: int_error(); return(-EINVAL); } if (skb_headroom(skb) < CAPIMSG_BASELEN) { capidebug(CAPI_DBG_NCCI_L3, "%s: only %d bytes headroom, need %d", __FUNCTION__, skb_headroom(skb), CAPIMSG_BASELEN); int_error(); return(-ENOSPC); } skb_push(skb, CAPIMSG_BASELEN); CAPIMSG_SETLEN(skb->data, (hh->prim == CAPI_DATA_B3_IND) ? CAPI_B3_DATA_IND_HEADER_SIZE : skb->len); CAPIMSG_SETAPPID(skb->data, ncci->appl->ApplId); CAPIMSG_SETCOMMAND(skb->data, (hh->prim>>8) & 0xff); CAPIMSG_SETSUBCOMMAND(skb->data, hh->prim & 0xff); CAPIMSG_SETMSGID(skb->data, msgnr); switch (hh->prim) { case CAPI_DATA_B3_IND: case CAPI_DATA_B3_CONF: case CAPI_FACILITY_IND: case CAPI_FACILITY_CONF: case CAPI_MANUFACTURER_IND: case CAPI_MANUFACTURER_CONF: #ifdef OLDCAPI_DRIVER_INTERFACE ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, skb); #else capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, skb); #endif return(0); case CAPI_CONNECT_B3_IND: event = EV_NC_CONNECT_B3_IND; break; case CAPI_CONNECT_B3_ACTIVE_IND: event = EV_NC_CONNECT_B3_ACTIVE_IND; break; case CAPI_DISCONNECT_B3_IND: event = EV_NC_DISCONNECT_B3_IND; break; case CAPI_RESET_B3_IND: event = EV_NC_RESET_B3_IND; break; case CAPI_CONNECT_B3_CONF: event = EV_NC_CONNECT_B3_CONF; break; case CAPI_DISCONNECT_B3_CONF: event = EV_NC_DISCONNECT_B3_CONF; break; case CAPI_RESET_B3_CONF: event = EV_NC_RESET_B3_CONF; break; default: int_error(); return(-EINVAL); } if (mISDN_FsmEvent(&ncci->ncci_m, event, skb)) dev_kfree_skb(skb); return(0); } int ncci_l3l4(Ncci_t *ncci, mISDN_head_t *hh, struct sk_buff *skb) { capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dinfo (%x) skb(%p) s(%x)", __FUNCTION__, ncci->addr, hh->prim, hh->dinfo, skb, ncci->state); switch (hh->prim) { // we're not using the Fsm for DL_DATA for performance reasons case DL_DATA | INDICATION: if (ncci->ncci_m.state == ST_NCCI_N_ACT) { ncciDataInd(ncci, hh->prim, skb); return(0); } break; case DL_DATA | CONFIRM: if (ncci->ncci_m.state == ST_NCCI_N_ACT) { return(ncciDataConf(ncci, hh->prim, skb)); } break; case DL_ESTABLISH | INDICATION: mISDN_FsmEvent(&ncci->ncci_m, EV_DL_ESTABLISH_IND, skb); break; case DL_ESTABLISH | CONFIRM: mISDN_FsmEvent(&ncci->ncci_m, EV_DL_ESTABLISH_CONF, skb); break; case DL_RELEASE | INDICATION: mISDN_FsmEvent(&ncci->ncci_m, EV_DL_RELEASE_IND, skb); break; case DL_RELEASE | CONFIRM: mISDN_FsmEvent(&ncci->ncci_m, EV_DL_RELEASE_CONF, skb); break; case PH_CONTROL | INDICATION: /* e.g touch tones */ /* handled by AppPlci */ AppPlci_l3l4(ncci->AppPlci, hh->prim, skb->data); break; default: capidebug(CAPI_DBG_WARN, "%s: unknown prim(%x) dinfo(%x) len(%d) skb(%p)", __FUNCTION__, hh->prim, hh->dinfo, skb->len, skb); int_error(); return(-EINVAL); } dev_kfree_skb(skb); return(0); } static int ncciL4L3(Ncci_t *ncci, u_int prim, int dtyp, int len, void *arg, struct sk_buff *skb) { 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(mISDN_queuedown_newhead(&ncci->link->inst, 0, prim, dtyp, skb)); else return(mISDN_queue_data(&ncci->link->inst, FLG_MSG_DOWN, prim, dtyp, len, arg, 8)); } void init_ncci(void) { ncci_fsm.state_count = ST_NCCI_COUNT; ncci_fsm.event_count = EV_NCCI_COUNT; ncci_fsm.strEvent = str_ev_ncci; ncci_fsm.strState = str_st_ncci; mISDN_FsmNew(&ncci_fsm, fn_ncci_list, FN_NCCI_COUNT); ncciD_fsm.state_count = ST_NCCI_COUNT; ncciD_fsm.event_count = EV_NCCI_COUNT; ncciD_fsm.strEvent = str_ev_ncci; ncciD_fsm.strState = str_st_ncci; mISDN_FsmNew(&ncciD_fsm, fn_ncciD_list, FN_NCCID_COUNT); } void free_ncci(void) { mISDN_FsmFree(&ncci_fsm); mISDN_FsmFree(&ncciD_fsm); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/netdev.c0000644000000000000500000001427511135651702020031 0ustar rootsrc/* * gateway between mISDN and Linux's netdev subsystem * * Copyright (C) 2005-2006 Christian Richter * * Authors: Christian Richter * * Thanks to Daniele Orlandi from the vISDN Developer Team * * This program is free software and may be modified and distributed * under the terms and conditions of the GNU General Public License. * */ #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "lapd.h" wait_queue_head_t waitq; struct sk_buff_head finishq; struct mISDN_netdev { struct sk_buff_head workq; struct list_head list; struct net_device *netdev; struct net_device_stats stats; mISDNstack_t *st; int nt; int addr; }; LIST_HEAD(mISDN_netdev_list); static int misdn_chan_frame_xmit( struct net_device *netdev, struct sk_buff *skb, int rx) { struct mISDN_netdev *ndev=netdev->priv; struct lapd_prim_hdr *prim_hdr; netdev->last_rx = jiffies; skb->protocol = htons(ETH_P_LAPD); skb->dev = netdev; skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_HOST; skb_push(skb, sizeof(struct lapd_prim_hdr)); prim_hdr = (struct lapd_prim_hdr *)skb->data; prim_hdr->primitive_type = LAPD_PH_DATA_INDICATION; if (rx) ndev->stats.rx_packets++; else ndev->stats.rx_packets++; return netif_rx(skb); } static struct net_device_stats *misdn_netdev_get_stats( struct net_device *netdev) { struct mISDN_netdev *ndev=netdev->priv; return &ndev->stats; } static void misdn_netdev_set_multicast_list( struct net_device *netdev) { } static int misdn_netdev_do_ioctl( struct net_device *netdev, struct ifreq *ifr, int cmd) { return -EOPNOTSUPP; } static int misdn_netdev_change_mtu( struct net_device *netdev, int new_mtu) { return 0; } static int lapd_mac_addr(struct net_device *dev, void *addr) { return 0; } static void setup_lapd(struct net_device *netdev) { netdev->change_mtu = NULL; netdev->hard_header = NULL; netdev->rebuild_header = NULL; netdev->set_mac_address = lapd_mac_addr; netdev->hard_header_cache = NULL; netdev->header_cache_update= NULL; //netdev->hard_header_parse = lapd_hard_header_parse; netdev->hard_header_parse = NULL; netdev->type = ARPHRD_LAPD; netdev->hard_header_len = 0; netdev->addr_len = 1; netdev->tx_queue_len = 10; memset(netdev->broadcast, 0, sizeof(netdev->broadcast)); netdev->flags = 0; } static int misdn_netdev_open(struct net_device *netdev) { return 0; } static int misdn_netdev_stop(struct net_device *netdev) { return 0; } static int mISDN_netdevd(void *data) { struct sk_buff *skb; struct mISDN_netdev *ndev; for(;;) { wait_event_interruptible(waitq, 1); if ((skb=skb_dequeue(&finishq))) { kfree_skb(skb); return 0; } list_for_each_entry(ndev, &mISDN_netdev_list, list) { while ((skb=skb_dequeue(&ndev->workq))) { misdn_chan_frame_xmit(ndev->netdev, skb, 1); } } } return 0; } static int misdn_netdev_frame_xmit( struct sk_buff *skb, struct net_device *netdev) { return 0; } /*************************/ /** Interface Functions **/ /*************************/ void misdn_log_frame(mISDNstack_t* st, unsigned char *data, int len, int direction) { struct sk_buff *skb; struct mISDN_netdev *ndev; int i; skb = alloc_skb(sizeof(struct lapd_prim_hdr)+len, GFP_KERNEL); if (!skb) { printk ("%s: no mem for skb\n", __FUNCTION__); return; } skb_reserve(skb, sizeof(struct lapd_prim_hdr)); skb_reserve(skb, 4); memcpy(skb_put(skb, len), data, len); list_for_each_entry(ndev, &mISDN_netdev_list, list) { if (ndev->st==st) break; } // use syslog as long as kernel oops when using netdev printk ("%s: st(%x): %s ", __FUNCTION__, st->id, (direction==FLG_MSG_UP)?"-->":"<--"); for (i=0; inetdev, skb , 1); else printk(KERN_WARNING "No mISDN_netdev for stack:%x\n",st->id); */ } int misdn_netdev_addstack(mISDNstack_t *st) { struct net_device *netdev; struct mISDN_netdev *ndev; int err; char name[8]; sprintf(name, "mISDN%02x", ((((st->id >> 8) & 0xFF) + ((st->id >> 12) & 0xFF)) & 0xFF)); printk(KERN_NOTICE "allocating netdev(%s) for stack(%x)\n", name, st->id); netdev = alloc_netdev(0, name, setup_lapd); if(!netdev) { printk(KERN_ERR "net_device alloc failed, abort.\n"); return -ENOMEM; } ndev = kmalloc(sizeof(struct mISDN_netdev), GFP_KERNEL); if(!ndev) { printk(KERN_ERR "mISDN_netdevice alloc failed, abort.\n"); return -ENOMEM; } memset(&ndev->stats,0,sizeof(ndev->stats)); skb_queue_head_init(&ndev->workq); ndev->netdev=netdev; netdev->priv = ndev; netdev->open = misdn_netdev_open; netdev->stop = misdn_netdev_stop; netdev->hard_start_xmit = misdn_netdev_frame_xmit; netdev->get_stats = misdn_netdev_get_stats; netdev->set_multicast_list = misdn_netdev_set_multicast_list; netdev->do_ioctl = misdn_netdev_do_ioctl; netdev->change_mtu = misdn_netdev_change_mtu; netdev->features = NETIF_F_NO_CSUM; switch (st->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK) { case ISDN_PID_L0_TE_S0: case ISDN_PID_L0_TE_E1: printk(KERN_NOTICE " --> TE Mode\n"); memset(netdev->dev_addr, 0, sizeof(netdev->dev_addr)); break; case ISDN_PID_L0_NT_S0: case ISDN_PID_L0_NT_E1: printk(KERN_NOTICE " --> NT Mode\n"); memset(netdev->dev_addr, 1, sizeof(netdev->dev_addr)); break; } SET_MODULE_OWNER(netdev); netdev->irq = 0; netdev->base_addr = 0; list_add(&ndev->list, &mISDN_netdev_list); err = register_netdev(netdev); if (err < 0) { printk(KERN_ERR "Cannot register net device %s, aborting.\n", netdev->name); } return 0; } int misdn_netdev_init(void) { int err=0; init_waitqueue_head(&waitq); skb_queue_head_init(&finishq); kernel_thread(mISDN_netdevd, NULL, 0); return err; } void misdn_netdev_exit(void) { struct sk_buff *skb=alloc_skb(8,GFP_KERNEL); struct mISDN_netdev *ndev; skb_queue_tail(&finishq,skb); list_for_each_entry(ndev, &mISDN_netdev_list, list) { unregister_netdev(ndev->netdev); } } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/netjet.c0000644000000000000500000013064411135651702020034 0ustar rootsrc/* * NETJet mISDN driver * * Author Daniel Potts * Copyright (C) 2005 Traverse Technologies P/L * * Based on HiSax NETJet driver by Karsten Keil */ #include #include #include #include "core.h" #include "channel.h" #include "isac.h" #include "layer1.h" #include "debug.h" #include #include "netjet.h" //#define DPRINT(...) //#define DTRACE(...) #define DTRACE printk #define DPRINT printk static const char *netjet_rev = "$Revision: 1.8 $"; #define MAX_CARDS 4 static int debug; static int netjet_cnt; static u_int protocol[MAX_CARDS]; static int layermask[MAX_CARDS]; #ifdef MODULE MODULE_AUTHOR("Daniel Potts"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #define MODULE_PARM_T "1-4i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #ifdef OLD_MODULE_PARAM_ARRAY static int num_protocol=0,num_layermask=0; module_param_array(protocol, uint, num_protocol, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, num_layermask, S_IRUGO | S_IWUSR); #else module_param_array(protocol, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, NULL, S_IRUGO | S_IWUSR); #endif #endif #endif #ifdef LOCAL_FCSTAB static __u16 fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; #endif static mISDNobject_t netjet_mISDN; enum { NETJET_S_TJ300, NETJET_S_TJ320, }; typedef struct { long size; u_int32_t *dmabuf; dma_addr_t dmaaddr; // (system phys) } tiger_dmabuf_t; struct tiger_hw { tiger_dmabuf_t send; u_int32_t *s_irq; u_int32_t *s_end; u_int32_t *sendp; tiger_dmabuf_t rec; int free; u_char *rcvbuf; u_char *sendbuf; u_char *sp; int sendcnt; u_int s_tot; u_int r_bitcnt; u_int r_tot; u_int r_err; u_int r_fcs; u_char r_state; u_char r_one; u_char r_val; u_char s_state; u_char *tx_buf; u_char *rx_buf; }; typedef struct { struct list_head list; struct pci_dev *pdev; u_int subtype; u_int irq; u_int irqcnt; u_long base; spinlock_t lock; isac_chip_t isac; struct tiger_hw tiger[2]; // bch hdlc's channel_t dch; channel_t bch[2]; u_char ctrlreg; u_char dmactrl; u_char auxd; unsigned char last_is0; unsigned char irqmask0; } netjet_t; /* Local Prototypes */ static int tiger_l2l1B (mISDNinstance_t *inst, struct sk_buff *skb); static void nj_release_card(netjet_t *card); static void netjet_fill_dma(channel_t *bch); static void write_raw(channel_t *bch, u_int *buf, int cnt); static void write_tiger_bch(channel_t *bch, u_int *buf, int cnt); static void nj_disable_hwirq(netjet_t *card) { outb (0, card->base + NETJET_IRQMASK0); outb (0, card->base + NETJET_IRQMASK1); } static u_char nj_readISAC(void *cs, u_char offset) { netjet_t *card = (netjet_t *)cs; u_char ret; card->auxd &= 0xfc; card->auxd |= (offset>>4) & 3; outb (card->auxd, card->base + NETJET_AUXDATA); ret = inb ((card->base | NETJET_ISAC_OFF) | ((offset & NETJET_HA_MASK) << NETJET_HA_OFFSET)); return (ret); } void nj_writeISAC(void *cs, u_char offset, u_char value) { netjet_t *card = (netjet_t *)cs; card->auxd &= 0xfc; card->auxd |= (offset>>4) & 3; outb (card->auxd, card->base + NETJET_AUXDATA); outb (value, (card->base | NETJET_ISAC_OFF) | ((offset & NETJET_HA_MASK) << NETJET_HA_OFFSET)); } void nj_readISACfifo(void *cs, u_char *data, int size) { netjet_t *card = (netjet_t *)cs; card->auxd &= 0xfc; outb (card->auxd, card->base + NETJET_AUXDATA); insb((card->base | NETJET_ISAC_OFF), data, size); } void nj_writeISACfifo(void *cs, u_char *data, int size) { netjet_t *card = (netjet_t *)cs; card->auxd &= 0xfc; outb (card->auxd, card->base + NETJET_AUXDATA); outsb((card->base | NETJET_ISAC_OFF), data, size); } void fill_mem(channel_t *bch, u_int *pos, u_int cnt, int chan, u_char fill) { u_int mask=0x000000ff, val = 0, *p=pos; u_int i; struct tiger_hw *tiger; tiger = bch->hw; val |= fill; if (chan) { val <<= 8; mask <<= 8; } mask ^= 0xffffffff; for (i=0; i tiger->s_end) p = tiger->send.dmabuf; } } int mode_tiger(channel_t *bch, int bc, int protocol) { struct tiger_hw *tiger; netjet_t *card; tiger = bch->hw; card = bch->inst.privat; // u_char led; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "Tiger bchan %d/%d protocol %x-->%x", bc, bch->channel, bch->state, protocol); if ((protocol != -1) && (bc != bch->channel)) printk(KERN_WARNING "%s: netjet mismatch channel(%d/%d)\n", __FUNCTION__, bch->channel, bc); switch (protocol) { case (-1): /* used for init */ bch->state = protocol; //james bch->channel = bc; case (ISDN_PID_NONE): if (bch->state == ISDN_PID_NONE) { break; } fill_mem(bch, tiger->send.dmabuf, NETJET_DMA_TXSIZE, bc, 0xff); if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "Tiger stat rec %d/%d send %d", tiger->r_tot, tiger->r_err, tiger->s_tot); bch->state = ISDN_PID_NONE; /* only stop dma and interrupts if both channels NULL */ if ((card->bch[0].state == ISDN_PID_NONE) && (card->bch[1].state == ISDN_PID_NONE)) { card->dmactrl = 0; outb(card->dmactrl, card->base + NETJET_DMACTRL); outb(0, card->base + NETJET_IRQMASK0); } test_and_clear_bit(FLG_HDLC, &bch->Flags); test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); #if 0 // led stuff if (cs->typ == ISDN_CTYPE_NETJET_S) { // led off led = bc & 0x01; led = 0x01 << (6 + led); // convert to mask led = ~led; cs->hw.njet.auxd &= led; byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); } #endif break; case (ISDN_PID_L1_B_64TRANS): /* fall through for transparancy (eg voice).. */ test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); // case (L1_MODE_HDLC_56K): case (ISDN_PID_L1_B_64HDLC): bch->state = protocol; //james if (protocol == ISDN_PID_L1_B_64HDLC) test_and_set_bit(FLG_HDLC, &bch->Flags); fill_mem(bch, tiger->send.dmabuf, NETJET_DMA_TXSIZE, bc, 0xff); tiger->r_state = HDLC_ZERO_SEARCH; tiger->r_tot = 0; tiger->r_bitcnt = 0; tiger->r_one = 0; tiger->r_err = 0; tiger->s_tot = 0; if (! card->dmactrl) { fill_mem(bch, tiger->send.dmabuf, NETJET_DMA_TXSIZE, !bc, 0xff); card->dmactrl = 1; outb(card->dmactrl, card->base + NETJET_DMACTRL); outb(0x0f, card->base + NETJET_IRQMASK0); /* was 0x3f now 0x0f for TJ300 and TJ320 GE 13/07/00 */ } tiger->sendp = tiger->send.dmabuf; tiger->free = NETJET_DMA_TXSIZE; test_and_set_bit(FLG_EMPTY, &bch->Flags); #if 0 // old led stuff if (cs->typ == ISDN_CTYPE_NETJET_S) { // led on led = bc & 0x01; led = 0x01 << (6 + led); // convert to mask cs->hw.njet.auxd |= led; byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); } #endif break; default: mISDN_debugprint(&bch->inst, "nj prot not known %x", protocol); return(-ENOPROTOOPT); } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "tiger: set %x %x %x %x/%x pulse=%d", inb(card->base + NETJET_DMACTRL), inb(card->base + NETJET_IRQMASK0), inb(card->base + NETJET_IRQSTAT0), inl(card->base + NETJET_DMA_READ_ADR), inl(card->base + NETJET_DMA_WRITE_ADR), inb(card->base + NETJET_PULSE_CNT)); return 0; } static void nj_reset (netjet_t *card) { outb (0xff, card->base + NETJET_CTRL); /* Reset On */ mdelay(10); /* now edge triggered for TJ320 GE 13/07/00 */ /* see comment in IRQ function */ if (card->subtype == NETJET_S_TJ320) /* TJ320 */ card->ctrlreg = 0x40; /* Reset Off and status read clear */ else card->ctrlreg = 0x00; /* Reset Off and status read clear */ outb (card->ctrlreg, card->base + NETJET_CTRL); mdelay(10); /* configure AUX pins (all output except ISAC IRQ pin) */ card->auxd = 0; card->dmactrl = 0; outb (~NETJET_ISACIRQ, card->base + NETJET_AUXCTRL); outb (NETJET_ISACIRQ, card->base + NETJET_IRQMASK1); outb (card->auxd, card->base + NETJET_AUXDATA); } static int nj_manager (void *data, u_int prim, void *arg) { mISDNinstance_t *inst = data; netjet_t *card; struct sk_buff *skb; u_long flags; int channel = -1; if (!data) { MGR_HASPROTOCOL_HANDLER(prim,arg,&netjet_mISDN) printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); return(-EINVAL); } spin_lock_irqsave(&netjet_mISDN.lock, flags); list_for_each_entry(card, &netjet_mISDN.ilist, list) { if (&card->dch.inst == inst) { channel = 2; break; } if (&card->bch[0].inst == inst) { channel = 0; break; } if (&card->bch[1].inst == inst) { channel = 1; break; } } spin_unlock_irqrestore(&netjet_mISDN.lock, flags); if (channel<0) { printk(KERN_WARNING "%s: no channel data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return(-EINVAL); } switch (prim) { case MGR_REGLAYER | CONFIRM: if (channel == 2) mISDN_setpara(&card->dch, &inst->st->para); else mISDN_setpara(&card->bch[channel], &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: 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 (tiger_l2l1B(inst, skb)) dev_kfree_skb(skb); } } else { printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); } mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: if (channel == 2) mISDN_setpara(&card->dch, arg); else mISDN_setpara(&card->bch[channel], arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { nj_release_card(card); } else { netjet_mISDN.refcnt--; } break; case MGR_SETSTACK | INDICATION: if ((channel!=2) && (inst->pid.global == 2)) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (tiger_l2l1B(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; } static int tiger_l2l1B(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *bch; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; 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 */ netjet_fill_dma(bch); ret = 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 = mode_tiger(bch, bch->channel, bch->inst.pid.protocol[1]); spin_unlock_irqrestore(inst->hwlock, flags); if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) test_and_set_bit(FLG_L2DATA, &bch->Flags); } 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)))) { spin_lock_irqsave(inst->hwlock, flags); if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { if (bch->next_skb == NULL) printk(KERN_WARNING "%s: TX_NEXT set with no next_skb\n", __FUNCTION__); else { dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; bch->tx_idx = 0; } test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); mode_tiger(bch, bch->channel, 0); test_and_clear_bit(FLG_ACTIVE, &bch->Flags); test_and_clear_bit(FLG_L2DATA, &bch->Flags); spin_unlock_irqrestore(inst->hwlock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) if (!mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, 0, skb)) return 0; } else { printk(KERN_WARNING "tiger_l2l1B unknown %x prim(%x)\n", (int)skb, hh->prim); ret = -EINVAL; } if (!ret) { dev_kfree_skb(skb); } return(ret); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) void inittiger(netjet_t *card) { #else void __init inittiger(netjet_t *card) { #endif struct tiger_hw *tiger0, *tiger1; /* NOTE: I believe hisax tiger driver is wrong. * It allocates 4 times too much memory (unsigned int) and does not * use pci consistent, which won't work on all systems. */ tiger0 = &card->tiger[0]; tiger1 = &card->tiger[1]; /* XXX review buffer size */ tiger0->tx_buf = kmalloc(4 * NETJET_DMA_TXSIZE * sizeof (u_int32_t), GFP_ATOMIC); tiger1->tx_buf = kmalloc(4 * NETJET_DMA_TXSIZE * sizeof (u_int32_t), GFP_ATOMIC); tiger0->send.size = NETJET_DMA_TXSIZE * sizeof (u_int32_t); tiger0->send.dmabuf = pci_alloc_consistent ((struct pci_dev *) card->pdev, tiger0->send.size, &tiger0->send.dmaaddr); if (!tiger0->send.dmabuf) { printk(KERN_WARNING "mISDN: No memory for tiger.send\n"); return; } /* * NOTE: This code is seedy int ptr arithmetic. * You have been warned. */ tiger0->s_irq = tiger0->send.dmabuf + NETJET_DMA_TXSIZE/2 - 1; tiger0->s_end = tiger0->send.dmabuf + NETJET_DMA_TXSIZE - 1; tiger1->send.dmabuf = tiger0->send.dmabuf; tiger1->s_irq = tiger0->s_irq; tiger1->s_end = tiger0->s_end; memset(tiger0->send.dmabuf, 0xff, NETJET_DMA_TXSIZE *sizeof(uint32_t)); mISDN_debugprint(&card->bch[0].inst, "tiger: send buf %p - %p", tiger0->send.dmabuf, tiger0->send.dmabuf + NETJET_DMA_TXSIZE - 1); outl(tiger0->send.dmaaddr, card->base + NETJET_DMA_READ_START); outl(virt_to_bus(tiger0->s_irq), card->base + NETJET_DMA_READ_IRQ); outl(virt_to_bus(tiger0->s_end), card->base + NETJET_DMA_READ_END); tiger0->rec.size = NETJET_DMA_RXSIZE * sizeof (u_int32_t); tiger0->rec.dmabuf = pci_alloc_consistent ((struct pci_dev *) card->pdev, tiger0->rec.size, &tiger0->rec.dmaaddr); if (!tiger0->rec.dmabuf) { printk(KERN_WARNING "mISDN: No memory for tiger.rec\n"); return; } mISDN_debugprint(&card->bch[0].inst, "tiger: rec buf %p - %p", tiger0->rec.dmabuf, tiger0->rec.dmabuf + NETJET_DMA_RXSIZE - 1); tiger1->rec.dmabuf = tiger0->rec.dmabuf; memset(tiger0->rec.dmabuf, 0xff, NETJET_DMA_RXSIZE *sizeof(u_int32_t)); outl(tiger0->rec.dmaaddr, card->base + NETJET_DMA_WRITE_START); outl(virt_to_bus(tiger0->rec.dmabuf + NETJET_DMA_RXSIZE/2 - 1), card->base + NETJET_DMA_WRITE_IRQ); outl(virt_to_bus(tiger0->rec.dmabuf + NETJET_DMA_RXSIZE - 1), card->base + NETJET_DMA_WRITE_END); mISDN_debugprint(&card->bch[0].inst, "tiger: dmacfg %x/%x pulse=%d", inl(card->base + NETJET_DMA_WRITE_ADR), inl(card->base + NETJET_DMA_READ_ADR), inb(card->base + NETJET_PULSE_CNT)); card->last_is0 = 0; } static void printframe(mISDNinstance_t *cs, u_char *buf, int count, char *s) { char tmp[128]; char *t = tmp; int i=count,j; u_char *p = buf; t += sprintf(t, "tiger %s(%4d)", s, count); while (i>0) { if (i>16) j=16; else j=i; mISDN_QuickHex(t, p, j); mISDN_debugprint(cs, tmp); p += j; i -= j; t = tmp; t += sprintf(t, "tiger %s ", s); } } // macro for 64k #define MAKE_RAW_BYTE for (j=0; j<8; j++) { \ bitcnt++;\ s_val >>= 1;\ if (val & 1) {\ s_one++;\ s_val |= 0x80;\ } else {\ s_one = 0;\ s_val &= 0x7f;\ }\ if (bitcnt==8) {\ tiger->tx_buf[s_cnt++] = s_val;\ bitcnt = 0;\ }\ if (s_one == 5) {\ s_val >>= 1;\ s_val &= 0x7f;\ bitcnt++;\ s_one = 0;\ }\ if (bitcnt==8) {\ tiger->tx_buf[s_cnt++] = s_val;\ bitcnt = 0;\ }\ val >>= 1;\ } static int make_raw_data_transparent(channel_t *bch, struct sk_buff *skb) { // this make_raw is for transparent (Voice) u_int i,s_cnt=0; u_char val; struct tiger_hw *tiger; tiger = bch->hw; if (!skb) { mISDN_debugprint(&bch->inst, "tiger make_raw_trans: NULL skb"); return(1); } for (i=0; ilen; i++) { val = skb->data[i]; // input tiger->tx_buf[s_cnt++] = val; // output } tiger->sendcnt = s_cnt; tiger->sp = tiger->tx_buf; return(0); } static int make_raw_data(channel_t *bch, struct sk_buff *skb) { // this make_raw is for 64k u_int i,s_cnt=0; u_char j; u_char val; u_char s_one = 0; u_char s_val = 0; u_char bitcnt = 0; u_int fcs; struct tiger_hw *tiger; tiger = bch->hw; if (!skb) { mISDN_debugprint(&bch->inst, "tiger make_raw: NULL skb"); return(1); } tiger->tx_buf[s_cnt++] = HDLC_FLAG_VALUE; fcs = PPP_INITFCS; for (i=0; ilen; i++) { val = skb->data[i]; fcs = PPP_FCS (fcs, val); MAKE_RAW_BYTE; } fcs ^= 0xffff; val = fcs & 0xff; MAKE_RAW_BYTE; val = (fcs>>8) & 0xff; MAKE_RAW_BYTE; val = HDLC_FLAG_VALUE; for (j=0; j<8; j++) { bitcnt++; s_val >>= 1; if (val & 1) s_val |= 0x80; else s_val &= 0x7f; if (bitcnt==8) { tiger->tx_buf[s_cnt++] = s_val; bitcnt = 0; } val >>= 1; } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst,"tiger make_raw: in %ld out %d.%d", skb->len, s_cnt, bitcnt); if (bitcnt) { while (8>bitcnt++) { s_val >>= 1; s_val |= 0x80; } tiger->tx_buf[s_cnt++] = s_val; tiger->tx_buf[s_cnt++] = 0xff; // NJ<->NJ thoughput bug fix } tiger->sendcnt = s_cnt; tiger->sp = tiger->tx_buf; return(0); } // macro for 56k #define MAKE_RAW_BYTE_56K for (j=0; j<8; j++) { \ bitcnt++;\ s_val >>= 1;\ if (val & 1) {\ s_one++;\ s_val |= 0x80;\ } else {\ s_one = 0;\ s_val &= 0x7f;\ }\ if (bitcnt==7) {\ s_val >>= 1;\ s_val |= 0x80;\ tiger->tx_buf[s_cnt++] = s_val;\ bitcnt = 0;\ }\ if (s_one == 5) {\ s_val >>= 1;\ s_val &= 0x7f;\ bitcnt++;\ s_one = 0;\ }\ if (bitcnt==7) {\ s_val >>= 1;\ s_val |= 0x80;\ tiger->tx_buf[s_cnt++] = s_val;\ bitcnt = 0;\ }\ val >>= 1;\ } static int make_raw_data_56k(channel_t *bch, struct sk_buff *skb) { // this make_raw is for 56k u_int i,s_cnt=0; u_char j; u_char val; u_char s_one = 0; u_char s_val = 0; u_char bitcnt = 0; u_int fcs; struct tiger_hw *tiger; tiger = bch->hw; if (!skb) { mISDN_debugprint(&bch->inst, "tiger make_raw_56k: NULL skb"); return(1); } val = HDLC_FLAG_VALUE; for (j=0; j<8; j++) { bitcnt++; s_val >>= 1; if (val & 1) s_val |= 0x80; else s_val &= 0x7f; if (bitcnt==7) { s_val >>= 1; s_val |= 0x80; tiger->tx_buf[s_cnt++] = s_val; bitcnt = 0; } val >>= 1; }; fcs = PPP_INITFCS; for (i=0; ilen; i++) { val = skb->data[i]; fcs = PPP_FCS (fcs, val); MAKE_RAW_BYTE_56K; } fcs ^= 0xffff; val = fcs & 0xff; MAKE_RAW_BYTE_56K; val = (fcs>>8) & 0xff; MAKE_RAW_BYTE_56K; val = HDLC_FLAG_VALUE; for (j=0; j<8; j++) { bitcnt++; s_val >>= 1; if (val & 1) s_val |= 0x80; else s_val &= 0x7f; if (bitcnt==7) { s_val >>= 1; s_val |= 0x80; tiger->tx_buf[s_cnt++] = s_val; bitcnt = 0; } val >>= 1; } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "tiger make_raw_56k: in %ld out %d.%d", skb->len, s_cnt, bitcnt); if (bitcnt) { while (8>bitcnt++) { s_val >>= 1; s_val |= 0x80; } tiger->tx_buf[s_cnt++] = s_val; tiger->tx_buf[s_cnt++] = 0xff; // NJ<->NJ thoughput bug fix } tiger->sendcnt = s_cnt; tiger->sp = tiger->tx_buf; return(0); } static void netjet_fill_dma(channel_t *bch) { u_int *p, *sp; int cnt; struct tiger_hw *tiger; netjet_t *card; struct sk_buff *skb; tiger = bch->hw; card = bch->inst.privat; skb = bch->tx_skb; if (!skb) { printk(KERN_WARNING "%s: called with no skb\n", __FUNCTION__); return; } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst,"tiger fill_dma1: c%d %4x", bch->channel, bch->Flags); if (test_bit(FLG_HDLC, &bch->Flags)) { // it's 64k if (make_raw_data(bch, skb)) return; } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { if (make_raw_data_transparent(bch, skb)) return; } else { // it's 56k if (make_raw_data_56k(bch, skb)) return; } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst,"tiger fill_dma2: c%d %4x", bch->channel, bch->Flags); if (test_and_clear_bit(FLG_NOFRAME, &bch->Flags)) { write_raw(bch, tiger->sendp, tiger->free); } else if (test_and_clear_bit(FLG_HALF, &bch->Flags)) { /* urky, don't we know this already? */ p = bus_to_virt(inl(card->base + NETJET_DMA_READ_ADR)); sp = tiger->sendp; if (p == tiger->s_end) p = tiger->send.dmabuf -1; if (sp == tiger->s_end) sp = tiger->send.dmabuf -1; cnt = p - sp; if (cnt <0) { write_raw(bch, tiger->sendp, tiger->free); } else { p++; cnt++; if (p > tiger->s_end) p = tiger->send.dmabuf; p++; cnt++; if (p > tiger->s_end) p = tiger->send.dmabuf; write_raw(bch, p, tiger->free - cnt); } } else if (test_and_clear_bit(FLG_EMPTY, &bch->Flags)) { p = bus_to_virt(inl(card->base + NETJET_DMA_READ_ADR)); cnt = tiger->s_end - p; if (cnt < 2) { p = tiger->send.dmabuf + 1; cnt = NETJET_DMA_TXSIZE/2 - 2; } else { p++; p++; if (cnt <= (NETJET_DMA_TXSIZE/2)) cnt += NETJET_DMA_TXSIZE/2; cnt--; cnt--; } write_raw(bch, p, cnt); } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst,"tiger fill_dma3: c%d %4x", bch->channel, bch->Flags); } static void write_raw (channel_t *bch, u_int *buf, int cnt) { u_int mask, val, *p=buf; u_int i, s_cnt; struct tiger_hw *tiger; tiger = bch->hw; if (cnt <= 0) { return; } if (test_bit(FLG_TX_BUSY, &bch->Flags)) { if (tiger->sendcnt > cnt) { s_cnt = cnt; tiger->sendcnt -= cnt; } else { s_cnt = tiger->sendcnt; tiger->sendcnt = 0; } if (bch->channel) mask = 0xffff00ff; else mask = 0xffffff00; for (i=0; ichannel ? ((tiger->sp[i] <<8) & 0xff00) : (tiger->sp[i]); *p &= mask; *p++ |= val; if (p > tiger->s_end) p = tiger->send.dmabuf; } tiger->s_tot += s_cnt; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "tiger write_raw: c%d %p-%p %d/%d %d N/A", bch->channel, buf, p, s_cnt, cnt, tiger->sendcnt/*, bcs->cs->hw.njet.irqstat0*/); if (bch->debug & L1_DEB_HSCX_FIFO) printframe(&bch->inst, tiger->sp, s_cnt, "snd"); tiger->sp += s_cnt; tiger->sendp = p; /* Test to see if we can send more */ if (tiger->sendcnt == 0) { /* XXX old block, all needed? */ tiger->free = cnt - s_cnt; if (tiger->free > (NETJET_DMA_TXSIZE/2)) test_and_set_bit(FLG_HALF, &bch->Flags); else { test_and_clear_bit(FLG_HALF, &bch->Flags); test_and_set_bit(FLG_NOFRAME, &bch->Flags); } /* end old block */ if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } #if 0 if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { bch->tx_skb = bch->next_skb; bch->tx_idx = 0; bch->next_skb = NULL; if (bch->tx_skb) { mISDN_head_t *hh = mISDN_HEAD_P(bch->tx_skb); queue_ch_frame(bch, CONFIRM, hh->dinfo, NULL); netjet_fill_dma(bch); } else { //bch->tx_len = 0; printk(KERN_WARNING "hdlc tx irq TX_NEXT without skb\n"); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } } else { bch->tx_skb = NULL; mask ^= 0xffffffff; if (s_cnt < cnt) { for (i=s_cnt; itiger->s_end) p = tiger->send.dmabuf; } if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "tiger write_raw: fill rest %d", cnt - s_cnt); } test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } #endif } } else if (test_and_clear_bit(FLG_NOFRAME, &bch->Flags)) { test_and_set_bit(FLG_HALF, &bch->Flags); fill_mem(bch, buf, cnt, bch->channel, 0xff); tiger->free += cnt; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "tiger write_raw: fill half"); } else if (test_and_clear_bit(FLG_HALF, &bch->Flags)) { test_and_set_bit(FLG_EMPTY, &bch->Flags); fill_mem(bch, buf, cnt, bch->channel, 0xff); if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "tiger write_raw: fill full"); } } static void got_frame(channel_t *bch, int count) { struct sk_buff *skb; if (bch->rx_skb == NULL) return; 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; } if (bch->debug & L1_DEB_RECEIVE_FRAME) printframe(&bch->inst, skb->data, skb->len, "got_frame"); queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, skb); } static void read_raw_transparent(channel_t *bch, u_int *buf, int cnt) { struct tiger_hw *tiger; int i; u_char val; u_int *pend; u_int *p = buf; u_char *skb_ptr; tiger = bch->hw; pend = tiger->rec.dmabuf + NETJET_DMA_RXSIZE -1; 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 + cnt) > bch->maxlen) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "read_raw_transparent overrun %d", bch->rx_skb->len + cnt); return; } skb_ptr = skb_put(bch->rx_skb, cnt); for (i=0;ichannel ? ((*p>>8) & 0xff) : (*p & 0xff); p++; if (p > pend) p = tiger->rec.dmabuf; skb_ptr[i] = val; } got_frame(bch, cnt); } static void read_raw(channel_t *bch, u_int *buf, int cnt) { int i; u_char j; u_char val; struct tiger_hw *tiger; u_int *pend; u_char state; u_char r_one; u_char r_val; u_int bitcnt; int bits; u_char mask; u_int *p = buf; u_char *skb_ptr; 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; } } skb_ptr = skb_put (bch->rx_skb, 0); tiger = bch->hw; pend = tiger->rec.dmabuf + NETJET_DMA_RXSIZE - 1; state = tiger->r_state; r_one = tiger->r_one; r_val = tiger->r_val; bitcnt = tiger->r_bitcnt; if (test_bit(FLG_HDLC, &bch->Flags)) { mask = 0xff; bits = 8; } else { // it's 56K mask = 0x7f; bits = 7; }; for (i=0;ichannel ? ((*p>>8) & 0xff) : (*p & 0xff); p++; if (p > pend) p = tiger->rec.dmabuf; if ((val & mask) == mask) { state = HDLC_ZERO_SEARCH; tiger->r_tot++; bitcnt = 0; r_one = 0; continue; } for (j=0;jdebug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst,"tiger read_raw: zBit(%d,%d,%d) %x", tiger->r_tot,i,j,val); } } else if (state == HDLC_FLAG_SEARCH) { if (val & 1) { r_one++; if (r_one>6) { state = HDLC_ZERO_SEARCH; } } else { if (r_one==6) { bitcnt = 0; r_val = 0; state = HDLC_FLAG_FOUND; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst,"tiger read_raw: flag(%d,%d,%d) %x", tiger->r_tot,i,j,val); } r_one=0; } } else if (state == HDLC_FLAG_FOUND) { if (val & 1) { r_one++; if (r_one>6) { state=HDLC_ZERO_SEARCH; } else { r_val >>= 1; r_val |= 0x80; bitcnt++; } } else { if (r_one==6) { bitcnt=0; r_val=0; r_one=0; val >>= 1; continue; } else if (r_one!=5) { r_val >>= 1; r_val &= 0x7f; bitcnt++; } r_one=0; } if ((state != HDLC_ZERO_SEARCH) && !(bitcnt & 7)) { state=HDLC_FRAME_FOUND; tiger->r_fcs = PPP_INITFCS; skb_ptr[0] = r_val; tiger->r_fcs = PPP_FCS (tiger->r_fcs, r_val); if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i (N/A)", tiger->r_tot,i,j,r_val,val /*,bcs->cs->hw.njet.irqstat0*/); } } else if (state == HDLC_FRAME_FOUND) { if (val & 1) { r_one++; if (r_one>6) { state=HDLC_ZERO_SEARCH; bitcnt=0; } else { r_val >>= 1; r_val |= 0x80; bitcnt++; } } else { if (r_one==6) { r_val=0; r_one=0; bitcnt++; if (bitcnt & 7) { mISDN_debugprint(&bch->inst, "tiger: frame not byte aligned"); state=HDLC_FLAG_SEARCH; tiger->r_err++; #ifdef ERROR_STATISTIC bcs->err_inv++; #endif } else { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst,"tiger frame end(%d,%d): fcs(%x) i (N/A)", i,j,tiger->r_fcs/*, bcs->cs->hw.njet.irqstat0*/); if (tiger->r_fcs == PPP_GOODFCS) { skb_put (bch->rx_skb, (bitcnt>>3)-3); got_frame(bch, (bitcnt>>3)-3); 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; } } skb_ptr = skb_put (bch->rx_skb, 0); } else { if (bch->debug) { mISDN_debugprint(&bch->inst, "tiger FCS error"); tiger->r_err++; } #ifdef ERROR_STATISTIC bcs->err_crc++; #endif } state=HDLC_FLAG_FOUND; } bitcnt=0; } else if (r_one==5) { val >>= 1; r_one=0; continue; } else { r_val >>= 1; r_val &= 0x7f; bitcnt++; } r_one=0; } if ((state == HDLC_FRAME_FOUND) && !(bitcnt & 7)) { if ((bitcnt>>3) >= bch->maxlen) { mISDN_debugprint(&bch->inst, "tiger: frame too big"); r_val=0; state=HDLC_FLAG_SEARCH; tiger->r_err++; #ifdef ERROR_STATISTIC bcs->err_inv++; #endif } else { skb_ptr[(bitcnt>>3)-1] = r_val; tiger->r_fcs = PPP_FCS (tiger->r_fcs, r_val); } } } val >>= 1; } tiger->r_tot++; } tiger->r_state = state; tiger->r_one = r_one; tiger->r_val = r_val; tiger->r_bitcnt = bitcnt; } /* read_tiger() * Read tiger state and process it * - assumes lock held */ static void read_tiger (netjet_t *card, uint8_t irq_stat) { u_int *p; int cnt = NETJET_DMA_RXSIZE/2; channel_t *bch; if ((irq_stat & card->last_is0) & NETJET_IRQM0_READ_MASK) { printk ("netjet: tiger warn read double dma %x/%x", irq_stat, card->last_is0); return; } else { card->last_is0 &= ~NETJET_IRQM0_READ_MASK; card->last_is0 |= (irq_stat & NETJET_IRQM0_READ_MASK); } if (irq_stat & NETJET_IRQM0_READ_1) { p = card->tiger[0].rec.dmabuf + NETJET_DMA_RXSIZE - 1; } else { p = card->tiger[0].rec.dmabuf + cnt - 1; } /* Note: Unhandled 56K */ bch = &card->bch[0]; if (test_bit(FLG_HDLC, &bch->Flags)) { read_raw(bch, p, cnt); } if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { read_raw_transparent(bch, p, cnt); } bch = &card->bch[1]; if (test_bit(FLG_HDLC, &bch->Flags)) { read_raw(bch, p, cnt); } if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { read_raw_transparent(bch, p, cnt); } } /* write_tiger() * Update tiger state * - assumes lock held */ void write_tiger (netjet_t *card, uint8_t irq_stat) { u_int *p, cnt = NETJET_DMA_TXSIZE/2; if ((irq_stat & card->last_is0) & NETJET_IRQM0_WRITE_MASK) { DPRINT ("netjet: tiger warn write double dma %x/%x", irq_stat, card->last_is0); DPRINT(KERN_WARNING "%s: done A\n", __FUNCTION__); return; } else { card->last_is0 &= ~NETJET_IRQM0_WRITE_MASK; card->last_is0 |= (irq_stat & NETJET_IRQM0_WRITE_MASK); } if (irq_stat & NETJET_IRQM0_WRITE_1) p = card->tiger[0].send.dmabuf + NETJET_DMA_TXSIZE - 1; else p = card->tiger[0].send.dmabuf + cnt - 1; write_tiger_bch(&card->bch[0], p, cnt); write_tiger_bch(&card->bch[1], p, cnt); } static void write_tiger_bch(channel_t *bch, u_int *buf, int cnt) { mISDN_head_t *hh; if (!test_bit(FLG_TRANSPARENT, &bch->Flags) && !test_bit(FLG_HDLC, &bch->Flags)) { if (test_bit(FLG_TX_BUSY, &bch->Flags)) printk(KERN_WARNING "%s: (busy)\n", __FUNCTION__); if (test_bit(FLG_TX_NEXT, &bch->Flags)) printk(KERN_WARNING "%s: (next)\n", __FUNCTION__); return; } write_raw(bch, buf, cnt); if (!test_bit(FLG_TX_BUSY, &bch->Flags) && test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { bch->tx_skb = bch->next_skb; bch->tx_idx = 0; bch->next_skb = NULL; if (bch->tx_skb) { test_and_set_bit(FLG_TX_BUSY, &bch->Flags); hh = mISDN_HEAD_P(bch->tx_skb); queue_ch_frame(bch, CONFIRM, hh->dinfo, NULL); netjet_fill_dma(bch); } else { //bch->tx_len = 0; printk(KERN_WARNING "hdlc tx irq TX_NEXT without skb\n"); test_and_set_bit(FLG_TX_BUSY, &bch->Flags); } } } static irqreturn_t nj_interrupt(int intno, void *dev_id, struct pt_regs *regs) { netjet_t *card = dev_id; u_int8_t val, s1val, s0val; spin_lock(&card->lock); s0val = inb (card->base | NETJET_IRQSTAT0); s1val = inb (card->base | NETJET_IRQSTAT1); if ( ((s1val & NETJET_ISACIRQ) != 0) && (s0val == 0)) { /* no interrupts for us */ /* shared IRQ */ spin_unlock(&card->lock); return IRQ_NONE; } card->irqcnt++; if (!(s1val & NETJET_ISACIRQ)) { val = nj_readISAC(card, ISAC_ISTA); if (val) { mISDN_isac_interrupt(&card->dch, val); nj_writeISAC(card, ISAC_MASK, 0xFF); nj_writeISAC(card, ISAC_MASK, 0x0); } } if (s0val) { /* write to clear */ outb (s0val, card->base | NETJET_IRQSTAT0); } /* set bits in sval to indicate which page is free */ if (inl(card->base | NETJET_DMA_WRITE_ADR) < inl(card->base | NETJET_DMA_WRITE_IRQ)) { /* the 2nd write page is free */ s0val = 0x08; } else { /* the 1st write page is free */ s0val = 0x04; } if (inl(card->base | NETJET_DMA_READ_ADR) < inl(card->base | NETJET_DMA_READ_IRQ)) { /* the 2nd read page is free */ s0val |= 0x02; } else { /* the 1st read page is free */ s0val |= 0x01; } /* test if we have a DMA interrupt */ if (s0val != card->last_is0) { if ((s0val & NETJET_IRQM0_READ_MASK) != (card->last_is0 & NETJET_IRQM0_READ_MASK)) { /* got a read dma int */ read_tiger (card, s0val); } if ((s0val & NETJET_IRQM0_WRITE_MASK) != (card->last_is0 & NETJET_IRQM0_WRITE_MASK)) { /* got a write dma int */ write_tiger (card, s0val); } } spin_unlock(&card->lock); return IRQ_HANDLED; } static int nj_init_card (netjet_t *card) { u_long flags; int ret; spin_lock_irqsave(&card->lock, flags); nj_disable_hwirq(card); spin_unlock_irqrestore(&card->lock, flags); if (request_irq(card->irq, (void*)nj_interrupt, SA_SHIRQ, "NETjet", card)) { printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", card->irq); return (-EIO); } spin_lock_irqsave(&card->lock, flags); nj_reset (card); mISDN_clear_isac (&card->dch); if ((ret=mISDN_isac_init (&card->dch))) { printk(KERN_WARNING "mISDN: mISDN_isac_init failed with %d\n", ret); /* XXX return or retry ? */ spin_unlock_irqrestore(&card->lock, flags); return (-EIO); } inittiger (card); mode_tiger(&card->bch[0], 0, -1); mode_tiger(&card->bch[1], 1, -1); spin_unlock_irqrestore(&card->lock, flags); return 0; } static void nj_release_card(netjet_t *card) { u_long flags; nj_disable_hwirq(card); spin_lock_irqsave(&card->lock, flags); mode_tiger(&card->bch[0], 0, ISDN_PID_NONE); mode_tiger(&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->base, 256); mISDN_freechannel(&card->bch[1]); mISDN_freechannel(&card->bch[0]); mISDN_freechannel(&card->dch); spin_unlock_irqrestore(&card->lock, flags); mISDN_ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); spin_lock_irqsave(&netjet_mISDN.lock, flags); list_del(&card->list); spin_unlock_irqrestore(&netjet_mISDN.lock, flags); pci_disable_device(card->pdev); pci_set_drvdata(card->pdev, NULL); kfree(card); } static int setup_netjet (netjet_t *card) { u_int bytecnt; bytecnt = 256; // NOTE: we use this size in release_card if (!request_region(card->base, bytecnt, "netjet-s isdn")) { printk(KERN_WARNING "mISDN: NETjet config port %#lx-%#lx already in use\n", card->base, card->base + bytecnt -1); return (-EIO); } /* Register D-Channel (ISAC) callbacks */ card->dch.read_reg = &nj_readISAC; card->dch.write_reg = &nj_writeISAC; card->dch.read_fifo = &nj_readISACfifo; card->dch.write_fifo = &nj_writeISACfifo; card->dch.type = ISAC_TYPE_ISAC; card->dch.hw = &card->isac; return 0; } static int __devinit setup_instance (netjet_t *card) { int i, err; mISDN_pid_t pid; u_long flags; DPRINT(KERN_WARNING "NETJet setup_instance: protocol is %x layermask is %x\n", protocol[0], layermask[0]); spin_lock_irqsave(&netjet_mISDN.lock, flags); list_add_tail(&card->list, &netjet_mISDN.ilist); spin_unlock_irqrestore(&netjet_mISDN.lock, flags); card->dch.debug = debug; spin_lock_init(&card->lock); card->dch.inst.hwlock = &card->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->dch.inst.class_dev.parent = &card->pdev->dev; #else card->dch.inst.class_dev.dev = &card->pdev->dev; #endif 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, &netjet_mISDN, card, mISDN_ISAC_l1hw); sprintf(card->dch.inst.name, "NETJet%d", netjet_cnt+1); mISDN_set_dchannel_pid(&pid, protocol[netjet_cnt], layermask[netjet_cnt]); 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, &netjet_mISDN, card, tiger_l2l1B); card->bch[i].inst.pid.layermask = ISDN_LAYER(0); card->bch[i].inst.hwlock = &card->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->bch[i].inst.class_dev.parent = &card->pdev->dev; #else card->bch[i].inst.class_dev.dev = &card->pdev->dev; #endif card->bch[i].debug = debug; sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1); mISDN_initchannel(&card->bch[i], MSK_INIT_BCHANNEL, MAX_DATA_MEM); card->bch[i].hw = &card->tiger[i]; } printk(KERN_DEBUG "NETJet card %p dch %p bch1 %p bch2 %p\n", card, &card->dch, &card->bch[0], &card->bch[1]); err = setup_netjet(card); if (err) { mISDN_freechannel(&card->dch); mISDN_freechannel(&card->bch[1]); mISDN_freechannel(&card->bch[0]); spin_lock_irqsave(&netjet_mISDN.lock, flags); list_del(&card->list); spin_unlock_irqrestore(&netjet_mISDN.lock, flags); kfree(card); return(err); } netjet_cnt++; err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst); if (err) { nj_release_card(card); return(err); } for (i=0; i<2; i++) { err = mISDN_ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst); if (err) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } } err = mISDN_ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid); if (err) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } err = nj_init_card(card); if (err) { mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } mISDN_ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL); printk(KERN_INFO "NETJet %d cards installed\n", netjet_cnt); return(0); } static int __devinit nj_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int err = -ENOMEM; int cfg; netjet_t *card; if (pdev->subsystem_vendor == PCI_VENDOR_ID_INTEL && pdev->subsystem_device == 0x0003) return -ENODEV; if (!(card = kmalloc(sizeof(netjet_t), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for netjet\n"); return(err); } memset(card, 0, sizeof(netjet_t)); card->pdev = pdev; err = pci_enable_device(pdev); if (err) { kfree(card); return(err); } printk(KERN_INFO "nj_probe(mISDN): found adapter %s at %s\n", (char *) ent->driver_data, pci_name(pdev)); pci_set_master (pdev); /* the TJ300 and TJ320 must be detected, the IRQ handling is different * unfortunately the chips use the same device ID, but the TJ320 has * the bit20 in status PCI cfg register set */ pci_read_config_dword(pdev, 0x04, &cfg); if (cfg & 0x00100000) card->subtype = NETJET_S_TJ320; else card->subtype = NETJET_S_TJ300; card->base = pci_resource_start(pdev, 0); card->irq = pdev->irq; pci_set_drvdata(pdev, card); err = setup_instance(card); if (err) pci_set_drvdata(pdev, NULL); return (err); } static void __devexit nj_remove(struct pci_dev *pdev) { netjet_t *card = pci_get_drvdata(pdev); if (card) mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); else printk(KERN_WARNING "%s drvdata already removed\n", __FUNCTION__); } static struct pci_device_id nj_pci_ids[] __devinitdata = { { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) "NETJet S" }, { } }; MODULE_DEVICE_TABLE(pci, nj_pci_ids); static struct pci_driver nj_pci_driver = { name: "netjet", probe: nj_probe, remove: __devexit_p(nj_remove), id_table: nj_pci_ids, }; static int __init nj_init (void) { int err; printk(KERN_INFO "Traverse Tech. NETjet-S driver, revision %s\n", mISDN_getrev(netjet_rev)); #ifdef MODULE netjet_mISDN.owner = THIS_MODULE; #endif spin_lock_init(&netjet_mISDN.lock); INIT_LIST_HEAD(&netjet_mISDN.ilist); netjet_mISDN.name = "NETjet-S"; netjet_mISDN.own_ctrl = nj_manager; netjet_mISDN.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; netjet_mISDN.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; netjet_mISDN.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS; if ((err = mISDN_register (&netjet_mISDN))) { printk(KERN_ERR "Can't register NETJet PCI error(%d)\n", err); return err; } err = pci_register_driver (&nj_pci_driver); if (err < 0) return err; mISDN_module_register(THIS_MODULE); return 0; } static void __exit nj_cleanup (void) { int err; netjet_t *card, *next; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister (&netjet_mISDN))) { printk(KERN_ERR "Can't unregister NETJet PCI error(%d)\n", err); } list_for_each_entry_safe(card, next, &netjet_mISDN.ilist, list) { printk(KERN_ERR "NetJet PCI card struct not empty refs %d\n", netjet_mISDN.refcnt); nj_release_card(card); } pci_unregister_driver (&nj_pci_driver); } module_init (nj_init); module_exit (nj_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/sedl_fax.c0000644000000000000500000006202311135651702020323 0ustar rootsrc/* $Id: sedl_fax.c,v 1.30 2007/02/13 10:43:45 crich Exp $ * * sedl_fax.c low level stuff for Sedlbauer Speedfax + cards * * Copyright (C) 2000,2001 Karsten Keil (kkeil@suse.de) * * Author Karsten Keil (kkeil@suse.de) * * * Thanks to Sedlbauer AG for informations * Marcus Niemann * Edgar Toernig * * This file is (c) under GNU PUBLIC LICENSE * */ /* Supported cards: * Card: Chip: Configuration: Comment: * --------------------------------------------------------------------- * Speed Fax+ ISAC_ISAR ISAPNP Full analog support * Speed Fax+ ISAC_ISAR PCI PNP Full analog support * * Important: * For the sedlbauer speed fax+ to work properly you have to download * the firmware onto the card. */ #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) # include #else # include #endif #ifdef NEW_ISAPNP #include #else #include #endif #include "core.h" #include "channel.h" #include "isac.h" #include "isar.h" #include "layer1.h" #include "helper.h" #include "debug.h" extern const char *CardType[]; const char *Sedlfax_revision = "$Revision: 1.30 $"; const char *Sedlbauer_Types[] = {"None", "speed fax+", "speed fax+ pyramid", "speed fax+ pci"}; #ifndef PCI_VENDOR_ID_TIGERJET #define PCI_VENDOR_ID_TIGERJET 0xe159 #endif #ifndef PCI_DEVICE_ID_TIGERJET_100 #define PCI_DEVICE_ID_TIGERJET_100 0x0002 #endif #define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51 #define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54 #define PCI_SUB_ID_SEDLBAUER 0x01 #define SEDL_SPEEDFAX_ISA 1 #define SEDL_SPEEDFAX_PYRAMID 2 #define SEDL_SPEEDFAX_PCI 3 #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) #define SEDL_ISA_ISAC 4 #define SEDL_ISA_ISAR 6 #define SEDL_ISA_ADR 8 #define SEDL_ISA_RESET_ON 10 #define SEDL_ISA_RESET_OFF 12 #define SEDL_PCI_ADR 0xc8 #define SEDL_PCI_ISAC 0xd0 #define SEDL_PCI_ISAR 0xe0 /* TIGER 100 Registers */ #define TIGER_RESET_ADDR 0x00 #define TIGER_EXTERN_RESET_ON 0x01 #define TIGER_EXTERN_RESET_OFF 0x00 #define TIGER_AUX_CTRL 0x02 #define TIGER_AUX_DATA 0x03 #define TIGER_AUX_IRQMASK 0x05 #define TIGER_AUX_STATUS 0x07 /* Tiger AUX BITs */ #define SEDL_AUX_IOMASK 0xdd /* 1 and 5 are inputs */ #define SEDL_ISAR_RESET_BIT_OFF 0x00 #define SEDL_ISAR_RESET_BIT_ON 0x01 #define SEDL_TIGER_IRQ_BIT 0x02 #define SEDL_ISAR_PCI_LED1_BIT 0x08 #define SEDL_ISAR_PCI_LED2_BIT 0x10 #define SEDL_PCI_RESET_ON (SEDL_ISAR_RESET_BIT_ON) #define SEDL_PCI_RESET_OFF (SEDL_ISAR_PCI_LED1_BIT | SEDL_ISAR_PCI_LED2_BIT) #define SEDL_RESET 0x3 /* same as DOS driver */ /* data struct */ typedef struct _sedl_fax { struct list_head list; 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; u_int cfg; u_int addr; u_int isac; u_int isar; spinlock_t lock; isar_reg_t ir; isac_chip_t isac_hw; isar_hw_t isar_hw[2]; channel_t dch; channel_t bch[2]; } sedl_fax; static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off) { byteout(ale, off); return (bytein(adr)); } static inline void readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { byteout(ale, off); insb(adr, data, size); } static inline void writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) { byteout(ale, off); byteout(adr, data); } static inline void writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { byteout(ale, off); outsb(adr, data, size); } /* Interface functions */ static u_char ReadISAC(void *p, u_char offset) { return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, offset)); } static void WriteISAC(void *p, u_char offset, u_char value) { writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, offset, value); } static void ReadISACfifo(void *p, u_char * data, int size) { readfifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, 0, data, size); } static void WriteISACfifo(void *p, u_char * data, int size) { writefifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, 0, data, size); } /* ISAR access routines * mode = 0 access with IRQ on * mode = 1 access with IRQ off * mode = 2 access with IRQ off and using last offset */ static u_char ReadISAR(void *p, u_char offset) { return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset)); } static void WriteISAR(void *p, u_char offset, u_char 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 do_sedl_interrupt(sedl_fax *sf) { u_char val; int cnt = 8; val = readreg(sf->addr, sf->isar, ISAR_IRQBIT); Start_ISAR: if (val & ISAR_IRQSTA) isar_int_main(&sf->bch[0]); val = readreg(sf->addr, sf->isac, ISAC_ISTA); Start_ISAC: if (val) mISDN_isac_interrupt(&sf->dch, val); val = readreg(sf->addr, sf->isar, ISAR_IRQBIT); if ((val & ISAR_IRQSTA) && cnt) { cnt--; if (sf->dch.debug & L1_DEB_HSCX) printk(KERN_DEBUG "ISAR IntStat after IntRoutine cpu%d\n", smp_processor_id()); goto Start_ISAR; } val = readreg(sf->addr, sf->isac, ISAC_ISTA); if (val && cnt) { cnt--; if (sf->dch.debug & L1_DEB_ISAC) printk(KERN_DEBUG "ISAC IntStat after IntRoutine cpu%d\n", smp_processor_id()); goto Start_ISAC; } if (!cnt) if (sf->dch.debug & L1_DEB_ISAC) printk(KERN_DEBUG "Sedlbauer IRQ LOOP\n"); writereg(sf->addr, sf->isar, ISAR_IRQBIT, 0); writereg(sf->addr, sf->isac, ISAC_MASK, 0xFF); writereg(sf->addr, sf->isac, ISAC_MASK, 0x0); writereg(sf->addr, sf->isar, ISAR_IRQBIT, ISAR_IRQMSK); } static irqreturn_t speedfax_isa_interrupt(int intno, void *dev_id, struct pt_regs *regs) { sedl_fax *sf = dev_id; spin_lock(&sf->lock); sf->irqcnt++; do_sedl_interrupt(sf); spin_unlock(&sf->lock); return IRQ_HANDLED; } static irqreturn_t speedfax_pci_interrupt(int intno, void *dev_id, struct pt_regs *regs) { sedl_fax *sf = dev_id; u_char val; spin_lock(&sf->lock); val = bytein(sf->cfg + TIGER_AUX_STATUS); if (val & SEDL_TIGER_IRQ_BIT) { /* for us or shared ? */ spin_unlock(&sf->lock); return IRQ_NONE; /* shared */ } sf->irqcnt++; do_sedl_interrupt(sf); 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) { int bytecnt = 256; if (sf->subtyp == SEDL_SPEEDFAX_ISA) bytecnt = 16; if (sf->cfg) release_region(sf->cfg, bytecnt); } static void reset_speedfax(sedl_fax *sf) { printk(KERN_INFO "Sedlbauer: resetting card\n"); if (sf->subtyp == SEDL_SPEEDFAX_ISA) { byteout(sf->cfg + SEDL_ISA_RESET_ON, SEDL_RESET); mdelay(1); byteout(sf->cfg + SEDL_ISA_RESET_OFF, 0); mdelay(1); } else { byteout(sf->cfg + TIGER_RESET_ADDR, TIGER_EXTERN_RESET_ON); byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_ON); mdelay(1); byteout(sf->cfg + TIGER_RESET_ADDR, TIGER_EXTERN_RESET_OFF); byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_OFF); mdelay(1); } } static int init_card(sedl_fax *sf) { int cnt = 3; u_long flags; u_int shared = SA_SHIRQ; void *irq_func = speedfax_pci_interrupt; if (sf->subtyp == SEDL_SPEEDFAX_ISA) { irq_func = speedfax_isa_interrupt; shared = 0; } if (request_irq(sf->irq, irq_func, shared, "speedfax", sf)) { printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", sf->irq); return(-EIO); } spin_lock_irqsave(&sf->lock, flags); while (cnt) { int ret; mISDN_clear_isac(&sf->dch); if ((ret=mISDN_isac_init(&sf->dch))) { printk(KERN_WARNING "mISDN: mISDN_isac_init failed with %d\n", ret); break; } init_isar(&sf->bch[0]); init_isar(&sf->bch[1]); enable_hwirq(sf); /* RESET Receiver and Transmitter */ WriteISAC(sf, ISAC_CMDR, 0x41); spin_unlock_irqrestore(&sf->lock, flags); current->state = TASK_UNINTERRUPTIBLE; /* Timeout 10ms */ schedule_timeout((10*HZ)/1000); printk(KERN_INFO "%s: IRQ %d count %d\n", sf->dch.inst.name, sf->irq, sf->irqcnt); if (!sf->irqcnt) { printk(KERN_WARNING "Sedlbauer speedfax: IRQ(%d) getting no interrupts during init %d\n", sf->irq, 4 - cnt); if (cnt == 1) { return (-EIO); } else { spin_lock_irqsave(&sf->lock, flags); reset_speedfax(sf); cnt--; } } else { return(0); } } spin_unlock_irqrestore(&sf->lock, flags); return(-EIO); } #define MAX_CARDS 4 static int sedl_cnt; static mISDNobject_t speedfax; static uint debug; static uint protocol[MAX_CARDS]; static uint layermask[MAX_CARDS]; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #define MODULE_PARM_T "1-4i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #else module_param (debug, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC (debug, "sedlfax debug mask"); static uint protocol_num; static uint layermask_num; #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 #endif static char SpeedfaxName[] = "Speedfax"; int setup_speedfax(sedl_fax *sf) { 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")) { printk(KERN_WARNING "mISDN: %s config port %x-%x already in use\n", "Speedfax +", sf->cfg, sf->cfg + bytecnt - 1); return(-EIO); } sf->dch.read_reg = &ReadISAC; sf->dch.write_reg = &WriteISAC; sf->dch.read_fifo = &ReadISACfifo; sf->dch.write_fifo = &WriteISACfifo; sf->dch.hw = &sf->isac_hw; if (sf->subtyp != SEDL_SPEEDFAX_ISA) { sf->addr = sf->cfg + SEDL_PCI_ADR; sf->isac = sf->cfg + SEDL_PCI_ISAC; sf->isar = sf->cfg + SEDL_PCI_ISAR; byteout(sf->cfg + TIGER_RESET_ADDR, 0xff); mdelay(1); byteout(sf->cfg + TIGER_RESET_ADDR, 0x00); mdelay(1); byteout(sf->cfg + TIGER_AUX_CTRL, SEDL_AUX_IOMASK); byteout(sf->cfg + TIGER_AUX_IRQMASK, 0); byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_ON); mdelay(1); byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_OFF); mdelay(1); } else { sf->addr = sf->cfg + SEDL_ISA_ADR; sf->isac = sf->cfg + SEDL_ISA_ISAC; sf->isar = sf->cfg + SEDL_ISA_ISAR; } sf->isar_hw[0].reg = &sf->ir; 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[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:"); spin_unlock_irqrestore(&sf->lock, flags); if (ver < 0) { printk(KERN_WARNING "Sedlbauer: wrong ISAR version (ret = %d)\n", ver); release_sedlbauer(sf); return (-EIO); } return (0); } static void release_card(sedl_fax *card) { u_long flags; 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); release_sedlbauer(card); mISDN_freechannel(&card->bch[1]); mISDN_freechannel(&card->bch[0]); mISDN_freechannel(&card->dch); spin_unlock_irqrestore(&card->lock, flags); mISDN_ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); spin_lock_irqsave(&speedfax.lock, flags); list_del(&card->list); spin_unlock_irqrestore(&speedfax.lock, flags); if (card->subtyp == SEDL_SPEEDFAX_ISA) { #if defined(CONFIG_PNP) pnp_disable_dev(card->dev.pnp); pnp_set_drvdata(card->dev.pnp, NULL); #endif } else { pci_disable_device(card->dev.pci); pci_set_drvdata(card->dev.pci, NULL); } kfree(card); sedl_cnt--; } static int speedfax_manager(void *data, u_int prim, void *arg) { sedl_fax *card; mISDNinstance_t *inst=data; int channel = -1; struct sk_buff *skb; u_long flags; 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; break; } if (&card->bch[0].inst == inst) { channel = 0; break; } if (&card->bch[1].inst == inst) { channel = 1; 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) mISDN_setpara(&card->dch, &inst->st->para); else mISDN_setpara(&card->bch[channel], &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: 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 printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: if (channel == 2) mISDN_setpara(&card->dch, arg); else mISDN_setpara(&card->bch[channel], arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { release_card(card); } else { speedfax.refcnt--; } break; #ifdef OBSOLETE 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, isar_down, NULL, &card->bch[channel])); case MGR_DISCONNECT | REQUEST: case MGR_DISCONNECT | INDICATION: return(mISDN_DisConnectIF(inst, arg)); #endif case MGR_LOADFIRM | REQUEST: { struct firm { int len; void *data; } *firm = arg; if (!arg) return(-EINVAL); return(isar_load_firmware(&card->bch[0], firm->data, firm->len)); } case MGR_LOADFIRM | CONFIRM: mISDN_ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL); break; 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 (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)) 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 "speedfax_manager prim %x not handled\n", prim); return(-EINVAL); } return(0); } 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; 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; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->dch.inst.class_dev.parent = dev; #else card->dch.inst.class_dev.dev = dev; #endif 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_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, isar_down); card->bch[i].inst.pid.layermask = ISDN_LAYER(0); card->bch[i].inst.hwlock = &card->lock; card->bch[i].debug = debug; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->bch[i].inst.class_dev.parent = dev; #else card->bch[i].inst.class_dev.dev = dev; #endif sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1); 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_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); } sedl_cnt++; err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst); if (err) { release_card(card); return(err); } mISDN_ctrl(card->dch.inst.st, MGR_STOPSTACK | REQUEST, NULL); for (i=0; i<2; i++) { err = mISDN_ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst); if (err) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } } err = mISDN_ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid); if (err) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } err = init_card(card); if (err) { mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } mISDN_ctrl(card->dch.inst.st, MGR_STARTSTACK | REQUEST, NULL); printk(KERN_INFO "SpeedFax %d cards installed\n", sedl_cnt); return(0); } static int __devinit sedlpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int err = -ENOMEM; sedl_fax *card; if (!(card = kmalloc(sizeof(sedl_fax), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for Speedfax + PCI\n"); return(err); } memset(card, 0, sizeof(sedl_fax)); card->dev.pci = pdev; if (PCI_SUBVENDOR_SPEEDFAX_PYRAMID == pdev->subsystem_vendor) card->subtyp = SEDL_SPEEDFAX_PYRAMID; else card->subtyp = SEDL_SPEEDFAX_PCI; err = pci_enable_device(pdev); if (err) { kfree(card); return(err); } printk(KERN_INFO "mISDN: sedlpci found adapter %s at %s\n", (char *) ent->driver_data, pci_name(pdev)); card->cfg = pci_resource_start(pdev, 0); card->irq = pdev->irq; pci_set_drvdata(pdev, card); err = setup_instance(card); if (err) pci_set_drvdata(pdev, NULL); return(err); } #if defined(CONFIG_PNP) #ifdef NEW_ISAPNP static int __devinit sedlpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) #else static int __devinit sedlpnp_probe(struct pci_dev *pdev, const struct isapnp_device_id *dev_id) #endif { int err; sedl_fax *card; if (!pdev) return(-ENODEV); if (!(card = kmalloc(sizeof(sedl_fax), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for Speedfax + PnP\n"); return(-ENOMEM); } memset(card, 0, sizeof(sedl_fax)); card->subtyp = SEDL_SPEEDFAX_ISA; card->dev.pnp = pdev; pnp_disable_dev(pdev); err = pnp_activate_dev(pdev); if (err<0) { printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __FUNCTION__, (char *)dev_id->driver_data, err); kfree(card); return(err); } card->cfg = pnp_port_start(pdev, 0); card->irq = pnp_irq(pdev, 0); printk(KERN_INFO "mISDN: sedlpnp_probe found adapter %s at IO %#x irq %d\n", (char *)dev_id->driver_data, card->addr, card->irq); pnp_set_drvdata(pdev, card); err = setup_instance(card); if (err) pnp_set_drvdata(pdev, NULL); return(err); } #endif /* CONFIG_PNP */ static void __devexit sedl_remove_pci(struct pci_dev *pdev) { sedl_fax *card = pci_get_drvdata(pdev); if (card) mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); else printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); } #if defined(CONFIG_PNP) #ifdef NEW_ISAPNP static void __devexit sedl_remove_pnp(struct pnp_dev *pdev) #else static void __devexit sedl_remove_pnp(struct pci_dev *pdev) #endif { sedl_fax *card = pnp_get_drvdata(pdev); if (card) mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); else printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); } #endif static struct pci_device_id sedlpci_ids[] __devinitdata = { { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER, 0, 0, (unsigned long) "Pyramid Speedfax + PCI" }, { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0, (unsigned long) "Sedlbauer Speedfax + PCI" }, { } }; MODULE_DEVICE_TABLE(pci, sedlpci_ids); static struct pci_driver sedlpci_driver = { name: "speedfax pci", probe: sedlpci_probe, remove: __devexit_p(sedl_remove_pci), id_table: sedlpci_ids, }; #if defined(CONFIG_PNP) #ifdef NEW_ISAPNP static struct pnp_device_id sedlpnp_ids[] __devinitdata = { { .id = "SAG0002", .driver_data = (unsigned long) "Speedfax + PnP", }, }; static struct pnp_driver sedlpnp_driver = { #else static struct isapnp_device_id sedlpnp_ids[] __devinitdata = { { ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02), ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02), (unsigned long) "Speedfax + PnP" }, { } }; MODULE_DEVICE_TABLE(isapnp, sedlpnp_ids); static struct isapnp_driver sedlpnp_driver = { #endif name: "speedfax pnp", probe: sedlpnp_probe, remove: __devexit_p(sedl_remove_pnp), id_table: sedlpnp_ids, }; #endif /* CONFIG_PNP */ static int __init Speedfax_init(void) { int err; #ifdef OLD_PCI_REGISTER_DRIVER int pci_nr_found; #endif #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; speedfax.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; speedfax.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC | ISDN_PID_L1_B_T30FAX | ISDN_PID_L1_B_MODEM_ASYNC; speedfax.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_T30; if ((err = mISDN_register(&speedfax))) { printk(KERN_ERR "Can't register Speedfax error(%d)\n", err); return(err); } err = pci_register_driver(&sedlpci_driver); if (err < 0) goto out; #ifdef OLD_PCI_REGISTER_DRIVER pci_nr_found = err; #endif #if defined(CONFIG_PNP) err = pnp_register_driver(&sedlpnp_driver); if (err < 0) goto out_unregister_pci; #endif #ifdef OLD_PCI_REGISTER_DRIVER #if !defined(CONFIG_HOTPLUG) || defined(MODULE) if (pci_nr_found + err == 0) { err = -ENODEV; goto out_unregister_isapnp; } #endif #endif mISDN_module_register(THIS_MODULE); return 0; #ifdef OLD_PCI_REGISTER_DRIVER #if !defined(CONFIG_HOTPLUG) || defined(MODULE) out_unregister_isapnp: #if defined(CONFIG_PNP) pnp_unregister_driver(&sedlpnp_driver); #endif #endif #endif #if defined(CONFIG_PNP) out_unregister_pci: #endif pci_unregister_driver(&sedlpci_driver); out: return err; } static void __exit Speedfax_cleanup(void) { int err; sedl_fax *card, *next; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&speedfax))) { printk(KERN_ERR "Can't unregister Speedfax PCI error(%d)\n", err); } list_for_each_entry_safe(card, next, &speedfax.ilist, list) { printk(KERN_ERR "Speedfax PCI card struct not empty refs %d\n", speedfax.refcnt); release_card(card); } #if defined(CONFIG_PNP) pnp_unregister_driver(&sedlpnp_driver); #endif pci_unregister_driver(&sedlpci_driver); } module_init(Speedfax_init); module_exit(Speedfax_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/l3helper.c0000644000000000000500000001463211110524073020250 0ustar rootsrc/* $Id: l3helper.c,v 1.8 2006/12/27 18:50:50 jolly Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * L3 data struct helper functions * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include "dss1.h" #include "helper.h" static signed char _mISDN_l3_ie2pos[128] = { -1,-1,-1,-1, 0,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1, 2,-1,-1,-1, 3,-1,-1,-1, 4,-1,-1,-1, 5,-1, 6,-1, 7,-1,-1,-1,-1,-1,-1, 8, 9,10,-1,-1,11,-1,-1,-1, -1,-1,-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 13,-1,14,15,16,17,18,19,-1,-1,-1,-1,20,21,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,23,-1,-1, 24,25,-1,-1,26,-1,27,-1,28,29,-1,-1,30,31,32,-1 }; static unsigned char _mISDN_l3_pos2ie[33] = { 0x04, 0x08, 0x10, 0x14, 0x18, 0x1c, 0x1e, 0x20, 0x27, 0x28, 0x29, 0x2c, 0x34, 0x40, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x4c, 0x4d, 0x6c, 0x6d, 0x70, 0x71, 0x74, 0x76, 0x78, 0x79, 0x7c, 0x7d, 0x7e }; signed int mISDN_l3_ie2pos(u_char c) { if (c>0x7f) return(-1); return(_mISDN_l3_ie2pos[c]); } unsigned char mISDN_l3_pos2ie(int pos) { return(_mISDN_l3_pos2ie[pos]); } void mISDN_initQ931_info(Q931_info_t *qi) { memset(qi, 0, sizeof(Q931_info_t)); }; struct sk_buff * #ifdef MISDN_MEMDEBUG __mid_alloc_l3msg(int len, u_char type, char *fn, int line) #else mISDN_alloc_l3msg(int len, u_char type) #endif { struct sk_buff *skb; Q931_info_t *qi; #ifdef MISDN_MEMDEBUG if (!(skb = __mid_alloc_skb(len + L3_EXTRA_SIZE +1, GFP_ATOMIC, fn, line))) { #else if (!(skb = alloc_skb(len + L3_EXTRA_SIZE +1, GFP_ATOMIC))) { #endif printk(KERN_WARNING "mISDN: No skb for L3\n"); return (NULL); } qi = (Q931_info_t *)skb_put(skb, L3_EXTRA_SIZE +1); mISDN_initQ931_info(qi); qi->type = type; return (skb); } void mISDN_AddvarIE(struct sk_buff *skb, u_char *ie) { 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; ps += L3_EXTRA_SIZE; if (*ie & 0x80) { /* one octett IE */ if (*ie == IE_MORE_DATA) ies = &qi->more_data; else if (*ie == IE_COMPLETE) ies = &qi->sending_complete; else if ((*ie & 0xf0) == IE_CONGESTION) ies = &qi->congestion_level; else { 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) { int_error(); 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->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; 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) ies = &qi->more_data; else if (ie == IE_COMPLETE) ies = &qi->sending_complete; else if ((ie & 0xf0) == IE_CONGESTION) ies = &qi->congestion_level; else { int_error(); return; } if (ies->off) { /* already used, no dupes for single octett */ int_error(); return; } l = 0; } else { if (!iep || !iep[0]) return; ies = &qi->bearer_capability; if (_mISDN_l3_ie2pos[ie]<0) { int_error(); 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->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[128]; ie_info_t *ies; int i,j; Q931_info_t *qi = (Q931_info_t *)skb->data; mISDN_head_t *hh; hh = mISDN_HEAD_P(skb); printk(KERN_DEBUG "L3Msg prim(%x) id(%x) len(%d)\n", hh->prim, hh->dinfo, skb->len); if (skb->len < sizeof(Q931_info_t)) return; ies = &qi->bearer_capability; ps = (u_char *) qi; ps += L3_EXTRA_SIZE; printk(KERN_DEBUG "L3Msg type(%02x) qi(%p) ies(%p) ps(%p)\n", qi->type, qi, ies, ps); for (i=0;i<33;i++) { if (ies[i].off) { p = ps + ies[i].off; t = tmp; *t = 0; for (j=0; j40) { sprintf(t, " ..."); break; } t += sprintf(t, " %02x", p[j+2]); } 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; jext[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; j40) { 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); } } } } EXPORT_SYMBOL(mISDN_l3_pos2ie); EXPORT_SYMBOL(mISDN_l3_ie2pos); EXPORT_SYMBOL(mISDN_initQ931_info); #ifdef MISDN_MEMDEBUG EXPORT_SYMBOL(__mid_alloc_l3msg); #else 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); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/lapd.h0000644000000000000500000001502011110524073017447 0ustar rootsrc/* * vISDN LAPD/q.921 protocol implementation * * Copyright (C) 2004-2006 Daniele Orlandi * * Authors: Daniele "Vihai" Orlandi * * This program is free software and may be modified and distributed * under the terms and conditions of the GNU General Public License. * */ #ifndef _LAPD_H #define _LAPD_H #include #ifdef __KERNEL__ #include #else #include #endif #ifndef ARPHRD_LAPD #define ARPHRD_LAPD 8445 #endif #ifndef ETH_P_LAPD #define ETH_P_LAPD 0x0030 /* LAPD pseudo type */ #endif #ifndef AF_LAPD #define AF_LAPD 30 #endif #ifndef PF_LAPD #define PF_LAPD AF_LAPD #endif #ifndef SOL_LAPD #define SOL_LAPD 300 #endif #define LAPD_SAPI_Q931 0x00 #define LAPD_SAPI_X25 0x0f #define LAPD_SAPI_MGMT 0x10 #define LAPD_BROADCAST_TEI 127 #define LAPD_DYNAMIC_TEI 255 #define LAPD_DEV_IOC_ACTIVATE _IOR(0xd0, 0x00, unsigned int) #define LAPD_DEV_IOC_DEACTIVATE _IOR(0xd0, 0x01, unsigned int) enum { LAPD_INTF_TYPE = 0, LAPD_INTF_MODE = 1, LAPD_INTF_ROLE = 2, LAPD_TEI = 3, LAPD_SAPI = 4, LAPD_TEI_MGMT_STATUS = 5, LAPD_TEI_MGMT_T201 = 6, LAPD_TEI_MGMT_N202 = 7, LAPD_TEI_MGMT_T202 = 8, LAPD_DLC_STATE = 9, LAPD_T200 = 10, LAPD_N200 = 11, LAPD_T203 = 12, LAPD_N201 = 13, LAPD_K = 14, }; enum lapd_intf_type { LAPD_INTF_TYPE_BRA = 0, LAPD_INTF_TYPE_PRA = 1, }; enum lapd_intf_mode { LAPD_INTF_MODE_POINT_TO_POINT = 0, LAPD_INTF_MODE_MULTIPOINT = 1 }; enum lapd_intf_role { LAPD_INTF_ROLE_TE = 0, LAPD_INTF_ROLE_NT = 1 }; struct sockaddr_lapd { sa_family_t sal_family; __u8 sal_tei; }; enum lapd_primitive_type { LAPD_PH_DATA_REQUEST = 1, LAPD_PH_DATA_INDICATION, LAPD_PH_ACTIVATE_INDICATION, LAPD_PH_DEACTIVATE_INDICATION, LAPD_PH_ACTIVATE_REQUEST, LAPD_MPH_ERROR_INDICATION, LAPD_MPH_ACTIVATE_INDICATION, LAPD_MPH_DEACTIVATE_INDICATION, LAPD_MPH_DEACTIVATE_REQUEST, LAPD_MPH_INFORMATION_INDICATION, }; enum lapd_mph_information_indication { LAPD_MPH_II_CONNECTED, LAPD_MPH_II_DISCONNECTED, }; struct lapd_prim_hdr { __u8 primitive_type; __u8 reserved[3]; }; struct lapd_ctrl_hdr { __u8 param; }; #ifdef __KERNEL__ #include #include #include #include #include #define lapd_MODULE_NAME "lapd" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define LAPD_HASHBITS 8 #define LAPD_HASHSIZE ((1 << LAPD_HASHBITS) - 1) #define LAPD_SK_STATE_NULL TCP_LAST_ACK #define LAPD_SK_STATE_LISTEN TCP_LISTEN #define LAPD_SK_STATE_NORMAL_DLC TCP_ESTABLISHED #define LAPD_SK_STATE_NORMAL_DLC_CLOSING TCP_CLOSING #define LAPD_SK_STATE_BROADCAST_DLC TCP_SYN_SENT #define LAPD_SK_STATE_MGMT TCP_SYN_RECV #define LAPD_SK_STATE_CLOSE TCP_CLOSE #ifdef DEBUG_CODE #define lapd_debug(format, arg...) \ printk(KERN_DEBUG \ "lapd: " \ format, \ ## arg) #define lapd_debug_ls(ls, format, arg...) \ SOCK_DEBUG(&ls->sk, \ "lapd: " \ "%s " \ format, \ (ls)->dev ? (ls)->dev->dev->name : "", \ ## arg) #else #define lapd_debug(ls, format, arg...) do { } while (0) #define lapd_debug_ls(ls, format, arg...) do { } while (0) #define lapd_debug_dev(ls, format, arg...) do { } while (0) #endif #define lapd_msg(lvl, format, arg...) \ printk(lvl "lapd: " \ format, \ ## arg) #define lapd_msg_ls(ls, lvl, format, arg...) \ printk(lvl "lapd: " \ "%s: " \ format, \ (ls)->dev ? (ls)->dev->dev->name : "", \ ## arg) extern struct hlist_head lapd_hash[LAPD_HASHSIZE]; extern rwlock_t lapd_hash_lock; // Do not changes these values, user mode binary compatibility needs them enum lapd_datalink_state { LAPD_DLS_NULL = 0, LAPD_DLS_1_TEI_UNASSIGNED = 1, LAPD_DLS_2_AWAITING_TEI = 2, LAPD_DLS_3_ESTABLISH_AWAITING_TEI = 3, LAPD_DLS_4_TEI_ASSIGNED = 4, LAPD_DLS_5_AWAITING_ESTABLISH = 5, LAPD_DLS_6_AWAITING_RELEASE = 6, LAPD_DLS_7_LINK_CONNECTION_ESTABLISHED = 7, LAPD_DLS_8_TIMER_RECOVERY = 8, LAPD_DLS_LISTENING = 0xFF, }; enum lapd_mdl_error_indications { LAPD_MDL_ERROR_INDICATION_A = (1 << 0), LAPD_MDL_ERROR_INDICATION_B = (1 << 1), LAPD_MDL_ERROR_INDICATION_C = (1 << 2), LAPD_MDL_ERROR_INDICATION_D = (1 << 3), LAPD_MDL_ERROR_INDICATION_E = (1 << 4), LAPD_MDL_ERROR_INDICATION_F = (1 << 5), LAPD_MDL_ERROR_INDICATION_G = (1 << 6), LAPD_MDL_ERROR_INDICATION_H = (1 << 7), LAPD_MDL_ERROR_INDICATION_I = (1 << 8), LAPD_MDL_ERROR_INDICATION_J = (1 << 9), LAPD_MDL_ERROR_INDICATION_K = (1 << 10), LAPD_MDL_ERROR_INDICATION_L = (1 << 11), LAPD_MDL_ERROR_INDICATION_M = (1 << 12), LAPD_MDL_ERROR_INDICATION_N = (1 << 13), LAPD_MDL_ERROR_INDICATION_O = (1 << 14), }; enum lapd_format_errors { LAPD_FE_LENGTH, LAPD_FE_N201, LAPD_FE_UNDEFINED_COMMAND, LAPD_FE_I_FIELD_NOT_PERMITTED, }; struct lapd_sap { atomic_t refcnt; // SAP parameters int k; int N200; int N201; int T200; int T203; }; //#include "device.h" struct lapd_new_dlc { struct hlist_node node; struct lapd_sock *lapd_sock; }; static inline struct lapd_sap *lapd_sap_alloc(void) { return kmalloc(sizeof(struct lapd_sap), GFP_ATOMIC); } static inline void lapd_sap_hold( struct lapd_sap *entity) { atomic_inc(&entity->refcnt); } static inline void lapd_sap_put( struct lapd_sap *entity) { if (atomic_dec_and_test(&entity->refcnt)) kfree(entity); } struct lapd_sock { struct sock sk; struct lapd_device *dev; struct sk_buff_head u_queue; int retrans_cnt; struct timer_list timer_T200; struct timer_list timer_T203; u8 v_s; u8 v_r; u8 v_a; enum lapd_datalink_state state; int peer_receiver_busy; int own_receiver_busy; int reject_exception; int acknowledge_pending; int layer_3_initiated; // ------------------ struct lapd_sap *sap; struct lapd_utme *usr_tme; int tei; int sapi; struct hlist_head new_dlcs; }; #define to_lapd_sock(obj) container_of(obj, struct lapd_sock, sk) enum lapd_dl_primitive_type { LAPD_DL_RELEASE_INDICATION, LAPD_DL_RELEASE_CONFIRM, LAPD_DL_ESTABLISH_INDICATION, LAPD_DL_ESTABLISH_CONFIRM, }; struct lapd_dl_primitive { enum lapd_dl_primitive_type type; int param; }; struct lapd_sock *lapd_new_sock( struct lapd_sock *parent_lapd_sock, u8 tei, int sapi); void lapd_mdl_error_indication( struct lapd_sock *lapd_sock, unsigned long indication); void lapd_dl_primitive( struct lapd_sock *lapd_sock, enum lapd_dl_primitive_type type, int param); int lapd_dl_unit_data_indication( struct lapd_sock *lapd_sock, struct sk_buff *skb); void lapd_dl_data_indication( struct lapd_sock *lapd_sock, struct sk_buff *skb); #endif #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/stack.c0000644000000000000500000010257711137306013017646 0ustar rootsrc/* $Id: stack.c,v 1.21 2006/10/09 12:51:33 crich Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * This file is released under the GPLv2 * */ #include "core.h" static LIST_HEAD(mISDN_stacklist); static rwlock_t stacklist_lock = RW_LOCK_UNLOCKED; static LIST_HEAD(mISDN_instlist); static rwlock_t instlist_lock = RW_LOCK_UNLOCKED; int get_stack_cnt(void) { int cnt = 0; mISDNstack_t *st; read_lock(&stacklist_lock); list_for_each_entry(st, &mISDN_stacklist, list) cnt++; read_unlock(&stacklist_lock); return(cnt); } void get_stack_info(struct sk_buff *skb) { mISDN_head_t *hp; mISDNstack_t *cst, *st; stack_info_t *si; int i; hp = mISDN_HEAD_P(skb); if (hp->addr == 0) { hp->dinfo = get_stack_cnt(); hp->len = 0; return; } else if (hp->addr <= 127 && hp->addr <= get_stack_cnt()) { /* stack nr */ i = 1; read_lock(&stacklist_lock); list_for_each_entry(st, &mISDN_stacklist, list) { if (i == hp->addr) break; i++; } read_unlock(&stacklist_lock); } else st = get_stack4id(hp->addr); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: addr(%08x) st(%p) id(%08x)\n", __FUNCTION__, hp->addr, st, st ? st->id : 0); if (!st) { hp->len = -ENODEV; return; } else { si = (stack_info_t *)skb->data; memset(si, 0, sizeof(stack_info_t)); si->id = st->id; si->extentions = st->extentions; if (st->mgr) si->mgr = st->mgr->id; else si->mgr = 0; memcpy(&si->pid, &st->pid, sizeof(mISDN_pid_t)); memcpy(&si->para, &st->para, sizeof(mISDN_stPara_t)); if (st->clone) si->clone = st->clone->id; else si->clone = 0; if (st->master) si->master = st->master->id; else si->master = 0; si->instcnt = 0; for (i = 0; i <= MAX_LAYER_NR; i++) { if (st->i_array[i]) { si->inst[si->instcnt] = st->i_array[i]->id; si->instcnt++; } } si->childcnt = 0; list_for_each_entry(cst, &st->childlist, list) { si->child[si->childcnt] = cst->id; si->childcnt++; } hp->len = sizeof(stack_info_t); if (si->childcnt>2) hp->len += (si->childcnt-2)*sizeof(int); } skb_put(skb, hp->len); } static int get_free_stackid(mISDNstack_t *mst, int flag) { u_int id = 0, found; mISDNstack_t *st; if (!mst) { while(id < STACK_ID_MAX) { found = 0; id += STACK_ID_INC; read_lock(&stacklist_lock); list_for_each_entry(st, &mISDN_stacklist, list) { if (st->id == id) { found++; break; } } read_unlock(&stacklist_lock); if (!found) return(id); } } else if (flag & FLG_CLONE_STACK) { id = mst->id | FLG_CLONE_STACK; while(id < CLONE_ID_MAX) { found = 0; id += CLONE_ID_INC; st = mst->clone; while (st) { if (st->id == id) { found++; break; } st = st->clone; } if (!found) return(id); } } else if (flag & FLG_CHILD_STACK) { id = mst->id | FLG_CHILD_STACK; while(id < CHILD_ID_MAX) { id += CHILD_ID_INC; found = 0; list_for_each_entry(st, &mst->childlist, list) { if (st->id == id) { found++; break; } } if (!found) return(id); } } return(0); } mISDNstack_t * get_stack4id(u_int id) { mISDNstack_t *cst, *st; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "get_stack4id(%x)\n", id); if (!id) /* 0 isn't a valid id */ return(NULL); read_lock(&stacklist_lock); list_for_each_entry(st, &mISDN_stacklist, list) { if (id == st->id) { read_unlock(&stacklist_lock); return(st); } if ((id & FLG_CHILD_STACK) && ((id & MASTER_ID_MASK) == st->id)) { list_for_each_entry(cst, &st->childlist, list) { if (cst->id == id) { read_unlock(&stacklist_lock); return(cst); } } } if ((id & FLG_CLONE_STACK) && ((id & MASTER_ID_MASK) == st->id)) { cst = st->clone; while (cst) { if (cst->id == id) { read_unlock(&stacklist_lock); return(cst); } cst = cst->clone; } } } read_unlock(&stacklist_lock); return(NULL); } mISDNinstance_t * getlayer4lay(mISDNstack_t *st, int layermask) { int i; if (!st) { int_error(); return(NULL); } for (i = 0; i <= MAX_LAYER_NR; i++) { if (st->i_array[i] && (st->i_array[i]->pid.layermask & layermask)) return(st->i_array[i]); } return(NULL); } static mISDNinstance_t * get_nextlayer(mISDNstack_t *st, u_int addr) { mISDNinstance_t *inst=NULL; int layer = addr & LAYER_ID_MASK; if (!(addr & FLG_MSG_TARGET)) { switch(addr & MSG_DIR_MASK) { case FLG_MSG_DOWN: if (addr & FLG_MSG_CLONED) { /* OK */ } else layer -= LAYER_ID_INC; break; case FLG_MSG_UP: if (addr & FLG_MSG_CLONED) { /* OK */ } else layer += LAYER_ID_INC; break; case MSG_TO_OWNER: break; default: /* broadcast */ int_errtxt("st(%08x) addr(%08x) wrong address", st->id, addr); return(NULL); } } if ((layer < 0) || (layer > MAX_LAYER_NR)) { int_errtxt("st(%08x) addr(%08x) layer %d out of range", st->id, addr, layer); return(NULL); } inst = st->i_array[layer]; if (core_debug & DEBUG_QUEUE_FUNC) printk(KERN_DEBUG "%s: st(%08x) addr(%08x) -> inst(%08x)\n", __FUNCTION__, st->id, addr, inst ? inst->id : 0); return(inst); } mISDNinstance_t * get_instance(mISDNstack_t *st, int layer_nr, int protocol) { mISDNinstance_t *inst=NULL; int i; if (!st) { int_error(); return(NULL); } if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "get_instance st(%08x) lnr(%d) prot(%x)\n", st->id, layer_nr, protocol); if ((layer_nr < 0) || (layer_nr > MAX_LAYER_NR)) { int_errtxt("lnr %d", layer_nr); return(NULL); } list_for_each_entry(inst, &st->prereg, list) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "get_instance prereg(%p, %x) lm %x/%x prot %x/%x\n", inst, inst->id, inst->pid.layermask, ISDN_LAYER(layer_nr), inst->pid.protocol[layer_nr], protocol); if ((inst->pid.layermask & ISDN_LAYER(layer_nr)) && (inst->pid.protocol[layer_nr] == protocol)) { i = register_layer(st, inst); if (i) { int_errtxt("error(%d) register preregistered inst(%08x) on st(%08x)", i, inst->id, st->id); return(NULL); } return(inst); } } for (i = 0; i <= MAX_LAYER_NR; i++) { if ((inst = st->i_array[i])) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "get_instance inst%d(%p, %x) lm %x/%x prot %x/%x\n", i,inst, inst->id, inst->pid.layermask, ISDN_LAYER(layer_nr), inst->pid.protocol[layer_nr], protocol); if ((inst->pid.layermask & ISDN_LAYER(layer_nr)) && (inst->pid.protocol[layer_nr] == protocol)) return(inst); } } return(NULL); } mISDNinstance_t * get_instance4id(u_int id) { mISDNinstance_t *inst; read_lock(&instlist_lock); list_for_each_entry(inst, &mISDN_instlist, list) if (inst->id == id) { read_unlock(&instlist_lock); return(inst); } read_unlock(&instlist_lock); return(NULL); } #ifdef OBSOLETE int get_layermask(mISDNlayer_t *layer) { int mask = 0; if (layer->inst) mask |= layer->inst->pid.layermask; return(mask); } int insertlayer(mISDNstack_t *st, mISDNlayer_t *layer, int layermask) { mISDNlayer_t *item; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s(%p, %p, %x)\n", __FUNCTION__, st, layer, layermask); if (!st || !layer) { int_error(); return(-EINVAL); } if (list_empty(&st->layerlist)) { list_add(&layer->list, &st->layerlist); } else { list_for_each_entry(item, &st->layerlist, list) { if (layermask < get_layermask(item)) { list_add_tail(&layer->list, &item->list); return(0); } } list_add_tail(&layer->list, &st->layerlist); } return(0); } #endif inline void _queue_message(mISDNstack_t *st, struct sk_buff *skb) { skb_queue_tail(&st->msgq, skb); if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) { test_and_set_bit(mISDN_STACK_WORK, &st->status); wake_up_interruptible(&st->workq); } } int mISDN_queue_message(mISDNinstance_t *inst, u_int aflag, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); mISDNstack_t *st = inst->st; u_int id; if (core_debug & DEBUG_QUEUE_FUNC) printk(KERN_DEBUG "%s(%08x, %x, prim(%x))\n", __FUNCTION__, inst->id, aflag, hh->prim); if (aflag & FLG_MSG_TARGET) { id = aflag; } else { id = (inst->id & INST_ID_MASK) | aflag; } if ((aflag & MSG_DIR_MASK) == FLG_MSG_DOWN) { if (inst->parent) { inst = inst->parent; st = inst->st; id = (inst->id & INST_ID_MASK) | FLG_MSG_TARGET | FLG_MSG_CLONED | FLG_MSG_DOWN; } } if (!st) return(-EINVAL); if (st->id == 0 || test_bit(mISDN_STACK_ABORT, &st->status)) return(-EBUSY); if (inst->id == 0) { /* instance is not initialised */ if (!(aflag & FLG_MSG_TARGET)) { id &= INST_ID_MASK; id |= (st->id & INST_ID_MASK) | aflag | FLG_INSTANCE; } } if (test_bit(mISDN_STACK_KILLED, &st->status)) return(-EBUSY); if ((st->id & STACK_ID_MASK) != (id & STACK_ID_MASK)) { int_errtxt("stack id not match st(%08x) id(%08x) inst(%08x) aflag(%08x) prim(%x)", st->id, id, inst->id, aflag, hh->prim); } hh->addr = id; _queue_message(st, skb); return(0); } static void do_broadcast(mISDNstack_t *st, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); mISDNinstance_t *inst = NULL; struct sk_buff *c_skb = NULL; int i, err; for(i=0; i<=MAX_LAYER_NR; i++) { if (i == (hh->addr & LAYER_ID_MASK)) continue; // skip own layer inst = st->i_array[i]; if (!inst) continue; // maybe we have a gap if (!c_skb) c_skb = skb_copy(skb, GFP_KERNEL); // we need a new private copy if (!c_skb) break; // stop here when copy not possible if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: inst(%08x) msg call addr(%08x) prim(%x)\n", __FUNCTION__, inst->id, hh->addr, hh->prim); if (inst->function) { err = inst->function(inst, c_skb); if (!err) c_skb = NULL; /* function consumed the skb */ if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: inst(%08x) msg call return %d\n", __FUNCTION__, inst->id, err); } else { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: instance(%08x) no function\n", __FUNCTION__, inst->id); } } if (c_skb) dev_kfree_skb(c_skb); dev_kfree_skb(skb); } static void release_layers(mISDNstack_t *st, u_int prim) { int i; for (i = 0; i <= MAX_LAYER_NR; i++) { if (st->i_array[i]) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p) inst%d(%p):%x %s lm(%x)\n", __FUNCTION__, st, i, st->i_array[i], st->i_array[i]->id, st->i_array[i]->name, st->i_array[i]->pid.layermask); st->i_array[i]->obj->own_ctrl(st->i_array[i], prim, NULL); } } } static void do_clear_stack(mISDNstack_t *st) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%08x)\n", __FUNCTION__, st->id); kfree(st->pid.pbuf); memset(&st->pid, 0, sizeof(mISDN_pid_t)); memset(&st->para, 0, sizeof(mISDN_stPara_t)); release_layers(st, MGR_UNREGLAYER | REQUEST); } static int mISDNStackd(void *data) { mISDNstack_t *st = data; int err = 0; #ifdef CONFIG_SMP lock_kernel(); #endif MAKEDAEMON("mISDNStackd"); sigfillset(¤t->blocked); st->thread = current; #ifdef CONFIG_SMP unlock_kernel(); #endif if ( core_debug & DEBUG_THREADS) printk(KERN_DEBUG "mISDNStackd started for id(%08x)\n", st->id); for (;;) { struct sk_buff *skb, *c_skb; mISDN_head_t *hh; if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { test_and_clear_bit(mISDN_STACK_WORK, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); } else test_and_set_bit(mISDN_STACK_RUNNING, &st->status); while (test_bit(mISDN_STACK_WORK, &st->status)) { mISDNinstance_t *inst; skb = skb_dequeue(&st->msgq); if (!skb) { test_and_clear_bit(mISDN_STACK_WORK, &st->status); /* test if a race happens */ if (!(skb = skb_dequeue(&st->msgq))) continue; test_and_set_bit(mISDN_STACK_WORK, &st->status); } #ifdef MISDN_MSG_STATS st->msg_cnt++; #endif hh = mISDN_HEAD_P(skb); if (hh->prim == (MGR_CLEARSTACK | REQUEST)) { mISDN_headext_t *hhe = (mISDN_headext_t *)hh; if (test_and_set_bit(mISDN_STACK_CLEARING, &st->status)) { int_errtxt("double clearing"); } if (hhe->data[0]) { if (st->notify) { int_errtxt("notify already set"); up(st->notify); } st->notify = hhe->data[0]; } dev_kfree_skb(skb); continue; } if ((hh->addr & MSG_DIR_MASK) == MSG_BROADCAST) { do_broadcast(st, skb); continue; } inst = get_nextlayer(st, hh->addr); if (!inst) { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: st(%08x) no instance for addr(%08x) prim(%x) dinfo(%x)\n", __FUNCTION__, st->id, hh->addr, hh->prim, hh->dinfo); dev_kfree_skb(skb); continue; } if (inst->clone && ((hh->addr & MSG_DIR_MASK) == FLG_MSG_UP)) { u_int id = (inst->clone->id & INST_ID_MASK) | FLG_MSG_TARGET | FLG_MSG_CLONED | FLG_MSG_UP; #ifdef MISDN_MSG_STATS st->clone_cnt++; #endif c_skb = skb_copy(skb, GFP_KERNEL); if (c_skb) { if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: inst(%08x) msg clone msg to(%08x) caddr(%08x) prim(%x)\n", __FUNCTION__, inst->id, inst->clone->id, id, hh->prim); err = mISDN_queue_message(inst->clone, id, c_skb); if (err) { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: clone instance(%08x) cannot queue msg(%08x) err(%d)\n", __FUNCTION__, inst->clone->id, id, err); dev_kfree_skb(c_skb); } } else { printk(KERN_WARNING "%s OOM on msg cloning inst(%08x) caddr(%08x) prim(%x) len(%d)\n", __FUNCTION__, inst->id, id, hh->prim, skb->len); } } if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: inst(%08x) msg call addr(%08x) prim(%x)\n", __FUNCTION__, inst->id, hh->addr, hh->prim); if (!inst->function) { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: instance(%08x) no function\n", __FUNCTION__, inst->id); dev_kfree_skb(skb); continue; } err = inst->function(inst, skb); if (err) { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: instance(%08x)->function return(%d)\n", __FUNCTION__, inst->id, err); dev_kfree_skb(skb); continue; } if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { test_and_clear_bit(mISDN_STACK_WORK, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); break; } } if (test_bit(mISDN_STACK_CLEARING, &st->status)) { test_and_set_bit(mISDN_STACK_STOPPED, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); do_clear_stack(st); test_and_clear_bit(mISDN_STACK_CLEARING, &st->status); test_and_set_bit(mISDN_STACK_RESTART, &st->status); } if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) { test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); test_and_set_bit(mISDN_STACK_RUNNING, &st->status); if (!skb_queue_empty(&st->msgq)) test_and_set_bit(mISDN_STACK_WORK, &st->status); } if (test_bit(mISDN_STACK_ABORT, &st->status)) break; if (st->notify != NULL) { up(st->notify); st->notify = NULL; } #ifdef MISDN_MSG_STATS st->sleep_cnt++; #endif test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); wait_event_interruptible(st->workq, (st->status & mISDN_STACK_ACTION_MASK)); if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: %08x wake status %08lx\n", __FUNCTION__, st->id, st->status); test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); if (test_bit(mISDN_STACK_STOPPED, &st->status)) { test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); #ifdef MISDN_MSG_STATS st->stopped_cnt++; #endif } } #ifdef MISDN_MSG_STATS printk(KERN_DEBUG "mISDNStackd daemon for id(%08x) proceed %d msg %d clone %d sleep %d stopped\n", st->id, st->msg_cnt, st->clone_cnt, st->sleep_cnt, st->stopped_cnt); printk(KERN_DEBUG "mISDNStackd daemon for id(%08x) utime(%ld) stime(%ld)\n", st->id, st->thread->utime, st->thread->stime); printk(KERN_DEBUG "mISDNStackd daemon for id(%08x) nvcsw(%ld) nivcsw(%ld)\n", st->id, st->thread->nvcsw, st->thread->nivcsw); printk(KERN_DEBUG "mISDNStackd daemon for id(%08x) killed now\n", st->id); #endif test_and_set_bit(mISDN_STACK_KILLED, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); test_and_clear_bit(mISDN_STACK_ABORT, &st->status); discard_queue(&st->msgq); st->thread = NULL; if (st->notify != NULL) { up(st->notify); st->notify = NULL; } return(0); } int mISDN_start_stack_thread(mISDNstack_t *st) { int err = 0; if (st->thread == NULL && test_bit(mISDN_STACK_KILLED, &st->status)) { test_and_clear_bit(mISDN_STACK_KILLED, &st->status); kernel_thread(mISDNStackd, (void *)st, 0); } else err = -EBUSY; return(err); } mISDNstack_t * new_stack(mISDNstack_t *master, mISDNinstance_t *inst) { mISDNstack_t *newst; #ifndef SYSFS_SUPPORT_2_6_24 int err; #endif u_long flags; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "create %s stack inst(%p)\n", master ? "child" : "master", inst); if (!(newst = kmalloc(sizeof(mISDNstack_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc mISDN_stack failed\n"); return(NULL); } memset(newst, 0, sizeof(mISDNstack_t)); INIT_LIST_HEAD(&newst->list); INIT_LIST_HEAD(&newst->childlist); INIT_LIST_HEAD(&newst->prereg); init_waitqueue_head(&newst->workq); skb_queue_head_init(&newst->msgq); if (!master) { if (inst && inst->st) { master = inst->st; while(master->clone) master = master->clone; newst->id = get_free_stackid(inst->st, FLG_CLONE_STACK); newst->master = master; master->clone = newst; master = NULL; } else { newst->id = get_free_stackid(NULL, 0); } } else { newst->id = get_free_stackid(master, FLG_CHILD_STACK); } newst->mgr = inst; if (master) { list_add_tail(&newst->list, &master->childlist); newst->parent = master; } else if (!(newst->id & FLG_CLONE_STACK)) { write_lock_irqsave(&stacklist_lock, flags); list_add_tail(&newst->list, &mISDN_stacklist); write_unlock_irqrestore(&stacklist_lock, flags); } if (inst) { inst->st = newst; } #ifndef SYSFS_SUPPORT_2_6_24 err = mISDN_register_sysfs_stack(newst); if (err) { // FIXME error handling printk(KERN_ERR "Stack id %x not registered in sysfs\n", newst->id); } #endif if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "Stack id %x added\n", newst->id); kernel_thread(mISDNStackd, (void *)newst, 0); return(newst); } int mISDN_start_stop(mISDNstack_t *st, int start) { int ret; if (start) { ret = test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); test_and_set_bit(mISDN_STACK_WAKEUP, &st->status); if (!skb_queue_empty(&st->msgq)) test_and_set_bit(mISDN_STACK_WORK, &st->status); wake_up_interruptible(&st->workq); } else ret = test_and_set_bit(mISDN_STACK_STOPPED, &st->status); return(ret); } int do_for_all_layers(void *data, u_int prim, void *arg) { mISDNstack_t *st = data; int i; if (!st) { int_error(); return(-EINVAL); } for (i = 0; i <= MAX_LAYER_NR; i++) { if (st->i_array[i]) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p) inst%d(%p):%x %s prim(%x) arg(%p)\n", __FUNCTION__, st, i, st->i_array[i], st->i_array[i]->id, st->i_array[i]->name, prim, arg); st->i_array[i]->obj->own_ctrl(st->i_array[i], prim, arg); } } return(0); } int change_stack_para(mISDNstack_t *st, u_int prim, mISDN_stPara_t *stpara) { int changed = 0; if (!st) { int_error(); return(-EINVAL); } if (prim == (MGR_ADDSTPARA | REQUEST)) { if (!stpara) { int_error(); return(-EINVAL); } prim = MGR_ADDSTPARA | INDICATION; if (stpara->maxdatalen > 0 && stpara->maxdatalen < st->para.maxdatalen) { changed++; st->para.maxdatalen = stpara->maxdatalen; } if (stpara->up_headerlen > st->para.up_headerlen) { changed++; st->para.up_headerlen = stpara->up_headerlen; } if (stpara->down_headerlen > st->para.down_headerlen) { changed++; st->para.down_headerlen = stpara->down_headerlen; } if (!changed) return(0); stpara = &st->para; } else if (prim == (MGR_CLRSTPARA | REQUEST)) { prim = MGR_CLRSTPARA | INDICATION; memset(&st->para, 0, sizeof(mISDN_stPara_t)); stpara = NULL; } return(do_for_all_layers(st, prim, stpara)); } static int delete_stack(mISDNstack_t *st) { struct semaphore sem; u_long flags; init_MUTEX_LOCKED(&sem); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p:%08x)\n", __FUNCTION__, st, st->id); #ifndef SYSFS_SUPPORT_2_6_24 mISDN_unregister_sysfs_st(st); #endif if (st->parent) st->parent = NULL; if (!list_empty(&st->prereg)) { mISDNinstance_t *inst, *ni; int_errtxt("st(%08x)->prereg not empty\n", st->id); list_for_each_entry_safe(inst, ni, &st->prereg, list) { int_errtxt("inst(%p:%08x) preregistered", inst, inst->id); list_del(&inst->list); } } if (st->thread) { if (st->thread != current) { if (st->notify) { int_error(); up(st->notify); } st->notify = &sem; } test_and_set_bit(mISDN_STACK_ABORT, &st->status); mISDN_start_stop(st, 1); if (st->thread != current) /* we cannot wait for us */ down(&sem); } release_layers(st, MGR_RELEASE | INDICATION); // FIXME dirty test_and_set_bit(mISDN_STACK_DELETED, &st->status); write_lock_irqsave(&stacklist_lock, flags); list_del(&st->list); write_unlock_irqrestore(&stacklist_lock, flags); kfree(st); return(0); } int release_stack(mISDNstack_t *st) { int err; mISDNstack_t *cst, *nst; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p)\n", __FUNCTION__, st); // FIXME dirty if (test_bit(mISDN_STACK_DELETED, &st->status)) { WARN_ON(1); return -EINVAL; } list_for_each_entry_safe(cst, nst, &st->childlist, list) { if ((err = delete_stack(cst))) { return(err); } } if (st->clone) { st->clone->master = st->master; } if (st->master) { st->master->clone = st->clone; } else if (st->clone) { /* no master left -> delete clone too */ delete_stack(st->clone); st->clone = NULL; } if ((err = delete_stack(st))) return(err); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: mISDN_stacklist(%p<-%p->%p)\n", __FUNCTION__, mISDN_stacklist.prev, &mISDN_stacklist, mISDN_stacklist.next); return(0); } void cleanup_object(mISDNobject_t *obj) { mISDNstack_t *st, *nst; mISDNinstance_t *inst; int i; read_lock(&stacklist_lock); list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) { for (i = 0; i < MAX_LAYER_NR; i++) { inst = st->i_array[i]; if (inst && inst->obj == obj) { read_unlock(&stacklist_lock); inst->obj->own_ctrl(st, MGR_RELEASE | INDICATION, inst); read_lock(&stacklist_lock); } } } read_unlock(&stacklist_lock); } void check_stacklist(void) { mISDNstack_t *st, *nst; read_lock(&stacklist_lock); 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; } } } read_unlock(&stacklist_lock); } void release_stacks(mISDNobject_t *obj) { mISDNstack_t *st, *tmp; int rel, i; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: obj(%p) %s\n", __FUNCTION__, obj, obj->name); read_lock(&stacklist_lock); list_for_each_entry_safe(st, tmp, &mISDN_stacklist, list) { rel = 0; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p)\n", __FUNCTION__, st); for (i = 0; i <= MAX_LAYER_NR; i++) { if (!st->i_array[i]) continue; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: inst%d(%p)\n", __FUNCTION__, i, st->i_array[i]); if (st->i_array[i]->obj == obj) rel++; } if (rel) { read_unlock(&stacklist_lock); release_stack(st); read_lock(&stacklist_lock); } } read_unlock(&stacklist_lock); if (obj->refcnt) printk(KERN_WARNING "release_stacks obj %s refcnt is %d\n", obj->name, obj->refcnt); } #ifdef OBSOLETE static void get_free_instid(mISDNstack_t *st, mISDNinstance_t *inst) { mISDNinstance_t *il; inst->id = mISDN_get_lowlayer(inst->pid.layermask)<<20; inst->id |= FLG_INSTANCE; if (st) { inst->id |= st->id; } else { list_for_each_entry(il, &mISDN_instlist, list) { if (il->id == inst->id) { if ((inst->id & IF_INSTMASK) >= INST_ID_MAX) { inst->id = 0; return; } inst->id += LAYER_ID_INC; il = list_entry(mISDN_instlist.next, mISDNinstance_t, list); } } } } #endif int register_layer(mISDNstack_t *st, mISDNinstance_t *inst) { int idx; #ifndef SYSFS_SUPPORT_2_6_24 int err; #endif mISDNinstance_t *dup; u_long flags; if (!inst || !st) return(-EINVAL); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s:st(%p) inst(%p/%p) lmask(%x) id(%x)\n", __FUNCTION__, st, inst, inst->obj, inst->pid.layermask, inst->id); if (inst->id) { /* already registered */ // if (inst->st || !st) { int_errtxt("register duplicate %08x %p %p", inst->id, inst->st, st); return(-EBUSY); //} } /* * To simplify registration we assume that our stacks are * always build with monoton increasing layernumbers from * bottom (HW,L0) to highest number */ // if (st) { for (idx = 0; idx <= MAX_LAYER_NR; idx++) if (!st->i_array[idx]) break; if (idx > MAX_LAYER_NR) { int_errtxt("stack %08x overflow", st->id); return(-EXFULL); } inst->regcnt++; st->i_array[idx] = inst; inst->id = st->id | FLG_INSTANCE | idx; dup = get_instance4id(inst->id); if (dup) { int_errtxt("register duplicate %08x i1(%p) i2(%p) i1->st(%p) i2->st(%p) st(%p)", inst->id, inst, dup, inst->st, dup->st, st); inst->regcnt--; st->i_array[idx] = NULL; inst->id = 0; return(-EBUSY); } // } if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: inst(%p/%p) id(%x)\n", __FUNCTION__, inst, inst->obj, inst->id); if (!list_empty(&inst->list)) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: register preregistered instance st(%p/%p)\n", __FUNCTION__, st, inst->st); list_del_init(&inst->list); } inst->st = st; write_lock_irqsave(&instlist_lock, flags); list_add_tail(&inst->list, &mISDN_instlist); write_unlock_irqrestore(&instlist_lock, flags); #ifndef SYSFS_SUPPORT_2_6_24 err = mISDN_register_sysfs_inst(inst); if (err) { // FIXME error handling printk(KERN_ERR "%s: register_sysfs failed %d st(%08x) inst(%08x)\n", __FUNCTION__, err, st->id, inst->id); } #endif return(0); } int preregister_layer(mISDNstack_t *st, mISDNinstance_t *inst) { if (!inst || !st) return(-EINVAL); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s:st(%08x) inst(%p:%08x) lmask(%x)\n", __FUNCTION__, st->id, inst, inst->id, inst->pid.layermask); if (inst->id) { /* already registered */ int_errtxt("register duplicate %08x %p %p", inst->id, inst->st, st); return(-EBUSY); } inst->st = st; list_add_tail(&inst->list, &st->prereg); return(0); } int unregister_instance(mISDNinstance_t *inst) { int i; u_long flags; if (!inst) return(-EINVAL); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p) inst(%p):%x lay(%x)\n", __FUNCTION__, inst->st, inst, inst->id, inst->pid.layermask); #ifndef SYSFS_SUPPORT_2_6_24 mISDN_unregister_sysfs_inst(inst); #endif if (inst->st && inst->id) { i = inst->id & LAYER_ID_MASK; if (i > MAX_LAYER_NR) { int_errtxt("unregister %08x st(%08x) wrong layer", inst->id, inst->st->id); return(-EINVAL); } if (inst->st->i_array[i] == inst) { inst->regcnt--; inst->st->i_array[i] = NULL; } else if (inst->st->i_array[i]) { int_errtxt("unregister %08x st(%08x) wrong instance %08x", inst->id, inst->st->id, inst->st->i_array[i]->id); return(-EINVAL); } else printk(KERN_WARNING "unregister %08x st(%08x) not in stack", inst->id, inst->st->id); if (inst->st && (inst->st->mgr != inst)) inst->st = NULL; } if (inst->parent) { /*we are cloned */ inst->parent->clone = inst->clone; if (inst->clone) inst->clone->parent = inst->parent; inst->clone = NULL; inst->parent = NULL; } else if (inst->clone) { /* deleting the top level master of a clone */ /* FIXME: should be handled somehow, maybe unregister the clone */ int_errtxt("removed master(%08x) of clone(%08x)", inst->id, inst->clone->id); inst->clone->parent = NULL; inst->clone = NULL; } write_lock_irqsave(&instlist_lock, flags); if (inst->list.prev && inst->list.next) list_del_init(&inst->list); else int_errtxt("uninitialized list inst(%08x)", inst->id); inst->id = 0; write_unlock_irqrestore(&instlist_lock, flags); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: mISDN_instlist(%p<-%p->%p)\n", __FUNCTION__, mISDN_instlist.prev, &mISDN_instlist, mISDN_instlist.next); return(0); } int copy_pid(mISDN_pid_t *dpid, mISDN_pid_t *spid, u_char *pbuf) { memcpy(dpid, spid, sizeof(mISDN_pid_t)); if (spid->pbuf && spid->maxplen) { if (!pbuf) { int_error(); return(-ENOMEM); } dpid->pbuf = pbuf; memcpy(dpid->pbuf, spid->pbuf, spid->maxplen); } return(0); } int set_stack(mISDNstack_t *st, mISDN_pid_t *pid) { int err, i; u_char *pbuf = NULL; mISDNinstance_t *inst; // mISDNlayer_t *hl, *hln; if (!st || !pid) { int_error(); return(-EINVAL); } if (!st->mgr || !st->mgr->obj) { int_error(); return(-EINVAL); } if (pid->pbuf) pbuf = kmalloc(pid->maxplen, GFP_ATOMIC); err = copy_pid(&st->pid, pid, pbuf); if (err) return(err); memcpy(&st->mgr->pid, &st->pid, sizeof(mISDN_pid_t)); if (!mISDN_SetHandledPID(st->mgr->obj, &st->mgr->pid)) { int_error(); return(-ENOPROTOOPT); } else { mISDN_RemoveUsedPID(pid, &st->mgr->pid); } err = mISDN_ctrl(st, MGR_REGLAYER | REQUEST, st->mgr); if (err) { int_error(); return(err); } while (pid->layermask) { inst = get_next_instance(st, pid); if (!inst) { int_error(); mISDN_ctrl(st, MGR_CLEARSTACK| REQUEST, (void *)1); return(-ENOPROTOOPT); } mISDN_RemoveUsedPID(pid, &inst->pid); } if (!list_empty(&st->prereg)) int_errtxt("st(%08x)->prereg not empty\n", st->id); for (i = 0; i <= MAX_LAYER_NR; i++) { inst = st->i_array[i]; if (!inst) break; if (!inst->obj) { int_error(); continue; } if (!inst->obj->own_ctrl) { int_error(); continue; } inst->obj->own_ctrl(inst, MGR_SETSTACK | INDICATION, NULL); } return(0); } int clear_stack(mISDNstack_t *st, int wait) { struct sk_buff *skb; mISDN_headext_t *hhe; if (!st) return(-EINVAL); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%08x)\n", __FUNCTION__, st->id); if (!(skb = alloc_skb(8, GFP_ATOMIC))) return(-ENOMEM); hhe = mISDN_HEADEXT_P(skb); hhe->prim = MGR_CLEARSTACK | REQUEST; hhe->addr = st->id; if (wait) { struct semaphore sem; init_MUTEX_LOCKED(&sem); hhe->data[0] = &sem; _queue_message(st, skb); if (st->thread == current) {/* we cannot wait for us */ int_error(); return(-EBUSY); } down(&sem); } else { hhe->data[0] = NULL; _queue_message(st, skb); } return(0); } static int test_stack_protocol(mISDNstack_t *st, u_int l1prot, u_int l2prot, u_int l3prot) { int cnt = MAX_LAYER_NR + 1, ret = 1; mISDN_pid_t pid; mISDNinstance_t *inst; clear_stack(st,1); memset(&pid, 0, sizeof(mISDN_pid_t)); pid.layermask = ISDN_LAYER(1); if (!(((l2prot == 2) || (l2prot == 0x40)) && (l3prot == 1))) pid.layermask |= ISDN_LAYER(2); if (!(l3prot == 1)) pid.layermask |= ISDN_LAYER(3); pid.protocol[1] = l1prot | ISDN_PID_LAYER(1) | ISDN_PID_BCHANNEL_BIT; if (pid.layermask & ISDN_LAYER(2)) pid.protocol[2] = l2prot | ISDN_PID_LAYER(2) | ISDN_PID_BCHANNEL_BIT; if (pid.layermask & ISDN_LAYER(3)) pid.protocol[3] = l3prot | ISDN_PID_LAYER(3) | ISDN_PID_BCHANNEL_BIT; copy_pid(&st->pid, &pid, NULL); memcpy(&st->mgr->pid, &pid, sizeof(mISDN_pid_t)); if (!mISDN_SetHandledPID(st->mgr->obj, &st->mgr->pid)) { memset(&st->pid, 0, sizeof(mISDN_pid_t)); return(-ENOPROTOOPT); } else { mISDN_RemoveUsedPID(&pid, &st->mgr->pid); } if (!pid.layermask) { memset(&st->pid, 0, sizeof(mISDN_pid_t)); return(0); } ret = mISDN_ctrl(st, MGR_REGLAYER | REQUEST, st->mgr); if (ret) { clear_stack(st, 1); return(ret); } while (pid.layermask && cnt--) { inst = get_next_instance(st, &pid); if (!inst) { ret = -ENOPROTOOPT; break; } mISDN_RemoveUsedPID(&pid, &inst->pid); } if (!cnt) ret = -ENOPROTOOPT; clear_stack(st, 1); return(ret); } static u_int validL1pid4L2[ISDN_PID_IDX_MAX + 1] = { 0x022d, 0x03ff, 0x0000, 0x0000, 0x0010, 0x022d, 0x03ff, 0x0380, 0x022d, 0x022d, 0x022d, 0x01c6, 0x0000, }; static u_int validL2pid4L3[ISDN_PID_IDX_MAX + 1] = { 0x1fff, 0x0000, 0x0101, 0x0101, 0x0010, 0x0010, 0x0000, 0x00c0, 0x0000, }; int evaluate_stack_pids(mISDNstack_t *st, mISDN_pid_t *pid) { int err; mISDN_pid_t pidmask; u_int l1bitm, l2bitm, l3bitm; u_int l1idx, l2idx, l3idx; if (!st || !pid) { int_error(); return(-EINVAL); } if (!st->mgr || !st->mgr->obj) { int_error(); return(-EINVAL); } copy_pid(&pidmask, pid, NULL); memset(pid, 0, sizeof(mISDN_pid_t)); for (l1idx=0; l1idx <= ISDN_PID_IDX_MAX; l1idx++) { l1bitm = 1 << l1idx; if (!(pidmask.protocol[1] & l1bitm)) continue; for (l2idx=0; l2idx <= ISDN_PID_IDX_MAX; l2idx++) { l2bitm = 1 << l2idx; if (!(pidmask.protocol[2] & l2bitm)) continue; if (!(validL1pid4L2[l2idx] & l1bitm)) continue; for (l3idx=0; l3idx <= ISDN_PID_IDX_MAX; l3idx++) { err = 1; l3bitm = 1 << l3idx; if (!(pidmask.protocol[3] & l3bitm)) continue; if (!(validL2pid4L3[l3idx] & l2bitm)) continue; err = test_stack_protocol(st, l1bitm, l2bitm, l3bitm); if (!err) { pid->protocol[3] |= l3bitm; pid->protocol[2] |= l2bitm; pid->protocol[1] |= l1bitm; } } } } clear_stack(st, 1); return(0); } EXPORT_SYMBOL(mISDN_queue_message); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/layer1.h0000644000000000000500000000235011110524073017726 0ustar rootsrc/* $Id: layer1.h,v 1.5 2006/03/06 12:52:07 keil Exp $ * * Layer 1 defines * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include "fsm.h" #ifdef MISDN_MEMDEBUG #include "memdbg.h" #endif #ifdef OBSOLETE #define D_RCVBUFREADY 0 #define D_XMTBUFREADY 1 #define D_L1STATECHANGE 2 #define D_CLEARBUSY 3 #define D_RX_MON0 4 #define D_RX_MON1 5 #define D_TX_MON0 6 #define D_TX_MON1 7 #define D_BLOCKEDATOMIC 8 #define D_LOS 9 #define D_LOS_OFF 10 #define D_AIS 11 #define D_AIS_OFF 12 #define D_SLIP_TX 13 #define D_SLIP_RX 14 #define B_RCVBUFREADY 0 #define B_XMTBUFREADY 1 #define B_BLOCKEDATOMIC 2 #define B_DTMFREADY 3 #endif #define FLG_L1_ACTIVATING 1 #define FLG_L1_ACTIVATED 2 #define FLG_L1_DEACTTIMER 3 #define FLG_L1_ACTTIMER 4 #define FLG_L1_T3RUN 5 #define FLG_L1_PULL_REQ 6 #define FLG_L1_UINT 7 #define FLG_L1_DBLOCKED 8 /* L1 Debug */ #define L1_DEB_WARN 0x01 #define L1_DEB_INTSTAT 0x02 #define L1_DEB_ISAC 0x04 #define L1_DEB_ISAC_FIFO 0x08 #define L1_DEB_HSCX 0x10 #define L1_DEB_HSCX_FIFO 0x20 #define L1_DEB_LAPD 0x40 #define L1_DEB_IPAC 0x80 #define L1_DEB_RECEIVE_FRAME 0x100 #define L1_DEB_MONITOR 0x200 #define DEB_DLOG_HEX 0x400 #define DEB_DLOG_VERBOSE 0x800 mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/sysfs.h0000644000000000000500000001107511135651702017713 0ustar rootsrc/* $Id: sysfs.h,v 1.2 2006/03/06 12:58:31 keil Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * mISDN sysfs common defines * * This file is (c) under GNU PUBLIC LICENSE * */ #include extern ssize_t mISDN_show_pid_protocol(mISDN_pid_t *, char *); extern ssize_t mISDN_show_pid_parameter(mISDN_pid_t *, char *); static inline ssize_t show_pid_layermask(mISDN_pid_t *pid, char *buf) { return sprintf(buf, "0x%08x\n", pid->layermask); } static inline ssize_t show_pid_global(mISDN_pid_t *pid, char *buf) { return sprintf(buf, "0x%04x\n", pid->global); } static inline ssize_t show_pid_maxplen(mISDN_pid_t *pid, char *buf) { return sprintf(buf, "%d\n", pid->maxplen); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) #define MISDN_PROTO(_type, _name, _mode) \ static ssize_t show_protocol_##_name(struct device *class_dev, struct device_attribute *attr, char *buf) \ { \ _type##_t *p = to_##_type(class_dev); \ return(mISDN_show_pid_protocol(&p->_name, buf)); \ } \ struct device_attribute _type##_attr_protocol_##_name = \ __ATTR(protocol,_mode,show_protocol_##_name, NULL); \ static ssize_t show_parameter_##_name(struct device *class_dev, struct device_attribute *attr, char *buf) \ { \ _type##_t *p = to_##_type(class_dev); \ return(mISDN_show_pid_parameter(&p->_name, buf)); \ } \ struct device_attribute _type##_attr_parameter_##_name = \ __ATTR(parameter,_mode,show_parameter_##_name, NULL); \ static ssize_t show_layermask_##_name(struct device *class_dev, struct device_attribute *attr, char *buf) \ { \ _type##_t *p = to_##_type(class_dev); \ return(show_pid_layermask(&p->_name, buf)); \ } \ struct device_attribute _type##_attr_layermask_##_name = \ __ATTR(layermask,_mode,show_layermask_##_name, NULL); \ static ssize_t show_global_##_name(struct device *class_dev, struct device_attribute *attr, char *buf) \ { \ _type##_t *p = to_##_type(class_dev); \ return(show_pid_global(&p->_name, buf)); \ } \ struct device_attribute _type##_attr_global_##_name = \ __ATTR(global,_mode,show_global_##_name, NULL); \ static ssize_t show_maxplen_##_name(struct device *class_dev, struct device_attribute *attr, char *buf) \ { \ _type##_t *p = to_##_type(class_dev); \ return(show_pid_maxplen(&p->_name, buf)); \ } \ struct 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, \ } #else #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, \ } #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/layer2.h0000644000000000000500000000466711110524073017744 0ustar rootsrc/* $Id: layer2.h,v 1.7 2006/03/06 12:52:07 keil Exp $ * * Layer 2 defines * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include "fsm.h" #ifdef MISDN_MEMDEBUG #include "memdbg.h" #endif #define MAX_WINDOW 8 typedef struct _teimgr { int ri; struct FsmInst tei_m; struct FsmTimer t202; int T202, N202; int debug; struct _layer2 *l2; } teimgr_t; typedef struct _laddr { u_char A; u_char B; } laddr_t; typedef struct _layer2 { struct list_head list; int sapi; int tei; laddr_t addr; u_int maxlen; teimgr_t *tm; u_long flag; u_int vs, va, vr; int rc; u_int window; u_int sow; int entity; struct FsmInst l2m; struct FsmTimer t200, t203; int T200, N200, T203; int debug; mISDNinstance_t inst; // mISDNif_t *cloneif; int next_id; u_int down_id; struct sk_buff *windowar[MAX_WINDOW]; struct sk_buff_head i_queue; struct sk_buff_head ui_queue; struct sk_buff_head down_queue; struct sk_buff_head tmp_queue; } layer2_t; /* l2 status_info */ typedef struct _status_info_l2 { int len; int typ; int protocol; int state; int sapi; int tei; laddr_t addr; u_int maxlen; u_long flag; u_int vs; u_int va; u_int vr; int rc; u_int window; u_int sow; int T200; int N200; int T203; int len_i_queue; int len_ui_queue; int len_d_queue; int debug; int tei_state; int tei_ri; int T202; int N202; int tei_debug; } status_info_l2_t; /* from mISDN_l2.c */ extern int tei_l2(layer2_t *l2, struct sk_buff *skb); /* from tei.c */ extern int l2_tei(teimgr_t *tm, struct sk_buff *skb); extern int create_teimgr(layer2_t *l2); extern void release_tei(teimgr_t *tm); extern int TEIInit(void); extern void TEIFree(void); #define GROUP_TEI 127 #define TEI_SAPI 63 #define CTRL_SAPI 0 #define RR 0x01 #define RNR 0x05 #define REJ 0x09 #define SABME 0x6f #define SABM 0x2f #define DM 0x0f #define UI 0x03 #define DISC 0x43 #define UA 0x63 #define FRMR 0x87 #define XID 0xaf #define CMD 0 #define RSP 1 #define LC_FLUSH_WAIT 1 #define FLG_LAPB 0 #define FLG_LAPD 1 #define FLG_ORIG 2 #define FLG_MOD128 3 #define FLG_PEND_REL 4 #define FLG_L3_INIT 5 #define FLG_T200_RUN 6 #define FLG_ACK_PEND 7 #define FLG_REJEXC 8 #define FLG_OWN_BUSY 9 #define FLG_PEER_BUSY 10 #define FLG_DCHAN_BUSY 11 #define FLG_L1_ACTIV 12 #define FLG_ESTAB_PEND 13 #define FLG_PTP 14 #define FLG_FIXED_TEI 15 #define FLG_L2BLOCK 16 #define FLG_L1_BUSY 17 #define FLG_LAPD_NET 18 mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/tei.c0000644000000000000500000002730011135651702017316 0ustar rootsrc/* $Id: tei.c,v 1.11 2006/03/23 13:11:43 keil Exp $ * * Author Karsten Keil (keil@isdn4linux.de) * * This file is (c) under GNU PUBLIC LICENSE * For changes and modifications please read * ../../../Documentation/isdn/mISDN.cert * */ #include "layer2.h" #include "helper.h" #include "debug.h" #include const char *tei_revision = "$Revision: 1.11 $"; #define ID_REQUEST 1 #define ID_ASSIGNED 2 #define ID_DENIED 3 #define ID_CHK_REQ 4 #define ID_CHK_RES 5 #define ID_REMOVE 6 #define ID_VERIFY 7 #define TEI_ENTITY_ID 0xf static struct Fsm teifsm = {NULL, 0, 0, NULL, NULL}; enum { ST_TEI_NOP, ST_TEI_IDREQ, ST_TEI_IDVERIFY, }; #define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) static char *strTeiState[] = { "ST_TEI_NOP", "ST_TEI_IDREQ", "ST_TEI_IDVERIFY", }; enum { EV_IDREQ, EV_ASSIGN, EV_ASSIGN_REQ, EV_DENIED, EV_CHKREQ, EV_REMOVE, EV_VERIFY, EV_T202, }; #define TEI_EVENT_COUNT (EV_T202+1) static char *strTeiEvent[] = { "EV_IDREQ", "EV_ASSIGN", "EV_ASSIGN_REQ", "EV_DENIED", "EV_CHKREQ", "EV_REMOVE", "EV_VERIFY", "EV_T202", }; unsigned int random_ri(void) { unsigned int x; get_random_bytes(&x, sizeof(x)); return (x & 0xffff); } static teimgr_t * findtei(teimgr_t *tm, int tei) { static teimgr_t *ptr = NULL; struct sk_buff *skb; if (tei == 127) return (NULL); skb = create_link_skb(MDL_FINDTEI | REQUEST, tei, sizeof(void), &ptr, 0); if (!skb) return (NULL); if (!tei_l2(tm->l2, skb)) return(ptr); dev_kfree_skb(skb); return (NULL); } static void put_tei_msg(teimgr_t *tm, u_char m_id, unsigned int ri, u_char tei) { struct sk_buff *skb; u_char bp[8]; bp[0] = (TEI_SAPI << 2); if (test_bit(FLG_LAPD_NET, &tm->l2->flag)) bp[0] |= 2; /* CR:=1 for net command */ bp[1] = (GROUP_TEI << 1) | 0x1; bp[2] = UI; bp[3] = TEI_ENTITY_ID; bp[4] = ri >> 8; bp[5] = ri & 0xff; bp[6] = m_id; bp[7] = (tei << 1) | 1; skb = create_link_skb(MDL_UNITDATA | REQUEST, 0, 8, bp, 0); if (!skb) { printk(KERN_WARNING "mISDN: No skb for TEI manager\n"); return; } if (tei_l2(tm->l2, skb)) dev_kfree_skb(skb); } static void tei_id_request(struct FsmInst *fi, int event, void *arg) { teimgr_t *tm = fi->userdata; if (tm->l2->tei != -1) { tm->tei_m.printdebug(&tm->tei_m, "assign request for allready assigned tei %d", tm->l2->tei); return; } tm->ri = random_ri(); if (tm->debug) tm->tei_m.printdebug(&tm->tei_m, "assign request ri %d", tm->ri); put_tei_msg(tm, ID_REQUEST, tm->ri, 127); mISDN_FsmChangeState(fi, ST_TEI_IDREQ); mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 1); tm->N202 = 3; } static void tei_assign_req(struct FsmInst *fi, int event, void *arg) { teimgr_t *tm = fi->userdata; u_char *dp = arg; if (tm->l2->tei == -1) { tm->tei_m.printdebug(&tm->tei_m, "net tei assign request without tei"); return; } tm->ri = ((unsigned int) *dp++ << 8); tm->ri += *dp++; if (tm->debug) tm->tei_m.printdebug(&tm->tei_m, "net assign request ri %d teim %d", tm->ri, *dp); put_tei_msg(tm, ID_ASSIGNED, tm->ri, tm->l2->tei); mISDN_FsmChangeState(fi, ST_TEI_NOP); } static void tei_id_assign(struct FsmInst *fi, int event, void *arg) { teimgr_t *otm, *tm = fi->userdata; struct sk_buff *skb; u_char *dp = arg; int ri, tei; ri = ((unsigned int) *dp++ << 8); ri += *dp++; dp++; tei = *dp >> 1; if (tm->debug) tm->tei_m.printdebug(fi, "identity assign ri %d tei %d", ri, tei); if ((otm = findtei(tm, tei))) { /* same tei is in use */ if (ri != otm->ri) { tm->tei_m.printdebug(fi, "possible duplicate assignment tei %d", tei); skb = create_link_skb(MDL_ERROR | RESPONSE, 0, 0, NULL, 0); if (!skb) return; if (tei_l2(otm->l2, skb)) dev_kfree_skb(skb); } } else if (ri == tm->ri) { mISDN_FsmDelTimer(&tm->t202, 1); mISDN_FsmChangeState(fi, ST_TEI_NOP); skb = create_link_skb(MDL_ASSIGN | REQUEST, tei, 0, NULL, 0); if (!skb) return; if (tei_l2(tm->l2, skb)) dev_kfree_skb(skb); // cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); } } static void tei_id_test_dup(struct FsmInst *fi, int event, void *arg) { teimgr_t *otm, *tm = fi->userdata; u_char *dp = arg; int tei, ri; ri = ((unsigned int) *dp++ << 8); ri += *dp++; dp++; tei = *dp >> 1; if (tm->debug) tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d", ri, tei); if ((otm = findtei(tm, tei))) { /* same tei is in use */ if (ri != otm->ri) { /* and it wasn't our request */ tm->tei_m.printdebug(fi, "possible duplicate assignment tei %d", tei); mISDN_FsmEvent(&otm->tei_m, EV_VERIFY, NULL); } } } static void tei_id_denied(struct FsmInst *fi, int event, void *arg) { teimgr_t *tm = fi->userdata; u_char *dp = arg; int ri, tei; ri = ((unsigned int) *dp++ << 8); ri += *dp++; dp++; tei = *dp >> 1; if (tm->debug) tm->tei_m.printdebug(fi, "identity denied ri %d tei %d", ri, tei); } static void tei_id_chk_req(struct FsmInst *fi, int event, void *arg) { teimgr_t *tm = fi->userdata; u_char *dp = arg; int tei; tei = *(dp+3) >> 1; if (tm->debug) tm->tei_m.printdebug(fi, "identity check req tei %d", tei); if ((tm->l2->tei != -1) && ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { mISDN_FsmDelTimer(&tm->t202, 4); mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); put_tei_msg(tm, ID_CHK_RES, random_ri(), tm->l2->tei); } } static void tei_id_remove(struct FsmInst *fi, int event, void *arg) { teimgr_t *tm = fi->userdata; u_char *dp = arg; struct sk_buff *skb; int tei; tei = *(dp+3) >> 1; if (tm->debug) tm->tei_m.printdebug(fi, "identity remove tei %d", tei); if ((tm->l2->tei != -1) && ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { mISDN_FsmDelTimer(&tm->t202, 5); mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); skb = create_link_skb(MDL_REMOVE | REQUEST, 0, 0, NULL, 0); if (!skb) return; if (tei_l2(tm->l2, skb)) dev_kfree_skb(skb); // cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); } } static void tei_id_verify(struct FsmInst *fi, int event, void *arg) { teimgr_t *tm = fi->userdata; if (tm->debug) tm->tei_m.printdebug(fi, "id verify request for tei %d", tm->l2->tei); put_tei_msg(tm, ID_VERIFY, 0, tm->l2->tei); mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 2); tm->N202 = 2; } static void tei_id_req_tout(struct FsmInst *fi, int event, void *arg) { teimgr_t *tm = fi->userdata; struct sk_buff *skb; if (--tm->N202) { tm->ri = random_ri(); if (tm->debug) tm->tei_m.printdebug(fi, "assign req(%d) ri %d", 4 - tm->N202, tm->ri); put_tei_msg(tm, ID_REQUEST, tm->ri, 127); mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 3); } else { tm->tei_m.printdebug(fi, "assign req failed"); skb = create_link_skb(MDL_ERROR | REQUEST, 0, 0, NULL, 0); if (!skb) return; if (tei_l2(tm->l2, skb)) dev_kfree_skb(skb); // cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); mISDN_FsmChangeState(fi, ST_TEI_NOP); } } static void tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) { teimgr_t *tm = fi->userdata; struct sk_buff *skb; if (--tm->N202) { if (tm->debug) tm->tei_m.printdebug(fi, "id verify req(%d) for tei %d", 3 - tm->N202, tm->l2->tei); put_tei_msg(tm, ID_VERIFY, 0, tm->l2->tei); mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 4); } else { tm->tei_m.printdebug(fi, "verify req for tei %d failed", tm->l2->tei); skb = create_link_skb(MDL_REMOVE | REQUEST, 0, 0, NULL, 0); if (!skb) return; if (tei_l2(tm->l2, skb)) dev_kfree_skb(skb); // cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); mISDN_FsmChangeState(fi, ST_TEI_NOP); } } static int tei_ph_data_ind(teimgr_t *tm, int dtyp, struct sk_buff *skb) { u_char *dp; int mt; int ret = -EINVAL; if (!skb) return(ret); if (test_bit(FLG_FIXED_TEI, &tm->l2->flag) && !test_bit(FLG_LAPD_NET, &tm->l2->flag)) return(ret); if (skb->len < 8) { tm->tei_m.printdebug(&tm->tei_m, "short mgr frame %ld/8", skb->len); return(ret); } dp = skb->data + 2; if ((*dp & 0xef) != UI) { tm->tei_m.printdebug(&tm->tei_m, "mgr frame is not ui %x", *dp); return(ret); } dp++; if (*dp++ != TEI_ENTITY_ID) { /* wrong management entity identifier, ignore */ dp--; tm->tei_m.printdebug(&tm->tei_m, "tei handler wrong entity id %x", *dp); return(ret); } else { mt = *(dp+2); if (tm->debug) tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt); if (mt == ID_ASSIGNED) mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp); else if (mt == ID_DENIED) mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp); else if (mt == ID_CHK_REQ) mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp); else if (mt == ID_REMOVE) mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp); else if (mt == ID_REQUEST && test_bit(FLG_LAPD_NET, &tm->l2->flag)) mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN_REQ, dp); else { tm->tei_m.printdebug(&tm->tei_m, "tei handler wrong mt %x", mt); return(ret); } } dev_kfree_skb(skb); return(0); } int l2_tei(teimgr_t *tm, struct sk_buff *skb) { mISDN_head_t *hh; int ret = -EINVAL; if (!tm || !skb) return(ret); hh = mISDN_HEAD_P(skb); if (tm->debug) printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim); switch(hh->prim) { case (MDL_UNITDATA | INDICATION): return(tei_ph_data_ind(tm, hh->dinfo, skb)); case (MDL_ASSIGN | INDICATION): if (test_bit(FLG_FIXED_TEI, &tm->l2->flag)) { if (tm->debug) tm->tei_m.printdebug(&tm->tei_m, "fixed assign tei %d", tm->l2->tei); skb_trim(skb, 0); mISDN_sethead(MDL_ASSIGN | REQUEST, tm->l2->tei, skb); if (!tei_l2(tm->l2, skb)) return(0); // cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); } else mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL); break; case (MDL_ERROR | INDICATION): if (!test_bit(FLG_FIXED_TEI, &tm->l2->flag)) mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL); break; } dev_kfree_skb(skb); return(0); } static void tei_debug(struct FsmInst *fi, char *fmt, ...) { teimgr_t *tm = fi->userdata; logdata_t log; char head[24]; va_start(log.args, fmt); sprintf(head,"tei %s", tm->l2->inst.name); log.fmt = fmt; log.head = head; mISDN_ctrl(&tm->l2->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } static struct FsmNode TeiFnList[] = { {ST_TEI_NOP, EV_IDREQ, tei_id_request}, {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req}, {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, {ST_TEI_IDREQ, EV_T202, tei_id_req_tout}, {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, {ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout}, {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, }; #define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode)) void release_tei(teimgr_t *tm) { mISDN_FsmDelTimer(&tm->t202, 1); kfree(tm); } int create_teimgr(layer2_t *l2) { teimgr_t *ntei; if (!l2) { printk(KERN_ERR "create_tei no layer2\n"); return(-EINVAL); } if (!(ntei = kmalloc(sizeof(teimgr_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc teimgr failed\n"); return(-ENOMEM); } memset(ntei, 0, sizeof(teimgr_t)); ntei->l2 = l2; ntei->T202 = 2000; /* T202 2000 milliseconds */ ntei->debug = l2->debug; ntei->tei_m.debug = l2->debug; ntei->tei_m.userdata = ntei; ntei->tei_m.printdebug = tei_debug; if (test_bit(FLG_LAPD_NET, &l2->flag)) { ntei->tei_m.fsm = &teifsm; ntei->tei_m.state = ST_TEI_NOP; } else { ntei->tei_m.fsm = &teifsm; ntei->tei_m.state = ST_TEI_NOP; } mISDN_FsmInitTimer(&ntei->tei_m, &ntei->t202); l2->tm = ntei; return(0); } int TEIInit(void) { teifsm.state_count = TEI_STATE_COUNT; teifsm.event_count = TEI_EVENT_COUNT; teifsm.strEvent = strTeiEvent; teifsm.strState = strTeiState; mISDN_FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT); return(0); } void TEIFree(void) { mISDN_FsmFree(&teifsm); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/layer3.h0000644000000000000500000000451411110524073017734 0ustar rootsrc/* $Id: layer3.h,v 1.13 2006/06/01 11:02:10 crich Exp $ * * This file is (c) under GNU PUBLIC LICENSE * */ #include #include #include "fsm.h" #ifdef MISDN_MEMDEBUG #include "memdbg.h" #endif #define SBIT(state) (1<userdata; if (!fi->debug) return; va_start(args, fmt); p += sprintf(p, "Controller 0x%x ApplId %d listen ", app->contr->addr, app->ApplId); p += vsprintf(p, fmt, args); *p = 0; listenDebug(app, CAPI_DBG_LISTEN_STATE, tmp); va_end(args); } static void listen_req_l_x(struct FsmInst *fi, int event, void *arg, int state) { Application_t *app = fi->userdata; _cmsg *cmsg = arg; mISDN_FsmChangeState(fi, state); app->InfoMask = cmsg->InfoMask; app->CIPmask = cmsg->CIPmask; app->CIPmask2 = cmsg->CIPmask2; listenDebug(app, CAPI_DBG_LISTEN_INFO, "set InfoMask to 0x%x", app->InfoMask); listenDebug(app, CAPI_DBG_LISTEN_INFO, "set CIP to 0x%x,0x%x", app->CIPmask, app->CIPmask2); capi_cmsg_answer(cmsg); cmsg->Info = CAPI_NOERROR; if (mISDN_FsmEvent(&app->listen_m, EV_LISTEN_CONF, cmsg)) cmsg_free(cmsg); } static void listen_req_l_0(struct FsmInst *fi, int event, void *arg) { listen_req_l_x(fi, event, arg, ST_LISTEN_L_0_1); } static void listen_req_l_1(struct FsmInst *fi, int event, void *arg) { listen_req_l_x(fi, event, arg, ST_LISTEN_L_1_1); } static void listen_conf_l_x_1(struct FsmInst *fi, int event, void *arg, int state) { Application_t *app = fi->userdata; _cmsg *cmsg = arg; if (cmsg->Info != CAPI_NOERROR) { mISDN_FsmChangeState(fi, state); } else { // Info == 0 if (app->CIPmask == 0) { test_and_clear_bit(APPL_STATE_LISTEN, &app->state); mISDN_FsmChangeState(fi, ST_LISTEN_L_0); } else { test_and_set_bit(APPL_STATE_LISTEN, &app->state); mISDN_FsmChangeState(fi, ST_LISTEN_L_1); } } SendCmsg2Application(app, cmsg); } static void listen_conf_l_0_1(struct FsmInst *fi, int event, void *arg) { listen_conf_l_x_1(fi, event, arg, ST_LISTEN_L_0); } static void listen_conf_l_1_1(struct FsmInst *fi, int event, void *arg) { listen_conf_l_x_1(fi, event, arg, ST_LISTEN_L_1); } static struct FsmNode fn_listen_list[] = { {ST_LISTEN_L_0, EV_LISTEN_REQ, listen_req_l_0}, {ST_LISTEN_L_0_1, EV_LISTEN_CONF, listen_conf_l_0_1}, {ST_LISTEN_L_1, EV_LISTEN_REQ, listen_req_l_1}, {ST_LISTEN_L_1_1, EV_LISTEN_CONF, listen_conf_l_1_1}, }; const int FN_LISTEN_COUNT = sizeof(fn_listen_list)/sizeof(struct FsmNode); // ---------------------------------------------------------------------- // Methods void listenConstr(Application_t *app) { app->listen_m.fsm = &listen_fsm; app->listen_m.state = ST_LISTEN_L_0; app->listen_m.debug = app->contr->debug & CAPI_DBG_LISTEN_STATE; app->listen_m.userdata = app; app->listen_m.printdebug = listen_debug; app->InfoMask = 0; app->CIPmask = 0; app->CIPmask2 = 0; } void listenDestr(Application_t *app) { test_and_clear_bit(APPL_STATE_LISTEN, &app->state); listenDebug(app, CAPI_DBG_LISTEN, "%s", __FUNCTION__); } __u16 listenSendMessage(Application_t *app, struct sk_buff *skb) { _cmsg *cmsg; cmsg = cmsg_alloc(); if (!cmsg) { int_error(); return (CAPI_MSGOSRESOURCEERR); } capi_message2cmsg(cmsg, skb->data); switch (CMSGCMD(cmsg)) { case CAPI_LISTEN_REQ: if (mISDN_FsmEvent(&app->listen_m, EV_LISTEN_REQ, cmsg)) cmsg_free(cmsg); break; default: int_error(); cmsg_free(cmsg); } dev_kfree_skb(skb); return(CAPI_NOERROR); } int listenHandle(Application_t *app, __u16 CIPValue) { if ((app->CIPmask & 1) || (app->CIPmask & (1 << CIPValue))) return 1; return 0; } void init_listen(void) { listen_fsm.state_count = ST_LISTEN_COUNT; listen_fsm.event_count = EV_LISTEN_COUNT; listen_fsm.strEvent = str_ev_listen; listen_fsm.strState = str_st_listen; mISDN_FsmNew(&listen_fsm, fn_listen_list, FN_LISTEN_COUNT); } void free_listen(void) { mISDN_FsmFree(&listen_fsm); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/mISDNManufacturer.h0000644000000000000500000000214411110524073022021 0ustar rootsrc/* $Id: mISDNManufacturer.h,v 1.1 2003/11/11 20:31:34 keil Exp $ * * definition of mISDN own Manufacturer functions * */ #ifndef mISDNManufacturer_H #define mISDNManufacturer_H #define mISDN_MANUFACTURER_ID 0x44534963 /* "mISD" */ /* mISDN_MANUFACTURER message layout * * Controller dword Controller Address * ManuID dword mISDN_MANUFACTURER_ID * Class dword Function Class * Function dword Function Identifier * Function specific struct Data for this Function * * in a CONF the Function specific struct contain at least * a word which is coded as Capi Info word (error code) */ /* * HANDSET special functions * */ #define mISDN_MF_CLASS_HANDSET 1 #define mISDN_MF_HANDSET_ENABLE 1 /* no function specific data */ #define mISDN_MF_HANDSET_DISABLE 2 /* no function specific data */ #define mISDN_MF_HANDSET_SETMICVOLUME 3 /* word volume value */ #define mISDN_MF_HANDSET_SETSPKVOLUME 4 /* word volume value */ #define mISDN_MF_HANDSET_GETMICVOLUME 5 /* CONF: Info, word volume value */ #define mISDN_MF_HANDSET_GETSPKVOLUME 6 /* CONF: Info, word volume value */ #endif /* mISDNManufactor_H */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/supp_serv.c0000644000000000000500000005271211135651702020570 0ustar rootsrc/* $Id: supp_serv.c,v 1.11 2006/03/06 12:52:07 keil Exp $ * */ #include "m_capi.h" #include "asn1_comp.h" #include "asn1_enc.h" #include "dss1.h" #include "helper.h" #include "debug.h" #define T_ACTIVATE 4000 #define T_DEACTIVATE 4000 #define T_INTERROGATE 4000 static void SSProcessAddTimer(SSProcess_t *, int); static __u8 * encodeInvokeComponentHead(__u8 *p) { *p++ = 0; // length -- not known yet *p++ = 0x91; // remote operations protocol *p++ = 0xa1; // invoke component *p++ = 0; // length -- not known yet return p; } static void encodeInvokeComponentLength(__u8 *msg, __u8 *p) { msg[3] = p - &msg[4]; msg[0] = p - &msg[1]; } static int SSProcess_L4L3(SSProcess_t *spc, __u32 prim, struct sk_buff *skb) { int err; err = ControllerL4L3(spc->contr, prim, MISDN_ID_DUMMY, skb); if (err) dev_kfree_skb(skb); return(err); } static __inline__ int capiGetWord(__u8 *p, __u8 *end, __u16 *word) { if (p + 2 > end) { return -1; } *word = *p + (*(p+1) << 8); return 2; } static __inline__ int capiGetDWord(__u8 *p, __u8 *end, __u32 *dword) { if (p + 4 > end) { return -1; } *dword = *p + (*(p+1) << 8) + (*(p+2) << 16) + (*(p+3) << 24); return 4; } static __inline__ int capiGetStruct(__u8 *p, __u8 *_end, __u8 **strct) { int len = *p++; __u8 *end = p + len; if (end > _end) return -1; *strct = p - 1; return len + 1; } #define CAPI_GET(func, parm) \ ret = func(p, end, parm); \ if (ret < 0) return -1; \ p += ret static __inline__ int capiGetFacReqListen(__u8 *p, __u8 *_end, struct FacReqListen *listen) { int len = *p++; int ret; __u8 *end = p + len; if (end > _end) return -1; CAPI_GET(capiGetDWord, &listen->NotificationMask); if (p != end) return -1; return len + 1; } static __inline__ int capiGetFacReqSuspend(__u8 *p, __u8 *_end, struct FacReqSuspend *suspend) { int len = *p++; int ret; __u8 *end = p + len; if (end > _end) return -1; CAPI_GET(capiGetStruct, &suspend->CallIdentity); if (p != end) return -1; return len + 1; } static __inline__ int capiGetFacReqResume(__u8 *p, __u8 *_end, struct FacReqResume *resume) { int len = *p++; int ret; __u8 *end = p + len; if (end > _end) return -1; CAPI_GET(capiGetStruct, &resume->CallIdentity); if (p != end) return -1; return len + 1; } static __inline__ int capiGetFacReqCFActivate(__u8 *p, __u8 *_end, struct FacReqCFActivate *CFActivate) { int len = *p++; int ret; __u8 *end = p + len; if (end > _end) return -1; CAPI_GET(capiGetDWord, &CFActivate->Handle); CAPI_GET(capiGetWord, &CFActivate->Procedure); CAPI_GET(capiGetWord, &CFActivate->BasicService); CAPI_GET(capiGetStruct, &CFActivate->ServedUserNumber); CAPI_GET(capiGetStruct, &CFActivate->ForwardedToNumber); CAPI_GET(capiGetStruct, &CFActivate->ForwardedToSubaddress); if (p != end) return -1; return len + 1; } static __inline__ int capiGetFacReqCFDeactivate(__u8 *p, __u8 *_end, struct FacReqCFDeactivate *CFDeactivate) { int len = *p++; int ret; __u8 *end = p + len; if (end > _end) return -1; CAPI_GET(capiGetDWord, &CFDeactivate->Handle); CAPI_GET(capiGetWord, &CFDeactivate->Procedure); CAPI_GET(capiGetWord, &CFDeactivate->BasicService); CAPI_GET(capiGetStruct, &CFDeactivate->ServedUserNumber); if (p != end) return -1; return len + 1; } #define capiGetFacReqCFInterrogateParameters capiGetFacReqCFDeactivate static __inline__ int capiGetFacReqCFInterrogateNumbers(__u8 *p, __u8 *_end, struct FacReqCFInterrogateNumbers *CFInterrogateNumbers) { int len = *p++; int ret; __u8 *end = p + len; if (end > _end) return -1; CAPI_GET(capiGetDWord, &CFInterrogateNumbers->Handle); if (p != end) return(-1); 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) { int len = *p++; int ret; __u8 *end = p + len; CAPI_GET(capiGetWord, &facReqParm->Function); switch (facReqParm->Function) { case 0x0000: // GetSupportedServices if (*p++ != 0x00) return -1; // empty struct break; case 0x0001: // Listen CAPI_GET(capiGetFacReqListen, &facReqParm->u.Listen); break; case 0x0004: // Suspend CAPI_GET(capiGetFacReqSuspend, &facReqParm->u.Suspend); break; case 0x0005: // Resume CAPI_GET(capiGetFacReqResume, &facReqParm->u.Resume); break; case 0x0009: // CF Activate CAPI_GET(capiGetFacReqCFActivate, &facReqParm->u.CFActivate); break; case 0x000a: // CF Deactivate CAPI_GET(capiGetFacReqCFDeactivate, &facReqParm->u.CFDeactivate); break; case 0x000b: // CF Interrogate Parameters CAPI_GET(capiGetFacReqCFInterrogateParameters, &facReqParm->u.CFInterrogateParameters); break; 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); } if (p != end) return(-1); return(len + 1); } static int GetSupportedServices(FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) { facConfParm->u.GetSupportedServices.SupplementaryServiceInfo = CapiSuccess; facConfParm->u.GetSupportedServices.SupportedServices = mISDNSupportedServices; return CapiSuccess; } static int FacListen(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm) { if (facReqParm->u.Listen.NotificationMask &~ mISDNSupportedServices) { facConfParm->u.Info.SupplementaryServiceInfo = CapiSupplementaryServiceNotSupported; } else { appl->NotificationMask = facReqParm->u.Listen.NotificationMask; facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; } return CapiSuccess; } static int FacCFInterrogateParameters(Application_t *appl, 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(appl, facReqParm->Function, facReqParm->u.CFInterrogateParameters.Handle); if (!sspc) { kfree_skb(skb); return CAPI_MSGOSRESOURCEERR; } p = encodeInvokeComponentHead(sspc->buf); p += encodeInt(p, sspc->invokeId); p += encodeInt(p, 11); // interrogationDiversion p += encodeInterrogationDiversion(p, &facReqParm->u.CFInterrogateParameters); encodeInvokeComponentLength(sspc->buf, p); mISDN_AddIE(skb, IE_FACILITY, sspc->buf); SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb); SSProcessAddTimer(sspc, T_INTERROGATE); facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; return CapiSuccess; } static int FacCFInterrogateNumbers(Application_t *appl, 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(appl, facReqParm->Function, facReqParm->u.CFInterrogateNumbers.Handle); if (!sspc) { kfree_skb(skb); return CAPI_MSGOSRESOURCEERR; } p = encodeInvokeComponentHead(sspc->buf); p += encodeInt(p, sspc->invokeId); p += encodeInt(p, 17); // InterrogateServedUserNumbers encodeInvokeComponentLength(sspc->buf, p); mISDN_AddIE(skb, IE_FACILITY, sspc->buf); SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb); SSProcessAddTimer(sspc, T_INTERROGATE); facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; return CapiSuccess; } static int FacCFActivate(Application_t *appl, 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(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, 7); // activationDiversion p += encodeActivationDiversion(p, &facReqParm->u.CFActivate); encodeInvokeComponentLength(sspc->buf, p); mISDN_AddIE(skb, IE_FACILITY, sspc->buf); SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb); SSProcessAddTimer(sspc, T_ACTIVATE); facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; return CapiSuccess; } static int FacCFDeactivate(Application_t *appl, 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(appl, facReqParm->Function, facReqParm->u.CFDeactivate.Handle); if (!sspc) { kfree_skb(skb); return CAPI_MSGOSRESOURCEERR; } p = encodeInvokeComponentHead(sspc->buf); p += encodeInt(p, sspc->invokeId); p += encodeInt(p, 8); // dectivationDiversion p += encodeDeactivationDiversion(p, &facReqParm->u.CFDeactivate); encodeInvokeComponentLength(sspc->buf, p); mISDN_AddIE(skb, IE_FACILITY, sspc->buf); SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb); SSProcessAddTimer(sspc, T_DEACTIVATE); facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess; 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) { __u8 tmp[10]; __u16 Info; FacReqParm_t facReqParm; FacConfParm_t facConfParm; Plci_t *plci; AppPlci_t *aplci; int ret; if (capiGetFacReqParm(cmsg->FacilityRequestParameter, &facReqParm) < 0) { SendCmsgAnswer2Application(appl, cmsg, CapiIllMessageParmCoding); return; } facConfParm.Function = facReqParm.Function; switch (facReqParm.Function) { case 0x0000: // GetSupportedServices Info = GetSupportedServices(&facReqParm, &facConfParm); break; 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) { Info = CapiIllContrPlciNcci; break; } Info = AppPlciFacSuspendReq(aplci, &facReqParm, &facConfParm); break; case 0x0005: // Resume ret = ControllerNewPlci(appl->contr, &plci, MISDN_ID_ANY); if (ret) { Info = CapiNoPlciAvailable; break; } aplci = ApplicationNewAppPlci(appl, plci); if (!aplci) { ControllerReleasePlci(plci); Info = CapiNoPlciAvailable; break; } Info = AppPlciFacResumeReq(aplci, &facReqParm, &facConfParm); if (Info == CapiSuccess) cmsg->adr.adrPLCI = plci->addr; break; case 0x0009: // CF Activate Info = FacCFActivate(appl, &facReqParm, &facConfParm); break; case 0x000a: // CF Deactivate Info = FacCFDeactivate(appl, &facReqParm, &facConfParm); break; case 0x000b: // CF Interrogate Parameters Info = FacCFInterrogateParameters(appl, &facReqParm, &facConfParm); break; 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; } if (Info == 0x0000) capiEncodeFacConfParm(tmp, &facConfParm); else tmp[0] = 0; cmsg->FacilityConfirmationParameter = tmp; SendCmsgAnswer2Application(appl, cmsg, Info); } SSProcess_t * SSProcessConstr(Application_t *appl, __u16 Function, __u32 Handle) { SSProcess_t *sspc; sspc = SSProcess_alloc(); if (!sspc) return(NULL); memset(sspc, 0, sizeof(SSProcess_t)); sspc->ApplId = appl->ApplId; sspc->Function = Function; sspc->Handle = Handle; ControllerAddSSProcess(appl->contr, sspc); return(sspc); } void SSProcessDestr(SSProcess_t *sspc) { del_timer(&sspc->tl); list_del_init(&sspc->head); SSProcess_free(sspc); } static void SendSSFacilityInd(Application_t *appl, __u32 addr, __u8 *para) { _cmsg *cmsg; CMSG_ALLOC(cmsg); capi_cmsg_header(cmsg, appl->ApplId, CAPI_FACILITY, CAPI_IND, appl->MsgId++, addr); cmsg->FacilitySelector = 0x0003; cmsg->FacilityIndicationParameter = 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) { struct list_head *item; Application_t *appl; list_for_each(item, &contr->Applications) { appl = (Application_t *)item; if (test_bit(APPL_STATE_RELEASE, &appl->state)) continue; if (!(appl->NotificationMask & nMask)) continue; SendSSFacilityInd(appl, contr->addr, para); } } void SSProcessTimeout(unsigned long arg) { SSProcess_t *sspc = (SSProcess_t *) arg; Application_t *appl; __u8 tmp[10], *p; appl = getApplication4Id(sspc->contr, sspc->ApplId); if (!appl) { SSProcessDestr(sspc); return; } p = &tmp[1]; p += capiEncodeWord(p, sspc->Function); p += capiEncodeFacIndCFact(p, CapiTimeOut, sspc->Handle); tmp[0] = p - &tmp[1]; SendSSFacilityInd(appl, sspc->addr, tmp); SSProcessDestr(sspc); } void SSProcessAddTimer(SSProcess_t *sspc, int msec) { sspc->tl.function = SSProcessTimeout; sspc->tl.data = (unsigned long) sspc; init_timer(&sspc->tl); sspc->tl.expires = jiffies + (msec * HZ) / 1000; add_timer(&sspc->tl); } #ifdef ASN1_DEBUG void printPublicPartyNumber(struct PublicPartyNumber *publicPartyNumber) { printk(KERN_DEBUG "(%d) %s\n", publicPartyNumber->publicTypeOfNumber, publicPartyNumber->numberDigits); } void printPartyNumber(struct PartyNumber *partyNumber) { switch (partyNumber->type) { case 0: printk(KERN_DEBUG "unknown %s\n", partyNumber->p.unknown); break; case 1: printPublicPartyNumber(&partyNumber->p.publicPartyNumber); break; } } void printServedUserNr(struct ServedUserNr *servedUserNr) { if (servedUserNr->all) { printk("all\n"); } else { printPartyNumber(&servedUserNr->partyNumber); } } void printAddress(struct Address *address) { printPartyNumber(&address->partyNumber); if (address->partySubaddress[0]) { printk("sub %s\n", address->partySubaddress); } } #endif static void SSProcessSingleFacility(Controller_t *contr, struct asn1_parm *parm) { Application_t *appl; SSProcess_t *sspc; __u8 *p, tmp[256]; switch (parm->comp) { case invoke: #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) { case 0x0009: #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); tmp[0] = p - &tmp[1]; SendSSFacilityInd2All(contr, SuppServiceCF, tmp); break; case 0x000a: #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); tmp[0] = p - &tmp[1]; SendSSFacilityInd2All(contr, SuppServiceCF, tmp); break; default: int_error(); } break; case returnResult: sspc = getSSProcess4Id(contr, parm->u.retResult.invokeId); if (!sspc) return; appl = getApplication4Id(contr, sspc->ApplId); if (!appl) return; p = &tmp[1]; p += capiEncodeWord(p, sspc->Function); switch (sspc->Function) { case 0x0009: p += capiEncodeFacIndCFact(p, 0, sspc->Handle); break; case 0x000a: p += capiEncodeFacIndCFdeact(p, 0, sspc->Handle); break; case 0x000b: p += capiEncodeFacIndCFinterParameters(p, 0, sspc->Handle, &parm->u.retResult.o.resultList); break; case 0x000c: p += capiEncodeFacIndCFinterNumbers(p, 0, sspc->Handle, &parm->u.retResult.o.list); break; default: int_errtxt("returnResult for Function %04x", sspc->Function); break; } tmp[0] = p - &tmp[1]; SendSSFacilityInd(appl, sspc->addr, tmp); SSProcessDestr(sspc); break; case returnError: sspc = getSSProcess4Id(contr, parm->u.retResult.invokeId); if (!sspc) return; appl = getApplication4Id(contr, sspc->ApplId); if (!appl) return; p = &tmp[1]; p += capiEncodeWord(p, sspc->Function); 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_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; Q931_info_t *qi; ie_info_t *fac; __u8 *p, *end; if (!skb) return(ret); switch (prim) { case CC_FACILITY | INDICATION: 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; default: int_error(); } return(ret); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/sysfs_inst.c0000644000000000000500000001427211135651702020745 0ustar rootsrc/* $Id: sysfs_inst.c,v 1.10 2006/09/06 17:24:22 crich Exp $ * * 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) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static ssize_t show_inst_id(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNinstance_t *inst = to_mISDNinstance(class_dev); return sprintf(buf, "%08x\n", inst->id); } static DEVICE_ATTR(id, S_IRUGO, show_inst_id, NULL); static ssize_t show_inst_name(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNinstance_t *inst = to_mISDNinstance(class_dev); return sprintf(buf, "%s\n", inst->name); } static DEVICE_ATTR(name, S_IRUGO, show_inst_name, NULL); static ssize_t show_inst_extentions(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNinstance_t *inst = to_mISDNinstance(class_dev); return sprintf(buf, "%08x\n", inst->extentions); } static DEVICE_ATTR(extentions, S_IRUGO, show_inst_extentions, NULL); static ssize_t show_inst_regcnt(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNinstance_t *inst = to_mISDNinstance(class_dev); return sprintf(buf, "%d\n", inst->regcnt); } static DEVICE_ATTR(regcnt, S_IRUGO, show_inst_regcnt, NULL); #else 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); #endif #ifdef SYSFS_SUPPORT MISDN_PROTO(mISDNinstance, pid, S_IRUGO); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static void release_mISDN_inst(struct device *dev) { #ifdef SYSFS_SUPPORT mISDNinstance_t *inst = to_mISDNinstance(dev); if (inst->obj) sysfs_remove_link(&dev->kobj, "obj"); sysfs_remove_group(&inst->class_dev.kobj, &pid_group); #endif if (core_debug & DEBUG_SYSFS) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) printk(KERN_INFO "release instance class dev %s\n", dev->bus_id); #else printk(KERN_INFO "release instance class dev %s\n", dev->class_id); #endif } #else static void release_mISDN_inst(struct class_device *dev) { #ifdef SYSFS_SUPPORT mISDNinstance_t *inst = to_mISDNinstance(dev); if (inst->obj) sysfs_remove_link(&dev->kobj, "obj"); sysfs_remove_group(&inst->class_dev.kobj, &pid_group); #endif if (core_debug & DEBUG_SYSFS) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) printk(KERN_INFO "release instance class dev %s\n", dev->bus_id); #else printk(KERN_INFO "release instance class dev %s\n", dev->class_id); #endif } #endif static struct class inst_dev_class = { .name = "mISDN-instances", #ifndef CLASS_WITHOUT_OWNER .owner = THIS_MODULE, #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) .dev_release = &release_mISDN_inst, #else .release = &release_mISDN_inst #endif }; int mISDN_register_sysfs_inst(mISDNinstance_t *inst) { int err; #ifdef SYSFS_SUPPORT char name[8]; #endif inst->class_dev.class = &inst_dev_class; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) snprintf(inst->class_dev.bus_id, BUS_ID_SIZE, "inst-%08x", inst->id); err = device_register(&inst->class_dev); #else snprintf(inst->class_dev.class_id, BUS_ID_SIZE, "inst-%08x", inst->id); err = class_device_register(&inst->class_dev); #endif if (err) return(err); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) device_create_file(&inst->class_dev, &dev_attr_id); device_create_file(&inst->class_dev, &dev_attr_name); device_create_file(&inst->class_dev, &dev_attr_extentions); device_create_file(&inst->class_dev, &dev_attr_regcnt); #else 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); #endif #ifdef SYSFS_SUPPORT 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"); } } #endif return(err); #ifdef SYSFS_SUPPORT out_unreg: class_device_unregister(&inst->class_dev); return(err); #endif } void mISDN_unregister_sysfs_inst(mISDNinstance_t *inst) { char name[8]; if (inst && inst->id) { if (inst->st) { sprintf(name,"layer.%d", inst->id & LAYER_ID_MASK); #ifdef SYSFS_SUPPORT 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"); #endif } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) device_unregister(&inst->class_dev); #else class_device_unregister(&inst->class_dev); #endif } } int mISDN_sysfs_inst_init(void) { return(class_register(&inst_dev_class)); } void mISDN_sysfs_inst_cleanup(void) { class_unregister(&inst_dev_class); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/sysfs_obj.c0000644000000000000500000001226111137306013020530 0ustar rootsrc/* $Id: sysfs_obj.c,v 1.10 2006/08/07 23:35:59 keil Exp $ * * 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) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static ssize_t show_obj_name(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNobject_t *obj = to_mISDNobject(class_dev); return sprintf(buf, "%s\n", obj->name); } static DEVICE_ATTR(name, S_IRUGO, show_obj_name, NULL); static ssize_t show_obj_id(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNobject_t *obj = to_mISDNobject(class_dev); return sprintf(buf, "%d\n", obj->id); } static DEVICE_ATTR(id, S_IRUGO, show_obj_id, NULL); static ssize_t show_obj_refcnt(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNobject_t *obj = to_mISDNobject(class_dev); return sprintf(buf, "%d\n", obj->id); } static DEVICE_ATTR(refcnt, S_IRUGO, show_obj_refcnt, NULL); #else 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->id); } static CLASS_DEVICE_ATTR(refcnt, S_IRUGO, show_obj_refcnt, NULL); #endif 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]) { if (pid->pbuf) { t = pid->param[i] + pid->pbuf; l = *t++; p += sprintf(p,"0x%02x,", l); while(l--) p += sprintf(p,"0x%02x,", *t++); } else { p += sprintf(p,"0x00,"); } } else { p += sprintf(p,"0x00,"); } } p--; *p++ = '\n'; return (p -buf); } #ifdef SYSFS_SUPPORT MISDN_PROTO(mISDNobject, BPROTO, S_IRUGO); MISDN_PROTO(mISDNobject, DPROTO, S_IRUGO); #endif static void release_mISDN_obj(struct device *dev) { #ifdef SYSFS_SUPPORT mISDNobject_t *obj = to_mISDNobject(dev); if ( core_debug & DEBUG_SYSFS) 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); #endif } static struct class obj_dev_class = { .name = "mISDN-objects", #ifndef CLASS_WITHOUT_OWNER .owner = THIS_MODULE, #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) .release = &release_mISDN_obj, #else .dev_release = &release_mISDN_obj, #endif }; int mISDN_register_sysfs_obj(mISDNobject_t *obj) { int err; obj->class_dev.class = &obj_dev_class; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) snprintf(obj->class_dev.bus_id, BUS_ID_SIZE, "obj-%d", obj->id); err = device_register(&obj->class_dev); #else snprintf(obj->class_dev.class_id, BUS_ID_SIZE, "obj-%d", obj->id); err = class_device_register(&obj->class_dev); #endif if (err) goto out; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) device_create_file(&obj->class_dev, &dev_attr_id); device_create_file(&obj->class_dev, &dev_attr_name); device_create_file(&obj->class_dev, &dev_attr_refcnt); #else 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); #endif #ifdef SYSFS_SUPPORT 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 #endif return(err); #ifdef SYSFS_SUPPORT out_unreg: class_device_unregister(&obj->class_dev); #endif 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(); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/sysfs_st.c0000644000000000000500000002351011135651702020411 0ustar rootsrc/* $Id: sysfs_st.c,v 1.11 2006/09/06 17:24:22 crich Exp $ * * 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) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static ssize_t show_st_id(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNstack_t *st = to_mISDNstack(class_dev); return sprintf(buf, "%08x\n", st->id); } static DEVICE_ATTR(id, S_IRUGO, show_st_id, NULL); static ssize_t show_st_status(struct device *class_dev, struct device_attribute *attr, char *buf) { mISDNstack_t *st = to_mISDNstack(class_dev); return sprintf(buf, "0x%08lx\n", st->status); } static ssize_t store_st_status(struct device *class_dev, struct device_attribute *attr, const char *buf, size_t count) #else 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) #endif { mISDNstack_t *st = to_mISDNstack(class_dev); ulong status; int err; status = simple_strtol(buf, NULL, 0); printk(KERN_DEBUG "%s: status %08lx\n", __FUNCTION__, status); if (status == (1<new_pid activ */ err = clear_stack(st, 1); if (err) { int_errtxt("clear_stack:%d", err); return(err); } err = set_stack(st ,&st->new_pid); if (err) { int_errtxt("set_stack:%d", err); return(err); } return(count); } else if (status == (1<status = status; wake_up_interruptible(&st->workq); return(count); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static DEVICE_ATTR(status, S_IRUGO | S_IWUSR, show_st_status, store_st_status); static ssize_t store_st_protocol(struct device *class_dev, struct device_attribute *attr, const char *buf, size_t count) #else 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) #endif { 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); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static ssize_t store_st_layermask(struct device *class_dev, struct device_attribute *attr, const char *buf, size_t count) #else static ssize_t store_st_layermask(struct class_device *class_dev, const char *buf, size_t count) #endif { 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); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static ssize_t store_st_parameter(struct device *class_dev, struct device_attribute *attr, const char *buf, size_t count) #else static ssize_t store_st_parameter(struct class_device *class_dev, struct device_attribute *attr, const char *buf, size_t count) #endif { mISDNstack_t *st = to_mISDNstack(class_dev); ulong tmp; char *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 = kzalloc(l + 1, GFP_ATOMIC); if (!st->new_pid.pbuf) return(-ENOMEM); st->new_pid.maxplen = l + 1; st->new_pid.pidx = 1; 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] = st->new_pid.pidx; st->new_pid.pbuf[st->new_pid.pidx++] = tmp & 0xff; while(j--) { if (!*p) break; tmp = simple_strtol(p, &p, 0); st->new_pid.pbuf[st->new_pid.pidx++] = tmp & 0xff; if (*p) p++; else break; } } } return(count); } #ifdef SYSFS_SUPPORT MISDN_PROTO(mISDNstack, pid, S_IRUGO); #endif MISDN_PROTO(mISDNstack, new_pid, S_IRUGO); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) static ssize_t show_st_qlen(struct device *class_dev, struct device_attribute *sttr, char *buf) { mISDNstack_t *st = to_mISDNstack(class_dev); return sprintf(buf, "%d\n", skb_queue_len(&st->msgq)); } static DEVICE_ATTR(qlen, S_IRUGO, show_st_qlen, NULL); static void release_mISDN_stack(struct device *dev) #else 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) #endif { #ifdef SYSFS_SUPPORT 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); if (core_debug & DEBUG_SYSFS) 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); } #endif } static struct class stack_dev_class = { .name = "mISDN-stacks", #ifndef CLASS_WITHOUT_OWNER .owner = THIS_MODULE, #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) .dev_release = &release_mISDN_stack, #else .release = &release_mISDN_stack, #endif }; int mISDN_register_sysfs_stack(mISDNstack_t *st) { int err; #ifdef SYSFS_SUPPORT char name[12]; #endif st->class_dev.class = &stack_dev_class; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) if (st->id & FLG_CHILD_STACK) snprintf(st->class_dev.bus_id, BUS_ID_SIZE, "chst-%08x", st->id); else if (st->id & FLG_CLONE_STACK) snprintf(st->class_dev.bus_id, BUS_ID_SIZE, "clst-%08x", st->id); else snprintf(st->class_dev.bus_id, BUS_ID_SIZE, "st-%08x", st->id); if (st->mgr) st->class_dev.parent = st->mgr->class_dev.parent; err = device_register(&st->class_dev); #else 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); #endif if (err) return(err); #ifdef SYSFS_SUPPORT err = sysfs_create_group(&st->class_dev.kobj, &pid_group); if (err) goto out_unreg; #endif 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; #ifdef SYSFS_SUPPORT err = sysfs_create_group(&st->class_dev.kobj, &new_pid_group); if (err) goto out_unreg; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) device_create_file(&st->class_dev, &dev_attr_id); device_create_file(&st->class_dev, &dev_attr_qlen); device_create_file(&st->class_dev, &dev_attr_status); #else 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); #endif #ifdef SYSFS_SUPPORT 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); } #endif return(err); #ifdef SYSFS_SUPPORT out_unreg: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) device_unregister(&st->class_dev); #else class_device_unregister(&st->class_dev); #endif return(err); #endif } void mISDN_unregister_sysfs_st(mISDNstack_t *st) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) device_unregister(&st->class_dev); #else class_device_unregister(&st->class_dev); #endif } int mISDN_sysfs_st_init(void) { return(class_register(&stack_dev_class)); } void mISDN_sysfs_st_cleanup(void) { class_unregister(&stack_dev_class); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/w6692.c0000755000000000000500000012724311135651702017344 0ustar rootsrc/* $Id: w6692.c,v 1.23 2007/02/13 10:43:45 crich Exp $ * w6692.c low level driver for CCD's hfc-pci based cards * * Author Karsten Keil * based on the w6692 I4L driver from Petr Novak * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include "core.h" #include "channel.h" #include "layer1.h" #include "helper.h" #include "debug.h" #include "w6692.h" #include extern const char *CardType[]; const char *w6692_rev = "$Revision: 1.23 $"; #define DBUSY_TIMER_VALUE 80 enum { W6692_ASUS, W6692_WINBOND, W6692_USR }; /* private data in the PCI devices list */ typedef struct _w6692_map{ u_int subtype; char *name; } w6692_map_t; static w6692_map_t w6692_map[] = { {W6692_ASUS, "Dynalink/AsusCom IS64PH"}, {W6692_WINBOND, "Winbond W6692"}, {W6692_USR, "USR W6692"} }; #ifndef PCI_VENDOR_ID_USR2 #define PCI_VENDOR_ID_USR2 0x16ec #define PCI_DEVICE_ID_USR2_6692 0x3409 #endif typedef struct _w6692_bc { struct timer_list timer; u_char b_mode; } w6692_bc; typedef struct _w6692pci { struct list_head list; struct pci_dev *pdev; u_int subtype; u_int irq; u_int irqcnt; u_int addr; int pots; int led; spinlock_t lock; u_char imask; u_char pctl; u_char xaddr; u_char xdata; w6692_bc wbc[2]; channel_t dch; channel_t bch[2]; } w6692pci; #define W_LED1_ON 1 #define W_LED1_S0STATUS 2 static __inline__ u_char ReadW6692(w6692pci *card, u_char offset) { return (inb(card->addr + offset)); } static __inline__ void WriteW6692(w6692pci *card, u_char offset, u_char value) { outb(value, card->addr + offset); } static __inline__ u_char ReadW6692B(w6692pci *card, int bchan, u_char offset) { return (inb(card->addr + (bchan ? 0x40 : 0) + offset)); } static __inline__ void WriteW6692B(w6692pci *card, int bchan, u_char offset, u_char value) { outb(value, card->addr + (bchan ? 0x40 : 0) + offset); } static void enable_hwirq(w6692pci *card) { WriteW6692(card, W_IMASK, card->imask); } static void disable_hwirq(w6692pci *card) { WriteW6692(card, W_IMASK, 0xff); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) static char *W6692Ver[] __devinitdata = #else static char *W6692Ver[] __initdata = #endif {"W6692 V00", "W6692 V01", "W6692 V10", "W6692 V11"}; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) static void __devinit W6692Version(w6692pci *card, char *s) { #else static void __init W6692Version(w6692pci *card, char *s) { #endif int val; val = ReadW6692(card, W_D_RBCH); printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]); } static void w6692_led_handler(w6692pci *card, int on) { if (!card->led || card->subtype == W6692_USR) return; if (on) { card->xdata &= 0xfb; /* LED ON */ WriteW6692(card, W_XDATA, card->xdata); } else { card->xdata |= 0x04; /* LED OFF */ WriteW6692(card, W_XDATA, card->xdata); } } static void ph_command(w6692pci *card, u_char command) { if (card->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&card->dch.inst, "ph_command %x", command); WriteW6692(card, W_CIX, command); } static void W6692_new_ph(channel_t *dch) { u_int prim = PH_SIGNAL | INDICATION; u_int para = 0; switch (dch->state) { case W_L1CMD_RST: ph_command(dch->hw, W_L1CMD_DRC); mISDN_queue_data(&dch->inst, FLG_MSG_UP, PH_CONTROL | INDICATION, HW_RESET, 0, NULL, 0); /* fall trough */ case W_L1IND_CD: prim = PH_CONTROL | CONFIRM; para = HW_DEACTIVATE; test_and_clear_bit(FLG_ACTIVE, &dch->Flags); break; case W_L1IND_DRD: prim = PH_CONTROL | INDICATION; para = HW_DEACTIVATE; test_and_clear_bit(FLG_ACTIVE, &dch->Flags); break; case W_L1IND_CE: prim = PH_CONTROL | INDICATION; para = HW_POWERUP; break; case W_L1IND_LD: para = ANYSIGNAL; break; case W_L1IND_ARD: para = INFO2; break; case W_L1IND_AI8: para = INFO4_P8; test_and_set_bit(FLG_ACTIVE, &dch->Flags); break; case W_L1IND_AI10: para = INFO4_P10; test_and_set_bit(FLG_ACTIVE, &dch->Flags); break; default: return; } mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0); } static void W6692_empty_Dfifo(w6692pci *card, int count) { channel_t *dch = &card->dch; u_char *ptr; if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) mISDN_debugprint(&dch->inst, "empty_Dfifo"); if (!dch->rx_skb) { if (!(dch->rx_skb = alloc_stack_skb(dch->maxlen, dch->up_headerlen))) { printk(KERN_WARNING "mISDN: D receive out of memory\n"); WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); return; } } if ((dch->rx_skb->len + count) >= dch->maxlen) { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "empty_Dfifo overrun %d", dch->rx_skb->len + count); WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); return; } ptr = skb_put(dch->rx_skb, count); insb(card->addr + W_D_RFIFO, ptr, count); WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK); if (dch->debug & L1_DEB_ISAC_FIFO) { char *t = dch->log; t += sprintf(t, "empty_Dfifo cnt %d", count); mISDN_QuickHex(t, ptr, count); mISDN_debugprint(&dch->inst, dch->log); } } static void W6692_fill_Dfifo(w6692pci *card) { channel_t *dch = &card->dch; int count; u_char *ptr; u_char cmd = W_D_CMDR_XMS; if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO)) mISDN_debugprint(&dch->inst, "fill_Dfifo"); if (!dch->tx_skb) return; count = dch->tx_skb->len - dch->tx_idx; if (count <= 0) return; if (count > 32) count = 32; else cmd |= W_D_CMDR_XME; ptr = dch->tx_skb->data + dch->tx_idx; dch->tx_idx += count; outsb(card->addr + W_D_XFIFO, ptr, count); WriteW6692(card, W_D_CMDR, cmd); if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) { mISDN_debugprint(&dch->inst, "fill_Dfifo dbusytimer running"); del_timer(&dch->timer); } 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->log; t += sprintf(t, "fill_Dfifo cnt %d", count); mISDN_QuickHex(t, ptr, count); mISDN_debugprint(&dch->inst, dch->log); } } static void d_retransmit(w6692pci *card) { channel_t *dch = &card->dch; if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) del_timer(&dch->timer); #ifdef FIXME if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) dchannel_sched_event(dch, D_CLEARBUSY); #endif if (test_bit(FLG_TX_BUSY, &dch->Flags)) { /* Restart frame */ dch->tx_idx = 0; W6692_fill_Dfifo(card); } else if (dch->tx_skb) { /* should not happen */ int_error(); test_and_set_bit(FLG_TX_BUSY, &dch->Flags); dch->tx_idx = 0; W6692_fill_Dfifo(card); } else { printk(KERN_WARNING "mISDN: w6692 XDU no TX_BUSY\n"); mISDN_debugprint(&dch->inst, "XDU no TX_BUSY"); 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); W6692_fill_Dfifo(card); } else { printk(KERN_WARNING "w6692 xdu irq TX_NEXT without skb\n"); test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); } } } } static void handle_rxD(w6692pci *card) { u_char stat; int count; stat = ReadW6692(card, W_D_RSTA); if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) { if (stat & W_D_RSTA_RDOV) { if (card->dch.debug & L1_DEB_WARN) mISDN_debugprint(&card->dch.inst, "D-channel RDOV"); #ifdef ERROR_STATISTIC card->dch.err_rx++; #endif } if (stat & W_D_RSTA_CRCE) { if (card->dch.debug & L1_DEB_WARN) mISDN_debugprint(&card->dch.inst, "D-channel CRC error"); #ifdef ERROR_STATISTIC card->dch.err_crc++; #endif } if (stat & W_D_RSTA_RMB) { if (card->dch.debug & L1_DEB_WARN) mISDN_debugprint(&card->dch.inst, "D-channel ABORT"); #ifdef ERROR_STATISTIC card->dch.err_rx++; #endif } if (card->dch.rx_skb) dev_kfree_skb(card->dch.rx_skb); WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST); } else { count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1); if (count == 0) count = W_D_FIFO_THRESH; W6692_empty_Dfifo(card, count); if (card->dch.rx_skb) mISDN_queueup_newhead(&card->dch.inst, 0, PH_DATA_IND, MISDN_ID_ANY, card->dch.rx_skb); } card->dch.rx_skb = NULL; } static void handle_txD(w6692pci *card) { register channel_t *dch = &card->dch; if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) del_timer(&dch->timer); #ifdef FIXME if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) dchannel_sched_event(dch, D_CLEARBUSY); #endif if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len) { W6692_fill_Dfifo(card); } else { 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); W6692_fill_Dfifo(card); } else { printk(KERN_WARNING "w6692 txD irq TX_NEXT without skb\n"); test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); } } else { dch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); } } } static void handle_statusD(w6692pci *card) { register channel_t *dch = &card->dch; u_char exval, v1, cir; exval = ReadW6692(card, W_D_EXIR); if (card->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&card->dch.inst, "D_EXIR %02x", exval); if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { /* Transmit underrun/collision */ if (card->dch.debug & L1_DEB_WARN) mISDN_debugprint(&card->dch.inst, "D-channel underrun/collision"); #ifdef ERROR_STATISTIC dch->err_tx++; #endif d_retransmit(card); } if (exval & W_D_EXI_RDOV) { /* RDOV */ if (card->dch.debug & L1_DEB_WARN) mISDN_debugprint(&card->dch.inst, "D-channel RDOV"); WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST); } if (exval & W_D_EXI_TIN2) /* TIN2 - never */ if (card->dch.debug & L1_DEB_WARN) mISDN_debugprint(&card->dch.inst, "spurious TIN2 interrupt"); if (exval & W_D_EXI_MOC) { /* MOC - not supported */ v1 = ReadW6692(card, W_MOSR); if (card->dch.debug & L1_DEB_ISAC) { mISDN_debugprint(&card->dch.inst, "spurious MOC interrupt"); mISDN_debugprint(&card->dch.inst, "MOSR %02x", v1); } } if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */ cir = ReadW6692(card, W_CIR); if (card->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&card->dch.inst, "ISC CIR=0x%02X", cir); if (cir & W_CIR_ICC) { v1 = cir & W_CIR_COD_MASK; if (card->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&card->dch.inst, "ph_state_change %x -> %x", dch->state, v1); dch->state = v1; if (card->led & W_LED1_S0STATUS) { switch (v1) { case W_L1IND_AI8: case W_L1IND_AI10: w6692_led_handler(card, 1); break; default: w6692_led_handler(card, 0); break; } } W6692_new_ph(dch); } if (cir & W_CIR_SCC) { v1 = ReadW6692(card, W_SQR); if (card->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&card->dch.inst, "SCC SQR=0x%02X", v1); } } if (exval & W_D_EXI_WEXP) { if (card->dch.debug & L1_DEB_WARN) mISDN_debugprint(&card->dch.inst, "spurious WEXP interrupt!"); } if (exval & W_D_EXI_TEXP) { if (card->dch.debug & L1_DEB_WARN) mISDN_debugprint(&card->dch.inst, "spurious TEXP interrupt!"); } } static void W6692_empty_Bfifo(channel_t *bch, int count) { u_char *ptr; w6692pci *card = bch->inst.privat; if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) mISDN_debugprint(&bch->inst, "empty_Bfifo %d", count); if (unlikely(bch->state == ISDN_PID_NONE)) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "empty_Bfifo ISDN_PID_NONE"); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); if (bch->rx_skb) skb_trim(bch->rx_skb, 0); return; } if (!bch->rx_skb) { bch->rx_skb = alloc_stack_skb(bch->maxlen, bch->up_headerlen); if (unlikely(!bch->rx_skb)) { printk(KERN_WARNING "mISDN: B receive out of memory\n"); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); return; } } if (bch->rx_skb->len + count > bch->maxlen) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "empty_Bfifo incoming packet too large"); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); skb_trim(bch->rx_skb, 0); return; } ptr = skb_put(bch->rx_skb, count); insb(card->addr + W_B_RFIFO + (bch->channel ? 0x40 : 0), ptr, count); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); if (bch->debug & L1_DEB_HSCX_FIFO) { char *t = bch->log; t += sprintf(t, "empty_Bfifo B%d cnt %d", bch->channel, count); mISDN_QuickHex(t, ptr, count); mISDN_debugprint(&bch->inst, bch->log); } } static void W6692_fill_Bfifo(channel_t *bch) { w6692pci *card = bch->inst.privat; int count; u_char *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS; if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) mISDN_debugprint(&bch->inst, "%s", __FUNCTION__); if (!bch->tx_skb) return; count = bch->tx_skb->len - bch->tx_idx; if (count <= 0) return; ptr = bch->tx_skb->data + bch->tx_idx; if (count > W_B_FIFO_THRESH) { count = W_B_FIFO_THRESH; } else { if (test_bit(FLG_HDLC, &bch->Flags)) cmd |= W_B_CMDR_XME; } if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO)) mISDN_debugprint(&bch->inst, "%s: %d/%d", __FUNCTION__, count, bch->tx_idx); bch->tx_idx += count; outsb(card->addr + W_B_XFIFO + (bch->channel ? 0x40 : 0), ptr, count); WriteW6692B(card, bch->channel, W_B_CMDR, cmd); if (bch->debug & L1_DEB_HSCX_FIFO) { char *t = bch->log; t += sprintf(t, "fill_Bfifo B%d cnt %d", bch->channel, count); mISDN_QuickHex(t, ptr, count); mISDN_debugprint(&bch->inst, bch->log); } } static int setvolume(channel_t *bch, int mic, struct sk_buff *skb) { w6692pci *card = bch->inst.privat; u16 *vol = (u16 *)skb->data; u_char val; if ((card->pots == 0) || !test_bit(FLG_TRANSPARENT, &bch->Flags)) return(-ENODEV); if (skb->len < 2) return(-EINVAL); if (*vol > 7) return(-EINVAL); val = *vol & 7; val = 7 - val; if (mic) { val <<= 3; card->xaddr &= 0xc7; } else { card->xaddr &= 0xf8; } card->xaddr |= val; WriteW6692(card, W_XADDR, card->xaddr); return(0); } static int enable_pots(channel_t *bch) { w6692_bc *bhw = bch->hw; w6692pci *card = bch->inst.privat; if ((card->pots == 0) || !test_bit(FLG_TRANSPARENT, &bch->Flags)) return(-ENODEV); bhw->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0; WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); card->pctl |= (bch->channel ? W_PCTL_PCX : 0); WriteW6692(card, W_PCTL, card->pctl); return(0); } static int disable_pots(channel_t *bch) { w6692_bc *bhw = bch->hw; w6692pci *card = bch->inst.privat; if (card->pots == 0) return(-ENODEV); bhw->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0); WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST); return(0); } static int mode_w6692(channel_t *bch, int bc, int protocol) { w6692pci *card = bch->inst.privat; w6692_bc *bhw = bch->hw; if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "B%d protocol %x-->%x ch %d-->%d", bch->channel, bch->state, protocol, bch->channel, bc); switch (protocol) { case (-1): /* used for init */ bch->state = -1; bch->channel = bc; case (ISDN_PID_NONE): if (bch->state == ISDN_PID_NONE) break; bch->state = ISDN_PID_NONE; if (card->pots && (bhw->b_mode & W_B_MODE_EPCM)) disable_pots(bch); bhw->b_mode = 0; bch->tx_idx = 0; if (bch->next_skb) { dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; } if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); test_and_clear_bit(FLG_HDLC, &bch->Flags); test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); break; case (ISDN_PID_L1_B_64TRANS): bch->state = protocol; bhw->b_mode = W_B_MODE_MMS; WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); WriteW6692B(card, bch->channel, W_B_EXIM, 0); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST); test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); break; case (ISDN_PID_L1_B_64HDLC): bch->state = protocol; bhw->b_mode = W_B_MODE_ITF; WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode); WriteW6692B(card, bch->channel, W_B_ADM1, 0xff); WriteW6692B(card, bch->channel, W_B_ADM2, 0xff); WriteW6692B(card, bch->channel, W_B_EXIM, 0); WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST); test_and_set_bit(FLG_HDLC, &bch->Flags); break; default: mISDN_debugprint(&bch->inst, "prot not known %x", protocol); return(-ENOPROTOOPT); } return(0); } static void send_next(channel_t *bch) { if (test_bit(FLG_ACTIVE, &bch->Flags)) return; if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) W6692_fill_Bfifo(bch); else { if (bch->tx_skb) dev_kfree_skb(bch->tx_skb); bch->tx_idx = 0; 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); W6692_fill_Bfifo(bch); } else { test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); printk(KERN_WARNING "W6692 tx irq TX_NEXT without skb\n"); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } } else { bch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); } } } static void W6692B_interrupt(w6692pci *card, int ch) { channel_t *bch = &card->bch[ch]; int count; u_char stat, star = 0; struct sk_buff *skb; stat = ReadW6692B(card, ch, W_B_EXIR); if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat); if (stat & W_B_EXI_RME) { star = ReadW6692B(card, ch, W_B_STAR); if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) { if ((star & W_B_STAR_RDOV) && test_bit(FLG_ACTIVE, &bch->Flags)) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "B%d RDOV protocol=%x", ch +1, bch->state); #ifdef ERROR_STATISTIC bch->err_rdo++; #endif } if (test_bit(FLG_HDLC, &bch->Flags)) { if (star & W_B_STAR_CRCE) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "B%d CRC error", ch+1); #ifdef ERROR_STATISTIC bch->err_crc++; #endif } if (star & W_B_STAR_RMB) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "B%d message abort", ch+1); #ifdef ERROR_STATISTIC bch->err_inv++; #endif } } WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT); if (bch->rx_skb) skb_trim(bch->rx_skb, 0); } else { count = ReadW6692B(card, ch, W_B_RBCL) & (W_B_FIFO_THRESH - 1); if (count == 0) count = W_B_FIFO_THRESH; W6692_empty_Bfifo(bch, count); if (bch->rx_skb && bch->rx_skb->len > 0) { if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "Bchan Frame %d", bch->rx_skb->len); 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; } queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, skb); } } } if (stat & W_B_EXI_RMR) { if (!(stat & W_B_EXI_RME)) star = ReadW6692B(card, ch, W_B_STAR); if (star & W_B_STAR_RDOV) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "B%d RDOV protocol=%x", ch +1, bch->state); #ifdef ERROR_STATISTIC bch->err_rdo++; #endif WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT); } else { W6692_empty_Bfifo(bch, W_B_FIFO_THRESH); if (test_bit(FLG_TRANSPARENT, &bch->Flags) && bch->rx_skb && (bch->rx_skb->len > 0)) { /* receive audio data */ if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "Bchan Frame %d", bch->rx_skb->len); queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, bch->rx_skb); bch->rx_skb = NULL; } } } if (stat & W_B_EXI_RDOV) { if (!(star & W_B_STAR_RDOV)) { /* only if it is not handled yet */ if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "B%d RDOV IRQ protocol=%x", ch +1, bch->state); #ifdef ERROR_STATISTIC bch->err_rdo++; #endif WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT); } } if (stat & W_B_EXI_XFR) { if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) { star = ReadW6692B(card, ch, W_B_STAR); if (bch->debug & L1_DEB_HSCX) mISDN_debugprint(&bch->inst, "B%d star %02x", ch +1, star); } if (star & W_B_STAR_XDOW) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "B%d XDOW protocol=%x", ch +1, bch->state); #ifdef ERROR_STATISTIC bch->err_xdu++; #endif WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); /* resend */ if (bch->tx_skb) { if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) bch->tx_idx = 0; } } send_next(bch); if (stat & W_B_EXI_XDUN) return; /* handle XDOW only once */ } if (stat & W_B_EXI_XDUN) { if (bch->debug & L1_DEB_WARN) mISDN_debugprint(&bch->inst, "B%d XDUN protocol=%x", ch +1, bch->state); #ifdef ERROR_STATISTIC bch->err_xdu++; #endif WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT); /* resend */ if (bch->tx_skb) { if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) bch->tx_idx = 0; } send_next(bch); } } static irqreturn_t w6692_interrupt(int intno, void *dev_id, struct pt_regs *regs) { w6692pci *card = dev_id; u_char ista; spin_lock(&card->lock); ista = ReadW6692(card, W_ISTA); if ((ista | card->imask) == card->imask) { /* possible a shared IRQ reqest */ spin_unlock(&card->lock); return IRQ_NONE; } card->irqcnt++; /* Begin IRQ handler */ if (card->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&card->dch.inst, "ista %02x", ista); ista &= ~card->imask; if (ista & W_INT_B1_EXI) W6692B_interrupt(card, 0); if (ista & W_INT_B2_EXI) W6692B_interrupt(card, 1); if (ista & W_INT_D_RME) handle_rxD(card); if (ista & W_INT_D_RMR) W6692_empty_Dfifo(card, W_D_FIFO_THRESH); if (ista & W_INT_D_XFR) handle_txD(card); if (ista & W_INT_D_EXI) handle_statusD(card); if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */ mISDN_debugprint(&card->dch.inst, "W6692 spurious XINT!"); /* End IRQ Handler */ spin_unlock(&card->lock); return IRQ_HANDLED; } static void dbusy_timer_handler(channel_t *dch) { w6692pci *card = dch->hw; int rbch, star; u_long flags; if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) { spin_lock_irqsave(dch->inst.hwlock, flags); rbch = ReadW6692(card, W_D_RBCH); star = ReadW6692(card, W_D_STAR); if (dch->debug) mISDN_debugprint(&dch->inst, "D-Channel Busy RBCH %02x STAR %02x", rbch, star); if (star & W_D_STAR_XBZ) { /* D-Channel Busy */ test_and_set_bit(FLG_L1_BUSY, &dch->Flags); } else { /* discard frame; reset transceiver */ test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags); if (dch->tx_idx) { dch->tx_idx = 0; } else { printk(KERN_WARNING "mISDN: W6692 D-Channel Busy no tx_idx\n"); mISDN_debugprint(&dch->inst, "D-Channel Busy no tx_idx"); } /* Transmitter reset */ WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST); /* Transmitter reset */ } spin_unlock_irqrestore(dch->inst.hwlock, flags); } } void initW6692(w6692pci *card) { u_char val; card->dch.timer.function = (void *) dbusy_timer_handler; card->dch.timer.data = (u_long) &card->dch; init_timer(&card->dch.timer); mode_w6692(&card->bch[0], 0, -1); mode_w6692(&card->bch[1], 1, -1); WriteW6692(card, W_D_CTL, 0x00); disable_hwirq(card); WriteW6692(card, W_D_SAM, 0xff); WriteW6692(card, W_D_TAM, 0xff); WriteW6692(card, W_D_MODE, W_D_MODE_RACT); card->dch.state = W_L1CMD_RST; ph_command(card, W_L1CMD_RST); ph_command(card, W_L1CMD_ECK); /* enable all IRQ but extern */ card->imask = 0x18; WriteW6692(card, W_D_EXIM, 0x00); WriteW6692B(card, 0, W_B_EXIM, 0); WriteW6692B(card, 1, W_B_EXIM, 0); /* Reset D-chan receiver and transmitter */ WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST); /* Reset B-chan receiver and transmitter */ WriteW6692B(card, 0, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); WriteW6692B(card, 1, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST); /* enable peripheral */ if (card->subtype == W6692_USR) { /* seems that USR implemented some power control features * Pin 79 is connected to the oscilator circuit so we * have to handle it here */ card->pctl = 0x80; card->xdata = 0; WriteW6692(card, W_PCTL, card->pctl); WriteW6692(card, W_XDATA, card->xdata); } else { card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | W_PCTL_OE1 | W_PCTL_OE0; if (card->pots) { card->xaddr = 0x00; /* all sw off */ card->xdata |= 0x06; /* POWER UP/ LED OFF / ALAW */ WriteW6692(card, W_PCTL, card->pctl); WriteW6692(card, W_XADDR, card->xaddr); WriteW6692(card, W_XDATA, card->xdata); val = ReadW6692(card, W_XADDR); if (card->dch.debug & L1_DEB_ISAC) mISDN_debugprint(&card->dch.inst, "W_XADDR: %02x", val); if (card->led & W_LED1_ON) w6692_led_handler(card, 1); else w6692_led_handler(card, 0); } } } static void reset_w6692(w6692pci *card) { WriteW6692(card, W_D_CTL, W_D_CTL_SRST); mdelay(10); WriteW6692(card, W_D_CTL, 0); } static int init_card(w6692pci *card) { int cnt = 3; u_long flags; spin_lock_irqsave(&card->lock, flags); disable_hwirq(card); spin_unlock_irqrestore(&card->lock, flags); if (request_irq(card->irq, (void*)w6692_interrupt, SA_SHIRQ, "w6692", card)) { printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", card->irq); return(-EIO); } spin_lock_irqsave(&card->lock, flags); while (cnt) { initW6692(card); enable_hwirq(card); spin_unlock_irqrestore(&card->lock, flags); /* Timeout 10ms */ current->state = TASK_UNINTERRUPTIBLE; schedule_timeout((10*HZ)/1000); printk(KERN_INFO "w6692: IRQ %d count %d\n", card->irq, card->irqcnt); if (!card->irqcnt) { printk(KERN_WARNING "w6692: IRQ(%d) getting no interrupts during init %d\n", card->irq, 4 - cnt); if (cnt == 1) { return (-EIO); } else { spin_lock_irqsave(&card->lock, flags); reset_w6692(card); cnt--; } } else { return(0); } } spin_unlock_irqrestore(&card->lock, flags); return(-EIO); } #define MAX_CARDS 4 static int w6692_cnt; static u_int protocol[MAX_CARDS]; static int layermask[MAX_CARDS]; static mISDNobject_t w6692; static int debug; static int pots[MAX_CARDS]; static int led[MAX_CARDS]; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #define MODULE_PARM_T "1-4i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #ifdef OLD_MODULE_PARAM_ARRAY static int num_led=0, num_pots=0, num_protocol=0, num_layermask=0; module_param_array(led, uint, num_led, S_IRUGO | S_IWUSR); module_param_array(pots, uint, num_pots, S_IRUGO | S_IWUSR); module_param_array(protocol, uint, num_protocol, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, num_layermask, S_IRUGO | S_IWUSR); #else module_param_array(led, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(pots, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(protocol, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, NULL, S_IRUGO | S_IWUSR); #endif #endif #endif /******************************/ /* Layer2 -> Layer 1 Transfer */ /******************************/ static int w6692_bmsg(channel_t *bch, struct sk_buff *skb) { int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == (PH_ACTIVATE | REQUEST)) || (hh->prim == (DL_ESTABLISH | REQUEST))) { if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { spin_lock_irqsave(bch->inst.hwlock, flags); ret = mode_w6692(bch, bch->channel, bch->inst.pid.protocol[1]); spin_unlock_irqrestore(bch->inst.hwlock, flags); } #ifdef FIXME if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif skb_trim(skb, 0); return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb)); } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || (hh->prim == (DL_RELEASE | REQUEST)) || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { spin_lock_irqsave(bch->inst.hwlock, flags); if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; } if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); mode_w6692(bch, bch->channel, ISDN_PID_NONE); test_and_clear_bit(FLG_ACTIVE, &bch->Flags); spin_unlock_irqrestore(bch->inst.hwlock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) { #ifdef FIXME if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb)) return(0); } } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(bch->inst.hwlock, flags); if (hh->dinfo == HW_POTS_ON) { ret = enable_pots(bch); } else if (hh->dinfo == HW_POTS_OFF) { ret = disable_pots(bch); } else if (hh->dinfo == HW_POTS_SETMICVOL) { ret = setvolume(bch, 1, skb); } else if (hh->dinfo == HW_POTS_SETSPKVOL) { ret = setvolume(bch, 0, skb); } else ret = -EINVAL; spin_unlock_irqrestore(bch->inst.hwlock, flags); } else ret = -EAGAIN; if (!ret) dev_kfree_skb(skb); return(ret); } static int w6692_dmsg(channel_t *dch, struct sk_buff *skb) { int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if (hh->prim == (PH_SIGNAL | REQUEST)) { spin_lock_irqsave(dch->inst.hwlock, flags); if (hh->dinfo == INFO3_P8) ph_command(dch->hw, W_L1CMD_AR8); else if (hh->dinfo == INFO3_P10) ph_command(dch->hw, W_L1CMD_AR10); else ret = -EINVAL; spin_unlock_irqrestore(dch->inst.hwlock, flags); } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(dch->inst.hwlock, flags); if (hh->dinfo == HW_RESET) { if (dch->state != W_L1IND_DRD) ph_command(dch->hw, W_L1CMD_RST); ph_command(dch->hw, W_L1CMD_ECK); } else if (hh->dinfo == HW_POWERUP) { ph_command(dch->hw, W_L1CMD_ECK); } else if (hh->dinfo == HW_DEACTIVATE) { if (dch->next_skb) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } if (dch->tx_skb) { dev_kfree_skb(dch->tx_skb); dch->tx_skb = NULL; } if (dch->rx_skb) { dev_kfree_skb(dch->rx_skb); dch->rx_skb = NULL; } test_and_clear_bit(FLG_ACTIVE, &dch->Flags); 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); #ifdef FIXME if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) dchannel_sched_event(dch, D_CLEARBUSY); #endif } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) { u_char val = 0; if (1 & hh->dinfo) val |= 0x0c; if (2 & hh->dinfo) val |= 0x3; /* !!! not implemented yet */ } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "w6692_dmsg unknown ctrl %x", hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(dch->inst.hwlock, flags); } else ret = -EAGAIN; if (!ret) dev_kfree_skb(skb); return(ret); } static int w6692_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *chan = container_of(inst, channel_t, inst); int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(chan, hh->dinfo, skb); if (ret > 0) { /* direct TX */ if (test_bit(FLG_BCHANNEL, &chan->Flags)) W6692_fill_Dfifo(chan->hw); else if (test_bit(FLG_DCHANNEL, &chan->Flags)) W6692_fill_Bfifo(chan); else int_error(); ret = 0; } spin_unlock_irqrestore(inst->hwlock, flags); return(ret); } if (test_bit(FLG_DCHANNEL, &chan->Flags)) { ret = w6692_dmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (test_bit(FLG_BCHANNEL, &chan->Flags)) { ret = w6692_bmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } int __devinit setup_w6692(w6692pci *card) { u_int val; if (!request_region(card->addr, 256, "w6692")) { printk(KERN_WARNING "mISDN: %s config port %x-%x already in use\n", "w6692", card->addr, card->addr + 255); return(-EIO); } W6692Version(card, "W6692:"); val = ReadW6692(card, W_ISTA); if (debug) printk(KERN_DEBUG "W6692 ISTA=0x%X\n", val); val = ReadW6692(card, W_IMASK); if (debug) printk(KERN_DEBUG "W6692 IMASK=0x%X\n", val); val = ReadW6692(card, W_D_EXIR); if (debug) printk(KERN_DEBUG "W6692 D_EXIR=0x%X\n", val); val = ReadW6692(card, W_D_EXIM); if (debug) printk(KERN_DEBUG "W6692 D_EXIM=0x%X\n", val); val = ReadW6692(card, W_D_RSTA); if (debug) printk(KERN_DEBUG "W6692 D_RSTA=0x%X\n", val); return (0); } static void release_card(w6692pci *card) { u_long flags; 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); mode_w6692(&card->bch[0], 0, ISDN_PID_NONE); mode_w6692(&card->bch[1], 1, ISDN_PID_NONE); if (card->led || card->subtype == W6692_USR) { card->xdata |= 0x04; /* LED OFF */ WriteW6692(card, W_XDATA, card->xdata); } release_region(card->addr, 256); mISDN_freechannel(&card->bch[1]); mISDN_freechannel(&card->bch[0]); mISDN_freechannel(&card->dch); spin_unlock_irqrestore(&card->lock, flags); mISDN_ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL); spin_lock_irqsave(&w6692.lock, flags); list_del(&card->list); spin_unlock_irqrestore(&w6692.lock, flags); pci_disable_device(card->pdev); pci_set_drvdata(card->pdev, NULL); kfree(card); } static int w6692_manager(void *data, u_int prim, void *arg) { w6692pci *card; mISDNinstance_t *inst = data; struct sk_buff *skb; int channel = -1; u_long flags; if (debug & 0x10000) printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n", __FUNCTION__, data, prim, arg); if (!data) { MGR_HASPROTOCOL_HANDLER(prim,arg,&w6692) printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); return(-EINVAL); } spin_lock_irqsave(&w6692.lock, flags); list_for_each_entry(card, &w6692.ilist, list) { if (&card->dch.inst == inst) { channel = 2; break; } if (&card->bch[0].inst == inst) { channel = 0; break; } if (&card->bch[1].inst == inst) { channel = 1; break; } } spin_unlock_irqrestore(&w6692.lock, flags); if (channel<0) { printk(KERN_WARNING "%s: no channel data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return(-EINVAL); } switch(prim) { case MGR_REGLAYER | CONFIRM: if (channel == 2) mISDN_setpara(&card->dch, &inst->st->para); else mISDN_setpara(&card->bch[channel], &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { if (w6692_l2l1(inst, skb)) dev_kfree_skb(skb); } else printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: if (channel == 2) mISDN_setpara(&card->dch, arg); else mISDN_setpara(&card->bch[channel], arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { release_card(card); } else { w6692.refcnt--; } break; case MGR_SETSTACK | INDICATION: if ((channel!=2) && (inst->pid.global == 2)) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (w6692_l2l1(inst, skb)) dev_kfree_skb(skb); } if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } break; case MGR_GLOBALOPT | REQUEST: if (arg) { /* FIXME: detect cards with HEADSET */ u_int *gopt = arg; *gopt = GLOBALOPT_INTERNAL_CTRL | GLOBALOPT_EXTERNAL_EQUIPMENT | GLOBALOPT_HANDSET; } else return(-EINVAL); break; case MGR_SELCHANNEL | REQUEST: /* no special procedure */ return(-EINVAL); PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); default: printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim); return(-EINVAL); } return(0); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) static int __devinit setup_instance(w6692pci *card) { #else static int setup_instance(w6692pci *card) { #endif int i, err; mISDN_pid_t pid; u_long flags; spin_lock_irqsave(&w6692.lock, flags); list_add_tail(&card->list, &w6692.ilist); spin_unlock_irqrestore(&w6692.lock, flags); card->dch.debug = debug; spin_lock_init(&card->lock); card->dch.inst.hwlock = &card->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->dch.inst.class_dev.parent = &card->pdev->dev; #else card->dch.inst.class_dev.dev = &card->pdev->dev; #endif 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, &w6692, card, w6692_l2l1); sprintf(card->dch.inst.name, "W6692_%d", w6692_cnt+1); mISDN_set_dchannel_pid(&pid, protocol[w6692_cnt], layermask[w6692_cnt]); mISDN_initchannel(&card->dch, MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); card->dch.hw = card; for (i=0; i<2; i++) { card->bch[i].channel = i; mISDN_init_instance(&card->bch[i].inst, &w6692, card, w6692_l2l1); card->bch[i].inst.pid.layermask = ISDN_LAYER(0); card->bch[i].inst.hwlock = &card->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) card->bch[i].inst.class_dev.parent = &card->pdev->dev; #else card->bch[i].inst.class_dev.dev = &card->pdev->dev; #endif card->bch[i].debug = debug; sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1); mISDN_initchannel(&card->bch[i], MSK_INIT_BCHANNEL, MAX_DATA_MEM); card->bch[i].hw = &card->wbc[i]; } if (debug) printk(KERN_DEBUG "w6692 card %p dch %p bch1 %p bch2 %p\n", card, &card->dch, &card->bch[0], &card->bch[1]); err = setup_w6692(card); if (err) { mISDN_freechannel(&card->dch); mISDN_freechannel(&card->bch[1]); mISDN_freechannel(&card->bch[0]); list_del(&card->list); kfree(card); return(err); } card->pots = pots[w6692_cnt]; card->led = led[w6692_cnt]; w6692_cnt++; err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst); if (err) { release_card(card); return(err); } for (i=0; i<2; i++) { err = mISDN_ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst); if (err) { printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } } err = mISDN_ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid); if (err) { printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err); mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } err = init_card(card); if (err) { mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); return(err); } mISDN_ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL); printk(KERN_INFO "w6692 %d cards installed\n", w6692_cnt); return(0); } static int __devinit w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int err = -ENOMEM; w6692pci *card; w6692_map_t *m = (w6692_map_t *)ent->driver_data; if (!(card = kmalloc(sizeof(w6692pci), GFP_ATOMIC))) { printk(KERN_ERR "No kmem for w6692 card\n"); return(err); } memset(card, 0, sizeof(w6692pci)); card->pdev = pdev; card->subtype = m->subtype; err = pci_enable_device(pdev); if (err) { kfree(card); return(err); } printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n", m->name, pci_name(pdev)); card->addr = pci_resource_start(pdev, 1); card->irq = pdev->irq; pci_set_drvdata(pdev, card); err = setup_instance(card); return(err); } static void __devexit w6692_remove_pci(struct pci_dev *pdev) { w6692pci *card = pci_get_drvdata(pdev); if (card) mISDN_ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL); else printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__); } static struct pci_device_id w6692_ids[] = { { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &w6692_map[0] }, { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, PCI_VENDOR_ID_USR2, PCI_DEVICE_ID_USR2_6692, 0, 0, (unsigned long) &w6692_map[2] }, { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &w6692_map[1] }, { } }; MODULE_DEVICE_TABLE(pci, w6692_ids); static struct pci_driver w6692_driver = { name: "w6692", probe: w6692_probe, remove: __devexit_p(w6692_remove_pci), id_table: w6692_ids, }; static char W6692Name[] = "W6692"; static int __init w6692_init(void) { int err; printk(KERN_INFO "Winbond W6692 PCI driver Rev. %s\n", mISDN_getrev(w6692_rev)); #ifdef MODULE w6692.owner = THIS_MODULE; #endif spin_lock_init(&w6692.lock); INIT_LIST_HEAD(&w6692.ilist); w6692.name = W6692Name; w6692.own_ctrl = w6692_manager; w6692.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0; w6692.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; w6692.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS; if ((err = mISDN_register(&w6692))) { printk(KERN_ERR "Can't register Winbond W6692 PCI error(%d)\n", err); return(err); } err = pci_register_driver(&w6692_driver); if (err < 0) goto out; #ifdef OLD_PCI_REGISTER_DRIVER if (err == 0) { err = -ENODEV; pci_unregister_driver(&w6692_driver); goto out; } #endif mISDN_module_register(THIS_MODULE); return 0; out: mISDN_unregister(&w6692); return err; } static void __exit w6692_cleanup(void) { int err; w6692pci *card, *next; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&w6692))) { printk(KERN_ERR "Can't unregister Winbond W6692 PCI error(%d)\n", err); } list_for_each_entry_safe(card, next, &w6692.ilist, list) { printk(KERN_ERR "Winbond W6692 PCI card struct not empty refs %d\n", w6692.refcnt); release_card(card); } pci_unregister_driver(&w6692_driver); } module_init(w6692_init); module_exit(w6692_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/netjet.h0000644000000000000500000000333411110524073020025 0ustar rootsrc/* * * NETjet common header file * * Author Karsten Keil * Copyright by Karsten Keil * by Matt Henderson and Daniel Potts, * Traverse Technologies P/L www.traverse.com.au * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * Ported to mISDN from HiSax by Daniel Potts * */ #define NETJET_CTRL 0x00 #define NETJET_DMACTRL 0x01 #define NETJET_AUXCTRL 0x02 #define NETJET_AUXDATA 0x03 #define NETJET_IRQMASK0 0x04 #define NETJET_IRQMASK1 0x05 #define NETJET_IRQSTAT0 0x06 #define NETJET_IRQSTAT1 0x07 #define NETJET_DMA_READ_START 0x08 #define NETJET_DMA_READ_IRQ 0x0c #define NETJET_DMA_READ_END 0x10 #define NETJET_DMA_READ_ADR 0x14 #define NETJET_DMA_WRITE_START 0x18 #define NETJET_DMA_WRITE_IRQ 0x1c #define NETJET_DMA_WRITE_END 0x20 #define NETJET_DMA_WRITE_ADR 0x24 #define NETJET_PULSE_CNT 0x28 #define NETJET_ISAC_OFF 0xc0 #define NETJET_ISACIRQ 0x10 #define NETJET_IRQM0_READ_MASK 0x0c #define NETJET_IRQM0_READ_1 0x04 #define NETJET_IRQM0_READ_2 0x08 #define NETJET_IRQM0_WRITE_MASK 0x03 #define NETJET_IRQM0_WRITE_1 0x01 #define NETJET_IRQM0_WRITE_2 0x02 #define NETJET_HA_OFFSET 2 #define NETJET_HA_BITS 4 #define NETJET_HA_MASK 0xf // mask from offset #define NETJET_DMA_TXSIZE 512 // dma buf size in 32-bit words #define NETJET_DMA_RXSIZE 128 // dma buf size in 32-bit words #define HDLC_ZERO_SEARCH 0 #define HDLC_FLAG_SEARCH 1 #define HDLC_FLAG_FOUND 2 #define HDLC_FRAME_FOUND 3 #define HDLC_NULL 4 #define HDLC_PART 5 #define HDLC_FULL 6 #define HDLC_FLAG_VALUE 0x7e #define FLG_NOFRAME 26 #define FLG_HALF 27 #define FLG_EMPTY 28 mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/octvqe/0002755000000000000500000000000011135652737017703 5ustar rootsrcmISDN-1_1_9.1/drivers/isdn/hardware/mISDN/octvqe/octvqe_linux.o0000644000000000000500000003630411135343644022601 0ustar rootsrcELF (4(S YЋ,+((0=0BЁr:,Qt\$D$$ C,Q1 [UWVS$D$ D$T$ tt$D$ |$ ~փ|$ E~ k L$@W4oG G4G$؉G(hG,؉G0؉rBW@D$B G@BWL耉BBWXOGLBJGXGGGGGG T$ D$~D$L$k4 L$0@W4oG G4G$G(hG,G0BL$JW@D$B G@BWL耉BBWXOGLJBGXGGGGGG uD$$'1ǃ[ǃD$ ǃǃǃǃǃǃǃǃǃǃt$1{󫹠󫹠؃$[^_]VS1VV1uD$$X_Z 9At RC~=t9Aǁǁt ǁRt Y[[^XZ[^WV1SSSuD$$z9XtB~tt}S9։C }*NjK$S(frF1K9|>{,s$tCs${0t_Z[^_UWV1Sf$D$|D$xD$tD$pDŽ$uD$$U$9htB$~ NjUx) ‰Uxf)$f|$^}4]4GDX>1)ЋUL$)‹D$1)CUE4yGE4GU4$fP1ɋGWE4fB1ۋ}(|$LU4E<PT$H}9}T$Lt$HJNA9D$^\$Xf9EluEpEpT$^fUlf|$^fD$VL$^fL$V}p|$X}TD$XE X]$u0t$t1;M}U$E(TfHAEtr|$X~XD$XE X}0]$|$ߋt$t1;M}U$E(TfHAEt |$XtT$Xо|$^1)f|$^EǙ1))t)|$XD$XDƙ1É)f|$^EǙ1))Éؙ1)= ~SD$XE]$U0T$߉t\$X1;]}U$E(TfHA;MEtt$^T$^f+t$Xft$Vy|$XD$^f+D$XfD$VELMLHELyAELULA\$VfPAQELfBEtuMu0EEtt$U,tE]$U,׉މtHEtu@]@FDX>1)ЋU)D$^1)CUE@yFE@L$^U@Ff PFVE@f BMXEX]HEXyAEXUXAfPAQEXfB} u E E D$PED$PD$PULETP1)ЋU)D$V1)‹E1)ЋU|$)‰1)CUE4yFE4FU4$f PFVE4f B1ɋ](\$(1ۋU4E1)ЋU)D$:1)CUE@yFE@L$:U@Ff PFVE@f BMXEX]HEXyAEXUXAfPAQEXfB} u E E D$,ED$,D$,ULETP1)ЋU)D$21)‹EXPE%$|$t$8eD$T$D$ ee@@uee@@Ui@@T$Ƹ@ED$D$YGՙ9‰u ǃyQD$t$t$D$$X[^_]UWVS D$XpuD$$1=҉tZAHD$D$RD$ [^_]SSSBp\$uD$$y wi$D=6/(!  u1ZY[ÉыR| v1ËËQ҉Q| v1ËWV1SӃ;u(D$LD$ UD$aD$x$9tF~DƒD$D$D‰<$D$ FD$D$<$D$ D$D$ D$D$<$D$ D$D$D$ D$D$ID$<$1[^_ÉкW1VS1ۃ00y@D$D$$0GD$D$ $-X1ljtĹC~Ӄt:1ۋtC~ߺ01ۋǀCǀ~11Ҹt@$ 0D$LD$UD$D$D$ D$aD$$10[^_S1Q1QtC~ߺ0D$$9XZ[verbose0\ZapOctVqeApiEcChannelInitializeZapOctVqeApiEcChannelProcessZapOctVqeApiEcChannelFreeZapOctVqeApiEcChannelTrainTap =W parmtype=verbose:intauthor=Octasic Inc.description=OCTVQE echo cancellerlicense=GPLoctvqe<4>%s: %d messages suppressed. <4>%s: Could not create default echo canceller! <3>%s: Cannot free NULL channel! <3>%s: Cannot train NULL channel! <3>%s: Cannot process NULL channel! <4>%s: OCTVQE service deactivated on zaptel channel #%d! <4>%s: Xin buffer overrun on channel #%d <4>%s: Sout buffer underrun on channel #%d <4>%s: Cannot open channel #%d, only %d channels supported <4>%s: Echo cancellation channel #%d is already opened <4>%s: Cannot close channel #%d, only %d channels supported <3>%s: Invalid private data (NULL) <4>%s: Channel #%lu not opened <4>%s: Channel #%lu byte space provided less then %d bytes <3>%s: copy_to_user failed with error %lu <3>%s: Invalid private data channel (NULL) <4>%s: written data for chan #%lu must be the size of a buffer (%d) <3>%s: copy_from_user failed with error %lu 13:46:28Jan 20 2009OCTVQE-MOD-01.01.02-PR%s, built on %s %s NO Octvqed ConnectedOctvqed ConnectedInactiveActiveChannel %i (%s) (%s) Tail Length (%d ms), Default ERL (%d dB) Rin/Sin Current Energy (%d dB, %d dB), Av. Energy (%d dB, %d dB) Number Buffers (%d), Buffer Size (%d ms), Av./Max CPU Time (%d us, %d us) <3>Registering the dev %s failed with %d <3>Allocating %d bytes of memory for dev %s failed MG2<6>%s: %s. Using major %d, Default EC "%s%s" (Built on %s %s). <6>%s: Echo cancellation support unloaded U\cjqxGCC: (GNU) 3.4.6 (Debian 3.4.6-5).symtab.strtab.shstrtab.rel.text.rel.data.bss__ksymtab_strings.rel__ksymtab.rel__param.modinfo.rodata.str1.1.rel.rodata.note.GNU-stack.comment4> 5)  % ;/ 4 4 u J! F <@X8!T L<  ``!n i2!d|4',x l<X `'`'#'@+p- 0 :d =W"J R$m    ((, \bw  l/>lJSV0]Yi z@" b    .(6DU]dnt|(#3BCNXaG r~S Yoctvqe_linux.c__kstrtab_ZapOctVqeApiEcChannelInitialize__ksymtab_ZapOctVqeApiEcChannelInitialize__kstrtab_ZapOctVqeApiEcChannelProcess__ksymtab_ZapOctVqeApiEcChannelProcess__kstrtab_ZapOctVqeApiEcChannelFree__ksymtab_ZapOctVqeApiEcChannelFree__kstrtab_ZapOctVqeApiEcChannelTrainTap__ksymtab_ZapOctVqeApiEcChannelTrainTapverbose__param_perm_check_verbose__param_str_verbose__param_verbose__mod_verbosetype185ratelimit_lock.1toks.2last_msg.3missed.4octdev_openoctdev_releaseoctdev_readoctdev_writeoctdev_polloctdev_ioctloctdev_proc_opsoctdev_seq_opsoctdev_fopsoctvqe_initiMajoroctvqe_exit__mod_author1286__mod_description1287__mod_license1288g_apEchoChanInstZapOctVqeApiEcChannelInitializeZapOctVqeApiEcChannelProcessZapOctVqeApiEcChannelFreeZapOctVqeApiEcChannelTrainTapparam_set_intparam_get_intoctvqe_ratelimitjiffiesprintk__kzallockfreememmove__wake_up__this_modulemodule_putdefault_wake_functionmemcpyadd_wait_queuescheduleremove_wait_queuecopy_to_usercopy_from_useroctdev_seq_startoctdev_seq_nextoctdev_seq_stopoctdev_seq_showseq_printfseq_lseekseq_readoctdev_proc_openseq_releaseseq_openregister_chrdevmalloc_sizeskmem_cache_allocunregister_chrdevinit_waitqueue_headcreate_proc_entryremove_proc_entryinit_modulecleanup_module5(<JSelq6-7-766-CJO6`-SZ_6u- 9y   69::,:9A4T[e6rz466-#60;9;F-`;h<6-66,3=6=>?@AB=B6629C6=>?@ACz66'.36Q!C---%-5=HD-`et}HHH.:HJW]hNo}66OP--8- Q&-0R7-ARH-RRY-j-S6T-8 -"Q*166%8OM ;$I(JPKXLDFEG;./ 012 3  $(mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/octvqe/octvqe.o0000644000000000000500000003650211135343644021362 0ustar rootsrcELF (4(S YЋ,+((0=0BЁr:,Qt\$D$$ C,Q1 [UWVS$D$ D$T$ tt$D$ |$ ~փ|$ E~ k L$@W4oG G4G$؉G(hG,؉G0؉rBW@D$B G@BWL耉BBWXOGLBJGXGGGGGG T$ D$~D$L$k4 L$0@W4oG G4G$G(hG,G0BL$JW@D$B G@BWL耉BBWXOGLJBGXGGGGGG uD$$'1ǃ[ǃD$ ǃǃǃǃǃǃǃǃǃǃt$1{󫹠󫹠؃$[^_]VS1VV1uD$$X_Z 9At RC~=t9Aǁǁt ǁRt Y[[^XZ[^WV1SSSuD$$z9XtB~tt}S9։C }*NjK$S(frF1K9|>{,s$tCs${0t_Z[^_UWV1Sf$D$|D$xD$tD$pDŽ$uD$$U$9htB$~ NjUx) ‰Uxf)$f|$^}4]4GDX>1)ЋUL$)‹D$1)CUE4yGE4GU4$fP1ɋGWE4fB1ۋ}(|$LU4E<PT$H}9}T$Lt$HJNA9D$^\$Xf9EluEpEpT$^fUlf|$^fD$VL$^fL$V}p|$X}TD$XE X]$u0t$t1;M}U$E(TfHAEtr|$X~XD$XE X}0]$|$ߋt$t1;M}U$E(TfHAEt |$XtT$Xо|$^1)f|$^EǙ1))t)|$XD$XDƙ1É)f|$^EǙ1))Éؙ1)= ~SD$XE]$U0T$߉t\$X1;]}U$E(TfHA;MEtt$^T$^f+t$Xft$Vy|$XD$^f+D$XfD$VELMLHELyAELULA\$VfPAQELfBEtuMu0EEtt$U,tE]$U,׉މtHEtu@]@FDX>1)ЋU)D$^1)CUE@yFE@L$^U@Ff PFVE@f BMXEX]HEXyAEXUXAfPAQEXfB} u E E D$PED$PD$PULETP1)ЋU)D$V1)‹E1)ЋU|$)‰1)CUE4yFE4FU4$f PFVE4f B1ɋ](\$(1ۋU4E1)ЋU)D$:1)CUE@yFE@L$:U@Ff PFVE@f BMXEX]HEXyAEXUXAfPAQEXfB} u E E D$,ED$,D$,ULETP1)ЋU)D$21)‹EXPE%$|$t$8eD$T$D$ ee@@uee@@Ui@@T$Ƹ@ED$D$YGՙ9‰u ǃyQD$t$t$D$$X[^_]UWVS D$XpuD$$1=҉tZAHD$D$RD$ [^_]SSSBp\$uD$$y wi$D=6/(!  u1ZY[ÉыR| v1ËËQ҉Q| v1ËWV1SӃ;u(D$LD$ UD$aD$x$9tF~DƒD$D$D‰<$D$ FD$D$<$D$ D$D$ D$D$<$D$ D$D$D$ D$D$ID$<$1[^_ÉкW1VS1ۃ00y@D$D$$0GD$D$ $-X1ljtĹC~Ӄt:1ۋtC~ߺ01ۋǀCǀ~11Ҹt@$ 0D$LD$UD$D$D$ D$aD$$10[^_S1Q1QtC~ߺ0D$$9XZ[U\cjqxZapOctVqeApiEcChannelInitializeZapOctVqeApiEcChannelProcessZapOctVqeApiEcChannelFreeZapOctVqeApiEcChannelTrainTap =W parmtype=verbose:intauthor=Octasic Inc.description=OCTVQE echo cancellerlicense=GPLoctvqe<4>%s: %d messages suppressed. <4>%s: Could not create default echo canceller! <3>%s: Cannot free NULL channel! <3>%s: Cannot train NULL channel! <3>%s: Cannot process NULL channel! <4>%s: OCTVQE service deactivated on zaptel channel #%d! <4>%s: Xin buffer overrun on channel #%d <4>%s: Sout buffer underrun on channel #%d <4>%s: Cannot open channel #%d, only %d channels supported <4>%s: Echo cancellation channel #%d is already opened <4>%s: Cannot close channel #%d, only %d channels supported <3>%s: Invalid private data (NULL) <4>%s: Channel #%lu not opened <4>%s: Channel #%lu byte space provided less then %d bytes <3>%s: copy_to_user failed with error %lu <3>%s: Invalid private data channel (NULL) <4>%s: written data for chan #%lu must be the size of a buffer (%d) <3>%s: copy_from_user failed with error %lu 13:46:28Jan 20 2009OCTVQE-MOD-01.01.02-PR%s, built on %s %s NO Octvqed ConnectedOctvqed ConnectedInactiveActiveChannel %i (%s) (%s) Tail Length (%d ms), Default ERL (%d dB) Rin/Sin Current Energy (%d dB, %d dB), Av. Energy (%d dB, %d dB) Number Buffers (%d), Buffer Size (%d ms), Av./Max CPU Time (%d us, %d us) <3>Registering the dev %s failed with %d <3>Allocating %d bytes of memory for dev %s failed MG2<6>%s: %s. Using major %d, Default EC "%s%s" (Built on %s %s). <6>%s: Echo cancellation support unloaded verbose0\GCC: (GNU) 3.4.6 (Debian 3.4.6-5).symtab.strtab.shstrtab.rel.text.rel.rodata__ksymtab_strings.rel__ksymtab.rel__param.modinfo.rodata.str1.1.rel.data.bss.comment.note.GNU-stack4> @+)t,% 1X1u G C X1@U8 Q 1 ]` n f2 dy@&  u 1 `'4 `'#''825 (8S( < JS e l qODQDQ  O  ODC J OO`DS Z _OuD 8y   O8AA,A9AIT [ eOrzI  O  OD  #O0797FD`7hL  OD  O  O, 3 =OP=@J[N=N  O  O  2 9 COP=@J[5z  O  O' . 3OQ5DDD % - 5 =ZDD` e t }  Z Z Z. :ZJ W ] hGo}  O  O\KDD>D  :&D0B7DABHDRBYDjD F        O WD> D ":* 1 6O%>O]  $(EY R; U ^ 7$?(TP6X9HXMV7      :d =W"J R$m  ( (,\bw l / >l JSV0]Yi z@" b  G .Y#*0:ISgSs  (*1GQks|B octvqe_linux.c__kstrtab_ZapOctVqeApiEcChannelInitialize__ksymtab_ZapOctVqeApiEcChannelInitialize__kstrtab_ZapOctVqeApiEcChannelProcess__ksymtab_ZapOctVqeApiEcChannelProcess__kstrtab_ZapOctVqeApiEcChannelFree__ksymtab_ZapOctVqeApiEcChannelFree__kstrtab_ZapOctVqeApiEcChannelTrainTap__ksymtab_ZapOctVqeApiEcChannelTrainTapverbose__param_perm_check_verbose__param_str_verbose__param_verbose__mod_verbosetype185ratelimit_lock.1toks.2last_msg.3missed.4octdev_openoctdev_releaseoctdev_readoctdev_writeoctdev_polloctdev_ioctloctdev_proc_opsoctdev_seq_opsoctdev_fopsoctvqe_initiMajoroctvqe_exit__mod_author1286__mod_description1287__mod_license1288copy_from_useroctdev_proc_open__this_modulememmoveseq_releaseunregister_chrdevZapOctVqeApiEcChannelTrainTapcleanup_modulememcpykfreeseq_lseekadd_wait_queue__wake_upinit_waitqueue_headinit_moduleg_apEchoChanInstZapOctVqeApiEcChannelInitializecreate_proc_entryregister_chrdevoctdev_seq_startoctvqe_ratelimitschedulekmem_cache_allocmodule_putoctdev_seq_nextcopy_to_userprintkdefault_wake_function__kzallocZapOctVqeApiEcChannelFreejiffiesseq_readparam_set_intoctdev_seq_showremove_proc_entryoctdev_seq_stopZapOctVqeApiEcChannelProcessseq_printfremove_wait_queuemalloc_sizesseq_openparam_get_intmISDN-1_1_9.1/drivers/isdn/hardware/mISDN/octvqe/octvqe.mod.o0000644000000000000500000000327011135343655022136 0ustar rootsrcELF4( vermagic=2.6.20.10 mod_unload PENTIUM4 depends=octvqeGCC: (GNU) 3.4.6 (Debian 3.4.6-5).symtab.strtab.shstrtab.text.data.bss.modinfo.rel.gnu.linkonce.this_module.note.GNU-stack.comment4!4'4,@1 95  Sc##lp  PX(( /=Ioctvqe.mod.c__mod_vermagic5__module_depends__this_moduleinit_modulecleanup_module  mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/octvqe/octvqe.ko0000644000000000000500000004060411135343655021535 0ustar rootsrcELF+4(S YЋ,+((0=0BЁr:,Qt\$D$$ C,Q1 [UWVS$D$ D$T$ tt$D$ |$ ~փ|$ E~ k L$@W4oG G4G$؉G(hG,؉G0؉rBW@D$B G@BWL耉BBWXOGLBJGXGGGGGG T$ D$~D$L$k4 L$0@W4oG G4G$G(hG,G0BL$JW@D$B G@BWL耉BBWXOGLJBGXGGGGGG uD$$'1ǃ[ǃD$ ǃǃǃǃǃǃǃǃǃǃt$1{󫹠󫹠؃$[^_]VS1VV1uD$$X_Z 9At RC~=t9Aǁǁt ǁRt Y[[^XZ[^WV1SSSuD$$z9XtB~tt}S9։C }*NjK$S(frF1K9|>{,s$tCs${0t_Z[^_UWV1Sf$D$|D$xD$tD$pDŽ$uD$$U$9htB$~ NjUx) ‰Uxf)$f|$^}4]4GDX>1)ЋUL$)‹D$1)CUE4yGE4GU4$fP1ɋGWE4fB1ۋ}(|$LU4E<PT$H}9}T$Lt$HJNA9D$^\$Xf9EluEpEpT$^fUlf|$^fD$VL$^fL$V}p|$X}TD$XE X]$u0t$t1;M}U$E(TfHAEtr|$X~XD$XE X}0]$|$ߋt$t1;M}U$E(TfHAEt |$XtT$Xо|$^1)f|$^EǙ1))t)|$XD$XDƙ1É)f|$^EǙ1))Éؙ1)= ~SD$XE]$U0T$߉t\$X1;]}U$E(TfHA;MEtt$^T$^f+t$Xft$Vy|$XD$^f+D$XfD$VELMLHELyAELULA\$VfPAQELfBEtuMu0EEtt$U,tE]$U,׉މtHEtu@]@FDX>1)ЋU)D$^1)CUE@yFE@L$^U@Ff PFVE@f BMXEX]HEXyAEXUXAfPAQEXfB} u E E D$PED$PD$PULETP1)ЋU)D$V1)‹E1)ЋU|$)‰1)CUE4yFE4FU4$f PFVE4f B1ɋ](\$(1ۋU4E1)ЋU)D$:1)CUE@yFE@L$:U@Ff PFVE@f BMXEX]HEXyAEXUXAfPAQEXfB} u E E D$,ED$,D$,ULETP1)ЋU)D$21)‹EXPE%$|$t$8eD$T$D$ ee@@uee@@Ui@@T$Ƹ@ED$D$YGՙ9‰u ǃyQD$t$t$D$$X[^_]UWVS D$XpuD$$1=҉tZAHD$D$RD$ [^_]SSSBp\$uD$$y wi$D=6/(!  u1ZY[ÉыR| v1ËËQ҉Q| v1ËWV1SӃ;u(D$LD$ UD$aD$x$9tF~DƒD$D$D‰<$D$ FD$D$<$D$ D$D$ D$D$<$D$ D$D$D$ D$D$ID$<$1[^_ÉкW1VS1ۃ00y@D$D$$0GD$D$ $-X1ljtĹC~Ӄt:1ۋtC~ߺ01ۋǀCǀ~11Ҹt@$ 0D$LD$UD$D$D$ D$aD$$10[^_S1Q1QtC~ߺ0D$$9XZ[ÐU\cjqxZapOctVqeApiEcChannelInitializeZapOctVqeApiEcChannelProcessZapOctVqeApiEcChannelFreeZapOctVqeApiEcChannelTrainTap =W parmtype=verbose:intauthor=Octasic Inc.description=OCTVQE echo cancellerlicense=GPLvermagic=2.6.20.10 mod_unload PENTIUM4 depends=octvqe<4>%s: %d messages suppressed. <4>%s: Could not create default echo canceller! <3>%s: Cannot free NULL channel! <3>%s: Cannot train NULL channel! <3>%s: Cannot process NULL channel! <4>%s: OCTVQE service deactivated on zaptel channel #%d! <4>%s: Xin buffer overrun on channel #%d <4>%s: Sout buffer underrun on channel #%d <4>%s: Cannot open channel #%d, only %d channels supported <4>%s: Echo cancellation channel #%d is already opened <4>%s: Cannot close channel #%d, only %d channels supported <3>%s: Invalid private data (NULL) <4>%s: Channel #%lu not opened <4>%s: Channel #%lu byte space provided less then %d bytes <3>%s: copy_to_user failed with error %lu <3>%s: Invalid private data channel (NULL) <4>%s: written data for chan #%lu must be the size of a buffer (%d) <3>%s: copy_from_user failed with error %lu 13:46:28Jan 20 2009OCTVQE-MOD-01.01.02-PR%s, built on %s %s NO Octvqed ConnectedOctvqed ConnectedInactiveActiveChannel %i (%s) (%s) Tail Length (%d ms), Default ERL (%d dB) Rin/Sin Current Energy (%d dB, %d dB), Av. Energy (%d dB, %d dB) Number Buffers (%d), Buffer Size (%d ms), Av./Max CPU Time (%d us, %d us) <3>Registering the dev %s failed with %d <3>Allocating %d bytes of memory for dev %s failed MG2<6>%s: %s. Using major %d, Default EC "%s%s" (Built on %s %s). <6>%s: Echo cancellation support unloaded verbose0\octvqeGCC: (GNU) 3.4.6 (Debian 3.4.6-5)GCC: (GNU) 3.4.6 (Debian 3.4.6-5).symtab.strtab.shstrtab.rel.text.rel.rodata__ksymtab_strings.rel__ksymtab.rel__param.modinfo.rodata.str1.1.rel.data.rel.gnu.linkonce.this_module.bss.comment.note.GNU-stack4@ .)t,% 4X1u G C 5@U8 Q L5 ]` f2!dy&  u l5 ( 5*4 *F**5@: <<HX( < JS e l qTIVIV  T  TIC J OT`IS Z _TuI <y   T<FF,F9ANT [ eTrzN  T  TI  #T0=9=FI`=hQ  TI  T  T, 3 =TUBEO`S=S  T  T  2 9 CTUBEO`:z  T  T' . 3TQ:III % - 5 =_DI` e t }  _ _ _. :_J W ] hLo}  T  TaPIICI  ?&I0G7IAGHIRGYIjI K        T \IC I "?* 1 6T%COb  $(J^ W@ Z c =$D(YP;X>M]R[=H A      :d =W"J R$m  ( (,\bw l / >l JSV0]Yi z@" b  (   G $.BYQX^hwS 0;(KX_uB$1:octvqe_linux.c__kstrtab_ZapOctVqeApiEcChannelInitialize__ksymtab_ZapOctVqeApiEcChannelInitialize__kstrtab_ZapOctVqeApiEcChannelProcess__ksymtab_ZapOctVqeApiEcChannelProcess__kstrtab_ZapOctVqeApiEcChannelFree__ksymtab_ZapOctVqeApiEcChannelFree__kstrtab_ZapOctVqeApiEcChannelTrainTap__ksymtab_ZapOctVqeApiEcChannelTrainTapverbose__param_perm_check_verbose__param_str_verbose__param_verbose__mod_verbosetype185ratelimit_lock.1toks.2last_msg.3missed.4octdev_openoctdev_releaseoctdev_readoctdev_writeoctdev_polloctdev_ioctloctdev_proc_opsoctdev_seq_opsoctdev_fopsoctvqe_initiMajoroctvqe_exit__mod_author1286__mod_description1287__mod_license1288octvqe.mod.c__mod_vermagic5__module_dependscopy_from_useroctdev_proc_openmemmove__this_moduleseq_releaseunregister_chrdevZapOctVqeApiEcChannelTrainTapcleanup_modulememcpykfreeseq_lseekadd_wait_queue__wake_upinit_waitqueue_headinit_moduleg_apEchoChanInstZapOctVqeApiEcChannelInitializecreate_proc_entryregister_chrdevoctdev_seq_startoctvqe_ratelimitschedulekmem_cache_allocmodule_putoctdev_seq_nextcopy_to_userprintkdefault_wake_function__kzallocZapOctVqeApiEcChannelFreejiffiesseq_readparam_set_intoctdev_seq_showremove_proc_entryoctdev_seq_stopZapOctVqeApiEcChannelProcessseq_printfremove_wait_queuemalloc_sizesseq_openparam_get_intmISDN-1_1_9.1/drivers/isdn/hardware/mISDN/udevice.c0000644000000000000500000014721111135651702020165 0ustar rootsrc/* $Id: udevice.c,v 1.25 2007/02/13 10:43:45 crich Exp $ * * Copyright 2000 by Karsten Keil * */ #include #include #include #include #include #ifdef CONFIG_DEVFS_FS #include #endif #include "core.h" #define MAX_HEADER_LEN 4 #define FLG_MGR_SETSTACK 1 #define FLG_MGR_OWNSTACK 2 #define FLG_MGR_TIMER_INIT 1 #define FLG_MGR_TIMER_RUNING 2 #define MAX_USERDEVICE_ID MAX_LAYER_NR typedef struct _devicelayer { struct list_head list; mISDNdevice_t *dev; mISDNinstance_t inst; mISDNinstance_t *slave; // mISDNif_t s_up; // mISDNif_t s_down; u_int id; u_int lm_st; u_long Flags; } devicelayer_t; typedef struct _devicestack { struct list_head list; mISDNdevice_t *dev; mISDNstack_t *st; int extentions; } devicestack_t; typedef struct _mISDNtimer { struct list_head list; struct _mISDNdevice *dev; struct timer_list tl; u_int id; u_long Flags; } mISDNtimer_t; typedef struct entity_item { struct list_head head; int entity; } entity_item_t; static LIST_HEAD(mISDN_devicelist); static rwlock_t mISDN_device_lock = RW_LOCK_UNLOCKED; static mISDNobject_t udev_obj; static char MName[] = "UserDevice"; static int device_debug = 0; static int from_up_down(mISDNinstance_t *, struct sk_buff *); // static int from_peer(mISDNif_t *, u_int, int, int, void *); // static int to_peer(mISDNif_t *, u_int, int, int, void *); static mISDNdevice_t * get_mISDNdevice4minor(int minor) { mISDNdevice_t *dev; read_lock(&mISDN_device_lock); list_for_each_entry(dev, &mISDN_devicelist, list) { if (dev->minor == minor) { read_unlock(&mISDN_device_lock); return(dev); } } read_unlock(&mISDN_device_lock); return(NULL); } #ifdef FIXME static int mISDN_rdata_raw(mISDNinstance_t *inst, struct sk_buff *skb) { mISDNdevice_t *dev; mISDN_head_t *hh; u_long flags; int retval = 0; dev = inst->priv; hh = mISDN_HEAD_P(skb); if (hh->prim == (PH_DATA | INDICATION)) { if (test_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag)) { spin_lock_irqsave(&dev->rport.lock, flags); if (skb_queue_len(&dev->rport.queue) >= dev->rport.maxqlen) retval = -ENOSPC; else skb_queue_tail(&dev->rport.queue, skb); spin_unlock_irqrestore(&dev->rport.lock, flags); wake_up_interruptible(&dev->rport.procq); } else { printk(KERN_WARNING "%s: PH_DATA_IND device(%d) not read open\n", __FUNCTION__, dev->minor); retval = -ENOENT; } } else if (hh->prim == (PH_DATA | CONFIRM)) { test_and_clear_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag); dev_kfree_skb_any(skb); spin_lock_irqsave(&dev->wport.lock, flags); if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) { spin_unlock_irqrestore(&dev->wport.lock, flags); return(0); } while ((skb = skb_dequeue(&dev->wport.queue))) { if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "%s: wflg(%lx)\n", __FUNCTION__, dev->wport.Flag); if (test_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag)) { skb_queue_head(&dev->wport.queue, skb); break; } if (test_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag)) { spin_unlock_irqrestore(&dev->wport.lock, flags); retval = if_newhead(&dev->wport.pif, PH_DATA | REQUEST, (int)skb, skb); spin_lock_irqsave(&dev->wport.lock, flags); if (retval) { printk(KERN_WARNING "%s: dev(%d) down err(%d)\n", __FUNCTION__, dev->minor, retval); dev_kfree_skb(skb); } else { test_and_set_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag); wake_up(&dev->wport.procq); break; } } else { printk(KERN_WARNING "%s: dev(%d) wport not enabled\n", __FUNCTION__, dev->minor); dev_kfree_skb(skb); } wake_up(&dev->wport.procq); } test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag); spin_unlock_irqrestore(&dev->wport.lock, flags); } else if ((hh->prim == (PH_ACTIVATE | CONFIRM)) || (hh->prim == (PH_ACTIVATE | INDICATION))) { test_and_set_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag); test_and_clear_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag); } else if ((hh->prim == (PH_DEACTIVATE | CONFIRM)) || (hh->prim == (PH_DEACTIVATE | INDICATION))) { test_and_clear_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag); } else { printk(KERN_WARNING "%s: prim(%x) dinfo(%x) not supported\n", __FUNCTION__, hh->prim, hh->dinfo); retval = -EINVAL; } if (!retval) dev_kfree_skb_any(skb); return(retval); } #endif static int mISDN_rdata(mISDNdevice_t *dev, struct sk_buff *skb) { mISDN_head_t *hp; u_long flags; hp = mISDN_HEAD_P(skb); if (hp->len <= 0) skb_trim(skb, 0); if (device_debug & DEBUG_RDATA) printk(KERN_DEBUG "%s: %x:%x %x %d %d\n", __FUNCTION__, hp->addr, hp->prim, hp->dinfo, hp->len, skb->len); spin_lock_irqsave(&dev->rport.lock, flags); if (skb_queue_len(&dev->rport.queue) >= dev->rport.maxqlen) { /*print the rport queue overflow error, only X times per second..*/ static unsigned long j; static int flag=1; if (flag) { flag=0; j=jiffies; } if ( jiffies > (j+(HZ*500/1000)) ) { flag=1; printk(KERN_WARNING "%s: rport queue overflow (after 500ms!) %d/%d [addr:%x prim:%x dinfo:%x]\n", __FUNCTION__, skb_queue_len(&dev->rport.queue), dev->rport.maxqlen, hp->addr, hp->prim, hp->dinfo); } spin_unlock_irqrestore(&dev->rport.lock, flags); return(-ENOSPC); } skb_queue_tail(&dev->rport.queue, skb); spin_unlock_irqrestore(&dev->rport.lock, flags); wake_up_interruptible(&dev->rport.procq); return(0); } static int error_answer(mISDNdevice_t *dev, struct sk_buff *skb, int err) { mISDN_head_t *hp; hp = mISDN_HEAD_P(skb); hp->prim |= 1; /* CONFIRM or RESPONSE */ hp->len = err; return(mISDN_rdata(dev, skb)); } static devicelayer_t *get_devlayer(mISDNdevice_t *dev, u_int addr) { devicelayer_t *dl; if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr); list_for_each_entry(dl, &dev->layerlist, list) { // if (device_debug & DEBUG_MGR_FUNC) // printk(KERN_DEBUG "%s: dl(%p) id:%x\n", // __FUNCTION__, dl, dl->id); if (dl->id == (INST_ID_MASK & addr)) return(dl); if (dl->inst.id == (INST_ID_MASK & addr)) return(dl); if (dl->slave && (dl->slave->id == (INST_ID_MASK & addr))) return(dl); } return(NULL); } static u_int get_new_devicelayer_id(mISDNdevice_t *dev, u_int addr) { devicelayer_t *dl; u_int i, found; addr |= (FLG_INSTANCE | FLG_ID_USER); for (i=0; i<= MAX_USERDEVICE_ID; i++) { found = 0; list_for_each_entry(dl, &dev->layerlist, list) { if (dl->id == (addr | i)) found++; } if (!found) return(addr | i); } return(0); } static devicestack_t *get_devstack(mISDNdevice_t *dev, int addr) { devicestack_t *ds; if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr); list_for_each_entry(ds, &dev->stacklist, list) { if (ds->st && (ds->st->id == (u_int)addr)) return(ds); } return(NULL); } static mISDNtimer_t *get_devtimer(mISDNdevice_t *dev, int id) { mISDNtimer_t *ht; if (device_debug & DEBUG_DEV_TIMER) printk(KERN_DEBUG "%s: dev:%p id:%x\n", __FUNCTION__, dev, id); list_for_each_entry(ht, &dev->timerlist, list) { if (ht->id == id) return(ht); } return(NULL); } static int stack_inst_flg(mISDNdevice_t *dev, mISDNstack_t *st, int bit, int clear) { int ret; devicelayer_t *dl; list_for_each_entry(dl, &dev->layerlist, list) { if (dl->inst.st == st) { if (clear) ret = test_and_clear_bit(bit, &dl->Flags); else ret = test_and_set_bit(bit, &dl->Flags); return(ret); } } return(-1); } static int new_devstack(mISDNdevice_t *dev, stack_info_t *si) { int err; mISDNstack_t *st; mISDNinstance_t inst; devicestack_t *nds; memset(&inst, 0, sizeof(mISDNinstance_t)); st = get_stack4id(si->id); if (si->extentions & EXT_STACK_CLONE) { if (st) { inst.st = st; } else { int_errtxt("ext(%x) st(%x)", si->extentions, si->id); return(-EINVAL); } } err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &inst); if (err) { int_error(); return(err); } if (!(nds = kmalloc(sizeof(devicestack_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc devicestack failed\n"); mISDN_ctrl(inst.st, MGR_DELSTACK | REQUEST, NULL); return(-ENOMEM); } memset(nds, 0, sizeof(devicestack_t)); nds->dev = dev; if (si->extentions & EXT_STACK_CLONE) { // memcpy(&inst.st->pid, &st->pid, sizeof(mISDN_pid_t)); // FIXME that is a ugly idea, but I don't have a better one inst.st->childlist.prev = &st->childlist; } else { memcpy(&inst.st->pid, &si->pid, sizeof(mISDN_pid_t)); } nds->extentions = si->extentions; inst.st->extentions |= si->extentions; inst.st->mgr = get_instance4id(si->mgr); nds->st = inst.st; list_add_tail(&nds->list, &dev->stacklist); return(inst.st->id); } static mISDNstack_t * sel_channel(u_int addr, u_int channel) { mISDNstack_t *st; channel_info_t ci; st = get_stack4id(addr); if (!st) return(NULL); ci.channel = channel; ci.st.p = NULL; if (mISDN_ctrl(st, MGR_SELCHANNEL | REQUEST, &ci)) return(NULL); return(ci.st.p); } static int from_up_down(mISDNinstance_t *inst, struct sk_buff *skb) { devicelayer_t *dl; mISDN_head_t *hh; int retval = -EINVAL; dl = inst->privat; hh = mISDN_HEAD_P(skb); hh->len = skb->len; // hh->addr = dl->iaddr; if (device_debug & DEBUG_RDATA) printk(KERN_DEBUG "from_up_down: %x(%x) dinfo:%x len:%d\n", hh->prim, hh->addr, hh->dinfo, hh->len); retval = mISDN_rdata(dl->dev, skb); return(retval); } static int create_layer(mISDNdevice_t *dev, struct sk_buff *skb) { layer_info_t *linfo; mISDNstack_t *st; int i, ret; devicelayer_t *nl; mISDNobject_t *obj; mISDNinstance_t *inst = NULL; mISDN_head_t *hp; hp = mISDN_HEAD_P(skb); linfo = (layer_info_t *)skb->data; if (!(st = get_stack4id(linfo->st))) { /* should be changed */ printk(KERN_WARNING "%s: no stack found for id(%08x)\n", __FUNCTION__, linfo->st); return(-ENODEV); } if (linfo->object_id != -1) { obj = get_object(linfo->object_id); if (!obj) { printk(KERN_WARNING "%s: no object %x found\n", __FUNCTION__, linfo->object_id); return(-ENODEV); } ret = obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, &linfo->pid); if (ret) { printk(KERN_WARNING "%s: error nl req %d\n", __FUNCTION__, ret); return(ret); } inst = getlayer4lay(st, linfo->pid.layermask); if (!inst) { printk(KERN_WARNING "%s: no inst found\n", __FUNCTION__); return(-EINVAL); } } else if (linfo->extentions & EXT_INST_CLONE) { mISDN_pid_t *pid; inst = get_instance4id(linfo->parent); if (!inst) { printk(KERN_WARNING "%s: no parent inst found\n", __FUNCTION__); return(-EINVAL); } if (!(inst->extentions & EXT_INST_CLONE)) { printk(KERN_WARNING "%s: inst(%08x) ext(%x) is not cloneable\n", __FUNCTION__, inst->id, inst->extentions); return(-ENOSYS); } for(i=0; i<=MAX_LAYER_NR; i++) if (!st->i_array[i]) break; if (i > MAX_LAYER_NR) { printk(KERN_WARNING "%s: no free instance slot in stack id(%08x)\n", __FUNCTION__, st->id); return(-EBUSY); } pid = kmalloc(sizeof(mISDN_pid_t), GFP_ATOMIC); if (!pid) { printk(KERN_ERR "kmalloc pid failed\n"); return(-ENOMEM); } memset(pid, 0, sizeof(mISDN_pid_t)); if (inst->pid.pbuf && inst->pid.maxplen) { pid->pbuf = kmalloc(inst->pid.maxplen, GFP_ATOMIC); if (!pid->pbuf) { kfree(pid); printk(KERN_ERR "kmalloc pid->pbuf failed\n"); return(-ENOMEM); } memset(pid->pbuf, 0, inst->pid.maxplen); } copy_pid(pid, &inst->pid, pid->pbuf); ret = inst->obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, pid); kfree(pid->pbuf); kfree(pid); if (ret) { printk(KERN_WARNING "%s: MGR_NEWLAYER | REQUEST for clone returns %d\n", __FUNCTION__, ret); return(ret); } if (!st->i_array[i]) { int_error(); return(-EINVAL); } else { while (inst->clone) inst = inst->clone; inst->clone = st->i_array[i]; st->i_array[i]->parent = inst; inst = st->i_array[i]; } } #if 0 else if ((inst = getlayer4lay(st, linfo->pid.layermask))) { if (!(linfo->extentions & EXT_INST_MIDDLE)) { printk(KERN_WARNING "mISDN create_layer st(%x) LM(%x) inst not empty(%08x)\n", st->id, linfo->pid.layermask, inst->id); return(-EBUSY); } } #endif if (!(nl = kmalloc(sizeof(devicelayer_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc devicelayer failed\n"); return(-ENOMEM); } memset(nl, 0, sizeof(devicelayer_t)); if (inst) nl->id = get_new_devicelayer_id(dev, inst->id); else if (st) nl->id = get_new_devicelayer_id(dev, st->id); else nl->id = get_new_devicelayer_id(dev, 0); if (!nl->id) { int_errtxt("overflow devicelayer ids"); kfree(nl); return(-EBUSY); } if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "mISDN create_layer LM(%x) nl(%p:%08x) nl->inst(%p) inst(%p)\n", linfo->pid.layermask, nl, nl->id, &nl->inst, inst); nl->dev = dev; list_add_tail(&nl->list, &dev->layerlist); if (!inst) { mISDN_init_instance(&nl->inst, &udev_obj, nl, from_up_down); memcpy(&nl->inst.pid, &linfo->pid, sizeof(mISDN_pid_t)); strcpy(nl->inst.name, linfo->name); nl->inst.extentions = linfo->extentions; for (i=0; i<= MAX_LAYER_NR; i++) { if (linfo->pid.layermask & ISDN_LAYER(i)) { if (st && (st->pid.protocol[i] == ISDN_PID_NONE)) { st->pid.protocol[i] = linfo->pid.protocol[i]; nl->lm_st |= ISDN_LAYER(i); } } } if (st && (linfo->extentions & EXT_INST_MGR)) { st->mgr = &nl->inst; test_and_set_bit(FLG_MGR_OWNSTACK, &nl->Flags); } if (st) mISDN_ctrl(st, MGR_ADDLAYER | REQUEST, &nl->inst); } else { nl->slave = inst; nl->inst.extentions |= EXT_INST_UNUSED; } skb_trim(skb, 0); memcpy(skb_put(skb, sizeof(nl->id)), &nl->id, sizeof(nl->id)); if (inst) memcpy(skb_put(skb, sizeof(inst->id)), &inst->id, sizeof(inst->id)); else memset(skb_put(skb, sizeof(nl->id)), 0, sizeof(nl->id)); return(8); } static int del_stack(devicestack_t *ds) { mISDNdevice_t *dev; if (!ds) { int_error(); return(-EINVAL); } dev = ds->dev; if (device_debug & DEBUG_MGR_FUNC) { printk(KERN_DEBUG "%s: ds(%p) dev(%p)\n", __FUNCTION__, ds, dev); } if (!dev) return(-EINVAL); if (ds->st) { if (ds->extentions & EXT_STACK_CLONE) INIT_LIST_HEAD(&ds->st->childlist); mISDN_ctrl(ds->st, MGR_DELSTACK | REQUEST, NULL); } list_del(&ds->list); kfree(ds); return(0); } static int del_layer(devicelayer_t *dl) { mISDNinstance_t *inst = &dl->inst; mISDNdevice_t *dev = dl->dev; int i; if (device_debug & DEBUG_MGR_FUNC) { printk(KERN_DEBUG "%s: dl(%p) inst(%p) LM(%x) dev(%p)\n", __FUNCTION__, dl, inst, inst->pid.layermask, dev); printk(KERN_DEBUG "%s: iaddr %x inst(%08x) %s slave %p\n", __FUNCTION__, dl->id, inst->id, inst->name, dl->slave); } if (dl->slave) { if (dl->slave->obj) dl->slave->obj->own_ctrl(dl->slave, MGR_UNREGLAYER | REQUEST, NULL); else dl->slave = NULL; } if (dl->lm_st && inst->st) { for (i=0; i<= MAX_LAYER_NR; i++) { if (dl->lm_st & ISDN_LAYER(i)) { inst->st->pid.protocol[i] = ISDN_PID_NONE; } } dl->lm_st = 0; } if (test_and_clear_bit(FLG_MGR_SETSTACK, &dl->Flags) && inst->st) { if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "del_layer: CLEARSTACK id(%x)\n", inst->st->id); mISDN_ctrl(inst->st, MGR_CLEARSTACK | REQUEST, NULL); } dl->id = 0; list_del(&dl->list); if (!(inst->extentions & EXT_INST_UNUSED)) { mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); } if (test_and_clear_bit(FLG_MGR_OWNSTACK, &dl->Flags)) { if (dl->inst.st) { del_stack(get_devstack(dev, dl->inst.st->id)); } } kfree(dl); return(0); } #ifdef OBSOLETE static mISDNinstance_t * clone_instance(devicelayer_t *dl, mISDNstack_t *st, mISDNinstance_t *peer) { int err; if (dl->slave) { printk(KERN_WARNING "%s: layer has slave, cannot clone\n", __FUNCTION__); return(NULL); } if (!(peer->extentions & EXT_INST_CLONE)) { printk(KERN_WARNING "%s: peer cannot clone\n", __FUNCTION__); return(NULL); } dl->slave = (mISDNinstance_t *)st; if ((err = peer->obj->own_ctrl(peer, MGR_CLONELAYER | REQUEST, &dl->slave))) { dl->slave = NULL; printk(KERN_WARNING "%s: peer clone error %d\n", __FUNCTION__, err); return(NULL); } return(dl->slave); } static int remove_if(devicelayer_t *dl, int stat) { mISDNif_t *hif,*phif,*shif; int err; if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: dl(%p) stat(%x)\n", __FUNCTION__, dl, stat); phif = NULL; if (stat & IF_UP) { hif = &dl->inst.up; shif = &dl->s_up; if (shif->owner) phif = &shif->owner->down; } else if (stat & IF_DOWN) { hif = &dl->inst.down; shif = &dl->s_down; if (shif->owner) phif = &shif->owner->up; } else { printk(KERN_WARNING "%s: stat not UP/DOWN\n", __FUNCTION__); return(-EINVAL); } err = mISDN_ctrl(hif->peer, MGR_DISCONNECT | REQUEST, hif); if (phif) { memcpy(phif, shif, sizeof(mISDNif_t)); memset(shif, 0, sizeof(mISDNif_t)); } if (hif->predecessor) hif->predecessor->clone = hif->clone; if (hif->clone) hif->clone->predecessor = hif->predecessor; return(err); } static int connect_if_req(mISDNdevice_t *dev, struct sk_buff *skb) { devicelayer_t *dl; interface_info_t *ifi = (interface_info_t *)skb->data; mISDNinstance_t *owner; mISDNinstance_t *peer; mISDNinstance_t *pp; mISDNif_t *hifp; int stat; mISDN_head_t *hp; hp = mISDN_HEAD_P(skb); if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n", __FUNCTION__, hp->addr, ifi->owner, ifi->peer); if (!(dl=get_devlayer(dev, ifi->owner))) { int_errtxt("no devive_layer for %08x", ifi->owner); return(-ENXIO); } if (!(owner = get_instance4id(ifi->owner))) { printk(KERN_WARNING "%s: owner(%x) not found\n", __FUNCTION__, ifi->owner); return(-ENODEV); } if (!(peer = get_instance4id(ifi->peer))) { printk(KERN_WARNING "%s: peer(%x) not found\n", __FUNCTION__, ifi->peer); return(-ENODEV); } if (owner->pid.layermask < peer->pid.layermask) { hifp = &peer->down; stat = IF_DOWN; } else if (owner->pid.layermask > peer->pid.layermask) { hifp = &peer->up; stat = IF_UP; } else { int_errtxt("OLM == PLM: %x", owner->pid.layermask); return(-EINVAL); } if (ifi->extentions == EXT_IF_CHAIN) { if (!(pp = hifp->peer)) { printk(KERN_WARNING "%s: peer if has no peer\n", __FUNCTION__); return(-EINVAL); } if (stat == IF_UP) { memcpy(&owner->up, hifp, sizeof(mISDNif_t)); memcpy(&dl->s_up, hifp, sizeof(mISDNif_t)); owner->up.owner = owner; hifp->peer = owner; hifp->func = from_up_down; hifp->fdata = dl; hifp = &pp->down; memcpy(&owner->down, hifp, sizeof(mISDNif_t)); memcpy(&dl->s_down, hifp, sizeof(mISDNif_t)); owner->down.owner = owner; hifp->peer = owner; hifp->func = from_up_down; hifp->fdata = dl; } else { memcpy(&owner->down, hifp, sizeof(mISDNif_t)); memcpy(&dl->s_down, hifp, sizeof(mISDNif_t)); owner->up.owner = owner; hifp->peer = owner; hifp->func = from_up_down; hifp->fdata = dl; hifp = &pp->up; memcpy(&owner->up, hifp, sizeof(mISDNif_t)); memcpy(&dl->s_up, hifp, sizeof(mISDNif_t)); owner->down.owner = owner; hifp->peer = owner; hifp->func = from_up_down; hifp->fdata = dl; } return(0); } if (ifi->extentions & EXT_IF_CREATE) { /* create new instance if allready in use */ if (hifp->stat != IF_NOACTIV) { if ((peer = clone_instance(dl, owner->st, peer))) { if (stat == IF_UP) hifp = &peer->up; else hifp = &peer->down; } else { printk(KERN_WARNING "%s: cannot create new peer instance\n", __FUNCTION__); return(-EBUSY); } } } if (ifi->extentions & EXT_IF_EXCLUSIV) { if (hifp->stat != IF_NOACTIV) { printk(KERN_WARNING "%s: peer if is in use\n", __FUNCTION__); return(-EBUSY); } } return(mISDN_ConnectIF(owner, peer)); } static int set_if_req(mISDNdevice_t *dev, struct sk_buff *skb) { mISDNif_t *hif,*phif,*shif; int stat; interface_info_t *ifi = (interface_info_t *)skb->data; devicelayer_t *dl; mISDNinstance_t *inst, *peer; mISDN_head_t *hp; hp = mISDN_HEAD_P(skb); if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n", __FUNCTION__, hp->addr, ifi->owner, ifi->peer); if (!(dl=get_devlayer(dev, hp->addr))) return(-ENXIO); if (!(inst = get_instance4id(ifi->owner))) { printk(KERN_WARNING "%s: owner(%x) not found\n", __FUNCTION__, ifi->owner); return(-ENODEV); } if (!(peer = get_instance4id(ifi->peer))) { printk(KERN_WARNING "%s: peer(%x) not found\n", __FUNCTION__, ifi->peer); return(-ENODEV); } if (ifi->stat == IF_UP) { hif = &dl->inst.up; phif = &peer->down; shif = &dl->s_up; stat = IF_DOWN; } else if (ifi->stat == IF_DOWN) { hif = &dl->inst.down; shif = &dl->s_down; phif = &peer->up; stat = IF_UP; } else { printk(KERN_WARNING "%s: if not UP/DOWN\n", __FUNCTION__); return(-EINVAL); } if (shif->stat != IF_NOACTIV) { printk(KERN_WARNING "%s: save if busy\n", __FUNCTION__); return(-EBUSY); } if (hif->stat != IF_NOACTIV) { printk(KERN_WARNING "%s: own if busy\n", __FUNCTION__); return(-EBUSY); } hif->stat = stat; hif->owner = inst; memcpy(shif, phif, sizeof(mISDNif_t)); memset(phif, 0, sizeof(mISDNif_t)); return(peer->obj->own_ctrl(peer, hp->prim, hif)); } static int add_if_req(mISDNdevice_t *dev, struct sk_buff *skb) { mISDNif_t *hif; interface_info_t *ifi = (interface_info_t *)skb->data; mISDNinstance_t *inst, *peer; mISDN_head_t *hp; hp = mISDN_HEAD_P(skb); if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n", __FUNCTION__, hp->addr, ifi->owner, ifi->peer); if (!(inst = get_instance4id(ifi->owner))) { printk(KERN_WARNING "%s: owner(%x) not found\n", __FUNCTION__, ifi->owner); return(-ENODEV); } if (!(peer = get_instance4id(ifi->peer))) { printk(KERN_WARNING "%s: peer(%x) not found\n", __FUNCTION__, ifi->peer); return(-ENODEV); } if (ifi->stat == IF_DOWN) { hif = &inst->up; } else if (ifi->stat == IF_UP) { hif = &inst->down; } else { printk(KERN_WARNING "%s: if not UP/DOWN\n", __FUNCTION__); return(-EINVAL); } return(peer->obj->ctrl(peer, hp->prim, hif)); } static int del_if_req(mISDNdevice_t *dev, u_int addr) { devicelayer_t *dl; if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr); if (!(dl=get_devlayer(dev, addr))) return(-ENXIO); return(remove_if(dl, addr)); } static void get_if_info(struct sk_buff *skb) { mISDN_head_t *hp; mISDNinstance_t *inst; mISDNif_t *hif; interface_info_t *ii = (interface_info_t *)skb->data; hp = mISDN_HEAD_P(skb); if (!(inst = get_instance4id(hp->addr & IF_ADDRMASK))) { printk(KERN_WARNING "%s: no instance\n", __FUNCTION__); hp->len = -ENODEV; return; } if (hp->dinfo == IF_DOWN) hif = &inst->down; else if (hp->dinfo == IF_UP) hif = &inst->up; else { printk(KERN_WARNING "%s: wrong interface %x\n", __FUNCTION__, hp->dinfo); hp->len = -EINVAL; return; } hp->dinfo = 0; memset(ii, 0, sizeof(interface_info_t)); if (hif->owner) ii->owner = hif->owner->id; if (hif->peer) ii->peer = hif->peer->id; ii->extentions = hif->extentions; ii->stat = hif->stat; hp->len = sizeof(interface_info_t); skb_put(skb, hp->len); } #endif static int new_entity_req(mISDNdevice_t *dev, int *entity) { int ret; entity_item_t *ei = kmalloc(sizeof(entity_item_t), GFP_ATOMIC); if (!ei) return(-ENOMEM); ret = mISDN_alloc_entity(entity); ei->entity = *entity; if (ret) kfree(entity); else list_add((struct list_head *)ei, &dev->entitylist); return(ret); } static int del_entity_req(mISDNdevice_t *dev, int entity) { struct list_head *item, *nxt; list_for_each_safe(item, nxt, &dev->entitylist) { if (((entity_item_t *)item)->entity == entity) { list_del(item); mISDN_delete_entity(entity); kfree(item); return(0); } } return(-ENODEV); } static void dev_expire_timer(mISDNtimer_t *ht) { struct sk_buff *skb; mISDN_head_t *hp; if (device_debug & DEBUG_DEV_TIMER) printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id); if (test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags)) { skb = alloc_stack_skb(16, 0); if (!skb) { printk(KERN_WARNING "%s: timer(%x) no skb\n", __FUNCTION__, ht->id); return; } hp = mISDN_HEAD_P(skb); hp->dinfo = 0; hp->prim = MGR_TIMER | INDICATION; hp->addr = ht->id; hp->len = 0; if (mISDN_rdata(ht->dev, skb)) dev_kfree_skb(skb); } else printk(KERN_WARNING "%s: timer(%x) not active\n", __FUNCTION__, ht->id); } static int dev_init_timer(mISDNdevice_t *dev, u_int id) { mISDNtimer_t *ht; ht = get_devtimer(dev, id); if (!ht) { ht = kmalloc(sizeof(mISDNtimer_t), GFP_ATOMIC); if (!ht) return(-ENOMEM); ht->dev = dev; ht->id = id; ht->tl.data = (long) ht; ht->tl.function = (void *) dev_expire_timer; init_timer(&ht->tl); list_add_tail(&ht->list, &dev->timerlist); if (device_debug & DEBUG_DEV_TIMER) printk(KERN_DEBUG "%s: new(%x)\n", __FUNCTION__, ht->id); } else if (device_debug & DEBUG_DEV_TIMER) printk(KERN_DEBUG "%s: old(%x)\n", __FUNCTION__, ht->id); if (timer_pending(&ht->tl)) { if (device_debug & DEBUG_DEV_TIMER) printk(KERN_WARNING "%s: timer(%x) pending\n", __FUNCTION__, ht->id); del_timer(&ht->tl); } init_timer(&ht->tl); test_and_set_bit(FLG_MGR_TIMER_INIT, &ht->Flags); return(0); } static int dev_add_timer(mISDNdevice_t *dev, mISDN_head_t *hp) { mISDNtimer_t *ht; ht = get_devtimer(dev, hp->addr); if (!ht) { printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__, hp->addr); return(-ENODEV); } if (timer_pending(&ht->tl)) { printk(KERN_WARNING "%s: timer(%x) pending\n", __FUNCTION__, ht->id); return(-EBUSY); } if (hp->dinfo < 10) { printk(KERN_WARNING "%s: timer(%x): %d ms too short\n", __FUNCTION__, ht->id, hp->dinfo); return(-EINVAL); } if (device_debug & DEBUG_DEV_TIMER) printk(KERN_DEBUG "%s: timer(%x) %d ms\n", __FUNCTION__, ht->id, hp->dinfo); init_timer(&ht->tl); ht->tl.expires = jiffies + (hp->dinfo * HZ) / 1000; test_and_set_bit(FLG_MGR_TIMER_RUNING, &ht->Flags); add_timer(&ht->tl); return(0); } static int dev_del_timer(mISDNdevice_t *dev, u_int id) { mISDNtimer_t *ht; ht = get_devtimer(dev, id); if (!ht) { if (device_debug) printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__, id); return(-ENODEV); } if (device_debug & DEBUG_DEV_TIMER) printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id); del_timer(&ht->tl); if (!test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags)) { if (device_debug & DEBUG_DEV_TIMER) printk(KERN_WARNING "%s: timer(%x) not running\n", __FUNCTION__, ht->id); } return(0); } static void dev_free_timer(mISDNtimer_t *ht) { if (device_debug & DEBUG_DEV_TIMER) printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id); del_timer(&ht->tl); list_del(&ht->list); kfree(ht); } static int dev_remove_timer(mISDNdevice_t *dev, u_int id) { mISDNtimer_t *ht; ht = get_devtimer(dev, id); if (!ht) { if (device_debug) printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__, id); return(-ENODEV); } dev_free_timer(ht); return(0); } static int get_status(struct sk_buff *skb) { mISDN_head_t *hp; status_info_t *si = (status_info_t *)skb->data; mISDNinstance_t *inst; int err; hp = mISDN_HEAD_P(skb); if (!(inst = get_instance4id(hp->addr & INST_ID_MASK))) { printk(KERN_WARNING "%s: no instance\n", __FUNCTION__); err = -ENODEV; } else { err = inst->obj->own_ctrl(inst, MGR_STATUS | REQUEST, si); } if (err) hp->len = err; else { hp->len = si->len + 2*sizeof(int); skb_put(skb, hp->len); } return(err); } static void get_layer_info(struct sk_buff *skb) { mISDN_head_t *hp; mISDNinstance_t *inst; layer_info_t *li = (layer_info_t *)skb->data; hp = mISDN_HEAD_P(skb); if (!(inst = get_instance4id(hp->addr & INST_ID_MASK))) { printk(KERN_WARNING "%s: no instance\n", __FUNCTION__); hp->len = -ENODEV; return; } memset(li, 0, sizeof(layer_info_t)); if (inst->obj) li->object_id = inst->obj->id; strcpy(li->name, inst->name); li->extentions = inst->extentions; li->id = inst->id; if (inst->st) li->st = inst->st->id; if (inst->parent) li->parent = inst->parent->id; if (inst->clone) li->clone = inst->clone->id; memcpy(&li->pid, &inst->pid, sizeof(mISDN_pid_t)); hp->len = sizeof(layer_info_t); skb_put(skb, hp->len); } static int wdata_frame(mISDNdevice_t *dev, struct sk_buff *skb) { mISDN_head_t *hp; devicelayer_t *dl; int err = -ENXIO; hp = mISDN_HEAD_P(skb); if (device_debug & DEBUG_WDATA) printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, hp->addr); if (!(dl=get_devlayer(dev, hp->addr))) return(err); if (device_debug & DEBUG_WDATA) printk(KERN_DEBUG "%s: pr(%x) di(%x) l(%d)\n", __FUNCTION__, hp->prim, hp->dinfo, hp->len); if (hp->len < 0) { printk(KERN_WARNING "%s: data negativ(%d)\n", __FUNCTION__, hp->len); return(-EINVAL); } err = mISDN_queue_message(&dl->inst, hp->addr, skb); if (device_debug & DEBUG_WDATA && err) printk(KERN_DEBUG "%s: mISDN_send_message ret(%x)\n", __FUNCTION__, err); return(err); } static int mISDN_wdata_if(mISDNdevice_t *dev, struct sk_buff *skb) { struct sk_buff *nskb = NULL; mISDN_head_t *hp; mISDNstack_t *st; devicelayer_t *dl; mISDNinstance_t *inst; int lay; int err = 0; hp = mISDN_HEAD_P(skb); if (device_debug & DEBUG_WDATA) printk(KERN_DEBUG "%s: %x:%x %x %d %d\n", __FUNCTION__, hp->addr, hp->prim, hp->dinfo, hp->len, skb->len); if ((hp->len > 0) && (skb->len < hp->len)) { printk(KERN_WARNING "%s: frame(%d/%d) too short\n", __FUNCTION__, skb->len, hp->len); return(error_answer(dev, skb, -EINVAL)); } switch(hp->prim) { case (MGR_VERSION | REQUEST): hp->prim = MGR_VERSION | CONFIRM; hp->len = 0; hp->dinfo = MISDN_VERSION; break; case (MGR_GETSTACK | REQUEST): hp->prim = MGR_GETSTACK | CONFIRM; hp->dinfo = 0; if (hp->addr <= 0) { hp->dinfo = get_stack_cnt(); hp->len = 0; } else { nskb = alloc_stack_skb(1000, 0); if (!nskb) return(error_answer(dev, skb, -ENOMEM)); memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t)); get_stack_info(nskb); } break; case (MGR_SETSTACK | REQUEST): if (skb->len < sizeof(mISDN_pid_t)) return(error_answer(dev, skb, -EINVAL)); hp->dinfo = 0; if ((st = get_stack4id(hp->addr))) { stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 0); hp->len = mISDN_ctrl(st, hp->prim, skb->data); } else hp->len = -ENODEV; hp->prim = MGR_SETSTACK | CONFIRM; break; case (MGR_NEWSTACK | REQUEST): hp->dinfo = 0; hp->prim = MGR_NEWSTACK | CONFIRM; hp->len = 0; err = new_devstack(dev, (stack_info_t *)skb->data); if (err<0) hp->len = err; else hp->dinfo = err; break; case (MGR_CLEARSTACK | REQUEST): hp->dinfo = 0; if ((st = get_stack4id(hp->addr))) { stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 1); hp->len = mISDN_ctrl(st, hp->prim, NULL); } else hp->len = -ENODEV; hp->prim = MGR_CLEARSTACK | CONFIRM; break; case (MGR_SELCHANNEL | REQUEST): hp->prim = MGR_SELCHANNEL | CONFIRM; st = sel_channel(hp->addr, hp->dinfo); if (st) { hp->len = 0; hp->dinfo = st->id; } else { hp->dinfo = 0; hp->len = -ENODEV; } break; case (MGR_GETLAYERID | REQUEST): hp->prim = MGR_GETLAYERID | CONFIRM; lay = hp->dinfo; hp->dinfo = 0; if (LAYER_OUTRANGE(lay)) { hp->len = -EINVAL; } else { hp->len = 0; lay = ISDN_LAYER(lay); if ((st = get_stack4id(hp->addr))) { if ((inst = getlayer4lay(st, lay))) { if (inst) hp->dinfo = inst->id; } } } break; case (MGR_GETLAYER | REQUEST): hp->prim = MGR_GETLAYER | CONFIRM; hp->dinfo = 0; skb_trim(skb, 0); if (skb_tailroom(skb) < sizeof(layer_info_t)) { nskb = alloc_stack_skb(sizeof(layer_info_t), 0); if (!nskb) return(error_answer(dev, skb, -ENOMEM)); memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t)); get_layer_info(nskb); } else { get_layer_info(skb); } break; case (MGR_NEWLAYER | REQUEST): if (skb->len < sizeof(layer_info_t)) return(error_answer(dev, skb, -EINVAL)); hp->dinfo = 0; hp->prim = MGR_NEWLAYER | CONFIRM; hp->len = create_layer(dev, skb); break; case (MGR_REGLAYER | REQUEST): lay = hp->dinfo; hp->dinfo = 0; if (!(st = get_stack4id(hp->addr))) return(error_answer(dev, skb, -ENODEV)); if (!(dl = get_devlayer(dev, lay))) return(error_answer(dev, skb, -ENODEV)); hp->prim = MGR_REGLAYER | CONFIRM; hp->len = mISDN_ctrl(st, MGR_REGLAYER | REQUEST, &dl->inst); if (core_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "MGR_REGLAYER | REQUEST: ret(%d)\n", hp->len); break; case (MGR_UNREGLAYER | REQUEST): lay = hp->dinfo; hp->dinfo = 0; if (!(st = get_stack4id(hp->addr))) return(error_answer(dev, skb, -ENODEV)); if (!(dl = get_devlayer(dev, lay))) return(error_answer(dev, skb, -ENODEV)); hp->prim = MGR_UNREGLAYER | CONFIRM; hp->len = mISDN_ctrl(st, MGR_UNREGLAYER | REQUEST, &dl->inst); break; case (MGR_DELLAYER | REQUEST): hp->prim = MGR_DELLAYER | CONFIRM; hp->dinfo = 0; if ((dl = get_devlayer(dev, hp->addr))) hp->len = del_layer(dl); else hp->len = -ENXIO; break; #ifdef OBSOLETE case (MGR_GETIF | REQUEST): hp->prim = MGR_GETIF | CONFIRM; hp->dinfo = 0; skb_trim(skb, 0); if (skb_tailroom(skb) < sizeof(interface_info_t)) { nskb = alloc_stack_skb(sizeof(interface_info_t), 0); if (!nskb) return(error_answer(dev, skb, -ENOMEM)); memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t)); get_if_info(nskb); } else { get_if_info(skb); } break; case (MGR_CONNECT | REQUEST): if (skb->len < sizeof(interface_info_t)) return(error_answer(dev, skb, -EINVAL)); hp->len = connect_if_req(dev, skb); hp->dinfo = 0; hp->prim = MGR_CONNECT | CONFIRM; break; case (MGR_SETIF | REQUEST): hp->len = set_if_req(dev, skb); hp->prim = MGR_SETIF | CONFIRM; hp->dinfo = 0; break; case (MGR_ADDIF | REQUEST): hp->len = add_if_req(dev, skb); hp->prim = MGR_ADDIF | CONFIRM; hp->dinfo = 0; break; case (MGR_DISCONNECT | REQUEST): hp->len = del_if_req(dev, hp->addr); hp->prim = MGR_DISCONNECT | CONFIRM; hp->dinfo = 0; break; #endif case (MGR_NEWENTITY | REQUEST): hp->prim = MGR_NEWENTITY | CONFIRM; hp->len = new_entity_req(dev, &hp->dinfo); break; case (MGR_DELENTITY | REQUEST): hp->prim = MGR_DELENTITY | CONFIRM; hp->len = del_entity_req(dev, hp->dinfo); break; case (MGR_INITTIMER | REQUEST): hp->len = dev_init_timer(dev, hp->addr); hp->prim = MGR_INITTIMER | CONFIRM; break; case (MGR_ADDTIMER | REQUEST): hp->len = dev_add_timer(dev, hp); hp->prim = MGR_ADDTIMER | CONFIRM; hp->dinfo = 0; break; case (MGR_DELTIMER | REQUEST): hp->len = dev_del_timer(dev, hp->addr); hp->prim = MGR_DELTIMER | CONFIRM; break; case (MGR_REMOVETIMER | REQUEST): hp->len = dev_remove_timer(dev, hp->addr); hp->prim = MGR_REMOVETIMER | CONFIRM; hp->dinfo = 0; break; case (MGR_TIMER | RESPONSE): dev_kfree_skb(skb); return(0); break; case (MGR_STATUS | REQUEST): hp->prim = MGR_STATUS | CONFIRM; nskb = alloc_stack_skb(1000, 0); if (!nskb) return(error_answer(dev, skb, -ENOMEM)); memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t)); get_status(nskb); hp->dinfo = 0; break; case (MGR_SETDEVOPT | REQUEST): hp->prim = MGR_SETDEVOPT | CONFIRM; hp->len = 0; if (hp->dinfo == FLG_mISDNPORT_ONEFRAME) { test_and_set_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag); } else if (!hp->dinfo) { test_and_clear_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag); } else { hp->len = -EINVAL; } hp->dinfo = 0; break; case (MGR_GETDEVOPT | REQUEST): hp->prim = MGR_GETDEVOPT | CONFIRM; hp->len = 0; if (test_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag)) hp->dinfo = FLG_mISDNPORT_ONEFRAME; else hp->dinfo = 0; break; default: if (hp->addr & FLG_INSTANCE) { err = wdata_frame(dev, skb); if (err) { if (device_debug & DEBUG_WDATA) printk(KERN_DEBUG "wdata_frame returns error %d\n", err); err = error_answer(dev, skb, err); } } else { if (device_debug) printk(KERN_WARNING "mISDN: prim %x addr %x not implemented\n", hp->prim, hp->addr); err = error_answer(dev, skb, -EINVAL); } return(err); break; } if (nskb) { err = mISDN_rdata(dev, nskb); if (err) kfree_skb(nskb); else kfree_skb(skb); } else err = mISDN_rdata(dev, skb); return(err); } static mISDNdevice_t * init_device(u_int minor) { mISDNdevice_t *dev; u_long flags; dev = kmalloc(sizeof(mISDNdevice_t), GFP_KERNEL); if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: dev(%d) %p\n", __FUNCTION__, minor, dev); if (dev) { memset(dev, 0, sizeof(mISDNdevice_t)); dev->minor = minor; init_waitqueue_head(&dev->rport.procq); init_waitqueue_head(&dev->wport.procq); skb_queue_head_init(&dev->rport.queue); skb_queue_head_init(&dev->wport.queue); init_MUTEX(&dev->io_sema); INIT_LIST_HEAD(&dev->layerlist); INIT_LIST_HEAD(&dev->stacklist); INIT_LIST_HEAD(&dev->timerlist); INIT_LIST_HEAD(&dev->entitylist); write_lock_irqsave(&mISDN_device_lock, flags); list_add_tail(&dev->list, &mISDN_devicelist); write_unlock_irqrestore(&mISDN_device_lock, flags); } return(dev); } #ifdef FIXME mISDNdevice_t * get_free_rawdevice(void) { mISDNdevice_t *dev; u_int minor; if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s:\n", __FUNCTION__); for (minor=mISDN_MINOR_RAW_MIN; minor<=mISDN_MINOR_RAW_MAX; minor++) { dev = get_mISDNdevice4minor(minor); if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: dev(%d) %p\n", __FUNCTION__, minor, dev); if (!dev) { dev = init_device(minor); if (!dev) return(NULL); dev->rport.pif.func = mISDN_rdata_raw; dev->rport.pif.fdata = dev; return(dev); } } return(NULL); } #endif int free_device(mISDNdevice_t *dev) { struct list_head *item, *ni; u_long flags; if (!dev) return(-ENODEV); if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "%s: dev(%d)\n", __FUNCTION__, dev->minor); /* release related stuff */ list_for_each_safe(item, ni, &dev->layerlist) del_layer(list_entry(item, devicelayer_t, list)); list_for_each_safe(item, ni, &dev->stacklist) del_stack(list_entry(item, devicestack_t, list)); list_for_each_safe(item, ni, &dev->timerlist) dev_free_timer(list_entry(item, mISDNtimer_t, list)); if (!skb_queue_empty(&dev->rport.queue)) discard_queue(&dev->rport.queue); if (!skb_queue_empty(&dev->wport.queue)) discard_queue(&dev->wport.queue); write_lock_irqsave(&mISDN_device_lock, flags); list_del(&dev->list); write_unlock_irqrestore(&mISDN_device_lock, flags); if (!list_empty(&dev->entitylist)) { if (device_debug ) printk(KERN_WARNING "MISDN %s: entitylist not empty\n", __FUNCTION__); list_for_each_safe(item, ni, &dev->entitylist) { struct entity_item *ei = list_entry(item, struct entity_item, head); list_del(item); mISDN_delete_entity(ei->entity); kfree(ei); } } kfree(dev); return(0); } static int mISDN_open(struct inode *ino, struct file *filep) { u_int minor = iminor(ino); mISDNdevice_t *dev = NULL; int isnew = 0; if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN_open in: minor(%d) %p %p mode(%x)\n", minor, filep, filep->private_data, filep->f_mode); if (minor) { dev = get_mISDNdevice4minor(minor); if (dev) { if ((dev->open_mode & filep->f_mode) & (FMODE_READ | FMODE_WRITE)) return(-EBUSY); } else return(-ENODEV); } else if ((dev = init_device(minor))) isnew = 1; else return(-ENOMEM); dev->open_mode |= filep->f_mode & (FMODE_READ | FMODE_WRITE); if (dev->open_mode & FMODE_READ){ dev->rport.lock = SPIN_LOCK_UNLOCKED; dev->rport.maxqlen = DEFAULT_PORT_QUEUELEN; test_and_set_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag); } if (dev->open_mode & FMODE_WRITE) { dev->wport.lock = SPIN_LOCK_UNLOCKED; dev->wport.maxqlen = DEFAULT_PORT_QUEUELEN; test_and_set_bit(FLG_mISDNPORT_OPEN, &dev->wport.Flag); } filep->private_data = dev; if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN_open out: %p %p\n", filep, filep->private_data); mISDN_inc_usage(); return(0); } static int mISDN_close(struct inode *ino, struct file *filep) { mISDNdevice_t *dev, *nd; if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN: mISDN_close %p %p\n", filep, filep->private_data); read_lock(&mISDN_device_lock); list_for_each_entry_safe(dev, nd, &mISDN_devicelist, list) { if (dev == filep->private_data) { if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN: dev(%d) %p mode %x/%x\n", dev->minor, dev, dev->open_mode, filep->f_mode); dev->open_mode &= ~filep->f_mode; read_unlock(&mISDN_device_lock); if (filep->f_mode & FMODE_READ) { test_and_clear_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag); } if (filep->f_mode & FMODE_WRITE) { test_and_clear_bit(FLG_mISDNPORT_OPEN, &dev->wport.Flag); } filep->private_data = NULL; if (!dev->minor) free_device(dev); mISDN_dec_usage(); return 0; } } read_unlock(&mISDN_device_lock); mISDN_dec_usage(); printk(KERN_WARNING "mISDN: No private data while closing device\n"); return 0; } static __inline__ ssize_t do_mISDN_read(struct file *file, char *buf, size_t count, loff_t * off) { mISDNdevice_t *dev = file->private_data; size_t len; // u_long flags; struct sk_buff *skb; if (*off != file->f_pos) return(-ESPIPE); if (!access_ok(VERIFY_WRITE, buf, count)) return(-EFAULT); if ((dev->minor == 0) && (count < mISDN_HEADER_LEN)) { printk(KERN_WARNING "mISDN_read: count(%ld) too small\n", (long)count); return(-ENOSPC); } if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN_read: file(%d) %p max %ld\n", dev->minor, file, (long)count); if (skb_queue_empty(&dev->rport.queue)) { if (file->f_flags & O_NONBLOCK) return(-EAGAIN); wait_event_interruptible(dev->rport.procq, (!skb_queue_empty(&dev->rport.queue))); if (signal_pending(current)) return(-ERESTARTSYS); } // spin_lock_irqsave(&dev->rport.lock, flags); len = 0; while ((skb = skb_dequeue(&dev->rport.queue))) { if (dev->minor == mISDN_CORE_DEVICE) { if ((skb->len + mISDN_HEADER_LEN) > (count - len)) goto nospace; if (copy_to_user(buf, skb->cb, mISDN_HEADER_LEN)) goto efault; len += mISDN_HEADER_LEN; buf += mISDN_HEADER_LEN; } else { if (skb->len > (count - len)) { nospace: skb_queue_head(&dev->rport.queue, skb); if (len) break; // spin_unlock_irqrestore(&dev->rport.lock, flags); return(-ENOSPC); } } if (skb->len) { if (copy_to_user(buf, skb->data, skb->len)) { efault: skb_queue_head(&dev->rport.queue, skb); // spin_unlock_irqrestore(&dev->rport.lock, flags); return(-EFAULT); } len += skb->len; buf += skb->len; } dev_kfree_skb(skb); if (test_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag)) break; } *off += len; // spin_unlock_irqrestore(&dev->rport.lock, flags); if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN_read: file(%d) %ld\n", dev->minor, (long)len); return(len); } static ssize_t mISDN_read(struct file *file, char *buf, size_t count, loff_t * off) { mISDNdevice_t *dev = file->private_data; ssize_t ret; if (!dev) return(-ENODEV); down(&dev->io_sema); ret = do_mISDN_read(file, buf, count, off); up(&dev->io_sema); return(ret); } static loff_t mISDN_llseek(struct file *file, loff_t offset, int orig) { return -ESPIPE; } static __inline__ ssize_t do_mISDN_write(struct file *file, const char *buf, size_t count, loff_t * off) { mISDNdevice_t *dev = file->private_data; size_t len; // u_long flags; struct sk_buff *skb; mISDN_head_t head; if (*off != file->f_pos) return(-ESPIPE); if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN_write: file(%d) %p count %ld queue(%d)\n", dev->minor, file, (long)count, skb_queue_len(&dev->wport.queue)); if (!access_ok(VERIFY_WRITE, buf, count)) return(-EFAULT); if (dev->minor == 0) { if (count < mISDN_HEADER_LEN) return(-EINVAL); } if (skb_queue_len(&dev->wport.queue) >= dev->wport.maxqlen) { if (file->f_flags & O_NONBLOCK) return(-EAGAIN); wait_event_interruptible(dev->wport.procq, (skb_queue_len(&dev->wport.queue) < dev->wport.maxqlen)); if (signal_pending(current)) return(-ERESTARTSYS); } // spin_lock_irqsave(&dev->wport.lock, flags); if (dev->minor == mISDN_CORE_DEVICE) { len = count; while (len >= mISDN_HEADER_LEN) { if (copy_from_user(&head.addr, buf, mISDN_HEADER_LEN)) { // spin_unlock_irqrestore(&dev->rport.lock, flags); return(-EFAULT); } if (head.len > 0) skb = alloc_stack_skb((head.len > PORT_SKB_MINIMUM) ? head.len : PORT_SKB_MINIMUM, PORT_SKB_RESERVE); else skb = alloc_stack_skb(PORT_SKB_MINIMUM, PORT_SKB_RESERVE); if (!skb) break; memcpy(skb->cb, &head.addr, mISDN_HEADER_LEN); len -= mISDN_HEADER_LEN; buf += mISDN_HEADER_LEN; if (head.len > 0) { if (head.len > len) { /* since header is complete we can handle this later */ if (copy_from_user(skb_put(skb, len), buf, len)) { dev_kfree_skb(skb); // spin_unlock_irqrestore(&dev->rport.lock, flags); return(-EFAULT); } len = 0; } else { if (copy_from_user(skb_put(skb, head.len), buf, head.len)) { dev_kfree_skb(skb); // spin_unlock_irqrestore(&dev->rport.lock, flags); return(-EFAULT); } len -= head.len; buf += head.len; } } skb_queue_tail(&dev->wport.queue, skb); } if (len) printk(KERN_WARNING "%s: incomplete frame data (%ld/%ld)\n", __FUNCTION__, (long)len, (long)count); if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) { // spin_unlock_irqrestore(&dev->wport.lock, flags); return(count-len); } // spin_unlock_irqrestore(&dev->wport.lock, flags); while ((skb = skb_dequeue(&dev->wport.queue))) { if (mISDN_wdata_if(dev, skb)) dev_kfree_skb(skb); wake_up(&dev->wport.procq); } test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag); } else { /* raw device */ len = 0; #ifdef FIXME skb = alloc_stack_skb(count, PORT_SKB_RESERVE); if (!skb) { // spin_unlock_irqrestore(&dev->wport.lock, flags); return(0); } if (copy_from_user(skb_put(skb, count), buf, count)) { dev_kfree_skb(skb); // spin_unlock_irqrestore(&dev->wport.lock, flags); return(-EFAULT); } skb_queue_tail(&dev->wport.queue, skb); if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) { // spin_unlock_irqrestore(&dev->wport.lock, flags); return(count); } while ((skb = skb_dequeue(&dev->wport.queue))) { if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "%s: wflg(%lx)\n", __FUNCTION__, dev->wport.Flag); if (test_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag)) { skb_queue_head(&dev->wport.queue, skb); break; } if (test_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag)) { int ret; // spin_unlock_irqrestore(&dev->wport.lock, flags); ret = if_newhead(&dev->wport.pif, PH_DATA | REQUEST, (int)skb, skb); // spin_lock_irqsave(&dev->wport.lock, flags); if (ret) { printk(KERN_WARNING "%s: dev(%d) down err(%d)\n", __FUNCTION__, dev->minor, ret); dev_kfree_skb(skb); } else test_and_set_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag); } else { printk(KERN_WARNING "%s: dev(%d) wport not enabled\n", __FUNCTION__, dev->minor); dev_kfree_skb(skb); } wake_up(&dev->wport.procq); } test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag); // spin_unlock_irqrestore(&dev->wport.lock, flags); #endif } return(count - len); } static ssize_t mISDN_write(struct file *file, const char *buf, size_t count, loff_t * off) { mISDNdevice_t *dev = file->private_data; ssize_t ret; if (!dev) return(-ENODEV); down(&dev->io_sema); ret = do_mISDN_write(file, buf, count, off); up(&dev->io_sema); return(ret); } static unsigned int mISDN_poll(struct file *file, poll_table * wait) { unsigned int mask = POLLERR; mISDNdevice_t *dev = file->private_data; mISDNport_t *rport = (file->f_mode & FMODE_READ) ? &dev->rport : NULL; mISDNport_t *wport = (file->f_mode & FMODE_WRITE) ? &dev->wport : NULL; if (dev) { if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN_poll in: file(%d) %p\n", dev->minor, file); if (rport) { poll_wait(file, &rport->procq, wait); mask = 0; if (!skb_queue_empty(&rport->queue)) mask |= (POLLIN | POLLRDNORM); } if (wport) { poll_wait(file, &wport->procq, wait); if (mask == POLLERR) mask = 0; if (skb_queue_len(&wport->queue) < wport->maxqlen) mask |= (POLLOUT | POLLWRNORM); } } if (device_debug & DEBUG_DEV_OP) printk(KERN_DEBUG "mISDN_poll out: file %p mask %x\n", file, mask); return(mask); } static struct file_operations mISDN_fops = { llseek: mISDN_llseek, read: mISDN_read, write: mISDN_write, poll: mISDN_poll, // ioctl: mISDN_ioctl, open: mISDN_open, release: mISDN_close, }; #ifdef OBSOLETE static int set_if(devicelayer_t *dl, u_int prim, mISDNif_t *hif) { int err = 0; err = mISDN_SetIF(&dl->inst, hif, prim, from_up_down, from_up_down, dl); return(err); } #endif static void setstack_conf(devicelayer_t *dl) { struct sk_buff *skb = create_link_skb(MGR_SETSTACK | INDICATION, 0, 0, NULL, 16); mISDN_head_t *hh; if (!skb) return; hh = mISDN_HEAD_P(skb); hh->addr = dl->id; if (mISDN_rdata(dl->dev, skb)) kfree_skb(skb); } static int udev_manager(void *data, u_int prim, void *arg) { mISDNinstance_t *inst = data; mISDNdevice_t *dev; devicelayer_t *dl; int err = -EINVAL; if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "udev_manager data:%p prim:%x arg:%p\n", data, prim, arg); if (!data) return(-EINVAL); read_lock(&mISDN_device_lock); list_for_each_entry(dev, &mISDN_devicelist, list) { list_for_each_entry(dl, &dev->layerlist, list) { if (&dl->inst == inst) { err = 0; break; } } if (!err) break; } if (err) { if (device_debug) printk(KERN_WARNING "dev_manager prim %x without device layer\n", prim); goto out; } switch(prim) { #ifdef OBSOLETE case MGR_CONNECT | REQUEST: err = mISDN_ConnectIF(inst, arg); break; case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: err = set_if(dl, prim, arg); break; case MGR_DISCONNECT | REQUEST: case MGR_DISCONNECT | INDICATION: err = mISDN_DisConnectIF(inst, arg); break; #endif case MGR_REGLAYER | CONFIRM: err = 0; break; case MGR_SETSTACK | INDICATION: setstack_conf(dl); err = 0; break; case MGR_RELEASE | INDICATION: if (device_debug & DEBUG_MGR_FUNC) printk(KERN_DEBUG "release_dev id %x\n", dl->inst.st->id); del_layer(dl); err = 0; break; default: if (device_debug) printk(KERN_WARNING "dev_manager prim %x not handled\n", prim); err = -EINVAL; break; } out: read_unlock(&mISDN_device_lock); return(err); } int init_mISDNdev (int debug) { int err,i; udev_obj.name = MName; udev_obj.owner = THIS_MODULE; for (i=0; i<=MAX_LAYER_NR; i++) { udev_obj.DPROTO.protocol[i] = ISDN_PID_ANY; udev_obj.BPROTO.protocol[i] = ISDN_PID_ANY; } spin_lock_init(&udev_obj.lock); INIT_LIST_HEAD(&udev_obj.ilist); udev_obj.own_ctrl = udev_manager; device_debug = debug; if (register_chrdev(mISDN_MAJOR, "mISDN", &mISDN_fops)) { printk(KERN_WARNING "mISDN: Could not register devices\n"); return(-EIO); } #ifdef CLASSDEV_HAS_DEVT udev_obj.class_dev.devt = MKDEV(mISDN_MAJOR, 0); #endif err = mISDN_register(&udev_obj); if (err) { printk(KERN_ERR "Can't register %s error(%d)\n", MName, err); unregister_chrdev(mISDN_MAJOR, "mISDN"); return(err); } else #ifdef CONFIG_DEVFS_FS devfs_mk_cdev(MKDEV(mISDN_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR, "mISDN"); #endif return(err); } int free_mISDNdev(void) { int err = 0; mISDNdevice_t *dev, *nd; if (!list_empty(&mISDN_devicelist)) { printk(KERN_WARNING "mISDN: devices open on remove\n"); list_for_each_entry_safe(dev, nd, &mISDN_devicelist, list) { free_device(dev); } err = -EBUSY; } if ((err = mISDN_unregister(&udev_obj))) { printk(KERN_ERR "Can't unregister UserDevice(%d)\n", err); } unregister_chrdev(mISDN_MAJOR, "mISDN"); #ifdef CONFIG_DEVFS_FS devfs_remove("mISDN"); #endif return(err); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/x25_l3.c0000644000000000000500000006301311135651702017552 0ustar rootsrc/* $Id: x25_l3.c,v 1.10 2007/02/13 10:43:45 crich Exp $ * * Linux modular ISDN subsystem, mISDN * X.25/X.31 common Layer3 functions * * Author Karsten Keil (kkeil@suse.de) * * Copyright 2003 by Karsten Keil (kkeil@suse.de) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ #include #include "x25_l3.h" #include "helper.h" #include "debug.h" /* LinkLayer (L2) maintained by L3 statemachine */ static struct Fsm llfsm = {NULL, 0, 0, NULL, NULL}; enum { ST_LL_REL, ST_LL_ESTAB_WAIT, ST_LL_REL_WAIT, ST_LL_ESTAB, }; #define LL_STATE_COUNT (ST_LL_ESTAB+1) static char *strLLState[] = { "ST_LL_REL", "ST_LL_ESTAB_WAIT", "ST_LL_REL_WAIT", "ST_LL_ESTAB", }; static char *strLLEvent[] = { "EV_L3_ESTABLISH_REQ", "EV_LL_ESTABLISH_IND", "EV_LL_ESTABLISH_CNF", "EV_L3_RELEASE_REQ", "EV_LL_RELEASE_CNF", "EV_LL_RELEASE_IND", }; /* X.25 Restart state machine */ char *X25strRState[] = { "ST_R0", "ST_R1", "ST_R2", "ST_R3", }; char *X25strREvent[] = { "EV_LL_READY", "EV_L3_RESTART_REQ", "EV_L2_RESTART", "EV_L2_RESTART_CNF", "EV_L3_RESTART_TOUT", }; /* X.25 connection state machine */ char *X25strPState[] = { "ST_P0", "ST_P1", "ST_P2", "ST_P3", "ST_P4", "ST_P5", "ST_P6", "ST_P7", }; char *X25strPEvent[] = { "EV_L3_READY", "EV_L3_OUTGOING_CALL", "EV_L2_INCOMING_CALL", "EV_L2_CALL_CNF", "EV_L3_CALL_ACCEPT", "EV_L3_CLEARING", "EV_L2_CLEAR", "EV_L2_CLEAR_CNF", "EV_L2_INVALPKT", "EV_L3_CALL_TOUT", "EV_L3_CLEAR_TOUT", }; /* X.25 Flowcontrol state machine */ char *X25strDState[] = { "ST_D0", "ST_D1", "ST_D2", "ST_D3", }; char *X25strDEvent[] = { "EV_L3_CONNECT", "EV_L2_RESETING", "EV_L2_RESET", "EV_L2_RESET_CNF", "EV_L3_RESET_TOUT", }; static void l3m_debug(struct FsmInst *fi, char *fmt, ...) { x25_l3_t *l3 = fi->userdata; logdata_t log; va_start(log.args, fmt); log.fmt = fmt; log.head = l3->inst.name; mISDN_ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } /* LinkLayer (L2) maintained by L3 statemachine */ static void ll_activate(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_LL_ESTAB_WAIT); X25_l3down(l3, DL_ESTABLISH | REQUEST, 0, NULL); } static void ll_connect(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; struct sk_buff *skb; int dequeued = 0; mISDN_FsmChangeState(fi, ST_LL_ESTAB); mISDN_FsmEvent(&l3->x25r, EV_LL_READY, NULL); while ((skb = skb_dequeue(&l3->downq))) { mISDN_head_t *hh = mISDN_HEAD_P(skb); if (X25_l3down(l3, hh->prim, hh->dinfo, skb)) dev_kfree_skb(skb); dequeued++; } } static void ll_connected(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; struct sk_buff *skb; int dequeued = 0; mISDN_FsmChangeState(fi, ST_LL_ESTAB); mISDN_FsmEvent(&l3->x25r, EV_LL_READY, NULL); while ((skb = skb_dequeue(&l3->downq))) { mISDN_head_t *hh = mISDN_HEAD_P(skb); if (X25_l3down(l3, hh->prim, hh->dinfo, skb)) dev_kfree_skb(skb); dequeued++; } } static void ll_release_req(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_LL_REL_WAIT); X25_l3down(l3, DL_RELEASE | REQUEST, 0, NULL); } static void ll_release_ind(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_LL_REL); discard_queue(&l3->downq); } static void ll_release_cnf(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_LL_REL); discard_queue(&l3->downq); } /* *INDENT-OFF* */ static struct FsmNode LLFnList[] = { {ST_LL_REL, EV_L3_ESTABLISH_REQ, ll_activate}, {ST_LL_REL, EV_LL_ESTABLISH_IND, ll_connect}, {ST_LL_REL, EV_LL_ESTABLISH_CNF, ll_connect}, {ST_LL_ESTAB_WAIT, EV_LL_ESTABLISH_CNF, ll_connected}, {ST_LL_ESTAB_WAIT, EV_L3_RELEASE_REQ, ll_release_req}, {ST_LL_ESTAB_WAIT, EV_LL_RELEASE_IND, ll_release_ind}, {ST_LL_ESTAB, EV_LL_RELEASE_IND, ll_release_ind}, {ST_LL_ESTAB, EV_L3_RELEASE_REQ, ll_release_req}, {ST_LL_REL_WAIT, EV_LL_RELEASE_CNF, ll_release_cnf}, {ST_LL_REL_WAIT, EV_L3_ESTABLISH_REQ, ll_activate}, }; /* *INDENT-ON* */ #define LL_FN_COUNT (sizeof(LLFnList)/sizeof(struct FsmNode)) static void l3c_debug(struct FsmInst *fi, char *fmt, ...) { x25_channel_t *l3c = fi->userdata; logdata_t log; va_start(log.args, fmt); log.fmt = fmt; log.head = l3c->l3->inst.name; mISDN_ctrl(&l3c->l3->inst, MGR_DEBUGDATA | REQUEST, &log); va_end(log.args); } static void discard_confq(x25_channel_t *l3c) { int i; x25_ConfQueue_t *cq = l3c->confq; for (i = 0; i < l3c->lwin; i++) { if (cq->PktId) { dev_kfree_skb(cq->skb); cq->PktId = 0; } cq++; } } int X25_reset_channel(x25_channel_t *l3c, struct sk_buff *skb) { discard_confq(l3c); l3c->pr = 0; l3c->ps = 0; l3c->rps = 0; // TODO requeue outstanding pakets // TODO timers ??? if (skb) { if (skb->len == 2) memcpy(l3c->cause, skb->data, 2); else printk(KERN_DEBUG "%s: skb len not 2 (%d)\n", __FUNCTION__, skb->len); } if (ST_P4 == l3c->x25p.state) { X25sendL4frame(l3c, l3c->l3, CAPI_RESET_B3_IND, 0, 2, l3c->cause); // invoke send ??? } return(0); } int X25_restart(x25_l3_t *l3) { x25_channel_t *l3c; list_for_each_entry(l3c, &l3->channellist, list) { memcpy(l3c->cause, l3->cause, 2); X25_reset_channel(l3c, NULL); mISDN_FsmEvent(&l3c->x25p, EV_L3_READY, NULL); } return(0); } int X25_next_id(x25_l3_t *l3) { int id; id = l3->next_id++; if (id == 0x0fff) l3->next_id = 1; id |= (l3->entity << 16); return(id); } int X25_get_header(x25_l3_t *l3, struct sk_buff *skb, u_char *gfi, __u16 *channel, u_char *ptype) { u_char *p = skb->data; int l = 3; if (skb->len < 2) return(38); // packet too short if ((*p & 0x30) == 0x30) { if (*p != 0x30) return(40); // invalid GFI p++; l++; if (skb->len < 3) return(38); // packet too short if ((*p & 0x30)!= 0x30) return(40); // invalid GFI if (!test_bit(X25_STATE_MOD32768, &l3->state)) return(40); // invalid GFI } *gfi = (*p & 0xf0); if (!(*gfi & 0x30)) return(40); // invalid GFI if (((*gfi & 0x30) == 0x20) && !test_bit(X25_STATE_MOD128, &l3->state)) return(40); // invalid GFI *channel = (*p & 0xf) << 8; p++; *channel |= *p++; if (skb->len < l) { *ptype = X25_PTYPE_NOTYPE; return(38); } *ptype = *p; skb_pull(skb, l); return(0); } int X25_cansend(x25_channel_t *chan) { u_int m = 7; if (test_bit(X25_STATE_MOD128, &chan->state)) m = 0x7f; else if (test_bit(X25_STATE_MOD32768, &chan->state)) m = 0x7fff; return((((chan->ps - chan->pr) & m) < chan->lwin) && !test_bit(X25_STATE_DTE_RNR, &chan->state) && (chan->x25d.state == ST_D1)); } void X25_confirmed(x25_channel_t *chan) { int i, ret; x25_ConfQueue_t *cq = chan->confq; struct sk_buff *skb; for (i = 0; i < chan->lwin; i++) { if ((cq->PktId & 0x7fff) == chan->pr) break; cq++; } if (i == chan->lwin) { int_error(); return; } skb = cq->skb; cq->skb = NULL; if (!skb) { return; } skb_push(skb, 8); skb_trim(skb, 0); capimsg_setu32(skb_put(skb, 4), 0, chan->ncci); capimsg_setu16(skb_put(skb, 2), 0, cq->DataHandle); capimsg_setu16(skb_put(skb, 2), 0, 0); i = cq->MsgId; /* free entry */ cq->PktId = 0; 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); } return; } void X25_confirm_pr(x25_channel_t *chan, u_int pr) { u_int mod = 8; if (test_bit(X25_STATE_MOD128, &chan->state)) mod = 128; else if (test_bit(X25_STATE_MOD32768, &chan->state)) mod = 32768; while (chan->pr != pr) { X25_confirmed(chan); chan->pr++; if (chan->pr >= mod) chan->pr = 0; } } int X25_receive_data(x25_channel_t *chan, int ps, int flag, struct sk_buff *skb) { int l, i, m = 8; u_char *p = skb->data; struct sk_buff *nskb; if (test_bit(X25_STATE_DTE_RNR, &chan->state)) return(X25_ERRCODE_DISCARD); for (i = 0; i < CAPI_MAXDATAWINDOW; i++) { if (chan->recv_handles[i] == 0) break; } if (i == CAPI_MAXDATAWINDOW) { test_and_set_bit(X25_STATE_DTE_RNR, &chan->state); printk(KERN_DEBUG "%s: frame %d dropped\n", __FUNCTION__, skb->len); return(X25_ERRCODE_DISCARD); } if (skb_headroom(skb) < CAPI_B3_DATA_IND_HEADER_SIZE) { printk(KERN_DEBUG "%s: only %d bytes headroom, need %d", __FUNCTION__, skb_headroom(skb), CAPI_B3_DATA_IND_HEADER_SIZE); nskb = skb_realloc_headroom(skb, CAPI_B3_DATA_IND_HEADER_SIZE); dev_kfree_skb(skb); if (!nskb) { int_error(); return(0); } } else { nskb = skb; } chan->recv_handles[i] = 0x100 | flag; l = skb->len; skb_push(nskb, CAPI_B3_DATA_IND_HEADER_SIZE - CAPIMSG_BASELEN); capimsg_setu32(nskb->data, 0, chan->ncci); if (sizeof(nskb) == 4) { capimsg_setu32(nskb->data, 4, (u_long)p); capimsg_setu32(nskb->data, 14, 0); capimsg_setu32(nskb->data, 18, 0); } else { capimsg_setu32(nskb->data, 4, 0); capimsg_setu32(nskb->data, 14, ((u_long)p) & 0xffffffff); capimsg_setu32(nskb->data, 18, (((__u64)((u_long)p)) >> 32) & 0xffffffff); } capimsg_setu16(nskb->data, 8, l); capimsg_setu16(nskb->data, 10, i); capimsg_setu16(nskb->data, 12, flag); if (mISDN_queueup_newhead(&chan->l3->inst, 0, CAPI_DATA_B3_IND, 0, nskb)) { chan->recv_handles[i] = 0; return(X25_ERRCODE_DISCARD); } if (!(flag & CAPI_FLAG_DELIVERCONF)) { if (test_bit(X25_STATE_MOD32768, &chan->state)) m = 32768; else if (test_bit(X25_STATE_MOD128, &chan->state)) m = 128; chan->rps++; if (chan->rps >= m) chan->rps = 0; if (X25_cansend(chan) && skb_queue_len(&chan->dataq)) X25_invoke_sending(chan); else { if (test_bit(X25_STATE_DTE_RNR, &chan->state)) X25sendL3frame(chan, chan->l3, X25_PTYPE_RNR, 0, NULL); else X25sendL3frame(chan, chan->l3, X25_PTYPE_RR, 0, NULL); } } return(0); } int X25_get_and_test_pr(x25_channel_t *chan, u_char ptype, struct sk_buff *skb) { u_char *p = skb->data; u_int pr_m, pr, m = 7; if (test_bit(X25_STATE_MOD128, &chan->state)) { if (skb->len < 1) return(-38); pr_m = *p; skb_pull(skb, 1); m = 0x7f; } else if (test_bit(X25_STATE_MOD32768, &chan->state)) { if (skb->len < 2) return(-38); pr_m = *p++; pr_m |= (*p << 8); skb_pull(skb, 2); m = 0x7fff; } else { pr_m = ptype >> 4; } pr = pr_m >> 1; if (chan->debug) printk(KERN_DEBUG "%s: pr(%d) chan: pr(%d) ps(%d)\n", __FUNCTION__, pr, chan->pr, chan->ps); if (((pr - chan->pr) & m) <= ((chan->ps - chan->pr) & m)) { if (chan->pr != pr) X25_confirm_pr(chan, pr); return(pr_m); } else return(-1); } int X25_get_and_test_ps(x25_channel_t *chan, u_char ptype, struct sk_buff *skb) { u_char *p = skb->data; int m = 128, ps = ptype >> 1; if (test_bit(X25_STATE_MOD32768, &chan->state)) { if (skb->len < 1) return(-38); ps |= (*p << 7); skb_pull(skb, 1); m = 32768; } else if (!test_bit(X25_STATE_MOD128, &chan->state)) { ps &= 7; m = 8; } if (chan->debug) printk(KERN_DEBUG "%s: ps(%d) chan: rps(%d)\n", __FUNCTION__, ps, chan->rps); if (ps != chan->rps) return(-2); return(ps); } void X25_release_channel(x25_channel_t *l3c) { list_del(&l3c->list); kfree(l3c->ncpi_data); l3c->ncpi_data = NULL; l3c->ncpi_len = 0; discard_queue(&l3c->dataq); mISDN_FsmDelTimer(&l3c->TP, 1); mISDN_FsmDelTimer(&l3c->TD, 2); discard_queue(&l3c->dataq); discard_confq(l3c); kfree(l3c->confq); kfree(l3c); } void 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); } if (inst->down.peer) { 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) X25_release_channel(ch); mISDN_FsmDelTimer(&l3->TR, 3); if (inst->obj) { if (l3->entity != MISDN_ENTITY_NONE) mISDN_ctrl(inst, MGR_DELENTITY | REQUEST, (void *)((u_long)l3->entity)); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); } kfree(l3); } int X25_realloc_ncpi_data(x25_channel_t *l3c, int len, u_char *data) { if (len) { if (len > l3c->ncpi_len) { kfree(l3c->ncpi_data); l3c->ncpi_data = kmalloc(len, GFP_ATOMIC); if (!l3c->ncpi_data) { l3c->ncpi_len = 0; return(-ENOMEM); } } memcpy(l3c->ncpi_data, data, len); } else { kfree(l3c->ncpi_data); l3c->ncpi_data = NULL; } l3c->ncpi_len = len; return(0); } int new_x25_channel(x25_l3_t *l3, x25_channel_t **ch_p, __u16 ch, int dlen, u_char *data) { x25_channel_t *l3c; l3c = kmalloc(sizeof(x25_channel_t), GFP_ATOMIC); if (!l3c) { printk(KERN_ERR "kmalloc x25_channel_t failed\n"); return(-ENOMEM); } memset(l3c, 0, sizeof(x25_channel_t)); if (X25_realloc_ncpi_data(l3c, dlen, data)) { printk(KERN_ERR "kmalloc ncpi_data (%d) failed\n", dlen); kfree(l3c); return(-ENOMEM); } l3c->lwin = l3->B3cfg.winsize; l3c->rwin = l3->B3cfg.winsize; l3c->datasize = l3->maxdatalen; l3c->lchan = ch; l3c->ncci = ch << 16; 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); kfree(l3c->ncpi_data); kfree(l3c); return(-ENOMEM); } memset(l3c->confq, 0, l3c->lwin * sizeof(x25_ConfQueue_t)); l3c->l3 = l3; l3c->debug = l3->debug; l3c->state = l3->state; l3c->x25p.debug = l3->debug; l3c->x25p.userdata = l3c; l3c->x25p.userint = 0; l3c->x25p.printdebug = l3c_debug; mISDN_FsmInitTimer(&l3c->x25p, &l3c->TP); l3c->x25d.debug = l3->debug; l3c->x25d.userdata = l3c; l3c->x25d.userint = 0; l3c->x25d.printdebug = l3c_debug; mISDN_FsmInitTimer(&l3c->x25d, &l3c->TD); skb_queue_head_init(&l3c->dataq); list_add_tail(&l3c->list, &l3->channellist); *ch_p = l3c; return(0); } int 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); if (!(n_l3 = kmalloc(sizeof(x25_l3_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc x25_l3_t failed\n"); return(-ENOMEM); } memset(n_l3, 0, sizeof(x25_l3_t)); INIT_LIST_HEAD(&n_l3->channellist); n_l3->entity = MISDN_ENTITY_NONE; n_l3->next_id = 1; memcpy(&n_l3->inst.pid, pid, sizeof(mISDN_pid_t)); mISDN_init_instance(&n_l3->inst, obj, n_l3, function); if (!mISDN_SetHandledPID(obj, &n_l3->inst.pid)) { int_error(); kfree(n_l3); return(-ENOPROTOOPT); } n_l3->debug = debug; n_l3->B3cfg = (x25_B3_cfg_t)DEFAULT_X25_B3_CFG; if (pid->param[3] && pid->pbuf) { u_char *p = pid->pbuf + pid->param[3]; memcpy(&n_l3->B3cfg, &p[1], p[0]); } if (n_l3->B3cfg.modulo == 128) test_and_set_bit(X25_STATE_MOD128, &n_l3->state); if (n_l3->inst.pid.global == 1) test_and_set_bit(X25_STATE_ORGINATE, &n_l3->state); n_l3->l2l3m.fsm = &llfsm; n_l3->l2l3m.state = ST_LL_REL; n_l3->l2l3m.debug = debug; n_l3->l2l3m.userdata = n_l3; n_l3->l2l3m.userint = 0; n_l3->l2l3m.printdebug = l3m_debug; n_l3->x25r.debug = debug; n_l3->x25r.userdata = n_l3; n_l3->x25r.userint = 0; 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 = mISDN_ctrl(&n_l3->inst, MGR_NEWENTITY | REQUEST, NULL); if (err) { printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%d)\n", __FUNCTION__, err); } err = mISDN_ctrl(st, MGR_REGLAYER | INDICATION, &n_l3->inst); if (err) { list_del(&n_l3->list); kfree(n_l3); n_l3 = NULL; } else { if (st->para.maxdatalen) n_l3->maxdatalen = st->para.maxdatalen; if (st->para.up_headerlen) n_l3->up_headerlen = st->para.up_headerlen; if (st->para.down_headerlen) n_l3->down_headerlen = st->para.down_headerlen; if (debug) printk(KERN_DEBUG "%s:mlen(%d) hup(%d) hdown(%d)\n", __FUNCTION__, n_l3->maxdatalen, n_l3->up_headerlen, n_l3->down_headerlen); } *l3_p = n_l3; return(err); } int X25_add_header(x25_channel_t *l3c, x25_l3_t *l3, u_char pt, u_char *head, u_char flag) { u_char *p = head; if (test_bit(X25_STATE_MOD32768, &l3->state)) { *p++ = 0x30; *p = 0x30; } else if (test_bit(X25_STATE_MOD128, &l3->state)) *p = 0x20; else *p = 0x10; switch (pt) { case X25_PTYPE_RESTART: case X25_PTYPE_RESTART_CNF: case X25_PTYPE_REGISTER: case X25_PTYPE_REGISTER_CNF: case X25_PTYPE_DIAGNOSTIC: p++; *p++ = 0; *p++ = pt; break; case X25_PTYPE_RESET: case X25_PTYPE_RESET_CNF: case X25_PTYPE_INTERRUPT: case X25_PTYPE_INTERRUPT_CNF: *p++ |= (((l3c->lchan) >> 8) & 0xf); *p++ = l3c->lchan & 0xff; *p++ = pt; break; case X25_PTYPE_CALL: case X25_PTYPE_CLEAR: if (test_bit(X25_STATE_DBIT, &l3c->state)) *p |= X25_GFI_DBIT; case X25_PTYPE_CALL_CNF: case X25_PTYPE_CLEAR_CNF: if (test_bit(X25_STATE_ABIT, &l3c->state)) *p |= X25_GFI_ABIT; *p++ |= (((l3c->lchan) >> 8) & 0xf); *p++ = l3c->lchan & 0xff; *p++ = pt; break; case X25_PTYPE_RR: case X25_PTYPE_RNR: case X25_PTYPE_REJ: if (*p == 0x10) pt |= (l3c->rps << 5); *p++ |= (((l3c->lchan) >> 8) & 0xf); *p++ = l3c->lchan & 0xff; *p++ = pt; if (test_bit(X25_STATE_MOD128, &l3->state)) *p++ = l3c->rps << 1; else if (test_bit(X25_STATE_MOD32768, &l3->state)) { *p++ = (0x7f & l3c->rps) << 1; *p++ = l3c->rps >> 7; } break; case X25_PTYPE_DATA: if (*p == 0x10) { *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT)); *p++ |= (((l3c->lchan) >> 8) & 0xf); *p++ = l3c->lchan & 0xff; if (flag & X25_MBIT) pt |= X25_MBIT_MOD8; pt |= (l3c->rps << 5); pt |= (l3c->ps << 1); *p++ = pt; l3c->ps++; if (l3c->ps > 7) l3c->ps = 0; } else if (*p == 0x20) { *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT)); *p++ |= (((l3c->lchan) >> 8) & 0xf); *p++ = l3c->lchan & 0xff; *p++ = (l3c->ps << 1); *p = (flag & X25_MBIT) ? 1 : 0; *p++ |= (l3c->rps << 1); l3c->ps++; if (l3c->ps > 0x7f) l3c->ps = 0; } else { *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT)); *p++ |= (((l3c->lchan) >> 8) & 0xf); *p++ = l3c->lchan & 0xff; *p++ = ((l3c->ps & 0x7f) << 1); *p++ = (l3c->ps >> 7); *p = (flag & X25_MBIT) ? 1 : 0; *p++ = ((l3c->rps & 0x7f) << 1); *p++ = (l3c->rps >> 7); l3c->ps++; if (l3c->ps > 0x7fff) l3c->ps = 0; } break; default: return(-EINVAL); } return(p - head); } int X25sendL3frame(x25_channel_t *l3c, x25_l3_t *l3, u_char pt, int len, void *arg) { struct sk_buff *skb; int ret; skb = alloc_stack_skb(len + X25_MINSIZE, l3->down_headerlen); if (!skb) return(-ENOMEM); ret = X25_add_header(l3c, l3, pt, skb->tail, 0); if (ret<0) { int_error(); dev_kfree_skb(skb); return(ret); } skb_put(skb, ret); if (arg && len) memcpy(skb_put(skb, len), arg, len); mISDN_sethead(DL_DATA_REQ, X25_next_id(l3), skb); if (l3->l2l3m.state == ST_LL_ESTAB) { ret = mISDN_queue_message(&l3->inst, FLG_MSG_DOWN, skb); if (ret) { dev_kfree_skb(skb); } } else { skb_queue_tail(&l3->downq, skb); ret = 0; } return(ret); } int X25_l3down(x25_l3_t *l3, u_int prim, u_int dinfo, struct sk_buff *skb) { int ret; if (!skb) { if (!(skb = alloc_stack_skb(0, l3->down_headerlen))) return(-ENOMEM); } ret = mISDN_queuedown_newhead(&l3->inst, 0, prim, dinfo, skb); if (ret) { dev_kfree_skb(skb); } return(0); } void X25_send_diagnostic(x25_l3_t *l3, struct sk_buff *skb, int err, int channel) { u_char diagp[8], *p; u_int i,l = 3; p = diagp; *p++ = err & 0xff; if (test_bit(X25_STATE_MOD32768, &l3->state)) { *p++ = 0x30; l++; } if (skb) { if (skb->len < l) l = skb->len; for (i = 0; i < l; i++) *p++ = skb->data[i]; } else { if ((err & 0xf0) == 0x30) { /* Timer Expired */ if (test_bit(X25_STATE_MOD32768, &l3->state)) *p = 0x30; else if (test_bit(X25_STATE_MOD128, &l3->state)) *p = 0x20; else *p = 0x10; if (err == 0x34) channel = 0; *p |= ((channel >> 8) & 0x0f); p++; *p++ = channel & 0xff; } } X25sendL3frame(NULL, l3, X25_PTYPE_DIAGNOSTIC, p - diagp, diagp); } x25_channel_t * X25_get_channel(x25_l3_t *l3, __u16 ch) { x25_channel_t *l3c; list_for_each_entry(l3c, &l3->channellist, list) { if (l3c->lchan == ch) return(l3c); } return(NULL); } x25_channel_t * X25_get_channel4NCCI(x25_l3_t *l3, __u32 addr) { x25_channel_t *l3c; list_for_each_entry(l3c, &l3->channellist, list) { if ((l3c->ncci & 0xffff0000) == (addr & 0xffff0000)) return(l3c); } return(NULL); } int X25sendL4skb(x25_channel_t *l3c, x25_l3_t *l3, __u32 addr, int prim, int dinfo, struct sk_buff *skb) { skb_push(skb, 4); if (l3c) capimsg_setu32(skb->data, 0, l3c->ncci); else capimsg_setu32(skb->data, 0, addr); return(mISDN_queueup_newhead(&l3->inst, 0, prim, dinfo, skb)); } int X25sendL4frame(x25_channel_t *l3c, x25_l3_t *l3, int prim, int flags, int len, void *arg) { struct sk_buff *skb; u_char *p; int ret; skb = alloc_stack_skb(len + X25_MINSIZE + 2, l3->up_headerlen); if (!skb) return(-ENOMEM); capimsg_setu32(skb_put(skb, 4), 0, l3c->ncci); switch(prim) { case CAPI_DISCONNECT_B3_IND: capimsg_setu16(skb_put(skb, 2), 0, flags & 0xffff); case CAPI_CONNECT_B3_IND: case CAPI_CONNECT_B3_ACTIVE_IND: case CAPI_RESET_B3_IND: if (len) { p = skb_put(skb, len + 4); *p++ = len +3; if (flags & 0x10000) *p++ = 1; else *p++ = 0; *p++ = l3c->lchan >> 8; *p++ = l3c->lchan & 0xff; memcpy(p, arg, len); } else { p = skb_put(skb, 1); *p = 0; } break; default: dev_kfree_skb(skb); return(-EINVAL); } 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); } return(ret); } static int confq_len(x25_channel_t *l3c) { int i,n = 0; x25_ConfQueue_t *cq = l3c->confq; for (i = 0; i < l3c->lwin; i++) if (cq[i].PktId) n++; return(n); } static inline x25_ConfQueue_t * get_free_confentry(x25_channel_t *l3c) { int i; x25_ConfQueue_t *cq = l3c->confq; for (i = 0; i < l3c->lwin; i++) { if (!cq->PktId) break; cq++; } if (i == l3c->lwin) return(NULL); return(cq); } int X25_invoke_sending(x25_channel_t *l3c) { int l,n = 0; x25_ConfQueue_t *cq; struct sk_buff *skb, *nskb; u_char flg; if (!X25_cansend(l3c)) return(0); cq = get_free_confentry(l3c); skb = skb_dequeue(&l3c->dataq); while(cq && skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); cq->MsgId = hh->dinfo; hh++; cq->DataHandle = hh->prim; nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) { skb_queue_head(&l3c->dataq, skb); break; } cq->skb = skb; cq->PktId = 0x10000 | l3c->ps; flg = (hh->dinfo & CAPI_FLAG_DELIVERCONF) ? X25_GFI_DBIT : 0; if (hh->dinfo & CAPI_FLAG_QUALIFIER) flg |= X25_GFI_QBIT; if (hh->dinfo & CAPI_FLAG_MOREDATA) flg |= X25_MBIT; l = 3; if (test_bit(X25_STATE_MOD128, &l3c->state)) l++; else if (test_bit(X25_STATE_MOD32768, &l3c->state)) l += 4; skb_push(nskb, l); if (l != X25_add_header(l3c, l3c->l3, X25_PTYPE_DATA, nskb->data, flg)) int_error(); if (l3c->l3->l2l3m.state == ST_LL_ESTAB) X25_l3down(l3c->l3, DL_DATA_REQ, X25_next_id(l3c->l3), nskb); else { mISDN_sethead(DL_DATA_REQ, X25_next_id(l3c->l3), nskb); skb_queue_tail(&l3c->l3->downq, nskb); break; } cq = get_free_confentry(l3c); skb = skb_dequeue(&l3c->dataq); n++; } return(n); } __u16 x25_data_b3_req(x25_channel_t *l3c, int dinfo, struct sk_buff *skb) { __u16 size; mISDN_head_t *hh = mISDN_HEAD_P(skb); if (!l3c) return(0x2002); if (skb->len < 10) return(0x2007); if ((confq_len(l3c) + skb_queue_len(&l3c->dataq)) > 7) return(CAPI_SENDQUEUEFULL); if ((l3c->x25p.state != ST_P4) && (l3c->x25d.state != ST_D1)) return(0x2001); size = CAPIMSG_U16(skb->data, 4); /* we save DataHandle and Flags in a area after normal mISDN_HEAD */ hh++; hh->prim = CAPIMSG_U16(skb->data, 6); hh->dinfo = CAPIMSG_U16(skb->data, 8); /* the data begins behind the header, we don't use Data32/Data64 here */ if ((skb->len - size) == 18) skb_pull(skb, 18); else if ((skb->len - size) == 10) // old format skb_pull(skb, 10); else return(0x2007); if (hh->dinfo & CAPI_FLAG_EXPEDITED) { // TODO Interrupt packet } skb_queue_tail(&l3c->dataq, skb); X25_invoke_sending(l3c); return(0); } int x25_data_b3_resp(x25_channel_t *l3c, int dinfo, struct sk_buff *skb) { int i, m = 8; if (!l3c) return(-ENODEV); i = CAPIMSG_U16(skb->data, 0); dev_kfree_skb(skb); if (i >= CAPI_MAXDATAWINDOW) { int_error(); return(-EINVAL); } if (l3c->recv_handles[i] == 0) { int_error(); return(-EINVAL); } if (l3c->recv_handles[i] & CAPI_FLAG_DELIVERCONF) { if (test_bit(X25_STATE_MOD32768, &l3c->state)) m = 32768; else if (test_bit(X25_STATE_MOD128, &l3c->state)) m = 128; l3c->rps++; if (l3c->rps >= m) l3c->rps = 0; l3c->recv_handles[i] = 0; i = 0; if (X25_cansend(l3c) && skb_queue_len(&l3c->dataq)) X25_invoke_sending(l3c); else { i = 1; X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RR, 0, NULL); } } else { l3c->recv_handles[i] = 0; i = 0; } if (test_and_clear_bit(X25_STATE_DTE_RNR, &l3c->state)) { if (!i) X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RR, 0, NULL); } return(0); } int X25_l3_init(void) { llfsm.state_count = LL_STATE_COUNT; llfsm.event_count = LL_EVENT_COUNT; llfsm.strEvent = strLLEvent; llfsm.strState = strLLState; mISDN_FsmNew(&llfsm, LLFnList, LL_FN_COUNT); return(0); } void X25_l3_cleanup(void) { mISDN_FsmFree(&llfsm); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/xhfc24succ.h0000644000000000000500000021145211135651702020521 0ustar rootsrc/*___________________________________________________________________________________*/ /* */ /* (C) Copyright Cologne Chip AG, 2006 */ /*___________________________________________________________________________________*/ /* */ /* */ /* File name: xhfc24succ.h */ /* File content: This file contains the XHFC-2S4U / XHFC-4SU register definitions. */ /* Creation date: 22.02.2006 13:14 */ /* Creator: Genero 3.4 */ /* Data base: HFC XML 1.6 for XHFC-1SU, XHFC-2SU, XHFC-2S4U and XHFC-4SU */ /* Address range: 0x00 - 0xFF */ /* */ /* The information presented can not be considered as assured characteristics. */ /* Data can change without notice. Please check version numbers in case of doubt. */ /* */ /* For further information or questions please contact support@CologneChip.com */ /* */ /* */ /*___________________________________________________________________________________*/ /* */ /* WARNING: This file has been generated automatically and should not be */ /* changed to maintain compatibility with later versions. */ /*___________________________________________________________________________________*/ /* */ #ifndef _XHFC24SUCC_H_ #define _XHFC24SUCC_H_ typedef unsigned char BYTE; typedef BYTE REGWORD; // maximum register length (standard) typedef BYTE REGADDR; // address width typedef enum {no=0, yes} REGBOOL; typedef enum { // register and bitmap access modes: writeonly=0, // write only readonly, // read only readwrite, // read/write // following modes only for mixed mode registers: readwrite_write, // read/write and write only readwrite_read, // read/write and read only write_read, // write only and read only readwrite_write_read // read/write, write only and read only } ACCESSMODE; /*___________________________________________________________________________________*/ /* */ /* common chip information: */ /*___________________________________________________________________________________*/ #define CHIP_NAME_2S4U "XHFC-2S4U" #define CHIP_NAME_4SU "XHFC-4SU" #define CHIP_TITLE_2S4U "ISDN HDLC FIFO controller 2 S/T interfaces combined with 4 Up interfaces (Universal ISDN Ports)" #define CHIP_TITLE_4SU "ISDN HDLC FIFO controller with 4 S/T interfaces combined with 4 Up interfaces (Universal ISDN Ports)" #define CHIP_MANUFACTURER "Cologne Chip" #define CHIP_ID_2S4U 0x62 #define CHIP_ID_4SU 0x63 #define CHIP_REGISTER_COUNT 122 #define CHIP_DATABASE "Version HFC-XMLHFC XML 1.6 for XHFC-1SU, XHFC-2SU, XHFC-2S4U and XHFC-4SU - GeneroGenero 3.4 " // This register file can also be used for XHFC-2SU and XHFC-1SU programming. // For this reason these chip names, IDs and titles are defined here as well: #define CHIP_NAME_2SU "XHFC-2SU" #define CHIP_TITLE_2SU "ISDN HDLC FIFO controller with 2 combined S/T and Up Interfaces" #define CHIP_ID_2SU 0x61 #define CHIP_NAME_1SU "XHFC-1SU" #define CHIP_TITLE_1SU "ISDN HDLC FIFO controller with a combined S/T and Up Interface" #define CHIP_ID_1SU 0x60 /*___________________________________________________________________________________*/ /* */ /* Begin of XHFC-2S4U / XHFC-4SU register definitions. */ /*___________________________________________________________________________________*/ /* */ #define R_CIRM 0x00 // register access #define M_CLK_OFF 0x01 // bitmap mask (1bit) #define M_WAIT_PROC 0x02 // bitmap mask (1bit) #define M_WAIT_REG 0x04 // bitmap mask (1bit) #define M_SRES 0x08 // bitmap mask (1bit) #define M_HFC_RES 0x10 // bitmap mask (1bit) #define M_PCM_RES 0x20 // bitmap mask (1bit) #define M_SU_RES 0x40 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_clk_off:1; REGWORD v_wait_proc:1; REGWORD v_wait_reg:1; REGWORD v_sres:1; REGWORD v_hfc_res:1; REGWORD v_pcm_res:1; REGWORD v_su_res:1; REGWORD reserved_0:1; } bit_r_cirm; // register and bitmap data typedef union {REGWORD reg; bit_r_cirm bit;} reg_r_cirm; // register and bitmap access #define R_CTRL 0x01 // register access #define M_FIFO_LPRIO 0x02 // bitmap mask (1bit) #define M_NT_SYNC 0x08 // bitmap mask (1bit) #define M_OSC_OFF 0x20 // bitmap mask (1bit) #define M_SU_CLK 0xC0 // bitmap mask (2bit) #define M1_SU_CLK 0x40 typedef struct // bitmap construction { REGWORD reserved_1:1; REGWORD v_fifo_lprio:1; REGWORD reserved_2:1; REGWORD v_nt_sync:1; REGWORD reserved_3:1; REGWORD v_osc_off:1; REGWORD v_su_clk:2; } bit_r_ctrl; // register and bitmap data typedef union {REGWORD reg; bit_r_ctrl bit;} reg_r_ctrl; // register and bitmap access #define R_CLK_CFG 0x02 // register access #define M_CLK_PLL 0x01 // bitmap mask (1bit) #define M_CLKO_HI 0x02 // bitmap mask (1bit) #define M_CLKO_PLL 0x04 // bitmap mask (1bit) #define M_PCM_CLK 0x20 // bitmap mask (1bit) #define M_CLKO_OFF 0x40 // bitmap mask (1bit) #define M_CLK_F1 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_clk_pll:1; REGWORD v_clko_hi:1; REGWORD v_clko_pll:1; REGWORD reserved_4:2; REGWORD v_pcm_clk:1; REGWORD v_clko_off:1; REGWORD v_clk_f1:1; } bit_r_clk_cfg; // register and bitmap data typedef union {REGWORD reg; bit_r_clk_cfg bit;} reg_r_clk_cfg; // register and bitmap access #define A_Z1 0x04 // register access #define M_Z1 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_z1:8; } bit_a_z1; // register and bitmap data typedef union {REGWORD reg; bit_a_z1 bit;} reg_a_z1; // register and bitmap access #define A_Z2 0x06 // register access #define M_Z2 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_z2:8; } bit_a_z2; // register and bitmap data typedef union {REGWORD reg; bit_a_z2 bit;} reg_a_z2; // register and bitmap access #define R_RAM_ADDR 0x08 // register access #define M_RAM_ADDR0 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_ram_addr0:8; } bit_r_ram_addr; // register and bitmap data typedef union {REGWORD reg; bit_r_ram_addr bit;} reg_r_ram_addr; // register and bitmap access #define R_RAM_CTRL 0x09 // register access #define M_RAM_ADDR1 0x0F // bitmap mask (4bit) #define M1_RAM_ADDR1 0x01 #define M_ADDR_RES 0x40 // bitmap mask (1bit) #define M_ADDR_INC 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_ram_addr1:4; REGWORD reserved_5:2; REGWORD v_addr_res:1; REGWORD v_addr_inc:1; } bit_r_ram_ctrl; // register and bitmap data typedef union {REGWORD reg; bit_r_ram_ctrl bit;} reg_r_ram_ctrl; // register and bitmap access #define R_FIRST_FIFO 0x0B // register access #define M_FIRST_FIFO_DIR 0x01 // bitmap mask (1bit) #define M_FIRST_FIFO_NUM 0x1E // bitmap mask (4bit) #define M1_FIRST_FIFO_NUM 0x02 typedef struct // bitmap construction { REGWORD v_first_fifo_dir:1; REGWORD v_first_fifo_num:4; REGWORD reserved_6:3; } bit_r_first_fifo; // register and bitmap data typedef union {REGWORD reg; bit_r_first_fifo bit;} reg_r_first_fifo; // register and bitmap access #define R_FIFO_THRES 0x0C // register access #define M_THRES_TX 0x0F // bitmap mask (4bit) #define M1_THRES_TX 0x01 #define M_THRES_RX 0xF0 // bitmap mask (4bit) #define M1_THRES_RX 0x10 typedef struct // bitmap construction { REGWORD v_thres_tx:4; REGWORD v_thres_rx:4; } bit_r_fifo_thres; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_thres bit;} reg_r_fifo_thres; // register and bitmap access #define A_F1 0x0C // register access #define M_F1 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_f1:8; } bit_a_f1; // register and bitmap data typedef union {REGWORD reg; bit_a_f1 bit;} reg_a_f1; // register and bitmap access #define R_FIFO_MD 0x0D // register access #define M_FIFO_MD 0x03 // bitmap mask (2bit) #define M1_FIFO_MD 0x01 #define M_DF_MD 0x0C // bitmap mask (2bit) #define M1_DF_MD 0x04 #define M_UNIDIR_MD 0x10 // bitmap mask (1bit) #define M_UNIDIR_RX 0x20 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo_md:2; REGWORD v_df_md:2; REGWORD v_unidir_md:1; REGWORD v_unidir_rx:1; REGWORD reserved_7:2; } bit_r_fifo_md; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_md bit;} reg_r_fifo_md; // register and bitmap access #define A_F2 0x0D // register access #define M_F2 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_f2:8; } bit_a_f2; // register and bitmap data typedef union {REGWORD reg; bit_a_f2 bit;} reg_a_f2; // register and bitmap access #define A_INC_RES_FIFO 0x0E // register access #define M_INC_F 0x01 // bitmap mask (1bit) #define M_RES_FIFO 0x02 // bitmap mask (1bit) #define M_RES_LOST 0x04 // bitmap mask (1bit) #define M_RES_FIFO_ERR 0x08 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_inc_f:1; REGWORD v_res_fifo:1; REGWORD v_res_lost:1; REGWORD v_res_fifo_err:1; REGWORD reserved_8:4; } bit_a_inc_res_fifo; // register and bitmap data typedef union {REGWORD reg; bit_a_inc_res_fifo bit;} reg_a_inc_res_fifo; // register and bitmap access #define A_FIFO_STA 0x0E // register access #define M_FIFO_ERR 0x01 // bitmap mask (1bit) #define M_ABO_DONE 0x10 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo_err:1; REGWORD reserved_11:3; REGWORD v_abo_done:1; REGWORD reserved_12:3; } bit_a_fifo_sta; // register and bitmap data typedef union {REGWORD reg; bit_a_fifo_sta bit;} reg_a_fifo_sta; // register and bitmap access #define R_FSM_IDX 0x0F // register access #define M_IDX 0x1F // bitmap mask (5bit) #define M1_IDX 0x01 typedef struct // bitmap construction { REGWORD v_idx:5; REGWORD reserved_10:3; } bit_r_fsm_idx; // register and bitmap data typedef union {REGWORD reg; bit_r_fsm_idx bit;} reg_r_fsm_idx; // register and bitmap access #define IDX_FSM_IDX 0x01 // index value selecting this multi-register #define R_FIFO 0x0F // register access #define M_FIFO_DIR 0x01 // bitmap mask (1bit) #define M_FIFO_NUM 0x1E // bitmap mask (4bit) #define M1_FIFO_NUM 0x02 #define M_REV 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo_dir:1; REGWORD v_fifo_num:4; REGWORD reserved_9:2; REGWORD v_rev:1; } bit_r_fifo; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo bit;} reg_r_fifo; // register and bitmap access #define IDX_FIFO 0x00 // index value selecting this multi-register #define R_SLOT 0x10 // register access #define M_SL_DIR 0x01 // bitmap mask (1bit) #define M_SL_NUM 0xFE // bitmap mask (7bit) typedef struct // bitmap construction { REGWORD v_sl_dir:1; REGWORD v_sl_num:7; } bit_r_slot; // register and bitmap data typedef union {REGWORD reg; bit_r_slot bit;} reg_r_slot; // register and bitmap access #define R_IRQ_OVIEW 0x10 // register access #define M_FIFO_BL0_IRQ 0x01 // bitmap mask (1bit) #define M_FIFO_BL1_IRQ 0x02 // bitmap mask (1bit) #define M_FIFO_BL2_IRQ 0x04 // bitmap mask (1bit) #define M_FIFO_BL3_IRQ 0x08 // bitmap mask (1bit) #define M_MISC_IRQ 0x10 // bitmap mask (1bit) #define M_CH_IRQ 0x20 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo_bl0_irq:1; REGWORD v_fifo_bl1_irq:1; REGWORD v_fifo_bl2_irq:1; REGWORD v_fifo_bl3_irq:1; REGWORD v_misc_irq:1; REGWORD v_ch_irq:1; REGWORD reserved_19:2; } bit_r_irq_oview; // register and bitmap data typedef union {REGWORD reg; bit_r_irq_oview bit;} reg_r_irq_oview; // register and bitmap access #define R_MISC_IRQMSK 0x11 // register access #define M_SLIP_IRQMSK 0x01 // bitmap mask (1bit) #define M_TI_IRQMSK 0x02 // bitmap mask (1bit) #define M_PROC_IRQMSK 0x04 // bitmap mask (1bit) #define M_CI_IRQMSK 0x10 // bitmap mask (1bit) #define M_WAK_IRQMSK 0x20 // bitmap mask (1bit) #define M_MON_TX_IRQMSK 0x40 // bitmap mask (1bit) #define M_MON_RX_IRQMSK 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_slip_irqmsk:1; REGWORD v_ti_irqmsk:1; REGWORD v_proc_irqmsk:1; REGWORD reserved_13:1; REGWORD v_ci_irqmsk:1; REGWORD v_wak_irqmsk:1; REGWORD v_mon_tx_irqmsk:1; REGWORD v_mon_rx_irqmsk:1; } bit_r_misc_irqmsk; // register and bitmap data typedef union {REGWORD reg; bit_r_misc_irqmsk bit;} reg_r_misc_irqmsk; // register and bitmap access #define R_MISC_IRQ 0x11 // register access #define M_SLIP_IRQ 0x01 // bitmap mask (1bit) #define M_TI_IRQ 0x02 // bitmap mask (1bit) #define M_PROC_IRQ 0x04 // bitmap mask (1bit) #define M_CI_IRQ 0x10 // bitmap mask (1bit) #define M_WAK_IRQ 0x20 // bitmap mask (1bit) #define M_MON_TX_IRQ 0x40 // bitmap mask (1bit) #define M_MON_RX_IRQ 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_slip_irq:1; REGWORD v_ti_irq:1; REGWORD v_proc_irq:1; REGWORD reserved_20:1; REGWORD v_ci_irq:1; REGWORD v_wak_irq:1; REGWORD v_mon_tx_irq:1; REGWORD v_mon_rx_irq:1; } bit_r_misc_irq; // register and bitmap data typedef union {REGWORD reg; bit_r_misc_irq bit;} reg_r_misc_irq; // register and bitmap access #define R_SU_IRQ 0x12 // register access #define M_SU0_IRQ 0x01 // bitmap mask (1bit) #define M_SU1_IRQ 0x02 // bitmap mask (1bit) #define M_SU2_IRQ 0x04 // bitmap mask (1bit) #define M_SU3_IRQ 0x08 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_su0_irq:1; REGWORD v_su1_irq:1; REGWORD v_su2_irq:1; REGWORD v_su3_irq:1; REGWORD reserved_27:4; } bit_r_su_irq; // register and bitmap data typedef union {REGWORD reg; bit_r_su_irq bit;} reg_r_su_irq; // register and bitmap access #define R_SU_IRQMSK 0x12 // register access #define M_SU0_IRQMSK 0x01 // bitmap mask (1bit) #define M_SU1_IRQMSK 0x02 // bitmap mask (1bit) #define M_SU2_IRQMSK 0x04 // bitmap mask (1bit) #define M_SU3_IRQMSK 0x08 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_su0_irqmsk:1; REGWORD v_su1_irqmsk:1; REGWORD v_su2_irqmsk:1; REGWORD v_su3_irqmsk:1; REGWORD reserved_29:4; } bit_r_su_irqmsk; // register and bitmap data typedef union {REGWORD reg; bit_r_su_irqmsk bit;} reg_r_su_irqmsk; // register and bitmap access #define R_AF0_OVIEW 0x13 // register access #define M_SU0_AF0 0x01 // bitmap mask (1bit) #define M_SU1_AF0 0x02 // bitmap mask (1bit) #define M_SU2_AF0 0x04 // bitmap mask (1bit) #define M_SU3_AF0 0x08 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_su0_af0:1; REGWORD v_su1_af0:1; REGWORD v_su2_af0:1; REGWORD v_su3_af0:1; REGWORD reserved_28:4; } bit_r_af0_oview; // register and bitmap data typedef union {REGWORD reg; bit_r_af0_oview bit;} reg_r_af0_oview; // register and bitmap access #define R_IRQ_CTRL 0x13 // register access #define M_FIFO_IRQ_EN 0x01 // bitmap mask (1bit) #define M_GLOB_IRQ_EN 0x08 // bitmap mask (1bit) #define M_IRQ_POL 0x10 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo_irq_en:1; REGWORD reserved_14:2; REGWORD v_glob_irq_en:1; REGWORD v_irq_pol:1; REGWORD reserved_15:3; } bit_r_irq_ctrl; // register and bitmap data typedef union {REGWORD reg; bit_r_irq_ctrl bit;} reg_r_irq_ctrl; // register and bitmap access #define A_USAGE 0x14 // register access #define M_USAGE 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_usage:8; } bit_a_usage; // register and bitmap data typedef union {REGWORD reg; bit_a_usage bit;} reg_a_usage; // register and bitmap access #define R_PCM_MD0 0x14 // register access #define M_PCM_MD 0x01 // bitmap mask (1bit) #define M_C4_POL 0x02 // bitmap mask (1bit) #define M_F0_NEG 0x04 // bitmap mask (1bit) #define M_F0_LEN 0x08 // bitmap mask (1bit) #define M_PCM_IDX 0xF0 // bitmap mask (4bit) #define M1_PCM_IDX 0x10 typedef struct // bitmap construction { REGWORD v_pcm_md:1; REGWORD v_c4_pol:1; REGWORD v_f0_neg:1; REGWORD v_f0_len:1; REGWORD v_pcm_idx:4; } bit_r_pcm_md0; // register and bitmap data typedef union {REGWORD reg; bit_r_pcm_md0 bit;} reg_r_pcm_md0; // register and bitmap access #define R_SL_SEL0 0x15 // register access #define M_SL_SEL0 0x7F // bitmap mask (7bit) #define M_SH_SEL0 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_sl_sel0:7; REGWORD v_sh_sel0:1; } bit_r_sl_sel0; // register and bitmap data typedef union {REGWORD reg; bit_r_sl_sel0 bit;} reg_r_sl_sel0; // register and bitmap access #define IDX_SL_SEL0 0x00 // index value selecting this multi-register #define R_SL_SEL1 0x15 // register access #define M_SL_SEL1 0x7F // bitmap mask (7bit) #define M_SH_SEL1 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_sl_sel1:7; REGWORD v_sh_sel1:1; } bit_r_sl_sel1; // register and bitmap data typedef union {REGWORD reg; bit_r_sl_sel1 bit;} reg_r_sl_sel1; // register and bitmap access #define IDX_SL_SEL1 0x01 // index value selecting this multi-register #define R_SL_SEL7 0x15 // register access #define M_SL_SEL7 0x7F // bitmap mask (7bit) typedef struct // bitmap construction { REGWORD v_sl_sel7:7; REGWORD reserved_30:1; } bit_r_sl_sel7; // register and bitmap data typedef union {REGWORD reg; bit_r_sl_sel7 bit;} reg_r_sl_sel7; // register and bitmap access #define IDX_SL_SEL7 0x07 // index value selecting this multi-register #define R_MSS0 0x15 // register access #define M_MSS_MOD 0x01 // bitmap mask (1bit) #define M_MSS_MOD_REP 0x02 // bitmap mask (1bit) #define M_MSS_SRC_EN 0x04 // bitmap mask (1bit) #define M_MSS_SRC_GRD 0x08 // bitmap mask (1bit) #define M_MSS_OUT_EN 0x10 // bitmap mask (1bit) #define M_MSS_OUT_REP 0x20 // bitmap mask (1bit) #define M_MSS_SRC 0xC0 // bitmap mask (2bit) #define M1_MSS_SRC 0x40 typedef struct // bitmap construction { REGWORD v_mss_mod:1; REGWORD v_mss_mod_rep:1; REGWORD v_mss_src_en:1; REGWORD v_mss_src_grd:1; REGWORD v_mss_out_en:1; REGWORD v_mss_out_rep:1; REGWORD v_mss_src:2; } bit_r_mss0; // register and bitmap data typedef union {REGWORD reg; bit_r_mss0 bit;} reg_r_mss0; // register and bitmap access #define IDX_MSS0 0x08 // index value selecting this multi-register #define R_PCM_MD1 0x15 // register access #define M_PCM_OD 0x02 // bitmap mask (1bit) #define M_PLL_ADJ 0x0C // bitmap mask (2bit) #define M1_PLL_ADJ 0x04 #define M_PCM_DR 0x30 // bitmap mask (2bit) #define M1_PCM_DR 0x10 #define M_PCM_LOOP 0x40 // bitmap mask (1bit) #define M_PCM_SMPL 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD reserved_31:1; REGWORD v_pcm_od:1; REGWORD v_pll_adj:2; REGWORD v_pcm_dr:2; REGWORD v_pcm_loop:1; REGWORD v_pcm_smpl:1; } bit_r_pcm_md1; // register and bitmap data typedef union {REGWORD reg; bit_r_pcm_md1 bit;} reg_r_pcm_md1; // register and bitmap access #define IDX_PCM_MD1 0x09 // index value selecting this multi-register #define R_PCM_MD2 0x15 // register access #define M_SYNC_OUT1 0x02 // bitmap mask (1bit) #define M_SYNC_SRC 0x04 // bitmap mask (1bit) #define M_SYNC_OUT2 0x08 // bitmap mask (1bit) #define M_C2O_EN 0x10 // bitmap mask (1bit) #define M_C2I_EN 0x20 // bitmap mask (1bit) #define M_PLL_ICR 0x40 // bitmap mask (1bit) #define M_PLL_MAN 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD reserved_32:1; REGWORD v_sync_out1:1; REGWORD v_sync_src:1; REGWORD v_sync_out2:1; REGWORD v_c2o_en:1; REGWORD v_c2i_en:1; REGWORD v_pll_icr:1; REGWORD v_pll_man:1; } bit_r_pcm_md2; // register and bitmap data typedef union {REGWORD reg; bit_r_pcm_md2 bit;} reg_r_pcm_md2; // register and bitmap access #define IDX_PCM_MD2 0x0A // index value selecting this multi-register #define R_MSS1 0x15 // register access #define M_MSS_OFFS 0x07 // bitmap mask (3bit) #define M1_MSS_OFFS 0x01 #define M_MS_SSYNC1 0x08 // bitmap mask (1bit) #define M_MSS_DLY 0xF0 // bitmap mask (4bit) #define M1_MSS_DLY 0x10 typedef struct // bitmap construction { REGWORD v_mss_offs:3; REGWORD v_ms_ssync1:1; REGWORD v_mss_dly:4; } bit_r_mss1; // register and bitmap data typedef union {REGWORD reg; bit_r_mss1 bit;} reg_r_mss1; // register and bitmap access #define IDX_MSS1 0x0B // index value selecting this multi-register #define R_SH0L 0x15 // register access #define M_SH0L 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_sh0l:8; } bit_r_sh0l; // register and bitmap data typedef union {REGWORD reg; bit_r_sh0l bit;} reg_r_sh0l; // register and bitmap access #define IDX_SH0L 0x0C // index value selecting this multi-register #define R_SH0H 0x15 // register access #define M_SH0H 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_sh0h:8; } bit_r_sh0h; // register and bitmap data typedef union {REGWORD reg; bit_r_sh0h bit;} reg_r_sh0h; // register and bitmap access #define IDX_SH0H 0x0D // index value selecting this multi-register #define R_SH1L 0x15 // register access #define M_SH1L 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_sh1l:8; } bit_r_sh1l; // register and bitmap data typedef union {REGWORD reg; bit_r_sh1l bit;} reg_r_sh1l; // register and bitmap access #define IDX_SH1L 0x0E // index value selecting this multi-register #define R_SH1H 0x15 // register access #define M_SH1H 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_sh1h:8; } bit_r_sh1h; // register and bitmap data typedef union {REGWORD reg; bit_r_sh1h bit;} reg_r_sh1h; // register and bitmap access #define IDX_SH1H 0x0F // index value selecting this multi-register #define R_RAM_USE 0x15 // register access #define M_SRAM_USE 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_sram_use:8; } bit_r_ram_use; // register and bitmap data typedef union {REGWORD reg; bit_r_ram_use bit;} reg_r_ram_use; // register and bitmap access #define R_SU_SEL 0x16 // register access #define M_SU_SEL 0x03 // bitmap mask (2bit) #define M1_SU_SEL 0x01 #define M_MULT_SU 0x08 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_su_sel:2; REGWORD reserved_25:1; REGWORD v_mult_su:1; REGWORD reserved_26:4; } bit_r_su_sel; // register and bitmap data typedef union {REGWORD reg; bit_r_su_sel bit;} reg_r_su_sel; // register and bitmap access #define R_CHIP_ID 0x16 // register access #define M_CHIP_ID 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_chip_id:8; } bit_r_chip_id; // register and bitmap data typedef union {REGWORD reg; bit_r_chip_id bit;} reg_r_chip_id; // register and bitmap access #define R_SU_SYNC 0x17 // register access #define M_SYNC_SEL 0x07 // bitmap mask (3bit) #define M1_SYNC_SEL 0x01 #define M_MAN_SYNC 0x08 // bitmap mask (1bit) #define M_AUTO_SYNCI 0x10 // bitmap mask (1bit) #define M_D_MERGE_TX 0x20 // bitmap mask (1bit) #define M_E_MERGE_RX 0x40 // bitmap mask (1bit) #define M_D_MERGE_RX 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_sync_sel:3; REGWORD v_man_sync:1; REGWORD v_auto_synci:1; REGWORD v_d_merge_tx:1; REGWORD v_e_merge_rx:1; REGWORD v_d_merge_rx:1; } bit_r_su_sync; // register and bitmap data typedef union {REGWORD reg; bit_r_su_sync bit;} reg_r_su_sync; // register and bitmap access #define R_BERT_STA 0x17 // register access #define M_RD_SYNC_SRC 0x07 // bitmap mask (3bit) #define M1_RD_SYNC_SRC 0x01 #define M_BERT_SYNC 0x10 // bitmap mask (1bit) #define M_BERT_INV_DATA 0x20 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_rd_sync_src:3; REGWORD reserved_21:1; REGWORD v_bert_sync:1; REGWORD v_bert_inv_data:1; REGWORD reserved_22:2; } bit_r_bert_sta; // register and bitmap data typedef union {REGWORD reg; bit_r_bert_sta bit;} reg_r_bert_sta; // register and bitmap access #define R_F0_CNTL 0x18 // register access #define M_F0_CNTL 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_f0_cntl:8; } bit_r_f0_cntl; // register and bitmap data typedef union {REGWORD reg; bit_r_f0_cntl bit;} reg_r_f0_cntl; // register and bitmap access #define R_F0_CNTH 0x19 // register access #define M_F0_CNTH 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_f0_cnth:8; } bit_r_f0_cnth; // register and bitmap data typedef union {REGWORD reg; bit_r_f0_cnth bit;} reg_r_f0_cnth; // register and bitmap access #define R_BERT_ECL 0x1A // register access #define M_BERT_ECL 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_bert_ecl:8; } bit_r_bert_ecl; // register and bitmap data typedef union {REGWORD reg; bit_r_bert_ecl bit;} reg_r_bert_ecl; // register and bitmap access #define R_TI_WD 0x1A // register access #define M_EV_TS 0x0F // bitmap mask (4bit) #define M1_EV_TS 0x01 #define M_WD_TS 0xF0 // bitmap mask (4bit) #define M1_WD_TS 0x10 typedef struct // bitmap construction { REGWORD v_ev_ts:4; REGWORD v_wd_ts:4; } bit_r_ti_wd; // register and bitmap data typedef union {REGWORD reg; bit_r_ti_wd bit;} reg_r_ti_wd; // register and bitmap access #define R_BERT_ECH 0x1B // register access #define M_BERT_ECH 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_bert_ech:8; } bit_r_bert_ech; // register and bitmap data typedef union {REGWORD reg; bit_r_bert_ech bit;} reg_r_bert_ech; // register and bitmap access #define R_BERT_WD_MD 0x1B // register access #define M_PAT_SEQ 0x07 // bitmap mask (3bit) #define M1_PAT_SEQ 0x01 #define M_BERT_ERR 0x08 // bitmap mask (1bit) #define M_AUTO_WD_RES 0x20 // bitmap mask (1bit) #define M_WD_RES 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_pat_seq:3; REGWORD v_bert_err:1; REGWORD reserved_16:1; REGWORD v_auto_wd_res:1; REGWORD reserved_17:1; REGWORD v_wd_res:1; } bit_r_bert_wd_md; // register and bitmap data typedef union {REGWORD reg; bit_r_bert_wd_md bit;} reg_r_bert_wd_md; // register and bitmap access #define R_STATUS 0x1C // register access #define M_BUSY 0x01 // bitmap mask (1bit) #define M_PROC 0x02 // bitmap mask (1bit) #define M_LOST_STA 0x08 // bitmap mask (1bit) #define M_PCM_INIT 0x10 // bitmap mask (1bit) #define M_WAK_STA 0x20 // bitmap mask (1bit) #define M_MISC_IRQSTA 0x40 // bitmap mask (1bit) #define M_FR_IRQSTA 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_busy:1; REGWORD v_proc:1; REGWORD reserved_23:1; REGWORD v_lost_sta:1; REGWORD v_pcm_init:1; REGWORD v_wak_sta:1; REGWORD v_misc_irqsta:1; REGWORD v_fr_irqsta:1; } bit_r_status; // register and bitmap data typedef union {REGWORD reg; bit_r_status bit;} reg_r_status; // register and bitmap access #define R_SL_MAX 0x1D // register access #define M_SL_MAX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_sl_max:8; } bit_r_sl_max; // register and bitmap data typedef union {REGWORD reg; bit_r_sl_max bit;} reg_r_sl_max; // register and bitmap access #define R_PWM_CFG 0x1E // register access #define M_PWM0_16KHZ 0x10 // bitmap mask (1bit) #define M_PWM1_16KHZ 0x20 // bitmap mask (1bit) #define M_PWM_FRQ 0xC0 // bitmap mask (2bit) #define M1_PWM_FRQ 0x40 typedef struct // bitmap construction { REGWORD reserved_18:4; REGWORD v_pwm0_16khz:1; REGWORD v_pwm1_16khz:1; REGWORD v_pwm_frq:2; } bit_r_pwm_cfg; // register and bitmap data typedef union {REGWORD reg; bit_r_pwm_cfg bit;} reg_r_pwm_cfg; // register and bitmap access #define R_CHIP_RV 0x1F // register access #define M_CHIP_RV 0x0F // bitmap mask (4bit) #define M1_CHIP_RV 0x01 typedef struct // bitmap construction { REGWORD v_chip_rv:4; REGWORD reserved_24:4; } bit_r_chip_rv; // register and bitmap data typedef union {REGWORD reg; bit_r_chip_rv bit;} reg_r_chip_rv; // register and bitmap access #define R_FIFO_BL0_IRQ 0x20 // register access #define M_FIFO0_TX_IRQ 0x01 // bitmap mask (1bit) #define M_FIFO0_RX_IRQ 0x02 // bitmap mask (1bit) #define M_FIFO1_TX_IRQ 0x04 // bitmap mask (1bit) #define M_FIFO1_RX_IRQ 0x08 // bitmap mask (1bit) #define M_FIFO2_TX_IRQ 0x10 // bitmap mask (1bit) #define M_FIFO2_RX_IRQ 0x20 // bitmap mask (1bit) #define M_FIFO3_TX_IRQ 0x40 // bitmap mask (1bit) #define M_FIFO3_RX_IRQ 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo0_tx_irq:1; REGWORD v_fifo0_rx_irq:1; REGWORD v_fifo1_tx_irq:1; REGWORD v_fifo1_rx_irq:1; REGWORD v_fifo2_tx_irq:1; REGWORD v_fifo2_rx_irq:1; REGWORD v_fifo3_tx_irq:1; REGWORD v_fifo3_rx_irq:1; } bit_r_fifo_bl0_irq; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_bl0_irq bit;} reg_r_fifo_bl0_irq; // register and bitmap access #define R_FIFO_BL1_IRQ 0x21 // register access #define M_FIFO4_TX_IRQ 0x01 // bitmap mask (1bit) #define M_FIFO4_RX_IRQ 0x02 // bitmap mask (1bit) #define M_FIFO5_TX_IRQ 0x04 // bitmap mask (1bit) #define M_FIFO5_RX_IRQ 0x08 // bitmap mask (1bit) #define M_FIFO6_TX_IRQ 0x10 // bitmap mask (1bit) #define M_FIFO6_RX_IRQ 0x20 // bitmap mask (1bit) #define M_FIFO7_TX_IRQ 0x40 // bitmap mask (1bit) #define M_FIFO7_RX_IRQ 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo4_tx_irq:1; REGWORD v_fifo4_rx_irq:1; REGWORD v_fifo5_tx_irq:1; REGWORD v_fifo5_rx_irq:1; REGWORD v_fifo6_tx_irq:1; REGWORD v_fifo6_rx_irq:1; REGWORD v_fifo7_tx_irq:1; REGWORD v_fifo7_rx_irq:1; } bit_r_fifo_bl1_irq; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_bl1_irq bit;} reg_r_fifo_bl1_irq; // register and bitmap access #define R_FIFO_BL2_IRQ 0x22 // register access #define M_FIFO8_TX_IRQ 0x01 // bitmap mask (1bit) #define M_FIFO8_RX_IRQ 0x02 // bitmap mask (1bit) #define M_FIFO9_TX_IRQ 0x04 // bitmap mask (1bit) #define M_FIFO9_RX_IRQ 0x08 // bitmap mask (1bit) #define M_FIFO10_TX_IRQ 0x10 // bitmap mask (1bit) #define M_FIFO10_RX_IRQ 0x20 // bitmap mask (1bit) #define M_FIFO11_TX_IRQ 0x40 // bitmap mask (1bit) #define M_FIFO11_RX_IRQ 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo8_tx_irq:1; REGWORD v_fifo8_rx_irq:1; REGWORD v_fifo9_tx_irq:1; REGWORD v_fifo9_rx_irq:1; REGWORD v_fifo10_tx_irq:1; REGWORD v_fifo10_rx_irq:1; REGWORD v_fifo11_tx_irq:1; REGWORD v_fifo11_rx_irq:1; } bit_r_fifo_bl2_irq; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_bl2_irq bit;} reg_r_fifo_bl2_irq; // register and bitmap access #define R_FIFO_BL3_IRQ 0x23 // register access #define M_FIFO12_TX_IRQ 0x01 // bitmap mask (1bit) #define M_FIFO12_RX_IRQ 0x02 // bitmap mask (1bit) #define M_FIFO13_TX_IRQ 0x04 // bitmap mask (1bit) #define M_FIFO13_RX_IRQ 0x08 // bitmap mask (1bit) #define M_FIFO14_TX_IRQ 0x10 // bitmap mask (1bit) #define M_FIFO14_RX_IRQ 0x20 // bitmap mask (1bit) #define M_FIFO15_TX_IRQ 0x40 // bitmap mask (1bit) #define M_FIFO15_RX_IRQ 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo12_tx_irq:1; REGWORD v_fifo12_rx_irq:1; REGWORD v_fifo13_tx_irq:1; REGWORD v_fifo13_rx_irq:1; REGWORD v_fifo14_tx_irq:1; REGWORD v_fifo14_rx_irq:1; REGWORD v_fifo15_tx_irq:1; REGWORD v_fifo15_rx_irq:1; } bit_r_fifo_bl3_irq; // register and bitmap data typedef union {REGWORD reg; bit_r_fifo_bl3_irq bit;} reg_r_fifo_bl3_irq; // register and bitmap access #define R_FILL_BL0 0x24 // register access #define M_FILL_FIFO0_TX 0x01 // bitmap mask (1bit) #define M_FILL_FIFO0_RX 0x02 // bitmap mask (1bit) #define M_FILL_FIFO1_TX 0x04 // bitmap mask (1bit) #define M_FILL_FIFO1_RX 0x08 // bitmap mask (1bit) #define M_FILL_FIFO2_TX 0x10 // bitmap mask (1bit) #define M_FILL_FIFO2_RX 0x20 // bitmap mask (1bit) #define M_FILL_FIFO3_TX 0x40 // bitmap mask (1bit) #define M_FILL_FIFO3_RX 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fill_fifo0_tx:1; REGWORD v_fill_fifo0_rx:1; REGWORD v_fill_fifo1_tx:1; REGWORD v_fill_fifo1_rx:1; REGWORD v_fill_fifo2_tx:1; REGWORD v_fill_fifo2_rx:1; REGWORD v_fill_fifo3_tx:1; REGWORD v_fill_fifo3_rx:1; } bit_r_fill_bl0; // register and bitmap data typedef union {REGWORD reg; bit_r_fill_bl0 bit;} reg_r_fill_bl0; // register and bitmap access #define R_FILL_BL1 0x25 // register access #define M_FILL_FIFO4_TX 0x01 // bitmap mask (1bit) #define M_FILL_FIFO4_RX 0x02 // bitmap mask (1bit) #define M_FILL_FIFO5_TX 0x04 // bitmap mask (1bit) #define M_FILL_FIFO5_RX 0x08 // bitmap mask (1bit) #define M_FILL_FIFO6_TX 0x10 // bitmap mask (1bit) #define M_FILL_FIFO6_RX 0x20 // bitmap mask (1bit) #define M_FILL_FIFO7_TX 0x40 // bitmap mask (1bit) #define M_FILL_FIFO7_RX 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fill_fifo4_tx:1; REGWORD v_fill_fifo4_rx:1; REGWORD v_fill_fifo5_tx:1; REGWORD v_fill_fifo5_rx:1; REGWORD v_fill_fifo6_tx:1; REGWORD v_fill_fifo6_rx:1; REGWORD v_fill_fifo7_tx:1; REGWORD v_fill_fifo7_rx:1; } bit_r_fill_bl1; // register and bitmap data typedef union {REGWORD reg; bit_r_fill_bl1 bit;} reg_r_fill_bl1; // register and bitmap access #define R_FILL_BL2 0x26 // register access #define M_FILL_FIFO8_TX 0x01 // bitmap mask (1bit) #define M_FILL_FIFO8_RX 0x02 // bitmap mask (1bit) #define M_FILL_FIFO9_TX 0x04 // bitmap mask (1bit) #define M_FILL_FIFO9_RX 0x08 // bitmap mask (1bit) #define M_FILL_FIFO10_TX 0x10 // bitmap mask (1bit) #define M_FILL_FIFO10_RX 0x20 // bitmap mask (1bit) #define M_FILL_FIFO11_TX 0x40 // bitmap mask (1bit) #define M_FILL_FIFO11_RX 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fill_fifo8_tx:1; REGWORD v_fill_fifo8_rx:1; REGWORD v_fill_fifo9_tx:1; REGWORD v_fill_fifo9_rx:1; REGWORD v_fill_fifo10_tx:1; REGWORD v_fill_fifo10_rx:1; REGWORD v_fill_fifo11_tx:1; REGWORD v_fill_fifo11_rx:1; } bit_r_fill_bl2; // register and bitmap data typedef union {REGWORD reg; bit_r_fill_bl2 bit;} reg_r_fill_bl2; // register and bitmap access #define R_FILL_BL3 0x27 // register access #define M_FILL_FIFO12_TX 0x01 // bitmap mask (1bit) #define M_FILL_FIFO12_RX 0x02 // bitmap mask (1bit) #define M_FILL_FIFO13_TX 0x04 // bitmap mask (1bit) #define M_FILL_FIFO13_RX 0x08 // bitmap mask (1bit) #define M_FILL_FIFO14_TX 0x10 // bitmap mask (1bit) #define M_FILL_FIFO14_RX 0x20 // bitmap mask (1bit) #define M_FILL_FIFO15_TX 0x40 // bitmap mask (1bit) #define M_FILL_FIFO15_RX 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fill_fifo12_tx:1; REGWORD v_fill_fifo12_rx:1; REGWORD v_fill_fifo13_tx:1; REGWORD v_fill_fifo13_rx:1; REGWORD v_fill_fifo14_tx:1; REGWORD v_fill_fifo14_rx:1; REGWORD v_fill_fifo15_tx:1; REGWORD v_fill_fifo15_rx:1; } bit_r_fill_bl3; // register and bitmap data typedef union {REGWORD reg; bit_r_fill_bl3 bit;} reg_r_fill_bl3; // register and bitmap access #define R_CI_TX 0x28 // register access #define M_GCI_C 0x3F // bitmap mask (6bit) typedef struct // bitmap construction { REGWORD v_gci_c:6; REGWORD reserved_33:2; } bit_r_ci_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_ci_tx bit;} reg_r_ci_tx; // register and bitmap access #define R_CI_RX 0x28 // register access #define M_GCI_I 0x3F // bitmap mask (6bit) typedef struct // bitmap construction { REGWORD v_gci_i:6; REGWORD reserved_35:2; } bit_r_ci_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_ci_rx bit;} reg_r_ci_rx; // register and bitmap access #define R_GCI_CFG0 0x29 // register access #define M_MON_END 0x01 // bitmap mask (1bit) #define M_MON_SLOW 0x02 // bitmap mask (1bit) #define M_MON_DLL 0x04 // bitmap mask (1bit) #define M_MON_CI6 0x08 // bitmap mask (1bit) #define M_GCI_SWAP_TXHS 0x10 // bitmap mask (1bit) #define M_GCI_SWAP_RXHS 0x20 // bitmap mask (1bit) #define M_GCI_SWAP_STIO 0x40 // bitmap mask (1bit) #define M_GCI_EN 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_mon_end:1; REGWORD v_mon_slow:1; REGWORD v_mon_dll:1; REGWORD v_mon_ci6:1; REGWORD v_gci_swap_txhs:1; REGWORD v_gci_swap_rxhs:1; REGWORD v_gci_swap_stio:1; REGWORD v_gci_en:1; } bit_r_gci_cfg0; // register and bitmap data typedef union {REGWORD reg; bit_r_gci_cfg0 bit;} reg_r_gci_cfg0; // register and bitmap access #define R_GCI_STA 0x29 // register access #define M_MON_RXR 0x01 // bitmap mask (1bit) #define M_MON_TXR 0x02 // bitmap mask (1bit) #define M_GCI_MX 0x04 // bitmap mask (1bit) #define M_GCI_MR 0x08 // bitmap mask (1bit) #define M_GCI_RX 0x10 // bitmap mask (1bit) #define M_GCI_ABO 0x20 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_mon_rxr:1; REGWORD v_mon_txr:1; REGWORD v_gci_mx:1; REGWORD v_gci_mr:1; REGWORD v_gci_rx:1; REGWORD v_gci_abo:1; REGWORD reserved_36:2; } bit_r_gci_sta; // register and bitmap data typedef union {REGWORD reg; bit_r_gci_sta bit;} reg_r_gci_sta; // register and bitmap access #define R_GCI_CFG1 0x2A // register access #define M_GCI_SL 0x1F // bitmap mask (5bit) #define M1_GCI_SL 0x01 typedef struct // bitmap construction { REGWORD v_gci_sl:5; REGWORD reserved_34:3; } bit_r_gci_cfg1; // register and bitmap data typedef union {REGWORD reg; bit_r_gci_cfg1 bit;} reg_r_gci_cfg1; // register and bitmap access #define R_MON_RX 0x2A // register access #define M_MON_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_mon_rx:8; } bit_r_mon_rx; // register and bitmap data typedef union {REGWORD reg; bit_r_mon_rx bit;} reg_r_mon_rx; // register and bitmap access #define R_MON_TX 0x2B // register access #define M_MON_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_mon_tx:8; } bit_r_mon_tx; // register and bitmap data typedef union {REGWORD reg; bit_r_mon_tx bit;} reg_r_mon_tx; // register and bitmap access #define A_SU_WR_STA 0x30 // register access #define M_SU_SET_STA 0x0F // bitmap mask (4bit) #define M1_SU_SET_STA 0x01 #define M_SU_LD_STA 0x10 // bitmap mask (1bit) #define M_SU_ACT 0x60 // bitmap mask (2bit) #define M1_SU_ACT 0x20 #define M_SU_SET_G2_G3 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_su_set_sta:4; REGWORD v_su_ld_sta:1; REGWORD v_su_act:2; REGWORD v_su_set_g2_g3:1; } bit_a_su_wr_sta; // register and bitmap data typedef union {REGWORD reg; bit_a_su_wr_sta bit;} reg_a_su_wr_sta; // register and bitmap access #define A_SU_RD_STA 0x30 // register access #define M_SU_STA 0x0F // bitmap mask (4bit) #define M1_SU_STA 0x01 #define M_SU_FR_SYNC 0x10 // bitmap mask (1bit) #define M_SU_T2_EXP 0x20 // bitmap mask (1bit) #define M_SU_INFO0 0x40 // bitmap mask (1bit) #define M_G2_G3 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_su_sta:4; REGWORD v_su_fr_sync:1; REGWORD v_su_t2_exp:1; REGWORD v_su_info0:1; REGWORD v_g2_g3:1; } bit_a_su_rd_sta; // register and bitmap data typedef union {REGWORD reg; bit_a_su_rd_sta bit;} reg_a_su_rd_sta; // register and bitmap access #define A_SU_CTRL0 0x31 // register access #define M_B1_TX_EN 0x01 // bitmap mask (1bit) #define M_B2_TX_EN 0x02 // bitmap mask (1bit) #define M_SU_MD 0x04 // bitmap mask (1bit) #define M_ST_D_LPRIO 0x08 // bitmap mask (1bit) #define M_ST_SQ_EN 0x10 // bitmap mask (1bit) #define M_SU_TST_SIG 0x20 // bitmap mask (1bit) #define M_ST_PU_CTRL 0x40 // bitmap mask (1bit) #define M_SU_STOP 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_b1_tx_en:1; REGWORD v_b2_tx_en:1; REGWORD v_su_md:1; REGWORD v_st_d_lprio:1; REGWORD v_st_sq_en:1; REGWORD v_su_tst_sig:1; REGWORD v_st_pu_ctrl:1; REGWORD v_su_stop:1; } bit_a_su_ctrl0; // register and bitmap data typedef union {REGWORD reg; bit_a_su_ctrl0 bit;} reg_a_su_ctrl0; // register and bitmap access #define A_SU_DLYL 0x31 // register access #define M_SU_DLYL 0x1F // bitmap mask (5bit) #define M1_SU_DLYL 0x01 typedef struct // bitmap construction { REGWORD v_su_dlyl:5; REGWORD reserved_46:3; } bit_a_su_dlyl; // register and bitmap data typedef union {REGWORD reg; bit_a_su_dlyl bit;} reg_a_su_dlyl; // register and bitmap access #define A_SU_CTRL1 0x32 // register access #define M_G2_G3_EN 0x01 // bitmap mask (1bit) #define M_D_RES 0x04 // bitmap mask (1bit) #define M_ST_E_IGNO 0x08 // bitmap mask (1bit) #define M_ST_E_LO 0x10 // bitmap mask (1bit) #define M_BAC_D 0x40 // bitmap mask (1bit) #define M_B12_SWAP 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_g2_g3_en:1; REGWORD reserved_37:1; REGWORD v_d_res:1; REGWORD v_st_e_igno:1; REGWORD v_st_e_lo:1; REGWORD reserved_38:1; REGWORD v_bac_d:1; REGWORD v_b12_swap:1; } bit_a_su_ctrl1; // register and bitmap data typedef union {REGWORD reg; bit_a_su_ctrl1 bit;} reg_a_su_ctrl1; // register and bitmap access #define A_SU_DLYH 0x32 // register access #define M_SU_DLYH 0x1F // bitmap mask (5bit) #define M1_SU_DLYH 0x01 typedef struct // bitmap construction { REGWORD v_su_dlyh:5; REGWORD reserved_47:3; } bit_a_su_dlyh; // register and bitmap data typedef union {REGWORD reg; bit_a_su_dlyh bit;} reg_a_su_dlyh; // register and bitmap access #define A_SU_CTRL2 0x33 // register access #define M_B1_RX_EN 0x01 // bitmap mask (1bit) #define M_B2_RX_EN 0x02 // bitmap mask (1bit) #define M_MS_SSYNC2 0x04 // bitmap mask (1bit) #define M_BAC_S_SEL 0x08 // bitmap mask (1bit) #define M_SU_SYNC_NT 0x10 // bitmap mask (1bit) #define M_SU_2KHZ 0x20 // bitmap mask (1bit) #define M_SU_TRI 0x40 // bitmap mask (1bit) #define M_SU_EXCHG 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_b1_rx_en:1; REGWORD v_b2_rx_en:1; REGWORD v_ms_ssync2:1; REGWORD v_bac_s_sel:1; REGWORD v_su_sync_nt:1; REGWORD v_su_2khz:1; REGWORD v_su_tri:1; REGWORD v_su_exchg:1; } bit_a_su_ctrl2; // register and bitmap data typedef union {REGWORD reg; bit_a_su_ctrl2 bit;} reg_a_su_ctrl2; // register and bitmap access #define A_MS_TX 0x34 // register access #define M_MS_TX 0x0F // bitmap mask (4bit) #define M1_MS_TX 0x01 #define M_UP_S_TX 0x40 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_ms_tx:4; REGWORD reserved_39:2; REGWORD v_up_s_tx:1; REGWORD reserved_40:1; } bit_a_ms_tx; // register and bitmap data typedef union {REGWORD reg; bit_a_ms_tx bit;} reg_a_ms_tx; // register and bitmap access #define A_MS_RX 0x34 // register access #define M_MS_RX 0x0F // bitmap mask (4bit) #define M1_MS_RX 0x01 #define M_MS_RX_RDY 0x10 // bitmap mask (1bit) #define M_UP_S_RX 0x40 // bitmap mask (1bit) #define M_MS_TX_RDY 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_ms_rx:4; REGWORD v_ms_rx_rdy:1; REGWORD reserved_48:1; REGWORD v_up_s_rx:1; REGWORD v_ms_tx_rdy:1; } bit_a_ms_rx; // register and bitmap data typedef union {REGWORD reg; bit_a_ms_rx bit;} reg_a_ms_rx; // register and bitmap access #define A_ST_CTRL3 0x35 // register access #define M_ST_SEL 0x01 // bitmap mask (1bit) #define M_ST_PULSE 0xFE // bitmap mask (7bit) typedef struct // bitmap construction { REGWORD v_st_sel:1; REGWORD v_st_pulse:7; } bit_a_st_ctrl3; // register and bitmap data typedef union {REGWORD reg; bit_a_st_ctrl3 bit;} reg_a_st_ctrl3; // register and bitmap access #define IDX_ST_CTRL3 0x00 // index value selecting this multi-register #define A_UP_CTRL3 0x35 // register access #define M_UP_SEL 0x01 // bitmap mask (1bit) #define M_UP_VIO 0x02 // bitmap mask (1bit) #define M_UP_DC_STR 0x04 // bitmap mask (1bit) #define M_UP_DC_OFF 0x08 // bitmap mask (1bit) #define M_UP_RPT_PAT 0x10 // bitmap mask (1bit) #define M_UP_SCRM_MD 0x20 // bitmap mask (1bit) #define M_UP_SCRM_TX_OFF 0x40 // bitmap mask (1bit) #define M_UP_SCRM_RX_OFF 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_up_sel:1; REGWORD v_up_vio:1; REGWORD v_up_dc_str:1; REGWORD v_up_dc_off:1; REGWORD v_up_rpt_pat:1; REGWORD v_up_scrm_md:1; REGWORD v_up_scrm_tx_off:1; REGWORD v_up_scrm_rx_off:1; } bit_a_up_ctrl3; // register and bitmap data typedef union {REGWORD reg; bit_a_up_ctrl3 bit;} reg_a_up_ctrl3; // register and bitmap access #define IDX_UP_CTRL3 0x01 // index value selecting this multi-register #define A_SU_STA 0x35 // register access #define M_ST_D_HPRIO9 0x01 // bitmap mask (1bit) #define M_ST_D_LPRIO11 0x02 // bitmap mask (1bit) #define M_ST_D_CONT 0x04 // bitmap mask (1bit) #define M_ST_D_ACT 0x08 // bitmap mask (1bit) #define M_SU_AF0 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_st_d_hprio9:1; REGWORD v_st_d_lprio11:1; REGWORD v_st_d_cont:1; REGWORD v_st_d_act:1; REGWORD reserved_49:3; REGWORD v_su_af0:1; } bit_a_su_sta; // register and bitmap data typedef union {REGWORD reg; bit_a_su_sta bit;} reg_a_su_sta; // register and bitmap access #define A_MS_DF 0x36 // register access #define M_BAC_NINV 0x01 // bitmap mask (1bit) #define M_SG_AB_INV 0x02 // bitmap mask (1bit) #define M_SQ_T_SRC 0x04 // bitmap mask (1bit) #define M_M_S_SRC 0x08 // bitmap mask (1bit) #define M_SQ_T_DST 0x10 // bitmap mask (1bit) #define M_SU_RX_VAL 0x20 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_bac_ninv:1; REGWORD v_sg_ab_inv:1; REGWORD v_sq_t_src:1; REGWORD v_m_s_src:1; REGWORD v_sq_t_dst:1; REGWORD v_su_rx_val:1; REGWORD reserved_41:2; } bit_a_ms_df; // register and bitmap data typedef union {REGWORD reg; bit_a_ms_df bit;} reg_a_ms_df; // register and bitmap access #define A_SU_CLK_DLY 0x37 // register access #define M_SU_CLK_DLY 0x0F // bitmap mask (4bit) #define M1_SU_CLK_DLY 0x01 #define M_ST_SMPL 0x70 // bitmap mask (3bit) #define M1_ST_SMPL 0x10 typedef struct // bitmap construction { REGWORD v_su_clk_dly:4; REGWORD v_st_smpl:3; REGWORD reserved_42:1; } bit_a_su_clk_dly; // register and bitmap data typedef union {REGWORD reg; bit_a_su_clk_dly bit;} reg_a_su_clk_dly; // register and bitmap access #define R_PWM0 0x38 // register access #define M_PWM0 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_pwm0:8; } bit_r_pwm0; // register and bitmap data typedef union {REGWORD reg; bit_r_pwm0 bit;} reg_r_pwm0; // register and bitmap access #define R_PWM1 0x39 // register access #define M_PWM1 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_pwm1:8; } bit_r_pwm1; // register and bitmap data typedef union {REGWORD reg; bit_r_pwm1 bit;} reg_r_pwm1; // register and bitmap access #define A_B1_TX 0x3C // register access #define M_B1_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_b1_tx:8; } bit_a_b1_tx; // register and bitmap data typedef union {REGWORD reg; bit_a_b1_tx bit;} reg_a_b1_tx; // register and bitmap access #define A_B1_RX 0x3C // register access #define M_B1_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_b1_rx:8; } bit_a_b1_rx; // register and bitmap data typedef union {REGWORD reg; bit_a_b1_rx bit;} reg_a_b1_rx; // register and bitmap access #define A_B2_TX 0x3D // register access #define M_B2_TX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_b2_tx:8; } bit_a_b2_tx; // register and bitmap data typedef union {REGWORD reg; bit_a_b2_tx bit;} reg_a_b2_tx; // register and bitmap access #define A_B2_RX 0x3D // register access #define M_B2_RX 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_b2_rx:8; } bit_a_b2_rx; // register and bitmap data typedef union {REGWORD reg; bit_a_b2_rx bit;} reg_a_b2_rx; // register and bitmap access #define A_D_TX 0x3E // register access #define M_D_TX_S 0x01 // bitmap mask (1bit) #define M_D_TX_BAC 0x20 // bitmap mask (1bit) #define M_D_TX 0xC0 // bitmap mask (2bit) #define M1_D_TX 0x40 typedef struct // bitmap construction { REGWORD v_d_tx_s:1; REGWORD reserved_43:4; REGWORD v_d_tx_bac:1; REGWORD v_d_tx:2; } bit_a_d_tx; // register and bitmap data typedef union {REGWORD reg; bit_a_d_tx bit;} reg_a_d_tx; // register and bitmap access #define A_D_RX 0x3E // register access #define M_D_RX_S 0x01 // bitmap mask (1bit) #define M_D_RX_AB 0x10 // bitmap mask (1bit) #define M_D_RX_SG 0x20 // bitmap mask (1bit) #define M_D_RX 0xC0 // bitmap mask (2bit) #define M1_D_RX 0x40 typedef struct // bitmap construction { REGWORD v_d_rx_s:1; REGWORD reserved_50:3; REGWORD v_d_rx_ab:1; REGWORD v_d_rx_sg:1; REGWORD v_d_rx:2; } bit_a_d_rx; // register and bitmap data typedef union {REGWORD reg; bit_a_d_rx bit;} reg_a_d_rx; // register and bitmap access #define A_E_RX 0x3F // register access #define M_E_RX_S 0x01 // bitmap mask (1bit) #define M_E_RX_AB 0x10 // bitmap mask (1bit) #define M_E_RX_SG 0x20 // bitmap mask (1bit) #define M_E_RX 0xC0 // bitmap mask (2bit) #define M1_E_RX 0x40 typedef struct // bitmap construction { REGWORD v_e_rx_s:1; REGWORD reserved_51:3; REGWORD v_e_rx_ab:1; REGWORD v_e_rx_sg:1; REGWORD v_e_rx:2; } bit_a_e_rx; // register and bitmap data typedef union {REGWORD reg; bit_a_e_rx bit;} reg_a_e_rx; // register and bitmap access #define A_BAC_S_TX 0x3F // register access #define M_S_TX 0x01 // bitmap mask (1bit) #define M_BAC_TX 0x20 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_s_tx:1; REGWORD reserved_44:4; REGWORD v_bac_tx:1; REGWORD reserved_45:2; } bit_a_bac_s_tx; // register and bitmap data typedef union {REGWORD reg; bit_a_bac_s_tx bit;} reg_a_bac_s_tx; // register and bitmap access #define R_GPIO_OUT1 0x40 // register access #define M_GPIO_OUT8 0x01 // bitmap mask (1bit) #define M_GPIO_OUT9 0x02 // bitmap mask (1bit) #define M_GPIO_OUT10 0x04 // bitmap mask (1bit) #define M_GPIO_OUT11 0x08 // bitmap mask (1bit) #define M_GPIO_OUT12 0x10 // bitmap mask (1bit) #define M_GPIO_OUT13 0x20 // bitmap mask (1bit) #define M_GPIO_OUT14 0x40 // bitmap mask (1bit) #define M_GPIO_OUT15 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_out8:1; REGWORD v_gpio_out9:1; REGWORD v_gpio_out10:1; REGWORD v_gpio_out11:1; REGWORD v_gpio_out12:1; REGWORD v_gpio_out13:1; REGWORD v_gpio_out14:1; REGWORD v_gpio_out15:1; } bit_r_gpio_out1; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_out1 bit;} reg_r_gpio_out1; // register and bitmap access #define R_GPIO_IN1 0x40 // register access #define M_GPIO_IN8 0x01 // bitmap mask (1bit) #define M_GPIO_IN9 0x02 // bitmap mask (1bit) #define M_GPIO_IN10 0x04 // bitmap mask (1bit) #define M_GPIO_IN11 0x08 // bitmap mask (1bit) #define M_GPIO_IN12 0x10 // bitmap mask (1bit) #define M_GPIO_IN13 0x20 // bitmap mask (1bit) #define M_GPIO_IN14 0x40 // bitmap mask (1bit) #define M_GPIO_IN15 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_in8:1; REGWORD v_gpio_in9:1; REGWORD v_gpio_in10:1; REGWORD v_gpio_in11:1; REGWORD v_gpio_in12:1; REGWORD v_gpio_in13:1; REGWORD v_gpio_in14:1; REGWORD v_gpio_in15:1; } bit_r_gpio_in1; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_in1 bit;} reg_r_gpio_in1; // register and bitmap access #define R_GPIO_OUT3 0x41 // register access #define M_GPIO_OUT24 0x01 // bitmap mask (1bit) #define M_GPIO_OUT25 0x02 // bitmap mask (1bit) #define M_GPIO_OUT26 0x04 // bitmap mask (1bit) #define M_GPIO_OUT27 0x08 // bitmap mask (1bit) #define M_GPIO_OUT28 0x10 // bitmap mask (1bit) #define M_GPIO_OUT29 0x20 // bitmap mask (1bit) #define M_GPIO_OUT30 0x40 // bitmap mask (1bit) #define M_GPIO_OUT31 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_out24:1; REGWORD v_gpio_out25:1; REGWORD v_gpio_out26:1; REGWORD v_gpio_out27:1; REGWORD v_gpio_out28:1; REGWORD v_gpio_out29:1; REGWORD v_gpio_out30:1; REGWORD v_gpio_out31:1; } bit_r_gpio_out3; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_out3 bit;} reg_r_gpio_out3; // register and bitmap access #define R_GPIO_IN3 0x41 // register access #define M_GPIO_IN24 0x01 // bitmap mask (1bit) #define M_GPIO_IN25 0x02 // bitmap mask (1bit) #define M_GPIO_IN26 0x04 // bitmap mask (1bit) #define M_GPIO_IN27 0x08 // bitmap mask (1bit) #define M_GPIO_IN28 0x10 // bitmap mask (1bit) #define M_GPIO_IN29 0x20 // bitmap mask (1bit) #define M_GPIO_IN30 0x40 // bitmap mask (1bit) #define M_GPIO_IN31 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_in24:1; REGWORD v_gpio_in25:1; REGWORD v_gpio_in26:1; REGWORD v_gpio_in27:1; REGWORD v_gpio_in28:1; REGWORD v_gpio_in29:1; REGWORD v_gpio_in30:1; REGWORD v_gpio_in31:1; } bit_r_gpio_in3; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_in3 bit;} reg_r_gpio_in3; // register and bitmap access #define R_GPIO_EN1 0x42 // register access #define M_GPIO_EN8 0x01 // bitmap mask (1bit) #define M_GPIO_EN9 0x02 // bitmap mask (1bit) #define M_GPIO_EN10 0x04 // bitmap mask (1bit) #define M_GPIO_EN11 0x08 // bitmap mask (1bit) #define M_GPIO_EN12 0x10 // bitmap mask (1bit) #define M_GPIO_EN13 0x20 // bitmap mask (1bit) #define M_GPIO_EN14 0x40 // bitmap mask (1bit) #define M_GPIO_EN15 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_en8:1; REGWORD v_gpio_en9:1; REGWORD v_gpio_en10:1; REGWORD v_gpio_en11:1; REGWORD v_gpio_en12:1; REGWORD v_gpio_en13:1; REGWORD v_gpio_en14:1; REGWORD v_gpio_en15:1; } bit_r_gpio_en1; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_en1 bit;} reg_r_gpio_en1; // register and bitmap access #define R_GPIO_EN3 0x43 // register access #define M_GPIO_EN24 0x01 // bitmap mask (1bit) #define M_GPIO_EN25 0x02 // bitmap mask (1bit) #define M_GPIO_EN26 0x04 // bitmap mask (1bit) #define M_GPIO_EN27 0x08 // bitmap mask (1bit) #define M_GPIO_EN28 0x10 // bitmap mask (1bit) #define M_GPIO_EN29 0x20 // bitmap mask (1bit) #define M_GPIO_EN30 0x40 // bitmap mask (1bit) #define M_GPIO_EN31 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_en24:1; REGWORD v_gpio_en25:1; REGWORD v_gpio_en26:1; REGWORD v_gpio_en27:1; REGWORD v_gpio_en28:1; REGWORD v_gpio_en29:1; REGWORD v_gpio_en30:1; REGWORD v_gpio_en31:1; } bit_r_gpio_en3; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_en3 bit;} reg_r_gpio_en3; // register and bitmap access #define R_GPIO_SEL_BL 0x44 // register access #define M_GPIO_BL0 0x01 // bitmap mask (1bit) #define M_GPIO_BL1 0x02 // bitmap mask (1bit) #define M_GPIO_BL2 0x04 // bitmap mask (1bit) #define M_GPIO_BL3 0x08 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_bl0:1; REGWORD v_gpio_bl1:1; REGWORD v_gpio_bl2:1; REGWORD v_gpio_bl3:1; REGWORD reserved_54:4; } bit_r_gpio_sel_bl; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_sel_bl bit;} reg_r_gpio_sel_bl; // register and bitmap access #define R_GPIO_OUT2 0x45 // register access #define M_GPIO_OUT16 0x01 // bitmap mask (1bit) #define M_GPIO_OUT17 0x02 // bitmap mask (1bit) #define M_GPIO_OUT18 0x04 // bitmap mask (1bit) #define M_GPIO_OUT19 0x08 // bitmap mask (1bit) #define M_GPIO_OUT20 0x10 // bitmap mask (1bit) #define M_GPIO_OUT21 0x20 // bitmap mask (1bit) #define M_GPIO_OUT22 0x40 // bitmap mask (1bit) #define M_GPIO_OUT23 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_out16:1; REGWORD v_gpio_out17:1; REGWORD v_gpio_out18:1; REGWORD v_gpio_out19:1; REGWORD v_gpio_out20:1; REGWORD v_gpio_out21:1; REGWORD v_gpio_out22:1; REGWORD v_gpio_out23:1; } bit_r_gpio_out2; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_out2 bit;} reg_r_gpio_out2; // register and bitmap access #define R_GPIO_IN2 0x45 // register access #define M_GPIO_IN16 0x01 // bitmap mask (1bit) #define M_GPIO_IN17 0x02 // bitmap mask (1bit) #define M_GPIO_IN18 0x04 // bitmap mask (1bit) #define M_GPIO_IN19 0x08 // bitmap mask (1bit) #define M_GPIO_IN20 0x10 // bitmap mask (1bit) #define M_GPIO_IN21 0x20 // bitmap mask (1bit) #define M_GPIO_IN22 0x40 // bitmap mask (1bit) #define M_GPIO_IN23 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_in16:1; REGWORD v_gpio_in17:1; REGWORD v_gpio_in18:1; REGWORD v_gpio_in19:1; REGWORD v_gpio_in20:1; REGWORD v_gpio_in21:1; REGWORD v_gpio_in22:1; REGWORD v_gpio_in23:1; } bit_r_gpio_in2; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_in2 bit;} reg_r_gpio_in2; // register and bitmap access #define R_PWM_MD 0x46 // register access #define M_WAK_EN 0x02 // bitmap mask (1bit) #define M_PWM0_MD 0x30 // bitmap mask (2bit) #define M1_PWM0_MD 0x10 #define M_PWM1_MD 0xC0 // bitmap mask (2bit) #define M1_PWM1_MD 0x40 typedef struct // bitmap construction { REGWORD reserved_52:1; REGWORD v_wak_en:1; REGWORD reserved_53:2; REGWORD v_pwm0_md:2; REGWORD v_pwm1_md:2; } bit_r_pwm_md; // register and bitmap data typedef union {REGWORD reg; bit_r_pwm_md bit;} reg_r_pwm_md; // register and bitmap access #define R_GPIO_EN2 0x47 // register access #define M_GPIO_EN16 0x01 // bitmap mask (1bit) #define M_GPIO_EN17 0x02 // bitmap mask (1bit) #define M_GPIO_EN18 0x04 // bitmap mask (1bit) #define M_GPIO_EN19 0x08 // bitmap mask (1bit) #define M_GPIO_EN20 0x10 // bitmap mask (1bit) #define M_GPIO_EN21 0x20 // bitmap mask (1bit) #define M_GPIO_EN22 0x40 // bitmap mask (1bit) #define M_GPIO_EN23 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_en16:1; REGWORD v_gpio_en17:1; REGWORD v_gpio_en18:1; REGWORD v_gpio_en19:1; REGWORD v_gpio_en20:1; REGWORD v_gpio_en21:1; REGWORD v_gpio_en22:1; REGWORD v_gpio_en23:1; } bit_r_gpio_en2; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_en2 bit;} reg_r_gpio_en2; // register and bitmap access #define R_GPIO_IN0 0x48 // register access #define M_GPIO_IN0 0x01 // bitmap mask (1bit) #define M_GPIO_IN1 0x02 // bitmap mask (1bit) #define M_GPIO_IN2 0x04 // bitmap mask (1bit) #define M_GPIO_IN3 0x08 // bitmap mask (1bit) #define M_GPIO_IN4 0x10 // bitmap mask (1bit) #define M_GPIO_IN5 0x20 // bitmap mask (1bit) #define M_GPIO_IN6 0x40 // bitmap mask (1bit) #define M_GPIO_IN7 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_in0:1; REGWORD v_gpio_in1:1; REGWORD v_gpio_in2:1; REGWORD v_gpio_in3:1; REGWORD v_gpio_in4:1; REGWORD v_gpio_in5:1; REGWORD v_gpio_in6:1; REGWORD v_gpio_in7:1; } bit_r_gpio_in0; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_in0 bit;} reg_r_gpio_in0; // register and bitmap access #define R_GPIO_OUT0 0x48 // register access #define M_GPIO_OUT0 0x01 // bitmap mask (1bit) #define M_GPIO_OUT1 0x02 // bitmap mask (1bit) #define M_GPIO_OUT2 0x04 // bitmap mask (1bit) #define M_GPIO_OUT3 0x08 // bitmap mask (1bit) #define M_GPIO_OUT4 0x10 // bitmap mask (1bit) #define M_GPIO_OUT5 0x20 // bitmap mask (1bit) #define M_GPIO_OUT6 0x40 // bitmap mask (1bit) #define M_GPIO_OUT7 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_out0:1; REGWORD v_gpio_out1:1; REGWORD v_gpio_out2:1; REGWORD v_gpio_out3:1; REGWORD v_gpio_out4:1; REGWORD v_gpio_out5:1; REGWORD v_gpio_out6:1; REGWORD v_gpio_out7:1; } bit_r_gpio_out0; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_out0 bit;} reg_r_gpio_out0; // register and bitmap access #define R_GPIO_EN0 0x4A // register access #define M_GPIO_EN0 0x01 // bitmap mask (1bit) #define M_GPIO_EN1 0x02 // bitmap mask (1bit) #define M_GPIO_EN2 0x04 // bitmap mask (1bit) #define M_GPIO_EN3 0x08 // bitmap mask (1bit) #define M_GPIO_EN4 0x10 // bitmap mask (1bit) #define M_GPIO_EN5 0x20 // bitmap mask (1bit) #define M_GPIO_EN6 0x40 // bitmap mask (1bit) #define M_GPIO_EN7 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_en0:1; REGWORD v_gpio_en1:1; REGWORD v_gpio_en2:1; REGWORD v_gpio_en3:1; REGWORD v_gpio_en4:1; REGWORD v_gpio_en5:1; REGWORD v_gpio_en6:1; REGWORD v_gpio_en7:1; } bit_r_gpio_en0; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_en0 bit;} reg_r_gpio_en0; // register and bitmap access #define R_GPIO_SEL 0x4C // register access #define M_GPIO_SEL0 0x01 // bitmap mask (1bit) #define M_GPIO_SEL1 0x02 // bitmap mask (1bit) #define M_GPIO_SEL2 0x04 // bitmap mask (1bit) #define M_GPIO_SEL3 0x08 // bitmap mask (1bit) #define M_GPIO_SEL4 0x10 // bitmap mask (1bit) #define M_GPIO_SEL5 0x20 // bitmap mask (1bit) #define M_GPIO_SEL6 0x40 // bitmap mask (1bit) #define M_GPIO_SEL7 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_gpio_sel0:1; REGWORD v_gpio_sel1:1; REGWORD v_gpio_sel2:1; REGWORD v_gpio_sel3:1; REGWORD v_gpio_sel4:1; REGWORD v_gpio_sel5:1; REGWORD v_gpio_sel6:1; REGWORD v_gpio_sel7:1; } bit_r_gpio_sel; // register and bitmap data typedef union {REGWORD reg; bit_r_gpio_sel bit;} reg_r_gpio_sel; // register and bitmap access #define R_PLL_STA 0x50 // register access #define M_PLL_LOCK 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD reserved_56:7; REGWORD v_pll_lock:1; } bit_r_pll_sta; // register and bitmap data typedef union {REGWORD reg; bit_r_pll_sta bit;} reg_r_pll_sta; // register and bitmap access #define R_PLL_CTRL 0x50 // register access #define M_PLL_NRES 0x01 // bitmap mask (1bit) #define M_PLL_TST 0x02 // bitmap mask (1bit) #define M_PLL_FREEZE 0x20 // bitmap mask (1bit) #define M_PLL_M 0xC0 // bitmap mask (2bit) #define M1_PLL_M 0x40 typedef struct // bitmap construction { REGWORD v_pll_nres:1; REGWORD v_pll_tst:1; REGWORD reserved_55:3; REGWORD v_pll_freeze:1; REGWORD v_pll_m:2; } bit_r_pll_ctrl; // register and bitmap data typedef union {REGWORD reg; bit_r_pll_ctrl bit;} reg_r_pll_ctrl; // register and bitmap access #define R_PLL_P 0x51 // register access #define M_PLL_P 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_pll_p:8; } bit_r_pll_p; // register and bitmap data typedef union {REGWORD reg; bit_r_pll_p bit;} reg_r_pll_p; // register and bitmap access #define R_PLL_N 0x52 // register access #define M_PLL_N 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_pll_n:8; } bit_r_pll_n; // register and bitmap data typedef union {REGWORD reg; bit_r_pll_n bit;} reg_r_pll_n; // register and bitmap access #define R_PLL_S 0x53 // register access #define M_PLL_S 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_pll_s:8; } bit_r_pll_s; // register and bitmap data typedef union {REGWORD reg; bit_r_pll_s bit;} reg_r_pll_s; // register and bitmap access #define A_FIFO_DATA 0x80 // register access #define M_FIFO_DATA 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_fifo_data:8; } bit_a_fifo_data; // register and bitmap data typedef union {REGWORD reg; bit_a_fifo_data bit;} reg_a_fifo_data; // register and bitmap access #define A_FIFO_DATA_NOINC 0x84 // register access #define M_FIFO_DATA_NOINC 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_fifo_data_noinc:8; } bit_a_fifo_data_noinc; // register and bitmap data typedef union {REGWORD reg; bit_a_fifo_data_noinc bit;} reg_a_fifo_data_noinc; // register and bitmap access #define R_INT_DATA 0x88 // register access #define M_INT_DATA 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_int_data:8; } bit_r_int_data; // register and bitmap data typedef union {REGWORD reg; bit_r_int_data bit;} reg_r_int_data; // register and bitmap access #define R_RAM_DATA 0xC0 // register access #define M_RAM_DATA 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_ram_data:8; } bit_r_ram_data; // register and bitmap data typedef union {REGWORD reg; bit_r_ram_data bit;} reg_r_ram_data; // register and bitmap access #define A_SL_CFG 0xD0 // register access #define M_CH_SDIR 0x01 // bitmap mask (1bit) #define M_CH_SNUM 0x3E // bitmap mask (5bit) #define M1_CH_SNUM 0x02 #define M_ROUT 0xC0 // bitmap mask (2bit) #define M1_ROUT 0x40 typedef struct // bitmap construction { REGWORD v_ch_sdir:1; REGWORD v_ch_snum:5; REGWORD v_rout:2; } bit_a_sl_cfg; // register and bitmap data typedef union {REGWORD reg; bit_a_sl_cfg bit;} reg_a_sl_cfg; // register and bitmap access #define A_CH_MSK 0xF4 // register access #define M_CH_MSK 0xFF // bitmap mask (8bit) typedef struct // bitmap construction { REGWORD v_ch_msk:8; } bit_a_ch_msk; // register and bitmap data typedef union {REGWORD reg; bit_a_ch_msk bit;} reg_a_ch_msk; // register and bitmap access #define A_CON_HDLC 0xFA // register access #define M_IFF 0x01 // bitmap mask (1bit) #define M_HDLC_TRP 0x02 // bitmap mask (1bit) #define M_FIFO_IRQ 0x1C // bitmap mask (3bit) #define M1_FIFO_IRQ 0x04 #define M_DATA_FLOW 0xE0 // bitmap mask (3bit) #define M1_DATA_FLOW 0x20 typedef struct // bitmap construction { REGWORD v_iff:1; REGWORD v_hdlc_trp:1; REGWORD v_fifo_irq:3; REGWORD v_data_flow:3; } bit_a_con_hdlc; // register and bitmap data typedef union {REGWORD reg; bit_a_con_hdlc bit;} reg_a_con_hdlc; // register and bitmap access #define A_SUBCH_CFG 0xFB // register access #define M_BIT_CNT 0x07 // bitmap mask (3bit) #define M1_BIT_CNT 0x01 #define M_START_BIT 0x38 // bitmap mask (3bit) #define M1_START_BIT 0x08 #define M_LOOP_FIFO 0x40 // bitmap mask (1bit) #define M_INV_DATA 0x80 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_bit_cnt:3; REGWORD v_start_bit:3; REGWORD v_loop_fifo:1; REGWORD v_inv_data:1; } bit_a_subch_cfg; // register and bitmap data typedef union {REGWORD reg; bit_a_subch_cfg bit;} reg_a_subch_cfg; // register and bitmap access #define A_CHANNEL 0xFC // register access #define M_CH_FDIR 0x01 // bitmap mask (1bit) #define M_CH_FNUM 0x1E // bitmap mask (4bit) #define M1_CH_FNUM 0x02 typedef struct // bitmap construction { REGWORD v_ch_fdir:1; REGWORD v_ch_fnum:4; REGWORD reserved_57:3; } bit_a_channel; // register and bitmap data typedef union {REGWORD reg; bit_a_channel bit;} reg_a_channel; // register and bitmap access #define A_FIFO_SEQ 0xFD // register access #define M_NEXT_FIFO_DIR 0x01 // bitmap mask (1bit) #define M_NEXT_FIFO_NUM 0x1E // bitmap mask (4bit) #define M1_NEXT_FIFO_NUM 0x02 #define M_SEQ_END 0x40 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_next_fifo_dir:1; REGWORD v_next_fifo_num:4; REGWORD reserved_58:1; REGWORD v_seq_end:1; REGWORD reserved_59:1; } bit_a_fifo_seq; // register and bitmap data typedef union {REGWORD reg; bit_a_fifo_seq bit;} reg_a_fifo_seq; // register and bitmap access #define A_FIFO_CTRL 0xFF // register access #define M_FIFO_IRQMSK 0x01 // bitmap mask (1bit) #define M_BERT_EN 0x02 // bitmap mask (1bit) #define M_MIX_IRQ 0x04 // bitmap mask (1bit) #define M_FR_ABO 0x08 // bitmap mask (1bit) #define M_NO_CRC 0x10 // bitmap mask (1bit) #define M_NO_REP 0x20 // bitmap mask (1bit) typedef struct // bitmap construction { REGWORD v_fifo_irqmsk:1; REGWORD v_bert_en:1; REGWORD v_mix_irq:1; REGWORD v_fr_abo:1; REGWORD v_no_crc:1; REGWORD v_no_rep:1; REGWORD reserved_60:2; } bit_a_fifo_ctrl; // register and bitmap data typedef union {REGWORD reg; bit_a_fifo_ctrl bit;} reg_a_fifo_ctrl; // register and bitmap access #endif /* _XHFC24SUCC_H_ */ /*___________________________________________________________________________________*/ /* */ /* End of XHFC-2S4U / XHFC-4SU register definitions. */ /* */ /* Total number of registers processed: 122 of 122 */ /* Total number of bitmaps processed : 523 */ /* */ /*___________________________________________________________________________________*/ /* */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/xhfc_pci2pi.c0000644000000000000500000000764411135651702020744 0ustar rootsrc/* $Id: xhfc_pci2pi.c,v 1.5 2007/02/13 10:43:45 crich Exp $ * * PCI2PI Pci Bridge support for xhfc_su.c * * Authors : Martin Bachem, Joerg Ciesielski * Contact : info@colognechip.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include "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, }; /* base addr to address several XHFCs on one PCI2PI bridge */ __u32 PCI2PI_XHFC_OFFSETS[PCI2PI_MAX_XHFC] = {0, 0x400}; /***********************************/ /* initialise the XHFC PCI Bridge */ /* return 0 on success. */ /***********************************/ int init_pci_bridge(xhfc_pi * pi) { int err = -ENODEV; printk(KERN_INFO "%s %s: using PCI2PI Bridge at 0x%p\n", pi->name, __FUNCTION__, pi->hw_membase); /* test if Bridge regsiter accessable */ WritePCI2PI_u32(pi, PCI2PI_DEL_CS, 0x0); if (ReadPCI2PI_u32(pi, PCI2PI_DEL_CS) == 0x00) { WritePCI2PI_u32(pi, PCI2PI_DEL_CS, 0xFFFFFFFF); if (ReadPCI2PI_u32(pi, PCI2PI_DEL_CS) == 0xF) { err = 0; } } if (err) return (err); /* enable hardware reset XHFC */ WritePCI2PI_u32(pi, PCI2PI_GPIO_OUT, GPIO_OUT_VAL); WritePCI2PI_u32(pi, PCI2PI_PI_MODE, PCI2PI_config.pi_mode); WritePCI2PI_u32(pi, PCI2PI_DEL_CS, PCI2PI_config.del_cs); WritePCI2PI_u32(pi, PCI2PI_DEL_RD, PCI2PI_config.del_rd); WritePCI2PI_u32(pi, PCI2PI_DEL_WR, PCI2PI_config.del_wr); WritePCI2PI_u32(pi, PCI2PI_DEL_ALE, PCI2PI_config.del_ale); WritePCI2PI_u32(pi, PCI2PI_DEL_ADR, PCI2PI_config.del_adr); WritePCI2PI_u32(pi, PCI2PI_DEL_DOUT, PCI2PI_config.del_dout); WritePCI2PI_u32(pi, PCI2PI_DEFAULT_ADR, PCI2PI_config.default_adr); WritePCI2PI_u32(pi, PCI2PI_DEFAULT_DOUT, PCI2PI_config.default_dout); WritePCI2PI_u32(pi, PCI2PI_CYCLE_SHD, 0x80 * PCI2PI_config.setup + 0x40 * PCI2PI_config.hold + PCI2PI_config.cycle); WritePCI2PI_u32(pi, 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(pi, PCI2PI_CYCLE_PAUSE, 0x10 * PCI2PI_config.pause_seq + PCI2PI_config.pause_end); WritePCI2PI_u32(pi, PCI2PI_STATUS_INT_ENABLE, PCI2PI_config.status_int_enable); WritePCI2PI_u32(pi, PCI2PI_PI_INT_POL, 2 * PCI2PI_config.pi_wait_enable + PCI2PI_config.pi_int_pol); WritePCI2PI_u32(pi, PCI2PI_SPI_CFG0, PCI2PI_config.spi_cfg0); WritePCI2PI_u32(pi, PCI2PI_SPI_CFG1, PCI2PI_config.spi_cfg1); WritePCI2PI_u32(pi, PCI2PI_SPI_CFG2, PCI2PI_config.spi_cfg2); WritePCI2PI_u32(pi, PCI2PI_SPI_CFG3, PCI2PI_config.spi_cfg3); WritePCI2PI_u32(pi, PCI2PI_EEP_RECOVER, PCI2PI_config.eep_recover); ReadPCI2PI_u32(pi, PCI2PI_STATUS); /* release hardware reset XHFC */ WritePCI2PI_u32(pi, PCI2PI_GPIO_OUT, GPIO_OUT_VAL | PCI2PI_GPIO7_NRST); udelay(10); return (err); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/xhfc_pci2pi.h0000644000000000000500000005003511135651702020741 0ustar rootsrc/* $Id: xhfc_pci2pi.h,v 1.4 2006/03/22 23:29:59 keil Exp $ * * 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 /* membase offset to address XHFC controllers */ #define PCI2PI_MAX_XHFC 2 extern __u32 PCI2PI_XHFC_OFFSETS[PCI2PI_MAX_XHFC]; /*******************************************************/ /*******************************************************/ /* 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_pi * pi, __u16 reg_addr) { return (*(volatile __u8 *) (pi->membase + reg_addr)); } static inline __u16 ReadPCI2PI_u16(xhfc_pi * pi, __u16 reg_addr) { return (*(volatile __u16 *) (pi->membase + reg_addr)); } static inline __u32 ReadPCI2PI_u32(xhfc_pi * pi, __u16 reg_addr) { return (*(volatile __u32 *) (pi->membase + reg_addr)); } static inline void WritePCI2PI_u8(xhfc_pi * pi, __u16 reg_addr, __u8 value) { *((volatile __u8 *) (pi->membase + reg_addr)) = value; } static inline void WritePCI2PI_u16(xhfc_pi * pi, __u16 reg_addr, __u16 value) { *((volatile __u16 *) (pi->membase + reg_addr)) = value; } static inline void WritePCI2PI_u32(xhfc_pi * pi, __u16 reg_addr, __u32 value) { *((volatile __u32 *) (pi->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_t * xhfc, __u8 reg_addr) { return (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (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_t * xhfc, __u8 reg_addr) { __u32 value; value = (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (reg_addr << 2))); value |= (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (reg_addr << 2))) << 8; value |= (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (reg_addr << 2))) << 16; value |= (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (reg_addr << 2))) << 24; return (value); } static inline void write_xhfc(xhfc_t * xhfc, __u8 reg_addr, __u8 value) { *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (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_t * xhfc, __u8 reg_addr, __u32 value) { *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (reg_addr << 2))) = value & 0xff; *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (reg_addr << 2))) = (value >>8) & 0xff; *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (reg_addr << 2))) = (value >>16) & 0xff; *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (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_t * xhfc, __u8 reg_addr) { (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (reg_addr << 2))); return (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + (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_t * xhfc) { 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_t * xhfc, __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_t * xhfc, __u8 reg_addr) { *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + 4)) = reg_addr; return (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx])); } /* 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_t * xhfc, __u8 reg_addr) { *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + 4)) = reg_addr; return (*(volatile __u32 *) xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx]); } static inline void write_xhfc(xhfc_t * xhfc, __u8 reg_addr, __u8 value) { *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + 4)) = reg_addr; *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx])) = 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_t * xhfc, __u8 reg_addr, __u32 value) { *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + 4)) = reg_addr; *((volatile __u32 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx])) = 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_t * xhfc, __u8 reg_addr) { *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + 4)) = reg_addr; (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] )); *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + 4)) = R_INT_DATA; return (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx])); } /* this function reads the currently selected regsiter from XHFC */ static inline __u8 read_xhfcregptr(xhfc_t * xhfc) { return (*(volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + 4)); } /* this function writes the XHFC register address pointer */ static inline void write_xhfcregptr(xhfc_t * xhfc, __u8 reg_addr) { *((volatile __u8 *) (xhfc->pi->membase + PCI2PI_XHFC_OFFSETS[xhfc->chipidx] + 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_t * xhfc, __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 | xhfc->chipidx) << 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_t * xhfc, __u8 reg_addr) { // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 16 clock SPI master transfer WritePCI2PI_u16(xhfc->pi, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR | xhfc->chipidx) << 8) | reg_addr); // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 8 clock SPI master transfer WritePCI2PI_u8(xhfc->pi, PCI2PI_SPI_MO_DATA, (SPI_DATA | SPI_RD | SPI_MULTI)); // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 32 clock SPI master transfer // output data is arbitrary WritePCI2PI_u32(xhfc->pi, PCI2PI_SPI_MO_DATA, 0); while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // read data from the SPI data receive register and return four bytes return (__u32) be32_to_cpu(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_MI_DATA)); } static inline void write_xhfc(xhfc_t * xhfc, __u8 reg_addr, __u8 value) { // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 32 clock SPI master transfer WritePCI2PI_u32(xhfc->pi, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR | xhfc->chipidx) << 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_t * xhfc, __u8 reg_addr, __u32 value) { // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 16 clock SPI master transfer WritePCI2PI_u16(xhfc->pi, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR | xhfc->chipidx) << 8) | reg_addr); // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 8 clock SPI master transfer WritePCI2PI_u8(xhfc->pi, PCI2PI_SPI_MO_DATA, (SPI_DATA | SPI_WR | SPI_MULTI)); // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 32 clock SPI master transfer WritePCI2PI_u32(xhfc->pi, 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_t * xhfc, __u8 reg_addr) { // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 32 clock SPI master transfer WritePCI2PI_u32(xhfc->pi, PCI2PI_SPI_MO_DATA ,((SPI_ADDR | SPI_WR | xhfc->chipidx) << 24) | (reg_addr << 16) | ((SPI_DATA | SPI_RD) << 8)); // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 32 clock SPI master transfer to read R_INT_DATA register WritePCI2PI_u32(xhfc->pi, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR | xhfc->chipidx) << 24) | (R_INT_DATA << 16) | ((SPI_DATA | SPI_RD) << 8)); // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // read data from the SPI data receive register and return one byte return (__u8) (ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_MI_DATA) & 0xFF); } /* this function reads the currently selected regsiter from XHFC */ static inline __u8 read_xhfcregptr(xhfc_t * xhfc) { // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 16 clock SPI master transfer WritePCI2PI_u16(xhfc->pi, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_RD | xhfc->chipidx) << 8)); // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // read data from the SPI data receive register and return one byte return (__u8) (ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_MI_DATA) & 0xFF); } /* this function writes the XHFC register address pointer */ static inline void write_xhfcregptr(xhfc_t * xhfc, __u8 reg_addr) { // wait until SPI master is idle while (!(ReadPCI2PI_u32(xhfc->pi, PCI2PI_SPI_STATUS) & 1)); // initiate a 16 clock SPI master transfer WritePCI2PI_u16(xhfc->pi, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR | xhfc->chipidx) << 8) | reg_addr); } #endif /* PI_MODE == PI_SPI */ /* Function Prototypes */ int init_pci_bridge(xhfc_pi * pi); #endif /* _XHFC_PCI2PI_H_ */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/xhfc_su.c0000644000000000000500000016407211135651702020204 0ustar rootsrc/* xhfc_su.c * mISDN driver for CologneChip AG's XHFC * * Authors : Martin Bachem, Joerg Ciesielski * Contact : info@colognechip.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ******************************************************************************* * * MODULE PARAMETERS: * (NOTE: layermask and protocol must be given for all ports, * not for the number of cards.) * * - protocol=[,p2,p3...] * Values: * D-channel protocol id * Flags for special features * * D-channel protocol ids * - 1 1TR6 (not released yet) * - 2 DSS1 * * Flags for special features * 0x0010 Net side stack (NT mode) * 0x0020 Line Interface Mode (0=S0, 1=Up) * 0x0040 st line polarity (1=exchanged) * 0x0080 B1 channel loop ST-RX -> XHFC PCM -> ST-TX * 0x0100 B2 channel loop ST-RX -> XHFC PCM -> ST-TX * 0x0200 D channel loop ST-RX -> XHFC PCM -> ST-TX * * - layermask=[,l2,l3...] (32bit): * mask of layers to be used for D-channel stack * * - debug: * enable debugging (see xhfc_su.h for debug options) * */ #include #include #include #include #include #include "core.h" #include "helper.h" #include "debug.h" #include "xhfc_su.h" #include "xhfc24succ.h" #if BRIDGE == BRIDGE_PCI2PI #include "xhfc_pci2pi.h" #endif static const char xhfc_rev[] = "1.22 - 10.05.2007"; #define MAX_CARDS 8 static int card_cnt; static u_int protocol[MAX_CARDS * MAX_PORT]; static int layermask[MAX_CARDS * MAX_PORT]; static mISDNobject_t hw_mISDNObj; static int debug = 0; #ifdef MODULE #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #define MODULE_PARM_T "1-4i" MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #ifdef OLD_MODULE_PARAM_ARRAY static int num_protocol=0, num_layermask=0; module_param_array(protocol, uint, num_protocol, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, num_layermask, S_IRUGO | S_IWUSR); #else module_param_array(protocol, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(layermask, uint, NULL, S_IRUGO | S_IWUSR); #endif #endif #endif /* static function prototypes */ static void release_card(xhfc_pi * pi); static void setup_fifo(xhfc_t * xhfc, __u8 fifo, __u8 conhdlc, __u8 subcfg, __u8 fifoctrl, __u8 enable); static void setup_su(xhfc_t * xhfc, __u8 pt, __u8 bc, __u8 enable); static int setup_channel(xhfc_t * xhfc, __u8 channel, int protocol); /****************************************************/ /* Physical S/U commands to control Line Interface */ /****************************************************/ static char *HFC_PH_COMMANDS[] = { "HFC_L1_ACTIVATE_TE", "HFC_L1_FORCE_DEACTIVATE_TE", "HFC_L1_ACTIVATE_NT", "HFC_L1_DEACTIVATE_NT", "HFC_L1_TESTLOOP_B1", "HFC_L1_TESTLOOP_B2", "HFC_L1_TESTLOOP_D" }; static void xhfc_ph_command(xhfc_port_t * port, u_char command) { xhfc_t * xhfc = port->xhfc; if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s: %s (%i)\n", __FUNCTION__, port->name, HFC_PH_COMMANDS[command], command); switch (command) { case HFC_L1_ACTIVATE_TE: write_xhfc(xhfc, R_SU_SEL, port->idx); write_xhfc(xhfc, A_SU_WR_STA, STA_ACTIVATE); break; case HFC_L1_FORCE_DEACTIVATE_TE: write_xhfc(xhfc, R_SU_SEL, port->idx); write_xhfc(xhfc, A_SU_WR_STA, STA_DEACTIVATE); break; case HFC_L1_ACTIVATE_NT: write_xhfc(xhfc, R_SU_SEL, port->idx); write_xhfc(xhfc, A_SU_WR_STA, STA_ACTIVATE | M_SU_SET_G2_G3); break; case HFC_L1_DEACTIVATE_NT: write_xhfc(xhfc, R_SU_SEL, port->idx); write_xhfc(xhfc, A_SU_WR_STA, STA_DEACTIVATE); break; case HFC_L1_TESTLOOP_B1: setup_fifo(xhfc, port->idx*8, 0xC6, 0, 0, 0); /* connect B1-SU RX with PCM TX */ setup_fifo(xhfc, port->idx*8+1, 0xC6, 0, 0, 0); /* connect B1-SU TX with PCM RX */ write_xhfc(xhfc, R_SLOT, port->idx*8); /* PCM timeslot B1 TX */ write_xhfc(xhfc, A_SL_CFG, port->idx*8 + 0x80); /* enable B1 TX timeslot on STIO1 */ write_xhfc(xhfc, R_SLOT, port->idx*8+1); /* PCM timeslot B1 RX */ write_xhfc(xhfc, A_SL_CFG, port->idx*8+1 + 0xC0); /* enable B1 RX timeslot on STIO1*/ setup_su(xhfc, port->idx, 0, 1); break; case HFC_L1_TESTLOOP_B2: setup_fifo(xhfc, port->idx*8+2, 0xC6, 0, 0, 0); /* connect B2-SU RX with PCM TX */ setup_fifo(xhfc, port->idx*8+3, 0xC6, 0, 0, 0); /* connect B2-SU TX with PCM RX */ write_xhfc(xhfc, R_SLOT, port->idx*8+2); /* PCM timeslot B2 TX */ write_xhfc(xhfc, A_SL_CFG, port->idx*8+2 + 0x80); /* enable B2 TX timeslot on STIO1 */ write_xhfc(xhfc, R_SLOT, port->idx*8+3); /* PCM timeslot B2 RX */ write_xhfc(xhfc, A_SL_CFG, port->idx*8+3 + 0xC0); /* enable B2 RX timeslot on STIO1*/ setup_su(xhfc, port->idx, 1, 1); break; case HFC_L1_TESTLOOP_D: setup_fifo(xhfc, port->idx*8+4, 0xC4, 2, M_FR_ABO, 1); /* connect D-SU RX with PCM TX */ setup_fifo(xhfc, port->idx*8+5, 0xC4, 2, M_FR_ABO | M_FIFO_IRQMSK, 1); /* connect D-SU TX with PCM RX */ write_xhfc(xhfc, R_SLOT, port->idx*8+4); /* PCM timeslot D TX */ write_xhfc(xhfc, A_SL_CFG, port->idx*8 + 4 + 0x80); /* enable D TX timeslot on STIO1 */ write_xhfc(xhfc, R_SLOT, port->idx*8 + 5); /* PCM timeslot D RX */ write_xhfc(xhfc, A_SL_CFG, port->idx*8 + 5 + 0xC0); /* enable D RX timeslot on STIO1*/ break; } } static void l1_timer_start_t3(xhfc_port_t * port) { if (!timer_pending(&port->t3_timer)) { if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s\n", __FUNCTION__, port->name); port->t3_timer.expires = jiffies + (XHFC_TIMER_T3 * HZ) / 1000; add_timer(&port->t3_timer); } } static void l1_timer_stop_t3(xhfc_port_t * port) { clear_bit(HFC_L1_ACTIVATING, &port->l1_flags); if (timer_pending(&port->t3_timer)) { if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s\n", __FUNCTION__, port->name); del_timer(&port->t3_timer); } } /***********************************/ /* called when timer t3 expires */ /* -> activation failed */ /* force clean L1 deactivation */ /***********************************/ static void l1_timer_expire_t3(xhfc_port_t * port) { channel_t * dch = &port->xhfc->chan[port->idx * 4 + 2].ch; if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s\n", __FUNCTION__, port->name); clear_bit(HFC_L1_ACTIVATING, &port->l1_flags), xhfc_ph_command(port, HFC_L1_FORCE_DEACTIVATE_TE); mISDN_queue_data(&dch->inst, FLG_MSG_UP, (PH_DEACTIVATE | INDICATION), 0, 0, NULL, 0); mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_DEACTIVATED, 0, NULL, 0); } static void l1_timer_start_t4(xhfc_port_t * port) { set_bit(HFC_L1_DEACTTIMER, &port->l1_flags); if (!timer_pending(&port->t4_timer)) { if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s\n", __FUNCTION__, port->name); port->t4_timer.expires = jiffies + (XHFC_TIMER_T4 * HZ) / 1000; add_timer(&port->t4_timer); } } static void l1_timer_stop_t4(xhfc_port_t * port) { clear_bit(HFC_L1_DEACTTIMER, &port->l1_flags); if (timer_pending(&port->t4_timer)) { if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s\n", __FUNCTION__, port->name); del_timer(&port->t4_timer); } } /*****************************************************/ /* called when timer t4 expires */ /* send (PH_DEACTIVATE | INDICATION) to upper layer */ /*****************************************************/ static void l1_timer_expire_t4(xhfc_port_t * port) { channel_t * dch = &port->xhfc->chan[port->idx * 4 + 2].ch; if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s\n", __FUNCTION__, port->name); clear_bit(HFC_L1_DEACTTIMER, &port->l1_flags); mISDN_queue_data(&dch->inst, FLG_MSG_UP, (PH_DEACTIVATE | INDICATION), 0, 0, NULL, 0); mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_DEACTIVATED, 0, NULL, 0); } /*********************************/ /* Line Interface State handler */ /*********************************/ static void su_new_state(xhfc_port_t * port) { channel_t * dch = &port->xhfc->chan[port->idx * 4 + 2].ch; xhfc_t * xhfc = port->xhfc; u_int prim = 0; if (port->mode & PORT_MODE_TE) { if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s: TE F%d\n", __FUNCTION__, port->name, dch->state); if ((dch->state <= 3) || (dch->state >= 7)) l1_timer_stop_t3(port); switch (dch->state) { case (3): if (test_and_clear_bit(HFC_L1_ACTIVATED, &port->l1_flags)) l1_timer_start_t4(port); return; case (7): if (timer_pending(&port->t4_timer)) l1_timer_stop_t4(port); if (test_and_clear_bit(HFC_L1_ACTIVATING, &port->l1_flags)) { if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s: l1->l2 (PH_ACTIVATE | CONFIRM)\n", __FUNCTION__, port->name); set_bit(HFC_L1_ACTIVATED, &port->l1_flags); prim = PH_ACTIVATE | CONFIRM; } else { if (!(test_and_set_bit(HFC_L1_ACTIVATED, &port->l1_flags))) { if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s: l1->l2 (PH_ACTIVATE | INDICATION)\n", __FUNCTION__, port->name); prim = PH_ACTIVATE | INDICATION; } else { // L1 was already activated (e.g. F8->F7) return; } } mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, MGR_SHORTSTATUS | INDICATION, SSTATUS_L1_ACTIVATED, 0, NULL, 0); break; case (8): l1_timer_stop_t4(port); return; default: return; } } else if (port->mode & PORT_MODE_NT) { if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s: NT G%d\n", __FUNCTION__, port->name, dch->state); switch (dch->state) { case (1): clear_bit(FLG_ACTIVE, &dch->Flags); port->nt_timer = 0; port->mode &= ~NT_TIMER; prim = (PH_DEACTIVATE | INDICATION); if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s: l1->l2 (PH_DEACTIVATE | INDICATION)\n", __FUNCTION__, port->name); break; case (2): if (port->nt_timer < 0) { port->nt_timer = 0; port->mode &= ~NT_TIMER; xhfc_ph_command(port, HFC_L1_DEACTIVATE_NT); } else { port->nt_timer = NT_T1_COUNT; port->mode |= NT_TIMER; write_xhfc(xhfc, R_SU_SEL, port->idx); write_xhfc(xhfc, A_SU_WR_STA, M_SU_SET_G2_G3); } return; case (3): set_bit(FLG_ACTIVE, &dch->Flags); port->nt_timer = 0; port->mode &= ~NT_TIMER; prim = (PH_ACTIVATE | INDICATION); if (debug & DEBUG_HFC_S0_STATES) printk(KERN_INFO "%s %s: l1->l2 (PH_ACTIVATE | INDICATION)\n", __FUNCTION__, port->name); break; case (4): port->nt_timer = 0; port->mode &= ~NT_TIMER; return; default: break; } mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST, MGR_SHORTSTATUS | INDICATION, test_bit(FLG_ACTIVE, &dch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED, 0, NULL, 0); } mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, 0, 0, NULL, 0); } /*************************************/ /* Layer 1 D-channel hardware access */ /*************************************/ static int handle_dmsg(channel_t *dch, struct sk_buff *skb) { xhfc_t * xhfc = dch->hw; xhfc_port_t * port = xhfc->chan[dch->channel].port; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); switch (hh->prim) { case (PH_ACTIVATE | REQUEST): if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "l2->l1 (PH_ACTIVATE | REQUEST)"); if (port->mode & PORT_MODE_TE) { if (test_bit(HFC_L1_ACTIVATED, &port->l1_flags)) { if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "l1->l2 (PH_ACTIVATE | CONFIRM)"); mISDN_queue_data(&dch->inst, FLG_MSG_UP, PH_ACTIVATE | CONFIRM, 0, 0, NULL, 0); } else { test_and_set_bit(HFC_L1_ACTIVATING, &port->l1_flags); xhfc_ph_command(port, HFC_L1_ACTIVATE_TE); l1_timer_start_t3(port); } } else { if (dch->state == 3) { mISDN_queue_data(&dch->inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } else { xhfc_ph_command(port, HFC_L1_ACTIVATE_NT); } } break; case (PH_DEACTIVATE | REQUEST): if (port->mode & PORT_MODE_TE) { // no deact request in TE mode ! ret = -EINVAL; } else { xhfc_ph_command(port, HFC_L1_DEACTIVATE_NT); } break; case (MDL_FINDTEI | REQUEST): return(mISDN_queue_up(&dch->inst, 0, skb)); break; } return(ret); } /*************************************/ /* Layer 1 B-channel hardware access */ /*************************************/ static int handle_bmsg(channel_t *bch, struct sk_buff *skb) { xhfc_t *xhfc = bch->hw; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == (PH_ACTIVATE | REQUEST)) || (hh->prim == (DL_ESTABLISH | REQUEST))) { if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { spin_lock_irqsave(&xhfc->lock, flags); if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) test_and_set_bit(FLG_L2DATA, &bch->Flags); ret = setup_channel(xhfc, bch->channel, bch->inst.pid.protocol[1]); spin_unlock_irqrestore(&xhfc->lock, flags); } #ifdef FIXME if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif skb_trim(skb, 0); return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb)); } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || (hh->prim == (DL_RELEASE | REQUEST)) || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { spin_lock_irqsave(&xhfc->lock, flags); if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; } bch->tx_idx = 0; if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } test_and_clear_bit(FLG_L2DATA, &bch->Flags); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); setup_channel(xhfc, bch->channel, ISDN_PID_NONE); test_and_clear_bit(FLG_ACTIVE, &bch->Flags); spin_unlock_irqrestore(&xhfc->lock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) { #ifdef FIXME if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) if (bch->dev) if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0); #endif if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb)) return(0); } } else { printk(KERN_WARNING "%s %s: unknown prim(%x)\n", xhfc->name, __FUNCTION__, hh->prim); } if (!ret) dev_kfree_skb(skb); return (ret); } /***********************************************/ /* handle Layer2 -> Layer 1 D-Channel messages */ /***********************************************/ static int xhfc_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *chan = container_of(inst, channel_t, inst); mISDN_head_t *hh = mISDN_HEAD_P(skb); xhfc_t *xhfc = inst->privat; int ret = 0; u_long flags; if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(chan, hh->dinfo, skb); if (ret > 0) { /* direct TX */ tasklet_schedule(&xhfc->tasklet); // printk ("PH_DATA_REQ: %i bytes in channel(%i)\n", ret, chan->channel); ret = 0; } spin_unlock_irqrestore(inst->hwlock, flags); return(ret); } if (test_bit(FLG_DCHANNEL, &chan->Flags)) { ret = handle_dmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (test_bit(FLG_BCHANNEL, &chan->Flags)) { ret = handle_bmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } static int xhfc_manager(void *data, u_int prim, void *arg) { xhfc_t *xhfc = NULL; mISDNinstance_t *inst = data; struct sk_buff *skb; int channel = -1; int i; channel_t *chan = NULL; u_long flags; if (!data) { MGR_HASPROTOCOL_HANDLER(prim, arg, &hw_mISDNObj) printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg); return (-EINVAL); } spin_lock_irqsave(&hw_mISDNObj.lock, flags); /* find channel and card */ list_for_each_entry(xhfc, &hw_mISDNObj.ilist, list) { i = 0; while (i < MAX_CHAN) { if (xhfc->chan[i].ch.Flags && &xhfc->chan[i].ch.inst == inst) { channel = i; chan = &xhfc->chan[i].ch; break; } i++; } if (channel >= 0) break; } spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); if (channel < 0) { printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return (-EINVAL); } switch (prim) { case MGR_REGLAYER | CONFIRM: mISDN_setpara(chan, &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { if (xhfc_l2l1(inst, skb)) dev_kfree_skb(skb); } else printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); mISDN_ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: mISDN_setpara(chan, arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { // release_card(xhfc); } else { hw_mISDNObj.refcnt--; } break; case MGR_SETSTACK | INDICATION: if ((channel != 2) && (inst->pid.global == 2)) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (xhfc_l2l1(inst, skb)) dev_kfree_skb(skb); } if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } break; case MGR_GLOBALOPT | REQUEST: if (arg) { // FIXME: detect cards with HEADSET u_int *gopt = arg; *gopt = GLOBALOPT_INTERNAL_CTRL | GLOBALOPT_EXTERNAL_EQUIPMENT | GLOBALOPT_HANDSET; } else return (-EINVAL); break; case MGR_SELCHANNEL | REQUEST: // no special procedure return (-EINVAL); PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); default: printk(KERN_WARNING "%s %s: prim %x not handled\n", xhfc->name, __FUNCTION__, prim); return (-EINVAL); } return (0); } /***********************************/ /* check if new buffer for channel */ /* is waitinng is transmitt queue */ /***********************************/ static int next_tx_frame(xhfc_t * xhfc, __u8 channel) { channel_t *ch = &xhfc->chan[channel].ch; if (ch->tx_skb) dev_kfree_skb(ch->tx_skb); if (test_and_clear_bit(FLG_TX_NEXT, &ch->Flags)) { ch->tx_skb = ch->next_skb; if (ch->tx_skb) { mISDN_head_t *hh = mISDN_HEAD_P(ch->tx_skb); ch->next_skb = NULL; test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); ch->tx_idx = 0; queue_ch_frame(ch, CONFIRM, hh->dinfo, NULL); return (1); } else { printk(KERN_WARNING "%s channel(%i) TX_NEXT without skb\n", xhfc->name, channel); test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); } } else ch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); return (0); } static inline void xhfc_waitbusy(xhfc_t * xhfc) { while (read_xhfc(xhfc, R_STATUS) & M_BUSY); } static inline void xhfc_selfifo(xhfc_t * xhfc, __u8 fifo) { write_xhfc(xhfc, R_FIFO, fifo); xhfc_waitbusy(xhfc); } static inline void xhfc_inc_f(xhfc_t * xhfc) { write_xhfc(xhfc, A_INC_RES_FIFO, M_INC_F); xhfc_waitbusy(xhfc); } static inline void xhfc_resetfifo(xhfc_t * xhfc) { write_xhfc(xhfc, A_INC_RES_FIFO, M_RES_FIFO | M_RES_FIFO_ERR); xhfc_waitbusy(xhfc); } /**************************/ /* fill fifo with TX data */ /**************************/ static void xhfc_write_fifo(xhfc_t * xhfc, __u8 channel) { __u8 fcnt, tcnt, i; __u8 free; __u8 f1, f2; __u8 fstat; __u8 *data; int remain; channel_t *ch = &xhfc->chan[channel].ch; send_buffer: if (!ch->tx_skb) return; remain = ch->tx_skb->len - ch->tx_idx; if (remain <= 0) return; xhfc_selfifo(xhfc, (channel * 2)); fstat = read_xhfc(xhfc, A_FIFO_STA); free = (xhfc->max_z - (read_xhfc(xhfc, A_USAGE))); tcnt = (free >= remain) ? remain : free; f1 = read_xhfc(xhfc, A_F1); f2 = read_xhfc(xhfc, A_F2); fcnt = 0x07 - ((f1 - f2) & 0x07); /* free frame count in tx fifo */ if (debug & DEBUG_HFC_FIFO) { mISDN_debugprint(&ch->inst, "%s channel(%i) len(%i) idx(%i) f1(%i) f2(%i) fcnt(%i) tcnt(%i) free(%i) fstat(%i)", __FUNCTION__, channel, ch->tx_skb->len, ch->tx_idx, f1, f2, fcnt, tcnt, free, fstat); } /* check for fifo underrun during frame transmission */ fstat = read_xhfc(xhfc, A_FIFO_STA); if (fstat & M_FIFO_ERR) { if (debug & DEBUG_HFC_FIFO_ERR) { mISDN_debugprint(&ch->inst, "%s transmit fifo channel(%i) underrun idx(%i), A_FIFO_STA(0x%02x)", __FUNCTION__, channel, ch->tx_idx, fstat); } write_xhfc(xhfc, A_INC_RES_FIFO, M_RES_FIFO_ERR); /* restart frame transmission */ if ((test_bit(FLG_HDLC, &ch->Flags)) && ch->tx_idx) { ch->tx_idx = 0; goto send_buffer; } } if (free && fcnt && tcnt) { data = ch->tx_skb->data + ch->tx_idx; ch->tx_idx += tcnt; if (debug & DEBUG_HFC_FIFO) { printk("%s channel(%i) writing: ", xhfc->name, channel); i=0; while (i= 4) { write32_xhfc(xhfc, A_FIFO_DATA, *((__u32 *) (data+i))); i += 4; } else { write_xhfc(xhfc, A_FIFO_DATA, *(data+i)); i++; } } if (ch->tx_idx == ch->tx_skb->len) { if (test_bit(FLG_HDLC, &ch->Flags)) { /* terminate frame */ xhfc_inc_f(xhfc); } else { xhfc_selfifo(xhfc, (channel * 2)); } /* check for fifo underrun during frame transmission */ fstat = read_xhfc(xhfc, A_FIFO_STA); if (fstat & M_FIFO_ERR) { if (debug & DEBUG_HFC_FIFO_ERR) { mISDN_debugprint(&ch->inst, "%s transmit fifo channel(%i) underrun " "during transmission, A_FIFO_STA(0x%02x)\n", __FUNCTION__, channel, fstat); } write_xhfc(xhfc, A_INC_RES_FIFO, M_RES_FIFO_ERR); if (test_bit(FLG_HDLC, &ch->Flags)) { // restart frame transmission ch->tx_idx = 0; goto send_buffer; } } if (next_tx_frame(xhfc, channel)) { if (debug & DEBUG_HFC_BTRACE) mISDN_debugprint(&ch->inst, "channel(%i) has next_tx_frame", channel); if ((free - tcnt) > 8) { if (debug & DEBUG_HFC_BTRACE) mISDN_debugprint(&ch->inst, "channel(%i) continue B-TX immediatetly", channel); goto send_buffer; } } } else { /* tx buffer not complete, but fifo filled to maximum */ xhfc_selfifo(xhfc, (channel * 2)); } } } /****************************/ /* read RX data out of fifo */ /****************************/ static void xhfc_read_fifo(xhfc_t * xhfc, __u8 channel) { __u8 f1=0, f2=0, z1=0, z2=0; __u8 fstat = 0; int i; int rcnt; /* read rcnt bytes out of fifo */ __u8 *data; /* new data pointer */ struct sk_buff *skb; /* data buffer for upper layer */ channel_t *ch = &xhfc->chan[channel].ch; receive_buffer: xhfc_selfifo(xhfc, (channel * 2) + 1); fstat = read_xhfc(xhfc, A_FIFO_STA); if (fstat & M_FIFO_ERR) { if (debug & DEBUG_HFC_FIFO_ERR) mISDN_debugprint(&ch->inst, "RX fifo overflow channel(%i), " "A_FIFO_STA(0x%02x) f0cnt(%i)", channel, fstat, xhfc->f0_akku); write_xhfc(xhfc, A_INC_RES_FIFO, M_RES_FIFO_ERR); } if (test_bit(FLG_HDLC, &ch->Flags)) { /* hdlc rcnt */ f1 = read_xhfc(xhfc, A_F1); f2 = read_xhfc(xhfc, A_F2); z1 = read_xhfc(xhfc, A_Z1); z2 = read_xhfc(xhfc, A_Z2); rcnt = (z1 - z2) & xhfc->max_z; if (f1 != f2) rcnt++; } else { /* transparent rcnt */ rcnt = read_xhfc(xhfc, A_USAGE) - 1; } if (debug & DEBUG_HFC_FIFO) { if (ch->rx_skb) i = ch->rx_skb->len; else i = 0; mISDN_debugprint(&ch->inst, "reading %i bytes channel(%i) " "irq_cnt(%i) fstat(%i) idx(%i) f1(%i) f2(%i) z1(%i) z2(%i)", rcnt, channel, xhfc->irq_cnt, fstat, i, f1, f2, z1, z2); } if (rcnt > 0) { if (!ch->rx_skb) { ch->rx_skb = alloc_stack_skb(ch->maxlen + 3, ch->up_headerlen); if (!ch->rx_skb) { printk(KERN_DEBUG "%s: No mem for rx_skb\n", __FUNCTION__); return; } } data = skb_put(ch->rx_skb, rcnt); /* read data from FIFO */ i=0; while (i= 4) { *((__u32 *) (data+i)) = read32_xhfc(xhfc, A_FIFO_DATA); i += 4; } else { *(data+i) = read_xhfc(xhfc, A_FIFO_DATA); i++; } } } else return; if (test_bit(FLG_HDLC, &ch->Flags)) { if (f1 != f2) { xhfc_inc_f(xhfc); if ((ch->debug) && (debug & DEBUG_HFC_DTRACE)) { mISDN_debugprint(&ch->inst, "channel(%i) new RX len(%i): ", channel, ch->rx_skb->len); i = 0; printk(" "); while (i < ch->rx_skb->len) printk("%02x ", ch->rx_skb->data[i++]); printk("\n"); } /* check minimum frame size */ if (ch->rx_skb->len < 4) { if (debug & DEBUG_HFC_FIFO_ERR) mISDN_debugprint(&ch->inst, "%s: frame in channel(%i) < minimum size", __FUNCTION__, channel); goto read_exit; } /* check crc */ if (ch->rx_skb->data[ch->rx_skb->len - 1]) { if (debug & DEBUG_HFC_FIFO_ERR) mISDN_debugprint(&ch->inst, "%s: channel(%i) CRC-error", __FUNCTION__, channel); goto read_exit; } /* remove cksum */ skb_trim(ch->rx_skb, ch->rx_skb->len - 3); if (ch->rx_skb->len < MISDN_COPY_SIZE) { skb = alloc_stack_skb(ch->rx_skb->len, ch->up_headerlen); if (skb) { memcpy(skb_put(skb, ch->rx_skb->len), ch->rx_skb->data, ch->rx_skb->len); skb_trim(ch->rx_skb, 0); } else { skb = ch->rx_skb; ch->rx_skb = NULL; } } else { skb = ch->rx_skb; ch->rx_skb = NULL; } queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, skb); read_exit: if (ch->rx_skb) skb_trim(ch->rx_skb, 0); if (read_xhfc(xhfc, A_USAGE) > 8) { if (debug & DEBUG_HFC_FIFO) mISDN_debugprint(&ch->inst, "%s: channel(%i) continue xhfc_read_fifo", __FUNCTION__, channel); goto receive_buffer; } return; } else { xhfc_selfifo(xhfc, (channel * 2) + 1); } } else { xhfc_selfifo(xhfc, (channel * 2) + 1); if (ch->rx_skb->len >= TRANSP_PACKET_SIZE) { /* deliver transparent data to layer2 */ queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, ch->rx_skb); ch->rx_skb = NULL; } } } /*************************************/ /* bottom half handler for interrupt */ /*************************************/ static void xhfc_bh_handler(unsigned long ul_hw) { xhfc_t *xhfc = (xhfc_t *) ul_hw; int i; reg_a_su_rd_sta su_state; channel_t *dch; /* timer interrupt */ if (xhfc->misc_irq.bit.v_ti_irq) { xhfc->misc_irq.bit.v_ti_irq = 0; /* Handle tx Fifos */ for (i = 0; i < xhfc->max_fifo; i++) { if ((1 << (i * 2)) & (xhfc->fifo_irqmsk)) { xhfc->fifo_irq &= ~(1 << (i * 2)); if (test_bit(FLG_TX_BUSY, &xhfc->chan[i].ch.Flags)) { xhfc_write_fifo(xhfc, i); } } } /* handle NT Timer */ for (i = 0; i < xhfc->num_ports; i++) { if ((xhfc->port[i].mode & PORT_MODE_NT) && (xhfc->port[i].mode & NT_TIMER)) { if ((--xhfc->port[i].nt_timer) < 0) su_new_state(&xhfc->port[i]); } } } /* set fifo_irq when RX data over treshold */ for (i = 0; i < xhfc->num_ports; i++) { xhfc->fifo_irq |= read_xhfc(xhfc, R_FILL_BL0 + i) << (i * 8); } /* Handle rx Fifos */ if ((xhfc->fifo_irq & xhfc->fifo_irqmsk) & FIFO_MASK_RX) { for (i = 0; i < xhfc->max_fifo; i++) { if ((xhfc->fifo_irq & (1 << (i * 2 + 1))) & (xhfc->fifo_irqmsk)) { xhfc->fifo_irq &= ~(1 << (i * 2 + 1)); xhfc_read_fifo(xhfc, i); } } } /* su interrupt */ if (xhfc->su_irq.reg & xhfc->su_irqmsk.reg) { xhfc->su_irq.reg = 0; for (i = 0; i < xhfc->num_ports; i++) { write_xhfc(xhfc, R_SU_SEL, i); su_state.reg = read_xhfc(xhfc, A_SU_RD_STA); dch = &xhfc->chan[(i << 2) + 2].ch; if (su_state.bit.v_su_sta != dch->state) { dch->state = su_state.bit.v_su_sta; su_new_state(&xhfc->port[i]); } } } } /*********************/ /* Interrupt handler */ /*********************/ static irqreturn_t xhfc_interrupt(int intno, void *dev_id, struct pt_regs *regs) { xhfc_pi *pi = dev_id; xhfc_t * xhfc = NULL; __u8 i, j; __u32 xhfc_irqs; #ifdef USE_F0_COUNTER __u32 f0_cnt; #endif xhfc_irqs = 0; for (i=0; idriver_data.num_xhfcs; i++) { xhfc = &pi->xhfc[i]; if (xhfc->irq_ctrl.bit.v_glob_irq_en && (read_xhfc(xhfc, R_IRQ_OVIEW))) /* mark this xhfc possibly had irq */ xhfc_irqs |= (1 << i); } if (!xhfc_irqs) { if (debug & DEBUG_HFC_IRQ) printk(KERN_INFO "%s %s NOT M_GLOB_IRQ_EN or R_IRQ_OVIEW \n", xhfc->name, __FUNCTION__); return IRQ_NONE; } xhfc_irqs = 0; for (i=0; idriver_data.num_xhfcs; i++) { xhfc = &pi->xhfc[i]; xhfc->misc_irq.reg |= read_xhfc(xhfc, R_MISC_IRQ); xhfc->su_irq.reg |= read_xhfc(xhfc, R_SU_IRQ); /* get fifo IRQ states in bundle */ for (j = 0; j < 4; j++) { xhfc->fifo_irq |= (read_xhfc(xhfc, R_FIFO_BL0_IRQ + j) << (j * 8)); } /* call bottom half at events * - Timer Interrupt (or other misc_irq sources) * - SU State change * - Fifo FrameEnd interrupts (only at rx fifos enabled) */ if ((xhfc->misc_irq.reg & xhfc->misc_irqmsk.reg) || (xhfc->su_irq.reg & xhfc->su_irqmsk.reg) || (xhfc->fifo_irq & xhfc->fifo_irqmsk)) { /* mark this xhfc really had irq */ xhfc_irqs |= (1 << i); /* queue bottom half */ if (!(xhfc->testirq)) tasklet_schedule(&xhfc->tasklet); /* count irqs */ xhfc->irq_cnt++; #ifdef USE_F0_COUNTER /* akkumulate f0 counter diffs */ f0_cnt = read_xhfc(xhfc, R_F0_CNTL); f0_cnt += read_xhfc(xhfc, R_F0_CNTH) << 8; xhfc->f0_akku += (f0_cnt - xhfc->f0_cnt); if ((f0_cnt - xhfc->f0_cnt) < 0) xhfc->f0_akku += 0xFFFF; xhfc->f0_cnt = f0_cnt; #endif } } return ((xhfc_irqs)?IRQ_HANDLED:IRQ_NONE); } /*****************************************************/ /* disable all interrupts by disabling M_GLOB_IRQ_EN */ /*****************************************************/ static void disable_interrupts(xhfc_t * xhfc) { if (debug & DEBUG_HFC_IRQ) printk(KERN_INFO "%s %s\n", xhfc->name, __FUNCTION__); xhfc->irq_ctrl.bit.v_glob_irq_en = 0; write_xhfc(xhfc, R_IRQ_CTRL, xhfc->irq_ctrl.reg); } /******************************************/ /* start interrupt and set interrupt mask */ /******************************************/ static void enable_interrupts(xhfc_t * xhfc) { if (debug & DEBUG_HFC_IRQ) printk(KERN_INFO "%s %s\n", xhfc->name, __FUNCTION__); write_xhfc(xhfc, R_SU_IRQMSK, xhfc->su_irqmsk.reg); /* use defined timer interval */ write_xhfc(xhfc, R_TI_WD, xhfc->ti_wd.reg); xhfc->misc_irqmsk.bit.v_ti_irqmsk = 1; write_xhfc(xhfc, R_MISC_IRQMSK, xhfc->misc_irqmsk.reg); /* clear all pending interrupts bits */ read_xhfc(xhfc, R_MISC_IRQ); read_xhfc(xhfc, R_SU_IRQ); read_xhfc(xhfc, R_FIFO_BL0_IRQ); read_xhfc(xhfc, R_FIFO_BL1_IRQ); read_xhfc(xhfc, R_FIFO_BL2_IRQ); read_xhfc(xhfc, R_FIFO_BL3_IRQ); /* enable global interrupts */ xhfc->irq_ctrl.bit.v_glob_irq_en = 1; xhfc->irq_ctrl.bit.v_fifo_irq_en = 1; write_xhfc(xhfc, R_IRQ_CTRL, xhfc->irq_ctrl.reg); } /***********************************/ /* initialise the XHFC ISDN Chip */ /* return 0 on success. */ /***********************************/ static int init_xhfc(xhfc_t * xhfc) { int err = 0; int timeout = 0x2000; __u8 chip_id; chip_id = read_xhfc(xhfc, R_CHIP_ID); switch (chip_id) { case CHIP_ID_1SU: xhfc->num_ports = 1; xhfc->max_fifo = 4; xhfc->max_z = 0xFF; xhfc->ti_wd.bit.v_ev_ts = 0x6; /* timer irq interval 16 ms */ write_xhfc(xhfc, R_FIFO_MD, M1_FIFO_MD * 2); xhfc->su_irqmsk.bit.v_su0_irqmsk = 1; sprintf(xhfc->name, "%s_PI%d_%i", CHIP_NAME_1SU, xhfc->pi->cardnum, xhfc->chipidx); break; case CHIP_ID_2SU: xhfc->num_ports = 2; xhfc->max_fifo = 8; xhfc->max_z = 0x7F; xhfc->ti_wd.bit.v_ev_ts = 0x5; /* timer irq interval 8 ms */ write_xhfc(xhfc, R_FIFO_MD, M1_FIFO_MD * 1); xhfc->su_irqmsk.bit.v_su0_irqmsk = 1; xhfc->su_irqmsk.bit.v_su1_irqmsk = 1; sprintf(xhfc->name, "%s_PI%d_%i", CHIP_NAME_2SU, xhfc->pi->cardnum, xhfc->chipidx); break; case CHIP_ID_2S4U: xhfc->num_ports = 4; xhfc->max_fifo = 16; xhfc->max_z = 0x3F; xhfc->ti_wd.bit.v_ev_ts = 0x4; /* timer irq interval 4 ms */ write_xhfc(xhfc, R_FIFO_MD, M1_FIFO_MD * 0); xhfc->su_irqmsk.bit.v_su0_irqmsk = 1; xhfc->su_irqmsk.bit.v_su1_irqmsk = 1; xhfc->su_irqmsk.bit.v_su2_irqmsk = 1; xhfc->su_irqmsk.bit.v_su3_irqmsk = 1; sprintf(xhfc->name, "%s_PI%d_%i", CHIP_NAME_2S4U, xhfc->pi->cardnum, xhfc->chipidx); case CHIP_ID_4SU: xhfc->num_ports = 4; xhfc->max_fifo = 16; xhfc->max_z = 0x3F; xhfc->ti_wd.bit.v_ev_ts = 0x4; /* timer irq interval 4 ms */ write_xhfc(xhfc, R_FIFO_MD, M1_FIFO_MD * 0); xhfc->su_irqmsk.bit.v_su0_irqmsk = 1; xhfc->su_irqmsk.bit.v_su1_irqmsk = 1; xhfc->su_irqmsk.bit.v_su2_irqmsk = 1; xhfc->su_irqmsk.bit.v_su3_irqmsk = 1; sprintf(xhfc->name, "%s_PI%d_%i", CHIP_NAME_4SU, xhfc->pi->cardnum, xhfc->chipidx); break; default: err = -ENODEV; } if (err) { if (debug & DEBUG_HFC_INIT) printk(KERN_ERR "%s %s: unkown Chip ID 0x%x\n", xhfc->name, __FUNCTION__, chip_id); return (err); } else { if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s ChipID: 0x%x\n", xhfc->name, chip_id); } /* software reset to enable R_FIFO_MD setting */ write_xhfc(xhfc, R_CIRM, M_SRES); udelay(5); write_xhfc(xhfc, R_CIRM, 0); /* amplitude */ write_xhfc(xhfc, R_PWM_MD, 0x80); write_xhfc(xhfc, R_PWM1, 0x18); write_xhfc(xhfc, R_FIFO_THRES, 0x11); while ((read_xhfc(xhfc, R_STATUS) & (M_BUSY | M_PCM_INIT)) && (timeout)) timeout--; if (!(timeout)) { if (debug & DEBUG_HFC_INIT) printk(KERN_ERR "%s %s: initialization sequence could not finish\n", xhfc->name, __FUNCTION__); return (-ENODEV); } /* set PCM master mode */ xhfc->pcm_md0.bit.v_pcm_md = 1; write_xhfc(xhfc, R_PCM_MD0, xhfc->pcm_md0.reg); /* set pll adjust */ xhfc->pcm_md0.bit.v_pcm_idx = IDX_PCM_MD1; xhfc->pcm_md1.bit.v_pll_adj = 3; write_xhfc(xhfc, R_PCM_MD0, xhfc->pcm_md0.reg); write_xhfc(xhfc, R_PCM_MD1, xhfc->pcm_md1.reg); /* perfom short irq test */ xhfc->testirq=1; enable_interrupts(xhfc); mdelay(1 << xhfc->ti_wd.bit.v_ev_ts); disable_interrupts(xhfc); if (xhfc->irq_cnt > 2) { xhfc->testirq = 0; return (0); } else { if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: ERROR getting IRQ (irq_cnt %i)\n", xhfc->name, __FUNCTION__, xhfc->irq_cnt); return (-EIO); } } /*************************************/ /* free memory for all used channels */ /*************************************/ static void release_channels(xhfc_t * xhfc) { int i = 0; while (i < MAX_CHAN) { if (xhfc->chan[i].ch.Flags) { if (debug & DEBUG_HFC_INIT) printk(KERN_DEBUG "%s %s: free channel %d\n", xhfc->name, __FUNCTION__, i); mISDN_freechannel(&xhfc->chan[i].ch); mISDN_ctrl(&xhfc->chan[i].ch.inst, MGR_UNREGLAYER | REQUEST, NULL); } i++; } if (xhfc->chan) kfree(xhfc->chan); if (xhfc->port) kfree(xhfc->port); } /*********************************************/ /* setup port (line interface) with SU_CRTLx */ /*********************************************/ static void init_su(xhfc_t * xhfc, __u8 pt) { xhfc_port_t *port = &xhfc->port[pt]; if (debug & DEBUG_HFC_MODE) printk(KERN_INFO "%s %s port(%i)\n", xhfc->name, __FUNCTION__, pt); write_xhfc(xhfc, R_SU_SEL, pt); if (port->mode & PORT_MODE_NT) port->su_ctrl0.bit.v_su_md = 1; if (port->mode & PORT_MODE_EXCH_POL) port->su_ctrl2.reg = M_SU_EXCHG; if (port->mode & PORT_MODE_UP) { port->st_ctrl3.bit.v_st_sel = 1; write_xhfc(xhfc, A_MS_TX, 0x0F); port->su_ctrl0.bit.v_st_sq_en = 1; } /* configure end of pulse control for ST mode (TE & NT) */ if (port->mode & PORT_MODE_S0) { port->su_ctrl0.bit.v_st_pu_ctrl = 1; port->st_ctrl3.reg = 0xf8; } if (debug & DEBUG_HFC_MODE) printk(KERN_INFO "%s %s su_ctrl0(0x%02x) " "su_ctrl1(0x%02x) " "su_ctrl2(0x%02x) " "st_ctrl3(0x%02x)\n", xhfc->name, __FUNCTION__, port->su_ctrl0.reg, port->su_ctrl1.reg, port->su_ctrl2.reg, port->st_ctrl3.reg); write_xhfc(xhfc, A_ST_CTRL3, port->st_ctrl3.reg); write_xhfc(xhfc, A_SU_CTRL0, port->su_ctrl0.reg); write_xhfc(xhfc, A_SU_CTRL1, port->su_ctrl1.reg); write_xhfc(xhfc, A_SU_CTRL2, port->su_ctrl2.reg); if (port->mode & PORT_MODE_TE) write_xhfc(xhfc, A_SU_CLK_DLY, CLK_DLY_TE); else write_xhfc(xhfc, A_SU_CLK_DLY, CLK_DLY_NT); write_xhfc(xhfc, A_SU_WR_STA, 0); } /*********************************************************/ /* Setup Fifo using A_CON_HDLC, A_SUBCH_CFG, A_FIFO_CTRL */ /*********************************************************/ static void setup_fifo(xhfc_t * xhfc, __u8 fifo, __u8 conhdlc, __u8 subcfg, __u8 fifoctrl, __u8 enable) { xhfc_selfifo(xhfc, fifo); write_xhfc(xhfc, A_CON_HDLC, conhdlc); write_xhfc(xhfc, A_SUBCH_CFG, subcfg); write_xhfc(xhfc, A_FIFO_CTRL, fifoctrl); if (enable) xhfc->fifo_irqmsk |= (1 << fifo); else xhfc->fifo_irqmsk &= ~(1 << fifo); xhfc_resetfifo(xhfc); xhfc_selfifo(xhfc, fifo); if (debug & DEBUG_HFC_MODE) { printk(KERN_INFO "%s %s: fifo(%i) conhdlc(0x%02x) " "subcfg(0x%02x) fifoctrl(0x%02x)\n", xhfc->name, __FUNCTION__, fifo, sread_xhfc(xhfc, A_CON_HDLC), sread_xhfc(xhfc, A_SUBCH_CFG), sread_xhfc(xhfc, A_FIFO_CTRL) ); } } /**************************************************/ /* Setup S/U interface, enable/disable B-Channels */ /**************************************************/ static void setup_su(xhfc_t * xhfc, __u8 pt, __u8 bc, __u8 enable) { xhfc_port_t *port = &xhfc->port[pt]; if (!((bc == 0) || (bc == 1))) { printk(KERN_INFO "%s %s: pt(%i) ERROR: bc(%i) unvalid!\n", xhfc->name, __FUNCTION__, pt, bc); return; } if (debug & DEBUG_HFC_MODE) printk(KERN_INFO "%s %s %s pt(%i) bc(%i)\n", xhfc->name, __FUNCTION__, (enable) ? ("enable") : ("disable"), pt, bc); if (bc) { port->su_ctrl2.bit.v_b2_rx_en = (enable?1:0); port->su_ctrl0.bit.v_b2_tx_en = (enable?1:0); } else { port->su_ctrl2.bit.v_b1_rx_en = (enable?1:0); port->su_ctrl0.bit.v_b1_tx_en = (enable?1:0); } if (xhfc->port[pt].mode & PORT_MODE_NT) xhfc->port[pt].su_ctrl0.bit.v_su_md = 1; write_xhfc(xhfc, R_SU_SEL, pt); write_xhfc(xhfc, A_SU_CTRL0, xhfc->port[pt].su_ctrl0.reg); write_xhfc(xhfc, A_SU_CTRL2, xhfc->port[pt].su_ctrl2.reg); } /*********************************************/ /* (dis-) connect D/B-Channel using protocol */ /*********************************************/ static int setup_channel(xhfc_t * xhfc, __u8 channel, int protocol) { xhfc_port_t *port = xhfc->chan[channel].port; if (test_bit(FLG_BCHANNEL, &xhfc->chan[channel].ch.Flags)) { if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&xhfc->chan[channel].ch.inst, "channel(%i) protocol %x-->%x", channel, xhfc->chan[channel].ch.state, protocol); switch (protocol) { case (-1): /* used for init */ xhfc->chan[channel].ch.state = -1; xhfc->chan[channel].ch.channel = channel; /* fall trough */ case (ISDN_PID_NONE): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&xhfc-> chan[channel].ch.inst, "ISDN_PID_NONE"); if (xhfc->chan[channel].ch.state == ISDN_PID_NONE) return (0); /* already in idle state */ xhfc->chan[channel].ch.state = ISDN_PID_NONE; setup_fifo(xhfc, (channel << 1), 4, 0, 0, 0); /* B-TX fifo */ setup_fifo(xhfc, (channel << 1) + 1, 4, 0, 0, 0); /* B-RX fifo */ setup_su(xhfc, port->idx, (channel % 4) ? 1 : 0, 0); test_and_clear_bit(FLG_HDLC, &xhfc->chan[channel].ch.Flags); test_and_clear_bit(FLG_TRANSPARENT, &xhfc->chan[channel].ch.Flags); break; case (ISDN_PID_L1_B_64TRANS): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&xhfc->chan[channel].ch.inst, "ISDN_PID_L1_B_64TRANS"); setup_fifo(xhfc, (channel << 1), 6, 0, 0, 1); /* B-TX Fifo */ setup_fifo(xhfc, (channel << 1) + 1, 6, 0, 0, 1); /* B-RX Fifo */ setup_su(xhfc, port->idx, (channel % 4) ? 1 : 0, 1); xhfc->chan[channel].ch.state = ISDN_PID_L1_B_64TRANS; test_and_set_bit(FLG_TRANSPARENT, &xhfc->chan[channel].ch.Flags); break; case (ISDN_PID_L1_B_64HDLC): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&xhfc->chan[channel].ch.inst, "ISDN_PID_L1_B_64HDLC"); setup_fifo(xhfc, (channel << 1), 4, 0, M_FR_ABO, 1); // TX Fifo setup_fifo(xhfc, (channel << 1) + 1, 4, 0, M_FR_ABO | M_FIFO_IRQMSK, 1); // RX Fifo setup_su(xhfc, port->idx, (channel % 4) ? 1 : 0, 1); xhfc->chan[channel].ch.state = ISDN_PID_L1_B_64HDLC; test_and_set_bit(FLG_HDLC, &xhfc->chan[channel].ch.Flags); break; default: mISDN_debugprint(&xhfc->chan[channel].ch.inst, "prot not known %x", protocol); return (-ENOPROTOOPT); } return (0); } else if (test_bit(FLG_DCHANNEL, &xhfc->chan[channel].ch.Flags)) { if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&xhfc->chan[channel].ch.inst, "channel(%i) protocol(%i)", channel, protocol); setup_fifo(xhfc, (channel << 1), 5, 2, M_FR_ABO, 1); /* D TX fifo */ setup_fifo(xhfc, (channel << 1) + 1, 5, 2, M_FR_ABO | M_FIFO_IRQMSK, 1); /* D RX fifo */ return (0); } printk(KERN_INFO "%s %s ERROR: channel(%i) is NEITHER B nor D !!!\n", xhfc->name, __FUNCTION__, channel); return (-1); } /*******************************************************/ /* register ISDN stack for one XHFC card */ /* - register all ports and channels */ /* - set param_idx */ /* */ /* channel mapping in mISDN in xhfc->chan[MAX_CHAN]: */ /* 1st line interf: 0=B1, 1=B2, 2=D, 3=PCM */ /* 2nd line interf: 4=B1, 5=B2, 6=D, 7=PCM */ /* 3rd line interf: 8=B1, 9=B2, 10=D, 11=PCM */ /* 4th line interf; 12=B1, 13=B2, 14=D, 15=PCM */ /*******************************************************/ static int init_mISDN_channels(xhfc_t * xhfc) { int err; int pt; /* ST/U port index */ int ch_idx; /* channel index */ int b; channel_t *ch; mISDN_pid_t pid; u_long flags; for (pt = 0; pt < xhfc->num_ports; pt++) { /* init D channels */ ch_idx = (pt << 2) + 2; if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: Registering D-channel, card(%d) " "ch(%d) port(%d) protocol(%x)\n", xhfc->name, __FUNCTION__, xhfc->chipnum, ch_idx, pt, xhfc->port[pt].dpid); xhfc->port[pt].idx = pt; xhfc->port[pt].xhfc = xhfc; xhfc->chan[ch_idx].port = &xhfc->port[pt]; ch = &xhfc->chan[ch_idx].ch; memset(ch, 0, sizeof(channel_t)); ch->channel = ch_idx; ch->debug = debug; ch->inst.obj = &hw_mISDNObj; ch->inst.hwlock = &xhfc->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) ch->inst.class_dev.parent = &xhfc->pi->pdev->dev; #else ch->inst.class_dev.dev = &xhfc->pi->pdev->dev; #endif mISDN_init_instance(&ch->inst, &hw_mISDNObj, xhfc, xhfc_l2l1); ch->inst.pid.layermask = ISDN_LAYER(0); sprintf(ch->inst.name, "%s_%d_D", xhfc->name, pt); err = mISDN_initchannel(ch, MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); if (err) goto free_channels; ch->hw = xhfc; /* init t3 timer */ init_timer(&xhfc->port[pt].t3_timer); xhfc->port[pt].t3_timer.data = (long) &xhfc->port[pt]; xhfc->port[pt].t3_timer.function = (void *) l1_timer_expire_t3; /* init t4 timer */ init_timer(&xhfc->port[pt].t4_timer); xhfc->port[pt].t4_timer.data = (long) &xhfc->port[pt]; xhfc->port[pt].t4_timer.function = (void *) l1_timer_expire_t4; /* init B channels */ for (b = 0; b < 2; b++) { ch_idx = (pt << 2) + b; if (debug & DEBUG_HFC_INIT) printk(KERN_DEBUG "%s %s: Registering B-channel, card(%d) " "ch(%d) port(%d)\n", xhfc->name, __FUNCTION__, xhfc->chipnum, ch_idx, pt); xhfc->chan[ch_idx].port = &xhfc->port[pt]; ch = &xhfc->chan[ch_idx].ch; memset(ch, 0, sizeof(channel_t)); ch->channel = ch_idx; ch->debug = debug; mISDN_init_instance(&ch->inst, &hw_mISDNObj, xhfc, xhfc_l2l1); ch->inst.pid.layermask = ISDN_LAYER(0); ch->inst.hwlock = &xhfc->lock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) ch->inst.class_dev.parent = &xhfc->pi->pdev->dev; #else ch->inst.class_dev.dev = &xhfc->pi->pdev->dev; #endif sprintf(ch->inst.name, "%s_%d_B%d", xhfc->name, pt, b + 1); if (mISDN_initchannel(ch, MSK_INIT_BCHANNEL, MAX_DATA_MEM)) { err = -ENOMEM; goto free_channels; } ch->hw = xhfc; } /* clear PCM */ memset(&xhfc->chan[(pt << 2) + 3], 0, sizeof(channel_t)); mISDN_set_dchannel_pid(&pid, xhfc->port[pt].dpid, layermask[xhfc->param_idx + pt]); /* register D Channel */ ch = &xhfc->chan[(pt << 2) + 2].ch; /* set protocol for NT/TE */ if (xhfc->port[pt].mode & PORT_MODE_NT) { /* NT-mode */ xhfc->port[xhfc->param_idx + pt].mode |= NT_TIMER; xhfc->port[xhfc->param_idx + pt].nt_timer = 0; ch->inst.pid.protocol[0] = ISDN_PID_L0_NT_S0; ch->inst.pid.protocol[1] = ISDN_PID_L1_NT_S0; pid.protocol[0] = ISDN_PID_L0_NT_S0; pid.protocol[1] = ISDN_PID_L1_NT_S0; ch->inst.pid.layermask |= ISDN_LAYER(1); pid.layermask |= ISDN_LAYER(1); if (layermask[xhfc->param_idx + pt] & ISDN_LAYER(2)) pid.protocol[2] = ISDN_PID_L2_LAPD_NET; } else { /* TE-mode */ xhfc->port[xhfc->param_idx + pt].mode |= PORT_MODE_TE; ch->inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; ch->inst.pid.protocol[1] = ISDN_PID_L1_TE_S0; pid.protocol[0] = ISDN_PID_L0_TE_S0; pid.protocol[1] = ISDN_PID_L1_TE_S0; } if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: registering Stack for Port %i\n", xhfc->name, __FUNCTION__, pt); /* register stack */ err = mISDN_ctrl(NULL, MGR_NEWSTACK | REQUEST, &ch->inst); if (err) { printk(KERN_ERR "%s %s: MGR_NEWSTACK | REQUEST err(%d)\n", xhfc->name, __FUNCTION__, err); goto free_channels; } ch->state = 0; /* attach two BChannels to this DChannel (ch) */ for (b = 0; b < 2; b++) { err = mISDN_ctrl(ch->inst.st, MGR_NEWSTACK | REQUEST, &xhfc->chan[(pt << 2) + b].ch.inst); if (err) { printk(KERN_ERR "%s %s: MGR_ADDSTACK bchan error %d\n", xhfc->name, __FUNCTION__, err); goto free_stack; } } err = mISDN_ctrl(ch->inst.st, MGR_SETSTACK | REQUEST, &pid); if (err) { printk(KERN_ERR "%s %s: MGR_SETSTACK REQUEST dch err(%d)\n", xhfc->name, __FUNCTION__, err); mISDN_ctrl(ch->inst.st, MGR_DELSTACK | REQUEST, NULL); goto free_stack; } /* initial setup of each channel */ setup_channel(xhfc, ch->channel, -1); for (b = 0; b < 2; b++) setup_channel(xhfc, (pt << 2) + b, -1); /* delay some time */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ mISDN_ctrl(ch->inst.st, MGR_CTRLREADY | INDICATION, NULL); } return (0); free_stack: mISDN_ctrl(ch->inst.st, MGR_DELSTACK | REQUEST, NULL); free_channels: spin_lock_irqsave(&hw_mISDNObj.lock, flags); release_channels(xhfc); list_del(&xhfc->list); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); return (err); } /********************************/ /* parse module paramaters like */ /* NE/TE and S0/Up port mode */ /********************************/ static void parse_module_params(xhfc_t * xhfc) { __u8 pt; /* parse module parameters */ for (pt = 0; pt < xhfc->num_ports; pt++) { /* D-Channel protocol: (2=DSS1) */ xhfc->port[pt].dpid = (protocol[xhfc->param_idx + pt] & 0x0F); if (xhfc->port[pt].dpid == 0) { printk(KERN_INFO "%s %s: WARNING: wrong value for protocol[%i], " "assuming 0x02 (DSS1)...\n", xhfc->name, __FUNCTION__, xhfc->param_idx + pt); xhfc->port[pt].dpid = 0x02; } /* Line Interface TE or NT */ if (protocol[xhfc->param_idx + pt] & 0x10) xhfc->port[pt].mode |= PORT_MODE_NT; else xhfc->port[pt].mode |= PORT_MODE_TE; /* Line Interface in S0 or Up mode */ if (protocol[xhfc->param_idx + pt] & 0x20) xhfc->port[pt].mode |= PORT_MODE_UP; else xhfc->port[pt].mode |= PORT_MODE_S0; /* st line polarity */ if (protocol[xhfc->param_idx + pt] & 0x40) xhfc->port[pt].mode |= PORT_MODE_EXCH_POL; /* get layer1 loop-config */ if (protocol[xhfc->param_idx + pt] & 0x80) xhfc->port[pt].mode |= PORT_MODE_LOOP_B1; if (protocol[xhfc->param_idx + pt] & 0x0100) xhfc->port[pt].mode |= PORT_MODE_LOOP_B2; if (protocol[xhfc->param_idx + pt] & 0x0200) xhfc->port[pt].mode |= PORT_MODE_LOOP_D; if (debug & DEBUG_HFC_INIT) printk ("%s %s: protocol[%i]=0x%02x, dpid=%d, mode:%s,%s %s, Loops:0x%x\n", xhfc->name, __FUNCTION__, xhfc->param_idx+pt, protocol[xhfc->param_idx + pt], xhfc->port[pt].dpid, (xhfc->port[pt].mode & PORT_MODE_TE)?"TE":"NT", (xhfc->port[pt].mode & PORT_MODE_S0)?"S0":"Up", (xhfc->port[pt].mode & PORT_MODE_EXCH_POL)?"SU_EXCH":"", (xhfc->port[pt].mode & PORT_MODE_LOOPS) ); } } /********************************/ /* initialise the XHFC hardware */ /* return 0 on success. */ /********************************/ static int __devinit setup_instance(xhfc_t * xhfc) { int err; int pt; xhfc_t *previous_hw; xhfc_port_t * port = NULL; xhfc_chan_t * chan = NULL; u_long flags; spin_lock_init(&xhfc->lock); tasklet_init(&xhfc->tasklet, xhfc_bh_handler, (unsigned long) xhfc); /* search previous instances to index protocol[] array */ list_for_each_entry(previous_hw, &hw_mISDNObj.ilist, list) { xhfc->param_idx += previous_hw->num_ports; } spin_lock_irqsave(&hw_mISDNObj.lock, flags); /* add this instance to hardware list */ list_add_tail(&xhfc->list, &hw_mISDNObj.ilist); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); err = init_xhfc(xhfc); if (err) goto out; /* alloc mem for all ports and channels */ err = -ENOMEM; port = kmalloc(sizeof(xhfc_port_t) * xhfc->num_ports, GFP_KERNEL); if (port) { xhfc->port = port; memset(xhfc->port, 0, sizeof(xhfc_port_t) * xhfc->num_ports); chan = kmalloc(sizeof(xhfc_chan_t) * xhfc->num_ports * CHAN_PER_PORT, GFP_KERNEL); if (chan) { xhfc->chan = chan; memset(xhfc->chan, 0, sizeof(xhfc_chan_t) * xhfc->num_ports * CHAN_PER_PORT); err = 0; } else { printk(KERN_ERR "%s %s: No kmem for xhfc_chan_t*%i \n", xhfc->name, __FUNCTION__, xhfc->num_ports * CHAN_PER_PORT); goto out; } } else { printk(KERN_ERR "%s %s: No kmem for xhfc_port_t*%i \n", xhfc->name, __FUNCTION__, xhfc->num_ports); goto out; } parse_module_params(xhfc); /* init line interfaces (ports) */ for (pt = 0; pt < xhfc->num_ports; pt++) { sprintf(xhfc->port[pt].name, "%s_%i", xhfc->name, pt); init_su(xhfc, pt); } /* register all channels at ISDN procol stack */ err = init_mISDN_channels(xhfc); if (err) goto out; /* delay some time */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ enable_interrupts(xhfc); /* force initial layer1 statechanges */ xhfc->su_irq.reg = xhfc->su_irqmsk.reg; /* init loops if desired */ for (pt = 0; pt < xhfc->num_ports; pt++) { if (xhfc->port[pt].mode & PORT_MODE_LOOP_B1) xhfc_ph_command(&xhfc->port[pt], HFC_L1_TESTLOOP_B1); if (xhfc->port[pt].mode & PORT_MODE_LOOP_B2) xhfc_ph_command(&xhfc->port[pt], HFC_L1_TESTLOOP_B2); if (xhfc->port[pt].mode & PORT_MODE_LOOP_D) xhfc_ph_command(&xhfc->port[pt], HFC_L1_TESTLOOP_D); } return (0); out: if (xhfc->chan) kfree(xhfc->chan); if (xhfc->port) kfree(xhfc->port); return (err); } /************************/ /* release single card */ /************************/ static void release_card(xhfc_pi * pi) { u_long flags; __u8 i; for (i=0; idriver_data.num_xhfcs; i++) disable_interrupts(&pi->xhfc[i]); free_irq(pi->irq, pi); /* wait for pending tasklet to finish */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ spin_lock_irqsave(&hw_mISDNObj.lock, flags); for (i=0; idriver_data.num_xhfcs; i++) { release_channels(&pi->xhfc[i]); list_del(&pi->xhfc[i].list); } spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); kfree(pi->xhfc); kfree(pi); } #if BRIDGE == BRIDGE_PCI2PI /*****************************************/ /* PCI hotplug interface: probe new card */ /*****************************************/ static int __devinit xhfc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { pi_params * driver_data = (pi_params *) ent->driver_data; xhfc_pi * pi = NULL; __u8 i; int err = -ENOMEM; /* alloc mem for ProcessorInterface xhfc_pi */ if (!(pi = kmalloc(sizeof(xhfc_pi), GFP_KERNEL))) { printk(KERN_ERR "%s: No kmem for XHFC card\n", __FUNCTION__); goto out; } memset(pi, 0, sizeof(xhfc_pi)); pi->cardnum = card_cnt; sprintf(pi->name, "%s_PI%d", DRIVER_NAME, pi->cardnum); printk(KERN_INFO "%s %s: adapter '%s' found on PCI bus %02x dev %02x, using %i XHFC controllers\n", pi->name, __FUNCTION__, driver_data->device_name, pdev->bus->number, pdev->devfn, driver_data->num_xhfcs); /* alloc mem for all XHFCs (xhfc_t) */ if (!(pi->xhfc = kmalloc(sizeof(xhfc_t) * driver_data->num_xhfcs, GFP_KERNEL))) { printk(KERN_ERR "%s %s: No kmem for sizeof(xhfc_t)*%i \n", pi->name, __FUNCTION__, driver_data->num_xhfcs); goto out; } memset(pi->xhfc, 0, sizeof(xhfc_t) * driver_data->num_xhfcs); pi->pdev = pdev; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "%s %s: error with pci_enable_device\n", pi->name, __FUNCTION__); goto out; } if (driver_data->num_xhfcs > PCI2PI_MAX_XHFC) { printk(KERN_ERR "%s %s: max number og adressable XHFCs aceeded\n", pi->name, __FUNCTION__); goto out; } pi->driver_data = *driver_data; pi->irq = pdev->irq; pi->hw_membase = (u_char *) pci_resource_start(pdev, 1); pi->membase = ioremap((ulong) pi->hw_membase, 4096); pci_set_drvdata(pdev, pi); err = init_pci_bridge(pi); if (err) { printk(KERN_ERR "%s %s: init_pci_bridge failed!\n", pi->name, __FUNCTION__); goto out; } /* init interrupt engine */ if (request_irq(pi->irq, (void*)xhfc_interrupt, SA_SHIRQ, "XHFC", pi)) { printk(KERN_WARNING "%s %s: couldn't get interrupt %d\n", pi->name, __FUNCTION__, pi->irq); pi->irq = 0; err = -EIO; goto out; } err = 0; for (i=0; idriver_data.num_xhfcs; i++) { pi->xhfc[i].pi = pi; pi->xhfc[i].chipidx = i; err |= setup_instance(&pi->xhfc[i]); } if (!err) { card_cnt++; return (0); } else { goto out; } out: if (pi->xhfc) kfree(pi->xhfc); if (pi) kfree(pi); return (err); }; /**************************************/ /* PCI hotplug interface: remove card */ /**************************************/ static void __devexit xhfc_pci_remove(struct pci_dev *pdev) { xhfc_pi *pi = pci_get_drvdata(pdev); printk(KERN_INFO "%s %s: removing card\n", pi->name, __FUNCTION__); release_card(pi); card_cnt--; pci_disable_device(pdev); return; }; static struct pci_device_id xhfc_ids[] = { {.vendor = PCI_VENDOR_ID_CCD, .device = 0xA003, .subvendor = 0x1397, .subdevice = 0xA003, .driver_data = (unsigned long) &((pi_params) {1, "XHFC Evaluation Board"}), }, {} }; /***************/ /* Module init */ /***************/ static struct pci_driver xhfc_driver = { name:DRIVER_NAME, probe:xhfc_pci_probe, remove:__devexit_p(xhfc_pci_remove), id_table:xhfc_ids, }; MODULE_DEVICE_TABLE(pci, xhfc_ids); #endif // BRIDGE_PCI2PI /***************/ /* Module init */ /***************/ static int __init xhfc_init(void) { int err; printk(KERN_INFO "XHFC: %s driver Rev. %s (debug=%i)\n", __FUNCTION__, mISDN_getrev(xhfc_rev), debug); #ifdef MODULE hw_mISDNObj.owner = THIS_MODULE; #endif INIT_LIST_HEAD(&hw_mISDNObj.ilist); spin_lock_init(&hw_mISDNObj.lock); hw_mISDNObj.name = DRIVER_NAME; hw_mISDNObj.own_ctrl = xhfc_manager; hw_mISDNObj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0; hw_mISDNObj.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0 | ISDN_PID_L1_NT_S0; hw_mISDNObj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; hw_mISDNObj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; card_cnt = 0; if ((err = mISDN_register(&hw_mISDNObj))) { printk(KERN_ERR "XHFC: can't register xhfc error(%d)\n", err); goto out; } #if BRIDGE == BRIDGE_PCI2PI err = pci_register_driver(&xhfc_driver); if (err < 0) { goto out; } #endif printk(KERN_INFO "XHFC: %d cards installed\n", card_cnt); #if !defined(CONFIG_HOTPLUG) if (err == 0) { err = -ENODEV; pci_unregister_driver(&xhfc_driver); goto out; } #endif mISDN_module_register(THIS_MODULE); return 0; out: return (err); } static void __exit xhfc_cleanup(void) { int err; mISDN_module_unregister(THIS_MODULE); #if BRIDGE == BRIDGE_PCI2PI pci_unregister_driver(&xhfc_driver); #endif if ((err = mISDN_unregister(&hw_mISDNObj))) { printk(KERN_ERR "XHFC: can't unregister xhfc, error(%d)\n", err); } printk(KERN_INFO "%s: driver removed\n", __FUNCTION__); } module_init(xhfc_init); module_exit(xhfc_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/plci.c0000644000000000000500000000627511110524073017465 0ustar rootsrc/* $Id: plci.c,v 1.12 2006/03/06 12:52:07 keil Exp $ * */ #include "m_capi.h" #include "dss1.h" #include "helper.h" #include "debug.h" #define plciDebug(plci, lev, fmt, args...) \ capidebug(lev, fmt, ## args) void plciInit(Controller_t *contr) { Plci_t *plci = contr->plcis; int i; for (i = 0; i < contr->maxplci; i++) { memset(plci, 0, sizeof(Plci_t)); plci->addr = ((i + 1) << 8) | contr->addr; plci->l3id = MISDN_ID_NONE; INIT_LIST_HEAD(&plci->AppPlcis); plci->contr = contr; if (contr->debug & CAPI_DBG_PLCI) printk(KERN_DEBUG "%s: %p PLCI(%x) l3id(%x)\n", __FUNCTION__, plci, plci->addr, plci->l3id); plci++; } } void plciHandleSetupInd(Plci_t *plci, int pr, Q931_info_t *qi) { __u16 CIPValue; Application_t *appl; AppPlci_t *aplci; struct list_head *item, *next; if (!qi || !plci->contr) { int_error(); return; } CIPValue = q931CIPValue(qi); list_for_each_safe(item, next, &plci->contr->Applications) { appl = (Application_t *)item; if (test_bit(APPL_STATE_RELEASE, &appl->state)) continue; if (listenHandle(appl, CIPValue)) { aplci = ApplicationNewAppPlci(appl, plci); if (!aplci) { int_error(); break; } AppPlci_l3l4(aplci, pr, qi); } } if (plci->nAppl == 0) { struct sk_buff *skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE); u_char cause[4] = {IE_CAUSE,2,0x80,0xd8}; /* incompatible destination */ if (skb) { mISDN_AddvarIE(skb,cause); plciL4L3(plci, CC_RELEASE_COMPLETE | REQUEST, skb); } ControllerReleasePlci(plci); } } int plci_l3l4(Plci_t *plci, int pr, struct sk_buff *skb) { AppPlci_t *aplci; Q931_info_t *qi; struct list_head *item, *next; if (skb->len) qi = (Q931_info_t *)skb->data; else qi = NULL; switch (pr) { 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; AppPlci_l3l4(aplci, pr, qi); } break; } dev_kfree_skb(skb); return(0); } AppPlci_t * getAppPlci4Id(Plci_t *plci, __u16 appId) { struct list_head *item; AppPlci_t *aplci; list_for_each(item, &plci->AppPlcis) { aplci = (AppPlci_t *)item; if (appId == aplci->appl->ApplId) return(aplci); } return(NULL); } void plciAttachAppPlci(Plci_t *plci, AppPlci_t *aplci) { AppPlci_t *test = getAppPlci4Id(plci, aplci->appl->ApplId); if (test) { int_error(); return; } list_add(&aplci->head, &plci->AppPlcis); plci->nAppl++; } void plciDetachAppPlci(Plci_t *plci, AppPlci_t *aplci) { aplci->plci = NULL; list_del_init(&aplci->head); plci->nAppl--; if (!plci->nAppl) ControllerReleasePlci(plci); } void plciNewCrReq(Plci_t *plci) { plciL4L3(plci, CC_NEW_CR | REQUEST, NULL); } int plciL4L3(Plci_t *plci, __u32 prim, struct sk_buff *skb) { #define MY_RESERVE 8 int err; if (!skb) { if (!(skb = alloc_skb(MY_RESERVE, GFP_ATOMIC))) { printk(KERN_WARNING "%s: no skb size %d\n", __FUNCTION__, MY_RESERVE); return(-ENOMEM); } else skb_reserve(skb, MY_RESERVE); } err = ControllerL4L3(plci->contr, prim, plci->l3id, skb); if (err) dev_kfree_skb(skb); return(err); } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/xhfc_su.h0000644000000000000500000001364211135651702020205 0ustar rootsrc/* $Id: xhfc_su.h,v 1.6 2006/03/17 07:43:41 mbachem Exp $ * * mISDN driver for Colognechip xHFC chip * * Authors : Martin Bachem, Joerg Ciesielski * Contact : info@colognechip.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _XHFC_SU_H_ #define _XHFC_SU_H_ #include #include "channel.h" #include "xhfc24succ.h" #define DRIVER_NAME "XHFC" #ifndef CHIP_ID_2S4U #define CHIP_ID_2S4U 0x62 #endif #ifndef CHIP_ID_4SU #define CHIP_ID_4SU 0x63 #endif #ifndef CHIP_ID_1SU #define CHIP_ID_1SU 0x60 #endif #ifndef CHIP_ID_2SU #define CHIP_ID_2SU 0x61 #endif /* define bridge for chip register access */ #define BRIDGE_UNKWOWN 0 #define BRIDGE_PCI2PI 1 /* used at Cologne Chip AG's Evaluation Card */ #define BRIDGE BRIDGE_PCI2PI #define MAX_PORT 4 #define CHAN_PER_PORT 4 /* D, B1, B2, PCM */ #define MAX_CHAN MAX_PORT * CHAN_PER_PORT /* flags in _u16 port mode */ #define PORT_UNUSED 0x0000 #define PORT_MODE_NT 0x0001 #define PORT_MODE_TE 0x0002 #define PORT_MODE_S0 0x0004 #define PORT_MODE_UP 0x0008 #define PORT_MODE_EXCH_POL 0x0010 #define PORT_MODE_LOOP_B1 0x0020 #define PORT_MODE_LOOP_B2 0x0040 #define PORT_MODE_LOOP_D 0x0080 #define NT_TIMER 0x8000 #define PORT_MODE_LOOPS 0xE0 /* mask port mode Loop B1/B2/D */ /* 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 0x00 #define HFC_L1_FORCE_DEACTIVATE_TE 0x01 #define HFC_L1_ACTIVATE_NT 0x02 #define HFC_L1_DEACTIVATE_NT 0x03 #define HFC_L1_TESTLOOP_B1 0x04 #define HFC_L1_TESTLOOP_B2 0x05 #define HFC_L1_TESTLOOP_D 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 { __u8 num_xhfcs; char *device_name; } pi_params; struct _xhfc_t; struct _xhfc_pi; /* port struct for each S/U port */ typedef struct { int idx; struct _xhfc_t * xhfc; char name[20]; /* XHFC_PI0_0_0 = ProcessorInterface no. 0, Chip no. 0, port no 0 */ __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; /**********************/ /* hardware structure */ /**********************/ typedef struct _xhfc_t { char name[15]; /* XHFC_PI0_0 = ProcessorInterface no. 0, Chip no. 0 */ __u8 chipnum; /* global chip number */ __u8 chipidx; /* index in pi->xhfcs[NUM_XHFCS] */ struct _xhfc_pi * pi; /* backpointer to xhfc_pi */ __u8 param_idx; /* used to access module param arrays */ struct list_head list; spinlock_t lock; struct tasklet_struct tasklet; /* interrupt bottom half */ __u8 testirq; 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; /* one for each Line intercace */ xhfc_chan_t * 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_t; /**********************/ /* hardware structure */ /**********************/ typedef struct _xhfc_pi { #if BRIDGE == BRIDGE_PCI2PI struct pci_dev *pdev; int irq; int iobase; u_char *membase; u_char *hw_membase; int cardnum; char name[10]; /* 'XHFC_PI0' = ProcessorInterface no. 0 */ pi_params driver_data; #endif xhfc_t * xhfc; } xhfc_pi; #endif /* _XHFC_SU_H_ */ mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/socket.c0000644000000000000500000001040011110524073020007 0ustar rootsrc/* * socket handling, transfer, receive-process for Voice over IP * * Author Andreas Eversberg (jolly@eversberg.eu) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include "debug.h" #include "socket.h" #include /* socket thread */ static int mISDN_socket_thread(void *data) { mISDN_socket_t *ms = (mISDN_socket_t *)data; /* make daemon */ daemonize("mISDN-socket"); allow_signal(SIGTERM); /* read loop */ while(!signal_lending(current)) { ms->iov.iov_base = ms->rcvbuf; ms->iov_len = sizeof(ms->rcvbuf); ms->oldfs = get_fs(); set_fs(KERNEL_DS); ms->rcvlen = sock_recvmsg(ms->socket, &ms->msg, sizeof(ms->rcvbuf), 0); set_fs(oldfs); if (ms->rcvlen>0) { ms->func(ms, ms->pri); } } /* show that we terminate */ ms->thread_pid = 0; /* if we got killed, signal completion */ if (ms->thread_complete) complete(&ms->thread_complete); return(0); } /* * Adds a new socket with receive process. * func will be the pointer to the receive process. * -> The packet information is included in the mISDN_socket_t. * pri will be the private data when calling receive process. * local/remote ip/port will be the tupple for sending an receiving udp data. * -> local-ip must be 0 if any local ip is accepted. */ mISDN_socket_t *mISDN_add_socket(sock_func_t *func, void *pri, u32 local_ip, u32 remote_ip, u16 local_port, u16 remote_port) { mISDN_socket_t *ms; /* alloc memory structure */ if (!(sm = vmalloc(sizeof(mISDN_sock_t)))) { printk(KERN_ERR "%s: No memory for mISDN_sock_t.\n", __FUNCTION__); return(NULL); } memset(ms, 0, sizeof(mISDN_sock_t)); ms->func = func; ms->pri = pri; /* create socket */ if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &ms->socket)) { printk(KERN_ERR "%s: Failed to create socket.\n", __FUNCTION__); mISDN_free_socket(ms); return(NULL); } /* bind socket */ ms->server.sin_family = AF_INET; ms->server.sin_addr.s_addr = (local_ip)?local_ip:INADDR_ANY; ms->server.sin_port = htons((unsigned short)local_port); if (ms->socket->ops->bind(ms->socket, (struct sockaddr *)ms->server, sizeof(ms->server))) { printk(KERN_ERR "%s: Failed to bind socket.\n", __FUNCTION__); mISDN_free_socket(ms); return(NULL); } /* check sk */ if (ms->socket->sk == NULL) { printk(KERN_ERR "%s: socket->sk == NULL\n", __FUNCTION__); return(NULL); } /* build message */ ms->msg.msg_name = &ms->client; ms->msg.msg_namelen = sizeof(ms->client); ms->msg.msg_control = NULL; ms->msg.msg_controllen = 0; ms->msg.msg_iov = &ms->iov; ms->msg.msg_iovlen = 1; /* create receive process */ if ((ms->thread_pid = kernel_thread(mISDN_socket_thread, ms, CLONE_KERNEL)) < 0) { printk(KERN_ERR "%s: Failed to create receive process.\n", __FUNCTION__); mISDN_free_socket(ms); return(NULL); } } /* * free given socket with all resources */ void mISDN_free_socket(mISDN_sock_t *ms) { if (!ms) return; /* kill thread */ if (ms->thread_pid) { DECLARE_COMPLETION(thread_complete); ms->thread_complete = &thread_complete; kill_proc(ms->thread_pid, SIGTERM, 0); wait_for_completion(&thread_complete); } /* release socket */ if (ms->socket) sock_release(ms->socket); /* free memory structure */ vfree(sm); } /* * transfer a frame via given socket */ int mISDN_send_socket(mISDN_sock_t *ms, u8 *buf, int len) { /* send packet */ ms->send_iov.iov_base = ms->sndbuf; ms->send_iov_len = sizeof(ms->sndbuf); ms->send_oldfs = get_fs(); set_fs(KERNEL_DS); ms->rcvlen = sock_recvmsg(ms->socket, &ms->msg, sizeof(ms->rcvbuf), 0); set_fs(oldfs); if (ms->rcvlen>0) { ms->func(ms, ms->pri); } } mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/socket.h0000644000000000000500000000227611110524073020030 0ustar rootsrc/* * socket handling, transfer, receive-process for Voice over IP * * Author Andreas Eversberg (jolly@eversberg.eu) * * 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; mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/w6692.h0000644000000000000500000001042111110524073017324 0ustar rootsrc/* $Id: w6692.h,v 1.1 2003/11/09 09:33:22 keil Exp $ * * Winbond W6692 specific defines * * Author Karsten Keil * based on the w6692 I4L driver from Petr Novak * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ /* Specifications of W6692 registers */ #define W_D_RFIFO 0x00 /* R */ #define W_D_XFIFO 0x04 /* W */ #define W_D_CMDR 0x08 /* W */ #define W_D_MODE 0x0c /* R/W */ #define W_D_TIMR 0x10 /* R/W */ #define W_ISTA 0x14 /* R_clr */ #define W_IMASK 0x18 /* R/W */ #define W_D_EXIR 0x1c /* R_clr */ #define W_D_EXIM 0x20 /* R/W */ #define W_D_STAR 0x24 /* R */ #define W_D_RSTA 0x28 /* R */ #define W_D_SAM 0x2c /* R/W */ #define W_D_SAP1 0x30 /* R/W */ #define W_D_SAP2 0x34 /* R/W */ #define W_D_TAM 0x38 /* R/W */ #define W_D_TEI1 0x3c /* R/W */ #define W_D_TEI2 0x40 /* R/W */ #define W_D_RBCH 0x44 /* R */ #define W_D_RBCL 0x48 /* R */ #define W_TIMR2 0x4c /* W */ #define W_L1_RC 0x50 /* R/W */ #define W_D_CTL 0x54 /* R/W */ #define W_CIR 0x58 /* R */ #define W_CIX 0x5c /* W */ #define W_SQR 0x60 /* R */ #define W_SQX 0x64 /* W */ #define W_PCTL 0x68 /* R/W */ #define W_MOR 0x6c /* R */ #define W_MOX 0x70 /* R/W */ #define W_MOSR 0x74 /* R_clr */ #define W_MOCR 0x78 /* R/W */ #define W_GCR 0x7c /* R/W */ #define W_B_RFIFO 0x80 /* R */ #define W_B_XFIFO 0x84 /* W */ #define W_B_CMDR 0x88 /* W */ #define W_B_MODE 0x8c /* R/W */ #define W_B_EXIR 0x90 /* R_clr */ #define W_B_EXIM 0x94 /* R/W */ #define W_B_STAR 0x98 /* R */ #define W_B_ADM1 0x9c /* R/W */ #define W_B_ADM2 0xa0 /* R/W */ #define W_B_ADR1 0xa4 /* R/W */ #define W_B_ADR2 0xa8 /* R/W */ #define W_B_RBCL 0xac /* R */ #define W_B_RBCH 0xb0 /* R */ #define W_XADDR 0xf4 /* R/W */ #define W_XDATA 0xf8 /* R/W */ #define W_EPCTL 0xfc /* W */ /* W6692 register bits */ #define W_D_CMDR_XRST 0x01 #define W_D_CMDR_XME 0x02 #define W_D_CMDR_XMS 0x08 #define W_D_CMDR_STT 0x10 #define W_D_CMDR_RRST 0x40 #define W_D_CMDR_RACK 0x80 #define W_D_MODE_RLP 0x01 #define W_D_MODE_DLP 0x02 #define W_D_MODE_MFD 0x04 #define W_D_MODE_TEE 0x08 #define W_D_MODE_TMS 0x10 #define W_D_MODE_RACT 0x40 #define W_D_MODE_MMS 0x80 #define W_INT_B2_EXI 0x01 #define W_INT_B1_EXI 0x02 #define W_INT_D_EXI 0x04 #define W_INT_XINT0 0x08 #define W_INT_XINT1 0x10 #define W_INT_D_XFR 0x20 #define W_INT_D_RME 0x40 #define W_INT_D_RMR 0x80 #define W_D_EXI_WEXP 0x01 #define W_D_EXI_TEXP 0x02 #define W_D_EXI_ISC 0x04 #define W_D_EXI_MOC 0x08 #define W_D_EXI_TIN2 0x10 #define W_D_EXI_XCOL 0x20 #define W_D_EXI_XDUN 0x40 #define W_D_EXI_RDOV 0x80 #define W_D_STAR_DRDY 0x10 #define W_D_STAR_XBZ 0x20 #define W_D_STAR_XDOW 0x80 #define W_D_RSTA_RMB 0x10 #define W_D_RSTA_CRCE 0x20 #define W_D_RSTA_RDOV 0x40 #define W_D_CTL_SRST 0x20 #define W_CIR_SCC 0x80 #define W_CIR_ICC 0x40 #define W_CIR_COD_MASK 0x0f #define W_PCTL_PCX 0x01 #define W_PCTL_XMODE 0x02 #define W_PCTL_OE0 0x04 #define W_PCTL_OE1 0x08 #define W_PCTL_OE2 0x10 #define W_PCTL_OE3 0x20 #define W_PCTL_OE4 0x40 #define W_PCTL_OE5 0x80 #define W_B_CMDR_XRST 0x01 #define W_B_CMDR_XME 0x02 #define W_B_CMDR_XMS 0x04 #define W_B_CMDR_RACT 0x20 #define W_B_CMDR_RRST 0x40 #define W_B_CMDR_RACK 0x80 #define W_B_MODE_FTS0 0x01 #define W_B_MODE_FTS1 0x02 #define W_B_MODE_SW56 0x04 #define W_B_MODE_BSW0 0x08 #define W_B_MODE_BSW1 0x10 #define W_B_MODE_EPCM 0x20 #define W_B_MODE_ITF 0x40 #define W_B_MODE_MMS 0x80 #define W_B_EXI_XDUN 0x01 #define W_B_EXI_XFR 0x02 #define W_B_EXI_RDOV 0x10 #define W_B_EXI_RME 0x20 #define W_B_EXI_RMR 0x40 #define W_B_STAR_XBZ 0x01 #define W_B_STAR_XDOW 0x04 #define W_B_STAR_RMB 0x10 #define W_B_STAR_CRCE 0x20 #define W_B_STAR_RDOV 0x40 #define W_B_RBCH_LOV 0x20 /* W6692 Layer1 commands */ #define W_L1CMD_ECK 0x00 #define W_L1CMD_RST 0x01 #define W_L1CMD_SCP 0x04 #define W_L1CMD_SSP 0x02 #define W_L1CMD_AR8 0x08 #define W_L1CMD_AR10 0x09 #define W_L1CMD_EAL 0x0a #define W_L1CMD_DRC 0x0f /* W6692 Layer1 indications */ #define W_L1IND_CE 0x07 #define W_L1IND_DRD 0x00 #define W_L1IND_LD 0x04 #define W_L1IND_ARD 0x08 #define W_L1IND_TI 0x0a #define W_L1IND_ATI 0x0b #define W_L1IND_AI8 0x0c #define W_L1IND_AI10 0x0d #define W_L1IND_CD 0x0f /* FIFO thresholds */ #define W_D_FIFO_THRESH 64 #define W_B_FIFO_THRESH 64 mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/x25_dte.c0000644000000000000500000010520511110524073020001 0ustar rootsrc/* $Id: x25_dte.c,v 1.13 2007/02/13 10:43:45 crich Exp $ * * Linux modular ISDN subsystem, mISDN * X.25/X.31 Layer3 for DTE mode * * Author Karsten Keil (kkeil@suse.de) * * Copyright 2003 by Karsten Keil (kkeil@suse.de) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ #include #include "core.h" #include "x25_l3.h" #include "helper.h" #include "debug.h" static int debug = 0; static mISDNobject_t x25dte_obj; static char *mISDN_dte_revision = "$Revision: 1.13 $"; /* local prototypes */ static x25_channel_t * dte_create_channel(x25_l3_t *, int, u_char, __u16, int, u_char *); /* X.25 Restart state machine */ static struct Fsm dte_rfsm = {NULL, 0, 0, NULL, NULL}; /* X.25 connection state machine */ static struct Fsm dte_pfsm = {NULL, 0, 0, NULL, NULL}; /* X.25 Flowcontrol state machine */ static struct Fsm dte_dfsm = {NULL, 0, 0, NULL, NULL}; /* X.25 Restart state machine implementation */ static void r_llready(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_R1); if (test_and_clear_bit(X25_STATE_ESTABLISH, &l3->state)) { mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); } } static void r_r0_restart_l3(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; if (arg) memcpy(l3->cause, arg, 2); else memset(l3->cause, 0, 2); test_and_set_bit(X25_STATE_ESTABLISH, &l3->state); mISDN_FsmEvent(&l3->l2l3m, EV_L3_ESTABLISH_REQ, NULL); } static void r_restart_l3(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; if (arg) memcpy(l3->cause, arg, 2); mISDN_FsmChangeState(fi, ST_R2); l3->TRval = T20_VALUE; l3->TRrep = R20_VALUE; X25sendL3frame(NULL, l3, X25_PTYPE_RESTART, 2, l3->cause); mISDN_FsmAddTimer(&l3->TR, l3->TRval, EV_L3_RESTART_TOUT, NULL, 0); } static void r_restart_ind(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_R3); X25sendL3frame(NULL, l3, X25_PTYPE_RESTART_CNF, 0, NULL); mISDN_FsmChangeState(fi, ST_R1); mISDN_FsmDelTimer(&l3->TR, 0); X25_restart(l3); } static void r_restart_cnf(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; mISDN_FsmChangeState(fi, ST_R1); mISDN_FsmDelTimer(&l3->TR, 0); X25_restart(l3); } static void r_restart_cnf_err(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; u_char cause[2] = {0, 17}; if (fi->state == ST_R3) cause[1] = 19; mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, cause); } static void r_timeout(struct FsmInst *fi, int event, void *arg) { x25_l3_t *l3 = fi->userdata; if (l3->TRrep) { X25sendL3frame(NULL, l3, X25_PTYPE_RESTART, 2, l3->cause); mISDN_FsmRestartTimer(&l3->TR, l3->TRval, EV_L3_RESTART_TOUT, NULL, 0); l3->TRrep--; } else { mISDN_FsmDelTimer(&l3->TR, 0); mISDN_FsmChangeState(fi, ST_R1); /* signal failure */ } } /* *INDENT-OFF* */ static struct FsmNode RFnList[] = { {ST_R0, EV_LL_READY, r_llready}, {ST_R0, EV_L3_RESTART_REQ, r_r0_restart_l3}, {ST_R1, EV_LL_READY, r_llready}, {ST_R1, EV_L3_RESTART_REQ, r_restart_l3}, {ST_R1, EV_L2_RESTART, r_restart_ind}, {ST_R1, EV_L2_RESTART_CNF, r_restart_cnf_err}, {ST_R2, EV_L3_RESTART_REQ, r_restart_l3}, {ST_R2, EV_L2_RESTART, r_restart_cnf}, {ST_R2, EV_L2_RESTART_CNF, r_restart_cnf}, {ST_R2, EV_L3_RESTART_TOUT, r_timeout}, {ST_R2, EV_LL_READY, r_llready}, {ST_R3, EV_L3_RESTART_REQ, r_restart_l3}, {ST_R3, EV_L2_RESTART_CNF, r_restart_cnf_err}, {ST_R3, EV_LL_READY, r_llready}, }; /* *INDENT-ON* */ #define R_FN_COUNT (sizeof(RFnList)/sizeof(struct FsmNode)) /* X.25 connection state machine */ static void p_p0_ready(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; if (test_bit(X25_STATE_PERMANENT, &l3c->state)) { mISDN_FsmChangeState(fi, ST_P4); /* connect */ mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL); } else { mISDN_FsmChangeState(fi, ST_P1); if (test_bit(X25_STATE_ORGINATE, &l3c->state)) mISDN_FsmEvent(fi, EV_L3_OUTGOING_CALL, NULL); } } static void X25_clear_connection(x25_channel_t *l3c, struct sk_buff *skb, int reason) { if (skb) { if (test_bit(X25_STATE_DBIT, &l3c->state)) reason |= 0x10000; X25sendL4frame(l3c, l3c->l3, CAPI_DISCONNECT_B3_IND, reason, skb->len, skb->data); } else X25sendL4frame(l3c, l3c->l3, CAPI_DISCONNECT_B3_IND, reason, 0, NULL); } static void p_p0_outgoing(struct FsmInst *fi, int event, void *arg) { } static void p_ready(struct FsmInst *fi, int event, void *arg) { // x25_channel_t *l3c = fi->userdata; } static void p_outgoing(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; struct sk_buff *skb = arg; mISDN_FsmChangeState(fi, ST_P2); if (skb) X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL, skb->len, skb->data); else X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL, l3c->ncpi_len, l3c->ncpi_data); mISDN_FsmAddTimer(&l3c->TP, T21_VALUE, EV_L3_CALL_TOUT, NULL, 0); } static void p_incoming(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; int flg = 0; if (test_bit(X25_STATE_DBIT, &l3c->state)) flg = 0x10000; mISDN_FsmChangeState(fi, ST_P3); X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_IND, flg, l3c->ncpi_len, l3c->ncpi_data); } static void p_call_accept(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; struct sk_buff *skb = arg; if (skb) X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL_CNF, skb->len, skb->data); else X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL_CNF, 0, NULL); mISDN_FsmChangeState(fi, ST_P4); mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL); X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, 0, 0, NULL); } static void p_collision(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_P5); } static void p_connect(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; struct sk_buff *skb = arg; int flg = 0; mISDN_FsmDelTimer(&l3c->TP, 0); mISDN_FsmChangeState(fi, ST_P4); if (test_bit(X25_STATE_DBIT, &l3c->state)) flg = 0x10000; mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL); if (skb) X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, flg, skb->len, skb->data); else X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, flg, 0, NULL); } static void p_call_timeout(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; mISDN_FsmChangeState(fi, ST_P6); l3c->cause[0] = 0; l3c->cause[1] = 49; l3c->TPval = T23_VALUE; l3c->TPrep = R23_VALUE; mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0); X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause); } static void p_clear_ind(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; mISDN_FsmChangeState(fi, ST_P7); mISDN_FsmDelTimer(&l3c->TP, 0); X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR_CNF, 0, NULL); mISDN_FsmChangeState(fi, ST_P1); X25_clear_connection(l3c, arg, 0); } static void p_clear_cnf(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; mISDN_FsmChangeState(fi, ST_P1); mISDN_FsmDelTimer(&l3c->TP, 0); X25_clear_connection(l3c, arg, 0); } static void p_clear_timeout(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; if (l3c->TPrep) { mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0); X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause); } else { l3c->cause[0] = 0; l3c->cause[0] = 50; mISDN_FsmChangeState(fi, ST_P1); X25_clear_connection(l3c, NULL, 0x3303); } } static void p_clearing_req(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; struct sk_buff *skb = arg; mISDN_FsmChangeState(fi, ST_P6); l3c->TPval = T23_VALUE; l3c->TPrep = R23_VALUE; mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0); if (skb) { if (skb->len >= 2) { memcpy(l3c->cause, skb->data, 2); X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, skb->len, skb->data); return; } l3c->cause[0] = 0; l3c->cause[1] = 0; } X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause); } static void p_invalid_pkt(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; l3c->cause[0] = 0; switch(fi->state) { case ST_P1: l3c->cause[1] = 20; break; case ST_P2: l3c->cause[1] = 21; break; case ST_P3: l3c->cause[1] = 22; break; case ST_P4: l3c->cause[1] = 23; break; case ST_P5: l3c->cause[1] = 24; break; case ST_P6: l3c->cause[1] = 25; break; case ST_P7: l3c->cause[1] = 26; break; default: l3c->cause[1] = 16; break; } X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause); } /* *INDENT-OFF* */ static struct FsmNode PFnList[] = { {ST_P0, EV_L3_READY, p_p0_ready}, {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_L2_INCOMING_CALL, p_incoming}, {ST_P1, EV_L2_CLEAR, p_clear_ind}, {ST_P1, EV_L2_CALL_CNF, p_invalid_pkt}, {ST_P1, EV_L2_CLEAR_CNF, p_invalid_pkt}, {ST_P1, EV_L2_INVALPKT, p_invalid_pkt}, {ST_P1, EV_L3_CLEARING, p_clearing_req}, {ST_P2, EV_L2_INCOMING_CALL, p_collision}, {ST_P2, EV_L2_CALL_CNF, p_connect}, {ST_P2, EV_L2_CLEAR, p_clear_ind}, {ST_P2, EV_L3_CALL_TOUT, p_call_timeout}, {ST_P2, EV_L2_CLEAR_CNF, p_invalid_pkt}, {ST_P2, EV_L2_INVALPKT, p_invalid_pkt}, {ST_P2, EV_L3_CLEARING, p_clearing_req}, {ST_P3, EV_L3_CALL_ACCEPT, p_call_accept}, {ST_P3, EV_L2_CLEAR, p_clear_ind}, {ST_P3, EV_L2_INCOMING_CALL, p_invalid_pkt}, {ST_P3, EV_L2_CALL_CNF, p_invalid_pkt}, {ST_P3, EV_L2_CLEAR_CNF, p_invalid_pkt}, {ST_P3, EV_L2_INVALPKT, p_invalid_pkt}, {ST_P3, EV_L3_CLEARING, p_clearing_req}, {ST_P4, EV_L2_CLEAR, p_clear_ind}, {ST_P4, EV_L2_INCOMING_CALL, p_invalid_pkt}, {ST_P4, EV_L2_CALL_CNF, p_invalid_pkt}, {ST_P4, EV_L2_CLEAR_CNF, p_invalid_pkt}, {ST_P4, EV_L3_CLEARING, p_clearing_req}, {ST_P5, EV_L2_CALL_CNF, p_connect}, {ST_P5, EV_L3_CALL_ACCEPT, p_call_accept}, {ST_P5, EV_L2_CLEAR, p_clear_ind}, {ST_P5, EV_L3_CALL_TOUT, p_call_timeout}, {ST_P5, EV_L2_INCOMING_CALL, p_invalid_pkt}, {ST_P5, EV_L2_CLEAR_CNF, p_invalid_pkt}, {ST_P5, EV_L2_INVALPKT, p_invalid_pkt}, {ST_P5, EV_L3_CLEARING, p_clearing_req}, {ST_P6, EV_L2_CLEAR_CNF, p_clear_cnf}, {ST_P6, EV_L3_CLEAR_TOUT, p_clear_timeout}, {ST_P6, EV_L3_CLEARING, p_clearing_req}, {ST_P7, EV_L2_INCOMING_CALL, p_invalid_pkt}, {ST_P7, EV_L2_CALL_CNF, p_invalid_pkt}, {ST_P7, EV_L2_CLEAR_CNF, p_invalid_pkt}, {ST_P7, EV_L2_INVALPKT, p_invalid_pkt}, }; /* *INDENT-ON* */ #define P_FN_COUNT (sizeof(PFnList)/sizeof(struct FsmNode)) static void d_connect(struct FsmInst *fi, int event, void *arg) { mISDN_FsmChangeState(fi, ST_D1); } static void d_reset_req(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; struct sk_buff *skb = arg; mISDN_FsmChangeState(fi, ST_D2); l3c->TDval = T22_VALUE; l3c->TDrep = R22_VALUE; mISDN_FsmAddTimer(&l3c->TD, l3c->TDval, EV_L3_RESET_TOUT, NULL, 0); if (skb) { if (skb->len >= 2) { memcpy(l3c->cause, skb->data, 2); X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, skb->len, skb->data); return; } l3c->cause[0] = 0; l3c->cause[1] = 0; } X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause); } static void d_reset_ind(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; mISDN_FsmChangeState(fi, ST_D3); X25_reset_channel(l3c, arg); mISDN_FsmChangeState(fi, ST_D1); /* TODO normal operation trigger */ } static void d_reset_cnf(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; mISDN_FsmDelTimer(&l3c->TD, 0); X25_reset_channel(l3c, NULL); /* TODO normal opration trigger */ mISDN_FsmChangeState(fi, ST_D1); } static void d_reset_cnf_err(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; if (arg) memcpy(l3c->cause, arg, 2); mISDN_FsmChangeState(fi, ST_D2); l3c->TDval = T22_VALUE; l3c->TDrep = R22_VALUE; X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause); mISDN_FsmAddTimer(&l3c->TD, l3c->TDval, EV_L3_RESET_TOUT, NULL, 0); } static void d_reset_timeout(struct FsmInst *fi, int event, void *arg) { x25_channel_t *l3c = fi->userdata; if (l3c->TDrep) { X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause); l3c->TDrep--; } else { l3c->cause[0] = 0; l3c->cause[1] = 51; mISDN_FsmChangeState(fi, ST_D0); if (test_bit(X25_STATE_PERMANENT, &l3c->state)) X25_clear_connection(l3c, NULL, 0x3303); else mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, NULL); } } /* *INDENT-OFF* */ static struct FsmNode DFnList[] = { {ST_D0, EV_L3_CONNECT, d_connect}, {ST_D1, EV_L2_RESET, d_reset_ind}, {ST_D1, EV_L2_RESET_CNF, d_reset_cnf_err}, {ST_D1, EV_L3_RESETING, d_reset_req}, {ST_D2, EV_L2_RESET, d_reset_cnf}, {ST_D2, EV_L2_RESET_CNF, d_reset_cnf}, {ST_D3, EV_L2_RESET_CNF, d_reset_cnf_err}, {ST_D3, EV_L3_RESETING, d_reset_req}, {ST_D1, EV_L3_RESET_TOUT, d_reset_timeout}, }; /* *INDENT-ON* */ #define D_FN_COUNT (sizeof(DFnList)/sizeof(struct FsmNode)) static int got_diagnositic(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel) { /* not implemented yet */ return(X25_ERRCODE_DISCARD); } static int got_register(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel) { /* not implemented yet */ return(X25_ERRCODE_DISCARD); } static int got_register_cnf(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel) { /* not implemented yet */ return(X25_ERRCODE_DISCARD); } static int dte_data_ind_d(x25_channel_t *chan, struct sk_buff *skb, u_char gfi, u_char ptype) { int pr_m, ps, event; u_char ptype_s = ptype; if ((ptype & 1) == 0) ptype = X25_PTYPE_DATA; else { if ((!test_bit(X25_STATE_MOD128, &chan->state)) && !test_bit(X25_STATE_MOD32768, &chan->state)) ptype = ptype & 0x1f; if ((ptype != X25_PTYPE_RR) && (ptype != X25_PTYPE_RNR) && (ptype != X25_PTYPE_REJ)) ptype = ptype_s; } switch (ptype) { case X25_PTYPE_RESET: event = EV_L2_RESET; break; case X25_PTYPE_RESET_CNF: event = EV_L2_RESET_CNF; break; case X25_PTYPE_NOTYPE: chan->cause[0] = 0; chan->cause[1] = 38; event = EV_L3_RESETING; break; case X25_PTYPE_RESTART: case X25_PTYPE_RESTART_CNF: chan->cause[0] = 0; chan->cause[1] = 41; event = EV_L3_RESETING; break; case X25_PTYPE_INTERRUPT: case X25_PTYPE_INTERRUPT_CNF: case X25_PTYPE_DATA: case X25_PTYPE_RR: case X25_PTYPE_RNR: case X25_PTYPE_REJ: if (chan->x25d.state == ST_D2) return(X25_ERRCODE_DISCARD); else if (chan->x25d.state == ST_D3) { chan->cause[0] = 0; chan->cause[1] = 29; event = EV_L3_RESETING; } else event = -1; break; default: /* unknown paket type */ chan->cause[0] = 0; chan->cause[1] = 33; event = EV_L3_RESETING; break; } if (event != -1) { if (event == EV_L3_RESETING) mISDN_FsmEvent(&chan->x25d, event, NULL); else mISDN_FsmEvent(&chan->x25d, event, skb); return(X25_ERRCODE_DISCARD); } switch (ptype) { case X25_PTYPE_INTERRUPT: if (test_and_set_bit(X25_STATE_DXE_INTSENT, &chan->state)) { chan->cause[0] = 0; chan->cause[1] = 44; mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); } else { // X25_got_interrupt(chan, skb); } break; case X25_PTYPE_INTERRUPT_CNF: if (!test_and_clear_bit(X25_STATE_DTE_INTSENT, &chan->state)) { chan->cause[0] = 0; chan->cause[1] = 43; mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); } break; case X25_PTYPE_RR: case X25_PTYPE_RNR: case X25_PTYPE_REJ: pr_m = X25_get_and_test_pr(chan, ptype_s, skb); if (pr_m < 0) { chan->cause[0] = 0; chan->cause[1] = -pr_m; mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); } else if (skb->len) { chan->cause[0] = 0; chan->cause[1] = 39; mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); } else { if (ptype == X25_PTYPE_RR) { test_and_clear_bit(X25_STATE_DXE_RNR, &chan->state); if (X25_cansend(chan)) X25_invoke_sending(chan); } else if (ptype == X25_PTYPE_RNR) { if (!test_and_set_bit(X25_STATE_DXE_RNR, &chan->state)) { /* action for DXE RNR */ } } else { /* TODO REJ */ } } break; case X25_PTYPE_DATA: ps = X25_get_and_test_ps(chan, ptype_s, skb); if (ps == -38) pr_m = ps; else pr_m = X25_get_and_test_pr(chan, ptype_s, skb); if (pr_m < 0) { chan->cause[0] = 0; chan->cause[1] = -pr_m; mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL); } else if (ps < 0) { chan->cause[0] = 0; chan->cause[1] = -ps; 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) flag |= CAPI_FLAG_DELIVERCONF; return(X25_receive_data(chan, ps, flag, skb)); } break; } return(X25_ERRCODE_DISCARD); } static int dte_data_ind_p(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel, u_char ptype) { x25_channel_t *chan = X25_get_channel(l3, channel); int event, ret = X25_ERRCODE_DISCARD; if (ptype == X25_PTYPE_CALL) { if (!chan) chan = dte_create_channel(l3, X25_CHANNEL_INCOMING, gfi, channel, skb->len, skb->data); if (chan) { mISDN_FsmEvent(&chan->x25p, EV_L2_INCOMING_CALL, skb); } else { ret = 36; /* unassigned channel */ } return(ret); } if (!chan) return(36); /* unassigned channel */ switch (ptype) { case X25_PTYPE_CALL_CNF: event = EV_L2_CALL_CNF; break; case X25_PTYPE_CLEAR: event = EV_L2_CLEAR; break; case X25_PTYPE_CLEAR_CNF: event = EV_L2_CLEAR_CNF; break; case X25_PTYPE_NOTYPE: chan->cause[0] = 0; chan->cause[1] = 38; event = EV_L3_CLEARING; break; case X25_PTYPE_RESTART: case X25_PTYPE_RESTART_CNF: chan->cause[0] = 0; chan->cause[1] = 41; event = EV_L3_CLEARING; break; case X25_PTYPE_RESET: case X25_PTYPE_RESET_CNF: case X25_PTYPE_INTERRUPT: case X25_PTYPE_INTERRUPT_CNF: event = EV_L2_INVALPKT; break; default: if ((ptype & 1) == 0) { event = EV_L2_INVALPKT; break; } if (!test_bit(X25_STATE_MOD128, &chan->state) && !test_bit(X25_STATE_MOD32768, &chan->state)) event = ptype & 0x1f; else event = ptype; if ((event == X25_PTYPE_RR) || (event == X25_PTYPE_RNR) || (event == X25_PTYPE_REJ)) { event = EV_L2_INVALPKT; break; } /* unknown paket type */ chan->cause[0] = 0; chan->cause[1] = 33; event = EV_L3_CLEARING; break; } if (chan->x25p.state == ST_P4) { if ((event == EV_L2_INVALPKT) || (event == EV_L3_CLEARING)) return(dte_data_ind_d(chan, skb, gfi, ptype)); } if (event == EV_L3_CLEARING) mISDN_FsmEvent(&chan->x25p, event, NULL); else mISDN_FsmEvent(&chan->x25p, event, skb); return(ret); } static int dte_data_ind_r(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel, u_char ptype) { int ret = X25_ERRCODE_DISCARD; if (ptype == X25_PTYPE_NOTYPE) { if (channel) { if (l3->x25r.state == ST_R1) return(dte_data_ind_p(l3, skb, gfi, channel, ptype)); if (l3->x25r.state == ST_R2) { l3->cause[0] = 0; l3->cause[1] = 38; mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); } } else { if (l3->x25r.state == ST_R1) ret = 38; // packet too short else if (l3->x25r.state == ST_R3) { l3->cause[0] = 0; l3->cause[1] = 38; mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); } } return(ret); } if ((ptype == X25_PTYPE_RESTART) || (ptype == X25_PTYPE_RESTART_CNF)) { if (channel) { if (l3->x25r.state == ST_R1) return(dte_data_ind_p(l3, skb, gfi, channel, ptype)); else if (l3->x25r.state == ST_R3) { l3->cause[0] = 0; l3->cause[1] = 41; mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); } return(ret); } if (ptype == X25_PTYPE_RESTART) mISDN_FsmEvent(&l3->x25r, EV_L2_RESTART, skb); else mISDN_FsmEvent(&l3->x25r, EV_L2_RESTART_CNF, skb); } else { if (l3->x25r.state == ST_R1) return(dte_data_ind_p(l3, skb, gfi, channel, ptype)); if (l3->x25r.state == ST_R3) { l3->cause[0] = 0; l3->cause[1] = 19; mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL); } } return(ret); } static int dte_dl_data_ind(x25_l3_t *l3, struct sk_buff *skb) { int ret; __u16 channel; u_char gfi, ptype = 0; ret = X25_get_header(l3, skb, &gfi, &channel, &ptype); if (ret && (ptype != X25_PTYPE_NOTYPE)) { if (test_bit(X25_STATE_DTEDTE, &l3->state)) X25_send_diagnostic(l3, skb, ret, channel); dev_kfree_skb(skb); return(0); } switch(ptype) { case X25_PTYPE_DIAGNOSTIC: ret = got_diagnositic(l3, skb, gfi, channel); break; case X25_PTYPE_REGISTER: ret = got_register(l3, skb, gfi, channel); break; case X25_PTYPE_REGISTER_CNF: ret = got_register_cnf(l3, skb, gfi, channel); break; default: ret = dte_data_ind_r(l3, skb, gfi, channel, ptype); break; } if (ret == X25_ERRCODE_DISCARD) { dev_kfree_skb(skb); ret = 0; } else if (ret) { if (test_bit(X25_STATE_DTEDTE, &l3->state)) { if (ptype != X25_PTYPE_NOTYPE) { if (test_bit(X25_STATE_MOD32768, &l3->state)) skb_push(skb, 4); else skb_push(skb, 3); } X25_send_diagnostic(l3, skb, ret, channel); } dev_kfree_skb(skb); ret = 0; } return(ret); } static int dte_from_down(x25_l3_t *l3, struct sk_buff *skb, mISDN_head_t *hh) { int ret = -EINVAL; if (l3->debug) printk(KERN_DEBUG "%s: prim(%x) dinfo(%x)\n", __FUNCTION__, hh->prim, hh->dinfo); switch(hh->prim) { case DL_DATA_IND: ret = dte_dl_data_ind(l3, skb); break; case DL_DATA | CONFIRM: break; case DL_ESTABLISH_CNF: ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_ESTABLISH_CNF, NULL); if (ret) { int_error(); } ret = 0; dev_kfree_skb(skb); break; case DL_ESTABLISH_IND: ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_ESTABLISH_IND, NULL); if (ret) { int_error(); } ret = 0; dev_kfree_skb(skb); break; case DL_RELEASE_CNF: ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_RELEASE_CNF, NULL); if (ret) { int_error(); } ret = 0; dev_kfree_skb(skb); break; case DL_RELEASE_IND: ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_RELEASE_IND, NULL); if (ret) { int_error(); } ret = 0; dev_kfree_skb(skb); break; default: int_error(); break; } return(ret); } static x25_channel_t * dte_create_channel(x25_l3_t *l3, int typ, u_char flag, __u16 ch, int len, u_char *data) { 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 */ if (l3->B3cfg.HOC) { for (nch = l3->B3cfg.HOC; (nch && (nch >= l3->B3cfg.LOC)); nch--) { l3c = X25_get_channel(l3, nch); if (l3c && (l3c->x25p.state == ST_P1)) { X25_realloc_ncpi_data(l3c, len, data); return(l3c); } } } if (l3->B3cfg.HTC) { for (nch = l3->B3cfg.HTC; (nch && (nch >= l3->B3cfg.LTC)); nch--) { l3c = X25_get_channel(l3, nch); if (l3c && (l3c->x25p.state == ST_P1)) { X25_realloc_ncpi_data(l3c, len, data); return(l3c); } } } /* now search for still unused channels */ nch = 0; if (l3->B3cfg.HOC) { l3c = (x25_channel_t *)1; /* if loop is not executed */ for (nch = l3->B3cfg.HOC; (nch && (nch >= l3->B3cfg.LOC)); nch--) { l3c = X25_get_channel(l3, nch); if (!l3c) break; } if (l3c) nch = 0; } if ((nch == 0) && l3->B3cfg.HTC) { l3c = (x25_channel_t *)1; /* if loop is not executed */ for (nch = l3->B3cfg.HTC; (nch && (nch >= l3->B3cfg.LTC)); nch--) { l3c = X25_get_channel(l3, nch); if (!l3c) break; } if (l3c) nch = 0; } } else { if (ch >= l3->B3cfg.LIC) /* not a permanent channel */ return(NULL); l3c = X25_get_channel(l3, nch); if (l3c) { if (test_bit(X25_STATE_PERMANENT, &l3c->state)) { if (l3c->ncci) /* allready in use */ return(NULL); else { X25_realloc_ncpi_data(l3c, len, data); return(l3c); } } } nch = ch; } if (flag & 1) flag = X25_GFI_DBIT; } else { if (!ch) /* not Reference Number procedure implemented */ return(NULL); if (l3->B3cfg.HTC) { if (ch > l3->B3cfg.HTC) return(NULL); } else if (l3->B3cfg.HIC) { if (ch > l3->B3cfg.HIC) return(NULL); } nch = ch; if (l3->B3cfg.LIC && ch < l3->B3cfg.LIC) /* permanent channel */ nch = ch; else { nch = ch; ch = 0; } } if (!nch) return(NULL); ret = new_x25_channel(l3, &l3c, nch, len, data); if (ret) return(NULL); l3c->x25p.fsm = &dte_pfsm; l3c->x25d.fsm = &dte_dfsm; if (ch) { test_and_set_bit(X25_STATE_PERMANENT, &l3c->state); if (l3c->l3->x25r.state == ST_R1) { l3c->x25p.state = ST_P4; l3c->x25d.state = ST_D1; } else { l3c->x25p.state = ST_P0; l3c->x25d.state = ST_D0; } } else { test_and_clear_bit(X25_STATE_PERMANENT, &l3c->state); if (l3c->l3->x25r.state == ST_R1) { l3c->x25p.state = ST_P1; l3c->x25d.state = ST_D0; } else { l3c->x25p.state = ST_P0; l3c->x25d.state = ST_D0; } } if (flag & X25_GFI_DBIT) test_and_set_bit(X25_STATE_DBIT, &l3c->state); else test_and_clear_bit(X25_STATE_DBIT, &l3c->state); if (flag & X25_GFI_ABIT) test_and_set_bit(X25_STATE_ABIT, &l3c->state); else test_and_clear_bit(X25_STATE_DBIT, &l3c->state); return(l3c); } static int dte_from_up(x25_l3_t *l3, struct sk_buff *skb, mISDN_head_t *hh) { x25_channel_t *l3c; __u32 addr; __u16 info = 0; int err = 0; if (skb->len < 4) { printk(KERN_WARNING "%s: skb too short (%d)\n", __FUNCTION__, skb->len); return(-EINVAL); } else { addr = CAPIMSG_U32(skb->data, 0); skb_pull(skb, 4); } if (l3->debug) printk(KERN_DEBUG "%s: addr(%x)\n", __FUNCTION__, addr); l3c = X25_get_channel4NCCI(l3, addr); switch(hh->prim) { case CAPI_DATA_B3_REQ: info = x25_data_b3_req(l3c, hh->dinfo, skb); if (info) { if (info == CAPI_SENDQUEUEFULL) { err = -EXFULL; break; } skb_trim(skb, 0); memcpy(skb_put(skb, 2), &info, 2); err = X25sendL4skb(l3c, l3, addr, CAPI_RESET_B3_CONF, hh->dinfo, skb); } else err = 0; break; case CAPI_DATA_B3_RESP: return(x25_data_b3_resp(l3c, hh->dinfo, skb)); case CAPI_CONNECT_B3_REQ: if (!l3c) { x25_ncpi_t *ncpi; if (skb->len <= 4) { // default NCPI u_char a = 0; l3c = dte_create_channel(l3, X25_CHANNEL_OUTGOING, 0, 0, 1, &a); } else { 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]); } if (l3c) l3c->ncci = addr | (l3c->lchan << 16); } if (l3c) { err = 0; if (l3->x25r.state == ST_R0) mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, "\000\000"); else err = mISDN_FsmEvent(&l3c->x25p, EV_L3_OUTGOING_CALL, NULL); if (err) info = 0x2001; /* wrong state */ else info = 0; } else info = 0x2004; /* no NCCI available */ skb_trim(skb, 0); memcpy(skb_put(skb, 2), &info, 2); err = X25sendL4skb(l3c, l3, addr, CAPI_CONNECT_B3_CONF, hh->dinfo, skb); break; case CAPI_RESET_B3_REQ: if (l3c) { if (!(l3c->ncci & 0xffff)) l3c->ncci = addr; if (skb->len <= 4) { // default NCPI l3c->cause[0] = 0; l3c->cause[1] = 0; err = mISDN_FsmEvent(&l3c->x25d, EV_L3_RESETING, NULL); } else { skb_pull(skb, 4); err = mISDN_FsmEvent(&l3c->x25d, EV_L3_RESETING, skb); skb_push(skb, 4); } if (err) info = 0x2001; else info = 0; } else info = 0x2002; skb_trim(skb, 0); memcpy(skb_put(skb, 2), &info, 2); err = X25sendL4skb(l3c, l3, addr, CAPI_RESET_B3_CONF, hh->dinfo, skb); break; case CAPI_DISCONNECT_B3_REQ: if (l3c) { if (!(l3c->ncci & 0xffff)) l3c->ncci = addr; if (skb->len <= 4) { // default NCPI l3c->cause[0] = 0; l3c->cause[1] = 0; err = mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, NULL); } else { skb_pull(skb, 4); err = mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, skb); skb_push(skb, 4); } if (err) info = 0x2001; else 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); break; case CAPI_CONNECT_B3_RESP: if (l3c) { int event = EV_L3_CLEARING; l3c->ncci = addr; if (skb->len <= 2) { printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP skb too short (%d)\n", __FUNCTION__, skb->len); skb_push(skb, 4); return(-EINVAL); } info = CAPIMSG_U16(skb->data, 0); skb_pull(skb, 2); if (info == 0) event = EV_L3_CALL_ACCEPT; if (skb->len <= 4) { // default NCPI l3c->cause[0] = 0; l3c->cause[1] = 0; err = mISDN_FsmEvent(&l3c->x25p, event, NULL); } else { skb_pull(skb, 4); err = mISDN_FsmEvent(&l3c->x25p, event, skb); } } else { skb_push(skb, 4); printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP no channel found\n", __FUNCTION__); return(-ENODEV); } dev_kfree_skb(skb); err = 0; break; case CAPI_CONNECT_B3_ACTIVE_RESP: case CAPI_RESET_B3_RESP: if (!l3c) { skb_push(skb, 4); printk(KERN_WARNING "%s: prim %x dinfo %x no channel found\n", __FUNCTION__, hh->prim, hh->dinfo); return(-ENODEV); } dev_kfree_skb(skb); err = 0; break; case CAPI_DISCONNECT_B3_RESP: if (l3c) { l3c->ncci = 0; // TODO release NCCI } else { skb_push(skb, 4); printk(KERN_WARNING "%s: CAPI_DISCONNECT_B3_RESP no channel found\n", __FUNCTION__); return(-ENODEV); } dev_kfree_skb(skb); err = 0; break; default: printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n", __FUNCTION__, hh->prim, hh->dinfo); err = -EINVAL; } 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"; #ifdef MODULE MODULE_AUTHOR("Karsten Keil"); #ifdef OLD_MODULE_PARAM MODULE_PARM(debug, "1i"); #else module_param(debug, uint, S_IRUGO | S_IWUSR); #endif #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif #endif static int 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) { printk(KERN_WARNING "l3x25_manager prim(%x) no instance\n", prim); return(err); } switch(prim) { case MGR_NEWENTITY | CONFIRM: l3_l->entity = (u_long)arg & 0xffffffff; break; case MGR_ADDSTPARA | INDICATION: { mISDN_stPara_t *stp = arg; if (stp->down_headerlen) l3_l->down_headerlen = stp->down_headerlen; if (stp->up_headerlen) l3_l->up_headerlen = stp->up_headerlen; printk(KERN_DEBUG "MGR_ADDSTPARA: (%d/%d/%d)\n", stp->maxdatalen, stp->down_headerlen, stp->up_headerlen); } break; case MGR_CLRSTPARA | INDICATION: #ifdef OBSOLETE case MGR_CLONELAYER | REQUEST: break; case MGR_CONNECT | REQUEST: return(mISDN_ConnectIF(inst, arg)); case MGR_SETIF | REQUEST: case MGR_SETIF | INDICATION: return(mISDN_SetIF(inst, arg, prim, dte_from_up, dte_from_down, l3_l)); 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) printk(KERN_DEBUG "X25_release_l3 id %x\n", l3_l->inst.st->id); X25_release_l3(l3_l); break; // case MGR_STATUS | REQUEST: // return(l3x25_status(l3x25_l, arg)); default: if (debug & DEBUG_L3X25_MGR) printk(KERN_WARNING "l3x25_manager prim %x not handled\n", prim); return(-EINVAL); } return(0); } static int x25_dte_init(void) { int err; printk(KERN_INFO "X25 DTE modul version %s\n", mISDN_getrev(mISDN_dte_revision)); #ifdef MODULE x25dte_obj.owner = THIS_MODULE; #endif 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); } else { X25_l3_init(); dte_rfsm.state_count = R_STATE_COUNT; dte_rfsm.event_count = R_EVENT_COUNT; dte_rfsm.strEvent = X25strREvent; dte_rfsm.strState = X25strRState; mISDN_FsmNew(&dte_rfsm, RFnList, R_FN_COUNT); dte_pfsm.state_count = P_STATE_COUNT; dte_pfsm.event_count = P_EVENT_COUNT; dte_pfsm.strEvent = X25strPEvent; dte_pfsm.strState = X25strPState; mISDN_FsmNew(&dte_pfsm, PFnList, P_FN_COUNT); dte_dfsm.state_count = D_STATE_COUNT; dte_dfsm.event_count = D_EVENT_COUNT; dte_dfsm.strEvent = X25strDEvent; dte_dfsm.strState = X25strDState; mISDN_FsmNew(&dte_dfsm, DFnList, D_FN_COUNT); mISDN_module_register(THIS_MODULE); } return(err); } static void x25_dte_cleanup(void) { x25_l3_t *l3, *nl3; int err; mISDN_module_unregister(THIS_MODULE); if ((err = mISDN_unregister(&x25dte_obj))) { printk(KERN_ERR "Can't unregister l3x25 error(%d)\n", err); } if(!list_empty(&x25dte_obj.ilist)) { printk(KERN_WARNING "l3x25 inst list not empty\n"); list_for_each_entry_safe(l3, nl3, &x25dte_obj.ilist, list) X25_release_l3(l3); } X25_l3_cleanup(); mISDN_FsmFree(&dte_rfsm); mISDN_FsmFree(&dte_pfsm); mISDN_FsmFree(&dte_dfsm); } module_init(x25_dte_init); module_exit(x25_dte_cleanup); mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/x25_l3.h0000644000000000000500000001474711110524073017562 0ustar rootsrc/* $Id: x25_l3.h,v 1.6 2006/03/22 18:28:33 keil Exp $ * * Layer 3 X.25 defines * * This file is (c) under GNU PUBLIC LICENSE * */ #ifndef _L3_X25_H #define _L3_X25_H #include "m_capi.h" typedef struct _x25_l3 x25_l3_t; typedef struct _x25_channel x25_channel_t; typedef struct _x25_B3_cfg x25_B3_cfg_t; typedef struct _x25_ncpi x25_ncpi_t; typedef struct _x25_ConfQueue x25_ConfQueue_t; #define DEBUG_L3X25_WARN 0x0001 #define DEBUG_L3X25_MGR 0x1000 struct _x25_B3_cfg { __u16 LIC; __u16 HIC; __u16 LTC; __u16 HTC; __u16 LOC; __u16 HOC; __u16 modulo; __u16 winsize; }; #define DEFAULT_X25_B3_CFG {0, 0, 1, 1, 0, 0, 8, 2} struct _x25_ncpi { __u8 len; __u8 Flags; __u8 Group; __u8 Channel; __u8 Contens[4]; /* Note this can be less/more bytes in use */ } __attribute__((packed)); struct _x25_ConfQueue { __u32 PktId; __u16 DataHandle; __u16 MsgId; struct sk_buff *skb; }; struct _x25_l3 { struct list_head list; mISDNinstance_t inst; struct list_head channellist; struct FsmInst l2l3m; struct FsmInst x25r; struct FsmTimer TR; int TRval; int TRrep; int entity; int next_id; struct sk_buff_head downq; int down_headerlen; int up_headerlen; int maxdatalen; x25_B3_cfg_t B3cfg; u_long state; u_int debug; u_char cause[2]; }; struct _x25_channel { struct list_head list; x25_l3_t *l3; struct FsmInst x25p; struct FsmInst x25d; struct FsmTimer TP; int TPval; int TPrep; struct FsmTimer TD; int TDval; int TDrep; __u32 ncci; u_char *ncpi_data; u_int ncpi_len; u_long state; u_int debug; u_int pr; u_int ps; u_int rps; u_int lwin; u_int rwin; u_int datasize; struct sk_buff_head dataq; x25_ConfQueue_t *confq; u_int recv_handles[CAPI_MAXDATAWINDOW]; __u16 lchan; u_char cause[2]; }; #define X25_CHANNEL_INCOMING 1 #define X25_CHANNEL_OUTGOING 2 #define X25_STATE_ORGINATE 0 #define X25_STATE_DCE 1 #define X25_STATE_DTEDTE 2 #define X25_STATE_PERMANENT 3 #define X25_STATE_MOD128 4 #define X25_STATE_MOD32768 5 #define X25_STATE_ABIT 6 #define X25_STATE_DBIT 7 #define X25_STATE_ESTABLISH 16 #define X25_STATE_DXE_INTSENT 17 #define X25_STATE_DTE_INTSENT 18 #define X25_STATE_DXE_RNR 19 #define X25_STATE_DTE_RNR 20 #define X25_MINSIZE 8 #define X25_GFI_ABIT 0x80 #define X25_GFI_DBIT 0x40 #define X25_GFI_QBIT 0x80 #define X25_MBIT 0x01 #define X25_MBIT_MOD8 0x10 #define CAPI_FLAG_QUALIFIER 0x01 #define CAPI_FLAG_MOREDATA 0x02 #define CAPI_FLAG_DELIVERCONF 0x04 #define CAPI_FLAG_EXPEDITED 0x08 #define X25_PTYPE_CALL 0x0b #define X25_PTYPE_CALL_CNF 0x0f #define X25_PTYPE_CLEAR 0x13 #define X25_PTYPE_CLEAR_CNF 0x17 #define X25_PTYPE_INTERRUPT 0x23 #define X25_PTYPE_INTERRUPT_CNF 0x27 #define X25_PTYPE_DATA 0x00 #define X25_PTYPE_RR 0x01 #define X25_PTYPE_RNR 0x05 #define X25_PTYPE_REJ 0x09 #define X25_PTYPE_RESET 0x1f #define X25_PTYPE_RESET_CNF 0x1b #define X25_PTYPE_RESTART 0xfb #define X25_PTYPE_RESTART_CNF 0xff #define X25_PTYPE_REGISTER 0xf3 #define X25_PTYPE_REGISTER_CNF 0xf7 #define X25_PTYPE_DIAGNOSTIC 0xf1 #define X25_PTYPE_NOTYPE 0x7F #define T10_VALUE 60000 #define T11_VALUE 180000 #define T12_VALUE 60000 #define T13_VALUE 60000 #define T20_VALUE 180000 #define T21_VALUE 200000 #define T22_VALUE 180000 #define T23_VALUE 180000 #define T24_VALUE 60000 #define T25_VALUE 200000 #define T26_VALUE 180000 #define T27_VALUE 60000 #define T28_VALUE 300000 #define R20_VALUE 1 #define R22_VALUE 1 #define R23_VALUE 1 #define R25_VALUE 0 #define R27_VALUE 0 #define R28_VALUE 1 #define X25_ERRCODE_DISCARD 0x0100 /* LinkLayer (L2) maintained by L3 statemachine */ enum { EV_L3_ESTABLISH_REQ, EV_LL_ESTABLISH_IND, EV_LL_ESTABLISH_CNF, EV_L3_RELEASE_REQ, EV_LL_RELEASE_CNF, EV_LL_RELEASE_IND, }; #define LL_EVENT_COUNT (EV_LL_RELEASE_IND+1) /* X.25 Restart state machine */ enum { ST_R0, ST_R1, ST_R2, ST_R3, }; #define R_STATE_COUNT (ST_R3+1) extern char *X25strRState[]; enum { EV_LL_READY, EV_L3_RESTART_REQ, EV_L2_RESTART, EV_L2_RESTART_CNF, EV_L3_RESTART_TOUT, }; #define R_EVENT_COUNT (EV_L3_RESTART_TOUT+1) extern char *X25strREvent[]; /* X.25 connection state machine */ enum { ST_P0, ST_P1, ST_P2, ST_P3, ST_P4, ST_P5, ST_P6, ST_P7, }; #define P_STATE_COUNT (ST_P7+1) extern char *X25strPState[]; enum { EV_L3_READY, EV_L3_OUTGOING_CALL, EV_L2_INCOMING_CALL, EV_L2_CALL_CNF, EV_L3_CALL_ACCEPT, EV_L3_CLEARING, EV_L2_CLEAR, EV_L2_CLEAR_CNF, EV_L2_INVALPKT, EV_L3_CALL_TOUT, EV_L3_CLEAR_TOUT, }; #define P_EVENT_COUNT (EV_L3_CLEAR_TOUT+1) extern char *X25strPEvent[]; /* X.25 Flowcontrol state machine */ enum { ST_D0, ST_D1, ST_D2, ST_D3, }; #define D_STATE_COUNT (ST_D3+1) extern char *X25strDState[]; enum { EV_L3_CONNECT, EV_L3_RESETING, EV_L2_RESET, EV_L2_RESET_CNF, EV_L3_RESET_TOUT, }; #define D_EVENT_COUNT (EV_L3_RESET_TOUT+1) extern char *X25strDEvent[]; extern x25_channel_t *X25_get_channel(x25_l3_t *, __u16); extern x25_channel_t *X25_get_channel4NCCI(x25_l3_t *, __u32); extern int X25_reset_channel(x25_channel_t *, struct sk_buff *); extern int X25_restart(x25_l3_t *); extern int X25_get_header(x25_l3_t *, struct sk_buff *, u_char *, __u16 *, u_char *); 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, 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 *); extern int X25sendL4frame(x25_channel_t *, x25_l3_t *, int, int, int, void *); extern int X25sendL4skb(x25_channel_t *, x25_l3_t *, __u32, int, int, struct sk_buff *); extern void X25_send_diagnostic(x25_l3_t *, struct sk_buff *, int, int); extern int X25_l3down(x25_l3_t *, u_int, u_int, struct sk_buff *); extern int X25_l3_init(void); extern void X25_l3_cleanup(void); extern int X25_get_and_test_pr(x25_channel_t *, u_char, struct sk_buff *); extern int X25_get_and_test_ps(x25_channel_t *, u_char, struct sk_buff *); extern int X25_cansend(x25_channel_t *); extern __u16 x25_data_b3_req(x25_channel_t *, int, struct sk_buff *); extern int x25_data_b3_resp(x25_channel_t *, int, struct sk_buff *); extern int X25_invoke_sending(x25_channel_t *); extern int X25_receive_data(x25_channel_t *, int, int, struct sk_buff *); #endif mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/.tmp_versions/0002755000000000000500000000000011135652737021210 5ustar rootsrcmISDN-1_1_9.1/drivers/isdn/hardware/mISDN/Module.symvers0000644000000000000500000001455211135344003021246 0ustar rootsrc0x00000000 mISDN_FsmFree /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_get_lowlayer /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_SetHandledPID /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_module_register /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_HasProtocol /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_debug /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_get_free_ext_ie /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_get_up_layer /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_module_unregister /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 ZapOctVqeApiEcChannelProcess /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/octvqe/octvqe EXPORT_SYMBOL 0x00000000 mISDN_dsp_element_register /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_dsp EXPORT_SYMBOL 0x00000000 mISDN_l3_pos2ie /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_dec_usage /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_debugprint /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_initQ931_info /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_FsmDelTimer /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_dt_set_callback /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_FsmNew /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_ctrl /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_register /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 ZapOctVqeApiEcChannelInitialize /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/octvqe/octvqe EXPORT_SYMBOL 0x00000000 mISDN_AddvarIE /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 vmISDN_debug /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_init_instance /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_freechannel /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 dsp_audio_s16_to_law /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_dsp EXPORT_SYMBOL 0x00000000 mISDN_initchannel /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 dsp_audio_law_to_s32 /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_dsp EXPORT_SYMBOL 0x00000000 mISDN_dt_enable /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_clear_isac /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_isac EXPORT_SYMBOL 0x00000000 ZapOctVqeApiEcChannelFree /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/octvqe/octvqe EXPORT_SYMBOL 0x00000000 mISDN_isac_free /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_isac EXPORT_SYMBOL 0x00000000 mISDN_alloc_l3msg /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_getrev /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_add_pid_parameter /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_unregister /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_inc_usage /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_l3_ie2pos /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_dt_disable /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_FsmRestartTimer /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_isac_interrupt /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_isac EXPORT_SYMBOL 0x00000000 mISDN_isac_init /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_isac EXPORT_SYMBOL 0x00000000 mISDN_setpara /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_FsmEvent /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_RemoveUsedPID /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_dt_unset_callback /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_dsp_element_unregister /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_dsp EXPORT_SYMBOL 0x00000000 mISDN_QuickHex /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_layermask2layer /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_AddIE /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_ISAC_l1hw /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_isac EXPORT_SYMBOL 0x00000000 mISDN_get_last_repeated_ie /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_LogL3Msg /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_FsmAddTimer /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_bprotocol2pid /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_get_down_layer /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_FsmInitTimer /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_set_dchannel_pid /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_get_protocol /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 ZapOctVqeApiEcChannelTrainTap /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/octvqe/octvqe EXPORT_SYMBOL 0x00000000 mISDN_FsmChangeState /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_dt_new_frame /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL 0x00000000 mISDN_queue_message /usr/src/isdn/mISDN/drivers/isdn/hardware/mISDN/mISDN_core EXPORT_SYMBOL mISDN-1_1_9.1/drivers/isdn/hardware/mISDN/dsp_cancel.c0000644000000000000500000001765711135651701020645 0ustar rootsrc/* * * 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" #ifdef ARCH_I386 #include #else #define kernel_fpu_begin() #define kernel_fpu_end() #endif /* * how this works: * * * */ /* * send HW message to hfc card */ static void dsp_cancel_hw_message(dsp_t *dsp, u32 message, u32 param) { struct sk_buff *nskb; nskb = create_link_skb(PH_CONTROL | REQUEST, message, sizeof(param), ¶m, 0); if (!nskb) { printk(KERN_ERR "%s: No mem for skb.\n", __FUNCTION__); return; } /* unlocking is not required, because we don't expect a response */ if (mISDN_queue_down(&dsp->inst, 0, nskb)) dev_kfree_skb(nskb); } 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; if (dsp->feature_state != FEAT_STATE_RECEIVED) { dsp->queue_cancel[0]=deftaps; dsp->queue_cancel[1]=training; dsp->queue_cancel[2]=delay; return 0; } //printk("DSP_CANCEL_INIT called\n"); if (delay < 0) { //printk(KERN_NOTICE "Disabling EC\n"); dsp->cancel_enable = 0; dsp->txbuflen=0; if (dsp->features.hfc_echocanhw) { //printk(KERN_NOTICE "Disabling Hardware EC\n"); dsp_cancel_hw_message(dsp, HW_ECHOCAN_OFF, deftaps); } else { bchdev_echocancel_deactivate(dsp); } return(0); } if (dsp->features.hfc_echocanhw) { //printk(KERN_NOTICE "Using Hardware EC taps [%d]\n",deftaps); dsp_cancel_hw_message(dsp, HW_ECHOCAN_ON, deftaps); 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) { #if 0 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); #endif 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_ATOMIC); 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_ATOMIC); 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* ss, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size) { int16_t rxlin, txlin; uint16_t x; /* Perform echo cancellation on a chunk if requested */ if (ss->ec) { if (ss->echostate & __ECHO_STATE_MUTE) { /* Special stuff for training the echo can */ for (x=0;xechostate == ECHO_STATE_PRETRAINING) { if (--ss->echotimer <= 0) { ss->echotimer = 0; ss->echostate = ECHO_STATE_STARTTRAINING; } } if ((ss->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) { ss->echolastupdate = 0; ss->echostate = ECHO_STATE_TRAINING; } if (ss->echostate == ECHO_STATE_TRAINING) { if (echo_can_traintap(ss->ec, ss->echolastupdate++, rxlin)) { #if 0 printk("Finished training (%d taps trained)!\n", ss->echolastupdate); #endif ss->echostate = ECHO_STATE_ACTIVE; } } rxlin = 0; rxchunk[x] = dsp_audio_s16_to_law[(int)rxlin]; } } else { for (x=0;xec, txlin, rxlin); rxchunk[x] = dsp_audio_s16_to_law[rxlin &0xffff]; } } } } /******************************************************/ mISDN-1_1_9.1/include/0002755000000000000500000000000011110524073012663 5ustar rootsrcmISDN-1_1_9.1/include/linux/0002755000000000000500000000000011135651702014031 5ustar rootsrcmISDN-1_1_9.1/include/linux/isdn_compat.h0000644000000000000500000001064311135651702016504 0ustar rootsrc#ifndef _LINUX_ISDN_COMPAT_H #define _LINUX_ISDN_COMPAT_H #ifdef __KERNEL__ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) #define set_current_state(sta) (current->state = sta) #define module_init(x) int init_module(void) { return x(); } #define module_exit(x) void cleanup_module(void) { x(); } #define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0 = 0; } while (0) #define init_MUTEX(x) *(x)=MUTEX #define init_MUTEX_LOCKED(x) *(x)=MUTEX_LOCKED #define __devinit #define __devinitdata #else #define COMPAT_HAS_NEW_WAITQ #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) #define COMPAT_HAS_2_2_PCI #define get_pcibase(ps,nr) ps->base_address[nr] #define pci_resource_start_io(pdev,nr) pdev->base_address[nr] & PCI_BASE_ADDRESS_IO_MASK #define pci_resource_start_mem(pdev,nr) pdev->base_address[nr] & PCI_BASE_ADDRESS_MEM_MASK #define pci_get_sub_vendor(pdev, id) pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &id) #define pci_get_sub_system(pdev, id) pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &id) #define dev_kfree_skb_any(a) dev_kfree_skb(a) #define dev_kfree_skb_irq(a) dev_kfree_skb(a) typedef struct timer_list timer_t; #else /* 2.4.0 and later */ #include #define pci_resource_start_io(pdev, nr) pci_resource_start(pdev, nr) #define pci_resource_start_mem(pdev, nr) pci_resource_start(pdev, nr) #define get_pcibase(ps, nr) ps->resource[nr].start #define pci_get_sub_system(pdev, id) id = pdev->subsystem_device #define pci_get_sub_vendor(pdev, id) id = pdev->subsystem_vendor #endif /* 2,4,0 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) #ifndef IRQ_HANDLED /* maybe these are also defined in include/linux/interrupt.h */ typedef void irqreturn_t; #define IRQ_NONE #define IRQ_HANDLED #define IRQ_RETVAL(x) #endif #define CAPI_SendMessage_void #define OLDCAPI_DRIVER_INTERFACE #undef HAS_WORKQUEUE #define work_struct tq_struct #define INIT_WORK(q, f, d) (q)->routine=f;(q)->data=d; #define schedule_work(q) queue_task(q, &tq_immediate);mark_bh(IMMEDIATE_BH); #define MAKEDAEMON(n) daemonize();strcpy(current->comm, n) #undef NEW_ISAPNP #define pnp_register_driver(d) isapnp_register_driver(d) #define pnp_unregister_driver(d) isapnp_unregister_driver(d) #define pnp_get_drvdata(d) pci_get_drvdata(d) #define pnp_set_drvdata(p,d) pci_set_drvdata(p,d) #define pnp_activate_dev(d) isapnp_activate_dev(d, "mISDN") #define pnp_disable_dev(d) ((struct pci_dev *)d)->prepare(d);((struct pci_dev *)d)->deactivate(d) #define pnp_port_start(d,n) d->resource[n].start #define pnp_irq(d,n) d->irq_resource[n].start #undef iminor #define iminor(i) MINOR(i->i_rdev) #else #undef OLDCAPI_DRIVER_INTERFACE #define HAS_WORKQUEUE #undef MINOR #define MINOR(inode) minor(inode) #define NEED_JIFFIES_INCLUDE #define MAKEDAEMON(n) daemonize(n) #define NEW_ISAPNP #endif /* 2,5,0 */ #ifndef COMPAT_HAS_NEW_WAITQ typedef struct wait_queue wait_queue_t; typedef struct wait_queue *wait_queue_head_t; #define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL } #define DECLARE_WAIT_QUEUE_HEAD(wait) wait_queue_head_t wait #define init_waitqueue_head(x) *(x)=NULL #define init_waitqueue_entry(q,p) ((q)->task)=(p) #endif /* COMPAT_HAS_NEW_WAITQ */ #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,8) #define __ATTR(_name,_mode,_show,_store) { \ .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ .show = _show, \ .store = _store, \ } #define LOCAL_FCSTAB #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) #define OLD_MODULE_PARAM /* udev sysfs stuff */ #define CLASS_WITHOUT_OWNER #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9) #define kzalloc kmalloc #elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) #define kzalloc(s,f) kcalloc(1,s,f) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) #include #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) #define MISDN_COMPAT_KMEMCACHE #endif #ifndef SA_SHIRQ #define SA_SHIRQ IRQF_SHARED #define SYSFS_SUPPORT_2_6_24 #endif #endif /* __KERNEL__ */ #endif /* _LINUX_ISDN_COMPAT_H */ mISDN-1_1_9.1/include/linux/mISDNdebugtool.h0000644000000000000500000000215311110524073017011 0ustar rootsrc#ifndef __mISDNdebugtool_H__ #define __mISDNdebugtool_H__ /* * 0 8 16 24 31 * +--------+--------+--------+--------+ * | vers | type | reserved + * +--------+--------+--------+--------+ * | stack identifier + * +--------+--------+--------+--------+ * | seconds since epoch | * +--------+--------+--------+--------+ * | nanoseconds | * +--------+--------+--------+--------+ * | payload length | * +--------+--------+--------+--------+ */ enum mISDN_dt_type { D_RX = 1, /* payload: copy of dchannel payload */ D_TX, /* payload: copy of dchannel payload */ L1_UP, /* no payload */ L1_DOWN, /* no payload */ CRC_ERR, /* no payload */ NEWSTATE, /* payload: state-id (uint) :: message (NULL-terminated charstring) thrown by: hfcmulti */ }; typedef struct mISDN_dt_header { unsigned char version; unsigned char type; unsigned short reserved; unsigned int stack_id; unsigned int stack_protocol; struct timespec time; unsigned int plength; } mISDN_dt_header_t; #endif mISDN-1_1_9.1/include/linux/mISDNif.h0000644000000000000500000006062511135651702015442 0ustar rootsrc/* $Id: mISDNif.h,v 1.42 2006/12/27 18:50:50 jolly Exp $ * */ #ifndef mISDNIF_H #define mISDNIF_H #include #include #include /* * ABI Version 32 bit * * <16 bit> Major version * - changed if any interface become backwards incompatible * * <16 bit> Minor version * - changed if any interface is extended but backwards compatible * */ #define MISDN_MAJOR_VERSION 5 #define MISDN_MINOR_VERSION 0 #define MISDN_VERSION ((MISDN_MAJOR_VERSION<<16) | MISDN_MINOR_VERSION) #define MISDN_REVISION "$Revision: 1.42 $" #define MISDN_DATE "$Date: 2006/12/27 18:50:50 $" /* 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 #define INDICATION 0x82 #define RESPONSE 0x83 #define SUB_ERROR 0xff /* management */ #define MGR_FUNCTION 0x0f0000 #define MGR_GETOBJECT 0x0f0100 #define MGR_NEWOBJECT 0x0f0200 #define MGR_DELOBJECT 0x0f0300 #define MGR_NEWENTITY 0x0f0600 #define MGR_DELENTITY 0x0f0700 #define MGR_GETSTACK 0x0f1100 #define MGR_NEWSTACK 0x0f1200 #define MGR_DELSTACK 0x0f1300 #define MGR_SETSTACK 0x0f1400 #define MGR_CLEARSTACK 0x0f1500 #define MGR_REGLAYER 0x0f1600 #define MGR_UNREGLAYER 0x0f1700 #define MGR_SELCHANNEL 0x0f1800 #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_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 #define MGR_SETDEVOPT 0x0f5300 #define MGR_GETDEVOPT 0x0f5400 #define MGR_INITTIMER 0x0f8100 #define MGR_ADDTIMER 0x0f8200 #define MGR_DELTIMER 0x0f8300 #define MGR_REMOVETIMER 0x0f8400 #define MGR_TIMER 0x0f8800 #define MGR_CONTROL 0x0fe100 #define MGR_STATUS 0x0fe200 #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 #define MGR_VERSION 0x0fff00 /* layer 1 <-> hardware */ #define PH_SIGNAL 0x000100 #define PH_CONTROL 0x000200 #define PH_STATUS 0x000300 /* PH_SIGNAL parameter */ #define INFO0 0x1000 #define INFO1 0x1100 #define INFO2 0x1200 #define INFO3_P8 0x1308 #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_POWERUP 0x0101 #define HW_DEACTIVATE 0x0200 #define HW_ACTIVATE 0x0201 #define HW_MOD_FRM 0x0400 #define HW_MOD_FRH 0x0401 #define HW_MOD_FTM 0x0402 #define HW_MOD_FTH 0x0403 #define HW_MOD_FTS 0x0404 #define HW_MOD_CONNECT 0x0410 #define HW_MOD_OK 0x0411 #define HW_MOD_NOCARR 0x0412 #define HW_MOD_FCERROR 0x0413 #define HW_MOD_READY 0x0414 #define HW_MOD_LASTDATA 0x0415 #define HW_MOD_SILENCE 0x0416 #define HW_FEATURES 0x04ff #define HW_HFC_COEFF 0x0500 #define HW_LOS 0x0501 #define HW_LOS_OFF 0x0502 #define HW_AIS 0x0503 #define HW_AIS_OFF 0x0504 #define HW_SLIP_TX 0x0505 #define HW_SLIP_RX 0x0506 #define HW_PCM_CONN 0x0580 #define HW_PCM_DISC 0x0581 #define HW_CONF_JOIN 0x0582 #define HW_CONF_SPLIT 0x0583 #define HW_RECEIVE_OFF 0x0584 #define HW_RECEIVE_ON 0x0585 #define HW_SPL_LOOP_ON 0x0586 #define HW_SPL_LOOP_OFF 0x0587 #define HW_ECHOCAN_ON 0x0588 #define HW_ECHOCAN_OFF 0x0589 #define HW_TESTLOOP 0xFF00 #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_TESTRX_RAW 0xFF40 #define HW_TESTRX_HDLC 0xFF41 #define HW_TESTRX_OFF 0xFF4f /* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ #define DTMF_TONE_VAL 0x2000 #define DTMF_TONE_MASK 0x007F #define DTMF_TONE_START 0x2100 #define DTMF_TONE_STOP 0x2200 #define CMX_CONF_JOIN 0x2300 #define CMX_CONF_SPLIT 0x2301 #define CMX_ECHO_ON 0x2302 #define CMX_ECHO_OFF 0x2303 #define CMX_RECEIVE_OFF 0x2304 #define CMX_RECEIVE_ON 0x2305 #define CMX_MIX_ON 0x2306 #define CMX_MIX_OFF 0x2307 #define TONE_PATT_ON 0x2310 #define TONE_PATT_OFF 0x2311 #define VOL_CHANGE_TX 0x2312 #define VOL_CHANGE_RX 0x2313 #define BF_ENABLE_KEY 0x2314 #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 #define HW_POTS_SETSPKVOL 0x1101 #define HW_POTS_GETMICVOL 0x1110 #define HW_POTS_GETSPKVOL 0x1111 /* TONE_PATT_ON parameter */ #define TONE_OFF 0x0000 #define TONE_GERMAN_DIALTONE 0x0001 #define TONE_GERMAN_OLDDIALTONE 0x0002 #define TONE_AMERICAN_DIALTONE 0x0003 #define TONE_GERMAN_DIALPBX 0x0004 #define TONE_GERMAN_OLDDIALPBX 0x0005 #define TONE_AMERICAN_DIALPBX 0x0006 #define TONE_GERMAN_RINGING 0x0007 #define TONE_GERMAN_OLDRINGING 0x0008 #define TONE_AMERICAN_RINGPBX 0x000b #define TONE_GERMAN_RINGPBX 0x000c #define TONE_GERMAN_OLDRINGPBX 0x000d #define TONE_AMERICAN_RINGING 0x000e #define TONE_GERMAN_BUSY 0x000f #define TONE_GERMAN_OLDBUSY 0x0010 #define TONE_AMERICAN_BUSY 0x0011 #define TONE_GERMAN_HANGUP 0x0012 #define TONE_GERMAN_OLDHANGUP 0x0013 #define TONE_AMERICAN_HANGUP 0x0014 #define TONE_SPECIAL_INFO 0x0015 #define TONE_GERMAN_GASSENBESETZT 0x0016 #define TONE_GERMAN_AUFSCHALTTON 0x0016 #define GLOBALOPT_INTERNAL_CTRL 0x0001 #define GLOBALOPT_EXTERNAL_EQUIPMENT 0x0002 #define GLOBALOPT_HANDSET 0x0004 #define GLOBALOPT_DTMF 0x0008 #define GLOBALOPT_SUPPLEMENTARY_SERVICE 0x0010 #define GLOBALOPT_CHANNEL_ALLOCATION 0x0020 #define GLOBALOPT_PARAMETER_B_CHANNEL 0x0040 #define GLOBALOPT_LINE_INTERCONNECT 0x0080 /* layer 1 */ #define PH_ACTIVATE 0x010100 #define PH_DEACTIVATE 0x010000 #define PH_DATA 0x110200 #define MPH_DEACTIVATE 0x011000 #define MPH_ACTIVATE 0x011100 #define MPH_INFORMATION 0x012000 /* layer 2 */ #define DL_ESTABLISH 0x020100 #define DL_RELEASE 0x020000 #define DL_DATA 0x120200 #define DL_UNITDATA 0x120300 #define MDL_UNITDATA 0x121200 #define MDL_ASSIGN 0x022100 #define MDL_REMOVE 0x022000 #define MDL_ERROR 0x023000 #define MDL_INFORMATION 0x024000 #define MDL_STATUS 0x028100 #define MDL_FINDTEI 0x028200 /* layer 3 */ #define CC_ALERTING 0x030100 #define CC_PROCEEDING 0x030200 #define CC_PROGRESS 0x030300 #define CC_SETUP 0x030500 #define CC_CONNECT 0x030700 #define CC_SETUP_ACKNOWLEDGE 0x030d00 #define CC_CONNECT_ACKNOWLEDGE 0x030f00 #define CC_USER_INFORMATION 0x032000 #define CC_SUSPEND_REJECT 0x032100 #define CC_RESUME_REJECT 0x032200 #define CC_HOLD 0x032400 #define CC_SUSPEND 0x032500 #define CC_RESUME 0x032600 #define CC_HOLD_ACKNOWLEDGE 0x032800 #define CC_SUSPEND_ACKNOWLEDGE 0x032d00 #define CC_RESUME_ACKNOWLEDGE 0x032e00 #define CC_HOLD_REJECT 0x033000 #define CC_RETRIEVE 0x033100 #define CC_RETRIEVE_ACKNOWLEDGE 0x033300 #define CC_RETRIEVE_REJECT 0x033700 #define CC_DISCONNECT 0x034500 #define CC_RESTART 0x034600 #define CC_RELEASE 0x034d00 #define CC_RELEASE_COMPLETE 0x035a00 #define CC_FACILITY 0x036200 #define CC_NOTIFY 0x036e00 #define CC_STATUS_ENQUIRY 0x037500 #define CC_INFORMATION 0x037b00 #define CC_STATUS 0x037d00 #define CC_NEW_CR 0x03f000 #define CC_RELEASE_CR 0x03f100 #define CC_TIMEOUT 0x03ff00 #define CC_B3_DATA 0x138600 #define CAPI_MESSAGE_REQUEST 0x040080 #define LAYER_MASK 0x0F0000 #define COMMAND_MASK 0x00FF00 #define SUBCOMMAND_MASK 0x0000FF #define DATA_COMMAND 0x100000 #define CMD_IS_DATA(p) (p & DATA_COMMAND) /* short cuts layer 1 */ #define PH_ACTIVATE_REQ (PH_ACTIVATE | REQUEST) #define PH_ACTIVATE_IND (PH_ACTIVATE | INDICATION) #define PH_DEACTIVATE_IND (PH_DEACTIVATE | INDICATION) #define PH_DATA_REQ (PH_DATA | REQUEST) #define PH_DATA_IND (PH_DATA | INDICATION) #define PH_DATA_CNF (PH_DATA | CONFIRM) #define PH_DATA_RSP (PH_DATA | RESPONSE) #define MPH_ACTIVATE_REQ (MPH_ACTIVATE | REQUEST) #define MPH_DEACTIVATE_REQ (MPH_DEACTIVATE | REQUEST) #define MPH_INFORMATION_IND (MPH_INFORMATION | INDICATION) /* short cuts layer 2 */ #define DL_ESTABLISH_REQ (DL_ESTABLISH | REQUEST) #define DL_ESTABLISH_IND (DL_ESTABLISH | INDICATION) #define DL_ESTABLISH_CNF (DL_ESTABLISH | CONFIRM) #define DL_RELEASE_REQ (DL_RELEASE | REQUEST) #define DL_RELEASE_IND (DL_RELEASE | INDICATION) #define DL_RELEASE_CNF (DL_RELEASE | CONFIRM) #define DL_DATA_REQ (DL_DATA | REQUEST) #define DL_DATA_IND (DL_DATA | INDICATION) #define DL_UNITDATA_REQ (DL_UNITDATA | REQUEST) #define DL_UNITDATA_IND (DL_UNITDATA | INDICATION) #define MDL_ASSIGN_REQ (MDL_ASSIGN | REQUEST) #define MDL_ASSIGN_IND (MDL_ASSIGN | INDICATION) #define MDL_REMOVE_REQ (MDL_REMOVE | REQUEST) #define MDL_ERROR_IND (MDL_ERROR | INDICATION) #define MDL_ERROR_RSP (MDL_ERROR | RESPONSE) #define MDL_INFORMATION_IND (MDL_INFORMATION | INDICATION) /* protocol id */ #define ISDN_PID_NONE 0 #define ISDN_PID_ANY 0xffffffff #define ISDN_PID_L0_TE_S0 0x00000001 #define ISDN_PID_L0_NT_S0 0x00000100 #define ISDN_PID_L0_TE_U 0x00000002 #define ISDN_PID_L0_NT_U 0x00000200 #define ISDN_PID_L0_TE_UP2 0x00000004 #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 #define ISDN_PID_L1_NT_U 0x01000200 #define ISDN_PID_L1_TE_UP2 0x01000004 #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 #define ISDN_PID_L1_B_V110_HDLC 0x41000008 #define ISDN_PID_L1_B_T30FAX 0x41000010 #define ISDN_PID_L1_B_64HDLC_INV 0x41000020 #define ISDN_PID_L1_B_56TRANS 0x41000040 #define ISDN_PID_L1_B_MODEM_ALL 0x41000080 #define ISDN_PID_L1_B_MODEM_ASYNC 0x41000100 #define ISDN_PID_L1_B_MODEM_HDLC 0x41000200 /* Bit 15-0 reserved for CAPI defined protocols */ #define ISDN_PID_L2_B_X75SLP 0x42000001 #define ISDN_PID_L2_B_TRANS 0x42000002 #define ISDN_PID_L2_B_TRANSDTMF 0x42300002 #define ISDN_PID_L2_B_RAWDEV 0x42400002 #define ISDN_PID_L2_B_SDLC 0x42000004 #define ISDN_PID_L2_B_LAPD 0x42000008 #define ISDN_PID_L2_B_T30 0x42000010 #define ISDN_PID_L2_B_PPP 0x42000020 #define ISDN_PID_L2_B_TRANSNOERR 0x42000040 #define ISDN_PID_L2_B_MODEM 0x42000080 #define ISDN_PID_L2_B_X75SLPV42BIS 0x42000100 #define ISDN_PID_L2_B_V120ASYNC 0x42000200 #define ISDN_PID_L2_B_V120ASYNCV42BIS 0x42000400 #define ISDN_PID_L2_B_V120TRANS 0x42000800 #define ISDN_PID_L2_B_LAPDFREESAPI 0x42001000 #define ISDN_PID_L3_B_TRANS 0x43000001 #define ISDN_PID_L3_B_T90NL 0x43000002 #define ISDN_PID_L3_B_X25DTE 0x43000004 #define ISDN_PID_L3_B_X25DCE 0x43000008 #define ISDN_PID_L3_B_T30 0x43000010 #define ISDN_PID_L3_B_T30EXT 0x43000020 #define ISDN_PID_L3_B_MODEM 0x43000080 /* Bit 15-0 reserved for CAPI defined protocols */ #define ISDN_PID_L3_B_DSP 0x43010000 #define ISDN_PID_L3_DSS1USER 0x03000001 #define ISDN_PID_L3_DSS1NET 0x03000100 #define ISDN_PID_L4_CAPI20 0x04000001 #define ISDN_PID_L4_B_CAPI20 0x44000001 #define ISDN_PID_BCHANNEL_BIT 0x40000000 #define ISDN_PID_LAYER_MASK 0x0f000000 #define ISDN_PID_LAYER(n) (n<<24) #define ISDN_PID_FEATURE_MASK 0x00F00000 #define ISDN_PID_IDX_MAX 23 #define ISDN_PID_L2_DF_PTP 0x00100000 #define ISDN_PID_L2_DF_MULT_TEI 0x00200000 #define ISDN_PID_L3_DF_PTP 0x00100000 #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 #define FLG_mISDNPORT_BUSY 1 #define FLG_mISDNPORT_ENABLED 2 #define FLG_mISDNPORT_BLOCK 3 #define FLG_mISDNPORT_OPEN 4 #define FLG_mISDNPORT_ONEFRAME 5 /* * A "ENTITY" is a instance which assign id's with a none local * scope. A id has a local part (a range of ids which the instance * maintains) and the global ENTITY ID. * A instance which wan't to assign IDs have to request a unique * ENTITY ID with MGR_NEWENTITY | REQUEST. * If the instance is deleted or don't need this feature anymore * it has to delete the ENTITY with MGR_DELENTITY | REQUEST. * ENTITY ID 0xFFFF is a special one. * One example for using this is the L3/L4 process ID. */ #define MISDN_MAX_ENTITY 2048 #define MISDN_ENTITY_NONE 0x0000ffff #define MISDN_ID_ENTITYMASK 0xffff0000 #define MISDN_ID_LOCALMASK 0x0000FFFF #define MISDN_ID_ANY 0xffffffff #define MISDN_ID_NONE 0xfffffffe #define MISDN_ID_DUMMY 0xffff0001 #define MISDN_ID_GLOBAL 0xffff0002 #define MAX_LAYER_NR 7 #define ISDN_LAYER(n) (1<MAX_LAYER_NR)) /* 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 #define IF_CHAIN 0x04000000 #define IF_HANDSHAKE 0x08000000 #define IF_TYPEMASK 0x07000000 #define IF_ADDRMASK 0xF0FFFFFF #define IF_IADDRMASK 0xF0FFFFFF #define IF_CONTRMASK 0x000000FF #define IF_CHILDMASK 0x1000FF00 #define IF_CLONEMASK 0x2000FF00 #define IF_INSTMASK 0x400F0000 #define IF_LAYERMASK 0x00F00000 #define IF_TYPE(i) ((i)->stat & IF_TYPEMASK) #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 0x0000FF /* stack channel values */ #define CHANNEL_NUMBER 0x000000FF #define CHANNEL_RXSLOT 0x0000FF00 #define CHANNEL_TXSLOT 0x00FF0000 #define CHANNEL_EXTINFO 0xFF000000 #define CHANNEL_NR_D 0x00000000 #define CHANNEL_NR_B1 0x00000001 #define CHANNEL_NR_B2 0x00000002 #define CHANNEL_EXT_PCM 0x01000000 #define CHANNEL_EXT_REV 0x02000000 /* 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_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_DELETED 28 #define mISDN_STACK_ACTIVE 29 #define mISDN_STACK_RUNNING 30 #define mISDN_STACK_KILLED 31 /* special packet type */ #define PACKET_NOACK 250 /* limits for buffers */ #define MAX_PHONE_DIGIT 31 #define MAX_DFRAME_LEN 260 #define MAX_DATA_SIZE 2048 #define MAX_DATA_MEM 2080 #define MAX_HEADER_LEN 4 #define DEFAULT_PORT_QUEUELEN 256 #define PORT_SKB_RESERVE L3_EXTRA_SIZE #define PORT_SKB_MINIMUM 128 /* structure for information exchange between layer/entity boundaries */ #define STATUS_INFO_L1 1 #define STATUS_INFO_L2 2 typedef struct _mISDN_head { u_int addr; u_int prim; int dinfo; int len; } __attribute__((packed)) mISDN_head_t; #define mISDN_HEADER_LEN sizeof(mISDN_head_t) typedef struct _status_info { int len; int typ; u_char info[120]; } status_info_t; typedef struct _logdata { char *head; char *fmt; va_list args; } logdata_t; typedef struct _moditem { char *name; int protocol; } moditem_t; typedef struct _mISDN_pid { int protocol[MAX_LAYER_NR +1]; int layermask; int maxplen; /* 0 is defined as no prameter, all other values are offsets into pbuf * so pbuf[0] is always unused */ u16 param[MAX_LAYER_NR +1]; u16 global; u16 pidx; u_int modparm_protocol; u_char *pbuf; } mISDN_pid_t; typedef struct _mISDN_stPara { int maxdatalen; int up_headerlen; int down_headerlen; } mISDN_stPara_t; typedef struct _stack_info { u_int id; mISDN_pid_t pid; 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; u_int child[2]; /* this is correct handled for PRI see get_stack_info() */ } stack_info_t; typedef struct _layer_info { char name[mISDN_MAX_IDLEN]; int object_id; 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; union { u_int id; void *p; } st; } channel_info_t; /* 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; u_char crlen; u16 cr; ie_info_t bearer_capability; ie_info_t cause; ie_info_t call_id; ie_info_t call_state; ie_info_t channel_id; ie_info_t facility; ie_info_t progress; ie_info_t net_fac; ie_info_t notify; ie_info_t display; ie_info_t date; ie_info_t keypad; ie_info_t signal; ie_info_t info_rate; ie_info_t end2end_transit; ie_info_t transit_delay_sel; ie_info_t pktl_bin_para; ie_info_t pktl_window; ie_info_t pkt_size; ie_info_t closed_userg; ie_info_t connected_nr; ie_info_t connected_sub; ie_info_t calling_nr; ie_info_t calling_sub; ie_info_t called_nr; ie_info_t called_sub; ie_info_t redirect_nr; ie_info_t redirect_dn; ie_info_t transit_net_sel; ie_info_t restart_ind; ie_info_t llc; ie_info_t hlc; ie_info_t useruser; ie_info_t more_data; ie_info_t sending_complete; ie_info_t congestion_level; ie_info_t comprehension_required; ie_info_ext_t ext[8]; } __attribute__((packed)) Q931_info_t; #define L3_EXTRA_SIZE sizeof(Q931_info_t) #ifdef __KERNEL__ #include #include #include typedef struct _mISDNobject mISDNobject_t; typedef struct _mISDNinstance mISDNinstance_t; typedef struct _mISDNstack mISDNstack_t; typedef struct _mISDNport mISDNport_t; typedef struct _mISDNdevice mISDNdevice_t; typedef int (ctrl_func_t)(void *, u_int, 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 #define mISDN_HEAD_DINFO(s) ((mISDN_head_t *)&s->cb[0])->dinfo typedef struct _mISDN_headext { u_int addr; u_int prim; int dinfo; void *data[3]; union { ctrl_func_t *ctrl; if_func_t *iff; void *func; } func; } __attribute__((packed)) mISDN_headext_t; #define mISDN_HEADEXT_P(s) ((mISDN_headext_t *)&s->cb[0]) /* Basic struct of a mISDN component */ struct _mISDNobject { struct list_head list; char *name; u_int id; int refcnt; mISDN_pid_t DPROTO; mISDN_pid_t BPROTO; ctrl_func_t *own_ctrl; struct list_head ilist; spinlock_t lock; struct module *owner; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) struct device class_dev; #else struct class_device class_dev; #endif }; #ifdef OBSOLETE /* the interface between two mISDNinstances */ struct _mISDNif { if_func_t *func; void *fdata; mISDNif_t *clone; mISDNif_t *predecessor; int extentions; int stat; mISDNstack_t *st; mISDNinstance_t *owner; mISDNinstance_t *peer; }; #endif /* a instance of a mISDNobject */ struct _mISDNinstance { struct list_head list; char name[mISDN_MAX_IDLEN]; int extentions; u_int id; int regcnt; mISDN_pid_t pid; mISDNstack_t *st; mISDNobject_t *obj; void *privat; mISDNinstance_t *clone; mISDNinstance_t *parent; if_func_t *function; spinlock_t *hwlock; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) struct device class_dev; #else struct class_device class_dev; #endif }; #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 */ struct _mISDNlayer { struct list_head list; mISDNinstance_t *inst; }; #endif /* the STACK; a (vertical) chain of layers */ struct _mISDNstack { struct list_head list; u_int id; u_int extentions; mISDN_pid_t pid; mISDN_stPara_t para; 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; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) struct device class_dev; #else struct class_device class_dev; #endif /* 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; u_long Flag; struct sk_buff_head queue; u_int maxqlen; }; /* the user interface to handle /dev/mISDN */ struct _mISDNdevice { struct list_head list; int minor; struct semaphore io_sema; int open_mode; mISDNport_t rport; mISDNport_t wport; struct list_head layerlist; struct list_head stacklist; struct list_head timerlist; struct list_head entitylist; }; /* 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_queue_message(mISDNinstance_t *, u_int, struct sk_buff *); /* global register/unregister functions */ extern int mISDN_register(mISDNobject_t *obj); extern int mISDN_unregister(mISDNobject_t *obj); extern int mISDN_ctrl(void *, u_int, void *); #endif /* __KERNEL__ */ #endif /* mISDNIF_H */ mISDN-1_1_9.1/km_mISDN.spec0000644000000000000500000000303311110524073013512 0ustar rootsrcVendor: SuSE GmbH, Nuernberg, Germany Distribution: SuSE Linux 8.2 (i386) Name: km_mISDN Release: 14 Packager: feedback@suse.de Copyright: Karsten Keil GPL Group: unsorted Autoreqprov: on Version: 1.0 Summary: modular ISDN driver architecture Source: mISDN-%{version}-%{release}.tar.bz2 #Patch: isdn4k-utils.dif Buildroot: /var/tmp/mISDN.build %description This package provides the mISDN sourcecode for kernelmodules Attention!!! These modules are BETA code and experimental, they may be crash your machine. Here is no support from SuSE for it. Authors: -------- Karsten Keil SuSE series: unsorted %prep %setup -n mISDN #%patch %build mv Makefile.standalone Makefile %install rm -f -r $RPM_BUILD_ROOT DESTDIR=$RPM_BUILD_ROOT/usr/src/kernel-modules/mISDN mkdir -p $DESTDIR install -p Makefile* $DESTDIR install -p Rules.make.ext $DESTDIR install -p add.config $DESTDIR mkdir -p $DESTDIR/newinclude/linux install -p include/linux/*.h $DESTDIR/newinclude/linux mkdir -p $DESTDIR/drivers/isdn/hardware/mISDN install -p drivers/isdn/hardware/mISDN/Makefile $DESTDIR/drivers/isdn/hardware/mISDN install -p drivers/isdn/hardware/mISDN/*.[ch] $DESTDIR/drivers/isdn/hardware/mISDN install -p drivers/isdn/hardware/mISDN/Rules.mISDN.v2.4 $DESTDIR/drivers/isdn/hardware/mISDN/Rules.mISDN # %{?suse_check} %clean %files %dir %attr (-,root,root) /usr/src/kernel-modules/mISDN %attr (-,root,root) /usr/src/kernel-modules/mISDN/* %changelog -n km_mISDN * Tue Jul 01 2003 - kkeil@suse.de - first version mISDN-1_1_9.1/Makefile0000644000000000000500000000754211137306045012714 0ustar rootsrcBASEDIR=$(shell pwd) MAJOR=1 MINOR=1 SUBMINOR=9.1 INSTALL_PREFIX := / export INSTALL_PREFIX #PATH to linux source/headers #LINUX=/usr/src/linux ifndef KVERS KVERS:=$(shell uname -r) endif MODS=/lib/modules/$(KVERS) LINUX=$(MODS)/build LINUX_SOURCE=$(MODS)/source UPDATE_MODULES=$(shell which update-modules) MODULES_UPDATE=$(shell which modules-update) DEPMOD=$(shell which depmod) MISDNDIR=$(BASEDIR) MISDN_SRC=$(MISDNDIR)/drivers/isdn/hardware/mISDN ######################################## # USER CONFIGS END ######################################## CONFIGS+=CONFIG_MISDN_DRV=m CONFIGS+=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 CONFIGS+=CONFIG_MISDN_NETJET=m CONFIGS+=CONFIG_MISDN_DEBUGTOOL=m #CONFIGS+=CONFIG_MISDN_NETDEV=y MISDNVERSION=$(shell cat VERSION) MINCLUDES+=-I$(MISDNDIR)/include all: VERSION test_old_misdn cp $(MISDNDIR)/drivers/isdn/hardware/mISDN/Makefile.v2.6 $(MISDNDIR)/drivers/isdn/hardware/mISDN/Makefile export MINCLUDES=$(MISDNDIR)/include ; export MISDNVERSION=$(MISDNVERSION); make -C $(LINUX) SUBDIRS=$(MISDN_SRC) modules $(CONFIGS) install: all modules-install misdn-init $(DEPMOD) $(UPDATE_MODULES) $(MODULES_UPDATE) make -C config install modules-install: cd $(LINUX) ; make INSTALL_MOD_PATH=$(INSTALL_PREFIX) SUBDIRS=$(MISDN_SRC) modules_install mkdir -p $(INSTALL_PREFIX)/usr/include/linux/ cp $(MISDNDIR)/include/linux/*.h $(INSTALL_PREFIX)/usr/include/linux/ if [ -e $(INSTALL_PREFIX)/usr/include/linux/mISDNdsp.h ]; then rm -f $(INSTALL_PREFIX)/usr/include/linux/mISDNdsp.h; fi misdn-init: mkdir -p $(INSTALL_PREFIX)/usr/sbin/ install -m755 misdn-init $(INSTALL_PREFIX)/usr/sbin/ if [ -d $(INSTALL_PREFIX)/etc/init.d ]; then \ if [ -e $(INSTALL_PREFIX)/etc/init.d/misdn-init ]; then rm -rf $(INSTALL_PREFIX)/etc/init.d/misdn-init; fi; \ ln -s $(INSTALL_PREFIX)/usr/sbin/misdn-init $(INSTALL_PREFIX)/etc/init.d/misdn-init; \ fi mkdir -p $(INSTALL_PREFIX)/etc/modprobe.d cp mISDN.modprobe.d $(INSTALL_PREFIX)/etc/modprobe.d/mISDN mkdir -p $(INSTALL_PREFIX)/etc/modules.d cp mISDN.modprobe.d $(INSTALL_PREFIX)/etc/modules.d/mISDN test_old_misdn: @if echo -n "#include " | gcc -C -E - 2>/dev/null 1>/dev/null ; then \ if ! echo -n "#include \n#if MISDN_MAJOR_VERSION < 4\n#error old mISDNif.h\n#endif\n" | gcc -C -E - 2>/dev/null 1>/dev/null ; then \ echo -n "\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: modules-install install all clean misdn-init VERSION 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 {} \; VERSION: echo $(MAJOR)_$(MINOR)_$(SUBMINOR) > VERSION ; \ snapshot: clean DIR=mISDN-$$(date +"20%y_%m_%d") ; \ echo $(MAJOR)_$(MINOR)_$(SUBMINOR)-$$(date +"20%y_%m_%d" | sed -e "s/\//_/g") > VERSION ; \ mkdir -p /tmp/$$DIR ; \ cp -a * /tmp/$$DIR ; \ cd /tmp/; \ tar czf $$DIR.tar.gz $$DIR release: clean DIR=mISDN-$(MAJOR)_$(MINOR)_$(SUBMINOR) ; \ echo $(MAJOR)_$(MINOR)_$(SUBMINOR) > VERSION ; \ mkdir -p /tmp/$$DIR ; \ cp -a * /tmp/$$DIR ; \ cd /tmp/; \ tar czf $$DIR.tar.gz $$DIR mISDN-1_1_9.1/Makefile.module0000644000000000000500000000223511110524073014164 0ustar rootsrc# Master Makefile for the ipppcomp # (c) 03/2001 Karsten Keil # adapted from Kurt Garloffs SecuMod package DESTDIR = KDIR := /usr/src/linux TARGET := mISDN TARGETS := Rules.make arch scripts .config include $(TARGET) TARGETDIR := drivers/isdn/hardware/mISDN default: $(TARGETS) all: $(TARGETS) Rules.make: $(KDIR)/Rules.make Rules.make.ext cp -pf $(KDIR)/Rules.make . cat Rules.make.ext >> Rules.make .config: $(KDIR)/.config cp -pf $(KDIR)/.config . cat add.config >> .config arch: $(KDIR)/arch rm -f arch ln -s $(KDIR)/arch . scripts: $(KDIR)/scripts rm -f scripts ln -s $(KDIR)/scripts include: $(KDIR)/include rm -f include ln -s $(KDIR)/include clean: rm -f $(TARGETS) $(TARGETDIR)/.*.flags $(TARGETDIR)/*.o $(TARGETDIR)/.depend rm -f $(TARGETDIR)/*~ newinclude/linux/*~ *~ .kversion rm -f -r modules .depend .hdepend $(TARGET): $(MAKE) -f Makefile KDIR=$(KDIR) TARGETDIR=$(TARGETDIR) $(TARGET) install: $(TARGETS) $(MAKE) -f Makefile KDIR=$(KDIR) TARGETDIR=$(TARGETDIR) install_mod install newinclude/linux/mISDNif.h /usr/include/linux modlist: $(MAKE) -f Makefile KDIR=$(KDIR) TARGETDIR=$(TARGETDIR) modlist mISDN-1_1_9.1/Makefile.standalone0000644000000000000500000000317411110524073015032 0ustar rootsrc# km_mISDN slave Makefile # (c) 10/2001 Karsten Keil # KDIR = /usr/src/linux DESTDIR = TARGET = TARGETS := $(TARGET) default: $(TARGETS) all: $(TARGETS) MYDIR := $(PWD) TOPDIR := $(KDIR) include $(KDIR)/.config include $(KDIR)/Makefile CFLAGS := -I. -I $(KDIR)/drivers/isdn/avmb1 $(CFLAGS) -I $(MYDIR)/newinclude -DLINUX CC := $(filter-out -I$(HPATH), $(CC)) -I $(MYDIR)/newinclude -I $(HPATH) ifdef CONFIG_MODVERSIONS CFLAGS := -DMODULE -DMODVERSIONS -include $(MODVERFILE) $(CFLAGS) else CFLAGS := -DMODULE $(CFLAGS) endif FINDHPATH += $(MYDIR)/newinclude/linux MODLIB := $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) mISDN: $(TARGETDIR) depend mISDN_mod $(MAKE) -C $(TARGETDIR) CFLAGS="$(CFLAGS)" MAKING_MODULES=1 modules dep-files: scripts/mkdep archdep include/linux/version.h scripts/mkdep -- `find $(FINDHPATH) \( -name SCCS -o -name .svn \) -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend $(MAKE) $(patsubst %,_sfdep_%,$(TARGETDIR)) _FASTDEP_ALL_SUB_DIRS="$(TARGETDIR)" ifdef CONFIG_MODVERSIONS $(MAKE) update-modverfile endif depend dep: dep-files ifeq ($(PATCHLEVEL), 2) TARGETMODDIR = misc install: install_mod install_mod: modules_install mISDN_mod: echo patchlevel $(PATCHLEVEL) mkdir -p modules else TARGETMODDIR = ../misc mISDN_mod: echo patchlevel $(PATCHLEVEL) install: install_mod install_mod: $(MODLIB)/$(TARGETMODDIR) $(MAKE) -C $(TARGETDIR) MOD_DESTDIR=$(TARGETMODDIR) modules_install $(MODLIB)/$(TARGETMODDIR): mkdir -p $(MODLIB)/$(TARGETMODDIR) endif modlist: $(MODLIB)/$(TARGETMODDIR) $(MAKE) -C $(TARGETDIR) MOD_DESTDIR=$(TARGETMODDIR) mod_list mISDN-1_1_9.1/mISDN-1.1.tgz0000644000000000000500000160363211137306072013214 0ustar rootsrc:}I<[Ʋ+&@؀iMv0 imȒ*<пmL $5;;;;;WifU=W^ƍZ}fx&8kmxwg'ڬfن^=P?ԿW^WA}C_?aZd_q/UAakksߙ_7|<}k]cH1s7ַ^#Gez^Z` %Pu¥힫=tG{?MpTr-8s }hXC0;G=&߈Xh>*[1@Vڽ&RUƶ>1W). ދ˄T&KXwprӉ$[:vEC^ 4GW}^ QX՗D$"~I⑴iEC~h ڕt ٯHuKQs^lNz#9}׃~{~ZPj}hcs {߿;=ފqOzof6Ѣ^NLǽۣgbw99yw|pKa3hw>6E"Gʦ`BwvOMaiD猡msۦ.-h̩?)_{SD 7smRSO*`4QA.W IM w-bVnX!H&x;&" P\\6& &A![m deFLcރ鄦{퓕ŹS@6uÅ-maYK\ҁtJy4g7~ [9Y4'6(hw;3,5Ui o3sUG9e $&$FOF.Ia[gZPní39vRMDZ/BcTӛcR{"BϚ5?JqmdCɂbh~L1(^ 5x%bMPߐց}j-""\6GF Sk^|-D%Ki&Q0Xd@]wd&[p}11OQ='y'2(9(WX/%Zh?v|+d6,X>,u>9}#̵xa=̳TߐZ(e{L*^^_/"5Dx\'wT*J2,dyo;bY hmK*Lu 7y xW~7'*9tt,qGV[6k\zT _68d",k Wj0Au߂0s70UUG!*&S] _*-eCǭwcjHDm nTW2*.K"% F:1ĘT9 9 %/m3,c#ix:B+*Lu U=DdEwl!nY 0m<`QbGi\/ň~J :P 82h:w0D&_f?19a[!]ɍj AP]H(Q(ڀd)P"If*' :#q;5( ̉D41 v ~J]LJ* P/gU* lE ~Jl  6ّL]ToOZeQfwB`JatVAKݖe#D|4vx&!vۂx8\o3^klaZu;GN ul/nT4wwIJޛh@dg;$UaRT;"桿i8z*K%Mˑ8] vvp=]Xp;.KgA)U_7j0P$1,7L_"DqQ}orr8I(eiA0B0[墡PMl@Su? dKJeܵ]5% q i@Ɔ%Bd87)F4-Hz܄Ը\?]ù،KJ(t92[i۩!ӧ3+qp_Dw7pQZqGQ_6kS\P[u%=M\.3mU&"b  $| ,NV<0|wMR5߸J=gl2YrAS]+C.UC2N=Gv#x kS2 IDeǵe/NTӳ ]hgRI]S?/o;_\.oľ(06W'paxdȪPy̓!Ǹ#_nOks qKc%q%JEqLLAF%Ćmz(*|"(PݤR,_t- sM\Oz-٣$Dl ,} 4zY"XdHoC kt_x =^\=?kw/ń/&/IpCPƽ$R٠^T.g[a1u"|[GYDR&F^)_y"BT (?͌Yx.c G> ՘%9|ȥs~N#,q瞎%ك1yeıq[ĭdR[yH!awcE=|'X_Ev%?R=_5=d\ֱi*ب=Op-P{z#i@OZ!H ,du! NJ pOX?I,Q{3L,M:g//^U&v:<䧾 ^Ci Y> kN^&0(%zZ[1CiRB M=>L|a KCSYG.7BbT},.`4|#'h:~! 5-q9 L>psJ7  i.efr%$s7Zfd[&@gC@r(mIfzݱꝳ$j|wV)k}HWJ0^&(`)d5$iH$]HH lk|k8 z /  ڝNTDa) ]{dcZ}cc)k;)@^Xp=BPIGu=D4 HOƧ}MNICnLJ%9ǎK1Sàw~XCvťSk-y1כ_;7(iMMut%_\(i r2l=hfq=pDa6X-y%B(N)it7jG7oO젆Ɛk\XOA.|UU+;˿rW*Պ$]'Pw&O`VD؄`Eږ `EXloic47cF~KK.=&Mr3 I/ ij94RUS< O4iA[;)rGlVm#$L;j=I:j7ބrs)\`$\L$4dr >;ɷ+8 ZM)P!g,  ¯^^?= &Ea+ΛZ*D.mq$Ψb/N=l &W6 ˛oVsgmhʑjlʉ0*%5@8ZLl`S-$ 0^Ja)BKTIC !,,*-r0`1/L9njvZ&Nnz{|-}1}3f_XԶGZTB"M$aO.t3GEILN򙂅̧O.Lr`iJib&2N'b NAV;d&-4hn1:]@>)J8ؒd*-i4%j"#g?Q{A;n|&r|<͡*PLjӠq"ҫ'inPTN".$ѫHpp/7=C#_aw b!Wۿ8xTG[)ceƕOH! !-9Ҳ@sqi܂h[7ilh43F#oA(f,=NөwkQ}'0J-ӧ;`9q忄iĴ ?ĈP%z4pSEZ?qճo0 Iٖʠ3TBrc'øOgrn/3pz&)>8MqY{^a+2blF$LaTu?lN/ݞ qoF @җ~kqlju'x>BC@97sTS6\=|+<^F;%zOί)% ;36ab7 jLH-tC{y dCE)fA2 #ls(FSM3\Ա\j䔝֒ɐӁ\Fh/tf2پf(IXɔ^` #BY i2-g1~7g}DžuxuCXN]͜%Ǚ9sWMsf@5slϙkf .(O{Ōc#Wػ{$[Y\ڳfT^fpIq#!;}EC9?ciKK-i"(~@YZQsK&nWǟ $IiIJuih jAmNS n!'{)ޱgNJSܽcag]} ƀftڕ[W+"o[")D$H-Nt q}.ICP(xybk[!g^kZ}QRZXTzxLZ3v(E]Av_mrLW,PYWBW+Wg`N s[”F.M+;qX*Êasr2X9(X3Diz,L3H(ۦVEM,"K&bݱ k$2wPp%u0ȅ+gW=ksDKY{?R TJ$zoyt[3D$j b7XY- &5fukf׭YZeT\([3pon֖ҚRLٿ*,P,KKg [JABo~%sޒp @r'ոl\o,C8כs)qS k%IrbM-0 :o lA1W.bnnjB>7[bȸ8 ܳ4,nKȕOO8S|0,"GDj8tW5?p'f(hۙߐ#>zxzB7 \kaQĶ{}F*6Wp <AR0~?x:$'2]s uojx yW+|I!?ERlKuN6&nl[6666Ѹ " g"ڿcf;gEZɳ:֦p$_!;ۉ[qGpX@qZ͙-k.*j;[v@y﬒[T p)XjAfҡb 1bpa6Y\J' YSr*RWwwox ǃJ~?>1Ot3t<A=('w=W7&P$q'}cׯ_}h5G9P_ >t|NM x(]Kɥ nDk70juvH*a\0 &ˉzCeK?!x&&ܶsaG@3)ҡ9xB{f&VB }5&'{zODJg&)zUfdhqmr {N/lTd^ ٷXwj$LH%Ňd(6XD&+=OUuE^g@ry8.GUbڪ!V$WE}*m9H>y[;OƦ؃'7,{(`pt`E5hH!0@ ѣ>3!YvVϝ/mڲP~ Sb{|[#f?C ׾dNҎ$)TO>tPvAcF=•(/(Jq)2Ku'z]zctB&~dU7ڴ7 mncbBT9Лxq8 ]䠮ڶ2٠a4 ґ[l"=&ev+˟c\'xVj5ƁztW{z2ow "cQNWx! C3LFF Q9`Û*n'?$*yIΙȠ|2y*sg oa!Ov׎^;?#l 7WׁqG GHԊF"9jPo9@sP8-{Gok~o>4 &Gf[zM٥25bR̊XH1IΌ9l qձx4`V!lTυWO%ňY/Ԫ1iFҶ1^yOq3كsc%d}~v=?q#/sxGצyo9?}?1_AKHTCKk:\}QJ ٰ4MFlK  %&G[kjmm';׉b`/&Y MR\i,i"KFN3'96R7 s!5YWzTi% >-t=us. cˎ4VY9Kc%+q0?= $F{s-XtȘ |f GwysaG$ Ad=%Lހv QeΟzGShvb;p`'iNIv0 }910Q-G[$pG|\->E:q$.%s )MϿs۳C=@zEp ^1?Uz?CDM:7ВBc|nt(Dŭ&()j$#>/Ai{! AQZjIeBkt$.+Y8 7 ѵέ30zvxe-=aGWbzgw_k =²AтX>)v(A1/ .?K&߱ ,O\ئf$ySyþ{|;iWb*Җ'iqy8YU%ugg:GA{e' tme z98L7r&k -U9$j_>4+|%#tQH%%Y uq}:,I/Q؎Ѝ+ZR9>ƆuIl-W eۡ1 w2>d;Ḽ0&~&D;[/{L_q&o ӫ4u/ٙK綘X9.f.?I'GkvƳ3 o.^:@ONC8x~: #Ş83˒01f`g<쇞=A._'q\, [W+t1p0w܁?qL$5::aӑY٩)x<0{wZT'm DT޾ zQ~JRDڴWJDLY@B iL453;9EZ 7ƃzݮX'tH#]l萳 uyR!"3=j hDŽO ԡ H_/Si/F.b+_^a9D/73'7Ri1Qfv!kcfIR)AݏîO.N/@1d0$8qf_7mvz1n#=9IY$MUF&ӑNk 2N|A눉jP6X}^ CDb3 a-y>>aq}QHuJ fzGna&UX6= 5`#*hxʭ1lp5D)$0 Pag:i+M tLmUZcf&zuY;n46Y:Vwh} fTv\m1֮{ uH BO"Fj4W_=crzzNgnu]F3EزL2أ4/4x+Ak^*;P:Ӿi 3 w}P{?7HpwFr4a.3+veom9|ÐrVYp)+d۞]V<dFg4 4Pӥz`kZ~zkl\5@?”-&y*eϽVk@I% %i P}dj=Fz遇ӪJSzZ1W'5 oQ iTOWL lpl?p x[);]@о@NxPR+σ>)A~c濦ZwLl|̆CĨWOSbDeo3U0I+>ԕi>/اpaU7>vEvV=oO}Ti8:-7d*UOeL*0FV/r._VIQG"f>6aYS>;ƻV̸4#8U=z p&$ J`oҹZ 46g88Z|FR@aVxɰEH"2f+(0P}J)0!Su0ظCn6vƢ A% zUuʘ̙#EFJ$י |;qtLĹҵ9Q>w>bŒ/Ec4zѨQGT;:9ʲ\iƔ PRh )26iw¦a!|~z`ohj2j =+(`vCE!p25w$"IHQO56h/gIE 3Ì4?{ڝ6gv&?N͜@b&`g&wgcߝ﷪[j=!73;uWWUWWWWK. ,_kEBeR( **!UVhu:%f؍Sc_yj9aÕ;r> .k6(wx^,ƺ&Dm?[bfZD;cLg24<6~~~#).)}\?xsb>Ϊ堭_`P%dezdb4_f Aemظjo6E)tI}N=1Ls)RjԂ~ Ko|Q!I3ݿ(,c hg~E_vlOLF`I&Qb8nrQ:WvVᓺYRU,)˵HUI]TH|oDCUa`X;DB/AdA%"Q{dV (<1EȊ<@ kAJ~o #q2Xc+ϥ1_VX۝93})C8JC!˦vPLj7$6FPʎւHFnlW߆+V< =)6*LPD}Nd^ QF8Eo "UMvf 07֒LϚ-W*ȋxeaSxa4&Idjܱv,k-T0#ԥ7ė[~܌eqmSP3NJh{o>z'}QϚ ]&Ob2N,12bД}Pel9cXe}zWv;nJ;咊z@y'f#FaY=2+Y2Hz,OjPV * !1)*{OJ˅ Ay\`ҎXSiG:9iÎl؊S 0`)cgx7Κagc4F9R.OJ!2ѝ.ǰ|7D@W ݙ8x`6nlR3APP ܗ hDJț6 Uc9 ŰL,2qՙ4q;UI87gy~v݇SA~:(s<|2VTdȆV |*ytt8:n EaŔ+i1w60|q$$yBnx6.1Oh ߤõQ?0&o5Zw b+'ޗҬlg wJmn<) /U rtbs2O{dBTbI*= -{R?d??vYXqCikGZO-`|pq7١i8N01`{=dOrT_>MS AN~ ulg, Cf+пϺN<qk|'%O<@zAp/t@b5~Ϻυu4O7x6k-oZn- Yótvz|Y4b!XۑwRi/ ~hI9TJpXfyT[Ǧ9tOx.,&.hUjGpX/XD 1%+->Eo<ŹPdh噌N @ťі_X:<1w Qwx٪5l'4D}Gg 9[Z؎1aΊ {`=Kk{ }3Fg5la.&݃]gB,ή59v NZ~=8gX/,9#[8<82Kt ,ƶ+iٹH8eqK&;bkoxkWc{8KlI'ŃƤeߓ%FĈ{3ƴ hk`D'arWeݟ, @Z.1䠐ץ:םX+9H3WyЙ[2 9;1xh:._=6>P$5i[XmM)o{Å > @A:꼥9_p4O(T x)Q%5[A? &3;-z;W"XqhB MxkxiC(㿭/3g &r52TSd4XvxyMUEyZ9H֢97]ͮxu9zCTv4^@-7q9tFU7]\yRyw6P\kdx+R5& T D?SaDFi@HCʁ 3bKOqЀ~Ĝ,K$a [;3 vBhđv-u[.caUϗ;&$_8einq$ Ol?j(Aҗ%5W $O?ӝ[ =IrҼtu}48'Nar7>d~\v;oADmf>` g'[.*J!p5B!؃`#D>f^ ܡh~/3ze7bb x:WZsccz"}LBCJ![Єer$^/c1IoHCqB(P`ɚW:Cz.f`tkHdI= czް=V|e]r/^8R:{͎޹x{Ya;y.}7)vppz{}?oeDe-1L-C8_*dJ R_F2+AN͜AM0wWxL=>Hg߄9v9Qc25\ҡ`fg#u@qJlQMáÑiXjl0H4VOXdұΛtk*7{3ykR=ǿ٪"X`{Ek f.)+iF۹}QaaxܿXG&egiXCnh ICeQt=a޺Y3 mJ uBsQUr6ֆ4޷?-{~iLƘ|k,6l>u|_ƹL/<2ˀ:0*|ѪE{Ӫ1bV(xHIжYtU a F).Ȩ뛞,_tֳdž[gվp/ e׾x-xm-Zq8A;U@SP5k" E?N)K' z$t_lzX;l:cP;Ё9DR}i\Z52AغKs^kB*f U)⭐Aouaޚ 6+]J-1pZNiZ⽰nR"sxtg.ѻYXKDNE=EEsv{HxpD۳DZRʱQdKWxLg7yHVs& BP(iӰ c?۰aisyƊw. qHH{fR s0|y/wª+J!ի0/%2@TУIos}E^"x?R- gT\Q!ږLXri< /xVI\.>p*G TSR C{%88$sbn("PdЛa=TԈuSpV{#j'AmRnnnEM!uL}0_s5VQ 0r? 0>v`6m@κZ p:&s^VRCNG-k>J= =TАQvy(UhSBeq{6МV}_C}3//ϋl d5WX+rzyHr~6Zr)5 _!>UBDEsxܯT/ΛGx{r^nVG'ǀgq6fR4eEa%,cOv4=uuwnsX>+v cɚ("jN Gݶ z=hU1l\^`*?]y0.^&9>ǰ3p0,h @S| =҂|8%P*\8~s 1ms2SVbб'Q8Yl 1onI[Y!0{ԑ@wK8ja5oWriu'->|MTVpadZbhBz<[;DB$HXd ~m PP Ũ2Uˠ%KSzG8Y쯣8CJ#ӋFV(wXf2nҥmi|{_Z? *DE]Ol YY'bNR+oR% WJ",9 >Ъ$wzxyTJC 8^j2]8e?sfJs^dI~eSqŚ%MG˘ :4 M>Ƴw'.g!u`qT& ![ذlә+6|.t;_yo^@c2챣GRLt=*q>$Ɠ:!>p"#/!EomZ2ZC9;+b.t2z|a>> &lo]EP"<0$e2]L*DQIF?c/JI%1Y'C9|D{^XxGZ4UÉ9D6/-ܙ9whKȞߓyy0BsUx+ꃐ GJ}Q*lꈓ/PIsվPƧe]D?BE/c.%tL#5vMJ;v96nV.kNQ{N6T*KhU-QiZvƋmjhg#ya V0JUEv5,)wp%#8F4squU(OmyOjS8XsN]*ûw&2}4cYK s3C'!מ.In- r=Ů}*] G=v"oFd$,~ve X=MLo~ 0OOu1Gw|b֌1F 6o={~jKD| {9dQ0XE'Q:6|\ꞽN͗M7V-|Z s2=%Q Y\8*Hc,&ˡh[ Ю0?we+Q%K9c[|@Ll+[_쨋^'i`Mڍj*/-!7 N>[SܿѾ e}_ ~Z?2Z}2C,0À@|luÎ:@/qѸx[(C[%\߼>:Wr<$<$5n}4#Bu {>WͧIP߳542U;Q0~jY,z֗)pz">+Ƕ`J _~# 1;%lKe[zP #`YJO`@>m̀fl e[OBmB;[;+桾-)*yv`Yòۆ&Ət0lD5X]%[Ϧ 9 npxБ8&!Xۡ=T]&V]Ʃ0]Y7DPoK LVaN癢Q0 P. evQع}Z2ݞGcA=Wڔo?]Qz`q=JMl!xu,w!DKc} ~4PBzyldIkUjt_PzW5kOo& J $g%e<)4I1OMIQ]KSGsS?&Y]=bʊa )$wǍ@ uWqҁg ' VWxanЧv+ʘ[V,ۃow l8'J <,H7U 8eH:/Ȣ䋂)/5YJYW sC,' .=4±$Ny4M?ڋ]9آoXx}눾s2$^5g V4v?|_(ڮ68I'@L[:Ѳ%_I+#7vi M4Î0L %D]\2[,_*Y@`B>/Hca󲩢 | wY8*TIayK J\M5H,tR ԓhS2XTG^Bn$GGJ U.&+9ǜbsfCgW1b̢1e*&)UO|~,jˍj{`-p_BCB8FBHf1 ɱeE,iٯ͚HƋz,nSDvj:ߌxt?L&Mήپx竌Gd!,2,;ዩmUxg׌+*yӨ#kr*^=ScOQJZ y"evJ&hm9|yڼTm.Q K^[$XxU-kZ;K'5mYRT%Ij}nZe)t4վ ־MֲB֦ʹމʪa᩻XILUcڟRS^˘nV5;ZvaVm|-Rq2Ǝ&skuߛnTқ"Le8_'ӗP[LhúnR<&)\PbSmP%BkEþeX]nttK2" r"_YHd r#{—\_tlK*Z^>W]o-2˰ Rmv;xE&$1uU*5mU\fTu7qcu=fds%&䟨o;8q"mȇ -N| 7-&S %5w-r*+Ѧfj'ǵcMZH7({SÓ!`]Omk1N˓v;Aq uC/(/R[^Bt5]~٥u̜(?8/&SⲐi$[7}#0`kJS̔o=d}Ge Ĩ:WܢkuI9}␄j8 zyn'ݺ)vt7@yޟ}έ.{}yv@=֘7@Z`d7 L|KyO~0YN/{F}c9W\dW|"\ہ/eM` tsܺ LH.W8 F]9rX 4c]F O,ttI|i.m))_1ܺ /j׋ .l69Z)`n<;\d"Ʊ3_EFjeʮڸ0keUCbY3(2N2,&[pT읒qBeKtoXQqsYy3ua0 8B/NكOc[P+:ij@WGR?v $+c?ߥ' >$A!65 ]SBZQ}KZdC&G1-7$ _v6ԴA7̺w@{&tw'A4=c|(R HyX?%$%`JlΏ-VegZXze=uG=JkLD\B/~" ͓4x+m';lut mA(>FN܃lEU)עTv| ^cQgl#p[f˴;F*vn٢x p?e|F9aѾ*v9 O>dO?T'/n` ?E"z Z݊

2-3S~E# Z Pݠ7K$508=4Yڌvœx;A)bнVZF7*(}z`P՜XhQؖ pG?ve4278of-}tIPc4''Ƃ0Kޛqwº7 )h.F !j+5CށaZZF,Db;ZQ™a<8lQ}wCo7p_Jbߤ˵>Y¹9ן몢'EHXE#mcB꿜tZ>#c'E.' yOkFy>bE[p|1QmyvC/\`O ~ݸa9N/ b NLfېYyD$Ys!zJ@1%.eL݋F3h#a1?xOdڄ9ij'-_E*R`de+JeUHBGd)aˮ9]t63ה5ṡmubI|F'4sJ~Ko p%رFtxw-J[$Zm_,e5(DAA^I(H s>nP~g9G3PH/8׃%E)lP;L'a?PQlw h^d.Y3?j)«;?Ab~ݎ*s0?vnux2Goυ>:#g-F䤠A#JN*GBͺ͑Q6?<'658+HZSg6:mSF^uX9ܻ!|oGGJaİjCe2F[1Fh'Mo D+vyiݫٌ^K @kK -li>{^v0`?؟; ĤnOZQrxcItmC&T18M~{`&4ޞ6T*N?Bg[Bp&qGpEP'،%Cpr ++Oˤl~)׃_xꔉh6 6q$:_2 ֋Mk([Fc! q3$!L>F萕5k ;މDTm_*3A&r'DG'ѩ\;  BxjXA;Oo<nH%&7Pc؂/M<LDCwv] hZc8h XE/YkÇWr^MS(eқ*w4/) qJ%4XI~RoktkuV z5,3==/vOr&׫0O[z[06i%wtʹOZdx9%Ph5P= p "rФ9d;VC kxT/_SnNYݖB8iXt%U'[*~Q3d]SH7rI18 oZ5ze$j aX q%^1B^{FI0S/+ώ1s9sdFEh#hNˀS#(Pj)P%. 1vǣ0;|ꯡWƻ8 cͳ`Qфf5l]RfIY% E +I0Q׸FYGc~=fwSN0L89!.!w `u p0F*תFmXNa5@תovM_.v|1 S07R]%}_D3sȓ-`L"ɓ͔2>)'/^ Mx B,dU2Mȇʹ>UD8GcLi?uOlk\rhG Ys:: f/T2YmH׭%P-{*A6Pj&+3z3y iء)# /Q F[Qp Vbu4Tg='C}Kz|lBxyvp֬嬣Pc2RXl<߇<&>ta]@5Bj,rFd@*:מݓ=[N_s8~r$9fug^,/FC=;>ʡNtip0zӱ 09UcggGR##NB ⋤0\9O7B/L%^Ёy^aVR|o< ]d|ӱM.pX-rj"q?)kѠ QW.A6@c,' >7eAj7La \:Ě#\I's =,m`2 (dڱP&2D14ٯ5{iM6L<Zv2@ uh/]+`gZ:Nd=f9.УHïEIE9 A*C.+8E(^|}u_uf#Nr~"]fu|kM Md(GRɋMQ*Lꐏ.`765slEfd.ebaMs3< GÈY&]&l܏trJ$JI0"5;W=okR0C§#*Ҭ[ӽK  KQd~>Rn;55Q|Wڤ6WEi)n^!Q˙6;˞O V8A7(Vų.M[uw9^lP cNyI thlLTL, Vl'eIXm"%%Es yeBhLM/l4 .`{T[ nlK-=-z< md@CnՓ}JԻśk:=$yLߔ-Ci-3d@?Ĭk!J>_"# `]g&A> 4ll{$9$8$THsHuiO0alF^GgM訛9Ӑ1>zHinyǤ??oVO~-d9`A6Fn^{%Ĭ6,NR}Y7|"s;#Gd_iXM8V)[>Rvm|mJkϖՈS|jvW<-g=yި$[_Ѕ"rJ!ǼF35H/?g j?ԙH՚`$`|.Wō愕Hy7<]6j(y- / 'cqnƎ.FL+:sb+`]@Vj/!ZklS {(™d"Y)fYY,v8 _d-J$Rq2R5m~G7kY-('$3"jx;?n$| ZdݱtvXԶstfؘ=D䖔 Gי._@2LwJ'l,^ϑ(['jߎVo۽N 7R` }AuƇIM19ZgC&h\XN_m&_(p:aO͑:ث|iTXrAx'ҳNsAO7NjBQпT/ϛiQ9h"IjK;Hm(顚';|+}3 ԳgGߘB,rץ4;.DR9ɘ0mǭd5Obe3Id &]kY GPG-/DwA%Y'*[U=vQΥJ&%+U睇Ǧ]n-csYR1/ Cl}|zߨHa'h~̿}"-3 lXN*@Ps q@]ؙQސD0̬> '3APOX= Fޓ4LP{D$QC'?!GC%ȏcl!ܮS{c_sik6`; ,)Ҁ9$K0y!@`4_j}P[`u:ȆֈC`Y_Hpݰ?]\Rf9aoO.ś=NaV%UVO;*q>ceMCGL{̨Ӿ!]!/=^|J588jx0U9趮Gjn9Bݚ:BdvV(p=#r%"Wu\= w(bflR&R)De*$V2nڙ}v(g+ ӡv4\T#gm.yWbvqׁro(lNb{$kzHZi{+\e'u c Fuy|W7jp[fXt13q|>N^Ñ\{*dUp:͹'1\Q,^֙F%H>}+%/A$~ y~mpWlvzq.O1B<9=CKFFXSCМݵ _B[sJ3kNiU?yQqq|x%m$92R9?61pUdFV$YCjפJVT&"nևD&N탻<}fmzʚ(II%^ `Rf,tPwcb+ڃk^*4!~zQpdTo~ZH&tJ%LlA1rI-2^鬿ʬ_)pܞd%Z]7GV!Z!)UOE ػش|uI]1ʨ,䄍V@ݑC9r0@ij5 7˝1gb1~Q> K o( ~Ѩe6R0l:S#o䖝)!Pگ}@IʌWfFO4#$ %!63J7)%ڏBiPw>e':YH] ?3'_*WakX,GhO37ō·.l1ڨ Eqɂ[:܅hV'-v/ bGԠ4. (觃Rt ϔ: :#p3Y-`.c MzMeu| .('9ZG4I y"kg˗hh 6Cu&~1B6N®ոӵ%' ,o=IW/2?ZE6j^@TPϿzԔ8ɺGx/~%ђ6͝JTܥ '$ YǍ`@}fVV[t!K쩆V^[K/'/p[R7W*r>年%iV« 2 ٤UJ/ 걱z޽q!1@_b^;GKQ T_qU76VL)CjnnMg>7v6\v1RM/aVL cCzq:7 3bW:.+}#\/["F>Ѫt0΁ԥQcz)}*bPpAbN0|Q vxaѠt@p/[-ϙ6NӼNH"`3aa"WпHCn5kӛP}F X(4?!NeL&,[;@Xl`'ޙ+r'.Fkj'&#%bkJjZ 逹'!bnb(@b2i[HP`NArW2G5[yT)Hߙzn~v^tN`eQmeA m2U7G0,zHA6lfixUti[GrFϛ:hJ=%OknY,) `-7MIk'c,)Y;8lyJ6jsQ;٫9%osY.zGt\YE>ϠyQMŒ>|V/~qG7N?;9XUS<:Z~ =䋂hdzýsXѓs,VR,=+Q' ':/+WxQ0f\FlS0gwBWc^ }ȊpQZ<~_JbF ϥq*qPqNtB>^ޞ03?*,fJ(4ib&7} +{=dgbNYd#7 \N"V̭bjyn #`^ftkq%{QRJZI RTi>XXꓶ)c!bIE 2]/@BܜmwT 2` 3)0ER7(Di;\"ޚusF319{wz8)LKKiR%N)d#&F:A蒟̢Lqeteveq<2s!yBkQ60 ֳkE|"]a-Ԭ[+}M_xfT,}}kf x\$;(w7Kma.C5ުP vʆB\Nn/<9FK+/-_spǠwF:~[]]LHw,j'\(kE^g3?: ۓݯ"OWe)V*RC'nב׊*V3m =c١S]=Lm}2PCOчGoT1C3t7ڣ 8&D>~I'''{͑Ť.%N:W,*Jy"|%gub!=Ӏ$a6 ISo _6ASs0y{1 $vk2I#=A6ay<`ƍcac:\=-xMUK]-D @`nA:xfYBʱ Jκ6<Q-HLKU՚gkTDDT䢔6ap4xnjƱ\Fpll*my4B:@ѳ?EBT]؁5)&a%;Yrt4g-l掽. O}JZƤF{1]r’$3^ϯ#@DBZ@yh@X18ޝeN\ǹE;y0][rR}PuL+Ҍ<fyע\vJkFUxDU:1 iuJuIuO6Jb*^~;|}t*ܭx9:fQ_ 5+\O ՠwƻݽ{衹]/]yK+а1 Utbb]kըy!ٶy7ޛg:?4h|H' A&}6i$Vwn6Oۓ`ҘP1kS:wG?b'Ç(ӷ3jjl|ξ0| Z[`Zakߛ׋чf|KdpRyx s/| e1TvGR"`Q./qH/w<^Rw1rOY {bΰ)K_<Ǖp-u(*?ye%-/Q>U{*DczG2i׾Oi4nj$jNwBZ[Dؽ`_~NN E?DbZbDX'rrɥ%?"RjK-#RKK@#kI!J [T|nt|JqFB=hF:޿ǚ]3?V,QyfrzD)&q^><rAI"S0& X1D{VEe8$}RQB݆MRt6GBkodp>OAߑ4nQea%^*bՅ9D CV=4‡^,w˨g\T>{w)[X hԻq&b!9r,{L/ӺSEE v04XqzkBXRszmIp-{&4õh.? ?^"tA/A9v[RjIxsNOVtown My gYӫqhw1XyAeAbzuov֎G?Vyo~,hTY;qWG?~_$ c;1XU"fD/ WQx9.>;eR g\w,ɵϫ_T˟*fý)KhxYz/GɔSBطȩ=6a-Ρ)bz>Zxdcvb5?HKV_@\_1'ZxW.Dr1>Nj6ƾoiD` |˶-1nЪ?^.'Y5c|Rzg5.4gV~Luy$а싛 ͢>A](&/Rt22EeQEvk:e2=0 _Uũ]wyM#=Q6k]k+Ywk{cQgM:^O W^S5_7bNס{몋zdc޻uT *M/BfD>ڞ7TW`>7 /p|?Xp3h"{Ik2饡쑟[8_Ԧ®H *- NՕ΁\突 k;Y4R}5H*Qz4H0i ׃ӽb1P ku#=Y./S rR/vvnA.SVEZ9Ƈvu6?_{ƒd.U5RKq ւm&1V`>䗕FV*1Tצw97@ T~jdwы_)z8Kaz|Y̑f>`ΩĚ*B1eX FZZ+N/p(tBUQƦFv ;bL\EP(^u'j1ȇ^W#ݙCuT}?Ħ㵊 O1gzlA'_G8Q0LGeOb)sZ H/z&yRAVVhll< ]`%29jJnEpoz#*J$ E;}UeQg楴x6l+h[Fkdu\V#C_d}(~֘b݃uL`%{3[Rtu7Y!V -{KeuKɒ-*[3_y{A)- VY"t@%8t|1h%Q  GփyϿ"5+Od)XL/S]ն\媽XڷU+iezqBfv{_o~,z baw>E~&'2}<nL;o0`NU⠗'ax_ _78 Ͳ܊ ;_#|o0mEʴ |+ ax!<( Mh8:fi: ;kEqjbQЭm8tںNI00ET 3\$ ,D&sXUg$ d}4OX6VgI0})O8ϛZOy@mJ&#PjZ2;N{umPbUg]`z׼k_x9'YFy6 ϼgLY=e~|ǷJa˲mI&a"e:}eD(.{6Mf# =B`2{aI{f0aOC,tiia]/oTOy,$]Ak< X0a&E`=`^VwJOht颾g𞂏#<& ]]fؑ[uOa=5ώšP4(a^`2)M\DDc;{'@<,rb^X`~]vd焷LB\VkY!1fl0.ZZ`|p_dd}=+ #Ȟe1zu(P7 Rrَόc;{jHb_O s3EL:^ @K&v0}H!=mhED{ԇ"ҔF]т{Bx7BxzfԾV,bŷ|{¹)  b !/YA#_b!fU*|zOF`Je6O\ ]* x!>]ۇ5K-H_HUs _O=K;H>jħXx^eR/VK$*/8n8ژuځy? ATYΖ'CG&omtw*v-?{vkv7Aw<ؒ GbȋVEPV('J\M7_On!Vy%+rcoNOjb=Qo6sk{O9χ#pUb!KGC>% HOTWwŧlmXԑX@, >to5F >Hߠ(RmTCTA4náGlP5l5_zgnضn_a7|JMuI. ³\Ef d=Ca)؅ժL'x>/|akJ1ύ]LDt*sc*sI*(*K+pWeKEye*uU0& ÿ,xbk8_q9'9zLHu5VR 2}Zji<ԗ/ÓepJBşmh<`#J[ ٫:U䘘`߄,J"ᄯs~A VB187O~QxV(>v_B[X0_DW>^B@~a1O_sC|r,8ˋvg;z͎l?s(fL  %ֿQ,cM UjzUO| 0{JRcqŞV#Uq?Hk 6FP06P=/n'pNTxßL7,nWlBz'J7X\휘;H=goW;i>j7LD=WCF|vr/RA -kKќ[` Ch9 FN'^n¶hÔL$Qk=uOա <oh$>9G_ʯUGãzp EW~A#MQa=r9ńyZl`bR 0zL~<8<8@q^=hMbYCXXq_ڏn+R(:/굷#}w^q Pxv `vZ= q^kΓ0fvRž`ȬELlԎĕqE!Bcpm4޺C5Փ;Hroq,ncȎNO|1xV fϫ:*y^|ûZ5DT~\xPO床۫}PW`Zb&y_GWh?><+UkE"M@@{5r)@:z#on#RdzŴAB%]#7`a[hz^5awZ1˯7ncל|<4/gj:;]8܆% .fóU6D֞wY2\_Vq x!lK>F7MP$ y:ٽӓ.l9; cfhty[j]ͅ?q/3j'&Ga ;HT0xl#1|i߸.7\IY^PXV;W>]>%a@V0,aG^jLdk-嫰s7\sݖr]䁊;_S r)f5wqLJ}JHL'YKa|cI8Gja2kf~Uκ"r6;*Dл Syw9|8 EQUx5ㅙ*k?#8Ylș0?u^v<1@1ktZjC&fGm"dy ,nVln6^7fk!t~YgѧŹ$5D*. ׌3JF.)tj3 Ǜ1O5f/>4j@YXm7{ 8pʸ 9],Ϛսk^{rAAu=ΉEz*hV}e8.f7V6Vn̼pnt14%D1)_O7av-܆Y98&裶`ܔrNG(A]:Rj)*Z-&s|@眼vEgB֝1aT4CXZb3`@wl)QMC],V3o;¨cQƢ |R9pY'1PQm{?"5S^Dn$/gmi-7kβ<ױ9F͡$|d@/倝y!?pa! DP-Ic7E`MڳȲdd3Wg@NmcZi`Xsdgo{s:_E%`-ė T=s.w{bcʕo{|f FV?Z>0w+TGu kTB\yo85DەdȃmPJǔv@Te,i<-$ywKI>`2h~fO^j׸Q݈gIޫ,kQ~V|.xrYl/g )j[xx.z^YgY'Gf 'ͦCU+xffǻ({:䇤jϐjS"BحҌ%9;or,J([b}5r4a߳ߜ O%$CU܎QAK"UbtGwn6)62JS'Ϊɹ+X<ɿinoReƧW5⍺mz\`{ P^AP@wV'E!߰NN秧grM8i?zgV96`ڏ~pǑHpVTQ"2zYrH;K$smkb%r7zBGKf0V;|o{nЗtH=os ɿ}Qw1 A1 74W_ iV^CKhka '`Ori6`42XݿP9NX31$WܰbUUTD!J ?Raº>aTl좁C5anP.tHZ 6`7o7F-3N_E#FA3yҽ_Ns֥T+hYhlΡz4 pNPΙrҚ. _ؿLNnDOYMj674&VȂpM/C8PE4&.oc/] [ dħpr*Vv,1o.?yjܭ=vOtQw)șf[/ڰ|y9ʬ2}ͳ dh;n둩[APz0@m4'qMl5rB1I]r^Eb+T7XSQ8&Z;=:IIR [|zኄ@ HA=UZR(\^vUlO+K%LY5$W)nv!fqPI8M ý*rnXУf@8JAP{aGs1$ 4kF^~~FHQ >;OD1C8RbU $0Q??7y>55>Flmt͌A(3XI,7a mAQv|Y6'//2DNG!X"v?RҘ#t&-nmsr?#X"J9C;A .cr~V] ^0 r-!K-0)~3J=#a#ElVk KvT6|%cp'#(k{sׇɘS.sc,oU砚 ҺIR'LΧЪ5ӣhc_eqv[8MaO(,K\/-{d[ߖxCJ)t-g,V;u>=S=H_MȦ0sٱ *h݊V|c:$rDwJE[X] +&@YV{cmZnk<~p{uh&(~7kǩIvw :.3'6Szg:l BM|dyn/lg! h!L-;FrŘY9T)O |[T[ʒ$x/8˶yELoo(Ig47q1mǞi[Kx*ڙ՟5m\/4|`jr+f݄Za0kpM]8b\Bv ~+Ʀnb7%u>hCwU!FR'^qPWprbHi%rc몶\ewW}kK9"J(+5 j|UE* .5DÌ1)MR g)YfRՂ^{{TTx9,+ %7蘞ﴮzy'r%Ŕ;G fPOםGwJ!D 5J0v`m  %@oFJ-1אVySvTusRjIz@ffJqU2MP 8B1n^OzU4φsͨnEsfEޕ,3 &&?|_i쭅 WII]elKm _7k /eCYZ;Ȱ|xt$@v71_Gdbz{ HTXHVyWσ*ƅ [8u u$L0 FU;c4[Jm J@s枹(:񻤶54۸jɫSB%eQtZ}uZ:ATtTkKGv䱽$E6en͌ogy >GPCT.Գ1 ΕlVy^̥Uy{tztzvnY:Ŏ[g02#;uŇd{zCI"&2YIq 9@ F#-a7P'E?c3랒w>wibpf[;J KYpkNZsqŬ%F c{F#V63iףÓ xJ Վl)/mVT6귚lrH^X"G{o,#7:)pu *tu%̅ys?yxrxFsz<?tyYl;Kmhsd(+pbP3S偨:kx-]bJz [ȴL Ѣ7.Uwq8f#J:f:gFd)sQ-3olgіͣ.{HR- y> Fc:I%AŰꗏ}H%DX[Rf- m{W BYDa')SXL!R1x/2-9UiT9<hJ|Fgfz9|1)Z])\3Eg!\Y7X¿ Z;\ V%1/_x5ݓ8+G^6opex{p֬<fN"QBEOGf,NiU$k(W( WKf}[a\ʈ,!OĸwJu|6I0]Nk֒}?)gU6 vؼ^/TD&};݈[.*PXE,Z+An ij >ַnRבlT"ܬT6:a4'@/桽?gѥW _?7; 5 ׿HvZBP+ Փ8MdTb, hrޭitkY2RИU(%~%KBvق^"ϣ x N1ܔΡЅƽit-#X|z%l?2rN&îko R[;*lutFƝ$?@ZB9{ozXp#a-WS`YE<̼]&ǚ>.C]*YsNctɝ şH t@~~D>DN$HpB@TJ^{{8G~_sA:$4,*$J 5Ԥ1ix%hț>ÆtV1 ѸA#Foq'2hX 7eFǰJ';9)H[J$(8,R/M^"Ē Q_JQ.kkTn6^ >u&7=ì)4L|ere;%ntj(4ƴ-}q/R՜uUHNώ.R@"i97{7:JlKGC]t hGLL gۅa7/gM- bQo Je4nDvސu-x,hJ NVպ KԽ١ښ㠺W[3mP苫V;>6ë.*@ *u=$g]cS ۳J64F;3j" m0hukt A t}$l 4:(I2slOKM2B7R9WGChH)pN;;Ui]ڿZO01r~ĜrGTYqptm#oMzu3T'ԃot|&HI?@ D]tŗӰ; MR8[YZty,ղk5"ey'Ht+Y;A-ճ?-BD%hˋ=nNI1\%\$}Z99CJ?hI(Vx3J<Pg=sDѠ mj:1OfU9bcq] >n399xqPӎ$**$eVqFwDtd:-D,"5בj"ϔ9W/XaVIhXD[d 54c^{-е ^u;\5 :l޵ktKЦ! ~_HOZ`ۦ!11p,9v}v'@?uxAE۽Awbv+ؗǭ'Ab%aޥ%/iDY1TQ#]qvwHSD¬A0p]Erq?5Z }& ]q'G%ԍ zF=Cx /RwHP`]CaGS~*ϱoFa8D^@pOZ!066C*%}E8r5LV RUf;?>+tL8;!7rJm `|!~blfl48gV `$$PU)U>8k''%'0y Oh?  {7g"H3~#Q#7i[|E/;=>S+{սw5* fL# Ŷ6D\RcC\}q_> RDق1ܵG ||! qx‚0+(|ֹÇ/E!O+3|&<"X<OdeH7{2mzlװS&tArјH9Ys7pX*,iF&ZC͜k,6_S Qs|=:⌈+r]v!MڴwQsEN!7< JqNH"B)fFB"\cc D֨9=Kd)99LCt?m dPq~tJ;%@#krZ!8=tq']`P.OZv (IX6S-mrG(zLyq"}$Nr#BЀgTza $lSNd?ע1NONj{N'7`'2 E}4"A4LzBT;]hN+dzf^d'aINaÇ2nT g,8j?/rIc˔ؚB0wDc  UlZu{F[{>5-9}~` DK@7l SDpk#EQ]+#>5ϰ,sN-b&DbI&%xZ[ocFddeFs1=QwҒLT#RF#fʆ3\3eL~oK3/S{9tbq-K_c pM"Jow7CvLfqH(Z=? Fl9}!Vh0-n 6VY~馾bFTҢX.h.nGP䦧ɃVYk4|B׾x S[kpINĘfX"F3S V bcC-;O!зs0Kho^a.pޤBxN=*RK`ָMV"@csD˗bs!~xN=qX稭PN'bEĀ w~Yr5{%S|8|F8_,:a?\{Jd$k錋hJ=w3PkTfA?=R.ukԃp>DA1x )VCuӉ x:`3◭q ۾^RLw ¡@D!HCu AO/e$RQkD"(б Aa\,И 5,]ZA_mHeA'CPؾ%[<Ȱ;4 I/ QY]T+PsY.Nȍ ֓px~TdUX`9pYUv=V_rU]j"c|SNM٥C՞oBLe@N7_5M>ЛX2?4S|_[āw(ȅG/ ~`brJ$~` /@~JVA?!^r'krߜ 5/s$-I5d0FWa-׏Xu4hZeN|Ɵҕtr.T >eO63B~DZˆ)1\2{Lރ`tb2dތT=#dj'ΠkzⱨxvZ?o6~:~szl(+aqr$~njcakzOT!oSnvD[(7E{‘Tv݅g1oIIȏ)2^-w *|1ʑ1Wdh: q/5Ž v3˃ ?{JYAsIt|h53hRLPp4@&S.9oPɔ /8;=8_R\%^bH0vp"T]LzcEL98@&y;x<%10A,aɣ"315t)~Ѩg2[XV Xd)2xt؉qX(aTLEZ'|Fʿ'5]XTy+Y^;A:1cmm.pJh%L/}T”~P~3Z$^S~EnQx S8'Q?mTk'^FUhEZ;=83Yf9$ -GP"j3AOq aSm4Tp^7勢 YzdjZWOZlM]YTaWiqF0Ťǧu0gY;fd&akتI0[m`0DȮOKD/I|5>RF@b*Ċ㷐 GY|SEGɡs3: iYf ZK?#D8."*w)e0TvCN9ɹYlǓ=Ja+ɭs?&=8BGQQIj*J!?V-A+u9;Z*Iż}aַLz󲮬"BN÷ao$O1Zo hZ9wLN.fLoJ޶پrcWe7uuZ I|=;! 8Ǝh Dv_l `y挏Wg| 7?R[2^50JSp"R3Rt?F<}E /981{3t˜/BhL^ɴe=8ʉ@ .'- N /c-͍x$gB$~1F/'Ska!s arrYfOZ-_)[a&K$$7߽i~Bm@ YleIgeߞbSȊ g lWkPp5s֎->M_ż|Q!6$vQaG# C4v+o12 pIk5P3O4;k rN"P$SQ*b2J~1WbLF-zJހp|I-LJ iHZ.9 zp16|*y2gqYȜ(M:Aϟ 0kJЭX 74 C!<cT84 d@0>R8E(lu0w%hXIV?Mel^Hzy%J_0 l1,t"^Ѓ&3 8w@~N#_b/֬v?6'f7(ngu.խ[Cm]ّ8DyިUDq6gwc̽g i9<]bM|!_,zI&FwSg/[s {Fj4SN<1VX"TOD}j} ttxCM$Ἧqv Z  f:0kkb k?V>)ժQdHmbKx,\"2Ts3^'dxX߳TnuDk`,CBeNIrEKQz)fEe\.0/zVVljeXM=9^iv^y9 /‹sMQ})/e25Fyk|[RHIOH˥cr8U+^Zx97G.%q/Cb32`b=<56S09T#{J __Gb}@ Gt9k>KMSX V9=y!h=#6KCyΒWF[vg~Tba>-Uaϗl{:Y^8֠[qrMI~1V2~^K5׋ăc3 eyFƔ  鈝%FϺ $53bıu>1tLW-śZw&ηj(7@dFs dv$L 'Q_2F( : () 3PkypR77Zbg\b%vDj ;ag(2B]ְWP'[P*z\k4okMyp &jGIFwU;=>6._읞^E2hJO)OS" E|-y LmiDDDQ, 1:tM?I&q"B(k}D^0+FIT.EKYx|V??VHZ8;E+6jG4&E;=xصs`rCM:$5^JJ~BG隋#Dy =gD3l BLh=l@v|-q2Jv]8ВoK|rS$LK%xm;_ʶz/S^wTyഎ/)8lcn ?7F^[h/F\tbCTOiD挛>9ZoK犷s-vX Q(I+FF"illU2D8#Rm)+Q?#=5h/8C"yUy\]_n ] WǙtd*zHn >Ud8Ȁ|C}`@v/T %Ip)1l;`NfBg|]P ÌhjМT[ Ѽ (I X)s*ݗW`hP K-n=D #-2O2kRzՎZO "~zY;$2|zӼӨZRaJ ߝMi|j GXK/f]ՃEm۶쒂Mi6B\Q/G0}ƺe%8Tۆmx% i]«'- iGmhns yd||"J/ $`_tRӑ RZf:' {d\Q[) lP*r> is-; df'+U% I obU!{-mB