[Pkg-voip-commits] r1004 - in zaptel/trunk/debian: . patches

Tzafrir Cohen tzafrir-guest at costa.debian.org
Fri Nov 18 02:29:31 UTC 2005


Author: tzafrir-guest
Date: 2005-11-18 02:29:28 +0000 (Fri, 18 Nov 2005)
New Revision: 1004

Added:
   zaptel/trunk/debian/patches/Makefile_kbuild.dpatch
   zaptel/trunk/debian/patches/Makefile_xpp.dpatch
   zaptel/trunk/debian/patches/xpp.dpatch
Modified:
   zaptel/trunk/debian/changelog
   zaptel/trunk/debian/patches/00list
   zaptel/trunk/debian/rules
Log:
* Makefile_kbuild.dpatch: use kbuild for 2.6 modules build. used for:
* Makefile_xpp.dpatch: (not applied by default, to disable build) 
  a small patch to enable the build of:
* xpp.dpatch: drivers for Xorcom Asteribank
* applying echocan_env.dpatch


Modified: zaptel/trunk/debian/changelog
===================================================================
--- zaptel/trunk/debian/changelog	2005-11-18 02:20:21 UTC (rev 1003)
+++ zaptel/trunk/debian/changelog	2005-11-18 02:29:28 UTC (rev 1004)
@@ -1,9 +1,18 @@
 zaptel (1:1.2.0-1) unstable; urgency=low
 
   * New upstream release
-  * Remove Makefile_deps_kern.dpatch as it doesnt apply upstream
+  * fix Makefile_deps_kern.dpatch
+  * remove .rej from Makefile.uname.dpatch
+  * do install genzaptelconf man page
+  * update genzaptelconf and its man page
+  * echocan_env.dpatch: allow changing the echo canceller at zaptel-modules
+    build time
+  * Makefile_kbuild.dpatch: use kbuild for 2.6 modules build. used for:
+  * Makefile_xpp.dpatch: (not applied by default) 
+    a small patch to enable the build of:
+  * xpp.dpatch: drivers for Xorcom Asteribank
 
- -- Mark Purcell <msp at debian.org>  Thu, 17 Nov 2005 17:50:00 +0000
+ -- Tzafrir Cohen <tzafrir.cohen at xorcom.com>  Fri, 18 Nov 2005 04:11:52 +0200
 
 zaptel (1:1.2.0-rc2-1) experimental; urgency=low
 

Modified: zaptel/trunk/debian/patches/00list
===================================================================
--- zaptel/trunk/debian/patches/00list	2005-11-18 02:20:21 UTC (rev 1003)
+++ zaptel/trunk/debian/patches/00list	2005-11-18 02:29:28 UTC (rev 1004)
@@ -1,5 +1,10 @@
-#Makefile_deps_kern
+Makefile_deps_kern
 Makefile_deps_utils
 Makefile_uname
 Makefile_pscmd
 Makefile_targets
+Makefile_kbuild
+#Makefile_xpp
+# touches the Makefile as well:
+echocan_env
+xpp

