debian/0000775000000000000000000000000012177716456007210 5ustar debian/changelog0000664000000000000000000015136312177716446011072 0ustar dahdi-linux (1:2.5.0.1+dfsg-1ubuntu3) saucy; urgency=low * Fix compile issues with 3.11 kernel -- Stefan Bader Mon, 05 Aug 2013 13:39:27 +0100 dahdi-linux (1:2.5.0.1+dfsg-1ubuntu2) precise; urgency=low * Fix compile issues with 3.2 kernel (LP: #928499) -- Paul Belanger Fri, 10 Feb 2012 10:07:30 +0000 dahdi-linux (1:2.5.0.1+dfsg-1ubuntu1) precise; urgency=low * Merge from Debian testing (LP: #879743). Remaining changes: - debian/dahdi-dkms.install.in : add .version - Import modifications by Javier Uruen Val : - debian/system.conf - debian/modules - DKMS support: - debian/control: Added Depends to dahdi-linux (dahdi-dkms | dahdi-source) - debian/control: Add dkms package - debian/dahdi-dkms.install - debian/dahdi-dkms.postinst - debian/dahdi-dkms.prerm - debian/dahdi-linux.install - debian/rules: add code to automatically configure dkms.conf - debian/dkms.conf.in * Dropped changes: - debian/control: Fix typo (right command is m-a a-i dahdi-source) - debian/patches/define_spinlock: Fixed upstream * debian/rules: Debian moved to dh, re-add code to automatically configure dkms.conf * debian/dkms.conf.in: update to reflect dahdi-linux-extra patch update (opvxa1200's new location (subdir) and to include ap400 and opvxd115) * debian/patches/zaphfc_d_channel_fix.patch: Correct typo in zaphfc module until fix gets synced into dahdi-linux-extra patch in Debian (LP: #612091) -- Adam Gandelman Fri, 21 Oct 2011 17:32:09 -0700 dahdi-linux (1:2.5.0.1+dfsg-1) unstable; urgency=low * New upstream release (Closes: #639702). - Patch wcb4xxp_bn4s0e removed: merged upstream. - Patch define_spinlock removed: merged upstream. - Patch dahdi-linux-extra updated. * Updated dahdi-linux-extra: - "Upstream" is now a complete git mirror. - Actually include ap400 in the list of modules to build. - Updated OpenVox drivers: opvxa1200 is a subdirectory - Updated OpenVox drivers: opvxd115 added (digital cards). * Patch define_spinlock: include a (slightly big) build fix from upstream. * Standards version 3.9.2 (no change needed). * Switch to dh. * Patch notest: Remove a bogus upstream 'test' target. * Lintian override for an odd interpteter a dummy kernel module init script. * Dahdi udev rules are now named 'dahdi-linux.conf'. * Patch xpp_fix_2fxs6fxo: bugfix for Xorcom 2FXX6FXO module code. -- Tzafrir Cohen Sun, 25 Sep 2011 22:58:17 +0300 dahdi-linux (1:2.4.1+dfsg-1ubuntu2) oneiric; urgency=low * debian/patches/define_spinlock: Fix DKMS FTBFS, move test for DEFINE_SPINLOCK into include/dahdi/kernel.h. Cherry picked from upstream commit (r9411) -- Adam Gandelman Tue, 24 May 2011 11:21:53 -0700 dahdi-linux (1:2.4.1+dfsg-1ubuntu1) oneiric; urgency=low * Merge from Debian unstable (Remaining changes): - debian/dahdi-dkms.install.in : add .version - debian/rules : cleanup dahdi-dkms.install and dkms.conf after build - debian/control: Fix typo (right command is m-a a-i dahdi-source) - Import modifications by Javier Uruen Val : - debian/system.conf - debian/modules - DKMS support: - debian/control: Added Depends to dahdi-linux (dahdi-dkms | dahdi-source) - debian/control: Add dkms package - debian/dahdi-dkms.install - debian/dahdi-dkms.postinst - debian/dahdi-dkms.prerm - debian/dahdi-linux.install - debian/rules: add code to automatically configure dkms.conf - debian/dkms.conf.in -- Adam Gandelman Thu, 19 May 2011 15:15:46 -0700 dahdi-linux (1:2.4.1+dfsg-1) unstable; urgency=low * New Upstream release. - Patch uk_rotary dropped: merged upstream. - Patch oslec_include_2634 dropped: merged upstream. - Patch xpp_usb_buffer_2635 dropped: merged upstream. - Patch voicebus_sem_h_2635 dropped: merged upstream. * dahdi_linux_extra now includes AP400 drivers (Closes: #582095). * Patch oslec_auto: Makes adding OSLEC less painful. - But for now it requires manually editing patch dahdi_linux_extra. -- Tzafrir Cohen Mon, 07 Mar 2011 12:01:55 +0200 dahdi-linux (1:2.3.0.1+dfsg-2ubuntu2) natty; urgency=low * debian/patches/bkl_fix_ioctl: Do not ioctl when unlocked_ioctl is available. Previous behaviour was causing a dkms build failure. Patch cherry picked from upstream commit (r9142). (LP: #731197) * debian/patches/drop_semaphores_as_mutexes: Use of semaphores as mutexes feature was removed from the kernel, causing dkms build failure. Patch cherry picked from upstream commit (r9464), with minor alterations. -- Dave Walker (Daviey) Wed, 13 Apr 2011 10:56:26 +0100 dahdi-linux (1:2.3.0.1+dfsg-2ubuntu1) natty; urgency=low * Merge from Debian testing (Remaining changes): - debian/dahdi-dkms.install.in : add .version - debian/dkms.conf.in : do not 'make clean' after compile. We have to do this because the wanpipe (sangoma) drivers need Module.symvers. - debian/rules : cleanup dahdi-dkms.install and dkms.conf after build - debian/control: Change Maintainer - debian/control: Removed Uploaders field. - debian/control: Removed Debian Vcs-Svn entry and replaced with ubuntu-voip Vcs-Bzr, to reflect divergence in packages. - debian/control: Fix typo (right command is m-a a-i dahdi-source) - Import modifications by Javier Uruen Val : - debian/system.conf - debian/modules - DKMS support: - debian/control: Added Depends to dahdi-linux (dahdi-dkms | dahdi-source) - debian/control: Add dkms package - debian/dahdi-dkms.install - debian/dahdi-dkms.postinst - debian/dahdi-dkms.prerm - debian/dahdi-linux.install - debian/rules: add code to automatically configure dkms.conf - debian/dkms.conf.in [ Stefan Lesicnik ] * debian/dkms.conf.in: Add () so make.log captures all output * debian/dkms.conf.in: Build dahdi_dummy as module to fix dkms build failure -- Stefan Lesicnik Thu, 11 Nov 2010 11:24:07 +0200 dahdi-linux (1:2.3.0.1+dfsg-2) unstable; urgency=medium [ Faidon Liambotis ] * Add myself to uploaders. [ Tzafrir Cohen ] * Disable mmx_auto for now (Closes: #593438). * dahdi_linux_extra: Re-apply zaphfc r7 (Closes: #598886). * Patch wcb4xxp_bn4s0e: missing PCI IDs for beroNet BN4S0e (Closes: #600839). -- Tzafrir Cohen Wed, 20 Oct 2010 21:21:58 +0200 dahdi-linux (1:2.3.0.1+dfsg-1) unstable; urgency=low * New upstream version (Closes: #546319). * Patch no_dummy removed: merged upstream. * Patch wcb4xxp_extra_trunk removed: merged upstream. * Patch chanmute: make it also explicitly disable the untested DAHDI_AUDIO_NOTIFY. * Patch oslec_include_2634: fix building with 2.6.34 (Closes: #586216). * Patches xpp_usb_buffer_2635, voicebus_sem_h_2635: Likewise for 2.6.35. * Patch dahdi_linux_extra: revert last fix to zaphfc. * Standards version 3.9.1 (no change needed). -- Tzafrir Cohen Tue, 03 Aug 2010 03:58:11 +0300 dahdi-linux (1:2.2.1.1+dfsg-1) unstable; urgency=low * New upstream minor release. * Updated copyrights file. * Standards version 3.8.4: no change needed. * Patch wcb4xxp_extra_trunk: backport extra PCI IDs for wcb4xxp (more HFC-[248]S cards). * Switch to a @debian.org address. * Dpkg V3 format (it was quilt already). * Remove lintian override that is no longer needed. * Patch dahdi_linux_extra: manually fix to remove a leading '---'. * dahdi-modules packages now depend on the linux-image versions. * Other minor updates to the packaging of the dahdi-modules package. -- Tzafrir Cohen Sun, 18 Apr 2010 17:14:02 +0300 dahdi-linux (1:2.2.1+dfsg-1ubuntu3) maverick; urgency=low * 2.6.35-build.patch: - fixes builds on 2.6.35 kernels. Fixed in debian 1:2.3.0.1+dfsg-1 (LP: #591375) -- Niall Creech Fri, 17 Sep 2010 09:45:04 +0200 dahdi-linux (1:2.2.1+dfsg-1ubuntu2) lucid; urgency=low * Bug fixes: - debian/dahdi-dkms.install.in : add .version - debian/dkms.conf.in : do not 'make clean' after compile. We have to do this because the wanpipe (sangoma) drivers need Module.symvers. - debian/rules : cleanup dahdi-dkms.install and dkms.conf after build - debian/control : add Depends gcc - debian/control : add wget and gawk (LP: #481566, LP: #444522, LP: #429510) -- Jean-Michel Dault Sun, 07 Mar 2010 21:37:49 -0500 dahdi-linux (1:2.2.1+dfsg-1ubuntu1) lucid; urgency=low * Merge from debian pkg-voip * Changes from Debian: - debian/control: Change Maintainer - debian/control: Removed Uploaders field. - debian/control: Removed Debian Vcs-Svn entry and replaced with ubuntu-voip Vcs-Bzr, to reflect divergence in packages. - debian/control: Fix typo (right command is m-a a-i dahdi-source) - debian/rules: Fix typo (s/rules/rules.d/) * Import modifications by Javier Uruen Val : - debian/system.conf - debian/modules * DKMS support: - debian/control: Added Depends to dahdi-linux (dahdi-dkms | dahdi-source) - debian/control: Add dkms package - debian/dahdi-dkms.install - debian/dahdi-dkms.postinst - debian/dahdi-dkms.prerm - debian/dahdi-linux.install - debian/rules: add code to automatically configure dkms.conf - debian/dkms.conf.in * From debian pkg-voip SVN: [ Tzafrir Cohen ] * New upstream release (Closes: #564739). * Dropped patch wcfxo_reset_fix: merged upstream. * Section name of dahdi-source: kernel * Also provide an example xpp.conf . * Patch uk_rotary (Closes: #546329). * Override the bogus lintian warning for 'm-a a-i dahdi' * Remove some remaining Zaptel left-overs. * Replaced extra drivers and patches with huge patch dahdi_linux_extra (Closes: #564720). * Patch xpp_udev_rules_2632: fix XPP udev rules (Closes: #562024). * Also copy the .version file, to make sure modules have versions. * Move xpp udev rules under /lib. Name still kept for the sake of simplicity (thanks, Lintian). -- Jean-Michel Dault Mon, 15 Feb 2010 17:18:14 -0500 dahdi-linux (1:2.2.1+dfsg-1) unstable; urgency=low [ Tzafrir Cohen ] * New upstream release (Closes: #564739). * Dropped patch wcfxo_reset_fix: merged upstream. * Section name of dahdi-source: kernel * Also provide an example xpp.conf . * Patch uk_rotary (Closes: #546329). * Override the bogus lintian warning for 'm-a a-i dahdi' * Remove some remaining Zaptel left-overs. * Replaced extra drivers and patches with huge patch dahdi_linux_extra (Closes: #564720). * Patch xpp_udev_rules_2632: fix XPP udev rules (Closes: #562024). * Also copy the .version file, to make sure modules have versions. * Move xpp udev rules under /lib. Name still kept for the sake of simplicity (thanks, Lintian, Closes: #566876). * Patch no_dummy: use dahdi internal timing instead of dahdi_dummy. * Patch chanmute: Enable CHANMUTE optimization (for xpp, mostly). [ Mark Purcell ] * Cleanup debian/watch -- Mark Purcell Sun, 07 Mar 2010 04:41:21 +1100 dahdi-linux (1:2.2.0.2~dfsg-1) unstable; urgency=low * New upstream release * New openvox drivers: r113 from their SVN. * Fix copyright file for origin of OpenVox drivers. * Use new zaphfc drivers (r5) from http://code.google.com/p/zaphfc/ . Note that those use hardhdlc in system.conf (Closes: #532345). * Remove obsolete bristuff drivers we don't build anyway. Leave vzaphfc for now as this is practically upstream (Completely closes: #548061). * Not applying bri_dchan. Astribank BRI spans will use hardhdlc. * Patch wcfxo_reset_fix: Closes: #546331 . * Replace 'dh_clean -k' with dh_prep * Standards version 3.8.3 (no change needed). * Patch mmx_auto: relied on ARCH to tell between i386 and x86_64, which is not a good idea on newer kernels. Use DAHDI_ARCH instead. * Remove useless postinst and preinst. debhelper adds them anyway (and with the -e). * Do include examples of dahdi-linux . -- Tzafrir Cohen Sun, 04 Oct 2009 22:47:30 +0200 dahdi-linux (1:2.2.0~dfsg~rc5-1) unstable; urgency=low [ Mark Purcell ] * New upstream release [ Tzafrir Cohen ] * Enable MMX optimization on i386 that supports it (x86, not x86_64). Doubles OSLEC's performance: - Adds patches mmx_auto and mmx_fix from upstream bug report. - Fixes patch oslec_kbuild to use it if enabled. * modulestest: add -v to support building from debuild. -- Tzafrir Cohen Wed, 10 Jun 2009 15:59:34 +0300 dahdi-linux (1:2.2.0~dfsg~rc4-1) unstable; urgency=low * New upstream release [ Tzafrir Cohen ] * Dropped qozap as wcb4xxp provides that functionality. * New upstream RC. * Actually build OpenVox drivers. * opvxa1200.c: rev. 1.4.12.4 (battery fixes and such) * Fix '${match}' in udev rules file (hardwire). * no_firmware_download: Disable downloading a binary kernel module at build time. [ Victor Seva ] * fix debian/watch. -- Mark Purcell Wed, 20 May 2009 07:22:46 +1000 dahdi-linux (1:2.1.0.4~dfsg-1) experimental; urgency=low [ Tzafrir Cohen ] * Zaptel renamed DAHDI and split to dahdi-linux and dahdi-tools. * DAHDI is incompatible (ABI and API) with Zaptel. All modules need to be adapted. * OSLEC wrapper included upstream. Includeing a version of the OSLEC code from the kernel staging directory. - Patch oslec_kernelorg: Upstream kernel.org OSLEC code. * Dropping some aparantly unused external Zaptel drivers. * Extra drivers moved from kernel/ to drivers/dahdi/ to fit the kernel tree. * UDEV rules are handled here as well. No need to change device names, only set permissions. * Patch fix_readme_astribank: Fix an asciidoc formatting issue. * Patch oslec_kbuild: Make oslec's build unconditional. * Fix for zaphfc with DAHDI (by Timo Teräs ) * qozap no longer included: Use wcb4xxp instead. * ztgsm no longer built: nobody really used it. * cwain and vzaphfc currently included but not built. * Don't generate static device files automatically. Provide a script to do that. Until we know if there is an actual use for the non-udev setup. [ Mark Purcell ] * Update Description: fixes description-starts-with-package-name * Add ${misc:Depends} - debhelper-but-no-misc-depends * Add myself to uploaders * debian/compat -> 7 - Fixes:package-uses-deprecated-debhelper-compat-version * Upload to experimental while we await NEW & debian-release -- Mark Purcell Sun, 29 Mar 2009 17:58:23 +1100 zaptel (1:1.4.11~dfsg-3) unstable; urgency=low * zaptel.init: exit on a different condition: Nicer handling of ztdummy. * zaptel.init: Give a sane default to XPP_SYNC. -- Tzafrir Cohen Mon, 03 Nov 2008 09:35:23 +0200 zaptel (1:1.4.11~dfsg-2) unstable; urgency=low * Patch xpp_fxs_power: Fixed an issue with hook detection of the Astribank FXS module. * Don't fail init.d script if fxotune fails. This may happen if running it when Asterisk is already running. * Bump standards version to 3.8.0.0 . * Ignore false lintian warning ("m-a a-i" has "a a"). * Patch xpp_fxo_cid_always: do always pass PCM if that's what the user asked. * Patch vzaphfc_proc_root_dir: fix vzaphfc on 2.6.26. * Patch wcte12xp_flags: Proper time for irq save flags. * Patch headers_2627: Fix location of semaphore.h for 2.6.27 . * Patch xpp_fxs_dtmf_leak: Don't play DTMFs to the wrong channel. * Patch wctdm_fix_alarm: Fix sending channel alarms. * Patch device_class_2626: Fix building 2.6.26 (Closes: #493397). * Using dh_lintian for lintian overrides, hence requiring debhelper 6.0.7. * Lintian: we know we have direct changes. Too bad we're half-upstream :-( * Fix doc-base section names. -- Tzafrir Cohen Thu, 28 Aug 2008 22:58:23 +0300 zaptel (1:1.4.11~dfsg-1) unstable; urgency=medium [ Faidon Liambotis ] * Update qozap from bristuff-0.4.0-RC1. - fixed duoBRI miniPCI detection in qozap. - added support for PCIe variants of duoBRI and quadBRI. * Update cwain from bristuff-0.4.0-RC1. * Update ztgsm from bristuff-0.4.0-RC1. - reduced baudrate (serial interface to gsm modules) to be more robust against irq misses in loaded systems - improved serial debug output - added module parameter "baudrate" (the default is 19200) - added AT commands for SIM card selection - added AT commands for shutting down/starting GSM modules [ Tzafrir Cohen ] * New upstream release. * Patch xpp_fix_t1 dropped: merged upstream. * Finally removing 00list. * Patch sigcap_dacs: tell zaphfc and vzaphfc to report support for ZT_SIG_DACS. Makes ztscan report then as digital spans. * Patch bri_dchan: change ZT_FLAG_BRIDCHAN as ZT_FLAG_MTP2 is also defined to be 19 as of zaptel 1.4.11 . * Patch chan_release_check: fixes a regression from 1.4.10.1 that causes a panic if disconnecting an Astribank device with a channel still open. * Patch florz-vmalloc: proper includes for zaphfc on non-x86 platforms. (Closes: #488513) * oslec_wrap.c: cycles code for powerpc by Stelios Koroneos. * And dummy cycles code for other architectures. [ Mark Purcell ] * Build-Depends: asciidoc (>= 8.2.6-1.1) - asciidoc 8.2.6-1 is broken - FTBFS: ERROR: unsafe: include file: /etc/asciidoc/./javascripts/toc.js (Closes: #487011) * Urgency medium as we fix RC bug -- Mark Purcell Mon, 30 Jun 2008 20:22:37 +1000 zaptel (1:1.4.10.1~dfsg-1) unstable; urgency=low [ Tzafrir Cohen ] * New upstream bugfix release. * Note the buid "error" from http://bugs.digium.com/12426 . * Fix the generation of tonezone.txt. * Patch xpp_fix_t1: allow the xpp pri module to initialize as T1. * Set OSLEC as the default echo canceller. * The "unload" init.d script operation will now also attempt to unload oslec. -- Tzafrir Cohen Thu, 8 May 2008 02:03:34 +0300 zaptel (1:1.4.10~dfsg-1) unstable; urgency=low [ Mark Purcell ] * New upstream release [ Tzafrir Cohen ] * Added NEWS.Debian about zaptel modules incompatibility. * Do run 'make dist-clean' on modules build. It prints an ugly error, but only after the actualy relevant clean. * Watching downloads.digium.com directly again. -- Tzafrir Cohen Mon, 14 Apr 2008 14:07:54 +0300 zaptel (1:1.4.9.2~dfsg-1) unstable; urgency=low * New upstream release (Closes: #464900). - All kernel modules moved to under kernel/ * Converting patches to quilt (because bristuff uses them). * Include the separate xpp changelog file. * Fix a possible crash with oslec when a fax tone is detected: http://sourceforge.net/mailarchive/message.php?msg_name=20080217212421.GT15415%40xorcom.com (Closes: #447245). * Adjusted lintian overrides: mknod is now called from a function. * Adjust vzaphfc to netdevice API changes in kernel 2.6.24. * Once again ignoring 'make distclean' errors: it will fail if we don't have kernel sources / headers for current kernel :-( . * Remove some unnecessary changes from the florz zaphfc patch - fixes zaphfc warning. -- Tzafrir Cohen Thu, 20 Mar 2008 16:31:25 +0200 zaptel (1:1.4.8~dfsg-1) unstable; urgency=low * New upstream release * bristuff.dpatch broken to smaller patches, as in bristuff 0.4.0-test6. - ztpty.c is added as a source file. - bri_dchan.dpatch, proc_read.dpatch, ztcfg-start_stop.dpatch and zt_alarm_notify_no_master_change.dpatch . * beronet.dpatch: Support for Bero.net cards (Closes: #453496). * Adapted ztcfg-start_stop.dpatch to zaptel 1.4.8 . * Removing xpp_m_subdirs.dpatch: merged upstream. * tones.h is no longer generated. * kbuild_src.dpatch: A small build issue already fixed upstream. * oslec_zaptel.dpatch: Add a compatibility function for the old echo_can_create() of zaptel < 1.4.8 . * Also copy the new wcte12xp/ to the source tarball. * Delete old created files on purge (Closes: #454388). -- Tzafrir Cohen Tue, 29 Jan 2008 13:32:11 +0200 zaptel (1:1.4.7.1~dfsg-1) unstable; urgency=low [ Tzafrir Cohen ] * New upstream release. - New upstream version 1.4.6 (Closes: #452315, #442414). * Add Voicetronix OpenPCI Zaptel driver (wcopenpci.c) . * Explicitly delete depmod results (modules.dep at el.) * ztdiag.dpatch: removed (applied by upstream). * xpp_m_subdirs.dpatch: removed (applied by upstream). * Makefile_fix_clean.dpatch: removed (applied by upstream). * Makefile_opt_level.dpatch: removed (applied by upstream, except for 2.4 kernel modules). * Call 'make dist-clean' rather than 'make clean'. * Don't run 'make dist-clean' on zaptel-modules build. * Document zaptel-modules build test. * Don't run ztcfg if zaptel was not loaded. But do fail if ztcfg has failed. (Closes: #407996). * Don't build wcopenpci on big endian platforms (Module gives #error there). * Actually fix building xpp with M instead of SUBDIRS. * Updates to bristuff zap modules from bristuff-0.3.0-1y-l: - Fix build warnings. - Allow sharing interrupts. * Update cwain from recent bristuff. [ Faidon Liambotis ] * Don't delete old device nodes on installations since it's needed only for upgrades from <= sarge which isn't supported. Shuts up lintian error. * Correctly detect udev/devfsd and chrooted environments at postinst time. * Fix debian/watch by using a pkg-voip wrapper to avoid upstream's silly redirections. (Closes: #449673) * Fix OSLEC so that audio works again on x86_64 systems. * Update Standards-Version to 3.7.3, no changes needed. * Refresh Uploaders: add myself, remove Jose Carlos Garcia Sogo, Santiago Garcia Mantinan and Santiago Ruano Rincon. -- Faidon Liambotis Fri, 28 Dec 2007 17:59:57 +0200 zaptel (1:1.4.5.1~dfsg-2) unstable; urgency=low [ Kilian Krause ] * Update oslec to r942. Add mmx.h (disabled for now). * Do export oslec_echo_can_identify from oslec.ko (Closes: #439814). * Add Homepage field as added in dpkg-dev 1.14.6. * Fix debian-rules-uses-pwd [ Mark Purcell ] * Remove Build-Depends restrictions for packages in stable [ Tzafrir Cohen ] * vzaphfc: update to 1.44, and apply our fixes. * vzaphfc: fix building with Sarge 2.6 kernels. * Fix asciidoc buildign in Sarge (temporary fix, until we get upstream fix in 1.4.6). * Don't build oslec if it is not the Zaptel echo canceller. * Adjust line numbers in Lintian ignore file. * Include oslec-ctrl-panel.sh as an example (Closes: #443363). -- Kilian Krause Sat, 22 Sep 2007 12:08:52 +0200 zaptel (1:1.4.5.1~dfsg-1) unstable; urgency=low * New upstream release. [ Kilian Krause ] * Add dpkg-dev (>= 1.13.19) to Build-Depends for binary:Version * Correct the zaptel.init to point to correct path of fxotune (Closes: #439310) * Put opt level back to -O2 in accordance with Debian Policy. [ Tzafrir Cohen ] * Pass extra modules / subdirs to modules make. * Re-add zaptel_perl.dpatch. Clarified patch's description in hope to aviod re-re-re-removal of this patch. * There's now an HTML version of README.Astribank . And upstream Makefile is better at cleaning. * Don't install man pages. 'make install' does that already (Closes: #422943) * Fixed building vzaphfc, opvx1200p and ds1x1f of kernel >=2.6.22 (>= 2.6.19?). * xpp_m_subdirs.dpatch: Fixed building of xpp modules with M= . * modulestest -r: test current kernel. -- Kilian Krause Sun, 26 Aug 2007 12:08:10 +0200 zaptel (1:1.4.5~dfsg-1) unstable; urgency=low * New Upstream Maintenance Release - support for Digium's new 32 channel hardware echo canceler (VPMADT032) for the TDM800P and TDM2400P [ Tzafrir Cohen ] * zaptel_perl.dpatch: install perl modules to vendorlib rather than to sitelib. This fixes the zaptel-perl utilities (e.g: zaptel_hardware). * Build-depend on asciidoc to produce some package documentation. * Mark doc-base documentation. * Simplify Astribank initialization with zaptel-perl. * provide symlinks for headers files: compatibility with zaptel 1.2 locations of zaptel.h and tonezone.h . [ Faidon Liambotis ] * bristuff 0.4.0-test4 - Add zaptel.patch as debian/patches/bristuff.dpatch. - Update zaphfc, qozap, cwain and ztgsm. * Add florz' patch to zaphfc; removes RTAI support. (Closes: #382227) * Don't fail on mknod errors; helps on vserver setups. (Closes: #411850) [ Mark Purcell ] * Remove echocan_env.dpatch - merged upstream * Remove zaptel_perl.dpatch - merged upstream * Add debian/patches/inlcude.dpatch - upstream typo * debian/rules: upstream change INSTALL_PREFIX->DESTDIR * debian/zaptel.doc-base.readme add Index: doc-base-file-no-index * Ship new wctdm24xxp modules -- Mark Purcell Sat, 18 Aug 2007 13:20:31 +0100 zaptel (1:1.4.4~dfsg-1) unstable; urgency=low * New Upstream Release [ Tzafrir Cohen ] * echocan_env.dpatch: set the echo canceller from the environment. * oslec/ The bulk of the oslec files. * oslec_zaptel.dpatch: Minimal changes to zaptel to add oslec, beyond the oslec directory. * oslec_zaptap.dpatch: The oslec zaptap echo sampling device (probably still does not apply). * man_fixes.dpatch: Documentation fixes from upstream. * Removing unrequired/harmful and useless modprobe / modutils config. * README.Debian updates. [ Mark Purcell ] * debian/patches/zaptel_perl.dpatch now included upstream * Switch to ${binary:Version} substvar-source-version-is-deprecated -- Mark Purcell Wed, 18 Jul 2007 21:41:51 +0100 zaptel (1:1.4.3~dfsg-2) unstable; urgency=low [ Tzafrir Cohen ] * Fixed and re-added zaptel_perl. * And added zaptel_hardware (zaptel hardware lister), just for fun. -- Tzafrir Cohen Sat, 09 Jun 2007 03:36:17 +0300 zaptel (1:1.4.3~dfsg-1) unstable; urgency=low * New upstream release - A fix for the potential for a rare deadlock between zaptel and the wct4xxp, wcte11xp, and wct1xxp drivers - Fixes for the VPM450M module on FC6 to correct a potential stack overflow scenario at load time. - Many updates to the Astribank driver * disable debian/patches/zaptel_perl as it doesnt apply cleanly -- Mark Purcell Sat, 09 Jun 2007 00:01:55 +0100 zaptel (1:1.4.2.1~dfsg-2) unstable; urgency=low * Include debian/compat in zaptel-sources - missing debian/compat file in archive (Closes: #422153) -- Mark Purcell Sat, 02 Jun 2007 10:22:04 +0100 zaptel (1:1.4.2.1~dfsg-1) unstable; urgency=low * New upstream release - Added the ability to monitor pre-echo cancellation audio with ztmonitor - Fixed some places where there was the potential for memory corruption on SMP systems - FTBFS with 2.6.19-1 (Closes: #405562) * zaptel 1.4 provides wcfxs->wctdm alias - No mention of wcfxs -> wctdm module rename (Closes: #419161) - Missing modutils/modprobe rules for wctdm (Closes: #419162) * provide debian/compat fixes lintian: debian-rules-sets-DH_COMPAT * Cleanup debian/patches/ -- Mark Purcell Thu, 26 Apr 2007 09:07:48 +1000 zaptel (1:1.4.1~dfsg-3) unstable; urgency=low [ TzafrirCohen ] * Better shape for ztdiag.dpatch. Don't forget to free. -- Mark Purcell Wed, 04 Apr 2007 22:51:24 +0100 zaptel (1:1.4.1~dfsg-2) experimental; urgency=low * $(MAKE) install-libs needed to install libs to libtonezone -- Mark Purcell Sat, 24 Mar 2007 14:17:22 +0000 zaptel (1:1.4.1~dfsg-1) experimental; urgency=low [ Tzafrir Cohen ] * New upstream release. * Fix installation of tonezone.h . * merges from trunk: * Update standards version to 3.7.2 . * ztdiag.dpatch: fix the ioctl ZT_CHANDIAG and eable it. * Should fix modules building: restore generated files that were removed by a clean (Closes: #415280). * Update debian/watch for ~ versions * zaptel_perl.dpatch: Do install perl modules and scripts. * zaptel_perl.dpatch: a few other fixes (until next upstream release). * Add Makefile.kernel26 and wctc4xxp/ to source tarball. * Don't install /etc/zaptel.conf by default (Closes: #383081) . * List the tonezones supported by ztcfg and libtonezone in tonezones.txt (Closes: #379108). * Remove man pages from debian/, as they are included in upstream. -- Tzafrir Cohen Sat, 24 Mar 2007 10:03:54 +0200 zaptel (1:1.4.0~dfsg-1) experimental; urgency=low * Upgrading to 1.4. * Watch for 1.4.x tarballs, rather than 1.2.x tarballs. * Disable most patches: - ukcid fails to apply. - bristuff fails to apply. - No point in patching the Makefile. * Saving my attempts to apply bristuff. * .h files moved to /usr/include/zaptel -- Tzafrir Cohen Mon, 1 Jan 2007 21:31:18 +0200 zaptel (1:1.2.12~dfsg-2) UNRELEASED; urgency=low * NOT RELEASED YET * Remove -O4 from all Makefiles (Closes: #391840) * Remove gcc hardcoded as HOSTCC -- Kilian Krause Thu, 28 Dec 2006 13:12:16 +0100 zaptel (1:1.2.12~dfsg-1) unstable; urgency=low * New upstream release. (Closes: #403326) -- Kilian Krause Wed, 27 Dec 2006 23:23:40 +0100 zaptel (1:1.2.11.dfsg-1) unstable; urgency=low [ Tzafrir Cohen ] * Reverting my changes from 1:1.2.9.1.dfsg-2. Moved to the experimental branch. [ Kilian Krause ] * Remove bogus zaptel-modules from being Recommends (Closes: #387961) * Update vzaphfc as proposed by Jens Wilke [ Mark Purcell ] * New Upstream Release - Fixes: Fails to build with pristine upstream kernel, very recent version (Closes: #400705) - Fixes: Please package version 1.2.11 (Closes: #399634) - Fixes: vzaphfc: error: 'CHECKSUM_HW' undeclared (Closes: #386498) * Cleanup debian/patches/wct4xxp-dfsg.dpatch * debian/rules call dh_installmodules from binary_modules: - Fixes: I had to do depmod -a manually after doing m-a a-i zaptel (Closes: #332787) * Update debian/patches/Makefile_uname.dpatch to force -O2 - Fixes: Cannot initiate a call to BRI (Closes: #386052) * Remove Depends: zaptel from debian/control.modules.in - please don't depend on zaptel (Closes: #391826) -- Mark Purcell Sat, 2 Dec 2006 14:33:30 +0000 zaptel (1:1.2.10.dfsg-2) unstable; urgency=low * bristuff-0.3.0-PRE-1v * Remove redundant GPL LICENCE text -- Mark Purcell Tue, 24 Oct 2006 22:41:01 +0100 zaptel (1:1.2.10.dfsg-1) unstable; urgency=low * New upstream release -- Mark Purcell Sun, 22 Oct 2006 20:27:19 +0100 zaptel (1:1.2.9.1.dfsg-2) unstable; urgency=low [ Tzafrir Cohen ] * zaptel 1.4 compatibility changes: - place zaptel.h and tonezone.h in /usr/include/zaptel (through symlinks) - zaptelh_14.dpatch: declare some zaptel 1.4 interfaces (not implemented anywhere, though). [ Mark Purcell ] * debian/rules patch from Robert Millan - the package doesn't compile (Closes: #390903) * add debian/patches/dbug391840.dpatch - ztcfg segfaults because of -O4 (Closes: #391840) * add debian/patches/wct4xxp-dfsg.dpatch - wct4xxp and other modules are not built anymore on zaptel- 1.2.8.dfsg-1 (Closes: #388756) -- Mark Purcell Tue, 10 Oct 2006 09:36:58 +1000 zaptel (1:1.2.9.1.dfsg-1) unstable; urgency=low * New Upstream Release * firmware removed from wct4xxp/OCT6114-128D.ima * Lintian cleanup; spelling-error-in-copyright -- Mark Purcell Sat, 23 Sep 2006 13:58:15 +0100 zaptel (1:1.2.8.dfsg-1) unstable; urgency=low * New Upstream Release -- Mark Purcell Wed, 23 Aug 2006 07:30:22 +0100 zaptel (1:1.2.7.dfsg-4) unstable; urgency=low * Install zaptel.conf.sample as a confile under /etc * Add Recommends: zaptel-modules * Improve error handling and conf file checking in init.d. (Closes: Bug#382604) -- Mark Purcell Thu, 17 Aug 2006 08:34:43 +0100 zaptel (1:1.2.7.dfsg-3) unstable; urgency=low [ Kilian Krause ] * Simplified vzaphfc patch. [ Mark Purcell ] * Build-Depends: debhelper (>= 5.0.37) and dh_installmodules makes zaptel-source.postinst & zaptel-modules.post{inst,rm} obsolete Fixes: postinst/postrm depmod -- update templates to use dh_installmodules instead (Closes: #381754) * postinst failure (Closes: #361312) * zaptel-modules from testing don't compile on Sarge (Closes: #376719) * pciradio.c:1810: error: syntax error before string constant (Closes: #368145) * Can't recompile zaptel modules on Sarge (Closes: #375581) * zaptel-modules from testing don't compile on Sarge (Closes: #376719) -- Mark Purcell Thu, 10 Aug 2006 23:39:58 +0100 zaptel (1:1.2.7.dfsg-2) unstable; urgency=low * Fix get-orig-source target to make dfsg repacking work * Fix zaptel-source to build without firmware again. Required dropping wct4xxp module. Added vzaphfc to linux-2.6 modules. (Closes: #381123) -- Kilian Krause Thu, 3 Aug 2006 11:48:14 +0000 zaptel (1:1.2.7.dfsg-1) unstable; urgency=high * Urgency high as this is blocking a security fix for asterisk * Remove non-modifiable firmware to make DFSG compliant. Does anyone need this firmware? (Closes: #379458) -- Mark Purcell Tue, 1 Aug 2006 15:27:09 +0100 zaptel (1:1.2.7-2) unstable; urgency=low * Copying Makefile as before to the source package, Copying some extra files now needed for building (Closes: #378864) -- Mark Purcell Tue, 1 Aug 2006 06:29:39 +0100 zaptel (1:1.2.7-1) unstable; urgency=low * New upstream release [ Kilian Krause ] * Add vzaphfc driver (enhanced zaphfc) by Jens Wilke. [ Tzafrir Cohen ] * Separating ZapBRI modules to directories, rather than patches * Example configs moved from zaptel-source to zaptel * Removing some unneeded dirs from zaptel-source * debian/patches/Makefile_kbuild: a small part of the original one. Fixes building on Sarge * genzaptelconf is now in zaptel * xpp/utils/Makefile has a decent install target * debian/rules: Use CURDIR * debian/modulestest: Building modules for -3 kernels * fix x bit of files in /usr/share/zaptel * removed genzaptelconf from debian/ * Added support for the OpenVox A1200P card (http://www.openvox.com.cn/) * debian/control: require libusb-dev for building xpp firmware loader. * debian/control: Recommend package xpp-firmware (should be added to non-free) * bristuff_local_zaptelh.dpatch: Build bristuff modules with correct zaptel.conf (in Sarge) * Makefile_uname.dpatch: Updated. Note: watch for PWD issues. * Makefile_bristuff.dpatch: updated to reflect Makefile change. -- Mark Purcell Mon, 17 Jul 2006 21:48:21 +0100 zaptel (1:1.2.6-2) unstable; urgency=high * Urgency high as this is blocking a security fix for asterisk [CVE-2006-2898] * Add debian/libtonezone-dev.links - Realy fix: missing libtonezone.so.1 symlink (Closes: #372887) -- Mark Purcell Wed, 14 Jun 2006 13:40:31 +1000 zaptel (1:1.2.6-1) unstable; urgency=high [ Mark Purcell ] * Urgency high as this is blocking a security fix for asterisk [CVE-2006-2898] * New upstream release. - can't find zaptel.h during build (Closes: #330137) - errors in fxotune.c (Closes: #370213) - Cannot make zaptel-source: SUBDIR not found (Closes: #368561) [ Kilian Krause ] * Weed out old unused patches. Add comments which patches have been included upstream for next release. [ Lionel Elie Mamane ] * Load ztdummy when needed, not when not needed. [ Tzafrir Cohen ] * bristuff: 0.3.0-PRE1p * We have another ZapBRI module: ztgsm * Experimental support in genzaptelconf for ztgsm (from sample files) * genzaptelconf: 0.5.1 (does not require restart of asterisk) * zaptel.init: 'unload' operation. Better support for Astribank * moduletest script fixes * bristuff added ztpty * genzaptelconf: wait for xpp (astribank) module to register after loadin it * minor xpp driver fixes (already in 1.2 branch) [ Julien BLACHE ] * debian/libtonezone1.links: + Create the libtonezone.so.1 symlink (closes: #372887). -- Mark Purcell Wed, 14 Jun 2006 10:59:52 +1000 zaptel (1:1.2.5-1) unstable; urgency=low [ Tzafrir Cohen ] * New upstream version * Only build xpp for i386, as it currently crashes on other arches. * Fix compilation of xpp for 2.6.14 [ Kilian Krause ] * Fix gendigits to write to stdout. -- Kilian Krause Thu, 30 Mar 2006 23:52:38 +0300 zaptel (1:1.2.4-1) unstable; urgency=low * New upstrream release (Closes: #353094) * removing xpp.dpatch: merged in upstream * removing dot_version: bug fixed upstream * Makefile_kbuild.dpatch: modified, as it was not properly merged in upstream * Makefile_bristuff.dpatch: really build zaptel modules again * Makefile_xpp: fixed. * debian/modulestest: a script for postbuild of zaptel modules from a svn build * zaptel-source: removing unnecessary dependency on dpatch -- Tzafrir Cohen Thu, 23 Feb 2006 09:40:47 +0200 zaptel (1:1.2.3-2) unstable; urgency=low * bristuff 0.3.0-PRE1k. Also, renamed the dpatch to simply "bristuff" * updated version in dot_version.dpatch. * Include build_tools and .version in copied files * newer versions of genzaptelconf and xpp.dpatch -- Tzafrir Cohen Mon, 6 Feb 2006 15:30:06 +0200 zaptel (1:1.2.3-1) unstable; urgency=low * new upstrream release * ukcid.dpatch: for UK Caller ID support (Zaptel part, closes: #302380) * newer versions of genzaptelconf and xpp.dpatch * Makefile_pscmd.dpatch disabled due to a small upstream change. Revive it? * dot_version.dpatch: the tarball is missing the .version file. Remove it when it provides one -- Tzafrir Cohen Mon, 6 Feb 2006 14:02:04 +0200 zaptel (1:1.2.1-3) unstable; urgency=low * Fix compilation with binary-only mode. * bristuff 0.3.0-PRE-1f * make lintian override apply -- Tzafrir Cohen Sat, 7 Jan 2006 20:39:33 +0200 zaptel (1:1.2.1-2) unstable; urgency=low * Added bristuff 0.3.0-PRE1d patch. bristuff re-enabled. (Closes: #340627, #344432) * Documentation fixes (Closes: #316801) * Makefile_targets.dpatch is ba its small self * readded bristuff examples. with cwain this time * zaptel.init: a slightly different test for a zaptel timing source * Depend on procps due to using ps in postinst (Closes: #342699) -- Tzafrir Cohen Fri, 30 Dec 2005 19:12:54 +0200 zaptel (1:1.2.1-1) unstable; urgency=low * New upstream release * Disable bristuff for new upstream -- Mark Purcell Wed, 7 Dec 2005 21:28:23 +0000 zaptel (1:1.2.0-rc2-1) experimental; urgency=low * New upstream release -- Mark Purcell Sun, 13 Nov 2005 18:24:17 +0000 zaptel (1:1.2.0-rc1-1) experimental; urgency=low * New upstream release * Update Makefile_uname.dpatch * FTBFS: missing or incorrect directory modexamples/ (Closes: #329084) * debian/rules export DH_COMPAT=4 -- Mark Purcell Wed, 9 Nov 2005 21:37:47 +0000 zaptel (1:1.2.0-beta2-3) experimental; urgency=low * Not Released Yet * Copyright audit to debian/copyright -- Mark Purcell Mon, 7 Nov 2005 19:19:27 +0000 zaptel (1:1.2.0-beta2-2) experimental; urgency=low * Suggestions from Tzafrir Cohen - Makefile_man.dpatch can be removed: fixed by upstream - fxotune.tmpfile.dpatch can be removed: fixed by upstream - a small manual fix to Makefile_targets.dpatch: s/ manpages$// - debian/rules: dh_installman: 's/debian/doc/' a number of times (don't use doc/*.8 as there is no reason to install the pathetic torisatool.8 man page) -- Mark Purcell Tue, 1 Nov 2005 21:26:36 +0000 zaptel (1:1.2.0-beta2-1) experimental; urgency=low * New upstream release * Update Makefile_targets.dpatch * Update Makefile_man * Update fxotune_tmpfile.dpatch -- Mark Purcell Tue, 1 Nov 2005 20:51:02 +0000 zaptel (1:1.2.0-0beta1-1) experimental; urgency=low * New upstream release * Disable bristuff for experimental upload * Apply patch from Tzafrir Cohen for 1.2.0 build -- Mark Purcell Sun, 18 Sep 2005 12:48:59 +0100 zaptel (1:1.2.0-2) unstable; urgency=low [ Kilian Krause ] * Added bristuff 0.3.0-PRE1 for Asterisk 1.2.0 support. [Tzafrir Cohen] * fix Makefile_deps_kern.dpatch * remove .rej from Makefile.uname.dpatch * do install genzaptelconf man page * update genzaptelconf and its man page * echocan_env.dpatch: allow changing the echo canceller at zaptel-modules build time * Makefile_kbuild.dpatch: use kbuild for 2.6 modules build. used for: * Makefile_xpp.dpatch: (not applied by default) a small patch to enable the build of: * xpp.dpatch: drivers for Xorcom Asteribank [ Mark Purcell ] * Build and package libtonezone.so -- Mark Purcell Wed, 30 Nov 2005 16:28:51 +0000 zaptel (1:1.2.0-1) unstable; urgency=low * New upstream release * Remove Makefile_deps_kern.dpatch as it doesnt apply upstream -- Mark Purcell Thu, 17 Nov 2005 17:50:00 +0000 zaptel (1:1.0.9.2-1) unstable; urgency=low * New Upstream Release -- Mark Purcell Tue, 8 Nov 2005 20:47:48 +0000 zaptel (1:1.0.9.1-4) unstable; urgency=low (NOT YET RELEASED - needs still some tweaking with florz' patches) * debian/patches/ztcfg_init.dpatch: Make ztcfg not kill the channels when executed twice. * debian/patches/zaphfc_0.2.0-RC8n_florz-8.dpatch: Add florz' improved zaphfc implementation as zaphfc-florz. This should reduce system IO load among other improvements. Taken from http://zaphfc.florz.dyndns.org/ -- Kilian Krause Sat, 27 Aug 2005 21:32:50 +0200 zaptel (1:1.0.9.1-3) unstable; urgency=low * debian/control: fixed overrides disparity with zaptel-source belonging to devel rather than comm. -- Kilian Krause Sat, 27 Aug 2005 14:35:48 +0200 zaptel (1:1.0.9.1-2) unstable; urgency=low * Closes: #302836: zaptel-source: zaphfc module missing after compiling modules. * Closes: #323753: zaptel-source: cannot compile zaphfc in unstable with gcc-4.0.1. -- Santiago Ruano Rincon Fri, 19 Aug 2005 00:40:56 -0500 zaptel (1:1.0.9.1-1) unstable; urgency=low * New upstream release * Update debian/watch * Please package version 1.0.9.1 (Closes: #320600) * FXO hardware stops working after 25 days (Closes: #321239) -- Mark Purcell Mon, 8 Aug 2005 18:34:10 +0100 zaptel (1:1.0.9-5) unstable; urgency=low * Import bristuff-0.2.0-RC8l.dpatch -- Santiago Ruano Rincon Sat, 30 Jul 2005 11:26:42 -0500 zaptel (1:1.0.9-4) unstable; urgency=low Santiago Ruano Rincon: * Man pages are builded from sgml's using docbook-utils Deleted the *.8 files * Closes: #317297: Applied a patch to improve the ztdummy accuracy on kernel 2.6 Mark Purcell: * Reinstate debian/zaptel.install - Closes: #318575: this package does not install ztcfg, ztmonitor, ztspeed, zttest, zttool. -- Mark Purcell Sun, 17 Jul 2005 07:11:27 +1000 zaptel (1:1.0.9-3) unstable; urgency=low * Import bristuff-0.2.0-RC8j.dpatch * Closes: #315251: zaptel should be in group comm * Note that the cloned report is still active against ftp.debian.org * Closes: #316800: zaptel package 1.0.9 ships headers -- Mark Purcell Thu, 14 Jul 2005 12:19:10 +0100 zaptel (1:1.0.9-2) unstable; urgency=low * Import bristuff-0.2.0-RC8h.dpatch * Enable rtia.dpatch -- Mark Purcell Mon, 4 Jul 2005 02:35:37 +0100 zaptel (1:1.0.9-1) unstable; urgency=low * New upstream release * Disable bristuff to allow 1.0.9 upload * Disable rtia to allow 1.0.9 upload -- Mark Purcell Sun, 3 Jul 2005 15:51:32 +0100 zaptel (1:1.0.7-5) unstable; urgency=low * ACK NMUs. Thanks for helping with this. (Closes: #305731, #310150) * Actually doesn't fail if dpatch is not installed when building modules. * zaptel-modules.postinst: New. Run depmod -a on modules install * zaptel: should build-dep on debhelper (>= 4.0.4). (Closes: #310788) * zaptel: should build-dep on dpatch >= 2.0.9 (Closes: #314549) * zaptel: bashism in postinst (Closes: #314552) * zaptel-source: compilation error in zaphfc.c (Closes: #305193) * zaptel-source Build-Depends on dpatch, should Depend on it though. (Closes: #309258) * zaptel-source: Fails to enable RTAI support (Closes: #304648) -- Kilian Krause Sun, 19 Jun 2005 15:38:25 +0200 zaptel (1:1.0.7-4.1) unstable; urgency=high * Non-maintainer upload. * High-urgency upload for sarge-targetted RC bugfix * Make sure directories are created mode 755 instead of mode 644, as this otherwise causes problems for building (apparently on xfs filesystems). Closes: #310150. * Tweak debian/patches/Makefile.dpatch fix from the previous NMU so that it isn't unnecessarily fragile: -fsigned-char is *always* either a no-op or required, so lose the architecture checking and enable it unconditionally. Closes: #305731. -- Steve Langasek Sun, 22 May 2005 02:48:44 -0700 zaptel (1:1.0.7-4) unstable; urgency=high * NMU as VOIP team taking so long. Fix compiler flags so that ztcfg works. (Closes: #305731) -- Matthew Grant Fri, 22 Apr 2005 07:35:28 +1200 zaptel (1:1.0.7-3) unstable; urgency=medium * Closes: #302903: libtonezone1 package is empty * Closes: #302833: binary files missing, e.g. /sbin/ztcfg * Move debian/*.files -> debian/*.install * Closes: #302847: zaptel command ztcfg freezes on Debian PowerPC causing boot failure. -- Mark Purcell Sun, 3 Apr 2005 19:44:25 +0100 zaptel (1:1.0.7-2) unstable; urgency=medium * Debian VoIP Team upload. * Jose Carlos: + Working support for module-assistant included in zaptel-source. Thanks to Eduard Bloch for his help (Closes: #301665) + debian/control.modules.in: - make generated modules package depend on zaptel binary package. - updated description to refer to module-assistant. + debian/control: - build-depend on bzip2. - zaptel-source depends on module-assistant tool and bzip2. - updated and improved descriptions. + debian/rules: - remaked with the new m-a stuff. - don't need dpatch installed for building the modules (Closes: #301666) + debian/postinst: doesn't output garbage (Closes: #296958) + debian/postrm: don't remove creeated devices. Only box admin can do that, per Policy 10.6 + Removed zaphfc and qozap examples from zaptel-source. Only ship them in zaptel binary package. + README.Debian: file added. Talk about how compile modules and use them with udev (Closes: #163857) + Don't install zaptel.h file in zaptel-modules packages. (Closes: #297306) * Kilian Krause: + Increased urgency for fixing RC-bug and this is the last deb to allow the whole Debian VoIP suit proceed to testing. -- Jose Carlos Garcia Sogo Sat, 2 Apr 2005 01:14:23 +0200 zaptel (1:1.0.7-1) unstable; urgency=low * New upstream version. -- Kilian Krause Sat, 19 Mar 2005 23:28:07 +0100 zaptel (1:1.0.6-1) unstable; urgency=low * New upstream version. (zaptel 1.0.6, bristuff RC7k) * added zaphfc and qozap modules. -- Kilian Krause Sat, 5 Mar 2005 20:05:35 +0100 zaptel (1:1.0.4-3) unstable; urgency=low * Debian VoIP team upload. * debian/rules, debian/zaptel-source.files: fixed zaptel.h includes problem * debian/patches/Makefile.dpatch: imported from old package (now dpatch instead directly in the diff). Fixed building on hosts with differring userland and kernel arch. Now also including symlink for SONAME. * debian/patches/bristuff.dpatch: imported bristuff patch to include zaphfc. (Closes: #294183) * debian/zaptel.postinst: Fixed permissions issue problem. * debian/zaptel.modprobe.d: Added zaphfc RC7f. -- Kilian Krause Thu, 24 Feb 2005 01:42:36 +0100 zaptel (1:1.0.4-2) experimental; urgency=low * Better "use" of uname -r in Makefile for zaptel-source -- Santiago Ruano Rincon Mon, 21 Feb 2005 00:27:14 -0500 zaptel (1:1.0.4-1) experimental; urgency=low * New upstream release (zaptel-1.0.4) * Included zttest and ztspeed binaries * Added Depends on debhelper and Recomends zaptel for zaptel-source * Added /etc/modprobe.d/zaptel and corrected the path for the binaries, /sbin instead of /usr/sbin -- Santiago Ruano Rincon Wed, 26 Jan 2005 23:05:20 -0500 zaptel (1:1.0.2-2) unstable; urgency=low * libtonezone out of zaptel-source * /dev/zap/ are now created by zaptel.postinst and deleted by zaptel.postrm. Now, the zap devices match with the upstream version (Closes: #274384). * Added lintian overrides for mknod-in-maintainer-script warnings * docbook-to-man out of the Build-Depends -- Santiago Ruano Rincon Wed, 24 Nov 2004 22:05:52 -0500 zaptel (1:1.0.2-1) unstable; urgency=low * New upstream release (zaptel-1.0.2) -- Santiago Ruano Rincon Sat, 30 Oct 2004 00:51:54 -0500 zaptel (1:1.0.0-2) unstable; urgency=low * New maintainer (Closes: #251938). * Man pages created for ztcfg, ztmonitor and zttool (Closes: #274632, #274633, #274634). * Mark Purcell made the package for version 1.0 (Closes: #273255, #251929). * zaptel-modules can be build from zaptel-source with make-kpkg (Closes: #274085). * Now it compiles for 2.6 Kernels (Closes: #251930). -- Santiago Ruano Rincon Sun, 26 Sep 2004 02:05:44 -0500 zaptel (1:1.0.0-1) unstable; urgency=low * NMU (See Bug#251938) * New upstream release -- Mark Purcell Fri, 24 Sep 2004 22:46:55 +1000 zaptel (1:0.8.1+1.0-RC2-1) unstable; urgency=low * New upstream release -- Mark Purcell Thu, 9 Sep 2004 19:17:28 +1000 zaptel (1:0.8.1+1.0-RC1-1) unstable; urgency=low * New upstream release * Add a debian/watch file -- Mark Purcell Wed, 21 Jul 2004 17:51:22 +1000 zaptel (1:0.8.1-1) unstable; urgency=low * New upstream release -- Matt Zimmerman Wed, 11 Feb 2004 15:29:20 -0800 zaptel (1:0.8.0-2) unstable; urgency=low * Create usr/include ahead of time so that tonezone.h is installed correctly (Closes: #227795) -- Matt Zimmerman Wed, 14 Jan 2004 17:24:26 -0800 zaptel (1:0.8.0-1) unstable; urgency=low * New upstream release -- Matt Zimmerman Tue, 13 Jan 2004 14:44:56 -0800 zaptel (1:0.6.0-2) unstable; urgency=low * Rebuild with new libnewt -- Matt Zimmerman Mon, 30 Jun 2003 22:51:18 -0400 zaptel (1:0.6.0-1) unstable; urgency=low * New upstream release, needed for new asterisk (Closes: #189661) -- Matt Zimmerman Sat, 19 Apr 2003 23:56:59 -0400 zaptel (1:0.4.0-2) unstable; urgency=low * Break out into {build,install,binary}-indep targets (Closes: #184528) * libtonezone-dev Section: libdevel * Escape $ properly in instructions in postinst -- Matt Zimmerman Wed, 12 Mar 2003 19:16:10 -0500 zaptel (1:0.4.0-1) unstable; urgency=low * New upstream release -- Matt Zimmerman Sun, 16 Feb 2003 15:12:02 -0500 zaptel (0.cvs.20021029-1) unstable; urgency=low * New upstream CVS * Use MARK2 echo canceller -- Matt Zimmerman Tue, 29 Oct 2002 10:37:53 -0500 zaptel (0.cvs.20020729-1) unstable; urgency=low * New upstream CVS * Include ztmonitor binary -- Matt Zimmerman Mon, 29 Jul 2002 12:36:58 -0400 zaptel (0.cvs.20020708-1) unstable; urgency=low * New upstream CVS -- Matt Zimmerman Mon, 8 Jul 2002 15:32:20 -0400 zaptel (0.cvs.20020624-2) unstable; urgency=low * Include Makefile in the -source package (oops, Closes: #152014) -- Matt Zimmerman Fri, 5 Jul 2002 11:00:08 -0400 zaptel (0.cvs.20020624-1) unstable; urgency=low * Initial Release (Closes: #150874) -- Matt Zimmerman Mon, 17 Jun 2002 10:31:21 -0400 debian/dahdi-linux.doc-base.readme0000664000000000000000000000052711652357765014256 0ustar Document: dahdi-linux Title: DAHDI Telephony Interface Driver Author: Tzafrir Cohen Abstract: Basic documentation of the DAHDI telephony interface Section: Network/Communication Format: HTML Index: /usr/share/doc/dahdi-linux/README.html Files: /usr/share/doc/dahdi-linux/README.html Format: text Files: /usr/share/doc/dahdi-linux/README.gz debian/README.Debian0000664000000000000000000001067111652357765011257 0ustar Building kernel modules ----------------------- First, install dahdi-source package if you have not yet done so. You can build and install the modules package (as root) with the following command: # module-assistant a-i dahdi It may be handy (for e.g., testing purposes) to build the module packages for all of the kernel-header packages installed on your system. Something in the lines of: m-a -u . -t -i -f -k "`echo usr/src/kernel-headers-*`" build dahdi You can also use the environment variable TARBALL to build the modules with a custom dahdi.tar.bz2 tarball with some local modifications. Device Files ------------ DAHDI uses device files with major number 196 under /dev/dahdi/. In Debian all of those files are 0660 and owned by the group dialout. They should normally created by udev. If you can't generate device files with udev, use the script make_static_nodes in the examples directory of dahdi-linux. Please also let us know that generating static file is actually needed. Removed VPMADT032 Support ------------------------- VPMADT032 is a hardware echo canceller module. It is an optional addon in various Digium cards. Currently in those serviced by wcte12xp and wctdm24xxp. As of DAHDI 2.2 using it requires a binary-only object file to be linked with DAHDI at build time. This object file is downloaded at build time from downloads.digium.com . This is naturally not something we can support. And thus we disabled building it. Unlike the Digium firmwares, this is not something that can be reverted at run-time. Bristuff -------- No longer needed and used. Note that all the BRI drivers now use 'hardhdlc' rather than 'dchan' in system.conf. dahdi_genconf should generate a proper configuration for that. zaphfc included in this version is a newly-maintained version based on vzaphfc. All of those modules should work with either bristuffed Asterisk (if it is Asterisk >= 1.4.22) or with Asterisk 1.6.x (libpri >= 1.4.4). Echo Canceller -------------- Open Source Line Echo Canceller (OSLEC) is an alternative echo canceller developed outside the main Zaptel tree. It is currently labelled "Beta". For more information see http://www.rowetel.com/ucasterisk/oslec.html . It generally works much better than the default upstream echo canceller (MG2). On the downside, it has a higher CPU consumption. The version of OSLEC included is currently one from the staging directory. This is done temporarily until that driver will find its way into the mainline tree and to help test it. According to early tests by OSLEC users, the default Asterisk echo canceller size of 128 taps (16ms) should be good enough for most installations, and 256 taps (32 ms) should cover just about any case. For phones connected to FXS ports you can use substatially lower values in order to reduce CPU consumption. e.g: 8ms or even 4 ms (64 taps or 32 taps). Setting the number of taps for a channel in Asterisk's chan_zap is done using the following in Asterisk's zapata.conf: echocancell=NNN where NNN is the number of taps. See Asterisk sample zapata.conf . Build-time Tests ---------------- One sanity check to run when making changes in the package is to make (besides the usual lintian/linda) is to make sure that the modules packages still builds. For this to work you should first have a version of dahdi-source installed (any version. modules-assitant needs a few files from it in place). This allows you to use the script debian/modulestest as a post-build hook: svn-buildpackage --svn-postbuild='debian/modulestest -r' --svn-ignore-new -uc -us This will rebuild the zaptel-modules for your current kernel, or spend much more time with: svn-buildpackage --svn-postbuild='debian/modulestest -a' --svn-ignore-new -uc -us to build dahdi-modules for all of your currently-installed zaptel-modules packages. The log from the build will go to the file zaptel-modules-build-.log in the build-area directory. e.g: zaptel-modules-build-1.4.7~dfsg-1.log . The script should also run ls for the module packages built. You should chack that all packages were indeed built and that their size looks sane (no driver was left unbuilt). DAHDI-dummy ----------- The core DAHDI module (dahdi) will now provide timing if there's no other module to provide timing. This means there's no more need for a separate dahdi_dummy module. For the sake of backward compatibility, the module dahdi itself also has an alias 'dahdi_dummy', which means that 'modprobe dahdi_dummy' will still work. debian/make_static_nodes0000775000000000000000000000125511652357765012616 0ustar #!/bin/sh # Generate static device file nodes for dahdi # Most peopel won't need this, as the device file nodes are normally # generated by udev. Also note that if you do use udev, /dev is a # ramdisk, and thus any changes you do to it will not be preserved # on next boot. mknod_safe() { if [ -c $1 ]; then return; fi mknod "$@" || true } mkdir -p /dev/dahdi mknod_safe /dev/dahdi/ctl c 196 0 mknod_safe /dev/dahdi/transcode c 196 250 mknod_safe /dev/dahdi/timer c 196 253 mknod_safe /dev/dahdi/channel c 196 254 mknod_safe /dev/dahdi/pseudo c 196 255 for N in `seq 249`; do mknod_safe /dev/dahdi/$N c 196 $N done chown -R 0:dialout /dev/dahdi/ chmod 0660 /dev/dahdi/* debian/backports/0000775000000000000000000000000011652361351011163 5ustar debian/backports/etch0000775000000000000000000000021211652357765012045 0ustar #!/bin/sh # Etch has an older debhelper: sed -i -e '/^Build-Depends:/s/\(debhelper\) ([^)]*),/\1,/' debian/control echo 5 >debian/compat debian/backports/lenny0000775000000000000000000000021611652357765012253 0ustar #!/bin/sh # Lenny backporting script # udev rules go to /etc/udev in Lenny sed -i -e '/xpp.rules/s/lib/etc/' debian/dahdi-linux.install debian/dkms.conf.in0000664000000000000000000001343611652360160011411 0ustar PACKAGE_VERSION="#CVERSION#" # Items below here should not have to change with each driver version PACKAGE_NAME="dahdi" MAKE[0]="(make modules KERNVER=$kernelver MODULES_EXTRA='dahdi_dummy wcopenpci dahdi_echocan_oslec' SUBDIRS_EXTRA='../staging/echo zaphfc/' ;make;make firmware-loaders;echo : > drivers/dahdi/vpmadt032_loader/.vpmadt032_x86_32.o.cmd;echo : > drivers/dahdi/vpmadt032_loader/.vpmadt032_x86_64.o.cmd;make)" #Do not make clean, because Sangoma drivers need Module.symvers CLEAN="/bin/true" #CLEAN="make KERNVER=$kernelver clean" #MAKE[0]="make modules KERNVER=$kernelver DOWNLOAD=echo" #CLEAN="make KERNVER=$kernelver clean DOWNLOAD=echo" BUILT_MODULE_NAME[0]="dahdi_dummy" BUILT_MODULE_LOCATION[0]="drivers/dahdi/" DEST_MODULE_LOCATION[0]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[1]="dahdi_dynamic_eth" BUILT_MODULE_LOCATION[1]="drivers/dahdi/" DEST_MODULE_LOCATION[1]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[2]="dahdi_dynamic" BUILT_MODULE_LOCATION[2]="drivers/dahdi/" DEST_MODULE_LOCATION[2]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[3]="dahdi_dynamic_loc" BUILT_MODULE_LOCATION[3]="drivers/dahdi/" DEST_MODULE_LOCATION[3]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[4]="dahdi_echocan_jpah" BUILT_MODULE_LOCATION[4]="drivers/dahdi/" DEST_MODULE_LOCATION[4]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[5]="dahdi_echocan_kb1" BUILT_MODULE_LOCATION[5]="drivers/dahdi/" DEST_MODULE_LOCATION[5]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[6]="dahdi_echocan_mg2" BUILT_MODULE_LOCATION[6]="drivers/dahdi/" DEST_MODULE_LOCATION[6]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[7]="dahdi_echocan_sec2" BUILT_MODULE_LOCATION[7]="drivers/dahdi/" DEST_MODULE_LOCATION[7]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[8]="dahdi_echocan_sec" BUILT_MODULE_LOCATION[8]="drivers/dahdi/" DEST_MODULE_LOCATION[8]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[9]="dahdi" BUILT_MODULE_LOCATION[9]="drivers/dahdi/" DEST_MODULE_LOCATION[9]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[10]="dahdi_transcode" BUILT_MODULE_LOCATION[10]="drivers/dahdi/" DEST_MODULE_LOCATION[10]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[11]="pciradio" BUILT_MODULE_LOCATION[11]="drivers/dahdi/" DEST_MODULE_LOCATION[11]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[12]="tor2" BUILT_MODULE_LOCATION[12]="drivers/dahdi/" DEST_MODULE_LOCATION[12]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[13]="wcb4xxp" BUILT_MODULE_LOCATION[13]="drivers/dahdi/wcb4xxp/" DEST_MODULE_LOCATION[13]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[14]="wcfxo" BUILT_MODULE_LOCATION[14]="drivers/dahdi/" DEST_MODULE_LOCATION[14]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[15]="wct1xxp" BUILT_MODULE_LOCATION[15]="drivers/dahdi/" DEST_MODULE_LOCATION[15]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[16]="wct4xxp" BUILT_MODULE_LOCATION[16]="drivers/dahdi/wct4xxp/" DEST_MODULE_LOCATION[16]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[17]="wctc4xxp" BUILT_MODULE_LOCATION[17]="drivers/dahdi/wctc4xxp/" DEST_MODULE_LOCATION[17]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[18]="wctdm" BUILT_MODULE_LOCATION[18]="drivers/dahdi/" DEST_MODULE_LOCATION[18]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[19]="wctdm24xxp" BUILT_MODULE_LOCATION[19]="drivers/dahdi/wctdm24xxp/" DEST_MODULE_LOCATION[19]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[20]="wcte11xp" BUILT_MODULE_LOCATION[20]="drivers/dahdi/" DEST_MODULE_LOCATION[20]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[21]="wcte12xp" BUILT_MODULE_LOCATION[21]="drivers/dahdi/wcte12xp/" DEST_MODULE_LOCATION[21]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[22]="xpd_fxo" BUILT_MODULE_LOCATION[22]="drivers/dahdi/xpp/" DEST_MODULE_LOCATION[22]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[23]="xpd_fxs" BUILT_MODULE_LOCATION[23]="drivers/dahdi/xpp/" DEST_MODULE_LOCATION[23]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[24]="xpd_pri" BUILT_MODULE_LOCATION[24]="drivers/dahdi/xpp/" DEST_MODULE_LOCATION[24]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[25]="xpp" BUILT_MODULE_LOCATION[25]="drivers/dahdi/xpp/" DEST_MODULE_LOCATION[25]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[26]="xpp_usb" BUILT_MODULE_LOCATION[26]="drivers/dahdi/xpp/" DEST_MODULE_LOCATION[26]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[27]="xpd_bri" BUILT_MODULE_LOCATION[27]="drivers/dahdi/xpp/" DEST_MODULE_LOCATION[27]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[28]="dahdi_voicebus" BUILT_MODULE_LOCATION[28]="drivers/dahdi/voicebus/" DEST_MODULE_LOCATION[28]="/kernel/drivers/telephony/dahdi/voicebus" #Added by Debian / Ubuntu BUILT_MODULE_NAME[29]="opvxa1200" BUILT_MODULE_LOCATION[29]="drivers/dahdi/opvxa1200" DEST_MODULE_LOCATION[29]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[30]="wcopenpci" BUILT_MODULE_LOCATION[30]="drivers/dahdi/" DEST_MODULE_LOCATION[30]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[31]="dahdi_echocan_oslec" BUILT_MODULE_LOCATION[31]="drivers/dahdi/" DEST_MODULE_LOCATION[31]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[32]="echo" BUILT_MODULE_LOCATION[32]="drivers/staging/echo/" DEST_MODULE_LOCATION[32]="/kernel/drivers/telephony/staging/echo" BUILT_MODULE_NAME[33]="zaphfc" BUILT_MODULE_LOCATION[33]="drivers/dahdi/zaphfc" DEST_MODULE_LOCATION[33]="/kernel/drivers/telephony/dahdi/zaphfc" BUILT_MODULE_NAME[34]="dahdi_vpmadt032_loader" BUILT_MODULE_LOCATION[34]="drivers/dahdi/" DEST_MODULE_LOCATION[34]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[35]="ap400" BUILT_MODULE_LOCATION[35]="drivers/dahdi/ap400" DEST_MODULE_LOCATION[35]="/kernel/drivers/telephony/dahdi" BUILT_MODULE_NAME[36]="opvxd115" BUILT_MODULE_LOCATION[36]="drivers/dahdi/opvxd115" DEST_MODULE_LOCATION[36]="/kernel/drivers/telephony/dahdi" AUTOINSTALL=yes debian/dahdi-linux.examples0000664000000000000000000000006411652357765013157 0ustar debian/make_static_nodes drivers/dahdi/xpp/xpp.conf debian/NEWS.Debian0000664000000000000000000000106211652357765011070 0ustar zaptel (1:1.4.10~dfsg-1) unstable; urgency=low Certain versions of Zaptel introduce incompatibilities between older kernel modules and newer userspace programs. If programs such as ztcfg or asterisk fail talking with Zaptel kernel with error 25 (ENOTTY: "Inappropriate ioctl for this device") then you should upgrade the Zaptel modules by e.g: 'm-a a-i zaptel' and reload drivers. . The version of the currently running Zaptel is in /sys/module/zaptel/version . -- Tzafrir Cohen Thu, 20 Mar 2008 16:31:25 +0200 debian/docs0000664000000000000000000000007711652357765010070 0ustar README README.html UPGRADE.txt drivers/dahdi/xpp/Changelog_xpp debian/dahdi-source.install0000664000000000000000000000007011652357765013145 0ustar debian/lintian/dahdi-source usr/share/lintian/overrides debian/modulestest0000775000000000000000000000526411652357765011516 0ustar #!/bin/sh # debian/modulestest: a postbuild script to build dahdi modules # example usage: # # svn-buildpackage --svn-postbuild='debian/modulestest -r' --svn-ignore-new # # debuild --post-dpkg-buildpackage-hook="$PWD/debian/modulestest -d %v -r" # # At least one of the following two must be set to a sensible value: # If both are empty, the script does nothing useful) # # kernel versions: Comma-separated. Use those if you have their # kernel-headers/linux-headers packages installed # # PACKAGE is set by svn-buildpackage to "dahdi-linux" . Override that: PACKAGE=dahdi # Full pathes to trees: # Use this to provide a full path to a custom kernel tree: #KERNEL_SOURCES=$HOME/Proj/Debs/Kernel/SwSusp/linux-2.6.15-rc5 KERNEL_HEADERS= KERNEL_SOURCES= DEBUILD_VERSION= # run module-asustant with environment params that will generate # .changes files even without signing MODASS="env SIGNCHANGES=1 DEBSIGNCOMMAND=not_an_executable m-a" me=`basename $0` # workaround for silly bash parsing issue in our build scripts: if [ "$#" -lt 1 ]; then set -- $MODULESTEST_ARGS fi while getopts ah:rs:tv: arg do case "$arg" in a) # All of the kernel-headers packages installed: KERNEL_HEADERS=`COLUMNS=160 dpkg -l 'kernel-headers-2.[46].*-*-*' | awk '/^.i/{print $2}' | sed -e 's/^kernel-headers-//'| xargs| tr ' ' ,` ;; h) KERNEL_HEADERS=$OPTARG;; s) KERNEL_SOURCES=$OPTARG;; r) KERNEL_HEADERS=`uname -r`;; t) # TODO: make this test per-distro or something KERNEL_HEADERS=2.4.27-2-386,2.6.8-2-686-smp ;; v) DEBUILD_VERSION=$OPTARG;; esac done shift $(( $OPTIND-1 )) echo "Building for: Headers: $KERNEL_HEADERS, Sources: $KERNEL_SOURCES" if [ "$KERNEL_HEADERS" != '' ]; then hdrs_sw="-l $KERNEL_HEADERS"; fi if [ "$KERNEL_SOURCES" != '' ]; then srcs_sw="-k $KERNEL_SOURCES"; fi # must be absolute for m-a ta accept TARBALL: # Also note that $PWD is ugly and is about to be deleted. We need # $PWD/.. if [ "$DEBUILD_VERSION" != '' ]; then TOP_DIR="$PWD" TAG_VERSION=`echo $DEBUILD_VERSION | sed -e 's/.*://'` else TOP_DIR=`dirname $PWD` fi MODS_DIR=$TOP_DIR/modules SRC_DIR=$MODS_DIR/usr/src TAR_BALL=$SRC_DIR/$PACKAGE.tar.bz2 DEB_VERSION=${TAG_VERSION#*:} DEB=$TOP_DIR/$PACKAGE-source_${DEB_VERSION}_all.deb LOG_FILE=$TOP_DIR/$PACKAGE-modules-build-$DEB_VERSION.log rm -f $LOG_FILE dpkg -x $DEB $MODS_DIR if [ "$hdrs_sw" != '' ]; then MOD_SRCDIR=$SRC_DIR TARBALL=$TAR_BALL $MODASS -u $TOP_DIR -t -i -f $hdrs_sw build $PACKAGE >>$LOG_FILE fi if [ "$srcs_sw" != '' ]; then MOD_SRCDIR=$SRC_DIR TARBALL=$TAR_BALL $MODASS -u $TOP_DIR -t -i -f $srcs_sw build $PACKAGE >>$LOG_FILE fi ls -l $TOP_DIR/$PACKAGE-modules-*_$DEB_VERSION+*.deb echo "$me: Log file: $LOG_FILE" pwd >&2 debian/TODO.Debian0000664000000000000000000000041411652357765011061 0ustar * Replace modules in drivers/dahdi with patches: - vzaphfc: not actually used. Leave it for current release (2.2.0.2-1) until we're sur zaphfc actually works. Then remove it (this package is sadyl its upstream). - Convert the other two drivers to patches. debian/dahdi-linux.udev0000664000000000000000000000006311652360160012262 0ustar SUBSYSTEM=="dahdi", GROUP="dialout", MODE="0660" debian/dahdi-linux.lintian-overrides0000664000000000000000000000016311652360160014756 0ustar #/usr/share/dahdi/init_card_5_30 is a dummy script: unusual-interpreter usr/share/dahdi/init_card_5_30 #!/bin/true debian/dahdi-source.dirs0000664000000000000000000000001011652357765012432 0ustar usr/src debian/control.modules.in0000664000000000000000000000176311652357765012677 0ustar Source: dahdi-linux Section: comm Priority: extra Maintainer: Debian VoIP Team Uploaders: Tzafrir Cohen , Mark Purcell Build-Depends: debhelper (>> 7), bzip2 Standards-Version: 3.8.4 Homepage: http://www.asterisk.org/ Vcs-Svn: svn://svn.debian.org/pkg-voip/dahdi-linux/trunk/ Vcs-Browser: http://svn.debian.org/wsvn/pkg-voip/dahdi-linux/?op=log Package: dahdi-modules-_KVERS_ Architecture: any Provides: dahdi-modules Depends: linux-image-_KVERS_ Replaces: zaptel-modules-_KVERS_ Conflicts: zaptel-modules-_KVERS_ Description: DAHDI modules for Linux (kernel _KVERS_) DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. the Asterisk PBX software. The dahdi-* packages provide the kernel DAHDI kernel modules and their required setup environment. . This package contains the set of loadable kernel modules for the DAHDI telephony API. This package contains the compiled kernel modules for _KVERS_ debian/dahdi-dkms.prerm0000664000000000000000000000051311652357765012264 0ustar #!/bin/sh #DEBHELPER# NAME=dahdi PACKAGE_NAME=$NAME-dkms CVERSION=`dpkg-query -W -f='${Version}' $PACKAGE_NAME | cut -d\: -f2` case "$1" in remove|upgrade|purge) echo "Removing all DKMS Modules" dkms remove -m $NAME -v $CVERSION --all > /dev/null echo "Done." ;; purge) delgroup --quiet $NAME ;; esac debian/patches/0000775000000000000000000000000012177720120010616 5ustar debian/patches/ndo_set_multicast_list.diff0000664000000000000000000000264411715166140016234 0ustar From: Shaun Ruffell Subject: wctc4xxp: Replace 'ndo_set_multicast_list' with 'set_rx_mode' The ndo_set_multicast_list callback was removed in b81693d9, which was first released in Linux Kernel 3.2-rc1 Origin: upstream, http://svnview.digium.com/svn/dahdi?view=revision&revision=10362 --- a/drivers/dahdi/wctc4xxp/base.c +++ b/drivers/dahdi/wctc4xxp/base.c @@ -488,6 +488,7 @@ wctc4xxp_skb_to_cmd(struct wcdte *wc, co return cmd; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) static void wctc4xxp_net_set_multi(struct net_device *netdev) { @@ -495,6 +496,15 @@ wctc4xxp_net_set_multi(struct net_device DTE_DEBUG(DTE_DEBUG_GENERAL, "%s promiscuity:%d\n", __func__, netdev->promiscuity); } +#else +static void +wctc4xxp_set_rx_mode(struct net_device *netdev) +{ + struct wcdte *wc = wcdte_from_netdev(netdev); + DTE_DEBUG(DTE_DEBUG_GENERAL, "%s promiscuity:%d\n", + __func__, netdev->promiscuity); +} +#endif static int wctc4xxp_net_up(struct net_device *netdev) @@ -658,7 +668,11 @@ wctc4xxp_net_ioctl(struct net_device *ne #ifdef HAVE_NET_DEVICE_OPS static const struct net_device_ops wctc4xxp_netdev_ops = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) .ndo_set_multicast_list = &wctc4xxp_net_set_multi, +#else + .ndo_set_rx_mode = &wctc4xxp_set_rx_mode, +#endif .ndo_open = &wctc4xxp_net_up, .ndo_stop = &wctc4xxp_net_down, .ndo_start_xmit = &wctc4xxp_net_hard_start_xmit, debian/patches/missing_modules.h.diff0000664000000000000000000000303011715166140015076 0ustar From: Shaun Ruffell Subject: #include in dahdi/kernel.h and GpakCust.h Commit de47725, first released in 3.2-rc1 removed module.h from some kernel headers. Include it explicitly now. . Resolves compilation errors like: * error: implicit declaration of function 'try_module_get' * error: 'THIS_MODULE' undeclared (first use in this function) * error: implicit declaration of function 'module_put' Origin: upstream, http://svnview.digium.com/svn/dahdi?view=revision&revision=10363 Index: dahdi-linux-2.5.0.1+dfsg/include/dahdi/kernel.h =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/include/dahdi/kernel.h 2012-02-07 16:36:48.482751165 -0500 +++ dahdi-linux-2.5.0.1+dfsg/include/dahdi/kernel.h 2012-02-07 16:36:59.858746074 -0500 @@ -43,6 +43,7 @@ #endif #include #include +#include #include #ifdef CONFIG_DAHDI_NET Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/voicebus/GpakCust.h =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/voicebus/GpakCust.h 2011-06-02 16:03:20.000000000 -0400 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/voicebus/GpakCust.h 2012-02-07 16:37:28.830734088 -0500 @@ -36,6 +36,7 @@ #ifndef _GPAKCUST_H /* prevent multiple inclusion */ #define _GPAKCUST_H +#include #include #include #include ifdef CONFIG_DAHDI_NET debian/patches/notest0000664000000000000000000000051611652360160012060 0ustar Description: Remove a bodus "test" target Author: Tzafrir Cohen --- a/Makefile +++ b/Makefile @@ -206,9 +206,6 @@ dist-clean: clean firmware-download: @$(MAKE) -C drivers/dahdi/firmware all -test: - ./test-script $(DESTDIR)/lib/modules/$(KVERS) dahdi - docs: $(GENERATED_DOCS) README.html: README debian/patches/series0000664000000000000000000000033212177202351012032 0ustar oslec_auto # Mega-patch of extra drivers: dahdi_linux_extra no_firmware_download chanmute notest xpp_fix_2fxs6fxo zaphfc_d_channel_fix.patch missing_modules.h.diff ndo_set_multicast_list.diff kernel-v3-11-compat.patch debian/patches/xpp_fix_2fxs6fxo0000664000000000000000000001017511652360160013770 0ustar Subject: xpp: fxs: bugfix for 2fxs+6fxo cards From: Tzafrir Cohen Date: Sun, 25 Sep 2011 08:59:02 +0000 Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=10208 * Bug sympthoms: wrong FSK VMWI sent few seconds after offhook. That was caused because the driver kept polling the (physically unconnected) digital inputs. [note: a workaround for drivers without this patch is to zero the 'xpd_fxs.poll_digital_inputs' parameter.] * Also, the digital_inputs/digital_output masks were calculate using a different condition. * Now we determine number of channels, digital inputs and digital outputs in a single place and use this info later to calculate the correct masks. * We poll only if there are digital_inputs * We added a sanity check in process_digital_inputs, so we get a notice if it's called on an xpd without digital inputs (e.g: hypothetic firmware bug). Signed-off-by: Tzafrir Cohen Acked-by: Tzafrir Cohen --- drivers/dahdi/xpp/card_fxs.c | 39 +++++++++++++++++++++++++++++++-------- 1 files changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/dahdi/xpp/card_fxs.c b/drivers/dahdi/xpp/card_fxs.c index db80e42..d296164 100644 --- a/drivers/dahdi/xpp/card_fxs.c +++ b/drivers/dahdi/xpp/card_fxs.c @@ -386,6 +386,8 @@ static xpd_t *FXS_card_new(xbus_t *xbus, int unit, int subunit, const xproto_tab int regular_channels; struct FXS_priv_data *priv; int i; + int d_inputs = 0; + int d_outputs = 0; if(!to_phone) { XBUS_NOTICE(xbus, @@ -398,16 +400,30 @@ static xpd_t *FXS_card_new(xbus_t *xbus, int unit, int subunit, const xproto_tab else regular_channels = min(8, subunit_ports); channels = regular_channels; - if(unit == 0 && subtype != 4) + /* Calculate digital inputs/outputs */ + if(unit == 0 && subtype != 4) { channels += 6; /* 2 DIGITAL OUTPUTS, 4 DIGITAL INPUTS */ + d_inputs = LINES_DIGI_INP; + d_outputs = LINES_DIGI_OUT; + } xpd = xpd_alloc(xbus, unit, subunit, subtype, subunits, sizeof(struct FXS_priv_data), proto_table, channels); if(!xpd) return NULL; - if(unit == 0) { - XBUS_DBG(GENERAL, xbus, "First XPD detected. Initialize digital outputs/inputs\n"); - PHONEDEV(xpd).digital_outputs = BITMASK(LINES_DIGI_OUT) << regular_channels; - PHONEDEV(xpd).digital_inputs = BITMASK(LINES_DIGI_INP) << (regular_channels + LINES_DIGI_OUT); - } + /* Initialize digital inputs/outputs */ + if (d_inputs) { + XBUS_DBG(GENERAL, xbus, "Initialize %d digital inputs\n", + d_inputs); + PHONEDEV(xpd).digital_inputs = + BITMASK(d_inputs) << (regular_channels + d_outputs); + } else + XBUS_DBG(GENERAL, xbus, "No digital inputs\n"); + if (d_outputs) { + XBUS_DBG(GENERAL, xbus, "Initialize %d digital outputs\n", + d_outputs); + PHONEDEV(xpd).digital_outputs = + BITMASK(d_outputs) << regular_channels; + } else + XBUS_DBG(GENERAL, xbus, "No digital outputs\n"); PHONEDEV(xpd).direction = TO_PHONE; xpd->type_name = "FXS"; if(fxs_proc_create(xbus, xpd) < 0) @@ -1142,7 +1158,7 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) priv = xpd->priv; BUG_ON(!priv); #ifdef POLL_DIGITAL_INPUTS - if(poll_digital_inputs && xpd->xbus_idx == 0) { + if (poll_digital_inputs && PHONEDEV(xpd).digital_inputs) { if((xpd->timer_count % poll_digital_inputs) == 0) poll_inputs(xpd); } @@ -1254,6 +1270,13 @@ static void process_digital_inputs(xpd_t *xpd, const reg_cmd_t *info) bool offhook = (REG_FIELD(info, data_low) & 0x1) == 0; xpp_line_t lines = BIT(info->portnum); + /* Sanity check */ + if (!PHONEDEV(xpd).digital_inputs) { + XPD_NOTICE(xpd, + "%s called without digital inputs. Ignored\n", + __func__); + return; + } /* Map SLIC number into line number */ for(i = 0; i < ARRAY_SIZE(input_channels); i++) { int channo = input_channels[i]; @@ -1366,7 +1389,7 @@ static int FXS_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info) /* * Process digital inputs polling results */ - else if(xpd->xbus_idx == 0 && !indirect && regnum == REG_DIGITAL_IOCTRL) { + else if (!indirect && regnum == REG_DIGITAL_IOCTRL) { process_digital_inputs(xpd, info); } #endif -- 1.7.5.4 debian/patches/mmx_auto0000664000000000000000000000262411652357765012420 0ustar Description: Enable MMX support in the DAHDI EC for most i386 CPUs Author: Tzafrir Cohen Bug: http://bugs.digium.com/view.php?id=13500 Enable MMX support if safe on the target CPU type (on most i386 systems, not on x86_64). The OSLEC patch will build the module 'echo' with MMX support if DAHDI_USE_MMX from here is defined. Patch dahdi_mmx_auto.diff from http://bugs.digium.com/view.php?id=13500 This specific part of the fix has been rejected by upstream, as non of the echo cancellers included there uses MMX. See also http://bugs.debian.org/593438 before re-adding it. --- a/drivers/dahdi/Kbuild +++ b/drivers/dahdi/Kbuild @@ -134,3 +134,25 @@ $(obj)/makefw: $(src)/makefw.c $(HOSTCC) -o $@ $^ clean-files := radfw.h tor2fw.h + +# set CONFIG_DAHDI_MMX for a number of CPU types. +DAHDI_MMX_AUTO=yes +DAHDI_USE_MMX= +DAHDI_MMX_WHITELIST_x86_32 = M586MMX M686 MPENTIUMII MPENTIUMIII MPENTIUMM \ + MPENTIUM4 MVIAC3_2 MVIAC3_2 MK7 MK8 + +DAHDI_MMX_WHITELIST_x86_64 = + +# A list of configuration variables to test: CONFIG_M686 , etc. +DAHDI_MMX_CONFIG_VARS := $(DAHDI_MMX_WHITELIST_$(DAHDI_ARCH):%=CONFIG_%) + +# expand them: +DAHDI_MMX_CONFIG_VALS := $(strip $(foreach var,$(DAHDI_MMX_CONFIG_VARS),$(var)) ) +ifneq (,$(DAHDI_MMX_AUTO)) + ifneq (,$(DAHDI_MMX_CONFIG_VALS)) + DAHDI_USE_MMX=yes + CFLAGS_zaptel-base.o += -DCONFIG_DAHDI_MMX + endif +endif + +export DAHDI_USE_MMX debian/patches/oslec_auto0000664000000000000000000000116111652357765012717 0ustar Origin: https://issues.asterisk.org/view.php?id=15251 Author: biohumanoid diff --git a/drivers/dahdi/Kbuild b/drivers/dahdi/Kbuild index 1c2d1e3..60b997d 100644 --- a/drivers/dahdi/Kbuild +++ b/drivers/dahdi/Kbuild @@ -37,7 +37,11 @@ obj-m += $(DAHDI_MODULES_EXTRA) # A quick and dirty way to build OSLEC, if you happened to place it # yourself in the dahdi source tree. This is experimental. See README # regarding OSLEC. -#obj-m += ../staging/echo/ + +ifneq (,$(wildcard $(src)/../staging/echo/echo.c)) +obj-m += dahdi_echocan_oslec.o +obj-m += ../staging/echo/ +endif CFLAGS_MODULE += -I$(DAHDI_INCLUDE) -I$(src) debian/patches/oslec_kbuild0000664000000000000000000000046511652357765013227 0ustar Add a Kbuild file to oslec. It has a very similar Makefile but in it, the build is conditioned on a certain Kconfig variable. --- /dev/null +++ b/drivers/staging/echo/Kbuild @@ -0,0 +1,6 @@ +ifdef DAHDI_USE_MMX +EXTRA_CFLAGSi += USE_MMX +endif + +# An explicit 'obj-m' , unlike the Makefile +obj-m += echo.o debian/patches/dahdi_linux_extra0000664000000000000000000202067711652360160014253 0ustar Subject: dahdi-extra: out-of-tree DAHDI drivers Origin: http://gitorious.org/dahdi-extra/dahdi-linux-extra Forwarded: No Last-Update: 2011-08-08 This patch includes a number of out-of-tree DAHDI drivers from the dahdi-extra repository. They are all out-of-tree and are highly likely not to be included in DAHDI-linux in the forseeable future. Git-Commit: 1a5eb3aa8925626b7a7c333a5248c7dfe8ffe97b Dahdi: tags/2.5.0 --- diff --git a/drivers/dahdi/Kbuild b/drivers/dahdi/Kbuild index ec881dc..dcd6e3c 100644 --- a/drivers/dahdi/Kbuild +++ b/drivers/dahdi/Kbuild @@ -29,6 +29,14 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_STEVE2) += dahdi_echocan_sec2.o obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_KB1) += dahdi_echocan_kb1.o obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_MG2) += dahdi_echocan_mg2.o +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_AP400) += ap400/ +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115) += opvxd115/ +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200) += opvxa1200/ +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCOPENPCI) += wcopenpci.o +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ZAPHFC) += zaphfc/ +obj-$(DAHDI_BUILD_ALL)$(CONFIG_ECHO) += ../staging/echo/ +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_OSLEC) += dahdi_echocan_oslec.o + obj-m += $(DAHDI_MODULES_EXTRA) # Only enable this if you think you know what you're doing. This is not diff --git a/drivers/dahdi/Kconfig b/drivers/dahdi/Kconfig index 6952c6a..3ad3e6c 100644 --- a/drivers/dahdi/Kconfig +++ b/drivers/dahdi/Kconfig @@ -292,3 +292,76 @@ config DAHDI_WCTE11XP If unsure, say Y. source "drivers/dahdi/xpp/Kconfig" + + +config DAHDI_OPVXD115 + tristate "OpenVox Single-T1/E1/J1 Support" + depends on DAHDI && PCI + default DAHDI + ---help--- + This driver provides support for the following OpenVox + Wildcard products: + + * D115P/DE115P/D130P/DE130P (PCI) + * D115E/DE115E/D130E/DE130E (PCI-E) + + To compile this driver as a module, choose M here: the + module will be called opvxd115. + + If unsure, say Y. + +config DAHDI_OPVXA1200 + tristate "OpenVox 8/12 ports analog card Support" + depends on DAHDI && PCI + default DAHDI + ---help--- + This driver provides support for the following OpenVox + Wildcard products: + + * A1200P (PCI) + * A1200E (PCI-E) + * A800P (PCI) + * A800E (PCI-E) + + To compile this driver as a module, choose M here: the + module will be called opvxa1200. + + If unsure, say Y. + +config DAHDI_WCOPENPCI + tristate "Voicetronix OpenPCI Interface DAHDI driver" + depends on DAHDI && PCI + default DAHDI + ---help--- + This driver provides support for the Voicetronix OpenPCI Interface. + + To compile this driver as a module, choose M here: the + module will be called wcopenpci. + + If unsure, say Y. + +config DAHDI_ZAPHFC + tristate "HFC-S DAHDI Driver" + depends on DAHDI && PCI + default DAHDI + ---help--- + This driver provides DAHDI support for various HFC-S single-port + ISDN (BRI) cards. + + To compile this driver as a module, choose M here: the + module will be called zaphfc. + + If unsure, say Y. + +config ECHO + tristate "Line Echo Canceller support" + default DAHDI + --help-- + This driver provides line echo cancelling support for mISDN and + DAHDI drivers. + + To compile this driver as a module, choose M here: the + module will be called echo. + + If unsure, say Y. + diff --git a/drivers/dahdi/ap400/Kbuild b/drivers/dahdi/ap400/Kbuild new file mode 100644 index 0000000..d124261 --- /dev/null +++ b/drivers/dahdi/ap400/Kbuild @@ -0,0 +1,26 @@ +obj-m += ap400.o + +EXTRA_CFLAGS := -I$(src)/.. + +ap400-objs := ap400_drv.o + +# APEC_SUPPORT +ECHO_FIRMWARE := $(wildcard $(src)/OCT61*.ima) +ifneq ($(strip $(ECHO_FIRMWARE)),) + EXTRA_CFLAGS+=-DAPEC_SUPPORT $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef + ap400-objs += apec.o $(shell $(src)/../oct612x/octasic-helper objects ../oct612x) firmware_oct6104e-64d.o firmware_oct6104e-128d.o +endif + +$(obj)/apec.o: $(src)/apec.h $(src)/../oct612x/include/oct6100api/oct6100_api.h + +$(obj)/firmware_oct6104e-64d.o: $(src)/OCT6104E-64D.ima $(obj)/ap400_drv.o $(src)/../firmware/make_firmware_object + @echo Making firmware object file for $(notdir $<) + @cd $(src) && ../firmware/make_firmware_object $(notdir $<) $@ $(obj)/ap400_drv.o + +$(obj)/firmware_oct6104e-128d.o: $(src)/OCT6104E-128D.ima $(obj)/ap400_drv.o $(src)/../firmware/make_firmware_object + @echo Making firmware object file for $(notdir $<) + @cd $(src) && ../firmware/make_firmware_object $(notdir $<) $@ $(obj)/ap400_drv.o + +$(src)/../firmware/make_firmware_object: + make -C $(src)/../firmware make_firmware_object + diff --git a/drivers/dahdi/ap400/ap400.h b/drivers/dahdi/ap400/ap400.h new file mode 100644 index 0000000..ba233d1 --- /dev/null +++ b/drivers/dahdi/ap400/ap400.h @@ -0,0 +1,107 @@ +/* + * AP4XX T1/E1 PCI Driver + * + * Written by Ronaldo Valiati + * + * Based on previous works, designs, and archetectures conceived and + * written by Jim Dixon and Mark Spencer . + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001-2005, Digium, Inc. + * + * 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 + + +#define AP4_GET_ALARMS _IOW (DAHDI_CODE, 60, int) +#define AP4_GET_SLIPS _IOW (DAHDI_CODE, 61, int) + +#define AP4XX_CARD_ID 0x41434532 // "ACE2" +#define APE4XX_CARD_ID 0x41504534 // "APE4" + +#define AP_CAS_BASE 0x0080 +#define AP_DATA_BASE 0x0100 + +#define AP_CARD_TYPE_REG 0x0001 +#define AP_T1E1_CONFIG_REG 0x0003 +#define AP_E1_CONFIG_REG 0x0004 +#define AP_E1_STATUS_REG 0x0005 +#define AP_LEDS_REG 0x0006 +#define AP_CLKSRC_REG 0x0007 +#define AP_HWCONFIG_REG 0x0008 +#define AP_INT_CONTROL_REG 0x0009 +#define AP_CNT_IRQ_REG 0x000B +#define AP_CNT_CV_REG 0x000C +#define AP_CNT_CRC_REG 0x000D +#define AP_CLEAR_IRQ_REG 0x000E +#define AP_CNT_SLIP_REG 0x000F + +#define AP_HWID_MASK 0x00F0 + +#define AP_CLKSRC_MASK 0x07 + +#define AP_LIU1_LINECODE 0x0080 +#define AP_LIU2_LINECODE 0x0100 +#define AP_LIU_RESET_BIT 0x0200 + +#define AP_E1_AIS_STATUS 0x01 +#define AP_E1_BFAE_STATUS 0x02 +#define AP_E1_MFAE_STATUS 0x04 +#define AP_E1_SYNC_STATUS 0x08 +#define AP_E1_CAS_STATUS 0x10 +#define AP_E1_LOS_STATUS 0x20 +#define AP_E1_RAI_STATUS 0x40 + +#define AP_E1_RAI_CONFIG 0x01 +#define AP_E1_LOOP_CONFIG 0x10 +#define AP_E1_CASEN_CONFIG 0x20 +#define AP_E1_PCM30_CONFIG 0x40 +#define AP_E1_CRCEN_CONFIG 0x80 + +#define AP_INT_CTL_ENABLE 0x01 +#define AP_INT_CTL_ACTIVE 0x02 + +#define AP_HWID_1E1_RJ 0x01 +#define AP_HWID_2E1_RJ 0x00 +#define AP_HWID_4E1_RJ 0x02 +#define AP_HWID_T1 0x04 + +#define AP4_T1_NE1_SEL 0x04 +#define AP4_T1_ESF_NSF 0x02 +#define AP4_T1_CAS_ENABLE 0x01 + +#define AP4_T1_FRAME_SYNC 0x01 + + +typedef enum { + AP_PULS_E1_75 = 0, + AP_PULS_E1_120, + AP_PULS_DSX1_0FT, + AP_PULS_DSX1_133FT, + AP_PULS_DSX1_266FT, + AP_PULS_DSX1_399FT, + AP_PULS_DSX1_533FT, + AP_PULS_J1_110, + AP_PULS_DS1_0DB, + AP_PULS_DS1_M075DB, + AP_PULS_DS1_M150DB, + AP_PULS_DS1_M225DB +} liu_mode; + diff --git a/drivers/dahdi/ap400/ap400_drv.c b/drivers/dahdi/ap400/ap400_drv.c new file mode 100644 index 0000000..f625f12 --- /dev/null +++ b/drivers/dahdi/ap400/ap400_drv.c @@ -0,0 +1,2362 @@ +/* + * AP4XX PCI Card Driver + * + * Written by Ronaldo Valiati + * + * Based on previous works, designs, and architectures conceived and + * written by Jim Dixon and Mark Spencer . + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001-2005, Digium, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ap400.h" + +//#define AP400_DEBUG +#ifdef AP400_DEBUG +#define PDEBUG(fmt, args...) { \ + printk(KERN_DEBUG "AP400 (%d): ",__LINE__); \ + printk(fmt "\n", ## args); \ +} +#else +#define PDEBUG(fmt, args...) +#endif + +/* + * Tasklets provide better system interactive response at the cost of the + * possibility of losing a frame of data at very infrequent intervals. If + * you are more concerned with the performance of your machine, enable the + * tasklets. If you are strict about absolutely no drops, then do not enable + * tasklets. + */ + +/* #define ENABLE_TASKLETS */ + + +/* Work queues are a way to better distribute load on SMP systems */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) +/* + * Work queues can significantly improve performance and scalability + * on multi-processor machines, but requires bypassing some kernel + * API's, so it's not guaranteed to be compatible with all kernels. + */ +/* #define ENABLE_WORKQUEUES */ +#endif + +/* Enable HDLC support by hardware */ +#ifdef AP400_HDLC +#include "ap400_hdlc/ap400_hdlc.c" +#endif + +//#define APEC_SUPPORT +#ifdef APEC_SUPPORT +#include "apec.h" +#endif + +/* Workarounds */ +#ifndef IRQF_SHARED +#define IRQF_SHARED SA_SHIRQ +#endif +#ifndef IRQF_DISABLED +#define IRQF_DISABLED SA_INTERRUPT +#endif +#ifndef __iomem +#define __iomem +#endif + +/* Enable prefetching may help performance */ +#define ENABLE_PREFETCH + +/* Define to get more attention-grabbing but slightly more I/O using + alarm status */ +#define FANCY_ALARM + +#define DEBUG_MAIN (1 << 0) +#define DEBUG_DTMF (1 << 1) +#define DEBUG_REGS (1 << 2) +#define DEBUG_TSI (1 << 3) +#define DEBUG_ECHOCAN (1 << 4) +#define DEBUG_RBS (1 << 5) +#define DEBUG_FRAMER (1 << 6) + +static int clock_source = -1; +static int tdm_loop = 0; +static int apec_enable = 1; +module_param(tdm_loop, int, 0600); +module_param(apec_enable, int, 0600); + +#ifdef ENABLE_WORKQUEUES +#include + +/* XXX UGLY!!!! XXX We have to access the direct structures of the workqueue which + are only defined within workqueue.c because they don't give us a routine to allow us + to nail a work to a particular thread of the CPU. Nailing to threads gives us substantially + higher scalability in multi-CPU environments though! */ + +/* + * The per-CPU workqueue (if single thread, we always use cpu 0's). + * + * The sequence counters are for flush_scheduled_work(). It wants to wait + * until until all currently-scheduled works are completed, but it doesn't + * want to be livelocked by new, incoming ones. So it waits until + * remove_sequence is >= the insert_sequence which pertained when + * flush_scheduled_work() was called. + */ + +struct cpu_workqueue_struct { + + spinlock_t lock; + + long remove_sequence; /* Least-recently added (next to run) */ + long insert_sequence; /* Next to add */ + + struct list_head worklist; + wait_queue_head_t more_work; + wait_queue_head_t work_done; + + struct workqueue_struct *wq; + task_t *thread; + + int run_depth; /* Detect run_workqueue() recursion depth */ +} ____cacheline_aligned; + +/* + * The externally visible workqueue abstraction is an array of + * per-CPU workqueues: + */ +struct workqueue_struct { + struct cpu_workqueue_struct cpu_wq[NR_CPUS]; + const char *name; + struct list_head list; /* Empty if single thread */ +}; + +/* Preempt must be disabled. */ +static void __ap4_queue_work(struct cpu_workqueue_struct *cwq, + struct work_struct *work) +{ + unsigned long flags; + + spin_lock_irqsave(&cwq->lock, flags); + work->wq_data = cwq; + list_add_tail(&work->entry, &cwq->worklist); + cwq->insert_sequence++; + wake_up(&cwq->more_work); + spin_unlock_irqrestore(&cwq->lock, flags); +} + +/* + * Queue work on a workqueue. Return non-zero if it was successfully + * added. + * + * We queue the work to the CPU it was submitted, but there is no + * guarantee that it will be processed by that CPU. + */ +static inline int ap4_queue_work(struct workqueue_struct *wq, struct work_struct *work, int cpu) +{ + int ret = 0; + + if (!test_and_set_bit(0, &work->pending)) { + BUG_ON(!list_empty(&work->entry)); + __ap4_queue_work(wq->cpu_wq + cpu, work); + ret = 1; + } + return ret; +} + +#endif + +static int debug=0; +static int timingcable; +static int highestorder; +static int t1e1override = -1; +static int j1mode = 0; +static int loopback = 0; +static int alarmdebounce = 0; + +/* Enabling bursting can more efficiently utilize PCI bus bandwidth, but + can also cause PCI bus starvation, especially in combination with other + aggressive cards. Please note that burst mode has no effect on CPU + utilization / max number of calls / etc. */ +static int noburst = 1; +static int debugslips = 0; +static int polling = 0; + +#ifdef FANCY_ALARM +static int altab[] = { +0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, +}; +#endif + +#define FLAG_STARTED (1 << 0) +#define FLAG_NMF (1 << 1) +#define FLAG_SENDINGYELLOW (1 << 2) + +#define TYPE_T1 1 /* is a T1 card */ +#define TYPE_E1 2 /* is an E1 card */ +#define TYPE_J1 3 /* is a running J1 */ + +struct devtype { + char *desc; + unsigned int flags; +}; + +static struct devtype ap401 = { "Aligera AP401", 0 }; +static struct devtype ap402 = { "Aligera AP402", 0 }; +static struct devtype ap404 = { "Aligera AP404", 0 }; +static struct devtype ape401 = { "Aligera APE401", 0 }; +static struct devtype ape402 = { "Aligera APE402", 0 }; +static struct devtype ape404 = { "Aligera APE404", 0 }; + +struct ap4; + +struct ap4_span { + struct ap4 *owner; + unsigned int *writechunk; /* Double-word aligned write memory */ + unsigned int *readchunk; /* Double-word aligned read memory */ + int spantype; /* card type, T1 or E1 or J1 */ + int sync; + int psync; + int alarmtimer; + int redalarms; + int notclear; + int alarmcount; + int spanflags; + int syncpos; + int e1check; /* E1 check */ + int reload_cas; + unsigned char casbuf[15]; + unsigned int slipcount; + struct dahdi_span span; + unsigned char txsigs[16]; /* Transmit sigs */ + int loopupcnt; + int loopdowncnt; + unsigned char ec_chunk1[31][DAHDI_CHUNKSIZE]; /* first EC chunk buffer */ + unsigned char ec_chunk2[31][DAHDI_CHUNKSIZE]; /* second EC chunk buffer */ + int irqmisses; +#ifdef ENABLE_WORKQUEUES + struct work_struct swork; +#endif + struct dahdi_chan *chans[32]; /* Individual channels */ +}; + +struct ap4_regs { + volatile u32 card_id; // 00h R0 + volatile u16 fpga_ver; // 04h R1 + volatile u16 span_num; // 06h R1 + u32 __unused; // 08h R2 + volatile u32 liu_config; // 0Ch R3 + volatile u32 e1_config; // 10h R4 + volatile u32 e1_status; // 14h R5 + volatile u32 leds; // 18h R6 + volatile u32 clock_source; // 1Ch R7 + u32 __unused3[8]; // 20h - 3Ch R8 - R15 + volatile u32 echo_ctrl; // 40h R16 + volatile u32 echo_data; // 44h R17 + volatile u32 t1_status; // 48h R18 + volatile u32 t1_config; // 4Ch R19 +}; + +struct ap4 { + /* This structure exists one per card */ + struct pci_dev *dev; /* Pointer to PCI device */ + struct ap4_regs *hw_regs; + unsigned int intcount; + int flag_1st_irq; + int num; /* Which card we are */ + int fpgaver; /* version of FPGA */ + int hwid; /* hardware ID */ + int globalconfig; /* Whether global setup has been done */ + int syncsrc; /* active sync source */ + struct ap4_span *tspans[4]; /* Individual spans */ + int numspans; /* Number of spans on the card */ + int blinktimer[4]; +#ifdef FANCY_ALARM + int alarmpos[4]; +#endif + int irq; /* IRQ used by device */ + int order; /* Order */ + int flags; /* Device flags */ + int ledreg; /* LED Register */ + int e1recover; /* E1 recovery timer */ + unsigned long memaddr; /* Base address of card */ + unsigned long memlen; + volatile unsigned int *membase; /* Base address of card */ + int spansstarted; /* number of spans started */ + /* spinlock_t lock; */ /* lock context */ + spinlock_t reglock; /* lock register access */ + volatile unsigned int *writechunk; /* Double-word aligned write memory */ + volatile unsigned int *readchunk; /* Double-word aligned read memory */ +#ifdef ENABLE_WORKQUEUES + atomic_t worklist; + struct workqueue_struct *workq; +#else +#ifdef ENABLE_TASKLETS + int taskletrun; + int taskletsched; + int taskletpending; + int taskletexec; + int txerrors; + struct tasklet_struct ap4_tlet; +#endif +#endif + unsigned int passno; /* number of interrupt passes */ + struct devtype *dt; + char *variety; + int last0; /* for detecting double-missed IRQ */ + int checktiming; /* Set >0 to cause the timing source to be checked */ +#ifdef AP400_HDLC + struct card_s *hdlc_card; +#endif +#ifdef APEC_SUPPORT + int apec_enable; + struct apec_s *apec; +#endif +}; + + +static void __set_clear(struct ap4 *wc, int span); +static int ap4_startup(struct file *file, struct dahdi_span *span); +static int ap4_shutdown(struct dahdi_span *span); +static int ap4_rbsbits(struct dahdi_chan *chan, int bits); +static int ap4_maint(struct dahdi_span *span, int cmd); +static int ap4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data); +static void __ap4_set_timing_source(struct ap4 *wc, int unit); +static void __ap4_check_alarms(struct ap4 *wc, int span); +static void __ap4_check_sigbits(struct ap4 *wc, int span); + + +#define AP_ACTIVATE (1 << 12) + +#define AP_OFF (0) +#define AP_ON (1) + +#define MAX_AP4_CARDS 64 + +#ifdef ENABLE_TASKLETS +static void ap4_tasklet(unsigned long data); +#endif + +static struct ap4 *cards[MAX_AP4_CARDS]; + +//#define ap_debugk(fmt,args...) printk("ap400 -> %s: "fmt, __PRETTY_FUNCTION__, ##args) +#define ap_debugk(fmt,args...) + +//#define TIMER_DEBUG 1 + +#ifdef TIMER_DEBUG +struct timer_list ap4xx_opt_timer; +unsigned int delay = 1000; +module_param(delay, uint, S_IRUGO); +#endif + +#define PCI_DEVICE_ID_AP4XX 0x1004 + +static inline void __ap4_set_led(struct ap4 *wc, int span, int color) +{ + wc->ledreg &= ~(AP_ON << span); + wc->ledreg |= (color << span); + *(wc->membase+AP_LEDS_REG) &= ~0x0000000F; + *(wc->membase+AP_LEDS_REG) |= ((wc->ledreg)&0x0F); +} + +static inline void ap4_activate(struct ap4 *wc) +{ + wc->ledreg |= AP_ACTIVATE; +} + +static void __set_clear(struct ap4 *wc, int span) +{ + int i,j; + int oldnotclear; + unsigned short val=0; + struct ap4_span *ts = wc->tspans[span]; + + oldnotclear = ts->notclear; + if (ts->spantype == TYPE_T1) { + for (i=0;i<24;i++) { + j = (i/8); + if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) { + val |= 1 << (7 - (i % 8)); + ts->notclear &= ~(1 << i); + } else + ts->notclear |= (1 << i); + if ((i % 8)==7) { + val = 0; + } + } + } else { + for (i=0;i<31;i++) { + if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) + ts->notclear &= ~(1 << i); + else + ts->notclear |= (1 << i); + } + } +} + +#ifdef APEC_SUPPORT + +#define APEC_CTRL_RESET 0x80000000 +#define APEC_CTRL_DDR_NCKE 0x40000000 +#define APEC_CTRL_EC_DISABLE 0x20000000 +#define APEC_CTRL_DAS 0x00080000 +#define APEC_CTRL_RD 0x00040000 +#define APEC_CTRL_REQ 0x00020000 +#define APEC_CTRL_READY 0x00010000 + +#define APEC_ACCESS_TIMEOUT 1000 + +static inline u16 oct_raw_read (struct ap4_regs *regs, unsigned short addr) +{ + unsigned short data; + // Poll ready bit + while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); + // Write control bits and address + regs->echo_ctrl = APEC_CTRL_RD | APEC_CTRL_REQ | (addr & 0xFFFF); + while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); + data = regs->echo_data & 0xFFFF; + //PDEBUG("Raw Read 0x%04hX @ 0x%08X", data, addr); + return data; +} + +static inline void oct_raw_write (struct ap4_regs *regs, unsigned short addr, + unsigned short data) +{ + // Poll ready bit + while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); + // Write data, then control bits and address + regs->echo_data = data & 0xFFFF; + regs->echo_ctrl = APEC_CTRL_REQ | (addr & 0xFFFF); + // Poll ready bit + while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); + //PDEBUG("Raw Write 0x%04hX @ 0x%08X", data, addr); + //oct_raw_read(regs, addr); +} + +static inline int oct_ext_wait (struct ap4_regs *regs) +{ + int i = APEC_ACCESS_TIMEOUT; + while ((oct_raw_read(regs, 0x0) & 0x100) && (i-- > 0)); + if (i == -1) { + printk(KERN_WARNING "Wait access_req timeout\n"); + return -1; + } + return 0; +} + +static inline u16 oct_ind_read (struct ap4_regs *regs, unsigned int addr) +{ + // Poll access_req bit + if (oct_ext_wait(regs)) + return 0; + // Write extended indirect registers + oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF); + oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF); + oct_raw_write(regs, 0x0, ((addr & 0xE) << 8) | 0x101); + // Poll access_req bit + if (oct_ext_wait(regs)) + return 0; + // Return data + return oct_raw_read(regs, 0x4); +} + +static inline void oct_ind_write (struct ap4_regs *regs, unsigned int addr, + unsigned short data) +{ + // Poll access_req bit + if (oct_ext_wait(regs)) + return; + oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF); + oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF); + oct_raw_write(regs, 0x4, data); + oct_raw_write(regs, 0x0, ((addr & 0xE) << 8) | 0x3101); + // Poll access_req bit + if (oct_ext_wait(regs)) + return; +} + +static inline u16 oct_dir_read (struct ap4_regs *regs, unsigned int addr) +{ + // Poll access_req bit + if (oct_ext_wait(regs)) + return 0; + // Write extended direct registers + oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF); + oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF); + oct_raw_write(regs, 0x0, 0x1); + regs->echo_ctrl = APEC_CTRL_DAS | APEC_CTRL_RD | APEC_CTRL_REQ | (addr & 0xFFFF); + while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); + // Return data + return regs->echo_data; +} + +static inline void oct_dir_write (struct ap4_regs *regs, unsigned int addr, + unsigned short data) +{ + // Poll access_req bit + if (oct_ext_wait(regs)) + return; + // Write extended direct registers + oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF); + oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF); + oct_raw_write(regs, 0x0, 0x3001); + regs->echo_data = data & 0xFFFF; + regs->echo_ctrl = APEC_CTRL_DAS | APEC_CTRL_REQ | (addr & 0xFFFF); + while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); +} + + +unsigned int oct_read (void *card, unsigned int addr) +{ + struct ap4 *wc = card; + int flags; + unsigned short data; + spin_lock_irqsave(&wc->reglock, flags); + data = oct_ind_read(wc->hw_regs, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + PDEBUG("Read 0x%04hX @ 0x%08X", data, addr); + return data; +} + +void oct_write (void *card, unsigned int addr, unsigned int data) +{ + struct ap4 *wc = card; + int flags; + spin_lock_irqsave(&wc->reglock, flags); + oct_ind_write(wc->hw_regs, addr, data); + spin_unlock_irqrestore(&wc->reglock, flags); + PDEBUG("Write 0x%04hX @ 0x%08X", data, addr); +} + +static int ap4_apec_init(struct ap4 *wc) +{ + int laws[4]; + int i; + unsigned int apec_capacity; + struct firmware embedded_firmware; + const struct firmware *firmware = &embedded_firmware; +#if !defined(HOTPLUG_FIRMWARE) + extern void _binary_OCT6104E_64D_ima_size; + extern u8 _binary_OCT6104E_64D_ima_start[]; + extern void _binary_OCT6104E_128D_ima_size; + extern u8 _binary_OCT6104E_128D_ima_start[]; +#else + static const char oct64_firmware[] = "OCT6104E-64D.ima"; + static const char oct128_firmware[] = "OCT6104E-128D.ima"; +#endif + + // Enable DDR and Reset Octasic + wc->hw_regs->echo_ctrl |= APEC_CTRL_RESET; + wc->hw_regs->echo_ctrl |= APEC_CTRL_DDR_NCKE; + udelay(500); + wc->hw_regs->echo_ctrl &= APEC_CTRL_RESET; + wc->hw_regs->echo_ctrl &= APEC_CTRL_DDR_NCKE; + wc->hw_regs->echo_ctrl &= APEC_CTRL_EC_DISABLE; + + /* Setup alaw vs ulaw rules */ + for (i = 0; i < wc->numspans; i++) { + if (wc->tspans[i]->span.channels > 24) + laws[i] = 1; // E1: alaw + else + laws[i] = 0; // T1: ulaw + } + + switch ((apec_capacity = apec_capacity_get(wc))) { + case 64: +#if defined(HOTPLUG_FIRMWARE) + if ((request_firmware(&firmware, oct64_firmware, &wc->dev->dev) != 0) || + !firmware) { + printk("%s: firmware %s not available from userspace\n", + wc->variety, oct64_firmware); + return -1; + } +#else + embedded_firmware.data = _binary_OCT6104E_64D_ima_start; + /* Yes... this is weird. objcopy gives us a symbol containing + the size of the firmware, not a pointer to a variable containing + the size. The only way we can get the value of the symbol + is to take its address, so we define it as a pointer and + then cast that value to the proper type. + */ + embedded_firmware.size = (size_t) &_binary_OCT6104E_64D_ima_size; +#endif + break; + case 128: +#if defined(HOTPLUG_FIRMWARE) + if ((request_firmware(&firmware, oct128_firmware, &wc->dev->dev) != 0) || + !firmware) { + printk("%s: firmware %s not available from userspace\n", + wc->variety, oct128_firmware); + return -1; + } +#else + embedded_firmware.data = _binary_OCT6104E_128D_ima_start; + /* Yes... this is weird. objcopy gives us a symbol containing + the size of the firmware, not a pointer to a variable containing + the size. The only way we can get the value of the symbol + is to take its address, so we define it as a pointer and + then cast that value to the proper type. + */ + embedded_firmware.size = (size_t) &_binary_OCT6104E_128D_ima_size; +#endif + break; + default: + printk(KERN_INFO "Unsupported channel capacity found on" + "echo cancellation module (%d).\n", apec_capacity); + return -1; + } + + if (!(wc->apec = apec_init(wc, laws, wc->numspans, firmware))) { + printk(KERN_WARNING "APEC: Failed to initialize\n"); + if (firmware != &embedded_firmware) + release_firmware(firmware); + return -1; + } + + if (firmware != &embedded_firmware) + release_firmware(firmware); + + printk(KERN_INFO "APEC: Present and operational servicing %d span(s)\n", wc->numspans); + return 0; +} + +void ap4_apec_release(struct ap4 *wc) +{ + // Disabel DDR and reset Octasic + wc->hw_regs->echo_ctrl |= APEC_CTRL_RESET; + wc->hw_regs->echo_ctrl |= APEC_CTRL_DDR_NCKE; + wc->hw_regs->echo_ctrl |= APEC_CTRL_EC_DISABLE; + if (wc->apec) + apec_release(wc->apec); +} + + +static int ap4_echocan(struct dahdi_chan *chan, int eclen) +{ + struct ap4 *wc = chan->pvt; + int channel; + + if (!wc->apec) + return -ENODEV; + if (debug) + printk(KERN_DEBUG "AP400: ap4_echocan @ Span %d Channel %d Length: %d\n", + chan->span->offset, chan->chanpos, eclen); + channel = (chan->chanpos << 2) | chan->span->offset; + apec_setec(wc->apec, channel, eclen); + return 0; +} + +#endif // APEC_SUPPORT + + +static int ap4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) +{ + struct ap4 *wc = chan->pvt; + int span = 0; + int alarms = 0; + unsigned char c, e1_cfg; + + switch(cmd) { + case AP4_GET_ALARMS: + if (copy_from_user(&span, (int *)data, sizeof(int))) + return -EFAULT; + // span starts in zero + span--; + if (wc->tspans[span]->spantype == TYPE_E1) { + /* le status e configuracao do E1 */ + c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span)); + e1_cfg = ((*(wc->membase+AP_E1_CONFIG_REG))>>(8*span)); + if( c & AP_E1_LOS_STATUS) { + alarms = 0x01; + } else if( c & AP_E1_AIS_STATUS) { + alarms = 0x02; + } else if(!(c & AP_E1_BFAE_STATUS)) { + alarms = 0x04; + if (c & AP_E1_RAI_STATUS) + alarms |= 0x08; + // Erro de MFA: 00 - MFA desabilitado, 01 - erro de MFA, 10 - MFA OK + if ( (c & AP_E1_MFAE_STATUS) && (e1_cfg & AP_E1_CRCEN_CONFIG) ) + alarms |= 0x10; + else if ( (!(c & AP_E1_MFAE_STATUS)) && (e1_cfg & AP_E1_CRCEN_CONFIG) ) + alarms |= 0x20; + // Erro de CAS: 00 - desabilitado, 01 - erro de CAS, 10 - CAS OK + if ( (!(c & AP_E1_CAS_STATUS)) && (e1_cfg & AP_E1_PCM30_CONFIG)) + alarms |= 0x40; + else if ( (c & AP_E1_CAS_STATUS) && (e1_cfg & AP_E1_PCM30_CONFIG)) + alarms |= 0x80; + } + } else { + /* le status e configuracao do E1 */ + c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span)); + if( c & AP_E1_LOS_STATUS) + alarms = 0x01; + else { + c = wc->hw_regs->t1_status >> (8*span); + if (!(c & AP4_T1_FRAME_SYNC)) + alarms = 0x04; + } + } + if(debug) printk("AP4_GET_ALARMS: span = %d, alarms = 0x%02x\n", span+1, alarms); + if (copy_to_user((int *)data, &alarms, sizeof(int))) + return -EFAULT; + break; + + case AP4_GET_SLIPS: + if (copy_from_user(&span, (int *)data, sizeof(int))) + return -EFAULT; + // span starts in zero + span--; + if((span < wc->numspans) && (span >=0)) + alarms = wc->tspans[span]->slipcount; + if(debug) printk("AP4_GET_SLIPS: span = %d, slips = 0x%02x\n", span+1, alarms); + if (copy_to_user((int *)data, &alarms, sizeof(int))) + return -EFAULT; + break; + + default: + PDEBUG("%s: Unknown IOCTL CODE!", wc->variety); + return -ENOTTY; + } + return 0; +} + +static inline struct ap4_span* ap4_span_from_span(struct dahdi_span *span) { + return container_of(span, struct ap4_span, span); +} + +static int ap4_maint(struct dahdi_span *span, int cmd) +{ + struct ap4_span *ts = ap4_span_from_span(span); + struct ap4 *wc = ts->owner; + + + if (ts->spantype == TYPE_E1) { + switch(cmd) { + case DAHDI_MAINT_NONE: + printk("XXX Turn off local and remote loops E1 XXX\n"); + *(wc->membase+AP_E1_CONFIG_REG) &= ~(AP_E1_LOOP_CONFIG<<((span->spanno-1)*8)); + break; + case DAHDI_MAINT_LOCALLOOP: + printk("XXX Turn on local loopback E1 XXX\n"); + break; + case DAHDI_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopback E1 XXX\n"); + break; + case DAHDI_MAINT_LOOPUP: + printk("XXX Turn on local loopback on E1 #%d instead of send loopup code XXX\n", span->spanno); + *(wc->membase+AP_E1_CONFIG_REG) |= (AP_E1_LOOP_CONFIG<<((span->spanno-1)*8)); + break; + case DAHDI_MAINT_LOOPDOWN: + printk("XXX Turn on local loopback on E1 #%d instead of send loopdown code XXX\n", span->spanno); + *(wc->membase+AP_E1_CONFIG_REG) |= (AP_E1_LOOP_CONFIG<<((span->spanno-1)*8)); + break; + default: + printk("%s: Unknown E1 maint command: %d\n", wc->variety, cmd); + break; + } + } else { + switch(cmd) { + case DAHDI_MAINT_NONE: + printk("XXX Turn off local and remote loops T1 XXX\n"); + break; + case DAHDI_MAINT_LOCALLOOP: + printk("XXX Turn on local loop and no remote loop XXX\n"); + break; + case DAHDI_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopup XXX\n"); + break; + case DAHDI_MAINT_LOOPUP: + break; + case DAHDI_MAINT_LOOPDOWN: + break; + default: + printk("%s: Unknown T1 maint command: %d\n", wc->variety, cmd); + break; + } + } + return 0; +} + +static int ap4_rbsbits(struct dahdi_chan *chan, int bits) +{ + u_char m,c; + int k,n,b; + struct ap4 *wc = chan->pvt; + struct ap4_span *ts = wc->tspans[chan->span->offset]; + unsigned long flags; + volatile unsigned int *writecas = (wc->membase+AP_CAS_BASE); + unsigned int allspansbits; + + //ap_debugk("chan->channo = %d, int bits = 0x%08x\n", chan->channo, bits); + if(debug & DEBUG_RBS) printk("Setting bits to %d on channel %s\n", bits, chan->name); + spin_lock_irqsave(&wc->reglock, flags); + k = chan->span->offset; + if (ts->spantype == TYPE_E1) { /* do it E1 way */ + if (chan->chanpos == 16) { + spin_unlock_irqrestore(&wc->reglock, flags); + return 0; + } + n = chan->chanpos - 1; + if (chan->chanpos > 15) n--; + b = (n % 15); + c = ts->txsigs[b]; + m = (n / 15) << 2; /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + ts->txsigs[b] = c; + /* monta a word de 32 bits com informacao de todos os spans */ + allspansbits = wc->tspans[0]->txsigs[b]; + if (wc->numspans > 1) { + allspansbits |= (wc->tspans[1]->txsigs[b] << 8); + } + if (wc->numspans == 4) { + allspansbits |= (wc->tspans[2]->txsigs[b] << 16) | + (wc->tspans[3]->txsigs[b] << 24); + } + /* output them to the chip */ + writecas[b] = allspansbits; + ap_debugk("escrito 0x%08x para ser transmitido pelo CAS (b = %d)\n", allspansbits, b); +#if 0 + } else if (ts->span.lineconfig & DAHDI_CONFIG_D4) { + n = chan->chanpos - 1; + b = (n/4); + c = ts->txsigs[b]; + m = ((3 - (n % 4)) << 1); /* nibble selector */ + c &= ~(0x3 << m); /* keep the other nibble */ + c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */ + ts->txsigs[b] = c; + /* output them to the chip */ + //__ap4_out( ... ); + } else if (ts->span.lineconfig & DAHDI_CONFIG_ESF) { +#endif + } else { + n = chan->chanpos - 1; + b = (n/2); + c = ts->txsigs[b]; + m = ((n % 2) << 2); /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + ts->txsigs[b] = c; + /* output them to the chip */ + /* monta a word de 32 bits com informacao de todos os spans */ + allspansbits = wc->tspans[0]->txsigs[b]; + if (wc->numspans > 1) { + allspansbits |= (wc->tspans[1]->txsigs[b] << 8); + } + if (wc->numspans == 4) { + allspansbits |= (wc->tspans[2]->txsigs[b] << 16) | + (wc->tspans[3]->txsigs[b] << 24); + } + /* output them to the chip */ + writecas[b] = allspansbits; + ap_debugk("escrito 0x%08x para ser transmitido pelo CAS (b = %d)\n", allspansbits, b); + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (debug & DEBUG_RBS) + printk("Finished setting RBS bits\n"); + return 0; +} + +static int ap4_shutdown(struct dahdi_span *span) +{ + int tspan; + int wasrunning; + unsigned long flags; + struct ap4_span *ts = ap4_span_from_span(span); + struct ap4 *wc = ts->owner; + + tspan = span->offset + 1; + if (tspan < 0) { + printk("%s: '%d' isn't us?\n", wc->variety, span->spanno); + return -1; + } + + spin_lock_irqsave(&wc->reglock, flags); + wasrunning = span->flags & DAHDI_FLAG_RUNNING; + + span->flags &= ~DAHDI_FLAG_RUNNING; + if (wasrunning) + wc->spansstarted--; + __ap4_set_led(wc, span->offset, AP_OFF); + if (((wc->numspans == 4) && + (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)) && + (!(wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING)) && + (!(wc->tspans[2]->span.flags & DAHDI_FLAG_RUNNING)) && + (!(wc->tspans[3]->span.flags & DAHDI_FLAG_RUNNING))) + || + ((wc->numspans == 2) && + (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)) && + (!(wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING))) + || + ((wc->numspans == 1) && + (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)))) { + /* No longer in use, disable interrupts */ + printk("%s: Disabling interrupts since there are no active spans\n", + wc->variety); + } else wc->checktiming = 1; + spin_unlock_irqrestore(&wc->reglock, flags); + if (debug & DEBUG_MAIN) + printk("Span %d (%s) shutdown\n", span->spanno, span->name); + return 0; +} + +static int ap4_spanconfig(struct file *file, struct dahdi_span *span, + struct dahdi_lineconfig *lc) +{ + int i; + struct ap4_span *ts = ap4_span_from_span(span); + struct ap4 *wc = ts->owner; + unsigned int val; + + printk("About to enter spanconfig!\n"); + if (debug & DEBUG_MAIN) + printk("%s: Configuring span %d\n", wc->variety, span->spanno); + /* XXX We assume lineconfig is okay and shouldn't XXX */ + span->lineconfig = lc->lineconfig; + span->txlevel = lc->lbo; + span->rxlevel = 0; + if (lc->sync < 0) + lc->sync = 0; + if (lc->sync > 4) + lc->sync = 0; + + /* remove this span number from the current sync sources, if there */ + for(i = 0; i < wc->numspans; i++) { + if (wc->tspans[i]->sync == span->spanno) { + wc->tspans[i]->sync = 0; + wc->tspans[i]->psync = 0; + } + } + wc->tspans[span->offset]->syncpos = lc->sync; + /* if a sync src, put it in proper place */ + if (lc->sync) { + wc->tspans[lc->sync - 1]->sync = span->spanno; + wc->tspans[lc->sync - 1]->psync = span->offset + 1; + } + wc->checktiming = 1; + /* If we're already running, then go ahead and apply the changes */ + if (span->flags & DAHDI_FLAG_RUNNING) + return ap4_startup(file, span); + + // Limpa contadores de slips, crc e bpv + val = (*(wc->membase + AP_CNT_SLIP_REG)); + val = (*(wc->membase + AP_CNT_CRC_REG)); + val = (*(wc->membase + AP_CNT_CV_REG)); + + ap_debugk("habilitando interrupcao!\n"); + // Nao considera as primeiras interrupcoes na soma das IRQs perdidas + wc->flag_1st_irq = 16; + // Enable interrupt + *(wc->membase + AP_INT_CONTROL_REG) |= AP_INT_CTL_ENABLE; + // Limpa interrupcao da FPGA para forcar borda de subida na proxima + val = *(wc->membase + AP_CLEAR_IRQ_REG); + + printk("Done with spanconfig!\n"); + return 0; +} + +static int ap4_chanconfig(struct file *file, struct dahdi_chan *chan, + int sigtype) +{ + int alreadyrunning; + unsigned long flags; + struct ap4 *wc = chan->pvt; + + alreadyrunning = wc->tspans[chan->span->offset]->span.flags & DAHDI_FLAG_RUNNING; + if (debug & DEBUG_MAIN) { + if (alreadyrunning) + printk("%s: Reconfigured channel %d (%s) sigtype %d\n", + wc->variety, chan->channo, chan->name, sigtype); + else + printk("%s: Configured channel %d (%s) sigtype %d\n", + wc->variety, chan->channo, chan->name, sigtype); + } + spin_lock_irqsave(&wc->reglock, flags); + if (alreadyrunning) + __set_clear(wc, chan->span->offset); + spin_unlock_irqrestore(&wc->reglock, flags); + return 0; +} + +static int ap4_open(struct dahdi_chan *chan) +{ + try_module_get(THIS_MODULE); + return 0; +} + +static int ap4_close(struct dahdi_chan *chan) +{ + module_put(THIS_MODULE); + return 0; +} + +static const struct dahdi_span_ops ap4_span_ops = { + .owner = THIS_MODULE, + .spanconfig = ap4_spanconfig, + .chanconfig = ap4_chanconfig, + .startup = ap4_startup, + .shutdown = ap4_shutdown, + .rbsbits = ap4_rbsbits, + .maint = ap4_maint, + .open = ap4_open, + .close = ap4_close, +#ifdef APEC_SUPPORT + .echocan = ap4_echocan, +#endif + .ioctl = ap4_ioctl +}; + +static void init_spans(struct ap4 *wc) +{ + int x,y; + struct ap4_span *ts; + + for (x=0;xnumspans;x++) { + ts = wc->tspans[x]; + sprintf(ts->span.name, "AP4%d%d/%d/%d", 0, wc->numspans, wc->num, x + 1); + snprintf(ts->span.desc, sizeof(ts->span.desc) - 1, "AP4%d%d Card %d Span %d", 0, wc->numspans, wc->num+1, x+1); + snprintf(ts->span.location, sizeof(ts->span.location) - 1, + "PCI Bus %02d Slot %02d", wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); + ts->span.manufacturer = "Aligera"; + dahdi_copy_string(ts->span.devicetype, wc->variety, sizeof(ts->span.devicetype)); + ts->span.ops = &ap4_span_ops; + if (ts->spantype == TYPE_E1) { + ts->span.channels = 31; + ts->span.spantype = "E1"; + ts->span.linecompat = DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4; + ts->span.deflaw = DAHDI_LAW_ALAW; + } else { + ts->span.channels = 24; + ts->span.spantype = "T1"; + ts->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 | DAHDI_CONFIG_ESF; + ts->span.deflaw = DAHDI_LAW_MULAW; + } + ts->span.chans = ts->chans; + ts->span.flags = DAHDI_FLAG_RBS; + ts->owner = wc; + ts->span.offset = x; + ts->writechunk = (void *)(wc->writechunk + x * 32 * 2); + ts->readchunk = (void *)(wc->readchunk + x * 32 * 2); + for (y=0;ytspans[x]->span.channels;y++) { + struct dahdi_chan *mychans = ts->chans[y]; + sprintf(mychans->name, "AP4%d%d/%d/%d/%d", 0, wc->numspans, wc->num, x + 1, y + 1); + mychans->sigcap = DAHDI_SIG_EM | DAHDI_SIG_CLEAR | DAHDI_SIG_FXSLS | DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | + DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_FXOKS | DAHDI_SIG_CAS | DAHDI_SIG_EM_E1 | DAHDI_SIG_DACS_RBS; + mychans->pvt = wc; + mychans->chanpos = y + 1; + } + } + printk("%s: Spans initialized\n", wc->variety); +} + + + +static void __ap4_set_timing_source(struct ap4 *wc, int unit) +{ + unsigned int timing; + int x; + + if (unit != wc->syncsrc) { + if ((unit > -1) && (unit < 4)) { + /* define fonte de clock para interface escolhida */ + timing = *(wc->membase+AP_CLKSRC_REG); + timing &= ~AP_CLKSRC_MASK; + timing |= unit+1; + *(wc->membase+AP_CLKSRC_REG) = timing; + } else { + /* define clock para interno */ + timing = *(wc->membase+AP_CLKSRC_REG); + timing &= ~AP_CLKSRC_MASK; + *(wc->membase+AP_CLKSRC_REG) = timing; + } + wc->syncsrc = unit; + if ((unit < 0) || (unit > 3)) + unit = 0; + else + unit++; + for (x=0;xnumspans;x++) + wc->tspans[x]->span.syncsrc = unit; + } else { + if (debug & DEBUG_MAIN) + printk("%s: Timing source already set to %d\n", + wc->variety, unit); + } + printk("%s: Timing source set to %d (clksrc_reg = 0x%08x)\n", + wc->variety, unit, *(wc->membase+AP_CLKSRC_REG)); +} + +static void __ap4_set_timing_source_auto(struct ap4 *wc) +{ + int x; + + wc->checktiming = 0; + for (x=0;xnumspans;x++) { + if (wc->tspans[x]->sync) { + if ((wc->tspans[wc->tspans[x]->psync - 1]->span.flags & DAHDI_FLAG_RUNNING) && + !(wc->tspans[wc->tspans[x]->psync - 1]->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE) )) { + /* Valid timing source */ + __ap4_set_timing_source(wc, wc->tspans[x]->psync - 1); + return; + } + } + } + __ap4_set_timing_source(wc, 4); +} + +static void __ap4_configure_t1(struct ap4 *wc, int unit, int lineconfig, int txlevel) +{ + char *framing, *line; + unsigned int config = 0; + unsigned int param = 0; + unsigned int linecode = 0; + + wc->tspans[unit]->spantype = TYPE_T1; + wc->tspans[unit]->span.channels = 24; + wc->tspans[unit]->span.deflaw = DAHDI_LAW_MULAW; + + /* Configure line code */ + if (unit < 2) + linecode = AP_LIU1_LINECODE; + else + linecode = AP_LIU2_LINECODE; + if (lineconfig & DAHDI_CONFIG_AMI) { + *(wc->membase+AP_LEDS_REG) |= linecode; + line = "AMI"; + } else { + *(wc->membase+AP_LEDS_REG) &= ~linecode; + line = "B8ZS"; + } + + /* loopback test*/ + //wc->hw_regs->e1_config |= (AP_E1_LOOP_CONFIG << (8 * unit)); + //printk("E1 config = 0x%08x\n", wc->hw_regs->e1_config); + + /* Configure T1 */ + config = wc->hw_regs->liu_config; + config &= ~(0x000000ff << (8 * unit)); + config |= (AP_PULS_DSX1_0FT << (8 * unit)); + wc->hw_regs->liu_config = config; + + param = AP4_T1_NE1_SEL | AP4_T1_CAS_ENABLE; + if (lineconfig & DAHDI_CONFIG_D4) { + framing = "D4"; + } else { + framing = "ESF"; + param |= AP4_T1_ESF_NSF; + } + config = wc->hw_regs->t1_config; + config &= ~(0x000000ff << (8 * unit)); + config |= (param << (8 * unit)); + wc->hw_regs->t1_config = config; + + printk("T1 Status: 0x%08x\tT1 Config: 0x%08x\tPARAM: 0x%08x\n", + wc->hw_regs->t1_status, wc->hw_regs->t1_config, param); + + if (!polling) { + __ap4_check_alarms(wc, unit); + __ap4_check_sigbits(wc, unit); + } + printk("%s: Span %d configured for %s/%s\n", wc->variety, unit + 1, framing, line); +} + +static void __ap4_configure_e1(struct ap4 *wc, int unit, int lineconfig) +{ + char *crc4 = ""; + char *framing, *line; + unsigned int e1s_cfg, config = 0; + unsigned int linecode = 0; + + wc->tspans[unit]->spantype = TYPE_E1; + wc->tspans[unit]->span.channels = 31; + wc->tspans[unit]->span.deflaw = DAHDI_LAW_ALAW; + + if (loopback) { + } + + if (lineconfig & DAHDI_CONFIG_CRC4) { + crc4 = "/CRC4"; + config |= AP_E1_CRCEN_CONFIG; + } + + if(unit < 2) + linecode = AP_LIU1_LINECODE; + else + linecode = AP_LIU2_LINECODE; + /* Configure line interface */ + if (lineconfig & DAHDI_CONFIG_AMI) { + *(wc->membase+AP_LEDS_REG) |= linecode; + line = "AMI"; + } else { + *(wc->membase+AP_LEDS_REG) &= ~linecode; + line = "HDB3"; + } + + if (lineconfig & DAHDI_CONFIG_CCS) { + framing = "CCS"; + } else { + framing = "CAS"; + config |= (AP_E1_CASEN_CONFIG | AP_E1_PCM30_CONFIG); + } + + e1s_cfg = *(wc->membase+AP_E1_CONFIG_REG); + e1s_cfg &= ~(0x000000ff<<(8*unit)); + e1s_cfg |= (config<<(8*unit)); + *(wc->membase+AP_E1_CONFIG_REG) = e1s_cfg; + + /* Disable T1 framer */ + config = wc->hw_regs->t1_config; + config &= ~(0x000000ff << (8 * unit)); + wc->hw_regs->t1_config = config; + + /* Configure LIU Signalling */ + e1s_cfg = *(wc->membase+AP_T1E1_CONFIG_REG); + e1s_cfg &= ~(0x000000ff<<(8*unit)); + e1s_cfg |= (AP_PULS_E1_120<<(8*unit)); + *(wc->membase+AP_T1E1_CONFIG_REG) = e1s_cfg; + + if (!polling) { + __ap4_check_alarms(wc, unit); + __ap4_check_sigbits(wc, unit); + } + printk("%s: Span %d configured for %s/%s%s\n", + wc->variety, unit + 1, framing, line, crc4); +} + +static int ap4_startup(struct file *file, struct dahdi_span *span) +{ + int i; + int tspan; + unsigned long flags; + int alreadyrunning; + struct ap4_span *ts = ap4_span_from_span(span); + struct ap4 *wc = ts->owner; + + printk("About to enter startup!\n"); + tspan = span->offset + 1; + if (tspan < 0) { + printk("%s: Span '%d' isn't us?\n", wc->variety, span->spanno); + return -1; + } + + spin_lock_irqsave(&wc->reglock, flags); + + alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; + + /* initialize the start value for the entire chunk of last ec buffer */ + for(i = 0; i < span->channels; i++) + { + memset(ts->ec_chunk1[i], + DAHDI_LIN2X(0, span->chans[i]),DAHDI_CHUNKSIZE); + memset(ts->ec_chunk2[i], + DAHDI_LIN2X(0, span->chans[i]),DAHDI_CHUNKSIZE); + } + + /* Force re-evaluation fo timing source */ +// if (timingcable) + wc->syncsrc = -1; + + if ((span->lineconfig & DAHDI_CONFIG_D4) || (span->lineconfig & DAHDI_CONFIG_ESF)) { + /* is a T1 card */ + __ap4_configure_t1(wc, span->offset, span->lineconfig, span->txlevel); + } else { /* is a E1 card */ + __ap4_configure_e1(wc, span->offset, span->lineconfig); + } + + /* Note clear channel status */ + wc->tspans[span->offset]->notclear = 0; + __set_clear(wc, span->offset); + + if (!alreadyrunning) { + span->flags |= DAHDI_FLAG_RUNNING; + wc->spansstarted++; + /* enable interrupts */ + + if (!polling) { + __ap4_check_alarms(wc, span->offset); + __ap4_check_sigbits(wc, span->offset); + } + } + spin_unlock_irqrestore(&wc->reglock, flags); + + if (wc->tspans[0]->sync == span->spanno) printk("SPAN %d: Primary Sync Source\n",span->spanno); + if (wc->numspans > 1) { + if (wc->tspans[1]->sync == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno); + } + if (wc->numspans == 4) { + if (wc->tspans[2]->sync == span->spanno) printk("SPAN %d: Tertiary Sync Source\n",span->spanno); + if (wc->tspans[3]->sync == span->spanno) printk("SPAN %d: Quaternary Sync Source\n",span->spanno); + } + +#ifdef APEC_SUPPORT + if (!apec_enable || !wc->apec_enable) + wc->hw_regs->echo_ctrl = 0xe0000000; + else if (!alreadyrunning && !wc->apec) + if (ap4_apec_init(wc)) + ap4_apec_release(wc); +#else + wc->hw_regs->echo_ctrl = 0xe0000000; +#endif + + printk("Completed startup!\n"); + return 0; +} + + +static void ap4_receiveprep(struct ap4 *wc) +{ + volatile unsigned int *readchunk; + unsigned int buffer[32]; + unsigned char *byte = (unsigned char *) buffer; + int i, j, k; + + readchunk = (wc->membase + (AP_DATA_BASE)); + for (i = 0; i < DAHDI_CHUNKSIZE; i++) { + /* Prefetch Card data */ + for (j = 0; j < 32; ++j) { + buffer[j] = readchunk[j]; + } + for (j = 0; j < wc->numspans; j++) { + /* Set first timeslot for first channel */ + if (wc->tspans[j]->spantype == TYPE_E1) { + for (k = 0; k < 31; ++k) { + /* Skip first timeslot from E1 */ + wc->tspans[j]->span.chans[k]->readchunk[i] = + byte[4*(k+1)+j]; + } + } + else { + for (k = 0; k < 24; ++k) { + wc->tspans[j]->span.chans[k]->readchunk[i] = + byte[4*k+j]; + } + } + } + readchunk += 32; + } + + for (i = 0; i < wc->numspans; i++) { + if (wc->tspans[i]->span.flags & DAHDI_FLAG_RUNNING) { + for (j = 0; j < wc->tspans[i]->span.channels; j++) { + /* Echo cancel double buffered data */ + dahdi_ec_chunk(wc->tspans[i]->span.chans[j], + wc->tspans[i]->span.chans[j]->readchunk, + wc->tspans[i]->ec_chunk2[j]); + memcpy(wc->tspans[i]->ec_chunk2[j],wc->tspans[i]->ec_chunk1[j], + DAHDI_CHUNKSIZE); + memcpy(wc->tspans[i]->ec_chunk1[j], + wc->tspans[i]->span.chans[j]->writechunk, + DAHDI_CHUNKSIZE); + } + dahdi_receive(&wc->tspans[i]->span); + } + } +} + +#if (DAHDI_CHUNKSIZE != 8) +#error Sorry, AP400 driver does not support chunksize != 8 +#endif + +#ifdef ENABLE_WORKQUEUES +static void workq_handlespan(void *data) +{ + struct ap4_span *ts = data; + struct ap4 *wc = ts->owner; + +// __receive_span(ts); +// __transmit_span(ts); + atomic_dec(&wc->worklist); + atomic_read(&wc->worklist); + +} +#endif + +static void ap4_transmitprep(struct ap4 *wc) +{ + volatile unsigned int *writechunk; + int x,y,z; + unsigned int tmp; + + for (y=0;ynumspans;y++) { + if (wc->tspans[y]->span.flags & DAHDI_FLAG_RUNNING) + dahdi_transmit(&wc->tspans[y]->span); + } + + writechunk = (wc->membase+(AP_DATA_BASE)); + for (x=0;xnumspans; ++y) { + if (wc->tspans[y]->spantype == TYPE_T1 && z < 24) + tmp |= (wc->tspans[y]->span.chans[z]->writechunk[x] + << (8*y)); + else /* Span Type is E1 */ + if (z > 0) /* Skip first timeslot */ + tmp |= (wc->tspans[y]->span.chans[z-1]->writechunk[x] + << (8*y)); + } + writechunk[z] = tmp; + } + // Advance pointer by 4 TDM frame lengths + writechunk += 32; + } + +} + +static void ap4_tdm_loop(struct ap4 *wc) +{ + volatile unsigned int *buf_ptr; + int x,z; + unsigned int tmp; + + buf_ptr = (wc->membase+AP_DATA_BASE); + + for (x=0;xtspans[span]; + volatile unsigned int *readcas = (wc->membase+AP_CAS_BASE); + +// if (debug & DEBUG_RBS) +// printk("Checking sigbits on span %d\n", span + 1); + + if (!(ts->span.flags & DAHDI_FLAG_RUNNING)) + return; + // se span estiver com alarme RED ou BLUE... + if( (ts->span.alarms & DAHDI_ALARM_RED) || (ts->span.alarms & DAHDI_ALARM_BLUE) ) { + ts->reload_cas = 4; + } else if(ts->reload_cas > 0) { + // da mais um tempo para framer recuperar e enviar bits de CAS validos + ts->reload_cas--; + } + + if (ts->spantype == TYPE_E1) { + for (i = 0; i < 15; i++) { + + // Se estamos em alarme ou recuperando de um entao mascara os bits para "1101" (bloqueado) + if(ts->reload_cas) { + a = 0xdd; + } else { + a = (int) ts->casbuf[i]; + } + ts->casbuf[i] = (unsigned char) (readcas[i] >> (8*span))&0xff; + + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(ts->span.chans[i+16]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[i+16]->rxsig != rxs) { + ap_debugk("CAS no canal %d mudou de 0x%02x para 0x%02x\n", i+16, ts->span.chans[i+16]->rxsig, rxs); + dahdi_rbsbits(ts->span.chans[i+16], rxs); + } + } + rxs = (a >> 4) & 0xf; + if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[i]->rxsig != rxs) { + ap_debugk("CAS no canal %d mudou de 0x%02x para 0x%02x\n", i, ts->span.chans[i]->rxsig, rxs); + dahdi_rbsbits(ts->span.chans[i], rxs); + } + } + } + } else if (ts->span.lineconfig & DAHDI_CONFIG_D4) { + for (i = 0; i < 12; i++) { + a = (unsigned char) (readcas[i] >> (8*span)) & 0xcc; + rxs = a & 0xc; + //rxs = (a & 0xc) >> 2; + if (!(ts->span.chans[2*i]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[2*i]->rxsig != rxs) + dahdi_rbsbits(ts->span.chans[2*i], rxs); + } + rxs = (a >> 4) & 0xc; + //rxs = ((a >> 4) & 0xc) >> 2; + if (!(ts->span.chans[2*i+1]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[2*i+1]->rxsig != rxs) + dahdi_rbsbits(ts->span.chans[2*i+1], rxs); + } + } + } else { // ESF + for (i = 0; i < 12; i++) { + a = (unsigned char) (readcas[i] >> (8*span)) & 0xff; + rxs = (a & 0xf); + if (!(ts->span.chans[2*i+1]->sig & DAHDI_SIG_CLEAR)) { + /* XXX Not really reset on every trans! XXX */ + if (ts->span.chans[2*i+1]->rxsig != rxs) { + dahdi_rbsbits(ts->span.chans[2*i+1], rxs); + } + } + rxs = (a >> 4) & 0xf; + if (!(ts->span.chans[2*i]->sig & DAHDI_SIG_CLEAR)) { + /* XXX Not really reset on every trans! XXX */ + if (ts->span.chans[2*i]->rxsig != rxs) { + dahdi_rbsbits(ts->span.chans[2*i], rxs); + } + } + } + } +} + +static void __ap4_check_alarms(struct ap4 *wc, int span) +{ + unsigned char c; + int alarms; + int x,j; + struct ap4_span *ts = wc->tspans[span]; + unsigned int e1_cfg; + + if (!(ts->span.flags & DAHDI_FLAG_RUNNING)) + return; + + /* Assume no alarms */ + alarms = DAHDI_ALARM_NONE; + + /* And consider only carrier alarms */ + ts->span.alarms &= (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_NOTOPEN); + + if (ts->span.lineconfig & DAHDI_CONFIG_NOTOPEN) { + for (x=0,j=0;x < ts->span.channels;x++) + if ((ts->span.chans[x]->flags & DAHDI_FLAG_OPEN) +#ifdef CONFIG_DAHDI_NET + || + (ts->span.chans[x]->flags & DAHDI_FLAG_NETDEV) +#endif + ) + j++; + if (!j) + alarms |= DAHDI_ALARM_NOTOPEN; + } + +/* le status e configuracao do E1 */ + if (wc->tspans[span]->spantype == TYPE_E1) { + c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span)); + e1_cfg = ((*(wc->membase+AP_E1_CONFIG_REG))>>(8*span)); + + if ((c & AP_E1_LOS_STATUS)||(c & AP_E1_BFAE_STATUS)||(c & AP_E1_AIS_STATUS)) { + if (ts->alarmcount >= alarmdebounce) + alarms |= DAHDI_ALARM_RED; + else + ts->alarmcount++; + } else + ts->alarmcount = 0; + + if ( c & AP_E1_MFAE_STATUS ) + alarms |= DAHDI_ALARM_BLUE; + + if ( (!(c & AP_E1_CAS_STATUS)) && (e1_cfg & AP_E1_PCM30_CONFIG)) + alarms |= DAHDI_ALARM_BLUE; + } else { + c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span)); + if (c & AP_E1_LOS_STATUS) { + if (ts->alarmcount >= alarmdebounce) + alarms |= DAHDI_ALARM_RED; + else + ts->alarmcount++; + } else + ts->alarmcount = 0; + c = wc->hw_regs->t1_status >> (8 * span); + if (!(c & AP4_T1_FRAME_SYNC)) + alarms |= DAHDI_ALARM_RED; + } + + if (((!ts->span.alarms) && alarms) || + (ts->span.alarms && (!alarms))) + wc->checktiming = 1; + + /* Keep track of recovering */ + if ((!alarms) && ts->span.alarms) + ts->alarmtimer = DAHDI_ALARMSETTLE_TIME; + if (ts->alarmtimer) + alarms |= DAHDI_ALARM_RECOVER; + + + // If receiving alarms, go into Yellow alarm state + if (alarms && !(ts->spanflags & FLAG_SENDINGYELLOW)) { + printk("Setting yellow alarm on span %d\n", span + 1); + e1_cfg = *(wc->membase+AP_E1_CONFIG_REG); + e1_cfg |= (AP_E1_RAI_CONFIG<<(8*span)); + *(wc->membase+AP_E1_CONFIG_REG) = e1_cfg; + ts->spanflags |= FLAG_SENDINGYELLOW; + } else if ((!alarms) && (ts->spanflags & FLAG_SENDINGYELLOW)) { + printk("Clearing yellow alarm on span %d\n", span + 1); + e1_cfg = *(wc->membase+AP_E1_CONFIG_REG); + e1_cfg &= ~(AP_E1_RAI_CONFIG<<(8*span)); + *(wc->membase+AP_E1_CONFIG_REG) = e1_cfg; + ts->spanflags &= ~FLAG_SENDINGYELLOW; + } + + // Re-check the timing source when we enter/leave alarm, not withstanding yellow alarm + if (c & AP_E1_RAI_STATUS) + alarms |= DAHDI_ALARM_YELLOW; + + if (ts->span.mainttimer || ts->span.maintstat) + alarms |= DAHDI_ALARM_LOOPBACK; + + ts->span.alarms = alarms; + dahdi_alarm_notify(&ts->span); +} + +static void __ap4_do_counters(struct ap4 *wc) +{ + int span; + + for (span=0;spannumspans;span++) { + struct ap4_span *ts = wc->tspans[span]; + int docheck=0; + if (ts->loopupcnt || ts->loopdowncnt) + docheck++; + if (ts->alarmtimer) { + if (!--ts->alarmtimer) { + docheck++; + ts->span.alarms &= ~(DAHDI_ALARM_RECOVER); + } + } + if (docheck) { + if (!polling) + __ap4_check_alarms(wc, span); + dahdi_alarm_notify(&ts->span); + } + } +} + +static inline void __handle_leds(struct ap4 *wc) +{ + int x, span_status; + #define MAX_BLINKTIMER 0x14 + + for (x=0;xnumspans;x++) { + struct ap4_span *ts = wc->tspans[x]; + /* le status do E1 (para avaliar LOS) */ + span_status = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*x)); + if (ts->span.flags & DAHDI_FLAG_RUNNING) { + if(span_status&AP_E1_LOS_STATUS) { + if (wc->blinktimer[x] >= (altab[wc->alarmpos[x]] /*>> 1*/)) { + __ap4_set_led(wc, x, AP_ON); + } + if (wc->blinktimer[x] >= (MAX_BLINKTIMER-1)) { + __ap4_set_led(wc, x, AP_OFF); + } + wc->blinktimer[x] += 1; + } else if (ts->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE)) { + if (wc->blinktimer[x] >= (altab[wc->alarmpos[x]] /*>> 1*/)) { + __ap4_set_led(wc, x, AP_ON); + } + if (wc->blinktimer[x] >= (MAX_BLINKTIMER-2)) { + __ap4_set_led(wc, x, AP_OFF); + } + wc->blinktimer[x] += 3; + } /*else if (ts->span.alarms & DAHDI_ALARM_YELLOW) { + // Yellow Alarm + __ap4_set_led(wc, x, AP_ON); + } else if (ts->span.mainttimer || ts->span.maintstat) { + + if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { + __ap4_set_led(wc, x, AP_GREEN); + } + if (wc->blinktimer == 0xf) { + __ap4_set_led(wc, x, AP_OFF); + } + + } */else { + /* No Alarm */ + __ap4_set_led(wc, x, AP_ON); + } + } else + __ap4_set_led(wc, x, AP_OFF); + + if (wc->blinktimer[x] > MAX_BLINKTIMER) { + wc->blinktimer[x] = 0; + wc->alarmpos[x]++; + if (wc->alarmpos[x] >= (sizeof(altab) / sizeof(altab[0]))) + wc->alarmpos[x] = 0; + } + + } +} + + +DAHDI_IRQ_HANDLER(ap4_interrupt) +{ + struct ap4 *wc = dev_id; + unsigned long flags; + int x; + static unsigned int val, cfg; + unsigned int cnt_irq_misses; + static unsigned int cnt_tmp; + int ret = 0; + + /* retorna se interrupcao nao foi habilitada ou nao esta ativa */ + cfg = *(wc->membase + AP_INT_CONTROL_REG); + if((cfg & AP_INT_CTL_ENABLE) == 0 || (cfg & AP_INT_CTL_ACTIVE) == 0) { + ret = 0; + goto out; + } + /* se chegamos aqui eh porque a interrupcao esta habilitada + * e esta ativa, ou seja, foi gerada pelo nosso cartao. + * Agora damos o ack da interrupcao */ + val = *(wc->membase + AP_CLEAR_IRQ_REG); + + /* conta interrupcoes perdidas */ + if (wc->flag_1st_irq > 0) { + // nao considera as primeiras passagens pela rotina + cnt_irq_misses = (*(wc->membase+AP_CNT_IRQ_REG)); + // so considera int. para o cartao + if(cnt_irq_misses) { + wc->flag_1st_irq--; + *(wc->membase+AP_CNT_IRQ_REG)=0; + for(x=0;x<(wc->numspans);x++) + wc->tspans[x]->span.irqmisses = 0; + } + // zera erro de CRC + cnt_tmp = (*(wc->membase + AP_CNT_CRC_REG)); + } else { + // neste registro da FPGA temos o numero de interrupcoes que aconteceram + // desde o ultimo reset do contador de interrupcoes. O normal eh ler 1. + cnt_irq_misses = (*(wc->membase+AP_CNT_IRQ_REG)); + // Se for zero significa que a interrupcao nao foi gerada pelo nosso cartao + if(cnt_irq_misses == 0) { + if(debug) printk("Interrupcao gerada mas nao pela FPGA?!\n"); + ret = 0; + goto out; + } + // reseta o contador + *(wc->membase+AP_CNT_IRQ_REG)=0; + for(x=0;x<(wc->numspans);x++) + wc->tspans[x]->span.irqmisses += (cnt_irq_misses-1); + } + + if (!wc->spansstarted) { + /* Not prepped yet! */ + ret = 0; + goto out; + } + + wc->intcount++; + +#ifdef ENABLE_WORKQUEUES + int cpus = num_online_cpus(); + atomic_set(&wc->worklist, wc->numspans); + if (wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING) + ap4_queue_work(wc->workq, &wc->tspans[0]->swork, 0); + else + atomic_dec(&wc->worklist); + if (wc->numspans > 1) { + if (wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING) + ap4_queue_work(wc->workq, &wc->tspans[1]->swork, 1 % cpus); + else + atomic_dec(&wc->worklist); + } + if (wc->numspans == 4) { + if (wc->tspans[2]->span.flags & DAHDI_FLAG_RUNNING) + ap4_queue_work(wc->workq, &wc->tspans[2]->swork, 2 % cpus); + else + atomic_dec(&wc->worklist); + if (wc->tspans[3]->span.flags & DAHDI_FLAG_RUNNING) + ap4_queue_work(wc->workq, &wc->tspans[3]->swork, 3 % cpus); + else + atomic_dec(&wc->worklist); + } +#else + if (tdm_loop == 1) + ap4_tdm_loop(wc); + else { + ap4_receiveprep(wc); + ap4_transmitprep(wc); + } +#endif + + // Estatisticas a cada 128ms + if(!(wc->intcount&0x7f)){ + clock_source = wc->hw_regs->clock_source; + cnt_tmp = (*(wc->membase + AP_CNT_CV_REG)); + for(x=0;x<(wc->numspans);x++) + wc->tspans[x]->span.count.bpv += (cnt_tmp>>(8*x))&0xff; + cnt_tmp = (*(wc->membase + AP_CNT_CRC_REG)); + for(x=0;x<(wc->numspans);x++) + wc->tspans[x]->span.count.crc4 += (cnt_tmp>>(8*x))&0xff; + cnt_tmp = (*(wc->membase + AP_CNT_SLIP_REG)); + for(x=0;x<(wc->numspans);x++) { + if (((cnt_tmp>>(8*x))&0xff) && (!(wc->tspans[x]->span.alarms & DAHDI_ALARM_RED)) ){ + wc->tspans[x]->slipcount++; + if(debug) printk("Slip detected on span %d: slipcount = %d\n", x+1, wc->tspans[x]->slipcount); + } + } + } + + spin_lock_irqsave(&wc->reglock, flags); + + __handle_leds(wc); + + __ap4_do_counters(wc); + + //x = wc->intcount & 15; + x = wc->intcount & 7; + switch(x) { + case 0: + case 1: + case 2: + case 3: + __ap4_check_alarms(wc, x); + break; + case 4: + case 5: + case 6: + case 7: + __ap4_check_sigbits(wc, x - 4); + break; + } + + if (wc->checktiming > 0) + __ap4_set_timing_source_auto(wc); + spin_unlock_irqrestore(&wc->reglock, flags); + /* IRQ was treated */ + ret = 1; +out: +#ifdef AP400_HDLC + /* Call AP400_HDLC_CARD IRQ handler before leave */ + ret |= ap400_intr_handler(irq, wc->hdlc_card); +#endif + + return IRQ_RETVAL(ret); +} + + +static int __devinit ap4_launch(struct ap4 *wc) +{ + int x; + unsigned long flags; + + if (wc->tspans[0]->span.flags & DAHDI_FLAG_REGISTERED) + return 0; + printk("%s: Launching card: %d\n", wc->variety, wc->order); + + /* Setup serial parameters and system interface */ + for (x=0;x<4;x++) { + //ap4_serial_setup(wc, x); + wc->globalconfig = 1; + } + + if (dahdi_register(&wc->tspans[0]->span, 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->tspans[0]->span.name); + return -1; + } + if (wc->numspans > 1) { + if (dahdi_register(&wc->tspans[1]->span, 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->tspans[1]->span.name); + dahdi_unregister(&wc->tspans[0]->span); + return -1; + } + } + if (wc->numspans == 4) { + if (dahdi_register(&wc->tspans[2]->span, 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->tspans[2]->span.name); + dahdi_unregister(&wc->tspans[0]->span); + dahdi_unregister(&wc->tspans[1]->span); + return -1; + } + if (dahdi_register(&wc->tspans[3]->span, 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->tspans[3]->span.name); + dahdi_unregister(&wc->tspans[0]->span); + dahdi_unregister(&wc->tspans[1]->span); + dahdi_unregister(&wc->tspans[2]->span); + return -1; + } + } + wc->checktiming = 1; + spin_lock_irqsave(&wc->reglock, flags); +// __ap4_set_timing_source(wc,4); + spin_unlock_irqrestore(&wc->reglock, flags); +#ifdef ENABLE_TASKLETS + tasklet_init(&wc->ap4_tlet, ap4_tasklet, (unsigned long)wc); +#endif + return 0; +} + + +static int ap4xx_liu_reset(struct ap4 *wc) +{ + unsigned int jiffies_hold = jiffies; + *(wc->membase+AP_LEDS_REG) |= AP_LIU_RESET_BIT; + while(jiffies<=(jiffies_hold+2)); + *(wc->membase+AP_LEDS_REG) &= ~AP_LIU_RESET_BIT; + return 0; +} + + +static int ap4xx_bus_test(struct ap4 *wc) +{ + int tst_result = 0; + unsigned int val; + + *(wc->membase+AP_E1_CONFIG_REG) = 0xAAAAAAAA; + *wc->membase = 0; // flush + val = *(wc->membase+AP_E1_CONFIG_REG); + if(val != 0xAAAAAAAA) { + printk("Escrito 0xAAAAAAAA, lido 0x%08X!\n", val); + tst_result++; + } + *(wc->membase+AP_E1_CONFIG_REG) = 0x55555555; + *wc->membase = 0; // flush + val = *(wc->membase+AP_E1_CONFIG_REG); + if(val != 0x55555555) { + printk("Escrito 0x55555555, lido 0x%08X!\n", val); + tst_result++; + } + *(wc->membase+AP_E1_CONFIG_REG) = 0xFFFFFFFF; + *wc->membase = 0; // flush + val = *(wc->membase+AP_E1_CONFIG_REG); + if(val != 0xFFFFFFFF) { + printk("Escrito 0xFFFFFFFF, lido 0x%08X!\n", val); + tst_result++; + } + *(wc->membase+AP_E1_CONFIG_REG) = 0x00000000; + *wc->membase = 0xFFFFFFFF; // flush + val = *(wc->membase+AP_E1_CONFIG_REG); + if(val != 0x00000000) { + printk("Escrito 0x00000000, lido 0x%08X!\n", val); + tst_result++; + } + return tst_result; +} + +#ifdef TIMER_DEBUG +void ap4xx_opt_timeout(unsigned long arg) +{ + struct pci_dev *dev = (struct pci_dev *)arg; + struct ap4 *wc = pci_get_drvdata(dev); + +// ap_debugk("wc->tspans[0]->span.chans[1].readchunk[1] = 0x%02x\n", wc->tspans[0]->span.chans[0].readchunk[1]); +// ap_debugk("e1s_cfg = 0x%08x\n", *(wc->membase+AP_E1_CONFIG_REG)); +// ap_debugk("e1_status = 0x%08x\n", *(wc->membase + AP_E1_STATUS_REG)); +// ap_debugk("clk_cfg = 0x%08x\n", *(wc->membase+0x07)); +// ap_debugk("e1_data = 0x%08x\n", *(wc->membase + (AP_DATA_BASE + 1))); +// ap_debugk("cas_data = 0x%08x\n", *(wc->membase + AP_CAS_BASE)); + + // dispara timer novamente + init_timer(&ap4xx_opt_timer); + ap4xx_opt_timer.function = ap4xx_opt_timeout; + ap4xx_opt_timer.data = arg; + ap4xx_opt_timer.expires = jiffies + (delay/4); + add_timer(&ap4xx_opt_timer); + +} +#endif + +static inline int ap4_card_detect (struct ap4 *wc) { + int i; + if ((wc->hw_regs->card_id != AP4XX_CARD_ID) && + (wc->hw_regs->card_id != APE4XX_CARD_ID)) { + printk("AP400: Unknown card ID(0x%08X)! Aborting...\n", wc->hw_regs->card_id); + return -EPERM; + } + // Test bus integrity + for (i=0; i < 1000; i++) { + if (ap4xx_bus_test(wc)) { + printk("AP400: Bus integrity test failed! Aborting...\n"); + return -EIO; + } + } + printk("AP400: Bus integrity OK!\n"); + + wc->fpgaver = wc->hw_regs->fpga_ver; + wc->numspans = wc->hw_regs->span_num; + wc->hwid = ((*(wc->membase+AP_HWCONFIG_REG))&AP_HWID_MASK)>>4; + + if ((wc->hwid == AP_HWID_1E1_RJ && wc->numspans != 1) || + (wc->hwid == AP_HWID_2E1_RJ && wc->numspans != 2) || + (wc->hwid == AP_HWID_4E1_RJ && wc->numspans != 4)) { + printk("AP400: Incompatible Hardware ID(0x%02x)! Aborting...\n", wc->hwid); + return -EIO; + } + + if (wc->hw_regs->card_id == AP4XX_CARD_ID) + switch (wc->numspans) { + case 1: + wc->dt = (struct devtype *) &ap401; + break; + case 2: + wc->dt = (struct devtype *) &ap402; + break; + case 4: + wc->dt = (struct devtype *) &ap404; + break; + default: + printk("AP400: Unsupported spans number(%d)! Aborting...\n", + wc->numspans); + return -EPERM; + } + else + switch (wc->numspans) { + case 1: + wc->dt = (struct devtype *) &ape401; + break; + case 2: + wc->dt = (struct devtype *) &ape402; + break; + case 4: + wc->dt = (struct devtype *) &ape404; + break; + default: + printk("APE400: Unsupported spans number(%d)! Aborting...\n", + wc->numspans); + return -EPERM; + } + + wc->variety = wc->dt->desc; + printk("Found a %s (firmware version %d.%d) at base address %08lx, remapped to %p\n", + wc->variety, wc->fpgaver >> 8, wc->fpgaver & 0xFF, + wc->memaddr, wc->membase); + + return 0; +} + +static void __devexit ap4_remove_one(struct pci_dev *pdev); + +static int __devinit ap4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct ap4 *wc; + int x,f; + int basesize; + static int initd_ifaces=0; + // Initialize pointer struct + if(!initd_ifaces){ + memset((void *)cards,0,(sizeof(struct ap4 *))*MAX_AP4_CARDS); + initd_ifaces=1; + } + + if ((res = pci_enable_device(pdev)) != 0) { + goto out; + } + // Allocate card struct + wc = kmalloc(sizeof(struct ap4), GFP_KERNEL); + if (wc == NULL) { + res = -ENOMEM; + goto out; + } + + memset(wc, 0x0, sizeof(struct ap4)); + spin_lock_init(&wc->reglock); + + basesize = DAHDI_MAX_CHUNKSIZE * 32 * 2 * 4; + + // Request PCI regions + if ((res = pci_request_regions(pdev, "ap400")) != 0) { + printk("AP400: Unable to request regions!\n"); + goto out; + } + + // Remap PCI address + wc->memaddr = pci_resource_start(pdev, 2); + wc->memlen = pci_resource_len(pdev, 2); + wc->membase = ioremap_nocache(wc->memaddr, wc->memlen); + if(wc->membase == NULL) { + printk("AP400: ioremap failed!\n"); + res = -EIO; + goto out; + } + wc->hw_regs = (struct ap4_regs *) wc->membase; + + // Detect Card model + if ((res = ap4_card_detect(wc)) != 0) + goto out; + + ap4xx_liu_reset(wc); + + // This rids of the Double missed interrupt message after loading + wc->last0 = 1; + + wc->dev = pdev; + + // 32 channels, Double-buffer, Read/Write, 4 spans + wc->writechunk = kmalloc(basesize * 2, GFP_KERNEL); + if (!wc->writechunk) { + printk("%s: Unable to allocate memory!\n", wc->variety); + res = -ENOMEM; + goto out; + } + + // Read is after the whole write piece (in words) + wc->readchunk = wc->writechunk + basesize / 4; + + + // Initialize Write/Buffers to all blank data + memset((void *) wc->writechunk, 0x00, basesize); + memset((void *) wc->readchunk, 0xff, basesize); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + /* inicializa contador de interrupcao */ + wc->intcount = 0; + + for(x = 0; x < MAX_AP4_CARDS; x++) { + if (!cards[x]) break; + } + + if (x >= MAX_AP4_CARDS) { + printk("No cards[] slot available!!\n"); + res = -ENOMEM; + goto out; + } + + wc->num = x; + cards[x] = wc; + + /* Allocate pieces we need here, consider 31 channels for E1*/ + for (x=0;x<4;x++) { + wc->tspans[x] = kmalloc(sizeof(struct ap4_span), GFP_KERNEL); + if (wc->tspans[x]) { + memset(wc->tspans[x], 0, sizeof(struct ap4_span)); + wc->tspans[x]->spantype = TYPE_E1; + } else { + res = -ENOMEM; + goto out; + } + for (f = 0; f < 31; f++) { + if (!(wc->tspans[x]->chans[f] = kmalloc(sizeof(*wc->tspans[x]->chans[f]), GFP_KERNEL))) { + res = -ENOMEM; + goto out; + } + memset(wc->tspans[x]->chans[f], 0, sizeof(*wc->tspans[x]->chans[f])); + } +#ifdef ENABLE_WORKQUEUES + INIT_WORK(&wc->tspans[x]->swork, workq_handlespan, wc->tspans[x]); +#endif + wc->tspans[x]->spanflags |= wc->dt->flags; + } + + if (request_irq(pdev->irq, ap4_interrupt, IRQF_DISABLED | IRQF_SHARED, "ap400", wc)) + { + printk("%s: Unable to request IRQ %d\n", wc->variety, pdev->irq); + res = -EIO; + goto out; + } + + init_spans(wc); + + /* Launch cards as appropriate */ + x = 0; + for(;;) { + /* Find a card to activate */ + f = 0; + for (x=0;cards[x];x++) { + if (cards[x]->order <= highestorder) { + ap4_launch(cards[x]); + if (cards[x]->order == highestorder) + f = 1; + } + } + /* If we found at least one, increment the highest order and search again, otherwise stop */ + if (f) + highestorder++; + else + break; + } + +#ifdef APEC_SUPPORT + if (wc->fpgaver >= 0x0400) + wc->apec_enable = 1; +#endif + +#ifdef TIMER_DEBUG + // dispara timer de debug + init_timer(&ap4xx_opt_timer); + ap4xx_opt_timer.function = ap4xx_opt_timeout; + ap4xx_opt_timer.data = (unsigned long) pdev; + ap4xx_opt_timer.expires = jiffies + 100; + add_timer(&ap4xx_opt_timer); +#endif + + /* Initialize HDLC_CARD */ +#ifdef AP400_HDLC + u8 __iomem *base_addr[3]; + unsigned int bar_size[3]; + int i; + base_addr[2] = (void *) wc->membase; + bar_size[2] = wc->memlen; + for (i = 0; i < 2; i++) { + bar_size[i] = (u32) pci_resource_len(pdev, i); + base_addr[i] = ioremap_nocache(pci_resource_start(pdev, i), + bar_size[i]); + if (base_addr[i] == NULL) { + printk(KERN_ERR "Memory map failed\n"); + res = -ENODEV; + goto out; + } + } + ap400_card_init(&wc->hdlc_card, base_addr, bar_size); + ap400_intr_enable(wc->hdlc_card); +#endif + + res = 0; +out: + if (res != 0) { + ap4_remove_one(pdev); + } + return res; +} + +static void __devexit ap4_remove_one(struct pci_dev *pdev) +{ + struct ap4 *wc = pci_get_drvdata(pdev); + int x; + + if (wc) { + ap_debugk("desabilita interrupcao!\n"); + // desabilita interrupcao + *(wc->membase + AP_INT_CONTROL_REG) &= ~AP_INT_CTL_ENABLE; + +#ifdef APEC_SUPPORT + // Stop echo cancellation module + ap4_apec_release(wc); +#endif + /* Unregister spans */ + if (wc->tspans[0]->span.flags & DAHDI_FLAG_REGISTERED) + dahdi_unregister(&wc->tspans[0]->span); + if (wc->numspans > 1) { + if (wc->tspans[1]->span.flags & DAHDI_FLAG_REGISTERED) + dahdi_unregister(&wc->tspans[1]->span); + } + if (wc->numspans == 4) { + if (wc->tspans[2]->span.flags & DAHDI_FLAG_REGISTERED) + dahdi_unregister(&wc->tspans[2]->span); + if (wc->tspans[3]->span.flags & DAHDI_FLAG_REGISTERED) + dahdi_unregister(&wc->tspans[3]->span); + } +#ifdef ENABLE_WORKQUEUES + if (wc->workq) { + flush_workqueue(wc->workq); + destroy_workqueue(wc->workq); + } +#endif + +#ifdef TIMER_DEBUG + del_timer(&ap4xx_opt_timer); +#endif + + wc->hw_regs = NULL; + if(wc->membase) + iounmap((void *)wc->membase); + + /* Immediately free resources */ + kfree((void *) wc->writechunk); + +#ifdef AP400_HDLC + /* Remove HDLC Card */ + ap400_card_remove(wc->hdlc_card); + if (wc->hdlc_card->cfg_base_addr) + iounmap(wc->hdlc_card->cfg_base_addr); + if (wc->hdlc_card->buf_base_addr) + iounmap(wc->hdlc_card->buf_base_addr); + kfree(wc->hdlc_card); +#endif + free_irq(pdev->irq, wc); + + cards[wc->num] = NULL; + for (x=0;xnumspans;x++) { + if (wc->tspans[x]) + kfree(wc->tspans[x]); + } + kfree(wc); + } + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + printk(KERN_INFO "AP400 driver removed\n"); +} + + +static struct pci_device_id ap4_pci_tbl[] __devinitdata = +{ + { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_AP4XX), }, + { 0, } +}; + + +static struct pci_driver ap4_driver = { + .name = "Unified ap4xx driver", + .probe = ap4_init_one, +#ifdef LINUX26 + .remove = __devexit_p(ap4_remove_one), +#else + .remove = ap4_remove_one, +#endif + .id_table = ap4_pci_tbl, +}; + +static int __init ap4_init(void) +{ + int res; + printk("Unified AP4XX PCI Card Driver\n"); + res = dahdi_pci_module(&ap4_driver); + if (res) { + return -ENODEV; + } + return 0; +} + +static void __exit ap4_cleanup(void) +{ + printk("Unified AP4XX PCI Card Driver Cleanup\n"); + pci_unregister_driver(&ap4_driver); +} + + +MODULE_AUTHOR("Aligera (aligera@aligera.com.br)"); +MODULE_DESCRIPTION("Unified AP4XX PCI Card Driver"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +module_param(debug, int, 0600); +module_param(loopback, int, 0600); +module_param(noburst, int, 0600); +module_param(debugslips, int, 0600); +module_param(polling, int, 0600); +module_param(timingcable, int, 0600); +module_param(t1e1override, int, 0600); +module_param(alarmdebounce, int, 0600); +module_param(j1mode, int, 0600); + +MODULE_DEVICE_TABLE(pci, ap4_pci_tbl); + +module_init(ap4_init); +module_exit(ap4_cleanup); diff --git a/drivers/dahdi/ap400/apec.c b/drivers/dahdi/ap400/apec.c new file mode 100644 index 0000000..b43655e --- /dev/null +++ b/drivers/dahdi/ap400/apec.c @@ -0,0 +1,390 @@ +/* + * AP400 Echo Cancelation Hardware support + * + * Written by Wagner Gegler + * + * Based on previous work written by Mark Spencer + * + * Copyright (C) 2005-2006 Digium, Inc. + * + * Mark Spencer + * + * 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 +#include +#include +#include +#include +#include + +#include "apec.h" +#include "oct6100api/oct6100_api.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +#include +#else +#include +#endif + +/* API for Octasic access */ +UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime) +{ + /* Why couldn't they just take a timeval like everyone else? */ + struct timeval tv; + unsigned long long total_usecs; + unsigned int mask = ~0; + + do_gettimeofday(&tv); + total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + + (((unsigned long long)(tv.tv_usec))); + f_pTime->aulWallTimeUs[0] = (total_usecs & mask); + f_pTime->aulWallTimeUs[1] = (total_usecs >> 32); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength) +{ + memset(f_pAddress, f_ulPattern, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength) +{ + memcpy(f_pDestination, f_pSource, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate) +{ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy) +{ +#ifdef OCTASIC_DEBUG + printk("I should never be called! (destroy serialize object)\n"); +#endif + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams) +{ + oct_write(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams) +{ + unsigned int x; + for (x=0;xulWriteLength;x++) { + oct_write(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams) +{ + unsigned int x; + for (x=0;xulWriteLength;x++) { + oct_write(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams) +{ + *(f_pReadParams->pusReadData) = oct_read(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams) +{ + unsigned int x; + for (x=0;xulReadLength;x++) { + f_pBurstParams->pusReadData[x] = oct_read(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1)); + } + return cOCT6100_ERR_OK; +} + +#if 0 +#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE +#else +#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN +#endif + +struct apec_s { + tPOCT6100_INSTANCE_API pApiInstance; + UINT32 aulEchoChanHndl[128]; + int chanflags[128]; + int ecmode[128]; + int numchans; +}; + +#define FLAG_DTMF (1 << 0) +#define FLAG_MUTE (1 << 1) +#define FLAG_ECHO (1 << 2) + +static void apec_setecmode(struct apec_s *apec, int channel, int mode) +{ + tOCT6100_CHANNEL_MODIFY *modify; + UINT32 ulResult; + + if (apec->ecmode[channel] == mode) + return; + modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC); + if (!modify) { + printk("APEC: Unable to allocate memory for setec!\n"); + return; + } + Oct6100ChannelModifyDef(modify); + modify->ulEchoOperationMode = mode; + modify->ulChannelHndl = apec->aulEchoChanHndl[channel]; + ulResult = Oct6100ChannelModify(apec->pApiInstance, modify); + if (ulResult != GENERIC_OK) { + printk("Failed to apply echo can changes on channel %d!\n", channel); + } else { +#ifdef OCTASIC_DEBUG + printk("Echo can on channel %d set to %d\n", channel, mode); +#endif + apec->ecmode[channel] = mode; + } + kfree(modify); +} + +void apec_setec(struct apec_s *apec, int channel, int eclen) +{ + if (eclen) { + apec->chanflags[channel] |= FLAG_ECHO; + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_NORMAL); + } else { + apec->chanflags[channel] &= ~FLAG_ECHO; + if (apec->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) { + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); + } else + apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); + } + printk("APEC: Setting EC on channel %d to %d\n", channel, eclen); +} + +int apec_checkirq(struct apec_s *apec) +{ + tOCT6100_INTERRUPT_FLAGS InterruptFlags; + + Oct6100InterruptServiceRoutineDef(&InterruptFlags); + Oct6100InterruptServiceRoutine(apec->pApiInstance, &InterruptFlags); + + return InterruptFlags.fToneEventsPending ? 1 : 0; +} + +unsigned int apec_capacity_get(void *wc) +{ + UINT32 ulResult; + + tOCT6100_API_GET_CAPACITY_PINS CapacityPins; + + Oct6100ApiGetCapacityPinsDef(&CapacityPins); + CapacityPins.pProcessContext = wc; + CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR; + CapacityPins.fEnableMemClkOut = TRUE; + CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; + + ulResult = Oct6100ApiGetCapacityPins(&CapacityPins); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to get chip capacity, code %08x!\n", ulResult); + return 0; + } + return CapacityPins.ulCapacityValue; +} + +struct apec_s *apec_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware) +{ + tOCT6100_CHIP_OPEN *ChipOpen; + tOCT6100_GET_INSTANCE_SIZE InstanceSize; + tOCT6100_CHANNEL_OPEN *ChannelOpen; + UINT32 ulResult; + struct apec_s *apec; + int x, law; +#ifdef CONFIG_4KSTACKS + unsigned long flags; +#endif + + if (!(apec = kmalloc(sizeof(struct apec_s), GFP_KERNEL))) + return NULL; + + memset(apec, 0, sizeof(struct apec_s)); + + if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) { + kfree(apec); + return NULL; + } + + memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN)); + + if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) { + kfree(apec); + kfree(ChipOpen); + return NULL; + } + + memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN)); + + for (x=0;x<128;x++) + apec->ecmode[x] = -1; + + apec->numchans = numspans * 32; + printk("APEC: echo cancellation for %d channels\n", apec->numchans); + + Oct6100ChipOpenDef(ChipOpen); + + /* Setup Chip Open Parameters */ + ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ; + Oct6100GetInstanceSizeDef(&InstanceSize); + + ChipOpen->pProcessContext = wc; + + ChipOpen->pbyImageFile = firmware->data; + ChipOpen->ulImageSize = firmware->size; + + ChipOpen->fEnableMemClkOut = TRUE; + ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; + ChipOpen->ulMaxChannels = apec->numchans; + ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR; + ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB; + ChipOpen->ulNumMemoryChips = 1; + ChipOpen->ulMaxTdmStreams = 4; + ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ; + ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE; +#if 0 + ChipOpen->fEnableAcousticEcho = TRUE; +#endif + + ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to get instance size, code %08x!\n", ulResult); + kfree(apec); + return NULL; + } + + + apec->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize); + if (!apec->pApiInstance) { + printk("Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize); + kfree(apec); + kfree(ChipOpen); + kfree(ChannelOpen); + return NULL; + } + + /* I don't know what to curse more in this comment, the problems caused by + * the 4K kernel stack limit change or the octasic API for being so darn + * stack unfriendly. Stupid, stupid, stupid. So we disable IRQs so we + * don't run the risk of overflowing the stack while we initialize the + * octasic. */ +#ifdef CONFIG_4KSTACKS + local_irq_save(flags); +#endif + ulResult = Oct6100ChipOpen(apec->pApiInstance, ChipOpen); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to open chip, code %08x!\n", ulResult); +#ifdef CONFIG_4KSTACKS + local_irq_restore(flags); +#endif + kfree(apec); + kfree(ChipOpen); + kfree(ChannelOpen); + return NULL; + } + for (x=0; x < 128; x++) { + /* execute this loop always on 4 span cards but + * on 2 span cards only execute for the channels related to our spans */ + if ((x & 0x3) < numspans) { + /* span timeslots are interleaved 12341234... + * therefore, the lower 2 bits tell us which span this + * timeslot/channel + */ + if (isalaw[x & 0x03]) + law = cOCT6100_PCM_A_LAW; + else + law = cOCT6100_PCM_U_LAW; + Oct6100ChannelOpenDef(ChannelOpen); + ChannelOpen->pulChannelHndl = &apec->aulEchoChanHndl[x]; + ChannelOpen->ulUserChanId = x; + ChannelOpen->TdmConfig.ulRinPcmLaw = law; + ChannelOpen->TdmConfig.ulRinStream = 0; + ChannelOpen->TdmConfig.ulRinTimeslot = x; + ChannelOpen->TdmConfig.ulSinPcmLaw = law; + ChannelOpen->TdmConfig.ulSinStream = 1; + ChannelOpen->TdmConfig.ulSinTimeslot = x; + ChannelOpen->TdmConfig.ulSoutPcmLaw = law; + ChannelOpen->TdmConfig.ulSoutStream = 2; + ChannelOpen->TdmConfig.ulSoutTimeslot = x; + ChannelOpen->TdmConfig.ulRoutPcmLaw = law; + ChannelOpen->TdmConfig.ulRoutStream = 3; + ChannelOpen->TdmConfig.ulRoutTimeslot = x; + ChannelOpen->VqeConfig.fEnableNlp = TRUE; + ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE; + ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE; + + ChannelOpen->fEnableToneDisabler = TRUE; + ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL; + + ulResult = Oct6100ChannelOpen(apec->pApiInstance, ChannelOpen); + if (ulResult != GENERIC_OK) { + printk("Failed to open channel %d!\n", x); + } + } + } + +#ifdef CONFIG_4KSTACKS + local_irq_restore(flags); +#endif + kfree(ChipOpen); + kfree(ChannelOpen); + return apec; +} + +void apec_release(struct apec_s *apec) +{ + UINT32 ulResult; + tOCT6100_CHIP_CLOSE ChipClose; + + Oct6100ChipCloseDef(&ChipClose); + ulResult = Oct6100ChipClose(apec->pApiInstance, &ChipClose); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to close chip, code %08x!\n", ulResult); + } + vfree(apec->pApiInstance); + kfree(apec); + printk(KERN_INFO "APEC: Releasing...\n"); +} diff --git a/drivers/dahdi/ap400/apec.h b/drivers/dahdi/ap400/apec.h new file mode 100644 index 0000000..483b182 --- /dev/null +++ b/drivers/dahdi/ap400/apec.h @@ -0,0 +1,48 @@ +/* + * AP400 Echo Cancelation Hardware support + * + * Written by Wagner Gegler + * + * Based on previous work written by Mark Spencer + * + * Copyright (C) 2005-2006 Digium, Inc. + * + * Mark Spencer + * + * 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. + * + */ + +#ifndef _APEC_H_ +#define _APEC_H_ + +#include + +struct apec_s; + +/* From AP400 */ +unsigned int oct_read(void *card, unsigned int addr); +void oct_write(void *card, unsigned int addr, unsigned int data); + +/* From APEC */ +struct apec_s *apec_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware); +unsigned int apec_capacity_get(void *wc); +void apec_setec(struct apec_s *instance, int channel, int eclen); +int apec_checkirq(struct apec_s *apec); +void apec_release(struct apec_s *instance); + +#endif /*_APEC_H_*/ diff --git a/drivers/dahdi/opvxa1200/Kbuild b/drivers/dahdi/opvxa1200/Kbuild new file mode 100644 index 0000000..8f90819 --- /dev/null +++ b/drivers/dahdi/opvxa1200/Kbuild @@ -0,0 +1,19 @@ +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200) += opvxa1200.o + +EXTRA_CFLAGS += -I$(src)/.. -Wno-undef + +opvxa1200-objs := base.o + +DAHDI_KERNEL_H_NAME:=kernel.h +DAHDI_KERNEL_H_PATH:=$(DAHDI_INCLUDE)/dahdi/$(DAHDI_KERNEL_H_NAME) +ifneq ($(DAHDI_KERNEL_H_PATH),) + DAHDI_SPAN_MODULE:=$(shell if grep -C 5 "struct dahdi_span {" $(DAHDI_KERNEL_H_PATH) | grep -q "struct module \*owner"; then echo "yes"; else echo "no"; fi) + DAHDI_SPAN_OPS:=$(shell if grep -q "struct dahdi_span_ops {" $(DAHDI_KERNEL_H_PATH); then echo "yes"; else echo "no"; fi) + ifeq ($(DAHDI_SPAN_MODULE),yes) + EXTRA_CFLAGS+=-DDAHDI_SPAN_MODULE + else + ifeq ($(DAHDI_SPAN_OPS),yes) + EXTRA_CFLAGS+=-DDAHDI_SPAN_OPS + endif + endif +endif diff --git a/drivers/dahdi/opvxa1200/Makefile b/drivers/dahdi/opvxa1200/Makefile new file mode 100644 index 0000000..baaab35 --- /dev/null +++ b/drivers/dahdi/opvxa1200/Makefile @@ -0,0 +1,8 @@ +ifdef KBUILD_EXTMOD +# We only get here on kernels 2.6.0-2.6.9 . +# For newer kernels, Kbuild will be included directly by the kernel +# build system. +include $(src)/Kbuild + +else +endif diff --git a/drivers/dahdi/opvxa1200/base.c b/drivers/dahdi/opvxa1200/base.c new file mode 100644 index 0000000..32da0e0 --- /dev/null +++ b/drivers/dahdi/opvxa1200/base.c @@ -0,0 +1,3049 @@ +/* + * OpenVox A1200P FXS/FXO Interface Driver for DAHDI Telephony interface + * + * Written by MiaoLin + * + * Copyright (C) 2005-2010 OpenVox Communication Co. Ltd, + * + * 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. + * + */ + +/* Rev histroy + * + * Rev 0.10 initial version + * Rev 0.11 + * fixed the led light on/off bug. + * modify some wctdm print to opvxa1200 + * support firmware version 1.2, faster i/o operation, and better LED control. + * + * Rev 0.12 patched to support new pci id 0x8519 + * Rev 0.13 patched to remove the warning during compile under kernel 2.6.22 + * Rev 0.14 patched to remove the bug for ZAP_IRQ_SHARED , 3/9/2007 + * Rev 0.15 patched to support new pci ID 0X9532 by james.zhu, 23/10/2007 + * Rev 0.16 support new pci id 0x9559 by Miao Lin 21/3/2008 + * Rev 0.17 + * patched a few bugs, + * add hwgain support. + * fixed A800P version check + * Rev 1.4.9.2 + * Only generate 8 channels for A800P + * Version number synced to zaptel distribution. + * Rev 1.4.9.2.a + * Fixed freeregion. + * + * Rev 1.4.9.2.b + * Add cid before first ring support. + * New Paremeters: + * cidbeforering : set to 1 will cause the card enable cidbeforering function. default 0 + * cidbuflen : length of cid buffer, in msec, default 3000 msec. + * cidtimeout : time out of a ring, default 6000msec + * User must set cidstart=polarity in zapata.conf to use with this feature + * cidsignalling = signalling format send before 1st ring. most likely dtmf. + * + * Rev 1.4.9.2.c + * add driver parameter cidtimeout. + * + * Rev 1.4.9.2.d + * add debug stuff to test fxs power alarm + * + * Rev 1.4.11 + * Support enhanced full scale tx/rx for FXO required by europe standard (Register 30, acim) (module parm fxofullscale) + * + * Rev 1.4.12 2008/10/17 + * Fixed bug cause FXS module report fake power alarm. + * Power alarm debug stuff removed. + * + * Rev 2.0 DAHDI 2008/10/17 + * + * Rev 2.0.1 add new pci id 0x9599 + * Re 2.0.2 12/01/2009 + add fixedtimepolarity: set time(ms) when send polarity after 1st ring happen. + * Sometimes the dtmf cid is sent just after first ring off, and the system do not have + * enough time to start detect 1st dtmf. + * 0 means send polarity at the end of 1st ring. + * x means send ploarity after x ms of 1st ring begin. + * + * Rev 2.0.3 12/01/2009 + * Add touch_softlockup_watchdog() in wctdm_hardware_init, to avoid cpu softlockup system message for FXS. + * + * + * Rev 1.4.12.4 17/04/2009 James.zhu + * Changed wctdm_voicedaa_check_hook() to detect FXO battery and solved the problem with dial(dahdi/go/XXXXXXXXXX) + * add alarm detection for FXO + * + * Rev 1.4.12.5 01/10/2009 james.zhu + * Add jiffies for 5 second in wctdm_hardware_init + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proslic.h" + +/* MiaoLin debug start */ +#include +#include /* get_fs(), set_fs(), KERNEL_DS */ +#include /* fput() */ +/* MiaoLin debug end */ + + +/* + * Define for audio vs. register based ring detection + * + */ +/* #define AUDIO_RINGCHECK */ + +/* + Experimental max loop current limit for the proslic + Loop current limit is from 20 mA to 41 mA in steps of 3 + (according to datasheet) + So set the value below to: + 0x00 : 20mA (default) + 0x01 : 23mA + 0x02 : 26mA + 0x03 : 29mA + 0x04 : 32mA + 0x05 : 35mA + 0x06 : 37mA + 0x07 : 41mA +*/ +static int loopcurrent = 20; + +static int reversepolarity = 0; + +static alpha indirect_regs[] = +{ +{0,255,"DTMF_ROW_0_PEAK",0x55C2}, +{1,255,"DTMF_ROW_1_PEAK",0x51E6}, +{2,255,"DTMF_ROW2_PEAK",0x4B85}, +{3,255,"DTMF_ROW3_PEAK",0x4937}, +{4,255,"DTMF_COL1_PEAK",0x3333}, +{5,255,"DTMF_FWD_TWIST",0x0202}, +{6,255,"DTMF_RVS_TWIST",0x0202}, +{7,255,"DTMF_ROW_RATIO_TRES",0x0198}, +{8,255,"DTMF_COL_RATIO_TRES",0x0198}, +{9,255,"DTMF_ROW_2ND_ARM",0x0611}, +{10,255,"DTMF_COL_2ND_ARM",0x0202}, +{11,255,"DTMF_PWR_MIN_TRES",0x00E5}, +{12,255,"DTMF_OT_LIM_TRES",0x0A1C}, +{13,0,"OSC1_COEF",0x7B30}, +{14,1,"OSC1X",0x0063}, +{15,2,"OSC1Y",0x0000}, +{16,3,"OSC2_COEF",0x7870}, +{17,4,"OSC2X",0x007D}, +{18,5,"OSC2Y",0x0000}, +{19,6,"RING_V_OFF",0x0000}, +{20,7,"RING_OSC",0x7EF0}, +{21,8,"RING_X",0x0160}, +{22,9,"RING_Y",0x0000}, +{23,255,"PULSE_ENVEL",0x2000}, +{24,255,"PULSE_X",0x2000}, +{25,255,"PULSE_Y",0x0000}, +//{26,13,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower +{26,13,"RECV_DIGITAL_GAIN",0x2000}, // playback volume set lower +{27,14,"XMIT_DIGITAL_GAIN",0x4000}, +//{27,14,"XMIT_DIGITAL_GAIN",0x2000}, +{28,15,"LOOP_CLOSE_TRES",0x1000}, +{29,16,"RING_TRIP_TRES",0x3600}, +{30,17,"COMMON_MIN_TRES",0x1000}, +{31,18,"COMMON_MAX_TRES",0x0200}, +{32,19,"PWR_ALARM_Q1Q2",0x07C0}, +{33,20,"PWR_ALARM_Q3Q4",0x2600}, +{34,21,"PWR_ALARM_Q5Q6",0x1B80}, +{35,22,"LOOP_CLOSURE_FILTER",0x8000}, +{36,23,"RING_TRIP_FILTER",0x0320}, +{37,24,"TERM_LP_POLE_Q1Q2",0x008C}, +{38,25,"TERM_LP_POLE_Q3Q4",0x0100}, +{39,26,"TERM_LP_POLE_Q5Q6",0x0010}, +{40,27,"CM_BIAS_RINGING",0x0C00}, +{41,64,"DCDC_MIN_V",0x0C00}, +{42,255,"DCDC_XTRA",0x1000}, +{43,66,"LOOP_CLOSE_TRES_LOW",0x1000}, +}; + + +#include +#include + +#include "fxo_modes.h" + +#define NUM_FXO_REGS 60 + +#define WC_MAX_IFACES 128 + +#define WC_OFFSET 4 /* Offset between transmit and receive, in bytes. */ +#define WC_SYNCFLAG 0xca1ef1ac + +#define WC_CNTL 0x00 +#define WC_OPER 0x01 +#define WC_AUXC 0x02 +#define WC_AUXD 0x03 +#define WC_MASK0 0x04 +#define WC_MASK1 0x05 +#define WC_INTSTAT 0x06 +#define WC_AUXR 0x07 + +#define WC_DMAWS 0x08 +#define WC_DMAWI 0x0c +#define WC_DMAWE 0x10 +#define WC_DMARS 0x18 +#define WC_DMARI 0x1c +#define WC_DMARE 0x20 + +#define WC_AUXFUNC 0x2b +#define WC_SERCTL 0x2d +#define WC_FSCDELAY 0x2f + +#define WC_REGBASE 0xc0 + +#define WC_VER 0x0 +#define WC_CS 0x1 +#define WC_SPICTRL 0x2 +#define WC_SPIDATA 0x3 + +#define BIT_SPI_BYHW (1 << 0) +#define BIT_SPI_BUSY (1 << 1) // 0=can read/write spi, 1=spi working. +#define BIT_SPI_START (1 << 2) + + +#define BIT_LED_CLK (1 << 0) // MiaoLin add to control the led. +#define BIT_LED_DATA (1 << 1) // MiaoLin add to control the led. + +#define BIT_CS (1 << 2) +#define BIT_SCLK (1 << 3) +#define BIT_SDI (1 << 4) +#define BIT_SDO (1 << 5) + +#define FLAG_EMPTY 0 +#define FLAG_WRITE 1 +#define FLAG_READ 2 +#define DEFAULT_RING_DEBOUNCE 64 /* Ringer Debounce (64 ms) */ +#define POLARITY_DEBOUNCE 64 /* Polarity debounce (64 ms) */ +#define OHT_TIMER 6000 /* How long after RING to retain OHT */ + +#define FLAG_3215 (1 << 0) +#define FLAG_A800 (1 << 7) + +#define MAX_NUM_CARDS 12 +#define NUM_CARDS 12 +#define NUM_FLAG 4 /* number of flag channels. */ + + +enum cid_hook_state { + CID_STATE_IDLE = 0, + CID_STATE_RING_ON, + CID_STATE_RING_OFF, + CID_STATE_WAIT_RING_FINISH +}; + +/* if you want to record the last 8 sec voice before the driver unload, uncomment it and rebuild. */ +/* #define TEST_LOG_INCOME_VOICE */ +#define voc_buffer_size (8000*8) + + +#define MAX_ALARMS 10 + +#define MOD_TYPE_FXS 0 +#define MOD_TYPE_FXO 1 + +#define MINPEGTIME 10 * 8 /* 30 ms peak to peak gets us no more than 100 Hz */ +#define PEGTIME 50 * 8 /* 50ms peak to peak gets us rings of 10 Hz or more */ +#define PEGCOUNT 5 /* 5 cycles of pegging means RING */ + +#define NUM_CAL_REGS 12 + +struct calregs { + unsigned char vals[NUM_CAL_REGS]; +}; + +enum proslic_power_warn { + PROSLIC_POWER_UNKNOWN = 0, + PROSLIC_POWER_ON, + PROSLIC_POWER_WARNED, +}; + +enum battery_state { + BATTERY_UNKNOWN = 0, + BATTERY_PRESENT, + BATTERY_LOST, +}; +struct wctdm { + struct pci_dev *dev; + char *variety; + struct dahdi_span span; + unsigned char ios; + int usecount; + unsigned int intcount; + int dead; + int pos; + int flags[MAX_NUM_CARDS]; + int freeregion; + int alt; + int curcard; + int cardflag; /* Bit-map of present cards */ + enum proslic_power_warn proslic_power; + spinlock_t lock; + + union { + struct fxo { +#ifdef AUDIO_RINGCHECK + unsigned int pegtimer; + int pegcount; + int peg; + int ring; +#else + int wasringing; + int lastrdtx; +#endif + int ringdebounce; + int offhook; + unsigned int battdebounce; + unsigned int battalarm; + enum battery_state battery; + int lastpol; + int polarity; + int polaritydebounce; + } fxo; + struct fxs { + int oldrxhook; + int debouncehook; + int lastrxhook; + int debounce; + int ohttimer; + int idletxhookstate; /* IDLE changing hook state */ + int lasttxhook; + int palarms; + struct calregs calregs; + } fxs; + } mod[MAX_NUM_CARDS]; + + /* Receive hook state and debouncing */ + int modtype[MAX_NUM_CARDS]; + unsigned char reg0shadow[MAX_NUM_CARDS]; + unsigned char reg1shadow[MAX_NUM_CARDS]; + + unsigned long ioaddr; + unsigned long mem_region; /* 32 bit Region allocated to tiger320 */ + unsigned long mem_len; /* Length of 32 bit region */ + volatile unsigned long mem32; /* Virtual representation of 32 bit memory area */ + + dma_addr_t readdma; + dma_addr_t writedma; + volatile unsigned char *writechunk; /* Double-word aligned write memory */ + volatile unsigned char *readchunk; /* Double-word aligned read memory */ + /*struct dahdi_chan chans[MAX_NUM_CARDS];*/ + struct dahdi_chan _chans[NUM_CARDS]; + struct dahdi_chan *chans[NUM_CARDS]; + + +#ifdef TEST_LOG_INCOME_VOICE + char * voc_buf[MAX_NUM_CARDS + NUM_FLAG]; + int voc_ptr[MAX_NUM_CARDS + NUM_FLAG]; +#endif + int lastchan; + unsigned short ledstate; + unsigned char fwversion; + int max_cards; + char *card_name; + + char *cid_history_buf[MAX_NUM_CARDS]; + int cid_history_ptr[MAX_NUM_CARDS]; + int cid_history_clone_cnt[MAX_NUM_CARDS]; + enum cid_hook_state cid_state[MAX_NUM_CARDS]; + int cid_ring_on_time[MAX_NUM_CARDS]; +}; + +static char* A1200P_Name = "A1200P"; +static char* A800P_Name = "A800P"; + +struct wctdm_desc { + char *name; + int flags; +}; + +static struct wctdm_desc wctdme = { "OpenVox A1200P/A800P", 0 }; +static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 }; + +static struct wctdm *ifaces[WC_MAX_IFACES]; + +static void wctdm_release(struct wctdm *wc); + +static unsigned int battdebounce; +static unsigned int battalarm; +static unsigned int battthresh; +static int ringdebounce = DEFAULT_RING_DEBOUNCE; +/* times 4, because must be a multiple of 4ms: */ +static int dialdebounce = 8 * 8; +static int fwringdetect = 0; +static int debug = 0; +static int robust = 0; +static int timingonly = 0; +static int lowpower = 0; +static int boostringer = 0; +static int fastringer = 0; +static int _opermode = 0; +static char *opermode = "FCC"; +static int fxshonormode = 0; +static int alawoverride = 0; +static int fastpickup = 0; +static int fxotxgain = 0; +static int fxorxgain = 0; +static int fxstxgain = 0; +static int fxsrxgain = 0; +/* special h/w control command */ +static int spibyhw = 1; +static int usememio = 1; +static int cidbeforering = 0; +static int cidbuflen = 3000; /* in msec, default 3000 */ +static int cidtimeout = 6*1000; /* in msec, default 6000 */ +static int fxofullscale = 0; /* fxo full scale tx/rx, register 30, acim */ +static int fixedtimepolarity=0; /* time delay in ms when send polarity after rise edge of 1st ring.*/ + +static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane); + +static void wctdm_set_led(struct wctdm* wc, int card, int onoff) +{ + int i; + unsigned char c; + + wc->ledstate &= ~(0x01<ledstate |= (onoff<ioaddr + WC_AUXD)&~BIT_LED_CLK)|BIT_LED_DATA; + outb( c, wc->ioaddr + WC_AUXD); + for(i=MAX_NUM_CARDS-1; i>=0; i--) + { + if(wc->ledstate & (0x0001<fwversion == 0x11) + c &= ~BIT_LED_DATA; + else + c |= BIT_LED_DATA; + else + if(wc->fwversion == 0x11) + c |= BIT_LED_DATA; + else + c &= ~BIT_LED_DATA; + + outb( c, wc->ioaddr + WC_AUXD); + outb( c|BIT_LED_CLK, wc->ioaddr + WC_AUXD); + outb( (c&~BIT_LED_CLK)|BIT_LED_DATA, wc->ioaddr + WC_AUXD); + } +} + + +static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char ints) +{ + int x, y, chan_offset, pos; + volatile unsigned char *txbuf; + + if (ints & /*0x01*/ 0x04) + /* Write is at interrupt address. Start writing from normal offset */ + txbuf = wc->writechunk; + else + txbuf = wc->writechunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); + + /* Calculate Transmission */ + dahdi_transmit(&wc->span); + + if(wc->lastchan == -1) // not in sync. + return; + + chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); + + for (y=0;ymax_cards/*MAX_NUM_CARDS*/) + txbuf[pos] = wc->chans[x]->writechunk[y]; + else + txbuf[pos] = 0; + } +#endif + } +} + + +#ifdef AUDIO_RINGCHECK +static inline void ring_check(struct wctdm *wc, int card) +{ + int x; + short sample; + if (wc->modtype[card] != MOD_TYPE_FXO) + return; + wc->mod[card].fxo.pegtimer += DAHDI_CHUNKSIZE; + for (x=0;xchans[card].readchunk[x], (&(wc->chans[card]))); + if ((sample > 10000) && (wc->mod[card].fxo.peg != 1)) { + if (debug > 1) printk(KERN_DEBUG "High peg!\n"); + if ((wc->mod[card].fxo.pegtimer < PEGTIME) && (wc->mod[card].fxo.pegtimer > MINPEGTIME)) + wc->mod[card].fxo.pegcount++; + wc->mod[card].fxo.pegtimer = 0; + wc->mod[card].fxo.peg = 1; + } else if ((sample < -10000) && (wc->mod[card].fxo.peg != -1)) { + if (debug > 1) printk(KERN_DEBUG "Low peg!\n"); + if ((wc->mod[card].fxo.pegtimer < (PEGTIME >> 2)) && (wc->mod[card].fxo.pegtimer > (MINPEGTIME >> 2))) + wc->mod[card].fxo.pegcount++; + wc->mod[card].fxo.pegtimer = 0; + wc->mod[card].fxo.peg = -1; + } + } + if (wc->mod[card].fxo.pegtimer > PEGTIME) { + /* Reset pegcount if our timer expires */ + wc->mod[card].fxo.pegcount = 0; + } + /* Decrement debouncer if appropriate */ + if (wc->mod[card].fxo.ringdebounce) + wc->mod[card].fxo.ringdebounce--; + if (!wc->mod[card].fxo.offhook && !wc->mod[card].fxo.ringdebounce) { + if (!wc->mod[card].fxo.ring && (wc->mod[card].fxo.pegcount > PEGCOUNT)) { + /* It's ringing */ + if (debug) + printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); + if (!wc->mod[card].fxo.offhook) + dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_RING); + wc->mod[card].fxo.ring = 1; + } + if (wc->mod[card].fxo.ring && !wc->mod[card].fxo.pegcount) { + /* No more ring */ + if (debug) + printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); + dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); + wc->mod[card].fxo.ring = 0; + } + } +} +#endif + + +static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char ints) +{ + volatile unsigned char *rxbuf; + int x, y, chan_offset; + + + if (ints & 0x08/*0x04*/) + /* Read is at interrupt address. Valid data is available at normal offset */ + rxbuf = wc->readchunk; + else + rxbuf = wc->readchunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); + + for(x=0; x<4; x++) + if( *(int*)(rxbuf+x*4) == WC_SYNCFLAG) + break; + if(x==4) + { + printk("buffer sync misseed!\n"); + wc->lastchan = -1; + return; + } + else if(wc->lastchan != x) + { + printk("buffer re-sync occur from %d to %d\n", wc->lastchan, x); + wc->lastchan = x; + } + chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); + + for (x=0;xmax_cards/*MAX_NUM_CARDS*/;y++) { + if (wc->cardflag & (1 << y)) + wc->chans[y]->readchunk[x] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset ) & 0x0f)]; +#ifdef TEST_LOG_INCOME_VOICE + wc->voc_buf[y][wc->voc_ptr[y]] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset) & 0x0f)]; + wc->voc_ptr[y]++; + if(wc->voc_ptr[y] >= voc_buffer_size) + wc->voc_ptr[y] = 0; +#endif + } +#endif + } + + if(cidbeforering) + { + for(x=0; xmax_cards; x++) + { + if (wc->modtype[wc->chans[x]->chanpos - 1] == MOD_TYPE_FXO) + if(wc->mod[wc->chans[x]->chanpos - 1].fxo.offhook == 0) + { + /*unsigned int *p_readchunk, *p_cid_history; + + p_readchunk = (unsigned int*)wc->chans[x].readchunk; + p_cid_history = (unsigned int*)(wc->cid_history_buf[x] + wc->cid_history_ptr[x]);*/ + + if(wc->cid_state[x] == CID_STATE_IDLE) /* we need copy data to the cid voice buffer */ + { + memcpy(wc->cid_history_buf[x] + wc->cid_history_ptr[x], wc->chans[x]->readchunk, DAHDI_CHUNKSIZE); + wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); + } + else if (wc->cid_state[x] == CID_STATE_RING_ON) + wc->cid_history_clone_cnt[x] = cidbuflen; + else if (wc->cid_state[x] == CID_STATE_RING_OFF) + { + if(wc->cid_history_clone_cnt[x]) + { + memcpy(wc->chans[x]->readchunk, wc->cid_history_buf[x] + wc->cid_history_ptr[x], DAHDI_MAX_CHUNKSIZE); + wc->cid_history_clone_cnt[x]--; + wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_MAX_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); + } + else + { + wc->cid_state[x] = CID_STATE_WAIT_RING_FINISH; + wc->cid_history_clone_cnt[x] = cidtimeout; /* wait 6 sec, if no ring, return to idle */ + } + } + else if(wc->cid_state[x] == CID_STATE_WAIT_RING_FINISH) + { + if(wc->cid_history_clone_cnt[x] > 0) + wc->cid_history_clone_cnt[x]--; + else + { + wc->cid_state[x] = CID_STATE_IDLE; + wc->cid_history_ptr[x] = 0; + wc->cid_history_clone_cnt[x] = 0; + } + } + } + } + } + +#ifdef AUDIO_RINGCHECK + for (x=0;xmax_cards;x++) + ring_check(wc, x); +#endif + /* XXX We're wasting 8 taps. We should get closer :( */ + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + if (wc->cardflag & (1 << x)) + dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); + } + dahdi_receive(&wc->span); +} + +static void wctdm_stop_dma(struct wctdm *wc); +static void wctdm_reset_tdm(struct wctdm *wc); +static void wctdm_restart_dma(struct wctdm *wc); + + +static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg); +static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val); + + +static inline void __write_8bits(struct wctdm *wc, unsigned char bits) +{ + if(spibyhw == 0) + { + int x; + /* Drop chip select */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + /* Send out each bit, MSB first, drop SCLK as we do so */ + if (bits & 0x80) + wc->ios |= BIT_SDI; + else + wc->ios &= ~BIT_SDI; + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + bits <<= 1; + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + else + { + __wctdm_setcreg(wc, WC_SPIDATA, bits); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); + while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); + } +} + + +static inline void __reset_spi(struct wctdm *wc) +{ + __wctdm_setcreg(wc, WC_SPICTRL, 0); + + /* Drop chip select and clock once and raise and clock once */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios |= BIT_SDI; + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Clock again */ + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + + __wctdm_setcreg(wc, WC_SPICTRL, spibyhw); + +} + +static inline unsigned char __read_8bits(struct wctdm *wc) +{ + unsigned char res=0, c; + int x; + if(spibyhw == 0) + { + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Drop chip select */ + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + res <<= 1; + /* Get SCLK */ + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Read back the value */ + c = inb(wc->ioaddr + WC_AUXR); + if (c & BIT_SDO) + res |= 1; + /* Now raise SCLK high again */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + else + { + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); + while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); + res = __wctdm_getcreg(wc, WC_SPIDATA); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); + } + + /* And return our result */ + return res; +} + +static void __wctdm_setcreg_mem(struct wctdm *wc, unsigned char reg, unsigned char val) +{ + unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); + *p = val; +} + +static unsigned char __wctdm_getcreg_mem(struct wctdm *wc, unsigned char reg) +{ + unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); + return (*p)&0x00ff; +} + + +static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val) +{ + if(usememio) + __wctdm_setcreg_mem(wc, reg, val); + else + outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); +} + +static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg) +{ + if(usememio) + return __wctdm_getcreg_mem(wc, reg); + else + return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); +} + +static inline void __wctdm_setcard(struct wctdm *wc, int card) +{ + if (wc->curcard != card) { + __wctdm_setcreg(wc, WC_CS, card); + wc->curcard = card; + //printk("Select card %d\n", card); + } +} + +static void __wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) +{ + __wctdm_setcard(wc, card); + if (wc->modtype[card] == MOD_TYPE_FXO) { + __write_8bits(wc, 0x20); + __write_8bits(wc, reg & 0x7f); + } else { + __write_8bits(wc, reg & 0x7f); + } + __write_8bits(wc, value); +} + +static void wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->lock, flags); + __wctdm_setreg(wc, card, reg, value); + spin_unlock_irqrestore(&wc->lock, flags); +} + +static unsigned char __wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) +{ + __wctdm_setcard(wc, card); + if (wc->modtype[card] == MOD_TYPE_FXO) { + __write_8bits(wc, 0x60); + __write_8bits(wc, reg & 0x7f); + } else { + __write_8bits(wc, reg | 0x80); + } + return __read_8bits(wc); +} + +static inline void reset_spi(struct wctdm *wc, int card) +{ + unsigned long flags; + spin_lock_irqsave(&wc->lock, flags); + __wctdm_setcard(wc, card); + __reset_spi(wc); + __reset_spi(wc); + spin_unlock_irqrestore(&wc->lock, flags); +} + +static unsigned char wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) +{ + unsigned long flags; + unsigned char res; + spin_lock_irqsave(&wc->lock, flags); + res = __wctdm_getreg(wc, card, reg); + spin_unlock_irqrestore(&wc->lock, flags); + return res; +} + +static int __wait_access(struct wctdm *wc, int card) +{ + unsigned char data = 0; + long origjiffies; + int count = 0; + + #define MAX 6000 /* attempts */ + + + origjiffies = jiffies; + /* Wait for indirect access */ + while (count++ < MAX) + { + data = __wctdm_getreg(wc, card, I_STATUS); + + if (!data) + return 0; + + } + + if(count > (MAX-1)) printk(KERN_NOTICE " ##### Loop error (%02x) #####\n", data); + + return 0; +} + +static unsigned char translate_3215(unsigned char address) +{ + int x; + for (x=0;xflags[card] & FLAG_3215) { + address = translate_3215(address); + if (address == 255) + return 0; + } + spin_lock_irqsave(&wc->lock, flags); + if(!__wait_access(wc, card)) { + __wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF)); + __wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); + __wctdm_setreg(wc, card, IAA,address); + res = 0; + }; + spin_unlock_irqrestore(&wc->lock, flags); + return res; +} + +static int wctdm_proslic_getreg_indirect(struct wctdm *wc, int card, unsigned char address) +{ + unsigned long flags; + int res = -1; + char *p=NULL; + /* Translate 3215 addresses */ + if (wc->flags[card] & FLAG_3215) { + address = translate_3215(address); + if (address == 255) + return 0; + } + spin_lock_irqsave(&wc->lock, flags); + if (!__wait_access(wc, card)) { + __wctdm_setreg(wc, card, IAA, address); + if (!__wait_access(wc, card)) { + unsigned char data1, data2; + data1 = __wctdm_getreg(wc, card, IDA_LO); + data2 = __wctdm_getreg(wc, card, IDA_HI); + res = data1 | (data2 << 8); + } else + p = "Failed to wait inside\n"; + } else + p = "failed to wait\n"; + spin_unlock_irqrestore(&wc->lock, flags); + if (p) + printk(KERN_NOTICE "%s", p); + return res; +} + +static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) +{ + unsigned char i; + + for (i=0; iflags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) + { + printk(KERN_NOTICE "!!!!!!! %s iREG %X = %X should be %X\n", + indirect_regs[i].name,indirect_regs[i].address,j,initial ); + passed = 0; + } + } + + if (passed) { + if (debug) + printk(KERN_DEBUG "Init Indirect Registers completed successfully.\n"); + } else { + printk(KERN_NOTICE " !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); + return -1; + } + return 0; +} + +static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) +{ + int res; + /* Check loopback */ + res = wc->reg1shadow[card]; + + if (!res && (res != wc->mod[card].fxs.lasttxhook)) // read real state from register By wx + res=wctdm_getreg(wc, card, 64); + + if (!res && (res != wc->mod[card].fxs.lasttxhook)) { + res = wctdm_getreg(wc, card, 8); + if (res) { + printk(KERN_NOTICE "Ouch, part reset, quickly restoring reality (%d)\n", card); + wctdm_init_proslic(wc, card, 1, 0, 1); + } else { + if (wc->mod[card].fxs.palarms++ < MAX_ALARMS) { + printk(KERN_NOTICE "Power alarm on module %d, resetting!\n", card + 1); + if (wc->mod[card].fxs.lasttxhook == 4) + wc->mod[card].fxs.lasttxhook = 1; + wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook); + } else { + if (wc->mod[card].fxs.palarms == MAX_ALARMS) + printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); + } + } + } +} +static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) +{ +#define MS_PER_CHECK_HOOK 16 + +#ifndef AUDIO_RINGCHECK + unsigned char res; +#endif + signed char b; + int errors = 0; + struct fxo *fxo = &wc->mod[card].fxo; + + /* Try to track issues that plague slot one FXO's */ + b = wc->reg0shadow[card]; + if ((b & 0x2) || !(b & 0x8)) { + /* Not good -- don't look at anything else */ + if (debug) + printk(KERN_DEBUG "Error (%02x) on card %d!\n", b, card + 1); + errors++; + } + b &= 0x9b; + if (fxo->offhook) { + if (b != 0x9) + wctdm_setreg(wc, card, 5, 0x9); + } else { + if (b != 0x8) + wctdm_setreg(wc, card, 5, 0x8); + } + if (errors) + return; + if (!fxo->offhook) { + if(fixedtimepolarity) { + if ( wc->cid_state[card] == CID_STATE_RING_ON && wc->cid_ring_on_time[card]>0) + { + if(wc->cid_ring_on_time[card]>=fixedtimepolarity ) + { + dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + wc->cid_ring_on_time[card] = -1; /* the polarity already sent */ + } + else + wc->cid_ring_on_time[card] += 16; + } +} + if (fwringdetect) { + res = wc->reg0shadow[card] & 0x60; + if (fxo->ringdebounce) { + --fxo->ringdebounce; + if (res && (res != fxo->lastrdtx) && + (fxo->battery == BATTERY_PRESENT)) { + if (!fxo->wasringing) { + fxo->wasringing = 1; + if (debug) + printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); + if(cidbeforering) + { + if(wc->cid_state[card] == CID_STATE_IDLE) + { + wc->cid_state[card] = CID_STATE_RING_ON; + wc->cid_ring_on_time[card] = 16; /* check every 16ms */ + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + } + fxo->lastrdtx = res; + fxo->ringdebounce = 10; + } else if (!res) { + if ((fxo->ringdebounce == 0) && fxo->wasringing) { + fxo->wasringing = 0; + if (debug) + printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); + if(cidbeforering) + { + if(wc->cid_state[card] == CID_STATE_RING_ON) + { + if(fixedtimepolarity==0) + dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + wc->cid_state[card] = CID_STATE_RING_OFF; + } + else + { + if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) + wc->cid_history_clone_cnt[card] = cidtimeout; + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + } + } + else + + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + } + } + } else if (res && (fxo->battery == BATTERY_PRESENT)) { + fxo->lastrdtx = res; + fxo->ringdebounce = 10; + } + } else { + res = wc->reg0shadow[card]; + if ((res & 0x60) && (fxo->battery == BATTERY_PRESENT)) { + fxo->ringdebounce += (DAHDI_CHUNKSIZE * 16); + if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) { + if (!fxo->wasringing) { + fxo->wasringing = 1; + if(cidbeforering) + { + if(wc->cid_state[card] == CID_STATE_IDLE) + { + wc->cid_state[card] = CID_STATE_RING_ON; + wc->cid_ring_on_time[card] = 16; /* check every 16ms */ + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + if (debug) + printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); + } + fxo->ringdebounce = DAHDI_CHUNKSIZE * ringdebounce; + } + } else { + fxo->ringdebounce -= DAHDI_CHUNKSIZE * 4; + if (fxo->ringdebounce <= 0) { + if (fxo->wasringing) { + fxo->wasringing = 0; + if(cidbeforering) + { + if(wc->cid_state[card] == CID_STATE_RING_ON) + { + if(fixedtimepolarity==0) + dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + wc->cid_state[card] = CID_STATE_RING_OFF; + } + else + { + if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) + wc->cid_history_clone_cnt[card] = cidtimeout; + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + } + } + else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + if (debug) + printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); + } + fxo->ringdebounce = 0; + } + } + } + } + + b = wc->reg1shadow[card]; + if (abs(b) < battthresh) { + /* possible existing states: + battery lost, no debounce timer + battery lost, debounce timer (going to battery present) + battery present or unknown, no debounce timer + battery present or unknown, debounce timer (going to battery lost) + */ + + if (fxo->battery == BATTERY_LOST) { + if (fxo->battdebounce) { + /* we were going to BATTERY_PRESENT, but battery was lost again, + so clear the debounce timer */ + fxo->battdebounce = 0; + } + } else { + if (fxo->battdebounce) { + /* going to BATTERY_LOST, see if we are there yet */ + if (--fxo->battdebounce == 0) { + fxo->battery = BATTERY_LOST; + if (debug) + printk(KERN_DEBUG "NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); +#ifdef JAPAN + if (!wc->ohdebounce && wc->offhook) { + dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_ONHOOK); + if (debug) + printk(KERN_DEBUG "Signalled On Hook\n"); +#ifdef ZERO_BATT_RING + wc->onhook++; +#endif + } +#else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + /* set the alarm timer, taking into account that part of its time + period has already passed while debouncing occurred */ + fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; +#endif + } + } else { + /* start the debounce timer to verify that battery has been lost */ + fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; + } + } + } else { + /* possible existing states: + battery lost or unknown, no debounce timer + battery lost or unknown, debounce timer (going to battery present) + battery present, no debounce timer + battery present, debounce timer (going to battery lost) + */ + + if (fxo->battery == BATTERY_PRESENT) { + if (fxo->battdebounce) { + /* we were going to BATTERY_LOST, but battery appeared again, + so clear the debounce timer */ + fxo->battdebounce = 0; + } + } else { + if (fxo->battdebounce) { + /* going to BATTERY_PRESENT, see if we are there yet */ + if (--fxo->battdebounce == 0) { + fxo->battery = BATTERY_PRESENT; + if (debug) + printk(KERN_DEBUG "BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, + (b < 0) ? "-" : "+"); +#ifdef ZERO_BATT_RING + if (wc->onhook) { + wc->onhook = 0; + dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); + if (debug) + printk(KERN_DEBUG "Signalled Off Hook\n"); + } +#else + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); +#endif + /* set the alarm timer, taking into account that part of its time + period has already passed while debouncing occurred */ + fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; + } + } else { + /* start the debounce timer to verify that battery has appeared */ + fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; + } + } + } + + if (fxo->lastpol >= 0) { + if (b < 0) { + fxo->lastpol = -1; + fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; + } + } + if (fxo->lastpol <= 0) { + if (b > 0) { + fxo->lastpol = 1; + fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; + } + } + + if (fxo->battalarm) { + if (--fxo->battalarm == 0) { + /* the alarm timer has expired, so update the battery alarm state + for this channel */ + dahdi_alarm_channel(wc->chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); + } + } + + if (fxo->polaritydebounce) { + if (--fxo->polaritydebounce == 0) { + if (fxo->lastpol != fxo->polarity) { + if (debug) + printk(KERN_DEBUG "%lu Polarity reversed (%d -> %d)\n", jiffies, + fxo->polarity, + fxo->lastpol); + if (fxo->polarity) + dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + fxo->polarity = fxo->lastpol; + } + } + } +#undef MS_PER_CHECK_HOOK +} + +static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) +{ + char res; + int hook; + + /* For some reason we have to debounce the + hook detector. */ + + res = wc->reg0shadow[card]; + hook = (res & 1); + if (hook != wc->mod[card].fxs.lastrxhook) { + /* Reset the debounce (must be multiple of 4ms) */ + wc->mod[card].fxs.debounce = dialdebounce * 4; + +#if 0 + printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce); +#endif + } else { + if (wc->mod[card].fxs.debounce > 0) { + wc->mod[card].fxs.debounce-= 16 * DAHDI_CHUNKSIZE; +#if 0 + printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce); +#endif + if (!wc->mod[card].fxs.debounce) { +#if 0 + printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook); +#endif + wc->mod[card].fxs.debouncehook = hook; + } + if (!wc->mod[card].fxs.oldrxhook && wc->mod[card].fxs.debouncehook) { + /* Off hook */ +#if 1 + if (debug) +#endif + printk(KERN_DEBUG "opvxa1200: Card %d Going off hook\n", card); + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + if (robust) + wctdm_init_proslic(wc, card, 1, 0, 1); + wc->mod[card].fxs.oldrxhook = 1; + + } else if (wc->mod[card].fxs.oldrxhook && !wc->mod[card].fxs.debouncehook) { + /* On hook */ +#if 1 + if (debug) +#endif + printk(KERN_DEBUG "opvxa1200: Card %d Going on hook\n", card); + dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + wc->mod[card].fxs.oldrxhook = 0; + } + } + } + wc->mod[card].fxs.lastrxhook = hook; +} + +DAHDI_IRQ_HANDLER(wctdm_interrupt) +{ + struct wctdm *wc = dev_id; + unsigned char ints; + int x, y, z; + int mode; + + ints = inb(wc->ioaddr + WC_INTSTAT); + + if (!ints) + return IRQ_NONE; + + outb(ints, wc->ioaddr + WC_INTSTAT); + + if (ints & 0x10) { + /* Stop DMA, wait for watchdog */ + printk(KERN_INFO "TDM PCI Master abort\n"); + wctdm_stop_dma(wc); + return IRQ_RETVAL(1); + } + + if (ints & 0x20) { + printk(KERN_INFO "PCI Target abort\n"); + return IRQ_RETVAL(1); + } + + for (x=0;xmax_cards/*4*3*/;x++) { + if (wc->cardflag & (1 << x) && + (wc->modtype[x] == MOD_TYPE_FXS)) { + if (wc->mod[x].fxs.lasttxhook == 0x4) { + /* RINGing, prepare for OHT */ + wc->mod[x].fxs.ohttimer = OHT_TIMER << 3; + if (reversepolarity) + wc->mod[x].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[x].fxs.idletxhookstate = 0x2; + } else { + if (wc->mod[x].fxs.ohttimer) { + wc->mod[x].fxs.ohttimer-= DAHDI_CHUNKSIZE; + if (!wc->mod[x].fxs.ohttimer) { + if (reversepolarity) + wc->mod[x].fxs.idletxhookstate = 0x5; /* Switch to active */ + else + wc->mod[x].fxs.idletxhookstate = 0x1; + if ((wc->mod[x].fxs.lasttxhook == 0x2) || (wc->mod[x].fxs.lasttxhook == 0x6)) { + /* Apply the change if appropriate */ + if (reversepolarity) + wc->mod[x].fxs.lasttxhook = 0x5; + else + wc->mod[x].fxs.lasttxhook = 0x1; + wctdm_setreg(wc, x, 64, wc->mod[x].fxs.lasttxhook); + } + } + } + } + } + } + + if (ints & 0x0f) { + wc->intcount++; + z = wc->intcount & 0x3; + mode = wc->intcount & 0xc; + for(y=0; ymax_cards/4/*3*/; y++) + { + x = z + y*4; + if (wc->cardflag & (1 << x ) ) + { + switch(mode) + { + case 0: + /* Rest */ + break; + case 4: + /* Read first shadow reg */ + if (wc->modtype[x] == MOD_TYPE_FXS) + wc->reg0shadow[x] = wctdm_getreg(wc, x, 68); + else if (wc->modtype[x] == MOD_TYPE_FXO) + wc->reg0shadow[x] = wctdm_getreg(wc, x, 5); + break; + case 8: + /* Read second shadow reg */ + if (wc->modtype[x] == MOD_TYPE_FXS) + wc->reg1shadow[x] = wctdm_getreg(wc, x, 64); + else if (wc->modtype[x] == MOD_TYPE_FXO) + wc->reg1shadow[x] = wctdm_getreg(wc, x, 29); + break; + case 12: + /* Perform processing */ + if (wc->modtype[x] == MOD_TYPE_FXS) { + wctdm_proslic_check_hook(wc, x); + if (!(wc->intcount & 0xf0)) + wctdm_proslic_recheck_sanity(wc, x); + } else if (wc->modtype[x] == MOD_TYPE_FXO) { + wctdm_voicedaa_check_hook(wc, x); + } + break; + } + } + } + if (!(wc->intcount % 10000)) { + /* Accept an alarm once per 10 seconds */ + for (x=0;xmax_cards/*4*3*/;x++) + if (wc->modtype[x] == MOD_TYPE_FXS) { + if (wc->mod[x].fxs.palarms) + wc->mod[x].fxs.palarms--; + } + } + wctdm_receiveprep(wc, ints); + wctdm_transmitprep(wc, ints); + } + + return IRQ_RETVAL(1); + +} + +static int wctdm_voicedaa_insane(struct wctdm *wc, int card) +{ + int blah; + blah = wctdm_getreg(wc, card, 2); + if (blah != 0x3) + return -2; + blah = wctdm_getreg(wc, card, 11); + if (debug) + printk(KERN_DEBUG "VoiceDAA System: %02x\n", blah & 0xf); + return 0; +} + +static int wctdm_proslic_insane(struct wctdm *wc, int card) +{ + int blah,insane_report; + insane_report=0; + + blah = wctdm_getreg(wc, card, 0); + if (debug) + printk(KERN_DEBUG "ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); + +#if 0 + if ((blah & 0x30) >> 4) { + printk(KERN_DEBUG "ProSLIC on module %d is not a 3210.\n", card); + return -1; + } +#endif + if (((blah & 0xf) == 0) || ((blah & 0xf) == 0xf)) { + /* SLIC not loaded */ + return -1; + } + if ((blah & 0xf) < 2) { + printk(KERN_NOTICE "ProSLIC 3210 version %d is too old\n", blah & 0xf); + return -1; + } + if (wctdm_getreg(wc, card, 1) & 0x80) + /* ProSLIC 3215, not a 3210 */ + wc->flags[card] |= FLAG_3215; + + blah = wctdm_getreg(wc, card, 8); + if (blah != 0x2) { + printk(KERN_NOTICE "ProSLIC on module %d insane (1) %d should be 2\n", card, blah); + return -1; + } else if ( insane_report) + printk(KERN_NOTICE "ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah); + + blah = wctdm_getreg(wc, card, 64); + if (blah != 0x0) { + printk(KERN_NOTICE "ProSLIC on module %d insane (2)\n", card); + return -1; + } else if ( insane_report) + printk(KERN_NOTICE "ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah); + + blah = wctdm_getreg(wc, card, 11); + if (blah != 0x33) { + printk(KERN_NOTICE "ProSLIC on module %d insane (3)\n", card); + return -1; + } else if ( insane_report) + printk(KERN_NOTICE "ProSLIC on module %d Reg 11 Reads %d Expected is 0x33\n",card,blah); + + /* Just be sure it's setup right. */ + wctdm_setreg(wc, card, 30, 0); + + if (debug) + printk(KERN_DEBUG "ProSLIC on module %d seems sane.\n", card); + return 0; +} + +static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card) +{ + unsigned long origjiffies; + unsigned char vbat; + + /* Turn off linefeed */ + wctdm_setreg(wc, card, 64, 0); + + /* Power down */ + wctdm_setreg(wc, card, 14, 0x10); + + /* Wait for one second */ + origjiffies = jiffies; + + while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { + if ((jiffies - origjiffies) >= (HZ/2)) + break; + } + + if (vbat < 0x06) { + printk(KERN_NOTICE "Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card, + 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ)); + return -1; + } else if (debug) { + printk(KERN_NOTICE "Post-leakage voltage: %d volts\n", 376 * vbat / 1000); + } + return 0; +} + +static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) +{ + unsigned char vbat; + unsigned long origjiffies; + int lim; + + /* Set period of DC-DC converter to 1/64 khz */ + wctdm_setreg(wc, card, 92, 0xff /* was 0xff */); + + /* Wait for VBat to powerup */ + origjiffies = jiffies; + + /* Disable powerdown */ + wctdm_setreg(wc, card, 14, 0); + + /* If fast, don't bother checking anymore */ + if (fast) + return 0; + + while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { + /* Wait no more than 500ms */ + if ((jiffies - origjiffies) > HZ/2) { + break; + } + } + + if (vbat < 0xc0) { + if (wc->proslic_power == PROSLIC_POWER_UNKNOWN) + printk(KERN_NOTICE "ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE A1200P??\n", + card, (int)(((jiffies - origjiffies) * 1000 / HZ)), + vbat * 375); + wc->proslic_power = PROSLIC_POWER_WARNED; + return -1; + } else if (debug) { + printk(KERN_DEBUG "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", + card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); + } + wc->proslic_power = PROSLIC_POWER_ON; + + /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */ + /* If out of range, just set it to the default value */ + lim = (loopcurrent - 20) / 3; + if ( loopcurrent > 41 ) { + lim = 0; + if (debug) + printk(KERN_DEBUG "Loop current out of range! Setting to default 20mA!\n"); + } + else if (debug) + printk(KERN_DEBUG "Loop current set to %dmA!\n",(lim*3)+20); + wctdm_setreg(wc,card,LOOP_I_LIMIT,lim); + + /* Engage DC-DC converter */ + wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */); +#if 0 + origjiffies = jiffies; + while(0x80 & wctdm_getreg(wc, card, 93)) { + if ((jiffies - origjiffies) > 2 * HZ) { + printk(KERN_DEBUG "Timeout waiting for DC-DC calibration on module %d\n", card); + return -1; + } + } + +#if 0 + /* Wait a full two seconds */ + while((jiffies - origjiffies) < 2 * HZ); + + /* Just check to be sure */ + vbat = wctdm_getreg(wc, card, 82); + printk(KERN_DEBUG "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", + card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); +#endif +#endif + return 0; + +} + +static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card){ + unsigned long origjiffies; + unsigned char i; + + wctdm_setreg(wc, card, 21, 0);//(0) Disable all interupts in DR21 + wctdm_setreg(wc, card, 22, 0);//(0)Disable all interupts in DR21 + wctdm_setreg(wc, card, 23, 0);//(0)Disable all interupts in DR21 + wctdm_setreg(wc, card, 64, 0);//(0) + + wctdm_setreg(wc, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration. + wctdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM + + origjiffies=jiffies; + while( wctdm_getreg(wc,card,96)!=0 ){ + if((jiffies-origjiffies)>80) + return -1; + } +//Initialized DR 98 and 99 to get consistant results. +// 98 and 99 are the results registers and the search should have same intial conditions. + +/*******************************The following is the manual gain mismatch calibration****************************/ +/*******************************This is also available as a function *******************************************/ + // Delay 10ms + origjiffies=jiffies; + while((jiffies-origjiffies)<1); + wctdm_proslic_setreg_indirect(wc, card, 88,0); + wctdm_proslic_setreg_indirect(wc,card,89,0); + wctdm_proslic_setreg_indirect(wc,card,90,0); + wctdm_proslic_setreg_indirect(wc,card,91,0); + wctdm_proslic_setreg_indirect(wc,card,92,0); + wctdm_proslic_setreg_indirect(wc,card,93,0); + + wctdm_setreg(wc, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time + wctdm_setreg(wc, card, 99,0x10); + + for ( i=0x1f; i>0; i--) + { + wctdm_setreg(wc, card, 98,i); + origjiffies=jiffies; + while((jiffies-origjiffies)<4); + if((wctdm_getreg(wc,card,88)) == 0) + break; + } // for + + for ( i=0x1f; i>0; i--) + { + wctdm_setreg(wc, card, 99,i); + origjiffies=jiffies; + while((jiffies-origjiffies)<4); + if((wctdm_getreg(wc,card,89)) == 0) + break; + }//for + +/*******************************The preceding is the manual gain mismatch calibration****************************/ +/**********************************The following is the longitudinal Balance Cal***********************************/ + wctdm_setreg(wc,card,64,1); + while((jiffies-origjiffies)<10); // Sleep 100? + + wctdm_setreg(wc, card, 64, 0); + wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal + wctdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration + wctdm_setreg(wc, card, 96,0x40); + + wctdm_getreg(wc,card,96); /* Read Reg 96 just cause */ + + wctdm_setreg(wc, card, 21, 0xFF); + wctdm_setreg(wc, card, 22, 0xFF); + wctdm_setreg(wc, card, 23, 0xFF); + + /**The preceding is the longitudinal Balance Cal***/ + return(0); + +} +#if 1 +static int wctdm_proslic_calibrate(struct wctdm *wc, int card) +{ + unsigned long origjiffies; + int x; + /* Perform all calibrations */ + wctdm_setreg(wc, card, 97, 0x1f); + + /* Begin, no speedup */ + wctdm_setreg(wc, card, 96, 0x5f); + + /* Wait for it to finish */ + origjiffies = jiffies; + while(wctdm_getreg(wc, card, 96)) { + if ((jiffies - origjiffies) > 2 * HZ) { + printk(KERN_NOTICE "Timeout waiting for calibration of module %d\n", card); + return -1; + } + } + + if (debug) { + /* Print calibration parameters */ + printk(KERN_DEBUG "Calibration Vector Regs 98 - 107: \n"); + for (x=98;x<108;x++) { + printk(KERN_DEBUG "%d: %02x\n", x, wctdm_getreg(wc, card, x)); + } + } + return 0; +} +#endif + +static void wait_just_a_bit(int foo) +{ + long newjiffies; + newjiffies = jiffies + foo; + while(jiffies < newjiffies); +} + +/********************************************************************* + * Set the hwgain on the analog modules + * + * card = the card position for this module (0-23) + * gain = gain in dB x10 (e.g. -3.5dB would be gain=-35) + * tx = (0 for rx; 1 for tx) + * + *******************************************************************/ +static int wctdm_set_hwgain(struct wctdm *wc, int card, __s32 gain, __u32 tx) +{ + if (!(wc->modtype[card] == MOD_TYPE_FXO)) { + printk(KERN_NOTICE "Cannot adjust gain. Unsupported module type!\n"); + return -1; + } + if (tx) { + if (debug) + printk(KERN_DEBUG "setting FXO tx gain for card=%d to %d\n", card, gain); + if (gain >= -150 && gain <= 0) { + wctdm_setreg(wc, card, 38, 16 + (gain/-10)); + wctdm_setreg(wc, card, 40, 16 + (-gain%10)); + } else if (gain <= 120 && gain > 0) { + wctdm_setreg(wc, card, 38, gain/10); + wctdm_setreg(wc, card, 40, (gain%10)); + } else { + printk(KERN_INFO "FXO tx gain is out of range (%d)\n", gain); + return -1; + } + } else { /* rx */ + if (debug) + printk(KERN_DEBUG "setting FXO rx gain for card=%d to %d\n", card, gain); + if (gain >= -150 && gain <= 0) { + wctdm_setreg(wc, card, 39, 16+ (gain/-10)); + wctdm_setreg(wc, card, 41, 16 + (-gain%10)); + } else if (gain <= 120 && gain > 0) { + wctdm_setreg(wc, card, 39, gain/10); + wctdm_setreg(wc, card, 41, (gain%10)); + } else { + printk(KERN_INFO "FXO rx gain is out of range (%d)\n", gain); + return -1; + } + } + + return 0; +} + +static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane) +{ + unsigned char reg16=0, reg26=0, reg30=0, reg31=0; + long newjiffies; + wc->modtype[card] = MOD_TYPE_FXO; + /* Sanity check the ProSLIC */ + reset_spi(wc, card); + if (!sane && wctdm_voicedaa_insane(wc, card)) + return -2; + + /* Software reset */ + wctdm_setreg(wc, card, 1, 0x80); + + /* Wait just a bit */ + wait_just_a_bit(HZ/10); + + /* Enable PCM, ulaw */ + if (alawoverride) + wctdm_setreg(wc, card, 33, 0x20); + else + wctdm_setreg(wc, card, 33, 0x28); + + /* Set On-hook speed, Ringer impedence, and ringer threshold */ + reg16 |= (fxo_modes[_opermode].ohs << 6); + reg16 |= (fxo_modes[_opermode].rz << 1); + reg16 |= (fxo_modes[_opermode].rt); + wctdm_setreg(wc, card, 16, reg16); + + if(fwringdetect) { + /* Enable ring detector full-wave rectifier mode */ + wctdm_setreg(wc, card, 18, 2); + wctdm_setreg(wc, card, 24, 0); + } else { + /* Set to the device defaults */ + wctdm_setreg(wc, card, 18, 0); + wctdm_setreg(wc, card, 24, 0x19); + } + + /* Set DC Termination: + Tip/Ring voltage adjust, minimum operational current, current limitation */ + reg26 |= (fxo_modes[_opermode].dcv << 6); + reg26 |= (fxo_modes[_opermode].mini << 4); + reg26 |= (fxo_modes[_opermode].ilim << 1); + wctdm_setreg(wc, card, 26, reg26); + + /* Set AC Impedence */ + reg30 = (fxofullscale==1) ? (fxo_modes[_opermode].acim|0x10) : (fxo_modes[_opermode].acim); + wctdm_setreg(wc, card, 30, reg30); + + /* Misc. DAA parameters */ + if (fastpickup) + reg31 = 0xb3; + else + reg31 = 0xa3; + + reg31 |= (fxo_modes[_opermode].ohs2 << 3); + wctdm_setreg(wc, card, 31, reg31); + + /* Set Transmit/Receive timeslot */ + //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 34, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 35, 0x00); + wctdm_setreg(wc, card, 36, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 37, 0x00); + + /* Enable ISO-Cap */ + wctdm_setreg(wc, card, 6, 0x00); + + if (fastpickup) + wctdm_setreg(wc, card, 17, wctdm_getreg(wc, card, 17) | 0x20); + + /* Wait 1000ms for ISO-cap to come up */ + newjiffies = jiffies; + newjiffies += 2 * HZ; + while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) + wait_just_a_bit(HZ/10); + + if (!(wctdm_getreg(wc, card, 11) & 0xf0)) { + printk(KERN_NOTICE "VoiceDAA did not bring up ISO link properly!\n"); + return -1; + } + if (debug) + printk(KERN_DEBUG "ISO-Cap is now up, line side: %02x rev %02x\n", + wctdm_getreg(wc, card, 11) >> 4, + (wctdm_getreg(wc, card, 13) >> 2) & 0xf); + /* Enable on-hook line monitor */ + wctdm_setreg(wc, card, 5, 0x08); + + /* Take values for fxotxgain and fxorxgain and apply them to module */ + wctdm_set_hwgain(wc, card, fxotxgain, 1); + wctdm_set_hwgain(wc, card, fxorxgain, 0); + + /* NZ -- crank the tx gain up by 7 dB */ + if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) { + printk(KERN_INFO "Adjusting gain\n"); + wctdm_set_hwgain(wc, card, 7, 1); + } + + if(debug) + printk(KERN_DEBUG "DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (wctdm_getreg(wc, card, 38)/16)?-(wctdm_getreg(wc, card, 38) - 16) : wctdm_getreg(wc, card, 38), (wctdm_getreg(wc, card, 40)/16)? -(wctdm_getreg(wc, card, 40) - 16):wctdm_getreg(wc, card, 40), (wctdm_getreg(wc, card, 39)/16)? -(wctdm_getreg(wc, card, 39) - 16) : wctdm_getreg(wc, card, 39),(wctdm_getreg(wc, card, 41)/16)?-(wctdm_getreg(wc, card, 41) - 16):wctdm_getreg(wc, card, 41)); + + return 0; + +} + +static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane) +{ + + unsigned short tmp[5]; + unsigned char r19, r9; + int x; + int fxsmode=0; + + /* Sanity check the ProSLIC */ + if (!sane && wctdm_proslic_insane(wc, card)) + return -2; + + /* By default, don't send on hook */ + if (reversepolarity) + wc->mod[card].fxs.idletxhookstate = 5; + else + wc->mod[card].fxs.idletxhookstate = 1; + + if (sane) { + /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ + wctdm_setreg(wc, card, 14, 0x10); + } + + if (wctdm_proslic_init_indirect_regs(wc, card)) { + printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card); + return -1; + } + + /* Clear scratch pad area */ + wctdm_proslic_setreg_indirect(wc, card, 97,0); + + /* Clear digital loopback */ + wctdm_setreg(wc, card, 8, 0); + + /* Revision C optimization */ + wctdm_setreg(wc, card, 108, 0xeb); + + /* Disable automatic VBat switching for safety to prevent + Q7 from accidently turning on and burning out. */ + wctdm_setreg(wc, card, 67, 0x07); /* Note, if pulse dialing has problems at high REN loads + change this to 0x17 */ + + /* Turn off Q7 */ + wctdm_setreg(wc, card, 66, 1); + + /* Flush ProSLIC digital filters by setting to clear, while + saving old values */ + for (x=0;x<5;x++) { + tmp[x] = wctdm_proslic_getreg_indirect(wc, card, x + 35); + wctdm_proslic_setreg_indirect(wc, card, x + 35, 0x8000); + } + + /* Power up the DC-DC converter */ + if (wctdm_powerup_proslic(wc, card, fast)) { + printk(KERN_NOTICE "Unable to do INITIAL ProSLIC powerup on module %d\n", card); + return -1; + } + + if (!fast) { + + /* Check for power leaks */ + if (wctdm_proslic_powerleak_test(wc, card)) { + printk(KERN_NOTICE "ProSLIC module %d failed leakage test. Check for short circuit\n", card); + } + /* Power up again */ + if (wctdm_powerup_proslic(wc, card, fast)) { + printk(KERN_NOTICE "Unable to do FINAL ProSLIC powerup on module %d\n", card); + return -1; + } +#ifndef NO_CALIBRATION + /* Perform calibration */ + if(manual) { + if (wctdm_proslic_manual_calibrate(wc, card)) { + //printk(KERN_NOTICE "Proslic failed on Manual Calibration\n"); + if (wctdm_proslic_manual_calibrate(wc, card)) { + printk(KERN_NOTICE "Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n"); + return -1; + } + printk(KERN_NOTICE "Proslic Passed Manual Calibration on Second Attempt\n"); + } + } + else { + if(wctdm_proslic_calibrate(wc, card)) { + //printk(KERN_NOTICE "ProSlic died on Auto Calibration.\n"); + if (wctdm_proslic_calibrate(wc, card)) { + printk(KERN_NOTICE "Proslic Failed on Second Attempt to Auto Calibrate\n"); + return -1; + } + printk(KERN_NOTICE "Proslic Passed Auto Calibration on Second Attempt\n"); + } + } + /* Perform DC-DC calibration */ + wctdm_setreg(wc, card, 93, 0x99); + r19 = wctdm_getreg(wc, card, 107); + if ((r19 < 0x2) || (r19 > 0xd)) { + printk(KERN_NOTICE "DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19); + wctdm_setreg(wc, card, 107, 0x8); + } + + /* Save calibration vectors */ + for (x=0;xmod[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x); +#endif + + } else { + /* Restore calibration registers */ + for (x=0;xmod[card].fxs.calregs.vals[x]); + } + /* Calibration complete, restore original values */ + for (x=0;x<5;x++) { + wctdm_proslic_setreg_indirect(wc, card, x + 35, tmp[x]); + } + + if (wctdm_proslic_verify_indirect_regs(wc, card)) { + printk(KERN_INFO "Indirect Registers failed verification.\n"); + return -1; + } + + +#if 0 + /* Disable Auto Power Alarm Detect and other "features" */ + wctdm_setreg(wc, card, 67, 0x0e); + blah = wctdm_getreg(wc, card, 67); +#endif + +#if 0 + if (wctdm_proslic_setreg_indirect(wc, card, 97, 0x0)) { // Stanley: for the bad recording fix + printk(KERN_INFO "ProSlic IndirectReg Died.\n"); + return -1; + } +#endif + + if (alawoverride) + wctdm_setreg(wc, card, 1, 0x20); + else + wctdm_setreg(wc, card, 1, 0x28); + // U-Law 8-bit interface + wctdm_setreg(wc, card, 2, (3-(card%4)) * 8 + (card/4) * 64); // Tx Start count low byte 0 + wctdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0 + wctdm_setreg(wc, card, 4, (3-(card%4)) * 8 + (card/4) * 64); // Rx Start count low byte 0 + wctdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0 + wctdm_setreg(wc, card, 18, 0xff); // clear all interrupt + wctdm_setreg(wc, card, 19, 0xff); + wctdm_setreg(wc, card, 20, 0xff); + wctdm_setreg(wc, card, 73, 0x04); + if (fxshonormode) { + fxsmode = acim2tiss[fxo_modes[_opermode].acim]; + wctdm_setreg(wc, card, 10, 0x08 | fxsmode); + if (fxo_modes[_opermode].ring_osc) + wctdm_proslic_setreg_indirect(wc, card, 20, fxo_modes[_opermode].ring_osc); + if (fxo_modes[_opermode].ring_x) + wctdm_proslic_setreg_indirect(wc, card, 21, fxo_modes[_opermode].ring_x); + } + if (lowpower) + wctdm_setreg(wc, card, 72, 0x10); + +#if 0 + wctdm_setreg(wc, card, 21, 0x00); // enable interrupt + wctdm_setreg(wc, card, 22, 0x02); // Loop detection interrupt + wctdm_setreg(wc, card, 23, 0x01); // DTMF detection interrupt +#endif + +#if 0 + /* Enable loopback */ + wctdm_setreg(wc, card, 8, 0x2); + wctdm_setreg(wc, card, 14, 0x0); + wctdm_setreg(wc, card, 64, 0x0); + wctdm_setreg(wc, card, 1, 0x08); +#endif + + if (fastringer) { + /* Speed up Ringer */ + wctdm_proslic_setreg_indirect(wc, card, 20, 0x7e6d); + wctdm_proslic_setreg_indirect(wc, card, 21, 0x01b9); + /* Beef up Ringing voltage to 89V */ + if (boostringer) { + wctdm_setreg(wc, card, 74, 0x3f); + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x247)) + return -1; + printk(KERN_INFO "Boosting fast ringer on slot %d (89V peak)\n", card + 1); + } else if (lowpower) { + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x14b)) + return -1; + printk(KERN_INFO "Reducing fast ring power on slot %d (50V peak)\n", card + 1); + } else + printk(KERN_INFO "Speeding up ringer on slot %d (25Hz)\n", card + 1); + } else { + /* Beef up Ringing voltage to 89V */ + if (boostringer) { + wctdm_setreg(wc, card, 74, 0x3f); + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x1d1)) + return -1; + printk(KERN_INFO "Boosting ringer on slot %d (89V peak)\n", card + 1); + } else if (lowpower) { + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x108)) + return -1; + printk(KERN_INFO "Reducing ring power on slot %d (50V peak)\n", card + 1); + } + } + + if(fxstxgain || fxsrxgain) { + r9 = wctdm_getreg(wc, card, 9); + switch (fxstxgain) { + + case 35: + r9+=8; + break; + case -35: + r9+=4; + break; + case 0: + break; + } + + switch (fxsrxgain) { + + case 35: + r9+=2; + break; + case -35: + r9+=1; + break; + case 0: + break; + } + wctdm_setreg(wc,card,9,r9); + } + + if(debug) + printk(KERN_DEBUG "DEBUG: fxstxgain:%s fxsrxgain:%s\n",((wctdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((wctdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((wctdm_getreg(wc, card, 9)/2) == 1)?"3.5":((wctdm_getreg(wc,card,9)%2)?"-3.5":"0.0")); + + wctdm_setreg(wc, card, 64, 0x01); + return 0; +} + + +static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) +{ + struct wctdm_stats stats; + struct wctdm_regs regs; + struct wctdm_regop regop; + struct wctdm_echo_coefs echoregs; + struct dahdi_hwgain hwgain; + struct wctdm *wc = chan->pvt; + int x; + switch (cmd) { + case DAHDI_ONHOOKTRANSFER: + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + if (get_user(x, (__user int *)data)) + return -EFAULT; + wc->mod[chan->chanpos - 1].fxs.ohttimer = x << 3; + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x2; + if (wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1 || wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x5) { + /* Apply the change if appropriate */ + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6; + else + wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2; + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); + } + break; + case DAHDI_SETPOLARITY: + if (get_user(x, (__user int *)data)) + return -EFAULT; + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + /* Can't change polarity while ringing or when open */ + if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) || + (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00)) + return -EINVAL; + + if ((x && !reversepolarity) || (!x && reversepolarity)) + wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04; + else + wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04; + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); + break; + case WCTDM_GET_STATS: + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + stats.tipvolt = wctdm_getreg(wc, chan->chanpos - 1, 80) * -376; + stats.ringvolt = wctdm_getreg(wc, chan->chanpos - 1, 81) * -376; + stats.batvolt = wctdm_getreg(wc, chan->chanpos - 1, 82) * -376; + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + stats.tipvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + stats.ringvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + stats.batvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + } else + return -EINVAL; + if (copy_to_user((__user void *)data, &stats, sizeof(stats))) + return -EFAULT; + break; + case WCTDM_GET_REGS: + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + for (x=0;xchanpos -1, x); + for (x=0;xchanpos - 1, x); + } else { + memset(®s, 0, sizeof(regs)); + for (x=0;xchanpos - 1, x); + } + if (copy_to_user((__user void *)data, ®s, sizeof(regs))) + return -EFAULT; + break; + case WCTDM_SET_REG: + if (copy_from_user(®op, (__user void *)data, sizeof(regop))) + return -EFAULT; + if (regop.indirect) { + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + printk(KERN_INFO "Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos); + wctdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val); + } else { + regop.val &= 0xff; + printk(KERN_INFO "Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos); + wctdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val); + } + break; + case WCTDM_SET_ECHOTUNE: + printk(KERN_INFO "-- Setting echo registers: \n"); + if (copy_from_user(&echoregs, (__user void *)data, sizeof(echoregs))) + return -EFAULT; + + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + /* Set the ACIM register */ + wctdm_setreg(wc, chan->chanpos - 1, 30, (fxofullscale==1) ? (echoregs.acim|0x10) : echoregs.acim); + + /* Set the digital echo canceller registers */ + wctdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1); + wctdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2); + wctdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3); + wctdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4); + wctdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5); + wctdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6); + wctdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7); + wctdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8); + + printk(KERN_INFO "-- Set echo registers successfully\n"); + + break; + } else { + return -EINVAL; + + } + break; + case DAHDI_SET_HWGAIN: + if (copy_from_user(&hwgain, (__user void *) data, sizeof(hwgain))) + return -EFAULT; + + wctdm_set_hwgain(wc, chan->chanpos-1, hwgain.newgain, hwgain.tx); + + if (debug) + printk(KERN_DEBUG "Setting hwgain on channel %d to %d for %s direction\n", + chan->chanpos-1, hwgain.newgain, hwgain.tx ? "tx" : "rx"); + break; + default: + return -ENOTTY; + } + return 0; + +} + +static int wctdm_open(struct dahdi_chan *chan) +{ + struct wctdm *wc = chan->pvt; + if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) + return -ENODEV; + if (wc->dead) + return -ENODEV; + wc->usecount++; + + /*MOD_INC_USE_COUNT; */ + try_module_get(THIS_MODULE); + return 0; +} + +static inline struct wctdm *wctdm_from_span(struct dahdi_span *span) +{ + return container_of(span, struct wctdm, span); +} + +static int wctdm_watchdog(struct dahdi_span *span, int event) +{ + printk(KERN_INFO "opvxa1200: Restarting DMA\n"); + wctdm_restart_dma(wctdm_from_span(span)); + return 0; +} + +static int wctdm_close(struct dahdi_chan *chan) +{ + struct wctdm *wc = chan->pvt; + wc->usecount--; + + /*MOD_DEC_USE_COUNT;*/ + module_put(THIS_MODULE); + + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 5; + else + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 1; + } + /* If we're dead, release us now */ + if (!wc->usecount && wc->dead) + wctdm_release(wc); + return 0; +} + +static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) +{ + struct wctdm *wc = chan->pvt; + int reg=0; + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + /* XXX Enable hooksig for FXO XXX */ + switch(txsig) { + case DAHDI_TXSIG_START: + case DAHDI_TXSIG_OFFHOOK: + wc->mod[chan->chanpos - 1].fxo.offhook = 1; + wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); + if(cidbeforering) + { + wc->cid_state[chan->chanpos - 1] = CID_STATE_IDLE; + wc->cid_history_clone_cnt[chan->chanpos - 1] = 0; + wc->cid_history_ptr[chan->chanpos - 1] = 0; + memset(wc->cid_history_buf[chan->chanpos - 1], DAHDI_LIN2X(0, chan), cidbuflen * DAHDI_MAX_CHUNKSIZE); + } + break; + case DAHDI_TXSIG_ONHOOK: + wc->mod[chan->chanpos - 1].fxo.offhook = 0; + wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); + break; + default: + printk(KERN_NOTICE "wcfxo: Can't set tx state to %d\n", txsig); + } + } else { + switch(txsig) { + case DAHDI_TXSIG_ONHOOK: + switch(chan->sig) { + case DAHDI_SIG_EM: + case DAHDI_SIG_FXOKS: + case DAHDI_SIG_FXOLS: + wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; + break; + case DAHDI_SIG_FXOGS: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 3; + break; + } + break; + case DAHDI_TXSIG_OFFHOOK: + switch(chan->sig) { + case DAHDI_SIG_EM: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 5; + break; + default: + wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; + break; + } + break; + case DAHDI_TXSIG_START: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 4; + break; + case DAHDI_TXSIG_KEWL: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 0; + break; + default: + printk(KERN_NOTICE "opvxa1200: Can't set tx state to %d\n", txsig); + } + if (debug) + printk(KERN_DEBUG "Setting FXS hook state to %d (%02x)\n", txsig, reg); + +#if 1 + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos-1].fxs.lasttxhook); +#endif + } + return 0; +} + +#ifdef DAHDI_SPAN_OPS +static const struct dahdi_span_ops wctdm_span_ops = { + .owner = THIS_MODULE, + .hooksig = wctdm_hooksig, + .open = wctdm_open, + .close = wctdm_close, + .ioctl = wctdm_ioctl, + .watchdog = wctdm_watchdog, +}; +#endif + +static int wctdm_initialize(struct wctdm *wc) +{ + int x; + + /* Dahdi stuff */ + sprintf(wc->span.name, "OPVXA1200/%d", wc->pos); + snprintf(wc->span.desc, sizeof(wc->span.desc)-1, "%s Board %d", wc->variety, wc->pos + 1); + snprintf(wc->span.location, sizeof(wc->span.location) - 1, + "PCI Bus %02d Slot %02d", wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); + wc->span.manufacturer = "OpenVox"; + dahdi_copy_string(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype)); + if (alawoverride) { + printk(KERN_INFO "ALAW override parameter detected. Device will be operating in ALAW\n"); + wc->span.deflaw = DAHDI_LAW_ALAW; + } else + wc->span.deflaw = DAHDI_LAW_MULAW; + + x = __wctdm_getcreg(wc, WC_VER); + wc->fwversion = x; + if( x & FLAG_A800) + { + wc->card_name = A800P_Name; + wc->max_cards = 8; + } + else + { + wc->card_name = A1200P_Name; + wc->max_cards = 12; + } + + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + sprintf(wc->chans[x]->name, "OPVXA1200/%d/%d", wc->pos, x); + wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; + wc->chans[x]->chanpos = x+1; + wc->chans[x]->pvt = wc; + } + +#ifdef DAHDI_SPAN_MODULE + wc->span.owner = THIS_MODULE; +#endif + +#ifdef DAHDI_SPAN_OPS + wc->span.ops = &wctdm_span_ops; +#else + wc->span.hooksig = wctdm_hooksig, + wc->span.watchdog = wctdm_watchdog, + wc->span.open = wctdm_open; + wc->span.close = wctdm_close; + wc->span.ioctl = wctdm_ioctl; + wc->span.pvt = wc; +#endif + wc->span.chans = wc->chans; + wc->span.channels = wc->max_cards; /*MAX_NUM_CARDS;*/ + wc->span.irq = wc->dev->irq; + wc->span.flags = DAHDI_FLAG_RBS; + wc->span.ops = &wctdm_span_ops; + + if (dahdi_register(&wc->span, 0)) { + printk(KERN_NOTICE "Unable to register span with Dahdi\n"); + return -1; + } + return 0; +} + +static void wctdm_post_initialize(struct wctdm *wc) +{ + int x; + + /* Finalize signalling */ + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + if (wc->cardflag & (1 << x)) { + if (wc->modtype[x] == MOD_TYPE_FXO) + wc->chans[x]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; + else + wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + } else if (!(wc->chans[x]->sigcap & DAHDI_SIG_BROKEN)) { + wc->chans[x]->sigcap = 0; + } + } +} + +static int wctdm_hardware_init(struct wctdm *wc) +{ + /* Hardware stuff */ + unsigned char ver; + unsigned char x,y; + int failed; + long origjiffies; //ml. + + /* Signal Reset */ + printk("before raise reset\n"); + outb(0x01, wc->ioaddr + WC_CNTL); + + /* Wait for 5 second */ + + origjiffies = jiffies; + + while(1) + { + if ((jiffies - origjiffies) >= (HZ*5)) + break;; + } + + /* printk(KERN_INFO "after raise reset\n");*/ + + /* Check OpenVox chip */ + x=inb(wc->ioaddr + WC_CNTL); + ver = __wctdm_getcreg(wc, WC_VER); + wc->fwversion = ver; + /*if( ver & FLAG_A800) + { + wc->card_name = A800P_Name; + wc->max_cards = 8; + } + else + { + wc->card_name = A1200P_Name; + wc->max_cards = 12; + }*/ + printk(KERN_NOTICE "OpenVox %s version: %01x.%01x\n", wc->card_name, (ver&(~FLAG_A800))>>4, ver&0x0f); + + failed = 0; + if (ver != 0x00) { + for (x=0;x<16;x++) { + /* Test registers */ + __wctdm_setcreg(wc, WC_CS, x); + y = __wctdm_getcreg(wc, WC_CS) & 0x0f; + if (x != y) { + printk(KERN_INFO "%02x != %02x\n", x, y); + failed++; + } + } + + if (!failed) { + printk(KERN_INFO "OpenVox %s passed register test\n", wc->card_name); + } else { + printk(KERN_NOTICE "OpenVox %s failed register test\n", wc->card_name); + return -1; + } + } else { + printk(KERN_INFO "No OpenVox chip %02x\n", ver); + } + + if (spibyhw) + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); // spi controled by hw MiaoLin; + else + __wctdm_setcreg(wc, WC_SPICTRL, 0); + + /* Reset PCI Interface chip and registers (and serial) */ + outb(0x06, wc->ioaddr + WC_CNTL); + /* Setup our proper outputs for when we switch for our "serial" port */ + wc->ios = BIT_CS | BIT_SCLK | BIT_SDI; + + outb(wc->ios, wc->ioaddr + WC_AUXD); + + /* Set all to outputs except AUX 5, which is an input */ + outb(0xdf, wc->ioaddr + WC_AUXC); + + /* Select alternate function for AUX0 */ /* Useless in OpenVox by MiaoLin. */ + /* outb(0x4, wc->ioaddr + WC_AUXFUNC); */ + + /* Wait 1/4 of a sec */ + wait_just_a_bit(HZ/4); + + /* Back to normal, with automatic DMA wrap around */ + outb(0x30 | 0x01, wc->ioaddr + WC_CNTL); + wc->ledstate = 0; + wctdm_set_led(wc, 0, 0); + + /* Make sure serial port and DMA are out of reset */ + outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, wc->ioaddr + WC_CNTL); + + /* Configure serial port for MSB->LSB operation */ + outb(0xc1, wc->ioaddr + WC_SERCTL); + + /* Delay FSC by 0 so it's properly aligned */ + outb(0x01, wc->ioaddr + WC_FSCDELAY); /* Modify to 1 by MiaoLin */ + + /* Setup DMA Addresses */ + outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ + outl(wc->writedma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ + outl(wc->writedma + DAHDI_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMAWE); /* End */ + + outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ + outl(wc->readdma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ + outl(wc->readdma + DAHDI_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMARE); /* End */ + + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + + /* Wait 1/4 of a second more */ + wait_just_a_bit(HZ/4); + + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + int sane=0,ret=0,readi=0; +#if 1 + touch_softlockup_watchdog(); // avoid showing CPU softlock message + /* Init with Auto Calibration */ + if (!(ret=wctdm_init_proslic(wc, x, 0, 0, sane))) { + wc->cardflag |= (1 << x); + if (debug) { + readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); + printk("Proslic module %d loop current is %dmA\n",x, + ((readi*3)+20)); + } + printk(KERN_INFO "Module %d: Installed -- AUTO FXS/DPO\n",x); + wctdm_set_led(wc, (unsigned int)x, 1); + } else { + if(ret!=-2) { + sane=1; + + printk(KERN_INFO "Init ProSlic with Manual Calibration \n"); + /* Init with Manual Calibration */ + if (!wctdm_init_proslic(wc, x, 0, 1, sane)) { + wc->cardflag |= (1 << x); + if (debug) { + readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); + printk("Proslic module %d loop current is %dmA\n",x, + ((readi*3)+20)); + } + printk(KERN_INFO "Module %d: Installed -- MANUAL FXS\n",x); + } else { + printk(KERN_NOTICE "Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC"); + wc->chans[x]->sigcap = __DAHDI_SIG_FXO | DAHDI_SIG_BROKEN; + } + } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { + wc->cardflag |= (1 << x); + printk(KERN_INFO "Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name); + wctdm_set_led(wc, (unsigned int)x, 1); + } else + printk(KERN_NOTICE "Module %d: Not installed\n", x); + } +#endif + } + + /* Return error if nothing initialized okay. */ + if (!wc->cardflag && !timingonly) + return -1; + /*__wctdm_setcreg(wc, WC_SYNC, (wc->cardflag << 1) | 0x1); */ /* removed by MiaoLin */ + return 0; +} + +static void wctdm_enable_interrupts(struct wctdm *wc) +{ + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + + /* Enable interrupts (we care about all of them) */ + outb(0x3c, wc->ioaddr + WC_MASK0); + /* No external interrupts */ + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static void wctdm_restart_dma(struct wctdm *wc) +{ + /* Reset Master and TDM */ + outb(0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); +} + +static void wctdm_start_dma(struct wctdm *wc) +{ + /* Reset Master and TDM */ + outb(0x0f, wc->ioaddr + WC_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); +} + +static void wctdm_stop_dma(struct wctdm *wc) +{ + outb(0x00, wc->ioaddr + WC_OPER); +} + +static void wctdm_reset_tdm(struct wctdm *wc) +{ + /* Reset TDM */ + outb(0x0f, wc->ioaddr + WC_CNTL); +} + +static void wctdm_disable_interrupts(struct wctdm *wc) +{ + outb(0x00, wc->ioaddr + WC_MASK0); + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct wctdm *wc; + struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data; + int x; + int y; + + static int initd_ifaces=0; + + if(initd_ifaces){ + memset((void *)ifaces,0,(sizeof(struct wctdm *))*WC_MAX_IFACES); + initd_ifaces=1; + } + for (x=0;x= WC_MAX_IFACES) { + printk(KERN_NOTICE "Too many interfaces\n"); + return -EIO; + } + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL); + if (wc) { + int cardcount = 0; + + wc->lastchan = -1; /* first channel offset = -1; */ + wc->ledstate = 0; + + ifaces[x] = wc; + memset(wc, 0, sizeof(struct wctdm)); + for (x=0; x < sizeof(wc->chans)/sizeof(wc->chans[0]); ++x) { + wc->chans[x] = &wc->_chans[x]; + } + + spin_lock_init(&wc->lock); + wc->curcard = -1; + wc->ioaddr = pci_resource_start(pdev, 0); + wc->mem_region = pci_resource_start(pdev, 1); + wc->mem_len = pci_resource_len(pdev, 1); + wc->mem32 = (unsigned long)ioremap(wc->mem_region, wc->mem_len); + wc->dev = pdev; + wc->pos = x; + wc->variety = d->name; + for (y=0;yflags[y] = d->flags; + /* Keep track of whether we need to free the region */ + if (request_region(wc->ioaddr, 0xff, "opvxa1200")) + wc->freeregion = 1; + else + wc->freeregion = 0; + + if (request_mem_region(wc->mem_region, wc->mem_len, "opvxa1200")) + wc->freeregion |= 0x02; + + /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses + 8 bits. */ + wc->writechunk = pci_alloc_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, &wc->writedma); + if (!wc->writechunk) { + printk(KERN_NOTICE "opvxa1200: Unable to allocate DMA-able memory\n"); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + return -ENOMEM; + } + + wc->readchunk = wc->writechunk + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ + wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ + + if (wctdm_initialize(wc)) { + printk(KERN_NOTICE "opvxa1200: Unable to intialize FXS\n"); + /* Set Reset Low */ + x=inb(wc->ioaddr + WC_CNTL); + outb((~0x1)&x, wc->ioaddr + WC_CNTL); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + + if (request_irq(pdev->irq, wctdm_interrupt, DAHDI_IRQ_SHARED, "opvxa1200", wc)) { + printk(KERN_NOTICE "opvxa1200: Unable to request IRQ %d\n", pdev->irq); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + kfree(wc); + return -EIO; + } + + if (wctdm_hardware_init(wc)) { + unsigned char w; + + /* Set Reset Low */ + w=inb(wc->ioaddr + WC_CNTL); + outb((~0x1)&w, wc->ioaddr + WC_CNTL); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + dahdi_unregister(&wc->span); + kfree(wc); + return -EIO; + + } + +#ifdef TEST_LOG_INCOME_VOICE + for(x=0; xvoc_buf[x] = kmalloc(voc_buffer_size, GFP_KERNEL); + wc->voc_ptr[x] = 0; + } +#endif + + if(cidbeforering) + { + int len = cidbuflen * DAHDI_MAX_CHUNKSIZE; + if(debug) + printk("cidbeforering support enabled, length is %d msec\n", cidbuflen); + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) + { + wc->cid_history_buf[x] = kmalloc(len, GFP_KERNEL); + wc->cid_history_ptr[x] = 0; + wc->cid_history_clone_cnt[x] = 0; + wc->cid_state[x] = CID_STATE_IDLE; + } + } + + wctdm_post_initialize(wc); + + /* Enable interrupts */ + wctdm_enable_interrupts(wc); + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2); + + /* Start DMA */ + wctdm_start_dma(wc); + + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { + if (wc->cardflag & (1 << x)) + cardcount++; + } + + printk(KERN_INFO "Found an OpenVox %s: Version %x.%x (%d modules)\n", wc->card_name, (wc->fwversion&(~FLAG_A800))>>4, wc->fwversion&0x0f, cardcount); + if(debug) + printk(KERN_DEBUG "OpenVox %s debug On\n", wc->card_name); + + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void wctdm_release(struct wctdm *wc) +{ +#ifdef TEST_LOG_INCOME_VOICE + struct file * f = NULL; + mm_segment_t orig_fs; + int i; + char fname[20]; +#endif + + dahdi_unregister(&wc->span); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02) + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + +#ifdef TEST_LOG_INCOME_VOICE + for(i=0; if_op || !f->f_op->read) + { + printk("WARNING: File (read) object is a null pointer!!!\n"); + continue; + } + + f->f_pos = 0; + + orig_fs = get_fs(); + set_fs(KERNEL_DS); + + if(wc->voc_buf[i]) + { + f->f_op->write(f, wc->voc_buf[i], voc_buffer_size, &f->f_pos); + kfree(wc->voc_buf[i]); + } + + set_fs(orig_fs); + fput(f); + } +#endif + + if(cidbeforering) + { + int x; + for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) + kfree(wc->cid_history_buf[x]); + } + + kfree(wc); + printk(KERN_INFO "Free an OpenVox A1200 card\n"); +} + +static void __devexit wctdm_remove_one(struct pci_dev *pdev) +{ + struct wctdm *wc = pci_get_drvdata(pdev); + if (wc) { + + /* Stop any DMA */ + wctdm_stop_dma(wc); + wctdm_reset_tdm(wc); + + /* In case hardware is still there */ + wctdm_disable_interrupts(wc); + + /* Immediately free resources */ + pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + /* Reset PCI chip and registers */ + if(wc->fwversion > 0x11) + outb(0x0e, wc->ioaddr + WC_CNTL); + else + { + wc->ledstate = 0; + wctdm_set_led(wc,0,0); // power off all leds. + } + + /* Release span, possibly delayed */ + if (!wc->usecount) + wctdm_release(wc); + else + wc->dead = 1; + } +} + +static struct pci_device_id wctdm_pci_tbl[] = { + { 0xe159, 0x0001, 0x9100, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x95D9, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9500, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9532, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x8519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9559, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9599, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, wctdm_pci_tbl); + +static struct pci_driver wctdm_driver = { + .name = "opvxa1200", + .probe = wctdm_init_one, + .remove = __devexit_p(wctdm_remove_one), + .suspend = NULL, + .resume = NULL, + .id_table = wctdm_pci_tbl, +}; + +static int __init wctdm_init(void) +{ + int res; + int x; + for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) { + if (!strcmp(fxo_modes[x].name, opermode)) + break; + } + if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) { + _opermode = x; + } else { + printk(KERN_NOTICE "Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); + for (x=0;x"); +MODULE_LICENSE("GPL v2"); + +module_init(wctdm_init); +module_exit(wctdm_cleanup); diff --git a/drivers/dahdi/opvxd115/Kbuild b/drivers/dahdi/opvxd115/Kbuild new file mode 100644 index 0000000..474997d --- /dev/null +++ b/drivers/dahdi/opvxd115/Kbuild @@ -0,0 +1,32 @@ +obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115) += opvxd115.o + +FIRM_DIR := ../firmware + +EXTRA_CFLAGS += -I$(src)/.. $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef + +ifeq ($(HOTPLUG_FIRMWARE),yes) + EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE +endif + +opvxd115-objs := base.o vpm450m.o $(shell $(src)/../oct612x/octasic-helper objects ../oct612x) + +DAHDI_KERNEL_H_NAME:=kernel.h +DAHDI_KERNEL_H_PATH:=$(DAHDI_INCLUDE)/dahdi/$(DAHDI_KERNEL_H_NAME) +ifneq ($(DAHDI_KERNEL_H_PATH),) + DAHDI_SPAN_MODULE:=$(shell if grep -C 5 "struct dahdi_span {" $(DAHDI_KERNEL_H_PATH) | grep -q "struct module \*owner"; then echo "yes"; else echo "no"; fi) + DAHDI_SPAN_OPS:=$(shell if grep -q "struct dahdi_span_ops {" $(DAHDI_KERNEL_H_PATH); then echo "yes"; else echo "no"; fi) + ifeq ($(DAHDI_SPAN_MODULE),yes) + EXTRA_CFLAGS+=-DDAHDI_SPAN_MODULE + else + ifeq ($(DAHDI_SPAN_OPS),yes) + EXTRA_CFLAGS+=-DDAHDI_SPAN_OPS + endif + endif +endif + +ifneq ($(HOTPLUG_FIRMWARE),yes) +opvxd115-objs += $(FIRM_DIR)/dahdi-fw-oct6114-032.o +endif + +$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-032.o: $(obj)/base.o + $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-032.o diff --git a/drivers/dahdi/opvxd115/Makefile b/drivers/dahdi/opvxd115/Makefile new file mode 100644 index 0000000..baaab35 --- /dev/null +++ b/drivers/dahdi/opvxd115/Makefile @@ -0,0 +1,8 @@ +ifdef KBUILD_EXTMOD +# We only get here on kernels 2.6.0-2.6.9 . +# For newer kernels, Kbuild will be included directly by the kernel +# build system. +include $(src)/Kbuild + +else +endif diff --git a/drivers/dahdi/opvxd115/base.c b/drivers/dahdi/opvxd115/base.c new file mode 100644 index 0000000..123a2e0 --- /dev/null +++ b/drivers/dahdi/opvxd115/base.c @@ -0,0 +1,4903 @@ +/* + * OpenVox D115P/D115E PCI/PCI-E Driver version 0.1 01/07/2011 + * + * Written by Mark Spencer + * Modify from wct4xxp module by mark.liu@openvox.cn + + * Based on previous works, designs, and archetectures conceived and + * written by Jim Dixon . + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001-2010, Digium, Inc. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "opvxd115.h" +#include "vpm450m.h" + +/* Work queues are a way to better distribute load on SMP systems */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) +/* + * Work queues can significantly improve performance and scalability + * on multi-processor machines, but requires bypassing some kernel + * API's, so it's not guaranteed to be compatible with all kernels. + */ +/* #define ENABLE_WORKQUEUES */ +#endif + +/* Enable prefetching may help performance */ +#define ENABLE_PREFETCH + +/* Support first generation cards? */ +#define SUPPORT_GEN1 + +/* Define to get more attention-grabbing but slightly more I/O using + alarm status */ +#define FANCY_ALARM + +/* Define to support Digium Voice Processing Module expansion card */ +#define VPM_SUPPORT + +#define DEBUG_MAIN (1 << 0) +#define DEBUG_DTMF (1 << 1) +#define DEBUG_REGS (1 << 2) +#define DEBUG_TSI (1 << 3) +#define DEBUG_ECHOCAN (1 << 4) +#define DEBUG_RBS (1 << 5) +#define DEBUG_FRAMER (1 << 6) + +/* Maximum latency to be used with Gen 5 */ +#define GEN5_MAX_LATENCY 127 + +#define T4_BASE_SIZE (DAHDI_MAX_CHUNKSIZE * 32 * 4) + +#ifdef ENABLE_WORKQUEUES +#include + +/* XXX UGLY!!!! XXX We have to access the direct structures of the workqueue which + are only defined within workqueue.c because they don't give us a routine to allow us + to nail a work to a particular thread of the CPU. Nailing to threads gives us substantially + higher scalability in multi-CPU environments though! */ + +/* + * The per-CPU workqueue (if single thread, we always use cpu 0's). + * + * The sequence counters are for flush_scheduled_work(). It wants to wait + * until until all currently-scheduled works are completed, but it doesn't + * want to be livelocked by new, incoming ones. So it waits until + * remove_sequence is >= the insert_sequence which pertained when + * flush_scheduled_work() was called. + */ + +struct cpu_workqueue_struct { + + spinlock_t lock; + + long remove_sequence; /* Least-recently added (next to run) */ + long insert_sequence; /* Next to add */ + + struct list_head worklist; + wait_queue_head_t more_work; + wait_queue_head_t work_done; + + struct workqueue_struct *wq; + task_t *thread; + + int run_depth; /* Detect run_workqueue() recursion depth */ +} ____cacheline_aligned; + +/* + * The externally visible workqueue abstraction is an array of + * per-CPU workqueues: + */ +struct workqueue_struct { + /* TODO: Find out exactly where the API changed */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) + struct cpu_workqueue_struct *cpu_wq; +#else + struct cpu_workqueue_struct cpu_wq[NR_CPUS]; +#endif + const char *name; + struct list_head list; /* Empty if single thread */ +}; + +/* Preempt must be disabled. */ +static void __t4_queue_work(struct cpu_workqueue_struct *cwq, + struct work_struct *work) +{ + unsigned long flags; + + spin_lock_irqsave(&cwq->lock, flags); + work->wq_data = cwq; + list_add_tail(&work->entry, &cwq->worklist); + cwq->insert_sequence++; + wake_up(&cwq->more_work); + spin_unlock_irqrestore(&cwq->lock, flags); +} + +/* + * Queue work on a workqueue. Return non-zero if it was successfully + * added. + * + * We queue the work to the CPU it was submitted, but there is no + * guarantee that it will be processed by that CPU. + */ +static inline int t4_queue_work(struct workqueue_struct *wq, struct work_struct *work, int cpu) +{ + int ret = 0; + get_cpu(); + if (!test_and_set_bit(0, &work->pending)) { + BUG_ON(!list_empty(&work->entry)); + __t4_queue_work(wq->cpu_wq + cpu, work); + ret = 1; + } + put_cpu(); + return ret; +} + +#endif + +/* + * Define CONFIG_EXTENDED_RESET to allow the qfalc framer extra time + * to reset itself upon hardware initialization. This exits for rare + * cases for customers who are seeing the qfalc returning unexpected + * information at initialization + */ +#undef CONFIG_EXTENDED_RESET + +static int pedanticpci = 1; +static int debug=0; +static int timingcable = 0; +static int t1e1override = -1; /* 0xff for E1, 0x00 for T1 */ +static int j1mode = 0; +static int sigmode = FRMR_MODE_NO_ADDR_CMP; +static int alarmdebounce = 2500; /* LOF/LFA def to 2.5s AT&T TR54016*/ +static int losalarmdebounce = 2500;/* LOS def to 2.5s AT&T TR54016*/ +static int aisalarmdebounce = 2500;/* AIS(blue) def to 2.5s AT&T TR54016*/ +static int yelalarmdebounce = 500;/* RAI(yellow) def to 0.5s AT&T devguide */ +static int max_latency = GEN5_MAX_LATENCY; /* Used to set a maximum latency (if you don't wish it to hard cap it at a certain value) in milliseconds */ +#ifdef VPM_SUPPORT +static int vpmsupport = 1; +/* If set to auto, vpmdtmfsupport is enabled for VPM400M and disabled for VPM450M */ +static int vpmdtmfsupport = -1; /* -1=auto, 0=disabled, 1=enabled*/ +static int vpmspans = 1; +#define VPM_DEFAULT_DTMFTHRESHOLD 1000 +static int dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; +static int lastdtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; +#endif +/* Enabling bursting can more efficiently utilize PCI bus bandwidth, but + can also cause PCI bus starvation, especially in combination with other + aggressive cards. Please note that burst mode has no effect on CPU + utilization / max number of calls / etc. */ +static int noburst; +/* For 56kbps links, set this module parameter to 0x7f */ +static int hardhdlcmode = 0xff; + +static int latency = 1; + +static int ms_per_irq = 1; + +#ifdef FANCY_ALARM +static int altab[] = { +0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, +}; +#endif + +#define MAX_SPANS 16 + +#define FLAG_STARTED (1 << 0) +#define FLAG_NMF (1 << 1) +#define FLAG_SENDINGYELLOW (1 << 2) + + +#define TYPE_T1 1 /* is a T1 card */ +#define TYPE_E1 2 /* is an E1 card */ +#define TYPE_J1 3 /* is a running J1 */ + +#define FLAG_2NDGEN (1 << 3) +#define FLAG_2PORT (1 << 4) +#define FLAG_VPM2GEN (1 << 5) +#define FLAG_OCTOPT (1 << 6) +#define FLAG_3RDGEN (1 << 7) +#define FLAG_BURST (1 << 8) +#define FLAG_EXPRESS (1 << 9) +#define FLAG_5THGEN (1 << 10) + +#define CANARY 0xc0de + + +#define PORTS_PER_FRAMER 4 + +struct devtype { + char *desc; + unsigned int flags; +}; + +static struct devtype opvxd115 = { "OpenVox D115P/D115E ", FLAG_2NDGEN}; +static struct devtype opvxd130 = { "OpenVox D130P/D130E", FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN}; + + +struct t4; + +struct t4_span { + struct t4 *owner; + unsigned int *writechunk; /* Double-word aligned write memory */ + unsigned int *readchunk; /* Double-word aligned read memory */ + int spantype; /* card type, T1 or E1 or J1 */ + int sync; + int psync; + int alarmtimer; + int redalarms; + int notclear; + int alarmcount; + int losalarmcount; + int aisalarmcount; + int yelalarmcount; + int spanflags; + int syncpos; +#ifdef SUPPORT_GEN1 + int e1check; /* E1 check */ +#endif + struct dahdi_span span; + unsigned char txsigs[16]; /* Transmit sigs */ + int loopupcnt; + int loopdowncnt; +#ifdef SUPPORT_GEN1 + unsigned char ec_chunk1[31][DAHDI_CHUNKSIZE]; /* first EC chunk buffer */ + unsigned char ec_chunk2[31][DAHDI_CHUNKSIZE]; /* second EC chunk buffer */ +#endif + int irqmisses; + + /* HDLC controller fields */ + struct dahdi_chan *sigchan; + unsigned char sigmode; + int sigactive; + int frames_out; + int frames_in; + +#ifdef VPM_SUPPORT + unsigned long dtmfactive; + unsigned long dtmfmask; + unsigned long dtmfmutemask; + short dtmfenergy[31]; + short dtmfdigit[31]; +#endif +#ifdef ENABLE_WORKQUEUES + struct work_struct swork; +#endif + struct dahdi_chan *chans[32]; /* Individual channels */ + struct dahdi_echocan_state *ec[32]; /* Echocan state for each channel */ +}; + +struct t4 { + /* This structure exists one per card */ + struct pci_dev *dev; /* Pointer to PCI device */ + unsigned int intcount; + int num; /* Which card we are */ + int t1e1; /* T1/E1 select pins */ + int globalconfig; /* Whether global setup has been done */ + int syncsrc; /* active sync source */ + struct t4_span *tspans[4]; /* Individual spans */ + int numspans; /* Number of spans on the card */ + int blinktimer; +#ifdef FANCY_ALARM + int alarmpos; +#endif + int irq; /* IRQ used by device */ + int order; /* Order */ + int flags; /* Device flags */ + unsigned int falc31 : 1; /* are we falc v3.1 (atomic not necessary) */ + int master; /* Are we master */ + int ledreg; /* LED Register */ + unsigned int gpio; + unsigned int gpioctl; + int e1recover; /* E1 recovery timer */ + spinlock_t reglock; /* lock register access */ + int spansstarted; /* number of spans started */ + volatile unsigned int *writechunk; /* Double-word aligned write memory */ + volatile unsigned int *readchunk; /* Double-word aligned read memory */ + unsigned short canary; +#ifdef ENABLE_WORKQUEUES + atomic_t worklist; + struct workqueue_struct *workq; +#endif + unsigned int passno; /* number of interrupt passes */ + char *variety; + int last0; /* for detecting double-missed IRQ */ + + /* DMA related fields */ + unsigned int dmactrl; + dma_addr_t readdma; + dma_addr_t writedma; + unsigned long memaddr; /* Base address of card */ + unsigned long memlen; + __iomem volatile unsigned int *membase; /* Base address of card */ + + /* Add this for our softlockup protector */ + unsigned int oct_rw_count; + + /* Flags for our bottom half */ + unsigned long checkflag; + struct tasklet_struct t4_tlet; + unsigned int vpm400checkstatus; + /* Latency related additions */ + unsigned char rxident; + unsigned char lastindex; + int numbufs; + int needed_latency; + +#ifdef VPM_SUPPORT + struct vpm450m *vpm450m; + int vpm; +#endif + +}; + +#define T4_VPM_PRESENT (1 << 28) + +#ifdef VPM_SUPPORT +static void t4_vpm400_init(struct t4 *wc); +static void t4_vpm450_init(struct t4 *wc); +static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold); + +static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec); + +static const struct dahdi_echocan_features vpm400m_ec_features = { + .NLP_automatic = 1, + .CED_tx_detect = 1, + .CED_rx_detect = 1, +}; + +static const struct dahdi_echocan_features vpm450m_ec_features = { + .NLP_automatic = 1, + .CED_tx_detect = 1, + .CED_rx_detect = 1, +}; + +static const struct dahdi_echocan_ops vpm400m_ec_ops = { + .echocan_free = echocan_free, +}; + +static const struct dahdi_echocan_ops vpm450m_ec_ops = { + .echocan_free = echocan_free, +}; +#endif + +static void __set_clear(struct t4 *wc, int span); +static int t4_startup(struct file *file, struct dahdi_span *span); +static int t4_shutdown(struct dahdi_span *span); +static int t4_rbsbits(struct dahdi_chan *chan, int bits); +static int t4_maint(struct dahdi_span *span, int cmd); +static int t4_clear_maint(struct dahdi_span *span); +#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) +static int t4_reset_counters(struct dahdi_span *span); +#endif +#ifdef SUPPORT_GEN1 +static int t4_reset_dma(struct t4 *wc); +#endif +static void t4_hdlc_hard_xmit(struct dahdi_chan *chan); +static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data); +static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan); +static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan); +static void __t4_set_rclk_src(struct t4 *wc, int span); +static void __t4_set_sclk_src(struct t4 *wc, int mode, int master, int slave); +static void t4_check_alarms(struct t4 *wc, int span); +static void t4_check_sigbits(struct t4 *wc, int span); + +#define WC_RDADDR 0 +#define WC_WRADDR 1 +#define WC_COUNT 2 +#define WC_DMACTRL 3 +#define WC_INTR 4 +/* #define WC_GPIO 5 */ +#define WC_VERSION 6 +#define WC_LEDS 7 +#define WC_GPIOCTL 8 +#define WC_GPIO 9 +#define WC_LADDR 10 +#define WC_LDATA 11 +#define WC_LCS (1 << 11) +#define WC_LCS2 (1 << 12) +#define WC_LALE (1 << 13) +#define WC_LFRMR_CS (1 << 10) /* Framer's ChipSelect signal */ +#define WC_ACTIVATE (1 << 12) +#define WC_LREAD (1 << 15) +#define WC_LWRITE (1 << 16) + +#define WC_OFF (0) +#define WC_RED (1) +#define WC_GREEN (2) +#define WC_YELLOW (3) + +#define WC_RECOVER 0 +#define WC_SELF 1 + +#define LIM0_T 0x36 /* Line interface mode 0 register */ +#define LIM0_LL (1 << 1) /* Local Loop */ +#define LIM1_T 0x37 /* Line interface mode 1 register */ +#define LIM1_RL (1 << 1) /* Remote Loop */ + +#define FMR0 0x1C /* Framer Mode Register 0 */ +#define FMR0_SIM (1 << 0) /* Alarm Simulation */ +#define FMR1_T 0x1D /* Framer Mode Register 1 */ +#define FMR1_ECM (1 << 2) /* Error Counter 1sec Interrupt Enable */ +#define DEC_T 0x60 /* Diable Error Counter */ +#define IERR_T 0x1B /* Single Bit Defect Insertion Register */ +#define IBV 0 /* Bipolar violation */ +#define IPE (1 << 1) /* PRBS defect */ +#define ICASE (1 << 2) /* CAS defect */ +#define ICRCE (1 << 3) /* CRC defect */ +#define IMFE (1 << 4) /* Multiframe defect */ +#define IFASE (1 << 5) /* FAS defect */ +#define ISR3_SEC (1 << 6) /* Internal one-second interrupt bit mask */ +#define ISR3_ES (1 << 7) /* Errored Second interrupt bit mask */ +#define ESM 0x47 /* Errored Second mask register */ + +#define FMR2_T 0x1E /* Framer Mode Register 2 */ +#define FMR2_PLB (1 << 2) /* Framer Mode Register 2 */ + +#define FECL_T 0x50 /* Framing Error Counter Lower Byte */ +#define FECH_T 0x51 /* Framing Error Counter Higher Byte */ +#define CVCL_T 0x52 /* Code Violation Counter Lower Byte */ +#define CVCH_T 0x53 /* Code Violation Counter Higher Byte */ +#define CEC1L_T 0x54 /* CRC Error Counter 1 Lower Byte */ +#define CEC1H_T 0x55 /* CRC Error Counter 1 Higher Byte */ +#define EBCL_T 0x56 /* E-Bit Error Counter Lower Byte */ +#define EBCH_T 0x57 /* E-Bit Error Counter Higher Byte */ +#define BECL_T 0x58 /* Bit Error Counter Lower Byte */ +#define BECH_T 0x59 /* Bit Error Counter Higher Byte */ +#define COEC_T 0x5A /* COFA Event Counter */ +#define PRBSSTA_T 0xDA /* PRBS Status Register */ + +#define LCR1_T 0x3B /* Loop Code Register 1 */ +#define EPRM (1 << 7) /* Enable PRBS rx */ +#define XPRBS (1 << 6) /* Enable PRBS tx */ +#define FLLB (1 << 1) /* Framed line loop/Invert */ +#define LLBP (1 << 0) /* Line Loopback Pattern */ +#define TPC0_T 0xA8 /* Test Pattern Control Register */ +#define FRA (1 << 6) /* Framed/Unframed Selection */ +#define PRBS23 (3 << 4) /* Pattern selection (23 poly) */ +#define PRM (1 << 2) /* Non framed mode */ +#define FRS1_T 0x4D /* Framer Receive Status Reg 1 */ +#define LLBDD (1 << 4) +#define LLBAD (1 << 3) + +#define MAX_T4_CARDS 64 + +static void t4_isr_bh(unsigned long data); + +static struct t4 *cards[MAX_T4_CARDS]; + + +#define MAX_TDM_CHAN 32 +#define MAX_DTMF_DET 16 + +#define HDLC_IMR0_MASK (FRMR_IMR0_RME | FRMR_IMR0_RPF) +#if 0 +#define HDLC_IMR1_MASK (FRMR_IMR1_ALLS | FRMR_IMR1_XDU | FRMR_IMR1_XPR) +#else +#define HDLC_IMR1_MASK (FRMR_IMR1_XDU | FRMR_IMR1_XPR) +#endif + +static inline unsigned int __t4_pci_in(struct t4 *wc, const unsigned int addr) +{ + unsigned int res = readl(&wc->membase[addr]); + return res; +} + +static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + unsigned int tmp; + writel(value, &wc->membase[addr]); + if (pedanticpci) { + tmp = __t4_pci_in(wc, WC_VERSION); + if ((tmp & 0xffff0000) != 0xc01a0000) + dev_notice(&wc->dev->dev, + "Version Synchronization Error!\n"); + } +#if 0 + tmp = __t4_pci_in(wc, addr); + if ((value != tmp) && (addr != WC_LEDS) && (addr != WC_LDATA) && + (addr != WC_GPIO) && (addr != WC_INTR)) + dev_info(&wc->dev->dev, "Tried to load %08x into %08x, " + "but got %08x instead\n", value, addr, tmp); +#endif +} + +static inline void __t4_gpio_set(struct t4 *wc, unsigned bits, unsigned int val) +{ + unsigned int newgpio; + newgpio = wc->gpio & (~bits); + newgpio |= val; + if (newgpio != wc->gpio) { + wc->gpio = newgpio; + __t4_pci_out(wc, WC_GPIO, wc->gpio); + } +} + +static inline void __t4_gpio_setdir(struct t4 *wc, unsigned int bits, unsigned int val) +{ + unsigned int newgpioctl; + newgpioctl = wc->gpioctl & (~bits); + newgpioctl |= val; + if (newgpioctl != wc->gpioctl) { + wc->gpioctl = newgpioctl; + __t4_pci_out(wc, WC_GPIOCTL, wc->gpioctl); + } +} + +static inline void t4_gpio_setdir(struct t4 *wc, unsigned int bits, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_gpio_setdir(wc, bits, val); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void t4_gpio_set(struct t4 *wc, unsigned int bits, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_gpio_set(wc, bits, val); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_pci_out(wc, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void __t4_set_led(struct t4 *wc, int span, int color) +{ + int oldreg = wc->ledreg; + wc->ledreg &= ~(0x3 << (span << 1)); + wc->ledreg |= (color << (span << 1)); + if (oldreg != wc->ledreg) + __t4_pci_out(wc, WC_LEDS, wc->ledreg); +} + +static inline void t4_activate(struct t4 *wc) +{ + wc->ledreg |= WC_ACTIVATE; + t4_pci_out(wc, WC_LEDS, wc->ledreg); +} + +static inline unsigned int t4_pci_in(struct t4 *wc, const unsigned int addr) +{ + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_pci_in(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; +} + +static inline unsigned int __t4_framer_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned int ret; + unit &= 0x3; + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | WC_LFRMR_CS | WC_LREAD); + if (!pedanticpci) { + __t4_pci_in(wc, WC_VERSION); + } else { + __t4_pci_out(wc, WC_VERSION, 0); + } + ret = __t4_pci_in(wc, WC_LDATA); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + + if (unlikely(debug & DEBUG_REGS)) + dev_info(&wc->dev->dev, "Reading unit %d address %02x is " + "%02x\n", unit, addr, ret & 0xff); + + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + + return ret & 0xff; +} + +static inline unsigned int t4_framer_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned long flags; + unsigned int ret; + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_framer_in(wc, unit, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; + +} + +static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unit &= 0x3; + if (unlikely(debug & DEBUG_REGS)) + dev_info(&wc->dev->dev, "Writing %02x to address %02x of " + "unit %d\n", value, addr, unit); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + __t4_pci_out(wc, WC_LDATA, value); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | WC_LFRMR_CS | WC_LWRITE); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + if (unlikely(debug & DEBUG_REGS)) + dev_info(&wc->dev->dev, "Write complete\n"); +#if 0 + if ((addr != FRMR_TXFIFO) && (addr != FRMR_CMDR) && (addr != 0xbc)) + { unsigned int tmp; + tmp = __t4_framer_in(wc, unit, addr); + if (tmp != value) { + dev_notice(&wc->dev->dev, "Expected %d from unit %d " + "register %d but got %d instead\n", + value, unit, addr, tmp); + } } +#endif +} + +static inline void t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_framer_out(wc, unit, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +#ifdef VPM_SUPPORT + +static inline void wait_a_little(void) +{ + unsigned long newjiffies=jiffies+2; + while(jiffies < newjiffies); +} + +static inline unsigned int __t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned int ret; + unit &= 0x7; + __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12)); + __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12) | (1 << 11) | WC_LREAD); + ret = __t4_pci_in(wc, WC_LDATA); + __t4_pci_out(wc, WC_LADDR, 0); + return ret & 0xff; +} + +static inline void __t4_raw_oct_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + int octopt = wc->tspans[0]->spanflags & FLAG_OCTOPT; + if (!octopt) + __t4_gpio_set(wc, 0xff, (addr >> 8)); + __t4_pci_out(wc, WC_LDATA, 0x10000 | (addr & 0xffff)); + if (!octopt) + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE)); + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + if (!octopt) + __t4_gpio_set(wc, 0xff, (value >> 8)); + __t4_pci_out(wc, WC_LDATA, (value & 0xffff)); + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE | WC_LCS)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + __t4_pci_out(wc, WC_LADDR, (0)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); +} + +static inline unsigned int __t4_raw_oct_in(struct t4 *wc, const unsigned int addr) +{ + unsigned int ret; + int octopt = wc->tspans[0]->spanflags & FLAG_OCTOPT; + if (!octopt) + __t4_gpio_set(wc, 0xff, (addr >> 8)); + __t4_pci_out(wc, WC_LDATA, 0x10000 | (addr & 0xffff)); + if (!octopt) + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); +#ifdef PEDANTIC_OCTASIC_CHECKING + __t4_pci_out(wc, WC_LADDR, (WC_LALE)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); +#endif + if (!octopt) { + __t4_gpio_setdir(wc, 0xff, 0x00); + __t4_gpio_set(wc, 0xff, 0x00); + } + __t4_pci_out(wc, WC_LADDR, (WC_LREAD | WC_LALE | WC_LCS)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + if (octopt) { + ret = __t4_pci_in(wc, WC_LDATA) & 0xffff; + } else { + ret = __t4_pci_in(wc, WC_LDATA) & 0xff; + ret |= (__t4_pci_in(wc, WC_GPIO) & 0xff) << 8; + } + __t4_pci_out(wc, WC_LADDR, (0)); + if (!pedanticpci) + __t4_pci_in(wc, WC_VERSION); + if (!octopt) + __t4_gpio_setdir(wc, 0xff, 0xff); + return ret & 0xffff; +} + +static inline unsigned int __t4_oct_in(struct t4 *wc, unsigned int addr) +{ +#ifdef PEDANTIC_OCTASIC_CHECKING + int count = 1000; +#endif + __t4_raw_oct_out(wc, 0x0008, (addr >> 20)); + __t4_raw_oct_out(wc, 0x000a, (addr >> 4) & ((1 << 16) - 1)); + __t4_raw_oct_out(wc, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (1)); +#ifdef PEDANTIC_OCTASIC_CHECKING + while((__t4_raw_oct_in(wc, 0x0000) & (1 << 8)) && --count); + if (count != 1000) + dev_notice(&wc->dev->dev, "Yah, read can be slow...\n"); + if (!count) + dev_notice(&wc->dev->dev, "Read timed out!\n"); +#endif + return __t4_raw_oct_in(wc, 0x0004); +} + +static inline unsigned int t4_oct_in(struct t4 *wc, const unsigned int addr) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_oct_in(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; +} + +static inline unsigned int t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned long flags; + unsigned int ret; + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_vpm_in(wc, unit, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; +} + +static inline void __t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unit &= 0x7; + if (debug & DEBUG_REGS) + dev_notice(&wc->dev->dev, "Writing %02x to address %02x of " + "ec unit %d\n", value, addr, unit); + __t4_pci_out(wc, WC_LADDR, (addr & 0xff)); + __t4_pci_out(wc, WC_LDATA, value); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11)); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11) | WC_LWRITE); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11)); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff)); + __t4_pci_out(wc, WC_LADDR, 0); + if (debug & DEBUG_REGS) + dev_notice(&wc->dev->dev, "Write complete\n"); + + +#if 0 + { unsigned int tmp; + tmp = t4_vpm_in(wc, unit, addr); + if (tmp != value) { + dev_notice(&wc->dev->dev, "Expected %d from unit %d echo " + "register %d but got %d instead\n", + value, unit, addr, tmp); + } } +#endif +} + +static inline void __t4_oct_out(struct t4 *wc, unsigned int addr, unsigned int value) +{ +#ifdef PEDANTIC_OCTASIC_CHECKING + int count = 1000; +#endif + __t4_raw_oct_out(wc, 0x0008, (addr >> 20)); + __t4_raw_oct_out(wc, 0x000a, (addr >> 4) & ((1 << 16) - 1)); + __t4_raw_oct_out(wc, 0x0004, value); + __t4_raw_oct_out(wc, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1); +#ifdef PEDANTIC_OCTASIC_CHECKING + while((__t4_raw_oct_in(wc, 0x0000) & (1 << 8)) && --count); + if (count != 1000) + dev_notice(&wc->dev->dev, "Yah, write can be slow\n"); + if (!count) + dev_notice(&wc->dev->dev, "Write timed out!\n"); +#endif +} + +static inline void t4_oct_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + __t4_oct_out(wc, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_vpm_out(wc, unit, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static const char vpm_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', '*', '#'}; + +static void t4_check_vpm450(struct t4 *wc) +{ + int channel, tone, start, span; + + if (vpm450m_checkirq(wc->vpm450m)) { + while(vpm450m_getdtmf(wc->vpm450m, &channel, &tone, &start)) { + span = channel & 0x3; + channel >>= 2; + if (!wc->t1e1) + channel -= 5; + else + channel -= 1; + if (unlikely(debug)) + dev_info(&wc->dev->dev, "Got tone %s of '%c' " + "on channel %d of span %d\n", + (start ? "START" : "STOP"), + tone, channel, span + 1); + if (test_bit(channel, &wc->tspans[span]->dtmfmask) && (tone != 'u')) { + if (start) { + /* The octasic is supposed to mute us, but... Yah, you + guessed it. */ + if (test_bit(channel, &wc->tspans[span]->dtmfmutemask)) { + unsigned long flags; + struct dahdi_chan *chan = wc->tspans[span]->span.chans[channel]; + int y; + spin_lock_irqsave(&chan->lock, flags); + for (y=0;ynumbufs;y++) { + if ((chan->inreadbuf > -1) && (chan->readidx[y])) + memset(chan->readbuf[chan->inreadbuf], DAHDI_XLAW(0, chan), chan->readidx[y]); + } + spin_unlock_irqrestore(&chan->lock, flags); + } + set_bit(channel, &wc->tspans[span]->dtmfactive); + dahdi_qevent_lock(wc->tspans[span]->span.chans[channel], (DAHDI_EVENT_DTMFDOWN | tone)); + } else { + clear_bit(channel, &wc->tspans[span]->dtmfactive); + dahdi_qevent_lock(wc->tspans[span]->span.chans[channel], (DAHDI_EVENT_DTMFUP | tone)); + } + } + } + } +} + +static void t4_check_vpm400(struct t4 *wc, unsigned int newio) +{ + unsigned int digit, regval = 0; + unsigned int regbyte; + int x, i; + short energy=0; + static unsigned int lastio = 0; + struct t4_span *ts; + + if (debug && (newio != lastio)) + dev_notice(&wc->dev->dev, "Last was %08x, new is %08x\n", + lastio, newio); + + lastio = newio; + + for(x = 0; x < 8; x++) { + if (newio & (1 << (7 - x))) + continue; + ts = wc->tspans[x%4]; + /* Start of DTMF detection process */ + regbyte = t4_vpm_in(wc, x, 0xb8); + t4_vpm_out(wc, x, 0xb8, regbyte); /* Write 1 to clear */ + regval = regbyte << 8; + regbyte = t4_vpm_in(wc, x, 0xb9); + t4_vpm_out(wc, x, 0xb9, regbyte); + regval |= regbyte; + + for(i = 0; (i < MAX_DTMF_DET) && regval; i++) { + if(regval & 0x0001) { + int channel = (i << 1) + (x >> 2); + int base = channel - 1; + + if (!wc->t1e1) + base -= 4; + regbyte = t4_vpm_in(wc, x, 0xa8 + i); + digit = vpm_digits[regbyte]; + if (!(wc->tspans[0]->spanflags & FLAG_VPM2GEN)) { + energy = t4_vpm_in(wc, x, 0x58 + channel); + energy = DAHDI_XLAW(energy, ts->chans[0]); + ts->dtmfenergy[base] = energy; + } + set_bit(base, &ts->dtmfactive); + if (ts->dtmfdigit[base]) { + if (ts->dtmfmask & (1 << base)) + dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFUP | ts->dtmfdigit[base])); + } + ts->dtmfdigit[base] = digit; + if (test_bit(base, &ts->dtmfmask)) + dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFDOWN | digit)); + if (test_bit(base, &ts->dtmfmutemask)) { + /* Mute active receive buffer*/ + unsigned long flags; + struct dahdi_chan *chan = ts->span.chans[base]; + int y; + spin_lock_irqsave(&chan->lock, flags); + for (y=0;ynumbufs;y++) { + if ((chan->inreadbuf > -1) && (chan->readidx[y])) + memset(chan->readbuf[chan->inreadbuf], DAHDI_XLAW(0, chan), chan->readidx[y]); + } + spin_unlock_irqrestore(&chan->lock, flags); + } + if (debug) + dev_notice(&wc->dev->dev, "Digit " + "Seen: %d, Span: %d, channel:" + " %d, energy: %02x, 'channel " + "%d' chip %d\n", digit, x % 4, + base + 1, energy, channel, x); + + } + regval = regval >> 1; + } + if (!(wc->tspans[0]->spanflags & FLAG_VPM2GEN)) + continue; + + /* Start of DTMF off detection process */ + regbyte = t4_vpm_in(wc, x, 0xbc); + t4_vpm_out(wc, x, 0xbc, regbyte); /* Write 1 to clear */ + regval = regbyte << 8; + regbyte = t4_vpm_in(wc, x, 0xbd); + t4_vpm_out(wc, x, 0xbd, regbyte); + regval |= regbyte; + + for(i = 0; (i < MAX_DTMF_DET) && regval; i++) { + if(regval & 0x0001) { + int channel = (i << 1) + (x >> 2); + int base = channel - 1; + + if (!wc->t1e1) + base -= 4; + clear_bit(base, &ts->dtmfactive); + if (ts->dtmfdigit[base]) { + if (test_bit(base, &ts->dtmfmask)) + dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFUP | ts->dtmfdigit[base])); + } + digit = ts->dtmfdigit[base]; + ts->dtmfdigit[base] = 0; + if (debug) + dev_notice(&wc->dev->dev, "Digit " + "Gone: %d, Span: %d, channel:" + " %d, energy: %02x, 'channel " + "%d' chip %d\n", digit, x % 4, + base + 1, energy, channel, x); + + } + regval = regval >> 1; + } + + } +} +#endif + +static void hdlc_stop(struct t4 *wc, unsigned int span) +{ + struct t4_span *t = wc->tspans[span]; + unsigned char imr0, imr1, mode; + int i = 0; + + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Stopping HDLC controller on span " + "%d\n", span+1); + + /* Clear receive and transmit timeslots */ + for (i = 0; i < 4; i++) { + t4_framer_out(wc, span, FRMR_RTR_BASE + i, 0x00); + t4_framer_out(wc, span, FRMR_TTR_BASE + i, 0x00); + } + + imr0 = t4_framer_in(wc, span, FRMR_IMR0); + imr1 = t4_framer_in(wc, span, FRMR_IMR1); + + /* Disable HDLC interrupts */ + imr0 |= HDLC_IMR0_MASK; + t4_framer_out(wc, span, FRMR_IMR0, imr0); + + imr1 |= HDLC_IMR1_MASK; + t4_framer_out(wc, span, FRMR_IMR1, imr1); + + mode = t4_framer_in(wc, span, FRMR_MODE); + mode &= ~FRMR_MODE_HRAC; + t4_framer_out(wc, span, FRMR_MODE, mode); + + t->sigactive = 0; +} + +static inline void __t4_framer_cmd(struct t4 *wc, unsigned int span, int cmd) +{ + __t4_framer_out(wc, span, FRMR_CMDR, cmd); +} + +static inline void t4_framer_cmd_wait(struct t4 *wc, unsigned int span, int cmd) +{ + int sis; + int loops = 0; + + /* XXX could be time consuming XXX */ + for (;;) { + sis = t4_framer_in(wc, span, FRMR_SIS); + if (!(sis & 0x04)) + break; + if (!loops++ && (debug & DEBUG_FRAMER)) { + dev_notice(&wc->dev->dev, "!!!SIS Waiting before cmd " + "%02x\n", cmd); + } + } + if (loops && (debug & DEBUG_FRAMER)) + dev_notice(&wc->dev->dev, "!!!SIS waited %d loops\n", loops); + + t4_framer_out(wc, span, FRMR_CMDR, cmd); +} + +static int hdlc_start(struct t4 *wc, unsigned int span, struct dahdi_chan *chan, unsigned char mode) +{ + struct t4_span *t = wc->tspans[span]; + unsigned char imr0, imr1; + int offset = chan->chanpos; + unsigned long flags; + + if (debug & DEBUG_FRAMER) + dev_info(&wc->dev->dev, "Starting HDLC controller for channel " + "%d span %d\n", offset, span+1); + + if (mode != FRMR_MODE_NO_ADDR_CMP) + return -1; + + mode |= FRMR_MODE_HRAC; + + /* Make sure we're in the right mode */ + t4_framer_out(wc, span, FRMR_MODE, mode); + t4_framer_out(wc, span, FRMR_TSEO, 0x00); + t4_framer_out(wc, span, FRMR_TSBS1, hardhdlcmode); + + /* Set the interframe gaps, etc */ + t4_framer_out(wc, span, FRMR_CCR1, FRMR_CCR1_ITF|FRMR_CCR1_EITS); + + t4_framer_out(wc, span, FRMR_CCR2, FRMR_CCR2_RCRC); + + /* Set up the time slot that we want to tx/rx on */ + t4_framer_out(wc, span, FRMR_TTR_BASE + (offset / 8), (0x80 >> (offset % 8))); + t4_framer_out(wc, span, FRMR_RTR_BASE + (offset / 8), (0x80 >> (offset % 8))); + + imr0 = t4_framer_in(wc, span, FRMR_IMR0); + imr1 = t4_framer_in(wc, span, FRMR_IMR1); + + /* Enable our interrupts again */ + imr0 &= ~HDLC_IMR0_MASK; + t4_framer_out(wc, span, FRMR_IMR0, imr0); + + imr1 &= ~HDLC_IMR1_MASK; + t4_framer_out(wc, span, FRMR_IMR1, imr1); + + /* Reset the signaling controller */ + t4_framer_cmd_wait(wc, span, FRMR_CMDR_SRES); + + spin_lock_irqsave(&wc->reglock, flags); + t->sigchan = chan; + spin_unlock_irqrestore(&wc->reglock, flags); + + t->sigactive = 0; + + return 0; +} + +static void __set_clear(struct t4 *wc, int span) +{ + int i,j; + int oldnotclear; + unsigned short val=0; + struct t4_span *ts = wc->tspans[span]; + + oldnotclear = ts->notclear; + if ((ts->spantype == TYPE_T1) || (ts->spantype == TYPE_J1)) { + for (i=0;i<24;i++) { + j = (i/8); + if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) { + val |= 1 << (7 - (i % 8)); + ts->notclear &= ~(1 << i); + } else + ts->notclear |= (1 << i); + if ((i % 8)==7) { + if (debug) + dev_notice(&wc->dev->dev, "Putting %d " + "in register %02x on span %d" + "\n", val, 0x2f + j, span + 1); + __t4_framer_out(wc, span, 0x2f + j, val); + val = 0; + } + } + } else { + for (i=0;i<31;i++) { + if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) + ts->notclear &= ~(1 << i); + else + ts->notclear |= (1 << i); + } + } + if (ts->notclear != oldnotclear) { + unsigned char reg; + reg = __t4_framer_in(wc, span, FRMR_IMR0); + if (ts->notclear) + reg &= ~0x08; + else + reg |= 0x08; + __t4_framer_out(wc, span, FRMR_IMR0, reg); + } +} + +#if 0 +static void set_clear(struct t4 *wc, int span) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __set_clear(wc, span); + spin_unlock_irqrestore(&wc->reglock, flags); +} +#endif + +static int t4_dacs(struct dahdi_chan *dst, struct dahdi_chan *src) +{ + struct t4 *wc; + struct t4_span *ts; + wc = dst->pvt; + ts = wc->tspans[dst->span->offset]; + if (src && (src->pvt != dst->pvt)) { + if (ts->spanflags & FLAG_2NDGEN) + t4_tsi_unassign(wc, dst->span->offset, dst->chanpos); + wc = src->pvt; + if (ts->spanflags & FLAG_2NDGEN) + t4_tsi_unassign(wc, src->span->offset, src->chanpos); + if (debug) + dev_notice(&wc->dev->dev, "Unassigning %d/%d by " + "default and...\n", src->span->offset, + src->chanpos); + if (debug) + dev_notice(&wc->dev->dev, "Unassigning %d/%d by " + "default\n", dst->span->offset, dst->chanpos); + return -1; + } + if (src) { + t4_tsi_assign(wc, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + if (debug) + dev_notice(&wc->dev->dev, "Assigning channel %d/%d -> " + "%d/%d!\n", src->span->offset, src->chanpos, + dst->span->offset, dst->chanpos); + } else { + t4_tsi_unassign(wc, dst->span->offset, dst->chanpos); + if (debug) + dev_notice(&wc->dev->dev, "Unassigning channel %d/%d!" + "\n", dst->span->offset, dst->chanpos); + } + return 0; +} + +#ifdef VPM_SUPPORT + +void oct_set_reg(void *data, unsigned int reg, unsigned int val) +{ + struct t4 *wc = data; + t4_oct_out(wc, reg, val); +} + +unsigned int oct_get_reg(void *data, unsigned int reg) +{ + struct t4 *wc = data; + unsigned int ret; + ret = t4_oct_in(wc, reg); + return ret; +} + +static int t4_vpm_unit(int span, int channel) +{ + int unit = 0; + switch(vpmspans) { + case 4: + unit = span; + unit += (channel & 1) << 2; + break; + case 2: + unit = span; + unit += (channel & 0x3) << 1; + break; + case 1: + unit = span; + unit += (channel & 0x7); + } + return unit; +} + +static inline struct t4_span *t4_from_span(struct dahdi_span *span) +{ + return container_of(span, struct t4_span, span); +} + +static int t4_echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) +{ + struct t4 *wc = chan->pvt; + struct t4_span *tspan = container_of(chan->span, struct t4_span, span); + int channel; + const struct dahdi_echocan_ops *ops; + const struct dahdi_echocan_features *features; + + if (!vpmsupport || !wc->vpm) + return -ENODEV; + + if (chan->span->offset >= vpmspans) + return -ENODEV; + + if (wc->vpm450m) { + ops = &vpm450m_ec_ops; + features = &vpm450m_ec_features; + } else { + ops = &vpm400m_ec_ops; + features = &vpm400m_ec_features; + } + + if (ecp->param_count > 0) { + dev_warn(&wc->dev->dev, "echo canceller does not support " + "parameters; failing request\n"); + return -EINVAL; + } + + *ec = tspan->ec[chan->chanpos - 1]; + (*ec)->ops = ops; + (*ec)->features = *features; + + channel = wc->t1e1 ? chan->chanpos : chan->chanpos + 4; + + if (wc->vpm450m) { + channel = channel << 2; + channel |= chan->span->offset; + if (debug & DEBUG_ECHOCAN) + dev_notice(&wc->dev->dev, "echocan: Card is %d, " + "Channel is %d, Span is %d, offset is %d " + "length %d\n", wc->num, chan->chanpos, + chan->span->offset, channel, ecp->tap_length); + vpm450m_setec(wc->vpm450m, channel, ecp->tap_length); + } else { + int unit = t4_vpm_unit(chan->span->offset, channel); + + if (debug & DEBUG_ECHOCAN) + dev_notice(&wc->dev->dev, "echocan: Card is %d, " + "Channel is %d, Span is %d, unit is %d, " + "unit offset is %d length %d\n", wc->num, + chan->chanpos, chan->span->offset, unit, + channel, ecp->tap_length); + t4_vpm_out(wc, unit, channel, 0x3e); + } + + return 0; +} + +static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) +{ + struct t4 *wc = chan->pvt; + int channel; + + memset(ec, 0, sizeof(*ec)); + + channel = wc->t1e1 ? chan->chanpos : chan->chanpos + 4; + + if (wc->vpm450m) { + channel = channel << 2; + channel |= chan->span->offset; + if (debug & DEBUG_ECHOCAN) + dev_notice(&wc->dev->dev, "echocan: Card is %d, " + "Channel is %d, Span is %d, offset is %d " + "length 0\n", wc->num, chan->chanpos, + chan->span->offset, channel); + vpm450m_setec(wc->vpm450m, channel, 0); + } else { + int unit = t4_vpm_unit(chan->span->offset, channel); + + if (debug & DEBUG_ECHOCAN) + dev_notice(&wc->dev->dev, "echocan: Card is %d, " + "Channel is %d, Span is %d, unit is %d, " + "unit offset is %d length 0\n", wc->num, + chan->chanpos, chan->span->offset, unit, + channel); + t4_vpm_out(wc, unit, channel, 0x01); + } +} +#endif + +static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) +{ + struct t4_regs regs; + int x; + struct t4 *wc = chan->pvt; +#ifdef VPM_SUPPORT + int j; + int channel; + struct t4_span *ts = wc->tspans[chan->span->offset]; +#endif + +#ifdef VPM_SUPPORT + if (dtmfthreshold == 0) + dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; + if (lastdtmfthreshold != dtmfthreshold) { + lastdtmfthreshold = dtmfthreshold; + t4_vpm_set_dtmf_threshold(wc, dtmfthreshold); + } +#endif + + switch(cmd) { + case WCT4_GET_REGS: + for (x=0;xspan->offset, x); + if (copy_to_user((__user void *) data, ®s, sizeof(regs))) + return -EFAULT; + break; +#ifdef VPM_SUPPORT + case DAHDI_TONEDETECT: + if (get_user(j, (__user int *) data)) + return -EFAULT; + if (!wc->vpm) + return -ENOSYS; + if (j && (vpmdtmfsupport == 0)) + return -ENOSYS; + if (j & DAHDI_TONEDETECT_ON) + set_bit(chan->chanpos - 1, &ts->dtmfmask); + else + clear_bit(chan->chanpos - 1, &ts->dtmfmask); + if (j & DAHDI_TONEDETECT_MUTE) + set_bit(chan->chanpos - 1, &ts->dtmfmutemask); + else + clear_bit(chan->chanpos - 1, &ts->dtmfmutemask); + if (wc->vpm450m) { + channel = (chan->chanpos) << 2; + if (!wc->t1e1) + channel += (4 << 2); + channel |= chan->span->offset; + vpm450m_setdtmf(wc->vpm450m, channel, j & DAHDI_TONEDETECT_ON, j & DAHDI_TONEDETECT_MUTE); + } + return 0; +#endif + default: + return -ENOTTY; + } + return 0; +} + +static void inline t4_hdlc_xmit_fifo(struct t4 *wc, unsigned int span, struct t4_span *ts) +{ + int res, i; + unsigned int size = 32; + unsigned char buf[32]; + + res = dahdi_hdlc_getbuf(ts->sigchan, buf, &size); + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Got buffer sized %d and res %d " + "for %d\n", size, res, span); + if (size > 0) { + ts->sigactive = 1; + + if (debug & DEBUG_FRAMER) { + dev_notice(&wc->dev->dev, "TX("); + for (i = 0; i < size; i++) + dev_notice(&wc->dev->dev, "%s%02x", + (i ? " " : ""), buf[i]); + dev_notice(&wc->dev->dev, ")\n"); + } + + for (i = 0; i < size; i++) + t4_framer_out(wc, span, FRMR_TXFIFO, buf[i]); + + if (res) /* End of message */ { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, + "transmiting XHF|XME\n"); + t4_framer_cmd_wait(wc, span, FRMR_CMDR_XHF | FRMR_CMDR_XME); +#if 0 + ts->sigactive = (__t4_framer_in(wc, span, FRMR_SIS) & FRMR_SIS_XFW) ? 0 : 1; +#endif + ++ts->frames_out; + if ((debug & DEBUG_FRAMER) && !(ts->frames_out & 0x0f)) + dev_notice(&wc->dev->dev, "Transmitted %d " + "frames on span %d\n", ts->frames_out, + span); + } else { /* Still more to transmit */ + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "transmiting XHF\n"); + t4_framer_cmd_wait(wc, span, FRMR_CMDR_XHF); + } + } + else if (res < 0) + ts->sigactive = 0; +} + +static void t4_hdlc_hard_xmit(struct dahdi_chan *chan) +{ + struct t4 *wc = chan->pvt; + int span = chan->span->offset; + struct t4_span *ts = wc->tspans[span]; + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + if (!ts->sigchan) { + dev_notice(&wc->dev->dev, "t4_hdlc_hard_xmit: Invalid (NULL) " + "signalling channel\n"); + spin_unlock_irqrestore(&wc->reglock, flags); + return; + } + spin_unlock_irqrestore(&wc->reglock, flags); + + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "t4_hdlc_hard_xmit on channel %s " + "(sigchan %s), sigactive=%d\n", chan->name, + ts->sigchan->name, ts->sigactive); + + if ((ts->sigchan == chan) && !ts->sigactive) + t4_hdlc_xmit_fifo(wc, span, ts); +} + +static int t4_maint(struct dahdi_span *span, int cmd) +{ + struct t4_span *ts = t4_from_span(span); + struct t4 *wc = ts->owner; + unsigned int reg; +#ifdef DAHDI_SPAN_OPS + unsigned long flags; +#endif + + if (ts->spantype == TYPE_E1) { + switch(cmd) { + case DAHDI_MAINT_NONE: + dev_info(&wc->dev->dev, "Clearing all maint modes\n"); + t4_clear_maint(span); + break; + case DAHDI_MAINT_LOCALLOOP: + dev_info(&wc->dev->dev, + "Turning on local loopback\n"); + t4_clear_maint(span); + reg = t4_framer_in(wc, span->offset, LIM0_T); + t4_framer_out(wc, span->offset, LIM0_T, (reg|LIM0_LL)); + break; +#ifdef DAHDI_SPAN_OPS + case DAHDI_MAINT_NETWORKLINELOOP: + dev_info(&wc->dev->dev, + "Turning on network line loopback\n"); + t4_clear_maint(span); + reg = t4_framer_in(wc, span->offset, LIM1_T); + t4_framer_out(wc, span->offset, LIM1_T, (reg|LIM1_RL)); + break; + case DAHDI_MAINT_NETWORKPAYLOADLOOP: + dev_info(&wc->dev->dev, + "Turning on network payload loopback\n"); + t4_clear_maint(span); + reg = t4_framer_in(wc, span->offset, FMR2_T); + t4_framer_out(wc, span->offset, FMR2_T, (reg|FMR2_PLB)); + break; +#endif + case DAHDI_MAINT_LOOPUP: + case DAHDI_MAINT_LOOPDOWN: + dev_info(&wc->dev->dev, + "Loopup & loopdown supported in E1 mode\n"); + return -ENOSYS; +#ifdef DAHDI_SPAN_OPS + case DAHDI_MAINT_FAS_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, IFASE); + break; + case DAHDI_MAINT_MULTI_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, IMFE); + break; + case DAHDI_MAINT_CRC_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, ICRCE); + break; + case DAHDI_MAINT_CAS_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, ICASE); + break; + case DAHDI_MAINT_PRBS_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, IPE); + break; + case DAHDI_MAINT_BIPOLAR_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, IBV); + break; + case DAHDI_RESET_COUNTERS: + t4_reset_counters(span); + break; + case DAHDI_MAINT_ALARM_SIM: + dev_info(&wc->dev->dev, "Invoking alarm state"); + reg = t4_framer_in(wc, span->offset, FMR0); + t4_framer_out(wc, span->offset, FMR0, (reg|FMR0_SIM)); + break; +#endif + default: + dev_info(&wc->dev->dev, + "Unknown E1 maint command: %d\n", cmd); + return -ENOSYS; + } + } else { + switch(cmd) { + case DAHDI_MAINT_NONE: + dev_info(&wc->dev->dev, "Clearing all maint modes\n"); + t4_clear_maint(span); + break; + case DAHDI_MAINT_LOCALLOOP: + dev_info(&wc->dev->dev, + "Turning on local loopback\n"); + t4_clear_maint(span); + reg = t4_framer_in(wc, span->offset, LIM0_T); + t4_framer_out(wc, span->offset, LIM0_T, (reg|LIM0_LL)); + break; +#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) + case DAHDI_MAINT_NETWORKLINELOOP: + dev_info(&wc->dev->dev, + "Turning on network line loopback\n"); + t4_clear_maint(span); + reg = t4_framer_in(wc, span->offset, LIM1_T); + t4_framer_out(wc, span->offset, LIM1_T, (reg|LIM1_RL)); + break; + case DAHDI_MAINT_NETWORKPAYLOADLOOP: + dev_info(&wc->dev->dev, + "Turning on network payload loopback\n"); + t4_clear_maint(span); + reg = t4_framer_in(wc, span->offset, FMR2_T); + t4_framer_out(wc, span->offset, FMR2_T, (reg|FMR2_PLB)); + break; +#endif + case DAHDI_MAINT_LOOPUP: + dev_info(&wc->dev->dev, "Transmitting loopup code\n"); + t4_clear_maint(span); + t4_framer_out(wc, span->offset, 0x21, 0x50); + break; + case DAHDI_MAINT_LOOPDOWN: + dev_info(&wc->dev->dev, "Transmitting loopdown code\n"); + t4_clear_maint(span); + t4_framer_out(wc, span->offset, 0x21, 0x60); + break; +#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) + case DAHDI_MAINT_FAS_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, IFASE); + break; + case DAHDI_MAINT_MULTI_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, IMFE); + break; + case DAHDI_MAINT_CRC_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, ICRCE); + break; + case DAHDI_MAINT_CAS_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, ICASE); + break; + case DAHDI_MAINT_PRBS_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, IPE); + break; + case DAHDI_MAINT_BIPOLAR_DEFECT: + t4_framer_out(wc, span->offset, IERR_T, IBV); + break; + case DAHDI_MAINT_PRBS: + dev_info(&wc->dev->dev, "PRBS not supported\n"); +#if 0 + dev_notice(&wc->dev->dev, "Enabling PRBS!\n"); + span->mainttimer = 1; + /* Enable PRBS monitor */ + reg = t4_framer_in(wc, span->offset, LCR1_T); + reg |= EPRM; + + /* Setup PRBS xmit */ + t4_framer_out(wc, span->offset, TPC0_T, 0); + + /* Enable PRBS transmit */ + reg |= XPRBS; + reg &= ~LLBP; + reg &= ~FLLB; + t4_framer_out(wc, span->offset, LCR1_T, reg); +#endif + return -ENOSYS; + case DAHDI_RESET_COUNTERS: + t4_reset_counters(span); + break; +#endif +#ifdef DAHDI_SPAN_OPS + case DAHDI_MAINT_ALARM_SIM: + reg = t4_framer_in(wc, span->offset, FMR0); + + /* + * The alarm simulation state machine requires us to + * bring this bit up and down for at least 1 clock cycle + */ + spin_lock_irqsave(&wc->reglock, flags); + __t4_framer_out(wc, span->offset, + FMR0, (reg | FMR0_SIM)); + udelay(1); + __t4_framer_out(wc, span->offset, + FMR0, (reg & ~FMR0_SIM)); + udelay(1); + spin_unlock_irqrestore(&wc->reglock, flags); + + reg = t4_framer_in(wc, span->offset, 0x4e); + if (debug & DEBUG_MAIN) { + dev_info(&wc->dev->dev, + "FRS2(alarm state): %d\n", + ((reg & 0xe0) >> 5)); + } + break; +#endif + default: + dev_info(&wc->dev->dev, "Unknown T1 maint command:%d\n", + cmd); + break; + } + } + return 0; +} + +static int t4_clear_maint(struct dahdi_span *span) +{ + struct t4_span *ts = t4_from_span(span); + struct t4 *wc = ts->owner; + unsigned int reg; + + /* Clear local loop */ + reg = t4_framer_in(wc, span->offset, LIM0_T); + t4_framer_out(wc, span->offset, LIM0_T, (reg & ~LIM0_LL)); + + /* Clear Remote Loop */ + reg = t4_framer_in(wc, span->offset, LIM1_T); + t4_framer_out(wc, span->offset, LIM1_T, (reg & ~LIM1_RL)); + + /* Clear Remote Payload Loop */ + reg = t4_framer_in(wc, span->offset, FMR2_T); + t4_framer_out(wc, span->offset, FMR2_T, (reg & ~FMR2_PLB)); + + /* Clear PRBS */ + reg = t4_framer_in(wc, span->offset, LCR1_T); + t4_framer_out(wc, span->offset, LCR1_T, (reg & ~(XPRBS | EPRM))); + + span->mainttimer = 0; + + return 0; +} + +#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) +static int t4_reset_counters(struct dahdi_span *span) +{ + struct t4_span *ts = t4_from_span(span); + memset(&ts->span.count, 0, sizeof(ts->span.count)); + return 0; +} +#endif + +static int t4_rbsbits(struct dahdi_chan *chan, int bits) +{ + u_char m,c; + int k,n,b; + struct t4 *wc = chan->pvt; + struct t4_span *ts = wc->tspans[chan->span->offset]; + unsigned long flags; + + if (debug & DEBUG_RBS) + dev_notice(&wc->dev->dev, "Setting bits to %d on channel %s\n", + bits, chan->name); + spin_lock_irqsave(&wc->reglock, flags); + k = chan->span->offset; + if (ts->spantype == TYPE_E1) { /* do it E1 way */ + if (chan->chanpos == 16) { + spin_unlock_irqrestore(&wc->reglock, flags); + return 0; + } + n = chan->chanpos - 1; + if (chan->chanpos > 15) n--; + b = (n % 15); + c = ts->txsigs[b]; + m = (n / 15) << 2; /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + ts->txsigs[b] = c; + /* output them to the chip */ + __t4_framer_out(wc,k,0x71 + b,c); + } else if (ts->span.lineconfig & DAHDI_CONFIG_D4) { + n = chan->chanpos - 1; + b = (n/4); + c = ts->txsigs[b]; + m = ((3 - (n % 4)) << 1); /* nibble selector */ + c &= ~(0x3 << m); /* keep the other nibble */ + c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */ + ts->txsigs[b] = c; + /* output them to the chip */ + __t4_framer_out(wc,k,0x70 + b,c); + __t4_framer_out(wc,k,0x70 + b + 6,c); + } else if (ts->span.lineconfig & DAHDI_CONFIG_ESF) { + n = chan->chanpos - 1; + b = (n/2); + c = ts->txsigs[b]; + m = ((n % 2) << 2); /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + ts->txsigs[b] = c; + /* output them to the chip */ + __t4_framer_out(wc,k,0x70 + b,c); + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (debug & DEBUG_RBS) + dev_notice(&wc->dev->dev, "Finished setting RBS bits\n"); + return 0; +} + +static int t4_shutdown(struct dahdi_span *span) +{ + int tspan; + int wasrunning; + unsigned long flags; + struct t4_span *ts = t4_from_span(span); + struct t4 *wc = ts->owner; + + tspan = span->offset + 1; + if (tspan < 0) { + dev_notice(&wc->dev->dev, "opvxd115: Span '%d' isn't us?\n", + span->spanno); + return -1; + } + + if (debug & DEBUG_MAIN) + dev_notice(&wc->dev->dev, "Shutting down span %d (%s)\n", + span->spanno, span->name); + + /* Stop HDLC controller if runned */ + if (ts->sigchan) + hdlc_stop(wc, span->offset); + + spin_lock_irqsave(&wc->reglock, flags); + wasrunning = span->flags & DAHDI_FLAG_RUNNING; + + span->flags &= ~DAHDI_FLAG_RUNNING; + __t4_set_led(wc, span->offset, WC_OFF); + if ((wc->numspans == 1) && + (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING))) { + /* No longer in use, disable interrupts */ + dev_info(&wc->dev->dev, "opvxd115: Disabling interrupts since " + "there are no active spans\n"); + set_bit(T4_STOP_DMA, &wc->checkflag); + } else + set_bit(T4_CHECK_TIMING, &wc->checkflag); + + spin_unlock_irqrestore(&wc->reglock, flags); + + /* Wait for interrupt routine to shut itself down */ + msleep(10); + if (wasrunning) + wc->spansstarted--; + + if (debug & DEBUG_MAIN) + dev_notice(&wc->dev->dev, "Span %d (%s) shutdown\n", + span->spanno, span->name); + return 0; +} + +static void t4_chan_set_sigcap(struct dahdi_span *span, int x) +{ + struct t4_span *wc = container_of(span, struct t4_span, span); + struct dahdi_chan *chan = wc->chans[x]; + chan->sigcap = DAHDI_SIG_CLEAR; + /* E&M variant supported depends on span type */ + if (wc->spantype == TYPE_E1) { + /* E1 sigcap setup */ + if (span->lineconfig & DAHDI_CONFIG_CCS) { + /* CCS setup */ + chan->sigcap |= DAHDI_SIG_MTP2 | DAHDI_SIG_SF | + DAHDI_SIG_HARDHDLC; + return; + } + /* clear out sig and sigcap for channel 16 on E1 CAS + * lines, otherwise, set it correctly */ + if (x == 15) { + /* CAS signaling channel setup */ + wc->chans[15]->sigcap = 0; + wc->chans[15]->sig = 0; + return; + } + /* normal CAS setup */ + chan->sigcap |= DAHDI_SIG_EM_E1 | DAHDI_SIG_FXSLS | + DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_SF | + DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_FXOKS | + DAHDI_SIG_CAS | DAHDI_SIG_DACS_RBS; + } else { + /* T1 sigcap setup */ + chan->sigcap |= DAHDI_SIG_EM | DAHDI_SIG_FXSLS | + DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_MTP2 | + DAHDI_SIG_SF | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | + DAHDI_SIG_FXOKS | DAHDI_SIG_CAS | DAHDI_SIG_DACS_RBS | + DAHDI_SIG_HARDHDLC; + } +} + +static int t4_spanconfig(struct file *file, struct dahdi_span *span, + struct dahdi_lineconfig *lc) +{ + int i; + struct t4_span *ts = t4_from_span(span); + struct t4 *wc = ts->owner; + + if (debug) + dev_info(&wc->dev->dev, "About to enter spanconfig!\n"); + if (debug & DEBUG_MAIN) + dev_notice(&wc->dev->dev, "opvxd115: Configuring span %d\n", + span->spanno); + + if (lc->sync < 0) + lc->sync = 0; + if (lc->sync > wc->numspans) + lc->sync = 0; + + /* remove this span number from the current sync sources, if there */ + for(i = 0; i < wc->numspans; i++) { + if (wc->tspans[i]->sync == span->spanno) { + wc->tspans[i]->sync = 0; + wc->tspans[i]->psync = 0; + } + } + wc->tspans[span->offset]->syncpos = lc->sync; + /* if a sync src, put it in proper place */ + if (lc->sync) { + wc->tspans[lc->sync - 1]->sync = span->spanno; + wc->tspans[lc->sync - 1]->psync = span->offset + 1; + } + set_bit(T4_CHECK_TIMING, &wc->checkflag); + + /* Make sure this is clear in case of multiple startup and shutdown + * iterations */ + clear_bit(T4_STOP_DMA, &wc->checkflag); + + /* make sure that sigcaps gets updated if necessary */ + for (i = 0; i < span->channels; i++) + t4_chan_set_sigcap(span, i); + + /* If we're already running, then go ahead and apply the changes */ + if (span->flags & DAHDI_FLAG_RUNNING) + return t4_startup(file, span); + + if (debug) + dev_info(&wc->dev->dev, "Done with spanconfig!\n"); + return 0; +} + +static int t4_chanconfig(struct file *file, struct dahdi_chan *chan, + int sigtype) +{ + int alreadyrunning; + unsigned long flags; + struct t4 *wc = chan->pvt; + struct t4_span *ts = wc->tspans[chan->span->offset]; + + alreadyrunning = ts->span.flags & DAHDI_FLAG_RUNNING; + if (debug & DEBUG_MAIN) { + if (alreadyrunning) + dev_notice(&wc->dev->dev, "opvxd115: Reconfigured " + "channel %d (%s) sigtype %d\n", + chan->channo, chan->name, sigtype); + else + dev_notice(&wc->dev->dev, "opvxd115: Configured channel" + " %d (%s) sigtype %d\n", + chan->channo, chan->name, sigtype); + } + + spin_lock_irqsave(&wc->reglock, flags); + + if (alreadyrunning) + __set_clear(wc, chan->span->offset); + + spin_unlock_irqrestore(&wc->reglock, flags); + + /* (re)configure signalling channel */ + if ((sigtype == DAHDI_SIG_HARDHDLC) || (ts->sigchan == chan)) { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "%sonfiguring hardware HDLC " + "on %s\n", + ((sigtype == DAHDI_SIG_HARDHDLC) ? "C" : "Unc"), + chan->name); + if (alreadyrunning) { + if (ts->sigchan) + hdlc_stop(wc, ts->sigchan->span->offset); + if (sigtype == DAHDI_SIG_HARDHDLC) { + if (hdlc_start(wc, chan->span->offset, chan, ts->sigmode)) { + dev_notice(&wc->dev->dev, "Error " + "initializing signalling " + "controller\n"); + return -1; + } + } else { + spin_lock_irqsave(&wc->reglock, flags); + ts->sigchan = NULL; + spin_unlock_irqrestore(&wc->reglock, flags); + } + + } + else { + spin_lock_irqsave(&wc->reglock, flags); + ts->sigchan = (sigtype == DAHDI_SIG_HARDHDLC) ? chan : NULL; + spin_unlock_irqrestore(&wc->reglock, flags); + ts->sigactive = 0; + } + } + return 0; +} + +static int t4_open(struct dahdi_chan *chan) +{ + return 0; +} + +static int t4_close(struct dahdi_chan *chan) +{ + return 0; +} + +static void set_span_devicetype(struct t4 *wc) +{ + int x; + struct t4_span *ts; + + for (x = 0; x < wc->numspans; x++) { + ts = wc->tspans[x]; + dahdi_copy_string(ts->span.devicetype, wc->variety, sizeof(ts->span.devicetype)); + if (wc->vpm == T4_VPM_PRESENT) { + if (!wc->vpm450m) + strncat(ts->span.devicetype, " (VPM400M)", sizeof(ts->span.devicetype) - 1); + else + strncat(ts->span.devicetype, " (VPMOCT032)", + sizeof(ts->span.devicetype) - 1); + } + } +} + +/* The number of cards we have seen with each + possible 'order' switch setting. +*/ +static unsigned int order_index[16]; + +static void setup_chunks(struct t4 *wc, int which) +{ + struct t4_span *ts; + int offset = 1; + int x, y; + int gen2; + + if (!wc->t1e1) + offset += 4; + + gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN); + + for (x = 0; x < wc->numspans; x++) { + ts = wc->tspans[x]; + ts->writechunk = (void *)(wc->writechunk + (x * 32 * 2) + (which * (1024 >> 2))); + ts->readchunk = (void *)(wc->readchunk + (x * 32 * 2) + (which * (1024 >> 2))); + for (y=0;ytspans[x]->span.channels;y++) { + struct dahdi_chan *mychans = ts->chans[y]; + if (gen2) { + mychans->writechunk = (void *)(wc->writechunk + ((x * 32 + y + offset) * 2) + (which * (1024 >> 2))); + mychans->readchunk = (void *)(wc->readchunk + ((x * 32 + y + offset) * 2) + (which * (1024 >> 2))); + } + } + } +} + +#ifdef DAHDI_SPAN_OPS +static const struct dahdi_span_ops t4_gen1_span_ops = { + .owner = THIS_MODULE, + .spanconfig = t4_spanconfig, + .chanconfig = t4_chanconfig, + .startup = t4_startup, + .shutdown = t4_shutdown, + .rbsbits = t4_rbsbits, + .maint = t4_maint, + .open = t4_open, + .close = t4_close, + .ioctl = t4_ioctl, + .hdlc_hard_xmit = t4_hdlc_hard_xmit, +}; + +static const struct dahdi_span_ops t4_gen2_span_ops = { + .owner = THIS_MODULE, + .spanconfig = t4_spanconfig, + .chanconfig = t4_chanconfig, + .startup = t4_startup, + .shutdown = t4_shutdown, + .rbsbits = t4_rbsbits, + .maint = t4_maint, + .open = t4_open, + .close = t4_close, + .ioctl = t4_ioctl, + .hdlc_hard_xmit = t4_hdlc_hard_xmit, + .dacs = t4_dacs, +#ifdef VPM_SUPPORT + .echocan_create = t4_echocan_create, +#endif +}; +#endif + +static void init_spans(struct t4 *wc) +{ + int x,y; + int gen2; + struct t4_span *ts; + unsigned int reg; + + gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN); + for (x = 0; x < wc->numspans; x++) { + ts = wc->tspans[x]; + sprintf(ts->span.name, "D115/D130/%d/%d", wc->num, x + 1); + snprintf(ts->span.desc, sizeof(ts->span.desc) - 1, + "D115/D130 (E1|T1) Card %d Span %d", wc->num, x+1); + ts->span.manufacturer = "OpenVox"; + if (order_index[wc->order] == 1) + snprintf(ts->span.location, sizeof(ts->span.location) - 1, "Board ID Switch %d", wc->order); + else + snprintf(ts->span.location, sizeof(ts->span.location) - 1, + "PCI%s Bus %02d Slot %02d", (ts->spanflags & FLAG_EXPRESS) ? " Express" : " ", + wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); + switch (ts->spantype) { + case TYPE_T1: + ts->span.spantype = "T1"; + break; + case TYPE_E1: + ts->span.spantype = "E1"; + break; + case TYPE_J1: + ts->span.spantype = "J1"; + break; + } +#ifdef DAHDI_SPAN_MODULE + ts->span.owner = THIS_MODULE; +#endif +#ifdef DAHDI_SPAN_OPS + if (gen2) { + ts->span.ops = &t4_gen2_span_ops; + } else { + ts->span.ops = &t4_gen1_span_ops; + } +#else + ts->span.spanconfig = t4_spanconfig; + ts->span.chanconfig = t4_chanconfig; + ts->span.startup = t4_startup; + ts->span.shutdown = t4_shutdown; + ts->span.rbsbits = t4_rbsbits; + ts->span.maint = t4_maint; + ts->span.open = t4_open; + ts->span.close = t4_close; + ts->span.ioctl = t4_ioctl; + ts->span.hdlc_hard_xmit = t4_hdlc_hard_xmit; + if (gen2) { +#ifdef VPM_SUPPORT + if (vpmsupport) + ts->span.echocan_create = t4_echocan_create; +#endif + ts->span.dacs = t4_dacs; + } + ts->span.pvt = ts; +#endif + ts->span.irq = wc->dev->irq; + + /* HDLC Specific init */ + ts->sigchan = NULL; + ts->sigmode = sigmode; + ts->sigactive = 0; + + if (ts->spantype == TYPE_T1 || ts->spantype == TYPE_J1) { + ts->span.channels = 24; + ts->span.deflaw = DAHDI_LAW_MULAW; + ts->span.linecompat = DAHDI_CONFIG_AMI | + DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 | + DAHDI_CONFIG_ESF; + } else { + ts->span.channels = 31; + ts->span.deflaw = DAHDI_LAW_ALAW; + ts->span.linecompat = DAHDI_CONFIG_AMI | + DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | + DAHDI_CONFIG_CRC4; + } + ts->span.chans = ts->chans; + ts->span.flags = DAHDI_FLAG_RBS; + + ts->owner = wc; + ts->span.offset = x; + ts->writechunk = (void *)(wc->writechunk + x * 32 * 2); + ts->readchunk = (void *)(wc->readchunk + x * 32 * 2); + + for (y=0;ytspans[x]->span.channels;y++) { + struct dahdi_chan *mychans = ts->chans[y]; + sprintf(mychans->name, "D115/D130/%d/%d/%d", wc->num, x + 1, y + 1); + t4_chan_set_sigcap(&ts->span, x); + mychans->pvt = wc; + mychans->chanpos = y + 1; + } + + /* Enable 1sec timer interrupt */ + reg = t4_framer_in(wc, x, FMR1_T); + t4_framer_out(wc, x, FMR1_T, (reg | FMR1_ECM)); + + /* Enable Errored Second interrupt */ + t4_framer_out(wc, x, ESM, 0); + +#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) + t4_reset_counters(&ts->span); +#endif + } + + set_span_devicetype(wc); + setup_chunks(wc, 0); + wc->lastindex = 0; +} + +static void t4_serial_setup(struct t4 *wc, int unit) +{ + if (!wc->globalconfig) { + wc->globalconfig = 1; + if (debug) + dev_info(&wc->dev->dev, "opvxd115: Setting up global " + "serial parameters\n"); + t4_framer_out(wc, 0, 0x85, 0xe0); /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 */ + t4_framer_out(wc, 0, 0x08, 0x01); /* IPC: Interrupt push/pull active low */ + + /* Global clocks (8.192 Mhz CLK) */ + t4_framer_out(wc, 0, 0x92, 0x00); + t4_framer_out(wc, 0, 0x93, 0x18); + t4_framer_out(wc, 0, 0x94, 0xfb); + t4_framer_out(wc, 0, 0x95, 0x0b); + t4_framer_out(wc, 0, 0x96, 0x00); + t4_framer_out(wc, 0, 0x97, 0x0b); + t4_framer_out(wc, 0, 0x98, 0xdb); + t4_framer_out(wc, 0, 0x99, 0xdf); + } + + /* Configure interrupts */ + t4_framer_out(wc, unit, FRMR_GCR, 0x00); /* GCR: Interrupt on Activation/Deactivation of each */ + + /* Configure system interface */ + t4_framer_out(wc, unit, FRMR_SIC1, 0xc2); /* SIC1: 8.192 Mhz clock/bus, double buffer receive / transmit, byte interleaved */ + t4_framer_out(wc, unit, FRMR_SIC2, 0x20 | (unit << 1)); /* SIC2: No FFS, no center receive eliastic buffer, phase */ + t4_framer_out(wc, unit, FRMR_SIC3, 0x04); /* SIC3: Edges for capture */ + t4_framer_out(wc, unit, FRMR_CMR2, 0x00); /* CMR2: We provide sync and clock for tx and rx. */ + if (!wc->t1e1) { /* T1 mode */ + t4_framer_out(wc, unit, FRMR_XC0, 0x03); /* XC0: Normal operation of Sa-bits */ + t4_framer_out(wc, unit, FRMR_XC1, 0x84); /* XC1: 0 offset */ + if (wc->tspans[unit]->spantype == TYPE_J1) + t4_framer_out(wc, unit, FRMR_RC0, 0x83); /* RC0: Just shy of 1023 */ + else + t4_framer_out(wc, unit, FRMR_RC0, 0x03); /* RC0: Just shy of 1023 */ + t4_framer_out(wc, unit, FRMR_RC1, 0x84); /* RC1: The rest of RC0 */ + } else { /* E1 mode */ + t4_framer_out(wc, unit, FRMR_XC0, 0x00); /* XC0: Normal operation of Sa-bits */ + t4_framer_out(wc, unit, FRMR_XC1, 0x04); /* XC1: 0 offset */ + t4_framer_out(wc, unit, FRMR_RC0, 0x04); /* RC0: Just shy of 1023 */ + t4_framer_out(wc, unit, FRMR_RC1, 0x04); /* RC1: The rest of RC0 */ + } + + /* Configure ports */ + t4_framer_out(wc, unit, 0x80, 0x00); /* PC1: SPYR/SPYX input on RPA/XPA */ + if (wc->falc31) { + t4_framer_out(wc, unit, 0x81, 0xBB); /* PC2: RMFB/XSIG output/input on RPB/XPB */ + t4_framer_out(wc, unit, 0x82, 0xBB); /* PC3: Some unused stuff */ + t4_framer_out(wc, unit, 0x83, 0xBB); /* PC4: Some more unused stuff */ + } else { + t4_framer_out(wc, unit, 0x81, 0x22); /* PC2: RMFB/XSIG output/input on RPB/XPB */ + t4_framer_out(wc, unit, 0x82, 0x65); /* PC3: Some unused stuff */ + t4_framer_out(wc, unit, 0x83, 0x35); /* PC4: Some more unused stuff */ + } + t4_framer_out(wc, unit, 0x84, 0x01); /* PC5: XMFS active low, SCLKR is input, RCLK is output */ + if (debug & DEBUG_MAIN) + dev_notice(&wc->dev->dev, "Successfully initialized serial " + "bus for unit %d\n", unit); +} + +static int syncsrc = 0; +static int syncnum = 0 /* -1 */; +static int syncspan = 0; +#ifdef DEFINE_SPINLOCK +static DEFINE_SPINLOCK(synclock); +#else +static spinlock_t synclock = SPIN_LOCK_UNLOCKED; +#endif + +static void __t4_set_rclk_src(struct t4 *wc, int span) +{ + int cmr1 = 0x38; /* Clock Mode: RCLK sourced by DCO-R1 + by default, Disable Clock-Switching */ + + cmr1 |= (span << 6); + __t4_framer_out(wc, 0, 0x44, cmr1); + + dev_info(&wc->dev->dev, "RCLK source set to span %d\n", span+1); +} + +static void __t4_set_sclk_src(struct t4 *wc, int mode, int master, int slave) +{ + if (slave) { + wc->dmactrl |= (1 << 25); + dev_info(&wc->dev->dev, "SCLK is slaved to timing cable\n"); + } else { + wc->dmactrl &= ~(1 << 25); + } + + if (master) { + wc->dmactrl |= (1 << 24); + dev_info(&wc->dev->dev, "SCLK is master to timing cable\n"); + } else { + wc->dmactrl &= ~(1 << 24); + } + + if (mode == WC_RECOVER) + wc->dmactrl |= (1 << 29); /* Recover timing from RCLK */ + + if (mode == WC_SELF) + wc->dmactrl &= ~(1 << 29);/* Provide timing from MCLK */ + + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)) +static ssize_t t4_timing_master_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct t4 *wc = dev_get_drvdata(dev); + if (wc->dmactrl & (1 << 29)) + return sprintf(buf, "%d\n", wc->syncsrc); + else + return sprintf(buf, "%d\n", -1); +} + +static DEVICE_ATTR(timing_master, 0400, t4_timing_master_show, NULL); + +static void create_sysfs_files(struct t4 *wc) +{ + int ret; + ret = device_create_file(&wc->dev->dev, + &dev_attr_timing_master); + if (ret) { + dev_info(&wc->dev->dev, + "Failed to create device attributes.\n"); + } +} + +static void remove_sysfs_files(struct t4 *wc) +{ + device_remove_file(&wc->dev->dev, + &dev_attr_timing_master); +} + +#else + +static inline void create_sysfs_files(struct t4 *wc) { return; } +static inline void remove_sysfs_files(struct t4 *wc) { return; } + +#endif /* LINUX_KERNEL > 2.6.18 */ + +static inline void __t4_update_timing(struct t4 *wc) +{ + int i; + /* update sync src info */ + if (wc->syncsrc != syncsrc) { + dev_info(&wc->dev->dev, "Swapping card %d from %d to %d\n", + wc->num, wc->syncsrc, syncsrc); + wc->syncsrc = syncsrc; + /* Update sync sources */ + for (i = 0; i < wc->numspans; i++) { + wc->tspans[i]->span.syncsrc = wc->syncsrc; + } + if (syncnum == wc->num) { + __t4_set_rclk_src(wc, syncspan-1); + __t4_set_sclk_src(wc, WC_RECOVER, 1, 0); + if (debug) + dev_notice(&wc->dev->dev, "Card %d, using sync " + "span %d, master\n", wc->num, syncspan); + } else { + __t4_set_sclk_src(wc, WC_RECOVER, 0, 1); + if (debug) + dev_notice(&wc->dev->dev, "Card %d, using " + "Timing Bus, NOT master\n", wc->num); + } + } +} + +static int __t4_findsync(struct t4 *wc) +{ + int i; + int x; + unsigned long flags; + int p; + int nonzero; + int newsyncsrc = 0; /* DAHDI span number */ + int newsyncnum = 0; /* opvxd115 card number */ + int newsyncspan = 0; /* span on given opvxd115 card */ + spin_lock_irqsave(&synclock, flags); +#if 1 + if (!wc->num) { + /* If we're the first card, go through all the motions, up to 8 levels + of sync source */ + p = 1; + while (p < 8) { + nonzero = 0; + for (x=0;cards[x];x++) { + for (i = 0; i < wc->numspans; i++) { + if (cards[x]->tspans[i]->syncpos) { + nonzero = 1; + if ((cards[x]->tspans[i]->syncpos == p) && + !(cards[x]->tspans[i]->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_LOOPBACK)) && + (cards[x]->tspans[i]->span.flags & DAHDI_FLAG_RUNNING)) { + /* This makes a good sync source */ + newsyncsrc = cards[x]->tspans[i]->span.spanno; + newsyncnum = x; + newsyncspan = i + 1; + /* Jump out */ + goto found; + } + } + } + } + if (nonzero) + p++; + else + break; + } +found: + if ((syncnum != newsyncnum) || (syncsrc != newsyncsrc) || (newsyncspan != syncspan)) { + if (debug) + dev_notice(&wc->dev->dev, "New syncnum: %d " + "(was %d), syncsrc: %d (was %d), " + "syncspan: %d (was %d)\n", newsyncnum, + syncnum, newsyncsrc, syncsrc, + newsyncspan, syncspan); + syncnum = newsyncnum; + syncsrc = newsyncsrc; + syncspan = newsyncspan; + for (x=0;cards[x];x++) { + __t4_update_timing(cards[x]); + } + } + } + __t4_update_timing(wc); +#endif + spin_unlock_irqrestore(&synclock, flags); + return 0; +} + +static void __t4_set_timing_source_auto(struct t4 *wc) +{ + int x; + int firstprio, secondprio; + firstprio = secondprio = 4; + + if (debug) + dev_info(&wc->dev->dev, "timing source auto\n"); + clear_bit(T4_CHECK_TIMING, &wc->checkflag); + if (timingcable) { + __t4_findsync(wc); + } else { + if (debug) + dev_info(&wc->dev->dev, "Evaluating spans for timing " + "source\n"); + for (x=0;xnumspans;x++) { + if ((wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) && + !(wc->tspans[x]->span.alarms & (DAHDI_ALARM_RED | + DAHDI_ALARM_BLUE))) { + if (debug) + dev_info(&wc->dev->dev, "span %d is " + "green : syncpos %d\n", x+1, + wc->tspans[x]->syncpos); + if (wc->tspans[x]->syncpos) { + /* Valid rsync source in recovered + timing mode */ + if (firstprio == 4) + firstprio = x; + else if (wc->tspans[x]->syncpos < + wc->tspans[firstprio]->syncpos) + firstprio = x; + } else { + /* Valid rsync source in system timing + mode */ + if (secondprio == 4) + secondprio = x; + } + } + } + if (firstprio != 4) { + wc->syncsrc = firstprio; + __t4_set_rclk_src(wc, firstprio); + __t4_set_sclk_src(wc, WC_RECOVER, 0, 0); + dev_info(&wc->dev->dev, "Recovered timing mode, "\ + "RCLK set to span %d\n", + firstprio+1); + } else if (secondprio != 4) { + wc->syncsrc = -1; + __t4_set_rclk_src(wc, secondprio); + __t4_set_sclk_src(wc, WC_SELF, 0, 0); + dev_info(&wc->dev->dev, "System timing mode, "\ + "RCLK set to span %d\n", + secondprio+1); + } else { + wc->syncsrc = -1; + dev_info(&wc->dev->dev, "All spans in alarm : No valid"\ + "span to source RCLK from\n"); + /* Default rclk to lock with span 1 */ + __t4_set_rclk_src(wc, 0); + __t4_set_sclk_src(wc, WC_SELF, 0, 0); + } + } +} + +static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlevel) +{ + unsigned int fmr4, fmr2, fmr1, fmr0, lim2; + char *framing, *line; + int mytxlevel; + if ((txlevel > 7) || (txlevel < 4)) + mytxlevel = 0; + else + mytxlevel = txlevel - 4; + fmr1 = 0x9c; /* FMR1: Mode 1, T1 mode, CRC on for ESF, 8.192 Mhz system data rate, no XAIS */ + fmr2 = 0x20; /* FMR2: no payload loopback, don't auto yellow */ + fmr4 = 0x0c; /* FMR4: Lose sync on 2 out of 5 framing bits, auto resync */ + lim2 = 0x21; /* LIM2: 50% peak is a "1", Advanced Loss recovery */ + lim2 |= (mytxlevel << 6); /* LIM2: Add line buildout */ + __t4_framer_out(wc, unit, 0x1d, fmr1); + __t4_framer_out(wc, unit, 0x1e, fmr2); + + /* Configure line interface */ + if (lineconfig & DAHDI_CONFIG_AMI) { + line = "AMI"; + /* workaround for errata #2 in ES v3 09-10-16 */ + fmr0 = (wc->falc31) ? 0xb0 : 0xa0; + } else { + line = "B8ZS"; + fmr0 = 0xf0; + } + if (lineconfig & DAHDI_CONFIG_D4) { + framing = "D4"; + } else { + framing = "ESF"; + fmr4 |= 0x2; + fmr2 |= 0xc0; + } + __t4_framer_out(wc, unit, 0x1c, fmr0); + __t4_framer_out(wc, unit, 0x20, fmr4); + __t4_framer_out(wc, unit, 0x21, 0x40); /* FMR5: Enable RBS mode */ + + __t4_framer_out(wc, unit, 0x37, 0xf0 ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ + __t4_framer_out(wc, unit, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ + + __t4_framer_out(wc, unit, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ + __t4_framer_out(wc, unit, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ + + if (wc->falc31) { + if (debug) + dev_info(&wc->dev->dev, "card %d span %d: setting Rtx " + "to 0ohm for T1\n", wc->num, unit); + __t4_framer_out(wc, unit, 0x86, 0x00); /* PC6: set Rtx to 0ohm for T1 */ + + // Hitting the bugfix register to fix errata #3 + __t4_framer_out(wc, unit, 0xbd, 0x05); + } + + __t4_framer_out(wc, unit, 0x3a, lim2); /* LIM2: 50% peak amplitude is a "1" */ + __t4_framer_out(wc, unit, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ + __t4_framer_out(wc, unit, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ + + /* Generate pulse mask for T1 */ + switch(mytxlevel) { + case 3: + __t4_framer_out(wc, unit, 0x26, 0x07); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x01); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ + break; + case 2: + __t4_framer_out(wc, unit, 0x26, 0x8c); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x11); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */ + break; + case 1: + __t4_framer_out(wc, unit, 0x26, 0x8c); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x01); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ + break; + case 0: + default: + __t4_framer_out(wc, unit, 0x26, 0xd7); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x22); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */ + break; + } + + /* Don't mask framer interrupts if hardware HDLC is in use */ + __t4_framer_out(wc, unit, FRMR_IMR0, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR0_MASK : 0)); /* IMR0: We care about CAS changes, etc */ + __t4_framer_out(wc, unit, FRMR_IMR1, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR1_MASK : 0)); /* IMR1: We care about nothing */ + __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: All the alarm stuff! */ + __t4_framer_out(wc, unit, 0x17, 0x34); /* IMR3: AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: Slips on transmit */ + + dev_info(&wc->dev->dev, "Span %d configured for %s/%s\n", unit + 1, + framing, line); +} + +static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig) +{ + unsigned int fmr2, fmr1, fmr0; + unsigned int cas = 0; + unsigned int imr3extra=0; + char *crc4 = ""; + char *framing, *line; + fmr1 = 0x44; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */ + fmr2 = 0x03; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */ + if (lineconfig & DAHDI_CONFIG_CRC4) { + fmr1 |= 0x08; /* CRC4 transmit */ + fmr2 |= 0xc0; /* CRC4 receive */ + crc4 = "/CRC4"; + } + __t4_framer_out(wc, unit, 0x1d, fmr1); + __t4_framer_out(wc, unit, 0x1e, fmr2); + + /* Configure line interface */ + if (lineconfig & DAHDI_CONFIG_AMI) { + line = "AMI"; + /* workaround for errata #2 in ES v3 09-10-16 */ + fmr0 = (wc->falc31) ? 0xb0 : 0xa0; + } else { + line = "HDB3"; + fmr0 = 0xf0; + } + if (lineconfig & DAHDI_CONFIG_CCS) { + framing = "CCS"; + imr3extra = 0x28; + } else { + framing = "CAS"; + cas = 0x40; + } + __t4_framer_out(wc, unit, 0x1c, fmr0); + + __t4_framer_out(wc, unit, 0x37, 0xf0 /*| 0x6 */ ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ + __t4_framer_out(wc, unit, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ + + __t4_framer_out(wc, unit, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ + __t4_framer_out(wc, unit, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ + + if (wc->falc31) { + if (debug) + dev_info(&wc->dev->dev, + "setting Rtx to 7.5ohm for E1\n"); + __t4_framer_out(wc, unit, 0x86, 0x40); /* PC6: turn on 7.5ohm Rtx for E1 */ + } + + /* Condition receive line interface for E1 after reset */ + __t4_framer_out(wc, unit, 0xbb, 0x17); + __t4_framer_out(wc, unit, 0xbc, 0x55); + __t4_framer_out(wc, unit, 0xbb, 0x97); + __t4_framer_out(wc, unit, 0xbb, 0x11); + __t4_framer_out(wc, unit, 0xbc, 0xaa); + __t4_framer_out(wc, unit, 0xbb, 0x91); + __t4_framer_out(wc, unit, 0xbb, 0x12); + __t4_framer_out(wc, unit, 0xbc, 0x55); + __t4_framer_out(wc, unit, 0xbb, 0x92); + __t4_framer_out(wc, unit, 0xbb, 0x0c); + __t4_framer_out(wc, unit, 0xbb, 0x00); + __t4_framer_out(wc, unit, 0xbb, 0x8c); + + __t4_framer_out(wc, unit, 0x3a, 0x20); /* LIM2: 50% peak amplitude is a "1" */ + __t4_framer_out(wc, unit, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ + __t4_framer_out(wc, unit, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ + + __t4_framer_out(wc, unit, 0x20, 0x9f); /* XSW: Spare bits all to 1 */ + __t4_framer_out(wc, unit, 0x21, 0x1c|cas); /* XSP: E-bit set when async. AXS auto, XSIF to 1 */ + + + /* Generate pulse mask for E1 */ + __t4_framer_out(wc, unit, 0x26, 0x54); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x02); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ + + /* Don't mask framer interrupts if hardware HDLC is in use */ + __t4_framer_out(wc, unit, FRMR_IMR0, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR0_MASK : 0)); /* IMR0: We care about CRC errors, CAS changes, etc */ + __t4_framer_out(wc, unit, FRMR_IMR1, 0x3f & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR1_MASK : 0)); /* IMR1: We care about loopup / loopdown */ + __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: We care about all the alarm stuff! */ + __t4_framer_out(wc, unit, 0x17, 0x04 | imr3extra); /* IMR3: AIS */ + __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */ + + dev_info(&wc->dev->dev, "opvxd115: Span %d configured for %s/%s%s\n", + unit + 1, framing, line, crc4); +} + +static int t4_startup(struct file *file, struct dahdi_span *span) +{ +#ifdef SUPPORT_GEN1 + int i; +#endif + int tspan; + unsigned long flags; + int alreadyrunning; + struct t4_span *ts = t4_from_span(span); + struct t4 *wc = ts->owner; + + set_bit(T4_IGNORE_LATENCY, &wc->checkflag); + if (debug) + dev_info(&wc->dev->dev, "About to enter startup!\n"); + tspan = span->offset + 1; + if (tspan < 0) { + dev_info(&wc->dev->dev, "opvxd115: Span '%d' isn't us?\n", + span->spanno); + return -1; + } + + spin_lock_irqsave(&wc->reglock, flags); + + alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; + +#ifdef SUPPORT_GEN1 + /* initialize the start value for the entire chunk of last ec buffer */ + for(i = 0; i < span->channels; i++) + { + memset(ts->ec_chunk1[i], + DAHDI_LIN2X(0,span->chans[i]),DAHDI_CHUNKSIZE); + memset(ts->ec_chunk2[i], + DAHDI_LIN2X(0,span->chans[i]),DAHDI_CHUNKSIZE); + } +#endif + /* Force re-evaluation of timing source */ + wc->syncsrc = -1; + set_bit(T4_CHECK_TIMING, &wc->checkflag); + + if (ts->spantype == TYPE_E1) { /* if this is an E1 card */ + __t4_configure_e1(wc, span->offset, span->lineconfig); + } else { /* is a T1 card */ + __t4_configure_t1(wc, span->offset, span->lineconfig, span->txlevel); + } + + /* Note clear channel status */ + wc->tspans[span->offset]->notclear = 0; + __set_clear(wc, span->offset); + + if (!alreadyrunning) { + span->flags |= DAHDI_FLAG_RUNNING; + wc->spansstarted++; + + if (wc->flags & FLAG_5THGEN) + __t4_pci_out(wc, 5, (ms_per_irq << 16) | wc->numbufs); + /* enable interrupts */ + /* Start DMA, enabling DMA interrupts on read only */ +#if 0 + /* Enable framer only interrupts */ + wc->dmactrl |= 1 << 27; +#endif + wc->dmactrl |= (ts->spanflags & FLAG_2NDGEN) ? 0xc0000000 : 0xc0000003; +#ifdef VPM_SUPPORT + wc->dmactrl |= wc->vpm; +#endif + /* Seed interrupt register */ + __t4_pci_out(wc, WC_INTR, 0x0c); + if (noburst || !(ts->spanflags & FLAG_BURST)) + wc->dmactrl |= (1 << 26); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + + /* Startup HDLC controller too */ + } + + if (ts->sigchan) { + struct dahdi_chan *sigchan = ts->sigchan; + + spin_unlock_irqrestore(&wc->reglock, flags); + if (hdlc_start(wc, span->offset, sigchan, ts->sigmode)) { + dev_notice(&wc->dev->dev, "Error initializing " + "signalling controller\n"); + return -1; + } + spin_lock_irqsave(&wc->reglock, flags); + } + + spin_unlock_irqrestore(&wc->reglock, flags); + + t4_check_alarms(wc, span->offset); + t4_check_sigbits(wc, span->offset); + + if (wc->tspans[0]->sync == span->spanno) + dev_info(&wc->dev->dev, "SPAN %d: Primary Sync Source\n", + span->spanno); +#ifdef VPM_SUPPORT + if (!alreadyrunning && !wc->vpm) { + wait_a_little(); + t4_vpm400_init(wc); + if (!wc->vpm) + t4_vpm450_init(wc); + wc->dmactrl |= wc->vpm; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + if (wc->vpm) + set_span_devicetype(wc); + } +#endif + if (debug) + dev_info(&wc->dev->dev, "Completed startup!\n"); + clear_bit(T4_IGNORE_LATENCY, &wc->checkflag); + return 0; +} + +#ifdef SUPPORT_GEN1 +static inline void e1_check(struct t4 *wc, int span, int val) +{ + struct t4_span *ts = wc->tspans[span]; + if ((ts->span.channels > 24) && + (ts->span.flags & DAHDI_FLAG_RUNNING) && + !(ts->span.alarms) && + (!wc->e1recover)) { + if (val != 0x1b) { + ts->e1check++; + } else + ts->e1check = 0; + if (ts->e1check > 100) { + /* Wait 1000 ms */ + wc->e1recover = 1000 * 8; + wc->tspans[0]->e1check = 0; + if (debug & DEBUG_MAIN) + dev_notice(&wc->dev->dev, "Detected loss of " + "E1 alignment on span %d!\n", span); + t4_reset_dma(wc); + } + } +} + +static void t4_receiveprep(struct t4 *wc, int irq) +{ + volatile unsigned int *readchunk; + int dbl = 0; + int x,y,z; + unsigned int tmp; + int offset=0; + if (!wc->t1e1) + offset = 4; + if (irq & 1) { + /* First part */ + readchunk = wc->readchunk; + if (!wc->last0) + dbl = 1; + wc->last0 = 0; + } else { + readchunk = wc->readchunk + DAHDI_CHUNKSIZE * 32; + if (wc->last0) + dbl = 1; + wc->last0 = 1; + } + if (dbl) { + for (x=0;xnumspans;x++) + wc->tspans[x]->irqmisses++; + if (debug & DEBUG_MAIN) + dev_notice(&wc->dev->dev, "opvxd115: Double/missed " + "interrupt detected\n"); + } + for (x=0;xtspans[0]->span.chans[z]->readchunk[x] = tmp >> 24; + } + if (wc->t1e1) { + if (wc->e1recover > 0) + wc->e1recover--; + tmp = readchunk[0]; + e1_check(wc, 0, (tmp & 0x7f000000) >> 24); + for (z=24;z<31;z++) { + /* Only E1 channels now */ + tmp = readchunk[z+1]; + if (wc->tspans[0]->span.channels > 24) + wc->tspans[0]->span.chans[z]->readchunk[x] = tmp >> 24; + } + } + /* Advance pointer by 4 TDM frame lengths */ + readchunk += 32; + } + for (x=0;xnumspans;x++) { + if (wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) { + for (y=0;ytspans[x]->span.channels;y++) { + /* Echo cancel double buffered data */ + dahdi_ec_chunk(wc->tspans[x]->span.chans[y], + wc->tspans[x]->span.chans[y]->readchunk, + wc->tspans[x]->ec_chunk2[y]); + memcpy(wc->tspans[x]->ec_chunk2[y],wc->tspans[x]->ec_chunk1[y], + DAHDI_CHUNKSIZE); + memcpy(wc->tspans[x]->ec_chunk1[y], + wc->tspans[x]->span.chans[y]->writechunk, + DAHDI_CHUNKSIZE); + } + dahdi_receive(&wc->tspans[x]->span); + } + } +} +#endif + +#if (DAHDI_CHUNKSIZE != 8) +#error Sorry, nextgen does not support chunksize != 8 +#endif + +static inline void __receive_span(struct t4_span *ts) +{ +#ifdef VPM_SUPPORT + int y; + unsigned long merged; + merged = ts->dtmfactive & ts->dtmfmutemask; + if (merged) { + for (y=0;yspan.channels;y++) { + /* Mute any DTMFs which are supposed to be muted */ + if (test_bit(y, &merged)) { + memset(ts->span.chans[y]->readchunk, DAHDI_XLAW(0, ts->span.chans[y]), DAHDI_CHUNKSIZE); + } + } + } +#endif + +#ifdef ENABLE_PREFETCH + prefetch((void *)(ts->readchunk)); + prefetch((void *)(ts->writechunk)); + prefetch((void *)(ts->readchunk + 8)); + prefetch((void *)(ts->writechunk + 8)); + prefetch((void *)(ts->readchunk + 16)); + prefetch((void *)(ts->writechunk + 16)); + prefetch((void *)(ts->readchunk + 24)); + prefetch((void *)(ts->writechunk + 24)); + prefetch((void *)(ts->readchunk + 32)); + prefetch((void *)(ts->writechunk + 32)); + prefetch((void *)(ts->readchunk + 40)); + prefetch((void *)(ts->writechunk + 40)); + prefetch((void *)(ts->readchunk + 48)); + prefetch((void *)(ts->writechunk + 48)); + prefetch((void *)(ts->readchunk + 56)); + prefetch((void *)(ts->writechunk + 56)); +#endif + + dahdi_ec_span(&ts->span); + dahdi_receive(&ts->span); +} + +static inline void __transmit_span(struct t4_span *ts) +{ + dahdi_transmit(&ts->span); +} + +#ifdef ENABLE_WORKQUEUES +static void workq_handlespan(void *data) +{ + struct t4_span *ts = data; + struct t4 *wc = ts->owner; + + __receive_span(ts); + __transmit_span(ts); + atomic_dec(&wc->worklist); + if (!atomic_read(&wc->worklist)) + t4_pci_out(wc, WC_INTR, 0); +} +#else +static void t4_prep_gen2(struct t4 *wc) +{ + int x; + for (x=0;xnumspans;x++) { + if (wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) { + __receive_span(wc->tspans[x]); + __transmit_span(wc->tspans[x]); + } + } +} + +#endif +#ifdef SUPPORT_GEN1 +static void t4_transmitprep(struct t4 *wc, int irq) +{ + volatile unsigned int *writechunk; + int x,y,z; + unsigned int tmp; + int offset=0; + if (!wc->t1e1) + offset = 4; + if (irq & 1) { + /* First part */ + writechunk = wc->writechunk + 1; + } else { + writechunk = wc->writechunk + DAHDI_CHUNKSIZE * 32 + 1; + } + for (y=0;ynumspans;y++) { + if (wc->tspans[y]->span.flags & DAHDI_FLAG_RUNNING) + dahdi_transmit(&wc->tspans[y]->span); + } + + for (x=0;xtspans[0]->span.chans[z]->writechunk[x] << 24); + writechunk[z+offset] = tmp; + } + if (wc->t1e1) { + for (z=24;z<31;z++) { + /* Only E1 channels now */ + tmp = 0; + if (wc->tspans[0]->span.channels > 24) + tmp |= (wc->tspans[0]->span.chans[z]->writechunk[x] << 24); + writechunk[z] = tmp; + } + } + /* Advance pointer by 4 TDM frame lengths */ + writechunk += 32; + } + +} +#endif + +static void t4_check_sigbits(struct t4 *wc, int span) +{ + int a,i,rxs; + struct t4_span *ts = wc->tspans[span]; + + if (debug & DEBUG_RBS) + dev_notice(&wc->dev->dev, "Checking sigbits on span %d\n", + span + 1); + + if (!(ts->span.flags & DAHDI_FLAG_RUNNING)) + return; + if (ts->spantype == TYPE_E1) { + for (i = 0; i < 15; i++) { + a = t4_framer_in(wc, span, 0x71 + i); + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(ts->span.chans[i+16]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[i+16]->rxsig != rxs) + dahdi_rbsbits(ts->span.chans[i+16], rxs); + } + rxs = (a >> 4) & 0xf; + if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[i]->rxsig != rxs) + dahdi_rbsbits(ts->span.chans[i], rxs); + } + } + } else if (ts->span.lineconfig & DAHDI_CONFIG_D4) { + for (i = 0; i < 24; i+=4) { + a = t4_framer_in(wc, span, 0x70 + (i>>2)); + /* Get high channel in low bits */ + rxs = (a & 0x3) << 2; + if (!(ts->span.chans[i+3]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[i+3]->rxsig != rxs) + dahdi_rbsbits(ts->span.chans[i+3], rxs); + } + rxs = (a & 0xc); + if (!(ts->span.chans[i+2]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[i+2]->rxsig != rxs) + dahdi_rbsbits(ts->span.chans[i+2], rxs); + } + rxs = (a >> 2) & 0xc; + if (!(ts->span.chans[i+1]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[i+1]->rxsig != rxs) + dahdi_rbsbits(ts->span.chans[i+1], rxs); + } + rxs = (a >> 4) & 0xc; + if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) { + if (ts->span.chans[i]->rxsig != rxs) + dahdi_rbsbits(ts->span.chans[i], rxs); + } + } + } else { + for (i = 0; i < 24; i+=2) { + a = t4_framer_in(wc, span, 0x70 + (i>>1)); + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(ts->span.chans[i+1]->sig & DAHDI_SIG_CLEAR)) { + /* XXX Not really reset on every trans! XXX */ + if (ts->span.chans[i+1]->rxsig != rxs) { + dahdi_rbsbits(ts->span.chans[i+1], rxs); + } + } + rxs = (a >> 4) & 0xf; + if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) { + /* XXX Not really reset on every trans! XXX */ + if (ts->span.chans[i]->rxsig != rxs) { + dahdi_rbsbits(ts->span.chans[i], rxs); + } + } + } + } +} + +static void t4_check_alarms(struct t4 *wc, int span) +{ + unsigned char c, d, e; + int alarms; + int x,j; + struct t4_span *ts = wc->tspans[span]; + unsigned long flags; + + if (!(ts->span.flags & DAHDI_FLAG_RUNNING)) + return; + + spin_lock_irqsave(&wc->reglock, flags); + + c = __t4_framer_in(wc, span, 0x4c); + d = __t4_framer_in(wc, span, 0x4d); + + /* Assume no alarms */ + alarms = 0; + + /* And consider only carrier alarms */ + ts->span.alarms &= (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_NOTOPEN); + + if (ts->spantype == TYPE_E1) { + if (c & 0x04) { + /* No multiframe found, force RAI high after 400ms only if + we haven't found a multiframe since last loss + of frame */ + if (!(ts->spanflags & FLAG_NMF)) { + __t4_framer_out(wc, span, 0x20, 0x9f | 0x20); /* LIM0: Force RAI High */ + ts->spanflags |= FLAG_NMF; + dev_notice(&wc->dev->dev, + "NMF workaround on!\n"); + } + __t4_framer_out(wc, span, 0x1e, 0xc3); /* Reset to CRC4 mode */ + __t4_framer_out(wc, span, 0x1c, 0xf2); /* Force Resync */ + __t4_framer_out(wc, span, 0x1c, 0xf0); /* Force Resync */ + } else if (!(c & 0x02)) { + if ((ts->spanflags & FLAG_NMF)) { + __t4_framer_out(wc, span, 0x20, 0x9f); /* LIM0: Clear forced RAI */ + ts->spanflags &= ~FLAG_NMF; + dev_notice(&wc->dev->dev, + "NMF workaround off!\n"); + } + } + } else { + /* Detect loopup code if we're not sending one */ + if ((!ts->span.mainttimer) && (d & 0x08)) { + /* Loop-up code detected */ + if ((ts->loopupcnt++ > 80) && (ts->span.maintstat != DAHDI_MAINT_REMOTELOOP)) { + __t4_framer_out(wc, span, 0x36, 0x08); /* LIM0: Disable any local loop */ + __t4_framer_out(wc, span, 0x37, 0xf6 ); /* LIM1: Enable remote loop */ + ts->span.maintstat = DAHDI_MAINT_REMOTELOOP; + } + } else + ts->loopupcnt = 0; + /* Same for loopdown code */ + if ((!ts->span.mainttimer) && (d & 0x10)) { + /* Loop-down code detected */ + if ((ts->loopdowncnt++ > 80) && (ts->span.maintstat == DAHDI_MAINT_REMOTELOOP)) { + __t4_framer_out(wc, span, 0x36, 0x08); /* LIM0: Disable any local loop */ + __t4_framer_out(wc, span, 0x37, 0xf0 ); /* LIM1: Disable remote loop */ + ts->span.maintstat = DAHDI_MAINT_NONE; + } + } else + ts->loopdowncnt = 0; + } + + if (ts->span.lineconfig & DAHDI_CONFIG_NOTOPEN) { + for (x=0,j=0;x < ts->span.channels;x++) + if ((ts->span.chans[x]->flags & DAHDI_FLAG_OPEN) +#ifdef CONFIG_DAHDI_NET + || + (ts->span.chans[x]->flags & DAHDI_FLAG_NETDEV) +#endif + ) + j++; + if (!j) + alarms |= DAHDI_ALARM_NOTOPEN; + } + + /* Loss of Frame Alignment */ + if (c & 0x20) { + if (ts->alarmcount >= alarmdebounce) { + + /* Disable Slip Interrupts */ + e = __t4_framer_in(wc, span, 0x17); + __t4_framer_out(wc, span, 0x17, (e|0x03)); + + alarms |= DAHDI_ALARM_RED; + } else { + if (unlikely(debug && !ts->alarmcount)) { + /* starting to debounce LOF/LFA */ + dev_info(&wc->dev->dev, "opvxd115: LOF/LFA " + "detected on span %d but debouncing " + "for %d ms\n", span + 1, + alarmdebounce); + } + ts->alarmcount++; + } + } else + ts->alarmcount = 0; + + /* Loss of Signal */ + if (c & 0x80) { + if (ts->losalarmcount >= losalarmdebounce) { + /* Disable Slip Interrupts */ + e = __t4_framer_in(wc, span, 0x17); + __t4_framer_out(wc, span, 0x17, (e|0x03)); + + alarms |= DAHDI_ALARM_RED; + } else { + if (unlikely(debug && !ts->losalarmcount)) { + /* starting to debounce LOS */ + dev_info(&wc->dev->dev, "opvxd115: LOS " + "detected on span %d but debouncing " + "for %d ms\n", + span + 1, losalarmdebounce); + } + ts->losalarmcount++; + } + } else + ts->losalarmcount = 0; + + /* Alarm Indication Signal */ + if (c & 0x40) { + if (ts->aisalarmcount >= aisalarmdebounce) + alarms |= DAHDI_ALARM_BLUE; + else { + if (unlikely(debug && !ts->aisalarmcount)) { + /* starting to debounce AIS */ + dev_info(&wc->dev->dev, "opvxd115: AIS " + "detected on span %d but debouncing " + "for %d ms\n", + span + 1, aisalarmdebounce); + } + ts->aisalarmcount++; + } + } else + ts->aisalarmcount = 0; + +#ifdef DAHDI_SPAN_OPS + /* Add detailed alarm status information to a red alarm state */ + if (alarms & DAHDI_ALARM_RED) { + if (c & FRS0_LOS) + alarms |= DAHDI_ALARM_LOS; + if (c & FRS0_LFA) + alarms |= DAHDI_ALARM_LFA; + if (c & FRS0_LMFA) + alarms |= DAHDI_ALARM_LMFA; + } + + if (unlikely(debug)) { + /* Check to ensure the xmit line isn't shorted */ + if (unlikely(d & FRS1_XLS)) { + dev_info(&wc->dev->dev, + "Detected a possible hardware malfunction"\ + " this card may need servicing\n"); + } + } +#endif + + if (((!ts->span.alarms) && alarms) || + (ts->span.alarms && (!alarms))) + set_bit(T4_CHECK_TIMING, &wc->checkflag); + + /* Keep track of recovering */ + if ((!alarms) && ts->span.alarms) + ts->alarmtimer = DAHDI_ALARMSETTLE_TIME; + if (ts->alarmtimer) + alarms |= DAHDI_ALARM_RECOVER; + + /* If receiving alarms, go into Yellow alarm state */ + if (alarms && !(ts->spanflags & FLAG_SENDINGYELLOW)) { + /* We manually do yellow alarm to handle RECOVER and NOTOPEN, otherwise it's auto anyway */ + unsigned char fmr4; + fmr4 = __t4_framer_in(wc, span, 0x20); + __t4_framer_out(wc, span, 0x20, fmr4 | 0x20); + dev_info(&wc->dev->dev, "Setting yellow alarm span %d\n", + span+1); + ts->spanflags |= FLAG_SENDINGYELLOW; + } else if ((!alarms) && (ts->spanflags & FLAG_SENDINGYELLOW)) { + unsigned char fmr4; + /* We manually do yellow alarm to handle RECOVER */ + fmr4 = __t4_framer_in(wc, span, 0x20); + __t4_framer_out(wc, span, 0x20, fmr4 & ~0x20); + dev_info(&wc->dev->dev, "Clearing yellow alarm span %d\n", + span+1); + + /* Re-enable timing slip interrupts */ + e = __t4_framer_in(wc, span, 0x17); + + __t4_framer_out(wc, span, 0x17, (e & ~(0x03))); + + ts->spanflags &= ~FLAG_SENDINGYELLOW; + } + + /* Re-check the timing source when we enter/leave alarm, not withstanding + yellow alarm */ + if (c & 0x10) { /* receiving yellow (RAI) */ + if (ts->yelalarmcount >= yelalarmdebounce) + alarms |= DAHDI_ALARM_YELLOW; + else { + if (unlikely(debug && !ts->yelalarmcount)) { + /* starting to debounce AIS */ + dev_info(&wc->dev->dev, "wct%dxxp: yellow " + "(RAI) detected on span %d but " + "debouncing for %d ms\n", + wc->numspans, span + 1, + yelalarmdebounce); + } + ts->yelalarmcount++; + } + } else + ts->yelalarmcount = 0; + + if (ts->span.mainttimer || ts->span.maintstat) + alarms |= DAHDI_ALARM_LOOPBACK; + ts->span.alarms = alarms; + spin_unlock_irqrestore(&wc->reglock, flags); + dahdi_alarm_notify(&ts->span); +} + +static void t4_do_counters(struct t4 *wc) +{ + int span; + for (span=0;spannumspans;span++) { + struct t4_span *ts = wc->tspans[span]; + int docheck=0; + + spin_lock(&wc->reglock); + if (ts->loopupcnt || ts->loopdowncnt || ts->alarmcount + || ts->losalarmcount || ts->aisalarmcount + || ts->yelalarmcount) + docheck++; + + if (ts->alarmtimer) { + if (!--ts->alarmtimer) { + docheck++; + ts->span.alarms &= ~(DAHDI_ALARM_RECOVER); + } + } + spin_unlock(&wc->reglock); + if (docheck) { + t4_check_alarms(wc, span); + dahdi_alarm_notify(&ts->span); + } + } +} + +static inline void __handle_leds(struct t4 *wc) +{ + int x; + + wc->blinktimer++; + for (x=0;xnumspans;x++) { + struct t4_span *ts = wc->tspans[x]; + if (ts->span.flags & DAHDI_FLAG_RUNNING) { + if ((ts->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE)) || ts->losalarmcount) { +#ifdef FANCY_ALARM + if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { + __t4_set_led(wc, x, WC_RED); + } + if (wc->blinktimer == 0xf) { + __t4_set_led(wc, x, WC_OFF); + } +#else + if (wc->blinktimer == 160) { + __t4_set_led(wc, x, WC_RED); + } else if (wc->blinktimer == 480) { + __t4_set_led(wc, x, WC_OFF); + } +#endif + } else if (ts->span.alarms & DAHDI_ALARM_YELLOW) { + /* Yellow Alarm */ + __t4_set_led(wc, x, WC_YELLOW); + } else if (ts->span.mainttimer || ts->span.maintstat) { +#ifdef FANCY_ALARM + if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { + __t4_set_led(wc, x, WC_GREEN); + } + if (wc->blinktimer == 0xf) { + __t4_set_led(wc, x, WC_OFF); + } +#else + if (wc->blinktimer == 160) { + __t4_set_led(wc, x, WC_GREEN); + } else if (wc->blinktimer == 480) { + __t4_set_led(wc, x, WC_OFF); + } +#endif + } else { + /* No Alarm */ + __t4_set_led(wc, x, WC_GREEN); + } + } else + __t4_set_led(wc, x, WC_OFF); + + } +#ifdef FANCY_ALARM + if (wc->blinktimer == 0xf) { + wc->blinktimer = -1; + wc->alarmpos++; + if (wc->alarmpos >= (sizeof(altab) / sizeof(altab[0]))) + wc->alarmpos = 0; + } +#else + if (wc->blinktimer == 480) + wc->blinktimer = 0; +#endif +} + +static inline void t4_framer_interrupt(struct t4 *wc, int span) +{ + unsigned char gis, isr0, isr1, isr2, isr3, isr4; +#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) + /* Check interrupts for a given span */ + unsigned char reg; +#endif + int readsize = -1; + struct t4_span *ts = wc->tspans[span]; + struct dahdi_chan *sigchan; + unsigned long flags; + + + /* 1st gen cards isn't used interrupts */ + gis = t4_framer_in(wc, span, FRMR_GIS); + isr0 = (gis & FRMR_GIS_ISR0) ? t4_framer_in(wc, span, FRMR_ISR0) : 0; + isr1 = (gis & FRMR_GIS_ISR1) ? t4_framer_in(wc, span, FRMR_ISR1) : 0; + isr2 = (gis & FRMR_GIS_ISR2) ? t4_framer_in(wc, span, FRMR_ISR2) : 0; + isr3 = (gis & FRMR_GIS_ISR3) ? t4_framer_in(wc, span, FRMR_ISR3) : 0; + isr4 = (gis & FRMR_GIS_ISR4) ? t4_framer_in(wc, span, FRMR_ISR4) : 0; + + if ((debug & DEBUG_FRAMER) && !(isr3 & ISR3_SEC)) { + dev_info(&wc->dev->dev, "gis: %02x, isr0: %02x, isr1: %02x, "\ + "isr2: %02x, isr3: %08x, isr4: %02x, intcount=%u\n", + gis, isr0, isr1, isr2, isr3, isr4, wc->intcount); + } + +#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) + /* Collect performance counters once per second */ + if (isr3 & ISR3_SEC) { + ts->span.count.fe += t4_framer_in(wc, span, FECL_T); + ts->span.count.crc4 += t4_framer_in(wc, span, CEC1L_T); + ts->span.count.cv += t4_framer_in(wc, span, CVCL_T); + ts->span.count.ebit += t4_framer_in(wc, span, EBCL_T); + ts->span.count.be += t4_framer_in(wc, span, BECL_T); + ts->span.count.prbs = t4_framer_in(wc, span, FRS1_T); + } + + /* Collect errored second counter once per second */ + if (isr3 & ISR3_ES) { + ts->span.count.errsec += 1; + } + + if (isr3 & 0x08) { + reg = t4_framer_in(wc, span, FRS1_T); + dev_info(&wc->dev->dev, "FRS1: %d\n", reg); + if (reg & LLBDD) { + dev_info(&wc->dev->dev, "Line loop-back activation "\ + "signal detected with status: %01d "\ + "for span %d\n", reg & LLBAD, span+1); + } + } +#endif + + if (isr0) + t4_check_sigbits(wc, span); + + if (ts->spantype == TYPE_E1) { + /* E1 checks */ + if ((isr3 & 0x38) || isr2 || isr1) + t4_check_alarms(wc, span); + } else { + /* T1 checks */ + if (isr2 || (isr3 & 0x08)) + t4_check_alarms(wc, span); + } + if (!ts->span.alarms) { + if ((isr3 & 0x3) || (isr4 & 0xc0)) + ts->span.timingslips++; + + if (debug & DEBUG_MAIN) { + if (isr3 & 0x02) + dev_notice(&wc->dev->dev, "opvxd115: RECEIVE " + "slip NEGATIVE on span %d\n", + span + 1); + if (isr3 & 0x01) + dev_notice(&wc->dev->dev, "opvxd115: RECEIVE " + "slip POSITIVE on span %d\n", + span + 1); + if (isr4 & 0x80) + dev_notice(&wc->dev->dev, "opvxd115: TRANSMIT " + "slip POSITIVE on span %d\n", + span + 1); + if (isr4 & 0x40) + dev_notice(&wc->dev->dev, "opvxd115: TRANSMIT " + "slip NEGATIVE on span %d\n", + span + 1); + } + } else + ts->span.timingslips = 0; + + spin_lock_irqsave(&wc->reglock, flags); + /* HDLC controller checks - receive side */ + if (!ts->sigchan) { + spin_unlock_irqrestore(&wc->reglock, flags); + return; + } + + sigchan = ts->sigchan; + spin_unlock_irqrestore(&wc->reglock, flags); + + if (isr0 & FRMR_ISR0_RME) { + readsize = (t4_framer_in(wc, span, FRMR_RBCH) << 8) | t4_framer_in(wc, span, FRMR_RBCL); + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Received data length is %d " + "(%d)\n", readsize, + readsize & FRMR_RBCL_MAX_SIZE); + /* RPF isn't set on last part of frame */ + if ((readsize > 0) && ((readsize &= FRMR_RBCL_MAX_SIZE) == 0)) + readsize = FRMR_RBCL_MAX_SIZE + 1; + } else if (isr0 & FRMR_ISR0_RPF) + readsize = FRMR_RBCL_MAX_SIZE + 1; + + if (readsize > 0) { + int i; + unsigned char readbuf[FRMR_RBCL_MAX_SIZE + 1]; + + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Framer %d: Got RPF/RME! " + "readsize is %d\n", sigchan->span->offset, + readsize); + + for (i = 0; i < readsize; i++) + readbuf[i] = t4_framer_in(wc, span, FRMR_RXFIFO); + + /* Tell the framer to clear the RFIFO */ + t4_framer_cmd_wait(wc, span, FRMR_CMDR_RMC); + + if (debug & DEBUG_FRAMER) { + dev_notice(&wc->dev->dev, "RX("); + for (i = 0; i < readsize; i++) + dev_notice(&wc->dev->dev, "%s%02x", + (i ? " " : ""), readbuf[i]); + dev_notice(&wc->dev->dev, ")\n"); + } + + if (isr0 & FRMR_ISR0_RME) { + /* Do checks for HDLC problems */ + unsigned char rsis = readbuf[readsize-1]; +#if 0 + unsigned int olddebug = debug; +#endif + unsigned char rsis_reg = t4_framer_in(wc, span, FRMR_RSIS); + +#if 0 + if ((rsis != 0xA2) || (rsis != rsis_reg)) + debug |= DEBUG_FRAMER; +#endif + + ++ts->frames_in; + if ((debug & DEBUG_FRAMER) && !(ts->frames_in & 0x0f)) + dev_notice(&wc->dev->dev, "Received %d frames " + "on span %d\n", ts->frames_in, span); + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Received HDLC frame" + " %d. RSIS = 0x%x (%x)\n", + ts->frames_in, rsis, rsis_reg); + if (!(rsis & FRMR_RSIS_CRC16)) { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "CRC check " + "failed %d\n", span); + dahdi_hdlc_abort(sigchan, DAHDI_EVENT_BADFCS); + } else if (rsis & FRMR_RSIS_RAB) { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "ABORT of " + "current frame due to " + "overflow %d\n", span); + dahdi_hdlc_abort(sigchan, DAHDI_EVENT_ABORT); + } else if (rsis & FRMR_RSIS_RDO) { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "HDLC " + "overflow occured %d\n", + span); + dahdi_hdlc_abort(sigchan, DAHDI_EVENT_OVERRUN); + } else if (!(rsis & FRMR_RSIS_VFR)) { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Valid Frame" + " check failed on span %d\n", + span); + dahdi_hdlc_abort(sigchan, DAHDI_EVENT_ABORT); + } else { + dahdi_hdlc_putbuf(sigchan, readbuf, readsize - 1); + dahdi_hdlc_finish(sigchan); + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Received " + "valid HDLC frame on span %d" + "\n", span); + } +#if 0 + debug = olddebug; +#endif + } else if (isr0 & FRMR_ISR0_RPF) + dahdi_hdlc_putbuf(sigchan, readbuf, readsize); + } + + /* Transmit side */ + if (isr1 & FRMR_ISR1_XDU) { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "XDU: Resetting signal " + "controller!\n"); + t4_framer_cmd_wait(wc, span, FRMR_CMDR_SRES); + } else if (isr1 & FRMR_ISR1_XPR) { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Sigchan %d is %p\n", + sigchan->chanpos, sigchan); + + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "Framer %d: Got XPR!\n", + sigchan->span->offset); + t4_hdlc_xmit_fifo(wc, span, ts); + } + + if (isr1 & FRMR_ISR1_ALLS) { + if (debug & DEBUG_FRAMER) + dev_notice(&wc->dev->dev, "ALLS received\n"); + } +} + +#ifdef SUPPORT_GEN1 +DAHDI_IRQ_HANDLER(t4_interrupt) +{ + struct t4 *wc = dev_id; + unsigned long flags; + int x; + + unsigned int status; + unsigned int status2; + +#if 0 + if (wc->intcount < 20) + dev_notice(&wc->dev->dev, "Pre-interrupt\n"); +#endif + + /* Make sure it's really for us */ + status = __t4_pci_in(wc, WC_INTR); + + /* Process framer interrupts */ + status2 = t4_framer_in(wc, 0, FRMR_CIS); + if (status2 & 0x0f) { + for (x = 0; x < wc->numspans; ++x) { + if (status2 & (1 << x)) + t4_framer_interrupt(wc, x); + } + } + + /* Ignore if it's not for us */ + if (!status) + return IRQ_NONE; + + __t4_pci_out(wc, WC_INTR, 0); + + if (!wc->spansstarted) { + dev_notice(&wc->dev->dev, "Not prepped yet!\n"); + return IRQ_NONE; + } + + wc->intcount++; +#if 0 + if (wc->intcount < 20) + dev_notice(&wc->dev->dev, "Got interrupt, status = %08x\n", + status); +#endif + + if (status & 0x3) { + t4_receiveprep(wc, status); + t4_transmitprep(wc, status); + } + +#if 0 + if ((wc->intcount < 10) || !(wc->intcount % 1000)) { + status2 = t4_framer_in(wc, 0, FRMR_CIS); + dev_notice(&wc->dev->dev, "Status2: %04x\n", status2); + for (x = 0;xnumspans;x++) { + status2 = t4_framer_in(wc, x, FRMR_FRS0); + dev_notice(&wc->dev->dev, "FRS0/%d: %04x\n", x, + status2); + } + } +#endif + t4_do_counters(wc); + + x = wc->intcount & 15 /* 63 */; + switch(x) { + case 0: + case 1: + case 2: + case 3: + t4_check_sigbits(wc, x); + break; + case 4: + case 5: + case 6: + case 7: + t4_check_alarms(wc, x - 4); + break; + } + + spin_lock_irqsave(&wc->reglock, flags); + + __handle_leds(wc); + + if (test_bit(T4_CHECK_TIMING, &wc->checkflag)) + __t4_set_timing_source_auto(wc); + + spin_unlock_irqrestore(&wc->reglock, flags); + + return IRQ_RETVAL(1); +} +#endif + +static int t4_allocate_buffers(struct t4 *wc, int numbufs, volatile unsigned int **oldalloc, dma_addr_t *oldwritedma) +{ + volatile unsigned int *alloc; + dma_addr_t writedma; + + alloc = + /* 32 channels, Double-buffer, Read/Write, 4 spans */ + (unsigned int *)pci_alloc_consistent(wc->dev, numbufs * T4_BASE_SIZE * 2, &writedma); + + if (!alloc) { + dev_notice(&wc->dev->dev, "wct%dxxp: Unable to allocate " + "DMA-able memory\n", wc->numspans); + return -ENOMEM; + } + + if (oldwritedma) + *oldwritedma = wc->writedma; + if (oldalloc) + *oldalloc = wc->writechunk; + + wc->writechunk = alloc; + wc->writedma = writedma; + + /* Read is after the whole write piece (in words) */ + wc->readchunk = wc->writechunk + (T4_BASE_SIZE * numbufs) / 4; + + /* Same thing but in bytes... */ + wc->readdma = wc->writedma + (T4_BASE_SIZE * numbufs); + + wc->numbufs = numbufs; + + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0x00, T4_BASE_SIZE * numbufs); + memset((void *)wc->readchunk,0xff, T4_BASE_SIZE * numbufs); + + dev_notice(&wc->dev->dev, "DMA memory base of size %d at %p. Read: " + "%p and Write %p\n", numbufs * T4_BASE_SIZE * 2, + wc->writechunk, wc->readchunk, wc->writechunk); + + return 0; +} + +static void t4_increase_latency(struct t4 *wc, int newlatency) +{ + unsigned long flags; + volatile unsigned int *oldalloc; + dma_addr_t oldaddr; + int oldbufs; + + spin_lock_irqsave(&wc->reglock, flags); + + __t4_pci_out(wc, WC_DMACTRL, 0x00000000); + /* Acknowledge any pending interrupts */ + __t4_pci_out(wc, WC_INTR, 0x00000000); + + __t4_pci_in(wc, WC_VERSION); + + oldbufs = wc->numbufs; + + if (t4_allocate_buffers(wc, newlatency, &oldalloc, &oldaddr)) { + dev_info(&wc->dev->dev, "Error allocating latency buffers for " + "latency of %d\n", newlatency); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + spin_unlock_irqrestore(&wc->reglock, flags); + return; + } + + __t4_pci_out(wc, WC_RDADDR, wc->readdma); + __t4_pci_out(wc, WC_WRADDR, wc->writedma); + + __t4_pci_in(wc, WC_VERSION); + + __t4_pci_out(wc, 5, (ms_per_irq << 16) | newlatency); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + + __t4_pci_in(wc, WC_VERSION); + + wc->rxident = 0; + wc->lastindex = 0; + + spin_unlock_irqrestore(&wc->reglock, flags); + + pci_free_consistent(wc->dev, T4_BASE_SIZE * oldbufs * 2, (void *)oldalloc, oldaddr); + + dev_info(&wc->dev->dev, "Increased latency to %d\n", newlatency); + +} + +static void t4_isr_bh(unsigned long data) +{ + struct t4 *wc = (struct t4 *)data; + + if (test_bit(T4_CHANGE_LATENCY, &wc->checkflag)) { + if (wc->needed_latency != wc->numbufs) { + t4_increase_latency(wc, wc->needed_latency); + clear_bit(T4_CHANGE_LATENCY, &wc->checkflag); + } + } +#ifdef VPM_SUPPORT + if (wc->vpm) { + if (test_and_clear_bit(T4_CHECK_VPM, &wc->checkflag)) { + if (wc->vpm450m) { + /* How stupid is it that the octasic can't generate an + interrupt when there's a tone, in spite of what their + documentation says? */ + t4_check_vpm450(wc); + } else + t4_check_vpm400(wc, wc->vpm400checkstatus); + } + } +#endif +} + +DAHDI_IRQ_HANDLER(t4_interrupt_gen2) +{ + struct t4 *wc = dev_id; + unsigned int status; + unsigned char rxident, expected; + + /* Check this first in case we get a spurious interrupt */ + if (unlikely(test_bit(T4_STOP_DMA, &wc->checkflag))) { + /* Stop DMA cleanly if requested */ + wc->dmactrl = 0x0; + t4_pci_out(wc, WC_DMACTRL, 0x00000000); + /* Acknowledge any pending interrupts */ + t4_pci_out(wc, WC_INTR, 0x00000000); + spin_lock(&wc->reglock); + __t4_set_sclk_src(wc, WC_SELF, 0, 0); + spin_unlock(&wc->reglock); + return IRQ_RETVAL(1); + } + + /* Make sure it's really for us */ + status = __t4_pci_in(wc, WC_INTR); + + /* Ignore if it's not for us */ + if (!(status & 0x7)) { + return IRQ_NONE; + } + +#ifdef ENABLE_WORKQUEUES + __t4_pci_out(wc, WC_INTR, status & 0x00000008); +#endif + + if (unlikely(!wc->spansstarted)) { + dev_info(&wc->dev->dev, "Not prepped yet!\n"); + return IRQ_NONE; + } + + wc->intcount++; + + if ((wc->flags & FLAG_5THGEN) && (status & 0x2)) { + rxident = (status >> 16) & 0x7f; + expected = (wc->rxident + ms_per_irq) % 128; + + if ((rxident != expected) && !test_bit(T4_IGNORE_LATENCY, &wc->checkflag)) { + int needed_latency; + int smallest_max; + + if (debug & DEBUG_MAIN) + dev_warn(&wc->dev->dev, "Missed interrupt. " + "Expected ident of %d and got ident " + "of %d\n", expected, rxident); + + if (test_bit(T4_IGNORE_LATENCY, &wc->checkflag)) { + dev_info(&wc->dev->dev, + "Should have ignored latency\n"); + } + if (rxident > wc->rxident) { + needed_latency = rxident - wc->rxident; + } else { + needed_latency = (128 - wc->rxident) + rxident; + } + + needed_latency += 1; + + smallest_max = (max_latency >= GEN5_MAX_LATENCY) ? GEN5_MAX_LATENCY : max_latency; + + if (needed_latency > smallest_max) { + dev_info(&wc->dev->dev, "Truncating latency " + "request to %d instead of %d\n", + smallest_max, needed_latency); + needed_latency = smallest_max; + } + + if (needed_latency > wc->numbufs) { + int x; + + dev_info(&wc->dev->dev, "Need to increase " + "latency. Estimated latency should " + "be %d\n", needed_latency); + for (x = 0; x < wc->numspans; x++) + wc->tspans[x]->span.irqmisses++; + wc->needed_latency = needed_latency; + __t4_pci_out(wc, WC_DMACTRL, 0x00000000); + set_bit(T4_CHANGE_LATENCY, &wc->checkflag); + goto out; + } + } + + wc->rxident = rxident; + } + + if (unlikely((wc->intcount < 20))) + + dev_info(&wc->dev->dev, "2G: Got interrupt, status = %08x, " + "CIS = %04x\n", status, t4_framer_in(wc, 0, FRMR_CIS)); + + if (likely(status & 0x2)) { +#ifdef ENABLE_WORKQUEUES + int cpus = num_online_cpus(); + atomic_set(&wc->worklist, wc->numspans); + if (wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING) + t4_queue_work(wc->workq, &wc->tspans[0]->swork, 0); + else + atomic_dec(&wc->worklist); +#else +#if 1 + unsigned int reg5 = __t4_pci_in(wc, 5); + if (wc->intcount < 20) { + + dev_info(&wc->dev->dev, "Reg 5 is %08x\n", reg5); + } +#endif + + if (wc->flags & FLAG_5THGEN) { + unsigned int current_index = (reg5 >> 8) & 0x7f; + + while (((wc->lastindex + 1) % wc->numbufs) != current_index) { + wc->lastindex = (wc->lastindex + 1) % wc->numbufs; + setup_chunks(wc, wc->lastindex); + t4_prep_gen2(wc); + } + } else { + t4_prep_gen2(wc); + } + +#endif + t4_do_counters(wc); + spin_lock(&wc->reglock); + __handle_leds(wc); + spin_unlock(&wc->reglock); + + } + + if (unlikely(status & 0x1)) { + unsigned char cis; + + cis = t4_framer_in(wc, 0, FRMR_CIS); + if (cis & FRMR_CIS_GIS1) + t4_framer_interrupt(wc, 0); + if (cis & FRMR_CIS_GIS2) + t4_framer_interrupt(wc, 1); + if (cis & FRMR_CIS_GIS3) + t4_framer_interrupt(wc, 2); + if (cis & FRMR_CIS_GIS4) + t4_framer_interrupt(wc, 3); + } + + if (wc->vpm && vpmdtmfsupport) { + if (wc->vpm450m) { + /* How stupid is it that the octasic can't generate an + interrupt when there's a tone, in spite of what their + documentation says? */ + if (!(wc->intcount & 0xf)) { + set_bit(T4_CHECK_VPM, &wc->checkflag); + } + } else if ((status & 0xff00) != 0xff00) { + wc->vpm400checkstatus = (status & 0xff00) >> 8; + set_bit(T4_CHECK_VPM, &wc->checkflag); + } + } + + spin_lock(&wc->reglock); + + if (unlikely(test_bit(T4_CHECK_TIMING, &wc->checkflag))) { + __t4_set_timing_source_auto(wc); + } + + spin_unlock(&wc->reglock); + +out: + if (unlikely(test_bit(T4_CHANGE_LATENCY, &wc->checkflag) || test_bit(T4_CHECK_VPM, &wc->checkflag))) + tasklet_schedule(&wc->t4_tlet); + +#ifndef ENABLE_WORKQUEUES + __t4_pci_out(wc, WC_INTR, 0); +#endif + + return IRQ_RETVAL(1); +} + +#ifdef SUPPORT_GEN1 +static int t4_reset_dma(struct t4 *wc) +{ + /* Turn off DMA and such */ + wc->dmactrl = 0x0; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + t4_pci_out(wc, WC_COUNT, 0); + t4_pci_out(wc, WC_RDADDR, 0); + t4_pci_out(wc, WC_WRADDR, 0); + t4_pci_out(wc, WC_INTR, 0); + /* Turn it all back on */ + t4_pci_out(wc, WC_RDADDR, wc->readdma); + t4_pci_out(wc, WC_WRADDR, wc->writedma); + t4_pci_out(wc, WC_COUNT, ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); + t4_pci_out(wc, WC_INTR, 0); +#ifdef VPM_SUPPORT + wc->dmactrl = 0xc0000000 | (1 << 29) | wc->vpm; +#else + wc->dmactrl = 0xc0000000 | (1 << 29); +#endif + if (noburst) + wc->dmactrl |= (1 << 26); + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + return 0; +} +#endif + +#ifdef VPM_SUPPORT +static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold) +{ + unsigned int x; + + for (x = 0; x < 8; x++) { + t4_vpm_out(wc, x, 0xC4, (threshold >> 8) & 0xFF); + t4_vpm_out(wc, x, 0xC5, (threshold & 0xFF)); + } + dev_info(&wc->dev->dev, "VPM: DTMF threshold set to %d\n", threshold); +} + +static unsigned int t4_vpm_mask(int chip) +{ + unsigned int mask=0; + switch(vpmspans) { + case 4: + mask = 0x55555555 << (chip >> 2); + break; + case 2: + mask = 0x11111111 << (chip >> 1); + break; + case 1: + mask = 0x01010101 << chip; + break; + } + return mask; +} + +static int t4_vpm_spanno(int chip) +{ + int spanno = 0; + switch(vpmspans) { + case 4: + spanno = chip & 0x3; + break; + case 2: + spanno = chip & 0x1; + break; + /* Case 1 is implicit */ + } + return spanno; +} + +static int t4_vpm_echotail(void) +{ + int echotail = 0x01ff; + switch(vpmspans) { + case 4: + echotail = 0x007f; + break; + case 2: + echotail = 0x00ff; + break; + /* Case 1 is implicit */ + } + return echotail; +} + +static void t4_vpm450_init(struct t4 *wc) +{ + unsigned int check1, check2; + int laws[1] = { 0, }; + int x; + unsigned int vpm_capacity; + struct firmware embedded_firmware; + const struct firmware *firmware = &embedded_firmware; +#if !defined(HOTPLUG_FIRMWARE) + extern void _binary_dahdi_fw_oct6114_032_bin_size; + extern void _binary_dahdi_fw_oct6114_064_bin_size; + extern void _binary_dahdi_fw_oct6114_128_bin_size; + extern u8 _binary_dahdi_fw_oct6114_032_bin_start[]; + extern u8 _binary_dahdi_fw_oct6114_064_bin_start[]; + extern u8 _binary_dahdi_fw_oct6114_128_bin_start[]; +#else + static const char oct032_firmware[] = "dahdi-fw-oct6114-032.bin"; + static const char oct064_firmware[] = "dahdi-fw-oct6114-064.bin"; + static const char oct128_firmware[] = "dahdi-fw-oct6114-128.bin"; +#endif + + if (!vpmsupport) { + dev_info(&wc->dev->dev, "VPM450: Support Disabled\n"); + return; + } + + /* Turn on GPIO/DATA mux if supported */ + t4_gpio_setdir(wc, (1 << 24), (1 << 24)); + __t4_raw_oct_out(wc, 0x000a, 0x5678); + __t4_raw_oct_out(wc, 0x0004, 0x1234); + check1 = __t4_raw_oct_in(wc, 0x0004); + check2 = __t4_raw_oct_in(wc, 0x000a); + if (debug) + dev_notice(&wc->dev->dev, "OCT Result: %04x/%04x\n", + __t4_raw_oct_in(wc, 0x0004), + __t4_raw_oct_in(wc, 0x000a)); + if (__t4_raw_oct_in(wc, 0x0004) != 0x1234) { + dev_notice(&wc->dev->dev, "VPM450: Not Present\n"); + return; + } + + /* Setup alaw vs ulaw rules */ + for (x = 0;x < wc->numspans; x++) { + if (wc->tspans[x]->span.channels > 24) + laws[x] = 1; + } + + switch ((vpm_capacity = get_vpm450m_capacity(wc))) { + case 32: +#if defined(HOTPLUG_FIRMWARE) + if ((request_firmware(&firmware, oct032_firmware, &wc->dev->dev) != 0) || + !firmware) { + dev_notice(&wc->dev->dev, "VPM450: firmware %s not " + "available from userspace\n", oct032_firmware); + return; + } +#else + embedded_firmware.data = _binary_dahdi_fw_oct6114_032_bin_start; + /* Yes... this is weird. objcopy gives us a symbol containing + the size of the firmware, not a pointer a variable containing + the size. The only way we can get the value of the symbol + is to take its address, so we define it as a pointer and + then cast that value to the proper type. + */ + embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_032_bin_size; +#endif + break; + case 64: +#if defined(HOTPLUG_FIRMWARE) + if ((request_firmware(&firmware, oct064_firmware, &wc->dev->dev) != 0) || + !firmware) { + dev_notice(&wc->dev->dev, "VPM450: firmware %s not " + "available from userspace\n", oct064_firmware); + return; + } +#else + embedded_firmware.data = _binary_dahdi_fw_oct6114_064_bin_start; + /* Yes... this is weird. objcopy gives us a symbol containing + the size of the firmware, not a pointer a variable containing + the size. The only way we can get the value of the symbol + is to take its address, so we define it as a pointer and + then cast that value to the proper type. + */ + embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_064_bin_size; +#endif + break; + case 128: +#if defined(HOTPLUG_FIRMWARE) + if ((request_firmware(&firmware, oct128_firmware, &wc->dev->dev) != 0) || + !firmware) { + dev_notice(&wc->dev->dev, "VPM450: firmware %s not " + "available from userspace\n", oct128_firmware); + return; + } +#else + embedded_firmware.data = _binary_dahdi_fw_oct6114_128_bin_start; + /* Yes... this is weird. objcopy gives us a symbol containing + the size of the firmware, not a pointer a variable containing + the size. The only way we can get the value of the symbol + is to take its address, so we define it as a pointer and + then cast that value to the proper type. + */ + embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_128_bin_size; +#endif + break; + default: + dev_notice(&wc->dev->dev, "Unsupported channel capacity found " + "on VPM module (%d).\n", vpm_capacity); + return; + } + + if (!(wc->vpm450m = init_vpm450m(wc, laws, wc->numspans, firmware))) { + dev_notice(&wc->dev->dev, "VPM450: Failed to initialize\n"); + if (firmware != &embedded_firmware) + release_firmware(firmware); + return; + } + + if (firmware != &embedded_firmware) + release_firmware(firmware); + + if (vpmdtmfsupport == -1) { + dev_notice(&wc->dev->dev, "VPM450: hardware DTMF disabled.\n"); + vpmdtmfsupport = 0; + } + + wc->vpm = T4_VPM_PRESENT; + dev_info(&wc->dev->dev, "VPM450: Present and operational servicing %d " + "span(s)\n", wc->numspans); + +} + +static void t4_vpm400_init(struct t4 *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int ver; + unsigned int i, x, y, gen2vpm=0; + + if (!vpmsupport) { + dev_info(&wc->dev->dev, "VPM400: Support Disabled\n"); + return; + } + + switch(vpmspans) { + case 4: + case 2: + case 1: + break; + default: + dev_notice(&wc->dev->dev, "VPM400: %d is not a valid vpmspans " + "value, using 4\n", vpmspans); + vpmspans = 1; + } + + for (x=0;x<8;x++) { + int spanno = t4_vpm_spanno(x); + struct t4_span *ts = wc->tspans[spanno]; + int echotail = t4_vpm_echotail(); + + ver = t4_vpm_in(wc, x, 0x1a0); /* revision */ + if ((ver != 0x26) && (ver != 0x33)) { + if (x) + dev_notice(&wc->dev->dev, + "VPM400: Inoperable\n"); + return; + } + if (ver == 0x33) { + if (x && !gen2vpm) { + dev_notice(&wc->dev->dev, + "VPM400: Inconsistent\n"); + return; + } + ts->spanflags |= FLAG_VPM2GEN; + gen2vpm++; + } else if (gen2vpm) { + dev_notice(&wc->dev->dev, + "VPM400: Inconsistent\n"); + return; + } + + + /* Setup GPIO's */ + for (y=0;y<4;y++) { + t4_vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + t4_vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + t4_vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = t4_vpm_in(wc, x, 0x1a3); /* misc_con */ + t4_vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup timeslots */ + t4_vpm_out(wc, x, 0x02f, 0x20 | (spanno << 3)); + + /* Setup Echo length (128 taps) */ + t4_vpm_out(wc, x, 0x022, (echotail >> 8)); + t4_vpm_out(wc, x, 0x023, (echotail & 0xff)); + + /* Setup the tdm channel masks for all chips*/ + mask = t4_vpm_mask(x); + for (i = 0; i < 4; i++) + t4_vpm_out(wc, x, 0x30 + i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + reg = t4_vpm_in(wc,x,0x20); + reg &= 0xE0; + if (ts->spantype == TYPE_E1) { + if (x < vpmspans) + dev_info(&wc->dev->dev, "VPM400: Span %d " + "A-law mode\n", spanno); + reg |= 0x01; + } else { + if (x < vpmspans) + dev_info(&wc->dev->dev, "VPM400: Span %d " + "U-law mode\n", spanno); + reg &= ~0x01; + } + t4_vpm_out(wc,x,0x20,(reg | 0x20)); + + /* Initialize echo cans */ + for (i = 0 ; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + t4_vpm_out(wc,x,i,0x00); + } + + wait_a_little(); + + /* Put in bypass mode */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) { + t4_vpm_out(wc,x,i,0x01); + } + } + + /* Enable bypass */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) + t4_vpm_out(wc,x,0x78 + i,0x01); + } + + /* set DTMF detection threshold */ + t4_vpm_set_dtmf_threshold(wc, dtmfthreshold); + + /* Enable DTMF detectors (always DTMF detect all spans) */ + for (i = 0; i < MAX_DTMF_DET; i++) { + t4_vpm_out(wc, x, 0x98 + i, 0x40 | (i * 2) | ((x < 4) ? 0 : 1)); + } + for (i = 0x34; i < 0x38; i++) + t4_vpm_out(wc, x, i, 0x00); + for (i = 0x3C; i < 0x40; i++) + t4_vpm_out(wc, x, i, 0x00); + + for (i = 0x48; i < 0x4B; i++) + t4_vpm_out(wc, x, i, 0x00); + for (i = 0x50; i < 0x53; i++) + t4_vpm_out(wc, x, i, 0x00); + for (i = 0xB8; i < 0xBE; i++) + t4_vpm_out(wc, x, i, 0xFF); + if (gen2vpm) { + for (i = 0xBE; i < 0xC0; i++) + t4_vpm_out(wc, x, i, 0xFF); + } else { + for (i = 0xBE; i < 0xC0; i++) + t4_vpm_out(wc, x, i, 0x00); + } + for (i = 0xC0; i < 0xC4; i++) + t4_vpm_out(wc, x, i, (x < 4) ? 0x55 : 0xAA); + + } + if (vpmdtmfsupport == -1) { + dev_info(&wc->dev->dev, "VPM400: hardware DTMF enabled.\n"); + vpmdtmfsupport = 0; + } + dev_info(&wc->dev->dev, "VPM400%s: Present and operational servicing " + "%d span(s)\n", (gen2vpm ? " (2nd Gen)" : ""), wc->numspans); + wc->vpm = T4_VPM_PRESENT; +} + +#endif + +static void t4_tsi_reset(struct t4 *wc) +{ + int x; + for (x=0;x<128;x++) { + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (x << 7)); + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + } + wc->dmactrl &= ~0x00007fff; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); +} + +/* Note that channels here start from 1 */ +static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan) +{ + unsigned long flags; + int fromts, tots; + + fromts = (fromspan << 5) |(fromchan); + tots = (tospan << 5) | (tochan); + + if (!wc->t1e1) { + fromts += 4; + tots += 4; + } + spin_lock_irqsave(&wc->reglock, flags); + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (tots << 7) | (fromts)); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + wc->dmactrl &= ~0x00007fff; + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan) +{ + unsigned long flags; + int tots; + + tots = (tospan << 5) | (tochan); + + if (!wc->t1e1) + tots += 4; + spin_lock_irqsave(&wc->reglock, flags); + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (tots << 7)); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + if (debug & DEBUG_TSI) + dev_notice(&wc->dev->dev, "Sending '%08x\n", wc->dmactrl); + wc->dmactrl &= ~0x00007fff; + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + spin_unlock_irqrestore(&wc->reglock, flags); +} +#ifdef CONFIG_EXTENDED_RESET +static void t4_extended_reset(struct t4 *wc) +{ + unsigned int oldreg = t4_pci_in(wc, 0x4); + + udelay(1000); + + t4_pci_out(wc, 0x4, 0x42000000); + t4_pci_out(wc, 0xa, 0x42000000); + t4_pci_out(wc, 0xa, 0x00080000); + t4_pci_out(wc, 0xa, 0x00080000); + t4_pci_out(wc, 0xa, 0x00080000); + t4_pci_out(wc, 0xa, 0x00180000); + t4_pci_out(wc, 0xa, 0x00080000); + t4_pci_out(wc, 0xa, 0x00180000); + t4_pci_out(wc, 0xa, 0x00080000); + t4_pci_out(wc, 0xa, 0x00180000); + t4_pci_out(wc, 0xa, 0x00080000); + t4_pci_out(wc, 0xa, 0x00180000); + t4_pci_out(wc, 0xa, 0x00080000); + t4_pci_out(wc, 0xa, 0x00180000); + t4_pci_out(wc, 0xa, 0x00080000); + t4_pci_out(wc, 0xa, 0x00180000); + t4_pci_out(wc, 0x4, oldreg); + + udelay(1000); +} +#endif + +static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags) +{ + unsigned int version; + + version = t4_pci_in(wc, WC_VERSION); + dev_info(&wc->dev->dev, "Firmware Version: %08x\n", version); + dev_info(&wc->dev->dev, "Burst Mode: %s\n", + (!(cardflags & FLAG_BURST) && noburst) ? "Off" : "On"); +#ifdef ENABLE_WORKQUEUES + dev_info(&wc->dev->dev, "Work Queues: Enabled\n"); +#endif + +#ifdef CONFIG_EXTENDED_RESET + t4_extended_reset(wc); +#endif + + /* Make sure DMA engine is not running and interrupts are acknowledged */ + wc->dmactrl = 0x0; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + /* Reset Framer and friends */ + t4_pci_out(wc, WC_LEDS, 0x00000000); + + /* Set DMA addresses */ + t4_pci_out(wc, WC_RDADDR, wc->readdma); + t4_pci_out(wc, WC_WRADDR, wc->writedma); + + /* Setup counters, interrupt flags (ignored in Gen2) */ + if (cardflags & FLAG_2NDGEN) { + t4_tsi_reset(wc); + } else { + t4_pci_out(wc, WC_COUNT, ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); + } + + /* Reset pending interrupts */ + t4_pci_out(wc, WC_INTR, 0x00000000); + + /* Read T1/E1 status */ + if (t1e1override > -1) + wc->t1e1 = t1e1override; + else + wc->t1e1 = ((t4_pci_in(wc, WC_LEDS)) & 0x0f00) >> 8; + wc->order = ((t4_pci_in(wc, WC_LEDS)) & 0xf0000000) >> 28; + order_index[wc->order]++; + return 0; +} + +static int t4_hardware_init_2(struct t4 *wc) +{ + int x; + unsigned int regval; + + if (t4_pci_in(wc, WC_VERSION) >= 0xc01a0165) { + wc->tspans[0]->spanflags |= FLAG_OCTOPT; + dev_info(&wc->dev->dev, "Octasic Optimizations: Enabled\n"); + } + /* Setup LEDS, take out of reset */ + t4_pci_out(wc, WC_LEDS, 0x000000ff); + t4_activate(wc); + + /* + * In order to find out the QFALC framer version, we have to temporarily term off compat + * mode and take a peak at VSTR. We turn compat back on when we are done. + */ + if (t4_framer_in(wc, 0, 0x4a) != 0x05) + dev_info(&wc->dev->dev, "WARNING: FALC framer not intialized " + "in compatibility mode.\n"); + regval = t4_framer_in(wc, 0 ,0xd6); + regval |= (1 << 5); /* set COMP_DIS*/ + t4_framer_out(wc, 0, 0xd6, regval); + regval = t4_framer_in(wc, 0, 0x4a); + if (regval == 0x05) + dev_info(&wc->dev->dev, "FALC Framer Version: 2.1 or " + "earlier\n"); + else if (regval == 0x20) { + dev_info(&wc->dev->dev, "FALC Framer Version: 3.1\n"); + wc->falc31 = 1; + } else + dev_info(&wc->dev->dev, "FALC Framer Version: Unknown " + "(VSTR = 0x%02x)\n", regval); + regval = t4_framer_in(wc, 0 ,0xd6); + regval &= ~(1 << 5); /* clear COMP_DIS*/ + t4_framer_out(wc, 0, 0xd6, regval); + + t4_framer_out(wc, 0, 0x4a, 0xaa); + dev_info(&wc->dev->dev, "Board ID: %02x\n", wc->order); + + for (x=0;x< 11;x++) + dev_info(&wc->dev->dev, "Reg %d: 0x%08x\n", x, + t4_pci_in(wc, x)); + return 0; +} + +static int __devinit t4_launch(struct t4 *wc) +{ + int x; + unsigned long flags; + if (test_bit(DAHDI_FLAGBIT_REGISTERED, &wc->tspans[0]->span.flags)) + return 0; + dev_info(&wc->dev->dev, "opvxd115: Launching card: %d\n", + wc->order); + + /* Setup serial parameters and system interface */ + for (x=0;xtspans[0]->span, 0)) { + dev_err(&wc->dev->dev, "Unable to register span %s\n", + wc->tspans[0]->span.name); + return -1; + } + set_bit(T4_CHECK_TIMING, &wc->checkflag); + spin_lock_irqsave(&wc->reglock, flags); + __t4_set_sclk_src(wc, WC_SELF, 0, 0); + spin_unlock_irqrestore(&wc->reglock, flags); + tasklet_init(&wc->t4_tlet, t4_isr_bh, (unsigned long)wc); + return 0; +} + +static void free_wc(struct t4 *wc) +{ + unsigned int x, y; + + for (x = 0; x < sizeof(wc->tspans)/sizeof(wc->tspans[0]); x++) { + if (!wc->tspans[x]) { + continue; + } + + for (y = 0; y < sizeof(wc->tspans[x]->chans)/sizeof(wc->tspans[x]->chans[0]); y++) { + if (wc->tspans[x]->chans[y]) { + kfree(wc->tspans[x]->chans[y]); + } + if (wc->tspans[x]->ec[y]) + kfree(wc->tspans[x]->ec[y]); + } + kfree(wc->tspans[x]); + } + kfree(wc); +} + +static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct t4 *wc; + struct devtype *dt; + unsigned int x, f; + int init_latency; + + if (pci_enable_device(pdev)) { + return -EIO; + } + + if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) { + return -ENOMEM; + } + + memset(wc, 0x0, sizeof(*wc)); + spin_lock_init(&wc->reglock); + dt = (struct devtype *) (ent->driver_data); + + wc->flags = dt->flags; + + wc->numspans = 1; + + wc->variety = dt->desc; + + wc->memaddr = pci_resource_start(pdev, 0); + wc->memlen = pci_resource_len(pdev, 0); + wc->membase = ioremap(wc->memaddr, wc->memlen); + /* This rids of the Double missed interrupt message after loading */ + wc->last0 = 1; +#if 0 + if (!request_mem_region(wc->memaddr, wc->memlen, wc->variety)) + dev_info(&wc->dev->dev, "opvxd115: Unable to request memory " + "region :(, using anyway...\n"); +#endif + if (pci_request_regions(pdev, wc->variety)) + dev_info(&pdev->dev, "opvxd115: Unable to request regions\n"); + + dev_info(&pdev->dev, "Found opvxd115 at base address %08lx, remapped " + "to %p\n", wc->memaddr, wc->membase); + + wc->dev = pdev; + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + if (wc->flags & FLAG_5THGEN) { + if ((ms_per_irq > 1) && (latency <= ((ms_per_irq) << 1))) { + init_latency = ms_per_irq << 1; + } else { + if (latency > 2) + init_latency = latency; + else + init_latency = 2; + } + dev_info(&wc->dev->dev, "5th gen card with initial latency of " + "%d and %d ms per IRQ\n", init_latency, ms_per_irq); + } else { + if (wc->flags & FLAG_2NDGEN) + init_latency = 1; + else + init_latency = 2; + } + + if (max_latency < init_latency) { + printk(KERN_INFO "maxlatency must be set to something greater than %d ms, increasing it to %d\n", init_latency, init_latency); + max_latency = init_latency; + } + + if (t4_allocate_buffers(wc, init_latency, NULL, NULL)) { + return -ENOMEM; + } + + /* Initialize hardware */ + t4_hardware_init_1(wc, wc->flags); + + for(x = 0; x < MAX_T4_CARDS; x++) { + if (!cards[x]) + break; + } + + if (x >= MAX_T4_CARDS) { + dev_notice(&wc->dev->dev, "No cards[] slot available!!\n"); + kfree(wc); + return -ENOMEM; + } + + wc->num = x; + cards[x] = wc; + +#ifdef ENABLE_WORKQUEUES + if (wc->flags & FLAG_2NDGEN) { + char tmp[20]; + + sprintf(tmp, "opvxd115"); + wc->workq = create_workqueue(tmp); + } +#endif + + /* Allocate pieces we need here */ + for (x = 0; x < PORTS_PER_FRAMER; x++) { + if (!(wc->tspans[x] = kmalloc(sizeof(*wc->tspans[x]), GFP_KERNEL))) { + free_wc(wc); + return -ENOMEM; + } + + memset(wc->tspans[x], 0, sizeof(*wc->tspans[x])); + + if (wc->t1e1 & (1 << x)) { + wc->tspans[x]->spantype = TYPE_E1; + } else { + if (j1mode) + wc->tspans[x]->spantype = TYPE_J1; + else + wc->tspans[x]->spantype = TYPE_T1; + } + + for (f = 0; f < (wc->tspans[x]->spantype == TYPE_E1 ? 31 : 24); f++) { + if (!(wc->tspans[x]->chans[f] = kmalloc(sizeof(*wc->tspans[x]->chans[f]), GFP_KERNEL))) { + free_wc(wc); + return -ENOMEM; + } + memset(wc->tspans[x]->chans[f], 0, sizeof(*wc->tspans[x]->chans[f])); + if (!(wc->tspans[x]->ec[f] = kmalloc(sizeof(*wc->tspans[x]->ec[f]), GFP_KERNEL))) { + free_wc(wc); + return -ENOMEM; + } + memset(wc->tspans[x]->ec[f], 0, sizeof(*wc->tspans[x]->ec[f])); + } + +#ifdef ENABLE_WORKQUEUES + INIT_WORK(&wc->tspans[x]->swork, workq_handlespan, wc->tspans[x]); +#endif + wc->tspans[x]->spanflags |= wc->flags; + } + + /* Continue hardware intiialization */ + t4_hardware_init_2(wc); + +#ifdef SUPPORT_GEN1 + if (request_irq(pdev->irq, (wc->flags & FLAG_2NDGEN) ? t4_interrupt_gen2 :t4_interrupt, DAHDI_IRQ_SHARED_DISABLED, "opvxd115", wc)) +#else + if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN)) { + dev_notice(&wc->dev->dev, "This driver does not " + "support 1st gen modules\n"); + free_wc(wc); + return -ENODEV; + } + if (request_irq(pdev->irq, t4_interrupt_gen2, DAHDI_IRQ_SHARED_DISABLED, "opvxd115", wc)) +#endif + { + dev_notice(&wc->dev->dev, "opvxd115: Unable to request IRQ %d\n", + pdev->irq); + free_wc(wc); + return -EIO; + } + + init_spans(wc); + /* get the current number of probed cards and run a slice of a tail + * insertion sort */ + for (x = 0; x < MAX_T4_CARDS; x++) { + if (!cards[x+1]) + break; + } + for ( ; x > 0; x--) { + if (cards[x]->order < cards[x-1]->order) { + struct t4 *tmp = cards[x]; + cards[x] = cards[x-1]; + cards[x-1] = tmp; + } else { + /* if we're not moving it, we won't move any more + * since all cards are sorted on addition */ + break; + } + } + + dev_info(&wc->dev->dev, "Found an OpenVox Card: %s\n", wc->variety); + wc->gpio = 0x00000000; + t4_pci_out(wc, WC_GPIO, wc->gpio); + t4_gpio_setdir(wc, (1 << 17), (1 << 17)); + t4_gpio_setdir(wc, (0xff), (0xff)); + + create_sysfs_files(wc); + +#if 0 + for (x=0;x<0x10000;x++) { + __t4_raw_oct_out(wc, 0x0004, x); + __t4_raw_oct_out(wc, 0x000a, x ^ 0xffff); + if (__t4_raw_oct_in(wc, 0x0004) != x) + dev_notice(&wc->dev->dev, "Register 4 failed %04x\n", + x); + if (__t4_raw_oct_in(wc, 0x000a) != (x ^ 0xffff)) + dev_notice(&wc->dev->dev, "Register 10 failed %04x\n", + x); + } +#endif + + return 0; +} + +static int t4_hardware_stop(struct t4 *wc) +{ + + /* Turn off DMA, leave interrupts enabled */ + set_bit(T4_STOP_DMA, &wc->checkflag); + + /* Wait for interrupts to stop */ + msleep(25); + + /* Turn off counter, address, etc */ + if (wc->tspans[0]->spanflags & FLAG_2NDGEN) { + t4_tsi_reset(wc); + } else { + t4_pci_out(wc, WC_COUNT, 0x000000); + } + t4_pci_out(wc, WC_RDADDR, 0x0000000); + t4_pci_out(wc, WC_WRADDR, 0x0000000); + wc->gpio = 0x00000000; + t4_pci_out(wc, WC_GPIO, wc->gpio); + t4_pci_out(wc, WC_LEDS, 0x00000000); + + dev_notice(&wc->dev->dev, "\nStopped opvxd115, Turned off DMA\n"); + return 0; +} + +static void __devexit t4_remove_one(struct pci_dev *pdev) +{ + struct t4 *wc = pci_get_drvdata(pdev); + struct dahdi_span *span; + int basesize; + int i; + + if (!wc) { + return; + } + + remove_sysfs_files(wc); + + /* Stop hardware */ + t4_hardware_stop(wc); + + /* Release vpm450m */ + if (wc->vpm450m) + release_vpm450m(wc->vpm450m); + wc->vpm450m = NULL; + /* Unregister spans */ + + basesize = DAHDI_MAX_CHUNKSIZE * 32 * 4; + if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN)) + basesize = basesize * 2; + + for (i = 0; i < wc->numspans; ++i) { + span = &wc->tspans[i]->span; + if (test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) + dahdi_unregister(span); + } +#ifdef ENABLE_WORKQUEUES + if (wc->workq) { + flush_workqueue(wc->workq); + destroy_workqueue(wc->workq); + } +#endif + + free_irq(pdev->irq, wc); + + if (wc->membase) + iounmap(wc->membase); + + pci_release_regions(pdev); + + /* Immediately free resources */ + pci_free_consistent(pdev, T4_BASE_SIZE * wc->numbufs * 2, (void *)wc->writechunk, wc->writedma); + + order_index[wc->order]--; + + cards[wc->num] = NULL; + pci_set_drvdata(pdev, NULL); + free_wc(wc); +} + + +static struct pci_device_id t4_pci_tbl[] __devinitdata = +{ + { 0x1b74, 0x0115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&opvxd115 }, /* OpenVox D115P/D115E */ + { 0x1b74, 0xd130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&opvxd130 }, /* OpenVox D130P/D130E */ + { 0, } +}; + +static struct pci_driver t4_driver = { + .name = "opvxd115", + .probe = t4_init_one, + .remove = __devexit_p(t4_remove_one), + .id_table = t4_pci_tbl, +}; + +static int __init t4_init(void) +{ + int res; + res = dahdi_pci_module(&t4_driver); + if (res) + return -ENODEV; + /* initialize cards since we have all of them */ + /* warn for missing zero and duplicate numbers */ + if (cards[0] && cards[0]->order != 0) { + printk(KERN_NOTICE "opvxd115: Ident of first card is not zero (%d)\n", + cards[0]->order); + } + for (res = 0; cards[res]; res++) { + /* warn the user of duplicate ident values it is probably + * unintended */ + if (debug && res < 15 && cards[res+1] && + cards[res]->order == cards[res+1]->order) { + printk(KERN_NOTICE "opvxd115: Duplicate ident value found (%d)\n", + cards[res]->order); + } + t4_launch(cards[res]); + } + return 0; +} + +static void __exit t4_cleanup(void) +{ + pci_unregister_driver(&t4_driver); +} + + +MODULE_AUTHOR("mark.liu "); +MODULE_DESCRIPTION("Unified OpenVox Single T1/E1/J1 Card Driver"); +MODULE_ALIAS("opvxd115"); +MODULE_LICENSE("GPL v2"); + +module_param(pedanticpci, int, 0600); +module_param(debug, int, 0600); +module_param(noburst, int, 0600); +module_param(timingcable, int, 0600); +module_param(t1e1override, int, 0600); +module_param(alarmdebounce, int, 0600); +module_param(losalarmdebounce, int, 0600); +module_param(aisalarmdebounce, int, 0600); +module_param(yelalarmdebounce, int, 0600); +module_param(max_latency, int, 0600); +module_param(j1mode, int, 0600); +module_param(sigmode, int, 0600); +module_param(latency, int, 0600); +module_param(ms_per_irq, int, 0600); +#ifdef VPM_SUPPORT +module_param(vpmsupport, int, 0600); +module_param(vpmdtmfsupport, int, 0600); +module_param(vpmspans, int, 0600); +module_param(dtmfthreshold, int, 0600); +#endif + +MODULE_DEVICE_TABLE(pci, t4_pci_tbl); + +module_init(t4_init); +module_exit(t4_cleanup); diff --git a/drivers/dahdi/opvxd115/opvxd115-diag.c b/drivers/dahdi/opvxd115/opvxd115-diag.c new file mode 100644 index 0000000..7d1dfdb --- /dev/null +++ b/drivers/dahdi/opvxd115/opvxd115-diag.c @@ -0,0 +1,427 @@ +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "opvxd115.h" + +struct t4_reg_def { + int reg; + char *name; + int global; +}; +static struct t4_reg_def xreginfo[] = { + { 0x00, "RDADDR" }, + { 0x01, "WRADDR" }, + { 0x02, "COUNT" }, + { 0x03, "DMACTRL" }, + { 0x04, "WCINTR" }, + { 0x06, "VERSION" }, + { 0x07, "LEDS" }, + { 0x08, "GPIOCTL" }, + { 0x09, "GPIO" }, + { 0x0A, "LADDR" }, + { 0x0b, "LDATA" }, +}; + +static struct t4_reg_def reginfo[] = { + { 0x00, "XFIFO" }, + { 0x01, "XFIFO" }, + { 0x02, "CMDR" }, + { 0x03, "MODE" }, + { 0x04, "RAH1" }, + { 0x05, "RAH2" }, + { 0x06, "RAL1" }, + { 0x07, "RAL2" }, + { 0x08, "IPC", 1 }, + { 0x09, "CCR1" }, + { 0x0a, "CCR2" }, + { 0x0c, "RTR1" }, + { 0x0d, "RTR2" }, + { 0x0e, "RTR3" }, + { 0x0f, "RTR4" }, + { 0x10, "TTR1" }, + { 0x11, "TTR2" }, + { 0x12, "TTR3" }, + { 0x13, "TTR4" }, + { 0x14, "IMR0" }, + { 0x15, "IMR1" }, + { 0x16, "IMR2" }, + { 0x17, "IMR3" }, + { 0x18, "IMR4" }, + { 0x1b, "IERR" }, + { 0x1c, "FMR0" }, + { 0x1d, "FMR1" }, + { 0x1e, "FMR2" }, + { 0x1f, "LOOP" }, + { 0x20, "XSW" }, + { 0x21, "XSP" }, + { 0x22, "XC0" }, + { 0x23, "XC1" }, + { 0x24, "RC0" }, + { 0x25, "RC1" }, + { 0x26, "XPM0" }, + { 0x27, "XPM1" }, + { 0x28, "XPM2" }, + { 0x29, "TSWM" }, + { 0x2b, "IDLE" }, + { 0x2c, "XSA4" }, + { 0x2d, "XSA5" }, + { 0x2e, "XSA6" }, + { 0x2f, "XSA7" }, + { 0x30, "XSA8" }, + { 0x31, "FMR3" }, + { 0x32, "ICB1" }, + { 0x33, "ICB2" }, + { 0x34, "ICB3" }, + { 0x35, "ICB4" }, + { 0x36, "LIM0" }, + { 0x37, "LIM1" }, + { 0x38, "PCD" }, + { 0x39, "PCR" }, + { 0x3a, "LIM2" }, + { 0x3b, "LCR1" }, + { 0x3c, "LCR2" }, + { 0x3d, "LCR3" }, + { 0x3e, "SIC1" }, + { 0x3f, "SIC2" }, + { 0x40, "SIC3" }, + { 0x44, "CMR1" }, + { 0x45, "CMR2" }, + { 0x46, "GCR" }, + { 0x47, "ESM" }, + { 0x60, "DEC" }, + { 0x70, "XS1" }, + { 0x71, "XS2" }, + { 0x72, "XS3" }, + { 0x73, "XS4" }, + { 0x74, "XS5" }, + { 0x75, "XS6" }, + { 0x76, "XS7" }, + { 0x77, "XS8" }, + { 0x78, "XS9" }, + { 0x79, "XS10" }, + { 0x7a, "XS11" }, + { 0x7b, "XS12" }, + { 0x7c, "XS13" }, + { 0x7d, "XS14" }, + { 0x7e, "XS15" }, + { 0x7f, "XS16" }, + { 0x80, "PC1" }, + { 0x81, "PC2" }, + { 0x82, "PC3" }, + { 0x83, "PC4" }, + { 0x84, "PC5" }, + { 0x85, "GPC1", 1 }, + { 0x87, "CMDR2" }, + { 0x8d, "CCR5" }, + { 0x92, "GCM1", 1 }, + { 0x93, "GCM2", 1 }, + { 0x94, "GCM3", 1 }, + { 0x95, "GCM4", 1 }, + { 0x96, "GCM5", 1 }, + { 0x97, "GCM6", 1 }, + { 0x98, "GCM7", 1 }, + { 0x99, "GCM8", 1 }, + { 0xa0, "TSEO" }, + { 0xa1, "TSBS1" }, + { 0xa8, "TPC0" }, +}; + +static struct t4_reg_def t1_reginfo[] = { + { 0x00, "XFIFO" }, + { 0x01, "XFIFO" }, + { 0x02, "CMDR" }, + { 0x03, "MODE" }, + { 0x04, "RAH1" }, + { 0x05, "RAH2" }, + { 0x06, "RAL1" }, + { 0x07, "RAL2" }, + { 0x08, "IPC", 1 }, + { 0x09, "CCR1" }, + { 0x0a, "CCR2" }, + { 0x0c, "RTR1" }, + { 0x0d, "RTR2" }, + { 0x0e, "RTR3" }, + { 0x0f, "RTR4" }, + { 0x10, "TTR1" }, + { 0x11, "TTR2" }, + { 0x12, "TTR3" }, + { 0x13, "TTR4" }, + { 0x14, "IMR0" }, + { 0x15, "IMR1" }, + { 0x16, "IMR2" }, + { 0x17, "IMR3" }, + { 0x18, "IMR4" }, + { 0x1b, "IERR" }, + { 0x1c, "FMR0" }, + { 0x1d, "FMR1" }, + { 0x1e, "FMR2" }, + { 0x1f, "LOOP" }, + { 0x20, "FMR4" }, + { 0x21, "FMR5" }, + { 0x22, "XC0" }, + { 0x23, "XC1" }, + { 0x24, "RC0" }, + { 0x25, "RC1" }, + { 0x26, "XPM0" }, + { 0x27, "XPM1" }, + { 0x28, "XPM2" }, + { 0x2b, "IDLE" }, + { 0x2c, "XDL1" }, + { 0x2d, "XDL2" }, + { 0x2e, "XDL3" }, + { 0x2f, "CCB1" }, + { 0x30, "CCB2" }, + { 0x31, "CCB3" }, + { 0x32, "ICB1" }, + { 0x33, "ICB2" }, + { 0x34, "ICB3" }, + { 0x36, "LIM0" }, + { 0x37, "LIM1" }, + { 0x38, "PCD" }, + { 0x39, "PCR" }, + { 0x3a, "LIM2" }, + { 0x3b, "LCR1" }, + { 0x3c, "LCR2" }, + { 0x3d, "LCR3" }, + { 0x3e, "SIC1" }, + { 0x3f, "SIC2" }, + { 0x40, "SIC3" }, + { 0x44, "CMR1" }, + { 0x45, "CMR2" }, + { 0x46, "GCR" }, + { 0x47, "ESM" }, + { 0x60, "DEC" }, + { 0x70, "XS1" }, + { 0x71, "XS2" }, + { 0x72, "XS3" }, + { 0x73, "XS4" }, + { 0x74, "XS5" }, + { 0x75, "XS6" }, + { 0x76, "XS7" }, + { 0x77, "XS8" }, + { 0x78, "XS9" }, + { 0x79, "XS10" }, + { 0x7a, "XS11" }, + { 0x7b, "XS12" }, + { 0x80, "PC1" }, + { 0x81, "PC2" }, + { 0x82, "PC3" }, + { 0x83, "PC4" }, + { 0x84, "PC5" }, + { 0x85, "GPC1", 1 }, + { 0x87, "CMDR2" }, + { 0x8d, "CCR5" }, + { 0x92, "GCM1", 1 }, + { 0x93, "GCM2", 1 }, + { 0x94, "GCM3", 1 }, + { 0x95, "GCM4", 1 }, + { 0x96, "GCM5", 1 }, + { 0x97, "GCM6", 1 }, + { 0x98, "GCM7", 1 }, + { 0x99, "GCM8", 1 }, + { 0xa0, "TSEO" }, + { 0xa1, "TSBS1" }, + { 0xa8, "TPC0" }, +}; + +static struct t4_reg_def t1_sreginfo[] = { + { 0x00, "RFIFO" }, + { 0x01, "RFIFO" }, + { 0x49, "RBD" }, + { 0x4a, "VSTR", 1 }, + { 0x4b, "RES" }, + { 0x4c, "FRS0" }, + { 0x4d, "FRS1" }, + { 0x4e, "FRS2" }, + { 0x4f, "Old FRS1" }, + { 0x50, "FECL" }, + { 0x51, "FECH" }, + { 0x52, "CVCL" }, + { 0x53, "CVCH" }, + { 0x54, "CECL" }, + { 0x55, "CECH" }, + { 0x56, "EBCL" }, + { 0x57, "EBCH" }, + { 0x58, "BECL" }, + { 0x59, "BECH" }, + { 0x5a, "COEC" }, + { 0x5c, "RDL1" }, + { 0x5d, "RDL2" }, + { 0x5e, "RDL3" }, + { 0x62, "RSP1" }, + { 0x63, "RSP2" }, + { 0x64, "SIS" }, + { 0x65, "RSIS" }, + { 0x66, "RBCL" }, + { 0x67, "RBCH" }, + { 0x68, "ISR0" }, + { 0x69, "ISR1" }, + { 0x6a, "ISR2" }, + { 0x6b, "ISR3" }, + { 0x6c, "ISR4" }, + { 0x6e, "GIS" }, + { 0x6f, "CIS", 1 }, + { 0x70, "RS1" }, + { 0x71, "RS2" }, + { 0x72, "RS3" }, + { 0x73, "RS4" }, + { 0x74, "RS5" }, + { 0x75, "RS6" }, + { 0x76, "RS7" }, + { 0x77, "RS8" }, + { 0x78, "RS9" }, + { 0x79, "RS10" }, + { 0x7a, "RS11" }, + { 0x7b, "RS12" }, +}; + +static struct t4_reg_def sreginfo[] = { + { 0x00, "RFIFO" }, + { 0x01, "RFIFO" }, + { 0x49, "RBD" }, + { 0x4a, "VSTR", 1 }, + { 0x4b, "RES" }, + { 0x4c, "FRS0" }, + { 0x4d, "FRS1" }, + { 0x4e, "RSW" }, + { 0x4f, "RSP" }, + { 0x50, "FECL" }, + { 0x51, "FECH" }, + { 0x52, "CVCL" }, + { 0x53, "CVCH" }, + { 0x54, "CEC1L" }, + { 0x55, "CEC1H" }, + { 0x56, "EBCL" }, + { 0x57, "EBCH" }, + { 0x58, "CEC2L" }, + { 0x59, "CEC2H" }, + { 0x5a, "CEC3L" }, + { 0x5b, "CEC3H" }, + { 0x5c, "RSA4" }, + { 0x5d, "RSA5" }, + { 0x5e, "RSA6" }, + { 0x5f, "RSA7" }, + { 0x60, "RSA8" }, + { 0x61, "RSA6S" }, + { 0x62, "RSP1" }, + { 0x63, "RSP2" }, + { 0x64, "SIS" }, + { 0x65, "RSIS" }, + { 0x66, "RBCL" }, + { 0x67, "RBCH" }, + { 0x68, "ISR0" }, + { 0x69, "ISR1" }, + { 0x6a, "ISR2" }, + { 0x6b, "ISR3" }, + { 0x6c, "ISR4" }, + { 0x6e, "GIS" }, + { 0x6f, "CIS", 1 }, + { 0x70, "RS1" }, + { 0x71, "RS2" }, + { 0x72, "RS3" }, + { 0x73, "RS4" }, + { 0x74, "RS5" }, + { 0x75, "RS6" }, + { 0x76, "RS7" }, + { 0x77, "RS8" }, + { 0x78, "RS9" }, + { 0x79, "RS10" }, + { 0x7a, "RS11" }, + { 0x7b, "RS12" }, + { 0x7c, "RS13" }, + { 0x7d, "RS14" }, + { 0x7e, "RS15" }, + { 0x7f, "RS16" }, +}; + +static char *tobin(int x) +{ + static char s[9] = ""; + int y,z=0; + for (y=7;y>=0;y--) { + if (x & (1 << y)) + s[z++] = '1'; + else + s[z++] = '0'; + } + s[z] = '\0'; + return s; +} + +static char *tobin32(unsigned int x) +{ + static char s[33] = ""; + int y,z=0; + for (y=31;y>=0;y--) { + if (x & (1 << y)) + s[z++] = '1'; + else + s[z++] = '0'; + } + s[z] = '\0'; + return s; +} + +int main(int argc, char *argv[]) +{ + int fd; + int x; + char fn[256]; + struct t4_regs regs; + if ((argc < 2) || ((*(argv[1]) != '/') && !atoi(argv[1]))) { + fprintf(stderr, "Usage: opvxd115-diag \n"); + exit(1); + } + if (*(argv[1]) == '/') + dahdi_copy_string(fn, argv[1], sizeof(fn)); + else + snprintf(fn, sizeof(fn), "/dev/dahdi/%d", atoi(argv[1])); + fd = open(fn, O_RDWR); + if (fd <0) { + fprintf(stderr, "Unable to open '%s': %s\n", fn, strerror(errno)); + exit(1); + } + if (ioctl(fd, WCT4_GET_REGS, ®s)) { + fprintf(stderr, "Unable to get registers: %s\n", strerror(errno)); + exit(1); + } + printf("PCI Registers:\n"); + for (x=0;x + * + * Copyright (C) 2001-2008, Digium, Inc. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include + +#define FRMR_TTR_BASE 0x10 +#define FRMR_RTR_BASE 0x0c +#define FRMR_TSEO 0xa0 +#define FRMR_TSBS1 0xa1 +#define FRMR_CCR1 0x09 +#define FRMR_CCR1_ITF 0x08 +#define FRMR_CCR1_EITS 0x10 +#define FRMR_CCR2 0x0a +#define FRMR_CCR2_RCRC 0x04 +#define FRMR_CCR2_RADD 0x10 +#define FRMR_MODE 0x03 +#define FRMR_MODE_NO_ADDR_CMP 0x80 +#define FRMR_MODE_SS7 0x20 +#define FRMR_MODE_HRAC 0x08 +#define FRMR_IMR0 0x14 +#define FRMR_IMR0_RME 0x80 +#define FRMR_IMR0_RPF 0x01 +#define FRMR_IMR1 0x15 +#define FRMR_IMR1_ALLS 0x20 +#define FRMR_IMR1_XDU 0x10 +#define FRMR_IMR1_XPR 0x01 +#define FRMR_XC0 0x22 +#define FRMR_XC1 0x23 +#define FRMR_RC0 0x24 +#define FRMR_RC1 0x25 +#define FRMR_SIC1 0x3e +#define FRMR_SIC2 0x3f +#define FRMR_SIC3 0x40 +#define FRMR_CMR1 0x44 +#define FRMR_CMR2 0x45 +#define FRMR_GCR 0x46 +#define FRMR_ISR0 0x68 +#define FRMR_ISR0_RME 0x80 +#define FRMR_ISR0_RPF 0x01 +#define FRMR_ISR1 0x69 +#define FRMR_ISR1_ALLS 0x20 +#define FRMR_ISR1_XDU 0x10 +#define FRMR_ISR1_XPR 0x01 +#define FRMR_ISR2 0x6a +#define FRMR_ISR3 0x6b +#define FRMR_ISR4 0x6c +#define FRMR_GIS 0x6e +#define FRMR_GIS_ISR0 0x01 +#define FRMR_GIS_ISR1 0x02 +#define FRMR_GIS_ISR2 0x04 +#define FRMR_GIS_ISR3 0x08 +#define FRMR_GIS_ISR4 0x10 +#define FRMR_CIS 0x6f +#define FRMR_CIS_GIS1 0x01 +#define FRMR_CIS_GIS2 0x02 +#define FRMR_CIS_GIS3 0x04 +#define FRMR_CIS_GIS4 0x08 +#define FRMR_CMDR 0x02 +#define FRMR_CMDR_SRES 0x01 +#define FRMR_CMDR_XRES 0x10 +#define FRMR_CMDR_RMC 0x80 +#define FRMR_CMDR_XTF 0x04 +#define FRMR_CMDR_XHF 0x08 +#define FRMR_CMDR_XME 0x02 +#define FRMR_RSIS 0x65 +#define FRMR_RSIS_VFR 0x80 +#define FRMR_RSIS_RDO 0x40 +#define FRMR_RSIS_CRC16 0x20 +#define FRMR_RSIS_RAB 0x10 +#define FRMR_RBCL 0x66 +#define FRMR_RBCL_MAX_SIZE 0x1f +#define FRMR_RBCH 0x67 +#define FRMR_RXFIFO 0x00 +#define FRMR_SIS 0x64 +#define FRMR_SIS_XFW 0x40 +#define FRMR_TXFIFO 0x00 + +#define FRS0 0x4c +#define FRS0_LOS (1<<7) +#define FRS0_LFA (1<<5) +#define FRS0_LMFA (1<<1) + +#define FRS1 0x4d +#define FRS1_XLS (1<<1) +#define FRS1_XLO (1<<0) + +#define NUM_REGS 0xa9 +#define NUM_PCI 12 + +struct t4_regs { + unsigned int pci[NUM_PCI]; + unsigned char regs[NUM_REGS]; +}; + +#define T4_CHECK_VPM 0 +#define T4_LOADING_FW 1 +#define T4_STOP_DMA 2 +#define T4_CHECK_TIMING 3 +#define T4_CHANGE_LATENCY 4 +#define T4_IGNORE_LATENCY 5 + +#define WCT4_GET_REGS _IOW (DAHDI_CODE, 60, struct t4_regs) + diff --git a/drivers/dahdi/opvxd115/vpm450m.c b/drivers/dahdi/opvxd115/vpm450m.c new file mode 100644 index 0000000..f1ed421 --- /dev/null +++ b/drivers/dahdi/opvxd115/vpm450m.c @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2005-2006 Digium, Inc. + * + * Mark Spencer + * Modified by mark.liu@openvox.cn 06/16/2009 + + * All Rights Reserved + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include + +#include "vpm450m.h" +#include "oct6100api/oct6100_api.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +#include +#endif + +/* API for Octasic access */ +UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime) +{ + /* Why couldn't they just take a timeval like everyone else? */ + struct timeval tv; + unsigned long long total_usecs; + unsigned int mask = ~0; + + do_gettimeofday(&tv); + total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + + (((unsigned long long)(tv.tv_usec))); + f_pTime->aulWallTimeUs[0] = (total_usecs & mask); + f_pTime->aulWallTimeUs[1] = (total_usecs >> 32); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength) +{ + memset(f_pAddress, f_ulPattern, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength) +{ + memcpy(f_pDestination, f_pSource, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate) +{ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy) +{ +#ifdef OCTASIC_DEBUG + printk(KERN_DEBUG "I should never be called! (destroy serialize object)\n"); +#endif + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams) +{ + oct_set_reg(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams) +{ + unsigned int x; + for (x=0;xulWriteLength;x++) { + oct_set_reg(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams) +{ + unsigned int x; + for (x=0;xulWriteLength;x++) { + oct_set_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams) +{ + *(f_pReadParams->pusReadData) = oct_get_reg(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams) +{ + unsigned int x; + for (x=0;xulReadLength;x++) { + f_pBurstParams->pusReadData[x] = oct_get_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1)); + } + return cOCT6100_ERR_OK; +} + +#define SOUT_G168_1100GB_ON 0x40000004 +#define SOUT_DTMF_1 0x40000011 +#define SOUT_DTMF_2 0x40000012 +#define SOUT_DTMF_3 0x40000013 +#define SOUT_DTMF_A 0x4000001A +#define SOUT_DTMF_4 0x40000014 +#define SOUT_DTMF_5 0x40000015 +#define SOUT_DTMF_6 0x40000016 +#define SOUT_DTMF_B 0x4000001B +#define SOUT_DTMF_7 0x40000017 +#define SOUT_DTMF_8 0x40000018 +#define SOUT_DTMF_9 0x40000019 +#define SOUT_DTMF_C 0x4000001C +#define SOUT_DTMF_STAR 0x4000001E +#define SOUT_DTMF_0 0x40000010 +#define SOUT_DTMF_POUND 0x4000001F +#define SOUT_DTMF_D 0x4000001D + +#define ROUT_G168_2100GB_ON 0x10000000 +#define ROUT_G168_2100GB_WSPR 0x10000002 +#define ROUT_SOUT_G168_2100HB_END 0x50000003 +#define ROUT_G168_1100GB_ON 0x10000004 + +#define ROUT_DTMF_1 0x10000011 +#define ROUT_DTMF_2 0x10000012 +#define ROUT_DTMF_3 0x10000013 +#define ROUT_DTMF_A 0x1000001A +#define ROUT_DTMF_4 0x10000014 +#define ROUT_DTMF_5 0x10000015 +#define ROUT_DTMF_6 0x10000016 +#define ROUT_DTMF_B 0x1000001B +#define ROUT_DTMF_7 0x10000017 +#define ROUT_DTMF_8 0x10000018 +#define ROUT_DTMF_9 0x10000019 +#define ROUT_DTMF_C 0x1000001C +#define ROUT_DTMF_STAR 0x1000001E +#define ROUT_DTMF_0 0x10000010 +#define ROUT_DTMF_POUND 0x1000001F +#define ROUT_DTMF_D 0x1000001D + +#if 0 +#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE +#else +#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN +#endif + +struct vpm450m { + tPOCT6100_INSTANCE_API pApiInstance; + UINT32 aulEchoChanHndl[ 128 ]; + int chanflags[128]; + int ecmode[128]; + int numchans; +}; + +#define FLAG_DTMF (1 << 0) +#define FLAG_MUTE (1 << 1) +#define FLAG_ECHO (1 << 2) + +static unsigned int tones[] = { + SOUT_DTMF_1, + SOUT_DTMF_2, + SOUT_DTMF_3, + SOUT_DTMF_A, + SOUT_DTMF_4, + SOUT_DTMF_5, + SOUT_DTMF_6, + SOUT_DTMF_B, + SOUT_DTMF_7, + SOUT_DTMF_8, + SOUT_DTMF_9, + SOUT_DTMF_C, + SOUT_DTMF_STAR, + SOUT_DTMF_0, + SOUT_DTMF_POUND, + SOUT_DTMF_D, + SOUT_G168_1100GB_ON, + + ROUT_DTMF_1, + ROUT_DTMF_2, + ROUT_DTMF_3, + ROUT_DTMF_A, + ROUT_DTMF_4, + ROUT_DTMF_5, + ROUT_DTMF_6, + ROUT_DTMF_B, + ROUT_DTMF_7, + ROUT_DTMF_8, + ROUT_DTMF_9, + ROUT_DTMF_C, + ROUT_DTMF_STAR, + ROUT_DTMF_0, + ROUT_DTMF_POUND, + ROUT_DTMF_D, + ROUT_G168_1100GB_ON, +}; + +static void vpm450m_setecmode(struct vpm450m *vpm450m, int channel, int mode) +{ + tOCT6100_CHANNEL_MODIFY *modify; + UINT32 ulResult; + + if (vpm450m->ecmode[channel] == mode) + return; + modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC); + if (!modify) { + printk(KERN_NOTICE "opvxd115: Unable to allocate memory for setec!\n"); + return; + } + Oct6100ChannelModifyDef(modify); + modify->ulEchoOperationMode = mode; + modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel]; + ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify); + if (ulResult != GENERIC_OK) { + printk(KERN_NOTICE "Failed to apply echo can changes on channel %d!\n", channel); + } else { +#ifdef OCTASIC_DEBUG + printk(KERN_DEBUG "Echo can on channel %d set to %d\n", channel, mode); +#endif + vpm450m->ecmode[channel] = mode; + } + kfree(modify); +} + +void vpm450m_setdtmf(struct vpm450m *vpm450m, int channel, int detect, int mute) +{ + tOCT6100_CHANNEL_MODIFY *modify; + UINT32 ulResult; + + modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_KERNEL); + if (!modify) { + printk(KERN_NOTICE "opvxd115: Unable to allocate memory for setdtmf!\n"); + return; + } + Oct6100ChannelModifyDef(modify); + modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel]; + if (mute) { + vpm450m->chanflags[channel] |= FLAG_MUTE; + modify->VqeConfig.fDtmfToneRemoval = TRUE; + } else { + vpm450m->chanflags[channel] &= ~FLAG_MUTE; + modify->VqeConfig.fDtmfToneRemoval = FALSE; + } + if (detect) + vpm450m->chanflags[channel] |= FLAG_DTMF; + else + vpm450m->chanflags[channel] &= ~FLAG_DTMF; + if (vpm450m->chanflags[channel] & (FLAG_DTMF|FLAG_MUTE)) { + if (!(vpm450m->chanflags[channel] & FLAG_ECHO)) { + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); + } + } else { + if (!(vpm450m->chanflags[channel] & FLAG_ECHO)) + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); + } + + ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify); + if (ulResult != GENERIC_OK) { + printk(KERN_NOTICE "Failed to apply dtmf mute changes on channel %d!\n", channel); + } +/* printk(KERN_DEBUG "VPM450m: Setting DTMF on channel %d: %s / %s\n", channel, (detect ? "DETECT" : "NO DETECT"), (mute ? "MUTE" : "NO MUTE")); */ + kfree(modify); +} + +void vpm450m_setec(struct vpm450m *vpm450m, int channel, int eclen) +{ + if (eclen) { + vpm450m->chanflags[channel] |= FLAG_ECHO; + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_NORMAL); + } else { + vpm450m->chanflags[channel] &= ~FLAG_ECHO; + if (vpm450m->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) { + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); + } else + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); + } +/* printk(KERN_DEBUG "VPM450m: Setting EC on channel %d to %d\n", channel, eclen); */ +} + +int vpm450m_checkirq(struct vpm450m *vpm450m) +{ + tOCT6100_INTERRUPT_FLAGS InterruptFlags; + + Oct6100InterruptServiceRoutineDef(&InterruptFlags); + Oct6100InterruptServiceRoutine(vpm450m->pApiInstance, &InterruptFlags); + + return InterruptFlags.fToneEventsPending ? 1 : 0; +} + +int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start) +{ + tOCT6100_TONE_EVENT tonefound; + tOCT6100_EVENT_GET_TONE tonesearch; + UINT32 ulResult; + + Oct6100EventGetToneDef(&tonesearch); + tonesearch.pToneEvent = &tonefound; + tonesearch.ulMaxToneEvent = 1; + ulResult = Oct6100EventGetTone(vpm450m->pApiInstance, &tonesearch); + if (tonesearch.ulNumValidToneEvent) { + if (channel) + *channel = tonefound.ulUserChanId; + if (tone) { + switch(tonefound.ulToneDetected) { + case SOUT_DTMF_1: + *tone = '1'; + break; + case SOUT_DTMF_2: + *tone = '2'; + break; + case SOUT_DTMF_3: + *tone = '3'; + break; + case SOUT_DTMF_A: + *tone = 'A'; + break; + case SOUT_DTMF_4: + *tone = '4'; + break; + case SOUT_DTMF_5: + *tone = '5'; + break; + case SOUT_DTMF_6: + *tone = '6'; + break; + case SOUT_DTMF_B: + *tone = 'B'; + break; + case SOUT_DTMF_7: + *tone = '7'; + break; + case SOUT_DTMF_8: + *tone = '8'; + break; + case SOUT_DTMF_9: + *tone = '9'; + break; + case SOUT_DTMF_C: + *tone = 'C'; + break; + case SOUT_DTMF_STAR: + *tone = '*'; + break; + case SOUT_DTMF_0: + *tone = '0'; + break; + case SOUT_DTMF_POUND: + *tone = '#'; + break; + case SOUT_DTMF_D: + *tone = 'D'; + break; + case SOUT_G168_1100GB_ON: + *tone = 'f'; + break; + default: +#ifdef OCTASIC_DEBUG + printk(KERN_DEBUG "Unknown tone value %08x\n", tonefound.ulToneDetected); +#endif + *tone = 'u'; + break; + } + } + if (start) + *start = (tonefound.ulEventType == cOCT6100_TONE_PRESENT); + return 1; + } + return 0; +} + +unsigned int get_vpm450m_capacity(void *wc) +{ + UINT32 ulResult; + + tOCT6100_API_GET_CAPACITY_PINS CapacityPins; + + Oct6100ApiGetCapacityPinsDef(&CapacityPins); + CapacityPins.pProcessContext = wc; + CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR; + CapacityPins.fEnableMemClkOut = TRUE; + CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; + + ulResult = Oct6100ApiGetCapacityPins(&CapacityPins); + if (ulResult != cOCT6100_ERR_OK) { + printk(KERN_DEBUG "Failed to get chip capacity, code %08x!\n", ulResult); + return 0; + } + + return CapacityPins.ulCapacityValue; +} + +struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct firmware *firmware) +{ + tOCT6100_CHIP_OPEN *ChipOpen; + tOCT6100_GET_INSTANCE_SIZE InstanceSize; + tOCT6100_CHANNEL_OPEN *ChannelOpen; + UINT32 ulResult; + struct vpm450m *vpm450m; + int x,y,law; +#ifdef CONFIG_4KSTACKS + unsigned long flags; +#endif + + if (!(vpm450m = kmalloc(sizeof(struct vpm450m), GFP_KERNEL))) + return NULL; + + memset(vpm450m, 0, sizeof(struct vpm450m)); + + if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) { + kfree(vpm450m); + return NULL; + } + + memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN)); + + if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) { + kfree(vpm450m); + kfree(ChipOpen); + return NULL; + } + + memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN)); + + for (x=0;x<128;x++) + vpm450m->ecmode[x] = -1; + + vpm450m->numchans = numspans * 32; + printk(KERN_INFO "VPM450: echo cancellation for %d channels\n", vpm450m->numchans); + + Oct6100ChipOpenDef(ChipOpen); + + /* Setup Chip Open Parameters */ + ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ; + Oct6100GetInstanceSizeDef(&InstanceSize); + + ChipOpen->pProcessContext = wc; + + ChipOpen->pbyImageFile = firmware->data; + ChipOpen->ulImageSize = firmware->size; + ChipOpen->fEnableMemClkOut = TRUE; + ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; + ChipOpen->ulMaxChannels = vpm450m->numchans; + ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR; + ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB; + ChipOpen->ulNumMemoryChips = 1; + ChipOpen->ulMaxTdmStreams = 4; + ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ; + ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE; +#if 0 + ChipOpen->fEnableAcousticEcho = TRUE; +#endif + + ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize); + if (ulResult != cOCT6100_ERR_OK) { + printk(KERN_NOTICE "Failed to get instance size, code %08x!\n", ulResult); + kfree(vpm450m); + kfree(ChipOpen); + kfree(ChannelOpen); + return NULL; + } + + + vpm450m->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize); + if (!vpm450m->pApiInstance) { + printk(KERN_NOTICE "Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize); + kfree(vpm450m); + kfree(ChipOpen); + kfree(ChannelOpen); + return NULL; + } + + /* I don't know what to curse more in this comment, the problems caused by + * the 4K kernel stack limit change or the octasic API for being so darn + * stack unfriendly. Stupid, stupid, stupid. So we disable IRQs so we + * don't run the risk of overflowing the stack while we initialize the + * octasic. */ +#ifdef CONFIG_4KSTACKS + local_irq_save(flags); +#endif + ulResult = Oct6100ChipOpen(vpm450m->pApiInstance, ChipOpen); + if (ulResult != cOCT6100_ERR_OK) { + printk(KERN_NOTICE "Failed to open chip, code %08x!\n", ulResult); +#ifdef CONFIG_4KSTACKS + local_irq_restore(flags); +#endif + kfree(vpm450m); + kfree(ChipOpen); + kfree(ChannelOpen); + return NULL; + } + for (x=0;x<128;x++) { + if ((x & 0x03) < numspans) { + /* span timeslots are interleaved 12341234... + * therefore, the lower 2 bits tell us which span this + * timeslot/channel + */ + if (isalaw[x & 0x03]) + law = cOCT6100_PCM_A_LAW; + else + law = cOCT6100_PCM_U_LAW; + Oct6100ChannelOpenDef(ChannelOpen); + ChannelOpen->pulChannelHndl = &vpm450m->aulEchoChanHndl[x]; + ChannelOpen->ulUserChanId = x; + ChannelOpen->TdmConfig.ulRinPcmLaw = law; + ChannelOpen->TdmConfig.ulRinStream = 0; + ChannelOpen->TdmConfig.ulRinTimeslot = x; + ChannelOpen->TdmConfig.ulSinPcmLaw = law; + ChannelOpen->TdmConfig.ulSinStream = 1; + ChannelOpen->TdmConfig.ulSinTimeslot = x; + ChannelOpen->TdmConfig.ulSoutPcmLaw = law; + ChannelOpen->TdmConfig.ulSoutStream = 2; + ChannelOpen->TdmConfig.ulSoutTimeslot = x; + ChannelOpen->TdmConfig.ulRoutPcmLaw = law; + ChannelOpen->TdmConfig.ulRoutStream = 3; + ChannelOpen->TdmConfig.ulRoutTimeslot = x; + ChannelOpen->VqeConfig.fEnableNlp = TRUE; + ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE; + ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE; + + ChannelOpen->fEnableToneDisabler = TRUE; + ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL; + + ulResult = Oct6100ChannelOpen(vpm450m->pApiInstance, ChannelOpen); + if (ulResult != GENERIC_OK) { + printk(KERN_NOTICE "Failed to open channel %d!\n", x); + } + for (y=0;yaulEchoChanHndl[x]; + enable.ulToneNumber = tones[y]; + if (Oct6100ToneDetectionEnable(vpm450m->pApiInstance, &enable) != GENERIC_OK) + printk(KERN_NOTICE "Failed to enable tone detection on channel %d for tone %d!\n", x, y); + } + } + } + +#ifdef CONFIG_4KSTACKS + local_irq_restore(flags); +#endif + kfree(ChipOpen); + kfree(ChannelOpen); + return vpm450m; +} + +void release_vpm450m(struct vpm450m *vpm450m) +{ + UINT32 ulResult; + tOCT6100_CHIP_CLOSE ChipClose; + + Oct6100ChipCloseDef(&ChipClose); + ulResult = Oct6100ChipClose(vpm450m->pApiInstance, &ChipClose); + if (ulResult != cOCT6100_ERR_OK) { + printk(KERN_NOTICE "Failed to close chip, code %08x!\n", ulResult); + } + vfree(vpm450m->pApiInstance); + kfree(vpm450m); +} diff --git a/drivers/dahdi/opvxd115/vpm450m.h b/drivers/dahdi/opvxd115/vpm450m.h new file mode 100644 index 0000000..735a2cc --- /dev/null +++ b/drivers/dahdi/opvxd115/vpm450m.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2006 Digium, Inc. + * + * Mark Spencer + * + * All Rights Reserved + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#ifndef _VPM450M_H +#define _VPM450M_H + +#include + +struct vpm450m; + +/* From driver */ +unsigned int oct_get_reg(void *data, unsigned int reg); +void oct_set_reg(void *data, unsigned int reg, unsigned int val); + +/* From vpm450m */ +struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct firmware *firmware); +unsigned int get_vpm450m_capacity(void *wc); +void vpm450m_setec(struct vpm450m *instance, int channel, int eclen); +void vpm450m_setdtmf(struct vpm450m *instance, int channel, int dtmfdetect, int dtmfmute); +int vpm450m_checkirq(struct vpm450m *vpm450m); +int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start); +void release_vpm450m(struct vpm450m *instance); + +#endif diff --git a/drivers/dahdi/wcopenpci.c b/drivers/dahdi/wcopenpci.c new file mode 100644 index 0000000..ef4d1f3 --- /dev/null +++ b/drivers/dahdi/wcopenpci.c @@ -0,0 +1,1849 @@ +/* + * Voicetronix OpenPCI Interface Driver for Zapata Telephony interface + * + * Written by Mark Spencer + * Matthew Fredrickson + * Ben Kramer + * Ron Lee + * + * Copyright (C) 2001, Linux Support Services, Inc. + * Copyright (C) 2005 - 2007, Voicetronix + * + * 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. + * + */ + +/* Conditional debug options */ +#define VERBOSE_TIMING 0 + +/* Driver constants */ +#define DRIVER_DESCRIPTION "Voicetronix OpenPCI DAHDI driver" +#define DRIVER_AUTHOR "Mark Spencer "\ + "Voicetronix " + +#define NAME "wcopenpci" +#define MAX_PORTS 8 /* Maximum number of ports on each carrier */ +#define MAX_CARDS 8 /* Maximum number of carriers per host */ + +#define DEFAULT_COUNTRY "AUSTRALIA" + + +#include +#include +#include +#include +#include + +#include +#include +#include "proslic.h" +#include + + + +/* Compatibility helpers */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + #include +#else + typedef void irqreturn_t; + #define IRQ_NONE + #define IRQ_HANDLED + #define IRQ_RETVAL(x) + #define __devexit_p(x) x +#endif + +// Centos4.3 uses a modified 2.6.9 kernel, with no indication that +// it is different from the mainstream (or even Centos4.2 2.6.9) +// kernel, so we must crowbar off the dunce-hat manually here. +#if !defined CENTOS4_3 && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) + typedef int gfp_t; + static inline void *kzalloc( size_t n, gfp_t flags ){ + void *p = kmalloc(n,flags); + if (p) memset(p, 0, n); + return p; + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) + #define DEFINE_MUTEX(x) DECLARE_MUTEX(x) + #define mutex_init(x) init_MUTEX(x) + #define mutex_lock(x) down(x) + #define mutex_lock_interruptible(x) down_interruptible(x) + #define mutex_trylock(x) down_trylock(x) + #define mutex_unlock(x) up(x) +#else + #include +#endif + + +static struct fxo_mode { + char *name; + int ohs; + int ohs2; + int rz; + int rt; + int ilim; + int dcv; + int mini; + int acim; + int ring_osc; + int ring_x; +} fxo_modes[] = +{ + { "FCC", 0, 0, 0, 1, 0, 0x3, 0, 0, }, /* US, Canada */ + { "TBR21", 0, 0, 0, 0, 1, 0x3, 0, 0x2, 0x7e6c, 0x023a, }, + /* Austria, Belgium, Denmark, Finland, France, Germany, + Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands, + Norway, Portugal, Spain, Sweden, Switzerland, and UK */ + { "ARGENTINA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "AUSTRALIA", 1, 0, 0, 0, 0, 0, 0x3, 0x3, }, + { "AUSTRIA", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, + { "BAHRAIN", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "BELGIUM", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "BRAZIL", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "BULGARIA", 0, 0, 0, 0, 1, 0x3, 0x0, 0x3, }, + { "CANADA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CHILE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, }, + { "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "EGYPT", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "ELSALVADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "FINLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "FRANCE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "GERMANY", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, + { "GREECE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "GUAM", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "HONGKONG", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "HUNGARY", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "ICELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "INDIA", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, + { "INDONESIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "IRELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ISRAEL", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "ITALY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "JAPAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "JORDAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "KAZAKHSTAN", 0, 0, 0, 0, 0, 0x3, 0, }, + { "KUWAIT", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "LATVIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "LEBANON", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "LUXEMBOURG", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "MACAO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "MALAYSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, /* Current loop >= 20ma */ + { "MALTA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "MEXICO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "MOROCCO", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "NETHERLANDS", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "NEWZEALAND", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, + { "NIGERIA", 0, 0, 0, 0, 0x1, 0x3, 0, 0x2, }, + { "NORWAY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "OMAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "PAKISTAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "PERU", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "PHILIPPINES", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "POLAND", 0, 0, 1, 1, 0, 0x3, 0, 0, }, + { "PORTUGAL", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ROMANIA", 0, 0, 0, 0, 0, 3, 0, 0, }, + { "RUSSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "SAUDIARABIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SINGAPORE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SLOVAKIA", 0, 0, 0, 0, 0, 0x3, 0, 0x3, }, + { "SLOVENIA", 0, 0, 0, 0, 0, 0x3, 0, 0x2, }, + { "SOUTHAFRICA", 1, 0, 1, 0, 0, 0x3, 0, 0x3, }, + { "SOUTHKOREA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SPAIN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SWEDEN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SWITZERLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SYRIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "TAIWAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "THAILAND", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "UAE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "UK", 0, 1, 0, 0, 1, 0x3, 0, 0x5, }, + { "USA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "YEMEN", 0, 0, 0, 0, 0, 0x3, 0, 0, }, +}; + +static struct ps_country_reg { + const char *country; + unsigned short value; +} ps_country_regs[] = { + {"ARGENTINA", 0x8}, + {"AUSTRALIA", 0xD}, + {"AUSTRIA", 0xD}, + {"BAHRAIN", 0xC}, + {"BELGIUM", 0xC}, + {"BRAZIL", 0x8}, + {"BULGARIA", 0xD}, + {"CANADA", 0x8}, + {"CHILE", 0x8}, + {"CHINA", 0xC}, + {"COLOMBIA", 0x8}, + {"CROATIA", 0xC}, + {"CYPRUS", 0xC}, + {"CZECH", 0xC}, + {"DENMARK", 0xC}, + {"ECUADOR", 0x8}, + {"EGYPT", 0x8}, + {"ELSALVADOR", 0x8}, + {"FINLAND", 0xC}, + {"FRANCE", 0xC}, + {"GERMANY", 0xD}, + {"GREECE", 0xC}, + {"GUAM", 0x8}, + {"HONGKONG", 0x8}, + {"HUNGARY", 0x8}, + {"ICELAND", 0xC}, + {"INDIA", 0xF}, + {"INDONESIA", 0x8}, + {"IRELAND", 0xC}, + {"ISRAEL", 0xC}, + {"ITALY", 0xC}, + {"JAPAN", 0x8}, + {"JORDAN", 0x8}, + {"KAZAKHSTAN", 0x8}, + {"KUWAIT", 0x8}, + {"LATVIA", 0xC}, + {"LEBANON", 0xC}, + {"LUXEMBOURG", 0xC}, + {"MACAO", 0x8}, + {"MALAYSIA", 0x8}, + {"MALTA", 0xC}, + {"MEXICO", 0x8}, + {"MOROCCO", 0xC}, + {"NETHERLANDS",0xC}, + {"NEWZEALAND", 0xF}, + {"NIGERIA", 0xC}, + {"NORWAY", 0xC}, + {"OMAN", 0x8}, + {"PAKISTAN", 0x8}, + {"PERU", 0x8}, + {"PHILIPPINES",0x8}, + {"POLAND", 0x8}, + {"PORTUGAL", 0xC}, + {"ROMANIA", 0x8}, + {"RUSSIA", 0x8}, + {"SAUDIARABIA",0x8}, + {"SINGAPORE", 0x8}, + {"SLOVAKIA", 0xE}, + {"SLOVENIA", 0xE}, + {"SOUTHAFRICA",0xE}, + {"SOUTHKOREA", 0x8}, + {"SPAIN", 0xC}, + {"SWEDEN", 0xC}, + {"SWITZERLAND",0xC}, + {"SYRIA", 0x8}, + {"TAIWAN", 0x8}, + {"THAILAND", 0x8}, + {"UAE", 0x8}, + {"UK", 0xC}, + {"USA", 0x8}, + {"YEMEN", 0x8} +}; + +#define INOUT 2 + +/* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses + 32 bits. Allocate an extra set just for control too */ +#define VT_PCIDMA_BLOCKSIZE (DAHDI_MAX_CHUNKSIZE * INOUT * MAX_PORTS * 2 * 2) +#define VT_PCIDMA_MIDDLE (DAHDI_MAX_CHUNKSIZE * MAX_PORTS - 4) +#define VT_PCIDMA_END (DAHDI_MAX_CHUNKSIZE * MAX_PORTS * 2 - 4) + +#define ID_DATA_MAXSIZE 30 + +#define NUM_CAL_REGS 12 +#define NUM_FXO_REGS 60 + +#define TREG(addr) (wc->ioaddr + addr) + +#define TJ_CNTL TREG(0x00) +#define TJ_OPER TREG(0x01) +#define TJ_AUXC TREG(0x02) +#define TJ_AUXD TREG(0x03) +#define TJ_MASK0 TREG(0x04) +#define TJ_MASK1 TREG(0x05) +#define TJ_INTSTAT TREG(0x06) +#define TJ_AUXR TREG(0x07) + +#define TJ_DMAWS TREG(0x08) +#define TJ_DMAWI TREG(0x0c) +#define TJ_DMAWE TREG(0x10) +#define TJ_DMAWC TREG(0x14) +#define TJ_DMARS TREG(0x18) +#define TJ_DMARI TREG(0x1c) +#define TJ_DMARE TREG(0x20) +#define TJ_DMARC TREG(0x24) + +#define TJ_AUXINTPOL TREG(0x2A) + +#define TJ_AUXFUNC TREG(0x2b) +#define TJ_SFDELAY TREG(0x2c) +#define TJ_SERCTL TREG(0x2d) +#define TJ_SFLC TREG(0x2e) +#define TJ_FSCDELAY TREG(0x2f) + +#define TJ_REGBASE TREG(0xc0) + +#define PIB(addr) (TJ_REGBASE + addr * 4) + +#define HTXF_READY (inb(PIB(0)) & 0x10) +#define HRXF_READY (inb(PIB(0)) & 0x20) + + +#define VT_PORT_EMPTY 0 +#define VT_PORT_VDAA 1 /* Voice DAA - FXO */ +#define VT_PORT_PROSLIC 2 /* ProSLIC - FXS */ + +#define VBAT 0xC7 + +#define HKMODE_FWDACT 1 +#define HKMODE_FWDONACT 2 +#define HKMODE_RINGING 4 + +#define HOOK_ONHOOK 0 +#define HOOK_OFFHOOK 1 + +#define DSP_CODEC_RING 12 /* RING rising edge detected */ +#define DSP_CODEC_HKOFF 22 /* station port off hook */ +#define DSP_CODEC_HKON 23 /* station port on hook */ +#define DSP_RING_OFF 24 /* RING falling edge detected */ +#define DSP_DROP 25 + +#define DSP_CODEC_FLASH 26 /* station port hook flash */ + +#define DSP_LOOP_OFFHOOK 38 /* Loop Off hook from OpenPCI */ +#define DSP_LOOP_ONHOOK 39 /* Loop On hook from OpenPCI */ +#define DSP_LOOP_POLARITY 40 /* Loop Polarity from OpenPCI */ +#define DSP_LOOP_NOBATT 41 + +#define DSP_PROSLIC_SANITY 50 /* Sanity alert from a ProSLIC port */ +#define DSP_PROSLIC_PWR_ALARM 51 /* Power Alarm from a ProSLIC port */ +#define DSP_VDAA_ISO_FRAME_E 52 /* ISO-cap frame sync lost on VDAA port*/ + +#if VERBOSE_TIMING + #define REPORT_WAIT(n,x) \ + cardinfo(card->cardnum, #n " wait at %d, " #x " = %d", __LINE__, x ) +#else + #define REPORT_WAIT(n,x) +#endif + +#define BUSY_WAIT(countvar,cond,delay,iter,failret) \ + countvar=0; \ + while(cond){ \ + udelay(delay); \ + if(++countvar > iter){ \ + cardcrit(wc->boardnum, "busy wait FAILED at %d", __LINE__); \ + return failret; \ + } \ + } \ + REPORT_WAIT(busy,i) + +#define LOCKED_WAIT(countvar,cond,delay,iter,failret) \ + countvar=0; \ + while(cond){ \ + udelay(delay); \ + if(++countvar > iter){ \ + dbginfo(wc->boardnum,"busy wait failed at %d",__LINE__); \ + spin_unlock_irqrestore(&wc->lock, flags); \ + return failret; \ + } \ + } \ + REPORT_WAIT(locked,i) + +#define HTXF_WAIT() BUSY_WAIT(i,HTXF_READY,5,500,RET_FAIL) +#define HRXF_WAIT() BUSY_WAIT(i,!HRXF_READY,5,70000,RET_FAIL) +#define HTXF_WAIT_RET(failret) BUSY_WAIT(i,HTXF_READY,5,500,failret) +#define HRXF_WAIT_RET(failret) BUSY_WAIT(i,!HRXF_READY,5,1000,failret) + +#define HTXF_WAIT_LOCKED() LOCKED_WAIT(i,HTXF_READY,5,500,RET_FAIL) +#define HRXF_WAIT_LOCKED() LOCKED_WAIT(i,!HRXF_READY,5,1000,RET_FAIL) +#define HTXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,HTXF_READY,5,500,failret) +#define HRXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,!HRXF_READY,5,1000,failret) + + +struct openpci { + struct pci_dev *dev; + char *variety; + int boardnum; + int portcount; + int porttype[MAX_PORTS]; + + int firmware; + char serial[ID_DATA_MAXSIZE]; + + spinlock_t lock; + + //XXX Replace these with proper try_module_get locking in the dahdi driver. + //int usecount; //XXX + //int dead; //XXX + union { + struct { + int offhook; + } fxo; + struct { + int ohttimer; + int idletxhookstate; /* IDLE changing hook state */ + int lasttxhook; + } fxs; + } mod[MAX_PORTS]; + + unsigned long ioaddr; + dma_addr_t readdma; + dma_addr_t writedma; + volatile unsigned int *writechunk; /* Double-word aligned write memory */ + volatile unsigned int *readchunk; /* Double-word aligned read memory */ + + struct dahdi_chan _chans[MAX_PORTS]; + struct dahdi_chan *chans[MAX_PORTS]; + struct dahdi_span span; +} *cards[MAX_CARDS]; + +// You must hold this lock anytime you access or modify the cards[] array. +DEFINE_MUTEX(cards_mutex); + +static unsigned char fxo_port_lookup[8] = { 0x0, 0x8, 0x4, 0xc, 0x10, 0x18, 0x14, 0x1c}; +static unsigned char fxs_port_lookup[8] = { 0x0, 0x1, 0x2, 0x3, 0x10, 0x11, 0x12, 0x13}; +static char wcopenpci[] = "Voicetronix OpenPCI"; + +static char *country = DEFAULT_COUNTRY; +static int reversepolarity; // = 0 +static int debug; // = 0 + +module_param(country, charp, 0444); +module_param(debug, int, 0600); +module_param(reversepolarity, int, 0600); +MODULE_PARM_DESC(country, "Set the default country name"); +MODULE_PARM_DESC(debug, "Enable verbose logging"); + +//#define DEBUG_LOOP_VOLTAGE 1 +#ifdef DEBUG_LOOP_VOLTAGE + // This param is a 32 bit bitfield where bit 1 << cardnum * 8 << portnum + // will enable voltage monitoring on that port (fxo only presently) + static int voltmeter; // = 0 + module_param(voltmeter, int, 0600); + MODULE_PARM_DESC(voltmeter, "Enable loop voltage metering"); +#endif + + +/* boolean return values */ +#define RET_OK 1 +#define RET_FAIL 0 + +/* Convenience macros for logging */ +#define info(format,...) printk(KERN_INFO NAME ": " format "\n" , ## __VA_ARGS__) +#define warn(format,...) printk(KERN_WARNING NAME ": " format "\n" , ## __VA_ARGS__) +#define crit(format,...) printk(KERN_CRIT NAME ": " format "\n" , ## __VA_ARGS__) +#define cardinfo(cardnum,format,...) info("[%02d] " format, cardnum , ## __VA_ARGS__) +#define cardwarn(cardnum,format,...) warn("[%02d] " format, cardnum , ## __VA_ARGS__) +#define cardcrit(cardnum,format,...) crit("[%02d] " format, cardnum , ## __VA_ARGS__) +#define dbginfo(cardnum,format,...) if(debug) info("[%02d] " format, cardnum , ## __VA_ARGS__) + + +static inline const char *porttype(struct openpci *wc, int port) +{ //{{{ + switch( wc->porttype[port] ) { + case VT_PORT_VDAA: return "VDAA"; + case VT_PORT_PROSLIC: return "ProSLIC"; + case VT_PORT_EMPTY: return "empty port"; + default: return "unknown type"; + } +} //}}} + + +static int __read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned char portadr = fxo_port_lookup[port]; + int i; + + if (HRXF_READY) *value = inb(PIB(1)); + + outb(0x11, PIB(1)); HTXF_WAIT(); + outb(0x2, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); *value = inb(PIB(1)); + + return RET_OK; +} //}}} + +static int read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __read_reg_fxo(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXO port %d, reg %d, read failed!", port, reg); + return RET_FAIL; +} //}}} + +static int __read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned char portadr = fxs_port_lookup[port]; + int i; + + if (HRXF_READY) *value = inb(PIB(1)); + + outb(0x13, PIB(1)); HTXF_WAIT(); + outb(0x2, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); *value = inb(PIB(1)); + + return RET_OK; +} //}}} + +static int read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __read_reg_fxs(wc, port, reg, value) ) { + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS port %d, reg %d, read failed!", port, reg); + return RET_FAIL; +} //}}} + +static int __write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned char portadr = fxo_port_lookup[port]; + int i; + + outb(0x10, PIB(1) ); HTXF_WAIT(); + outb(0x3, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + outb(value, PIB(1)); HTXF_WAIT(); + + return RET_OK; +} //}}} + +static int write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __write_reg_fxo(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXO port %d, reg %d, write(%d) failed!", port, reg, value); + return RET_FAIL; +} //}}} + +static int __write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned char portadr = fxs_port_lookup[port]; + int i; + + outb(0x12, PIB(1) ); HTXF_WAIT(); + outb(0x3, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + outb(value, PIB(1)); HTXF_WAIT(); + + return RET_OK; +} //}}} + +static int write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __write_reg_fxs(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS port %d, reg %d, write(%d) failed!", port, reg, value); + return RET_FAIL; +} //}}} + +static int __wait_indreg_fxs(struct openpci *wc, int port) +{ //{{{ + unsigned char value; + int count = 100; + + while (--count) + { + if( __read_reg_fxs(wc, port, I_STATUS, &value) ){ + if( value == 0 ) + return RET_OK; + } else { + cardcrit(wc->boardnum, + "failed to read port %d PS_IND_ADDR_ST, retrying...", + port); + } + udelay(5); + } + cardcrit(wc->boardnum, "Failed to wait for indirect reg write to port %d", port); + return RET_FAIL; +} //}}} + +static int write_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __wait_indreg_fxs(wc, port) + && __write_reg_fxs(wc, port, IDA_LO, value & 0xff) + && __write_reg_fxs(wc, port, IDA_HI, (value & 0xff00)>>8) + && __write_reg_fxs(wc, port, IAA, reg) + && __wait_indreg_fxs(wc, port) ) + { + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS indreg %d write failed on port %d", reg, port); + return RET_FAIL; +} //}}} + +static int read_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short *value) +{ //{{{ + unsigned long flags; + unsigned char lo, hi; + + spin_lock_irqsave(&wc->lock, flags); + if( __wait_indreg_fxs(wc, port) + && __write_reg_fxs(wc, port, IAA, reg) + && __wait_indreg_fxs(wc, port) + && __read_reg_fxs(wc, port, IDA_LO, &lo) + && __read_reg_fxs(wc, port, IDA_HI, &hi) ) + { + *value = lo | hi << 8; + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + return RET_FAIL; +} //}}} + +static void start_dma(struct openpci *wc) +{ //{{{ + outb(0x0f, TJ_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(0x01, TJ_CNTL); + outb(0x01, TJ_OPER); +} //}}} + +static void restart_dma(struct openpci *wc) +{ //{{{ + /* Reset Master and TDM */ + outb(0x01, TJ_CNTL); + outb(0x01, TJ_OPER); +} //}}} + +/* You must hold the card spinlock to call this function */ +static int __ping_arm(struct openpci *wc) +{ //{{{ + int i; + int pong=0; + + while(pong != 0x02){ + outb(0x02, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); pong = inb(PIB(1)); + dbginfo(wc->boardnum, "ping_arm returned %x", pong); + } + while(pong == 0x02){ + // Poke no-ops into the arm while it is still returning data, + // if 500 usec elapses with no further response from it then + // the message queue is should be completely cleared. + outb(0x00, PIB(1)); HTXF_WAIT(); + i = 100; + while( !HRXF_READY && --i ) udelay(5); + if( i == 0 ) break; + pong = inb(PIB(1)); + dbginfo(wc->boardnum, "ping_arm returned %x.", pong); + } + return RET_OK; +} //}}} + +static void arm_event(struct openpci *wc, char *msg) +{ //{{{ + int port = msg[0]; + + switch(msg[1]){ + case DSP_LOOP_OFFHOOK: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Loop OffHook", port); + break; + + case DSP_LOOP_ONHOOK: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); + dbginfo(wc->boardnum, "Port %d Loop OnHook", port); + break; + + case DSP_LOOP_POLARITY: + dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_POLARITY); + dbginfo(wc->boardnum, "Port %d Loop Polarity", port); + break; + + case DSP_CODEC_RING: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_RING); + dbginfo(wc->boardnum, "Port %d Ring On", port); + break; + + case DSP_RING_OFF: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Ring Off", port); + break; + + case DSP_CODEC_HKOFF: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Station OffHook", port); + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 5; + else + wc->mod[port].fxs.idletxhookstate = 1; + break; + + case DSP_CODEC_HKON: + dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); + dbginfo(wc->boardnum, "Port %d Station OnHook", port); + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 6; + else + wc->mod[port].fxs.idletxhookstate = 2; + break; + + case DSP_CODEC_FLASH: + dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_WINKFLASH); + dbginfo(wc->boardnum, "Port %d Station Flash", port); + break; + + case DSP_DROP: + case DSP_LOOP_NOBATT: + break; + + //XXX What to do to recover from these? + case DSP_PROSLIC_SANITY: + dbginfo(wc->boardnum, "Port %d ProSlic has gone insane!", port); + break; + + case DSP_PROSLIC_PWR_ALARM: + { + char errbuf[32] = " Unknown", *p = errbuf; + int i = 49; + + msg[2] >>= 2; + for(; i < 55; ++i, msg[2] >>= 1 ) + if(msg[2] & 1){ *(++p)='Q'; *(++p)=i; *(++p)=','; } + if( p != errbuf ) *p = '\0'; + cardcrit(wc->boardnum,"%d: ProSlic power ALARM:%s",msg[0],errbuf); + //write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook ); + return; + } + + case DSP_VDAA_ISO_FRAME_E: + dbginfo(wc->boardnum, "Port %d VDAA has lost ISO-Cap frame lock", port); + break; + + default: + cardwarn(wc->boardnum, "Unknown message from Arm[%d] for port %d", + msg[1], port); + break; + } +} //}}} + +/* You must hold the card spinlock to call this function */ +static inline int __read_arm_byte( struct openpci *wc, unsigned char *msg ) +{ //{{{ + int i; + + HRXF_WAIT(); *msg = inb(PIB(1)); + return RET_OK; +} //}}} + +static inline int read_arm_msg( struct openpci *wc, unsigned char *msg ) +{ //{{{ + unsigned long flags; + int i, d, count; + int ret = RET_OK; + + spin_lock_irqsave(&wc->lock, flags); + outb(0x08, PIB(1)); HTXF_WAIT_LOCKED(); + //XXX Do we need to clear the interrupt flag even if this fails? + HRXF_WAIT_LOCKED(); count = inb(PIB(1)); + if( count == 0 ){ + ret = RET_FAIL; + } else if( count < 3 || count > 4 ){ + cardcrit(wc->boardnum, "BOGUS arm message size %d, flushing queue", count); + // NB: This may take a while (up to 500usec or more) to complete + // and we are in the isr at present when this is called, so + // we may miss an interrupt or two while this is done in the + // bottom half, but we are already in trouble, so... + d = debug; debug = 5; __ping_arm( wc ); debug = d; + ret = RET_FAIL; + } else while( --count ){ + if( ! __read_arm_byte(wc, msg) ){ + cardcrit(wc->boardnum, + "Failed to read arm message %d more bytes expected", + count); + ret = RET_FAIL; + break; + } + ++msg; + } + outb(0x09, PIB(1)); HTXF_WAIT_LOCKED(); + spin_unlock_irqrestore(&wc->lock, flags); + return ret; +} //}}} + +static void openpci_arm_work( void *cardptr ) +{ //{{{ + struct openpci *wc = (struct openpci*)cardptr; + unsigned char armmsg[4]; + + if( read_arm_msg(wc, armmsg) ) arm_event(wc, armmsg); +} //}}} + + +static inline void openpci_write(struct openpci *wc, unsigned char flags) +{ //{{{ + int x,y; + volatile unsigned int *writechunk; + + if (flags & 0x01) + writechunk = wc->writechunk; + else if (flags & 0x02) + writechunk = wc->writechunk + DAHDI_CHUNKSIZE*2; + else { + cardcrit(wc->boardnum, "bad write interrupt flags %x, at %x", + flags, inb(TJ_DMAWC) ); + return; + } + /* get data */ + dahdi_transmit(&wc->span); + for (y=0,x=0;xporttype[4]) + writechunk[y] |= (wc->chans[4]->writechunk[x] << 24); + else + writechunk[y] |= (0x01 << 24); + if (wc->porttype[5]) + writechunk[y] |= (wc->chans[5]->writechunk[x] << 16); + if (wc->porttype[6]) + writechunk[y] |= (wc->chans[6]->writechunk[x] << 8); + if (wc->porttype[7]) + writechunk[y] |= (wc->chans[7]->writechunk[x]); + ++y; + + /* transmit first 4 ports */ + writechunk[y]=0x01000000; + /* Make sure first port doesnt equal 0x00 */ + if (wc->porttype[0]){ + if (wc->chans[0]->writechunk[x] == 0) + writechunk[y] |= (0x01 << 24); + else + writechunk[y] |= (wc->chans[0]->writechunk[x] << 24); + } + //else writechunk[y] |= (0x00 << 24); + if (wc->porttype[1]) + writechunk[y] |= (wc->chans[1]->writechunk[x] << 16); + if (wc->porttype[2]) + writechunk[y] |= (wc->chans[2]->writechunk[x] << 8); + if (wc->porttype[3]) + writechunk[y] |= (wc->chans[3]->writechunk[x]); + ++y; +#endif + } +} //}}} + +static inline void openpci_read(struct openpci *wc, unsigned char flags) +{ //{{{ + int x,y; + volatile unsigned int *readchunk; + + if (flags & 0x08) + readchunk = wc->readchunk + DAHDI_CHUNKSIZE*2; + else if (flags & 0x04) + readchunk = wc->readchunk; + else { + cardcrit(wc->boardnum, "bad read interrupt flags %x, at %x", + flags, inb(TJ_DMARC)); + return; + } + + for (y=0,x=0;xporttype[0]) + wc->chans[0]->readchunk[x] = (readchunk[y] >> 24) & 0xff; + if (wc->porttype[1]) + wc->chans[1]->readchunk[x] = (readchunk[y] >> 16) & 0xff; + if (wc->porttype[2]) + wc->chans[2]->readchunk[x] = (readchunk[y] >> 8) & 0xff; + if (wc->porttype[3]) + wc->chans[3]->readchunk[x] = (readchunk[y]) & 0xff; + ++y; + /* Receive second 4 ports */ + if (wc->porttype[4]) + wc->chans[4]->readchunk[x] = (readchunk[y] >> 24) & 0xff; + if (wc->porttype[5]) + wc->chans[5]->readchunk[x] = (readchunk[y] >> 16) & 0xff; + if (wc->porttype[6]) + wc->chans[6]->readchunk[x] = (readchunk[y] >> 8) & 0xff; + if (wc->porttype[7]) + wc->chans[7]->readchunk[x] = (readchunk[y]) & 0xff; + ++y; +#endif + } + /* XXX We're wasting 8 taps. We should get closer :( */ + for (x = 0; x < MAX_PORTS; x++) { + if (wc->porttype[x]) + dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); + } + dahdi_receive(&wc->span); +} //}}} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static irqreturn_t openpci_isr(int irq, void *dev_id, struct pt_regs *regs) +#else +static irqreturn_t openpci_isr(int irq, void *dev_id) +#endif +{ //{{{ + struct openpci *wc = dev_id; + unsigned long flags; + unsigned char status; + + spin_lock_irqsave(&wc->lock, flags); + status = inb(TJ_INTSTAT); + outb(status, TJ_INTSTAT); + + if (!status) { + if(inb(TJ_AUXR) & 0x02) { + spin_unlock_irqrestore(&wc->lock, flags); + return IRQ_NONE; + } + spin_unlock_irqrestore(&wc->lock, flags); + openpci_arm_work(wc); + return IRQ_HANDLED; + } + if (status & 0x10){ + /* PCI Master abort */ + cardcrit(wc->boardnum, "PCI Master Abort."); + /* Stop DMA, wait for watchdog */ + outb(0x00, TJ_OPER); + spin_unlock_irqrestore(&wc->lock, flags); + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&wc->lock, flags); + + if (status & 0x20){ + /* PCI Target abort */ + cardcrit(wc->boardnum, "PCI Target Abort."); + return IRQ_HANDLED; + } + if (status & 0x03){ + openpci_write(wc, status); + } + if (status & 0x0c){ + #ifdef DEBUG_LOOP_VOLTAGE + //{{{ + static int counter[MAX_CARDS]; + int card = wc->boardnum; + int port = ++counter[card] & 0x07; + int ignore = counter[card] & 0xf0; + + if( ! ignore && (voltmeter & ((1 << (card * 8)) << port)) ) { + unsigned char lv; + if( wc->porttype[port] == VT_PORT_VDAA && read_reg_fxo(wc, port, 29, &lv) ) + cardinfo(wc->boardnum, "Port %d loop voltage %d", + port, lv < 128 ? lv : lv - 256); + } + //}}} + #endif + openpci_read(wc, status); + } + + return IRQ_HANDLED; +} //}}} + +static int openpci_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) +{ //{{{ + struct wctdm_stats stats; + struct wctdm_regs regs; + struct wctdm_regop regop; + struct wctdm_echo_coefs echoregs; + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + int x; + + switch (cmd) { + case DAHDI_ONHOOKTRANSFER: + if (wc->porttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + if (get_user(x, (int *)data)) + return -EFAULT; + wc->mod[port].fxs.ohttimer = x << 3; + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[port].fxs.idletxhookstate = 0x2; + switch(wc->mod[port].fxs.lasttxhook) { + case 0x1: + case 0x5: + if (reversepolarity) + wc->mod[port].fxs.lasttxhook = 0x6; + else + wc->mod[port].fxs.lasttxhook = 0x2; + if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) + return -EIO; + } + break; + case DAHDI_SETPOLARITY: + if (get_user(x, (int *)data)) + return -EFAULT; + if (wc->porttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + /* Can't change polarity while ringing or when open */ + if ((wc->mod[port].fxs.lasttxhook == 0x04) || + (wc->mod[port].fxs.lasttxhook == 0x00)) + return -EINVAL; + + if ((x && !reversepolarity) || (!x && reversepolarity)) + wc->mod[port].fxs.lasttxhook |= 0x04; + else + wc->mod[port].fxs.lasttxhook &= ~0x04; + if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) + return -EIO; + break; + case WCTDM_GET_STATS: + if (wc->porttype[port] == VT_PORT_PROSLIC) { + unsigned char linevolt; + if( read_reg_fxs(wc, port, 80, &linevolt) ) + stats.tipvolt = linevolt * -376; + else + return -EIO; + if( read_reg_fxs(wc, port, 81, &linevolt) ) + stats.ringvolt = linevolt * -376; + else + return -EIO; + if( read_reg_fxs(wc, port, 82, &linevolt) ) + stats.batvolt = linevolt * -376; + else + return -EIO; + } else if (wc->porttype[port] == VT_PORT_VDAA) { + unsigned char linevolt; + if( read_reg_fxo(wc, port, 29, &linevolt) ) + stats.tipvolt = stats.ringvolt = stats.batvolt = linevolt * 1000; + else + return -EIO; + } else + return -EINVAL; + if (copy_to_user((struct wctdm_stats *)data, &stats, sizeof(stats))) + return -EFAULT; + break; + case WCTDM_GET_REGS: + if (wc->porttype[port] == VT_PORT_PROSLIC) { + for (x=0;xporttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + printk("Setting indirect %d to 0x%04x on %d\n", + regop.reg, regop.val, chan->chanpos); + if( ! write_indreg_fxs(wc, port, regop.reg, regop.val) ) + return -EIO; + } else { + regop.val &= 0xff; + printk("Setting direct %d to %04x on %d\n", + regop.reg, regop.val, chan->chanpos); + if (wc->porttype[port] == VT_PORT_PROSLIC) { + if( ! write_reg_fxs(wc, port, regop.reg, regop.val) ) + return -EIO; + } else { + if( ! write_reg_fxo(wc, port, regop.reg, regop.val) ) + return -EIO; + } + } + break; + case WCTDM_SET_ECHOTUNE: + cardinfo(wc->boardnum, "Setting echo registers"); + if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)data, sizeof(echoregs))) + return -EFAULT; + + if (wc->porttype[port] == VT_PORT_VDAA) { + /* Set the ACIM and digital echo canceller registers */ + if( ! write_reg_fxo(wc, port, 30, echoregs.acim) + || ! write_reg_fxo(wc, port, 45, echoregs.coef1) + || ! write_reg_fxo(wc, port, 46, echoregs.coef2) + || ! write_reg_fxo(wc, port, 47, echoregs.coef3) + || ! write_reg_fxo(wc, port, 48, echoregs.coef4) + || ! write_reg_fxo(wc, port, 49, echoregs.coef5) + || ! write_reg_fxo(wc, port, 50, echoregs.coef6) + || ! write_reg_fxo(wc, port, 51, echoregs.coef7) + || ! write_reg_fxo(wc, port, 52, echoregs.coef8) ) + { + cardcrit(wc->boardnum, "Failed to set echo registers"); + return -EIO; + } + break; + } else { + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} //}}} + +static int openpci_open(struct dahdi_chan *chan) +{ + struct openpci *wc = chan->pvt; + if( ! wc->porttype[chan->chanpos-1] ) + return -ENODEV; + + //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE + // as the owner of the span that holds the pointer to this function, + // then bump the refcount in the dahdi code _BEFORE_ the potentially + // fatal call to an invalid pointer is made. + //if( wc->dead ) return -ENODEV; + //wc->usecount++; + try_module_get(THIS_MODULE); //XXX + + return 0; +} + +static inline struct openpci* openpci_from_span(struct dahdi_span *span) { + return container_of(span, struct openpci, span); +} + +static int openpci_watchdog(struct dahdi_span *span, int event) +{ + info("TDM: Restarting DMA"); + restart_dma(openpci_from_span(span)); + return 0; +} + +static int openpci_close(struct dahdi_chan *chan) +{ + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + + //XXX wc->usecount--; + //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE + // as the owner of the span that holds the pointer to this function, + // then bump the refcount in the dahdi code _BEFORE_ the potentially + // fatal call to an invalid pointer is made. + module_put(THIS_MODULE); + if (wc->porttype[port] == VT_PORT_PROSLIC) { + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 5; + else + wc->mod[port].fxs.idletxhookstate = 1; + } + /* If we're dead, release us now */ + //XXX if (!wc->usecount && wc->dead) openpci_release(wc); + + return 0; +} + +static int openpci_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) +{ //{{{ + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + int new_hk_state; + + dbginfo(wc->boardnum, "Setting %s port %d hook state %s", + wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", + port, + txsig == 0 ? "ONHOOK" : + txsig == 1 ? "OFFHOOK" : + txsig == 2 ? "START" : + txsig == 3 ? "KEWL" : "UNKNOWN" ); + + switch(wc->porttype[port]) { + case VT_PORT_VDAA: + switch(txsig) { + case DAHDI_TXSIG_START: + case DAHDI_TXSIG_OFFHOOK: + if( write_reg_fxo(wc, port, 5, 0x9) + && write_reg_fxo(wc, port, 0x20, 0x0) ) + wc->mod[port].fxo.offhook = 1; + else + cardcrit(wc->boardnum, "Failed set fxo off-hook"); + break; + + case DAHDI_TXSIG_ONHOOK: + if( write_reg_fxo(wc, port, 5, 0x8) + && write_reg_fxo(wc, port, 0x20, 0x3) ) + wc->mod[port].fxo.offhook = 0; + else + cardcrit(wc->boardnum, "Failed set fxo on-hook"); + break; + + default: + cardcrit(wc->boardnum, + "Can't set FXO port %d tx state to %d", + port, txsig); + } + break; + + case VT_PORT_PROSLIC: + new_hk_state = wc->mod[port].fxs.lasttxhook; + switch(txsig) { + case DAHDI_TXSIG_ONHOOK: + switch(chan->sig) { + case DAHDI_SIG_EM: + case DAHDI_SIG_FXOKS: + case DAHDI_SIG_FXOLS: + new_hk_state = wc->mod[port].fxs.idletxhookstate; + break; + case DAHDI_SIG_FXOGS: + new_hk_state = 3; + break; + } + break; + + case DAHDI_TXSIG_OFFHOOK: + switch(chan->sig) { + case DAHDI_SIG_EM: + new_hk_state = 5; + break; + default: + new_hk_state = wc->mod[port].fxs.idletxhookstate; + break; + } + break; + + case DAHDI_TXSIG_START: + new_hk_state = 4; + break; + + case DAHDI_TXSIG_KEWL: + new_hk_state = 0; + break; + + default: + cardinfo(wc->boardnum, + "Can't set FXS port %d tx state to %d", + port, txsig); + } + dbginfo(wc->boardnum, "%s port %d hook state old %d, new %d", + wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", + port, wc->mod[port].fxs.lasttxhook, new_hk_state ); + + if (new_hk_state != wc->mod[port].fxs.lasttxhook){ + if( write_reg_fxs(wc, port, 64, new_hk_state) ) + wc->mod[port].fxs.lasttxhook = new_hk_state; + else + cardcrit(wc->boardnum, + "Failed to set port %d fxs hookstate from %d to %d", + port, wc->mod[port].fxs.lasttxhook, new_hk_state); + } + break; + + default: + cardcrit(wc->boardnum, + "Unknown module type %d in openpci_hooksig", + wc->porttype[port] ); + } + return 0; +} //}}} + +static const struct dahdi_span_ops openpci_span_ops = { + .owner = THIS_MODULE, + .hooksig = openpci_hooksig, + .open = openpci_open, + .close = openpci_close, + .ioctl = openpci_ioctl, + .watchdog = openpci_watchdog +}; + +static int span_initialize(struct openpci *wc) +{ //{{{ + int x; + + //XXX Set a THIS_MODULE as the owner of the span... + /* Zapata stuff */ + sprintf(wc->span.name, "WCTDM/%d", wc->boardnum); + sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->boardnum + 1); + for (x = 0; x < MAX_PORTS; x++) { + struct dahdi_chan *chan = &wc->_chans[x]; + wc->chans[x] = chan; + sprintf(chan->name, "WCTDM/%d/%d", wc->boardnum, x); + chan->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS + | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + chan->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; + chan->chanpos = x+1; + chan->pvt = wc; + } + wc->span.deflaw = DAHDI_LAW_MULAW; + wc->span.chans = wc->chans; + wc->span.channels = MAX_PORTS; + wc->span.flags = DAHDI_FLAG_RBS; + wc->span.ops = &openpci_span_ops; + + if (dahdi_register(&wc->span, 0)) { + cardcrit(wc->boardnum, "Unable to register span with dahdi"); + return RET_FAIL; + } + return RET_OK; +} //}}} + +static int get_port_type(struct openpci *wc, int port) +{ //{{{ + int i, type; + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + outb(0x20, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); + outb(port, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); + HRXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); type = inb(PIB(1)); + spin_unlock_irqrestore(&wc->lock, flags); + + return type; +} //}}} + +static int check_ports(struct openpci *wc) +{ //{{{ + int i = 0; + + wc->portcount = 0; + for(; i < MAX_PORTS; ++i ){ + wc->porttype[i] = get_port_type(wc, i); + dbginfo(wc->boardnum,"%d: %s", i, porttype(wc,i)); + + switch( wc->porttype[i] ) { + case VT_PORT_PROSLIC: + /* By default, don't send on hook */ + if (reversepolarity) + wc->mod[i].fxs.idletxhookstate = 5; + else + wc->mod[i].fxs.idletxhookstate = 1; + + case VT_PORT_VDAA: + ++wc->portcount; + } + } + // we 'succeed' if any ports were discovered. + return wc->portcount ? RET_OK : RET_FAIL; +} //}}} + +static int configure_vdaa_country(struct openpci *wc, int port, char *name) +{ //{{{ + unsigned char value; + int i; + + for (i=0; i < sizeof(fxo_modes)/sizeof(struct fxo_mode); ++i){ + if(!strcmp(fxo_modes[i].name, name)){ + dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); + goto part2; + } + } + i = 3; + cardinfo(wc->boardnum, "Using default country %s", fxo_modes[i].name); + + part2: + value = (fxo_modes[i].ohs << 6); + value |= (fxo_modes[i].rz << 1); + value |= (fxo_modes[i].rt << 0); + if( ! write_reg_fxo(wc, port, 16, value) ) goto hell; + + /* DC Termination Control - Register 26 */ + value = (fxo_modes[i].dcv << 6); + value |= (fxo_modes[i].mini << 4); + value |= (fxo_modes[i].ilim << 1); + if( ! write_reg_fxo(wc, port, 26, value) ) goto hell; + + /* AC Termination Control - Register 30 */ + value = (fxo_modes[i].acim << 0); + if( ! write_reg_fxo(wc, port, 30, value) ) goto hell; + + /* DAA Control 5 - Register 31 */ + msleep(1); + if( ! read_reg_fxo(wc, port, 31, &value) ) goto hell; + + value = (value & 0xf7) | (fxo_modes[i].ohs2 << 3); + value = value | 0x02; + if( ! write_reg_fxo(wc, port, 31, value) ) goto hell; + + return RET_OK; + + hell: + cardcrit(wc->boardnum, "port %d failed configure vdaa country", port); + return RET_FAIL; +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static void configure_vdaa_port(struct openpci *wc, int port) +{ //{{{ + /* Set Country - default to Australia */ + if( configure_vdaa_country(wc, port, country) ) + ++wc->portcount; + else { + cardcrit(wc->boardnum, "FAILED to configure vdaa port %d. Disabled.", port); + wc->porttype[port] = VT_PORT_EMPTY; + } +} //}}} + +static int configure_proslic_country(struct openpci *wc, int port, const char *name) +{ //{{{ + int i; + + for(i=0; i < sizeof(ps_country_regs)/sizeof(struct ps_country_reg); ++i) { + if(!strcmp(ps_country_regs[i].country, name)){ + dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); + goto part2; + } + } + return -EINVAL; + + part2: + + if( ! write_reg_fxs(wc, port, 10, ps_country_regs[i].value) ){ + cardcrit(wc->boardnum,"%d: failed to write PS_IMPEDANCE", port); + return -EIO; + } + return 0; +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static void configure_proslic_port(struct openpci *wc, int port) +{ //{{{ + /* Set Country - default to Australia */ + switch( configure_proslic_country(wc, port, country) ){ + case 0: + break; + + case -EINVAL: + cardwarn(wc->boardnum,"%d: Country '%s' unknown, using default", port, country); + if( configure_proslic_country(wc, port, DEFAULT_COUNTRY) == 0 ) + goto hell; + + default: + goto hell; + } + + ++wc->portcount; + return; + + hell: + cardcrit(wc->boardnum, "FAILED to configure proslic port %d. Disabled.", port); + wc->porttype[port] = VT_PORT_EMPTY; +} //}}} + +// Do not call this from an interrupt context, it may (indirectly) sleep. +static int configure_ports(struct openpci *wc) +{ //{{{ + unsigned long flags; + int i; + + wc->portcount = 0; + for(i=0; i < MAX_PORTS; ++i){ + switch (wc->porttype[i]){ + case VT_PORT_VDAA: configure_vdaa_port(wc,i); break; + case VT_PORT_PROSLIC: configure_proslic_port(wc,i); break; + } + } + + spin_lock_irqsave(&wc->lock, flags); + outb(0x2c, PIB(1)); HTXF_WAIT_LOCKED(); + outb(0xff, PIB(1)); HTXF_WAIT_LOCKED(); + spin_unlock_irqrestore(&wc->lock, flags); + + // otherwise we 'succeed' if any ports were configured successfully. + return wc->portcount ? RET_OK : RET_FAIL; +} //}}} + +static int __get_arm_id(struct openpci *wc, int field, char *value) +{ //{{{ + int i; + int x=0; + int count=0; + + outb(0x01, PIB(1)); HTXF_WAIT(); + outb(field, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); count = inb(PIB(1)); + if (count > ID_DATA_MAXSIZE){ + cardcrit(wc->boardnum, "Too many bytes of id(%d) data %d/%d", + field, count, ID_DATA_MAXSIZE); + return RET_FAIL; + } + //cardinfo(wc->boardnum, "get_arm_id(%d): byte count %d",field,count); + for(; x < count; ++x){ + HRXF_WAIT(); *value = inb(PIB(1)); + //cardinfo(wc->boardnum, "get_arm_id(%d): byte %d => 0x%02x",field,x,tmp); + ++value; + } + return RET_OK; +} //}}} + +static void enable_interrupts(struct openpci *wc) +{ //{{{ + outb(0x3f, TJ_MASK0); + outb(0x02, TJ_MASK1); +} //}}} + +static void disable_interrupts(struct openpci *wc) +{ //{{{ + outb(0x00, TJ_MASK0); + outb(0x00, TJ_MASK1); +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static int check_arm(struct openpci *wc) +{ //{{{ + char model[ID_DATA_MAXSIZE+1] = { 0 }; + char date[ID_DATA_MAXSIZE+1] = { 0 }; + unsigned long flags; + int i=0; + int tmp=0; + + spin_lock_irqsave(&wc->lock, flags); + while ((tmp != 0x88)&&(++i<100)){ + outb(0x88, PIB(0)); + msleep(1); + tmp = inb(PIB(1)); + } + if (i>=1000) goto limbo; + dbginfo(wc->boardnum, "Arm responded on attempt %d",i); + + // Flush out the queue if we sent several pings before a response. + if(i>1) __ping_arm(wc); + + if( ! __get_arm_id(wc, 0, model) ) goto hell; + sscanf(model, "OpenPCI8.%02d", &(wc->firmware)); + cardinfo(wc->boardnum, " model: %s", model); + + if( ! __get_arm_id(wc, 1, date) ) goto hell; + cardinfo(wc->boardnum, " date: %s", date); + + if( ! __get_arm_id(wc, 2, wc->serial) ) goto hell; + cardinfo(wc->boardnum, " serial: %s", wc->serial); + + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + + hell: + spin_unlock_irqrestore(&wc->lock, flags); + cardwarn(wc->boardnum, "Found ARM processor, dumb firmware."); + return RET_OK; + + limbo: + spin_unlock_irqrestore(&wc->lock, flags); + return RET_FAIL; +} //}}} + +static int arm_monitor(struct openpci *wc, int on) +{ //{{{ + int i; + outb( on ? 0x06 : 0x07, PIB(1) ); HTXF_WAIT(); + return RET_OK; +} //}}} + +static int __devinit openpci_probe_board(struct pci_dev *pdev, const struct pci_device_id *ent) +{ //{{{ + struct openpci *wc; + int boardnum = 0; + int failret = -ENOMEM; + int tmp = 0; + int i; + unsigned long flags; + + if( ent->driver_data != (kernel_ulong_t)&wcopenpci ) + { + info("Probe of non-OpenPCI card, ignoring."); + return -EINVAL; + } + wc = kzalloc(sizeof(struct openpci), GFP_KERNEL); + if (!wc){ + return -ENOMEM; + } + + mutex_lock(&cards_mutex); + for (; boardnum < MAX_CARDS && cards[boardnum]; ++boardnum); + if (boardnum >= MAX_CARDS){ + crit("Too many OpenPCI cards(%d), max is %d.", boardnum, MAX_CARDS); + mutex_unlock(&cards_mutex); + goto hell; + } + cards[boardnum] = wc; + mutex_unlock(&cards_mutex); + + spin_lock_init(&wc->lock); + pci_set_drvdata(pdev, wc); + + wc->boardnum = boardnum; + wc->dev = pdev; + wc->variety = wcopenpci; + + cardinfo(boardnum, "Initialising card"); + if (pci_enable_device(pdev)) { + failret = -EIO; + goto hell_2; + } + wc->ioaddr = pci_resource_start(pdev, 0); + if( ! request_region(wc->ioaddr, 0xff, NAME) ){ + cardcrit(boardnum, "Failed to lock IO region, another driver already using it"); + failret = -EBUSY; + goto hell_2; + } + + spin_lock_irqsave(&wc->lock, flags); + outb(0xff, TJ_AUXD); /* Set up TJ to access the ARM */ + outb(0x78, TJ_AUXC); /* Set up for Jtag */ + outb(0x00, TJ_CNTL); /* pull ERST low */ + spin_unlock_irqrestore(&wc->lock, flags); + msleep(1); /* Wait a bit */ + + dbginfo(boardnum,"Starting ARM"); + spin_lock_irqsave(&wc->lock, flags); + outb(0x01, TJ_CNTL); /* pull ERST high again */ + spin_unlock_irqrestore(&wc->lock, flags); + msleep(100); /* Give it all a chance to boot */ + + if( ! check_arm(wc) ){ + cardcrit(boardnum, "Couldnt find ARM processor"); + failret = -EIO; + goto hell_3; + } + if( wc->firmware < 11 ){ + cardcrit(boardnum, + "Firmware version %d not supported by this driver", + wc->firmware); + cardcrit(boardnum, " contact Voicetronix to have it updated"); + failret = -ENODEV; + goto hell_3; + } + if( ! check_ports(wc) ){ + cardcrit(boardnum, "Couldnt find ports!"); + failret = -EIO; + goto hell_3; + } + + wc->writechunk = pci_alloc_consistent(pdev, VT_PCIDMA_BLOCKSIZE, &wc->writedma); + if (!wc->writechunk) { + cardcrit(boardnum, "Couldnt get DMA memory."); + goto hell_3; + } + wc->readchunk = wc->writechunk + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2 / sizeof(int)); + wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2); + + memset((void*)wc->writechunk,0,VT_PCIDMA_BLOCKSIZE); + + spin_lock_irqsave(&wc->lock, flags); + outb(0xc1, TJ_SERCTL); + outb(0x0, TJ_FSCDELAY); + + outl(wc->writedma, TJ_DMAWS); + outl(wc->writedma + VT_PCIDMA_MIDDLE, TJ_DMAWI); + outl(wc->writedma + VT_PCIDMA_END, TJ_DMAWE); + outl(wc->readdma, TJ_DMARS); + outl(wc->readdma + VT_PCIDMA_MIDDLE, TJ_DMARI); + outl(wc->readdma + VT_PCIDMA_END, TJ_DMARE); + + /* Clear interrupts */ + outb(0xff, TJ_INTSTAT); + spin_unlock_irqrestore(&wc->lock, flags); + + if( ! arm_monitor(wc, 1) ){ + cardcrit(boardnum, "failed to start arm monitoring"); + failret = -EIO; + goto hell_4; + } + msleep(1000); + + i = 0; + while(tmp != 0x88 && ++i < 1000) { + outb(0x88, PIB(0)); + msleep(250); + tmp = inb(PIB(1)); + } + if(i>=1000) { + cardcrit(boardnum, "FAILED to initialise board"); + goto hell_4; + } + + if( ! check_ports(wc) ) { + cardcrit(boardnum, "FAILED to initialise ports"); + failret = -EIO; + goto hell_4; + } + if( ! configure_ports(wc) ){ + cardcrit(boardnum, "Failed to configure ports."); + failret = -EIO; + goto hell_4; + } + cardinfo(wc->boardnum, "have %d configured ports", wc->portcount); + + if( ! span_initialize(wc) ) { + cardcrit(boardnum, "Failed to register with dahdi driver"); + failret = -EFAULT; + goto hell_4; + } + + /* Finalize signalling */ + for (i=0; i < MAX_PORTS; ++i) { + if (wc->porttype[i] == VT_PORT_VDAA) + wc->chans[i]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS + | DAHDI_SIG_CLEAR | DAHDI_SIG_SF; + else if (wc->porttype[i] == VT_PORT_PROSLIC) + wc->chans[i]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS + | DAHDI_SIG_FXOGS | DAHDI_SIG_SF + | DAHDI_SIG_CLEAR | DAHDI_SIG_EM; + else if (wc->porttype[i]) + cardcrit(wc->boardnum, "Port %d has unknown type (%d)", + i, wc->porttype[i]); + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + if (request_irq(pdev->irq, openpci_isr, DAHDI_IRQ_SHARED, NAME, wc)) { + cardcrit(boardnum, "Cant get IRQ!"); + failret = -EIO; + goto hell_5; + } + cardinfo(boardnum, "Got IRQ %d", pdev->irq); + + enable_interrupts(wc); + start_dma(wc); + + cardinfo(boardnum,"Initialised card."); + return 0; + + hell_5: + dahdi_unregister(&wc->span); + hell_4: + if (wc->writechunk){ + pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, + (void*)wc->writechunk, wc->writedma); + } + hell_3: + outb(0x00, TJ_CNTL); + release_region(wc->ioaddr, 0xff); + hell_2: + cards[boardnum] = NULL; + hell: + kfree(wc); + return failret; +} //}}} + +static void __devexit openpci_remove_board(struct pci_dev *pdev) +{ //{{{ + struct openpci *wc = pci_get_drvdata(pdev); + + if(!wc) return; + + arm_monitor(wc,0); + + /* Stop DMA */ + outb(0x00, TJ_OPER); + disable_interrupts(wc); + + //XXX Replace this usecount business... + // and do this BEFORE we invalidate everything above... + // check that we wont try to write to it in the meantime. + /* Release span, possibly delayed */ + //XXX if (!wc->usecount) openpci_release(wc); else wc->dead = 1; + + dahdi_unregister(&wc->span); + outb(0x00, TJ_CNTL); + + pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + release_region(wc->ioaddr, 0xff); + + mutex_lock(&cards_mutex); + cards[wc->boardnum] = NULL; + mutex_unlock(&cards_mutex); + + kfree(wc); + cardinfo(wc->boardnum, "Removed OpenPCI card."); +} //}}} + +static struct pci_device_id openpci_pci_tbl[] = { + { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t) &wcopenpci }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, openpci_pci_tbl); + +static struct pci_driver openpci_driver = { + name: NAME, + probe: openpci_probe_board, + remove: __devexit_p(openpci_remove_board), + suspend: NULL, + resume: NULL, + id_table: openpci_pci_tbl, +}; + +static int __init openpci_init(void) +{ + if( dahdi_pci_module(&openpci_driver) ) + return -ENODEV; + + info("Module loaded %s", debug ? "with debug enabled" : ""); + return 0; +} + +static void __exit openpci_cleanup(void) +{ + pci_unregister_driver(&openpci_driver); + info("Module exit"); +} + +module_init(openpci_init); +module_exit(openpci_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_VERSION(DAHDI_VERSION); +MODULE_LICENSE("GPL"); + diff --git a/drivers/dahdi/zaphfc/Kbuild b/drivers/dahdi/zaphfc/Kbuild new file mode 100644 index 0000000..960fb3a --- /dev/null +++ b/drivers/dahdi/zaphfc/Kbuild @@ -0,0 +1,10 @@ +obj-m += zaphfc.o + +EXTRA_CFLAGS := -I$(src)/.. -Wno-undef + +zaphfc-objs := base.o fifo.o + +$(obj)/base.o: $(src)/zaphfc.h +$(obj)/fifo.o: $(src)/fifo.h + + diff --git a/drivers/dahdi/zaphfc/base.c b/drivers/dahdi/zaphfc/base.c new file mode 100644 index 0000000..841454c --- /dev/null +++ b/drivers/dahdi/zaphfc/base.c @@ -0,0 +1,1710 @@ +/* + * zaphfc.c - Dahdi driver for HFC-S PCI A based ISDN BRI cards + * + * Dahdi rewrite in hardhdlc mode + * Jose A. Deniz + * + * Copyright (C) 2009, Jose A. Deniz + * Copyright (C) 2006, headiisue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Jens Wilke + * + * Original author of this code is + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + * Please read the README file for important infos. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "zaphfc.h" +#include "fifo.h" + +#if CONFIG_PCI + +#define DAHDI_B1 0 +#define DAHDI_B2 1 +#define DAHDI_D 2 + +#define D 0 +#define B1 1 +#define B2 2 + +/* + * Mode Te for all + */ +static int modes; +static int nt_modes[hfc_MAX_BOARDS]; +static int nt_modes_count; +static int force_l1_up; +static struct proc_dir_entry *hfc_proc_zaphfc_dir; + +#ifdef DEBUG +int debug_level; +#endif + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) +#define SET_PROC_DIRENTRY_OWNER(p) do { (p)->owner = THIS_MODULE; } while(0); +#else +#define SET_PROC_DIRENTRY_OWNER(p) do { } while(0); +#endif + +static struct pci_device_id hfc_pci_ids[] = { + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_3069, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, hfc_pci_ids); + +static int __devinit hfc_probe(struct pci_dev *dev + , const struct pci_device_id *ent); +static void __devexit hfc_remove(struct pci_dev *dev); + +static struct pci_driver hfc_driver = { + .name = hfc_DRIVER_NAME, + .id_table = hfc_pci_ids, + .probe = hfc_probe, + .remove = hfc_remove, +}; + +/****************************************** + * HW routines + ******************************************/ + +static void hfc_softreset(struct hfc_card *card) +{ + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "resetting\n", + card->cardnum); + +/* + * Softreset procedure. Put it on, wait and off again + */ + hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET); + udelay(6); + hfc_outb(card, hfc_CIRM, 0); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((hfc_RESET_DELAY * HZ) / 1000); +} + +static void hfc_resetCard(struct hfc_card *card) +{ + card->regs.m1 = 0; + hfc_outb(card, hfc_INT_M1, card->regs.m1); + + card->regs.m2 = 0; + hfc_outb(card, hfc_INT_M2, card->regs.m2); + + hfc_softreset(card); + + card->regs.trm = 0; + hfc_outb(card, hfc_TRM, card->regs.trm); + + /* + * Select the non-capacitive line mode for the S/T interface + */ + card->regs.sctrl = hfc_SCTRL_NONE_CAP; + + if (card->nt_mode) { + /* + * ST-Bit delay for NT-Mode + */ + hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_NT); + + card->regs.sctrl |= hfc_SCTRL_MODE_NT; + } else { + /* + * ST-Bit delay for TE-Mode + */ + hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_TE); + + card->regs.sctrl |= hfc_SCTRL_MODE_TE; + } + + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + + /* + * S/T Auto awake + */ + card->regs.sctrl_e = hfc_SCTRL_E_AUTO_AWAKE; + hfc_outb(card, hfc_SCTRL_E, card->regs.sctrl_e); + + /* + * No B-channel enabled at startup + */ + card->regs.sctrl_r = 0; + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + /* + * HFC Master Mode + */ + hfc_outb(card, hfc_MST_MODE, hfc_MST_MODE_MASTER); + + /* + * Connect internal blocks + */ + card->regs.connect = + hfc_CONNECT_B1_HFC_from_ST | + hfc_CONNECT_B1_ST_from_HFC | + hfc_CONNECT_B1_GCI_from_HFC | + hfc_CONNECT_B2_HFC_from_ST | + hfc_CONNECT_B2_ST_from_HFC | + hfc_CONNECT_B2_GCI_from_HFC; + hfc_outb(card, hfc_CONNECT, card->regs.connect); + + /* + * All bchans are HDLC by default, not useful, actually + * since mode is set during open() + */ + hfc_outb(card, hfc_CTMT, 0); + + /* + * bit order + */ + hfc_outb(card, hfc_CIRM, 0); + + /* + * Enable D-rx FIFO. At least one FIFO must be enabled (by specs) + */ + card->regs.fifo_en = hfc_FIFOEN_DRX; + hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en); + + card->late_irqs = 0; + + /* + * Clear already pending ints + */ + hfc_inb(card, hfc_INT_S1); + hfc_inb(card, hfc_INT_S2); + + /* + * Enable IRQ output + */ + card->regs.m1 = hfc_INTS_DREC | hfc_INTS_L1STATE | hfc_INTS_TIMER; + hfc_outb(card, hfc_INT_M1, card->regs.m1); + + card->regs.m2 = hfc_M2_IRQ_ENABLE; + hfc_outb(card, hfc_INT_M2, card->regs.m2); + + /* + * Unlocks the states machine + */ + hfc_outb(card, hfc_STATES, 0); + + /* + * There's no need to explicitly activate L1 now. + * Activation is managed inside the interrupt routine. + */ +} + +static void hfc_update_fifo_state(struct hfc_card *card) +{ + /* + * I'm not sure if irqsave is needed but there could be a race + * condition since hfc_update_fifo_state could be called from + * both the IRQ handler and the *_(open|close) functions + */ + + unsigned long flags; + spin_lock_irqsave(&card->chans[B1].lock, flags); + if (!card->fifo_suspended && + (card->chans[B1].status == open_framed || + card->chans[B1].status == open_voice)) { + + if (!(card->regs.fifo_en & hfc_FIFOEN_B1RX)) { + card->regs.fifo_en |= hfc_FIFOEN_B1RX; + hfc_clear_fifo_rx(&card->chans[B1].rx); + } + + if (!(card->regs.fifo_en & hfc_FIFOEN_B1TX)) { + card->regs.fifo_en |= hfc_FIFOEN_B1TX; + hfc_clear_fifo_tx(&card->chans[B1].tx); + } + } else { + if (card->regs.fifo_en & hfc_FIFOEN_B1RX) + card->regs.fifo_en &= ~hfc_FIFOEN_B1RX; + if (card->regs.fifo_en & hfc_FIFOEN_B1TX) + card->regs.fifo_en &= ~hfc_FIFOEN_B1TX; + } + spin_unlock_irqrestore(&card->chans[B1].lock, flags); + + spin_lock_irqsave(&card->chans[B2].lock, flags); + if (!card->fifo_suspended && + (card->chans[B2].status == open_framed || + card->chans[B2].status == open_voice || + card->chans[B2].status == sniff_aux)) { + + if (!(card->regs.fifo_en & hfc_FIFOEN_B2RX)) { + card->regs.fifo_en |= hfc_FIFOEN_B2RX; + hfc_clear_fifo_rx(&card->chans[B2].rx); + } + + if (!(card->regs.fifo_en & hfc_FIFOEN_B2TX)) { + card->regs.fifo_en |= hfc_FIFOEN_B2TX; + hfc_clear_fifo_tx(&card->chans[B2].tx); + } + } else { + if (card->regs.fifo_en & hfc_FIFOEN_B2RX) + card->regs.fifo_en &= ~hfc_FIFOEN_B2RX; + if (card->regs.fifo_en & hfc_FIFOEN_B2TX) + card->regs.fifo_en &= ~hfc_FIFOEN_B2TX; + } + spin_unlock_irqrestore(&card->chans[B2].lock, flags); + + spin_lock_irqsave(&card->chans[D].lock, flags); + if (!card->fifo_suspended && + card->chans[D].status == open_framed) { + + if (!(card->regs.fifo_en & hfc_FIFOEN_DTX)) { + card->regs.fifo_en |= hfc_FIFOEN_DTX; + + card->chans[D].tx.ugly_framebuf_size = 0; + card->chans[D].tx.ugly_framebuf_off = 0; + } + } else { + if (card->regs.fifo_en & hfc_FIFOEN_DTX) + card->regs.fifo_en &= ~hfc_FIFOEN_DTX; + } + spin_unlock_irqrestore(&card->chans[D].lock, flags); + + hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en); +} + +static inline void hfc_suspend_fifo(struct hfc_card *card) +{ + card->fifo_suspended = TRUE; + + hfc_update_fifo_state(card); + + /* + * When L1 goes down D rx receives garbage; it is nice to + * clear it to avoid a CRC error on reactivation + * udelay is needed because the FIFO deactivation happens + * in 250us + */ + udelay(250); + hfc_clear_fifo_rx(&card->chans[D].rx); + +#ifdef DEBUG + if (debug_level >= 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "FIFOs suspended\n", + card->cardnum); + } +#endif +} + +static inline void hfc_resume_fifo(struct hfc_card *card) +{ + card->fifo_suspended = FALSE; + + hfc_update_fifo_state(card); + +#ifdef DEBUG + if (debug_level >= 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "FIFOs resumed\n", + card->cardnum); + } +#endif +} + +static void hfc_check_l1_up(struct hfc_card *card) +{ + if ((!card->nt_mode && card->l1_state != 7) + || (card->nt_mode && card->l1_state != 3)) { + + hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION | + hfc_STATES_ACTIVATE| + hfc_STATES_NT_G2_G3); + + /* + * 0 because this is quite verbose when an inferface is unconnected, jaw + */ +#if 0 + if (debug_level >= 1) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "L1 is down, bringing up L1.\n", + card->cardnum); + } +#endif + } +} + + +/******************* + * Dahdi interface * + *******************/ + +static int hfc_zap_open(struct dahdi_chan *zaptel_chan) +{ + struct hfc_chan_duplex *chan = zaptel_chan->pvt; + struct hfc_card *card = chan->card; + + spin_lock(&chan->lock); + + switch (chan->number) { + case D: + if (chan->status != free && + chan->status != open_framed) { + spin_unlock(&chan->lock); + return -EBUSY; + } + chan->status = open_framed; + break; + + case B1: + case B2: + if (chan->status != free) { + spin_unlock(&chan->lock); + return -EBUSY; + } + chan->status = open_voice; + break; + } + + chan->open_by_zaptel = TRUE; + try_module_get(THIS_MODULE); + spin_unlock(&chan->lock); + + switch (chan->number) { + case D: + break; + + case B1: + card->regs.m2 |= hfc_M2_PROC_TRANS; + /* + * Enable transparent mode + */ + card->regs.ctmt |= hfc_CTMT_TRANSB1; + /* + * Reversed bit order + */ + card->regs.cirm |= hfc_CIRM_B1_REV; + /* + * Enable transmission + */ + card->regs.sctrl |= hfc_SCTRL_B1_ENA; + /* + * Enable reception + */ + card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; + break; + + case B2: + card->regs.m2 |= hfc_M2_PROC_TRANS; + card->regs.ctmt |= hfc_CTMT_TRANSB2; + card->regs.cirm |= hfc_CIRM_B2_REV; + card->regs.sctrl |= hfc_SCTRL_B2_ENA; + card->regs.sctrl_r |= hfc_SCTRL_R_B2_ENA; + break; + + } + + /* + * If not already enabled, enable processing transition (8KHz) + * interrupt + */ + hfc_outb(card, hfc_INT_M2, card->regs.m2); + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + hfc_outb(card, hfc_CIRM, card->regs.cirm); + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + hfc_update_fifo_state(card); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s opened as %s.\n", + card->cardnum, + chan->name, + zaptel_chan->name); + + return 0; +} + +static int hfc_zap_close(struct dahdi_chan *zaptel_chan) +{ + struct hfc_chan_duplex *chan = zaptel_chan->pvt; + struct hfc_card *card = chan->card; + + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "hfc_zap_close called with NULL card\n"); + return -1; + } + + spin_lock(&chan->lock); + + if (chan->status == free) { + spin_unlock(&chan->lock); + return -EINVAL; + } + + chan->status = free; + chan->open_by_zaptel = FALSE; + + spin_unlock(&chan->lock); + + switch (chan->number) { + case D: + break; + + case B1: + card->regs.ctmt &= ~hfc_CTMT_TRANSB1; + card->regs.cirm &= ~hfc_CIRM_B1_REV; + card->regs.sctrl &= ~hfc_SCTRL_B1_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B1_ENA; + break; + + case B2: + card->regs.ctmt &= ~hfc_CTMT_TRANSB2; + card->regs.cirm &= ~hfc_CIRM_B2_REV; + card->regs.sctrl &= ~hfc_SCTRL_B2_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA; + break; + } + + if (card->chans[B1].status == free && + card->chans[B2].status == free) + card->regs.m2 &= ~hfc_M2_PROC_TRANS; + + hfc_outb(card, hfc_INT_M2, card->regs.m2); + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + hfc_outb(card, hfc_CIRM, card->regs.cirm); + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + hfc_update_fifo_state(card); + + module_put(THIS_MODULE); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s closed as %s.\n", + card->cardnum, + chan->name, + zaptel_chan->name); + + return 0; +} + +static int hfc_zap_rbsbits(struct dahdi_chan *chan, int bits) +{ + return 0; +} + +static int hfc_zap_ioctl(struct dahdi_chan *chan, + unsigned int cmd, unsigned long data) +{ + switch (cmd) { + + default: + return -ENOTTY; + } + + return 0; +} + +static void hfc_hdlc_hard_xmit(struct dahdi_chan *d_chan) +{ + struct hfc_chan_duplex *chan = d_chan->pvt; + struct hfc_card *card = chan->card; + struct dahdi_hfc *hfccard = card->ztdev; + + atomic_inc(&hfccard->hdlc_pending); + +} + +static int hfc_zap_startup(struct file *file, struct dahdi_span *span) +{ + struct dahdi_hfc *zthfc = dahdi_hfc_from_span(span); + struct hfc_card *hfctmp = zthfc->card; + int alreadyrunning; + + if (!hfctmp) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "no card for span at startup!\n", + hfctmp->cardnum); + } + + alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; + + if (!alreadyrunning) + span->flags |= DAHDI_FLAG_RUNNING; + + return 0; +} + +static int hfc_zap_shutdown(struct dahdi_span *span) +{ + return 0; +} + +static int hfc_zap_maint(struct dahdi_span *span, int cmd) +{ + return 0; +} + +static int hfc_zap_chanconfig(struct file *file, struct dahdi_chan *d_chan, + int sigtype) +{ + struct hfc_chan_duplex *chan = d_chan->pvt; + struct hfc_card *card = chan->card; + struct dahdi_hfc *hfccard = card->ztdev; + + if ((sigtype == DAHDI_SIG_HARDHDLC) && (hfccard->sigchan == d_chan)) { + hfccard->sigactive = 0; + atomic_set(&hfccard->hdlc_pending, 0); + } + + return 0; +} + +static int hfc_zap_spanconfig(struct file *file, struct dahdi_span *span, + struct dahdi_lineconfig *lc) +{ + span->lineconfig = lc->lineconfig; + + return 0; +} + +static const struct dahdi_span_ops hfc_zap_span_ops = { + .owner = THIS_MODULE, + .chanconfig = hfc_zap_chanconfig, + .spanconfig = hfc_zap_spanconfig, + .startup = hfc_zap_startup, + .shutdown = hfc_zap_shutdown, + .maint = hfc_zap_maint, + .rbsbits = hfc_zap_rbsbits, + .open = hfc_zap_open, + .close = hfc_zap_close, + .ioctl = hfc_zap_ioctl, + .hdlc_hard_xmit = hfc_hdlc_hard_xmit +}; + +static int hfc_zap_initialize(struct dahdi_hfc *hfccard) +{ + struct hfc_card *hfctmp = hfccard->card; + int i; + + memset(&hfccard->span, 0x0, sizeof(struct dahdi_span)); + sprintf(hfccard->span.name, "ZTHFC%d", hfctmp->cardnum + 1); + sprintf(hfccard->span.desc, + "HFC-S PCI A ISDN card %d [%s] ", + hfctmp->cardnum, + hfctmp->nt_mode ? "NT" : "TE"); + hfccard->span.spantype = hfctmp->nt_mode ? "NT" : "TE"; + hfccard->span.manufacturer = "Cologne Chips"; + hfccard->span.flags = 0; + hfccard->span.ops = &hfc_zap_span_ops; + hfccard->span.irq = hfctmp->pcidev->irq; + dahdi_copy_string(hfccard->span.devicetype, "HFC-S PCI-A ISDN", + sizeof(hfccard->span.devicetype)); + sprintf(hfccard->span.location, "PCI Bus %02d Slot %02d", + hfctmp->pcidev->bus->number, + PCI_SLOT(hfctmp->pcidev->devfn) + 1); + hfccard->span.chans = hfccard->_chans; + hfccard->span.channels = 3; + for (i = 0; i < hfccard->span.channels; i++) + hfccard->_chans[i] = &hfccard->chans[i]; + hfccard->span.deflaw = DAHDI_LAW_ALAW; + hfccard->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS; + hfccard->span.offset = 0; + + for (i = 0; i < hfccard->span.channels; i++) { + memset(&hfccard->chans[i], 0x0, sizeof(struct dahdi_chan)); + + sprintf(hfccard->chans[i].name, + "ZTHFC%d/%d/%d", + hfctmp->cardnum + 1, 0, i + 1); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "registered %s\n", + hfctmp->cardnum, + hfccard->chans[i].name); + + if (i == hfccard->span.channels - 1) { + hfccard->chans[i].sigcap = DAHDI_SIG_HARDHDLC; + hfccard->sigchan = &hfccard->chans[D]; + hfccard->sigactive = 0; + atomic_set(&hfccard->hdlc_pending, 0); + } else { + hfccard->chans[i].sigcap = + DAHDI_SIG_CLEAR | DAHDI_SIG_DACS; + } + + hfccard->chans[i].chanpos = i + 1; + } + + hfccard->chans[DAHDI_D].readchunk = + hfctmp->chans[D].rx.zaptel_buffer; + + hfccard->chans[DAHDI_D].writechunk = + hfctmp->chans[D].tx.zaptel_buffer; + + hfccard->chans[DAHDI_D].pvt = &hfctmp->chans[D]; + + hfccard->chans[DAHDI_B1].readchunk = + hfctmp->chans[B1].rx.zaptel_buffer; + + hfccard->chans[DAHDI_B1].writechunk = + hfctmp->chans[B1].tx.zaptel_buffer; + + hfccard->chans[DAHDI_B1].pvt = &hfctmp->chans[B1]; + + hfccard->chans[DAHDI_B2].readchunk = + hfctmp->chans[B2].rx.zaptel_buffer; + + hfccard->chans[DAHDI_B2].writechunk = + hfctmp->chans[B2].tx.zaptel_buffer; + + hfccard->chans[DAHDI_B2].pvt = &hfctmp->chans[B2]; + + if (dahdi_register(&hfccard->span, 0)) { + printk(KERN_CRIT "unable to register zaptel device!\n"); + return -1; + } + + return 0; +} + +static void hfc_zap_transmit(struct hfc_chan_simplex *chan) +{ + hfc_fifo_put(chan, chan->zaptel_buffer, DAHDI_CHUNKSIZE); +} + +static void hfc_zap_receive(struct hfc_chan_simplex *chan) +{ + hfc_fifo_get(chan, chan->zaptel_buffer, DAHDI_CHUNKSIZE); +} + +/****************************************** + * Interrupt Handler + ******************************************/ + +static void hfc_handle_timer_interrupt(struct hfc_card *card); +static void hfc_handle_state_interrupt(struct hfc_card *card); +static void hfc_handle_processing_interrupt(struct hfc_card *card); +static void hfc_frame_arrived(struct hfc_chan_duplex *chan); +static void hfc_handle_voice(struct hfc_card *card); + +#if (KERNEL_VERSION(2, 6, 24) < LINUX_VERSION_CODE) +static irqreturn_t hfc_interrupt(int irq, void *dev_id) +#else +static irqreturn_t hfc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#endif +{ + struct hfc_card *card = dev_id; + unsigned long flags; + u8 status, s1, s2; + + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "spurious interrupt (IRQ %d)\n", + irq); + return IRQ_NONE; + } + + spin_lock_irqsave(&card->lock, flags); + status = hfc_inb(card, hfc_STATUS); + if (!(status & hfc_STATUS_ANYINT)) { + /* + * maybe we are sharing the irq + */ + spin_unlock_irqrestore(&card->lock, flags); + return IRQ_NONE; + } + + /* We used to ingore the IRQ when the card was in processing + * state but apparently there is no restriction to access the + * card in such state: + * + * Joerg Ciesielski wrote: + * > There is no restriction for the IRQ handler to access + * > HFC-S PCI during processing phase. A IRQ latency of 375 us + * > is also no problem since there are no interrupt sources in + * > HFC-S PCI which must be handled very fast. + * > Due to its deep fifos the IRQ latency can be several ms with + * > out the risk of loosing data. Even the S/T state interrupts + * > must not be handled with a latency less than <5ms. + * > + * > The processing phase only indicates that HFC-S PCI is + * > processing the Fifos as PCI master so that data is read and + * > written in the 32k memory window. But there is no restriction + * > to access data in the memory window during this time. + * + * // if (status & hfc_STATUS_PCI_PROC) { + * // return IRQ_HANDLED; + * // } + */ + + s1 = hfc_inb(card, hfc_INT_S1); + s2 = hfc_inb(card, hfc_INT_S2); + + if (s1 != 0) { + if (s1 & hfc_INTS_TIMER) { + /* + * timer (bit 7) + */ + hfc_handle_timer_interrupt(card); + } + + if (s1 & hfc_INTS_L1STATE) { + /* + * state machine (bit 6) + */ + hfc_handle_state_interrupt(card); + } + + if (s1 & hfc_INTS_DREC) { + /* + * D chan RX (bit 5) + */ + hfc_frame_arrived(&card->chans[D]); + } + + if (s1 & hfc_INTS_B1REC) { + /* + * B1 chan RX (bit 3) + */ + hfc_frame_arrived(&card->chans[B1]); + } + + if (s1 & hfc_INTS_B2REC) { + /* + * B2 chan RX (bit 4) + */ + hfc_frame_arrived(&card->chans[B2]); + } + + if (s1 & hfc_INTS_DTRANS) { + /* + * D chan TX (bit 2) + */ + } + + if (s1 & hfc_INTS_B1TRANS) { + /* + * B1 chan TX (bit 0) + */ + } + + if (s1 & hfc_INTS_B2TRANS) { + /* + * B2 chan TX (bit 1) + */ + } + + } + + if (s2 != 0) { + if (s2 & hfc_M2_PMESEL) { + /* + * kaboom irq (bit 7) + * + * CologneChip says: + * + * the meaning of this fatal error bit is that HFC-S + * PCI as PCI master could not access the PCI bus + * within 125us to finish its data processing. If this + * happens only very seldom it does not cause big + * problems but of course some B-channel or D-channel + * data will be corrupted due to this event. + * + * Unfortunately this bit is only set once after the + * problem occurs and can only be reseted by a + * software reset. That means it is not easily + * possible to check how often this fatal error + * happens. + * + */ + + if (!card->sync_loss_reported) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "sync lost, pci performance too low!\n", + card->cardnum); + + card->sync_loss_reported = TRUE; + } + } + + if (s2 & hfc_M2_GCI_MON_REC) { + /* + * RxR monitor channel (bit 2) + */ + } + + if (s2 & hfc_M2_GCI_I_CHG) { + /* + * GCI I-change (bit 1) + */ + } + + if (s2 & hfc_M2_PROC_TRANS) { + /* + * processing/non-processing transition (bit 0) + */ + hfc_handle_processing_interrupt(card); + } + + } + + spin_unlock_irqrestore(&card->lock, flags); + + return IRQ_HANDLED; +} + +static void hfc_handle_timer_interrupt(struct hfc_card *card) +{ + if (card->ignore_first_timer_interrupt) { + card->ignore_first_timer_interrupt = FALSE; + return; + } + + if ((card->nt_mode && card->l1_state == 3) || + (!card->nt_mode && card->l1_state == 7)) { + + card->regs.ctmt &= ~hfc_CTMT_TIMER_MASK; + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + + hfc_resume_fifo(card); + } +} + +static void hfc_handle_state_interrupt(struct hfc_card *card) +{ + u8 new_state = hfc_inb(card, hfc_STATES) & hfc_STATES_STATE_MASK; + +#ifdef DEBUG + if (debug_level >= 1) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "layer 1 state = %c%d\n", + card->cardnum, + card->nt_mode ? 'G' : 'F', + new_state); + } +#endif + + if (card->nt_mode) { + /* + * NT mode + */ + + if (new_state == 3) { + /* + * fix to G3 state (see specs) + */ + hfc_outb(card, hfc_STATES, hfc_STATES_LOAD_STATE | 3); + } + + if (new_state == 3 && card->l1_state != 3) + hfc_resume_fifo(card); + + if (new_state != 3 && card->l1_state == 3) + hfc_suspend_fifo(card); + + } else { + if (new_state == 3) { + /* + * Keep L1 up... zaptel & libpri expects + * a always up L1... + * Enable only when using an unpatched libpri + */ + + if (force_l1_up) { + hfc_outb(card, hfc_STATES, + hfc_STATES_DO_ACTION | + hfc_STATES_ACTIVATE| + hfc_STATES_NT_G2_G3); + } + } + + if (new_state == 7 && card->l1_state != 7) { + /* + * TE is now active, schedule FIFO activation after + * some time, otherwise the first frames are lost + */ + + card->regs.ctmt |= hfc_CTMT_TIMER_50 | + hfc_CTMT_TIMER_CLEAR; + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + + /* + * Activating the timer firest an + * interrupt immediately, we + * obviously need to ignore it + */ + card->ignore_first_timer_interrupt = TRUE; + } + + if (new_state != 7 && card->l1_state == 7) { + /* + * TE has become inactive, disable FIFO + */ + hfc_suspend_fifo(card); + } + } + + card->l1_state = new_state; +} + +static void hfc_handle_processing_interrupt(struct hfc_card *card) +{ + int available_bytes = 0; + + /* + * Synchronize with the first enabled channel + */ + if (card->regs.fifo_en & hfc_FIFOEN_B1RX) + available_bytes = hfc_fifo_used_rx(&card->chans[B1].rx); + if (card->regs.fifo_en & hfc_FIFOEN_B2RX) + available_bytes = hfc_fifo_used_rx(&card->chans[B2].rx); + else + available_bytes = -1; + + if ((available_bytes == -1 && card->ticks == 8) || + available_bytes >= DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) { + card->ticks = 0; + + if (available_bytes > DAHDI_CHUNKSIZE*2 + hfc_RX_FIFO_PRELOAD) { + card->late_irqs++; + /* + * we are out of sync, clear fifos, jaw + */ + hfc_clear_fifo_rx(&card->chans[B1].rx); + hfc_clear_fifo_tx(&card->chans[B1].tx); + hfc_clear_fifo_rx(&card->chans[B2].rx); + hfc_clear_fifo_tx(&card->chans[B2].tx); + +#ifdef DEBUG + if (debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "late IRQ, %d bytes late\n", + card->cardnum, + available_bytes - + (DAHDI_CHUNKSIZE + + hfc_RX_FIFO_PRELOAD)); + } +#endif + } else { + hfc_handle_voice(card); + } + } + + card->ticks++; +} + + +static void hfc_handle_voice(struct hfc_card *card) +{ + struct dahdi_hfc *hfccard = card->ztdev; + int frame_left, res; + unsigned char buf[hfc_HDLC_BUF_LEN]; + unsigned int size = sizeof(buf) / sizeof(buf[0]); + + + if (card->chans[B1].status != open_voice && + card->chans[B2].status != open_voice) + return; + + dahdi_transmit(&hfccard->span); + + if (card->regs.fifo_en & hfc_FIFOEN_B1TX) + hfc_zap_transmit(&card->chans[B1].tx); + if (card->regs.fifo_en & hfc_FIFOEN_B2TX) + hfc_zap_transmit(&card->chans[B2].tx); + + /* + * dahdi hdlc frame tx + */ + + if (atomic_read(&hfccard->hdlc_pending)) { + hfc_check_l1_up(card); + res = dahdi_hdlc_getbuf(hfccard->sigchan, buf, &size); + if (size > 0) { + hfccard->sigactive = 1; + memcpy(card->chans[D].tx.ugly_framebuf + + card->chans[D].tx.ugly_framebuf_size, + buf, size); + card->chans[D].tx.ugly_framebuf_size += size; + if (res != 0) { + hfc_fifo_put_frame(&card->chans[D].tx, + card->chans[D].tx.ugly_framebuf, + card->chans[D].tx.ugly_framebuf_size); + ++hfccard->frames_out; + hfccard->sigactive = 0; + card->chans[D].tx.ugly_framebuf_size + = 0; + atomic_dec(&hfccard->hdlc_pending); + } + } + } + /* + * dahdi hdlc frame tx done + */ + + if (card->regs.fifo_en & hfc_FIFOEN_B1RX) + hfc_zap_receive(&card->chans[B1].rx); + else + memset(&card->chans[B1].rx.zaptel_buffer, 0x7f, + sizeof(card->chans[B1].rx.zaptel_buffer)); + + if (card->regs.fifo_en & hfc_FIFOEN_B2RX) + hfc_zap_receive(&card->chans[B2].rx); + else + memset(&card->chans[B2].rx.zaptel_buffer, 0x7f, + sizeof(card->chans[B1].rx.zaptel_buffer)); + + /* + * Echo cancellation + */ + dahdi_ec_chunk(&hfccard->chans[DAHDI_B1], + card->chans[B1].rx.zaptel_buffer, + card->chans[B1].tx.zaptel_buffer); + dahdi_ec_chunk(&hfccard->chans[DAHDI_B2], + card->chans[B2].rx.zaptel_buffer, + card->chans[B2].tx.zaptel_buffer); + + /* + * dahdi hdlc frame rx + */ + if (hfc_fifo_has_frames(&card->chans[D].rx)) + hfc_frame_arrived(&card->chans[D]); + + if (card->chans[D].rx.ugly_framebuf_size) { + frame_left = card->chans[D].rx.ugly_framebuf_size - + card->chans[D].rx.ugly_framebuf_off ; + if (frame_left > hfc_HDLC_BUF_LEN) { + dahdi_hdlc_putbuf(hfccard->sigchan, + card->chans[D].rx.ugly_framebuf + + card->chans[D].rx.ugly_framebuf_off, + hfc_HDLC_BUF_LEN); + card->chans[D].rx.ugly_framebuf_off += + hfc_HDLC_BUF_LEN; + } else { + dahdi_hdlc_putbuf(hfccard->sigchan, + card->chans[D].rx.ugly_framebuf + + card->chans[D].rx.ugly_framebuf_off, + frame_left); + dahdi_hdlc_finish(hfccard->sigchan); + card->chans[D].rx.ugly_framebuf_size = 0; + card->chans[D].rx.ugly_framebuf_off = 0; + } + } + /* + * dahdi hdlc frame rx done + */ + + if (hfccard->span.flags & DAHDI_FLAG_RUNNING) + dahdi_receive(&hfccard->span); + +} + +static void hfc_frame_arrived(struct hfc_chan_duplex *chan) +{ + struct hfc_card *card = chan->card; + int antiloop = 16; + struct sk_buff *skb; + + while (hfc_fifo_has_frames(&chan->rx) && --antiloop) { + int frame_size = hfc_fifo_get_frame_size(&chan->rx); + + if (frame_size < 3) { +#ifdef DEBUG + if (debug_level >= 2) + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "invalid frame received, " + "just %d bytes\n", + card->cardnum, + chan->name, + frame_size); +#endif + + hfc_fifo_drop_frame(&chan->rx); + + + continue; + } else if (frame_size == 3) { +#ifdef DEBUG + if (debug_level >= 2) + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "empty frame received\n", + card->cardnum, + chan->name); +#endif + + hfc_fifo_drop_frame(&chan->rx); + + + continue; + } + + if (chan->open_by_zaptel && + card->chans[D].rx.ugly_framebuf_size) { + + /* + * We have to wait for Dahdi to transmit the + * frame... wait for next time + */ + + break; + } + + skb = dev_alloc_skb(frame_size - 3); + + if (!skb) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "cannot allocate skb: frame dropped\n", + card->cardnum, + chan->name); + + hfc_fifo_drop_frame(&chan->rx); + + + continue; + } + + + /* + * HFC does the checksum + */ +#ifndef CHECKSUM_HW + skb->ip_summed = CHECKSUM_COMPLETE; +#else + skb->ip_summed = CHECKSUM_HW; +#endif + + if (chan->open_by_zaptel) { + card->chans[D].rx.ugly_framebuf_size = frame_size - 1; + + if (hfc_fifo_get_frame(&card->chans[D].rx, + card->chans[D].rx.ugly_framebuf, + frame_size - 1) == -1) { + dev_kfree_skb(skb); + continue; + } + + memcpy(skb_put(skb, frame_size - 3), + card->chans[D].rx.ugly_framebuf, + frame_size - 3); + } else { + if (hfc_fifo_get_frame(&chan->rx, + skb_put(skb, frame_size - 3), + frame_size - 3) == -1) { + dev_kfree_skb(skb); + continue; + } + } + } + + if (!antiloop) + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "Infinite loop detected\n", + card->cardnum); +} + +/****************************************** + * Module initialization and cleanup + ******************************************/ + +static int __devinit hfc_probe(struct pci_dev *pci_dev, + const struct pci_device_id *ent) +{ + static int cardnum; + int err; + int i; + + struct hfc_card *card = NULL; + struct dahdi_hfc *zthfc = NULL; + card = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "unable to kmalloc!\n"); + err = -ENOMEM; + goto err_alloc_hfccard; + } + + memset(card, 0x00, sizeof(struct hfc_card)); + card->cardnum = cardnum; + card->pcidev = pci_dev; + spin_lock_init(&card->lock); + + pci_set_drvdata(pci_dev, card); + + err = pci_enable_device(pci_dev); + if (err) + goto err_pci_enable_device; + + err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT); + if (err) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "card %d: " + "No suitable DMA configuration available.\n", + card->cardnum); + goto err_pci_set_dma_mask; + } + + pci_write_config_word(pci_dev, PCI_COMMAND, PCI_COMMAND_MEMORY); + err = pci_request_regions(pci_dev, hfc_DRIVER_NAME); + if (err) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "cannot request I/O memory region\n", + card->cardnum); + goto err_pci_request_regions; + } + + pci_set_master(pci_dev); + + if (!pci_dev->irq) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "no irq!\n", + card->cardnum); + err = -ENODEV; + goto err_noirq; + } + + card->io_bus_mem = pci_resource_start(pci_dev, 1); + if (!card->io_bus_mem) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "no iomem!\n", + card->cardnum); + err = -ENODEV; + goto err_noiobase; + } + + card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE); + if (!(card->io_mem)) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "cannot ioremap I/O memory\n", + card->cardnum); + err = -ENODEV; + goto err_ioremap; + } + + /* + * pci_alloc_consistent guarantees alignment + * (Documentation/DMA-mapping.txt) + */ + card->fifo_mem = pci_alloc_consistent(pci_dev, + hfc_FIFO_SIZE, &card->fifo_bus_mem); + if (!card->fifo_mem) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "unable to allocate FIFO DMA memory!\n", + card->cardnum); + err = -ENOMEM; + goto err_alloc_fifo; + } + + memset(card->fifo_mem, 0x00, hfc_FIFO_SIZE); + + card->fifos = card->fifo_mem; + + pci_write_config_dword(card->pcidev, hfc_PCI_MWBA, card->fifo_bus_mem); + + err = request_irq(card->pcidev->irq, &hfc_interrupt, + +#if (KERNEL_VERSION(2, 6, 23) < LINUX_VERSION_CODE) + IRQF_SHARED, hfc_DRIVER_NAME, card); +#else + SA_SHIRQ, hfc_DRIVER_NAME, card); +#endif + + if (err) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "unable to register irq\n", + card->cardnum); + goto err_request_irq; + } + + card->nt_mode = FALSE; + + if (modes & (1 << card->cardnum)) + card->nt_mode = TRUE; + + for (i = 0; i < nt_modes_count; i++) { + if (nt_modes[i] == card->cardnum) + card->nt_mode = TRUE; + } + + /* + * D Channel + */ + card->chans[D].card = card; + card->chans[D].name = "D"; + card->chans[D].status = free; + card->chans[D].number = D; + spin_lock_init(&card->chans[D].lock); + + card->chans[D].rx.chan = &card->chans[D]; + card->chans[D].rx.fifo_base = card->fifos + 0x4000; + card->chans[D].rx.z_base = card->fifos + 0x4000; + card->chans[D].rx.z1_base = card->fifos + 0x6080; + card->chans[D].rx.z2_base = card->fifos + 0x6082; + card->chans[D].rx.z_min = 0x0000; + card->chans[D].rx.z_max = 0x01FF; + card->chans[D].rx.f_min = 0x10; + card->chans[D].rx.f_max = 0x1F; + card->chans[D].rx.f1 = card->fifos + 0x60a0; + card->chans[D].rx.f2 = card->fifos + 0x60a1; + card->chans[D].rx.fifo_size = card->chans[D].rx.z_max + - card->chans[D].rx.z_min + 1; + card->chans[D].rx.f_num = card->chans[D].rx.f_max + - card->chans[D].rx.f_min + 1; + + card->chans[D].tx.chan = &card->chans[D]; + card->chans[D].tx.fifo_base = card->fifos + 0x0000; + card->chans[D].tx.z_base = card->fifos + 0x0000; + card->chans[D].tx.z1_base = card->fifos + 0x2080; + card->chans[D].tx.z2_base = card->fifos + 0x2082; + card->chans[D].tx.z_min = 0x0000; + card->chans[D].tx.z_max = 0x01FF; + card->chans[D].tx.f_min = 0x10; + card->chans[D].tx.f_max = 0x1F; + card->chans[D].tx.f1 = card->fifos + 0x20a0; + card->chans[D].tx.f2 = card->fifos + 0x20a1; + card->chans[D].tx.fifo_size = card->chans[D].tx.z_max - + card->chans[D].tx.z_min + 1; + card->chans[D].tx.f_num = card->chans[D].tx.f_max - + card->chans[D].tx.f_min + 1; + + /* + * B1 Channel + */ + card->chans[B1].card = card; + card->chans[B1].name = "B1"; + card->chans[B1].status = free; + card->chans[B1].number = B1; + card->chans[B1].protocol = 0; + spin_lock_init(&card->chans[B1].lock); + + card->chans[B1].rx.chan = &card->chans[B1]; + card->chans[B1].rx.fifo_base = card->fifos + 0x4200; + card->chans[B1].rx.z_base = card->fifos + 0x4000; + card->chans[B1].rx.z1_base = card->fifos + 0x6000; + card->chans[B1].rx.z2_base = card->fifos + 0x6002; + card->chans[B1].rx.z_min = 0x0200; + card->chans[B1].rx.z_max = 0x1FFF; + card->chans[B1].rx.f_min = 0x00; + card->chans[B1].rx.f_max = 0x1F; + card->chans[B1].rx.f1 = card->fifos + 0x6080; + card->chans[B1].rx.f2 = card->fifos + 0x6081; + card->chans[B1].rx.fifo_size = card->chans[B1].rx.z_max - + card->chans[B1].rx.z_min + 1; + card->chans[B1].rx.f_num = card->chans[B1].rx.f_max - + card->chans[B1].rx.f_min + 1; + + card->chans[B1].tx.chan = &card->chans[B1]; + card->chans[B1].tx.fifo_base = card->fifos + 0x0200; + card->chans[B1].tx.z_base = card->fifos + 0x0000; + card->chans[B1].tx.z1_base = card->fifos + 0x2000; + card->chans[B1].tx.z2_base = card->fifos + 0x2002; + card->chans[B1].tx.z_min = 0x0200; + card->chans[B1].tx.z_max = 0x1FFF; + card->chans[B1].tx.f_min = 0x00; + card->chans[B1].tx.f_max = 0x1F; + card->chans[B1].tx.f1 = card->fifos + 0x2080; + card->chans[B1].tx.f2 = card->fifos + 0x2081; + card->chans[B1].tx.fifo_size = card->chans[B1].tx.z_max - + card->chans[B1].tx.z_min + 1; + card->chans[B1].tx.f_num = card->chans[B1].tx.f_max - + card->chans[B1].tx.f_min + 1; + + /* + * B2 Channel + */ + card->chans[B2].card = card; + card->chans[B2].name = "B2"; + card->chans[B2].status = free; + card->chans[B2].number = B2; + card->chans[B2].protocol = 0; + spin_lock_init(&card->chans[B2].lock); + + card->chans[B2].rx.chan = &card->chans[B2]; + card->chans[B2].rx.fifo_base = card->fifos + 0x6200, + card->chans[B2].rx.z_base = card->fifos + 0x6000; + card->chans[B2].rx.z1_base = card->fifos + 0x6100; + card->chans[B2].rx.z2_base = card->fifos + 0x6102; + card->chans[B2].rx.z_min = 0x0200; + card->chans[B2].rx.z_max = 0x1FFF; + card->chans[B2].rx.f_min = 0x00; + card->chans[B2].rx.f_max = 0x1F; + card->chans[B2].rx.f1 = card->fifos + 0x6180; + card->chans[B2].rx.f2 = card->fifos + 0x6181; + card->chans[B2].rx.fifo_size = card->chans[B2].rx.z_max - + card->chans[B2].rx.z_min + 1; + card->chans[B2].rx.f_num = card->chans[B2].rx.f_max - + card->chans[B2].rx.f_min + 1; + + card->chans[B2].tx.chan = &card->chans[B2]; + card->chans[B2].tx.fifo_base = card->fifos + 0x2200; + card->chans[B2].tx.z_base = card->fifos + 0x2000; + card->chans[B2].tx.z1_base = card->fifos + 0x2100; + card->chans[B2].tx.z2_base = card->fifos + 0x2102; + card->chans[B2].tx.z_min = 0x0200; + card->chans[B2].tx.z_max = 0x1FFF; + card->chans[B2].tx.f_min = 0x00; + card->chans[B2].tx.f_max = 0x1F; + card->chans[B2].tx.f1 = card->fifos + 0x2180; + card->chans[B2].tx.f2 = card->fifos + 0x2181; + card->chans[B2].tx.fifo_size = card->chans[B2].tx.z_max - + card->chans[B2].tx.z_min + 1; + card->chans[B2].tx.f_num = card->chans[B2].tx.f_max - + card->chans[B2].tx.f_min + 1; + + /* + * All done + */ + + zthfc = kmalloc(sizeof(struct dahdi_hfc), GFP_KERNEL); + if (!zthfc) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "unable to kmalloc!\n"); + goto err_request_irq; + } + memset(zthfc, 0x0, sizeof(struct dahdi_hfc)); + + zthfc->card = card; + hfc_zap_initialize(zthfc); + card->ztdev = zthfc; + + snprintf(card->proc_dir_name, + sizeof(card->proc_dir_name), + "%d", card->cardnum); + card->proc_dir = proc_mkdir(card->proc_dir_name, hfc_proc_zaphfc_dir); + SET_PROC_DIRENTRY_OWNER(card->proc_dir); + + hfc_resetCard(card); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d configured for %s mode at mem %#lx (0x%p) IRQ %u\n", + card->cardnum, + card->nt_mode ? "NT" : "TE", + card->io_bus_mem, + card->io_mem, + card->pcidev->irq); + + cardnum++; + + return 0; + +err_request_irq: + pci_free_consistent(pci_dev, hfc_FIFO_SIZE, + card->fifo_mem, card->fifo_bus_mem); +err_alloc_fifo: + iounmap(card->io_mem); +err_ioremap: +err_noiobase: +err_noirq: + pci_release_regions(pci_dev); +err_pci_request_regions: +err_pci_set_dma_mask: +err_pci_enable_device: + kfree(card); +err_alloc_hfccard: + return err; +} + +static void __devexit hfc_remove(struct pci_dev *pci_dev) +{ + struct hfc_card *card = pci_get_drvdata(pci_dev); + + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "shutting down card at %p.\n", + card->cardnum, + card->io_mem); + + hfc_softreset(card); + + dahdi_unregister(&card->ztdev->span); + + + /* + * disable memio and bustmaster + */ + pci_write_config_word(pci_dev, PCI_COMMAND, 0); + + remove_proc_entry("bufs", card->proc_dir); + remove_proc_entry("fifos", card->proc_dir); + remove_proc_entry("info", card->proc_dir); + remove_proc_entry(card->proc_dir_name, hfc_proc_zaphfc_dir); + + free_irq(pci_dev->irq, card); + + pci_free_consistent(pci_dev, hfc_FIFO_SIZE, + card->fifo_mem, card->fifo_bus_mem); + + iounmap(card->io_mem); + + pci_release_regions(pci_dev); + + pci_disable_device(pci_dev); + + kfree(card); +} + +/****************************************** + * Module stuff + ******************************************/ + +static int __init hfc_init_module(void) +{ + int ret; + + printk(KERN_INFO hfc_DRIVER_PREFIX + hfc_DRIVER_STRING " loading\n"); + +#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) + hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, NULL); +#else + hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver); +#endif + + ret = dahdi_pci_module(&hfc_driver); + return ret; +} + +module_init(hfc_init_module); + +static void __exit hfc_module_exit(void) +{ + pci_unregister_driver(&hfc_driver); + +#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) + remove_proc_entry(hfc_DRIVER_NAME, NULL); +#else + remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver); +#endif + + printk(KERN_INFO hfc_DRIVER_PREFIX + hfc_DRIVER_STRING " unloaded\n"); +} + +module_exit(hfc_module_exit); + +#endif + +MODULE_DESCRIPTION(hfc_DRIVER_DESCR); +MODULE_AUTHOR("Jens Wilke , " + "Daniele (Vihai) Orlandi , " + "Jose A. Deniz "); +MODULE_ALIAS("vzaphfc"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + + +module_param(modes, int, 0444); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) +module_param_array(nt_modes, int, &nt_modes_count, 0444); +#else +module_param_array(nt_modes, int, nt_modes_count, 0444); +#endif + +module_param(force_l1_up, int, 0444); +#ifdef DEBUG +module_param(debug_level, int, 0444); +#endif + +MODULE_PARM_DESC(modes, "[Deprecated] bit-mask to configure NT mode"); +MODULE_PARM_DESC(nt_modes, + "Comma-separated list of card IDs to configure in NT mode"); +MODULE_PARM_DESC(force_l1_up, "Don't allow L1 to go down"); +#ifdef DEBUG +MODULE_PARM_DESC(debug_level, "Debug verbosity level"); +#endif diff --git a/drivers/dahdi/zaphfc/fifo.c b/drivers/dahdi/zaphfc/fifo.c new file mode 100644 index 0000000..8c23fa3 --- /dev/null +++ b/drivers/dahdi/zaphfc/fifo.c @@ -0,0 +1,375 @@ +/* + * fifo.c - HFC FIFO management routines + * + * Copyright (C) 2006 headissue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Original author of this code is + * Daniele "Vihai" Orlandi + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#include + +#include + +#include "fifo.h" + +static void hfc_fifo_mem_read(struct hfc_chan_simplex *chan, + int z_start, + void *data, int size) +{ + int bytes_to_boundary = chan->z_max - z_start + 1; + if (bytes_to_boundary >= size) { + memcpy(data, + chan->z_base + z_start, + size); + } else { + /* + * Buffer wrap + */ + memcpy(data, + chan->z_base + z_start, + bytes_to_boundary); + + memcpy(data + bytes_to_boundary, + chan->fifo_base, + size - bytes_to_boundary); + } +} + +static void hfc_fifo_mem_write(struct hfc_chan_simplex *chan, + void *data, int size) +{ + int bytes_to_boundary = chan->z_max - *Z1_F1(chan) + 1; + if (bytes_to_boundary >= size) { + memcpy(chan->z_base + *Z1_F1(chan), + data, + size); + } else { + /* + * FIFO wrap + */ + + memcpy(chan->z_base + *Z1_F1(chan), + data, + bytes_to_boundary); + + memcpy(chan->fifo_base, + data + bytes_to_boundary, + size - bytes_to_boundary); + } +} + +int hfc_fifo_get(struct hfc_chan_simplex *chan, + void *data, int size) +{ + int available_bytes; + + /* + * Some useless statistic + */ + chan->bytes += size; + + available_bytes = hfc_fifo_used_rx(chan); + + if (available_bytes < size && !chan->fifo_underrun++) { + /* + * print the warning only once + */ + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX FIFO not enough (%d) bytes to receive!\n", + chan->chan->card->cardnum, + chan->chan->name, + available_bytes); + return -1; + } + + hfc_fifo_mem_read(chan, *Z2_F2(chan), data, size); + *Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size); + return available_bytes - size; +} + +void hfc_fifo_put(struct hfc_chan_simplex *chan, + void *data, int size) +{ + struct hfc_card *card = chan->chan->card; + int used_bytes = hfc_fifo_used_tx(chan); + int free_bytes = hfc_fifo_free_tx(chan); + + if (!used_bytes && !chan->fifo_underrun++) { + /* + * print warning only once, to make timing not worse + */ + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO has become empty\n", + card->cardnum, + chan->chan->name); + } + if (free_bytes < size) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO full!\n", + chan->chan->card->cardnum, + chan->chan->name); + chan->fifo_full++; + hfc_clear_fifo_tx(chan); + } + + hfc_fifo_mem_write(chan, data, size); + chan->bytes += size; + *Z1_F1(chan) = Z_inc(chan, *Z1_F1(chan), size); +} + +int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size) +{ + int frame_size; + u16 newz2 ; + + if (*chan->f1 == *chan->f2) { + /* + * nothing received, strange uh? + */ + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "get_frame called with no frame in FIFO.\n", + chan->chan->card->cardnum, + chan->chan->name); + + return -1; + } + + /* + * frame_size includes CRC+CRC+STAT + */ + frame_size = hfc_fifo_get_frame_size(chan); + +#ifdef DEBUG + if (debug_level == 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + frame_size); + } else if (debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + *chan->f1, *chan->f2, *Z1_F2(chan), *Z2_F2(chan), + frame_size); + } + + if (debug_level >= 3) { + int i; + for (i = 0; i < frame_size; i++) { + printk("%02x", hfc_fifo_u8(chan, + Z_inc(chan, *Z2_F2(chan), i))); + } + + printk("\n"); + } +#endif + + if (frame_size <= 0) { +#ifdef DEBUG + if (debug_level >= 2) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "invalid (empty) frame received.\n", + chan->chan->card->cardnum, + chan->chan->name); + } +#endif + + hfc_fifo_drop_frame(chan); + return -1; + } + + /* + * STAT is not really received + */ + chan->bytes += frame_size - 1; + + /* + * Calculate beginning of the next frame + */ + newz2 = Z_inc(chan, *Z2_F2(chan), frame_size); + + /* + * We cannot use hfc_fifo_get because of different semantic of + * "available bytes" and to avoid useless increment of Z2 + */ + hfc_fifo_mem_read(chan, *Z2_F2(chan), data, + frame_size < max_size ? frame_size : max_size); + + if (hfc_fifo_u8(chan, Z_inc(chan, *Z2_F2(chan), + frame_size - 1)) != 0x00) { + /* + * CRC not ok, frame broken, skipping + */ +#ifdef DEBUG + if (debug_level >= 2) { + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "Received frame with wrong CRC\n", + chan->chan->card->cardnum, + chan->chan->name); + } +#endif + + chan->crc++; + + hfc_fifo_drop_frame(chan); + return -1; + } + + chan->frames++; + + *chan->f2 = F_inc(chan, *chan->f2, 1); + + /* + * Set Z2 for the next frame we're going to receive + */ + *Z2_F2(chan) = newz2; + + return frame_size; +} + +void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan) +{ + int available_bytes; + u16 newz2; + + if (*chan->f1 == *chan->f2) { + /* + * nothing received, strange eh? + */ + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "skip_frame called with no frame in FIFO.\n", + chan->chan->card->cardnum, + chan->chan->name); + + return; + } + + available_bytes = hfc_fifo_used_rx(chan) + 1; + + /* + * Calculate beginning of the next frame + */ + newz2 = Z_inc(chan, *Z2_F2(chan), available_bytes); + + *chan->f2 = F_inc(chan, *chan->f2, 1); + + /* + * Set Z2 for the next frame we're going to receive + */ + *Z2_F2(chan) = newz2; +} + +void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, + void *data, int size) +{ + u16 newz1; + int available_frames; + +#ifdef DEBUG + if (debug_level == 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + size); + } else if (debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + *chan->f1, *chan->f2, *Z1_F1(chan), *Z2_F1(chan), + size); + } + + if (debug_level >= 3) { + int i; + for (i = 0; i < size; i++) + printk("%02x", ((u8 *)data)[i]); + + printk("\n"); + } +#endif + + available_frames = hfc_fifo_free_frames(chan); + + if (available_frames >= chan->f_num) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO total number of frames exceeded!\n", + chan->chan->card->cardnum, + chan->chan->name); + + chan->fifo_full++; + + return; + } + + hfc_fifo_put(chan, data, size); + + newz1 = *Z1_F1(chan); + + *chan->f1 = F_inc(chan, *chan->f1, 1); + + *Z1_F1(chan) = newz1; + + chan->frames++; +} + +void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan) +{ + *chan->f2 = *chan->f1; + *Z2_F2(chan) = *Z1_F2(chan); +} + +void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan) +{ + *chan->f1 = *chan->f2; + *Z1_F1(chan) = *Z2_F1(chan); + + if (chan->chan->status == open_voice) { + /* + * Make sure that at least hfc_TX_FIFO_PRELOAD bytes are + * present in the TX FIFOs + * Create hfc_TX_FIFO_PRELOAD bytes of empty data + * (0x7f is mute audio) + */ + u8 empty_fifo[hfc_TX_FIFO_PRELOAD + + DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD]; + memset(empty_fifo, 0x7f, sizeof(empty_fifo)); + + hfc_fifo_put(chan, empty_fifo, sizeof(empty_fifo)); + } +} + diff --git a/drivers/dahdi/zaphfc/fifo.h b/drivers/dahdi/zaphfc/fifo.h new file mode 100644 index 0000000..1866f30 --- /dev/null +++ b/drivers/dahdi/zaphfc/fifo.h @@ -0,0 +1,139 @@ +/* + * fifo.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#ifndef _HFC_FIFO_H +#define _HFC_FIFO_H + +#include "zaphfc.h" + +static inline u16 *Z1_F1(struct hfc_chan_simplex *chan) +{ + return chan->z1_base + (*chan->f1 * 4); +} + +static inline u16 *Z2_F1(struct hfc_chan_simplex *chan) +{ + return chan->z2_base + (*chan->f1 * 4); +} + +static inline u16 *Z1_F2(struct hfc_chan_simplex *chan) +{ + return chan->z1_base + (*chan->f2 * 4); +} + +static inline u16 *Z2_F2(struct hfc_chan_simplex *chan) +{ + return chan->z2_base + (*chan->f2 * 4); +} + +static inline u16 Z_inc(struct hfc_chan_simplex *chan, u16 z, u16 inc) +{ + /* + * declared as u32 in order to manage overflows + */ + u32 newz = z + inc; + if (newz > chan->z_max) + newz -= chan->fifo_size; + + return newz; +} + +static inline u8 F_inc(struct hfc_chan_simplex *chan, u8 f, u8 inc) +{ + /* + * declared as u16 in order to manage overflows + */ + u16 newf = f + inc; + if (newf > chan->f_max) + newf -= chan->f_num; + + return newf; +} + +static inline u16 hfc_fifo_used_rx(struct hfc_chan_simplex *chan) +{ + return (*Z1_F2(chan) - *Z2_F2(chan) + + chan->fifo_size) % chan->fifo_size; +} + +static inline u16 hfc_fifo_get_frame_size(struct hfc_chan_simplex *chan) +{ + /* + * This +1 is needed because in frame mode the available bytes are Z2-Z1+1 + * while in transparent mode I wouldn't consider the byte pointed by Z2 to + * be available, otherwise, the FIFO would always contain one byte, even + * when Z1==Z2 + */ + + return hfc_fifo_used_rx(chan) + 1; +} + +static inline u8 hfc_fifo_u8(struct hfc_chan_simplex *chan, u16 z) +{ + return *((u8 *)(chan->z_base + z)); +} + +static inline u16 hfc_fifo_used_tx(struct hfc_chan_simplex *chan) +{ + return (*Z1_F1(chan) - *Z2_F1(chan) + + chan->fifo_size) % chan->fifo_size; +} + +static inline u16 hfc_fifo_free_rx(struct hfc_chan_simplex *chan) +{ + u16 free_bytes = *Z2_F1(chan) - *Z1_F1(chan); + + if (free_bytes > 0) + return free_bytes; + else + return free_bytes + chan->fifo_size; +} + +static inline u16 hfc_fifo_free_tx(struct hfc_chan_simplex *chan) +{ + u16 free_bytes = *Z2_F1(chan) - *Z1_F1(chan); + + if (free_bytes > 0) + return free_bytes; + else + return free_bytes + chan->fifo_size; +} + +static inline int hfc_fifo_has_frames(struct hfc_chan_simplex *chan) +{ + return *chan->f1 != *chan->f2; +} + +static inline u8 hfc_fifo_used_frames(struct hfc_chan_simplex *chan) +{ + return (*chan->f1 - *chan->f2 + chan->f_num) % chan->f_num; +} + +static inline u8 hfc_fifo_free_frames(struct hfc_chan_simplex *chan) +{ + return (*chan->f2 - *chan->f1 + chan->f_num) % chan->f_num; +} + +int hfc_fifo_get(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_fifo_put(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size); +int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size); +void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan); +void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan); +void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan); + +#endif diff --git a/drivers/dahdi/zaphfc/zaphfc.h b/drivers/dahdi/zaphfc/zaphfc.h new file mode 100644 index 0000000..29dd304 --- /dev/null +++ b/drivers/dahdi/zaphfc/zaphfc.h @@ -0,0 +1,418 @@ +/* + * zaphfc.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards + * + * Dahdi port by Jose A. Deniz + * + * Copyright (C) 2009 Jose A. Deniz + * Copyright (C) 2006 headissue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Jens Wilke + * + * Orginal author of this code is + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#ifndef _HFC_ZAPHFC_H +#define _HFC_ZAPHFC_H + +#include + +#define hfc_DRIVER_NAME "vzaphfc" +#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": " +#define hfc_DRIVER_DESCR "HFC-S PCI A ISDN" +#define hfc_DRIVER_VERSION "1.42" +#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")" + +#define hfc_MAX_BOARDS 32 + +#ifndef PCI_DMA_32BIT +#define PCI_DMA_32BIT 0x00000000ffffffffULL +#endif + +#ifndef PCI_VENDOR_ID_SITECOM +#define PCI_VENDOR_ID_SITECOM 0x182D +#endif + +#ifndef PCI_DEVICE_ID_SITECOM_3069 +#define PCI_DEVICE_ID_SITECOM_3069 0x3069 +#endif + +#define hfc_RESET_DELAY 20 + +#define hfc_CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define hfc_CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +/* PCI memory mapped I/O */ + +#define hfc_PCI_MEM_SIZE 0x0100 +#define hfc_PCI_MWBA 0x80 + +/* GCI/IOM bus monitor registers */ + +#define hfc_C_I 0x08 +#define hfc_TRxR 0x0C +#define hfc_MON1_D 0x28 +#define hfc_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define hfc_B1_SSL 0x80 +#define hfc_B2_SSL 0x84 +#define hfc_AUX1_SSL 0x88 +#define hfc_AUX2_SSL 0x8C +#define hfc_B1_RSL 0x90 +#define hfc_B2_RSL 0x94 +#define hfc_AUX1_RSL 0x98 +#define hfc_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define hfc_B1_D 0xA0 +#define hfc_B2_D 0xA4 +#define hfc_AUX1_D 0xA8 +#define hfc_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define hfc_MST_EMOD 0xB4 +#define hfc_MST_MODE 0xB8 +#define hfc_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define hfc_FIFO_EN 0x44 +#define hfc_TRM 0x48 +#define hfc_B_MODE 0x4C +#define hfc_CHIP_ID 0x58 +#define hfc_CIRM 0x60 +#define hfc_CTMT 0x64 +#define hfc_INT_M1 0x68 +#define hfc_INT_M2 0x6C +#define hfc_INT_S1 0x78 +#define hfc_INT_S2 0x7C +#define hfc_STATUS 0x70 + +/* S/T section registers */ + +#define hfc_STATES 0xC0 +#define hfc_SCTRL 0xC4 +#define hfc_SCTRL_E 0xC8 +#define hfc_SCTRL_R 0xCC +#define hfc_SQ 0xD0 +#define hfc_CLKDEL 0xDC +#define hfc_B1_REC 0xF0 +#define hfc_B1_SEND 0xF0 +#define hfc_B2_REC 0xF4 +#define hfc_B2_SEND 0xF4 +#define hfc_D_REC 0xF8 +#define hfc_D_SEND 0xF8 +#define hfc_E_REC 0xFC + +/* Bits and values in various HFC PCI registers */ + +/* bits in status register (READ) */ +#define hfc_STATUS_PCI_PROC 0x02 +#define hfc_STATUS_NBUSY 0x04 +#define hfc_STATUS_TIMER_ELAP 0x10 +#define hfc_STATUS_STATINT 0x20 +#define hfc_STATUS_FRAMEINT 0x40 +#define hfc_STATUS_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define hfc_CTMT_TRANSB1 0x01 +#define hfc_CTMT_TRANSB2 0x02 +#define hfc_CTMT_TIMER_CLEAR 0x80 +#define hfc_CTMT_TIMER_MASK 0x1C +#define hfc_CTMT_TIMER_3_125 (0x01 << 2) +#define hfc_CTMT_TIMER_6_25 (0x02 << 2) +#define hfc_CTMT_TIMER_12_5 (0x03 << 2) +#define hfc_CTMT_TIMER_25 (0x04 << 2) +#define hfc_CTMT_TIMER_50 (0x05 << 2) +#define hfc_CTMT_TIMER_400 (0x06 << 2) +#define hfc_CTMT_TIMER_800 (0x07 << 2) +#define hfc_CTMT_AUTO_TIMER 0x20 + +/* bits in CIRM (Write) */ +#define hfc_CIRM_AUX_MSK 0x07 +#define hfc_CIRM_RESET 0x08 +#define hfc_CIRM_B1_REV 0x40 +#define hfc_CIRM_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define hfc_INTS_B1TRANS 0x01 +#define hfc_INTS_B2TRANS 0x02 +#define hfc_INTS_DTRANS 0x04 +#define hfc_INTS_B1REC 0x08 +#define hfc_INTS_B2REC 0x10 +#define hfc_INTS_DREC 0x20 +#define hfc_INTS_L1STATE 0x40 +#define hfc_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define hfc_M2_PROC_TRANS 0x01 +#define hfc_M2_GCI_I_CHG 0x02 +#define hfc_M2_GCI_MON_REC 0x04 +#define hfc_M2_IRQ_ENABLE 0x08 +#define hfc_M2_PMESEL 0x80 + +/* bits in STATES */ +#define hfc_STATES_STATE_MASK 0x0F +#define hfc_STATES_LOAD_STATE 0x10 +#define hfc_STATES_ACTIVATE 0x20 +#define hfc_STATES_DO_ACTION 0x40 +#define hfc_STATES_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define hfc_MST_MODE_MASTER 0x01 +#define hfc_MST_MODE_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define hfc_SCTRL_B1_ENA 0x01 +#define hfc_SCTRL_B2_ENA 0x02 +#define hfc_SCTRL_MODE_TE 0x00 +#define hfc_SCTRL_MODE_NT 0x04 +#define hfc_SCTRL_LOW_PRIO 0x08 +#define hfc_SCTRL_SQ_ENA 0x10 +#define hfc_SCTRL_TEST 0x20 +#define hfc_SCTRL_NONE_CAP 0x40 +#define hfc_SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define hfc_SCTRL_E_AUTO_AWAKE 0x01 +#define hfc_SCTRL_E_DBIT_1 0x04 +#define hfc_SCTRL_E_IGNORE_COL 0x08 +#define hfc_SCTRL_E_CHG_B1_B2 0x80 + +/* bits in SCTRL_R */ +#define hfc_SCTRL_R_B1_ENA 0x01 +#define hfc_SCTRL_R_B2_ENA 0x02 + +/* bits in FIFO_EN register */ +#define hfc_FIFOEN_B1TX 0x01 +#define hfc_FIFOEN_B1RX 0x02 +#define hfc_FIFOEN_B2TX 0x04 +#define hfc_FIFOEN_B2RX 0x08 +#define hfc_FIFOEN_DTX 0x10 +#define hfc_FIFOEN_DRX 0x20 + +#define hfc_FIFOEN_B1 (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX) +#define hfc_FIFOEN_B2 (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX) +#define hfc_FIFOEN_D (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX) + +/* bits in the CONNECT register */ +#define hfc_CONNECT_B1_HFC_from_ST 0x00 +#define hfc_CONNECT_B1_HFC_from_GCI 0x01 +#define hfc_CONNECT_B1_ST_from_HFC 0x00 +#define hfc_CONNECT_B1_ST_from_GCI 0x02 +#define hfc_CONNECT_B1_GCI_from_HFC 0x00 +#define hfc_CONNECT_B1_GCI_from_ST 0x04 + +#define hfc_CONNECT_B2_HFC_from_ST 0x00 +#define hfc_CONNECT_B2_HFC_from_GCI 0x08 +#define hfc_CONNECT_B2_ST_from_HFC 0x00 +#define hfc_CONNECT_B2_ST_from_GCI 0x10 +#define hfc_CONNECT_B2_GCI_from_HFC 0x00 +#define hfc_CONNECT_B2_GCI_from_ST 0x20 + +/* bits in the TRM register */ +#define hfc_TRM_TRANS_INT_00 0x00 +#define hfc_TRM_TRANS_INT_01 0x01 +#define hfc_TRM_TRANS_INT_10 0x02 +#define hfc_TRM_TRANS_INT_11 0x04 +#define hfc_TRM_ECHO 0x20 +#define hfc_TRM_B1_PLUS_B2 0x40 +#define hfc_TRM_IOM_TEST_LOOP 0x80 + +/* bits in the __SSL and __RSL registers */ +#define hfc_SRSL_STIO 0x40 +#define hfc_SRSL_ENABLE 0x80 +#define hfc_SRCL_SLOT_MASK 0x1f + +/* FIFO memory definitions */ + +#define hfc_FIFO_SIZE 0x8000 + +#define hfc_UGLY_FRAMEBUF 0x2000 + +#define hfc_TX_FIFO_PRELOAD (DAHDI_CHUNKSIZE + 2) +#define hfc_RX_FIFO_PRELOAD 4 + +/* HDLC STUFF */ +#define hfc_HDLC_BUF_LEN 32 +/* arbitrary, just the max # of byts we will send to DAHDI per call */ + + +/* NOTE: FIFO pointers are not declared volatile because accesses to the + * FIFOs are inherently safe. + */ + +#ifdef DEBUG +extern int debug_level; +#endif + +struct hfc_chan; + +struct hfc_chan_simplex { + struct hfc_chan_duplex *chan; + + u8 zaptel_buffer[DAHDI_CHUNKSIZE]; + + u8 ugly_framebuf[hfc_UGLY_FRAMEBUF]; + int ugly_framebuf_size; + u16 ugly_framebuf_off; + + void *z1_base, *z2_base; + void *fifo_base; + void *z_base; + u16 z_min; + u16 z_max; + u16 fifo_size; + + u8 *f1, *f2; + u8 f_min; + u8 f_max; + u8 f_num; + + unsigned long long frames; + unsigned long long bytes; + unsigned long long fifo_full; + unsigned long long crc; + unsigned long long fifo_underrun; +}; + +enum hfc_chan_status { + free, + open_framed, + open_voice, + sniff_aux, + loopback, +}; + +struct hfc_chan_duplex { + struct hfc_card *card; + + char *name; + int number; + + enum hfc_chan_status status; + int open_by_netdev; + int open_by_zaptel; + + unsigned short protocol; + + spinlock_t lock; + + struct hfc_chan_simplex rx; + struct hfc_chan_simplex tx; + +}; + +typedef struct hfc_card { + int cardnum; + struct pci_dev *pcidev; + struct dahdi_hfc *ztdev; + struct proc_dir_entry *proc_dir; + char proc_dir_name[32]; + + struct proc_dir_entry *proc_info; + struct proc_dir_entry *proc_fifos; + struct proc_dir_entry *proc_bufs; + + unsigned long io_bus_mem; + void __iomem *io_mem; + + dma_addr_t fifo_bus_mem; + void *fifo_mem; + void *fifos; + + int nt_mode; + int sync_loss_reported; + int late_irqs; + + u8 l1_state; + int fifo_suspended; + int ignore_first_timer_interrupt; + + struct { + u8 m1; + u8 m2; + u8 fifo_en; + u8 trm; + u8 connect; + u8 sctrl; + u8 sctrl_r; + u8 sctrl_e; + u8 ctmt; + u8 cirm; + } regs; + + struct hfc_chan_duplex chans[3]; + int echo_enabled; + + + + int debug_event; + + spinlock_t lock; + unsigned int irq; + unsigned int iomem; + int ticks; + int clicks; + unsigned char *pci_io; + void *fifomem; /* start of the shared mem */ + + unsigned int pcibus; + unsigned int pcidevfn; + + int drecinframe; + + unsigned char cardno; + struct hfc_card *next; + +} hfc_card; + +typedef struct dahdi_hfc { + unsigned int usecount; + struct dahdi_span span; + struct dahdi_chan chans[3]; + struct dahdi_chan *_chans[3]; + struct hfc_card *card; + + /* pointer to the signalling channel for this span */ + struct dahdi_chan *sigchan; + /* nonzero means we're in the middle of sending an HDLC frame */ + int sigactive; + /* hdlc_hard_xmit() increments, hdlc_tx_frame() decrements */ + atomic_t hdlc_pending; + int frames_out; + int frames_in; + +} dahdi_hfc; + +static inline struct dahdi_hfc* dahdi_hfc_from_span(struct dahdi_span *span) { + return container_of(span, struct dahdi_hfc, span); +} + +static inline u8 hfc_inb(struct hfc_card *card, int offset) +{ + return readb(card->io_mem + offset); +} + +static inline void hfc_outb(struct hfc_card *card, int offset, u8 value) +{ + writeb(value, card->io_mem + offset); +} + +#endif diff --git a/drivers/staging/echo/Kbuild b/drivers/staging/echo/Kbuild new file mode 100644 index 0000000..f813006 --- /dev/null +++ b/drivers/staging/echo/Kbuild @@ -0,0 +1,6 @@ +ifdef DAHDI_USE_MMX +EXTRA_CFLAGS += -DUSE_MMX +endif + +# An explicit 'obj-m' , unlike the Makefile +obj-m += echo.o diff --git a/drivers/staging/echo/echo.c b/drivers/staging/echo/echo.c new file mode 100644 index 0000000..4cc4f2e --- /dev/null +++ b/drivers/staging/echo/echo.c @@ -0,0 +1,662 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * echo.c - A line echo canceller. This code is being developed + * against and partially complies with G168. + * + * Written by Steve Underwood + * and David Rowe + * + * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe + * + * Based on a bit from here, a bit from there, eye of toad, ear of + * bat, 15 years of failed attempts by David and a few fried brain + * cells. + * + * 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 version 2, as + * published by the Free Software Foundation. + * + * 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. + */ + +/*! \file */ + +/* Implementation Notes + David Rowe + April 2007 + + This code started life as Steve's NLMS algorithm with a tap + rotation algorithm to handle divergence during double talk. I + added a Geigel Double Talk Detector (DTD) [2] and performed some + G168 tests. However I had trouble meeting the G168 requirements, + especially for double talk - there were always cases where my DTD + failed, for example where near end speech was under the 6dB + threshold required for declaring double talk. + + So I tried a two path algorithm [1], which has so far given better + results. The original tap rotation/Geigel algorithm is available + in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit. + It's probably possible to make it work if some one wants to put some + serious work into it. + + At present no special treatment is provided for tones, which + generally cause NLMS algorithms to diverge. Initial runs of a + subset of the G168 tests for tones (e.g ./echo_test 6) show the + current algorithm is passing OK, which is kind of surprising. The + full set of tests needs to be performed to confirm this result. + + One other interesting change is that I have managed to get the NLMS + code to work with 16 bit coefficients, rather than the original 32 + bit coefficents. This reduces the MIPs and storage required. + I evaulated the 16 bit port using g168_tests.sh and listening tests + on 4 real-world samples. + + I also attempted the implementation of a block based NLMS update + [2] but although this passes g168_tests.sh it didn't converge well + on the real-world samples. I have no idea why, perhaps a scaling + problem. The block based code is also available in SVN + http://svn.rowetel.com/software/oslec/tags/before_16bit. If this + code can be debugged, it will lead to further reduction in MIPS, as + the block update code maps nicely onto DSP instruction sets (it's a + dot product) compared to the current sample-by-sample update. + + Steve also has some nice notes on echo cancellers in echo.h + + References: + + [1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo + Path Models", IEEE Transactions on communications, COM-25, + No. 6, June + 1977. + http://www.rowetel.com/images/echo/dual_path_paper.pdf + + [2] The classic, very useful paper that tells you how to + actually build a real world echo canceller: + Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice + Echo Canceller with a TMS320020, + http://www.rowetel.com/images/echo/spra129.pdf + + [3] I have written a series of blog posts on this work, here is + Part 1: http://www.rowetel.com/blog/?p=18 + + [4] The source code http://svn.rowetel.com/software/oslec/ + + [5] A nice reference on LMS filters: + http://en.wikipedia.org/wiki/Least_mean_squares_filter + + Credits: + + Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan + Muthukrishnan for their suggestions and email discussions. Thanks + also to those people who collected echo samples for me such as + Mark, Pawel, and Pavel. +*/ + +#include +#include +#include + +#include "echo.h" + +#define MIN_TX_POWER_FOR_ADAPTION 64 +#define MIN_RX_POWER_FOR_ADAPTION 64 +#define DTD_HANGOVER 600 /* 600 samples, or 75ms */ +#define DC_LOG2BETA 3 /* log2() of DC filter Beta */ + + +/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */ + +#ifdef __bfin__ +static inline void lms_adapt_bg(struct oslec_state *ec, int clean, + int shift) +{ + int i, j; + int offset1; + int offset2; + int factor; + int exp; + int16_t *phist; + int n; + + if (shift > 0) + factor = clean << shift; + else + factor = clean >> -shift; + + /* Update the FIR taps */ + + offset2 = ec->curr_pos; + offset1 = ec->taps - offset2; + phist = &ec->fir_state_bg.history[offset2]; + + /* st: and en: help us locate the assembler in echo.s */ + + /* asm("st:"); */ + n = ec->taps; + for (i = 0, j = offset2; i < n; i++, j++) { + exp = *phist++ * factor; + ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); + } + /* asm("en:"); */ + + /* Note the asm for the inner loop above generated by Blackfin gcc + 4.1.1 is pretty good (note even parallel instructions used): + + R0 = W [P0++] (X); + R0 *= R2; + R0 = R0 + R3 (NS) || + R1 = W [P1] (X) || + nop; + R0 >>>= 15; + R0 = R0 + R1; + W [P1++] = R0; + + A block based update algorithm would be much faster but the + above can't be improved on much. Every instruction saved in + the loop above is 2 MIPs/ch! The for loop above is where the + Blackfin spends most of it's time - about 17 MIPs/ch measured + with speedtest.c with 256 taps (32ms). Write-back and + Write-through cache gave about the same performance. + */ +} + +/* + IDEAS for further optimisation of lms_adapt_bg(): + + 1/ The rounding is quite costly. Could we keep as 32 bit coeffs + then make filter pluck the MS 16-bits of the coeffs when filtering? + However this would lower potential optimisation of filter, as I + think the dual-MAC architecture requires packed 16 bit coeffs. + + 2/ Block based update would be more efficient, as per comments above, + could use dual MAC architecture. + + 3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC + packing. + + 4/ Execute the whole e/c in a block of say 20ms rather than sample + by sample. Processing a few samples every ms is inefficient. +*/ + +#else +static inline void lms_adapt_bg(struct oslec_state *ec, int clean, + int shift) +{ + int i; + + int offset1; + int offset2; + int factor; + int exp; + + if (shift > 0) + factor = clean << shift; + else + factor = clean >> -shift; + + /* Update the FIR taps */ + + offset2 = ec->curr_pos; + offset1 = ec->taps - offset2; + + for (i = ec->taps - 1; i >= offset1; i--) { + exp = (ec->fir_state_bg.history[i - offset1] * factor); + ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); + } + for (; i >= 0; i--) { + exp = (ec->fir_state_bg.history[i + offset2] * factor); + ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); + } +} +#endif + +static inline int top_bit(unsigned int bits) +{ + if (bits == 0) + return -1; + else + return (int)fls((int32_t)bits)-1; +} + +struct oslec_state *oslec_create(int len, int adaption_mode) +{ + struct oslec_state *ec; + int i; + + ec = kzalloc(sizeof(*ec), GFP_KERNEL); + if (!ec) + return NULL; + + ec->taps = len; + ec->log2taps = top_bit(len); + ec->curr_pos = ec->taps - 1; + + for (i = 0; i < 2; i++) { + ec->fir_taps16[i] = + kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); + if (!ec->fir_taps16[i]) + goto error_oom; + } + + fir16_create(&ec->fir_state, ec->fir_taps16[0], ec->taps); + fir16_create(&ec->fir_state_bg, ec->fir_taps16[1], ec->taps); + + for (i = 0; i < 5; i++) + ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0; + + ec->cng_level = 1000; + oslec_adaption_mode(ec, adaption_mode); + + ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); + if (!ec->snapshot) + goto error_oom; + + ec->cond_met = 0; + ec->Pstates = 0; + ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; + ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; + ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; + ec->Lbgn = ec->Lbgn_acc = 0; + ec->Lbgn_upper = 200; + ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; + + return ec; + +error_oom: + for (i = 0; i < 2; i++) + kfree(ec->fir_taps16[i]); + + kfree(ec); + return NULL; +} +EXPORT_SYMBOL_GPL(oslec_create); + +void oslec_free(struct oslec_state *ec) +{ + int i; + + fir16_free(&ec->fir_state); + fir16_free(&ec->fir_state_bg); + for (i = 0; i < 2; i++) + kfree(ec->fir_taps16[i]); + kfree(ec->snapshot); + kfree(ec); +} +EXPORT_SYMBOL_GPL(oslec_free); + +void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode) +{ + ec->adaption_mode = adaption_mode; +} +EXPORT_SYMBOL_GPL(oslec_adaption_mode); + +void oslec_flush(struct oslec_state *ec) +{ + int i; + + ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; + ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; + ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; + + ec->Lbgn = ec->Lbgn_acc = 0; + ec->Lbgn_upper = 200; + ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; + + ec->nonupdate_dwell = 0; + + fir16_flush(&ec->fir_state); + fir16_flush(&ec->fir_state_bg); + ec->fir_state.curr_pos = ec->taps - 1; + ec->fir_state_bg.curr_pos = ec->taps - 1; + for (i = 0; i < 2; i++) + memset(ec->fir_taps16[i], 0, ec->taps * sizeof(int16_t)); + + ec->curr_pos = ec->taps - 1; + ec->Pstates = 0; +} +EXPORT_SYMBOL_GPL(oslec_flush); + +void oslec_snapshot(struct oslec_state *ec) +{ + memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t)); +} +EXPORT_SYMBOL_GPL(oslec_snapshot); + +/* Dual Path Echo Canceller */ + +int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx) +{ + int32_t echo_value; + int clean_bg; + int tmp, tmp1; + + /* + * Input scaling was found be required to prevent problems when tx + * starts clipping. Another possible way to handle this would be the + * filter coefficent scaling. + */ + + ec->tx = tx; + ec->rx = rx; + tx >>= 1; + rx >>= 1; + + /* + * Filter DC, 3dB point is 160Hz (I think), note 32 bit precision + * required otherwise values do not track down to 0. Zero at DC, Pole + * at (1-Beta) on real axis. Some chip sets (like Si labs) don't + * need this, but something like a $10 X100P card does. Any DC really + * slows down convergence. + * + * Note: removes some low frequency from the signal, this reduces the + * speech quality when listening to samples through headphones but may + * not be obvious through a telephone handset. + * + * Note that the 3dB frequency in radians is approx Beta, e.g. for Beta + * = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz. + */ + + if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) { + tmp = rx << 15; + + /* + * Make sure the gain of the HPF is 1.0. This can still + * saturate a little under impulse conditions, and it might + * roll to 32768 and need clipping on sustained peak level + * signals. However, the scale of such clipping is small, and + * the error due to any saturation should not markedly affect + * the downstream processing. + */ + tmp -= (tmp >> 4); + + ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2; + + /* + * hard limit filter to prevent clipping. Note that at this + * stage rx should be limited to +/- 16383 due to right shift + * above + */ + tmp1 = ec->rx_1 >> 15; + if (tmp1 > 16383) + tmp1 = 16383; + if (tmp1 < -16383) + tmp1 = -16383; + rx = tmp1; + ec->rx_2 = tmp; + } + + /* Block average of power in the filter states. Used for + adaption power calculation. */ + + { + int new, old; + + /* efficient "out with the old and in with the new" algorithm so + we don't have to recalculate over the whole block of + samples. */ + new = (int)tx * (int)tx; + old = (int)ec->fir_state.history[ec->fir_state.curr_pos] * + (int)ec->fir_state.history[ec->fir_state.curr_pos]; + ec->Pstates += + ((new - old) + (1 << (ec->log2taps-1))) >> ec->log2taps; + if (ec->Pstates < 0) + ec->Pstates = 0; + } + + /* Calculate short term average levels using simple single pole IIRs */ + + ec->Ltxacc += abs(tx) - ec->Ltx; + ec->Ltx = (ec->Ltxacc + (1 << 4)) >> 5; + ec->Lrxacc += abs(rx) - ec->Lrx; + ec->Lrx = (ec->Lrxacc + (1 << 4)) >> 5; + + /* Foreground filter */ + + ec->fir_state.coeffs = ec->fir_taps16[0]; + echo_value = fir16(&ec->fir_state, tx); + ec->clean = rx - echo_value; + ec->Lcleanacc += abs(ec->clean) - ec->Lclean; + ec->Lclean = (ec->Lcleanacc + (1 << 4)) >> 5; + + /* Background filter */ + + echo_value = fir16(&ec->fir_state_bg, tx); + clean_bg = rx - echo_value; + ec->Lclean_bgacc += abs(clean_bg) - ec->Lclean_bg; + ec->Lclean_bg = (ec->Lclean_bgacc + (1 << 4)) >> 5; + + /* Background Filter adaption */ + + /* Almost always adap bg filter, just simple DT and energy + detection to minimise adaption in cases of strong double talk. + However this is not critical for the dual path algorithm. + */ + ec->factor = 0; + ec->shift = 0; + if ((ec->nonupdate_dwell == 0)) { + int P, logP, shift; + + /* Determine: + + f = Beta * clean_bg_rx/P ------ (1) + + where P is the total power in the filter states. + + The Boffins have shown that if we obey (1) we converge + quickly and avoid instability. + + The correct factor f must be in Q30, as this is the fixed + point format required by the lms_adapt_bg() function, + therefore the scaled version of (1) is: + + (2^30) * f = (2^30) * Beta * clean_bg_rx/P + factor = (2^30) * Beta * clean_bg_rx/P ----- (2) + + We have chosen Beta = 0.25 by experiment, so: + + factor = (2^30) * (2^-2) * clean_bg_rx/P + + (30 - 2 - log2(P)) + factor = clean_bg_rx 2 ----- (3) + + To avoid a divide we approximate log2(P) as top_bit(P), + which returns the position of the highest non-zero bit in + P. This approximation introduces an error as large as a + factor of 2, but the algorithm seems to handle it OK. + + Come to think of it a divide may not be a big deal on a + modern DSP, so its probably worth checking out the cycles + for a divide versus a top_bit() implementation. + */ + + P = MIN_TX_POWER_FOR_ADAPTION + ec->Pstates; + logP = top_bit(P) + ec->log2taps; + shift = 30 - 2 - logP; + ec->shift = shift; + + lms_adapt_bg(ec, clean_bg, shift); + } + + /* very simple DTD to make sure we dont try and adapt with strong + near end speech */ + + ec->adapt = 0; + if ((ec->Lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->Lrx > ec->Ltx)) + ec->nonupdate_dwell = DTD_HANGOVER; + if (ec->nonupdate_dwell) + ec->nonupdate_dwell--; + + /* Transfer logic */ + + /* These conditions are from the dual path paper [1], I messed with + them a bit to improve performance. */ + + if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) && + (ec->nonupdate_dwell == 0) && + /* (ec->Lclean_bg < 0.875*ec->Lclean) */ + (8 * ec->Lclean_bg < 7 * ec->Lclean) && + /* (ec->Lclean_bg < 0.125*ec->Ltx) */ + (8 * ec->Lclean_bg < ec->Ltx)) { + if (ec->cond_met == 6) { + /* + * BG filter has had better results for 6 consecutive + * samples + */ + ec->adapt = 1; + memcpy(ec->fir_taps16[0], ec->fir_taps16[1], + ec->taps * sizeof(int16_t)); + } else + ec->cond_met++; + } else + ec->cond_met = 0; + + /* Non-Linear Processing */ + + ec->clean_nlp = ec->clean; + if (ec->adaption_mode & ECHO_CAN_USE_NLP) { + /* + * Non-linear processor - a fancy way to say "zap small + * signals, to avoid residual echo due to (uLaw/ALaw) + * non-linearity in the channel.". + */ + + if ((16 * ec->Lclean < ec->Ltx)) { + /* + * Our e/c has improved echo by at least 24 dB (each + * factor of 2 is 6dB, so 2*2*2*2=16 is the same as + * 6+6+6+6=24dB) + */ + if (ec->adaption_mode & ECHO_CAN_USE_CNG) { + ec->cng_level = ec->Lbgn; + + /* + * Very elementary comfort noise generation. + * Just random numbers rolled off very vaguely + * Hoth-like. DR: This noise doesn't sound + * quite right to me - I suspect there are some + * overlfow issues in the filtering as it's too + * "crackly". + * TODO: debug this, maybe just play noise at + * high level or look at spectrum. + */ + + ec->cng_rndnum = + 1664525U * ec->cng_rndnum + 1013904223U; + ec->cng_filter = + ((ec->cng_rndnum & 0xFFFF) - 32768 + + 5 * ec->cng_filter) >> 3; + ec->clean_nlp = + (ec->cng_filter * ec->cng_level * 8) >> 14; + + } else if (ec->adaption_mode & ECHO_CAN_USE_CLIP) { + /* This sounds much better than CNG */ + if (ec->clean_nlp > ec->Lbgn) + ec->clean_nlp = ec->Lbgn; + if (ec->clean_nlp < -ec->Lbgn) + ec->clean_nlp = -ec->Lbgn; + } else { + /* + * just mute the residual, doesn't sound very + * good, used mainly in G168 tests + */ + ec->clean_nlp = 0; + } + } else { + /* + * Background noise estimator. I tried a few + * algorithms here without much luck. This very simple + * one seems to work best, we just average the level + * using a slow (1 sec time const) filter if the + * current level is less than a (experimentally + * derived) constant. This means we dont include high + * level signals like near end speech. When combined + * with CNG or especially CLIP seems to work OK. + */ + if (ec->Lclean < 40) { + ec->Lbgn_acc += abs(ec->clean) - ec->Lbgn; + ec->Lbgn = (ec->Lbgn_acc + (1 << 11)) >> 12; + } + } + } + + /* Roll around the taps buffer */ + if (ec->curr_pos <= 0) + ec->curr_pos = ec->taps; + ec->curr_pos--; + + if (ec->adaption_mode & ECHO_CAN_DISABLE) + ec->clean_nlp = rx; + + /* Output scaled back up again to match input scaling */ + + return (int16_t) ec->clean_nlp << 1; +} +EXPORT_SYMBOL_GPL(oslec_update); + +/* This function is seperated from the echo canceller is it is usually called + as part of the tx process. See rx HP (DC blocking) filter above, it's + the same design. + + Some soft phones send speech signals with a lot of low frequency + energy, e.g. down to 20Hz. This can make the hybrid non-linear + which causes the echo canceller to fall over. This filter can help + by removing any low frequency before it gets to the tx port of the + hybrid. + + It can also help by removing and DC in the tx signal. DC is bad + for LMS algorithms. + + This is one of the classic DC removal filters, adjusted to provide + sufficient bass rolloff to meet the above requirement to protect hybrids + from things that upset them. The difference between successive samples + produces a lousy HPF, and then a suitably placed pole flattens things out. + The final result is a nicely rolled off bass end. The filtering is + implemented with extended fractional precision, which noise shapes things, + giving very clean DC removal. +*/ + +int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx) +{ + int tmp, tmp1; + + if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { + tmp = tx << 15; + + /* + * Make sure the gain of the HPF is 1.0. The first can still + * saturate a little under impulse conditions, and it might + * roll to 32768 and need clipping on sustained peak level + * signals. However, the scale of such clipping is small, and + * the error due to any saturation should not markedly affect + * the downstream processing. + */ + tmp -= (tmp >> 4); + + ec->tx_1 += -(ec->tx_1 >> DC_LOG2BETA) + tmp - ec->tx_2; + tmp1 = ec->tx_1 >> 15; + if (tmp1 > 32767) + tmp1 = 32767; + if (tmp1 < -32767) + tmp1 = -32767; + tx = tmp1; + ec->tx_2 = tmp; + } + + return tx; +} +EXPORT_SYMBOL_GPL(oslec_hpf_tx); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Rowe"); +MODULE_DESCRIPTION("Open Source Line Echo Canceller"); +MODULE_VERSION("0.3.0"); diff --git a/drivers/staging/echo/echo.h b/drivers/staging/echo/echo.h new file mode 100644 index 0000000..754e66d --- /dev/null +++ b/drivers/staging/echo/echo.h @@ -0,0 +1,175 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * echo.c - A line echo canceller. This code is being developed + * against and partially complies with G168. + * + * Written by Steve Underwood + * and David Rowe + * + * Copyright (C) 2001 Steve Underwood and 2007 David Rowe + * + * 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 version 2, as + * published by the Free Software Foundation. + * + * 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 __ECHO_H +#define __ECHO_H + +/* +Line echo cancellation for voice + +What does it do? + +This module aims to provide G.168-2002 compliant echo cancellation, to remove +electrical echoes (e.g. from 2-4 wire hybrids) from voice calls. + + +How does it work? + +The heart of the echo cancellor is FIR filter. This is adapted to match the +echo impulse response of the telephone line. It must be long enough to +adequately cover the duration of that impulse response. The signal transmitted +to the telephone line is passed through the FIR filter. Once the FIR is +properly adapted, the resulting output is an estimate of the echo signal +received from the line. This is subtracted from the received signal. The result +is an estimate of the signal which originated at the far end of the line, free +from echos of our own transmitted signal. + +The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and +was introduced in 1960. It is the commonest form of filter adaption used in +things like modem line equalisers and line echo cancellers. There it works very +well. However, it only works well for signals of constant amplitude. It works +very poorly for things like speech echo cancellation, where the signal level +varies widely. This is quite easy to fix. If the signal level is normalised - +similar to applying AGC - LMS can work as well for a signal of varying +amplitude as it does for a modem signal. This normalised least mean squares +(NLMS) algorithm is the commonest one used for speech echo cancellation. Many +other algorithms exist - e.g. RLS (essentially the same as Kalman filtering), +FAP, etc. Some perform significantly better than NLMS. However, factors such +as computational complexity and patents favour the use of NLMS. + +A simple refinement to NLMS can improve its performance with speech. NLMS tends +to adapt best to the strongest parts of a signal. If the signal is white noise, +the NLMS algorithm works very well. However, speech has more low frequency than +high frequency content. Pre-whitening (i.e. filtering the signal to flatten its +spectrum) the echo signal improves the adapt rate for speech, and ensures the +final residual signal is not heavily biased towards high frequencies. A very +low complexity filter is adequate for this, so pre-whitening adds little to the +compute requirements of the echo canceller. + +An FIR filter adapted using pre-whitened NLMS performs well, provided certain +conditions are met: + + - The transmitted signal has poor self-correlation. + - There is no signal being generated within the environment being + cancelled. + +The difficulty is that neither of these can be guaranteed. + +If the adaption is performed while transmitting noise (or something fairly +noise like, such as voice) the adaption works very well. If the adaption is +performed while transmitting something highly correlative (typically narrow +band energy such as signalling tones or DTMF), the adaption can go seriously +wrong. The reason is there is only one solution for the adaption on a near +random signal - the impulse response of the line. For a repetitive signal, +there are any number of solutions which converge the adaption, and nothing +guides the adaption to choose the generalised one. Allowing an untrained +canceller to converge on this kind of narrowband energy probably a good thing, +since at least it cancels the tones. Allowing a well converged canceller to +continue converging on such energy is just a way to ruin its generalised +adaption. A narrowband detector is needed, so adapation can be suspended at +appropriate times. + +The adaption process is based on trying to eliminate the received signal. When +there is any signal from within the environment being cancelled it may upset +the adaption process. Similarly, if the signal we are transmitting is small, +noise may dominate and disturb the adaption process. If we can ensure that the +adaption is only performed when we are transmitting a significant signal level, +and the environment is not, things will be OK. Clearly, it is easy to tell when +we are sending a significant signal. Telling, if the environment is generating +a significant signal, and doing it with sufficient speed that the adaption will +not have diverged too much more we stop it, is a little harder. + +The key problem in detecting when the environment is sourcing significant +energy is that we must do this very quickly. Given a reasonably long sample of +the received signal, there are a number of strategies which may be used to +assess whether that signal contains a strong far end component. However, by the +time that assessment is complete the far end signal will have already caused +major mis-convergence in the adaption process. An assessment algorithm is +needed which produces a fairly accurate result from a very short burst of far +end energy. + +How do I use it? + +The echo cancellor processes both the transmit and receive streams sample by +sample. The processing function is not declared inline. Unfortunately, +cancellation requires many operations per sample, so the call overhead is only +a minor burden. +*/ + +#include "fir.h" +#include "oslec.h" + +/* + G.168 echo canceller descriptor. This defines the working state for a line + echo canceller. +*/ +struct oslec_state { + int16_t tx, rx; + int16_t clean; + int16_t clean_nlp; + + int nonupdate_dwell; + int curr_pos; + int taps; + int log2taps; + int adaption_mode; + + int cond_met; + int32_t Pstates; + int16_t adapt; + int32_t factor; + int16_t shift; + + /* Average levels and averaging filter states */ + int Ltxacc, Lrxacc, Lcleanacc, Lclean_bgacc; + int Ltx, Lrx; + int Lclean; + int Lclean_bg; + int Lbgn, Lbgn_acc, Lbgn_upper, Lbgn_upper_acc; + + /* foreground and background filter states */ + struct fir16_state_t fir_state; + struct fir16_state_t fir_state_bg; + int16_t *fir_taps16[2]; + + /* DC blocking filter states */ + int tx_1, tx_2, rx_1, rx_2; + + /* optional High Pass Filter states */ + int32_t xvtx[5], yvtx[5]; + int32_t xvrx[5], yvrx[5]; + + /* Parameters for the optional Hoth noise generator */ + int cng_level; + int cng_rndnum; + int cng_filter; + + /* snapshot sample of coeffs used for development */ + int16_t *snapshot; +}; + +#endif /* __ECHO_H */ diff --git a/drivers/staging/echo/fir.h b/drivers/staging/echo/fir.h new file mode 100644 index 0000000..288bffc --- /dev/null +++ b/drivers/staging/echo/fir.h @@ -0,0 +1,286 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * fir.h - General telephony FIR routines + * + * Written by Steve Underwood + * + * Copyright (C) 2002 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 version 2, as + * published by the Free Software Foundation. + * + * 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. + */ + +#if !defined(_FIR_H_) +#define _FIR_H_ + +/* + Blackfin NOTES & IDEAS: + + A simple dot product function is used to implement the filter. This performs + just one MAC/cycle which is inefficient but was easy to implement as a first + pass. The current Blackfin code also uses an unrolled form of the filter + history to avoid 0 length hardware loop issues. This is wasteful of + memory. + + Ideas for improvement: + + 1/ Rewrite filter for dual MAC inner loop. The issue here is handling + history sample offsets that are 16 bit aligned - the dual MAC needs + 32 bit aligmnent. There are some good examples in libbfdsp. + + 2/ Use the hardware circular buffer facility tohalve memory usage. + + 3/ Consider using internal memory. + + Using less memory might also improve speed as cache misses will be + reduced. A drop in MIPs and memory approaching 50% should be + possible. + + The foreground and background filters currenlty use a total of + about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo + can. +*/ + +#if defined(USE_MMX) || defined(USE_SSE2) +#include "mmx.h" +#endif + +/* + * 16 bit integer FIR descriptor. This defines the working state for a single + * instance of an FIR filter using 16 bit integer coefficients. + */ +struct fir16_state_t { + int taps; + int curr_pos; + const int16_t *coeffs; + int16_t *history; +}; + +/* + * 32 bit integer FIR descriptor. This defines the working state for a single + * instance of an FIR filter using 32 bit integer coefficients, and filtering + * 16 bit integer data. + */ +struct fir32_state_t { + int taps; + int curr_pos; + const int32_t *coeffs; + int16_t *history; +}; + +/* + * Floating point FIR descriptor. This defines the working state for a single + * instance of an FIR filter using floating point coefficients and data. + */ +struct fir_float_state_t { + int taps; + int curr_pos; + const float *coeffs; + float *history; +}; + +static inline const int16_t *fir16_create(struct fir16_state_t *fir, + const int16_t *coeffs, int taps) +{ + fir->taps = taps; + fir->curr_pos = taps - 1; + fir->coeffs = coeffs; +#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__) + fir->history = kcalloc(2 * taps, sizeof(int16_t), GFP_KERNEL); +#else + fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); +#endif + return fir->history; +} + +static inline void fir16_flush(struct fir16_state_t *fir) +{ +#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__) + memset(fir->history, 0, 2 * fir->taps * sizeof(int16_t)); +#else + memset(fir->history, 0, fir->taps * sizeof(int16_t)); +#endif +} + +static inline void fir16_free(struct fir16_state_t *fir) +{ + kfree(fir->history); +} + +#ifdef __bfin__ +static inline int32_t dot_asm(short *x, short *y, int len) +{ + int dot; + + len--; + + __asm__("I0 = %1;\n\t" + "I1 = %2;\n\t" + "A0 = 0;\n\t" + "R0.L = W[I0++] || R1.L = W[I1++];\n\t" + "LOOP dot%= LC0 = %3;\n\t" + "LOOP_BEGIN dot%=;\n\t" + "A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t" + "LOOP_END dot%=;\n\t" + "A0 += R0.L*R1.L (IS);\n\t" + "R0 = A0;\n\t" + "%0 = R0;\n\t" + : "=&d"(dot) + : "a"(x), "a"(y), "a"(len) + : "I0", "I1", "A1", "A0", "R0", "R1" + ); + + return dot; +} +#endif + +static inline int16_t fir16(struct fir16_state_t *fir, int16_t sample) +{ + int32_t y; +#if defined(USE_MMX) + int i; + union mmx_t *mmx_coeffs; + union mmx_t *mmx_hist; + + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + + mmx_coeffs = (union mmx_t *) fir->coeffs; + mmx_hist = (union mmx_t *) &fir->history[fir->curr_pos]; + i = fir->taps; + pxor_r2r(mm4, mm4); + /* 8 samples per iteration, so the filter must be a multiple of + 8 long. */ + while (i > 0) { + movq_m2r(mmx_coeffs[0], mm0); + movq_m2r(mmx_coeffs[1], mm2); + movq_m2r(mmx_hist[0], mm1); + movq_m2r(mmx_hist[1], mm3); + mmx_coeffs += 2; + mmx_hist += 2; + pmaddwd_r2r(mm1, mm0); + pmaddwd_r2r(mm3, mm2); + paddd_r2r(mm0, mm4); + paddd_r2r(mm2, mm4); + i -= 8; + } + movq_r2r(mm4, mm0); + psrlq_i2r(32, mm0); + paddd_r2r(mm0, mm4); + movd_r2m(mm4, y); + emms(); +#elif defined(USE_SSE2) + int i; + union xmm_t *xmm_coeffs; + union xmm_t *xmm_hist; + + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + + xmm_coeffs = (union xmm_t *) fir->coeffs; + xmm_hist = (union xmm_t *) &fir->history[fir->curr_pos]; + i = fir->taps; + pxor_r2r(xmm4, xmm4); + /* 16 samples per iteration, so the filter must be a multiple of + 16 long. */ + while (i > 0) { + movdqu_m2r(xmm_coeffs[0], xmm0); + movdqu_m2r(xmm_coeffs[1], xmm2); + movdqu_m2r(xmm_hist[0], xmm1); + movdqu_m2r(xmm_hist[1], xmm3); + xmm_coeffs += 2; + xmm_hist += 2; + pmaddwd_r2r(xmm1, xmm0); + pmaddwd_r2r(xmm3, xmm2); + paddd_r2r(xmm0, xmm4); + paddd_r2r(xmm2, xmm4); + i -= 16; + } + movdqa_r2r(xmm4, xmm0); + psrldq_i2r(8, xmm0); + paddd_r2r(xmm0, xmm4); + movdqa_r2r(xmm4, xmm0); + psrldq_i2r(4, xmm0); + paddd_r2r(xmm0, xmm4); + movd_r2m(xmm4, y); +#elif defined(__bfin__) + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + y = dot_asm((int16_t *) fir->coeffs, &fir->history[fir->curr_pos], + fir->taps); +#else + int i; + int offset1; + int offset2; + + fir->history[fir->curr_pos] = sample; + + offset2 = fir->curr_pos; + offset1 = fir->taps - offset2; + y = 0; + for (i = fir->taps - 1; i >= offset1; i--) + y += fir->coeffs[i] * fir->history[i - offset1]; + for (; i >= 0; i--) + y += fir->coeffs[i] * fir->history[i + offset2]; +#endif + if (fir->curr_pos <= 0) + fir->curr_pos = fir->taps; + fir->curr_pos--; + return (int16_t) (y >> 15); +} + +static inline const int16_t *fir32_create(struct fir32_state_t *fir, + const int32_t *coeffs, int taps) +{ + fir->taps = taps; + fir->curr_pos = taps - 1; + fir->coeffs = coeffs; + fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); + return fir->history; +} + +static inline void fir32_flush(struct fir32_state_t *fir) +{ + memset(fir->history, 0, fir->taps * sizeof(int16_t)); +} + +static inline void fir32_free(struct fir32_state_t *fir) +{ + kfree(fir->history); +} + +static inline int16_t fir32(struct fir32_state_t *fir, int16_t sample) +{ + int i; + int32_t y; + int offset1; + int offset2; + + fir->history[fir->curr_pos] = sample; + offset2 = fir->curr_pos; + offset1 = fir->taps - offset2; + y = 0; + for (i = fir->taps - 1; i >= offset1; i--) + y += fir->coeffs[i] * fir->history[i - offset1]; + for (; i >= 0; i--) + y += fir->coeffs[i] * fir->history[i + offset2]; + if (fir->curr_pos <= 0) + fir->curr_pos = fir->taps; + fir->curr_pos--; + return (int16_t) (y >> 15); +} + +#endif diff --git a/drivers/staging/echo/mmx.h b/drivers/staging/echo/mmx.h new file mode 100644 index 0000000..c030cdf --- /dev/null +++ b/drivers/staging/echo/mmx.h @@ -0,0 +1,288 @@ +/* + * mmx.h + * Copyright (C) 1997-2001 H. Dietz and R. Fisher + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVCODEC_I386MMX_H +#define AVCODEC_I386MMX_H + +/* + * The type of an value that fits in an MMX register (note that long + * long constant values MUST be suffixed by LL and unsigned long long + * values by ULL, lest they be truncated by the compiler) + */ + +union mmx_t { + long long q; /* Quadword (64-bit) value */ + unsigned long long uq; /* Unsigned Quadword */ + int d[2]; /* 2 Doubleword (32-bit) values */ + unsigned int ud[2]; /* 2 Unsigned Doubleword */ + short w[4]; /* 4 Word (16-bit) values */ + unsigned short uw[4]; /* 4 Unsigned Word */ + char b[8]; /* 8 Byte (8-bit) values */ + unsigned char ub[8]; /* 8 Unsigned Byte */ + float s[2]; /* Single-precision (32-bit) value */ +}; /* On an 8-byte (64-bit) boundary */ + +/* SSE registers */ +union xmm_t { + char b[16]; +}; + + +#define mmx_i2r(op, imm, reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "i" (imm)) + +#define mmx_m2r(op, mem, reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "m" (mem)) + +#define mmx_r2m(op, reg, mem) \ + __asm__ __volatile__ (#op " %%" #reg ", %0" \ + : "=m" (mem) \ + : /* nothing */) + +#define mmx_r2r(op, regs, regd) \ + __asm__ __volatile__ (#op " %" #regs ", %" #regd) + + +#define emms() __asm__ __volatile__ ("emms") + +#define movd_m2r(var, reg) mmx_m2r(movd, var, reg) +#define movd_r2m(reg, var) mmx_r2m(movd, reg, var) +#define movd_r2r(regs, regd) mmx_r2r(movd, regs, regd) + +#define movq_m2r(var, reg) mmx_m2r(movq, var, reg) +#define movq_r2m(reg, var) mmx_r2m(movq, reg, var) +#define movq_r2r(regs, regd) mmx_r2r(movq, regs, regd) + +#define packssdw_m2r(var, reg) mmx_m2r(packssdw, var, reg) +#define packssdw_r2r(regs, regd) mmx_r2r(packssdw, regs, regd) +#define packsswb_m2r(var, reg) mmx_m2r(packsswb, var, reg) +#define packsswb_r2r(regs, regd) mmx_r2r(packsswb, regs, regd) + +#define packuswb_m2r(var, reg) mmx_m2r(packuswb, var, reg) +#define packuswb_r2r(regs, regd) mmx_r2r(packuswb, regs, regd) + +#define paddb_m2r(var, reg) mmx_m2r(paddb, var, reg) +#define paddb_r2r(regs, regd) mmx_r2r(paddb, regs, regd) +#define paddd_m2r(var, reg) mmx_m2r(paddd, var, reg) +#define paddd_r2r(regs, regd) mmx_r2r(paddd, regs, regd) +#define paddw_m2r(var, reg) mmx_m2r(paddw, var, reg) +#define paddw_r2r(regs, regd) mmx_r2r(paddw, regs, regd) + +#define paddsb_m2r(var, reg) mmx_m2r(paddsb, var, reg) +#define paddsb_r2r(regs, regd) mmx_r2r(paddsb, regs, regd) +#define paddsw_m2r(var, reg) mmx_m2r(paddsw, var, reg) +#define paddsw_r2r(regs, regd) mmx_r2r(paddsw, regs, regd) + +#define paddusb_m2r(var, reg) mmx_m2r(paddusb, var, reg) +#define paddusb_r2r(regs, regd) mmx_r2r(paddusb, regs, regd) +#define paddusw_m2r(var, reg) mmx_m2r(paddusw, var, reg) +#define paddusw_r2r(regs, regd) mmx_r2r(paddusw, regs, regd) + +#define pand_m2r(var, reg) mmx_m2r(pand, var, reg) +#define pand_r2r(regs, regd) mmx_r2r(pand, regs, regd) + +#define pandn_m2r(var, reg) mmx_m2r(pandn, var, reg) +#define pandn_r2r(regs, regd) mmx_r2r(pandn, regs, regd) + +#define pcmpeqb_m2r(var, reg) mmx_m2r(pcmpeqb, var, reg) +#define pcmpeqb_r2r(regs, regd) mmx_r2r(pcmpeqb, regs, regd) +#define pcmpeqd_m2r(var, reg) mmx_m2r(pcmpeqd, var, reg) +#define pcmpeqd_r2r(regs, regd) mmx_r2r(pcmpeqd, regs, regd) +#define pcmpeqw_m2r(var, reg) mmx_m2r(pcmpeqw, var, reg) +#define pcmpeqw_r2r(regs, regd) mmx_r2r(pcmpeqw, regs, regd) + +#define pcmpgtb_m2r(var, reg) mmx_m2r(pcmpgtb, var, reg) +#define pcmpgtb_r2r(regs, regd) mmx_r2r(pcmpgtb, regs, regd) +#define pcmpgtd_m2r(var, reg) mmx_m2r(pcmpgtd, var, reg) +#define pcmpgtd_r2r(regs, regd) mmx_r2r(pcmpgtd, regs, regd) +#define pcmpgtw_m2r(var, reg) mmx_m2r(pcmpgtw, var, reg) +#define pcmpgtw_r2r(regs, regd) mmx_r2r(pcmpgtw, regs, regd) + +#define pmaddwd_m2r(var, reg) mmx_m2r(pmaddwd, var, reg) +#define pmaddwd_r2r(regs, regd) mmx_r2r(pmaddwd, regs, regd) + +#define pmulhw_m2r(var, reg) mmx_m2r(pmulhw, var, reg) +#define pmulhw_r2r(regs, regd) mmx_r2r(pmulhw, regs, regd) + +#define pmullw_m2r(var, reg) mmx_m2r(pmullw, var, reg) +#define pmullw_r2r(regs, regd) mmx_r2r(pmullw, regs, regd) + +#define por_m2r(var, reg) mmx_m2r(por, var, reg) +#define por_r2r(regs, regd) mmx_r2r(por, regs, regd) + +#define pslld_i2r(imm, reg) mmx_i2r(pslld, imm, reg) +#define pslld_m2r(var, reg) mmx_m2r(pslld, var, reg) +#define pslld_r2r(regs, regd) mmx_r2r(pslld, regs, regd) +#define psllq_i2r(imm, reg) mmx_i2r(psllq, imm, reg) +#define psllq_m2r(var, reg) mmx_m2r(psllq, var, reg) +#define psllq_r2r(regs, regd) mmx_r2r(psllq, regs, regd) +#define psllw_i2r(imm, reg) mmx_i2r(psllw, imm, reg) +#define psllw_m2r(var, reg) mmx_m2r(psllw, var, reg) +#define psllw_r2r(regs, regd) mmx_r2r(psllw, regs, regd) + +#define psrad_i2r(imm, reg) mmx_i2r(psrad, imm, reg) +#define psrad_m2r(var, reg) mmx_m2r(psrad, var, reg) +#define psrad_r2r(regs, regd) mmx_r2r(psrad, regs, regd) +#define psraw_i2r(imm, reg) mmx_i2r(psraw, imm, reg) +#define psraw_m2r(var, reg) mmx_m2r(psraw, var, reg) +#define psraw_r2r(regs, regd) mmx_r2r(psraw, regs, regd) + +#define psrld_i2r(imm, reg) mmx_i2r(psrld, imm, reg) +#define psrld_m2r(var, reg) mmx_m2r(psrld, var, reg) +#define psrld_r2r(regs, regd) mmx_r2r(psrld, regs, regd) +#define psrlq_i2r(imm, reg) mmx_i2r(psrlq, imm, reg) +#define psrlq_m2r(var, reg) mmx_m2r(psrlq, var, reg) +#define psrlq_r2r(regs, regd) mmx_r2r(psrlq, regs, regd) +#define psrlw_i2r(imm, reg) mmx_i2r(psrlw, imm, reg) +#define psrlw_m2r(var, reg) mmx_m2r(psrlw, var, reg) +#define psrlw_r2r(regs, regd) mmx_r2r(psrlw, regs, regd) + +#define psubb_m2r(var, reg) mmx_m2r(psubb, var, reg) +#define psubb_r2r(regs, regd) mmx_r2r(psubb, regs, regd) +#define psubd_m2r(var, reg) mmx_m2r(psubd, var, reg) +#define psubd_r2r(regs, regd) mmx_r2r(psubd, regs, regd) +#define psubw_m2r(var, reg) mmx_m2r(psubw, var, reg) +#define psubw_r2r(regs, regd) mmx_r2r(psubw, regs, regd) + +#define psubsb_m2r(var, reg) mmx_m2r(psubsb, var, reg) +#define psubsb_r2r(regs, regd) mmx_r2r(psubsb, regs, regd) +#define psubsw_m2r(var, reg) mmx_m2r(psubsw, var, reg) +#define psubsw_r2r(regs, regd) mmx_r2r(psubsw, regs, regd) + +#define psubusb_m2r(var, reg) mmx_m2r(psubusb, var, reg) +#define psubusb_r2r(regs, regd) mmx_r2r(psubusb, regs, regd) +#define psubusw_m2r(var, reg) mmx_m2r(psubusw, var, reg) +#define psubusw_r2r(regs, regd) mmx_r2r(psubusw, regs, regd) + +#define punpckhbw_m2r(var, reg) mmx_m2r(punpckhbw, var, reg) +#define punpckhbw_r2r(regs, regd) mmx_r2r(punpckhbw, regs, regd) +#define punpckhdq_m2r(var, reg) mmx_m2r(punpckhdq, var, reg) +#define punpckhdq_r2r(regs, regd) mmx_r2r(punpckhdq, regs, regd) +#define punpckhwd_m2r(var, reg) mmx_m2r(punpckhwd, var, reg) +#define punpckhwd_r2r(regs, regd) mmx_r2r(punpckhwd, regs, regd) + +#define punpcklbw_m2r(var, reg) mmx_m2r(punpcklbw, var, reg) +#define punpcklbw_r2r(regs, regd) mmx_r2r(punpcklbw, regs, regd) +#define punpckldq_m2r(var, reg) mmx_m2r(punpckldq, var, reg) +#define punpckldq_r2r(regs, regd) mmx_r2r(punpckldq, regs, regd) +#define punpcklwd_m2r(var, reg) mmx_m2r(punpcklwd, var, reg) +#define punpcklwd_r2r(regs, regd) mmx_r2r(punpcklwd, regs, regd) + +#define pxor_m2r(var, reg) mmx_m2r(pxor, var, reg) +#define pxor_r2r(regs, regd) mmx_r2r(pxor, regs, regd) + + +/* 3DNOW extensions */ + +#define pavgusb_m2r(var, reg) mmx_m2r(pavgusb, var, reg) +#define pavgusb_r2r(regs, regd) mmx_r2r(pavgusb, regs, regd) + + +/* AMD MMX extensions - also available in intel SSE */ + + +#define mmx_m2ri(op, mem, reg, imm) \ + __asm__ __volatile__ (#op " %1, %0, %%" #reg \ + : /* nothing */ \ + : "m" (mem), "i" (imm)) +#define mmx_r2ri(op, regs, regd, imm) \ + __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ + : /* nothing */ \ + : "i" (imm)) + +#define mmx_fetch(mem, hint) \ + __asm__ __volatile__ ("prefetch" #hint " %0" \ + : /* nothing */ \ + : "m" (mem)) + + +#define maskmovq(regs, maskreg) mmx_r2ri(maskmovq, regs, maskreg) + +#define movntq_r2m(mmreg, var) mmx_r2m(movntq, mmreg, var) + +#define pavgb_m2r(var, reg) mmx_m2r(pavgb, var, reg) +#define pavgb_r2r(regs, regd) mmx_r2r(pavgb, regs, regd) +#define pavgw_m2r(var, reg) mmx_m2r(pavgw, var, reg) +#define pavgw_r2r(regs, regd) mmx_r2r(pavgw, regs, regd) + +#define pextrw_r2r(mmreg, reg, imm) mmx_r2ri(pextrw, mmreg, reg, imm) + +#define pinsrw_r2r(reg, mmreg, imm) mmx_r2ri(pinsrw, reg, mmreg, imm) + +#define pmaxsw_m2r(var, reg) mmx_m2r(pmaxsw, var, reg) +#define pmaxsw_r2r(regs, regd) mmx_r2r(pmaxsw, regs, regd) + +#define pmaxub_m2r(var, reg) mmx_m2r(pmaxub, var, reg) +#define pmaxub_r2r(regs, regd) mmx_r2r(pmaxub, regs, regd) + +#define pminsw_m2r(var, reg) mmx_m2r(pminsw, var, reg) +#define pminsw_r2r(regs, regd) mmx_r2r(pminsw, regs, regd) + +#define pminub_m2r(var, reg) mmx_m2r(pminub, var, reg) +#define pminub_r2r(regs, regd) mmx_r2r(pminub, regs, regd) + +#define pmovmskb(mmreg, reg) \ + __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) + +#define pmulhuw_m2r(var, reg) mmx_m2r(pmulhuw, var, reg) +#define pmulhuw_r2r(regs, regd) mmx_r2r(pmulhuw, regs, regd) + +#define prefetcht0(mem) mmx_fetch(mem, t0) +#define prefetcht1(mem) mmx_fetch(mem, t1) +#define prefetcht2(mem) mmx_fetch(mem, t2) +#define prefetchnta(mem) mmx_fetch(mem, nta) + +#define psadbw_m2r(var, reg) mmx_m2r(psadbw, var, reg) +#define psadbw_r2r(regs, regd) mmx_r2r(psadbw, regs, regd) + +#define pshufw_m2r(var, reg, imm) mmx_m2ri(pshufw, var, reg, imm) +#define pshufw_r2r(regs, regd, imm) mmx_r2ri(pshufw, regs, regd, imm) + +#define sfence() __asm__ __volatile__ ("sfence\n\t") + +/* SSE2 */ +#define pshufhw_m2r(var, reg, imm) mmx_m2ri(pshufhw, var, reg, imm) +#define pshufhw_r2r(regs, regd, imm) mmx_r2ri(pshufhw, regs, regd, imm) +#define pshuflw_m2r(var, reg, imm) mmx_m2ri(pshuflw, var, reg, imm) +#define pshuflw_r2r(regs, regd, imm) mmx_r2ri(pshuflw, regs, regd, imm) + +#define pshufd_r2r(regs, regd, imm) mmx_r2ri(pshufd, regs, regd, imm) + +#define movdqa_m2r(var, reg) mmx_m2r(movdqa, var, reg) +#define movdqa_r2m(reg, var) mmx_r2m(movdqa, reg, var) +#define movdqa_r2r(regs, regd) mmx_r2r(movdqa, regs, regd) +#define movdqu_m2r(var, reg) mmx_m2r(movdqu, var, reg) +#define movdqu_r2m(reg, var) mmx_r2m(movdqu, reg, var) +#define movdqu_r2r(regs, regd) mmx_r2r(movdqu, regs, regd) + +#define pmullw_r2m(reg, var) mmx_r2m(pmullw, reg, var) + +#define pslldq_i2r(imm, reg) mmx_i2r(pslldq, imm, reg) +#define psrldq_i2r(imm, reg) mmx_i2r(psrldq, imm, reg) + +#define punpcklqdq_r2r(regs, regd) mmx_r2r(punpcklqdq, regs, regd) +#define punpckhqdq_r2r(regs, regd) mmx_r2r(punpckhqdq, regs, regd) + + +#endif /* AVCODEC_I386MMX_H */ diff --git a/drivers/staging/echo/oslec.h b/drivers/staging/echo/oslec.h new file mode 100644 index 0000000..f417536 --- /dev/null +++ b/drivers/staging/echo/oslec.h @@ -0,0 +1,94 @@ +/* + * OSLEC - A line echo canceller. This code is being developed + * against and partially complies with G168. Using code from SpanDSP + * + * Written by Steve Underwood + * and David Rowe + * + * Copyright (C) 2001 Steve Underwood and 2007-2008 David Rowe + * + * 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 version 2, as + * published by the Free Software Foundation. + * + * 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 __OSLEC_H +#define __OSLEC_H + +/* Mask bits for the adaption mode */ +#define ECHO_CAN_USE_ADAPTION 0x01 +#define ECHO_CAN_USE_NLP 0x02 +#define ECHO_CAN_USE_CNG 0x04 +#define ECHO_CAN_USE_CLIP 0x08 +#define ECHO_CAN_USE_TX_HPF 0x10 +#define ECHO_CAN_USE_RX_HPF 0x20 +#define ECHO_CAN_DISABLE 0x40 + +/** + * oslec_state: G.168 echo canceller descriptor. + * + * This defines the working state for a line echo canceller. + */ +struct oslec_state; + +/** + * oslec_create - Create a voice echo canceller context. + * @len: The length of the canceller, in samples. + * @return: The new canceller context, or NULL if the canceller could not be + * created. + */ +struct oslec_state *oslec_create(int len, int adaption_mode); + +/** + * oslec_free - Free a voice echo canceller context. + * @ec: The echo canceller context. + */ +void oslec_free(struct oslec_state *ec); + +/** + * oslec_flush - Flush (reinitialise) a voice echo canceller context. + * @ec: The echo canceller context. + */ +void oslec_flush(struct oslec_state *ec); + +/** + * oslec_adaption_mode - set the adaption mode of a voice echo canceller context. + * @ec The echo canceller context. + * @adaption_mode: The mode. + */ +void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode); + +void oslec_snapshot(struct oslec_state *ec); + +/** + * oslec_update: Process a sample through a voice echo canceller. + * @ec: The echo canceller context. + * @tx: The transmitted audio sample. + * @rx: The received audio sample. + * + * The return value is the clean (echo cancelled) received sample. + */ +int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx); + +/** + * oslec_hpf_tx: Process to high pass filter the tx signal. + * @ec: The echo canceller context. + * @tx: The transmitted auio sample. + * + * The return value is the HP filtered transmit sample, send this to your D/A. + */ +int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx); + +#endif /* __OSLEC_H */ debian/patches/mmx_fix0000664000000000000000000000706311652357765012240 0ustar safer MMX support for echo canceller Properly save MMX registers on i386 (but not x86_64) Upstream issue: http://bugs.digium.com/view.php?id=13500 Index: drivers/dahdi/dahdi-base.c =================================================================== --- a/drivers/dahdi/dahdi-base.c (revision 6648) +++ b/drivers/dahdi/dahdi-base.c (working copy) @@ -296,9 +296,58 @@ static struct dahdi_dialparams global_dialparams = static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit); #if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP) +#if (defined(CONFIG_X86) && !defined(CONFIG_X86_64)) || defined(CONFIG_I386) +struct fpu_save_buf { + unsigned long cr0; + unsigned long fpu_buf[128]; +}; + +static DEFINE_PER_CPU(struct fpu_save_buf, fpu_buf); + +/** dahdi_kernel_fpu_begin() - Save floating point registers + * + * This function is similar to kernel_fpu_begin() . However it is + * designed to work in an interrupt context. Restoring must be done with + * dahdi_kernel_fpu_end(). + * + * Furthermore, the whole code between the call to + * dahdi_kernel_fpu_begin() and dahdi_kernel_fpu_end() must reside + * inside a spinlock. Otherwise the context might be restored to the + * wrong process. + * + * Current implementation is x86/ia32-specific and will not even build on + * x86_64) + * */ +static inline void dahdi_kernel_fpu_begin(void) +{ + struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf); + __asm__ __volatile__ ("movl %%cr0,%0; clts" : "=r" (buf->cr0)); + __asm__ __volatile__ ("fnsave %0" : "=m" (buf->fpu_buf)); +} + +/** dahdi_kernel_fpu_end() - restore floating point context + * + * Must be used with context saved by dahdi_kernel_fpu_begin(). See its + * documentation for further information. + */ +static inline void dahdi_kernel_fpu_end(void) +{ + struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf); + __asm__ __volatile__ ("frstor %0" : "=m" (buf->fpu_buf)); + __asm__ __volatile__ ("movl %0,%%cr0" : : "r" (buf->cr0)); +} + +#else /* We haven't fixed FP context saving/restoring yet */ +/* Very strange things can happen when the context is not properly + * restored. OTOH, some people do report success with this. Hence we + * so far just issue a warning */ +#warning CONFIG_ZAPTEL_MMX may behave randomely on this platform #define dahdi_kernel_fpu_begin kernel_fpu_begin +#define dahdi_kernel_fpu_end kernel_fpu_end #endif +#endif + struct dahdi_timer { int ms; /* Countdown */ int pos; /* Position */ @@ -6664,7 +6713,7 @@ static inline void __dahdi_ec_chunk(struct dahdi_c } #if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP) - kernel_fpu_end(); + dahdi_kernel_fpu_end(); #endif } spin_unlock_irqrestore(&ss->lock, flags); @@ -6997,6 +7046,7 @@ static inline void __putbuf_chunk(struct dahdi_cha int res; int left, x; + while(bytes) { #if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP) skb = NULL; @@ -7558,7 +7608,7 @@ static void __dahdi_transmit_chunk(struct dahdi_ch #endif __dahdi_process_getaudio_chunk(chan, buf); #ifdef CONFIG_DAHDI_MMX - kernel_fpu_end(); + dahdi_kernel_fpu_end(); #endif } } @@ -7643,7 +7693,7 @@ static void __dahdi_receive_chunk(struct dahdi_cha #endif __dahdi_process_putaudio_chunk(chan, buf); #ifdef CONFIG_DAHDI_MMX - kernel_fpu_end(); + dahdi_kernel_fpu_end(); #endif } __dahdi_putbuf_chunk(chan, buf); @@ -7865,7 +7915,7 @@ int dahdi_receive(struct dahdi_span *span) } } #ifdef CONFIG_DAHDI_MMX - kernel_fpu_end(); + dahdi_kernel_fpu_end(); #endif } /* do all the pseudo/conferenced channel transmits (putbuf's) */ debian/patches/zaphfc_d_channel_fix.patch0000664000000000000000000000152511652360160015757 0ustar From: Adam Gandelman Bug: http://code.google.com/p/zaphfc/issues/detail?id=1&can=1 Bug-Ubuntu: http://launchpad.net/bugs/612091 Subject: Fix typo, allowing d-channel to connect. Debian imports the dahdi-extras via a big patch that is apparently out-of-date from upstream. Index: ubuntu/drivers/dahdi/zaphfc/base.c =================================================================== --- ubuntu.orig/drivers/dahdi/zaphfc/base.c 2011-10-21 18:38:50.469506871 -0700 +++ ubuntu/drivers/dahdi/zaphfc/base.c 2011-10-21 18:39:41.177509288 -0700 @@ -702,7 +702,7 @@ if (i == hfccard->span.channels - 1) { hfccard->chans[i].sigcap = DAHDI_SIG_HARDHDLC; - hfccard->sigchan = &hfccard->chans[D]; + hfccard->sigchan = &hfccard->chans[DAHDI_D]; hfccard->sigactive = 0; atomic_set(&hfccard->hdlc_pending, 0); } else { debian/patches/kernel-v3-11-compat.patch0000664000000000000000000012644212177720120015156 0ustar From: Stefan Bader Date: Mon, 05 Aug 2013 13:41:28 +0100 Subject: Fix compile issues with 3.11 kernel Lots of proc_create_read_entry issues, some __devinit/exit annotations and missing slab.h includes. Signed-off-by: Stefan Bader Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/dahdi-base.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/dahdi-base.c 2011-07-21 17:26:31.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/dahdi-base.c 2013-08-03 14:56:17.105260455 +0100 @@ -896,7 +896,11 @@ static int dahdi_seq_show(struct seq_fil static int dahdi_proc_open(struct inode *inode, struct file *file) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, dahdi_seq_show, PDE_DATA(inode)); +#else return single_open(file, dahdi_seq_show, PDE(inode)->data); +#endif } static const struct file_operations dahdi_proc_ops = { @@ -6639,15 +6643,23 @@ static int _dahdi_register(struct dahdi_ { char tempfile[17]; snprintf(tempfile, sizeof(tempfile), "%d", span->spanno); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + span->proc_entry = proc_create_data(tempfile, 0444, + root_proc_entry, &dahdi_proc_ops, + ((void *) ((long) span->spanno))); +#else span->proc_entry = create_proc_entry(tempfile, 0444, root_proc_entry); +#endif if (!span->proc_entry) { res = -EFAULT; span_err(span, "Error creating procfs entry\n"); goto cleanup; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) span->proc_entry->data = (void *)(long)span->spanno; span->proc_entry->proc_fops = &dahdi_proc_ops; +#endif } #endif @@ -6736,7 +6748,11 @@ static int _dahdi_unregister(struct dahd if (debug & DEBUG_MAIN) module_printk(KERN_NOTICE, "Unregistering Span '%s' with %d channels\n", span->name, span->channels); #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + proc_remove(span->proc_entry); +#else remove_proc_entry(span->proc_entry->name, root_proc_entry); +#endif #endif /* CONFIG_PROC_FS */ span_sysfs_remove(span); Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/dahdi_dynamic_ethmf.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/dahdi_dynamic_ethmf.c 2011-01-03 18:26:29.000000000 +0000 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/dahdi_dynamic_ethmf.c 2013-08-03 15:36:48.157158324 +0100 @@ -722,6 +722,8 @@ static void timer_callback(unsigned long #ifdef USE_PROC_FS static struct proc_dir_entry *proc_entry; static const char *ztdethmf_procname = "dahdi/dynamic-ethmf"; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) static int ztdethmf_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -787,6 +789,70 @@ static int ztdethmf_proc_read(char *page len = count; return len; } + +#else /* kernel version > 3.10 */ + +static int ztdethmf_proc_read(struct seq_file *m, void *v) +{ + struct ztdeth *z = NULL; + int i = 0; + unsigned int group = 0, c = 0; + + seq_printf(m, "Errors: %d\n\n", atomic_read(&errcount)); + + for (group = 0; group < ETHMF_MAX_GROUPS; ++group) { + if (atomic_read(&(ethmf_groups[group].spans))) { + seq_printf(m, "Group #%d (0x%x)\n", i++, + ethmf_groups[group].hash_addr); + seq_printf(m, " Spans: %d\n", + atomic_read(&(ethmf_groups[group].spans))); + + c = 1; + list_for_each_entry_rcu(z, ðmf_list, list) { + if (z->addr_hash == ethmf_groups[group].hash_addr) { + if (c == 1) { + seq_printf(m, + " Device: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x)\n", + z->ethdev, + z->addr[0], z->addr[1], z->addr[2], + z->addr[3], z->addr[4], z->addr[5]); + } + seq_printf(m, " Span %d: subaddr=%u ready=%d delay=%d real_channels=%d no_front_padding=%d\n", + c++, ntohs(z->subaddr), + atomic_read(&z->ready), atomic_read(&z->delay), + z->real_channels, atomic_read(&z->no_front_padding)); + } + } + seq_printf(m, " Device UPs: %u\n", + atomic_read(&(ethmf_groups[group].devupcount))); + seq_printf(m, " Device DOWNs: %u\n", + atomic_read(&(ethmf_groups[group].devdowncount))); + seq_printf(m, " Rx Frames: %u\n", + atomic_read(&(ethmf_groups[group].rxframecount))); + seq_printf(m, " Tx Frames: %u\n", + atomic_read(&(ethmf_groups[group].txframecount))); + seq_printf(m, " Rx Bytes: %u\n", + atomic_read(&(ethmf_groups[group].rxbytecount))); + seq_printf(m, " Tx Bytes: %u\n", + atomic_read(&(ethmf_groups[group].txbytecount))); + } + } + + return 0; +} + +static int ztdethmf_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ztdethmf_proc_read, PDE_DATA(inode)); +} + +static const struct file_operations proc_status_fops = { + .open = ztdethmf_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif #endif static int __init ztdethmf_init(void) @@ -804,8 +870,12 @@ static int __init ztdethmf_init(void) skb_queue_head_init(&skbs); #ifdef USE_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + proc_entry = proc_create(ztdethmf_procname, 0444, NULL, &proc_status_fops); +#else proc_entry = create_proc_read_entry(ztdethmf_procname, 0444, NULL, ztdethmf_proc_read, NULL); +#endif if (!proc_entry) { printk(KERN_ALERT "create_proc_read_entry failed.\n"); } Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/wcb4xxp/base.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/wcb4xxp/base.c 2011-07-22 18:56:07.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/wcb4xxp/base.c 2013-08-05 13:53:23.587048123 +0100 @@ -35,7 +35,10 @@ #include #include /* dev_err() */ #include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0) #include /* cli(), *_flags */ +#endif #include /* copy_*_user */ #include /* work_struct */ #include /* timer_struct */ Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/card_bri.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/xpp/card_bri.c 2011-07-04 15:05:19.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/card_bri.c 2013-08-05 10:59:03.091487578 +0100 @@ -160,7 +160,27 @@ static int write_state_register(xpd_t *x static bool bri_packet_is_valid(xpacket_t *pack); static void bri_packet_dump(const char *msg, xpacket_t *pack); #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) + static int proc_bri_info_read(char *page, char **start, off_t off, int count, int *eof, void *data); + +#else + +#include +static int bri_proc_read(struct seq_file *m, void *v); + +static int bri_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, bri_proc_read, PDE_DATA(inode)); +} + +static const struct file_operations bri_proc_info_fops = { + .open = bri_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif #endif static int bri_spanconfig(struct file *file, struct dahdi_span *span, struct dahdi_lineconfig *lc); @@ -789,7 +809,11 @@ static int bri_proc_create(xbus_t *xbus, XPD_DBG(PROC, xpd, "\n"); #ifdef CONFIG_PROC_FS XPD_DBG(PROC, xpd, "Creating '%s'\n", PROC_BRI_INFO_FNAME); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) priv->bri_info = create_proc_read_entry(PROC_BRI_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_bri_info_read, xpd); +#else + priv->bri_info = proc_create_data(PROC_BRI_INFO_FNAME, 0444, xpd->proc_xpd_dir, &bri_proc_info_fops, xpd); +#endif if(!priv->bri_info) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_BRI_INFO_FNAME); bri_proc_remove(xbus, xpd); @@ -1733,6 +1757,8 @@ static void bri_packet_dump(const char * /*------------------------- REGISTER Handling --------------------------*/ #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + static int proc_bri_info_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; @@ -1795,6 +1821,62 @@ static int proc_bri_info_read(char *page len = 0; return len; } +#else +static int bri_proc_read(struct seq_file *m, void *v) +{ + unsigned long flags; + xpd_t *xpd = v; + struct BRI_priv_data *priv; + + DBG(PROC, "\n"); + if(!xpd) + return -ENODEV; + + spin_lock_irqsave(&xpd->lock, flags); + priv = xpd->priv; + BUG_ON(!priv); + seq_printf(m, "%05d Layer 1: ", priv->poll_counter); + if(priv->reg30_good) { + seq_printf(m, "%-5s ", (priv->layer1_up) ? "UP" : "DOWN"); + seq_printf(m, "%c%d %-15s -- fr_sync=%d t2_exp=%d info0=%d g2_g3=%d\n", + IS_NT(xpd)?'G':'F', + priv->state_register.bits.v_su_sta, + xhfc_state_name(IS_NT(xpd), priv->state_register.bits.v_su_sta), + priv->state_register.bits.v_su_fr_sync, + priv->state_register.bits.v_su_t2_exp, + priv->state_register.bits.v_su_info0, + priv->state_register.bits.v_g2_g3); + } else + seq_printf(m, "Unknown\n"); + if(IS_NT(xpd)) { + seq_printf(m, "T1 Timer: %d\n", priv->t1); + } else { + seq_printf(m, "T3 Timer: %d\n", priv->t3); + } + seq_printf(m, "Tick Counter: %d\n", priv->tick_counter); + seq_printf(m, "Last Poll Reply: %d ticks ago\n", priv->reg30_ticks); + seq_printf(m, "reg30_good=%d\n", priv->reg30_good); + seq_printf(m, "D-Channel: TX=[%5d] RX=[%5d] BAD=[%5d] ", + priv->dchan_tx_counter, priv->dchan_rx_counter, priv->dchan_rx_drops); + if(priv->dchan_alive) { + seq_printf(m, "(alive %d K-ticks)\n", + priv->dchan_alive_ticks/1000); + } else { + seq_printf(m, "(dead)\n"); + } +#ifndef CONFIG_DAHDI_BRI_DCHANS + seq_printf(m, "hdlc_pending=%d\n", atomic_read(&priv->hdlc_pending)); +#endif + seq_printf(m, "dchan_notx_ticks: %d\n", priv->dchan_notx_ticks); + seq_printf(m, "dchan_norx_ticks: %d\n", priv->dchan_norx_ticks); + seq_printf(m, "LED: %-10s = %d\n", "GREEN", priv->ledstate[GREEN_LED]); + seq_printf(m, "LED: %-10s = %d\n", "RED", priv->ledstate[RED_LED]); + seq_printf(m, "\nDCHAN:\n"); + seq_printf(m, "\n"); + spin_unlock_irqrestore(&xpd->lock, flags); + return 0; +} +#endif #endif static DRIVER_ATTR_READER(dchan_hardhdlc_show, drv,buf) Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/card_fxo.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/xpp/card_fxo.c 2011-06-28 19:23:00.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/card_fxo.c 2013-08-05 11:35:30.675395676 +0100 @@ -86,11 +86,43 @@ enum fxo_leds { static bool fxo_packet_is_valid(xpacket_t *pack); static void fxo_packet_dump(const char *msg, xpacket_t *pack); #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int proc_fxo_info_read(char *page, char **start, off_t off, int count, int *eof, void *data); #ifdef WITH_METERING static int proc_xpd_metering_read(char *page, char **start, off_t off, int count, int *eof, void *data); +#endif /* WITH_METERING */ +#else /* KERNEL_VERSION >= 3.10.0 */ + +#include + +static int fxo_proc_read(struct seq_file *m, void *v); +static int proc_fxo_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, fxo_proc_read, PDE_DATA(inode)); +} + +static const struct file_operations proc_fxo_info_fops = { + .open = proc_fxo_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#ifdef WITH_METERING +static int fxo_metering_read(struct seq_file *m, void *v); + +static int proc_fxo_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, fxo_metering_read, PDE_DATA(inode)); +} +static const struct file_operations proc_fxo_metering_fops = { + .open = proc_fxo_metering_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; #endif -#endif +#endif /* KERNEL_VERSION */ +#endif /* PROCFS */ static void dahdi_report_battery(xpd_t *xpd, lineno_t chan); #define PROC_REGISTER_FNAME "slics" @@ -400,7 +432,11 @@ static int fxo_proc_create(xbus_t *xbus, priv = xpd->priv; #ifdef CONFIG_PROC_FS XPD_DBG(PROC, xpd, "Creating FXO_INFO file\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) priv->fxo_info = create_proc_read_entry(PROC_FXO_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_fxo_info_read, xpd); +#else + priv->fxo_info = proc_create_data(PROC_FXO_INFO_FNAME, 0444, xpd->proc_xpd_dir, &proc_fxo_info_fops, xpd); +#endif if(!priv->fxo_info) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_FXO_INFO_FNAME); fxo_proc_remove(xbus, xpd); @@ -409,8 +445,14 @@ static int fxo_proc_create(xbus_t *xbus, SET_PROC_DIRENTRY_OWNER(priv->fxo_info); #ifdef WITH_METERING XPD_DBG(PROC, xpd, "Creating Metering tone file\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) priv->meteringfile = create_proc_read_entry(PROC_METERING_FNAME, 0444, xpd->proc_xpd_dir, proc_xpd_metering_read, xpd); +#else + priv->meteringfile = proc_create_data(PROC_METERING_FNAME, 0444, + xpd->proc_xpd_dir, + &proc_fxo_metering_fops, xpd); +#endif if(!priv->meteringfile) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_METERING_FNAME); fxo_proc_remove(xbus, xpd); @@ -1157,6 +1199,7 @@ static void fxo_packet_dump(const char * /*------------------------- DAA Handling --------------------------*/ #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int proc_fxo_info_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; @@ -1308,7 +1351,143 @@ static int proc_xpd_metering_read(char * priv->metering_count[i] = 0; return len; } +#else + +static int fxo_proc_read(struct seq_file *m, void *v) +{ + unsigned long flags; + xpd_t *xpd = v; + struct FXO_priv_data *priv; + int i; + + if(!xpd) + return -ENODEV; + spin_lock_irqsave(&xpd->lock, flags); + priv = xpd->priv; + BUG_ON(!priv); + seq_printf(m, "\t%-17s: ", "Channel"); + for_each_line(xpd, i) { + if(!IS_SET(PHONEDEV(xpd).digital_outputs, i) && !IS_SET(PHONEDEV(xpd).digital_inputs, i)) + seq_printf(m, "%4d ", i % 10); + } + seq_printf(m, "\nLeds:"); + seq_printf(m, "\n\t%-17s: ", "state"); + for_each_line(xpd, i) { + if(!IS_SET(PHONEDEV(xpd).digital_outputs, i) && !IS_SET(PHONEDEV(xpd).digital_inputs, i)) + seq_printf(m, " %d%d ", + IS_SET(priv->ledstate[LED_GREEN], i), + IS_SET(priv->ledstate[LED_RED], i)); + } + seq_printf(m, "\n\t%-17s: ", "blinking"); + for_each_line(xpd, i) { + if(!IS_SET(PHONEDEV(xpd).digital_outputs, i) && !IS_SET(PHONEDEV(xpd).digital_inputs, i)) + seq_printf(m, " %d%d ", + IS_BLINKING(priv,i,LED_GREEN), + IS_BLINKING(priv,i,LED_RED)); + } + seq_printf(m, "\nBattery-Data:"); + seq_printf(m, "\n\t%-17s: ", "voltage"); + for_each_line(xpd, i) { + seq_printf(m, "%4d ", priv->battery_voltage[i]); + } + seq_printf(m, "\n\t%-17s: ", "current"); + for_each_line(xpd, i) { + seq_printf(m, "%4d ", priv->battery_current[i]); + } + seq_printf(m, "\nBattery:"); + seq_printf(m, "\n\t%-17s: ", "on"); + for_each_line(xpd, i) { + char *bat; + + if(priv->battery[i] == BATTERY_ON) + bat = "+"; + else if(priv->battery[i] == BATTERY_OFF) + bat = "-"; + else + bat = "."; + seq_printf(m, "%4s ", bat); + } + seq_printf(m, "\n\t%-17s: ", "debounce"); + for_each_line(xpd, i) { + seq_printf(m, "%4d ", priv->nobattery_debounce[i]); + } + seq_printf(m, "\nPolarity-Reverse:"); + seq_printf(m, "\n\t%-17s: ", "polarity"); + for_each_line(xpd, i) { + char *polname; + + if(priv->polarity[i] == POL_POSITIVE) + polname = "+"; + else if(priv->polarity[i] == POL_NEGATIVE) + polname = "-"; + else + polname = "."; + seq_printf(m, "%4s ", polname); + } + seq_printf(m, "\n\t%-17s: ", "debounce"); + for_each_line(xpd, i) { + seq_printf(m, "%4d ", priv->polarity_debounce[i]); + } + seq_printf(m, "\nPower-Denial:"); + seq_printf(m, "\n\t%-17s: ", "power"); + for_each_line(xpd, i) { + char *curr; + + if(priv->power[i] == POWER_ON) + curr = "+"; + else if(priv->power[i] == POWER_OFF) + curr = "-"; + else + curr = "."; + seq_printf(m, "%4s ", curr); + } + seq_printf(m, "\n\t%-17s: ", "safezone"); + for_each_line(xpd, i) { + seq_printf(m, "%4d ", priv->power_denial_safezone[i]); + } + seq_printf(m, "\n\t%-17s: ", "delay"); + for_each_line(xpd, i) { + seq_printf(m, "%4d ", priv->power_denial_delay[i]); + } +#ifdef WITH_METERING + seq_printf(m, "\nMetering:"); + seq_printf(m, "\n\t%-17s: ", "count"); + for_each_line(xpd, i) { + seq_printf(m, "%4d ", priv->metering_count[i]); + } #endif + seq_printf(m, "\n"); + spin_unlock_irqrestore(&xpd->lock, flags); + return 0; +} + +#ifdef WITH_METERING +static int fxo_metering_read(struct seq_file *m, void *v) +{ + unsigned long flags; + xpd_t *xpd = v; + struct FXO_priv_data *priv; + int i; + + if(!xpd) + return -ENODEV; + priv = xpd->priv; + BUG_ON(!priv); + spin_lock_irqsave(&xpd->lock, flags); + seq_printf(m, "# Chan\tMeter (since last read)\n"); + for_each_line(xpd, i) { + seq_printf(m, "%d\t%d\n", + i, priv->metering_count[i]); + } + spin_unlock_irqrestore(&xpd->lock, flags); + /* Zero meters */ + for_each_line(xpd, i) + priv->metering_count[i] = 0; + return 0; +} +#endif /* WITH_METERING */ +#endif /* KERNEL_VERSION */ +#endif /* PROCFS */ static DEVICE_ATTR_READER(fxo_battery_show, dev, buf) { Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/card_fxs.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/xpp/card_fxs.c 2013-08-03 13:48:31.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/card_fxs.c 2013-08-05 11:58:05.539338756 +0100 @@ -108,11 +108,42 @@ enum fxs_state { static bool fxs_packet_is_valid(xpacket_t *pack); static void fxs_packet_dump(const char *msg, xpacket_t *pack); #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int proc_fxs_info_read(char *page, char **start, off_t off, int count, int *eof, void *data); #ifdef WITH_METERING static int proc_xpd_metering_write(struct file *file, const char __user *buffer, unsigned long count, void *data); #endif -#endif +#else +#include + +static int fxs_info_read(struct seq_file *m, void *v); + +static int fxs_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, fxs_info_read, PDE_DATA(inode)); +} +static const struct file_operations fxs_info_fops = { + .open = fxs_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#if 0 +static int fxs_metering_read(struct seq_file *m, void *v); + +static int fxs_metering_open(struct inode *inode, struct file *file) +{ + return single_open(file, fxs_metering_read, PDE_DATA(inode)); +} +static const struct file_operations fxs_metering_fops = { + .open = fxs_metering_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif /* WITH_METERING */ +#endif /* KERNEL_VERSION */ +#endif /* PROCFS */ static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos); #define PROC_REGISTER_FNAME "slics" @@ -354,7 +385,13 @@ static int fxs_proc_create(xbus_t *xbus, #ifdef CONFIG_PROC_FS XPD_DBG(PROC, xpd, "Creating FXS_INFO file\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) priv->fxs_info = create_proc_read_entry(PROC_FXS_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_fxs_info_read, xpd); +#else + priv->fxs_info = proc_create_data(PROC_FXS_INFO_FNAME, 0444, + xpd->proc_xpd_dir, &fxs_info_fops, + xpd); +#endif if(!priv->fxs_info) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_FXS_INFO_FNAME); fxs_proc_remove(xbus, xpd); @@ -1490,6 +1527,7 @@ static void fxs_packet_dump(const char * /*------------------------- SLIC Handling --------------------------*/ #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int proc_fxs_info_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; @@ -1594,6 +1632,103 @@ static int proc_xpd_metering_write(struc } return count; } +#else +static int fxs_info_read(struct seq_file *m, void *v) +{ + unsigned long flags; + xpd_t *xpd = v; + struct FXS_priv_data *priv; + int i; + int led; + + if(!xpd) + return -ENODEV; + spin_lock_irqsave(&xpd->lock, flags); + priv = xpd->priv; + BUG_ON(!priv); + seq_printf(m, "%-8s %-10s %-10s %-10s\n", + "Channel", + "idletxhookstate", + "lasttxhook", + "ohttimer" + ); + for_each_line(xpd, i) { + char pref; + + if(IS_SET(PHONEDEV(xpd).digital_outputs, i)) + pref = 'O'; + else if(IS_SET(PHONEDEV(xpd).digital_inputs, i)) + pref = 'I'; + else + pref = ' '; + seq_printf(m, "%c%7d %10d %10d %10d\n", + pref, + i, + priv->idletxhookstate[i], + priv->lasttxhook[i], + priv->ohttimer[i] + ); + } + seq_printf(m, "\n"); + for(led = 0; led < NUM_LEDS; led++) { + seq_printf(m, "LED #%d", led); + seq_printf(m, "\n\t%-17s: ", "ledstate"); + for_each_line(xpd, i) { + if(!IS_SET(PHONEDEV(xpd).digital_outputs, i) && !IS_SET(PHONEDEV(xpd).digital_inputs, i)) + seq_printf(m, "%d ", IS_SET(priv->ledstate[led], i)); + } + seq_printf(m, "\n\t%-17s: ", "ledcontrol"); + for_each_line(xpd, i) { + if(!IS_SET(PHONEDEV(xpd).digital_outputs, i) && !IS_SET(PHONEDEV(xpd).digital_inputs, i)) + seq_printf(m, "%d ", IS_SET(priv->ledcontrol[led], i)); + } + seq_printf(m, "\n\t%-17s: ", "led_counter"); + for_each_line(xpd, i) { + if(!IS_SET(PHONEDEV(xpd).digital_outputs, i) && !IS_SET(PHONEDEV(xpd).digital_inputs, i)) + seq_printf(m, "%d ", LED_COUNTER(priv,i,led)); + } + seq_printf(m, "\n"); + } + spin_unlock_irqrestore(&xpd->lock, flags); + return 0; +} +#ifdef WITH_METERING +//static int fxs_metering_read(struct seq_file *m, void *v) +static int proc_xpd_metering_write(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + xpd_t *xpd = data; + char buf[MAX_PROC_WRITE]; + lineno_t chan; + int num; + int ret; + + if(!xpd) + return -ENODEV; + if(count >= MAX_PROC_WRITE - 1) { + XPD_ERR(xpd, "Metering string too long (%lu)\n", count); + return -EINVAL; + } + if(copy_from_user(&buf, buffer, count)) + return -EFAULT; + buf[count] = '\0'; + ret = sscanf(buf, "%d", &num); + if(ret != 1) { + XPD_ERR(xpd, "Metering value should be number. Got '%s'\n", buf); + return -EINVAL; + } + chan = num; + if(chan != PORT_BROADCAST && chan > xpd->channels) { + XPD_ERR(xpd, "Metering tone: bad channel number %d\n", chan); + return -EINVAL; + } + if((ret = metering_gen(xpd, chan, 1)) < 0) { + XPD_ERR(xpd, "Failed sending metering tone\n"); + return ret; + } + return count; +} +#endif +#endif #endif static int fxs_xpd_probe(struct device *dev) Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/xbus-core.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/xpp/xbus-core.c 2011-07-20 17:49:53.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/xbus-core.c 2013-08-05 13:24:49.599120129 +0100 @@ -52,7 +52,18 @@ static const char rcsid[] = "$Id: xbus-c #ifdef PROTOCOL_DEBUG #ifdef CONFIG_PROC_FS #define PROC_XBUS_COMMAND "command" +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int proc_xbus_command_write(struct file *file, const char __user *buffer, unsigned long count, void *data); +#else +static ssize_t proc_xbus_command_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos); + +static const struct file_operations xbus_cmd_fops = { + .llseek = noop_llseek, + .write = proc_xbus_command_write, +}; +#endif #endif #endif @@ -63,7 +74,24 @@ static DEF_PARM(uint, poll_timeout, 1000 static DEF_PARM_BOOL(rx_tasklet, 0, 0644, "Use receive tasklets"); #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int xbus_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +#else +#include + +static int xbus_read_proc(struct seq_file *m, void *v); + +static int xbus_read_open(struct inode *inode, struct file *file) +{ + return single_open(file, xbus_read_proc, PDE_DATA(inode)); +} +static const struct file_operations xbus_read_fops = { + .open = xbus_read_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif #endif static void transport_init(xbus_t *xbus, struct xbus_ops *ops, ushort max_send_size, struct device *transport_device, void *priv); static void transport_destroy(xbus_t *xbus); @@ -1354,10 +1382,18 @@ xbus_t *xbus_new(struct xbus_ops *ops, u err = -EIO; goto nobus; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) xbus->proc_xbus_summary = create_proc_read_entry(PROC_XBUS_SUMMARY, 0444, xbus->proc_xbus_dir, xbus_read_proc, (void *)((unsigned long)(xbus->num))); +#else + xbus->proc_xbus_summary = proc_create_data(PROC_XBUS_SUMMARY, 0444, + xbus->proc_xbus_dir, + &xbus_read_fops, + (void *) + ((unsigned long)xbus->num)); +#endif if (!xbus->proc_xbus_summary) { XBUS_ERR(xbus, "Failed to create proc file '%s'\n", PROC_XBUS_SUMMARY); err = -EIO; @@ -1365,6 +1401,7 @@ xbus_t *xbus_new(struct xbus_ops *ops, u } SET_PROC_DIRENTRY_OWNER(xbus->proc_xbus_summary); #ifdef PROTOCOL_DEBUG +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) xbus->proc_xbus_command = create_proc_entry(PROC_XBUS_COMMAND, 0200, xbus->proc_xbus_dir); if (!xbus->proc_xbus_command) { XBUS_ERR(xbus, "Failed to create proc file '%s'\n", PROC_XBUS_COMMAND); @@ -1373,6 +1410,17 @@ xbus_t *xbus_new(struct xbus_ops *ops, u } xbus->proc_xbus_command->write_proc = proc_xbus_command_write; xbus->proc_xbus_command->data = xbus; +#else + xbus->proc_xbus_command = proc_create_data(PROC_XBUS_COMMAND, 0200, + xbus->proc_xbus_dir, + &xbus_cmd_fops, xbus); + if (!xbus->proc_xbus_command) { + XBUS_ERR(xbus, "Failed to create proc file '%s'\n", + PROC_XBUS_COMMAND); + err = -EIO; + goto nobus; + } +#endif SET_PROC_DIRENTRY_OWNER(xbus->proc_xbus_command); #endif #endif @@ -1484,6 +1532,7 @@ out: #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int xbus_fill_proc_queue(char *p, struct xframe_queue *q) { int len; @@ -1573,8 +1622,87 @@ out: return len; } +#else +static void xbus_fill_proc_queue(struct seq_file *m, struct xframe_queue *q) +{ + seq_printf(m, + "%-15s: counts %3d, %3d, %3d worst %3d, overflows %3d worst_lag %02ld.%ld ms\n", + q->name, + q->steady_state_count, + q->count, + q->max_count, + q->worst_count, + q->overflows, + q->worst_lag_usec / 1000, + q->worst_lag_usec % 1000); + xframe_queue_clearstats(q); +} + +static int xbus_read_proc(struct seq_file *m, void *v) +{ + xbus_t *xbus; + unsigned long flags; + int i = (int)((unsigned long)v); + + xbus = get_xbus(__func__, i); /* until end of xbus_read_proc */ + if(!xbus) + goto out; + spin_lock_irqsave(&xbus->lock, flags); + + seq_printf(m, "%s: CONNECTOR=%s LABEL=[%s] STATUS=%s\n", + xbus->busname, + xbus->connector, + xbus->label, + (XBUS_FLAGS(xbus, CONNECTED)) ? "connected" : "missing" + ); + xbus_fill_proc_queue(m, &xbus->send_pool); + xbus_fill_proc_queue(m, &xbus->receive_pool); + xbus_fill_proc_queue(m, &xbus->command_queue); + xbus_fill_proc_queue(m, &xbus->receive_queue); + xbus_fill_proc_queue(m, &xbus->pcm_tospan); + if(rx_tasklet) { + seq_printf(m, "\ncpu_rcv_intr: "); + for_each_online_cpu(i) + seq_printf(m, "%5d ", xbus->cpu_rcv_intr[i]); + seq_printf(m, "\ncpu_rcv_tasklet: "); + for_each_online_cpu(i) + seq_printf(m, "%5d ", xbus->cpu_rcv_tasklet[i]); + seq_printf(m, "\n"); + } + seq_printf(m, "self_ticking: %d (last_tick at %ld)\n", + xbus->self_ticking, xbus->ticker.last_sample.tv.tv_sec); + seq_printf(m, "command_tick: %d\n", xbus->command_tick_counter); + seq_printf(m, "usec_nosend: %d\n", xbus->usec_nosend); + seq_printf(m, "xbus: pcm_rx_counter = %d, frag = %d\n", + atomic_read(&xbus->pcm_rx_counter), xbus->xbus_frag_count); + seq_printf(m, "max_rx_process = %2ld.%ld ms\n", + xbus->max_rx_process / 1000, + xbus->max_rx_process % 1000); + xbus->max_rx_process = 0; + seq_printf(m, "\nTRANSPORT: max_send_size=%d refcount=%d\n", + MAX_SEND_SIZE(xbus), + atomic_read(&xbus->transport.transport_refcount) + ); + seq_printf(m, "PCM Metrices:\n"); + seq_printf(m, "\tPCM TX: min=%ld max=%ld\n", + xbus->min_tx_sync, xbus->max_tx_sync); + seq_printf(m, "\tPCM RX: min=%ld max=%ld\n", + xbus->min_rx_sync, xbus->max_rx_sync); + seq_printf(m, "COUNTERS:\n"); + for(i = 0; i < XBUS_COUNTER_MAX; i++) { + seq_printf(m, "\t%-15s = %d\n", + xbus_counters[i].name, xbus->counters[i]); + } + spin_unlock_irqrestore(&xbus->lock, flags); + put_xbus(__FUNCTION__, xbus); /* from xbus_read_proc() */ +out: + return 0; + +} +#endif #ifdef PROTOCOL_DEBUG +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int proc_xbus_command_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { char *buf; @@ -1651,9 +1779,90 @@ out: kfree(buf); return count; } +#else +static ssize_t proc_xbus_command_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + char *buf; + xbus_t *xbus = PDE_DATA(file_inode(file)); + char *p; + byte *pack_start; + byte *q; + xframe_t *xframe; + size_t len; + const size_t max_len = xbus->transport.max_send_size; + const size_t max_text = max_len * 3 + 10; + + if(count > max_text) { + XBUS_ERR(xbus, "%s: line too long (%ld > %zd)\n", __FUNCTION__, count, max_len); + return -EFBIG; + } + /* 3 bytes per hex-digit and space */ + buf = kmalloc(max_text, GFP_KERNEL); + if(!buf) + return -ENOMEM; + if(copy_from_user(buf, buffer, count)) { + count = -EINVAL; + goto out; + } + buf[count] = '\0'; + XBUS_DBG(GENERAL, xbus, "count=%ld\n", count); + /* + * We replace the content of buf[] from + * ascii representation to packet content + * as the binary representation is shorter + */ + q = pack_start = buf; + for(p = buf; *p;) { + int val; + char hexdigit[3]; + + while(*p && isspace(*p)) // skip whitespace + p++; + if(!(*p)) + break; + if(!isxdigit(*p)) { + XBUS_ERR(xbus, "%s: bad hex value ASCII='0x%X' at position %ld\n", + __FUNCTION__, *p, (long)(p - buf)); + count = -EINVAL; + goto out; + } + hexdigit[0] = *p++; + hexdigit[1] = '\0'; + hexdigit[2] = '\0'; + if(isxdigit(*p)) + hexdigit[1] = *p++; + if(sscanf(hexdigit, "%2X", &val) != 1) { + XBUS_ERR(xbus, "%s: bad hex value '%s' at position %ld\n", + __FUNCTION__, hexdigit, (long)(p - buf)); + count = -EINVAL; + goto out; + } + *q++ = val; + XBUS_DBG(GENERAL, xbus, "%3zd> '%s' val=%d\n", q - pack_start, hexdigit, val); + } + len = q - pack_start; + xframe = ALLOC_SEND_XFRAME(xbus); + if(!xframe) { + count = -ENOMEM; + goto out; + } + if(len > max_len) + len = max_len; + atomic_set(&xframe->frame_len, len); + memcpy(xframe->packets, pack_start, len); /* FIXME: checksum? */ + dump_xframe("COMMAND", xbus, xframe, debug); + send_cmd_frame(xbus, xframe); +out: + kfree(buf); + return count; +} +#endif #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int read_proc_xbuses(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; @@ -1686,6 +1895,39 @@ static int read_proc_xbuses(char *page, return len; } +#else +static int xbuses_read(struct seq_file *m, void *v) +{ + int i; + + for(i = 0; i < MAX_BUSES; i++) { + xbus_t *xbus = get_xbus(__func__, i); + + if(xbus) { + seq_printf(m, "%s: CONNECTOR=%s LABEL=[%s] STATUS=%s\n", + xbus->busname, + xbus->connector, + xbus->label, + (XBUS_FLAGS(xbus, CONNECTED)) ? + "connected" : "missing" + ); + put_xbus(__func__, xbus); + } + } + return 0; +} + +static int xbuses_read_open(struct inode *inode, struct file *file) +{ + return single_open(file, xbuses_read, PDE_DATA(inode)); +} +static const struct file_operations xbuses_fops = { + .open = xbuses_read_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif #endif static void transport_init(xbus_t *xbus, struct xbus_ops *ops, ushort max_send_size, struct device *transport_device, void *priv) @@ -1769,7 +2011,12 @@ int __init xbus_core_init(void) INFO("FEATURE: with PROTOCOL_DEBUG\n"); #endif #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) proc_xbuses = create_proc_read_entry(PROC_XBUSES, 0444, xpp_proc_toplevel, read_proc_xbuses, NULL); +#else + proc_xbuses = proc_create(PROC_XBUSES, 0444, xpp_proc_toplevel, + &xbuses_fops); +#endif if (!proc_xbuses) { ERR("Failed to create proc file %s\n", PROC_XBUSES); ret = -EFAULT; Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/xpd.h =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/xpp/xpd.h 2011-06-28 19:23:00.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/xpd.h 2013-08-05 10:06:03.675621148 +0100 @@ -31,6 +31,9 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) +#include +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) #include #else @@ -54,10 +57,17 @@ typedef unsigned gfp_t; /* Added in 2.6 * bool is now defined as a proper boolean type (gcc _Bool) * but the command line parsing framework handles it as int. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) #define DEF_PARM_BOOL(name,init,perm,desc) \ int name = init; \ module_param(name, bool, perm); \ MODULE_PARM_DESC(name, desc " [default " #init "]") +#else +#define DEF_PARM_BOOL(name,init,perm,desc) \ + bool name = init; \ + module_param(name, bool, perm); \ + MODULE_PARM_DESC(name, desc " [default " #init "]") +#endif #define DEF_PARM(type,name,init,perm,desc) \ type name = init; \ Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/xpp_dahdi.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/xpp/xpp_dahdi.c 2011-07-12 19:08:42.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/xpp_dahdi.c 2013-08-05 13:26:27.439116019 +0100 @@ -109,7 +109,25 @@ int total_registered_spans(void) } #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +#else +#include + +static int xpd_read_proc(struct seq_file *m, void *v); + +static int xpd_read_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, xpd_read_proc, PDE_DATA(inode)); +} + +static const struct file_operations xpd_read_proc_fops = { + .open = xpd_read_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif #endif /*------------------------- XPD Management -------------------------*/ @@ -174,8 +192,14 @@ static int xpd_proc_create(xbus_t *xbus, XPD_ERR(xpd, "Failed to create proc directory\n"); goto err; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) xpd->proc_xpd_summary = create_proc_read_entry(PROC_XPD_SUMMARY, 0444, xpd->proc_xpd_dir, xpd_read_proc, xpd); +#else + xpd->proc_xpd_summary = proc_create_data(PROC_XPD_SUMMARY, 0444, + xpd->proc_xpd_dir, + &xpd_read_proc_fops, xpd); +#endif if(!xpd->proc_xpd_summary) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_XPD_SUMMARY); goto err; @@ -261,7 +285,7 @@ void xpd_post_init(xpd_t *xpd) } #ifdef CONFIG_PROC_FS - +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) /** * Prints a general procfs entry for the bus, under xpp/BUSNAME/summary * @page TODO: figure out procfs @@ -392,7 +416,108 @@ out: return len; } +#else +static int xpd_read_proc(struct seq_file *m, void *v) +{ + xpd_t *xpd = v; + int i; + if(!xpd) + goto out; + + seq_printf(m, "%s (%s, card %s, span %d)\n" + "timing_priority: %d\n" + "timer_count: %d span->mainttimer=%d\n" + , + xpd->xpdname, xpd->type_name, + (xpd->card_present) ? "present" : "missing", + (SPAN_REGISTERED(xpd)) ? PHONEDEV(xpd).span.spanno : 0, + PHONEDEV(xpd).timing_priority, + xpd->timer_count, PHONEDEV(xpd).span.mainttimer + ); + seq_printf(m, "xpd_state: %s (%d)\n", + xpd_statename(xpd->xpd_state), xpd->xpd_state); + seq_printf(m, "open_counter=%d refcount=%d\n", + atomic_read(&PHONEDEV(xpd).open_counter), refcount_xpd(xpd)); + seq_printf(m, "Address: U=%d S=%d\n", xpd->addr.unit, xpd->addr.subunit); + seq_printf(m, "Subunits: %d\n", xpd->subunits); + seq_printf(m, "Type: %d.%d\n\n", xpd->type, xpd->subtype); + seq_printf(m, "pcm_len=%d\n\n", PHONEDEV(xpd).pcm_len); + seq_printf(m, "wanted_pcm_mask=0x%04X\n\n", PHONEDEV(xpd).wanted_pcm_mask); + seq_printf(m, "mute_dtmf=0x%04X\n\n", PHONEDEV(xpd).mute_dtmf); + seq_printf(m, "STATES:"); + seq_printf(m, "\n\t%-17s: ", "output_relays"); + for_each_line(xpd, i) { + seq_printf(m, "%d ", IS_SET(PHONEDEV(xpd).digital_outputs, i)); + } + seq_printf(m, "\n\t%-17s: ", "input_relays"); + for_each_line(xpd, i) { + seq_printf(m, "%d ", IS_SET(PHONEDEV(xpd).digital_inputs, i)); + } + seq_printf(m, "\n\t%-17s: ", "offhook"); + for_each_line(xpd, i) { + seq_printf(m, "%d ", IS_OFFHOOK(xpd, i)); + } + seq_printf(m, "\n\t%-17s: ", "oht_pcm_pass"); + for_each_line(xpd, i) { + seq_printf(m, "%d ", IS_SET(PHONEDEV(xpd).oht_pcm_pass, i)); + } + seq_printf(m, "\n\t%-17s: ", "msg_waiting"); + for_each_line(xpd, i) { + seq_printf(m, "%d ", PHONEDEV(xpd).msg_waiting[i]); + } + seq_printf(m, "\n\t%-17s: ", "ringing"); + for_each_line(xpd, i) { + seq_printf(m, "%d ", PHONEDEV(xpd).ringing[i]); + } + seq_printf(m, "\n\t%-17s: ", "no_pcm"); + for_each_line(xpd, i) { + seq_printf(m, "%d ", IS_SET(PHONEDEV(xpd).no_pcm, i)); + } + + if(SPAN_REGISTERED(xpd)) { + seq_printf(m, "\nPCM:\n | [readchunk] | [writechunk] | W D"); + for_each_line(xpd, i) { + struct dahdi_chan *chan = XPD_CHAN(xpd, i); + byte rchunk[DAHDI_CHUNKSIZE]; + byte wchunk[DAHDI_CHUNKSIZE]; + byte *rp; + byte *wp; + int j; + + if(IS_SET(PHONEDEV(xpd).digital_outputs, i)) + continue; + if(IS_SET(PHONEDEV(xpd).digital_inputs, i)) + continue; + if(IS_SET(PHONEDEV(xpd).digital_signalling, i)) + continue; + rp = chan->readchunk; + wp = chan->writechunk; + memcpy(rchunk, rp, DAHDI_CHUNKSIZE); + memcpy(wchunk, wp, DAHDI_CHUNKSIZE); + seq_printf(m, "\n port %2d> | ", i); + for(j = 0; j < DAHDI_CHUNKSIZE; j++) { + seq_printf(m, "%02X ", rchunk[j]); + } + seq_printf(m, " | "); + for(j = 0; j < DAHDI_CHUNKSIZE; j++) { + seq_printf(m, "%02X ", wchunk[j]); + } + seq_printf(m, " | %c", + (IS_SET(PHONEDEV(xpd).wanted_pcm_mask, i))?'+':' '); + seq_printf(m, " %c", + (IS_SET(PHONEDEV(xpd).mute_dtmf, i))?'-':' '); + } + } + seq_printf(m, "\nCOUNTERS:\n"); + for(i = 0; i < XPD_COUNTER_MAX; i++) { + seq_printf(m, "\t\t%-20s = %d\n", + xpd_counters[i].name, xpd->counters[i]); + } +out: + return 0; +} +#endif #endif const char *xpd_statename(enum xpd_state st) Index: dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/xpp_usb.c =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/drivers/dahdi/xpp/xpp_usb.c 2011-06-02 21:00:36.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/drivers/dahdi/xpp/xpp_usb.c 2013-08-05 13:36:42.635090174 +0100 @@ -266,7 +266,23 @@ static void xpp_receive_callback(USB_PAS static int xusb_probe (struct usb_interface *interface, const struct usb_device_id *id); static void xusb_disconnect (struct usb_interface *interface); #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int xusb_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +#else +#include + +static int xusb_read_proc(struct seq_file *m, void *v); +static int xusb_read_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, xusb_read_proc, PDE_DATA(inode)); +} +static const struct file_operations xusb_read_proc_fops = { + .open = xusb_read_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif #endif /*------------------------------------------------------------------*/ @@ -758,8 +774,14 @@ static int xusb_probe(struct usb_interfa #ifdef CONFIG_PROC_FS DBG(PROC, "Creating proc entry " PROC_USBXPP_SUMMARY " in bus proc dir.\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) procsummary = create_proc_read_entry(PROC_USBXPP_SUMMARY, 0444, xbus->proc_xbus_dir, xusb_read_proc, xusb); +#else + procsummary = proc_create_data(PROC_USBXPP_SUMMARY, 0444, + xbus->proc_xbus_dir, + &xusb_read_proc_fops, xusb); +#endif if (!procsummary) { XBUS_ERR(xbus, "Failed to create proc file '%s'\n", PROC_USBXPP_SUMMARY); // FIXME: better error handling @@ -1022,7 +1044,7 @@ static void __exit xpp_usb_shutdown(void #ifdef CONFIG_PROC_FS - +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static int xusb_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; @@ -1094,7 +1116,66 @@ out: return len; } +#else +static int xusb_read_proc(struct seq_file *m, void *v) +{ + unsigned long flags; + int i; + //unsigned long stamp = jiffies; + xusb_t *xusb = v; + uint usb_tx_delay[NUM_BUCKETS]; + const int mark_limit = tx_sluggish/USEC_BUCKET; + if(!xusb) + goto out; + // TODO: probably needs a per-xusb lock: + spin_lock_irqsave(&xusb_lock, flags); + seq_printf(m, "Device: %03d/%03d\n", + xusb->udev->bus->busnum, + xusb->udev->devnum + ); + seq_printf(m, "USB: manufacturer=%s\n", xusb->manufacturer); + seq_printf(m, "USB: product=%s\n", xusb->product); + seq_printf(m, "USB: serial=%s\n", xusb->serial); + seq_printf(m, "Minor: %d\nModel Info: %s\n", + xusb->minor, xusb->model_info->desc); + seq_printf(m, "Endpoints:\n" + "\tIn: 0x%02X - Size: %d)\n" + "\tOut: 0x%02X - Size: %d)\n", + xusb->endpoints[XUSB_RECV].ep_addr, + xusb->endpoints[XUSB_RECV].max_size, + xusb->endpoints[XUSB_SEND].ep_addr, + xusb->endpoints[XUSB_SEND].max_size + ); + seq_printf(m, "\npending_writes=%d\n", atomic_read(&xusb->pending_writes)); + seq_printf(m, "pending_reads=%d\n", atomic_read(&xusb->pending_reads)); + seq_printf(m, "max_tx_delay=%d\n", xusb->max_tx_delay); + xusb->max_tx_delay = 0; +#ifdef DEBUG_PCM_TIMING + seq_printf(m, "\nstamp_last_pcm_read=%lld accumulate_diff=%lld\n", stamp_last_pcm_read, accumulate_diff); +#endif + memcpy(usb_tx_delay, xusb->usb_tx_delay, sizeof(usb_tx_delay)); + seq_printf(m, "usb_tx_delay[%d,%d,%d]: ", + USEC_BUCKET, BUCKET_START, NUM_BUCKETS); + for(i = BUCKET_START; i < NUM_BUCKETS; i++) { + seq_printf(m, "%6d ", + usb_tx_delay[i]); + if(i == mark_limit) + seq_printf(m, "| "); + } + seq_printf(m, "\nPCM_TX_DROPS: %5d (sluggish: %d)\n", + atomic_read(&xusb->pcm_tx_drops), + atomic_read(&xusb->usb_sluggish_count) + ); + seq_printf(m, "\nCOUNTERS:\n"); + for(i = 0; i < XUSB_COUNTER_MAX; i++) { + seq_printf(m, "\t%-15s = %d\n", xusb_counters[i].name, xusb->counters[i]); + } + spin_unlock_irqrestore(&xusb_lock, flags); +out: + return 0; +} +#endif #endif Index: dahdi-linux-2.5.0.1+dfsg/include/dahdi/kernel.h =================================================================== --- dahdi-linux-2.5.0.1+dfsg.orig/include/dahdi/kernel.h 2013-08-03 13:48:31.000000000 +0100 +++ dahdi-linux-2.5.0.1+dfsg/include/dahdi/kernel.h 2013-08-03 15:49:22.621126628 +0100 @@ -58,6 +58,13 @@ #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0) +#define __devinit +#define __devinitdata +#define __devexit +#define __devexit_p +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) #define dahdi_pci_module pci_register_driver #else debian/patches/no_firmware_download0000664000000000000000000000141011652357765014756 0ustar The firmware downloaders are extra kernel objects that are now required for the Hardware echo canceller support in some Digium cards (wctdm24xxp, wcte12xp). They are downloaded at build time. The makefile will build with support for them if they were indeed downloaded. This patch removes this downloading and thus keeps those modules DFSG-compliant. Unlike the Digium firmwares, this is a compile-time decision and hence cannot be reverted once a dahdi-modules package is built. --- a/Makefile +++ b/Makefile @@ -88,7 +88,7 @@ include/dahdi/version.h: FORCE fi @rm -f $@.tmp -prereq: include/dahdi/version.h firmware-loaders +prereq: include/dahdi/version.h stackcheck: $(CHECKSTACK) modules objdump -d drivers/dahdi/*.ko drivers/dahdi/*/*.ko | $(CHECKSTACK) debian/patches/oslec_zaptap0000775000000000000000000001503511652357765013256 0ustar ## oslec_zaptap by Tzafrir Cohen ## ## The zaptap device for sampling echo. Part of the oslec echo canceller. diff -urNad zaptel-1.2.17.1.xpp.r3965~/zaptel-base.c zaptel-1.2.17.1.xpp.r3965/zaptel-base.c --- zaptel-1.2.17.1.xpp.r3965~/kernel/zaptel-base.c 2007-06-16 07:10:17.000000000 +0300 +++ zaptel-1.2.17.1.xpp.r3965/kernel/zaptel-base.c 2007-06-16 07:55:33.000000000 +0300 @@ -5708,6 +5708,74 @@ spin_unlock_irqrestore(&chan->lock, flags); } +#ifdef USE_ZAPTAP +/* Zaptap code -----------------------------------------------------------*/ + +#define SAMPLE_BUF_SZ 1000 +#define SAMPLE_IDLE 0 +#define SAMPLE_PING 1 +#define SAMPLE_PONG 2 + +DECLARE_WAIT_QUEUE_HEAD(sample_wait); +static int sample_state = 0; +static int samples = 0; +static short *psample; +static short ping[3*SAMPLE_BUF_SZ]; +static short pong[3*SAMPLE_BUF_SZ]; +static int sample_ch = 1; +static int sample_impulse = 0; +static int tmp1,tmp2; + +static inline void sample_echo_before(int channo, short rxlin, short txlin) { + /* Sample echo canceller signals + * Notes: + * 1. Samples are multiplexed in buffer: + * tx sample + * rx sample + * ec sample + * 2. We needs to sample rx here before echo can as it is + * overwritten. + */ + tmp1++; + tmp2 = channo; + if ((sample_state != SAMPLE_IDLE) && (channo == sample_ch)) { + *psample++ = txlin; + *psample++ = rxlin; + } +} + +static inline void sample_echo_after(int channo, short rxlin) { + + if ((sample_state != SAMPLE_IDLE) && (channo == sample_ch)) { + + *psample++ = rxlin; + + /* sample collection ping-pong buffer logic */ + + samples++; + if (samples >= SAMPLE_BUF_SZ) { + /* time to swap buffers */ + samples = 0; + + if (sample_state == SAMPLE_PING) { + sample_state = SAMPLE_PONG; + psample = pong; + } + else { + sample_state = SAMPLE_PING; + psample = ping; + } + wake_up_interruptible(&sample_wait); + } + } +} + +/* end Zaptap code -----------------------------------------------------*/ +#else /* USE_ZAPTAP */ +#define sample_echo_before(a,b,c) +#define sample_echo_after(a,b) +#endif /* USE_ZAPTAP */ + static inline void __zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk) { short rxlin, txlin; @@ -5758,7 +5826,9 @@ #if !defined(ZT_EC_ARRAY_UPDATE) for (x=0;xchanno, rxlin, ZT_XLAW(txchunk[x], ss)); /* Zaptap code */ rxlin = echo_can_update(ss->ec, ZT_XLAW(txchunk[x], ss), rxlin); + sample_echo_after(ss->channo, rxlin); /* Zaptap code */ rxchunk[x] = ZT_LIN2X((int)rxlin, ss); } #else /* defined(ZT_EC_ARRAY_UPDATE) */ @@ -6505,6 +6575,10 @@ static void __zt_transmit_chunk(struct zt_chan *chan, unsigned char *buf) { unsigned char silly[ZT_CHUNKSIZE]; +#ifdef USE_ZAPTAP + int x; +#endif + /* Called with chan->lock locked */ if (!buf) buf = silly; @@ -6519,6 +6593,22 @@ kernel_fpu_end(); #endif } + +#ifdef USE_ZAPTAP + /* Start Zaptap code -----------------------------------------*/ + if (sample_impulse && (samples == 0)) { + + /* option impulse insertion, tx stream becomes one */ + /* impulse followed by SAMPLE_BUF_SZ-1 0's */ + + buf[0] = ZT_LIN2MU(10000); + for (x=1;x Forwarded: no Enable the OPTIMIZE_CHANMUTE code: don't waste CPU time on channels whose audio is not useful. Currently only used by xpp drivers, and disabled by default by upstream. --- a/include/dahdi/dahdi_config.h +++ b/include/dahdi/dahdi_config.h @@ -176,7 +176,7 @@ /* * Skip processing PCM if low-level driver won't use it anyway */ -/* #define OPTIMIZE_CHANMUTE */ +#define OPTIMIZE_CHANMUTE /* debian/watch0000664000000000000000000000027211652357765010243 0ustar version=3 opts=uversionmangle=s/\-rc/~rc/,dversionmangle=s/\+dfsg//; \ http://downloads.asterisk.org/pub/telephony/dahdi-linux/releases/dahdi-linux-(.*)\.tar\.gz\ debian svn-upgrade debian/compat0000664000000000000000000000000211652357765010407 0ustar 7 debian/copyright0000664000000000000000000001467611652357765011162 0ustar This package was debianized by Matt Zimmerman on Mon, 17 Jun 2002 10:31:21 -0400. It was downloaded from http://downloads.asterisk.org/pub/telephony/dahdi-linux/ The DAHDI-extra patch was generated from the repository: http://git.tzafrir.org.il/?p=dahdi-extra.git;a=summary http://git.tzafrir.org.il/git/dahdi-extra.git DAHDI-extra includes the following: * drivers/staging/echo: echo.ko (OSLEC) * drivers/dahdi/opvxa1200.c: opvxa1200.ko (OpenVox A800P/A1200P) * drivers/dahdi/wcopenpci.c: wcopenpci.ko (Voicetronix OpenPCI) * drivers/dahdi/zaphfc/: zaphfc.c (HFC-S-based PCI cards) Upstream source has been modified to comply with the Debian Free Software Guildlines (DFSG), by the removal of the firmware files: drivers/dahdi/xpp/firmwares/*.hex drivers/dahdi/xpp/firmwares/LICENSE.firmware permits redistribution but does not mention modification, which is a requirement of Debian Policy 2.1 ("Derived works"). Summary: The bulk of the code, under drivers/dahdi/ , is licensed under the terms of GPLv2. Various drivers (e.g.: all non-Digium drivers) are licensed as GPLv2.+ . Two other files (mmx.h, XppConfig.pm) are licensed under a more permissive license. Upstream Authors: Jim Dixon / Zapate Telephony, Linux Support Services, Inc. Copyright (from drivers/dahdi/tor2.c): /* * Tormenta 2 Quad-T1 PCI Driver * * Written by Mark Spencer * Based on previous works, designs, and archetectures conceived and * written by Jim Dixon . * * Copyright (C) 2001 Jim Dixon / Zapata Telephony. * Copyright (C) 2001-2008, Digium, Inc. * */ /* * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2 as published by the * Free Software Foundation. See the LICENSE file included with * this program for more details. */ The file LICENSE is a verbatim copy of the GNU General Public License, v.2. On Debian systems, a copy of the GNU General Public License v.2 may be found in /usr/share/common-licenses/GPL-2. Other Portions: drivers/dahdi/octapi: GPLv.2+ Copyright (c) 2001-2007 Octasic Inc. This file is part of the Octasic OCT6100 GPL API . The OCT6100 GPL API 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. The OCT6100 GPL API 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 the OCT6100 GPL API; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. drivers/dahdi/datamods/syncppp.c: GPLv.2+ * 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. * * Port for Linux-2.1 by Jan "Yenya" Kasprzak * Copyright (C) 1994 Cronyx Ltd. * Author: Serge Vakulenko, drivers/dahdi/wcopenpci.c: GPLv.2+ * Written by Mark Spencer * Matthew Fredrickson * Ben Kramer * Ron Lee * * Copyright (C) 2001, Linux Support Services, Inc. * Copyright (C) 2005 - 2007, Voicetronix * * 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. drivers/dahdi/opvxa1200.c: GPLv.2+ * Modify from wctdm.c by MiaoLin * * 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. drivers/dahdi/zaphfc/*: GPLv.2 * Dahdi rewrite in hardhdlc mode * Jose A. Deniz * * Copyright (C) 2009, Jose A. Deniz * Copyright (C) 2006, headiisue GmbH; Jens Wilke * Copyright (C) 2004 Daniele Orlandi * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH * * Jens Wilke * * Original author of this code is * Daniele "Vihai" Orlandi * * Major rewrite of the driver made by * Klaus-Peter Junghanns * * This program is free software and may be modified and * distributed under the terms of the GNU Public License. drivers/dahdi/xpp/XppConfig.pm: GPL 1+ || Artistic # Written by Oron Peled # Copyright (C) 2008, Xorcom # This program is free software; you can redistribute and/or # modify it under the same terms as Perl itself. drivers/staging/echo/mmx.h: * Copyright (C) 1997-2001 H. Dietz and R. Fisher * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg 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 * Lesser General Public License for more details. Other copyright holders: drivers/dahdi/dahdi_dummy.c: * Copyright (C) 2002, Hermes Softlab drivers/dahdi/dahdi_echocan_sec2.c: Copyright (C) 2001 Steve Underwood drivers/dahdi/dahdi_echocan_sec.c: Copyright (C) 2001 Steve Underwood drivers/dahdi/ecdis.h: Copyright (C) 2001 Steve Underwood drivers/dahdi/datamods/hdlc_*.c: * Copyright (C) 1999 - 2005 Krzysztof Halasa Other files under drivers/staging/echo/: Copyright (C) 2001, 2003 Steve Underwood, 2007,2008 David Rowe debian/dahdi-dkms.postinst0000664000000000000000000000266311652357765013032 0ustar #!/bin/sh NAME=dahdi PACKAGE_NAME=$NAME-dkms CVERSION=`dpkg-query -W -f='${Version}' $PACKAGE_NAME | cut -d\: -f2` ARCH=`dpkg --print-architecture` case $ARCH in amd64) ARCH="x86_64" ;; lpia) ARCH="i686" ;; i386) ARCH="i686" ;; armel) ;; *) echo "WARNING: potentially unsupported arch: $ARCH" ARCH="$ARCH" ;; esac case "$1" in configure) for POSTINST in /usr/lib/dkms/common.postinst /usr/share/$PACKAGE_NAME/postinst; do if [ -f $POSTINST ]; then $POSTINST $NAME $CVERSION /usr/share/$PACKAGE_NAME $ARCH $2 exit $? fi echo "WARNING: $POSTINST does not exist." done echo "ERROR: DKMS version is too old and $PACKAGE_NAME was not" echo "built with legacy DKMS support." echo "You must either rebuild $PACKAGE_NAME with legacy postinst" echo "support or upgrade DKMS to a more current version." exit 1 ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# debian/lintian/0000775000000000000000000000000011652361351010631 5ustar debian/lintian/dahdi-source0000664000000000000000000000013011652357765013133 0ustar # Complete phrase: m-a a-i dahdi dahdi-source: description-contains-duplicated-word a a debian/system.conf0000664000000000000000000002666011652357765011416 0ustar # # DAHDI Configuration File # # This file is parsed by the DAHDI Configurator, dahdi_cfg # # Span Configuration # ^^^^^^^^^^^^^^^^^^ # First come the span definitions, in the format # # span=,,,,[,yellow] # # All T1/E1/BRI spans generate a clock signal on their transmit side. The # parameter determines whether the clock signal from the far # end of the T1/E1/BRI is used as the master source of clock timing. If it is, our # own clock will synchronise to it. T1/E1/BRI connected directly or indirectly to # a PSTN provider (telco) should generally be the first choice to sync to. The # PSTN will never be a slave to you. You must be a slave to it. # # Choose 1 to make the equipment at the far end of the E1/T1/BRI link the preferred # source of the master clock. Choose 2 to make it the second choice for the master # clock, if the first choice port fails (the far end dies, a cable breaks, or # whatever). Choose 3 to make a port the third choice, and so on. If you have, say, # 2 ports connected to the PSTN, mark those as 1 and 2. The number used for each # port should be different. # # If you choose 0, the port will never be used as a source of timing. This is # appropriate when you know the far end should always be a slave to you. If # the port is connected to a channel bank, for example, you should always be # its master. Likewise, BRI TE ports should always be configured as a slave. # Any number of ports can be marked as 0. # # Incorrect timing sync may cause clicks/noise in the audio, poor quality or failed # faxes, unreliable modem operation, and is a general all round bad thing. # # The line build-out (or LBO) is an integer, from the following table: # # 0: 0 db (CSU) / 0-133 feet (DSX-1) # 1: 133-266 feet (DSX-1) # 2: 266-399 feet (DSX-1) # 3: 399-533 feet (DSX-1) # 4: 533-655 feet (DSX-1) # 5: -7.5db (CSU) # 6: -15db (CSU) # 7: -22.5db (CSU) # # If the span is a BRI port the line build-out is not used and should be set # to 0. # # framing:: # one of 'd4' or 'esf' for T1 or 'cas' or 'ccs' for E1. Use 'ccs' for BRI. # 'd4' could be referred to as 'sf' or 'superframe' # # coding:: # one of 'ami' or 'b8zs' for T1 or 'ami' or 'hdb3' for E1. Use 'ami' for # BRI. # # * For E1 there is the optional keyword 'crc4' to enable CRC4 checking. # * If the keyword 'yellow' follows, yellow alarm is transmitted when no # channels are open. # #span=1,0,0,esf,b8zs #span=2,1,0,esf,b8zs #span=3,0,0,ccs,hdb3,crc4 # # Dynamic Spans # ^^^^^^^^^^^^^ # Next come the dynamic span definitions, in the form: # # dynamic=,
,, # # Where is the name of the driver (e.g. eth),
is the # driver specific address (like a MAC for eth), is the number # of channels, and is a timing priority, like for a normal span. # use "0" to not use this as a timing source, or prioritize them as # primary, secondard, etc. Note that you MUST have a REAL DAHDI device # if you are not using external timing. # # dynamic=eth,eth0/00:02:b3:35:43:9c,24,0 # # If a non-zero timing value is used, as above, only the last span should # have the non-zero value. # # Channel Configuration # ^^^^^^^^^^^^^^^^^^^^^ # Next come the definitions for using the channels. The format is: # = # # Valid devices are: # # e&m:: # Channel(s) are signalled using E&M signalling (specific # implementation, such as Immediate, Wink, or Feature Group D # are handled by the userspace library). # fxsls:: # Channel(s) are signalled using FXS Loopstart protocol. # fxsgs:: # Channel(s) are signalled using FXS Groundstart protocol. # fxsks:: # Channel(s) are signalled using FXS Koolstart protocol. # fxols:: # Channel(s) are signalled using FXO Loopstart protocol. # fxogs:: # Channel(s) are signalled using FXO Groundstart protocol. # fxoks:: # Channel(s) are signalled using FXO Koolstart protocol. # sf:: # Channel(s) are signalled using in-band single freq tone. # Syntax as follows: # # channel# => sf:,,,,, # # rxfreq is rx tone freq in Hz, rxbw is rx notch (and decode) # bandwith in hz (typically 10.0), rxflag is either 'normal' or # 'inverted', txfreq is tx tone freq in hz, txlevel is tx tone # level in dbm, txflag is either 'normal' or 'inverted'. Set # rxfreq or txfreq to 0.0 if that tone is not desired. # # unused:: # No signalling is performed, each channel in the list remains idle # clear:: # Channel(s) are bundled into a single span. No conversion or # signalling is performed, and raw data is available on the master. # bchan:: # Like 'clear' except all channels are treated individually and # are not bundled. 'inclear' is an alias for this. # rawhdlc:: # The DAHDI driver performs HDLC encoding and decoding on the # bundle, and the resulting data is communicated via the master # device. # dchan:: # The DAHDI driver performs HDLC encoding and decoding on the # bundle and also performs incoming and outgoing FCS insertion # and verification. 'fcshdlc' is an alias for this. # hardhdlc:: # The hardware driver performs HDLC encoding and decoding on the # bundle and also performs incoming and outgoing FCS insertion # and verification. Is subject to limitations and support of underlying # hardware. BRI spans serviced by the wcb4xxp driver must use hardhdlc # channels for the signalling channels. # nethdlc:: # The DAHDI driver bundles the channels together into an # hdlc network device, which in turn can be configured with # sethdlc (available separately). In 2.6.x kernels you can also optionally # pass the name for the network interface after the channel list. # Syntax: # # nethdlc=[:interface name] # Use original names, don't use the names which have been already registered # in system e.g eth. # # dacs:: # The DAHDI driver cross connects the channels starting at # the channel number listed at the end, after a colon # dacsrbs:: # The DAHDI driver cross connects the channels starting at # the channel number listed at the end, after a colon and # also performs the DACSing of RBS bits # # The channel list is a comma-separated list of channels or ranges, for # example: # # 1,3,5 (channels one, three, and five) # 16-23, 29 (channels 16 through 23, as well as channel 29) # # So, some complete examples are: # # e&m=1-12 # nethdlc=13-24 # fxsls=25,26,27,28 # fxols=29-32 # # An example of BRI port: # # span=1,1,0,ccs,ami # bchan=1,2 # hardhdlc=3 # # NOTE: When using BRI channels in asterisk, use the bri_cpe, bri_net, or # bri_cpe_ptmp (for point to multipoint mode). libpri does not currently # support point to multipoint when in NT mode. Otherwise, the bearer channel # are configured identically to other DAHDI channels. # #fxoks=1-24 #bchan=25-47 #dchan=48 #fxols=1-12 #fxols=13-24 #e&m=25-29 #nethdlc=30-33 #clear=44 #clear=45 #clear=46 #clear=47 #fcshdlc=48 #dacs=1-24:48 #dacsrbs=1-24:48 # # Tone Zone Data # ^^^^^^^^^^^^^^ # Finally, you can preload some tone zones, to prevent them from getting # overwritten by other users (if you allow non-root users to open /dev/dahdi/* # interfaces anyway. Also this means they won't have to be loaded at runtime. # The format is "loadzone=" where the zone is a two letter country code. # # You may also specify a default zone with "defaultzone=" where zone # is a two letter country code. # # An up-to-date list of the zones can be found in the file zonedata.c # loadzone = us #loadzone = us-old #loadzone=gr #loadzone=it #loadzone=fr #loadzone=de #loadzone=uk #loadzone=fi #loadzone=jp #loadzone=sp #loadzone=no #loadzone=hu #loadzone=lt #loadzone=pl defaultzone=us # # PCI Radio Interface # ^^^^^^^^^^^^^^^^^^^ # (see http://www.zapatatelephony.org/app_rpt.html) # # The PCI Radio Interface card interfaces up to 4 two-way radios (either # a base/mobile radio or repeater system) to DAHDI channels. The driver # may work either independent of an application, or with it, through # the driver;s ioctl() interface. This file gives you access to specify # load-time parameters for Radio channels, so that the driver may run # by itself, and just act like a generic DAHDI radio interface. # # Unlike the rest of this file, you specify a block of parameters, and # then the channel(s) to which they apply. CTCSS is specified as a frequency # in tenths of hertz, for example 131.8 HZ is specified as 1318. DCS # for receive is specified as the code directly, for example 223. DCS for # transmit is specified as D and then the code, for example D223. # # The hardware supports a "community" CTCSS decoder system that has # arbitrary transmit CTCSS or DCS codes associated with them, unlike # traditional "community" systems that encode the same tone they decode. # # this example is a single tone DCS transmit and receive # # specify the transmit tone (in DCS mode this stays constant): #tx=D371 # # specify the receive DCS code: #dcsrx=223 # # this example is a "community" CTCSS (if you only want a single tone, then # only specify 1 in the ctcss list) # # specify the default transmit tone (when not receiving): #tx=1000 # # Specify the receive freq, the tag (use 0 if none), and the transmit code. # The tag may be used by applications to determine classification of tones. # The tones are to be specified in order of presedence, most important first. # Currently, 15 tones may be specified.. # #ctcss=1318,1,1318 #ctcss=1862,1,1862 # # The following parameters may be omitted if their default value is acceptible # # Set the receive debounce time in milliseconds: #debouncetime=123 # # set the transmit quiet dropoff burst time in milliseconds: #bursttime=234 # # set the COR level threshold (specified in tenths of millivolts) # valid values are {3125,6250,9375,12500,15625,18750,21875,25000} #corthresh=12500 # # Invert COR signal {y,n} #invertcor=y # Set the external tone mode; yes, no, internal {y,n,i} #exttone=y # # Now apply the configuration to the specified channels: # # We are all done with our channel parameters, so now we specify what # channels they apply to #channels=1-4 # # Overiding PCM encoding # ^^^^^^^^^^^^^^^^^^^^^^ # Usually the channel driver sets the encoding of the PCM for the # channel (mulaw / alaw. That is: g711u or g711a). However there are # some cases where you would like to override that. 'mulaw' and 'alaw' # set different such encoding. Use them for channels you have already # defined with e.g. 'bchan' or 'fxoks'. #mulaw=1-4 #alaw=1-4 # # 'deflaw' is similar, but resets the encoding to the channel driver's # default. It must be useful for something, I guess. #mulaw=1-10 #deflaw=5 # # Echo Cancellers # ^^^^^^^^^^^^^^^ # DAHDI uses modular echo cancellers that are configured per channel. The echo # cancellers are compiled and installed as part of the dahdi-linux package. # You can specify in this file the echo canceller to be used for each # channel. The default behavior is for there to be NO echo canceller on any # channel, so it is very important that you specify one here if you do # not have hardware echo cancellers and need echo cancellation. # # Valid echo cancellers are: mg2, kb1, sec2, and sec. # If compiled, 'hpec' is also a valid echo canceller. # # To configure the default echo cancellers, use the format: # echocanceller=, # # Example: # Configure channels 1 through 8 to use the mg2 echo canceller #echocanceller=mg2,1-8 # # And change channel 2 to use the kb1 echo canceller. #echocanceller=kb1,2 # debian/rules0000775000000000000000000001060211652360160010247 0ustar #!/usr/bin/make -f # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 export HOTPLUG_FIRMWARE=1 ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) CFLAGS += -g endif ## MODULE-ASSISTANT STUFF # prefix of the target package name PREFIX:=dahdi SKPG:=$(PREFIX)-source PACKAGE:=$(PREFIX)-modules PACKAGE_SRC:=$(PREFIX)-linux # modifieable for experiments or debugging m-a MA_DIR ?= /usr/share/modass # load generic variable handling -include $(MA_DIR)/include/generic.make # load default rules -include $(MA_DIR)/include/common-rules.make DEBVERSION:=$(shell head -n 1 debian/changelog \ | sed -e 's/^[^(]*(\([^)]*\)).*/\1/') DEB_BASE_VERSION:=$(shell echo $(DEBVERSION) | sed -e 's/^.*://' -e 's/-[0-9~.a-z]*$$//') UPVERSION:=$(shell echo $(DEB_BASE_VERSION) | sed -e 's/[~+]dfsg\(~\|$$\)/\1/' -e 's/~\(rc\|beta\)/-\1/') # For DKMS. Use the full release (e.g.: 2.2.1+dfsg-1ubuntu1) CVERSION := $(shell dpkg-parsechangelog | grep '^Version:' | cut -d' ' -f2 | cut -d\: -f2) UPFILENAME := $(PACKAGE_SRC)_$(UPVERSION).orig.tar.gz FILENAME := $(PACKAGE_SRC)_$(DEB_BASE_VERSION).orig.tar.gz URL := http://downloads.asterisk.org/pub/telephony/$(PACKAGE_SRC)/releases/$(PACKAGE_SRC)-$(UPVERSION).tar.gz # If the makefile was properly-writen, there was a good separation # between kernel and userspace. As things stand now I'd like to work # around problems with bilding stuff with HOSTCC by simply providing # the generated headers as part of the source: GENERATED_SOURCES := include/dahdi/version.h EXTRA_MODS=dahdi_echocan_oslec EXTRA_SUBDIRS = ap400 opvxa1200 opvxd115 zaphfc ../staging/echo ifneq (,$(filter-out powerpc m68k armeb mips,$(shell dpkg-architecture -qDEB_HOST_ARCH))) EXTRA_MODS += wcopenpci endif MOD_ARGS=MODULES_EXTRA="$(EXTRA_MODS)" SUBDIRS_EXTRA="$(EXTRA_SUBDIRS)" %: dh $@ kdist_clean: clean kdist_config: prep-deb-files binary-modules: prep-deb-files dh_testdir dh_testroot dh_prep #cp -a $(CURDIR)/debian/generated/* . make $(MOD_ARGS) modules KERNEL_SOURCES=$(KSRC) MODVERSIONS=detect KERNEL=linux-$(KVERS) make $(MOD_ARGS) install-modules KERNELRELEASE=$(KVERS) DESTDIR=$(CURDIR)/debian/$(PKGNAME) ifeq (2.6,$(shell echo $(KVERS) | cut -d. -f1-2)) # The 2.6 modules are way too big. This is only in kernel 2.6 find debian/$(PKGNAME)/lib/modules -name '*.ko' |xargs strip -g $(RM) -f debian/$(PKGNAME)/lib/modules/$(KVERS)/modules.* endif dh_installmodules dh_installdebconf dh_installdocs dh_installchangelogs dh_compress dh_fixperms dh_installdeb dh_gencontrol -- -v$(VERSION) dh_md5sums dh_builddeb --destdir=$(DEB_DESTDIR) ## END OF M-A SECTION override_dh_auto_build: # For DKMS sed -e "s|#CVERSION#|$(CVERSION)|" \ debian/dkms.conf.in > debian/dkms.conf sed -e "s|#CVERSION#|$(CVERSION)|" \ debian/dahdi-dkms.install.in > debian/dahdi-dkms.install $(MAKE) docs $(MAKE) $(GENERATED_SOURCES) override_dh_auto_clean: #Delete the generated dkms files rm -f debian/dahdi-dkms.install rm -f debian/dkms.conf rm -f $(GENERATED_SOURCES) rm -f dahdi/include/version.h [ ! -f Makefile ] || $(MAKE) dist-clean || true TARPARDIR=$(CURDIR)/debian/tmp TARDIR=$(TARPARDIR)/modules/$(PREFIX) override_dh_auto_install: $(MAKE) install-include DESTDIR=$(CURDIR)/debian/$(SKPG) override_dh_install: dh_install # driver source code mkdir -p $(TARDIR)/debian/generated cp Makefile $(TARDIR)/ cp -p .version $(TARDIR)/ for dir in build_tools firmware include drivers; do \ if [ -d $$dir ]; then cp -r $$dir $(TARDIR); fi; \ done # Packaging infrastructure cp -r debian/rules debian/changelog debian/copyright\ debian/control debian/compat \ debian/control.modules.in \ $(TARDIR)/debian/ tar cjf debian/$(SKPG)/usr/src/$(PREFIX).tar.bz2 \ -C $(TARPARDIR) modules print-version: @@echo "Debian version: $(DEBVERSION)" @@echo "Upstream version: $(UPVERSION)" TARBALL_DIR=../tarballs/$(PACKAGE_SRC)-$(UPVERSION).tmp get-orig-source: @@dh_testdir @@[ -d ../tarballs/. ]||mkdir -p ../tarballs @@echo Downloading $(UPFILENAME) from $(URL) ... @@wget -nv -T10 -t3 --verbose -O ../tarballs/$(UPFILENAME) $(URL) @@echo Repacking as DFSG-free... @@mkdir -p $(TARBALL_DIR)/ @@cd $(TARBALL_DIR) ; \ tar xfz ../$(UPFILENAME) @@rm -rf $(TARBALL_DIR)/$(PACKAGE_SRC)-$(UPVERSION)/drivers/dahdi/xpp/firmwares/*.hex @@cd $(TARBALL_DIR) ; \ tar cfz ../$(FILENAME) * @@echo Cleaning up... @@$(RM) -rf $(TARBALL_DIR)/ @@$(RM) -f ../tarballs/$(UPFILENAME) debian/dahdi-linux.install0000664000000000000000000000034711652357765013013 0ustar drivers/dahdi/xpp/xpp.conf etc/dahdi drivers/dahdi/xpp/xpp.rules lib/udev/rules.d drivers/dahdi/xpp/init_card_* usr/share/dahdi/ drivers/dahdi/xpp/XppConfig.pm usr/share/dahdi/ debian/modules etc/dahdi debian/system.conf etc/dahdi debian/modules0000664000000000000000000000004211652357765010600 0ustar dahdi dahdi_dummy dahdi_transcode debian/source/0000775000000000000000000000000011652361351010473 5ustar debian/source/format0000664000000000000000000000001411652357765011717 0ustar 3.0 (quilt) debian/dahdi-dkms.install.in0000664000000000000000000000045411652357765013216 0ustar Makefile usr/src/dahdi-#CVERSION# build_tools usr/src/dahdi-#CVERSION# drivers usr/src/dahdi-#CVERSION# include usr/src/dahdi-#CVERSION# debian/dkms.conf usr/src/dahdi-#CVERSION# .version usr/src/dahdi-#CVERSION# debian/control0000664000000000000000000000435011652360160010575 0ustar Source: dahdi-linux Section: comm Priority: optional Maintainer: Ubuntu Developers Uploaders: Tzafrir Cohen , Mark Purcell , Faidon Liambotis XSBC-Original-Maintainer: Debian VoIP Team Build-Depends: debhelper (>= 7.0.50~), bzip2, asciidoc Standards-Version: 3.9.2 Homepage: http://www.asterisk.org/ Vcs-Svn: svn://svn.debian.org/pkg-voip/dahdi-linux/trunk/ Vcs-Browser: http://svn.debian.org/wsvn/pkg-voip/dahdi-linux/?op=log Package: dahdi-linux Architecture: all # Xorcom packages depend on dahdi-firmware. Debian zaptel will probably # just recommend it. Depends: dahdi-dkms | dahdi-source, ${shlibs:Depends}, ${misc:Depends}, procps, fxload Replaces: zaptel Conflicts: zaptel Description: DAHDI telephony interface - Linux userspace parts DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. the Asterisk PBX software. The dahdi-* packages provide the kernel DAHDI kernel modules and their required setup environment. . This package includes the minimal Linux-specific userspace parts: udev configuration, xpp initialization scripts. Package: dahdi-source Section: kernel Architecture: all Depends: ${misc:Depends}, debhelper (>> 4.0), module-assistant (>= 0.8.1), bzip2 Recommends: dahdi-linux Description: DAHDI telephony interface - source code for kernel driver DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. the Asterisk PBX software. The dahdi-* packages provide the kernel DAHDI kernel modules and their required setup environment, as well as basic headers for building DAHDI modules and utilities. . It is normally used to build kernel modules package: m-a a-i dahdi-source Package: dahdi-dkms Section: devel Architecture: all Depends: dkms, make, libc6-dev, gcc, wget, gawk Recommends: dahdi-linux Description: DAHDI telephony interface (dkms kernel driver) DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. the Asterisk PBX software. The dahdi-* packages provide the kernel DAHDI kernel modules and their required setup environment. . The dkms package will automatically compile the driver for your current kernel version.