summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile30
-rw-r--r--README.Astribank135
-rw-r--r--xpp/FPGA_XPD.hex243
-rw-r--r--xpp/Makefile5
-rw-r--r--xpp/card_fxs.c703
-rw-r--r--xpp/card_fxs.h45
-rw-r--r--xpp/card_global.c256
-rw-r--r--xpp/card_global.h55
-rw-r--r--xpp/cards.c394
-rw-r--r--xpp/cards.h23
-rwxr-xr-xxpp/gen_slic_init37
-rw-r--r--xpp/slic.c129
-rw-r--r--xpp/slic.h65
-rw-r--r--xpp/slic_init.inc65
-rwxr-xr-xxpp/sync.sh31
-rw-r--r--xpp/xdefs.h82
-rw-r--r--xpp/xpd.h290
-rw-r--r--xpp/xpp_fxloader24
-rw-r--r--xpp/xpp_fxloader.usermap2
-rw-r--r--xpp/xpp_modprobe10
-rw-r--r--xpp/xpp_proto.c1044
-rw-r--r--xpp/xpp_proto.h188
-rw-r--r--xpp/xpp_usb.c882
-rw-r--r--xpp/xpp_zap.c2312
-rw-r--r--xpp/xpp_zap.h58
-rw-r--r--xpp/xproto.c334
-rw-r--r--xpp/xproto.h239
-rw-r--r--xpp/zap_debug.c136
-rw-r--r--xpp/zap_debug.h16
29 files changed, 7823 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 9b484f1..2559a62 100644
--- a/Makefile
+++ b/Makefile
@@ -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
@@ -138,6 +138,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
@@ -146,6 +147,7 @@ MODULES:=zaptel tor2 torisa wcusb wcfxo wctdm wctdm24xxp \
ifeq (${BUILDVER},linux26)
MODULES+=ztdummy
endif
+
MODULESO:=$(MODULES:%=%.o)
MODULESKO:=$(MODULES:%=%.ko)
ifeq ($(BUILDVER),linux26)
@@ -159,6 +161,14 @@ LIB_DIR:=$(INSTALL_PREFIX)/usr/lib
INC_DIR:=$(INSTALL_PREFIX)/usr/include
MOD_DIR:=$(INSTALL_PREFIX)/lib/modules/$(KVERS)/misc
+MOD_DESTDIR:=zaptel
+
+obj-m:=$(MODULESO) ${XPPMOD}
+
+ifneq ($(filter xpp,${MAKECMDGOALS}),)
+XPPMOD=xpp/
+endif
+
ifneq (,$(wildcard /usr/include/newt.h))
ZTTOOL:=zttool
endif
@@ -183,16 +193,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
- $(KMAKE) modules
+ $(KMAKE) 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 \
@@ -211,15 +219,15 @@ zaptel.o: digits.h arith.h sec.h mec.h sec-2.h mec2.h mec3.h zconfig.h
torisa.o: torisa.h
wcusb.o: wcusb.h
-
+
wcfxsusb.o: wcfxsusb.h
-
+
wctdm.o: wctdm.h
-
+
wctdm24xxp.o: wctdm.h
-
+
pciradio.o: radfw.h
-
+
ztdummy.o: ztdummy.h
$(MODULESO): %.o: %.c zaptel.h
@@ -489,6 +497,8 @@ clean:
ifeq (${BUILDVER},linux26)
$(KMAKE) clean
endif
+ 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, &reg_type, &reg_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, &reg_type, &reg_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 */