diff options
author | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-02-15 02:24:18 +0000 |
---|---|---|
committer | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-02-15 02:24:18 +0000 |
commit | 41e0e5c06fead266ce179424b18134aeac2c5762 (patch) | |
tree | 246b5055fddf29d43c4d1a7c195ff3f3e1460ac4 | |
parent | 6aa681528c72a21a011883dcdcd35130bd066eac (diff) |
initial import of Xorcom Astribank driver (issue #6452, with minor mods)
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@949 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | README.Astribank | 135 | ||||
-rw-r--r-- | xpp/FPGA_XPD.hex | 243 | ||||
-rw-r--r-- | xpp/Makefile | 5 | ||||
-rw-r--r-- | xpp/card_fxs.c | 703 | ||||
-rw-r--r-- | xpp/card_fxs.h | 45 | ||||
-rw-r--r-- | xpp/card_global.c | 256 | ||||
-rw-r--r-- | xpp/card_global.h | 55 | ||||
-rw-r--r-- | xpp/cards.c | 394 | ||||
-rw-r--r-- | xpp/cards.h | 23 | ||||
-rwxr-xr-x | xpp/gen_slic_init | 37 | ||||
-rw-r--r-- | xpp/slic.c | 129 | ||||
-rw-r--r-- | xpp/slic.h | 65 | ||||
-rw-r--r-- | xpp/slic_init.inc | 65 | ||||
-rwxr-xr-x | xpp/sync.sh | 31 | ||||
-rw-r--r-- | xpp/xdefs.h | 82 | ||||
-rw-r--r-- | xpp/xpd.h | 290 | ||||
-rw-r--r-- | xpp/xpp_fxloader | 24 | ||||
-rw-r--r-- | xpp/xpp_fxloader.usermap | 2 | ||||
-rw-r--r-- | xpp/xpp_modprobe | 10 | ||||
-rw-r--r-- | xpp/xpp_proto.c | 1044 | ||||
-rw-r--r-- | xpp/xpp_proto.h | 188 | ||||
-rw-r--r-- | xpp/xpp_usb.c | 882 | ||||
-rw-r--r-- | xpp/xpp_zap.c | 2312 | ||||
-rw-r--r-- | xpp/xpp_zap.h | 58 | ||||
-rw-r--r-- | xpp/xproto.c | 334 | ||||
-rw-r--r-- | xpp/xproto.h | 239 | ||||
-rw-r--r-- | xpp/zap_debug.c | 136 | ||||
-rw-r--r-- | xpp/zap_debug.h | 16 |
29 files changed, 7818 insertions, 5 deletions
@@ -1,7 +1,7 @@ # # Makefile for Zaptel driver modules and utilities # -# Copyright (C) 2001-2005 Digium, Inc. +# Copyright (C) 2001-2006 Digium, Inc. # # BASEADDR=0xd0000 @@ -120,6 +120,7 @@ TZOBJS:=zonedata.lo tonezone.lo LIBTONEZONE_SO:=libtonezone.so LIBTONEZONE_SO_MAJOR_VER:=1 LIBTONEZONE_SO_MINOR_VER:=0 + MODULES:=zaptel tor2 torisa wcusb wcfxo wctdm wctdm24xxp \ ztdynamic ztd-eth wct1xxp wct4xxp wcte11xp pciradio \ ztd-loc # ztdummy @@ -128,9 +129,18 @@ MODULES:=zaptel tor2 torisa wcusb wcfxo wctdm wctdm24xxp \ ifeq (${BUILDVER},linux26) MODULES+=ztdummy endif + MODULESO:=$(MODULES:%=%.o) MODULESKO:=$(MODULES:%=%.ko) +MOD_DESTDIR:=zaptel + +obj-m:=$(MODULESO) ${XPPMOD} + +ifneq ($(filter xpp,${MAKECMDGOALS}),) +XPPMOD=xpp/ +endif + ifneq (,$(wildcard /usr/include/newt.h)) ZTTOOL:=zttool endif @@ -148,16 +158,14 @@ linux24: $(MODULESO) $(BINS) linux26: prereq $(BINS) @echo $(KSRC) @if [ -z "$(KSRC)" -o ! -d "$(KSRC)" ]; then echo "You do not appear to have the sources for the $(KVERS) kernel installed."; exit 1 ; fi - $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules + $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) XPPMOD=$(XPPMOD) modules -obj-m := $(MODULESO) +xpp: linux26 #ifneq ($(TOPDIR),) #include $(TOPDIR)/Rules.make #endif -MOD_DESTDIR:=zaptel - version.h: FORCE ZAPTELVERSION="${ZAPTELVERSION}" build_tools/make_version_h > $@.tmp if cmp -s $@.tmp $@ ; then echo; else \ @@ -471,6 +479,8 @@ clean: rm -f *.o ztcfg tzdriver sethdlc sethdlc-new rm -f $(TZOBJS) $(LIBTONEZONE_SO) *.lo rm -f *.ko *.mod.c .*o.cmd + rm -f xpp/*.ko xpp/*.mod.c xpp/.*o.cmd + rm -f xpp/*.o xpp/*.mod.o rm -rf .tmp_versions rm -f gendigits tones.h rm -f libtonezone* diff --git a/README.Astribank b/README.Astribank new file mode 100644 index 0000000..aa24371 --- /dev/null +++ b/README.Astribank @@ -0,0 +1,135 @@ +This file documents the Zaptel drivers for the Xorcom Astribank8 Channel Bank. +The drivers reside in a separate subdirectory, xpp/ . + + +Building and Installation: +""""""""""""""""""""""""" +Building and installation is basically like the normal procedure of +installing Zaptel. Follow the rest of the documentation here. + +In addition, the file xpp/xpp_modprobe contains modprobe settings. It +should be copied verbatim into /etc/modprobe.conf or (better) copied to +/etc/modprobe.d/ . If you fail to do so, xpp_usb.ko will fail to load +xpd_fxs.ko and as a result will not detect your Astribank. + +Loading Firmware +"""""""""""""""" +The Astribank needs a firmware loaded into it. Without the firmware, +the device will appear in lsusb with vendor ID 04b4 and product ID 8613 +The firmware is provided in the Intel hex format. It can be loaded using +the program fxload, which is typically part of the package 'fxload' or +'hotplug-utils' . + +To load the firmware automatically using the standard hotplug script, +place xpp/xpp_fxloader and xpp/xpp_fxloader.usermap in /etc/hotplug/usb +and place xpp/FPGA_XPD.hex in /etc/xortel (or edit xpp_fxloader +accordingly). + +Alternatively, xpp_fxloader when given the parameter 'xppdetect' will load +the firmware from /etc/xortel/FPGA_XPD.hex . You can use it to load the +firmware manually. + +You should then get in lsusb the vendor ID e4e4 and device ID 2121 +(those IDs are temporary and likely to change in upcoming versions). +Once there is such a device, the xpp_usb driver should load +automatically on hot-plugging. In fact, you may find it simpler to +disconnect and reconnect the device than running 'modprobe xpp_usb'. + + +The driver has been separated into several modules: xpp.ko, xpd_fxs.ko and +xpp_usb.ko . Generally you only need to modprobe xpp_usb, and it should also +be loaded by hotplug when you connect the Astribank. However in order for it +to load xpd_fks.ko correctly + +Refer to the instructions for Zaptel. After our small patches were applies, +you get xpp.ko which is basically yet another zaptel driver, like wcfxo +and wctdm. + +When loaded, you should get one span, of 8 extensions, 2 output ports and +4 input ports: + + root@rapid:~# cat /proc/zaptel/2 + Span 1: XBUS-0/XPD-0 "Xorcom XPD #0/0: FXS" NOTOPEN + + 1 XPP_FXS/0-0 FXOKS (In use) + 2 XPP_FXS/0-1 FXOKS (In use) + 3 XPP_FXS/0-2 FXOKS (In use) + 4 XPP_FXS/0-3 FXOKS (In use) + 5 XPP_FXS/0-4 FXOKS (In use) + 6 XPP_FXS/0-5 FXOKS (In use) + 7 XPP_FXS/0-6 FXOKS (In use) + 8 XPP_FXS/0-7 FXOKS (In use) + 9 XPP_OUT/0-8 FXOKS (In use) + 10 XPP_OUT/0-9 FXOKS (In use) + 11 XPP_IN/0-10 FXOKS (In use) + 12 XPP_IN/0-11 FXOKS (In use) + 13 XPP_IN/0-12 FXOKS (In use) + 14 XPP_IN/0-13 FXOKS (In use) + +For such a simple case you could use: + +/etc/zaptel.conf: + +fxoks=1-14 +loadzone=us +tonezone=us + +/etc/asterisk/zapata.conf: + +[channels] +group=1 +signalling=fxo_ks +immediate=no + +context=from-internal +channels => 1-8 + +; actually they will never generate calls, so the context +; here is irrelevant +;context=outputs +channels => 9-10 + +; input ports should get an answer: +immediate=yes +context=inputs +channels => 11-14 + +;;;;;; end of zapata.conf + + +/proc Interface +""""""""""""""" +The Astribank drivers provide their own /proc interface under /proc/xpp . +(Note that the details of this interface are still potentially subject to +changes) + +/proc/xpp/xbuses lists the connected devices (an xbus is such a device), +one per line. A device is normally "connected". "missing" means that it +was disconnected, but Asterisk still holds channels from it open. You can +also see in the xbuses file to which physical connection the Astribank +is connected. + +/proc/xpp/sync is a read/write file . It prints the current +synchronization source. printing to it can change the synchronization +source. Host-synchronization is currently the default but for better +sound quality you should synchronize from the Astribank. + +/proc/xpp/XBUS-n gives information about device number n (starting from +0). under it, /proc/XBUS-n/XPD-m gives information regarding span number +m in that device. + +/proc/xpp/XBUS-n/XPD-m/zt_registration is a read-write file for +manually registering/unregistering the span with Zaptel. A span will +register automatically when generated, though. Span unregistration may +fail if some channels from the span are used (e.g: by Asterisk). +Registration is by writing 1 and unregistration is by writing 0 to the +file. + +(There are a bunch of other status files under /proc/xpp/ ) + + + +BTW: XPP here does not stand for X Printing Panel, XML Pull Parser, +X-Windows Phase Plane or XML Professional Publisher. It is simply the +Xorcom Peripheral Protocol, which connects a computer to a XPD (Xorcom +Peripheral Device). diff --git a/xpp/FPGA_XPD.hex b/xpp/FPGA_XPD.hex new file mode 100644 index 0000000..094f2c9 --- /dev/null +++ b/xpp/FPGA_XPD.hex @@ -0,0 +1,243 @@ +:0A0B3C000001020203030404050592 +:100546005010C0C0F9A4B0999282F880988883C6EA +:03055600A1868EED +:1002B700E4F513F512F511F510C203C200C202C22C +:1002C700011206377E077F008E238F24752B077553 +:1002D7002C1275210775221C752907752A4A752D59 +:1002E70007752E78EE54C070030203B875140075B5 +:1002F70015808E168F17C374C09FFF74079ECF2477 +:1003070002CF3400FEE48F0F8E0EF50DF50CF50BC2 +:10031700F50AF509F508AF0FAE0EAD0DAC0CAB0B3A +:10032700AA0AA909A808C3120B205033E517250B01 +:10033700F582E516350AF583E0FFE515250BF5820D +:10034700E514350AF583EFF0E50B2401F50BE435E9 +:100357000AF50AE43509F509E43508F50880B78593 +:10036700142385152474002480FF740734FFFEC30B +:10037700E52C9FF52CE52B9EF52BC3E5269FF5264F +:10038700E5259EF525C3E5289FF528E5279EF52752 +:10039700C3E5229FF522E5219EF521C3E52A9FF5B6 +:1003A7002AE5299EF529C3E52E9FF52EE52D9EF515 +:1003B7002DD2E843D82090E668E0440BF090E65C45 +:1003C700E0443DF0000000000000E4F5A20000005A +:1003D700D2AF90E680E020E105D2041206D090E685 +:1003E70080E054F7F0538EF8C20390E6C2E054FB66 +:1003F700F000000000000090E6187410F000000004 +:1004070090E61A7408F000000090E6017403F0000B +:100417000000300105120080C2013003F5120B5AAB +:1004270050F0C203120A7B20001690E682E030E704 +:1004370004E020E1EF90E682E030E604E020E0E42B +:080447001209FC120B5C80CAD3 +:0B0B310090E50DE030E402C322D32267 +:1000800090E6B9E0700302013F1470030201BC2442 +:10009000FE700302023F24FB700302013914700357 +:1000A00002013314700302012714700302012D248E +:1000B0000560030202A3120B5E40030202AF90E64A +:1000C000BBE024FE602714603824FD601114602713 +:1000D00024067050E52390E6B3F0E524803C120B33 +:1000E00031503EE52B90E6B3F0E52C802DE52590D0 +:1000F000E6B3F0E5268023E52790E6B3F0E5288017 +:100100001990E6BAE0FF120A28AA06A9077B01EABD +:10011000494B600DEE90E6B3F0EF90E6B4F00202CA +:10012000AF02029E02029E120B0E0202AF120B4E93 +:100130000202AF120B460202AF120AFC0202AF1219 +:100140000B6040030202AF90E6B8E0247F60151414 +:10015000601924027063A200E43325E0FFA202E4E8 +:10016000334F8041E490E740F0803F90E6BCE0549C +:100170007EFF7E00E0D394807C0040047D018002FD +:100180007D00EC4EFEED4F243CF582740B3EF58372 +:10019000E493FF3395E0FEEF24A1FFEE34E68F8277 +:1001A000F583E0540190E740F0E4A3F090E68AF094 +:1001B00090E68B7402F00202AF02029E120B6240C4 +:1001C000030202AF90E6B8E024FE6016240260034A +:1001D0000202AF90E6BAE0B40105C2000202AF022B +:1001E000029E90E6BAE0705590E6BCE0547EFF7E39 +:1001F00000E0D394807C0040047D0180027D00EC0F +:100200004EFEED4F243CF582740B3EF583E493FFE4 +:100210003395E0FEEF24A1FFEE34E68F82F583E014 +:1002200054FEF090E6BCE05480131313541FFFE01B +:10023000540F2F90E683F0E04420F08072805F122C +:100240000B64506B90E6B8E024FE60192402704EF7 +:1002500090E6BAE0B40104D200805490E6BAE064BB +:1002600002604C803990E6BCE0547EFF7E00E0D313 +:1002700094807C0040047D0180027D00EC4EFEED08 +:100280004F243CF582740B3EF583E493FF3395E0F5 +:10029000FEEF24A1FFEE34E68F82F583800D90E619 +:1002A000A08008120A53500790E6A0E04401F090A5 +:0602B000E6A0E04480F02E +:0102B6002225 +:03003300020B5667 +:040B560053D8EF324F +:100700001201000200000040E4E411220000010296 +:1007100000010A06000200000040010009022E004C +:1007200001010080320904000004FF0000000705F9 +:10073000020200020007050402000200070586020B +:100740000002000705880200020009022E000101D4 +:100750000080320904000004FF00000007050202C7 +:100760004000000705040240000007058602400023 +:100770000007058802400000040309041C0341002F +:100780007300740072006900620061006E006B000B +:10079000320030003000360028035800500044007A +:1007A00028004200610073006500640020006F00B3 +:1007B0006E002000410058005500500050002900F4 +:0207C000000037 +:100559005010B0C0F9A4B0999282F880988883C6E7 +:10056900A1868E4110ABFF4110AD004110AEFF4195 +:0705790010AC004110AF00BF +:1006370090E600E054E74410F000000090E60474F0 +:1006470080F00000007406F0000000E4F0000000F5 +:1006570090E610F090E611F000000090E613F0002D +:10066700000090E615F090E61074A0F090E611F007 +:1006770000000000000090E61274AAF0000000904D +:10068700E61474EAF000000090E6047480F00000BD +:10069700007404F0000000E4F000000090E64974E4 +:1006A70082F0000000F000000090E6187410F000DF +:1006B700000090E61A7408F090E6917480F000004C +:0906C70000F000000043AF012225 +:020B5A00D322A4 +:020B5C00D322A2 +:020B5E00D322A0 +:080B460090E6BAE0F51BD32292 +:100AFC0090E740E51BF0E490E68AF090E68B04F07A +:020B0C00D322F2 +:080B4E0090E6BAE0F51AD3228B +:100B0E0090E740E51AF0E490E68AF090E68B04F068 +:020B1E00D322E0 +:020B6000D3229E +:020B6200D3229C +:020B6400D3229A +:100A530090E6B9E0242F600D04701990E604E0FFDE +:100A6300430780800890E604E0FF53077F000000FF +:070A7300EFF08002D322C363 +:010A7A002259 +:100AA000C0E0C083C082D2015391EF90E65D740133 +:080AB000F0D082D083D0E032C7 +:100AD000C0E0C083C0825391EF90E65D7404F0D013 +:060AE00082D083D0E03259 +:100AE600C0E0C083C0825391EF90E65D7402F0D0FF +:060AF60082D083D0E03243 +:1009C600C0E0C083C082852925852A2685268285A2 +:1009D6002583A37402F08521278522288528828510 +:1009E6002783A37407F05391EF90E65D7410F0D05F +:0609F60082D083D0E03244 +:100AB800C0E0C083C082D2035391EF90E65D740812 +:080AC800F0D082D083D0E032AF +:1007C200C0E0C083C08290E680E030E7208521252A +:1007D200852226852682852583A37402F085292712 +:1007E200852A28852882852783A37407F05391EFF1 +:0D07F20090E65D7420F0D082D083D0E0321C +:0106FF0032C8 +:0107FF0032C7 +:010B6600325C +:010B6700325B +:010B6800325A +:010B69003259 +:010B6A003258 +:010B6B003257 +:010B6C003256 +:010B6D003255 +:010B6E003254 +:010B6F003253 +:010B70003252 +:010B71003251 +:010B72003250 +:010B7300324F +:010B7400324E +:010B7500324D +:010B7600324C +:010B7700324B +:010B7800324A +:010B79003249 +:010B7A003248 +:010B7B003247 +:010B7C003246 +:010B7D003245 +:010B7E003244 +:010B7F003243 +:010B80003242 +:010B81003241 +:010B82003240 +:010B8300323F +:010B8400323E +:010B8500323D +:10098A00C0E0C083C08290E6D1E09010ACF090E65F +:10099A00D0E4F000000090E6D17402F000000053A9 +:1009AA0091BF00000090E66104F000000090E69913 +:0C09BA0004F075BB06D082D083D0E03280 +:010B8600323C +:03004300020800B0 +:03005300020800A0 +:10080000020AA000020AE600020AD000020AB800AA +:100810000209C6000207C2000206FF000207FF002D +:10082000020B6600020B6700020B6800020B6900F6 +:10083000020B6A00020B6B00020B6C00020B6D00D6 +:10084000020B6E000207FF00020B6F00020B70002C +:10085000020B7100020B7200020B7300020B74009A +:10086000020B75000207FF000207FF000207FF00EE +:10087000020B7600020B7700020B7800020B790066 +:10088000020B7A00020B7B00020B7C00020B7D0046 +:10089000020B7E00020B7F00020B8000020B810026 +:1008A000020B8200020B8300020B8400020B850006 +:0808B00002098A00020B860018 +:1009FC0090E682E030E004E020E60B90E682E03006 +:100A0C00E119E030E71590E680E04401F07F147EB8 +:0C0A1C000012094490E680E054FEF02235 +:1006D00030040990E680E0440AF0800790E680E06C +:1006E0004408F07FDC7E0512094490E65D74FFF05B +:0F06F00090E65FF05391EF90E680E054F7F02230 +:020A2800A9071C +:100A2A00AE2DAF2E8F828E83A3E064037017AD01C3 +:100A3A0019ED7001228F828E83E07C002FFDEC3E3F +:080A4A00FEAF0580DFE4FEFFB2 +:010A52002281 +:100A7B0090E682E044C0F090E681F04387010000ED +:040A8B000000002245 +:100944008E188F1990E600E054187012E5192401EE +:10095400FFE43518C313F518EF13F519801590E665 +:1009640000E05418FFBF100BE51925E0F519E51850 +:1009740033F518E5191519AE18700215184E6005EF +:06098400120A8F80EE2232 +:100A8F007400F58690FDA57C05A3E582458370F97A +:010A9F002234 +:1005800060801000011113213C01010703030305E2 +:10059000010000000C0D0C0C0E0E0E0E410000367A +:1005A0000900003F010A013B0101010701010201AD +:1005B0000000000002020000020202024049004066 +:1005C0000000003F010101010101010700000000DE +:1005D000000000000E0E0E0E0E0E0E0E00000000AB +:1005E0000000003F0139010101010107000302027F +:1005F000020202020E0E0E0E0E0E0E0E002D000056 +:100600000000003F60241087000000000000000090 +:1006100000000000000000000000833611170004F5 +:10062000030201813616170004030201471080E01F +:0606300000000ECE4E009A +:10044F0090E60174CEF090E6F574FFF0901080E026 +:10045F0090E6F3F0901081E090E6C3F0901082E008 +:10046F0090E6C1F0901083E090E6C2F0901085E026 +:10047F0090E6C0F0901086E090E6F4F075AF077448 +:10048F0010F59A7400F59B759DE4E4F59EFF90E6D8 +:10049F007BE090E67CF00FBF80F490E67174FFF084 +:1004AF00F5B490E672E04480F043B680000000E4BB +:1004BF0090E6C4F000000090E6C5F0901087E09041 +:1004CF00E6C6F0901088E090E6C7F0901089E090B3 +:1004DF00E6C8F090108AE090E6C9F090108BE0909B +:1004EF00E6CAF090108CE090E6CBF090108DE09083 +:1004FF00E6CCF090108EE090E6CDF000000090E694 +:10050F00D27401F000000090E6E204F00000000059 +:10051F00000090E6EB14F000000000000000000067 +:10052F0090E6C0F090E6C1F090E6247408F0000069 +:06053F0000E490E625F047 +:010545002293 +:030000000208B83B +:0C08B800787FE4F6D8FD75812E0208FF61 +:100B2000EB9FF5F0EA9E42F0E99D42F0E89C45F02B +:010B300022A2 +:1008C4000202B7E493A3F8E493A34003F68001F291 +:1008D40008DFF48029E493A3F85407240CC8C33335 +:1008E400C4540F4420C8834004F456800146F6DF04 +:1008F400E4800B0102040810204080900546E47E49 +:10090400019360BCA3FF543F30E509541FFEE493F8 +:10091400A360010ECF54C025E060A840B8E493A3BF +:10092400FAE493A3F8E493A3C8C582C8CAC583CAEA +:10093400F0A3C8C582C8CAC583CADFE9DEE780BEA2 +:0106360000C3 +:00000001FF diff --git a/xpp/Makefile b/xpp/Makefile new file mode 100644 index 0000000..1207332 --- /dev/null +++ b/xpp/Makefile @@ -0,0 +1,5 @@ +EXTRA_CFLAGS = -I$(src)/.. -DSOFT_SIMULATOR=0 + +obj-m = xpd_fxs.o xpp.o xpp_usb.o +xpp-y += xproto.o card_global.o xpp_zap.o zap_debug.o +xpd_fxs-y += card_fxs.o slic.o diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c new file mode 100644 index 0000000..d408647 --- /dev/null +++ b/xpp/card_fxs.c @@ -0,0 +1,703 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 <linux/init.h> +#include <linux/module.h> +#include <linux/fs.h> +#include "xpd.h" +#include "xproto.h" +#include "xpp_zap.h" +#include <linux/delay.h> + +static const char rcsid[] = "$Id$"; +static const char revision[] = "$Revision$"; + +DEF_PARM(int, print_dbg, 1, "Print DBG statements"); /* must be before zap_debug.h */ + +#define LINES_REGULAR 8 +#define LINES_DIGI_OUT 2 +#define LINES_DIGI_INP 4 + +#define MASK_BITS(b) ((1U << (b)) - 1) + +#define MASK_DIGI_OUT (MASK_BITS(LINES_DIGI_OUT) << LINES_REGULAR) +#define MASK_DIGI_INP (MASK_BITS(LINES_DIGI_INP) << (LINES_REGULAR + LINES_DIGI_OUT)) + +/*---------------- FXS Protocol Commands ----------------------------------*/ + +/* 0x0F */ DECLARE_CMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on); +/* 0x0F */ DECLARE_CMD(FXS, CHAN_POWER, xpp_line_t lines, bool on); +/* 0x0F */ DECLARE_CMD(FXS, CHAN_CID, xpp_line_t lines); +/* 0x0F */ DECLARE_CMD(FXS, RING, int pos, bool on); +/* 0x0F */ DECLARE_CMD(FXS, SETHOOK, xpp_line_t hook_status); +/* 0x0F */ DECLARE_CMD(FXS, LED, xpp_line_t lines, byte which, bool on); +/* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on); +/* 0x0F */ DECLARE_CMD(FXS, SLIC_INIT); +/* 0x0F */ DECLARE_CMD(FXS, SLIC_QUERY, int pos, byte reg_num); + +static bool fxs_packet_is_valid(xpacket_t *pack); +static void fxs_packet_dump(xpacket_t *pack); +static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data); +static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data); + +#define S_(s,l,...) \ + { \ + .lines = s, \ + { \ + .len = l, \ + .data = { __VA_ARGS__ }, \ + } \ + } +struct slic_init_data { + xpp_line_t lines; + slic_data_t slic_data; +} slic_init_data[] = { +#include "slic_init.inc" +}; +#undef S_ + +#define PROC_SLIC_FNAME "slics" + +struct FXS_priv_data { + struct proc_dir_entry *xpd_slic; + slic_reply_t last_reply; +}; + +/*---------------- FXS: Methods -------------------------------------------*/ + +static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision) +{ + xpd_t *xpd = NULL; + int channels = min(8, CHANNELS_PERXPD); + + if(xpd_num == 0) + channels += 6; /* 2 DIGITAL OUTPUTS, 4 DIGITAL INPUTS */ + xpd = xpd_alloc(sizeof(struct FXS_priv_data), xbus, xpd_num, proto_table, channels, revision); + if(!xpd) + return NULL; + if(xpd_num == 0) { + DBG("First XPD on %s detected. Initialize digital outputs/inputs\n", xbus->busname); + xpd->digital_outputs = MASK_DIGI_OUT; + xpd->digital_inputs = MASK_DIGI_INP; + } + xpd->direction = TO_PHONE; + return xpd; +} + +static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) +{ + struct FXS_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + CALL_PROTO(FXS, SLIC_INIT, xbus, xpd); +#ifdef CONFIG_PROC_FS + DBG("Creating SLICs file for %s/%s\n", xbus->busname, xpd->xpdname); + priv->xpd_slic = create_proc_entry(PROC_SLIC_FNAME, 0644, xpd->proc_xpd_dir); + if(!priv->xpd_slic) { + ERR("Failed to create proc file for SLICs of %s/%s\n", xbus->busname, xpd->xpdname); + goto out; + } + priv->xpd_slic->write_proc = proc_xpd_slic_write; + priv->xpd_slic->read_proc = proc_xpd_slic_read; + priv->xpd_slic->data = xpd; +out: +#endif + return 0; +} + +static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd) +{ + struct FXS_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + DBG("%s/%s\n", xbus->busname, xpd->xpdname); +#ifdef CONFIG_PROC_FS + if(priv->xpd_slic) { + DBG("Removing xpd SLIC file %s/%s\n", xbus->busname, xpd->xpdname); + remove_proc_entry(PROC_SLIC_FNAME, xpd->proc_xpd_dir); + } +#endif + return 0; +} + +/* + * INPUT polling is done via SLIC register 0x06 (same as LEDS): + * 7 6 5 4 3 2 1 0 + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * | I1 | I3 | | | I2 | I4 | | | + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * + */ +static int input_channels[] = { 6, 7, 2, 3 }; // Slic numbers of input relays + +static void poll_inputs(xbus_t *xbus, xpd_t *xpd) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(input_channels); i++) { + int pos = input_channels[i]; + + CALL_PROTO(FXS, SLIC_QUERY, xbus, xpd, pos, 0x06); + } +} + +static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) +{ + static int rate_limit = 0; + + if((rate_limit++ % 1000) == 0) { + poll_inputs(xbus, xpd); + } + return 0; +} + +/*---------------- FXS: HOST COMMANDS -------------------------------------*/ + +/* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on) +{ + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off"); + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, lines, 0x40, (on)?0x01:0x00); + pack->datalen = len; + + packet_send(xbus, pack); + return ret; +} + +/* 0x0F */ HOSTCMD(FXS, CHAN_POWER, xpp_line_t lines, bool on) +{ + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("Channel Power: 0x%04X %s\n", lines, (on) ? "up" : "down"); + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + if(on) { + // Power up + len = slic_cmd_direct_write(sc, lines, 0x42, 0x06); + } else { + // Power down + len = slic_cmd_direct_write(sc, lines, 0x42, 0x00); + } + pack->datalen = len; + + packet_send(xbus, pack); + return ret; +} + +/* 0x0F */ HOSTCMD(FXS, CHAN_CID, xpp_line_t lines) +{ + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("Channel CID: 0x%04X\n", lines); + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + pack->datalen = slic_cmd_direct_write(sc, lines, 0x40, 0x02); + packet_send(xbus, pack); + return ret; +} + + +/* 0x0F */ HOSTCMD(FXS, RING, int pos, bool on) +{ + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + xpp_line_t mask = (1 << pos); + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + mask &= xpd->enabled_chans; // Ignore disabled channels + if(!mask) { + return 0; + } + DBG("%s pos=%d %s\n", xpd->xpdname, pos, (on) ? "on" : "off"); + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, mask, 0x40, (on)?0x04:0x01); + pack->datalen = len; + + packet_send(xbus, pack); + return ret; +} + +/* 0x0F */ HOSTCMD(FXS, SETHOOK, xpp_line_t hook_status) +{ + DBG("\n"); + return 0; +} + +/* + * LED control is done via SLIC register 0x06: + * 7 6 5 4 3 2 1 0 + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * | MR | MG | MB | R | OG | OB | G | B | + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * + * B - BLUE LED (0 - OFF, 1 - ON) + * G - GREEN LED (0 - OFF, 1 - ON) + * OB - Output BLUE (this line is output) + * OG - Output GREEN (this line is output) + * R - RED LED (0 - OFF, 1 - ON) + * MB - Mask BLUE. (1 - B effect the BLUE LED) + * MR - Mask RED. (1 - R effect the RED LED) + * MG - Mask GREEN. (1 - G effect the GREEN LED) + * + * The BLUE LED (actually a relay out) is connected to line 0 and 4 only. + */ + +// GREEN RED BLUE +static const int led_mask[NUM_LEDS] = { BIT(7), BIT(6), BIT(5) }; +static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) }; + +/* 0x0F */ HOSTCMD(FXS, LED, xpp_line_t lines, byte which, bool on) +{ + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + int len; + int value; + int i; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("LED: lines=0x%04X which=%d -- %s\n", lines, which, (on) ? "on" : "off"); + which = which % NUM_LEDS; + value = BIT(2) | BIT(3); + value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[which]); + if(on) + value |= led_vals[which]; + for(i = 0; i < CHANNELS_PERXPD; i++) { + if(!IS_SET(lines, i)) + continue; + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, lines, 0x06, value); + DBG("LED pack: line=%d value=0x%04X\n", i, value); + pack->datalen = len; + packet_send(xbus, pack); + } + return ret; +} + +/* 0x0F */ HOSTCMD(FXS, RELAY_OUT, byte which, bool on) +{ + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + int len; + int value; + xpp_line_t lines; + int relay_channels[] = { 0, 4 }; + + BUG_ON(!xbus); + BUG_ON(!xpd); + + DBG("RELAY_OUT: which=%d -- %s\n", which, (on) ? "on" : "off"); + which = which % ARRAY_SIZE(relay_channels); + lines = BIT(relay_channels[which]); + value = BIT(2) | BIT(3); + value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[LED_BLUE]); + if(on) + value |= led_vals[LED_BLUE]; + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, lines, 0x06, value); + + DBG("RELAY_OUT pack: line=%d value=0x%04X\n", lines, value); + pack->datalen = len; + packet_send(xbus, pack); + return ret; +} + +/* 0x0F */ HOSTCMD(FXS, SLIC_INIT) +{ + int ret = 0; + xpacket_t *pack; + slic_data_t *slic; + struct slic_init_data *source; + int i; + + BUG_ON(!xbus); + BUG_ON(!xpd); + DBG("INITIALIZING SLIC\n"); + for(i = 0; i < ARRAY_SIZE(slic_init_data); i++) { + source = &slic_init_data[i]; + XPACKET_NEW(pack, xbus, FXS, SLIC_INIT, xpd->id); + RPACKET_FIELD(pack, FXS, SLIC_INIT, lines) = source->lines; + + slic = &RPACKET_FIELD(pack, FXS, SLIC_INIT, slic_data); + slic->len = source->slic_data.len; + memcpy(slic->data, source->slic_data.data, source->slic_data.len); + pack->datalen = sizeof(xpp_line_t) + slic->len + 1; +// dump_packet("SLIC", pack, print_dbg); + packet_send(xbus, pack); + mdelay(10); // FIXME: check with Dima + } + return ret; +} + +/* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num) +{ + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + DBG("\n"); + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_read(sc, BIT(pos), reg_num); + + pack->datalen = len; + + packet_send(xbus, pack); + return ret; +} + +/*---------------- FXS: Astribank Reply Handlers --------------------------*/ + +HANDLER_DEF(FXS, SIG_CHANGED) +{ + xpp_line_t sig_status = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_status); + + if(!xpd) { + NOTICE("%s: received %s for non-existing xpd: %d\n", + __FUNCTION__, cmd->name, XPD_NUM(pack->content.addr)); + return -EPROTO; + } + if(xpd->direction == TO_PHONE) { /* Hook state changes */ + DBG("%s (PHONE) sig_status=0x%04X\n", xpd->xpdname, sig_status); + xpp_check_hookstate(xpd, sig_status); + } else { /* TO_PSTN - line ring changes */ + unsigned long flags; + int i; + + DBG("%s (PSTN) sig_status=0x%04X\n", xpd->xpdname, sig_status); + spin_lock_irqsave(&xpd->lock, flags); + for(i = 0; i < xpd->channels; i++) { + if(IS_SET(sig_status, i)) { + xpd->ringing[i] = RINGS_NUM*2; + zt_hooksig(&xpd->chans[i], ZT_RXSIG_OFFHOOK); + } else { + zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK); + xpd->ringing[i] = 0; + } + } + spin_unlock_irqrestore(&xpd->lock, flags); + } + return 0; +} + +HANDLER_DEF(FXS, SLIC_REPLY) +{ + slic_reply_t *info = &RPACKET_FIELD(pack, FXS, SLIC_REPLY, info); + xpp_line_t lines = RPACKET_FIELD(pack, FXS, SLIC_REPLY, lines); + unsigned long flags; + struct FXS_priv_data *priv; + + if(!xpd) { + NOTICE("%s: received %s for non-existing xpd: %d\n", + __FUNCTION__, cmd->name, XPD_NUM(pack->content.addr)); + return -EPROTO; + } + spin_lock_irqsave(&xpd->lock, flags); + priv = xpd->priv; + BUG_ON(!priv); + DBG("SLIC_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n", + xpd->id, (info->indirect)?"I":"D", + info->reg_num, info->data_low, info->data_high); + priv->last_reply = *info; + if(xpd->id == 0 && info->indirect == 0 && info->reg_num == 0x06) { /* Digital Inputs Poll Result */ + int i; + bool offhook = (info->data_low & 0x1) == 0; + + /* Map SLIC number into line number */ + for(i = 0; i < ARRAY_SIZE(input_channels); i++) { + int channo = input_channels[i]; + int newchanno; + + if(IS_SET(lines, channo)) { + newchanno = LINES_REGULAR + LINES_DIGI_OUT + i; + BIT_CLR(lines, channo); + BIT_SET(lines, newchanno); + phone_hook(xpd, newchanno, offhook); + } + } + } + spin_unlock_irqrestore(&xpd->lock, flags); + return 0; +} + + +xproto_table_t PROTO_TABLE(FXS) = { + .entries = { + /* Card Opcode */ + XENTRY( FXS, SIG_CHANGED ), + XENTRY( FXS, SLIC_REPLY ), + }, + .name = "FXS", + .type = XPD_TYPE(FXS), + .xops = { + .card_new = FXS_card_new, + .card_init = FXS_card_init, + .card_remove = FXS_card_remove, + .card_tick = FXS_card_tick, + + .RING = XPROTO_CALLER(FXS, RING), + .SETHOOK = XPROTO_CALLER(FXS, SETHOOK), + .LED = XPROTO_CALLER(FXS, LED), + .RELAY_OUT = XPROTO_CALLER(FXS, RELAY_OUT), + .CHAN_ENABLE = XPROTO_CALLER(FXS, CHAN_ENABLE), + .CHAN_POWER = XPROTO_CALLER(FXS, CHAN_POWER), + .CHAN_CID = XPROTO_CALLER(FXS, CHAN_CID), + + .SYNC_SOURCE = XPROTO_CALLER(GLOBAL, SYNC_SOURCE), + .PCM_WRITE = XPROTO_CALLER(GLOBAL, PCM_WRITE), + }, + .packet_is_valid = fxs_packet_is_valid, + .packet_dump = fxs_packet_dump, +}; + +static bool fxs_packet_is_valid(xpacket_t *pack) +{ + const xproto_entry_t *xe; + + DBG("\n"); + xe = xproto_card_entry(&PROTO_TABLE(FXS), pack->content.opcode); + return xe != NULL; +} + +static void fxs_packet_dump(xpacket_t *pack) +{ + DBG("\n"); +} + +/*------------------------- SLIC Handling --------------------------*/ + +static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + xpd_t *xpd = data; + slic_reply_t *info; + struct FXS_priv_data *priv; + + BUG_ON(!xpd); + spin_lock_irqsave(&xpd->lock, flags); + priv = xpd->priv; + BUG_ON(!priv); + info = &priv->last_reply; + len += sprintf(page + len, "# Writing bad data into this file may damage your hardware!\n"); + len += sprintf(page + len, "# Consult firmware docs first\n"); + len += sprintf(page + len, "SLIC_REPLY: %s reg_num=0x%X, dataH=0x%X dataL=0x%X\n", + (info->indirect)?"I":"D", + info->reg_num, info->data_high, info->data_low); + spin_unlock_irqrestore(&xpd->lock, flags); + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +/* + * Direct/Indirect + * v + * FF FF FF FF WD 06 1 + * ^---------^ ^ Reg + * | Write/Read + * | + * SLIC # + */ +static int parse_slic_cmd(const char *buf, slic_cmd_t *sc) +{ + char op; /* [W]rite, [R]ead */ + char reg_type; /* [D]irect, [I]ndirect */ + int s1, s2, s3, s4; + int reg_num; + int data_low, data_high; + xpp_line_t lines; + int ret; + + ret = sscanf(buf, "%x %x %x %x %c%c %x %x %x", + &s1, &s2, &s3, &s4, &op, ®_type, ®_num, &data_high, &data_low); + lines = (s4 << 24) | (s3 << 16) | (s2 << 8) | (s1); + switch(op) { + case 'R': + if(reg_type == 'D' && ret == 7) { + // DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num); + ret = slic_cmd_direct_read(sc, lines, reg_num); + } else if(reg_type == 'I' && ret == 7) { + // DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num); + ret = slic_cmd_indirect_read(sc, lines, reg_num); + } else { + NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type); + goto err; + } + break; + case 'W': + if(reg_type == 'D' && ret == 8) { + // DBG("0x%X 0x%X 0x%X 0x%X %c %x %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high); + ret = slic_cmd_direct_write(sc, lines, reg_num, data_high); + } else if(reg_type == 'I' && ret == 9) { + // DBG("0x%X 0x%X 0x%X 0x%X %c %x %X %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high, data_low); + ret = slic_cmd_indirect_write(sc, lines, reg_num, data_low, data_high); + } else { + NOTICE("%s: Bad write input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type); + goto err; + } + break; + default: + NOTICE("%s: Bad input: ret=%d buf='%s' op=%c\n", __FUNCTION__, ret, buf, op); + goto err; + } + return ret; +err: + return -EINVAL; +} + +static int process_slic_cmdline(xpd_t *xpd, char *cmdline) +{ + xbus_t *xbus; + slic_cmd_t sc; + xpacket_t *pack; + char *p; + int len = strlen(cmdline); + + BUG_ON(!xpd); + xbus = xpd->xbus; + if((p = strchr(cmdline, '#')) != NULL) /* Truncate comments */ + *p = '\0'; + if((p = strchr(cmdline, ';')) != NULL) /* Truncate comments */ + *p = '\0'; + for(p = cmdline; *p && (*p == ' ' || *p == '\t'); p++) /* Trim leading whitespace */ + ; + if(*p == '\0') + return 0; + len = parse_slic_cmd(p, &sc); + if(len < 0) + return len; + sc.lines &= xpd->enabled_chans; // Ignore disabled channels + if(!sc.lines) { + NOTICE("%s: no enabled channels are marked. Skip.\n", __FUNCTION__); + return 0; + } + dump_slic_cmd("WRITE_SLIC", &sc); + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd) = sc; + pack->datalen = len; + packet_send(xbus, pack); + return 0; +} + +static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + xpd_t *xpd = data; + const int LINE_LEN = 500; + char buf[LINE_LEN]; + char *p; + int i; + int ret; + + BUG_ON(!xpd); + for(i = 0; i < count; /* noop */) { + for(p = buf; p < buf + LINE_LEN; p++) { /* read a line */ + if(i >= count) + break; + if(get_user(*p, buffer + i)) + return -EFAULT; + i++; + if(*p == '\n' || *p == '\r') /* whatever */ + break; + } + if(p >= buf + LINE_LEN) + return -E2BIG; + *p = '\0'; + ret = process_slic_cmdline(xpd, buf); + if(ret < 0) + return ret; + } + return count; +} + + +int __init card_fxs_startup(void) +{ + INFO("%s revision %s\n", THIS_MODULE->name, revision); + xproto_register(&PROTO_TABLE(FXS)); + return 0; +} + +void __exit card_fxs_cleanup(void) +{ + xproto_unregister(&PROTO_TABLE(FXS)); +} + +MODULE_DESCRIPTION("XPP FXS Card Driver"); +MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("$Id$"); + +module_init(card_fxs_startup); +module_exit(card_fxs_cleanup); diff --git a/xpp/card_fxs.h b/xpp/card_fxs.h new file mode 100644 index 0000000..503b34e --- /dev/null +++ b/xpp/card_fxs.h @@ -0,0 +1,45 @@ +#ifndef CARD_FXS_H +#define CARD_FXS_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 "xpd.h" +#include "slic.h" + +DEF_RPACKET_DATA(FXS, SIG_CHANGED, + byte type; /* unused -- we have it from DEV_DESC */ + xpp_line_t sig_status; /* channels: lsb=1, msb=8 */ + xpp_line_t sig_toggles; /* channels: lsb=1, msb=8 */ + ); +DEF_RPACKET_DATA(FXS, SLIC_REPLY, /* Get status of a single SLIC (for debugging) */ + xpp_line_t lines; + slic_reply_t info; + ); +DEF_RPACKET_DATA(FXS, SLIC_INIT, + xpp_line_t lines; + slic_data_t slic_data; + ); +DEF_RPACKET_DATA(FXS, SLIC_WRITE, + slic_cmd_t slic_cmd; + ); + +#endif /* CARD_FXS_H */ diff --git a/xpp/card_global.c b/xpp/card_global.c new file mode 100644 index 0000000..8b73f6e --- /dev/null +++ b/xpp/card_global.c @@ -0,0 +1,256 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 "xdefs.h" +#include "xpd.h" +#include "xpp_zap.h" +#include "xproto.h" +#include <linux/module.h> + +static const char rcsid[] = "$Id$"; + +extern int print_dbg; +static bool pcm_valid(xpd_t *xpd, xpacket_t *pack); + +/*---------------- GLOBAL Protocol Commands -------------------------------*/ + +static bool global_packet_is_valid(xpacket_t *pack); +static void global_packet_dump(xpacket_t *pack); + +/*---------------- GLOBAL: HOST COMMANDS ----------------------------------*/ + +/* 0x04 */ HOSTCMD(GLOBAL, DESC_REQ, int xpd_num) +{ + int ret = 0; + xpacket_t *pack; + + if(!xbus) { + DBG("NO XBUS\n"); + return -EINVAL; + } + XPACKET_NEW(pack, xbus, GLOBAL, DESC_REQ, xpd_num); + DBG("on %s #%d\n", xbus->busname, xpd_num); + ret = packet_send(xbus, pack); + XBUS_COUNTER(xbus, DESC_REQ)++; + return ret; +} + +/* 0x11 */ HOSTCMD(GLOBAL, PCM_WRITE, xpp_line_t lines, volatile byte *buf) +{ + int ret = 0; + xpacket_t *pack; + byte *pcm; + byte *start_pcm; + int i; + extern ulong pcm_gen; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; + if(pcm_gen != 0) + return 0; +// if(lines == 0) +// return 0; + + /* + * FIXME: Workaround a bug in sync code of the Astribank. + * Send dummy PCM for sync. + */ + if(lines == 0) + lines = BIT(0); + + XPACKET_NEW(pack, xbus, GLOBAL, PCM_WRITE, xpd->id); + RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = lines; + start_pcm = pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm); + for(i = 0; i < CHANNELS_PERXPD; i++) { + if(IS_SET(lines, i)) { + memcpy(pcm, (byte *)buf, ZT_CHUNKSIZE); + pcm += ZT_CHUNKSIZE; + } + buf += ZT_CHUNKSIZE; + } + pack->datalen = sizeof(xpp_line_t) + (pcm - start_pcm); + packet_send(xbus, pack); + XPD_COUNTER(xpd, PCM_WRITE)++; + XBUS_COUNTER(xbus, PCM_WRITE)++; + return ret; +} + +/* 0x19 */ HOSTCMD(GLOBAL, SYNC_SOURCE, bool setit, bool is_master) +{ + xpacket_t *pack; + byte mask = 0; + + BUG_ON(!xbus); + BUG_ON(!xpd); + if(is_master) + mask |= BIT(0); + if(!setit) + mask |= BIT(1); + DBG("SYNC_SOURCE %s setit=%s is_master=%s (mask=0x%X)\n", + xpd->xpdname, (setit)?"yes":"no", (is_master)?"yes":"no", mask); + XPACKET_NEW(pack, xbus, GLOBAL, SYNC_SOURCE, xpd->id); + RPACKET_FIELD(pack, GLOBAL, SYNC_SOURCE, mask) = mask; + packet_send(xbus, pack); + return 0; +} + +/*---------------- GLOBAL: Astribank Reply Handlers -----------------------*/ + +HANDLER_DEF(GLOBAL, DEV_DESC) +{ + byte rev = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, rev); + byte type = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, type); + xpp_line_t line_status = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, line_status); + int xpd_num = XPD_NUM(pack->content.addr); + struct card_desc_struct *card_desc; + + DBG("xpd=%d type=%d rev=%d line_status=0x%04X\n", + xpd_num, type, rev, line_status); + if((card_desc = kmalloc(sizeof(struct card_desc_struct), GFP_ATOMIC)) == NULL) { + ERR("%s: Card description allocation failed.\n", __FUNCTION__); + return -ENOMEM; + } + memset(card_desc, 0, sizeof(struct card_desc_struct)); + card_desc->magic = CARD_DESC_MAGIC; + card_desc->xbus = xbus; + card_desc->type = type; + card_desc->rev = rev; + card_desc->xpd_num = xpd_num; + INIT_WORK(&card_desc->work, card_detected, card_desc); + DBG("Queueing xpp_worker for xpd %d\n", xpd_num); + if(!queue_work(xpp_worker, &card_desc->work)) { + ERR("Failed to queue card description work\n"); + return -EINVAL; + } + return 0; +} + +HANDLER_DEF(GLOBAL, PCM_READ) +{ + /* FIXME: work around temporary hardware bug */ + xpp_line_t lines = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines); + const byte *pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm); + volatile u_char *readchunk; + volatile u_char *r; + unsigned long flags; + int i; + + if(!xpd) { +#if 0 + int xpd_num = XPD_NUM(pack->content.addr); + NOTICE("%s: received %s for non-existing xpd: %d\n", + __FUNCTION__, cmd->name, xpd_num); +#endif + return -EPROTO; + } + // DBG("lines=0x%04X\n", lines); + + if(!pcm_valid(xpd, pack)) { + return -EPROTO; + } + spin_lock_irqsave(&xpd->lock, flags); + if (xpd->timer_count & 1) { + /* First part */ + r = readchunk = xpd->readchunk; + } else { + r = readchunk = xpd->readchunk + ZT_CHUNKSIZE * CHANNELS_PERXPD; + } + + /* Copy PCM and put each channel in its index */ + for (i = 0; i < CHANNELS_PERXPD; i++) { + if(IS_SET(lines, i)) { + memcpy((u_char *)r, pcm, ZT_CHUNKSIZE); + //memset((u_char *)r, 0x5A, ZT_CHUNKSIZE); // DEBUG + pcm += ZT_CHUNKSIZE; + } + r += ZT_CHUNKSIZE; + } + + XPD_COUNTER(xpd, PCM_READ)++; + XBUS_COUNTER(xpd->xbus, PCM_READ)++; + spin_unlock_irqrestore(&xpd->lock, flags); + xpp_tick((unsigned long)xpd); + return 0; +} + +HANDLER_DEF(GLOBAL, SYNC_REPLY) +{ + if(!xpd) { + int xpd_num = XPD_NUM(pack->content.addr); + NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num); + return -EPROTO; + } + DBG("SYNC_REPLY: 0x%X\n", RPACKET_FIELD(pack, GLOBAL, SYNC_REPLY, mask)); + return 0; +} + + +xproto_table_t PROTO_TABLE(GLOBAL) = { + .entries = { + /* Card Opcode */ + XENTRY( GLOBAL, DEV_DESC ), + XENTRY( GLOBAL, PCM_READ ), + XENTRY( GLOBAL, SYNC_REPLY ), + }, + .name = "GLOBAL", + .packet_is_valid = global_packet_is_valid, + .packet_dump = global_packet_dump, +}; + +static bool global_packet_is_valid(xpacket_t *pack) +{ + const xproto_entry_t *xe; + + //DBG("\n"); + xe = xproto_global_entry(pack->content.opcode); + return xe != NULL; +} + +static void global_packet_dump(xpacket_t *pack) +{ + DBG("\n"); +} + +static bool pcm_valid(xpd_t *xpd, xpacket_t *pack) +{ + xpp_line_t lines = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines); + int i; + int count = 0; + + BUG_ON(!pack); + BUG_ON(pack->content.opcode != XPROTO_NAME(GLOBAL, PCM_READ)); + for (i = 0; i < CHANNELS_PERXPD; i++) + if(IS_SET(lines, i)) + count++; + if(pack->datalen != (sizeof(xpp_line_t) + count * 8)) { + static int rate_limit = 0; + + XPD_COUNTER(xpd, RECV_ERRORS)++; + if((rate_limit++ % 1000) <= 10) { + ERR("BAD PCM REPLY: pack->datalen=%d, count=%d\n", pack->datalen, count); + } + return 0; + } + return 1; +} + diff --git a/xpp/card_global.h b/xpp/card_global.h new file mode 100644 index 0000000..377487c --- /dev/null +++ b/xpp/card_global.h @@ -0,0 +1,55 @@ +#ifndef CARD_GLOBAL_H +#define CARD_GLOBAL_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 "xdefs.h" + +DEF_RPACKET_DATA(GLOBAL, DESC_REQ); +DEF_RPACKET_DATA(GLOBAL, DEV_DESC, + byte rev; /* Revision number */ + byte type; /* LSB: 1 - to_phone, 0 - to_line */ + xpp_line_t line_status; /* hook/ring status, depending on unit */ + ); +DEF_RPACKET_DATA(GLOBAL, PCM_WRITE, + xpp_line_t lines; // Must be 0xFF + byte pcm[PCM_CHUNKSIZE]; + ); +DEF_RPACKET_DATA(GLOBAL, PCM_READ, + xpp_line_t lines; // Must be 0xFF + byte pcm[PCM_CHUNKSIZE]; + ); +DEF_RPACKET_DATA(GLOBAL, SYNC_SOURCE, + byte mask; + ); +DEF_RPACKET_DATA(GLOBAL, SYNC_REPLY, + byte mask; + ); + + +/* 0x04 */ DECLARE_CMD(GLOBAL, DESC_REQ, int xpd_num); +/* 0x19 */ DECLARE_CMD(GLOBAL, SYNC_SOURCE, bool setit, bool is_master); +/* 0x11 */ DECLARE_CMD(GLOBAL, PCM_WRITE, xpp_line_t lines, volatile byte *buf); + +extern xproto_table_t PROTO_TABLE(GLOBAL); + +#endif /* CARD_GLOBAL_H */ diff --git a/xpp/cards.c b/xpp/cards.c new file mode 100644 index 0000000..dec0570 --- /dev/null +++ b/xpp/cards.c @@ -0,0 +1,394 @@ +#include <linux/module.h> +#include "xpd.h" +#include "xpp_zap.h" +#include "xpp_proto.h" +#include "cards.h" + +static char rcsid[] = "$Id$"; + +extern int print_dbg; +#include "zap_debug.h" + +#define MAX_SLIC_REGISTERS 100 + +struct FXS_private_data { + slic_reply_t last_slic_reply; +}; + +/*------------------------- FXS Functions --------------------------*/ +int FXS_card_new(xpd_t *xpd) +{ + xpd->direction = TO_PHONE; + xpd->channels = min(8, CHANNELS_PERXPD); + if(xpd->id == 0) { + DBG("First XPD detected. Initialize digital outputs\n"); + xpd->channels += 2; + xpd->digital_outputs = BIT(8) | BIT(9); // Two extra channels + } + return 0; +} + +int FXS_card_remove(xpd_t *xpd) +{ + return 0; +} + +static int FXS_card_startup(struct zt_span *span) +{ + DBG("\n"); + return 0; +} + +/* + * Called only for 'span' keyword in /etc/zaptel.conf + */ +static int FXS_card_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + xpd_t *xpd = span->pvt; + + DBG("%s\n", xpd->xpdname); + return 0; +} + +/* Set signalling type (if appropriate) */ +static int FXS_card_chanconfig(struct zt_chan *chan, int sigtype) +{ + DBG("channel %d (%s), sigtype %d.\n", chan->channo, chan->name, sigtype); + dump_sigtype(print_dbg, " ", sigtype); + // FIXME: sanity checks: + // - should be supported (within the sigcap) + // - should not replace fxs <->fxo ??? (covered by previous?) + return 0; +} + +/* + * Called only for 'span' keyword in /etc/zaptel.conf + */ +static int FXS_card_shutdown(struct zt_span *span) +{ + xpd_t *xpd = span->pvt; + + DBG("%s\n", xpd->xpdname); + return 0; +} + +static int FXS_card_sethook(struct zt_chan *chan, int hookstate) +{ + int pos = chan->chanpos - 1; + xpd_t *xpd = chan->pvt; + xbus_t *xbus; + int ret = 0; + + if(!xpd) { + ERR("%s: channel=%d without an XPD!\n", __FUNCTION__, pos); + return -EINVAL; + } + xbus = xpd->xbus; + // DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate); + switch(hookstate) { + /* On-hook, off-hook: The PBX is playing a phone on an FXO line. + * Can be ignored for an FXS line + */ + case ZT_ONHOOK: + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("ZT_ONHOOK %s digital output OFF\n", chan->name); + ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); + return ret; + } + DBG("ZT_ONHOOK: %s hookstate=0x%04X (stop ringing pos=%d)\n", chan->name, xpd->hookstate, pos); + xpd->ringing[pos] = 0; +#if 1 // FIXME: Not needed -- verify + ret = CALL_PROTO(RING, xbus, xpd, pos, 0); // RING off +#endif + ret = CALL_PROTO(LED, xpd->xbus, xpd, BIT(pos), LED_GREEN, 0); + ret = CALL_PROTO(CHAN_POWER, xbus, xpd, BIT(pos), 0); // Power down (prevent overheating!!!) + if(ret) { + DBG("ZT_ONHOOK(stop ring) Failed: ret=0x%02X\n", ret); + break; + } + break; + case ZT_START: + DBG("ZT_START: %s hookstate=0x%04X (fall through ZT_OFFHOOK)\n", chan->name, xpd->hookstate); + // Fall through + case ZT_OFFHOOK: + DBG("ZT_OFFHOOK: %s hookstate=0x%04X -- ignoring (FXS)\n", chan->name, xpd->hookstate); + break; + case ZT_WINK: + DBG("ZT_WINK %s\n", chan->name); + break; + case ZT_FLASH: + DBG("ZT_FLASH %s\n", chan->name); + break; + case ZT_RING: + DBG("ZT_RING %s pos=%d (ringing[pos]=%d)\n", chan->name, pos, xpd->ringing[pos]); + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("ZT_ONHOOK %s digital output ON\n", chan->name); + ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 1); + return ret; + } + xpd->ringing[pos] = RINGS_NUM*2; + ret = CALL_PROTO(CHAN_POWER, xbus, xpd, (1 << pos), 1); // Power up (for ring) + ret = CALL_PROTO(RING, xbus, xpd, pos, 1); // RING on + if(ret) { + DBG("ZT_RING Failed: ret=0x%02X\n", ret); + } + break; + case ZT_RINGOFF: + DBG("ZT_RINGOFF %s\n", chan->name); + break; + default: + DBG("UNKNOWN hookstate=0x%X\n", hookstate); + } + return ret; +} + +static int FXS_card_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + default: + return xpp_ioctl(chan, cmd, arg); + } + return 0; +} + +#if 0 +int FXS_zaptel_setup(xpd_t *xpd) +{ + struct zt_chan *cur_chan; + struct zt_span *span; + xbus_t *xbus; + int i; + int cn; + + BUG_ON(!xpd); + + sigfxs = ! (xpd->direction == TO_PHONE); /* signaling is opposite */ + cn = xpd->channels; + DBG("Initializing span: xpd %d have %d channels.\n", xpd->id, cn); + + xpd->chans = kmalloc(sizeof(struct zt_chan)*cn, GFP_ATOMIC); + if (xpd->chans == NULL) { + ERR("xpd: Unable to allocate channels\n"); + return -ENOMEM; + } + memset(xpd->chans, 0, sizeof(struct zt_chan)*cn); + memset(&xpd->span, 0, sizeof(struct zt_span)); + + span = &xpd->span; + xbus = xpd->xbus; + snprintf(span->name, MAX_SPANNAME, "%s/%s", + xbus->busname, xpd->xpdname); + { + char tmp[MAX_SPANNAME]; + struct xpd_sim *sim = &xbus->sim[xpd->id]; + + if(sim->simulated) + snprintf(tmp, MAX_SPANNAME, " (sim to=%d)", sim->loopto); + else + tmp[0] = '\0'; + + snprintf(span->desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: %s%s", + xbus->num, xpd->id, + (xpd->direction == TO_PHONE) ? "FXS" : "FXO", + tmp + ); + } + for(i = 0; i < cn; i++) { + + cur_chan = &xpd->chans[i]; + DBG("setting channel %d\n", i); + snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXS/%d-%d", xpd->id, i); + cur_chan->chanpos = i + 1; + cur_chan->pvt = xpd; + cur_chan->sigcap = +#if 1 + ZT_SIG_FXOKS | + ZT_SIG_FXOLS | + ZT_SIG_FXOGS | +#else + ZT_SIG_SF | + ZT_SIG_EM | +#endif + 0; + } + span->deflaw = ZT_LAW_MULAW; + init_waitqueue_head(&span->maintq); + span->pvt = xpd; + span->channels = cn; + span->chans = xpd->chans; + + span->startup = FXS_xpp_startup; + span->shutdown = FXS_xpp_shutdown; + span->spanconfig = FXS_xpp_spanconfig; + span->chanconfig = FXS_xpp_chanconfig; + span->open = xpp_open; + span->close = xpp_close; +#ifdef WITH_RBS + span->flags = ZT_FLAG_RBS; + span->hooksig = xpp_hooksig; /* Only with RBS bits */ +#else + span->sethook = FXS_xpp_sethook; +#endif + span->ioctl = FXS_xpp_ioctl; + span->maint = xpp_maint; +#ifdef CONFIG_ZAPTEL_WATCHDOG + span->watchdog = xpp_watchdog; +#endif + + return 0; +} +#endif + +int FXS_zaptel_cleanup(xpd_t *xpd) +{ + return 0; +} + +/*------------------------- FXO Functions --------------------------*/ +static int FXO_card_new(xpd_t *xpd) +{ + xpd->direction = TO_TRUNK; + xpd->channels = min(8, CHANNELS_PERXPD); + return 0; +} + +static int FXO_card_remove(xpd_t *xpd) +{ + return 0; +} + +static int FXO_card_startup(struct zt_span *span) +{ + DBG("\n"); + return 0; +} + +/* + * Called only for 'span' keyword in /etc/zaptel.conf + */ +static int FXO_card_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + xpd_t *xpd = span->pvt; + + DBG("%s\n", xpd->xpdname); + return 0; +} + +/* Set signalling type (if appropriate) */ +static int FXO_card_chanconfig(struct zt_chan *chan, int sigtype) +{ + DBG("channel %d (%s), sigtype %d.\n", chan->channo, chan->name, sigtype); + dump_sigtype(print_dbg, " ", sigtype); + // FIXME: sanity checks: + // - should be supported (within the sigcap) + // - should not replace fxs <->fxo ??? (covered by previous?) + return 0; +} + +/* + * Called only for 'span' keyword in /etc/zaptel.conf + */ +static int FXO_card_shutdown(struct zt_span *span) +{ + xpd_t *xpd = span->pvt; + + DBG("%s\n", xpd->xpdname); + return 0; +} + + +static int FXO_card_sethook(struct zt_chan *chan, int hookstate) +{ + int pos = chan->chanpos - 1; + xpd_t *xpd = chan->pvt; + xbus_t *xbus; + int ret = 0; + + BUG_ON(!xpd); + xbus = xpd->xbus; + // DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate); + switch(hookstate) { + /* On-hook, off-hook: The PBX is playing a phone on an FXO line. + * Can be ignored for an FXS line + */ + case ZT_ONHOOK: + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("ZT_ONHOOK %s digital output OFF\n", chan->name); + ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); + return ret; + } + DBG("ZT_ONHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos); + xpd->ringing[pos] = 0; + BIT_CLR(xpd->hookstate, pos); + ret = CALL_PROTO(SETHOOK, xbus, xpd, xpd->hookstate); + if(ret) { + DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret); + break; + } + break; + case ZT_START: + DBG("ZT_START: %s hookstate=0x%04X (fall through ZT_OFFHOOK)\n", chan->name, xpd->hookstate); + // Fall through + case ZT_OFFHOOK: + DBG("ZT_OFFHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos); + BIT_SET(xpd->hookstate, pos); + xpd->ringing[pos] = 0; + ret = CALL_PROTO(SETHOOK, xbus, xpd, xpd->hookstate); + if(ret) { + DBG("ZT_OFFHOOK Failed: ret=0x%02X\n", ret); + break; + } + break; + case ZT_WINK: + DBG("ZT_WINK %s\n", chan->name); + break; + case ZT_FLASH: + DBG("ZT_FLASH %s\n", chan->name); + break; + case ZT_RING: + DBG("ZT_RING %s pos=%d (ringing[pos]=%d)\n", chan->name, pos, xpd->ringing[pos]); + break; + case ZT_RINGOFF: + DBG("ZT_RINGOFF %s\n", chan->name); + break; + default: + DBG("UNKNOWN hookstate=0x%X\n", hookstate); + } + return ret; +} + +static int FXO_card_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + default: + return xpp_ioctl(chan, cmd, arg); + } + return 0; +} + + +/*------------------------- Function table -------------------------*/ + +#define DEF_(t) \ + [XPD_TYPE_ ## t] { \ + .card_new = t ## _card_new, \ + .card_remove = t ## _card_remove, \ + .card_startup = t ## _card_startup, \ + .card_shutdown = t ## _card_shutdown, \ + .card_spanconfig = t ## _card_spanconfig, \ + .card_chanconfig = t ## _card_chanconfig, \ + .card_sethook = t ## _card_sethook, \ + .card_ioctl = t ## _card_ioctl, \ + } + +xops_t xpd_card_ops[XPD_TYPE_NOMODULE] = { + DEF_(FXS), + DEF_(FXO) +}; + +xops_t *get_xops(xpd_type_t xpd_type) +{ + if(xpd_type >= XPD_TYPE_NOMODULE) + return NULL; + return &xpd_card_ops[xpd_type]; +} diff --git a/xpp/cards.h b/xpp/cards.h new file mode 100644 index 0000000..26e53f0 --- /dev/null +++ b/xpp/cards.h @@ -0,0 +1,23 @@ +#ifndef CARDS_H +#define CARDS_H + +#include "xpd.h" + +struct xpd_card_ops { + int (*card_new)(xpd_t *xpd); + int (*card_remove)(xpd_t *xpd); +#if 0 + int (*zaptel_setup)(xpd_t *xpd); + int (*zaptel_cleanup)(xpd_t *xpd); +#endif + int (*card_startup)(struct zt_span *span); + int (*card_shutdown)(struct zt_span *span); + int (*card_spanconfig)(struct zt_span *span, struct zt_lineconfig *lc); + int (*card_chanconfig)(struct zt_chan *chan, int sigtype); + int (*card_sethook)(struct zt_chan *chan, int hookstate); + int (*card_ioctl)(struct zt_chan *chan, unsigned int cmd, unsigned long arg); +}; + +xops_t *get_xops(xpd_type_t xpd_type); + +#endif /* CARDS_H */ diff --git a/xpp/gen_slic_init b/xpp/gen_slic_init new file mode 100755 index 0000000..f102c33 --- /dev/null +++ b/xpp/gen_slic_init @@ -0,0 +1,37 @@ +#! /usr/bin/perl -w + +use strict; + +my $input; +my $header; +my $comment; + +@ARGV == 2 or die "Usage: $0 <infile> <outfile>\n"; +$input = $ARGV[0]; +$header = $ARGV[1]; +open(IF, "$input") or die "Failed to write '$input': $!\n"; +open(HF, ">$header") or die "Failed to write '$header': $!\n"; + +while(<IF>) { + chomp; + undef $comment; + s/\r//; # CRLF -> LF + if(s/\s*[;#]\s*(.*?)$//) { # Comments + $comment = $1; + } + if(/^\s*$/) { # Empty lines + next; + } + my ($slic0, $slic1, $slic2, $slic3, $len, @data) = split; + die "Bad input (len=$len)" if hex($len) != @data; + my $slic = "$slic3$slic2$slic1$slic0"; + die "Bad slic address '$slic'" if length($slic) != 8; + grep(s/\w+/0x$&/g, ($slic, $len, @data)); + my $str = join(", ", @data); + print HF "S_($slic,\t$len,\t$str),\t"; +} continue { + if(defined($comment)) { + print HF "// $comment"; + } + print HF "\n"; +} diff --git a/xpp/slic.c b/xpp/slic.c new file mode 100644 index 0000000..fd718a5 --- /dev/null +++ b/xpp/slic.c @@ -0,0 +1,129 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 "xproto.h" +#include "slic.h" + +static const char rcsid[] = "$Id$"; + +#ifdef __KERNEL__ +#include <linux/module.h> + +extern int print_dbg; +#include "zap_debug.h" +#else +#include <stdio.h> +#endif + +int slic_cmd_direct_write(slic_cmd_t *sc, xpp_line_t lines, byte reg, byte data) +{ + struct slic_reg_d *p = (struct slic_reg_d *)&sc->content; + + sc->lines = lines; + sc->bytes = sizeof(struct slic_reg_d); + SLIC_REG_INIT(p, 0, reg, data); + return sizeof(xpp_line_t) + 1 + sc->bytes; +} + +int slic_cmd_direct_read(slic_cmd_t *sc, xpp_line_t lines, byte reg) +{ + struct slic_reg_d *p = (struct slic_reg_d *)&sc->content; + + sc->lines = lines; + sc->bytes = sizeof(struct slic_reg_d); + SLIC_REG_INIT(p, 1, reg, 0); + return sizeof(xpp_line_t) + 1 + sc->bytes; +} + +int slic_cmd_indirect_write(slic_cmd_t *sc, xpp_line_t lines, byte reg, byte data_low, byte data_high) +{ + struct slic_reg_iw *p = (struct slic_reg_iw *)&sc->content; + + sc->lines = lines; + sc->bytes = sizeof(struct slic_reg_iw); + SLIC_REG_INIT(&p->iw_data_low, 0, 0x1C, data_low); + SLIC_REG_INIT(&p->iw_data_high, 0, 0x1D, data_high); + SLIC_REG_INIT(&p->iw_reg, 0, 0x1E, reg); + return sizeof(xpp_line_t) + 1 + sc->bytes; +} + +int slic_cmd_indirect_read(slic_cmd_t *sc, xpp_line_t lines, byte reg) +{ + struct slic_reg_ir *p = (struct slic_reg_ir *)&sc->content; + + sc->lines = lines; + sc->bytes = sizeof(struct slic_reg_ir); + SLIC_REG_INIT(&p->ir_reg, 0, 0x1E, reg); + return sizeof(xpp_line_t) + 1 + sc->bytes; +} + +void dump_slic_cmd(const char msg[], slic_cmd_t *sc) +{ + int i; + struct slic_reg_d *sr; + int last_data_low = -1; + int last_data_high = -1; + + sr = (struct slic_reg_d *)&sc->content; + if(sc->bytes > sizeof(sc->content)) { + NOTICE("%s: Bug: sc->bytes = %d\n", __FUNCTION__, sc->bytes); + return; + } + if(sc->bytes % 2) { + NOTICE("%s: Bug: ODD sc->bytes = %d\n", __FUNCTION__, sc->bytes); + return; + } + for(i = 0; i < sc->bytes/2; i++, sr++) { + if(sr->reg_num == 0x1C) { + last_data_low = sr->reg_data; + continue; + } + if(sr->reg_num == 0x1D) { + last_data_high = sr->reg_data; + continue; + } + if(sr->reg_num == 0x1E) { + if(last_data_low == -1 && last_data_high == -1) // Indirect Read + DBG("%s: LINES=0x%08X bytes=%d INDIRECT READ: register=0x%02X\n", msg, sc->lines, sc->bytes, sr->reg_data); + else if(last_data_low == -1 || last_data_high == -1) { + NOTICE("%s: BUG: PARTIAL INDIRECT: register=%d last_data_low=0x%X last_data_high=0x%X\n", + msg, sr->reg_data, last_data_low, last_data_high); + } else + DBG("%s: LINES=0x%08X bytes=%d INDIRECT WRITE: register=%d data_low=0x%02x data_high=0x%02X\n", + msg, sc->lines, sc->bytes, sr->reg_data, (byte)last_data_low, (byte)last_data_high); + last_data_low = last_data_high = -1; + } else { + DBG("%s: LINES=0x%08X bytes=%d DIRECT %s: register=%d data=0x%02X\n", + msg, sc->lines, sc->bytes, (sr->read) ? "READ" : "WRITE", sr->reg_num, sr->reg_data); + } + } +} + +#ifdef __KERNEL__ + +EXPORT_SYMBOL(slic_cmd_direct_write); +EXPORT_SYMBOL(slic_cmd_direct_read); +EXPORT_SYMBOL(slic_cmd_indirect_write); +EXPORT_SYMBOL(slic_cmd_indirect_read); +EXPORT_SYMBOL(dump_slic_cmd); + +#endif diff --git a/xpp/slic.h b/xpp/slic.h new file mode 100644 index 0000000..76f2d6e --- /dev/null +++ b/xpp/slic.h @@ -0,0 +1,65 @@ +#ifndef SLIC_H +#define SLIC_H + +#include "xdefs.h" + +/*------------------------------ SLIC Data Structures ----------------------*/ + +struct slic_reg_d { /* SLIC Register Direct Read/Write */ + byte reg_num:7; + byte read:1; + byte reg_data; +} __attribute__((packed)); + +struct slic_reg_iw { /* SLIC Register Indirect-Write */ + struct slic_reg_d iw_data_low; + struct slic_reg_d iw_data_high; + struct slic_reg_d iw_reg; +} __attribute__((packed)); + +struct slic_reg_ir { /* SLIC Register Indirect-Read */ + struct slic_reg_d ir_reg; +} __attribute__((packed)); + +typedef struct slic_cmd { + xpp_line_t lines; + byte bytes; + union { + struct slic_reg_d direct; + struct slic_reg_iw indirect_write; + struct slic_reg_ir indirect_read; + } content; +} __attribute__((packed)) slic_cmd_t; + +typedef struct slic_reply { + byte size; + byte reg_num:7; + byte indirect:1; + byte data_low; + byte data_high; +} __attribute__((packed)) slic_reply_t; + +#define SLIC_REG_INIT(slic_reg, reading, num, data) \ + do { \ + (slic_reg)->read = reading; \ + (slic_reg)->reg_num = num; \ + (slic_reg)->reg_data = data; \ + } while(0); + +/* OLD SLIC_INIT data */ +typedef struct slic_data { + byte len; + byte data[40]; +} __attribute__((packed)) slic_data_t; + + +/*------------------------------ SLIC Initializers -------------------------*/ + +int slic_cmd_direct_write(slic_cmd_t *sc, xpp_line_t lines, byte reg, byte data); +int slic_cmd_direct_read(slic_cmd_t *sc, xpp_line_t lines, byte reg); +int slic_cmd_indirect_write(slic_cmd_t *sc, xpp_line_t lines, byte reg, byte data_low, byte data_high); +int slic_cmd_indirect_read(slic_cmd_t *sc, xpp_line_t lines, byte reg); +void dump_slic_cmd(const char msg[], slic_cmd_t *sc); + + +#endif /* SLIC_H */ diff --git a/xpp/slic_init.inc b/xpp/slic_init.inc new file mode 100644 index 0000000..5f118b8 --- /dev/null +++ b/xpp/slic_init.inc @@ -0,0 +1,65 @@ +// ----------------------------------==== 8-channel FXS unit initialization ===----------------------------------------- + +// INTERNAL PS +// Change SLICs states to "Open state"s (Off,all transfers tristated to avoid data collision), Voltage sense +S_(0x000000FF, 0x04, 0x40, 0x00, 0x6C, 0x01), + + +// ------------------------------------- Initialization of indirect registers ------------------------------------------ +S_(0x000000FF, 0x02, 0x40, 0x00), +S_(0x000000FF, 0x18, 0x1C, 0xC2, 0x1D, 0x55, 0x1E, 0x00, 0x1C, 0xE6, 0x1D, 0x51, 0x1E, 0x01, 0x1C, 0x85, 0x1D, 0x4B, 0x1E, 0x02, 0x1C, 0x37, 0x1D, 0x49, 0x1E, 0x03), +S_(0x000000FF, 0x18, 0x1C, 0x33, 0x1D, 0x33, 0x1E, 0x04, 0x1C, 0x02, 0x1D, 0x02, 0x1E, 0x05, 0x1C, 0x02, 0x1D, 0x02, 0x1E, 0x06, 0x1C, 0x98, 0x1D, 0x01, 0x1E, 0x07), +S_(0x000000FF, 0x18, 0x1C, 0x98, 0x1D, 0x01, 0x1E, 0x08, 0x1C, 0x11, 0x1D, 0x06, 0x1E, 0x09, 0x1C, 0x02, 0x1D, 0x02, 0x1E, 0x0A, 0x1C, 0xE5, 0x1D, 0x00, 0x1E, 0x0B), +S_(0x000000FF, 0x18, 0x1C, 0x1C, 0x1D, 0x0A, 0x1E, 0x0C, 0x1C, 0x30, 0x1D, 0x7B, 0x1E, 0x0D, 0x1C, 0x63, 0x1D, 0x00, 0x1E, 0x0E, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x0F), + +S_(0x000000FF, 0x18, 0x1C, 0x70, 0x1D, 0x78, 0x1E, 0x10, 0x1C, 0x7D, 0x1D, 0x00, 0x1E, 0x11, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x12, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x13), +S_(0x000000FF, 0x18, 0x1C, 0xF0, 0x1D, 0x7E, 0x1E, 0x14, 0x1C, 0x60, 0x1D, 0x01, 0x1E, 0x15, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x16, 0x1C, 0x00, 0x1D, 0x20, 0x1E, 0x17), +S_(0x000000FF, 0x18, 0x1C, 0x00, 0x1D, 0x20, 0x1E, 0x18, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x19, 0x1C, 0x00, 0x1D, 0x40, 0x1E, 0x1A, 0x1C, 0x00, 0x1D, 0x40, 0x1E, 0x1B), +S_(0x000000FF, 0x18, 0x1C, 0x00, 0x1D, 0x18, 0x1E, 0x1C, 0x1C, 0x00, 0x1D, 0x40, 0x1E, 0x1D, 0x1C, 0x00, 0x1D, 0x10, 0x1E, 0x1E, 0x1C, 0x80, 0x1D, 0x00, 0x1E, 0x1F), + +S_(0x000000FF, 0x18, 0x1C, 0xF4, 0x1D, 0x0F, 0x1E, 0x20, 0x1C, 0x7E, 0x1D, 0x6E, 0x1E, 0x21, 0x1C, 0xF4, 0x1D, 0x0F, 0x1E, 0x22, 0x1C, 0x00, 0x1D, 0x88, 0x1E, 0x23), +S_(0x000000FF, 0x18, 0x1C, 0x20, 0x1D, 0x03, 0x1E, 0x24, 0x1C, 0x12, 0x1D, 0x00, 0x1E, 0x25, 0x1C, 0x12, 0x1D, 0x00, 0x1E, 0x26, 0x1C, 0x12, 0x1D, 0x00, 0x1E, 0x27), +S_(0x000000FF, 0x12, 0x1C, 0x00, 0x1D, 0x0C, 0x1E, 0x28, 0x1C, 0x00, 0x1D, 0x0C, 0x1E, 0x29, 0x1C, 0x00, 0x1D, 0x08, 0x1E, 0x2B), + +S_(0x000000FF, 0x18, 0x1C, 0xDA, 0x1D, 0x00, 0x1E, 0x63, 0x1C, 0x60, 0x1D, 0x6B, 0x1E, 0x64, 0x1C, 0x74, 0x1D, 0x00, 0x1E, 0x65, 0x1C, 0xC0, 0x1D, 0x79, 0x1E, 0x66), +S_(0x000000FF, 0x0C, 0x1C, 0x20, 0x1D, 0x11, 0x1E, 0x67, 0x1C, 0xE0, 0x1D, 0x3B, 0x1E, 0x68), + +// ------------------------------------- Initialization of direct registers -------------------------------------------- + +// Mode(8-bit,u-Law,1 PCLK ) setting, Loopbacks and Interrupts clear +// --Temporary digital loopback +S_(0x000000FF, 0x08, 0x01, 0x29, 0x08, 0x00, 0x09, 0x00, 0x0E, 0x00), +S_(0x000000FF, 0x0C, 0x15, 0x00, 0x16, 0x03, 0x17, 0x00, 0x12, 0xFF, 0x13, 0xFF, 0x14, 0xFF), + +// Ring timers settings +S_(0x000000FF, 0x06, 0x30, 0x80, 0x31, 0x3E, 0x32, 0x80), +S_(0x000000FF, 0x02, 0x33, 0x3E), +S_(0x000000FF, 0x02, 0x22, 0x18), + +// Battery feed control(DCSW), Automatic control of Ring Trip and Loop Closure, VBATH, VBATL +S_(0x000000FF, 0x02, 0x42, 0x00), +S_(0x000000FF, 0x02, 0x43, 0x1E), +S_(0x000000FF, 0x04, 0x4A, 0x31, 0x4B, 0x10), + +S_(0x000000FF, 0x02, 0x45, 0x0A), +S_(0x000000FF, 0x02, 0x46, 0x0B), +S_(0x000000FF, 0x02, 0x47, 0x07), + + +// Setting of SLICs offsets +S_(0x00000001, 0x08, 0x02, 0x01, 0x03, 0x00, 0x04, 0x01, 0x05, 0x00), +S_(0x00000002, 0x08, 0x02, 0x09, 0x03, 0x00, 0x04, 0x09, 0x05, 0x00), +S_(0x00000004, 0x08, 0x02, 0x11, 0x03, 0x00, 0x04, 0x11, 0x05, 0x00), +S_(0x00000008, 0x08, 0x02, 0x19, 0x03, 0x00, 0x04, 0x19, 0x05, 0x00), +S_(0x00000010, 0x08, 0x02, 0x21, 0x03, 0x00, 0x04, 0x21, 0x05, 0x00), +S_(0x00000020, 0x08, 0x02, 0x29, 0x03, 0x00, 0x04, 0x29, 0x05, 0x00), +S_(0x00000040, 0x08, 0x02, 0x31, 0x03, 0x00, 0x04, 0x31, 0x05, 0x00), +S_(0x00000080, 0x08, 0x02, 0x39, 0x03, 0x00, 0x04, 0x39, 0x05, 0x00), + +// Change SLICs states to "Normal state"s (On, after offsets are set already) + + +S_(0x000000FF, 0x02, 0x40, 0x1), +S_(0x000000FF, 0x02, 0x42, 0x06), +// ------------------------------------------------------------- + diff --git a/xpp/sync.sh b/xpp/sync.sh new file mode 100755 index 0000000..1cac283 --- /dev/null +++ b/xpp/sync.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -e + +SVN_ROOT=/home/tzafrir/Proj/Svn +XORTEL_DIR=$SVN_ROOT/xpp-zaptel/trunk/xortel +XPP_DIR=$SVN_ROOT/xpp-zaptel/trunk/zaptel/xpp +TARGET_DIR=xpp +FIRMWARE=$SVN_ROOT/fpgafirmware/init.dat + +curdir=$PWD +cd $XORTEL_DIR + cd ..; svn update; + cd xortel + make FIRMWARE="$FIRMWARE" ../zaptel/xpp/slic_init.inc +cd $curdir + +cp -a $XORTEL_DIR/FPGA_XPD.hex ${TARGET_DIR}/ +cp -a $XORTEL_DIR/xpd.h ${TARGET_DIR}/ +cp -a $XORTEL_DIR/xpp_zap.[ch] ${TARGET_DIR}/ +cp -a $XORTEL_DIR/xproto.[ch] ${TARGET_DIR}/ +cp -a $XORTEL_DIR/xdefs.h ${TARGET_DIR}/ +cp -a $XORTEL_DIR/xpp_usb.c ${TARGET_DIR}/ +cp -a $XORTEL_DIR/zap_debug.[ch] ${TARGET_DIR}/ +cp -a $XORTEL_DIR/slic.[ch] ${TARGET_DIR}/ +cp -a $XORTEL_DIR/card_*.[ch] ${TARGET_DIR}/ +cp -a $XORTEL_DIR/xpp_fxloader{,.usermap} ${TARGET_DIR}/ +cp -a $XORTEL_DIR/xpp_modprobe ${TARGET_DIR}/ +cp -a $XORTEL_DIR/gen_slic_init ${TARGET_DIR}/ +cp -a $XPP_DIR/Makefile ${TARGET_DIR}/ +cp -a $XPP_DIR/slic_init.inc ${TARGET_DIR}/ diff --git a/xpp/xdefs.h b/xpp/xdefs.h new file mode 100644 index 0000000..8c902bd --- /dev/null +++ b/xpp/xdefs.h @@ -0,0 +1,82 @@ +#ifndef XDEFS_H +#define XDEFS_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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. + * + */ + +#ifdef __KERNEL__ + +#include <linux/kernel.h> + +#define DBG(fmt, ...) \ + ((print_dbg) && printk(KERN_DEBUG "DBG-%s: %s: " fmt, \ + THIS_MODULE->name, __FUNCTION__, ## __VA_ARGS__)) +#define INFO(fmt, ...) printk(KERN_INFO "INFO-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__) +#define NOTICE(fmt, ...) printk(KERN_NOTICE "NOTICE-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__) +#define ERR(fmt, ...) printk(KERN_ERR "ERR-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__) + +#else + +#include <stdint.h> +typedef uint32_t __u32; + +#include <stdio.h> + +#define DBG(fmt, ...) printf("DBG: %s: " fmt, __FUNCTION__, ## __VA_ARGS__) +#define INFO(fmt, ...) printf("INFO: " fmt, ## __VA_ARGS__) +#define NOTICE(fmt, ...) printf("NOTICE: " fmt, ## __VA_ARGS__) +#define ERR(fmt, ...) printf("ERR: " fmt, ## __VA_ARGS__) +#define __user + +struct list_head { struct list_head *next; struct list_head *prev; }; + +#endif + +typedef char *charp; +typedef unsigned char byte; +typedef int bool; +typedef struct xbus xbus_t; +typedef struct xpd xpd_t; +typedef struct xpacket_raw xpacket_raw_t; +typedef struct xpacket xpacket_t; +typedef struct xops xops_t; +typedef __u32 xpp_line_t; /* at most 31 lines for E1 */ + + +#define BIT_SET(x,i) ((x) |= (1 << (i))) +#define BIT_CLR(x,i) ((x) &= ~(1 << (i))) +#define IS_SET(x,i) (((x) & (1 << (i))) != 0) +#define BIT(i) (1 << (i)) + +#undef SUPPORT_USB1 + +#ifdef SUPPORT_USB1 +/* + * packet size <= 64 bytes: + * ZT_CHUNKSIZE * 7 channels + header size <= 64 + */ +#define CHANNELS_PERXPD 7 /* 7 * ZT_CHUNKSIZE + header <= 64 bytes */ +#else +#define CHANNELS_PERXPD 30 /* Depends on xpp_line_t and protocol fields */ +#endif + + +#endif /* XDEFS_H */ diff --git a/xpp/xpd.h b/xpp/xpd.h new file mode 100644 index 0000000..f563ca3 --- /dev/null +++ b/xpp/xpd.h @@ -0,0 +1,290 @@ +#ifndef XPD_H +#define XPD_H + +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 "xdefs.h" +#include "xproto.h" + +#ifdef __KERNEL__ +#include <linux/kernel.h> +#include <asm/atomic.h> +#include <asm/semaphore.h> +#include <linux/moduleparam.h> +#endif + +#include <zaptel.h> + +#ifdef __KERNEL__ +#define DEF_PARM(type,name,init,desc) \ + type name = init; \ + module_param(name, type, 0600); \ + MODULE_PARM_DESC(name, desc) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) +/* + * Old 2.6 kernels had module_param_array() macro that receive the counter + * by value. + */ +#define DEF_ARRAY(type,name,count,init,desc) \ + unsigned int name ## _num_values; \ + type name[count] = {[0 ... count-1] = init}; \ + module_param_array(name, type, name ## _num_values, 0600); \ + MODULE_PARM_DESC(name, desc " ( 1-" __MODULE_STRING(count) ")") +#else +#define DEF_ARRAY(type,name,count,init,desc) \ + unsigned int name ## _num_values; \ + type name[count] = {[0 ... count-1] = init}; \ + module_param_array(name, type, &name ## _num_values, 0600); \ + MODULE_PARM_DESC(name, desc " ( 1-" __MODULE_STRING(count) ")") +#endif +#endif // __KERNEL__ + + +#define MAX_SPANNAME 20 +#define MAX_SPANDESC 40 +#define MAX_CHANNAME 20 + +#define XPD_NAMELEN 10 /* must be <= from maximal workqueue name */ +#define XPD_DESCLEN 20 +#define XBUS_NAMELEN 20 /* must be <= from maximal workqueue name */ +#define XBUS_DESCLEN 40 + +/* Hardware does not check bank_num yet. So only 4 cards can be used */ +#define MAX_XPDS 4 // 1 FXS + 2 E1/T1 + 1 (Quad * E1/T1) + +#define VALID_XPD_NUM(x) ((x) < MAX_XPDS && (x) >= 0) + +typedef struct xbus_ops xbus_ops_t; + +typedef enum xbus_type { + FIRMWARE_LOOPBACK = 1, + FIRMWARE_XPP = 2, +} xbus_type_t; + +#ifdef __KERNEL__ + + +typedef struct packet_queue { + char qname[XPD_NAMELEN]; + struct list_head head; + unsigned int count; + unsigned int worst_count; + unsigned int overflows; + spinlock_t lock; +} packet_queue_t; + +struct xbus_ops { + int (*packet_send)(xbus_t *xbus, xpacket_t *packet); + xpacket_t *(*packet_new)(xbus_t *xbus, int flags); + void (*packet_free)(xbus_t *xbus, xpacket_t *p); +}; + +enum { + XBUS_N_DESC_REQ, + XBUS_N_DEV_DESC, + XBUS_N_PCM_WRITE, + XBUS_N_PCM_READ, + XBUS_N_TX_BYTES, + XBUS_N_RX_BYTES, + XBUS_N_SOFTSIM_PACKETS, + XBUS_N_SIM_PACKETS, +}; + +#define XBUS_COUNTER(xbus, counter) ((xbus)->counters[XBUS_N_ ## counter]) + +#define C_(x) [ XBUS_N_ ## x ] = { #x } + +/* yucky, make an instance so we can size it... */ +static struct xbus_counters { + char *name; +} xbus_counters[] = { + C_(DESC_REQ), + C_(DEV_DESC), + C_(PCM_WRITE), + C_(PCM_READ), + C_(TX_BYTES), + C_(RX_BYTES), + C_(SOFTSIM_PACKETS), + C_(SIM_PACKETS), +}; + +#undef C_ + +#define XBUS_COUNTER_MAX ARRAY_SIZE(xbus_counters) + +struct xpd_sim { + bool simulated; + bool softloop_xpd; + int loopto; + xpd_type_t xpd_type; + xpp_line_t hookstate; +}; + + +struct xbus { + char busname[XBUS_NAMELEN]; /* only xbus_new set this */ + char busdesc[XBUS_DESCLEN]; /* lowlevel drivers set this */ + int num; + xbus_ops_t *ops; + struct xpd *xpds[MAX_XPDS]; + + /* Simulator data */ + xbus_type_t bus_type; +#if SOFT_SIMULATOR + struct xpd_sim sim[MAX_XPDS]; + struct workqueue_struct *sim_workqueue; + struct work_struct sim_work; // workqueue job for running simulator + packet_queue_t sim_packet_queue; +#endif + + spinlock_t lock; + + bool hardware_exists; /* Hardware is functional */ + int open_counter; /* Number of open channels */ + atomic_t packet_counter; /* Allocated packets */ + wait_queue_head_t packet_cache_empty; + + struct timer_list poll_timer; + struct rw_semaphore in_use; + int num_xpds; + void *priv; /* Pointer to transport level data structures */ + +#ifdef XPP_PACKET_LOG + struct cyclic_buff *packet_log; +#endif + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_xbus_dir; + struct proc_dir_entry *proc_xbus_summary; +#endif + + /* statistics */ + int counters[XBUS_COUNTER_MAX]; + +}; +#endif + +typedef enum xpd_direction { + TO_PHONE = 0, + TO_PSTN = 1, +} xpd_direction_t; + +#define LINE_BITS (sizeof(xpp_line_t)*8) + + +#ifdef __KERNEL__ +#define BIT_SET(x,i) ((x) |= (1 << (i))) +#define BIT_CLR(x,i) ((x) &= ~(1 << (i))) +#define IS_SET(x,i) (((x) & (1 << (i))) != 0) +#define BIT(i) (1 << (i)) + +enum { + XPD_N_PCM_READ, + XPD_N_PCM_WRITE, + XPD_N_RECV_ERRORS, +}; + +#define XPD_COUNTER(xpd, counter) ((xpd)->counters[XPD_N_ ## counter]) + +#define C_(x) [ XPD_N_ ## x ] = { #x } + +/* yucky, make an instance so we can size it... */ +static struct xpd_counters { + char *name; +} xpd_counters[] = { + C_(PCM_READ), + C_(PCM_WRITE), + C_(RECV_ERRORS), +}; + +#undef C_ + +#define XPD_COUNTER_MAX (sizeof(xpd_counters)/sizeof(xpd_counters[0])) + +enum leds { + LED_GREEN, + LED_RED, + LED_BLUE, +}; + +#define NUM_LEDS 3 + +struct xpd { + char xpdname[XPD_NAMELEN]; + struct zt_span span; + struct zt_chan *chans; + int channels; + xpd_type_t type; + xpd_direction_t direction; /* TO_PHONE, TO_PSTN */ + xpp_line_t enabled_chans; /* hardware activation: 0 - off, 1 - on */ + xpp_line_t hookstate; /* 0 - ONHOOK, 1 - OFHOOK */ + xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */ + xpp_line_t digital_outputs; /* 0 - no, 1 - yes */ + xpp_line_t digital_inputs; /* 0 - no, 1 - yes */ + + int ringing[CHANNELS_PERXPD]; + bool ringer_on[CHANNELS_PERXPD]; /* For ring toggling */ + bool led_on[CHANNELS_PERXPD]; /* For led toggling */ + int lasttxhook[CHANNELS_PERXPD]; + + struct work_struct xpd_post_init; + xbus_t *xbus; + + spinlock_t lock; + atomic_t open_counter; /* Number of open channels */ + + int flags; + + unsigned int board_flags; +#define XPD_BOARD_LOOPBACK 1 + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_xpd_dir; + struct proc_dir_entry *proc_xpd_summary; + struct proc_dir_entry *proc_xpd_ztregister; +#endif + // Bit numbers of board_flags + + int counters[XPD_COUNTER_MAX]; + + const xops_t *xops; /* Card level operations */ + void *priv; /* Card level private data */ + atomic_t card_present; + + unsigned int recv_errors; + unsigned int seq_errors; + unsigned long last_response; /* in jiffies */ + unsigned id; + struct list_head xpd_list; + unsigned int timer_count; + volatile u_char *writechunk; /* Double-word aligned write memory */ + volatile u_char *readchunk; /* Double-word aligned read memory */ + /* Echo cancelation */ + u_char ec_chunk1[CHANNELS_PERXPD][ZT_CHUNKSIZE]; + u_char ec_chunk2[CHANNELS_PERXPD][ZT_CHUNKSIZE]; +}; + +#endif + +#endif /* XPD_H */ diff --git a/xpp/xpp_fxloader b/xpp/xpp_fxloader new file mode 100644 index 0000000..a46c156 --- /dev/null +++ b/xpp/xpp_fxloader @@ -0,0 +1,24 @@ +#!/bin/sh + +FIRMWARE="/etc/xortel/FPGA_XPD.hex" +me=`basename $0` + +# to run manually, pass the parameter 'xppdetect' +V_ID=04b4 +P_ID=8613 +if [ "$1" = 'xppdetect' ]; then + DEVICES=`lsusb | tr -d : | awk "/ ID $V_ID$P_ID /{printf \"/proc/bus/usb/%s/%s \",\\$2,\\$4}"` + echo "Loading firmware for $DEVICES" + for dev in $DEVICES + do + fxload -t fx2 -D $dev -I $FIRMWARE + done + exit 0 +fi + +if [ "$ACTION" = "add" ] && [ -f "$DEVICE" ] +then + logger -i -t "$me" "Loading firmware '$FIRMWARE' into '$DEVICE'" + fxload -t fx2 -D "$DEVICE" -I "$FIRMWARE" || exit 1 +fi + diff --git a/xpp/xpp_fxloader.usermap b/xpp/xpp_fxloader.usermap new file mode 100644 index 0000000..1989af9 --- /dev/null +++ b/xpp/xpp_fxloader.usermap @@ -0,0 +1,2 @@ +# module match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info +xpp_fxloader 0x0003 0x04b4 0x8613 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0 diff --git a/xpp/xpp_modprobe b/xpp/xpp_modprobe new file mode 100644 index 0000000..52d76b2 --- /dev/null +++ b/xpp/xpp_modprobe @@ -0,0 +1,10 @@ +# Some debugging options for the brave of heart: +#options zaptel debug=1 +#options wcfxo debug=1 +#options xpp print_dbg=1 + +# For pre-loading of card modules (e.g: xpp_fxs) +#install xpp_usb /sbin/modprobe xpd_fxs && /sbin/modprobe --ignore-install xpp_usb + +# For auto loading of card modules +alias xpd-type-3 xpd_fxs diff --git a/xpp/xpp_proto.c b/xpp/xpp_proto.c new file mode 100644 index 0000000..b020caf --- /dev/null +++ b/xpp/xpp_proto.c @@ -0,0 +1,1044 @@ +#include <linux/module.h> +#include <linux/delay.h> /* for udelay */ +#include "xpd.h" +#include "xpp_proto.h" +#include "xpp_zap.h" + +static char rcsid[] = "$Id$"; + +extern int print_dbg; +#include "zap_debug.h" + +typedef struct xpp_command xpp_command_t; +typedef int (*xpp_handler_t)(xbus_t *xbus, int id, xpp_command_t *cmd, xpacket_t *packet); + +struct xpp_command { + xpp_opcode_t opcode; + unsigned int header_size; + bool varsize; + const char *name; + const char *desc; + xpp_handler_t handler; +}; + +#define S_(s,l,...) \ + { \ + .lines = s, \ + { \ + .len = l, \ + .data = { __VA_ARGS__ }, \ + } \ + } + +struct slic_init_data { + xpp_line_t lines; + slic_data_t slic_data; +} slic_init_data[] = { +#include "slic_init.inc" +}; + +static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack); +static int simulate_xpd(xbus_t *xbus, int xpd_num, xpacket_t *sent_packet); +static bool pcm_valid(xpd_t *xpd, xpacket_t *reply); + +#define NEW_PACKET(p, xbus, name, to) \ + do { \ + p = xbus->ops->packet_new(xbus, GFP_ATOMIC); \ + if(!p) \ + return -ENOMEM; \ + PACKET_INIT(p, name); \ + XPD_ADDR_SET(p->content.addr, to); \ + } while(0); + +/*------------------------- SLIC Handling --------------------------*/ + +int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + xpd_t *xpd = data; + //slic_reply_t *info; + + BUG_ON(!xpd); + spin_lock_irqsave(&xpd->lock, flags); +#if 0 + info = (slic_reply_t *)&xpd->slic_info; + len += sprintf(page + len, "SLIC_REPLY: %s reg_num=0x%X, dataH=0x%X dataL=0x%X\n", + (info->indirect)?"I":"D", + info->reg_num, info->data_high, info->data_low); +#endif + spin_unlock_irqrestore(&xpd->lock, flags); + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +static int parse_slic_cmd(const char *buf, slic_cmd_t *sc) +{ + char op; /* [W]rite, [R]ead */ + char reg_type; /* [D]irect, [I]ndirect */ + int s1, s2, s3, s4; + int reg_num; + int data_low, data_high; + xpp_line_t lines; + int ret; + + ret = sscanf(buf, "%x %x %x %x %c%c %x %x %x", + &s1, &s2, &s3, &s4, &op, ®_type, ®_num, &data_high, &data_low); + lines = (s4 << 24) | (s3 << 16) | (s2 << 8) | (s1); + switch(op) { + case 'R': + if(reg_type == 'D' && ret == 7) { + // DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num); + ret = slic_cmd_direct_read(sc, lines, reg_num); + } else if(reg_type == 'I' && ret == 7) { + // DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num); + ret = slic_cmd_indirect_read(sc, lines, reg_num); + } else { + NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type); + goto err; + } + break; + case 'W': + if(reg_type == 'D' && ret == 8) { + // DBG("0x%X 0x%X 0x%X 0x%X %c %x %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high); + ret = slic_cmd_direct_write(sc, lines, reg_num, data_high); + } else if(reg_type == 'I' && ret == 9) { + // DBG("0x%X 0x%X 0x%X 0x%X %c %x %X %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high, data_low); + ret = slic_cmd_indirect_write(sc, lines, reg_num, data_low, data_high); + } else { + NOTICE("%s: Bad write input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type); + goto err; + } + break; + default: + NOTICE("%s: Bad input: ret=%d buf='%s' op=%c\n", __FUNCTION__, ret, buf, op); + goto err; + } + return ret; +err: + return -EINVAL; +} + +static int process_slic_cmdline(xpd_t *xpd, char *cmdline) +{ + xbus_t *xbus; + slic_cmd_t sc; + xpacket_t *pack_tx; + char *p; + int len = strlen(cmdline); + + BUG_ON(!xpd); + xbus = xpd->xbus; + if((p = strchr(cmdline, '#')) != NULL) /* Truncate comments */ + *p = '\0'; + if((p = strchr(cmdline, ';')) != NULL) /* Truncate comments */ + *p = '\0'; + for(p = cmdline; *p && (*p == ' ' || *p == '\t'); p++) /* Trim leading whitespace */ + ; + if(*p == '\0') + return 0; + len = parse_slic_cmd(p, &sc); + if(len < 0) + return len; + sc.lines &= xpd->enabled_chans; // Ignore disabled channels + if(!sc.lines) { + NOTICE("%s: no enabled channels are marked. Skip.\n", __FUNCTION__); + return 0; + } + dump_slic_cmd("WRITE_SLIC", &sc); + NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id); + PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd) = sc; + pack_tx->datalen = len; + packet_send(xbus, pack_tx); + return 0; +} + +int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + xpd_t *xpd = data; + const int LINE_LEN = 500; + char buf[LINE_LEN]; + char *p; + int i; + int ret; + + BUG_ON(!xpd); + for(i = 0; i < count; /* noop */) { + for(p = buf; p < buf + LINE_LEN; p++) { /* read a line */ + if(i >= count) + break; + if(get_user(*p, buffer + i)) + return -EFAULT; + i++; + if(*p == '\n' || *p == '\r') /* whatever */ + break; + } + if(p >= buf + LINE_LEN) + return -E2BIG; + *p = '\0'; + ret = process_slic_cmdline(xpd, buf); + if(ret < 0) + return ret; + } + return count; +} + + + +/*------------------------- Protocol Functions ---------------------*/ + +#define HOSTCMD(name, ...) \ + DECLARE_CMD(name, ## __VA_ARGS__ ); \ + EXPORT_SYMBOL(xpp_proto_ ## name); \ + DECLARE_CMD(name, ## __VA_ARGS__ ) + + +/* 0x04 */ HOSTCMD(DESC_REQ, int xpd_num) +{ + int ret = 0; + xpacket_t *pack_tx; + + DBG("\n"); + if(!xbus) { + DBG("NO XBUS\n"); + return -EINVAL; + } + NEW_PACKET(pack_tx, xbus, DESC_REQ, xpd_num); + DBG("calling packet_send for a DESC_REQ packet.\n"); + ret = packet_send(xbus, pack_tx); + DBG("after packet_send, updating counter (ret=%d)\n", ret); + XBUS_COUNTER(xbus, DESC_REQ)++; + return ret; +} + +/* 0x0F */ HOSTCMD(CHAN_POWER, xpp_line_t lines, bool on) +{ + int ret = 0; + xpacket_t *pack_tx; + slic_cmd_t *sc; + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("Channel Power: 0x%04X %s\n", lines, (on) ? "up" : "down"); + NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id); + sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd); + if(on) { + // Power up + len = slic_cmd_direct_write(sc, lines, 0x42, 0x06); + } else { + // Power down + len = slic_cmd_direct_write(sc, lines, 0x42, 0x00); + } + pack_tx->datalen = len; + + packet_send(xbus, pack_tx); + return ret; +} + +/* 0x0F */ HOSTCMD(CHAN_ENABLE, xpp_line_t lines, bool on) +{ + int ret = 0; + xpacket_t *pack_tx; + slic_cmd_t *sc; + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off"); + NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id); + sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, lines, 0x40, (on)?0x01:0x00); + pack_tx->datalen = len; + + packet_send(xbus, pack_tx); + return ret; +} + +/* 0x0F */ HOSTCMD(RING, int pos, bool on) +{ + int ret = 0; + xpacket_t *pack_tx; + slic_cmd_t *sc; + xpp_line_t mask = (1 << pos); + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + mask &= xpd->enabled_chans; // Ignore disabled channels + if(!mask) { + return 0; + } + DBG("%s pos=%d %s\n", xpd->xpdname, pos, (on) ? "on" : "off"); + NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id); + sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, mask, 0x40, (on)?0x04:0x01); + pack_tx->datalen = len; + + packet_send(xbus, pack_tx); + return ret; +} + +/* 0x0F */ HOSTCMD(SETHOOK, xpp_line_t hook_status) +{ + int ret = 0; + xpacket_t *pack_tx; + slic_cmd_t *sc; + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + hook_status &= xpd->enabled_chans; // Ignore disabled channels + if(!hook_status) { + return 0; + } + DBG("New hook_status: %d\n", hook_status); + NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id); + sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd); + /* FIXME: This is fake, until Dima implements FXO */ + len = slic_cmd_direct_write(sc, hook_status, 0x02, 0x00); + pack_tx->datalen = len; + + packet_send(xbus, pack_tx); + return ret; +} + +/* + * LED control is done via SLIC register 0x06: + * 7 6 5 4 3 2 1 0 + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * | MR | MG | MB | R | OG | OB | G | B | + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * + * B - BLUE LED (0 - OFF, 1 - ON) + * G - GREEN LED (0 - OFF, 1 - ON) + * OB - Output BLUE (this line is output) + * OG - Output GREEN (this line is output) + * R - RED LED (0 - OFF, 1 - ON) + * MB - Mask BLUE. (1 - B effect the BLUE LED) + * MR - Mask RED. (1 - R effect the RED LED) + * MG - Mask GREEN. (1 - G effect the GREEN LED) + * + * The BLUE LED (actually a relay out) is connected to line 0 and 4 only. + */ + +// GREEN RED BLUE +static int led_mask[NUM_LEDS] = { BIT(6), BIT(7), BIT(5) }; +static int led_vals[NUM_LEDS] = { BIT(1), BIT(4), BIT(0) }; + +/* 0x0F */ HOSTCMD(LED, xpp_line_t lines, byte which, bool on) +{ + int ret = 0; + xpacket_t *pack_tx; + slic_cmd_t *sc; + int len; + int value; + int i; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("LED: lines=0x%04X which=%d -- %s\n", lines, which, (on) ? "on" : "off"); + which = which % NUM_LEDS; + value = BIT(2) | BIT(3); + value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[which]); + if(on) + value |= led_vals[which]; + for(i = 0; i < CHANNELS_PERXPD; i++) { + if(!IS_SET(lines, i)) + continue; + NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id); + sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, lines, 0x06, value); + DBG("LED pack: line=%d value=0x%04X\n", i, value); + pack_tx->datalen = len; + packet_send(xbus, pack_tx); + } + return ret; +} + +/* 0x0F */ HOSTCMD(RELAY_OUT, byte which, bool on) +{ + int ret = 0; + xpacket_t *pack_tx; + slic_cmd_t *sc; + int len; + int value; + xpp_line_t lines; + int relay_channels[] = { 0, 4 }; + + BUG_ON(!xbus); + BUG_ON(!xpd); + + DBG("RELAY_OUT: which=%d -- %s\n", which, (on) ? "on" : "off"); + which = which % ARRAY_SIZE(relay_channels); + lines = BIT(relay_channels[which]); + value = BIT(2) | BIT(3); + value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[LED_BLUE]); + if(on) + value |= led_vals[LED_BLUE]; + NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id); + sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, lines, 0x06, value); + + DBG("RELAY_OUT pack: line=%d value=0x%04X\n", lines, value); + pack_tx->datalen = len; + packet_send(xbus, pack_tx); + return ret; +} + +/* 0x0F */ HOSTCMD(SLIC_INIT) +{ + int ret = 0; + xpacket_t *pack_tx; + slic_data_t *slic; + struct slic_init_data *source; + int i; + + BUG_ON(!xbus); + BUG_ON(!xpd); + DBG("INITIALIZING SLIC\n"); + for(i = 0; i < ARRAY_SIZE(slic_init_data); i++) { + source = &slic_init_data[i]; + NEW_PACKET(pack_tx, xbus, SLIC_INIT, xpd->id); + PACKET_FIELD(pack_tx, SLIC_INIT, lines) = source->lines; + + slic = &PACKET_FIELD(pack_tx, SLIC_INIT, slic_data); + slic->len = source->slic_data.len; + memcpy(slic->data, source->slic_data.data, source->slic_data.len); + pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1; +// dump_packet("SLIC", pack_tx, print_dbg); + packet_send(xbus, pack_tx); + mdelay(10); // FIXME: Temporary -- Dima need to fix it + } + return ret; +} + +/* 0x0F */ HOSTCMD(SLIC_QUERY, int pos, byte reg_num) +{ + int ret = 0; + xpacket_t *pack_tx; + slic_cmd_t *sc; + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id); + sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_read(sc, (1<<pos), reg_num); + + pack_tx->datalen = len; + + packet_send(xbus, pack_tx); + return ret; +} + +/* 0x11 */ HOSTCMD(PCM_WRITE, xpp_line_t lines, volatile byte *buf) +{ + int ret = 0; + xpacket_t *pack_tx; + byte *pcm; + byte *start_pcm; + int i; + extern ulong pcm_gen; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; + // DBG("PCM_WRITE\n"); + if(pcm_gen != 0) + return 0; +// if(lines == 0) +// return 0; + + /* + * FIXME: Workaround a bug in sync code of the Astribank. + * Send dummy PCM for sync. + */ + if(lines == 0) + lines = BIT(0); + + NEW_PACKET(pack_tx, xbus, PCM_WRITE, xpd->id); + PACKET_FIELD(pack_tx, PCM_WRITE, lines) = lines; + start_pcm = pcm = PACKET_FIELD(pack_tx, PCM_WRITE, pcm); + for(i = 0; i < CHANNELS_PERXPD; i++) { + if(IS_SET(lines, i)) { + memcpy(pcm, (byte *)buf, ZT_CHUNKSIZE); + pcm += ZT_CHUNKSIZE; + } + buf += ZT_CHUNKSIZE; + } + pack_tx->datalen = sizeof(xpp_line_t) + (pcm - start_pcm); + packet_send(xbus, pack_tx); + XPD_COUNTER(xpd, PCM_WRITE)++; + XBUS_COUNTER(xbus, PCM_WRITE)++; + return ret; +} + +/* 0x13 */ HOSTCMD(PCM_GEN, xpp_line_t lines, volatile byte *buf) +{ + xpacket_t *pack_tx; + bool gen_seq = ((lines != 0) && (buf != NULL)); + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("PCM_GEN lines=0x%04X %s\n", lines, (gen_seq) ? "seq" : "off"); + NEW_PACKET(pack_tx, xbus, PCM_GEN, xpd->id); + PACKET_FIELD(pack_tx, PCM_GEN, lines) = lines; + if(gen_seq) { + PACKET_FIELD(pack_tx, PCM_GEN, gen) = 0; + memcpy(&PACKET_FIELD(pack_tx, PCM_GEN, pcm_seq), (byte *)buf, ZT_CHUNKSIZE); + } else { + PACKET_FIELD(pack_tx, PCM_GEN, gen) = 2; + } + packet_send(xbus, pack_tx); + return 0; +} + +/* + * Sync source is controled by a mask byte to 0x19 command: + * 7 6 5 4 3 2 1 0 + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * | | | | | | | RW | AB | + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * + * RW - Read or set (0 - Write, 1 - Read) + * AB - This Astribank provide sync (0 - no, 1 - yes) + * + */ + +/* 0x19 */ HOSTCMD(SYNC_SOURCE, bool setit, bool is_master) +{ + xpacket_t *pack_tx; + byte mask = 0; + + BUG_ON(!xbus); + BUG_ON(!xpd); + if(is_master) + mask |= BIT(0); + if(!setit) + mask |= BIT(1); + DBG("SYNC_SOURCE %s setit=%s is_master=%s (mask=0x%X)\n", + xpd->xpdname, (setit)?"yes":"no", (is_master)?"yes":"no", mask); + NEW_PACKET(pack_tx, xbus, SYNC_SOURCE, xpd->id); + PACKET_FIELD(pack_tx, SYNC_SOURCE, mask) = mask; + packet_send(xbus, pack_tx); + return 0; +} + +/* 0x31 */ HOSTCMD(LOOPBACK_AX, byte *data, unsigned int size) +{ + xpacket_t *pack_tx; + + BUG_ON(!xbus); + BUG_ON(!xpd); + DBG("LOOPBACK_AX %d bytes\n", size); + NEW_PACKET(pack_tx, xbus, LOOPBACK_AX, xpd->id); + memcpy(&PACKET_FIELD(pack_tx, LOOPBACK_AX, data), data, size); + packet_send(xbus, pack_tx); + return 0; +} + +/*------------------------- Protocol Simulator ---------------------*/ + + +static int simulate_xpd(xbus_t *xbus, int xpd_num, xpacket_t *sent_packet) +{ + xpacket_t *pack = sent_packet; + struct xpd_sim *xpd_sim; + struct xpd_sim *loopto_sim; + xpp_opcode_t opcode; + int dest_xpd_num; + int ret = 0; + + // Sanity checks + BUG_ON(!xbus); + BUG_ON(xpd_num > MAX_XPDS || xpd_num < 0); + BUG_ON(!sent_packet); + BUG_ON(!xbus->sim[xpd_num].simulated); + + XBUS_COUNTER(xbus, SIM_PACKETS)++; + xpd_sim = &xbus->sim[xpd_num]; + opcode = pack->content.opcode; + dest_xpd_num = xpd_sim->loopto; + loopto_sim = &xbus->sim[dest_xpd_num]; +// DBG("before: addr=%d, opcode=0x%X\n", xpd_num, opcode); + switch(opcode) { + case XPP_DESC_REQ: + DBG("SIM DESC_REQ (xpd_num=%d)\n", xpd_num); + PACKET_INIT(pack, DEV_DESC); + PACKET_FIELD(pack, DEV_DESC, type) = xpd_sim->xpd_type; + dest_xpd_num = xpd_num; // Reply as the original XPD + break; + case XPP_PCM_WRITE: + PACKET_INIT(pack, PCM_READ); + XPD_ADDR_SET(pack->content.addr, dest_xpd_num); + break; + case XPP_SLIC_WRITE: +#if FINISHED_DECODING_SLICS + slic_cmd_t *sc; + int len; + + sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd); + lines = sc->lines; + bool slic_write = ! (slic->data[0] & 0x80); + int slic_reg = slic->data[0] & ~0x80; + + if(slic->len == 2 && slic_write && slic_reg == 0x40) { // RING + bool on = (slic->data[1] == 0x04); + if(on) { + loopto_sim->hookstate |= lines; + } else { + loopto_sim->hookstate &= ~lines; + } + + DBG("SIM RING to: xpd=%d (type=%d): (%s) ringing=0x%04X lines=0x%04X\n", dest_xpd_num, loopto_sim->xpd_type, + (on)?"on":"off", loopto_sim->hookstate, lines); + PACKET_INIT(pack, SIG_CHANGED); + PACKET_FIELD(pack, SIG_CHANGED, type) = loopto_sim->xpd_type; + PACKET_FIELD(pack, SIG_CHANGED, sig_status) = loopto_sim->hookstate; + PACKET_FIELD(pack, SIG_CHANGED, sig_toggles) = lines; + dump_packet("SIM RING TO", pack, print_dbg); + break; + } else if(slic->len == 1 && slic_write && slic_reg == 0x02) { // SETHOOK + DBG("SIM SETHOOK: xpd=%d: hookstate=0x%04X lines=0x%04X\n", dest_xpd_num, loopto_sim->hookstate, lines); + PACKET_INIT(pack, SIG_CHANGED); + PACKET_FIELD(pack, SIG_CHANGED, type) = loopto_sim->xpd_type; + PACKET_FIELD(pack, SIG_CHANGED, sig_status) = lines; + PACKET_FIELD(pack, SIG_CHANGED, sig_toggles) = loopto_sim->hookstate ^ lines; + loopto_sim->hookstate = lines; + break; + } else if(slic->len == 2 && slic_write && slic_reg == 0x06) { // LED + DBG("SIM LED: xpd=%d: 0x%04X=%s\n", xpd_num, lines, (0x10)? "on" : "off"); + ret = 0; + goto junk; + } else if(slic->len == 2 && ! slic_write) { // SLIC_QUERY + DBG("SIM SLIC_QUERY: xpd=%d: register=0x%02X\n", xpd_num, slic_reg); + ret = 0; + goto junk; + } else if(slic->len >= 4) { // INITIALIZATION? + DBG("SIM INITIALIZATION? xpd=%d len=%d\n", xpd_num, slic->len); + ret = 0; + goto junk; + } + NOTICE("%s: xpd=%d: SLIC_WRITE: len=%d\n", __FUNCTION__, xpd_num, slic->len); +#endif + dump_packet("BAD SLIC_WRITE", pack, print_dbg); + // FALL THROUGH + default: + NOTICE("%s: xpd=%d: CANNOT SIMULATE OPCODE=0x%02X\n", + __FUNCTION__, xpd_num, opcode); +// dump_packet("BAD OPCODE", pack, print_dbg); + ret = -EINVAL; + goto junk; + } +// DBG("after reversing: addr=%d, opcode=0x%X\n", xpd_num, pack->header.opcode); + return packet_process(xbus, dest_xpd_num, pack); +junk: + xbus->ops->packet_free(xbus, pack); + return ret; +} + +#define VERBOSE_DEBUG 1 +#define ERR_REPORT_LIMIT 20 + +void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg) +{ + xpp_opcode_t op = (byte)packet->content.opcode; + + if(!print_dbg) + return; + DBG("%s: @0x%02X OP=0x%02X flags=0x%02X LEN=%d\n", + msg, + XPD_NUM(packet->content.addr), + op, + (byte)packet->flags, + (byte)packet->datalen); +#if VERBOSE_DEBUG + { + int i; + byte *p = packet->content.raw; + + for(i = 0; i < packet->datalen; i++) { + static int limiter = 0; + + if(i >= sizeof(xpp_packet_r_t)) { + if(limiter < ERR_REPORT_LIMIT) { + ERR("dump_packet: length overflow i=%d > sizeof(xpp_packet_r_t)=%d\n", + i+1, sizeof(xpp_packet_r_t)); + } else if(limiter == ERR_REPORT_LIMIT) { + ERR("dump_packet: error packet #%d... squelsh reports.\n", limiter); + } + limiter++; + break; + } + DBG(" %2d> %02X\n", i+1, p[i]); + } + } +#endif +} + +/*------------------------- Reply Handlers -------------------------*/ + +#define HANDLER_DEF(name) \ + int CALL_PROTO(name, xbus_t *xbus, int xpd_num, xpp_command_t *cmd, xpacket_t *reply) + +/* +static HANDLER_DEF(notimp) +{ + NOTICE("xpp protocol error: command %s is not implemented yet\n", cmd->name); + return -EPROTO; +} +*/ +static HANDLER_DEF(DEV_DESC) +{ + byte type = PACKET_FIELD(reply, DEV_DESC, type) & 0x7; // 3 LSB's + byte rev = PACKET_FIELD(reply, DEV_DESC, rev); + xpp_line_t line_status = PACKET_FIELD(reply, DEV_DESC, line_status); + xpd_t *xpd = xpd_of(xbus, xpd_num); + + if(xpd) { + NOTICE("Received DEV_DESC packet for an existing xpd %s of type %d\n", + xpd->xpdname, type); + return 0; + } + XBUS_COUNTER(xbus, DEV_DESC)++; + DBG("xpd=%d type=%d rev=%d line_status=0x%04X\n", xpd_num, type, rev, line_status); + switch(type) { + case XPD_TYPE_FXS: + break; + case XPD_TYPE_FXO: + break; + case XPD_TYPE_NOMODULE: + DBG("No module at address=%d\n", xpd_num); + return 0; + default: + NOTICE("DEV_DESC: unkown type=%d\n", type); + return -EPROTO; + } + if((xpd = xpd_new(xbus, xpd_num, type, rev)) == NULL) { + NOTICE("xpd_new failed\n"); + } + xpp_check_hookstate(xpd, line_status); + return 0; +} + +/** + * Handle signalling + */ +static HANDLER_DEF(SIG_CHANGED) +{ + xpd_t *xpd = xpd_of(xbus, xpd_num); + xpp_line_t sig_status = PACKET_FIELD(reply, SIG_CHANGED, sig_status); + + if(!xpd) { + NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num); + return -EPROTO; + } + if(xpd->direction == TO_PHONE) { /* Hook state changes */ + DBG("%s (PHONE) sig_status=0x%04X\n", xpd->xpdname, sig_status); + xpp_check_hookstate(xpd, sig_status); + } else { /* TO_TRUNK - line ring changes */ + unsigned long flags; + int i; + + DBG("%s (TRUNK) sig_status=0x%04X\n", xpd->xpdname, sig_status); + spin_lock_irqsave(&xpd->lock, flags); + for(i = 0; i < xpd->channels; i++) { + if(IS_SET(sig_status, i)) { + xpd->ringing[i] = RINGS_NUM*2; + zt_hooksig(&xpd->chans[i], ZT_RXSIG_OFFHOOK); + } else { + zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK); + xpd->ringing[i] = 0; + } + } + spin_unlock_irqrestore(&xpd->lock, flags); + } + return 0; +} + +static HANDLER_DEF(SLIC_REPLY) +{ + slic_reply_t *info = &PACKET_FIELD(reply, SLIC_REPLY, info); + xpd_t *xpd = xpd_of(xbus, xpd_num); + unsigned long flags; + + if(!xpd) { + NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num); + return -EPROTO; + } + spin_lock_irqsave(&xpd->lock, flags); + DBG("SLIC_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n", + xpd_num, (info->indirect)?"I":"D", + info->reg_num, info->data_low, info->data_high); + spin_unlock_irqrestore(&xpd->lock, flags); + return 0; +} + +static HANDLER_DEF(PCM_READ) +{ + /* FIXME: work around temporary hardware bug */ + xpd_num = 0; + + xpp_line_t lines = PACKET_FIELD(reply, PCM_READ, lines); + const byte *pcm = PACKET_FIELD(reply, PCM_READ, pcm); + xpd_t *xpd = xpd_of(xbus, xpd_num); + volatile u_char *readchunk; + volatile u_char *r; + unsigned long flags; + int i; + + if(!xpd) { + NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num); + return -EPROTO; + } + // DBG("lines=0x%04X\n", lines); + + if(!pcm_valid(xpd, reply)) { + return -EPROTO; + } + spin_lock_irqsave(&xpd->lock, flags); + if (xpd->timer_count & 1) { + /* First part */ + r = readchunk = xpd->readchunk; + } else { + r = readchunk = xpd->readchunk + ZT_CHUNKSIZE * CHANNELS_PERXPD; + } + + /* Copy PCM and put each channel in its index */ + for (i = 0; i < CHANNELS_PERXPD; i++) { + if(IS_SET(lines, i)) { + memcpy((u_char *)r, pcm, ZT_CHUNKSIZE); + //memset((u_char *)r, 0x5A, ZT_CHUNKSIZE); // DEBUG + pcm += ZT_CHUNKSIZE; + } + r += ZT_CHUNKSIZE; + } + + XPD_COUNTER(xpd, PCM_READ)++; + XBUS_COUNTER(xpd->xbus, PCM_READ)++; + spin_unlock_irqrestore(&xpd->lock, flags); + if(xpd->id == 0) + xpp_tick(0); + return 0; +} + +static HANDLER_DEF(SYNC_REPLY) +{ + xpd_t *xpd = xpd_of(xbus, xpd_num); + + if(!xpd) { + NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num); + return -EPROTO; + } + DBG("SYNC_REPLY: 0x%X\n", PACKET_FIELD(reply, SYNC_REPLY, mask)); + return 0; +} + +static HANDLER_DEF(LOOPBACK_XA) +{ + xpd_t *xpd = xpd_of(xbus, xpd_num); + + if(!xpd) { + NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num); + return -EPROTO; + } + dump_packet("LOOPBACK_XA", reply, print_dbg); + CALL_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 0); // FIXME: Find usage for extra LED + return 0; +} + +static HANDLER_DEF(DIAG_FE) +{ + dump_packet("DIAG_FE", reply, print_dbg); + return 0; +} + +static const xpp_packet_r_t FOR_SIZE_CALC; // Ugly hack, so we have something to sizeof() + +#define C_(op,var,txt) \ + [ XPP_##op ] { \ + .opcode = XPP_##op, \ + .varsize = var, \ + .header_size = sizeof(FOR_SIZE_CALC.cmd_##op), \ + .name = #op, \ + .desc = txt, \ + .handler = PROTO_FUNC(op) \ + } + +static xpp_command_t xpp_commands[] = { + /* OP V DESCRIPTION */ + C_( DEV_DESC, 0, "Device description reply"), + C_( SIG_CHANGED, 0, "Signaling change (hookstate/ringing)"), + C_( SLIC_REPLY, 0, "Reply to slic state"), + C_( PCM_READ, 1, "Read PCM data"), + C_( SYNC_REPLY, 0, "SYNC_REPLY"), + C_( LOOPBACK_XA, 1, "LOOPBACK Reply"), + C_( DIAG_FE, 1, "DIAG FE Opcode"), +}; + +#undef C_ + +static unsigned int xpp_max_opcode(void) +{ + return ARRAY_SIZE(xpp_commands); +} + +static bool xpp_valid_opcode(xpp_opcode_t op) +{ + if(op <= 0 || op >= xpp_max_opcode()) + return 0; + return xpp_commands[op].opcode != XPP_NOTIMP; +} + +static xpp_command_t *xpp_command(xpp_opcode_t op) +{ + if(!xpp_valid_opcode(op)) + return 0; + return &xpp_commands[op]; +} + +static bool xpp_valid_size(xpp_opcode_t op, xpacket_t *pack) +{ + xpp_command_t *cmd = xpp_command(op); + unsigned int hsize = cmd->header_size; + unsigned int size = pack->datalen; + int varsize = cmd->varsize; + + // ERR("op=%d hsize=%d size=%d\n", op, hsize, size); + return (hsize == size) || + (varsize && size <= sizeof(struct xpp_packet_r)); +} + +static bool pcm_valid(xpd_t *xpd, xpacket_t *reply) +{ + xpp_opcode_t op; + xpp_command_t *cmd; + xpp_line_t lines = PACKET_FIELD(reply, PCM_READ, lines); + int i; + int count = 0; + + BUG_ON(!reply); + op = reply->content.opcode; + cmd = xpp_command(op); + if(!cmd) { + ERR("xpp: %s -- bad command op=0x%02X\n", __FUNCTION__, op); + return 0; + } + for (i = 0; i < CHANNELS_PERXPD; i++) + if(IS_SET(lines, i)) + count++; + if(reply->datalen != (sizeof(xpp_line_t) + count * 8)) { + static int rate_limit = 0; + + XPD_COUNTER(xpd, RECV_ERRORS)++; + if((rate_limit++ % 1000) <= 10) { + ERR("BAD PCM REPLY: reply->datalen=%d, count=%d\n", reply->datalen, count); + } + return 0; + } + return 1; +} + +int packet_receive(xbus_t *xbus, xpacket_t *pack) +{ + int xpd_num = XPD_NUM(pack->content.addr); + + if(!VALID_XPD_NUM(xpd_num)) { + dump_packet("martian packet", pack, print_dbg); + xbus->ops->packet_free(xbus, pack); + return -EPROTO; + } + if(xbus->sim[xpd_num].simulated) { + //dump_packet("packet_receive -> simulate", pack, print_dbg); + return simulate_xpd(xbus, xpd_num, pack); + } else { + //dump_packet("packet_receive -> process", pack, print_dbg); + return packet_process(xbus, xpd_num, pack); + } +} + +static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack) +{ + xpp_opcode_t op; + xpp_command_t *cmd; + xpp_handler_t handler; + int ret = 0; + + BUG_ON(!pack); + op = pack->content.opcode; + cmd = xpp_command(op); + /*-------- Validations -----------*/ + if(!cmd) { + ERR("xpp: %s -- bad command op=0x%02X\n", __FUNCTION__, op); + dump_packet("packet_process -- bad command", pack, print_dbg); + ret = -EPROTO; + goto out; + } + if(!xpp_valid_size(op, pack)) { + ERR("xpp: %s: wrong size %d for op=0x%02X\n", + __FUNCTION__, pack->datalen, op); + dump_packet("packet_process -- wrong size", pack, print_dbg); + ret = -EPROTO; + goto out; + } + handler = cmd->handler; + BUG_ON(!handler); + XBUS_COUNTER(xbus, RX_BYTES) += pack->datalen; + handler(xbus, xpd_num, cmd, pack); +out: + xbus->ops->packet_free(xbus, pack); + return ret; +} + + +void process_sim_queue(void *data) +{ + xbus_t *xbus = data; + xpacket_t *pack; + +// DBG("\n"); + BUG_ON(!xbus); + while((pack = xbus_dequeue_packet(&xbus->sim_packet_queue)) != NULL) { +// DBG("pack->addr=0x%X pack->opcode=0x%X\n", XPD_NUM(pack->addr), pack->header.opcode); + packet_receive(xbus, pack); + } +} + +/** + * processes a packet recieved from the lower-level. + * @xbus the data bus + * @pack the handled packet + * @returns return status (0 for success). + * + * Should not be blocking. + * Has separate handling for PCM packets (direct write) and command packets (queued) + */ + +EXPORT_SYMBOL(dump_packet); +EXPORT_SYMBOL(packet_receive); +EXPORT_SYMBOL(proc_xpd_slic_read); +EXPORT_SYMBOL(proc_xpd_slic_write); diff --git a/xpp/xpp_proto.h b/xpp/xpp_proto.h new file mode 100644 index 0000000..46713d5 --- /dev/null +++ b/xpp/xpp_proto.h @@ -0,0 +1,188 @@ +#ifndef XPP_PROTO_H +#define XPP_PROTO_H + +#include "xpd.h" +#include "slic.h" +#ifdef __KERNEL__ +#include <linux/list.h> +#endif + +#define PCM_CHUNKSIZE (CHANNELS_PERXPD * ZT_MAX_CHUNKSIZE) + +typedef enum xpp_opcode { + XPP_NOTIMP = 0x00, +// + XPP_DESC_REQ = 0x04, + XPP_DEV_DESC = 0x05, +// + XPP_SIG_CHANGED = 0x06, +// + XPP_SLIC_WRITE = 0x0F, // Write to SLIC + XPP_CHAN_ENABLE = 0x0F, // Write to SLIC + XPP_CHAN_POWER = 0x0F, // Write to SLIC + XPP_RING = 0x0F, // Write to SLIC + XPP_SETHOOK = 0x0F, // Write to SLIC + XPP_LED = 0x0F, // Write to SLIC + XPP_RELAY_OUT = 0x0F, // Write to SLIC + XPP_SLIC_INIT = 0x0F, // Write to SLIC + XPP_SLIC_QUERY = 0x0F, // Write to SLIC +// + XPP_SLIC_REPLY = 0x10, +// + XPP_PCM_WRITE = 0x11, + XPP_PCM_READ = 0x12, +// + XPP_PCM_GEN = 0x13, +// + XPP_SYNC_SOURCE = 0x19, + XPP_SYNC_REPLY = 0x1A, +// + XPP_LOOPBACK_AX = 0x31, + XPP_LOOPBACK_XA = 0x32, + XPP_DIAG_FE = 0xFE, +} xpp_opcode_t; + +/*------------------------- PROTOCOL COMMANDS ----------------------*/ + +#define XPP_MAX_DATA 50 + +typedef struct slic_data { + byte len; + byte data[40]; +} __attribute__((packed)) slic_data_t; + +#define PROTO_FUNC(name) xpp_proto_ ## name +#define CALL_PROTO(name, ...) PROTO_FUNC(name)( __VA_ARGS__ ) +#define DECLARE_CMD(name, ...) \ + int CALL_PROTO(name, xbus_t *xbus, xpd_t *xpd, ## __VA_ARGS__ ) + +/* 0x04 */ DECLARE_CMD(DESC_REQ, int xpd_num); +/* 0x0F */ DECLARE_CMD(CHAN_ENABLE, xpp_line_t lines, bool on); +/* 0x0F */ DECLARE_CMD(CHAN_POWER, xpp_line_t lines, bool on); +/* 0x0F */ DECLARE_CMD(RING, int pos, bool on); +/* 0x0F */ DECLARE_CMD(SETHOOK, xpp_line_t hook_status); +/* 0x0F */ DECLARE_CMD(LED, xpp_line_t lines, byte which, bool on); +/* 0x0F */ DECLARE_CMD(RELAY_OUT, byte which, bool on); +/* 0x0F */ DECLARE_CMD(SLIC_INIT); +/* 0x0F */ DECLARE_CMD(SLIC_QUERY, int pos, byte reg_num); +/* 0x11 */ DECLARE_CMD(PCM_WRITE, xpp_line_t hookstate, volatile byte *buf); +/* 0x13 */ DECLARE_CMD(PCM_GEN, xpp_line_t lines, volatile byte *buf); +/* 0x19 */ DECLARE_CMD(SYNC_SOURCE, bool setit, bool is_master); +/* 0x31 */ DECLARE_CMD(LOOPBACK_AX, byte *data, unsigned int size); + +#define H_(op, ...) struct { \ + __VA_ARGS__ \ + } __attribute__((packed)) cmd_##op + +/* + * This struct must be packed exactly as the wire + * representation of the packet header after the + * XPD address byte + */ +typedef struct xpp_packet_r { + byte opcode; + xpp_addr_t addr; + union { + + H_(NOTIMP); + H_(DESC_REQ); + H_(DEV_DESC, + byte rev; /* Revision number */ + byte type; + xpp_line_t line_status; /* hook/ring status, depending on unit */ + ); + + H_(SIG_CHANGED, + byte type; /* unused -- we have it from DEV_DESC */ + xpp_line_t sig_status; /* channels: lsb=1, msb=8 */ + xpp_line_t sig_toggles; /* channels: lsb=1, msb=8 */ + ); + + H_(SLIC_INIT, + xpp_line_t lines; + slic_data_t slic_data; + ); + H_(SLIC_WRITE, + slic_cmd_t slic_cmd; + ); + H_(SLIC_REPLY, /* Get status of a single SLIC (for debugging) */ + xpp_line_t lines; + slic_reply_t info; + ); + + H_(PCM_WRITE, + xpp_line_t lines; // Must be 0xFF + byte pcm[PCM_CHUNKSIZE]; + ); + H_(PCM_READ, + xpp_line_t lines; // Must be 0xFF + byte pcm[PCM_CHUNKSIZE]; + ); + + H_(PCM_GEN, + xpp_line_t lines; + byte gen; + byte pcm_seq[ZT_CHUNKSIZE]; + ); + + H_(SYNC_SOURCE, + byte mask; + ); + H_(SYNC_REPLY, + byte mask; + ); + + H_(LOOPBACK_AX, + byte data[XPP_MAX_DATA]; // FIXME: what data size? + ); + H_(LOOPBACK_XA, + byte data[XPP_MAX_DATA]; // FIXME: what data size? + ); + H_(DIAG_FE); + unsigned char raw[0]; + }; +} __attribute__((packed)) xpp_packet_r_t; +#undef H_ + +#ifdef __KERNEL__ + +enum { + XPP_PACKET_FIREANDFORGET = 0x1, +}; + +/** + * The packet that will actually be sent on the wire. + * + * TODO: not a good medium-level abstrction + */ +struct xpp_packet { + struct xpp_packet_r content; + unsigned int flags; + size_t datalen; + struct list_head list; +}; + +#define DATA_LEN(p, name) \ + sizeof(p->content.cmd_ ## name) + +#define PACKET_LEN(p) \ + ((p)->datalen + sizeof(xpp_addr_t) + 1) + +#define PACKET_INIT(p, name) \ + p->content.opcode = XPP_ ## name; \ + p->datalen = DATA_LEN(p, name) + +#define PACKET_FIELD(p, name, field) \ + p->content.cmd_ ## name.field + +void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg); +void enqueue_xmit(xbus_t *xbus, xpacket_t *pack); +void process_sim_queue(void *xbus); +int validate_reply(xpacket_t *reply); +int packet_receive(xbus_t *xbus, xpacket_t *pack); +int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data); +int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data); + +#endif + +#endif /* XPP_PROTO_H */ diff --git a/xpp/xpp_usb.c b/xpp/xpp_usb.c new file mode 100644 index 0000000..2c8b4a6 --- /dev/null +++ b/xpp/xpp_usb.c @@ -0,0 +1,882 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 <linux/version.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +# warning "This module is tested only with 2.6 kernels" +#endif + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/delay.h> /* for udelay */ +#include <linux/seq_file.h> +#include <asm/uaccess.h> +#include <asm/atomic.h> +#include <asm/timex.h> +#include <linux/proc_fs.h> +#include <linux/usb.h> +#include "xpd.h" +#include "xproto.h" +#include "xpp_zap.h" + +static const char revision[] = "$Revision$"; + +DEF_PARM(int, print_dbg, 1, "Print DBG statements"); /* must be before zap_debug.h */ + +#include "zap_debug.h" + +/* FIXME: A flag that was deprecated at some point, and rather useless */ +/* anyway. Only used in the code or-ed to other flags */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,14) +# define URB_ASYNC_UNLINK 0 +#endif + +#define USBDEV_MAX 10 +/* Get a minor range for your devices from the usb maintainer */ +#define USB_SKEL_MINOR_BASE 192 + +#ifdef CONFIG_PROC_FS +#define PROC_XBUSES "xpp_usb" +#define PROC_USBXPP_SUMMARY "xpp_usb" +#endif + +#ifdef DEBUG_PCM_TIMING +static cycles_t stamp_last_pcm_read; +static cycles_t accumulate_diff; +#endif + +struct xusb_model_info; + +struct xusb_endpoint { + int epnum; + int max_size; +}; + +static int xusb_packet_send(xbus_t *xbus, xpacket_t *pack); + +xbus_ops_t xusb_ops = { + .packet_send = xusb_packet_send, + .packet_new = NULL, // Default allocator + .packet_free = NULL, // Default deallocator +}; + +enum { + XUSB_N_RX_PACKETS, + XUSB_N_TX_PACKETS, + XUSB_N_RX_ERRORS, + XUSB_N_TX_ERRORS, + XUSB_N_PCM_READS, + XUSB_N_PCM_WRITES, +}; + +#define XUSB_COUNTER(xusb, counter) ((xusb)->counters[XUSB_N_ ## counter]) + +#define C_(x) [ XUSB_N_ ## x ] = { #x } + +static struct xusb_counters { + char *name; +} xusb_counters[] = { + C_(RX_PACKETS), + C_(TX_PACKETS), + C_(RX_ERRORS), + C_(TX_ERRORS), + C_(PCM_READS), + C_(PCM_WRITES), +}; + +#undef C_ + +#define XUSB_COUNTER_MAX ARRAY_SIZE(xusb_counters) + +/* + * USB XPP Bus (a USB Device) + */ +struct xpp_usb_bus { + xbus_t *xbus; + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface; /* the interface for this device */ + unsigned char minor; /* the starting minor number for this device */ + + struct xusb_model_info *model_info; + struct xusb_endpoint ep_in; + struct xusb_endpoint ep_out; + + struct urb *read_urb; + + struct completion write_finished; /* wait for the write to finish */ + + int present; /* if the device is not disconnected */ + int reading; /* is the read_urb reading (listening) */ + struct semaphore sem; /* locks this structure */ + int counters[XUSB_COUNTER_MAX]; +}; + +static spinlock_t xusb_lock = SPIN_LOCK_UNLOCKED; +static struct xpp_usb_bus *xusb_array[USBDEV_MAX] = {}; +static unsigned bus_count = 0; + + +/* prevent races between open() and disconnect() */ +static DECLARE_MUTEX (disconnect_sem); + +/* + * Function Prototypes + */ +#if 0 +static ssize_t xusb_read (struct file *file, char *buffer, size_t count, loff_t *ppos); +static ssize_t xusb_write (struct file *file, const char *buffer, size_t count, loff_t *ppos); +static int xusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int xusb_open (struct inode *inode, struct file *file); +static int xusb_release (struct inode *inode, struct file *file); +static void xusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs); +#endif +static void xpp_urb_delete(struct urb *urb); +static struct urb *xpp_urb_new(struct xpp_usb_bus *dev, unsigned int ep_addr, size_t size, usb_complete_t urb_cb); +static void xpp_send_callback(struct urb *urb, struct pt_regs *regs); +static void xpp_receive_callback(struct urb *urb, struct pt_regs *regs); + +static int xusb_probe (struct usb_interface *interface, const struct usb_device_id *id); +static void xusb_disconnect (struct usb_interface *interface); +static int xusb_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); + +/*------------------------------------------------------------------*/ + +#if 0 +/** + * Allocates a new XPP packet. + * @xbus The XPP bus in which the packet will flow (for counters + * maintenance) + * @flags Flags for kernel memory allocation. + * @returns A pointer to the new packet, or NULL in case of failure. + * + * + * Packet allocation/deallocation: + * Sent packets: + * - Allocated by protocol commands + * - Deallocated by xmus_xmitter + * Receive packets: + * - Allocated/deallocated by xbus_xmiter + */ +xpacket_t *xusb_packet_new(xbus_t *xbus, int flags) +{ + xpacket_t *pack; + + /* To avoid races we increament counter in advance and decrement it later + * in case of failure */ + atomic_inc(&xbus->packet_counter); + //DBG("Incremented packet_counter of bus %s (new packet) to %d\n", + // xbus->busname, atomic_read(&xbus->packet_counter)); + pack = kmem_cache_alloc(packet_cache, flags); + if (pack) { + memset(pack, 0, sizeof(xpacket_t)); + atomic_inc(&xpacket_count); + } else { + atomic_dec(&xbus->packet_counter); + //DBG("Decremented packet_counter of bus %s (failed new packet) to %d\n", + // xbus->busname, atomic_read(&xbus->packet_counter)); + } + return pack; +} + +void xusb_packet_free(xbus_t *xbus, xpacket_t *p) +{ + kmem_cache_free(packet_cache, p); + atomic_dec(&xpacket_count); + atomic_dec(&xbus->packet_counter); + //DBG("Decremented packet_counter of bus %s (freed packet) to %d\n", + // xbus->busname, atomic_read(&xbus->packet_counter)); +} +#endif + +static int xusb_packet_send(xbus_t *xbus, xpacket_t *pack) +{ + struct xpp_usb_bus *xusb = xbus->priv; + struct urb *urb; + int ret = 0; + size_t size; + + BUG_ON(!pack); + if(!xusb->present) { + NOTICE("tried to send packets to non-exitant USB device. Ignored\n"); + goto error; + } +#if SOFT_SIMULATOR + { + int toxpd = XPD_NUM(pack->content.addr); + + if (xbus->sim[toxpd].softloop_xpd) { + // "send" through loopback queue + //DBG("%s: ENQUEUE toxpd=%d, opcode=%X\n", xbus->busname, toxpd, pack->content.opcode); + XBUS_COUNTER(xbus, SOFTSIM_PACKETS)++; + xbus_enqueue_packet(xbus, &xbus->sim_packet_queue, pack); + ret = queue_work(xbus->sim_workqueue, &xbus->sim_work); + if(ret < 0) { + ERR("%s: queue_work failed with %d (ignoring)\n", __FUNCTION__, ret); + goto error; + } + } + return 0; + } +#endif + size = min(PACKET_LEN(pack), (size_t)xusb->ep_out.max_size); + if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_WRITE)) { + XUSB_COUNTER(xusb, PCM_WRITES)++; + +#ifdef DEBUG_PCM_TIMING + /* + * DEBUG: high-res timing of PCM_READ to PCM_WRITE + */ + cycles_t diff = get_cycles() - stamp_last_pcm_read; + accumulate_diff += diff; +#endif +#if 0 + static int rate_limit; + if((rate_limit++ % 1009) < 3) { + dump_packet("USB SEND PCM", pack, print_dbg); + } +#endif + } else { + dump_packet("USB_PACKET_SEND", pack, print_dbg); + } + urb = xpp_urb_new(xusb, xusb->ep_out.epnum, size, xpp_send_callback); + if (!urb) { + ERR("No free urbs available\n"); + ret = -ENOMEM; + goto error; + } + + /* FIXME: FIXME: FIXME: we use copy+free until low-level drivers allocate memory themselves */ + memcpy(urb->transfer_buffer, &pack->content, size); + xbus->ops->packet_free(xbus, pack); + + ret = usb_submit_urb(urb, GFP_KERNEL); + if(ret < 0) { + ERR("%s: failed submit_urb\n", __FUNCTION__); + XUSB_COUNTER(xusb, TX_ERRORS)++; + xpp_urb_delete(urb); + return -EBADF; + } + return 0; +error: + xbus->ops->packet_free(xbus, pack); // FIXME: eventually will be done in the urb callback + return ret; +} + +static void xpp_urb_delete(struct urb *urb) +{ + // DBG("%s: (%d) %p %X", __FUNCTION__, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); + usb_buffer_free (urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb (urb); +} + +static struct urb *xpp_urb_new(struct xpp_usb_bus *dev, unsigned int ep_addr, size_t size, usb_complete_t urb_cb) + +{ + struct usb_device *udev = dev->udev; + struct urb *urb; + unsigned char *buffer; /* the buffer to send data */ + unsigned int epnum = ep_addr & USB_ENDPOINT_NUMBER_MASK; + int pipe = usb_pipein(ep_addr) + ? usb_rcvbulkpipe(udev, epnum) + : usb_sndbulkpipe(udev, epnum); + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + err("No free urbs available"); + return NULL; + } + + /* on some platforms using this kind of buffer alloc + * call eliminates a dma "bounce buffer". + * + * NOTE: you'd normally want i/o buffers that hold + * more than one packet, so that i/o delays between + * packets don't hurt throughput. (Probably applies only to isochronous + * transfers) + */ + urb->transfer_flags = (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK); + buffer = usb_buffer_alloc(udev, size, GFP_ATOMIC, &urb->transfer_dma); + // DBG("(%d) %p / %x", size, buffer, urb->transfer_dma); + if (!buffer) { + err("Couldn't allocate buffer"); + usb_free_urb(urb); + return NULL; + } + usb_fill_bulk_urb(urb, udev, pipe, buffer, size, urb_cb, dev); + return urb; +} + +/*------------------------- XPP USB Bus Handling -------------------*/ + +static struct xusb_model_info { + const char *desc; + struct xusb_endpoint in; + struct xusb_endpoint out; + xbus_type_t bus_type; +} model_table[] = { + { .in = { .epnum = 0x86 }, .out = { .epnum = 0x2 }, .bus_type = FIRMWARE_LOOPBACK, .desc = "bulkloop.hex" }, + { .in = { .epnum = 0x86 }, .out = { .epnum = 0x2 }, .bus_type = FIRMWARE_LOOPBACK, .desc = "FPGA_bulkloop.hex" }, + { .in = { .epnum = 0x86 }, .out = { .epnum = 0x2 }, .bus_type = FIRMWARE_XPP, .desc = "FPGA_XPD.hex" }, +}; + +/* table of devices that work with this driver */ +static struct usb_device_id xusb_table [] = { +// { USB_DEVICE(0x04B4, 0x8613) }, // default of cypress + { USB_DEVICE(0x0547, 0x1002), .driver_info=(int)&model_table[0] }, // bulkloop.hex +// { USB_DEVICE(0x04B4, 0x1004), .driver_info=(int)&model_table[1] }, // FPGA_bulkloop.hex +// { USB_DEVICE(0x04B4, 0x1004), .driver_info=(int)&model_table[2] }, // FIXME: temporary test for Dima + { USB_DEVICE(0xE4E4, 0x2211), .driver_info=(int)&model_table[2] }, // FPGA_XPD.hex + //{ USB_DEVICE(0x0548, 0x1) }, + //{ USB_DEVICE(0x062a, 0x0) }, + /* "Gadget Zero" firmware runs under Linux */ + //{ USB_DEVICE(0x0525, 0xa4a0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, xusb_table); + + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver xusb_driver = { + .owner = THIS_MODULE, + .name = "xpp_usb", + .probe = xusb_probe, + .disconnect = xusb_disconnect, + .id_table = xusb_table, +}; + +/* + * File operations needed when we register this driver. + * This assumes that this driver NEEDS file operations, + * of course, which means that the driver is expected + * to have a node in the /dev directory. If the USB + * device were for a network interface then the driver + * would use "struct net_driver" instead, and a serial + * device would use "struct tty_driver". + */ +static struct file_operations xusb_fops = { + /* + * The owner field is part of the module-locking + * mechanism. The idea is that the kernel knows + * which module to increment the use-counter of + * BEFORE it calls the device's open() function. + * This also means that the kernel can decrement + * the use-counter again before calling release() + * or should the open() function fail. + */ + .owner = THIS_MODULE, +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ +static struct usb_class_driver xusb_class = { + .name = "usb/xpp_usb%d", + .fops = &xusb_fops, +/* FIXME: The sysfs class interfase seems to have chaged around here */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) + .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, +#endif + .minor_base = USB_SKEL_MINOR_BASE, +}; + +/* + * set up the endpoint information + * check out the endpoints + * FIXME: Should be simplified (above 2.6.10) to use usb_dev->ep_in[0..16] and usb_dev->ep_out[0..16] + */ +static int set_endpoints(struct xpp_usb_bus *xusb, struct usb_interface *interface, struct xusb_model_info *model_info) +{ + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + + iface_desc = &interface->altsetting[0]; + DBG("Found interface. Endpoints: %d\n", iface_desc->desc.bNumEndpoints); + +#define BULK_ENDPOINT(ep) (((ep)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + int epnum = endpoint->bEndpointAddress; + + if(!BULK_ENDPOINT(endpoint)) { + DBG("endpoint 0x%x is not bulk: mbAttributes=0x%X\n", + epnum, endpoint->bmAttributes); + continue; + } + if(epnum & USB_DIR_IN) { // Input + if(epnum == model_info->in.epnum) { + if(endpoint->wMaxPacketSize < sizeof(xpacket_raw_t)) { + ERR("USB input endpoint 0x%X support only wMaxPacketSize=%d (need USB-2)\n", epnum, endpoint->wMaxPacketSize); + break; + } + xusb->ep_in.epnum = epnum; + xusb->ep_in.max_size = endpoint->wMaxPacketSize; + } + } else { // Output + if(epnum == model_info->out.epnum) { + if(endpoint->wMaxPacketSize < sizeof(xpacket_raw_t)) { + ERR("USB output endpoint 0x%X support only wMaxPacketSize=%d (need USB-2)\n", epnum, endpoint->wMaxPacketSize); + break; + } + xusb->ep_out.epnum = epnum; + xusb->ep_out.max_size = endpoint->wMaxPacketSize; + } + } + } + if (!xusb->ep_in.epnum || !xusb->ep_out.epnum) { + ERR("Couldn't find bulk-in or bulk-out endpoints\n"); + return 0; + } + return 1; +} + +/** + * xusb_probe + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int xusb_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct xpp_usb_bus *xusb = NULL; + struct xusb_model_info *model_info = (struct xusb_model_info*)id->driver_info; + struct proc_dir_entry *procsummary; + xbus_t *xbus; + unsigned long flags; + int retval = -ENOMEM; + int i; + + INFO("New XUSB device MODEL=%s bus_type=%d\n", model_info->desc, model_info->bus_type); + + if((retval = usb_reset_device(udev)) < 0) { + ERR("usb_reset_device failed: %d\n", retval); + goto probe_failed; + } + if (!model_info) { + ERR("Missing endpoint setup for this device %d:%d\n", + udev->descriptor.idVendor,udev->descriptor.idProduct); + retval = -ENODEV; + goto probe_failed; + } + + /* allocate memory for our device state and initialize it */ + xusb = kmalloc(sizeof(struct xpp_usb_bus), GFP_KERNEL); + if (xusb == NULL) { + ERR("xpp_usb: Unable to allocate new xpp usb bus\n"); + retval = -ENOMEM; + goto probe_failed; + } + memset(xusb, 0, sizeof(struct xpp_usb_bus)); + + init_MUTEX (&xusb->sem); + xusb->udev = udev; + xusb->interface = interface; + xusb->model_info = model_info; + + if(!set_endpoints(xusb, interface, model_info)) { + retval = -ENODEV; + goto probe_failed; + } + xusb->read_urb = xpp_urb_new(xusb, xusb->ep_in.epnum, xusb->ep_in.max_size, xpp_receive_callback); + if (!xusb->read_urb) { + ERR("No free urbs available\n"); + retval = -ENOMEM; + goto probe_failed; + } + /* allow device read, write and ioctl */ + xusb->present = 1; + + /* we can register the device now, as it is ready */ + usb_set_intfdata (interface, xusb); + retval = usb_register_dev (interface, &xusb_class); + if (retval) { + /* something prevented us from registering this driver */ + ERR ("Not able to get a minor for this device."); + goto probe_failed; + } + + xusb->minor = interface->minor; + + /* let the user know what node this device is now attached to */ + INFO ("USB XPP device now attached to minor %d\n", xusb->minor); + + /* Allocate high level structures */ + xbus = xbus_new((model_info->bus_type == FIRMWARE_LOOPBACK) ? ~0 : 0); + if(!xbus) { + retval = -ENOMEM; + goto probe_failed; + } + xusb->xbus = xbus; + xbus->priv = xusb; + xbus->bus_type = model_info->bus_type; + + spin_lock_irqsave(&xusb_lock, flags); + for(i = 0; i < USBDEV_MAX; i++) { + if(xusb_array[i] == NULL) + break; + } + if(i >= USBDEV_MAX) { + ERR("xpp_usb: Too many XPP USB buses\n"); + retval = -ENOMEM; + goto probe_failed; + } + spin_unlock_irqrestore(&xusb_lock, flags); + { + char path[XBUS_DESCLEN]; + + usb_make_path(udev, path, XBUS_DESCLEN); // May trunacte... ignore + snprintf(xbus->busdesc, XBUS_DESCLEN, "%s", path); + } + xbus->ops = &xusb_ops; + + DBG("GOT XPP USB BUS #%d: %s (type=%d)\n", i, xbus->busdesc, xbus->bus_type); + + xusb_array[i] = xusb; + + +#ifdef CONFIG_PROC_FS + DBG("Creating proc entry " PROC_USBXPP_SUMMARY " in bus proc dir.\n"); + procsummary = create_proc_read_entry(PROC_USBXPP_SUMMARY, 0444, xbus->proc_xbus_dir, + xusb_read_proc, xusb); + //xbus->procsummary = 1; // temporary: not 0, for the condition below + if (!procsummary) { + ERR("Failed to create proc read entry for xbus %s\n", xbus->busname); + // FIXME: better error handling + retval = -EIO; + goto probe_failed; + } +#endif + retval = usb_submit_urb(xusb->read_urb, GFP_ATOMIC); + if(retval < 0) { + ERR("%s: Failed to submit the receive URB errno=%d\n", __FUNCTION__, retval); + } + bus_count++; + xbus_activate(xusb->xbus); + return retval; +probe_failed: + ERR("Failed to initialize xpp usb bus: %d\n", retval); + usb_set_intfdata (interface, NULL); + if(xusb) { + if(xusb->read_urb) + xpp_urb_delete(xusb->read_urb); + if(xusb->minor) // passed registration phase + usb_deregister_dev(interface, &xusb_class); + kfree(xusb); + } + return retval; +} + +/** + * xusb_disconnect + * + * Called by the usb core when the device is removed from the system. + * + * This routine guarantees that the driver will not submit any more urbs + * by clearing dev->udev. It is also supposed to terminate any currently + * active urbs. Unfortunately, usb_bulk_msg(), used in xusb_read(), does + * not provide any way to do this. But at least we can cancel an active + * write. + */ +static void xusb_disconnect(struct usb_interface *interface) +{ + struct xpp_usb_bus *xusb; + xbus_t *xbus; + int minor; + int i; + + DBG("CALLED\n"); + /* prevent races with open() */ + down (&disconnect_sem); + + xusb = usb_get_intfdata (interface); + usb_set_intfdata (interface, NULL); + xbus = xusb->xbus; + + /* find our xusb */ + for(i = 0; i < USBDEV_MAX; i++) { + if(xusb_array[i] == xusb) + break; + } + BUG_ON(i >= USBDEV_MAX); + xusb_array[i] = NULL; + +#ifdef CONFIG_PROC_FS + if(xbus->proc_xbus_dir) { + remove_proc_entry(PROC_USBXPP_SUMMARY, xbus->proc_xbus_dir); + } +#endif + xusb->present = 0; + xbus_deactivate(xbus); // Blocking until fully deactivated! + + down (&xusb->sem); + + minor = xusb->minor; + + /* give back our minor */ + usb_deregister_dev (interface, &xusb_class); + + /* terminate an ongoing write */ + // FIXME: Does it really kill pending URB's? + + if(xusb->read_urb) + xpp_urb_delete(xusb->read_urb); + + up (&xusb->sem); + DBG("Semaphore released\n"); + + kfree(xusb); + + up (&disconnect_sem); + INFO("XUSB #%d now disconnected", minor); +} + +static void xpp_send_callback(struct urb *urb, struct pt_regs *regs) +{ + struct xpp_usb_bus *xusb = (struct xpp_usb_bus *)urb->context; + xbus_t *xbus = xusb->xbus; + + BUG_ON(!xbus); + /* sync/async unlink faults aren't errors */ + if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET)) { + static int rate_limit; + if((rate_limit++ % 1000) < 10) + DBG("nonzero read bulk status received: %d", urb->status); + XUSB_COUNTER(xusb, TX_ERRORS)++; + } + if(!xusb->present) { + ERR("A packet from non-connected device?\n"); + return; + } + xpp_urb_delete(urb); + /* allow device read, write and ioctl */ + XUSB_COUNTER(xusb, TX_PACKETS)++; +} + +static void xpp_receive_callback(struct urb *urb, struct pt_regs *regs) +{ + struct xpp_usb_bus *xusb = (struct xpp_usb_bus *)urb->context; + xbus_t *xbus = xusb->xbus; + + xpacket_t *pack; + size_t size; + int retval; + + BUG_ON(!xbus); + if (urb->status) { + /* sync/async unlink faults aren't errors */ + if (!(urb->status == -EOVERFLOW || urb->status == -EMSGSIZE)) { + ERR("Dropped connection due to bad URB status: %d\n", urb->status); + return; + } else { + DBG("nonzero read bulk status received: %d\n", urb->status); + goto end; + } + } + if(!down_read_trylock(&xbus->in_use)) { + ERR("%s: xbus is going down\n", __FUNCTION__); + return; + } + if(!xusb->present) { + ERR("A packet from non-connected device?\n"); + up_read(&xbus->in_use); + return; + } + pack = xbus->ops->packet_new(xbus, GFP_ATOMIC); + if(!pack) { + ERR("%s: Not enough memory for packets. Dropping\n", __FUNCTION__); + goto end; + } + + size = urb->actual_length; + memcpy(&pack->content, urb->transfer_buffer, size); + + pack->datalen = size - sizeof(xpd_addr_t) - 1; // opcode size + // DBG("datalen of new packet: %d\n", pack->datalen); + + // Send UP + if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_READ)) { + XUSB_COUNTER(xusb, PCM_READS)++; + +#ifdef DEBUG_PCM_TIMING + /* + * DEBUG: high-res timing of PCM_READ to PCM_WRITE + */ + stamp_last_pcm_read = get_cycles(); +#endif + // fill_beep((u_char *)&PACKET_FIELD(pack, PCM_READS, pcm), 2); // Debugging BEEP +#if 0 + static int rate_limit; + if((rate_limit++ % 1000) == 0) + dump_packet("USB RECEIVE PCM", pack, print_dbg); +#endif + } else if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_WRITE)) { // FIRMWARE_LOOPBACK +#if 0 + static int rate_limit; + if((rate_limit++ % 1000) == 0) + dump_packet("USB RECEIVE (LOOPBACK) PCM", pack, print_dbg); +#endif + } else { + char title[XBUS_DESCLEN]; + + snprintf(title, XBUS_DESCLEN, "USB_PACKET_RECEIVE callback (%s)", xbus->busname); + dump_packet(title, pack, print_dbg); + } + packet_receive(xbus, pack); + XUSB_COUNTER(xusb, RX_PACKETS)++; +end: + up_read(&xbus->in_use); + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval < 0) { + ERR("failed re-submitting read urb, error %d\n", retval); + return; + } +} + + +/*------------------------- Initialization -------------------------*/ + +int __init xpp_usb_init(void) +{ + int result; + //struct xpp_usb_bus *xusb; + + INFO("%s revision %s\n", THIS_MODULE->name, revision); + + /* register this driver with the USB subsystem */ + result = usb_register(&xusb_driver); + if (result) { + ERR("usb_register failed. Error number %d", result); + return result; + } + return 0; +} + + +void __exit xpp_usb_cleanup(void) +{ + int i, j; + + DBG("\n"); + for(i = 0; i < USBDEV_MAX; i++) { + xbus_t *xbus; + + if(xusb_array[i] == NULL) + continue; + xbus = xusb_array[i]->xbus; + if(!xbus) { + ERR("%s: missing xbus. Skipping\n", __FUNCTION__); + continue; + } + for(j = 0; j < MAX_XPDS; j++) { + xpd_t *xpd = xpd_of(xbus, j); + + if(xpd) { + if(xpd->id != j) { + ERR("%s: BUG: xpd->id=%d != j=%d\n", __FUNCTION__, xpd->id, j); + continue; + } +#if 0 // FIXME: retest after new driver start working + CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, 0xFF, 0); // Disable all hardware channels + CALL_XMETHOD(LED, xbus, xpd, 0xFF, 1, 0); // FIXME: Show activated channels +#endif + } + } + } + /* deregister this driver with the USB subsystem */ + usb_deregister(&xusb_driver); +} + + + +#ifdef CONFIG_PROC_FS + +static int xusb_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + //unsigned long stamp = jiffies; + struct xpp_usb_bus *xusb = data; + + if(!xusb) + goto out; + // TODO: probably needs a per-xusb lock: + spin_lock_irqsave(&xusb_lock, flags); + int i; + + len += sprintf(page + len, "device: %d, #altsettings: %d, minor: %d\n" + "\tBus Type:%d (Model Info: %s)\n" + "\tIn: 0x%02X - Size: %d\n" + "\tOut: 0x%02X - Size: %d\n", + xusb->udev->devnum, + xusb->interface->num_altsetting, + xusb->minor, + xusb->model_info->bus_type, + xusb->model_info->desc, + xusb->ep_in.epnum, + xusb->ep_in.max_size, + xusb->ep_out.epnum, + xusb->ep_out.max_size + ); +#ifdef DEBUG_PCM_TIMING + len += sprintf(page + len, "\nstamp_last_pcm_read=%lld accumulate_diff=%lld\n", stamp_last_pcm_read, accumulate_diff); +#endif + len += sprintf(page + len, "\nCOUNTERS:\n"); + for(i = 0; i < XUSB_COUNTER_MAX; i++) { + len += sprintf(page + len, "\t%-15s = %d\n", xusb_counters[i].name, xusb->counters[i]); + } +#if 0 + len += sprintf(page + len, "<-- len=%d\n", len); +#endif + spin_unlock_irqrestore(&xusb_lock, flags); +out: + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; + +} + +#endif + + + +MODULE_DESCRIPTION("XPP USB Driver"); +MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("$Id$"); + +module_init(xpp_usb_init); +module_exit(xpp_usb_cleanup); diff --git a/xpp/xpp_zap.c b/xpp/xpp_zap.c new file mode 100644 index 0000000..878bad7 --- /dev/null +++ b/xpp/xpp_zap.c @@ -0,0 +1,2312 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004, Xorcom + * + * Derived from ztdummy + * + * Copyright (C) 2002, Hermes Softlab + * Copyright (C) 2004, 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 <linux/version.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +# warning "This module is tested only with 2.6 kernels" +#endif + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> /* for udelay */ +#include <linux/workqueue.h> +#include <linux/proc_fs.h> +#include <zaptel.h> +#include "xproto.h" +#include "xpp_zap.h" + +static char revision[] = "$Revision$"; + +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *xpp_procdir = NULL; +#define PROC_DIR "xpp" +#define PROC_XBUSES "xbuses" +#define PROC_SYNC "sync" +#define PROC_XBUS_SUMMARY "summary" +#define PROC_XPD_ZTREGISTER "zt_registration" +#define PROC_XPD_SUMMARY "summary" +#endif + +#undef WITH_RBS +//#define WITH_RBS + +#define XPP_CTL_MAJOR 42 +#define MAX_BUSES 16 +#define MAX_QUEUE_LEN 10000 +#define LED_BLINK_PERIOD (HZ/8) +#define SAMPLE_TICKS 10000 + +static spinlock_t xbuses_lock = SPIN_LOCK_UNLOCKED; +static xbus_t *xbuses_array[MAX_BUSES] = {}; +static int bus_count = 0; +static struct timer_list xpp_timer; +xpd_t *sync_master = NULL; // Start with host based sync +static unsigned int xpp_timer_count = 0; +static unsigned int xpp_last_jiffies = 0; +struct workqueue_struct *xpp_worker = NULL; + +static LIST_HEAD(xpd_list); + +DEF_PARM(int, print_dbg, 1, "Print DBG statements"); +DEF_PARM(int, max_queue_len, MAX_QUEUE_LEN, "Maximum Queue Length."); +DEF_PARM(int, xbus_err_disable_bus, 1000, "Number of errors needed to disable bus"); +DEF_PARM(int, ignore_xpds, 0, "a bitmask of xpd numbers to ignore"); +#ifdef SOFT_SIMULATOR +DEF_PARM(ulong, softloop_xpds, 0, "a bitmask of software xpd numbers"); +#endif +DEF_PARM(ulong, pcm_gen, 0, "a bitmask of line numbers for hardware tone generator"); + +DEF_ARRAY(ulong, enabled_channels, MAX_XPDS, ~0, "Enabled channels for each xpd"); + +#include "zap_debug.h" + + +static int xpd_zaptel_register(xpd_t *xpd); +static int xpd_zaptel_unregister(xpd_t *xpd); +static void xbus_remove(xbus_t *xbus); +static void xpd_blink_leds(xpd_t *xpd); +static void xpp_ring_generate(xpd_t *xpd); +static void xpp_transmitprep(xpd_t *xpd); +static void xpp_receiveprep(xpd_t *xpd); +static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +static int proc_xpd_ztregister_read(char *page, char **start, off_t off, int count, int *eof, void *data); +static int proc_xpd_ztregister_write(struct file *file, const char __user *buffer, unsigned long count, void *data); +xbus_t *xbus_of(int xbus_num); +static void xpd_cleanup(xpd_t *xpd); +static void xpd_card_disable(xpd_t *xpd); +static void update_xpd_status(xpd_t *xpd, int alarm_flag); + +#define SPAN_REGISTERED(xpd) ((xpd)->span.flags & ZT_FLAG_REGISTERED) + +/*------------------------- Packet Handling ------------------------*/ +static kmem_cache_t *packet_cache = NULL; +static atomic_t xpacket_count = ATOMIC_INIT(0); + +void card_detected(void *data) +{ + struct card_desc_struct *card_desc = (struct card_desc_struct *)data; + xbus_t *xbus; + xpd_t *xpd; + int xpd_num; + byte type; + byte rev; + const xops_t *xops; + const xproto_table_t *proto_table; + + BUG_ON(!card_desc); + BUG_ON(card_desc->magic != CARD_DESC_MAGIC); + xbus = card_desc->xbus; + xpd_num = card_desc->xpd_num; + type = card_desc->type; + rev = card_desc->rev; + BUG_ON(!xbus); + DBG("%s: xpd_num=%d type=%d rev=%d\n", xbus->busname, xpd_num, type, rev); + xpd = xpd_of(xbus, xpd_num); + if(xpd) { + if(type == XPD_TYPE(NOMODULE)) { + NOTICE("%s: xpd #%d: removed\n", __FUNCTION__, xpd_num); + xpd_card_disable(xpd); + goto out; + } + NOTICE("%s: xpd #%d: already exists\n", __FUNCTION__, xpd_num); + goto out; + } + if(type == XPD_TYPE(NOMODULE)) { + DBG("No module at address=%d\n", xpd_num); + goto out; + } + proto_table = get_xproto_table(type); + if(!proto_table) { + NOTICE("%s: xpd #%d: missing protocol table for type=%d. Ignored.\n", __FUNCTION__, xpd_num, type); + goto out; + } + xops = &proto_table->xops; + BUG_ON(!xops); + xpd = xops->card_new(xbus, xpd_num, proto_table, rev); + if(!xpd) { + NOTICE("card_new(%s,%d,%d,%d) failed. Ignored.\n", xbus->busname, xpd_num, proto_table->type, rev); + goto out; + } +#if 0 + /* + * Is it nessessary? + */ + if(xpd->type == XPD_TYPE_FXO) { + int i; + + for(i = 0; i < xpd->channels; i++) { + zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK); + } + } +#endif +#ifdef CONFIG_PROC_FS + DBG("Creating xpd proc directory for %s/%s\n", xbus->busname, xpd->xpdname); + xpd->proc_xpd_dir = proc_mkdir(xpd->xpdname, xbus->proc_xbus_dir); + if(!xpd->proc_xpd_dir) { + ERR("Failed to create proc directory for %s/%s\n", xbus->busname, xpd->xpdname); + goto err; + } + xpd->proc_xpd_summary = create_proc_read_entry(PROC_XPD_SUMMARY, 0444, xpd->proc_xpd_dir, + xpd_read_proc, xpd); + if(!xpd->proc_xpd_summary) { + ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_SUMMARY, xbus->busname, xpd->xpdname); + goto err; + } + xpd->proc_xpd_ztregister = create_proc_entry(PROC_XPD_ZTREGISTER, 0644, xpd->proc_xpd_dir); + if (!xpd->proc_xpd_ztregister) { + ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_ZTREGISTER, xbus->busname, xpd->xpdname); + goto err; + } + xpd->proc_xpd_ztregister->data = xpd; + xpd->proc_xpd_ztregister->read_proc = proc_xpd_ztregister_read; + xpd->proc_xpd_ztregister->write_proc = proc_xpd_ztregister_write; +#endif + list_add(&xpd->xpd_list, &xpd_list); + xbus->xpds[xpd->id] = xpd; + xbus->num_xpds++; + CALL_XMETHOD(card_init, xbus, xpd); + // Turn off all channels + CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, 0xFF, 0); +// CALL_XMETHOD(LED, xbus, xpd, 0xFF, LED_RED, 0); // FIXME: Show activated channels + // Turn on enabled channels + CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, xpd->enabled_chans, 1); + atomic_set(&xpd->card_present, 1); + xpd_zaptel_register(xpd); +#if 0 + // FIXME: not yet initialized... + xpp_check_hookstate(xpd, line_status); +#endif + +out: + memset(card_desc, 0, sizeof(struct card_desc_struct)); + kfree(card_desc); + return; +err: + xpd_cleanup(xpd); + goto out; +} + +/** + * Allocates a new XPP packet. + * @xbus The XPP bus in which the packet will flow (for counters + * maintenance) + * @flags Flags for kernel memory allocation. + * @returns A pointer to the new packet, or NULL in case of failure. + * + * + * Packet allocation/deallocation: + * Sent packets: + * - Allocated by protocol commands + * - Deallocated by xmus_xmitter + * Receive packets: + * - Allocated/deallocated by xbus_xmiter + */ +xpacket_t *softloop_packet_new(xbus_t *xbus, int flags) +{ + xpacket_t *pack; + + /* To avoid races we increament counter in advance and decrement it later + * in case of failure */ + atomic_inc(&xbus->packet_counter); + //DBG("Incremented packet_counter of bus %s (new packet) to %d\n", + // xbus->busname, atomic_read(&xbus->packet_counter)); + pack = kmem_cache_alloc(packet_cache, flags); + if (pack) { + memset(pack, 0, sizeof(xpacket_t)); + atomic_inc(&xpacket_count); + } else { + atomic_dec(&xbus->packet_counter); + //DBG("Decremented packet_counter of bus %s (failed new packet) to %d\n", + // xbus->busname, atomic_read(&xbus->packet_counter)); + } + return pack; +} + +void softloop_packet_free(xbus_t *xbus, xpacket_t *p) +{ + kmem_cache_free(packet_cache, p); + atomic_dec(&xpacket_count); + atomic_dec(&xbus->packet_counter); + //DBG("Decremented packet_counter of bus %s (freed packet) to %d\n", + // xbus->busname, atomic_read(&xbus->packet_counter)); +} + +int call_proto(xbus_t *xbus, xpacket_t *pack) +{ + const xproto_entry_t *xe; + int toxpd = XPD_NUM(pack->content.addr); + xpd_t *xpd = xpd_of(xbus, toxpd); + + xe = find_xproto_entry(xpd, pack->content.opcode); + return 0; +} + +static void external_sync(xpd_t *the_xpd) +{ + int i, j; + + DBG("SYNC %s\n", (the_xpd) ? "EXTERNAL" : "HOST"); + for(i = 0; i < MAX_BUSES; i++) { + xbus_t *xbus = xbus_of(i); + if(!xbus) + continue; + if (!xbus->hardware_exists) + continue; + for(j = 0; j < MAX_XPDS; j++) { + xpd_t *xpd = xbus->xpds[j]; + if(xpd) + CALL_XMETHOD(SYNC_SOURCE, xbus, xpd, 1, (the_xpd != NULL)); + } + } +} + +void set_sync_master(xpd_t *xpd) +{ + DBG("SYNC: %s => %s\n", + (sync_master) ? sync_master->xpdname : "HOST", + (xpd) ? xpd->xpdname : "HOST" + ); + sync_master = xpd; + if(!sync_master) { + external_sync(NULL); + if(!timer_pending(&xpp_timer)) { + xpp_timer.function = xpp_tick; + xpp_timer.data = 0; + xpp_timer.expires = jiffies + 1; /* Must be 1KHz rate */ + add_timer(&xpp_timer); + } + } else { + del_timer_sync(&xpp_timer); + external_sync(xpd); + xpp_tick((unsigned long)xpd); + } +} + +void xpp_tick(unsigned long param) +{ + xbus_t *xbus; + xpd_t *the_xpd = (xpd_t *)param; + int i; + int j; + + if(!the_xpd) { /* Called from timer */ +#if 0 + static int rate_limit = 0; + if(rate_limit++ % 1000 == 0) + DBG("FROM_TIMER\n"); +#endif + mod_timer(&xpp_timer, jiffies + 1); /* Must be 1KHz rate */ + } + else if(the_xpd != sync_master) + return; + /* Statistics */ + if((xpp_timer_count % SAMPLE_TICKS) == 0) { + xpp_last_jiffies = jiffies; + } + xpp_timer_count++; + + for(i = 0; i < MAX_BUSES; i++) { + xbus = xbus_of(i); + if(!xbus) + continue; + if (!xbus->hardware_exists) + continue; +#if 0 + if(xbus->open_counter == 0) + continue; // optimize, but zttool loopback won't function +#endif + for(j = 0; j < MAX_XPDS; j++) { + xpd_t *xpd = xbus->xpds[j]; + + if(!xpd) + continue; + if(!atomic_read(&xpd->card_present)) + continue; + xpd->timer_count++; + CALL_XMETHOD(card_tick, xbus, xpd); + if(!SPAN_REGISTERED(xpd)) + continue; + xpd_blink_leds(xpd); + if(xpd->direction == TO_PSTN) + xpp_ring_generate(xpd); + xpp_transmitprep(xpd); + xpp_receiveprep(xpd); + } + } +} + +#if HZ != 1000 +#warning This module will not be usable since the kernel HZ setting is not 1000 ticks per second. +#endif + +static void xpd_cleanup(xpd_t *xpd) +{ + xbus_t *xbus = NULL; + + if(!xpd) + return; + xbus = xpd->xbus; + xpd_card_disable(xpd); + DBG("%s/%s\n", xbus->busname, xpd->xpdname); +#ifdef CONFIG_PROC_FS + if(xpd->proc_xpd_dir) { + if(xpd->proc_xpd_summary) { + DBG("Removing proc '%s' for %s/%s\n", PROC_XPD_SUMMARY, xbus->busname, xpd->xpdname); + remove_proc_entry(PROC_XPD_SUMMARY, xpd->proc_xpd_dir); + xpd->proc_xpd_summary = NULL; + } + if(xpd->proc_xpd_ztregister) { + DBG("Removing proc '%s' for %s/%s\n", PROC_XPD_ZTREGISTER, xbus->busname, xpd->xpdname); + remove_proc_entry(PROC_XPD_ZTREGISTER, xpd->proc_xpd_dir); + xpd->proc_xpd_ztregister = NULL; + } + DBG("Removing proc directory for %s/%s\n", xbus->busname, xpd->xpdname); + remove_proc_entry(xpd->xpdname, xbus->proc_xbus_dir); + xpd->proc_xpd_dir = NULL; + } +#endif +} + +void init_xbus_packet_queue(packet_queue_t *q, const char name[]) +{ + INIT_LIST_HEAD(&q->head); + spin_lock_init(&q->lock); + q->count = 0; + q->worst_count = 0; + q->overflows = 0; + snprintf(q->qname, XPD_NAMELEN, "%s", name); +} + +#if 0 +/* + * Assume the queue is locked + */ +void __dump_packet_queue(const char *msg, packet_queue_t *q) +{ + xpacket_t *tmp; + + list_for_each_entry(tmp, &q->head, list) { + dump_packet(msg, tmp); + } +} +#endif + +void drain_xbus_packet_queue(xbus_t *xbus, packet_queue_t *q) +{ + unsigned long flags; + xpacket_t *pack; + xpacket_t *next; + + spin_lock_irqsave(&q->lock, flags); + DBG("queue=%s count=%d\n", q->qname, q->count); + DBG(" total packets count=%d\n", atomic_read(&xpacket_count)); + list_for_each_entry_safe(pack, next, &q->head, list) { + list_del(&pack->list); + q->count--; + xbus->ops->packet_free(xbus, pack); + } + if(q->count != 0) + ERR("drain_xbus_packet_queue: queue %s still has %d packets\n", + q->qname, q->count); + spin_unlock_irqrestore(&q->lock, flags); +} + +void xbus_enqueue_packet(xbus_t *xbus, packet_queue_t *q, xpacket_t *pack) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + + if(q->count >= max_queue_len) { + static unsigned long last_notice = 0; // rate limit + + if((jiffies - last_notice) < HZ) { + NOTICE("xbus_enqueue_packet: dropping packet (queue len = %d, max=%d)\n", + q->count, max_queue_len); + last_notice = jiffies; + } + q->overflows++; + xbus->ops->packet_free(xbus, pack); + goto out; + } + list_add_tail(&pack->list, &q->head); + q->count++; + + if(q->count > q->worst_count) + q->worst_count = q->count; + + if(q->count < max_queue_len/100 && q->worst_count > q->count) // Decay worst_count + q->worst_count--; + + // dump_packet("ENQUEUED", pack, print_dbg); +out: + spin_unlock_irqrestore(&q->lock, flags); +} + +xpacket_t *xbus_dequeue_packet(packet_queue_t *q) +{ + unsigned long flags; + struct list_head *p; + xpacket_t *pack = NULL; + + spin_lock_irqsave(&q->lock, flags); + + if(list_empty(&q->head)) { + // DBG("LIST EMPTY (count=%d)\n", q->count); + goto out; + } + p = q->head.next; + list_del(p); + q->count--; + pack = list_entry(p, xpacket_t, list); + // dump_packet("DEQUEUED", pack, print_dbg); +out: + spin_unlock_irqrestore(&q->lock, flags); + return pack; +} + + +/*------------------------- XPD Management -------------------------*/ + +#ifdef CONFIG_PROC_FS + +/** + * Prints a general procfs entry for the bus, under xpp/BUSNAME/summary + * @page TODO: figure out procfs + * @start TODO: figure out procfs + * @off TODO: figure out procfs + * @count TODO: figure out procfs + * @eof TODO: figure out procfs + * @data an xbus_t pointer with the bus data. + */ +static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + xpd_t *xpd = data; + xbus_t *xbus; +#if SOFT_SIMULATOR + struct xpd_sim *sim; +#endif + int channels; + int i; + + if(!xpd) + goto out; + + xbus = xpd->xbus; +#if SOFT_SIMULATOR + sim = &xbus->sim[xpd->id]; +#endif + channels = xpd->channels; + len += sprintf(page + len, "%s (%s ,card %s, span_registered=%s)%s\n" + "timer_count: %d span->mainttimer=%d\n" + , + xpd->xpdname, xproto_name(xpd->type), + (atomic_read(&xpd->card_present))?"present":"missing", + (SPAN_REGISTERED(xpd))?"yes":"no", + (xpd == sync_master) ? " SYNCER" : "", + xpd->timer_count, xpd->span.mainttimer + ); + len += sprintf(page + len, "STATES:"); + len += sprintf(page + len, "\n\t%-17s: ", "enabled"); + for(i = 0; i < channels; i++) { + len += sprintf(page + len, "%d ", IS_SET(xpd->enabled_chans, i)); + } + len += sprintf(page + len, "\n\t%-17s: ", "output_relays"); + for(i = 0; i < channels; i++) { + len += sprintf(page + len, "%d ", IS_SET(xpd->digital_outputs, i)); + } + len += sprintf(page + len, "\n\t%-17s: ", "input_relays"); + for(i = 0; i < channels; i++) { + len += sprintf(page + len, "%d ", IS_SET(xpd->digital_inputs, i)); + } + len += sprintf(page + len, "\n\t%-17s: ", "hookstate"); + for(i = 0; i < channels; i++) { + len += sprintf(page + len, "%d ", IS_SET(xpd->hookstate, i)); + } + len += sprintf(page + len, "\n\t%-17s: ", "ring-state"); + for(i = 0; i < channels; i++) { + len += sprintf(page + len, "%d ", xpd->lasttxhook[i]); + } + len += sprintf(page + len, "\n\t%-17s: ", "ringing"); + for(i = 0; i < channels; i++) { + len += sprintf(page + len, "%d ", xpd->ringing[i]); + } +#if 1 + if(SPAN_REGISTERED(xpd)) { + len += sprintf(page + len, "\nreadchunk: "); + for(i = 0; i < channels; i++) { + struct zt_chan *chans = xpd->span.chans; + byte chunk[ZT_CHUNKSIZE]; + int j; + + memcpy(chunk, chans[i].readchunk, ZT_CHUNKSIZE); + len += sprintf(page + len, "\n\tport %2d> ", i); + for(j = 0; j < ZT_CHUNKSIZE; j++) { + len += sprintf(page + len, "%02X ", chunk[j]); + } + } + } +#endif +#if SOFT_SIMULATOR + if(sim->simulated) { + len += sprintf(page + len, "\nSIMULATED (xpd_type=%d, loopto=%d):", sim->xpd_type, sim->loopto); + len += sprintf(page + len, "\n\t%-17s: ", "hookstate"); + for(i = 0; i < channels; i++) { + len += sprintf(page + len, "%d ", IS_SET(sim->hookstate, i)); + } + } +#endif +#if 0 + if(SPAN_REGISTERED(xpd)) { + len += sprintf(page + len, "\nSignalling:\n"); + for(i = 0; i < channels; i++) { + struct zt_chan *chan = &xpd->span.chans[i]; + len += sprintf(page + len, "\t%2d> sigcap=0x%04X sig=0x%04X\n", i, chan->sigcap, chan->sig); + } + } +#endif + len += sprintf(page + len, "\nCOUNTERS:\n"); + for(i = 0; i < XPD_COUNTER_MAX; i++) { + len += sprintf(page + len, "\t\t%-20s = %d\n", + xpd_counters[i].name, xpd->counters[i]); + } + len += sprintf(page + len, "<-- len=%d\n", len); +out: + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; + +} + +#endif + +/* + * xpd_alloc - Allocator for new XPD's + * + */ +xpd_t *xpd_alloc(size_t privsize, xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, int channels, byte revision) +{ + xpd_t *xpd = NULL; + size_t alloc_size = sizeof(xpd_t) + privsize; + + INFO("New XPD #%d (Revision %d.%d) detected on xbus %s\n", + xpd_num, revision / 10, revision % 10, xbus->busname); + if(!VALID_XPD_NUM(xpd_num)) { + ERR("%s: illegal xpd id = %d\n", __FUNCTION__, xpd_num); + goto err; + } + if(channels > CHANNELS_PERXPD) { + ERR("%s: too many channels %d for xpd #%d\n", __FUNCTION__, channels, xpd_num); + goto err; + } + + if((xpd = kmalloc(alloc_size, GFP_KERNEL)) == NULL) { + ERR("%s: Unable to allocate memory for xpd #%d\n", __FUNCTION__, xpd_num); + goto err; + } + memset(xpd, 0, alloc_size); + xpd->priv = (byte *)xpd + sizeof(xpd_t); + + spin_lock_init(&xpd->lock); + xpd->xbus = xbus; + xpd->id = xpd_num; + xpd->channels = channels; + xpd->chans = NULL; + atomic_set(&xpd->card_present, 0); + snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%d", xpd_num); + xpd->hookstate = 0x0; /* ONHOOK */ + xpd->type = proto_table->type; + xpd->xops = &proto_table->xops; + xpd->enabled_chans = enabled_channels[xpd_num]; + xpd->digital_outputs = 0; + xpd->digital_inputs = 0; + atomic_set(&xpd->open_counter, 0); + + xpd->chans = kmalloc(sizeof(struct zt_chan)*xpd->channels, GFP_KERNEL); + if (xpd->chans == NULL) { + ERR("%s: Unable to allocate channels\n", __FUNCTION__); + goto err; + } + /* 8 channels, double buffer, Read/Write */ + int need = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2 * 2; + if((xpd->writechunk = kmalloc(need, GFP_KERNEL)) == NULL) { + ERR("%s: Unable to allocate memory for writechunks\n", __FUNCTION__); + goto err; + } + /* Initialize Write/Buffers to all blank data */ + memset((void *)xpd->writechunk, 0x00, need); + xpd->readchunk = xpd->writechunk + ZT_CHUNKSIZE * CHANNELS_PERXPD * 2; + + return xpd; +err: + if(xpd->chans) + kfree((void *)xpd->chans); + if(xpd->writechunk) + kfree((void *)xpd->writechunk); + if(xpd) + kfree(xpd); + return NULL; +} + +static void xpd_card_disable(xpd_t *xpd) +{ + BUG_ON(!xpd); + atomic_set(&xpd->card_present, 0); + if(SPAN_REGISTERED(xpd)) + update_xpd_status(xpd, ZT_ALARM_NOTOPEN); +} + +void xpd_remove(xpd_t *xpd) +{ + xbus_t *xbus; + + BUG_ON(!xpd); + xbus = xpd->xbus; + INFO("Remove XPD #%d from xbus=%s\n", xpd->id, xbus->busname); +#if 0 + // TODO: elect a new sync master + if(sync_master == xpd) + set_sync_master(NULL); +#endif + xpd_zaptel_unregister(xpd); + xbus->xpds[xpd->id] = NULL; + list_del(&xpd->xpd_list); + xbus->num_xpds--; + CALL_XMETHOD(card_remove, xbus, xpd); + xpd_cleanup(xpd); + kfree((void *)xpd->writechunk); + kfree(xpd); +} + +static void update_xpd_status(xpd_t *xpd, int alarm_flag) +{ + struct zt_span *span = &xpd->span; + + if(!SPAN_REGISTERED(xpd)) { + NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname); + return; + } + switch (alarm_flag) { + case ZT_ALARM_NONE: + xpd->last_response = jiffies; + break; + default: + // Nothing + break; + } + if(span->alarms == alarm_flag) + return; + span->alarms = alarm_flag; + zt_alarm_notify(span); + DBG("Update XPD alarms: %s -> %02X\n", xpd->span.name, alarm_flag); +} + +void phone_hook(xpd_t *xpd, int channo, bool offhook) +{ + struct zt_chan *chan = &xpd->span.chans[channo]; + + if(offhook && !IS_SET(xpd->hookstate, channo)) { // OFFHOOK + DBG("OFFHOOK: channo=%d\n", chan->channo); + xpd->ringing[channo] = 0; + BIT_SET(xpd->hookstate, channo); + zt_hooksig(chan, ZT_RXSIG_OFFHOOK); + if(!IS_SET(xpd->digital_outputs, channo) && !IS_SET(xpd->digital_inputs, channo)) { + CALL_XMETHOD(CHAN_POWER, xpd->xbus, xpd, BIT(channo), 0); // Power down (prevent overheating!!!) + CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(channo), LED_GREEN, 1); + } + } else if(!offhook && IS_SET(xpd->hookstate, channo)) { // ONHOOK + DBG("ONHOOK channo=%d\n", chan->channo); + xpd->ringing[channo] = 0; + BIT_CLR(xpd->hookstate, channo); + zt_hooksig(chan, ZT_RXSIG_ONHOOK); + if(!IS_SET(xpd->digital_outputs, channo) && !IS_SET(xpd->digital_inputs, channo)) { + CALL_XMETHOD(CHAN_POWER, xpd->xbus, xpd, BIT(channo), 0); // Power down (prevent overheating!!!) + CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(channo), LED_GREEN, 0); + } + } +} + +void xpp_check_hookstate(xpd_t *xpd, xpp_line_t fxs_off_hook) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&xpd->lock, flags); + if(xpd->direction != TO_PHONE) { + ERR("%s: %s: Only PHONE can report hookstate changes\n", __FUNCTION__, xpd->xpdname); + goto out; + } + if(!SPAN_REGISTERED(xpd)) { + NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname); + goto out; + } + DBG("%s: hookstate=0x%04X fxs_off_hook=0x%04X\n", xpd->xpdname, xpd->hookstate, fxs_off_hook); + for(i = 0; i < xpd->channels; i++) { + phone_hook(xpd, i, IS_SET(fxs_off_hook, i)); + } +out: + spin_unlock_irqrestore(&xpd->lock, flags); +} + +static void xpd_blink_leds(xpd_t *xpd) +{ + int i; + unsigned long flags; + + BUG_ON(!xpd); + + spin_lock_irqsave(&xpd->lock, flags); + for(i = 0; i < xpd->channels; i++) { + if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i)) + continue; + if(xpd->ringing[i]) { + // led state is toggled + if((xpd->timer_count % LED_BLINK_PERIOD) == 0) { + DBG("%s pos=%d ringing=%d led_on=%d\n", xpd->xpdname, i, xpd->ringing[i], xpd->led_on[i]); + if(xpd->ringing[i] && xpd->led_on[i]) { + CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 1); + } else { + CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0); + } + xpd->led_on[i] = !xpd->led_on[i]; + } + } + } + spin_unlock_irqrestore(&xpd->lock, flags); +} + +static void xpp_ring_generate(xpd_t *xpd) +{ + int i; + static int bug_counter = 0; + unsigned long flags; + + BUG_ON(!xpd); + + spin_lock_irqsave(&xpd->lock, flags); + if(xpd->direction != TO_PSTN && ((bug_counter++ % 1000) == 0)) { + ERR("%s: %s: Only FXO can report ring changes\n", __FUNCTION__, xpd->xpdname); + goto out; + } + if(!SPAN_REGISTERED(xpd)) { + NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname); + goto out; + } + /* + * Ring detect logic: + * fxo_power is toggled + */ + for(i = 0; i < xpd->channels; i++) { + if(xpd->ringing[i] || xpd->ringer_on[i]) { + // ring state is only changed once per second: + if((xpd->timer_count % 1000) == 0) { + DBG("pos=%d ringing=%d ringer_on=%d\n", i, xpd->ringing[i], xpd->ringer_on[i]); + if(xpd->ringer_on[i]) { + zt_hooksig(&xpd->chans[i], ZT_RXSIG_OFFHOOK); + } else { + zt_hooksig(&xpd->chans[i], ZT_RXSIG_RING); + } + xpd->ringing[i]--; + xpd->ringer_on[i] = !xpd->ringer_on[i]; + if (xpd->ringing[i] < 0) + xpd->ringing[i]=0; + } + } + } +out: + spin_unlock_irqrestore(&xpd->lock, flags); +} + +/*------------------------- Bus Management -------------------------*/ + +xbus_t *xbus_of(int xbus_num) +{ + if(xbus_num < 0 || xbus_num >= MAX_BUSES) + return NULL; + return xbuses_array[xbus_num]; +} + +xpd_t *xpd_of(xbus_t *xbus, int xpd_num) +{ + if(!VALID_XPD_NUM(xpd_num)) + return NULL; + return xbus->xpds[xpd_num]; +} + +void xbus_reset_counters(xbus_t *xbus) +{ + int i; + + DBG("Reseting counters of %s\n", xbus->busname); + for(i = 0; i < XBUS_COUNTER_MAX; i++) { + xbus->counters[i] = 0; + } +// xbus->xmit_queue.worst_count = 0; +// xbus->xmit_queue.overflows = 0; +} + +#ifdef CONFIG_PROC_FS + +/** + * Prints a general procfs entry for the bus, under xpp/BUSNAME/summary + * @page TODO: figure out procfs + * @start TODO: figure out procfs + * @off TODO: figure out procfs + * @count TODO: figure out procfs + * @eof TODO: figure out procfs + * @data an xbus_t pointer with the bus data. + */ +static int xbus_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + xbus_t *xbus = data; + int i; + + if(!xbus) + goto out; + spin_lock_irqsave(&xbus->lock, flags); + + len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n", + xbus->busname, + xbus->busdesc, + (xbus->hardware_exists) ? "connected" : "missing", + xbus->bus_type + ); + len += sprintf(page + len, "open_counter=%d packet_count=%d\n", + xbus->open_counter, + atomic_read(&xbus->packet_counter) + ); +#if SOFT_SIMULATOR + len += sprintf(page + len, "XPDS SIM\n"); + for(i = 0; i < MAX_XPDS; i++) { + struct xpd_sim *sim = &xbus->sim[i]; + xpd_t *xpd = xpd_of(xbus, i); + len += sprintf(page + len, "\t%d> ignored=%d simulated=%d softloop_xpd=%d loopto=%d instanciated=%s\n", + i, IS_SET(ignore_xpds, i), sim->simulated, sim->softloop_xpd, sim->loopto, (xpd) ? "yes" : "no"); + } +#endif + len += sprintf(page + len, "COUNTERS:\n"); + for(i = 0; i < XBUS_COUNTER_MAX; i++) { + len += sprintf(page + len, "\t%-15s = %d\n", + xbus_counters[i].name, xbus->counters[i]); + } + len += sprintf(page + len, "<-- len=%d\n", len); + spin_unlock_irqrestore(&xbus->lock, flags); +out: + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; + +} + +int proc_sync_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + len += sprintf(page + len, "# To modify sync source write into this file:\n"); + len += sprintf(page + len, "# HOST - For host based sync\n"); + len += sprintf(page + len, "# 0 0 - XBUS-0/XPD-0 provide sync\n"); + len += sprintf(page + len, "# m n - XBUS-m/XPD-n provide sync\n"); + if(!sync_master) + len += sprintf(page + len, "HOST\n"); + else + len += sprintf(page + len, "%s/%s\n", sync_master->xbus->busname, sync_master->xpdname); + len += sprintf(page + len, "tick: #%d\n", xpp_timer_count); + unsigned int xpp_timer_rate = 0; + unsigned int now = jiffies; + if(now - xpp_last_jiffies > 0) { + xpp_timer_rate = ((xpp_timer_count % SAMPLE_TICKS) * 1000) / (now - xpp_last_jiffies); + len += sprintf(page + len, "tick rate: %4d/second (average over %d seconds)\n", xpp_timer_rate, SAMPLE_TICKS/HZ); + } + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +static int proc_sync_write(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + DBG("%s: count=%ld\n", __FUNCTION__, count); + const int NUM_SIZE = 100; + char buf[NUM_SIZE]; + int xbus_num; + int xpd_num; + xbus_t *xbus; + xpd_t *xpd; + int ret; + + if(count >= NUM_SIZE) + return -EINVAL; + if(copy_from_user(buf, buffer, count)) + return -EFAULT; + buf[count] = '\0'; + if(strncmp("HOST", buf, 4) == 0) { + set_sync_master(NULL); + goto out; + } + ret = sscanf(buf, "%d %d", &xbus_num, &xpd_num); + if(ret != 2) + return -EINVAL; + DBG("%s: %d/%d\n", __FUNCTION__, xbus_num, xpd_num); + if(xbus_num >= MAX_BUSES) + return -EINVAL; + xbus = xbus_of(xbus_num); + if(!xbus) + return -EINVAL; + xpd = xpd_of(xbus, xpd_num); + if(!xpd) { + ERR("%s: XPD number %d does not exist\n", __FUNCTION__, xpd_num); + return -ENXIO; + } + set_sync_master(xpd); +out: + return count; +} + +int proc_xpd_ztregister_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + xpd_t *xpd = data; + + BUG_ON(!xpd); + spin_lock_irqsave(&xpd->lock, flags); + + len += sprintf(page + len, "%d\n", SPAN_REGISTERED(xpd)); + spin_unlock_irqrestore(&xpd->lock, flags); + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +static int proc_xpd_ztregister_write(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + xpd_t *xpd = data; + const int NUM_SIZE = 100; + char buf[NUM_SIZE]; + bool zt_reg; + int ret; + + BUG_ON(!xpd); + if(count >= NUM_SIZE) + return -EINVAL; + if(copy_from_user(buf, buffer, count)) + return -EFAULT; + buf[count] = '\0'; + ret = sscanf(buf, "%d", &zt_reg); + if(ret != 1) + return -EINVAL; + DBG("%s: %s/%s %s\n", __FUNCTION__, + xpd->xbus->busname, xpd->xpdname, (zt_reg) ? "register" : "unregister"); + if(zt_reg) + ret = xpd_zaptel_register(xpd); + else + ret = xpd_zaptel_unregister(xpd); + return (ret < 0) ? ret : count; +} + +#endif + +/** + * + * Packet is freed: + * - In case of error, by this function. + * - Otherwise, by the underlying sending mechanism + */ +int packet_send(xbus_t *xbus, xpacket_t *pack_tx) +{ + int ret = -ENODEV; + int toxpd; + + if(!pack_tx) { + DBG("null pack\n"); + return -EINVAL; + } + toxpd = XPD_NUM(pack_tx->content.addr); + if(!xbus) { + DBG("null xbus\n"); + ret = -EINVAL; + goto error; + } + if (!xbus->hardware_exists) { + DBG("xbus %s Dropped a packet -- NO HARDWARE.", xbus->busname); + ret = -ENODEV; + goto error; + } + if(!VALID_XPD_NUM(toxpd)) { + ERR("%s: toxpd=%d > MAX_XPDS\n", __FUNCTION__, toxpd); + ret = -EINVAL; + goto error; + } +#if 0 + // DEBUG: For Dima + if(pack_tx->content.opcode == XPP_PCM_WRITE) { + static int rate_limit; + static int count; + + if(sync_master == NULL) + count = 0; + if(count++ > 5) { + ret = 0; + goto error; + } + if(rate_limit++ % 1000 == 0) + INFO("DEBUG: TRANSMIT (PCM_WRITE)\n"); + } +#endif + if(down_read_trylock(&xbus->in_use)) { + ret = xbus->ops->packet_send(xbus, pack_tx); + XBUS_COUNTER(xbus, TX_BYTES) += pack_tx->datalen; + up_read(&xbus->in_use); + } else { + DBG("Dropped packet. %s is in_use\n", xbus->busname); + } + return ret; + +error: + xbus->ops->packet_free(xbus, pack_tx); + return ret; +} + +static void xbus_poll(xbus_t *xbus, int probe_all) +{ + int id; + int ret; + xpd_t **xpds; + xpd_t *xpd; + + DBG("%s (probe_all=%d)\n", xbus->busname, probe_all); + xpds = xbus->xpds; + for(id = 0; id < MAX_XPDS; id++) { + if(!xbus->hardware_exists) + break; + xpd = xpd_of(xbus, id); + if(!probe_all) { + if(!xpd) { + DBG(" Skipping XPD #%d is MISSING\n", id); + continue; + } + if(!atomic_read(&xpd->card_present)) { + DBG(" Skipping XPD #%d not present\n", id); + continue; + } + if(time_after(xpd->last_response+20, jiffies)) { + DBG(" SKIP DESC_REQ\n"); + continue; + } + } + if(IS_SET(ignore_xpds, id)) { /* skip xpds */ + DBG(" Ignoring XPD #%d\n", id); + continue; + } + DBG(" Polling slot %d %s\n", id, xbus->busname); + ret = CALL_PROTO(GLOBAL, DESC_REQ, xbus, NULL, id); + if(ret < 0) { + NOTICE("xpp: %s: Failed sending DESC_REQ to XPD #%d\n", __FUNCTION__, id); + } + } +} + +void process_xbus_poll(void *data) { + xbus_t *xbus = (xbus_t*) data; + + ERR("%s: issuing xbus_poll\n", __FUNCTION__); + xbus_poll(xbus, 1); +} + + +#if SOFT_SIMULATOR +/* + * Assume xbus->lock is held + */ +static void simulator_setup(xbus_t *xbus, ulong sim_xpds) +{ + int i; + int last_fxo = 0; + bool first = 1; + + DBG("%s: sim_xpds=0x%lX\n", xbus->busname, sim_xpds); + for(i = 0; i < MAX_XPDS; i++) { + if (!IS_SET(sim_xpds, i) || xbus->sim[i].simulated) + continue; + if (first) { + last_fxo=i; + } else { + // setting simulated twice here: in case of odd number of simulated XPDs + xbus->sim[i].xpd_type = XPD_TYPE_FXO; + xbus->sim[i].loopto = last_fxo; + xbus->sim[i].simulated = 1; + xbus->sim[last_fxo].xpd_type = XPD_TYPE_FXS; + xbus->sim[last_fxo].loopto = i; + xbus->sim[last_fxo].simulated = 1; + + } + if(IS_SET(softloop_xpds, i)) + xbus->sim[i].softloop_xpd = 1; + xbus->sim[i].hookstate = 0; + + first = !first; + } +} +#endif + +void xbus_activate(xbus_t *xbus) +{ + xbus_ops_t *ops; + + BUG_ON(!xbus); + ops = xbus->ops; + BUG_ON(!ops); + BUG_ON(!xbus->priv); + /* Sanity checks */ + if(!ops->packet_send) { + ERR("%s: missing mandatory handler: packet_send=\n", __FUNCTION__); + return; + } + if(!ops->packet_new || !ops->packet_free) { + ops->packet_new = softloop_packet_new; + ops->packet_free = softloop_packet_free; + } + + xbus->hardware_exists = 1; + DBG("Activating: %s\n", xbus->busname); + /* Poll it */ + xbus_poll(xbus, 1); +} + +void xbus_deactivate(xbus_t *xbus) +{ + int i; + + BUG_ON(!xbus); + DBG("%s\n", xbus->busname); + xbus->hardware_exists = 0; + for(i = 0; i < MAX_XPDS; i++) { + xpd_t *xpd = xpd_of(xbus, i); + if(!xpd) + continue; + if(xpd->id != i) { + ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i); + continue; + } + xpd_card_disable(xpd); + } + down_write(&xbus->in_use); + DBG("%s (deactivated)\n", xbus->busname); + if(xbus->open_counter == 0) { + xbus_remove(xbus); + } +} + + +static void xbus_cleanup(xbus_t *xbus) +{ + BUG_ON(!xbus); +#ifdef CONFIG_PROC_FS + if(xbus->proc_xbus_dir) { + if(xbus->proc_xbus_summary) { + DBG("Removing proc '%s' for %s\n", PROC_XBUS_SUMMARY, xbus->busname); + remove_proc_entry(PROC_XBUS_SUMMARY, xbus->proc_xbus_dir); + xbus->proc_xbus_summary = NULL; + } + DBG("Removing proc directory %s\n", xbus->busname); + remove_proc_entry(xbus->busname, xpp_procdir); + xbus->proc_xbus_dir = NULL; + } +#endif + kfree(xbus); +} + +xbus_t *xbus_new(ulong loopback_xpds) +{ + unsigned long flags; + int xbus_num; + int err; + xbus_t *xbus; + + xbus = kmalloc(sizeof(xbus_t), GFP_KERNEL); + if(!xbus) + return NULL; + memset(xbus, 0, sizeof(xbus_t)); + + spin_lock_irqsave(&xbuses_lock, flags); + for(xbus_num = 0; xbus_num < MAX_BUSES; xbus_num++) + if(xbuses_array[xbus_num] == NULL) + break; + if(xbus_num >= MAX_BUSES) { + spin_unlock_irqrestore(&xbuses_lock, flags); + err = -ENOMEM; + goto nobus; + } + /* Found empty slot */ + xbuses_array[xbus_num] = xbus; + bus_count++; + spin_unlock_irqrestore(&xbuses_lock, flags); + + /* Init data structures */ + spin_lock_init(&xbus->lock); + snprintf(xbus->busname, XBUS_NAMELEN, "XBUS-%d", xbus_num); + INFO("New xbus: %s\n", xbus->busname); + init_waitqueue_head(&xbus->packet_cache_empty); + atomic_set(&xbus->packet_counter, 0); + init_rwsem(&xbus->in_use); + xbus->num = xbus_num; + xbus->num_xpds = 0; +#if SOFT_SIMULATOR + xbus->sim_workqueue = create_singlethread_workqueue(xbus->busname); + if(!xbus->sim_workqueue) { + ERR("Failed to create workqueue for xbus %s\n", xbus->busname); + err = -ENOMEM; + goto nobus; + } + init_xbus_packet_queue(&xbus->sim_packet_queue, "SIM_PACKET_QUEUE"); + INIT_WORK(&xbus->sim_work, process_sim_queue, xbus); + simulator_setup(xbus, loopback_xpds); // Hardware loopback must use simulator + simulator_setup(xbus, softloop_xpds); // Add the soft loopback to the simulated set +#endif + xbus_reset_counters(xbus); +#ifdef CONFIG_PROC_FS + DBG("Creating xbus proc directory %s.\n",xbus->busname); + xbus->proc_xbus_dir = proc_mkdir(xbus->busname, xpp_procdir); + if(!xbus->proc_xbus_dir) { + ERR("Failed to create proc directory for xbus %s\n", xbus->busname); + err = -EIO; + goto nobus; + } + xbus->proc_xbus_summary = create_proc_read_entry(PROC_XBUS_SUMMARY, 0444, xbus->proc_xbus_dir, + xbus_read_proc, xbus); + if (!xbus->proc_xbus_summary) { + ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_SUMMARY, xbus->busname); + err = -EIO; + goto nobus; + } +#endif + return xbus; +nobus: + spin_lock_irqsave(&xbuses_lock, flags); + xbuses_array[xbus_num] = NULL; + bus_count--; + spin_unlock_irqrestore(&xbuses_lock, flags); + xbus_cleanup(xbus); + return NULL; +} + +static void xbus_remove(xbus_t *xbus) +{ + int i; + unsigned long flags; + + BUG_ON(!xbus); + DBG("%s\n", xbus->busname); + spin_lock_irqsave(&xbuses_lock, flags); + BUG_ON(xbus != xbus_of(xbus->num)); + xbuses_array[xbus->num] = NULL; + bus_count--; + spin_unlock_irqrestore(&xbuses_lock, flags); +#if SOFT_SIMULATOR + if(xbus->sim_workqueue) { + cancel_delayed_work(&xbus->sim_work); + } +#endif + INFO("Removing xbus(%d) %s\n", xbus->num, xbus->busname); + for(i = 0; i < MAX_XPDS; i++) { + xpd_t *xpd = xpd_of(xbus, i); + + if(xpd) { + const xops_t *xops = xpd->xops; + + if(xpd->id != i) { + ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i); + continue; + } + BUG_ON(!xops); + DBG(" Removing xpd id=%d\n", xpd->id); + xpd_remove(xpd); + } + xbus->xpds[i] = NULL; + } +#if SOFT_SIMULATOR + if(xbus->sim_workqueue) { + flush_workqueue(xbus->sim_workqueue); + destroy_workqueue(xbus->sim_workqueue); + xbus->sim_workqueue = NULL; + } + drain_xbus_packet_queue(xbus, &xbus->sim_packet_queue); +#endif + int ret = wait_event_interruptible(xbus->packet_cache_empty, + atomic_read(&xbus->packet_counter) == 0); + if(ret) { + ERR("waiting for packet_cache_empty interrupted!!!\n"); + } + xbus_cleanup(xbus); +} + + +#define XPP_MAX_LEN 512 + +/*------------------------- Zaptel Interfaces ----------------------*/ + +#define PREP_REPORT_RATE 1000 + +static void xpp_transmitprep(xpd_t *xpd) +{ + volatile u_char *writechunk; + volatile u_char *w; + int ret; + int i; + int channels = xpd->channels; + struct zt_chan *chans = xpd->span.chans; + +// if((xpd->timer_count % PREP_REPORT_RATE) < 10) +// DBG("%d\n", xpd->timer_count); + +// if(xpd->hookstate == 0) +// return; + if (xpd->timer_count & 1) { + /* First part */ + w = writechunk = xpd->writechunk /* + 1 */; + } else { + w = writechunk = xpd->writechunk + ZT_CHUNKSIZE * CHANNELS_PERXPD /* + 1 */; + } + zt_transmit(&xpd->span); + + for (i = 0; i < channels; i++) { + if(IS_SET(xpd->hookstate, i)) { + memcpy((u_char *)w, chans[i].writechunk, ZT_CHUNKSIZE); + // fill_beep((u_char *)w, 5); + } + w += ZT_CHUNKSIZE; + } + if(xpd->hookstate != 0 || sync_master == xpd || !sync_master) { + ret = CALL_XMETHOD(PCM_WRITE, xpd->xbus, xpd, xpd->hookstate, writechunk); + if(ret < 0) { + DBG("failed to write PCM %d\n", ret); + } + } +} + +void fill_beep(u_char *buf, int duration) +{ + int which = (jiffies/(duration*HZ)) & 0x3; + + /* + * debug tones + */ + static u_char beep[] = { +// 0x7F, 0xBE, 0xD8, 0xBE, 0x80, 0x41, 0x24, 0x41, /* Dima */ +// 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, /* silence */ + 0x67, 0x90, 0x89, 0x90, 0xFF, 0x10, 0x09, 0x10, /* Izzy */ +// 0x67, 0xCD, 0xC5, 0xCD, 0xFF, 0x49, 0x41, 0x49, /* Dima 2 */ +// 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, /* silence */ + }; + memcpy(buf, &beep[(which*8) % ARRAY_SIZE(beep)], ZT_CHUNKSIZE); +} + +static void xpp_receiveprep(xpd_t *xpd) +{ + volatile u_char *readchunk; + int i; + int channels = xpd->channels; + struct zt_chan *chans = xpd->span.chans; + +// if((xpd->timer_count % PREP_REPORT_RATE) == 0) +// DBG("%d\n", xpd->timer_count); + + if (xpd->timer_count & 1) { + /* First part */ + readchunk = xpd->readchunk; + } else { + readchunk = xpd->readchunk + ZT_CHUNKSIZE * CHANNELS_PERXPD; + } + + for (i = 0; i < channels; i++) { + if(IS_SET(xpd->hookstate, i)) { + memcpy(chans[i].readchunk, (u_char *)readchunk, ZT_CHUNKSIZE); + } + readchunk += ZT_CHUNKSIZE; + } + +#if 0 + /* FIXME: need to Echo cancel double buffered data */ + for (i = 0;i < xpd->span.channels; i++) { + zt_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]); + memcpy(xpd->ec_chunk2[i], xpd->ec_chunk1[i], ZT_CHUNKSIZE); + memcpy(xpd->ec_chunk1[i], chans[i].writechunk, ZT_CHUNKSIZE); + } +#endif + zt_receive(&xpd->span); +} + +static int xpp_startup(struct zt_span *span) +{ + DBG("\n"); + return 0; +} + +/* + * Called only for 'span' keyword in /etc/zaptel.conf + */ +static int xpp_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + xpd_t *xpd = span->pvt; + + DBG("%s\n", xpd->xpdname); + return 0; +} + +/* + * Called only for 'span' keyword in /etc/zaptel.conf + */ +static int xpp_shutdown(struct zt_span *span) +{ + xpd_t *xpd = span->pvt; + + DBG("%s\n", xpd->xpdname); + return 0; +} + +int xpp_open(struct zt_chan *chan) +{ + xpd_t *xpd = chan->pvt; + xbus_t *xbus = xpd->xbus; + unsigned long flags; + + spin_lock_irqsave(&xbus->lock, flags); + xbus->open_counter++; + atomic_inc(&xpd->open_counter); + DBG("chan=%d (open_counter=%d)\n", chan->chanpos, xbus->open_counter); + spin_unlock_irqrestore(&xbus->lock, flags); + return 0; +} + +int xpp_close(struct zt_chan *chan) +{ + xpd_t *xpd = chan->pvt; + xbus_t *xbus = xpd->xbus; + unsigned long flags; + bool should_remove = 0; + + spin_lock_irqsave(&xbus->lock, flags); + xbus->open_counter--; + atomic_dec(&xpd->open_counter); + if (!xbus->hardware_exists && xbus->open_counter == 0) + should_remove = 1; + spin_unlock_irqrestore(&xbus->lock, flags); + + DBG("chan=%d (open_counter=%d, should_remove=%d)\n", chan->chanpos, xbus->open_counter, should_remove); + if(should_remove) + xbus_remove(xbus); + return 0; +} + +int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg) +{ + xpd_t *xpd = chan->pvt; + int pos = chan->chanpos - 1; + int x; + + switch (cmd) { + case ZT_ONHOOKTRANSFER: + if (get_user(x, (int *)arg)) + return -EFAULT; + if (xpd->lasttxhook[pos] == 0x1) { + /* Apply the change if appropriate */ + xpd->lasttxhook[pos] = 0x2; + // CALL_XMETHOD(CHAN_CID, xpd->xbus, xpd, BIT(pos)); // CALLER ID + } + DBG("xpd=%d: ZT_ONHOOKTRANSFER (%d millis) chan=%d\n", xpd->id, x, pos); + return -ENOTTY; + case ZT_TONEDETECT: + if (get_user(x, (int *)arg)) + return -EFAULT; + DBG("xpd=%d: ZT_TONEDETECT chan=%d: TONEDETECT_ON=%d TONEDETECT_MUTE=%d\n", + xpd->id, pos, (x & ZT_TONEDETECT_ON), (x & ZT_TONEDETECT_MUTE)); + return -ENOTTY; + default: + DBG("ENOTTY: chan=%d cmd=0x%x\n", pos, cmd); + DBG(" IOC_TYPE=0x%02X\n", _IOC_TYPE(cmd)); + DBG(" IOC_DIR=0x%02X\n", _IOC_DIR(cmd)); + DBG(" IOC_NR=0x%02X\n", _IOC_NR(cmd)); + DBG(" IOC_SIZE=0x%02X\n", _IOC_SIZE(cmd)); + return -ENOTTY; + } + return 0; +} + +#ifdef WITH_RBS +static int xpp_hooksig(struct zt_chan *chan, zt_txsig_t txsig) +{ + xpd_t *xpd = chan->pvt; + xbus_t *xbus; + int pos = chan->chanpos - 1; + int ret = 0; + + if(!xpd) { + ERR("%s: channel=%d without an XPD!\n", __FUNCTION__, pos); + return -EINVAL; + } + xbus = xpd->xbus; + + if (txsig == ZT_TXSIG_START) { + if(xpd->direction == TO_PHONE) { + // A PHONE line: ZT_START will be treated as ZT_RING + DBG("Got ZT_START for PHONE channel %d, treated as ZT_RING\n", pos); + //hookstate = ZT_TXSIG_RING; + + if(IS_SET(xpd->digital_inputs, pos)) { + NOTICE("%s: Trying to RING a digital input channel %d. Ignoring\n", __FUNCTION__, pos); + return -EINVAL; + } + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("ZT_RING %s digital output ON\n", chan->name); + ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1); + return ret; + } + xpd->ringing[pos] = RINGS_NUM*2; + DBG("ZT_RING %s ringing=%d\n", chan->name, xpd->ringing[pos]); + ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1); // RING on + if(ret) { + DBG("ZT_RING Failed: ret=0x%02X\n", ret); + return ret; + } + return ret; + } else { /* TO_PSTN */ + // An FXO line: ZT_START will be treated as ZT_OFFHOOK + DBG("Got ZT_START for FXO channel %d, treated as ZT_OFFHOOK\n", pos); + txsig = ZT_TXSIG_OFFHOOK; + } + } + switch(txsig) { + case ZT_TXSIG_START: + DBG("ZT_TXSIG_START: (doing OFFHOOK) %s (chan->dialing=%d)\n", chan->name, chan->dialing); + break; + /* Fall through */ + case ZT_TXSIG_OFFHOOK: + DBG("ZT_TXSIG_OFFHOOK: %s hookstate=0x%04X\n", chan->name, xpd->hookstate); + BIT_SET(xpd->hookstate, pos); + xpd->ringing[pos] = 0; + if(IS_SET(xpd->digital_inputs, pos)) { + NOTICE("%s: Trying to OFFHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos); + return -EINVAL; + } + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("ZT_TXSIG_OFFHOOK %s digital output OFF\n", chan->name); + ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); + return ret; + } + ret = CALL_XMETHOD(SETHOOK, xbus, xpd->id, xpd->hookstate); + if(ret) { + DBG("ZT_TXSIG_OFFHOOK Failed: ret=0x%02X\n", ret); + break; + } + CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0); + break; + //DBG("ZT_TXSIG_OFFHOOK %d\n", pos); + //BIT_SET(xpd->hookstate, pos); + //break; + case ZT_TXSIG_KEWL: + DBG("ZT_TXSIG_KEWL (doing ONHOOK): %d.\n", pos); + break; + /* Fall through */ + case ZT_TXSIG_ONHOOK: + DBG("ZT_TXSIG_ONHOOK: %s hookstate=0x%04X\n", chan->name, xpd->hookstate); + xpd->ringing[pos] = 0; + if(IS_SET(xpd->digital_inputs, pos)) { + NOTICE("%s: Trying to ONHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos); + return -EINVAL; + } + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("ZT_TXSIG_ONHOOK %s digital output OFF\n", chan->name); + ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); + return ret; + } + BIT_CLR(xpd->hookstate, pos); + ret = CALL_XMETHOD(SETHOOK, xbus, xpd->id, xpd->hookstate); + if(ret) { + DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret); + break; + } + CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0); + break; + //DBG("ZT_TXSIG_ONHOOK: %d\n", pos); + //BIT_CLR(xpd->hookstate, pos); + //break; + default: + DBG("hooksig: unkown txsig=%d on channel %d\n", txsig, pos); + return -EINVAL; + } + //ret = CALL_XMETHOD(SETHOOK, xbus, xpd->id, xpd->hookstate); + //if(ret) { + // DBG("ZT_TXSIG_START Failed: ret=0x%02X\n", ret); + //} + return ret; +} +#endif + +static int xpp_sethook(struct zt_chan *chan, int hookstate) +{ + int pos = chan->chanpos - 1; + xpd_t *xpd = chan->pvt; + xbus_t *xbus; + int ret = 0; + + if(!xpd) { + ERR("%s: channel=%d without an XPD!\n", __FUNCTION__, pos); + return -EINVAL; + } + xbus = xpd->xbus; + DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate); + switch(hookstate) { + /* On-hook, off-hook: The PBX is playing a phone on an FXO line. + * Can be ignored for an FXS line + */ + case ZT_ONHOOK: + if(IS_SET(xpd->digital_inputs, pos)) { + NOTICE("%s: Trying to ONHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos); + return -EINVAL; + } + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("ZT_ONHOOK %s digital output OFF\n", chan->name); + ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); + return ret; + } + if(xpd->direction == TO_PHONE) { /* Stop ring */ + DBG("ZT_ONHOOK: %s hookstate=0x%04X (stop ringing pos=%d)\n", chan->name, xpd->hookstate, pos); + xpd->ringing[pos] = 0; +#if 1 // FIXME: Not needed -- verify + ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0); // RING off +#endif + xpd->lasttxhook[pos] = 1; + ret = CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(pos), LED_GREEN, 0); + ret = CALL_XMETHOD(CHAN_POWER, xbus, xpd, BIT(pos), 0); // Power down (prevent overheating!!!) + if(ret) { + DBG("ZT_ONHOOK(stop ring) Failed: ret=0x%02X\n", ret); + break; + } + } else { + DBG("ZT_ONHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos); + xpd->ringing[pos] = 0; + BIT_CLR(xpd->hookstate, pos); + ret = CALL_XMETHOD(SETHOOK, xbus, xpd, xpd->hookstate); + if(ret) { + DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret); + break; + } + } + break; + case ZT_START: + DBG("ZT_START: %s hookstate=0x%04X (fall through ZT_OFFHOOK)\n", chan->name, xpd->hookstate); + // Fall through + case ZT_OFFHOOK: + if(xpd->direction == TO_PHONE) { + DBG("ZT_OFFHOOK: %s hookstate=0x%04X -- ignoring (PHONE)\n", chan->name, xpd->hookstate); + break; + } + DBG("ZT_OFFHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos); + BIT_SET(xpd->hookstate, pos); + xpd->ringing[pos] = 0; + ret = CALL_XMETHOD(SETHOOK, xbus, xpd, xpd->hookstate); + if(ret) { + DBG("ZT_OFFHOOK Failed: ret=0x%02X\n", ret); + break; + } + break; + case ZT_WINK: + DBG("ZT_WINK %s\n", chan->name); + break; + case ZT_FLASH: + DBG("ZT_FLASH %s\n", chan->name); + break; + case ZT_RING: + DBG("ZT_RING %s pos=%d (ringing[pos]=%d)\n", chan->name, pos, xpd->ringing[pos]); + if(xpd->direction == TO_PHONE) { + if(IS_SET(xpd->digital_inputs, pos)) { + NOTICE("%s: Trying to RING a digital input channel %d. Ignoring\n", __FUNCTION__, pos); + return -EINVAL; + } + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("ZT_ONHOOK %s digital output ON\n", chan->name); + ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1); + return ret; + } + xpd->ringing[pos] = RINGS_NUM*2; + ret = CALL_XMETHOD(CHAN_POWER, xbus, xpd, BIT(pos), 1); // Power up (for ring) + ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1); // RING on + if(ret) { + DBG("ZT_RING Failed: ret=0x%02X\n", ret); + } + } + break; + case ZT_RINGOFF: + DBG("ZT_RINGOFF %s\n", chan->name); + break; + default: + DBG("UNKNOWN hookstate=0x%X\n", hookstate); + } + return ret; +} + +/* Req: Set the requested chunk size. This is the unit in which you must + report results for conferencing, etc */ +int xpp_setchunksize(struct zt_span *span, int chunksize); + +/* Enable maintenance modes */ +int xpp_maint(struct zt_span *span, int cmd) +{ + xpd_t *xpd = span->pvt; + int ret = 0; +#if 0 + char loopback_data[] = "THE-QUICK-BROWN-FOX-JUMPED-OVER-THE-LAZY-DOG"; +#endif + + BUG_ON(!xpd); + DBG("%s: span->mainttimer=%d\n", __FUNCTION__, span->mainttimer); + switch(cmd) { + case ZT_MAINT_NONE: + printk("XXX Turn off local and remote loops XXX\n"); + CALL_XMETHOD(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 0); // FIXME: Find usage for extra LED + break; + case ZT_MAINT_LOCALLOOP: + printk("XXX Turn on local loopback XXX\n"); + break; + case ZT_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopback XXX\n"); + break; + case ZT_MAINT_LOOPUP: + printk("XXX Send loopup code XXX\n"); + CALL_XMETHOD(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 1); // FIXME: Find usage for extra LED + // CALL_XMETHOD(LOOPBACK_AX, xpd->xbus, xpd, loopback_data, ARRAY_SIZE(loopback_data)); + break; + case ZT_MAINT_LOOPDOWN: + printk("XXX Send loopdown code XXX\n"); + CALL_XMETHOD(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 0); // FIXME: Find usage for extra LED + break; + case ZT_MAINT_LOOPSTOP: + printk("XXX Stop sending loop codes XXX\n"); + break; + default: + ERR("XPP: Unknown maint command: %d\n", cmd); + ret = -EINVAL; + break; + } + if (span->mainttimer || span->maintstat) + update_xpd_status(xpd, ZT_ALARM_LOOPBACK); + return ret; +} + +/* Set signalling type (if appropriate) */ +static int xpp_chanconfig(struct zt_chan *chan, int sigtype) +{ + DBG("channel %d (%s), sigtype %d.\n", chan->channo, chan->name, sigtype); + dump_sigtype(print_dbg, " ", sigtype); + // FIXME: sanity checks: + // - should be supported (within the sigcap) + // - should not replace fxs <->fxo ??? (covered by previous?) + return 0; +} + +#if 0 +/* Okay, now we get to the signalling. You have several options: */ + +/* Option 1: If you're a T1 like interface, you can just provide a + rbsbits function and we'll assert robbed bits for you. Be sure to + set the ZT_FLAG_RBS in this case. */ + +/* Opt: If the span uses A/B bits, set them here */ +int (*rbsbits)(struct zt_chan *chan, int bits); + +/* Option 2: If you don't know about sig bits, but do have their + equivalents (i.e. you can disconnect battery, detect off hook, + generate ring, etc directly) then you can just specify a + sethook function, and we'll call you with appropriate hook states + to set. Still set the ZT_FLAG_RBS in this case as well */ +int (*hooksig)(struct zt_chan *chan, zt_txsig_t hookstate); + +/* Option 3: If you can't use sig bits, you can write a function + which handles the individual hook states */ +int (*sethook)(struct zt_chan *chan, int hookstate); +#endif + +#ifdef CONFIG_ZAPTEL_WATCHDOG +/* + * If the watchdog detects no received data, it will call the + * watchdog routine + */ +static int xpp_watchdog(struct zt_span *span, int cause) +{ + static int rate_limit = 0; + + if((rate_limit++ % 1000) == 0) + DBG("\n"); + return 0; +} +#endif + +/** + * Unregister an xpd from zaptel and release related resources + * @xpd The xpd to be unregistered + * @returns 0 on success, errno otherwise + * + * Checks that nobody holds an open channel. + * + * Called by: + * - User action through /proc + * - During xpd_remove() + */ +static int xpd_zaptel_unregister(xpd_t *xpd) +{ + BUG_ON(!xpd); + + if(!SPAN_REGISTERED(xpd)) { + NOTICE("%s: %s is already unregistered\n", __FUNCTION__, xpd->xpdname); + return -EIDRM; + } + if(sync_master == xpd) + set_sync_master(NULL); // FIXME: it's better to elect a new prince + update_xpd_status(xpd, ZT_ALARM_NOTOPEN); + if(atomic_read(&xpd->open_counter)) { + NOTICE("%s: %s is busy (open_counter=%d). Skipping.\n", __FUNCTION__, xpd->xpdname, atomic_read(&xpd->open_counter)); + return -EBUSY; + } + mdelay(2); // FIXME: This is to give chance for transmit/receiveprep to finish. + zt_unregister(&xpd->span); + return 0; +} + +static int xpd_zaptel_register(xpd_t *xpd) +{ + struct zt_chan *cur_chan; + struct zt_span *span; + xbus_t *xbus; + int sigfxs; + int i; + int cn; + const xops_t *xops; + + BUG_ON(!xpd); + xops = xpd->xops; + + if (SPAN_REGISTERED(xpd)) { + ERR("xpd %s already registered\n", xpd->xpdname); + return -EEXIST; + } + sigfxs = ! (xpd->direction == TO_PHONE); /* signaling is opposite */ + cn = xpd->channels; + DBG("Initializing span: xpd %d have %d channels.\n", xpd->id, cn); + + memset(xpd->chans, 0, sizeof(struct zt_chan)*cn); + memset(&xpd->span, 0, sizeof(struct zt_span)); + + span = &xpd->span; + xbus = xpd->xbus; + snprintf(span->name, MAX_SPANNAME, "%s/%s", + xbus->busname, xpd->xpdname); + { + char tmp[MAX_SPANNAME]; +#if SOFT_SIMULATOR + struct xpd_sim *sim = &xbus->sim[xpd->id]; + + if(sim->simulated) + snprintf(tmp, MAX_SPANNAME, " (sim to=%d)", sim->loopto); + else +#endif + tmp[0] = '\0'; + + snprintf(span->desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: %s%s", + xbus->num, xpd->id, + (xpd->direction == TO_PHONE) ? "FXS" : "FXO", + tmp + ); + } + for(i = 0; i < cn; i++) { + + cur_chan = &xpd->chans[i]; + DBG("setting channel %d (sigfxs=%d)\n", i, sigfxs); + if(IS_SET(xpd->digital_outputs, i)) { + snprintf(cur_chan->name, MAX_CHANNAME, "XPP_OUT/%d-%d", xpd->id, i); + } else if(IS_SET(xpd->digital_inputs, i)) { + snprintf(cur_chan->name, MAX_CHANNAME, "XPP_IN/%d-%d", xpd->id, i); + } else { + snprintf(cur_chan->name, MAX_CHANNAME, "XPP_%s/%d-%d", (sigfxs) ? "FXO" : "FXS", xpd->id, i); + } + cur_chan->chanpos = i + 1; + cur_chan->pvt = xpd; + if (sigfxs) + cur_chan->sigcap = +#if 1 + ZT_SIG_FXSKS | + ZT_SIG_FXSLS | +#else + ZT_SIG_SF | +#endif + 0; + else + cur_chan->sigcap = +#if 1 + ZT_SIG_FXOKS | + ZT_SIG_FXOLS | + ZT_SIG_FXOGS | +#else + ZT_SIG_SF | + ZT_SIG_EM | +#endif + 0; + } + span->deflaw = ZT_LAW_MULAW; + init_waitqueue_head(&span->maintq); + span->pvt = xpd; + span->channels = cn; + span->chans = xpd->chans; + + span->startup = xpp_startup; + span->shutdown = xpp_shutdown; + span->spanconfig = xpp_spanconfig; + span->chanconfig = xpp_chanconfig; + span->open = xpp_open; + span->close = xpp_close; +#ifdef WITH_RBS + span->flags = ZT_FLAG_RBS; + span->hooksig = xpp_hooksig; /* Only with RBS bits */ +#else + span->sethook = xpp_sethook; +#endif + span->ioctl = xpp_ioctl; + span->maint = xpp_maint; +#ifdef CONFIG_ZAPTEL_WATCHDOG + span->watchdog = xpp_watchdog; +#endif + + DBG("Finished span_load: ZT_FLAG_RUNNING=%d\n", span->flags & ZT_FLAG_RUNNING); + + DBG("Registering span of %s.\n", xpd->xpdname); + if(zt_register(&xpd->span, 1)) { + xbus_t *xbus = xpd->xbus; + ERR("Failed to zt_register of span of xpd %s.\n", xpd->xpdname); + xbus->xpds[xpd->id] = NULL; + list_del(&xpd->xpd_list); + xbus->num_xpds--; + return -ENODEV; + } +// if(xpd->id == 0) +// set_sync_master(xpd); + + return 0; +} + + +/*------------------------- Proc debugging interface ---------------*/ + +#ifdef CONFIG_PROC_FS + +static int xpp_zap_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + int i; + + spin_lock_irqsave(&xbuses_lock, flags); + for(i = 0; i < MAX_BUSES; i++) { + xbus_t *xbus = xbus_of(i); + + if(xbus) { + len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n", + xbus->busname, + xbus->busdesc, + (xbus->hardware_exists) ? "connected" : "missing", + xbus->bus_type + ); + } + } +#if 0 + len += sprintf(page + len, "<-- len=%d\n", len); +#endif + spin_unlock_irqrestore(&xbuses_lock, flags); + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; + +} + +#if 0 +static int xpp_zap_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ +} +#endif + +#endif + +/*------------------------- File Operations ------------------------*/ + +#define MINOR_XBUS_NUM(m) ((m) >> 4) +#define MINOR_XPD_NUM(m) ((m) & 0xF); + +static int xpp_sys_open (struct inode * inode, struct file * file) +{ + xbus_t *xbus; + unsigned int minor = iminor(inode); + unsigned int busnum = MINOR_XBUS_NUM(minor); + unsigned long flags; + + spin_lock_irqsave(&xbuses_lock, flags); + xbus = xbus_of(busnum); + spin_unlock_irqrestore(&xbuses_lock, flags); + if(xbus == NULL) + return -ENODEV; + file->private_data = xbus; + DBG("unit %d xbus %s\n", busnum, xbus->busname); + return 0; +} + +static int xpp_sys_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + switch(cmd) { + default: + return -ENOTTY; + } + return 0; +} + +static int xpp_sys_release (struct inode * inode, struct file * file) +{ + unsigned int minor = iminor(inode); + unsigned int busnum = MINOR_XBUS_NUM(minor); + xbus_t *xbus = file->private_data; + DBG("unit %d xbus %s\n", busnum, xbus->busname); + if(xbus == NULL) { + ERR("xpp_sys_release: xbus has dissappeared\n"); + return -EINVAL; + } + return 0; +} + +#if 0 +static ssize_t xpp_sys_write (struct file * file, const char __user * buf, + size_t count, loff_t * ppos) +{ + unsigned int minor = iminor(file->f_dentry->d_inode); + unsigned int busnum = MINOR_XBUS_NUM(minor); + int xpdnum = MINOR_XPD_NUM(minor) + xbus_t *xbus = file->private_data; + xpacket_t *pack_tx; + + DBG("count=%d from %d/%d xbus %s\n", count, busnum, xpdnum, xbus->busname); + if(xbus == NULL) { + ERR("xpp_sys_write: xbus has dissappeared\n"); + return -EINVAL; + } + if(count == 0) + return 0; + if(count >= sizeof(xpacket_raw_t)) { + DBG("count=%d, partial write...\n", count); + count = sizeof(xpacket_raw_t); + } + pack_tx = xbus->ops->packet_new(xbus, GFP_KERNEL); + if (!pack_tx) { + return -ENOMEM; + } + if (copy_from_user (pack_tx->content.raw, buf, count)) { + return -EFAULT; + } + XPD_ADDR_SET(pack_tx->content.addr, xpdnum); + pack_tx->datalen = count; + // pack_tx->flags |= XPP_PACKET_FIREANDFORGET; + DBG("sending op=%d to %d\n", pack_tx->content.opcode, xpdnum); + xbus_reset_counters(xbus); + pcm_write_enable = 1; + packet_send(xbus, pack_tx); + return count; +} +#endif + +static struct file_operations xpp_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, +#if 0 + .write = xpp_sys_write, +#endif + .ioctl = xpp_sys_ioctl, + .open = xpp_sys_open, + .release = xpp_sys_release, +}; + + +/*------------------------- Initialization -------------------------*/ + +static void do_cleanup(void) +{ + if(timer_pending(&xpp_timer)) + del_timer_sync(&xpp_timer); + unregister_chrdev(XPP_CTL_MAJOR, THIS_MODULE->name); +#ifdef CONFIG_PROC_FS + remove_proc_entry(PROC_SYNC, xpp_procdir); + remove_proc_entry(PROC_XBUSES, xpp_procdir); + if(xpp_procdir) { + remove_proc_entry(PROC_DIR, NULL); + } +#endif + if (xpp_worker) { + flush_workqueue(xpp_worker); + destroy_workqueue(xpp_worker); + xpp_worker = NULL; + } + kmem_cache_destroy(packet_cache); +} + +int __init xpp_zap_init(void) +{ + INFO("%s revision %s\n", THIS_MODULE->name, revision); + + packet_cache = kmem_cache_create("xpp_packets", + sizeof(xpacket_t), + 0, 0, + NULL, NULL); + if(!packet_cache) { + return -ENOMEM; + } +#ifdef CONFIG_PROC_FS + xpp_procdir = proc_mkdir(PROC_DIR, NULL); + if(!xpp_procdir) { + do_cleanup(); + return -EIO; + } + struct proc_dir_entry *ent; + + ent = create_proc_entry(PROC_SYNC, 0644, xpp_procdir); + if(!ent) { + do_cleanup(); + return -EFAULT; + } + ent->read_proc = proc_sync_read; + ent->write_proc = proc_sync_write; + ent->data = NULL; + ent = create_proc_read_entry(PROC_XBUSES, 0444, xpp_procdir, xpp_zap_read_proc, 0); + if (!ent) { + do_cleanup(); + return -EFAULT; + } +#endif + xpp_worker = create_singlethread_workqueue("xppworker"); + if(!xpp_worker) { + ERR("Failed to create card detector workqueue.\n"); + do_cleanup(); + return -ENOMEM; + } + + if (register_chrdev(XPP_CTL_MAJOR, THIS_MODULE->name, &xpp_fops)) { + printk (KERN_WARNING "%s: unable to get major %d\n", THIS_MODULE->name, XPP_CTL_MAJOR); + do_cleanup(); + return -EIO; + } + + /* Only timer init. We add it only *after* zt_register */ + init_timer(&xpp_timer); + set_sync_master(NULL); /* Internal ticking */ + return 0; +} + +void __exit xpp_zap_cleanup(void) +{ +// unsigned long flags; + int i; + + for(i = 0; i < MAX_BUSES; i++) { + xbus_t *xbus = xbus_of(i); + if(!xbus) + continue; + xbus_remove(xbus); + } +// spin_lock_irqsave(&xbuses_lock, flags); + if(bus_count) { + ERR("%s: bus_count=%d!\n", __FUNCTION__, bus_count); + } +// spin_unlock_irqrestore(&xbuses_lock, flags); + do_cleanup(); +} + +EXPORT_SYMBOL(print_dbg); +EXPORT_SYMBOL(card_detected); +EXPORT_SYMBOL(xpd_alloc); +EXPORT_SYMBOL(xbus_activate); +EXPORT_SYMBOL(xbus_deactivate); +EXPORT_SYMBOL(xpd_of); +EXPORT_SYMBOL(xbus_new); +EXPORT_SYMBOL(xbus_remove); +EXPORT_SYMBOL(xbus_reset_counters); +EXPORT_SYMBOL(packet_send); +EXPORT_SYMBOL(fill_beep); +EXPORT_SYMBOL(xbus_enqueue_packet); +EXPORT_SYMBOL(xbus_dequeue_packet); +EXPORT_SYMBOL(init_xbus_packet_queue); +EXPORT_SYMBOL(drain_xbus_packet_queue); +EXPORT_SYMBOL(phone_hook); +EXPORT_SYMBOL(xpp_check_hookstate); +EXPORT_SYMBOL(xpp_tick); +EXPORT_SYMBOL(xpp_open); +EXPORT_SYMBOL(xpp_close); +EXPORT_SYMBOL(xpp_ioctl); +EXPORT_SYMBOL(xpp_maint); + +MODULE_DESCRIPTION("XPP Zaptel Driver"); +MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("$Id$"); + +module_init(xpp_zap_init); +module_exit(xpp_zap_cleanup); diff --git a/xpp/xpp_zap.h b/xpp/xpp_zap.h new file mode 100644 index 0000000..068f648 --- /dev/null +++ b/xpp/xpp_zap.h @@ -0,0 +1,58 @@ +#ifndef XPP_ZAP_H +#define XPP_ZAP_H + +#include <linux/workqueue.h> +#include "xpd.h" +#include "xproto.h" + +xpacket_t *xpacket_new(xbus_t *xbus, int flags); +void xpacket_free(xbus_t *xbus, xpacket_t *p); + +/* packet queues */ +void init_xbus_packet_queue(packet_queue_t *q, const char name[]); +void drain_xbus_packet_queue(xbus_t *xbus, packet_queue_t *q); +void xbus_enqueue_packet(xbus_t *xbus, packet_queue_t *q, xpacket_t *pack); +xpacket_t *xbus_dequeue_packet(packet_queue_t *q); + +xbus_t *xbus_new(ulong loopback_xpds); +void xbus_activate(xbus_t *xbus); +void xbus_deactivate(xbus_t *xbus); + +void xbus_reset_counters(xbus_t *xbus); +int packet_send(xbus_t *xbus, xpacket_t *pack_tx); +void phone_hook(xpd_t *xpd, int channo, bool offhook); +void xpp_check_hookstate(xpd_t *xpd, xpp_line_t fxs_off_hook); +xpd_t *xpd_of(xbus_t *xbus, int xpd_num); +void card_detected(void *data); +xpd_t *xpd_alloc(size_t privsize, xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, int channels, byte revision); +void xpd_remove(xpd_t *xpd); +void fill_beep(u_char *buf, int duration); +void xpp_tick(unsigned long param); +int xpp_open(struct zt_chan *chan); +int xpp_close(struct zt_chan *chan); +int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg); +int xpp_maint(struct zt_span *span, int cmd); + +#define CARD_DESC_MAGIC 0xca9dde5c + +struct card_desc_struct { + struct work_struct work; + u32 magic; + xbus_t *xbus; + byte rev; /* Revision number */ + byte type; /* LSB: 1 - to_phone, 0 - to_line */ + byte xpd_num; +}; +extern struct workqueue_struct *xpp_worker; + +#ifdef CONFIG_PROC_FS +#include <linux/proc_fs.h> + +extern struct proc_dir_entry *xpp_procdir; +#endif +extern xpd_t *sync_master; + +// Number of rings our simulated phone will ring: +#define RINGS_NUM 3 + +#endif /* XPP_ZAP_H */ diff --git a/xpp/xproto.c b/xpp/xproto.c new file mode 100644 index 0000000..5a0d5c6 --- /dev/null +++ b/xpp/xproto.c @@ -0,0 +1,334 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 "xpd.h" +#include "xproto.h" +#include "xpp_zap.h" +#include <linux/module.h> + +static const char rcsid[] = "$Id$"; + +extern int print_dbg; +static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack); + +static const xproto_table_t *xprotocol_tables[XPD_TYPE_NOMODULE]; + +bool valid_xpd_addr(const xpd_addr_t *addr) +{ + return ((addr->bank_num & ~0x1) == 0) && ((addr->card_id & ~0x3) == 0); +} + +int xpd_addr2num(const xpd_addr_t *addr) +{ + BUG_ON(!valid_xpd_addr(addr)); + return addr->bank_num * 4 + addr->card_id; +} + +void xpd_set_addr(xpd_addr_t *addr, int xpd_num) +{ + if(xpd_num < 4) { + addr->card_id = xpd_num; + addr->bank_num = 0; + } else { + addr->card_id = xpd_num % 4; + addr->bank_num = xpd_num / 4; + } +} + + +/*---------------- General Protocol Management ----------------------------*/ + +const xproto_entry_t *xproto_card_entry(const xproto_table_t *table, byte opcode) +{ + const xproto_entry_t *xe; + + //DBG("\n"); + xe = &table->entries[opcode]; + return (xe->handler != NULL) ? xe : NULL; +} + +const xproto_entry_t *xproto_global_entry(byte opcode) +{ + const xproto_entry_t *xe; + + xe = xproto_card_entry(&PROTO_TABLE(GLOBAL), opcode); + //DBG("opcode=0x%X xe=%p\n", opcode, xe); + return xe; +} + +const xproto_handler_t xproto_global_handler(byte opcode) +{ + return xproto_card_handler(&PROTO_TABLE(GLOBAL), opcode); +} + +const xproto_table_t *xproto_table(xpd_type_t cardtype) +{ + if(cardtype >= XPD_TYPE_NOMODULE) + return NULL; + return xprotocol_tables[cardtype]; +} + +const xproto_table_t *get_xproto_table(xpd_type_t cardtype) +{ + const xproto_table_t *xtable; + + if(cardtype >= XPD_TYPE_NOMODULE) + return NULL; + xtable = xprotocol_tables[cardtype]; + if(!xtable) { /* Try to load the relevant module */ + int ret = request_module("xpd-type-%d", cardtype); + if(ret != 0) { + NOTICE("%s: Failed to load module for type=%d. exit status=%d.\n", + __FUNCTION__, cardtype, ret); + /* Drop through: we may be luck... */ + } + xtable = xprotocol_tables[cardtype]; + } + return xtable; +} + +const xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode) +{ + const xproto_entry_t *xe; + + //DBG("\n"); + xe = xproto_card_entry(table, opcode); + return xe->handler; +} + +const xproto_entry_t *find_xproto_entry(xpd_t *xpd, byte opcode) +{ + const xproto_entry_t *xe; + + xe = xproto_global_entry(opcode); + // DBG("opcode=0x%X xe=%p\n", opcode, xe); + if(!xe) { + const xproto_table_t *xtable; + + if(!xpd) + return NULL; + xtable = xproto_table(xpd->type); + if(!xtable) + return NULL; + xe = xproto_card_entry(xtable, opcode); + if(!xe) + return NULL; + } + return xe; +} + +const xops_t *get_xops(xpd_type_t xpd_type) +{ + const xproto_table_t *proto_table; + + if(xpd_type >= XPD_TYPE_NOMODULE) + return NULL; + proto_table = xprotocol_tables[xpd_type]; + if(!proto_table) + return NULL; + return &proto_table->xops; +} + +int packet_receive(xbus_t *xbus, xpacket_t *pack) +{ + int xpd_num; + + if(!valid_xpd_addr(&pack->content.addr)) { + static int rate_limit = 0; + + if((rate_limit++ % 5003) < 3) + dump_packet("bad address", pack, print_dbg); + xbus->ops->packet_free(xbus, pack); + return -EPROTO; + } + xpd_num = XPD_NUM(pack->content.addr); +#if SOFT_SIMULATOR + if(xbus->sim[xpd_num].simulated) { + //dump_packet("packet_receive -> simulate", pack, print_dbg); + return simulate_xpd(xbus, xpd_num, pack); + } else +#endif + { + //dump_packet("packet_receive -> process", pack, print_dbg); + return packet_process(xbus, xpd_num, pack); + } +} + +static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack) +{ + byte op; + const xproto_entry_t *xe; + xproto_handler_t handler; + xproto_table_t *table; + xpd_t *xpd; + int ret = 0; + + BUG_ON(!pack); + op = pack->content.opcode; + xpd_num = XPD_NUM(pack->content.addr); + xpd = xpd_of(xbus, xpd_num); + xe = find_xproto_entry(xpd, op); + /*-------- Validations -----------*/ + if(!xe) { + ERR("xpp: %s -- bad command op=0x%02X\n", __FUNCTION__, op); + dump_packet("packet_process -- bad command", pack, print_dbg); + ret = -EPROTO; + goto out; + } + table = xe->table; + BUG_ON(!table); + if(!table->packet_is_valid(pack)) { + ERR("xpp: %s: wrong size %d for op=0x%02X\n", + __FUNCTION__, pack->datalen, op); + dump_packet("packet_process -- wrong size", pack, print_dbg); + ret = -EPROTO; + goto out; + } + handler = xe->handler; + BUG_ON(!handler); + XBUS_COUNTER(xbus, RX_BYTES) += pack->datalen; + handler(xbus, xpd, xe, pack); +out: + xbus->ops->packet_free(xbus, pack); + return ret; +} + +#define VERBOSE_DEBUG 1 +#define ERR_REPORT_LIMIT 20 + +void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg) +{ + byte op = packet->content.opcode; + + if(!print_dbg) + return; + DBG("%s: @0x%1X%1X OP=0x%02X LEN=%d\n", + msg, + packet->content.addr.bank_num, + packet->content.addr.card_id, + op, + (byte)packet->datalen); +#if VERBOSE_DEBUG + { + int i; + byte *p = packet->content.data; + + for(i = 0; i < packet->datalen; i++) { + static int limiter = 0; + + if(i >= sizeof(xpacket_raw_t)) { + if(limiter < ERR_REPORT_LIMIT) { + ERR("%s: length overflow i=%d > sizeof(xpacket_raw_t)=%d\n", + __FUNCTION__, i+1, sizeof(xpacket_raw_t)); + } else if(limiter == ERR_REPORT_LIMIT) { + ERR("%s: error packet #%d... squelsh reports.\n", + __FUNCTION__, limiter); + } + limiter++; + break; + } + DBG(" %2d> %02X\n", i+1, p[i]); + } + } +#endif +} + +const char *xproto_name(xpd_type_t xpd_type) +{ + const xproto_table_t *proto_table; + + BUG_ON(xpd_type >= XPD_TYPE(NOMODULE)); + proto_table = xprotocol_tables[xpd_type]; + if(!proto_table) + return NULL; + return proto_table->name; +} + +#define CHECK_XOP(f) \ + if(!(xops)->f) { \ + ERR("%s: missing xmethod %s [%s (%d)]\n", __FUNCTION__, #f, name, type); \ + return -EINVAL; \ + } + +int xproto_register(const xproto_table_t *proto_table) +{ + int type; + const char *name; + const xops_t *xops; + + BUG_ON(!proto_table); + type = proto_table->type; + name = proto_table->name; + if(type >= XPD_TYPE(NOMODULE)) { + NOTICE("%s: Bad xproto type %d\n", __FUNCTION__, type); + return -EINVAL; + } + DBG("%s (%d)\n", name, type); + if(xprotocol_tables[type]) + NOTICE("%s: overriding registration of %s (%d)\n", __FUNCTION__, name, type); + xops = &proto_table->xops; + CHECK_XOP(card_new); + CHECK_XOP(card_init); + CHECK_XOP(card_remove); + CHECK_XOP(card_tick); + CHECK_XOP(SYNC_SOURCE); + CHECK_XOP(PCM_WRITE); + CHECK_XOP(CHAN_ENABLE); + CHECK_XOP(CHAN_POWER); + CHECK_XOP(CHAN_CID); + CHECK_XOP(RING); + CHECK_XOP(SETHOOK); + CHECK_XOP(LED); + CHECK_XOP(RELAY_OUT); + + xprotocol_tables[type] = proto_table; + return 0; +} + +void xproto_unregister(const xproto_table_t *proto_table) +{ + int type; + const char *name; + + BUG_ON(!proto_table); + type = proto_table->type; + name = proto_table->name; + DBG("%s (%d)\n", name, type); + if(type >= XPD_TYPE(NOMODULE)) { + NOTICE("%s: Bad xproto type %s (%d)\n", __FUNCTION__, name, type); + return; + } + if(!xprotocol_tables[type]) + NOTICE("%s: xproto type %s (%d) is already unregistered\n", __FUNCTION__, name, type); + xprotocol_tables[type] = NULL; +} + +EXPORT_SYMBOL(dump_packet); +EXPORT_SYMBOL(packet_receive); +EXPORT_SYMBOL(valid_xpd_addr); +EXPORT_SYMBOL(xpd_addr2num); +EXPORT_SYMBOL(xpd_set_addr); +EXPORT_SYMBOL(xproto_global_entry); +EXPORT_SYMBOL(xproto_card_entry); +EXPORT_SYMBOL(xproto_name); +EXPORT_SYMBOL(xproto_register); +EXPORT_SYMBOL(xproto_unregister); diff --git a/xpp/xproto.h b/xpp/xproto.h new file mode 100644 index 0000000..a369a6b --- /dev/null +++ b/xpp/xproto.h @@ -0,0 +1,239 @@ +#ifndef XPROTO_H +#define XPROTO_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 "xdefs.h" + +#ifdef __KERNEL__ +#include <linux/list.h> +#endif + +#define XPD_TYPE(n) XPD_TYPE_ ## n +#define PROTO_TABLE(n) n ## _protocol_table + +typedef enum xpd_type { + XPD_TYPE(FXO) = 0x02, + XPD_TYPE(FXS) = 0x03, + XPD_TYPE(NOMODULE) = 0x0F, +} xpd_type_t; + +#define LINE_BITS (sizeof(xpp_line_t)*8) +#define PCM_CHUNKSIZE (CHANNELS_PERXPD * 8) /* samples of 8 bytes */ + +typedef struct xpd_addr { + byte card_id:4; + byte bank_num:4; +} __attribute__((packed)) xpd_addr_t; + +bool valid_xpd_addr(const xpd_addr_t *addr); +int xpd_addr2num(const xpd_addr_t *addr); +void xpd_set_addr(xpd_addr_t *addr, int xpd_num); + +#define XPD_NUM(x) xpd_addr2num(&x) +#define XPD_ADDR_SET(x,val) xpd_set_addr(&x, val) +#define MAX_XPACKET_DATALEN 100 + +#define XPROTO_NAME(card,op) card ## _ ## op +#define XPROTO_HANDLER(card,op) XPROTO_NAME(card,op ## _handler) +#define XPROTO_CALLER(card,op) XPROTO_NAME(card,op ## _send) + +#define HANDLER_DEF(card,op) \ + int XPROTO_HANDLER(card,op) ( \ + xbus_t *xbus, \ + xpd_t *xpd, \ + const xproto_entry_t *cmd, \ + xpacket_t *pack) + +#define CALL_PROTO(card,op, ...) XPROTO_CALLER(card,op)( __VA_ARGS__ ) + +#define DECLARE_CMD(card,op, ...) \ + int CALL_PROTO(card, op, xbus_t *xbus, xpd_t *xpd, ## __VA_ARGS__ ) + +#define HOSTCMD(card, op, ...) \ + DECLARE_CMD(card, op, ## __VA_ARGS__ ); \ + EXPORT_SYMBOL(XPROTO_CALLER(card, op)); \ + DECLARE_CMD(card, op, ## __VA_ARGS__ ) + +#define RPACKET_NAME(card,op) XPROTO_NAME(RPACKET_ ## card, op) +#define RPACKET_TYPE(card,op) struct RPACKET_NAME(card, op) + +#define DEF_RPACKET_DATA(card,op, ...) \ + struct RPACKET_NAME(card,op) { \ + byte opcode; \ + xpd_addr_t addr; \ + __VA_ARGS__ \ + } __attribute__((packed)) + +#define RPACKET_CAST(p,card,op) ((RPACKET_TYPE(card,op) *)p) +#define RPACKET_FIELD(p,card,op,field) (RPACKET_CAST(p,card,op)->field) +#define RPACKET_SIZE(card,op) sizeof(RPACKET_TYPE(card,op)) + +#define PACKET_LEN(p) \ + ((p)->datalen + sizeof(xpd_addr_t) + 1) + +#define XENTRY(card,op) \ + [ XPROTO_NAME(card,op) ] { \ + .handler = XPROTO_HANDLER(card,op), \ + .datalen = RPACKET_SIZE(card,op), \ + .name = #op, \ + .table = &PROTO_TABLE(card) \ + } + + +#define XPACKET_INIT(p, card, op) \ + do { \ + p->content.opcode = XPROTO_NAME(card,op); \ + p->datalen = RPACKET_SIZE(card,op); \ + } while(0) + +#define XPACKET_NEW(p, xbus, card, op, to) \ + do { \ + p = xbus->ops->packet_new(xbus, GFP_ATOMIC); \ + if(!p) \ + return -ENOMEM; \ + XPACKET_INIT(p, card, op); \ + XPD_ADDR_SET(p->content.addr, to); \ + } while(0); + +typedef struct xproto_entry xproto_entry_t; +typedef struct xproto_table xproto_table_t; + +typedef int (*xproto_handler_t)( + xbus_t *xbus, + xpd_t *xpd, + const xproto_entry_t *cmd, + xpacket_t *pack); + +const xproto_entry_t *find_xproto_entry(xpd_t *xpd, byte opcode); + +const xproto_table_t *get_xproto_table(xpd_type_t cardtype); +const xproto_entry_t *xproto_card_entry(const xproto_table_t *table, byte opcode); +const xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode); + +const xproto_entry_t *xproto_global_entry(byte opcode); +const xproto_handler_t xproto_global_handler(byte opcode); + +#define CALL_XMETHOD(name, xbus, xpd, ...) \ + (xpd)->xops->name(xbus, xpd, ## __VA_ARGS__ ) + +struct xops { + xpd_t *(*card_new)(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision); + int (*card_init)(xbus_t *xbus, xpd_t *xpd); + int (*card_remove)(xbus_t *xbus, xpd_t *xpd); + int (*card_tick)(xbus_t *xbus, xpd_t *xpd); + + int (*SYNC_SOURCE)(xbus_t *xbus, xpd_t *xpd, bool setit, bool is_master); + int (*PCM_WRITE)(xbus_t *xbus, xpd_t *xpd, xpp_line_t hookstate, volatile byte *buf); + + int (*CHAN_ENABLE)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on); + int (*CHAN_POWER)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on); + int (*CHAN_CID)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines); + int (*RING)(xbus_t *xbus, xpd_t *xpd, int pos, bool on); + int (*SETHOOK)(xbus_t *xbus, xpd_t *xpd, xpp_line_t hook_status); + int (*LED)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, byte which, bool on); + int (*RELAY_OUT)(xbus_t *xbus, xpd_t *xpd, byte which, bool on); +}; + +const xops_t *get_xops(xpd_type_t xpd_type); + +#undef XMETHOD + +struct xproto_entry { + xproto_handler_t handler; + int datalen; + const char *name; + xproto_table_t *table; +}; + +struct xproto_table { + xproto_entry_t entries[255]; /* Indexed by opcode */ + xops_t xops; + xpd_type_t type; + const char *name; + bool (*packet_is_valid)(xpacket_t *pack); + void (*packet_dump)(xpacket_t *pack); +}; + +#include "card_global.h" +#include "card_fxs.h" + +enum opcodes { + XPROTO_NAME(GLOBAL, DESC_REQ) = 0x04, + XPROTO_NAME(GLOBAL, DEV_DESC) = 0x05, +/**/ + XPROTO_NAME(GLOBAL, PCM_WRITE) = 0x11, + XPROTO_NAME(GLOBAL, PCM_READ) = 0x12, +/**/ + XPROTO_NAME(GLOBAL, SYNC_SOURCE) = 0x19, + XPROTO_NAME(GLOBAL, SYNC_REPLY) = 0x1A, + + XPROTO_NAME(FXS, SIG_CHANGED) = 0x06, +/**/ + XPROTO_NAME(FXS, SLIC_WRITE) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, CHAN_ENABLE) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, CHAN_POWER) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, CHAN_CID) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, RING) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, SETHOOK) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, LED) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, RELAY_OUT) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, SLIC_INIT) = 0x0F, /* Write to SLIC */ + XPROTO_NAME(FXS, SLIC_QUERY) = 0x0F, /* Write to SLIC */ +/**/ + XPROTO_NAME(FXS, SLIC_REPLY) = 0x10, +}; + + +#define MEMBER(card,op) RPACKET_TYPE(card,op) RPACKET_NAME(card,op) + +struct xpacket_raw { + byte opcode; + xpd_addr_t addr; + union { + MEMBER(GLOBAL, DESC_REQ); + MEMBER(GLOBAL, DEV_DESC); + MEMBER(GLOBAL, PCM_WRITE); + MEMBER(GLOBAL, PCM_READ); + MEMBER(GLOBAL, SYNC_REPLY); + + MEMBER(FXS, SIG_CHANGED); + MEMBER(FXS, SLIC_REPLY); + + byte data[0]; + }; +} __attribute__((packed)); + +struct xpacket { + xpacket_raw_t content; + size_t datalen; + struct list_head list; +}; + +void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg); +int packet_receive(xbus_t *xbus, xpacket_t *pack); +int xproto_register(const xproto_table_t *proto_table); +void xproto_unregister(const xproto_table_t *proto_table); +const xproto_entry_t *xproto_global_entry(byte opcode); +const char *xproto_name(xpd_type_t xpd_type); + +#endif /* XPROTO_H */ diff --git a/xpp/zap_debug.c b/xpp/zap_debug.c new file mode 100644 index 0000000..a5d7915 --- /dev/null +++ b/xpp/zap_debug.c @@ -0,0 +1,136 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2005, Xorcom + * + * 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 <linux/version.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +# warning "This module is tested only with 2.6 kernels" +#endif + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <zaptel.h> +#include "zap_debug.h" + +static const char rcsid[] = "$Id$"; + +#define P_(x) [ x ] = { .value = x, .name = #x, } +static struct { + int value; + char *name; +} poll_names[] = { + P_(POLLIN), + P_(POLLPRI), + P_(POLLOUT), + P_(POLLERR), + P_(POLLHUP), + P_(POLLNVAL), + P_(POLLRDNORM), + P_(POLLRDBAND), + P_(POLLWRNORM), + P_(POLLWRBAND), + P_(POLLMSG), + P_(POLLREMOVE) +}; +#undef P_ + +void dump_poll(int print_dbg, const char *msg, int poll) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(poll_names); i++) { + if(poll & poll_names[i].value) + DBG("%s: %s\n", msg, poll_names[i].name); + } +} + +#define E_(x) [ x ] = { .value = x, .name = #x, } +static struct { + int value; + char *name; +} zt_event_names[] = { + E_(ZT_EVENT_NONE), + E_(ZT_EVENT_ONHOOK), + E_(ZT_EVENT_RINGOFFHOOK), + E_(ZT_EVENT_WINKFLASH), + E_(ZT_EVENT_ALARM), + E_(ZT_EVENT_NOALARM), + E_(ZT_EVENT_ABORT), + E_(ZT_EVENT_OVERRUN), + E_(ZT_EVENT_BADFCS), + E_(ZT_EVENT_DIALCOMPLETE), + E_(ZT_EVENT_RINGERON), + E_(ZT_EVENT_RINGEROFF), + E_(ZT_EVENT_HOOKCOMPLETE), + E_(ZT_EVENT_BITSCHANGED), + E_(ZT_EVENT_PULSE_START), + E_(ZT_EVENT_TIMER_EXPIRED), + E_(ZT_EVENT_TIMER_PING), + E_(ZT_EVENT_POLARITY) +}; +#undef E_ + +char *event2str(int event) +{ + BUG_ON(event > ARRAY_SIZE(zt_event_names)); + return zt_event_names[event].name; +} + +#define S_(x) [ x ] = { .value = x, .name = #x, } +static struct { + int value; + char *name; +} zt_sig_types[] = { + S_(ZT_SIG_NONE), + S_(ZT_SIG_FXSLS), + S_(ZT_SIG_FXSGS), + S_(ZT_SIG_FXSKS), + S_(ZT_SIG_FXOLS), + S_(ZT_SIG_FXOGS), + S_(ZT_SIG_FXOKS), + S_(ZT_SIG_EM), + S_(ZT_SIG_CLEAR), + S_(ZT_SIG_HDLCRAW), + S_(ZT_SIG_HDLCFCS), + S_(ZT_SIG_HDLCNET), + S_(ZT_SIG_SLAVE), + S_(ZT_SIG_SF), + S_(ZT_SIG_CAS), + S_(ZT_SIG_DACS), + S_(ZT_SIG_EM_E1), + S_(ZT_SIG_DACS_RBS) +}; +#undef S_ + +void dump_sigtype(int print_dbg, const char *msg, int sigtype) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(zt_sig_types); i++) { + if(sigtype == zt_sig_types[i].value) + DBG("%s: %s\n", msg, zt_sig_types[i].name); + } +} + +EXPORT_SYMBOL(dump_poll); +EXPORT_SYMBOL(event2str); +EXPORT_SYMBOL(dump_sigtype); diff --git a/xpp/zap_debug.h b/xpp/zap_debug.h new file mode 100644 index 0000000..3f4651a --- /dev/null +++ b/xpp/zap_debug.h @@ -0,0 +1,16 @@ +#ifndef ZAP_DEBUG_H +#define ZAP_DEBUG_H + +#define DBG(fmt, ...) \ + ((print_dbg) && printk(KERN_DEBUG "DBG-%s: %s: " fmt, \ + THIS_MODULE->name, __FUNCTION__, ## __VA_ARGS__)) +#define INFO(fmt, ...) printk(KERN_INFO "INFO-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__) +#define NOTICE(fmt, ...) printk(KERN_NOTICE "NOTICE-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__) +#define ERR(fmt, ...) printk(KERN_ERR "ERR-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__) + + +void dump_poll(int print_dbg, const char *msg, int poll); +char *event2str(int event); +void dump_sigtype(int print_dbg, const char *msg, int sigtype); + +#endif /* ZAP_DEBUG_H */ |