Added: zaptel/trunk/debian/patches/Makefile_kbuild.dpatch
===================================================================
--- zaptel/trunk/debian/patches/Makefile_kbuild.dpatch	2005-11-18 02:20:21 UTC (rev 1003)
+++ zaptel/trunk/debian/patches/Makefile_kbuild.dpatch	2005-11-18 02:29:28 UTC (rev 1004)
@@ -0,0 +1,67 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## Makefile_kbuild.dpatch by Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: use kbuild (the kernel build system) more extensively. This will
+## DP: be useful for recursive builds.
+
+ at DPATCH@
+diff -urNad zaptel-1.2.0/Makefile /tmp/dpep.UEJtoR/zaptel-1.2.0/Makefile
+--- zaptel-1.2.0/Makefile	2005-11-18 03:55:06.922498053 +0200
++++ /tmp/dpep.UEJtoR/zaptel-1.2.0/Makefile	2005-11-18 03:59:07.152581731 +0200
+@@ -58,6 +58,9 @@
+ 
+ KFLAGS+=-DSTANDALONE_ZAPATA
+ CFLAGS+=-DSTANDALONE_ZAPATA
++KMAKE  = $(MAKE) -C $(KSRC) SUBDIRS=$(PWD)
++KMAKE_INST = $(KMAKE) \
++  INSTALL_MOD_PATH=$(INSTALL_PREFIX) INSTALL_MOD_DIR=misc modules_install
+ 
+ ROOT_PREFIX=
+ INSTALL_PREFIX=$(DESTDIR)
+@@ -153,9 +156,9 @@
+ linux26: prereq $(BINS)
+ 	@echo $(KSRC)
+ 	@if [ -z "$(KSRC)" -o ! -d "$(KSRC)" ]; then echo "You do not appear to have the sources for the $(KVERS) kernel installed."; exit 1 ; fi
+-	$(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules
++	$(KMAKE) modules
+ 
+-obj-m := $(MODULESO)
++obj-m += $(MODULESO)
+ 
+ #ifneq ($(TOPDIR),)
+ #include $(TOPDIR)/Rules.make
+@@ -303,7 +306,7 @@
+ 		for x in $(MODULESKO); do \
+ 			rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/extra/$$x ; \
+ 		done; \
+-		$(MAKE) -C $(KSRC) SUBDIRS=$(PWD) INSTALL_MOD_PATH=$(INSTALL_PREFIX) INSTALL_MOD_DIR=misc modules_install; \
++		$(KMAKE_INST); \
+ 		if ! [ -f wcfxsusb.ko ]; then \
+ 			rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/misc/wcfxsusb.ko; \
+ 		fi; \
+@@ -404,16 +407,22 @@
+ 	install -m 644 tonezone.h $(INC_DIR)
+ 	install -m 644 zaptel.h torisa.h $(INC_DIR)/linux
+ 
+-install-modules: $(MODULES_BUILD)
++install-modules: install-$(BUILDVER)
++install-linux24: $(MODULES_BUILD)
+ 	install -d $(MOD_DIR)
+ 	install -m 644 $(MODULES_BUILD) $(MOD_DIR)
+ 
++install-linux26: $(MODULESKO)
++	$(KMAKE_INST)
++
+ clean:
+ 	rm -f torisatool makefw tor2fw.h radfw.h
+ 	rm -f ${BINS}
+ 	rm -f *.o ztcfg tzdriver sethdlc sethdlc-new
+ 	rm -f $(TZOBJS) $(LIBTONEZONE_SO) *.lo
+-	rm -f *.ko *.mod.c .*o.cmd
++ifeq (${BUILDVER},linux26)
++	$(KMAKE) clean
++endif
+ 	rm -rf .tmp_versions
+ 	rm -f gendigits tones.h
+ 	rm -f libtonezone*


Property changes on: zaptel/trunk/debian/patches/Makefile_kbuild.dpatch
___________________________________________________________________
Name: svn:executable
   + *

Added: zaptel/trunk/debian/patches/Makefile_xpp.dpatch
===================================================================
--- zaptel/trunk/debian/patches/Makefile_xpp.dpatch	2005-11-18 02:20:21 UTC (rev 1003)
+++ zaptel/trunk/debian/patches/Makefile_xpp.dpatch	2005-11-18 02:29:28 UTC (rev 1004)
@@ -0,0 +1,20 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## Makefile_xpp.dpatch by  <tzafrir at localhost>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: patch the makefile to build (using kbuild on 2.6) the xpp modules
+## DP: in the subdirectory xpp/
+
+ at DPATCH@
+diff -urNad zaptel-1.2.0/Makefile /tmp/dpep.HPVfs0/zaptel-1.2.0/Makefile
+--- zaptel-1.2.0/Makefile	2005-11-16 20:09:29.000000000 +0200
++++ /tmp/dpep.HPVfs0/zaptel-1.2.0/Makefile	2005-11-18 02:43:44.385305666 +0200
+@@ -95,6 +95,8 @@
+ LIBTONEZONE_SO:=libtonezone.so
+ LIBTONEZONE_SO_MAJOR_VER:=1
+ LIBTONEZONE_SO_MINOR_VER:=0
++# Also build xpp in the subdirectory xpp/
++obj-m += xpp/
+ MODULES:=zaptel tor2 torisa wcusb wcfxo wctdm wctdm24xxp \
+ 	 ztdynamic ztd-eth wct1xxp wct4xxp wcte11xp pciradio \
+          ztd-loc # ztdummy


Property changes on: zaptel/trunk/debian/patches/Makefile_xpp.dpatch
___________________________________________________________________
Name: svn:executable
   + *

Added: zaptel/trunk/debian/patches/xpp.dpatch
===================================================================
--- zaptel/trunk/debian/patches/xpp.dpatch	2005-11-18 02:20:21 UTC (rev 1003)
+++ zaptel/trunk/debian/patches/xpp.dpatch	2005-11-18 02:29:28 UTC (rev 1004)
@@ -0,0 +1,4904 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## xpp.dpatch by Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: No description.
+
+ at DPATCH@
+diff -urNad trunk/xpp/FPGA_bulkloop.hex /tmp/dpep.oAj2YR/trunk/xpp/FPGA_bulkloop.hex
+--- trunk/xpp/FPGA_bulkloop.hex	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/FPGA_bulkloop.hex	2005-09-22 18:16:26.000000000 +0300
+@@ -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
++:1004070090E61A7408F000000090E6017413F000FB
++: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
++:100700001201000200000040B404041000000102C5
++: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 -urNad trunk/xpp/FPGA_XPD.hex /tmp/dpep.oAj2YR/trunk/xpp/FPGA_XPD.hex
+--- trunk/xpp/FPGA_XPD.hex	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/FPGA_XPD.hex	2005-09-25 15:31:51.000000000 +0300
+@@ -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 -urNad trunk/xpp/gen_slic_init /tmp/dpep.oAj2YR/trunk/xpp/gen_slic_init
+--- trunk/xpp/gen_slic_init	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/gen_slic_init	2005-11-06 18:00:07.000000000 +0200
+@@ -0,0 +1,34 @@
++#! /usr/bin/perl -w
++
++use strict;
++
++my $header;
++my $comment;
++
++ at ARGV == 2 or die "Usage: $0 <infile> <outfile>\n";
++$header = $ARGV[1];
++open(HF, ">$header") or die "Failed to write '$header': $!\n";
++
++while(<>) {
++	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 -urNad trunk/xpp/Makefile /tmp/dpep.oAj2YR/trunk/xpp/Makefile
+--- trunk/xpp/Makefile	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/Makefile	2005-11-10 04:05:17.000000000 +0200
+@@ -0,0 +1,5 @@
++EXTRA_CFLAGS	= -I$(src)/.. 
++
++obj-m		= xpp.o xpp_usb.o
++xpp-y		+= xpp_proto.o xpp_zap.o zap_debug.o
++
+diff -urNad trunk/xpp/slic_init.inc /tmp/dpep.oAj2YR/trunk/xpp/slic_init.inc
+--- trunk/xpp/slic_init.inc	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/slic_init.inc	2005-11-06 18:04:13.000000000 +0200
+@@ -0,0 +1,51 @@
++// ----------------------------------==== 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,	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,	0x0A,	0x30, 0x80, 0x31, 0x3E, 0x32, 0x80, 0x33, 0x3E, 0x22, 0x18),	
++
++// Battery feed control(DCSW), Automatic control of Ring Trip and Loop Closure, Manual VBATH, VBATL
++S_(0x000000FF,	0x02,	0x43, 0x16),	
++S_(0x000000FF,	0x04,	0x4A, 0x31, 0x4B, 0x10),	
++
++// SLICs calibration
++S_(0x000000FF,	0x0C,	0x61, 0x1F, 0x60, 0x5F, 0x45, 0x20, 0x46, 0x02, 0x42, 0x04, 0x47, 0x04),	
++
++// 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),	
++
+diff -urNad trunk/xpp/sync.sh /tmp/dpep.oAj2YR/trunk/xpp/sync.sh
+--- trunk/xpp/sync.sh	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/sync.sh	2005-11-14 16:43:00.829030244 +0200
+@@ -0,0 +1,21 @@
++#!/bin/sh
++
++set -e
++
++SVN_ROOT=/home/tzafrir/Proj/Svn
++XORTEL_DIR=$SVN_ROOT/xpp-zaptel/xortel
++XPP_DIR=$SVN_ROOT/xpp-zaptel/zaptel/xpp
++TARGET_DIR=xpp
++
++(cd $SVN_ROOT/xpp-zaptel; svn update)
++
++cp -a $XORTEL_DIR/FPGA_*.hex ${TARGET_DIR}/
++cp -a $XORTEL_DIR/xpd.h ${TARGET_DIR}/
++cp -a $XORTEL_DIR/xpp_{proto,zap}.{c,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/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 -urNad trunk/xpp/xpd.h /tmp/dpep.oAj2YR/trunk/xpp/xpd.h
+--- trunk/xpp/xpd.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpd.h	2005-11-06 18:00:07.000000000 +0200
+@@ -0,0 +1,318 @@
++#ifndef	XPD_H
++#define	XPD_H
++
++/*
++ * Written by Oron Peled <oron at 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.
++ *
++ */
++
++/*
++ * Undef to disable packet logging
++#define	XPP_PACKET_LOG
++ */
++
++#ifdef	__KERNEL__
++#include <linux/kernel.h>
++#include <asm/atomic.h>
++#include <asm/semaphore.h>
++#include <linux/moduleparam.h>
++#else
++#include <stdint.h>
++typedef uint32_t __u32;
++#endif
++
++#include <zaptel.h>
++
++typedef	char	*charp;
++
++#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
++#define	MAX_XPDS	8	// 1 FXS + 2 E1/T1 + 1 (Quad * E1/T1)
++
++#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
++
++#define	XPD_NUM(x)		((x).id)
++#define	XPD_ADDR_SET(x,val)	do { (x).id = val; (x).reserved = 0; } while(0);
++#define	VALID_XPD_NUM(x)	((x) < MAX_XPDS && (x) >= 0)
++
++typedef	unsigned char byte;
++typedef	int bool;
++typedef	__u32 xpp_line_t;	/* at most 31 lines for E1 */
++
++typedef struct xpp_addr {
++	byte	id:4;
++	byte	reserved:4;
++} __attribute__((packed)) xpp_addr_t;
++
++
++typedef struct xbus xbus_t;
++typedef	struct xbus_ops xbus_ops_t;
++typedef	struct xpd xpd_t;
++typedef struct xpp_packet xpacket_t;
++
++typedef enum xbus_type {
++		FIRMWARE_LOOPBACK = 1,
++		FIRMWARE_XPP = 2,
++} xbus_type_t;
++
++typedef enum xpd_type {
++	XPD_TYPE_FXO = 0x2,
++	XPD_TYPE_FXS = 0x3,
++	//XPD_TYPE_E1 = ,
++	XPD_TYPE_NOMODULE = 0x7,
++} xpd_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;
++	struct xpd_sim	sim[MAX_XPDS];
++	struct workqueue_struct *sim_workqueue;
++	struct work_struct sim_work;  // workqueue job for running zt_register
++	struct work_struct poll_work; // workqueue job for issuing poll
++	packet_queue_t	sim_packet_queue;
++
++	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;
++
++#ifdef	XPP_PACKET_LOG
++	struct cyclic_buff *packet_log;
++#endif
++
++#ifdef CONFIG_PROC_FS
++	struct proc_dir_entry	*procdir;
++	struct proc_dir_entry	*procsummary;
++#endif
++
++	/* statistics */
++	int		counters[XBUS_COUNTER_MAX];
++
++};
++#endif
++
++typedef enum xpd_direction {
++	TO_PHONE = 0,
++	TO_TRUNK = 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)
++
++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]))
++
++#define	NUM_LEDS	2
++
++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_LINE */
++	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 */
++
++	int	ringing[CHANNELS_PERXPD];
++	bool	ringer_on[CHANNELS_PERXPD];	/* For ring toggling */
++	bool	led_on[CHANNELS_PERXPD];	/* For led toggling */
++
++	atomic_t span_registered; /* is span zt_registered ? */
++	struct work_struct xpd_post_init;
++	xbus_t *xbus;
++
++	spinlock_t	lock;
++
++	int flags;
++	enum {
++		XPD_STATE_OFF = 0x01,
++		XPD_STATE_NOREPLY = 0x02,
++		XPD_STATE_ACTIVE = 0x03,
++		XPD_STATE_ZAPTEL_ERR = 0x04,
++	} state;
++
++	unsigned int	board_flags;
++#define	XPD_BOARD_LOOPBACK	1
++
++#ifdef CONFIG_PROC_FS
++	struct proc_dir_entry	*xpd_proc;
++#endif
++	// Bit numbers of board_flags
++
++	int		counters[XPD_COUNTER_MAX];
++
++	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 -urNad trunk/xpp/xpp_fxloader /tmp/dpep.oAj2YR/trunk/xpp/xpp_fxloader
+--- trunk/xpp/xpp_fxloader	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_fxloader	2005-10-09 12:51:54.000000000 +0200
+@@ -0,0 +1,11 @@
++#!/bin/bash
++
++FIRMWARE="/etc/xortel/FPGA_XPD.hex"
++me=`basename $0`
++
++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 -urNad trunk/xpp/xpp_fxloader.usermap /tmp/dpep.oAj2YR/trunk/xpp/xpp_fxloader.usermap
+--- trunk/xpp/xpp_fxloader.usermap	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_fxloader.usermap	2005-10-09 12:51:54.000000000 +0200
+@@ -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 -urNad trunk/xpp/xpp_modprobe /tmp/dpep.oAj2YR/trunk/xpp/xpp_modprobe
+--- trunk/xpp/xpp_modprobe	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_modprobe	2005-11-03 09:45:31.000000000 +0200
+@@ -0,0 +1,4 @@
++options zaptel debug=1
++options wcfxo debug=1
++options xpp print_dbg=1 ignore_xpds=0x00 softloop_xpds=0x00 enabled_channels=0x000000FF pcm_gen=0x00000000 max_queue_len=20000
++options usb_test vendor=0x4B4 product=0x8613 altsetting=1 innum=2 outnum=4
+diff -urNad trunk/xpp/xpp_proto.c /tmp/dpep.oAj2YR/trunk/xpp/xpp_proto.c
+--- trunk/xpp/xpp_proto.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_proto.c	2005-11-10 04:05:18.000000000 +0200
+@@ -0,0 +1,823 @@
++#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: xpp_proto.c 123 2005-11-09 13:21:20Z oron $";
++
++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);
++
++/*------------------------- Protocol Functions ---------------------*/
++
++#define	HOSTCMD(name, ...)		\
++			DECLARE_CMD(name, ## __VA_ARGS__ );	\
++			EXPORT_SYMBOL(xpp_proto_ ## name);	\
++			DECLARE_CMD(name, ## __VA_ARGS__ )
++
++#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);
++
++
++/* 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_data_t	*slic;
++	int		seq = 0;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	if(!(xpd->enabled_chans & lines)) {	// Ignore disabled channels
++		return 0;
++	}
++	DBG("Channel Power: 0x%4X %s\n", lines, (on) ? "up" : "down");
++	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
++	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = lines;
++	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++	if(on) {
++		// Power up
++		slic->data[seq++] = 0x42;
++		slic->data[seq++] = 0x06;
++	} else {
++		// Power down
++		slic->data[seq++] = 0x42;
++		slic->data[seq++] = 0x00;
++	}
++	slic->len = seq;
++	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++
++	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_data_t	*slic;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	if(!(xpd->enabled_chans & lines)) {	// Ignore disabled channels
++		return 0;
++	}
++	DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off");
++	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
++	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = lines;
++	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++	slic->len = 2;
++	slic->data[0] = 0x40;
++	slic->data[1] = (on)? 0x01 : 0x00;
++	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++
++	packet_send(xbus, pack_tx);
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(RING, int pos, bool on)
++{
++	int		ret = 0;
++	xpacket_t	*pack_tx;
++	slic_data_t	*slic;
++	xpp_line_t	mask = (1 << pos);
++	int		seq = 0;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	if(!(mask & xpd->enabled_chans)) {	// Ignore disabled channels
++		return 0;
++	}
++	DBG("%s pos=%d %s\n", xpd->xpdname, pos, (on) ? "on" : "off");
++	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
++	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = mask;
++	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++	slic->data[seq++] = 0x40;
++	slic->data[seq++] = (on)? 0x04 : 0x01;
++	slic->len = seq;
++	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++
++	packet_send(xbus, pack_tx);
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(SETHOOK, xpp_line_t hook_status)
++{
++	int		ret = 0;
++	xpacket_t	*pack_tx;
++	slic_data_t	*slic;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	if(!(xpd->enabled_chans & hook_status)) {	// Ignore disabled channels
++		return 0;
++	}
++	DBG("New hook_status: %d\n", hook_status);
++	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
++	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = hook_status;
++	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++	/* FIXME: This is fake, until Dima implements FXO */
++	slic->len = 1;
++	slic->data[0] = 0x02;
++	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++
++	packet_send(xbus, pack_tx);
++	return ret;
++}
++
++/*
++ * LED control is done via SLIC register 0x06:
++ *         8     7     6     5     4     3     2     1     0
++ * 	+-----+-----+-----+-----+-----+-----+-----+-----+-----+
++ * 	| MR  | MG  |     |     |  R  |  W  |     |  G  |     |
++ * 	+-----+-----+-----+-----+-----+-----+-----+-----+-----+
++ *
++ * 	MR	- Mask Red. (1 - R effect the Red LED)
++ * 	MG	- Mask Green. (1 - G effect the Green LED)
++ * 	R	- Red LED (0 - OFF, 1 - ON)
++ * 	G	- Green LED (0 - OFF, 1 - ON)
++ * 	W	- Write (actually change the LED state)
++ */
++
++/* 0x0F */ HOSTCMD(LED, xpp_line_t lines, byte which, bool on)
++{
++	int		ret = 0;
++	xpacket_t	*pack_tx;
++	slic_data_t	*slic;
++	int		value;
++	int		i;
++			//                 GREEN	RED
++	int		mask[NUM_LEDS] = { 0x40,	0x80 };
++	int		leds[NUM_LEDS] = { 0x02,	0x10 };
++
++	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 = 0x08;
++	value |= (0xC0 & ~mask[which]);
++	if(on)
++		value |= leds[which];
++	for(i = 0; i < CHANNELS_PERXPD; i++) {
++		if(!IS_SET(lines, i))
++				continue;
++		NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
++		PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = (1 << i);
++
++		DBG("LED pack: line=%d value=0x%04X\n", i, value);
++		slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++		slic->len = 2;
++		slic->data[0] = 0x06;
++		slic->data[1] = value;
++		pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++		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_WRITE, xpd->id);
++		PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = source->lines;
++
++		slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, 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_data_t	*slic;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
++	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = (1 << pos);
++
++	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++	slic->len = 2;
++	slic->data[0] = 0x80 | (reg_num & ~0x80);	// MSB: 1 -- read, 0 -- write
++	slic->data[1] = 0x00;
++	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++
++	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);
++	if(!(xpd->enabled_chans & lines)) {	// Ignore disabled channels
++		return 0;
++	}
++	// DBG("PCM_WRITE\n");
++	if(pcm_gen != 0)
++		return 0;
++	if(lines == 0)
++		return 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);
++	if(!(xpd->enabled_chans & lines)) {	// Ignore disabled channels
++		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;
++}
++
++/* 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;
++	xpp_line_t	lines;
++	struct xpd_sim	*xpd_sim;
++	struct xpd_sim	*loopto_sim;
++	slic_data_t	*slic;
++	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:
++			lines = PACKET_FIELD(pack, SLIC_WRITE, lines);
++			slic = &PACKET_FIELD(pack, SLIC_WRITE, slic_data);
++			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);
++			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)) == 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)
++{
++	byte		dataL = PACKET_FIELD(reply, SLIC_REPLY, dataL);
++	byte		dataH = PACKET_FIELD(reply, SLIC_REPLY, dataH);
++	const char	*str = "";
++
++#if 0
++	switch (dataL) {
++	case 0x04:
++		str = "RINGING";
++		break;
++	case 0x11:
++		str = "NORMAL";
++		break;
++	default:
++		str = "?????";
++		break;
++	}
++#endif
++	DBG("SLIC_REPLY: xpd #%d dataH=0x%X dataL=0x%X [%s]\n",
++			xpd_num, dataH, dataL, str);
++	return 0;
++}
++
++static HANDLER_DEF(PCM_READ)
++{
++	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);
++	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, 1, 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_(	DIAG_FE,	1,	"DIAG FE Opcode"),
++  C_(	LOOPBACK_XA,	1,	"LOOPBACK Reply"),
++};
++
++#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);
+diff -urNad trunk/xpp/xpp_proto.h /tmp/dpep.oAj2YR/trunk/xpp/xpp_proto.h
+--- trunk/xpp/xpp_proto.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_proto.h	2005-11-03 15:01:15.000000000 +0200
+@@ -0,0 +1,167 @@
++#ifndef	XPP_PROTO_H
++#define	XPP_PROTO_H
++
++#include "xpd.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_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_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(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);
++/* 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_WRITE,
++			xpp_line_t	lines;
++			slic_data_t	slic_data;
++			);
++		H_(SLIC_REPLY,					/* Get status of a single SLIC (for debugging) */
++			byte		dataL;
++			byte		dataH;
++			);
++
++		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_(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);
++
++#endif
++
++#endif	/* XPP_PROTO_H */
+diff -urNad trunk/xpp/xpp_usb.c /tmp/dpep.oAj2YR/trunk/xpp/xpp_usb.c
+--- trunk/xpp/xpp_usb.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_usb.c	2005-11-14 10:37:50.000000000 +0200
+@@ -0,0 +1,838 @@
++/*
++ * Written by Oron Peled <oron at 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 <linux/proc_fs.h>
++#include <linux/usb.h>
++#include "xpp_proto.h"
++#include "xpd.h"
++#include "xpp_zap.h"
++
++static char revision[] = "$Revision: 125 $";
++
++DEF_PARM(int, print_dbg, 1, "Print DBG statements");	/* must be before zap_debug.h */
++
++#include "zap_debug.h"
++
++#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
++
++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;
++	int		toxpd = XPD_NUM(pack->content.addr);
++	struct urb	*urb;
++	int		ret = 0;
++	
++	BUG_ON(!pack);
++	if(!xusb->present) {
++		NOTICE("tried to send packets to non-exitant USB device. Ignored\n");
++		goto error;
++	}
++	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;
++		}
++	} else {
++		size_t	size = min(PACKET_LEN(pack), (size_t)xusb->ep_out.max_size);
++		if(pack->content.opcode == XPP_PCM_WRITE) {
++			XUSB_COUNTER(xusb, PCM_WRITES)++;
++#if 0
++			static	int rate_limit;
++			if((rate_limit++ % 1000) == 0)
++				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,
++	.mode =		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
++	.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(xpp_packet_r_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(xpp_packet_r_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, 0644, xbus->procdir,
++			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->procdir) {
++		remove_proc_entry(PROC_USBXPP_SUMMARY, xbus->procdir);
++	}
++#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)) {
++                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(xpp_addr_t) - 1; 	// opcode size
++	// DBG("datalen of new packet: %d\n", pack->datalen);
++
++	// Send UP
++	if(pack->content.opcode == XPP_PCM_READ) {
++		XUSB_COUNTER(xusb, PCM_READS)++;
++		// 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 == XPP_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;
++				}
++				CALL_PROTO(CHAN_ENABLE, xbus, xpd, 0xFF, 0);		// Disable all hardware channels
++				CALL_PROTO(LED, xbus, xpd, 0xFF, 1, 0);			// FIXME: Show activated channels
++			}
++		}
++	}
++	/* 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
++	);
++	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]);
++	}
++	len += sprintf(page + len, "<-- len=%d\n", len);
++	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 at actcom.co.il>");
++MODULE_LICENSE("GPL");
++MODULE_VERSION("$Id: xpp_usb.c 125 2005-11-10 15:42:41Z oron $");
++
++module_init(xpp_usb_init);
++module_exit(xpp_usb_cleanup);
+diff -urNad trunk/xpp/xpp_zap.c /tmp/dpep.oAj2YR/trunk/xpp/xpp_zap.c
+--- trunk/xpp/xpp_zap.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_zap.c	2005-11-10 04:05:18.000000000 +0200
+@@ -0,0 +1,1881 @@
++/*
++ * Written by Oron Peled <oron at 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 "xpp_proto.h"
++#include "xpp_zap.h"
++#include "xpd.h"
++
++static char revision[] = "$Revision: 124 $";
++
++#ifdef CONFIG_PROC_FS
++struct proc_dir_entry *xpp_procdir = NULL;
++#define	PROC_DIR		"xpp"
++#define	PROC_XBUSES		"xbuses"
++#define	PROC_XBUS_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	N_(x)	[ x] = #x
++static const char *xpd_type_names[] = {
++	N_(XPD_TYPE_FXO),
++	N_(XPD_TYPE_FXS),
++	N_(XPD_TYPE_NOMODULE),
++};
++#undef N_
++
++static	spinlock_t		xbuses_lock = SPIN_LOCK_UNLOCKED;
++static	xbus_t			*xbuses_array[MAX_BUSES] = {};
++static	int			bus_count = 0;
++static	bool			pcm_write_enable = 1;	// DEBUG
++static struct timer_list	xpp_timer;
++
++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");
++DEF_PARM(ulong, softloop_xpds, 0, "a bitmask of software xpd numbers");
++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 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);
++
++/*------------------------- Packet Handling ------------------------*/
++static kmem_cache_t	*packet_cache = NULL;
++static atomic_t	xpacket_count = ATOMIC_INIT(0);
++/* global workqueue. needs to be global? */
++struct workqueue_struct *xpd_init_workqueue=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	*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));
++}
++
++static void xpp_timer_tick(unsigned long param)
++{
++	xbus_t	*xbus;
++	xpd_t	*xpd;
++	int	i;
++	int	j;
++
++	mod_timer(&xpp_timer, jiffies + 1);	/* Must be 1KHz rate */
++	for(i = 0; i < MAX_BUSES; i++) {
++		xbus = xbuses_array[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 = xbus->xpds[j];
++			if(!xpd)
++				continue;
++			xpd->timer_count++;
++			xpd_blink_leds(xpd);
++			if(xpd->state != XPD_STATE_ACTIVE)
++				continue;
++			if(xpd->direction == TO_TRUNK)
++				xpp_ring_generate(xpd);
++			xpp_transmitprep(xpd);
++			xpp_receiveprep(xpd);
++		}
++	}
++}
++
++#if HZ != 1000
++#error "xpp_timer must be sampled EXACTLY 1000/per second"
++#endif
++
++/**
++ * Process a registration request.
++ *
++ * A new xpd structure is created when we get a device description packet.
++ * This happens from a timer interrupt context and we can't call zt_register 
++ * from there.
++ *
++ * Thus the xpd structure is generated and registerted with xpp. However the
++ * registration to zaptel is done "off-line" by posting a registration to the
++ * "reg" workqueue that calls this function.
++ */
++void xpd_initialize(void *data) {
++	xpd_t		*xpd = (xpd_t *)data;
++	xbus_t		*xbus;
++	
++	if (!xpd) {
++		ERR("%s: called with a NULL xpd\n", __FUNCTION__);
++		return;
++	}
++	xbus = xpd->xbus;
++#ifdef	CONFIG_PROC_FS
++	DBG("Creating xpd proc file %s.\n", xpd->xpdname);
++	xpd->xpd_proc = create_proc_read_entry(xpd->xpdname, 0644, xpd->xbus->procdir,
++			xpd_read_proc, xpd);
++	if(!xpd->xpd_proc) {
++		ERR("Failed to create proc file for xpd %s\n", xpd->xpdname);
++		return;
++	}
++#endif
++	list_add(&xpd->xpd_list, &xpd_list);
++	xbus->xpds[xpd->id] = xpd;
++	xbus->num_xpds++;
++	CALL_PROTO(SLIC_INIT, xbus, xpd);
++	// Turn off all channels
++	CALL_PROTO(CHAN_ENABLE, xbus, xpd, 0xFF, 0);
++//	CALL_PROTO(LED, xbus, xpd, 0xFF, 1, 0);				// FIXME: Show activated channels
++	// Turn on enabled channels
++	CALL_PROTO(CHAN_ENABLE, xbus, xpd, xpd->enabled_chans, 1);
++	DBG("Registering span of %s.\n", xpd->xpdname);
++	if(zt_register(&xpd->span, 1)) {
++		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--;
++		xpd->state = XPD_STATE_ZAPTEL_ERR;
++		return;
++	}
++#if 0
++	{
++		static char buf[ZT_CHUNKSIZE] = { 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67 };	/* silence */
++		// static char buf[ZT_CHUNKSIZE] = { 0x7F, 0xBE, 0xD8, 0xBE, 0x80, 0x41, 0x24, 0x41 };	/* Dima */
++		CALL_PROTO(PCM_GEN, xbus, xpd->id, pcm_gen, buf);
++	}
++#endif
++
++	atomic_inc(&xpd->span_registered);
++
++	xpd->state = XPD_STATE_ACTIVE;
++}
++
++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;
++	struct xpd_sim	*sim;
++	int		channels;
++	int		i;
++
++	if(!xpd)
++		goto out;
++
++	xbus = xpd->xbus;
++	sim = &xbus->sim[xpd->id];
++	channels = xpd->channels;
++	len += sprintf(page + len, "%s (%s ,state=%d, span_registered=%d)\n"
++			"timer_count: %d span->mainttimer=%d\n"
++			,
++			xpd->xpdname, xpd_type_names[xpd->type], xpd->state, atomic_read(&xpd->span_registered),
++			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: ", "hookstate");
++	for(i = 0; i < channels; i++) {
++		len += sprintf(page + len, "%d ", IS_SET(xpd->hookstate, 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
++	len += sprintf(page + len, "\nreadchunk: ");
++	for(i = 0; i < channels; i++) {
++		struct zt_chan	*chans = xpd->span.chans;
++		int j;
++
++		len += sprintf(page + len, "\n\tport %2d> ", i);
++		for(j = 0; j < ZT_CHUNKSIZE; j++) {
++			len += sprintf(page + len, "%02X ", chans[i].readchunk[j]);
++		}
++	}
++#endif
++	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));
++		}
++	}
++#if 0
++	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
++
++static int span_init(xpd_t *xpd);
++
++xpd_t *xpd_new(xbus_t *xbus, int xpd_num, xpd_type_t type)
++{
++	int ret;
++	unsigned long	flags;
++	int channels;
++	xpd_t *xpd = NULL;
++
++	spin_lock_irqsave(&xbus->lock, flags);
++	INFO("New XPD #%d detected on xbus %s\n", xpd_num, xbus->busname);
++	if(!VALID_XPD_NUM(xpd_num)) {
++		ERR("xpd_new: illegal xpd id = %d\n", xpd_num);
++		goto err;
++	}
++	if((xpd = kmalloc(sizeof(xpd_t), GFP_ATOMIC)) == NULL) {
++		ERR("xpd_new: Unable to allocate memory for xpd\n");
++		goto err;
++	}
++	memset(xpd, 0, sizeof(xpd_t));
++
++	spin_lock_init(&xpd->lock);
++	xpd->xbus = xbus;
++	xpd->id = xpd_num;
++	xpd->chans = NULL;
++	xpd->state = XPD_STATE_OFF;
++	snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%d", xpd_num);
++	xpd->hookstate = 0x0;	/* ONHOOK */
++	xpd->type = type;
++	xpd->enabled_chans = enabled_channels[xpd_num];
++
++	switch(xpd->type) {
++		case XPD_TYPE_FXS:
++			channels = xpd->channels = min(8, CHANNELS_PERXPD);
++			xpd->direction = TO_PHONE;
++			break;
++		case XPD_TYPE_FXO:
++			channels = xpd->channels = min(8, CHANNELS_PERXPD);
++			xpd->direction = TO_TRUNK;
++			break;
++#if 0
++		case XPD_TYPE_E1:
++			channels = xpd->channels = CHANNELS_PERXPD;
++			break;
++#endif
++		case XPD_TYPE_NOMODULE:
++		default:
++			ERR("%s: bad xpd->type=0x%X\n", __FUNCTION__, xpd->type);
++			goto err;
++	}
++
++	INIT_WORK(&xpd->xpd_post_init, xpd_initialize, xpd);
++
++	/* 8 channels, double buffer, Read/Write */
++	int need = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2 * 2;
++	if((xpd->writechunk = kmalloc(need, GFP_ATOMIC)) == NULL) {
++		ERR("xpd_new: Unable to allocate memory for writechunks\n");
++		goto err;
++	}
++	/* Initialize Write/Buffers to all blank data */
++	memset((void *)xpd->writechunk, 0x00, need);
++	xpd->readchunk = xpd->writechunk + ZT_CHUNKSIZE * CHANNELS_PERXPD * 2;
++
++	if((ret = span_init(xpd)) < 0) {
++		ERR("Unable to initialize new span\n");
++		goto err;
++	}
++	DBG("Queueing xpd_post_init for xpd %d\n", xpd->id);
++	if(!queue_work(xpd_init_workqueue, &xpd->xpd_post_init)) {
++		ERR("Failed to queue xpd_post_init work\n");
++		goto err;
++	}
++
++
++#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
++	spin_unlock_irqrestore(&xbus->lock, flags);
++	return xpd;
++err:
++	spin_unlock_irqrestore(&xbus->lock, flags);
++	if(xpd->writechunk)
++		kfree((void *)xpd->writechunk);
++	if(xpd)
++		kfree(xpd);
++	return NULL;
++}
++
++static void xpd_remove(xbus_t *xbus, xpd_t *xpd)
++{
++	BUG_ON(xpd == NULL);
++	BUG_ON(xbus == NULL);
++	BUG_ON(xpd->xbus != xbus);	// xpd belong to another xbus
++	INFO("Remove XPD #%d from xbus=%s\n", xpd->id, xbus->busname);
++	xpd->state = XPD_STATE_OFF;
++	if (atomic_read(&xpd->span_registered)) {
++		zt_unregister(&xpd->span);
++		atomic_dec(&xpd->span_registered);
++	}
++	if(xpd->chans) {	/* Already registered */
++		kfree(xpd->chans);
++	}
++#ifdef CONFIG_PROC_FS
++	if(xpd->xpd_proc) {
++		DBG("Removing proc entry %s\n", xpd->xpdname);
++		remove_proc_entry(xpd->xpdname, xbus->procdir);
++	}
++#endif
++	xbus->xpds[xpd->id] = NULL;
++	list_del(&xpd->xpd_list);
++	xbus->num_xpds--;
++	cancel_delayed_work(&xpd->xpd_post_init);
++	kfree((void *)xpd->writechunk);
++	kfree(xpd);
++}
++
++void update_xpd_status(xpd_t *xpd, int alarm_flag)
++{
++	struct zt_span *span = &xpd->span;
++
++	switch (alarm_flag) {
++		case ZT_ALARM_NONE:
++			xpd->state = XPD_STATE_ACTIVE;
++			xpd->last_response = jiffies;
++			break;
++		case ZT_ALARM_RED:
++			xpd->state = XPD_STATE_NOREPLY;
++			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 xpp_check_hookstate(xpd_t *xpd, xpp_line_t fxs_off_hook)
++{
++	int	i;
++	unsigned long	flags;
++
++	if(xpd->direction != TO_PHONE) {
++		ERR("%s: %s: Only FXS can report hookstate changes\n", __FUNCTION__, xpd->xpdname);
++		return;
++	}
++	spin_lock_irqsave(&xpd->lock, flags);
++	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++) {
++		struct zt_chan *chan = &xpd->span.chans[i];
++		if(IS_SET(fxs_off_hook, i) && !IS_SET(xpd->hookstate, i)) {		// OFFHOOK
++			DBG("OFFHOOK: channo=%d\n", chan->channo);
++			xpd->ringing[i] = 0;
++			BIT_SET(xpd->hookstate, i);
++			zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
++			CALL_PROTO(CHAN_POWER, xpd->xbus, xpd, (1 << i), 0);		// Power down (prevent overheating!!!)
++			CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 1);
++		} else if(!IS_SET(fxs_off_hook, i) && IS_SET(xpd->hookstate, i)) {	// ONHOOK
++			DBG("ONHOOK channo=%d\n", chan->channo);
++			xpd->ringing[i] = 0;
++			BIT_CLR(xpd->hookstate, i);
++			zt_hooksig(chan, ZT_RXSIG_ONHOOK);
++			CALL_PROTO(CHAN_POWER, xpd->xbus, xpd, (1 << i), 0);		// Power down (prevent overheating!!!)
++			CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 0);
++		}
++	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
++}
++
++static void xpd_blink_leds(xpd_t *xpd)
++{
++	int		i;
++	unsigned long	flags;
++
++	BUG_ON(!xpd);
++
++	for(i = 0; i < xpd->channels; i++) {
++		spin_lock_irqsave(&xpd->lock, flags);
++		if(xpd->ringing[i]) {
++			// led state is toggled
++			if((xpd->timer_count % LED_BLINK_PERIOD) == 0) {
++				DBG("%s pos=%d =%d led_on=%d\n", xpd->xpdname, i, xpd->ringing[i], xpd->led_on[i]);
++				if(xpd->ringing[i] && xpd->led_on[i]) {
++					CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 1);
++				} else {
++					CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 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);
++
++	if(xpd->direction != TO_TRUNK && ((bug_counter++ % 1000) == 0)) {
++		ERR("%s: %s: Only FXO can report ring changes\n", __FUNCTION__, xpd->xpdname);
++		return;
++	}
++
++	/*
++	 * Ring detect logic:
++	 * 	fxo_power is toggled
++	 */
++	for(i = 0; i < xpd->channels; i++) {
++		spin_lock_irqsave(&xpd->lock, flags);
++		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;
++			}
++		}
++		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;
++
++	NOTICE("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: HW=%s bus_type=%d connected=%s\n",
++			xbus->busname,
++			xbus->busdesc,
++			xbus->bus_type,
++			(xbus->hardware_exists) ? "yes" : "no"
++		      );
++	len += sprintf(page + len, "open_counter=%d packet_count=%d\n",
++			xbus->open_counter,
++			atomic_read(&xbus->packet_counter)
++		      );
++	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");
++	}
++	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;
++
++}
++
++#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(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;
++			}
++			DBG("  XPD #%d STATE = %d\n", id, xpd->state);
++			if(xpd->state != XPD_STATE_ACTIVE) {
++				DBG("  Skipping XPD #%d not in ACTIVE state\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(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);
++}
++
++
++/*
++ * 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;
++	}
++}
++
++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) {
++			if(xpd->id != i) {
++				ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i);
++				continue;
++			}
++			update_xpd_status(xpd, ZT_ALARM_NOTOPEN);
++		}
++	}
++	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->procdir) {
++		if(xbus->procsummary) {
++			DBG("Removing proc entry " PROC_XBUS_SUMMARY ".\n");
++			remove_proc_entry(PROC_XBUS_SUMMARY, xbus->procdir);
++			xbus->procsummary = NULL;
++		}
++		DBG("Removing proc entry %s.\n",xbus->busname);
++		remove_proc_entry(xbus->busname, xpp_procdir);
++		xbus->procdir = 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->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");
++	xbus->num_xpds = 0;
++	INIT_WORK(&xbus->sim_work, process_sim_queue, xbus);
++	INIT_WORK(&xbus->poll_work, process_xbus_poll, xbus);
++	xbus_reset_counters(xbus);
++	simulator_setup(xbus, loopback_xpds);	// Hardware loopback must use simulator
++	simulator_setup(xbus, softloop_xpds);	// Add the soft loopback to the simulated set
++#ifdef CONFIG_PROC_FS
++	DBG("Creating xbus proc directory %s.\n",xbus->busname);
++	xbus->procdir = proc_mkdir(xbus->busname, xpp_procdir);
++	if(!xbus->procdir) {
++		ERR("Failed to create proc directory for xbus %s\n", xbus->busname);
++		err = -EIO;
++		goto nobus;
++	}
++	xbus->procsummary = create_proc_read_entry(PROC_XBUS_SUMMARY, 0644, xbus->procdir,
++			xbus_read_proc, xbus);
++	if (!xbus->procsummary) {
++		ERR("Failed to create proc read entry for xbus %s\n", 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;
++
++	DBG("\n");
++	BUG_ON(!xbus);
++	spin_lock_irqsave(&xbuses_lock, flags);
++	BUG_ON(xbus != xbuses_array[xbus->num]);
++	xbuses_array[xbus->num] = NULL;
++	bus_count--;
++	spin_unlock_irqrestore(&xbuses_lock, flags);
++	if(xbus->sim_workqueue) {
++		cancel_delayed_work(&xbus->sim_work);
++		cancel_delayed_work(&xbus->poll_work);
++	}
++	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) {
++			if(xpd->id != i) {
++				ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i);
++				continue;
++			}
++			DBG("  Removing xpd id=%d\n", xpd->id);
++			xpd_remove(xbus, xpd);
++		}
++		xbus->xpds[i] = NULL;
++	}
++	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);
++	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) == 0)
++//		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, 2);
++		}
++		w += ZT_CHUNKSIZE;
++	}
++	ret = CALL_PROTO(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 */
++		0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67,	/* 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 1
++	/* 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;
++}
++
++static 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++;
++	DBG("chan=%d (open_counter=%d)\n", chan->chanpos, xbus->open_counter);
++	spin_unlock_irqrestore(&xbus->lock, flags);
++	return 0;
++}
++
++static 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--;
++	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;
++}
++
++static 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;
++		DBG("xpd=%d: ZT_ONHOOKTRANSFER (%d millis) chan=%d\n", xpd->id, x, pos);
++		break;
++	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) {
++			// An FXS line: ZT_START will be treated as ZT_RING
++			DBG("Got ZT_START for FXS channel %d, treated as ZT_RING\n", pos);
++			//hookstate = ZT_TXSIG_RING;
++
++			xpd->ringing[pos] = RINGS_NUM*2;
++			DBG("ZT_RING %s ringing=%d\n", chan->name, xpd->ringing[pos]);
++			ret = CALL_PROTO(RING, xbus, xpd->id, (1 << pos));
++			if(ret) {
++				DBG("ZT_RING Failed: ret=0x%02X\n", ret);
++				return ret;
++			}
++			return ret;
++		} else {	/* TO_TRUNK */
++			// 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;
++			ret = CALL_PROTO(SETHOOK, xbus, xpd->id, xpd->hookstate);
++			if(ret) {
++				DBG("ZT_TXSIG_OFFHOOK Failed: ret=0x%02X\n", ret);
++				break;
++			}
++			CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 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;
++			BIT_CLR(xpd->hookstate, pos);
++			ret = CALL_PROTO(SETHOOK, xbus, xpd->id, xpd->hookstate);
++			if(ret) {
++				DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret);
++				break;
++			}
++			CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 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_PROTO(SETHOOK, xbus, xpd->id, xpd->hookstate);
++	//if(ret) {
++	//	DBG("ZT_TXSIG_START Failed: ret=0x%02X\n", ret);
++	//}
++	return ret;
++}
++#endif
++
++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(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_PROTO(RING, xbus, xpd, pos, 0);			// RING off
++#endif
++				ret = CALL_PROTO(CHAN_POWER, xbus, xpd, (1 << 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_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:
++			if(xpd->direction == TO_PHONE) {
++				DBG("ZT_OFFHOOK: %s hookstate=0x%04X -- ignoring (FXS)\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_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]);
++			if(xpd->direction == TO_PHONE) {
++				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;
++}
++
++/* 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 */
++static int xpp_maint(struct zt_span *span, int cmd)
++{
++	xpd_t		*xpd = span->pvt;
++	int		ret = 0;
++	char		loopback_data[] = "THE-QUICK-BROWN-FOX-JUMPED-OVER-THE-LAZY-DOG";
++
++	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_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, 1, 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_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, 1, 1);		// FIXME: Find usage for extra LED
++			CALL_PROTO(LOOPBACK_AX, xpd->xbus, xpd, loopback_data, ARRAY_SIZE(loopback_data));
++			break;
++		case ZT_MAINT_LOOPDOWN:
++			printk("XXX Send loopdown code XXX\n");
++			CALL_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, 1, 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
++
++#if 0
++static void dump_xpp(xpd_t *xpd, const char *msg)
++{
++	int i;
++	struct zt_chan *cur_chan;
++	struct zt_span *span = &xpd->span;
++
++	DBG("xpd=%p: %s\n", xpd, msg);
++	DBG("    Span name is: %s\n", span->name);
++	DBG("    pvt: %p\n", span->pvt);
++	for(i = 0; i < span->channels; i++) {
++		cur_chan = &(span->chans[i]);
++		DBG("    Channel %d (%p) name ='%s'\n",
++				cur_chan->channo, cur_chan, cur_chan->name);
++	}
++	DBG("    Parallel device: %p\n", xpd->pdev);
++}
++#endif
++
++static int span_init(xpd_t *xpd)
++{
++	struct zt_chan	*cur_chan;
++	struct zt_span	*span;
++	xbus_t		*xbus;
++	int		sigfxs;
++	int		i;
++	int		cn;
++
++	if(!xpd)
++		return -EINVAL;
++	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);
++
++	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 (sigfxs=%d)\n", i, sigfxs);
++		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);
++	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;
++	unsigned long stamp = jiffies;
++
++	spin_lock_irqsave(&xbuses_lock, flags);
++	len += sprintf(page + len, "Global:\n");
++	len += sprintf(page + len, "\tjiffies=%ld\n", stamp);
++	len += sprintf(page + len, "\txpacket_count=%d\n", atomic_read(&xpacket_count));
++	len += sprintf(page + len, "\tbus_count=%d\n", bus_count);
++	for(i = 0; i < MAX_BUSES; i++) {
++		xbus_t *xbus = xbuses_array[i];
++
++		if(xbus) {
++			len += sprintf(page + len, "%s: HW=%s bus_type=%d connected=%s\n",
++					xbus->busname,
++					xbus->busdesc,
++					xbus->bus_type,
++					(xbus->hardware_exists) ? "yes" : "no"
++				      );
++		}
++	}
++	len += sprintf(page + len, "<-- len=%d\n", len);
++	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;
++
++	if(busnum >= MAX_BUSES)
++		return -EINVAL;
++	spin_lock_irqsave(&xbuses_lock, flags);
++	xbus = xbuses_array[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;
++}
++
++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(xpp_packet_r_t)) {
++		DBG("count=%d, partial write...\n", count);
++		count = sizeof(xpp_packet_r_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;
++}
++
++static struct file_operations xpp_fops = {
++	.owner		= THIS_MODULE,
++	.llseek		= no_llseek,
++	.write		= xpp_sys_write,
++	.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_XBUSES, xpp_procdir);
++	if(xpp_procdir) {
++		remove_proc_entry(PROC_DIR, NULL);
++	}
++#endif
++	if (xpd_init_workqueue) {
++		flush_workqueue(xpd_init_workqueue);
++		destroy_workqueue(xpd_init_workqueue);
++		xpd_init_workqueue=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_read_entry(PROC_XBUSES, 0644, xpp_procdir, xpp_zap_read_proc, 0);
++	if (!ent) {
++		do_cleanup();
++		return -EFAULT;
++	}
++#endif
++	xpd_init_workqueue = create_singlethread_workqueue("XppReg");
++	if(!xpd_init_workqueue) {
++		ERR("Failed to create registration 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);
++	xpp_timer.function = xpp_timer_tick;
++	xpp_timer.data = 0;
++	xpp_timer.expires = jiffies + 1;	/* Must be 1KHz rate */
++	add_timer(&xpp_timer);
++	return 0;
++}
++
++void __exit xpp_zap_cleanup(void)
++{
++	unsigned long flags;
++
++	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(xpd_new);
++EXPORT_SYMBOL(xbus_activate);
++EXPORT_SYMBOL(xbus_deactivate);
++EXPORT_SYMBOL(xpd_of);
++EXPORT_SYMBOL(xbus_new);
++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(update_xpd_status);
++EXPORT_SYMBOL(xpp_check_hookstate);
++
++MODULE_DESCRIPTION("XPP Zaptel Driver");
++MODULE_AUTHOR("Oron Peled <oron at actcom.co.il>");
++MODULE_LICENSE("GPL");
++MODULE_VERSION("$Id: xpp_zap.c 124 2005-11-09 18:42:02Z oron $");
++
++module_init(xpp_zap_init);
++module_exit(xpp_zap_cleanup);
+diff -urNad trunk/xpp/xpp_zap.h /tmp/dpep.oAj2YR/trunk/xpp/xpp_zap.h
+--- trunk/xpp/xpp_zap.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_zap.h	2005-11-03 15:01:15.000000000 +0200
+@@ -0,0 +1,36 @@
++#ifndef	XPP_ZAP_H
++#define	XPP_ZAP_H
++
++#include "xpd.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 update_xpd_status(xpd_t *xpd, int alarm_flag);
++void xpp_check_hookstate(xpd_t *xpd, xpp_line_t fxs_off_hook);
++xpd_t *xpd_of(xbus_t *xbus, int xpd_num);
++xpd_t *xpd_new(xbus_t *xbus, int xpd_num, xpd_type_t type);
++void fill_beep(u_char *buf, int duration);
++
++#include <linux/proc_fs.h>
++
++#ifdef CONFIG_PROC_FS
++extern struct proc_dir_entry *xpp_procdir;
++#endif
++
++// Number of rings our simulated phone will ring:
++#define RINGS_NUM 3
++
++#endif	/* XPP_ZAP_H */
+diff -urNad trunk/xpp/zap_debug.c /tmp/dpep.oAj2YR/trunk/xpp/zap_debug.c
+--- trunk/xpp/zap_debug.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/zap_debug.c	2005-11-10 04:05:18.000000000 +0200
+@@ -0,0 +1,136 @@
++/*
++ * Written by Oron Peled <oron at 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 char rcsid[] = "$Id: zap_debug.c 124 2005-11-09 18:42:02Z oron $";
++
++#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 -urNad trunk/xpp/zap_debug.h /tmp/dpep.oAj2YR/trunk/xpp/zap_debug.h
+--- trunk/xpp/zap_debug.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.oAj2YR/trunk/xpp/zap_debug.h	2005-11-10 04:05:18.000000000 +0200
+@@ -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 */


Property changes on: zaptel/trunk/debian/patches/xpp.dpatch
___________________________________________________________________
Name: svn:executable
   + *

Modified: zaptel/trunk/debian/rules
===================================================================
--- zaptel/trunk/debian/rules	2005-11-18 02:20:21 UTC (rev 1003)
+++ zaptel/trunk/debian/rules	2005-11-18 02:29:28 UTC (rev 1004)
@@ -120,9 +120,9 @@
 	# driver source code
 	mkdir -p $(TARDIR)/debian
 	cp Makefile *.c *.h *.rbt $(TARDIR)/
-ifeq (1,$(USE_BRISTUFF))
-	cp -r zaphfc qozap cwain $(TARDIR)/
-endif
+	for dir in cwain qozap xpp zaphfc; do \
+	  if [ -d $$dir ]; then cp -r $$dir $(TARDIR); fi; \
+	done
 	dh_install -i zaptel.h torisa.h usr/include/linux/
 	
 	# Packaging infrastructure
@@ -175,7 +175,8 @@
 	#install -m644 debian/$(PREFIX).modprobe.d debian/$(PREFIX)/etc/modprobe.d/$(PREFIX)
 
 	dh_installdocs -a
-	dh_installman -a doc/*
+	# TODO: this installs a man page for torisatool, which we don't build
+	dh_installman -a doc/* debian/genzaptelconf.8
 	
 	# should be removed, eventually. Still left for compatibility
 	dh_installinit --update-rcd-params="defaults 15 30"




More information about the Pkg-voip-commits mailing list