浏览代码

Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1623 commits)
  netxen: update copyright
  netxen: fix tx timeout recovery
  netxen: fix file firmware leak
  netxen: improve pci memory access
  netxen: change firmware write size
  tg3: Fix return ring size breakage
  netxen: build fix for INET=n
  cdc-phonet: autoconfigure Phonet address
  Phonet: back-end for autoconfigured addresses
  Phonet: fix netlink address dump error handling
  ipv6: Add IFA_F_DADFAILED flag
  net: Add DEVTYPE support for Ethernet based devices
  mv643xx_eth.c: remove unused txq_set_wrr()
  ucc_geth: Fix hangs after switching from full to half duplex
  ucc_geth: Rearrange some code to avoid forward declarations
  phy/marvell: Make non-aneg speed/duplex forcing work for 88E1111 PHYs
  drivers/net/phy: introduce missing kfree
  drivers/net/wan: introduce missing kfree
  net: force bridge module(s) to be GPL
  Subject: [PATCH] appletalk: Fix skb leak when ipddp interface is not loaded
  ...

Fixed up trivial conflicts:

 - arch/x86/include/asm/socket.h

   converted to <asm-generic/socket.h> in the x86 tree.  The generic
   header has the same new #define's, so that works out fine.

 - drivers/net/tun.c

   fix conflict between 89f56d1e9 ("tun: reuse struct sock fields") that
   switched over to using 'tun->socket.sk' instead of the redundantly
   available (and thus removed) 'tun->sk', and 2b980dbd ("lsm: Add hooks
   to the TUN driver") which added a new 'tun->sk' use.

   Noted in 'next' by Stephen Rothwell.
Linus Torvalds 16 年之前
父节点
当前提交
d7e9660ad9
共有 100 个文件被更改,包括 13153 次插入431 次删除
  1. 2 0
      Documentation/00-INDEX
  2. 119 0
      Documentation/btmrvl.txt
  3. 5 0
      Documentation/connector/Makefile
  4. 18 15
      Documentation/connector/cn_test.c
  5. 61 58
      Documentation/connector/connector.txt
  6. 53 9
      Documentation/connector/ucon.c
  7. 29 25
      Documentation/feature-removal-schedule.txt
  8. 5 0
      Documentation/kernel-parameters.txt
  9. 2 0
      Documentation/networking/00-INDEX
  10. 12 8
      Documentation/networking/ieee802154.txt
  11. 36 11
      Documentation/networking/ip-sysctl.txt
  12. 39 13
      MAINTAINERS
  13. 2 0
      arch/alpha/include/asm/socket.h
  14. 3 0
      arch/arm/include/asm/socket.h
  15. 1 0
      arch/arm/mach-ixp4xx/common.c
  16. 3 0
      arch/avr32/include/asm/socket.h
  17. 3 0
      arch/cris/include/asm/socket.h
  18. 3 0
      arch/frv/include/asm/socket.h
  19. 3 0
      arch/h8300/include/asm/socket.h
  20. 1 1
      arch/ia64/hp/sim/simeth.c
  21. 3 0
      arch/ia64/include/asm/socket.h
  22. 3 0
      arch/m32r/include/asm/socket.h
  23. 3 0
      arch/m68k/include/asm/socket.h
  24. 15 0
      arch/mips/ar7/platform.c
  25. 2 0
      arch/mips/include/asm/socket.h
  26. 1 1
      arch/mips/kernel/scall64-n32.S
  27. 2 2
      arch/mips/kernel/scall64-o32.S
  28. 3 0
      arch/mn10300/include/asm/socket.h
  29. 2 0
      arch/parisc/include/asm/socket.h
  30. 1 0
      arch/powerpc/include/asm/qe.h
  31. 3 0
      arch/powerpc/include/asm/socket.h
  32. 13 0
      arch/powerpc/sysdev/qe_lib/qe.c
  33. 3 0
      arch/s390/include/asm/socket.h
  34. 3 0
      arch/sh/include/asm/sh_eth.h
  35. 3 0
      arch/sparc/include/asm/socket.h
  36. 1 1
      arch/sparc/kernel/sys32.S
  37. 2 2
      arch/um/drivers/net_kern.c
  38. 3 0
      arch/xtensa/include/asm/socket.h
  39. 1 1
      arch/xtensa/platforms/iss/network.c
  40. 2 2
      drivers/atm/horizon.c
  41. 1 1
      drivers/atm/solos-pci.c
  42. 25 0
      drivers/bluetooth/Kconfig
  43. 6 0
      drivers/bluetooth/Makefile
  44. 432 0
      drivers/bluetooth/btmrvl_debugfs.c
  45. 139 0
      drivers/bluetooth/btmrvl_drv.h
  46. 624 0
      drivers/bluetooth/btmrvl_main.c
  47. 1003 0
      drivers/bluetooth/btmrvl_sdio.c
  48. 108 0
      drivers/bluetooth/btmrvl_sdio.h
  49. 175 23
      drivers/bluetooth/btusb.c
  50. 2 1
      drivers/bluetooth/hci_bcsp.c
  51. 3 4
      drivers/char/pcmcia/synclink_cs.c
  52. 3 4
      drivers/char/synclink.c
  53. 3 4
      drivers/char/synclink_gt.c
  54. 3 4
      drivers/char/synclinkmp.c
  55. 1 2
      drivers/connector/cn_proc.c
  56. 5 2
      drivers/connector/cn_queue.c
  57. 3 3
      drivers/connector/connector.c
  58. 2 4
      drivers/firewire/net.c
  59. 6 13
      drivers/ieee1394/eth1394.c
  60. 175 16
      drivers/ieee802154/fakehard.c
  61. 0 1
      drivers/infiniband/hw/amso1100/c2.c
  62. 1 1
      drivers/infiniband/hw/nes/nes_hw.c
  63. 1 1
      drivers/infiniband/hw/nes/nes_nic.c
  64. 0 1
      drivers/infiniband/ulp/ipoib/ipoib_cm.c
  65. 0 1
      drivers/infiniband/ulp/ipoib/ipoib_ib.c
  66. 2 4
      drivers/isdn/Kconfig
  67. 1 2
      drivers/isdn/act2000/capi.c
  68. 15 16
      drivers/isdn/act2000/module.c
  69. 1 3
      drivers/isdn/hardware/eicon/message.c
  70. 1 2
      drivers/isdn/hardware/eicon/os_4bri.c
  71. 51 0
      drivers/isdn/hardware/mISDN/Kconfig
  72. 8 0
      drivers/isdn/hardware/mISDN/Makefile
  73. 1152 0
      drivers/isdn/hardware/mISDN/avmfritz.c
  74. 5 18
      drivers/isdn/hardware/mISDN/hfcmulti.c
  75. 1 15
      drivers/isdn/hardware/mISDN/hfcpci.c
  76. 1 15
      drivers/isdn/hardware/mISDN/hfcsusb.c
  77. 109 0
      drivers/isdn/hardware/mISDN/iohelper.h
  78. 405 0
      drivers/isdn/hardware/mISDN/ipac.h
  79. 269 0
      drivers/isdn/hardware/mISDN/isar.h
  80. 1178 0
      drivers/isdn/hardware/mISDN/mISDNinfineon.c
  81. 1655 0
      drivers/isdn/hardware/mISDN/mISDNipac.c
  82. 1726 0
      drivers/isdn/hardware/mISDN/mISDNisar.c
  83. 1156 0
      drivers/isdn/hardware/mISDN/netjet.c
  84. 58 0
      drivers/isdn/hardware/mISDN/netjet.h
  85. 526 0
      drivers/isdn/hardware/mISDN/speedfax.c
  86. 1440 0
      drivers/isdn/hardware/mISDN/w6692.c
  87. 190 0
      drivers/isdn/hardware/mISDN/w6692.h
  88. 1 5
      drivers/isdn/hisax/Kconfig
  89. 0 4
      drivers/isdn/hisax/Makefile
  90. 0 2
      drivers/isdn/hisax/amd7930_fn.c
  91. 1 3
      drivers/isdn/hisax/callc.c
  92. 0 2
      drivers/isdn/hisax/hfc_pci.c
  93. 0 2
      drivers/isdn/hisax/hfc_sx.c
  94. 0 2
      drivers/isdn/hisax/icc.c
  95. 0 2
      drivers/isdn/hisax/isac.c
  96. 0 70
      drivers/isdn/hisax/isdnhdlc.h
  97. 3 9
      drivers/isdn/hisax/isdnl1.c
  98. 1 3
      drivers/isdn/hisax/isdnl2.c
  99. 1 3
      drivers/isdn/hisax/isdnl3.c
  100. 6 14
      drivers/isdn/hisax/l3_1tr6.c

+ 2 - 0
Documentation/00-INDEX

@@ -82,6 +82,8 @@ block/
 	- info on the Block I/O (BIO) layer.
 blockdev/
 	- info on block devices & drivers
+btmrvl.txt
+	- info on Marvell Bluetooth driver usage.
 cachetlb.txt
 	- describes the cache/TLB flushing interfaces Linux uses.
 cdrom/

+ 119 - 0
Documentation/btmrvl.txt

@@ -0,0 +1,119 @@
+=======================================================================
+		README for btmrvl driver
+=======================================================================
+
+
+All commands are used via debugfs interface.
+
+=====================
+Set/get driver configurations:
+
+Path:	/debug/btmrvl/config/
+
+gpiogap=[n]
+hscfgcmd
+	These commands are used to configure the host sleep parameters.
+	bit 8:0  -- Gap
+	bit 16:8 -- GPIO
+
+	where GPIO is the pin number of GPIO used to wake up the host.
+	It could be any valid GPIO pin# (e.g. 0-7) or 0xff (SDIO interface
+	wakeup will be used instead).
+
+	where Gap is the gap in milli seconds between wakeup signal and
+	wakeup event, or 0xff for special host sleep setting.
+
+	Usage:
+		# Use SDIO interface to wake up the host and set GAP to 0x80:
+		echo 0xff80 > /debug/btmrvl/config/gpiogap
+		echo 1 > /debug/btmrvl/config/hscfgcmd
+
+		# Use GPIO pin #3 to wake up the host and set GAP to 0xff:
+		echo 0x03ff >  /debug/btmrvl/config/gpiogap
+		echo 1 > /debug/btmrvl/config/hscfgcmd
+
+psmode=[n]
+pscmd
+	These commands are used to enable/disable auto sleep mode
+
+	where the option is:
+			1 	-- Enable auto sleep mode
+			0 	-- Disable auto sleep mode
+
+	Usage:
+		# Enable auto sleep mode
+		echo 1 > /debug/btmrvl/config/psmode
+		echo 1 > /debug/btmrvl/config/pscmd
+
+		# Disable auto sleep mode
+		echo 0 > /debug/btmrvl/config/psmode
+		echo 1 > /debug/btmrvl/config/pscmd
+
+
+hsmode=[n]
+hscmd
+	These commands are used to enable host sleep or wake up firmware
+
+	where the option is:
+			1	-- Enable host sleep
+			0	-- Wake up firmware
+
+	Usage:
+		# Enable host sleep
+		echo 1 > /debug/btmrvl/config/hsmode
+		echo 1 > /debug/btmrvl/config/hscmd
+
+		# Wake up firmware
+		echo 0 > /debug/btmrvl/config/hsmode
+		echo 1 > /debug/btmrvl/config/hscmd
+
+
+======================
+Get driver status:
+
+Path:	/debug/btmrvl/status/
+
+Usage:
+	cat /debug/btmrvl/status/<args>
+
+where the args are:
+
+curpsmode
+	This command displays current auto sleep status.
+
+psstate
+	This command display the power save state.
+
+hsstate
+	This command display the host sleep state.
+
+txdnldrdy
+	This command displays the value of Tx download ready flag.
+
+
+=====================
+
+Use hcitool to issue raw hci command, refer to hcitool manual
+
+	Usage: Hcitool cmd <ogf> <ocf> [Parameters]
+
+	Interface Control Command
+	hcitool cmd 0x3f 0x5b 0xf5 0x01 0x00    --Enable All interface
+	hcitool cmd 0x3f 0x5b 0xf5 0x01 0x01    --Enable Wlan interface
+	hcitool cmd 0x3f 0x5b 0xf5 0x01 0x02    --Enable BT interface
+	hcitool cmd 0x3f 0x5b 0xf5 0x00 0x00    --Disable All interface
+	hcitool cmd 0x3f 0x5b 0xf5 0x00 0x01    --Disable Wlan interface
+	hcitool cmd 0x3f 0x5b 0xf5 0x00 0x02    --Disable BT interface
+
+=======================================================================
+
+
+SD8688 firmware:
+
+/lib/firmware/sd8688_helper.bin
+/lib/firmware/sd8688.bin
+
+
+The images can be downloaded from:
+
+git.infradead.org/users/dwmw2/linux-firmware.git/libertas/

+ 5 - 0
Documentation/connector/Makefile

@@ -9,3 +9,8 @@ hostprogs-y := ucon
 always := $(hostprogs-y)
 
 HOSTCFLAGS_ucon.o += -I$(objtree)/usr/include
+
+all: modules
+
+modules clean:
+	$(MAKE) -C ../.. SUBDIRS=$(PWD) $@

+ 18 - 15
Documentation/connector/cn_test.c

@@ -19,6 +19,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#define pr_fmt(fmt) "cn_test: " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -27,18 +29,17 @@
 
 #include <linux/connector.h>
 
-static struct cb_id cn_test_id = { 0x123, 0x456 };
+static struct cb_id cn_test_id = { CN_NETLINK_USERS + 3, 0x456 };
 static char cn_test_name[] = "cn_test";
 static struct sock *nls;
 static struct timer_list cn_test_timer;
 
-void cn_test_callback(void *data)
+static void cn_test_callback(struct cn_msg *msg)
 {
-	struct cn_msg *msg = (struct cn_msg *)data;
-
-	printk("%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n",
-	       __func__, jiffies, msg->id.idx, msg->id.val,
-	       msg->seq, msg->ack, msg->len, (char *)msg->data);
+	pr_info("%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n",
+	        __func__, jiffies, msg->id.idx, msg->id.val,
+	        msg->seq, msg->ack, msg->len,
+	        msg->len ? (char *)msg->data : "");
 }
 
 /*
@@ -63,9 +64,7 @@ static int cn_test_want_notify(void)
 
 	skb = alloc_skb(size, GFP_ATOMIC);
 	if (!skb) {
-		printk(KERN_ERR "Failed to allocate new skb with size=%u.\n",
-		       size);
-
+		pr_err("failed to allocate new skb with size=%u\n", size);
 		return -ENOMEM;
 	}
 
@@ -114,12 +113,12 @@ static int cn_test_want_notify(void)
 	//netlink_broadcast(nls, skb, 0, ctl->group, GFP_ATOMIC);
 	netlink_unicast(nls, skb, 0, 0);
 
-	printk(KERN_INFO "Request was sent. Group=0x%x.\n", ctl->group);
+	pr_info("request was sent: group=0x%x\n", ctl->group);
 
 	return 0;
 
 nlmsg_failure:
-	printk(KERN_ERR "Failed to send %u.%u\n", msg->seq, msg->ack);
+	pr_err("failed to send %u.%u\n", msg->seq, msg->ack);
 	kfree_skb(skb);
 	return -EINVAL;
 }
@@ -131,6 +130,8 @@ static void cn_test_timer_func(unsigned long __data)
 	struct cn_msg *m;
 	char data[32];
 
+	pr_debug("%s: timer fired with data %lu\n", __func__, __data);
+
 	m = kzalloc(sizeof(*m) + sizeof(data), GFP_ATOMIC);
 	if (m) {
 
@@ -150,7 +151,7 @@ static void cn_test_timer_func(unsigned long __data)
 
 	cn_test_timer_counter++;
 
-	mod_timer(&cn_test_timer, jiffies + HZ);
+	mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000));
 }
 
 static int cn_test_init(void)
@@ -168,8 +169,10 @@ static int cn_test_init(void)
 	}
 
 	setup_timer(&cn_test_timer, cn_test_timer_func, 0);
-	cn_test_timer.expires = jiffies + HZ;
-	add_timer(&cn_test_timer);
+	mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000));
+
+	pr_info("initialized with id={%u.%u}\n",
+		cn_test_id.idx, cn_test_id.val);
 
 	return 0;
 

+ 61 - 58
Documentation/connector/connector.txt

@@ -5,10 +5,10 @@ Kernel Connector.
 Kernel connector - new netlink based userspace <-> kernel space easy
 to use communication module.
 
-Connector driver adds possibility to connect various agents using
-netlink based network.  One must register callback and
-identifier. When driver receives special netlink message with
-appropriate identifier, appropriate callback will be called.
+The Connector driver makes it easy to connect various agents using a
+netlink based network.  One must register a callback and an identifier.
+When the driver receives a special netlink message with the appropriate
+identifier, the appropriate callback will be called.
 
 From the userspace point of view it's quite straightforward:
 
@@ -17,10 +17,10 @@ From the userspace point of view it's quite straightforward:
 	send();
 	recv();
 
-But if kernelspace want to use full power of such connections, driver
-writer must create special sockets, must know about struct sk_buff
-handling...  Connector allows any kernelspace agents to use netlink
-based networking for inter-process communication in a significantly
+But if kernelspace wants to use the full power of such connections, the
+driver writer must create special sockets, must know about struct sk_buff
+handling, etc...  The Connector driver allows any kernelspace agents to use
+netlink based networking for inter-process communication in a significantly
 easier way:
 
 int cn_add_callback(struct cb_id *id, char *name, void (*callback) (void *));
@@ -32,15 +32,15 @@ struct cb_id
 	__u32			val;
 };
 
-idx and val are unique identifiers which must be registered in
-connector.h for in-kernel usage.  void (*callback) (void *) - is a
-callback function which will be called when message with above idx.val
-will be received by connector core.  Argument for that function must
+idx and val are unique identifiers which must be registered in the
+connector.h header for in-kernel usage.  void (*callback) (void *) is a
+callback function which will be called when a message with above idx.val
+is received by the connector core.  The argument for that function must
 be dereferenced to struct cn_msg *.
 
 struct cn_msg
 {
-	struct cb_id 		id;
+	struct cb_id		id;
 
 	__u32			seq;
 	__u32			ack;
@@ -55,92 +55,95 @@ Connector interfaces.
 
 int cn_add_callback(struct cb_id *id, char *name, void (*callback) (void *));
 
-Registers new callback with connector core.
+ Registers new callback with connector core.
 
-struct cb_id *id 		- unique connector's user identifier.
-			  	  It must be registered in connector.h for legal in-kernel users.
-char *name 			- connector's callback symbolic name.
-void (*callback) (void *)	- connector's callback.
+ struct cb_id *id		- unique connector's user identifier.
+				  It must be registered in connector.h for legal in-kernel users.
+ char *name			- connector's callback symbolic name.
+ void (*callback) (void *)	- connector's callback.
 				  Argument must be dereferenced to struct cn_msg *.
 
+
 void cn_del_callback(struct cb_id *id);
 
-Unregisters new callback with connector core.
+ Unregisters new callback with connector core.
+
+ struct cb_id *id		- unique connector's user identifier.
 
-struct cb_id *id 		- unique connector's user identifier.
 
 int cn_netlink_send(struct cn_msg *msg, u32 __groups, int gfp_mask);
 
-Sends message to the specified groups.  It can be safely called from
-softirq context, but may silently fail under strong memory pressure.
-If there are no listeners for given group -ESRCH can be returned.
+ Sends message to the specified groups.  It can be safely called from
+ softirq context, but may silently fail under strong memory pressure.
+ If there are no listeners for given group -ESRCH can be returned.
 
-struct cn_msg *			- message header(with attached data).
-u32 __group			- destination group.
+ struct cn_msg *		- message header(with attached data).
+ u32 __group			- destination group.
 				  If __group is zero, then appropriate group will
 				  be searched through all registered connector users,
 				  and message will be delivered to the group which was
 				  created for user with the same ID as in msg.
 				  If __group is not zero, then message will be delivered
 				  to the specified group.
-int gfp_mask			- GFP mask.
+ int gfp_mask			- GFP mask.
 
-Note: When registering new callback user, connector core assigns
-netlink group to the user which is equal to it's id.idx.
+ Note: When registering new callback user, connector core assigns
+ netlink group to the user which is equal to it's id.idx.
 
 /*****************************************/
 Protocol description.
 /*****************************************/
 
-Current offers transport layer with fixed header.  Recommended
-protocol which uses such header is following:
+The current framework offers a transport layer with fixed headers.  The
+recommended protocol which uses such a header is as following:
 
 msg->seq and msg->ack are used to determine message genealogy.  When
-someone sends message it puts there locally unique sequence and random
-acknowledge numbers.  Sequence number may be copied into
+someone sends a message, they use a locally unique sequence and random
+acknowledge number.  The sequence number may be copied into
 nlmsghdr->nlmsg_seq too.
 
-Sequence number is incremented with each message to be sent.
+The sequence number is incremented with each message sent.
 
-If we expect reply to our message, then sequence number in received
-message MUST be the same as in original message, and acknowledge
-number MUST be the same + 1.
+If you expect a reply to the message, then the sequence number in the
+received message MUST be the same as in the original message, and the
+acknowledge number MUST be the same + 1.
 
-If we receive message and it's sequence number is not equal to one we
-are expecting, then it is new message.  If we receive message and it's
-sequence number is the same as one we are expecting, but it's
-acknowledge is not equal acknowledge number in original message + 1,
-then it is new message.
+If we receive a message and its sequence number is not equal to one we
+are expecting, then it is a new message.  If we receive a message and
+its sequence number is the same as one we are expecting, but its
+acknowledge is not equal to the acknowledge number in the original
+message + 1, then it is a new message.
 
-Obviously, protocol header contains above id.
+Obviously, the protocol header contains the above id.
 
-connector allows event notification in the following form: kernel
+The connector allows event notification in the following form: kernel
 driver or userspace process can ask connector to notify it when
-selected id's will be turned on or off(registered or unregistered it's
-callback). It is done by sending special command to connector
-driver(it also registers itself with id={-1, -1}).
+selected ids will be turned on or off (registered or unregistered its
+callback).  It is done by sending a special command to the connector
+driver (it also registers itself with id={-1, -1}).
 
-As example of usage Documentation/connector now contains cn_test.c -
-testing module which uses connector to request notification and to
-send messages.
+As example of this usage can be found in the cn_test.c module which
+uses the connector to request notification and to send messages.
 
 /*****************************************/
 Reliability.
 /*****************************************/
 
-Netlink itself is not reliable protocol, that means that messages can
+Netlink itself is not a reliable protocol.  That means that messages can
 be lost due to memory pressure or process' receiving queue overflowed,
-so caller is warned must be prepared. That is why struct cn_msg [main
-connector's message header] contains u32 seq and u32 ack fields.
+so caller is warned that it must be prepared.  That is why the struct
+cn_msg [main connector's message header] contains u32 seq and u32 ack
+fields.
 
 /*****************************************/
 Userspace usage.
 /*****************************************/
+
 2.6.14 has a new netlink socket implementation, which by default does not
-allow to send data to netlink groups other than 1.
-So, if to use netlink socket (for example using connector) 
-with different group number userspace application must subscribe to 
-that group. It can be achieved by following pseudocode:
+allow people to send data to netlink groups other than 1.
+So, if you wish to use a netlink socket (for example using connector)
+with a different group number, the userspace application must subscribe to
+that group first.  It can be achieved by the following pseudocode:
 
 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
 
@@ -160,8 +163,8 @@ if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
 }
 
 Where 270 above is SOL_NETLINK, and 1 is a NETLINK_ADD_MEMBERSHIP socket
-option. To drop multicast subscription one should call above socket option
-with NETLINK_DROP_MEMBERSHIP parameter which is defined as 0.
+option.  To drop a multicast subscription, one should call the above socket
+option with the NETLINK_DROP_MEMBERSHIP parameter which is defined as 0.
 
 2.6.14 netlink code only allows to select a group which is less or equal to
 the maximum group number, which is used at netlink_kernel_create() time.

+ 53 - 9
Documentation/connector/ucon.c

@@ -30,18 +30,24 @@
 
 #include <arpa/inet.h>
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
 #include <time.h>
+#include <getopt.h>
 
 #include <linux/connector.h>
 
 #define DEBUG
 #define NETLINK_CONNECTOR 	11
 
+/* Hopefully your userspace connector.h matches this kernel */
+#define CN_TEST_IDX		CN_NETLINK_USERS + 3
+#define CN_TEST_VAL		0x456
+
 #ifdef DEBUG
 #define ulog(f, a...) fprintf(stdout, f, ##a)
 #else
@@ -83,6 +89,25 @@ static int netlink_send(int s, struct cn_msg *msg)
 	return err;
 }
 
+static void usage(void)
+{
+	printf(
+		"Usage: ucon [options] [output file]\n"
+		"\n"
+		"\t-h\tthis help screen\n"
+		"\t-s\tsend buffers to the test module\n"
+		"\n"
+		"The default behavior of ucon is to subscribe to the test module\n"
+		"and wait for state messages.  Any ones received are dumped to the\n"
+		"specified output file (or stdout).  The test module is assumed to\n"
+		"have an id of {%u.%u}\n"
+		"\n"
+		"If you get no output, then verify the cn_test module id matches\n"
+		"the expected id above.\n"
+		, CN_TEST_IDX, CN_TEST_VAL
+	);
+}
+
 int main(int argc, char *argv[])
 {
 	int s;
@@ -94,17 +119,34 @@ int main(int argc, char *argv[])
 	FILE *out;
 	time_t tm;
 	struct pollfd pfd;
+	bool send_msgs = false;
 
-	if (argc < 2)
-		out = stdout;
-	else {
-		out = fopen(argv[1], "a+");
+	while ((s = getopt(argc, argv, "hs")) != -1) {
+		switch (s) {
+		case 's':
+			send_msgs = true;
+			break;
+
+		case 'h':
+			usage();
+			return 0;
+
+		default:
+			/* getopt() outputs an error for us */
+			usage();
+			return 1;
+		}
+	}
+
+	if (argc != optind) {
+		out = fopen(argv[optind], "a+");
 		if (!out) {
 			ulog("Unable to open %s for writing: %s\n",
 				argv[1], strerror(errno));
 			out = stdout;
 		}
-	}
+	} else
+		out = stdout;
 
 	memset(buf, 0, sizeof(buf));
 
@@ -115,9 +157,11 @@ int main(int argc, char *argv[])
 	}
 
 	l_local.nl_family = AF_NETLINK;
-	l_local.nl_groups = 0x123; /* bitmask of requested groups */
+	l_local.nl_groups = -1; /* bitmask of requested groups */
 	l_local.nl_pid = 0;
 
+	ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL);
+
 	if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
 		perror("bind");
 		close(s);
@@ -130,15 +174,15 @@ int main(int argc, char *argv[])
 		setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on));
 	}
 #endif
-	if (0) {
+	if (send_msgs) {
 		int i, j;
 
 		memset(buf, 0, sizeof(buf));
 
 		data = (struct cn_msg *)buf;
 
-		data->id.idx = 0x123;
-		data->id.val = 0x456;
+		data->id.idx = CN_TEST_IDX;
+		data->id.val = CN_TEST_VAL;
 		data->seq = seq++;
 		data->ack = 0;
 		data->len = 0;

+ 29 - 25
Documentation/feature-removal-schedule.txt

@@ -6,6 +6,35 @@ be removed from this file.
 
 ---------------------------
 
+What:	PRISM54
+When:	2.6.34
+
+Why:	prism54 FullMAC PCI / Cardbus devices used to be supported only by the
+	prism54 wireless driver. After Intersil stopped selling these
+	devices in preference for the newer more flexible SoftMAC devices
+	a SoftMAC device driver was required and prism54 did not support
+	them. The p54pci driver now exists and has been present in the kernel for
+	a while. This driver supports both SoftMAC devices and FullMAC devices.
+	The main difference between these devices was the amount of memory which
+	could be used for the firmware. The SoftMAC devices support a smaller
+	amount of memory. Because of this the SoftMAC firmware fits into FullMAC
+	devices's memory. p54pci supports not only PCI / Cardbus but also USB
+	and SPI. Since p54pci supports all devices prism54 supports
+	you will have a conflict. I'm not quite sure how distributions are
+	handling this conflict right now. prism54 was kept around due to
+	claims users may experience issues when using the SoftMAC driver.
+	Time has passed users have not reported issues. If you use prism54
+	and for whatever reason you cannot use p54pci please let us know!
+	E-mail us at: linux-wireless@vger.kernel.org
+
+	For more information see the p54 wiki page:
+
+	http://wireless.kernel.org/en/users/Drivers/p54
+
+Who:	Luis R. Rodriguez <lrodriguez@atheros.com>
+
+---------------------------
+
 What:	IRQF_SAMPLE_RANDOM
 Check:	IRQF_SAMPLE_RANDOM
 When:	July 2009
@@ -217,31 +246,6 @@ Who:	Thomas Gleixner <tglx@linutronix.de>
 ---------------------------
 
 What (Why):
-	- include/linux/netfilter_ipv4/ipt_TOS.h ipt_tos.h header files
-	  (superseded by xt_TOS/xt_tos target & match)
-
-	- "forwarding" header files like ipt_mac.h in
-	  include/linux/netfilter_ipv4/ and include/linux/netfilter_ipv6/
-
-	- xt_CONNMARK match revision 0
-	  (superseded by xt_CONNMARK match revision 1)
-
-	- xt_MARK target revisions 0 and 1
-	  (superseded by xt_MARK match revision 2)
-
-	- xt_connmark match revision 0
-	  (superseded by xt_connmark match revision 1)
-
-	- xt_conntrack match revision 0
-	  (superseded by xt_conntrack match revision 1)
-
-	- xt_iprange match revision 0,
-	  include/linux/netfilter_ipv4/ipt_iprange.h
-	  (superseded by xt_iprange match revision 1)
-
-	- xt_mark match revision 0
-	  (superseded by xt_mark match revision 1)
-
 	- xt_recent: the old ipt_recent proc dir
 	  (superseded by /proc/net/xt_recent)
 

+ 5 - 0
Documentation/kernel-parameters.txt

@@ -1543,6 +1543,11 @@ and is between 256 and 4096 characters. It is defined in the file
 			symbolic names: lapic and ioapic
 			Example: nmi_watchdog=2 or nmi_watchdog=panic,lapic
 
+	netpoll.carrier_timeout=
+			[NET] Specifies amount of time (in seconds) that
+			netpoll should wait for a carrier. By default netpoll
+			waits 4 seconds.
+
 	no387		[BUGS=X86-32] Tells the kernel to use the 387 maths
 			emulation library even if a 387 maths coprocessor
 			is present.

+ 2 - 0
Documentation/networking/00-INDEX

@@ -60,6 +60,8 @@ framerelay.txt
 	- info on using Frame Relay/Data Link Connection Identifier (DLCI).
 generic_netlink.txt
 	- info on Generic Netlink
+ieee802154.txt
+	- Linux IEEE 802.15.4 implementation, API and drivers
 ip-sysctl.txt
 	- /proc/sys/net/ipv4/* variables
 ip_dynaddr.txt

+ 12 - 8
Documentation/networking/ieee802154.txt

@@ -22,7 +22,7 @@ int sd = socket(PF_IEEE802154, SOCK_DGRAM, 0);
 .....
 
 The address family, socket addresses etc. are defined in the
-include/net/ieee802154/af_ieee802154.h header or in the special header
+include/net/af_ieee802154.h header or in the special header
 in our userspace package (see either linux-zigbee sourceforge download page
 or git tree at git://linux-zigbee.git.sourceforge.net/gitroot/linux-zigbee).
 
@@ -33,7 +33,7 @@ MLME - MAC Level Management
 ============================
 
 Most of IEEE 802.15.4 MLME interfaces are directly mapped on netlink commands.
-See the include/net/ieee802154/nl802154.h header. Our userspace tools package
+See the include/net/nl802154.h header. Our userspace tools package
 (see above) provides CLI configuration utility for radio interfaces and simple
 coordinator for IEEE 802.15.4 networks as an example users of MLME protocol.
 
@@ -54,10 +54,14 @@ Those types of devices require different approach to be hooked into Linux kernel
 HardMAC
 =======
 
-See the header include/net/ieee802154/netdevice.h. You have to implement Linux
+See the header include/net/ieee802154_netdev.h. You have to implement Linux
 net_device, with .type = ARPHRD_IEEE802154. Data is exchanged with socket family
-code via plain sk_buffs. The control block of sk_buffs will contain additional
-info as described in the struct ieee802154_mac_cb.
+code via plain sk_buffs. On skb reception skb->cb must contain additional
+info as described in the struct ieee802154_mac_cb. During packet transmission
+the skb->cb is used to provide additional data to device's header_ops->create
+function. Be aware, that this data can be overriden later (when socket code
+submits skb to qdisc), so if you need something from that cb later, you should
+store info in the skb->data on your own.
 
 To hook the MLME interface you have to populate the ml_priv field of your
 net_device with a pointer to struct ieee802154_mlme_ops instance. All fields are
@@ -69,8 +73,8 @@ We provide an example of simple HardMAC driver at drivers/ieee802154/fakehard.c
 SoftMAC
 =======
 
-We are going to provide intermediate layer impelementing IEEE 802.15.4 MAC
+We are going to provide intermediate layer implementing IEEE 802.15.4 MAC
 in software. This is currently WIP.
 
-See header include/net/ieee802154/mac802154.h and several drivers in
-drivers/ieee802154/
+See header include/net/mac802154.h and several drivers in drivers/ieee802154/.
+

+ 36 - 11
Documentation/networking/ip-sysctl.txt

@@ -311,9 +311,12 @@ tcp_no_metrics_save - BOOLEAN
 	connections.
 
 tcp_orphan_retries - INTEGER
-	How may times to retry before killing TCP connection, closed
-	by our side. Default value 7 corresponds to ~50sec-16min
-	depending on RTO. If you machine is loaded WEB server,
+	This value influences the timeout of a locally closed TCP connection,
+	when RTO retransmissions remain unacknowledged.
+	See tcp_retries2 for more details.
+
+	The default value is 7.
+	If your machine is a loaded WEB server,
 	you should think about lowering this value, such sockets
 	may consume significant resources. Cf. tcp_max_orphans.
 
@@ -327,16 +330,28 @@ tcp_retrans_collapse - BOOLEAN
 	certain TCP stacks.
 
 tcp_retries1 - INTEGER
-	How many times to retry before deciding that something is wrong
-	and it is necessary to report this suspicion to network layer.
-	Minimal RFC value is 3, it is default, which corresponds
-	to ~3sec-8min depending on RTO.
+	This value influences the time, after which TCP decides, that
+	something is wrong due to unacknowledged RTO retransmissions,
+	and reports this suspicion to the network layer.
+	See tcp_retries2 for more details.
+
+	RFC 1122 recommends at least 3 retransmissions, which is the
+	default.
 
 tcp_retries2 - INTEGER
-	How may times to retry before killing alive TCP connection.
-	RFC1122 says that the limit should be longer than 100 sec.
-	It is too small number.	Default value 15 corresponds to ~13-30min
-	depending on RTO.
+	This value influences the timeout of an alive TCP connection,
+	when RTO retransmissions remain unacknowledged.
+	Given a value of N, a hypothetical TCP connection following
+	exponential backoff with an initial RTO of TCP_RTO_MIN would
+	retransmit N times before killing the connection at the (N+1)th RTO.
+
+	The default value of 15 yields a hypothetical timeout of 924.6
+	seconds and is a lower bound for the effective timeout.
+	TCP will effectively time out at the first RTO which exceeds the
+	hypothetical timeout.
+
+	RFC 1122 recommends at least 100 seconds for the timeout,
+	which corresponds to a value of at least 8.
 
 tcp_rfc1337 - BOOLEAN
 	If set, the TCP stack behaves conforming to RFC1337. If unset,
@@ -1282,6 +1297,16 @@ sctp_rmem - vector of 3 INTEGERs: min, default, max
 sctp_wmem  - vector of 3 INTEGERs: min, default, max
 	See tcp_wmem for a description.
 
+addr_scope_policy - INTEGER
+	Control IPv4 address scoping - draft-stewart-tsvwg-sctp-ipv4-00
+
+	0   - Disable IPv4 address scoping
+	1   - Enable IPv4 address scoping
+	2   - Follow draft but allow IPv4 private addresses
+	3   - Follow draft but allow IPv4 link local addresses
+
+	Default: 1
+
 
 /proc/sys/net/core/*
 dev_weight - INTEGER

+ 39 - 13
MAINTAINERS

@@ -876,6 +876,7 @@ M:	"Luis R. Rodriguez" <lrodriguez@atheros.com>
 M:	Bob Copeland <me@bobcopeland.com>
 L:	linux-wireless@vger.kernel.org
 L:	ath5k-devel@lists.ath5k.org
+W:	http://wireless.kernel.org/en/users/Drivers/ath5k
 S:	Maintained
 F:	drivers/net/wireless/ath/ath5k/
 
@@ -887,6 +888,7 @@ M:	Vasanthakumar Thiagarajan <vasanth@atheros.com>
 M:	Senthil Balasubramanian <senthilkumar@atheros.com>
 L:	linux-wireless@vger.kernel.org
 L:	ath9k-devel@lists.ath9k.org
+W:	http://wireless.kernel.org/en/users/Drivers/ath9k
 S:	Supported
 F:	drivers/net/wireless/ath/ath9k/
 
@@ -2660,25 +2662,21 @@ F:	drivers/net/ixgbe/
 
 INTEL PRO/WIRELESS 2100 NETWORK CONNECTION SUPPORT
 M:	Zhu Yi <yi.zhu@intel.com>
-M:	James Ketrenos <jketreno@linux.intel.com>
 M:	Reinette Chatre <reinette.chatre@intel.com>
+M:	Intel Linux Wireless <ilw@linux.intel.com>
 L:	linux-wireless@vger.kernel.org
-L:	ipw2100-devel@lists.sourceforge.net
-W:	http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel
 W:	http://ipw2100.sourceforge.net
-S:	Supported
+S:	Odd Fixes
 F:	Documentation/networking/README.ipw2100
 F:	drivers/net/wireless/ipw2x00/ipw2100.*
 
 INTEL PRO/WIRELESS 2915ABG NETWORK CONNECTION SUPPORT
 M:	Zhu Yi <yi.zhu@intel.com>
-M:	James Ketrenos <jketreno@linux.intel.com>
 M:	Reinette Chatre <reinette.chatre@intel.com>
+M:	Intel Linux Wireless <ilw@linux.intel.com>
 L:	linux-wireless@vger.kernel.org
-L:	ipw2100-devel@lists.sourceforge.net
-W:	http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel
 W:	http://ipw2200.sourceforge.net
-S:	Supported
+S:	Odd Fixes
 F:	Documentation/networking/README.ipw2200
 F:	drivers/net/wireless/ipw2x00/ipw2200.*
 
@@ -2695,8 +2693,8 @@ F:	include/linux/wimax/i2400m.h
 INTEL WIRELESS WIFI LINK (iwlwifi)
 M:	Zhu Yi <yi.zhu@intel.com>
 M:	Reinette Chatre <reinette.chatre@intel.com>
+M:	Intel Linux Wireless <ilw@linux.intel.com>
 L:	linux-wireless@vger.kernel.org
-L:	ipw3945-devel@lists.sourceforge.net
 W:	http://intellinuxwireless.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-2.6.git
 S:	Supported
@@ -3279,6 +3277,12 @@ S:	Supported
 F:	drivers/net/mv643xx_eth.*
 F:	include/linux/mv643xx.h
 
+MARVELL MWL8K WIRELESS DRIVER
+M:	Lennert Buytenhek <buytenh@marvell.com>
+L:	linux-wireless@vger.kernel.org
+S:	Supported
+F:	drivers/net/wireless/mwl8k.c
+
 MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
 M:	Nicolas Pitre <nico@cam.org>
 S:	Maintained
@@ -3591,9 +3595,12 @@ M:	"John W. Linville" <linville@tuxdriver.com>
 L:	linux-wireless@vger.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git
 S:	Maintained
+F:	net/mac80211/
+F:	net/rfkill/
 F:	net/wireless/
 F:	include/net/ieee80211*
 F:	include/linux/wireless.h
+F:	drivers/net/wireless/
 
 NETWORKING DRIVERS
 L:	netdev@vger.kernel.org
@@ -4299,7 +4306,7 @@ L:	linux-wireless@vger.kernel.org
 W:	http://linuxwireless.org/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:	Maintained
-F:	drivers/net/wireless/rtl818*
+F:	drivers/net/wireless/rtl818x/rtl8180*
 
 RTL8187 WIRELESS DRIVER
 M:	Herton Ronaldo Krzesinski <herton@mandriva.com.br>
@@ -4526,9 +4533,10 @@ S:	Supported
 F:	drivers/net/benet/
 
 SFC NETWORK DRIVER
-P:	Steve Hodgson
-P:	Ben Hutchings
-M:	Robert Stonehouse <linux-net-drivers@solarflare.com>
+M:	Solarflare linux maintainers <linux-net-drivers@solarflare.com>
+M:	Steve Hodgson <shodgson@solarflare.com>
+M:	Ben Hutchings <bhutchings@solarflare.com>
+L:	netdev@vger.kernel.org
 S:	Supported
 F:	drivers/net/sfc/
 
@@ -5578,6 +5586,24 @@ M:	Miloslav Trmac <mitr@volny.cz>
 S:	Maintained
 F:	drivers/input/misc/wistron_btns.c
 
+WL1251 WIRELESS DRIVER
+P:	Kalle Valo
+M:	kalle.valo@nokia.com
+L:	linux-wireless@vger.kernel.org
+W:	http://wireless.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
+S:	Maintained
+F:	drivers/net/wireless/wl12xx/*
+X:	drivers/net/wireless/wl12xx/wl1271*
+
+WL1271 WIRELESS DRIVER
+M:	Luciano Coelho <luciano.coelho@nokia.com>
+L:	linux-wireless@vger.kernel.org
+W:	http://wireless.kernel.org
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
+S:	Maintained
+F:	drivers/net/wireless/wl12xx/wl1271*
+
 WL3501 WIRELESS PCMCIA CARD DRIVER
 M:	Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
 L:	linux-wireless@vger.kernel.org

+ 2 - 0
arch/alpha/include/asm/socket.h

@@ -32,6 +32,8 @@
 #define	SO_RCVTIMEO	0x1012
 #define	SO_SNDTIMEO	0x1013
 #define SO_ACCEPTCONN	0x1014
+#define SO_PROTOCOL	0x1028
+#define SO_DOMAIN	0x1029
 
 /* linux-specific, might as well be the same as on i386 */
 #define SO_NO_CHECK	11

+ 3 - 0
arch/arm/include/asm/socket.h

@@ -57,4 +57,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_SOCKET_H */

+ 1 - 0
arch/arm/mach-ixp4xx/common.c

@@ -416,6 +416,7 @@ static struct clocksource clocksource_ixp4xx = {
 };
 
 unsigned long ixp4xx_timer_freq = FREQ;
+EXPORT_SYMBOL(ixp4xx_timer_freq);
 static int __init ixp4xx_clocksource_init(void)
 {
 	clocksource_ixp4xx.mult =

+ 3 - 0
arch/avr32/include/asm/socket.h

@@ -57,4 +57,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* __ASM_AVR32_SOCKET_H */

+ 3 - 0
arch/cris/include/asm/socket.h

@@ -59,6 +59,9 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_SOCKET_H */
 
 

+ 3 - 0
arch/frv/include/asm/socket.h

@@ -57,5 +57,8 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_SOCKET_H */
 

+ 3 - 0
arch/h8300/include/asm/socket.h

@@ -57,4 +57,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_SOCKET_H */

+ 1 - 1
arch/ia64/hp/sim/simeth.c

@@ -412,7 +412,7 @@ simeth_tx(struct sk_buff *skb, struct net_device *dev)
 	 */
 
 	dev_kfree_skb(skb);
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 static inline struct sk_buff *

+ 3 - 0
arch/ia64/include/asm/socket.h

@@ -66,4 +66,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_IA64_SOCKET_H */

+ 3 - 0
arch/m32r/include/asm/socket.h

@@ -57,4 +57,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_M32R_SOCKET_H */

+ 3 - 0
arch/m68k/include/asm/socket.h

@@ -57,4 +57,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_SOCKET_H */

+ 15 - 0
arch/mips/ar7/platform.c

@@ -32,6 +32,8 @@
 #include <linux/leds.h>
 #include <linux/string.h>
 #include <linux/etherdevice.h>
+#include <linux/phy.h>
+#include <linux/phy_fixed.h>
 
 #include <asm/addrspace.h>
 #include <asm/mach-ar7/ar7.h>
@@ -208,6 +210,12 @@ static struct physmap_flash_data physmap_flash_data = {
 	.width = 2,
 };
 
+static struct fixed_phy_status fixed_phy_status __initdata = {
+	.link = 1,
+	.speed = 100,
+	.duplex = 1,
+};
+
 static struct plat_cpmac_data cpmac_low_data = {
 	.reset_bit = 17,
 	.power_bit = 20,
@@ -530,6 +538,9 @@ static int __init ar7_register_devices(void)
 	}
 
 	if (ar7_has_high_cpmac()) {
+		res = fixed_phy_add(PHY_POLL, cpmac_high.id, &fixed_phy_status);
+		if (res && res != -ENODEV)
+			return res;
 		cpmac_get_mac(1, cpmac_high_data.dev_addr);
 		res = platform_device_register(&cpmac_high);
 		if (res)
@@ -538,6 +549,10 @@ static int __init ar7_register_devices(void)
 		cpmac_low_data.phy_mask = 0xffffffff;
 	}
 
+	res = fixed_phy_add(PHY_POLL, cpmac_low.id, &fixed_phy_status);
+	if (res && res != -ENODEV)
+		return res;
+
 	cpmac_get_mac(0, cpmac_low_data.dev_addr);
 	res = platform_device_register(&cpmac_low);
 	if (res)

+ 2 - 0
arch/mips/include/asm/socket.h

@@ -42,6 +42,8 @@ To add: #define SO_REUSEPORT 0x0200	/* Allow local address and port reuse.  */
 #define SO_SNDTIMEO	0x1005	/* send timeout */
 #define SO_RCVTIMEO 	0x1006	/* receive timeout */
 #define SO_ACCEPTCONN	0x1009
+#define SO_PROTOCOL	0x1028	/* protocol type */
+#define SO_DOMAIN	0x1029	/* domain/socket family */
 
 /* linux-specific, might as well be the same as on i386 */
 #define SO_NO_CHECK	11

+ 1 - 1
arch/mips/kernel/scall64-n32.S

@@ -164,7 +164,7 @@ EXPORT(sysn32_call_table)
 	PTR	sys_connect
 	PTR	sys_accept
 	PTR	sys_sendto
-	PTR	sys_recvfrom
+	PTR	compat_sys_recvfrom
 	PTR	compat_sys_sendmsg		/* 6045 */
 	PTR	compat_sys_recvmsg
 	PTR	sys_shutdown

+ 2 - 2
arch/mips/kernel/scall64-o32.S

@@ -378,8 +378,8 @@ sys_call_table:
 	PTR	sys_getsockname
 	PTR	sys_getsockopt
 	PTR	sys_listen
-	PTR	sys_recv			/* 4175 */
-	PTR	sys_recvfrom
+	PTR	compat_sys_recv			/* 4175 */
+	PTR	compat_sys_recvfrom
 	PTR	compat_sys_recvmsg
 	PTR	sys_send
 	PTR	compat_sys_sendmsg

+ 3 - 0
arch/mn10300/include/asm/socket.h

@@ -57,4 +57,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_SOCKET_H */

+ 2 - 0
arch/parisc/include/asm/socket.h

@@ -24,6 +24,8 @@
 #define SO_RCVTIMEO	0x1006
 #define SO_ERROR	0x1007
 #define SO_TYPE		0x1008
+#define SO_PROTOCOL	0x1028
+#define SO_DOMAIN	0x1029
 #define SO_PEERNAME	0x2000
 
 #define SO_NO_CHECK	0x400b

+ 1 - 0
arch/powerpc/include/asm/qe.h

@@ -154,6 +154,7 @@ int qe_get_snum(void);
 void qe_put_snum(u8 snum);
 unsigned int qe_get_num_of_risc(void);
 unsigned int qe_get_num_of_snums(void);
+int qe_alive_during_sleep(void);
 
 /* we actually use cpm_muram implementation, define this for convenience */
 #define qe_muram_init cpm_muram_init

+ 3 - 0
arch/powerpc/include/asm/socket.h

@@ -64,4 +64,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif	/* _ASM_POWERPC_SOCKET_H */

+ 13 - 0
arch/powerpc/sysdev/qe_lib/qe.c

@@ -65,6 +65,19 @@ static unsigned int qe_num_of_snum;
 
 static phys_addr_t qebase = -1;
 
+int qe_alive_during_sleep(void)
+{
+	static int ret = -1;
+
+	if (ret != -1)
+		return ret;
+
+	ret = !of_find_compatible_node(NULL, NULL, "fsl,mpc8569-pmc");
+
+	return ret;
+}
+EXPORT_SYMBOL(qe_alive_during_sleep);
+
 phys_addr_t get_qe_base(void)
 {
 	struct device_node *qe;

+ 3 - 0
arch/s390/include/asm/socket.h

@@ -65,4 +65,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif /* _ASM_SOCKET_H */

+ 3 - 0
arch/sh/include/asm/sh_eth.h

@@ -6,6 +6,9 @@ enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN};
 struct sh_eth_plat_data {
 	int phy;
 	int edmac_endian;
+
+	unsigned no_ether_link:1;
+	unsigned ether_link_active_low:1;
 };
 
 #endif

+ 3 - 0
arch/sparc/include/asm/socket.h

@@ -29,6 +29,9 @@
 #define SO_RCVBUFFORCE	0x100b
 #define SO_ERROR	0x1007
 #define SO_TYPE		0x1008
+#define SO_PROTOCOL	0x1028
+#define SO_DOMAIN	0x1029
+
 
 /* Linux specific, keep the same. */
 #define SO_NO_CHECK	0x000b

+ 1 - 1
arch/sparc/kernel/sys32.S

@@ -121,7 +121,7 @@ SIGN2(sys32_syslog, sys_syslog, %o0, %o2)
 SIGN1(sys32_umask, sys_umask, %o0)
 SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2)
 SIGN1(sys32_sendto, sys_sendto, %o0)
-SIGN1(sys32_recvfrom, sys_recvfrom, %o0)
+SIGN1(sys32_recvfrom, compat_sys_recvfrom, %o0)
 SIGN3(sys32_socket, sys_socket, %o0, %o1, %o2)
 SIGN2(sys32_connect, sys_connect, %o0, %o2)
 SIGN2(sys32_bind, sys_bind, %o0, %o2)

+ 2 - 2
arch/um/drivers/net_kern.c

@@ -245,7 +245,7 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	dev_kfree_skb(skb);
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 static void uml_net_set_multicast_list(struct net_device *dev)
@@ -285,7 +285,7 @@ static void uml_net_get_drvinfo(struct net_device *dev,
 	strcpy(info->version, "42");
 }
 
-static struct ethtool_ops uml_net_ethtool_ops = {
+static const struct ethtool_ops uml_net_ethtool_ops = {
 	.get_drvinfo	= uml_net_get_drvinfo,
 	.get_link	= ethtool_op_get_link,
 };

+ 3 - 0
arch/xtensa/include/asm/socket.h

@@ -68,4 +68,7 @@
 #define SO_TIMESTAMPING		37
 #define SCM_TIMESTAMPING	SO_TIMESTAMPING
 
+#define SO_PROTOCOL		38
+#define SO_DOMAIN		39
+
 #endif	/* _XTENSA_SOCKET_H */

+ 1 - 1
arch/xtensa/platforms/iss/network.c

@@ -545,7 +545,7 @@ static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	spin_unlock_irqrestore(&lp->lock, flags);
 
 	dev_kfree_skb(skb);
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 

+ 2 - 2
drivers/atm/horizon.c

@@ -641,7 +641,7 @@ static int make_rate (const hrz_dev * dev, u32 c, rounding r,
 					pre = 1;
 				break;
 			case round_nearest:
-				pre = (br+(c<<div)/2)/(c<<div);
+				pre = DIV_ROUND_CLOSEST(br, c<<div);
 				// but p must be non-zero
 				if (!pre)
 					pre = 1;
@@ -671,7 +671,7 @@ static int make_rate (const hrz_dev * dev, u32 c, rounding r,
 					pre = DIV_ROUND_UP(br, c<<div);
 					break;
 				case round_nearest:
-					pre = (br+(c<<div)/2)/(c<<div);
+					pre = DIV_ROUND_CLOSEST(br, c<<div);
 					break;
 				default: /* round_up */
 					pre = br/(c<<div);

+ 1 - 1
drivers/atm/solos-pci.c

@@ -372,7 +372,7 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb
 	}
 
 	snr = next_string(skb);
-	if (!str)
+	if (!snr)
 		return -EIO;
 	attn = next_string(skb);
 	if (!attn)

+ 25 - 0
drivers/bluetooth/Kconfig

@@ -170,5 +170,30 @@ config BT_HCIVHCI
 	  Say Y here to compile support for virtual HCI devices into the
 	  kernel or say M to compile it as module (hci_vhci).
 
+config BT_MRVL
+	tristate "Marvell Bluetooth driver support"
+	help
+	  The core driver to support Marvell Bluetooth devices.
+
+	  This driver is required if you want to support
+	  Marvell Bluetooth devices, such as 8688.
+
+	  Say Y here to compile Marvell Bluetooth driver
+	  into the kernel or say M to compile it as module.
+
+config BT_MRVL_SDIO
+	tristate "Marvell BT-over-SDIO driver"
+	depends on BT_MRVL && MMC
+	select FW_LOADER
+	help
+	  The driver for Marvell Bluetooth chipsets with SDIO interface.
+
+	  This driver is required if you want to use Marvell Bluetooth
+	  devices with SDIO interface. Currently only SD8688 chipset is
+	  supported.
+
+	  Say Y here to compile support for Marvell BT-over-SDIO driver
+	  into the kernel or say M to compile it as module.
+
 endmenu
 

+ 6 - 0
drivers/bluetooth/Makefile

@@ -15,6 +15,12 @@ obj-$(CONFIG_BT_HCIBTUART)	+= btuart_cs.o
 obj-$(CONFIG_BT_HCIBTUSB)	+= btusb.o
 obj-$(CONFIG_BT_HCIBTSDIO)	+= btsdio.o
 
+obj-$(CONFIG_BT_MRVL)		+= btmrvl.o
+obj-$(CONFIG_BT_MRVL_SDIO)	+= btmrvl_sdio.o
+
+btmrvl-y			:= btmrvl_main.o
+btmrvl-$(CONFIG_DEBUG_FS)	+= btmrvl_debugfs.o
+
 hci_uart-y				:= hci_ldisc.o
 hci_uart-$(CONFIG_BT_HCIUART_H4)	+= hci_h4.o
 hci_uart-$(CONFIG_BT_HCIUART_BCSP)	+= hci_bcsp.o

+ 432 - 0
drivers/bluetooth/btmrvl_debugfs.c

@@ -0,0 +1,432 @@
+/**
+ * Marvell Bluetooth driver: debugfs related functions
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/debugfs.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+
+struct btmrvl_debugfs_data {
+	struct dentry *root_dir, *config_dir, *status_dir;
+
+	/* config */
+	struct dentry *drvdbg;
+	struct dentry *psmode;
+	struct dentry *pscmd;
+	struct dentry *hsmode;
+	struct dentry *hscmd;
+	struct dentry *gpiogap;
+	struct dentry *hscfgcmd;
+
+	/* status */
+	struct dentry *curpsmode;
+	struct dentry *hsstate;
+	struct dentry *psstate;
+	struct dentry *txdnldready;
+};
+
+static int btmrvl_open_generic(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t btmrvl_hscfgcmd_write(struct file *file,
+			const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+
+	priv->btmrvl_dev.hscfgcmd = result;
+
+	if (priv->btmrvl_dev.hscfgcmd) {
+		btmrvl_prepare_command(priv);
+		wake_up_interruptible(&priv->main_thread.wait_q);
+	}
+
+	return count;
+}
+
+static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+						priv->btmrvl_dev.hscfgcmd);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hscfgcmd_fops = {
+	.read	= btmrvl_hscfgcmd_read,
+	.write	= btmrvl_hscfgcmd_write,
+	.open	= btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_psmode_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+
+	priv->btmrvl_dev.psmode = result;
+
+	return count;
+}
+
+static ssize_t btmrvl_psmode_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+						priv->btmrvl_dev.psmode);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_psmode_fops = {
+	.read	= btmrvl_psmode_read,
+	.write	= btmrvl_psmode_write,
+	.open	= btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+
+	priv->btmrvl_dev.pscmd = result;
+
+	if (priv->btmrvl_dev.pscmd) {
+		btmrvl_prepare_command(priv);
+		wake_up_interruptible(&priv->main_thread.wait_q);
+	}
+
+	return count;
+
+}
+
+static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.pscmd);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_pscmd_fops = {
+	.read = btmrvl_pscmd_read,
+	.write = btmrvl_pscmd_write,
+	.open = btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_gpiogap_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 16, &result);
+
+	priv->btmrvl_dev.gpio_gap = result;
+
+	return count;
+}
+
+static ssize_t btmrvl_gpiogap_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "0x%x\n",
+						priv->btmrvl_dev.gpio_gap);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_gpiogap_fops = {
+	.read	= btmrvl_gpiogap_read,
+	.write	= btmrvl_gpiogap_write,
+	.open	= btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = (struct btmrvl_private *) file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+
+	priv->btmrvl_dev.hscmd = result;
+	if (priv->btmrvl_dev.hscmd) {
+		btmrvl_prepare_command(priv);
+		wake_up_interruptible(&priv->main_thread.wait_q);
+	}
+
+	return count;
+}
+
+static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hscmd);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hscmd_fops = {
+	.read	= btmrvl_hscmd_read,
+	.write	= btmrvl_hscmd_write,
+	.open	= btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_hsmode_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+
+	priv->btmrvl_dev.hsmode = result;
+
+	return count;
+}
+
+static ssize_t btmrvl_hsmode_read(struct file *file, char __user * userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hsmode);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hsmode_fops = {
+	.read	= btmrvl_hsmode_read,
+	.write	= btmrvl_hsmode_write,
+	.open	= btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_curpsmode_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->psmode);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_curpsmode_fops = {
+	.read	= btmrvl_curpsmode_read,
+	.open	= btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_psstate_read(struct file *file, char __user * userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->ps_state);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_psstate_fops = {
+	.read	= btmrvl_psstate_read,
+	.open	= btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_hsstate_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->hs_state);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hsstate_fops = {
+	.read	= btmrvl_hsstate_read,
+	.open	= btmrvl_open_generic,
+};
+
+static ssize_t btmrvl_txdnldready_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+					priv->btmrvl_dev.tx_dnld_rdy);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_txdnldready_fops = {
+	.read	= btmrvl_txdnldready_read,
+	.open	= btmrvl_open_generic,
+};
+
+void btmrvl_debugfs_init(struct hci_dev *hdev)
+{
+	struct btmrvl_private *priv = hdev->driver_data;
+	struct btmrvl_debugfs_data *dbg;
+
+	dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
+	priv->debugfs_data = dbg;
+
+	if (!dbg) {
+		BT_ERR("Can not allocate memory for btmrvl_debugfs_data.");
+		return;
+	}
+
+	dbg->root_dir = debugfs_create_dir("btmrvl", NULL);
+
+	dbg->config_dir = debugfs_create_dir("config", dbg->root_dir);
+
+	dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir,
+				hdev->driver_data, &btmrvl_psmode_fops);
+	dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir,
+				hdev->driver_data, &btmrvl_pscmd_fops);
+	dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir,
+				hdev->driver_data, &btmrvl_gpiogap_fops);
+	dbg->hsmode =  debugfs_create_file("hsmode", 0644, dbg->config_dir,
+				hdev->driver_data, &btmrvl_hsmode_fops);
+	dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir,
+				hdev->driver_data, &btmrvl_hscmd_fops);
+	dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
+				hdev->driver_data, &btmrvl_hscfgcmd_fops);
+
+	dbg->status_dir = debugfs_create_dir("status", dbg->root_dir);
+	dbg->curpsmode = debugfs_create_file("curpsmode", 0444,
+						dbg->status_dir,
+						hdev->driver_data,
+						&btmrvl_curpsmode_fops);
+	dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir,
+				hdev->driver_data, &btmrvl_psstate_fops);
+	dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir,
+				hdev->driver_data, &btmrvl_hsstate_fops);
+	dbg->txdnldready = debugfs_create_file("txdnldready", 0444,
+						dbg->status_dir,
+						hdev->driver_data,
+						&btmrvl_txdnldready_fops);
+}
+
+void btmrvl_debugfs_remove(struct hci_dev *hdev)
+{
+	struct btmrvl_private *priv = hdev->driver_data;
+	struct btmrvl_debugfs_data *dbg = priv->debugfs_data;
+
+	if (!dbg)
+		return;
+
+	debugfs_remove(dbg->psmode);
+	debugfs_remove(dbg->pscmd);
+	debugfs_remove(dbg->gpiogap);
+	debugfs_remove(dbg->hsmode);
+	debugfs_remove(dbg->hscmd);
+	debugfs_remove(dbg->hscfgcmd);
+	debugfs_remove(dbg->config_dir);
+
+	debugfs_remove(dbg->curpsmode);
+	debugfs_remove(dbg->psstate);
+	debugfs_remove(dbg->hsstate);
+	debugfs_remove(dbg->txdnldready);
+	debugfs_remove(dbg->status_dir);
+
+	debugfs_remove(dbg->root_dir);
+
+	kfree(dbg);
+}

+ 139 - 0
drivers/bluetooth/btmrvl_drv.h

@@ -0,0 +1,139 @@
+/*
+ * Marvell Bluetooth driver: global definitions & declarations
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ *
+ */
+
+#include <linux/kthread.h>
+#include <linux/bitops.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define BTM_HEADER_LEN			4
+#define BTM_UPLD_SIZE			2312
+
+/* Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED	5000
+/* Time to wait for command response in millisecond */
+#define WAIT_UNTIL_CMD_RESP		5000
+
+struct btmrvl_thread {
+	struct task_struct *task;
+	wait_queue_head_t wait_q;
+	void *priv;
+};
+
+struct btmrvl_device {
+	void *card;
+	struct hci_dev *hcidev;
+
+	u8 tx_dnld_rdy;
+
+	u8 psmode;
+	u8 pscmd;
+	u8 hsmode;
+	u8 hscmd;
+
+	/* Low byte is gap, high byte is GPIO */
+	u16 gpio_gap;
+
+	u8 hscfgcmd;
+	u8 sendcmdflag;
+};
+
+struct btmrvl_adapter {
+	u32 int_count;
+	struct sk_buff_head tx_queue;
+	u8 psmode;
+	u8 ps_state;
+	u8 hs_state;
+	u8 wakeup_tries;
+	wait_queue_head_t cmd_wait_q;
+	u8 cmd_complete;
+};
+
+struct btmrvl_private {
+	struct btmrvl_device btmrvl_dev;
+	struct btmrvl_adapter *adapter;
+	struct btmrvl_thread main_thread;
+	int (*hw_host_to_card) (struct btmrvl_private *priv,
+				u8 *payload, u16 nb);
+	int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
+	spinlock_t driver_lock;		/* spinlock used by driver */
+#ifdef CONFIG_DEBUG_FS
+	void *debugfs_data;
+#endif
+};
+
+#define MRVL_VENDOR_PKT			0xFE
+
+/* Bluetooth commands  */
+#define BT_CMD_AUTO_SLEEP_MODE		0x23
+#define BT_CMD_HOST_SLEEP_CONFIG	0x59
+#define BT_CMD_HOST_SLEEP_ENABLE	0x5A
+#define BT_CMD_MODULE_CFG_REQ		0x5B
+
+/* Sub-commands: Module Bringup/Shutdown Request */
+#define MODULE_BRINGUP_REQ		0xF1
+#define MODULE_SHUTDOWN_REQ		0xF2
+
+#define BT_EVENT_POWER_STATE		0x20
+
+/* Bluetooth Power States */
+#define BT_PS_ENABLE			0x02
+#define BT_PS_DISABLE			0x03
+#define BT_PS_SLEEP			0x01
+
+#define OGF				0x3F
+
+/* Host Sleep states */
+#define HS_ACTIVATED			0x01
+#define HS_DEACTIVATED			0x00
+
+/* Power Save modes */
+#define PS_SLEEP			0x01
+#define PS_AWAKE			0x00
+
+struct btmrvl_cmd {
+	__le16 ocf_ogf;
+	u8 length;
+	u8 data[4];
+} __attribute__ ((packed));
+
+struct btmrvl_event {
+	u8 ec;		/* event counter */
+	u8 length;
+	u8 data[4];
+} __attribute__ ((packed));
+
+/* Prototype of global function */
+
+struct btmrvl_private *btmrvl_add_card(void *card);
+int btmrvl_remove_card(struct btmrvl_private *priv);
+
+void btmrvl_interrupt(struct btmrvl_private *priv);
+
+void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
+int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
+
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
+int btmrvl_prepare_command(struct btmrvl_private *priv);
+
+#ifdef CONFIG_DEBUG_FS
+void btmrvl_debugfs_init(struct hci_dev *hdev);
+void btmrvl_debugfs_remove(struct hci_dev *hdev);
+#endif

+ 624 - 0
drivers/bluetooth/btmrvl_main.c

@@ -0,0 +1,624 @@
+/**
+ * Marvell Bluetooth driver
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+
+#define VERSION "1.0"
+
+/*
+ * This function is called by interface specific interrupt handler.
+ * It updates Power Save & Host Sleep states, and wakes up the main
+ * thread.
+ */
+void btmrvl_interrupt(struct btmrvl_private *priv)
+{
+	priv->adapter->ps_state = PS_AWAKE;
+
+	priv->adapter->wakeup_tries = 0;
+
+	priv->adapter->int_count++;
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+}
+EXPORT_SYMBOL_GPL(btmrvl_interrupt);
+
+void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+	struct hci_event_hdr *hdr = (void *) skb->data;
+	struct hci_ev_cmd_complete *ec;
+	u16 opcode, ocf;
+
+	if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+		ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);
+		opcode = __le16_to_cpu(ec->opcode);
+		ocf = hci_opcode_ocf(opcode);
+		if (ocf == BT_CMD_MODULE_CFG_REQ &&
+					priv->btmrvl_dev.sendcmdflag) {
+			priv->btmrvl_dev.sendcmdflag = false;
+			priv->adapter->cmd_complete = true;
+			wake_up_interruptible(&priv->adapter->cmd_wait_q);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(btmrvl_check_evtpkt);
+
+int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+	struct btmrvl_adapter *adapter = priv->adapter;
+	struct btmrvl_event *event;
+	u8 ret = 0;
+
+	event = (struct btmrvl_event *) skb->data;
+	if (event->ec != 0xff) {
+		BT_DBG("Not Marvell Event=%x", event->ec);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	switch (event->data[0]) {
+	case BT_CMD_AUTO_SLEEP_MODE:
+		if (!event->data[2]) {
+			if (event->data[1] == BT_PS_ENABLE)
+				adapter->psmode = 1;
+			else
+				adapter->psmode = 0;
+			BT_DBG("PS Mode:%s",
+				(adapter->psmode) ? "Enable" : "Disable");
+		} else {
+			BT_DBG("PS Mode command failed");
+		}
+		break;
+
+	case BT_CMD_HOST_SLEEP_CONFIG:
+		if (!event->data[3])
+			BT_DBG("gpio=%x, gap=%x", event->data[1],
+							event->data[2]);
+		else
+			BT_DBG("HSCFG command failed");
+		break;
+
+	case BT_CMD_HOST_SLEEP_ENABLE:
+		if (!event->data[1]) {
+			adapter->hs_state = HS_ACTIVATED;
+			if (adapter->psmode)
+				adapter->ps_state = PS_SLEEP;
+			wake_up_interruptible(&adapter->cmd_wait_q);
+			BT_DBG("HS ACTIVATED!");
+		} else {
+			BT_DBG("HS Enable failed");
+		}
+		break;
+
+	case BT_CMD_MODULE_CFG_REQ:
+		if (priv->btmrvl_dev.sendcmdflag &&
+				event->data[1] == MODULE_BRINGUP_REQ) {
+			BT_DBG("EVENT:%s", (event->data[2]) ?
+				"Bring-up failed" : "Bring-up succeed");
+		} else if (priv->btmrvl_dev.sendcmdflag &&
+				event->data[1] == MODULE_SHUTDOWN_REQ) {
+			BT_DBG("EVENT:%s", (event->data[2]) ?
+				"Shutdown failed" : "Shutdown succeed");
+		} else {
+			BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP");
+			ret = -EINVAL;
+		}
+		break;
+
+	case BT_EVENT_POWER_STATE:
+		if (event->data[1] == BT_PS_SLEEP)
+			adapter->ps_state = PS_SLEEP;
+		BT_DBG("EVENT:%s",
+			(adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+		break;
+
+	default:
+		BT_DBG("Unknown Event=%d", event->data[0]);
+		ret = -EINVAL;
+		break;
+	}
+
+exit:
+	if (!ret)
+		kfree_skb(skb);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(btmrvl_process_event);
+
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
+{
+	struct sk_buff *skb;
+	struct btmrvl_cmd *cmd;
+	int ret = 0;
+
+	skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+	if (skb == NULL) {
+		BT_ERR("No free skb");
+		return -ENOMEM;
+	}
+
+	cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+	cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ));
+	cmd->length = 1;
+	cmd->data[0] = subcmd;
+
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+	skb->dev = (void *) priv->btmrvl_dev.hcidev;
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+
+	priv->btmrvl_dev.sendcmdflag = true;
+
+	priv->adapter->cmd_complete = false;
+
+	BT_DBG("Queue module cfg Command");
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+
+	if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
+				priv->adapter->cmd_complete,
+				msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) {
+		ret = -ETIMEDOUT;
+		BT_ERR("module_cfg_cmd(%x): timeout: %d",
+					subcmd, priv->btmrvl_dev.sendcmdflag);
+	}
+
+	BT_DBG("module cfg Command done");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
+
+static int btmrvl_enable_hs(struct btmrvl_private *priv)
+{
+	struct sk_buff *skb;
+	struct btmrvl_cmd *cmd;
+	int ret = 0;
+
+	skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+	if (skb == NULL) {
+		BT_ERR("No free skb");
+		return -ENOMEM;
+	}
+
+	cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+	cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_ENABLE));
+	cmd->length = 0;
+
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+	skb->dev = (void *) priv->btmrvl_dev.hcidev;
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+
+	BT_DBG("Queue hs enable Command");
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+
+	if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
+			priv->adapter->hs_state,
+			msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) {
+		ret = -ETIMEDOUT;
+		BT_ERR("timeout: %d, %d,%d", priv->adapter->hs_state,
+						priv->adapter->ps_state,
+						priv->adapter->wakeup_tries);
+	}
+
+	return ret;
+}
+
+int btmrvl_prepare_command(struct btmrvl_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	struct btmrvl_cmd *cmd;
+	int ret = 0;
+
+	if (priv->btmrvl_dev.hscfgcmd) {
+		priv->btmrvl_dev.hscfgcmd = 0;
+
+		skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+		if (skb == NULL) {
+			BT_ERR("No free skb");
+			return -ENOMEM;
+		}
+
+		cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+		cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_CONFIG));
+		cmd->length = 2;
+		cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
+		cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
+
+		bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+		skb->dev = (void *) priv->btmrvl_dev.hcidev;
+		skb_queue_head(&priv->adapter->tx_queue, skb);
+
+		BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x",
+						cmd->data[0], cmd->data[1]);
+	}
+
+	if (priv->btmrvl_dev.pscmd) {
+		priv->btmrvl_dev.pscmd = 0;
+
+		skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+		if (skb == NULL) {
+			BT_ERR("No free skb");
+			return -ENOMEM;
+		}
+
+		cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+		cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_AUTO_SLEEP_MODE));
+		cmd->length = 1;
+
+		if (priv->btmrvl_dev.psmode)
+			cmd->data[0] = BT_PS_ENABLE;
+		else
+			cmd->data[0] = BT_PS_DISABLE;
+
+		bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+		skb->dev = (void *) priv->btmrvl_dev.hcidev;
+		skb_queue_head(&priv->adapter->tx_queue, skb);
+
+		BT_DBG("Queue PSMODE Command:%d", cmd->data[0]);
+	}
+
+	if (priv->btmrvl_dev.hscmd) {
+		priv->btmrvl_dev.hscmd = 0;
+
+		if (priv->btmrvl_dev.hsmode) {
+			ret = btmrvl_enable_hs(priv);
+		} else {
+			ret = priv->hw_wakeup_firmware(priv);
+			priv->adapter->hs_state = HS_DEACTIVATED;
+		}
+	}
+
+	return ret;
+}
+
+static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+	int ret = 0;
+
+	if (!skb || !skb->data)
+		return -EINVAL;
+
+	if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) {
+		BT_ERR("Tx Error: Bad skb length %d : %d",
+						skb->len, BTM_UPLD_SIZE);
+		return -EINVAL;
+	}
+
+	if (skb_headroom(skb) < BTM_HEADER_LEN) {
+		struct sk_buff *tmp = skb;
+
+		skb = skb_realloc_headroom(skb, BTM_HEADER_LEN);
+		if (!skb) {
+			BT_ERR("Tx Error: realloc_headroom failed %d",
+				BTM_HEADER_LEN);
+			skb = tmp;
+			return -EINVAL;
+		}
+
+		kfree_skb(tmp);
+	}
+
+	skb_push(skb, BTM_HEADER_LEN);
+
+	/* header type: byte[3]
+	 * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor
+	 * header length: byte[2][1][0]
+	 */
+
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = bt_cb(skb)->pkt_type;
+
+	if (priv->hw_host_to_card)
+		ret = priv->hw_host_to_card(priv, skb->data, skb->len);
+
+	return ret;
+}
+
+static void btmrvl_init_adapter(struct btmrvl_private *priv)
+{
+	skb_queue_head_init(&priv->adapter->tx_queue);
+
+	priv->adapter->ps_state = PS_AWAKE;
+
+	init_waitqueue_head(&priv->adapter->cmd_wait_q);
+}
+
+static void btmrvl_free_adapter(struct btmrvl_private *priv)
+{
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	kfree(priv->adapter);
+
+	priv->adapter = NULL;
+}
+
+static int btmrvl_ioctl(struct hci_dev *hdev,
+				unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+static void btmrvl_destruct(struct hci_dev *hdev)
+{
+}
+
+static int btmrvl_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct btmrvl_private *priv = NULL;
+
+	BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
+
+	if (!hdev || !hdev->driver_data) {
+		BT_ERR("Frame for unknown HCI device");
+		return -ENODEV;
+	}
+
+	priv = (struct btmrvl_private *) hdev->driver_data;
+	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+		BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
+		print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
+							skb->data, skb->len);
+		return -EBUSY;
+	}
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	}
+
+	skb_queue_tail(&priv->adapter->tx_queue, skb);
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+
+	return 0;
+}
+
+static int btmrvl_flush(struct hci_dev *hdev)
+{
+	struct btmrvl_private *priv = hdev->driver_data;
+
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	return 0;
+}
+
+static int btmrvl_close(struct hci_dev *hdev)
+{
+	struct btmrvl_private *priv = hdev->driver_data;
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	return 0;
+}
+
+static int btmrvl_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &hdev->flags);
+
+	return 0;
+}
+
+/*
+ * This function handles the event generated by firmware, rx data
+ * received from firmware, and tx data sent from kernel.
+ */
+static int btmrvl_service_main_thread(void *data)
+{
+	struct btmrvl_thread *thread = data;
+	struct btmrvl_private *priv = thread->priv;
+	struct btmrvl_adapter *adapter = priv->adapter;
+	wait_queue_t wait;
+	struct sk_buff *skb;
+	ulong flags;
+
+	init_waitqueue_entry(&wait, current);
+
+	current->flags |= PF_NOFREEZE;
+
+	for (;;) {
+		add_wait_queue(&thread->wait_q, &wait);
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (adapter->wakeup_tries ||
+				((!adapter->int_count) &&
+				(!priv->btmrvl_dev.tx_dnld_rdy ||
+				skb_queue_empty(&adapter->tx_queue)))) {
+			BT_DBG("main_thread is sleeping...");
+			schedule();
+		}
+
+		set_current_state(TASK_RUNNING);
+
+		remove_wait_queue(&thread->wait_q, &wait);
+
+		BT_DBG("main_thread woke up");
+
+		if (kthread_should_stop()) {
+			BT_DBG("main_thread: break from main thread");
+			break;
+		}
+
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		if (adapter->int_count) {
+			adapter->int_count = 0;
+		} else if (adapter->ps_state == PS_SLEEP &&
+					!skb_queue_empty(&adapter->tx_queue)) {
+			spin_unlock_irqrestore(&priv->driver_lock, flags);
+			adapter->wakeup_tries++;
+			priv->hw_wakeup_firmware(priv);
+			continue;
+		}
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+		if (adapter->ps_state == PS_SLEEP)
+			continue;
+
+		if (!priv->btmrvl_dev.tx_dnld_rdy)
+			continue;
+
+		skb = skb_dequeue(&adapter->tx_queue);
+		if (skb) {
+			if (btmrvl_tx_pkt(priv, skb))
+				priv->btmrvl_dev.hcidev->stat.err_tx++;
+			else
+				priv->btmrvl_dev.hcidev->stat.byte_tx += skb->len;
+
+			kfree_skb(skb);
+		}
+	}
+
+	return 0;
+}
+
+struct btmrvl_private *btmrvl_add_card(void *card)
+{
+	struct hci_dev *hdev = NULL;
+	struct btmrvl_private *priv;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		BT_ERR("Can not allocate priv");
+		goto err_priv;
+	}
+
+	priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL);
+	if (!priv->adapter) {
+		BT_ERR("Allocate buffer for btmrvl_adapter failed!");
+		goto err_adapter;
+	}
+
+	btmrvl_init_adapter(priv);
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can not allocate HCI device");
+		goto err_hdev;
+	}
+
+	BT_DBG("Starting kthread...");
+	priv->main_thread.priv = priv;
+	spin_lock_init(&priv->driver_lock);
+
+	init_waitqueue_head(&priv->main_thread.wait_q);
+	priv->main_thread.task = kthread_run(btmrvl_service_main_thread,
+				&priv->main_thread, "btmrvl_main_service");
+
+	priv->btmrvl_dev.hcidev = hdev;
+	priv->btmrvl_dev.card = card;
+
+	hdev->driver_data = priv;
+
+	priv->btmrvl_dev.tx_dnld_rdy = true;
+
+	hdev->type = HCI_SDIO;
+	hdev->open = btmrvl_open;
+	hdev->close = btmrvl_close;
+	hdev->flush = btmrvl_flush;
+	hdev->send = btmrvl_send_frame;
+	hdev->destruct = btmrvl_destruct;
+	hdev->ioctl = btmrvl_ioctl;
+	hdev->owner = THIS_MODULE;
+
+	ret = hci_register_dev(hdev);
+	if (ret < 0) {
+		BT_ERR("Can not register HCI device");
+		goto err_hci_register_dev;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	btmrvl_debugfs_init(hdev);
+#endif
+
+	return priv;
+
+err_hci_register_dev:
+	/* Stop the thread servicing the interrupts */
+	kthread_stop(priv->main_thread.task);
+
+	hci_free_dev(hdev);
+
+err_hdev:
+	btmrvl_free_adapter(priv);
+
+err_adapter:
+	kfree(priv);
+
+err_priv:
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(btmrvl_add_card);
+
+int btmrvl_remove_card(struct btmrvl_private *priv)
+{
+	struct hci_dev *hdev;
+
+	hdev = priv->btmrvl_dev.hcidev;
+
+	wake_up_interruptible(&priv->adapter->cmd_wait_q);
+
+	kthread_stop(priv->main_thread.task);
+
+#ifdef CONFIG_DEBUG_FS
+	btmrvl_debugfs_remove(hdev);
+#endif
+
+	hci_unregister_dev(hdev);
+
+	hci_free_dev(hdev);
+
+	priv->btmrvl_dev.hcidev = NULL;
+
+	btmrvl_free_adapter(priv);
+
+	kfree(priv);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btmrvl_remove_card);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");

+ 1003 - 0
drivers/bluetooth/btmrvl_sdio.c

@@ -0,0 +1,1003 @@
+/**
+ * Marvell BT-over-SDIO driver: SDIO interface related functions.
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/firmware.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+#include "btmrvl_sdio.h"
+
+#define VERSION "1.0"
+
+/* The btmrvl_sdio_remove() callback function is called
+ * when user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently.
+ * If the user is removing the module, a MODULE_SHUTDOWN_REQ
+ * command is sent to firmware and interrupt will be disabled.
+ * If the card is removed, there is no need to send command
+ * or disable interrupt.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+static u8 user_rmmod;
+
+static const struct btmrvl_sdio_device btmrvl_sdio_sd6888 = {
+	.helper		= "sd8688_helper.bin",
+	.firmware	= "sd8688.bin",
+};
+
+static const struct sdio_device_id btmrvl_sdio_ids[] = {
+	/* Marvell SD8688 Bluetooth device */
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
+			.driver_data = (unsigned long) &btmrvl_sdio_sd6888 },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btmrvl_sdio_ids);
+
+static int btmrvl_sdio_get_rx_unit(struct btmrvl_sdio_card *card)
+{
+	u8 reg;
+	int ret;
+
+	reg = sdio_readb(card->func, CARD_RX_UNIT_REG, &ret);
+	if (!ret)
+		card->rx_unit = reg;
+
+	return ret;
+}
+
+static int btmrvl_sdio_read_fw_status(struct btmrvl_sdio_card *card, u16 *dat)
+{
+	u8 fws0, fws1;
+	int ret;
+
+	*dat = 0;
+
+	fws0 = sdio_readb(card->func, CARD_FW_STATUS0_REG, &ret);
+
+	if (!ret)
+		fws1 = sdio_readb(card->func, CARD_FW_STATUS1_REG, &ret);
+
+	if (ret)
+		return -EIO;
+
+	*dat = (((u16) fws1) << 8) | fws0;
+
+	return 0;
+}
+
+static int btmrvl_sdio_read_rx_len(struct btmrvl_sdio_card *card, u16 *dat)
+{
+	u8 reg;
+	int ret;
+
+	reg = sdio_readb(card->func, CARD_RX_LEN_REG, &ret);
+	if (!ret)
+		*dat = (u16) reg << card->rx_unit;
+
+	return ret;
+}
+
+static int btmrvl_sdio_enable_host_int_mask(struct btmrvl_sdio_card *card,
+								u8 mask)
+{
+	int ret;
+
+	sdio_writeb(card->func, mask, HOST_INT_MASK_REG, &ret);
+	if (ret) {
+		BT_ERR("Unable to enable the host interrupt!");
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static int btmrvl_sdio_disable_host_int_mask(struct btmrvl_sdio_card *card,
+								u8 mask)
+{
+	u8 host_int_mask;
+	int ret;
+
+	host_int_mask = sdio_readb(card->func, HOST_INT_MASK_REG, &ret);
+	if (ret)
+		return -EIO;
+
+	host_int_mask &= ~mask;
+
+	sdio_writeb(card->func, host_int_mask, HOST_INT_MASK_REG, &ret);
+	if (ret < 0) {
+		BT_ERR("Unable to disable the host interrupt!");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int btmrvl_sdio_poll_card_status(struct btmrvl_sdio_card *card, u8 bits)
+{
+	unsigned int tries;
+	u8 status;
+	int ret;
+
+	for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
+		status = sdio_readb(card->func, CARD_STATUS_REG, &ret);
+		if (ret)
+			goto failed;
+		if ((status & bits) == bits)
+			return ret;
+
+		udelay(1);
+	}
+
+	ret = -ETIMEDOUT;
+
+failed:
+	BT_ERR("FAILED! ret=%d", ret);
+
+	return ret;
+}
+
+static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card,
+								int pollnum)
+{
+	int ret = -ETIMEDOUT;
+	u16 firmwarestat;
+	unsigned int tries;
+
+	 /* Wait for firmware to become ready */
+	for (tries = 0; tries < pollnum; tries++) {
+		if (btmrvl_sdio_read_fw_status(card, &firmwarestat) < 0)
+			continue;
+
+		if (firmwarestat == FIRMWARE_READY) {
+			ret = 0;
+			break;
+		} else {
+			msleep(10);
+		}
+	}
+
+	return ret;
+}
+
+static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card)
+{
+	const struct firmware *fw_helper = NULL;
+	const u8 *helper = NULL;
+	int ret;
+	void *tmphlprbuf = NULL;
+	int tmphlprbufsz, hlprblknow, helperlen;
+	u8 *helperbuf;
+	u32 tx_len;
+
+	ret = request_firmware(&fw_helper, card->helper,
+						&card->func->dev);
+	if ((ret < 0) || !fw_helper) {
+		BT_ERR("request_firmware(helper) failed, error code = %d",
+									ret);
+		ret = -ENOENT;
+		goto done;
+	}
+
+	helper = fw_helper->data;
+	helperlen = fw_helper->size;
+
+	BT_DBG("Downloading helper image (%d bytes), block size %d bytes",
+						helperlen, SDIO_BLOCK_SIZE);
+
+	tmphlprbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN);
+
+	tmphlprbuf = kmalloc(tmphlprbufsz, GFP_KERNEL);
+	if (!tmphlprbuf) {
+		BT_ERR("Unable to allocate buffer for helper."
+			" Terminating download");
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	memset(tmphlprbuf, 0, tmphlprbufsz);
+
+	helperbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, BTSDIO_DMA_ALIGN);
+
+	/* Perform helper data transfer */
+	tx_len = (FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE)
+			- SDIO_HEADER_LEN;
+	hlprblknow = 0;
+
+	do {
+		ret = btmrvl_sdio_poll_card_status(card,
+					    CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			BT_ERR("Helper download poll status timeout @ %d",
+				hlprblknow);
+			goto done;
+		}
+
+		/* Check if there is more data? */
+		if (hlprblknow >= helperlen)
+			break;
+
+		if (helperlen - hlprblknow < tx_len)
+			tx_len = helperlen - hlprblknow;
+
+		/* Little-endian */
+		helperbuf[0] = ((tx_len & 0x000000ff) >> 0);
+		helperbuf[1] = ((tx_len & 0x0000ff00) >> 8);
+		helperbuf[2] = ((tx_len & 0x00ff0000) >> 16);
+		helperbuf[3] = ((tx_len & 0xff000000) >> 24);
+
+		memcpy(&helperbuf[SDIO_HEADER_LEN], &helper[hlprblknow],
+				tx_len);
+
+		/* Now send the data */
+		ret = sdio_writesb(card->func, card->ioport, helperbuf,
+				FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE);
+		if (ret < 0) {
+			BT_ERR("IO error during helper download @ %d",
+				hlprblknow);
+			goto done;
+		}
+
+		hlprblknow += tx_len;
+	} while (true);
+
+	BT_DBG("Transferring helper image EOF block");
+
+	memset(helperbuf, 0x0, SDIO_BLOCK_SIZE);
+
+	ret = sdio_writesb(card->func, card->ioport, helperbuf,
+							SDIO_BLOCK_SIZE);
+	if (ret < 0) {
+		BT_ERR("IO error in writing helper image EOF block");
+		goto done;
+	}
+
+	ret = 0;
+
+done:
+	kfree(tmphlprbuf);
+	if (fw_helper)
+		release_firmware(fw_helper);
+
+	return ret;
+}
+
+static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card)
+{
+	const struct firmware *fw_firmware = NULL;
+	const u8 *firmware = NULL;
+	int firmwarelen, tmpfwbufsz, ret;
+	unsigned int tries, offset;
+	u8 base0, base1;
+	void *tmpfwbuf = NULL;
+	u8 *fwbuf;
+	u16 len;
+	int txlen = 0, tx_blocks = 0, count = 0;
+
+	ret = request_firmware(&fw_firmware, card->firmware,
+							&card->func->dev);
+	if ((ret < 0) || !fw_firmware) {
+		BT_ERR("request_firmware(firmware) failed, error code = %d",
+									ret);
+		ret = -ENOENT;
+		goto done;
+	}
+
+	firmware = fw_firmware->data;
+	firmwarelen = fw_firmware->size;
+
+	BT_DBG("Downloading FW image (%d bytes)", firmwarelen);
+
+	tmpfwbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN);
+	tmpfwbuf = kmalloc(tmpfwbufsz, GFP_KERNEL);
+	if (!tmpfwbuf) {
+		BT_ERR("Unable to allocate buffer for firmware."
+		       " Terminating download");
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	memset(tmpfwbuf, 0, tmpfwbufsz);
+
+	/* Ensure aligned firmware buffer */
+	fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, BTSDIO_DMA_ALIGN);
+
+	/* Perform firmware data transfer */
+	offset = 0;
+	do {
+		ret = btmrvl_sdio_poll_card_status(card,
+					CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			BT_ERR("FW download with helper poll status"
+						" timeout @ %d", offset);
+			goto done;
+		}
+
+		/* Check if there is more data ? */
+		if (offset >= firmwarelen)
+			break;
+
+		for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+			base0 = sdio_readb(card->func,
+					SQ_READ_BASE_ADDRESS_A0_REG, &ret);
+			if (ret) {
+				BT_ERR("BASE0 register read failed:"
+					" base0 = 0x%04X(%d)."
+					" Terminating download",
+					base0, base0);
+				ret = -EIO;
+				goto done;
+			}
+			base1 = sdio_readb(card->func,
+					SQ_READ_BASE_ADDRESS_A1_REG, &ret);
+			if (ret) {
+				BT_ERR("BASE1 register read failed:"
+					" base1 = 0x%04X(%d)."
+					" Terminating download",
+					base1, base1);
+				ret = -EIO;
+				goto done;
+			}
+
+			len = (((u16) base1) << 8) | base0;
+			if (len)
+				break;
+
+			udelay(10);
+		}
+
+		if (!len)
+			break;
+		else if (len > BTM_UPLD_SIZE) {
+			BT_ERR("FW download failure @%d, invalid length %d",
+								offset, len);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		txlen = len;
+
+		if (len & BIT(0)) {
+			count++;
+			if (count > MAX_WRITE_IOMEM_RETRY) {
+				BT_ERR("FW download failure @%d, "
+					"over max retry count", offset);
+				ret = -EIO;
+				goto done;
+			}
+			BT_ERR("FW CRC error indicated by the helper: "
+				"len = 0x%04X, txlen = %d", len, txlen);
+			len &= ~BIT(0);
+			/* Set txlen to 0 so as to resend from same offset */
+			txlen = 0;
+		} else {
+			count = 0;
+
+			/* Last block ? */
+			if (firmwarelen - offset < txlen)
+				txlen = firmwarelen - offset;
+
+			tx_blocks =
+			    (txlen + SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE;
+
+			memcpy(fwbuf, &firmware[offset], txlen);
+		}
+
+		ret = sdio_writesb(card->func, card->ioport, fwbuf,
+					tx_blocks * SDIO_BLOCK_SIZE);
+
+		if (ret < 0) {
+			BT_ERR("FW download, writesb(%d) failed @%d",
+							count, offset);
+			sdio_writeb(card->func, HOST_CMD53_FIN, CONFIG_REG,
+									&ret);
+			if (ret)
+				BT_ERR("writeb failed (CFG)");
+		}
+
+		offset += txlen;
+	} while (true);
+
+	BT_DBG("FW download over, size %d bytes", offset);
+
+	ret = 0;
+
+done:
+	kfree(tmpfwbuf);
+
+	if (fw_firmware)
+		release_firmware(fw_firmware);
+
+	return ret;
+}
+
+static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
+{
+	u16 buf_len = 0;
+	int ret, buf_block_len, blksz;
+	struct sk_buff *skb = NULL;
+	u32 type;
+	u8 *payload = NULL;
+	struct hci_dev *hdev = priv->btmrvl_dev.hcidev;
+	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+
+	if (!card || !card->func) {
+		BT_ERR("card or function is NULL!");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Read the length of data to be transferred */
+	ret = btmrvl_sdio_read_rx_len(card, &buf_len);
+	if (ret < 0) {
+		BT_ERR("read rx_len failed");
+		ret = -EIO;
+		goto exit;
+	}
+
+	blksz = SDIO_BLOCK_SIZE;
+	buf_block_len = (buf_len + blksz - 1) / blksz;
+
+	if (buf_len <= SDIO_HEADER_LEN
+			|| (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
+		BT_ERR("invalid packet length: %d", buf_len);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Allocate buffer */
+	skb = bt_skb_alloc(buf_block_len * blksz + BTSDIO_DMA_ALIGN,
+								GFP_ATOMIC);
+	if (skb == NULL) {
+		BT_ERR("No free skb");
+		goto exit;
+	}
+
+	if ((unsigned long) skb->data & (BTSDIO_DMA_ALIGN - 1)) {
+		skb_put(skb, (unsigned long) skb->data &
+					(BTSDIO_DMA_ALIGN - 1));
+		skb_pull(skb, (unsigned long) skb->data &
+					(BTSDIO_DMA_ALIGN - 1));
+	}
+
+	payload = skb->data;
+
+	ret = sdio_readsb(card->func, payload, card->ioport,
+			  buf_block_len * blksz);
+	if (ret < 0) {
+		BT_ERR("readsb failed: %d", ret);
+		ret = -EIO;
+		goto exit;
+	}
+
+	/* This is SDIO specific header length: byte[2][1][0], type: byte[3]
+	 * (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor)
+	 */
+
+	buf_len = payload[0];
+	buf_len |= (u16) payload[1] << 8;
+	type = payload[3];
+
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+	case HCI_SCODATA_PKT:
+	case HCI_EVENT_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb->dev = (void *)hdev;
+		skb_put(skb, buf_len);
+		skb_pull(skb, SDIO_HEADER_LEN);
+
+		if (type == HCI_EVENT_PKT)
+			btmrvl_check_evtpkt(priv, skb);
+
+		hci_recv_frame(skb);
+		hdev->stat.byte_rx += buf_len;
+		break;
+
+	case MRVL_VENDOR_PKT:
+		bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+		skb->dev = (void *)hdev;
+		skb_put(skb, buf_len);
+		skb_pull(skb, SDIO_HEADER_LEN);
+
+		if (btmrvl_process_event(priv, skb))
+			hci_recv_frame(skb);
+
+		hdev->stat.byte_rx += buf_len;
+		break;
+
+	default:
+		BT_ERR("Unknow packet type:%d", type);
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload,
+						blksz * buf_block_len);
+
+		kfree_skb(skb);
+		skb = NULL;
+		break;
+	}
+
+exit:
+	if (ret) {
+		hdev->stat.err_rx++;
+		if (skb)
+			kfree_skb(skb);
+	}
+
+	return ret;
+}
+
+static int btmrvl_sdio_get_int_status(struct btmrvl_private *priv, u8 * ireg)
+{
+	int ret;
+	u8 sdio_ireg = 0;
+	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+
+	*ireg = 0;
+
+	sdio_ireg = sdio_readb(card->func, HOST_INTSTATUS_REG, &ret);
+	if (ret) {
+		BT_ERR("sdio_readb: read int status register failed");
+		ret = -EIO;
+		goto done;
+	}
+
+	if (sdio_ireg != 0) {
+		/*
+		 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+		 * Clear the interrupt status register and re-enable the
+		 * interrupt.
+		 */
+		BT_DBG("sdio_ireg = 0x%x", sdio_ireg);
+
+		sdio_writeb(card->func, ~(sdio_ireg) & (DN_LD_HOST_INT_STATUS |
+							UP_LD_HOST_INT_STATUS),
+			    HOST_INTSTATUS_REG, &ret);
+		if (ret) {
+			BT_ERR("sdio_writeb: clear int status register "
+				"failed");
+			ret = -EIO;
+			goto done;
+		}
+	}
+
+	if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
+		if (priv->btmrvl_dev.tx_dnld_rdy)
+			BT_DBG("tx_done already received: "
+				" int_status=0x%x", sdio_ireg);
+		else
+			priv->btmrvl_dev.tx_dnld_rdy = true;
+	}
+
+	if (sdio_ireg & UP_LD_HOST_INT_STATUS)
+		btmrvl_sdio_card_to_host(priv);
+
+	*ireg = sdio_ireg;
+
+	ret = 0;
+
+done:
+	return ret;
+}
+
+static void btmrvl_sdio_interrupt(struct sdio_func *func)
+{
+	struct btmrvl_private *priv;
+	struct hci_dev *hcidev;
+	struct btmrvl_sdio_card *card;
+	u8 ireg = 0;
+
+	card = sdio_get_drvdata(func);
+	if (card && card->priv) {
+		priv = card->priv;
+		hcidev = priv->btmrvl_dev.hcidev;
+
+		if (btmrvl_sdio_get_int_status(priv, &ireg))
+			BT_ERR("reading HOST_INT_STATUS_REG failed");
+		else
+			BT_DBG("HOST_INT_STATUS_REG %#x", ireg);
+
+		btmrvl_interrupt(priv);
+	}
+}
+
+static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
+{
+	struct sdio_func *func;
+	u8 reg;
+	int ret = 0;
+
+	if (!card || !card->func) {
+		BT_ERR("Error: card or function is NULL!");
+		ret = -EINVAL;
+		goto failed;
+	}
+
+	func = card->func;
+
+	sdio_claim_host(func);
+
+	ret = sdio_enable_func(func);
+	if (ret) {
+		BT_ERR("sdio_enable_func() failed: ret=%d", ret);
+		ret = -EIO;
+		goto release_host;
+	}
+
+	ret = sdio_claim_irq(func, btmrvl_sdio_interrupt);
+	if (ret) {
+		BT_ERR("sdio_claim_irq failed: ret=%d", ret);
+		ret = -EIO;
+		goto disable_func;
+	}
+
+	ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE);
+	if (ret) {
+		BT_ERR("cannot set SDIO block size");
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	reg = sdio_readb(func, IO_PORT_0_REG, &ret);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	card->ioport = reg;
+
+	reg = sdio_readb(func, IO_PORT_1_REG, &ret);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	card->ioport |= (reg << 8);
+
+	reg = sdio_readb(func, IO_PORT_2_REG, &ret);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	card->ioport |= (reg << 16);
+
+	BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport);
+
+	sdio_set_drvdata(func, card);
+
+	sdio_release_host(func);
+
+	return 0;
+
+release_irq:
+	sdio_release_irq(func);
+
+disable_func:
+	sdio_disable_func(func);
+
+release_host:
+	sdio_release_host(func);
+
+failed:
+	return ret;
+}
+
+static int btmrvl_sdio_unregister_dev(struct btmrvl_sdio_card *card)
+{
+	if (card && card->func) {
+		sdio_claim_host(card->func);
+		sdio_release_irq(card->func);
+		sdio_disable_func(card->func);
+		sdio_release_host(card->func);
+		sdio_set_drvdata(card->func, NULL);
+	}
+
+	return 0;
+}
+
+static int btmrvl_sdio_enable_host_int(struct btmrvl_sdio_card *card)
+{
+	int ret;
+
+	if (!card || !card->func)
+		return -EINVAL;
+
+	sdio_claim_host(card->func);
+
+	ret = btmrvl_sdio_enable_host_int_mask(card, HIM_ENABLE);
+
+	btmrvl_sdio_get_rx_unit(card);
+
+	sdio_release_host(card->func);
+
+	return ret;
+}
+
+static int btmrvl_sdio_disable_host_int(struct btmrvl_sdio_card *card)
+{
+	int ret;
+
+	if (!card || !card->func)
+		return -EINVAL;
+
+	sdio_claim_host(card->func);
+
+	ret = btmrvl_sdio_disable_host_int_mask(card, HIM_DISABLE);
+
+	sdio_release_host(card->func);
+
+	return ret;
+}
+
+static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
+				u8 *payload, u16 nb)
+{
+	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+	int ret = 0;
+	int buf_block_len;
+	int blksz;
+	int i = 0;
+	u8 *buf = NULL;
+	void *tmpbuf = NULL;
+	int tmpbufsz;
+
+	if (!card || !card->func) {
+		BT_ERR("card or function is NULL!");
+		return -EINVAL;
+	}
+
+	buf = payload;
+	if ((unsigned long) payload & (BTSDIO_DMA_ALIGN - 1)) {
+		tmpbufsz = ALIGN_SZ(nb, BTSDIO_DMA_ALIGN);
+		tmpbuf = kzalloc(tmpbufsz, GFP_KERNEL);
+		if (!tmpbuf)
+			return -ENOMEM;
+		buf = (u8 *) ALIGN_ADDR(tmpbuf, BTSDIO_DMA_ALIGN);
+		memcpy(buf, payload, nb);
+	}
+
+	blksz = SDIO_BLOCK_SIZE;
+	buf_block_len = (nb + blksz - 1) / blksz;
+
+	sdio_claim_host(card->func);
+
+	do {
+		/* Transfer data to card */
+		ret = sdio_writesb(card->func, card->ioport, buf,
+				   buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			BT_ERR("i=%d writesb failed: %d", i, ret);
+			print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+						payload, nb);
+			ret = -EIO;
+			if (i > MAX_WRITE_IOMEM_RETRY)
+				goto exit;
+		}
+	} while (ret);
+
+	priv->btmrvl_dev.tx_dnld_rdy = false;
+
+exit:
+	sdio_release_host(card->func);
+
+	return ret;
+}
+
+static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
+{
+	int ret = 0;
+
+	if (!card || !card->func) {
+		BT_ERR("card or function is NULL!");
+		return -EINVAL;
+	}
+	sdio_claim_host(card->func);
+
+	if (!btmrvl_sdio_verify_fw_download(card, 1)) {
+		BT_DBG("Firmware already downloaded!");
+		goto done;
+	}
+
+	ret = btmrvl_sdio_download_helper(card);
+	if (ret) {
+		BT_ERR("Failed to download helper!");
+		ret = -EIO;
+		goto done;
+	}
+
+	if (btmrvl_sdio_download_fw_w_helper(card)) {
+		BT_ERR("Failed to download firmware!");
+		ret = -EIO;
+		goto done;
+	}
+
+	if (btmrvl_sdio_verify_fw_download(card, MAX_POLL_TRIES)) {
+		BT_ERR("FW failed to be active in time!");
+		ret = -ETIMEDOUT;
+		goto done;
+	}
+
+done:
+	sdio_release_host(card->func);
+
+	return ret;
+}
+
+static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv)
+{
+	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+	int ret = 0;
+
+	if (!card || !card->func) {
+		BT_ERR("card or function is NULL!");
+		return -EINVAL;
+	}
+
+	sdio_claim_host(card->func);
+
+	sdio_writeb(card->func, HOST_POWER_UP, CONFIG_REG, &ret);
+
+	sdio_release_host(card->func);
+
+	BT_DBG("wake up firmware");
+
+	return ret;
+}
+
+static int btmrvl_sdio_probe(struct sdio_func *func,
+					const struct sdio_device_id *id)
+{
+	int ret = 0;
+	struct btmrvl_private *priv = NULL;
+	struct btmrvl_sdio_card *card = NULL;
+
+	BT_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d",
+			id->vendor, id->device, id->class, func->num);
+
+	card = kzalloc(sizeof(*card), GFP_KERNEL);
+	if (!card) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	card->func = func;
+
+	if (id->driver_data) {
+		struct btmrvl_sdio_device *data = (void *) id->driver_data;
+		card->helper   = data->helper;
+		card->firmware = data->firmware;
+	}
+
+	if (btmrvl_sdio_register_dev(card) < 0) {
+		BT_ERR("Failed to register BT device!");
+		ret = -ENODEV;
+		goto free_card;
+	}
+
+	/* Disable the interrupts on the card */
+	btmrvl_sdio_disable_host_int(card);
+
+	if (btmrvl_sdio_download_fw(card)) {
+		BT_ERR("Downloading firmware failed!");
+		ret = -ENODEV;
+		goto unreg_dev;
+	}
+
+	msleep(100);
+
+	btmrvl_sdio_enable_host_int(card);
+
+	priv = btmrvl_add_card(card);
+	if (!priv) {
+		BT_ERR("Initializing card failed!");
+		ret = -ENODEV;
+		goto disable_host_int;
+	}
+
+	card->priv = priv;
+
+	/* Initialize the interface specific function pointers */
+	priv->hw_host_to_card = btmrvl_sdio_host_to_card;
+	priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
+
+	btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+
+	return 0;
+
+disable_host_int:
+	btmrvl_sdio_disable_host_int(card);
+unreg_dev:
+	btmrvl_sdio_unregister_dev(card);
+free_card:
+	kfree(card);
+done:
+	return ret;
+}
+
+static void btmrvl_sdio_remove(struct sdio_func *func)
+{
+	struct btmrvl_sdio_card *card;
+
+	if (func) {
+		card = sdio_get_drvdata(func);
+		if (card) {
+			/* Send SHUTDOWN command & disable interrupt
+			 * if user removes the module.
+			 */
+			if (user_rmmod) {
+				btmrvl_send_module_cfg_cmd(card->priv,
+							MODULE_SHUTDOWN_REQ);
+				btmrvl_sdio_disable_host_int(card);
+			}
+			BT_DBG("unregester dev");
+			btmrvl_sdio_unregister_dev(card);
+			btmrvl_remove_card(card->priv);
+			kfree(card);
+		}
+	}
+}
+
+static struct sdio_driver bt_mrvl_sdio = {
+	.name		= "btmrvl_sdio",
+	.id_table	= btmrvl_sdio_ids,
+	.probe		= btmrvl_sdio_probe,
+	.remove		= btmrvl_sdio_remove,
+};
+
+static int btmrvl_sdio_init_module(void)
+{
+	if (sdio_register_driver(&bt_mrvl_sdio) != 0) {
+		BT_ERR("SDIO Driver Registration Failed");
+		return -ENODEV;
+	}
+
+	/* Clear the flag in case user removes the card. */
+	user_rmmod = 0;
+
+	return 0;
+}
+
+static void btmrvl_sdio_exit_module(void)
+{
+	/* Set the flag as user is removing this module. */
+	user_rmmod = 1;
+
+	sdio_unregister_driver(&bt_mrvl_sdio);
+}
+
+module_init(btmrvl_sdio_init_module);
+module_exit(btmrvl_sdio_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");

+ 108 - 0
drivers/bluetooth/btmrvl_sdio.h

@@ -0,0 +1,108 @@
+/**
+ * Marvell BT-over-SDIO driver: SDIO interface related definitions
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ *
+ **/
+
+#define SDIO_HEADER_LEN			4
+
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/* define SD block size for data Tx/Rx */
+#define SDIO_BLOCK_SIZE			64
+
+/* Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK	2
+
+/* This is for firmware specific length */
+#define FW_EXTRA_LEN			36
+
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+	(HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN)
+
+#define ALLOC_BUF_SIZE	(((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+			MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+			+ SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
+			* SDIO_BLOCK_SIZE)
+
+/* The number of times to try when polling for status */
+#define MAX_POLL_TRIES			100
+
+/* Max retry number of CMD53 write */
+#define MAX_WRITE_IOMEM_RETRY		2
+
+/* Host Control Registers */
+#define IO_PORT_0_REG			0x00
+#define IO_PORT_1_REG			0x01
+#define IO_PORT_2_REG			0x02
+
+#define CONFIG_REG			0x03
+#define HOST_POWER_UP			BIT(1)
+#define HOST_CMD53_FIN			BIT(2)
+
+#define HOST_INT_MASK_REG		0x04
+#define HIM_DISABLE			0xff
+#define HIM_ENABLE			(BIT(0) | BIT(1))
+
+#define HOST_INTSTATUS_REG		0x05
+#define UP_LD_HOST_INT_STATUS		BIT(0)
+#define DN_LD_HOST_INT_STATUS		BIT(1)
+
+/* Card Control Registers */
+#define SQ_READ_BASE_ADDRESS_A0_REG  	0x10
+#define SQ_READ_BASE_ADDRESS_A1_REG  	0x11
+
+#define CARD_STATUS_REG              	0x20
+#define DN_LD_CARD_RDY               	BIT(0)
+#define CARD_IO_READY              	BIT(3)
+
+#define CARD_FW_STATUS0_REG		0x40
+#define CARD_FW_STATUS1_REG		0x41
+#define FIRMWARE_READY			0xfedc
+
+#define CARD_RX_LEN_REG			0x42
+#define CARD_RX_UNIT_REG		0x43
+
+
+struct btmrvl_sdio_card {
+	struct sdio_func *func;
+	u32 ioport;
+	const char *helper;
+	const char *firmware;
+	u8 rx_unit;
+	struct btmrvl_private *priv;
+};
+
+struct btmrvl_sdio_device {
+	const char *helper;
+	const char *firmware;
+};
+
+
+/* Platform specific DMA alignment */
+#define BTSDIO_DMA_ALIGN		8
+
+/* Macros for Data Alignment : size */
+#define ALIGN_SZ(p, a)	\
+	(((p) + ((a) - 1)) & ~((a) - 1))
+
+/* Macros for Data Alignment : address */
+#define ALIGN_ADDR(p, a)	\
+	((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \
+					~(((unsigned long)(a)) - 1))

+ 175 - 23
drivers/bluetooth/btusb.c

@@ -35,7 +35,7 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-#define VERSION "0.5"
+#define VERSION "0.6"
 
 static int ignore_dga;
 static int ignore_csr;
@@ -145,6 +145,7 @@ static struct usb_device_id blacklist_table[] = {
 #define BTUSB_INTR_RUNNING	0
 #define BTUSB_BULK_RUNNING	1
 #define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
 
 struct btusb_data {
 	struct hci_dev       *hdev;
@@ -157,11 +158,15 @@ struct btusb_data {
 	unsigned long flags;
 
 	struct work_struct work;
+	struct work_struct waker;
 
 	struct usb_anchor tx_anchor;
 	struct usb_anchor intr_anchor;
 	struct usb_anchor bulk_anchor;
 	struct usb_anchor isoc_anchor;
+	struct usb_anchor deferred;
+	int tx_in_flight;
+	spinlock_t txlock;
 
 	struct usb_endpoint_descriptor *intr_ep;
 	struct usb_endpoint_descriptor *bulk_tx_ep;
@@ -174,8 +179,23 @@ struct btusb_data {
 	unsigned int sco_num;
 	int isoc_altsetting;
 	int suspend_count;
+	int did_iso_resume:1;
 };
 
+static int inc_tx(struct btusb_data *data)
+{
+	unsigned long flags;
+	int rv;
+
+	spin_lock_irqsave(&data->txlock, flags);
+	rv = test_bit(BTUSB_SUSPENDING, &data->flags);
+	if (!rv)
+		data->tx_in_flight++;
+	spin_unlock_irqrestore(&data->txlock, flags);
+
+	return rv;
+}
+
 static void btusb_intr_complete(struct urb *urb)
 {
 	struct hci_dev *hdev = urb->context;
@@ -202,6 +222,7 @@ static void btusb_intr_complete(struct urb *urb)
 	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
 		return;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->intr_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -301,7 +322,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
 	struct urb *urb;
 	unsigned char *buf;
 	unsigned int pipe;
-	int err, size;
+	int err, size = HCI_MAX_FRAME_SIZE;
 
 	BT_DBG("%s", hdev->name);
 
@@ -312,8 +333,6 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
 	if (!urb)
 		return -ENOMEM;
 
-	size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
-
 	buf = kmalloc(size, mem_flags);
 	if (!buf) {
 		usb_free_urb(urb);
@@ -327,6 +346,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
 
 	urb->transfer_flags |= URB_FREE_BUFFER;
 
+	usb_mark_last_busy(data->udev);
 	usb_anchor_urb(urb, &data->bulk_anchor);
 
 	err = usb_submit_urb(urb, mem_flags);
@@ -462,6 +482,33 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
 }
 
 static void btusb_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = urb->context;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct btusb_data *data = hdev->driver_data;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		goto done;
+
+	if (!urb->status)
+		hdev->stat.byte_tx += urb->transfer_buffer_length;
+	else
+		hdev->stat.err_tx++;
+
+done:
+	spin_lock(&data->txlock);
+	data->tx_in_flight--;
+	spin_unlock(&data->txlock);
+
+	kfree(urb->setup_packet);
+
+	kfree_skb(skb);
+}
+
+static void btusb_isoc_tx_complete(struct urb *urb)
 {
 	struct sk_buff *skb = urb->context;
 	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
@@ -490,11 +537,17 @@ static int btusb_open(struct hci_dev *hdev)
 
 	BT_DBG("%s", hdev->name);
 
+	err = usb_autopm_get_interface(data->intf);
+	if (err < 0)
+		return err;
+
+	data->intf->needs_remote_wakeup = 1;
+
 	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
+		goto done;
 
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
-		return 0;
+		goto done;
 
 	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
 	if (err < 0)
@@ -509,17 +562,28 @@ static int btusb_open(struct hci_dev *hdev)
 	set_bit(BTUSB_BULK_RUNNING, &data->flags);
 	btusb_submit_bulk_urb(hdev, GFP_KERNEL);
 
+done:
+	usb_autopm_put_interface(data->intf);
 	return 0;
 
 failed:
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
 	clear_bit(HCI_RUNNING, &hdev->flags);
+	usb_autopm_put_interface(data->intf);
 	return err;
 }
 
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
 static int btusb_close(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hdev->driver_data;
+	int err;
 
 	BT_DBG("%s", hdev->name);
 
@@ -529,13 +593,16 @@ static int btusb_close(struct hci_dev *hdev)
 	cancel_work_sync(&data->work);
 
 	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->isoc_anchor);
-
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	usb_kill_anchored_urbs(&data->intr_anchor);
+
+	btusb_stop_traffic(data);
+	err = usb_autopm_get_interface(data->intf);
+	if (err < 0)
+		return 0;
+
+	data->intf->needs_remote_wakeup = 0;
+	usb_autopm_put_interface(data->intf);
 
 	return 0;
 }
@@ -622,7 +689,7 @@ static int btusb_send_frame(struct sk_buff *skb)
 		urb->dev      = data->udev;
 		urb->pipe     = pipe;
 		urb->context  = skb;
-		urb->complete = btusb_tx_complete;
+		urb->complete = btusb_isoc_tx_complete;
 		urb->interval = data->isoc_tx_ep->bInterval;
 
 		urb->transfer_flags  = URB_ISO_ASAP;
@@ -633,12 +700,21 @@ static int btusb_send_frame(struct sk_buff *skb)
 				le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
 
 		hdev->stat.sco_tx++;
-		break;
+		goto skip_waking;
 
 	default:
 		return -EILSEQ;
 	}
 
+	err = inc_tx(data);
+	if (err) {
+		usb_anchor_urb(urb, &data->deferred);
+		schedule_work(&data->waker);
+		err = 0;
+		goto done;
+	}
+
+skip_waking:
 	usb_anchor_urb(urb, &data->tx_anchor);
 
 	err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -646,10 +722,13 @@ static int btusb_send_frame(struct sk_buff *skb)
 		BT_ERR("%s urb %p submission failed", hdev->name, urb);
 		kfree(urb->setup_packet);
 		usb_unanchor_urb(urb);
+	} else {
+		usb_mark_last_busy(data->udev);
 	}
 
 	usb_free_urb(urb);
 
+done:
 	return err;
 }
 
@@ -721,8 +800,19 @@ static void btusb_work(struct work_struct *work)
 {
 	struct btusb_data *data = container_of(work, struct btusb_data, work);
 	struct hci_dev *hdev = data->hdev;
+	int err;
 
 	if (hdev->conn_hash.sco_num > 0) {
+		if (!data->did_iso_resume) {
+			err = usb_autopm_get_interface(data->isoc);
+			if (err < 0) {
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->isoc_anchor);
+				return;
+			}
+
+			data->did_iso_resume = 1;
+		}
 		if (data->isoc_altsetting != 2) {
 			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
 			usb_kill_anchored_urbs(&data->isoc_anchor);
@@ -742,9 +832,25 @@ static void btusb_work(struct work_struct *work)
 		usb_kill_anchored_urbs(&data->isoc_anchor);
 
 		__set_isoc_interface(hdev, 0);
+		if (data->did_iso_resume) {
+			data->did_iso_resume = 0;
+			usb_autopm_put_interface(data->isoc);
+		}
 	}
 }
 
+static void btusb_waker(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, waker);
+	int err;
+
+	err = usb_autopm_get_interface(data->intf);
+	if (err < 0)
+		return;
+
+	usb_autopm_put_interface(data->intf);
+}
+
 static int btusb_probe(struct usb_interface *intf,
 				const struct usb_device_id *id)
 {
@@ -814,11 +920,14 @@ static int btusb_probe(struct usb_interface *intf,
 	spin_lock_init(&data->lock);
 
 	INIT_WORK(&data->work, btusb_work);
+	INIT_WORK(&data->waker, btusb_waker);
+	spin_lock_init(&data->txlock);
 
 	init_usb_anchor(&data->tx_anchor);
 	init_usb_anchor(&data->intr_anchor);
 	init_usb_anchor(&data->bulk_anchor);
 	init_usb_anchor(&data->isoc_anchor);
+	init_usb_anchor(&data->deferred);
 
 	hdev = hci_alloc_dev();
 	if (!hdev) {
@@ -943,6 +1052,7 @@ static void btusb_disconnect(struct usb_interface *intf)
 	hci_free_dev(hdev);
 }
 
+#ifdef CONFIG_PM
 static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
 {
 	struct btusb_data *data = usb_get_intfdata(intf);
@@ -952,22 +1062,44 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
 	if (data->suspend_count++)
 		return 0;
 
+	spin_lock_irq(&data->txlock);
+	if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) {
+		set_bit(BTUSB_SUSPENDING, &data->flags);
+		spin_unlock_irq(&data->txlock);
+	} else {
+		spin_unlock_irq(&data->txlock);
+		data->suspend_count--;
+		return -EBUSY;
+	}
+
 	cancel_work_sync(&data->work);
 
+	btusb_stop_traffic(data);
 	usb_kill_anchored_urbs(&data->tx_anchor);
 
-	usb_kill_anchored_urbs(&data->isoc_anchor);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-	usb_kill_anchored_urbs(&data->intr_anchor);
-
 	return 0;
 }
 
+static void play_deferred(struct btusb_data *data)
+{
+	struct urb *urb;
+	int err;
+
+	while ((urb = usb_get_from_anchor(&data->deferred))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0)
+			break;
+
+		data->tx_in_flight++;
+	}
+	usb_scuttle_anchored_urbs(&data->deferred);
+}
+
 static int btusb_resume(struct usb_interface *intf)
 {
 	struct btusb_data *data = usb_get_intfdata(intf);
 	struct hci_dev *hdev = data->hdev;
-	int err;
+	int err = 0;
 
 	BT_DBG("intf %p", intf);
 
@@ -975,13 +1107,13 @@ static int btusb_resume(struct usb_interface *intf)
 		return 0;
 
 	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
+		goto done;
 
 	if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
 		err = btusb_submit_intr_urb(hdev, GFP_NOIO);
 		if (err < 0) {
 			clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-			return err;
+			goto failed;
 		}
 	}
 
@@ -989,9 +1121,10 @@ static int btusb_resume(struct usb_interface *intf)
 		err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
 		if (err < 0) {
 			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			return err;
-		} else
-			btusb_submit_bulk_urb(hdev, GFP_NOIO);
+			goto failed;
+		}
+
+		btusb_submit_bulk_urb(hdev, GFP_NOIO);
 	}
 
 	if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
@@ -1001,16 +1134,35 @@ static int btusb_resume(struct usb_interface *intf)
 			btusb_submit_isoc_urb(hdev, GFP_NOIO);
 	}
 
+	spin_lock_irq(&data->txlock);
+	play_deferred(data);
+	clear_bit(BTUSB_SUSPENDING, &data->flags);
+	spin_unlock_irq(&data->txlock);
+	schedule_work(&data->work);
+
 	return 0;
+
+failed:
+	usb_scuttle_anchored_urbs(&data->deferred);
+done:
+	spin_lock_irq(&data->txlock);
+	clear_bit(BTUSB_SUSPENDING, &data->flags);
+	spin_unlock_irq(&data->txlock);
+
+	return err;
 }
+#endif
 
 static struct usb_driver btusb_driver = {
 	.name		= "btusb",
 	.probe		= btusb_probe,
 	.disconnect	= btusb_disconnect,
+#ifdef CONFIG_PM
 	.suspend	= btusb_suspend,
 	.resume		= btusb_resume,
+#endif
 	.id_table	= btusb_table,
+	.supports_autosuspend = 1,
 };
 
 static int __init btusb_init(void)

+ 2 - 1
drivers/bluetooth/hci_bcsp.c

@@ -373,8 +373,9 @@ static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
 
 	i = 0;
 	skb_queue_walk_safe(&bcsp->unack, skb, tmp) {
-		if (i++ >= pkts_to_be_removed)
+		if (i >= pkts_to_be_removed)
 			break;
+		i++;
 
 		__skb_unlink(skb, &bcsp->unack);
 		kfree_skb(skb);

+ 3 - 4
drivers/char/pcmcia/synclink_cs.c

@@ -4005,10 +4005,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
  *
  * skb  socket buffer containing HDLC frame
  * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
  */
-static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
+				      struct net_device *dev)
 {
 	MGSLPC_INFO *info = dev_to_port(dev);
 	unsigned long flags;
@@ -4043,7 +4042,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
 	}
 	spin_unlock_irqrestore(&info->lock,flags);
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 /**

+ 3 - 4
drivers/char/synclink.c

@@ -7697,10 +7697,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
  *
  * skb  socket buffer containing HDLC frame
  * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
  */
-static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
+				      struct net_device *dev)
 {
 	struct mgsl_struct *info = dev_to_port(dev);
 	unsigned long flags;
@@ -7731,7 +7730,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
 	 	usc_start_transmitter(info);
 	spin_unlock_irqrestore(&info->irq_spinlock,flags);
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 /**

+ 3 - 4
drivers/char/synclink_gt.c

@@ -1497,10 +1497,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
  *
  * skb  socket buffer containing HDLC frame
  * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
  */
-static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
+				      struct net_device *dev)
 {
 	struct slgt_info *info = dev_to_port(dev);
 	unsigned long flags;
@@ -1529,7 +1528,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
 	update_tx_timer(info);
 	spin_unlock_irqrestore(&info->lock,flags);
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 /**

+ 3 - 4
drivers/char/synclinkmp.c

@@ -1608,10 +1608,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
  *
  * skb  socket buffer containing HDLC frame
  * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
  */
-static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
+				      struct net_device *dev)
 {
 	SLMP_INFO *info = dev_to_port(dev);
 	unsigned long flags;
@@ -1642,7 +1641,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
 	 	tx_start(info);
 	spin_unlock_irqrestore(&info->lock,flags);
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 /**

+ 1 - 2
drivers/connector/cn_proc.c

@@ -202,9 +202,8 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
  * cn_proc_mcast_ctl
  * @data: message sent from userspace via the connector
  */
-static void cn_proc_mcast_ctl(void *data)
+static void cn_proc_mcast_ctl(struct cn_msg *msg)
 {
-	struct cn_msg *msg = data;
 	enum proc_cn_mcast_op *mc_op = NULL;
 	int err = 0;
 

+ 5 - 2
drivers/connector/cn_queue.c

@@ -87,7 +87,9 @@ void cn_queue_wrapper(struct work_struct *work)
 	kfree(d->free);
 }
 
-static struct cn_callback_entry *cn_queue_alloc_callback_entry(char *name, struct cb_id *id, void (*callback)(void *))
+static struct cn_callback_entry *
+cn_queue_alloc_callback_entry(char *name, struct cb_id *id,
+			      void (*callback)(struct cn_msg *))
 {
 	struct cn_callback_entry *cbq;
 
@@ -120,7 +122,8 @@ int cn_cb_equal(struct cb_id *i1, struct cb_id *i2)
 	return ((i1->idx == i2->idx) && (i1->val == i2->val));
 }
 
-int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id, void (*callback)(void *))
+int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id,
+			  void (*callback)(struct cn_msg *))
 {
 	struct cn_callback_entry *cbq, *__cbq;
 	int found = 0;

+ 3 - 3
drivers/connector/connector.c

@@ -269,7 +269,8 @@ static void cn_notify(struct cb_id *id, u32 notify_event)
  *
  * May sleep.
  */
-int cn_add_callback(struct cb_id *id, char *name, void (*callback)(void *))
+int cn_add_callback(struct cb_id *id, char *name,
+		    void (*callback)(struct cn_msg *))
 {
 	int err;
 	struct cn_dev *dev = &cdev;
@@ -351,9 +352,8 @@ static int cn_ctl_msg_equals(struct cn_ctl_msg *m1, struct cn_ctl_msg *m2)
  *
  * Used for notification of a request's processing.
  */
-static void cn_callback(void *data)
+static void cn_callback(struct cn_msg *msg)
 {
-	struct cn_msg *msg = data;
 	struct cn_ctl_msg *ctl;
 	struct cn_ctl_entry *ent;
 	u32 size;

+ 2 - 4
drivers/firewire/net.c

@@ -663,8 +663,6 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
 	if (netif_queue_stopped(net))
 		netif_wake_queue(net);
 
-	net->last_rx = jiffies;
-
 	return 0;
 }
 
@@ -1188,7 +1186,7 @@ static int fwnet_stop(struct net_device *net)
 	return 0;
 }
 
-static int fwnet_tx(struct sk_buff *skb, struct net_device *net)
+static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
 {
 	struct fwnet_header hdr_buf;
 	struct fwnet_device *dev = netdev_priv(net);
@@ -1342,7 +1340,7 @@ static void fwnet_get_drvinfo(struct net_device *net,
 	strcpy(info->bus_info, "ieee1394");
 }
 
-static struct ethtool_ops fwnet_ethtool_ops = {
+static const struct ethtool_ops fwnet_ethtool_ops = {
 	.get_drvinfo = fwnet_get_drvinfo,
 };
 

+ 6 - 13
drivers/ieee1394/eth1394.c

@@ -169,10 +169,11 @@ static int ether1394_header_cache(const struct neighbour *neigh,
 static void ether1394_header_cache_update(struct hh_cache *hh,
 					  const struct net_device *dev,
 					  const unsigned char *haddr);
-static int ether1394_tx(struct sk_buff *skb, struct net_device *dev);
+static netdev_tx_t ether1394_tx(struct sk_buff *skb,
+				struct net_device *dev);
 static void ether1394_iso(struct hpsb_iso *iso);
 
-static struct ethtool_ops ethtool_ops;
+static const struct ethtool_ops ethtool_ops;
 
 static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
 			   quadlet_t *data, u64 addr, size_t len, u16 flags);
@@ -1300,7 +1301,6 @@ static void ether1394_iso(struct hpsb_iso *iso)
 
 	hpsb_iso_recv_release_packets(iso, i);
 
-	dev->last_rx = jiffies;
 }
 
 /******************************************
@@ -1555,7 +1555,8 @@ static void ether1394_complete_cb(void *__ptask)
 }
 
 /* Transmit a packet (called by kernel) */
-static int ether1394_tx(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t ether1394_tx(struct sk_buff *skb,
+				struct net_device *dev)
 {
 	struct eth1394hdr hdr_buf;
 	struct eth1394_priv *priv = netdev_priv(dev);
@@ -1694,14 +1695,6 @@ fail:
 	dev->stats.tx_errors++;
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	/*
-	 * FIXME: According to a patch from 2003-02-26, "returning non-zero
-	 * causes serious problems" here, allegedly.  Before that patch,
-	 * -ERRNO was returned which is not appropriate under Linux 2.6.
-	 * Perhaps more needs to be done?  Stop the queue in serious
-	 * conditions and restart it elsewhere?
-	 */
-	/* return NETDEV_TX_BUSY; */
 	return NETDEV_TX_OK;
 }
 
@@ -1712,7 +1705,7 @@ static void ether1394_get_drvinfo(struct net_device *dev,
 	strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */
 }
 
-static struct ethtool_ops ethtool_ops = {
+static const struct ethtool_ops ethtool_ops = {
 	.get_drvinfo = ether1394_get_drvinfo
 };
 

+ 175 - 16
drivers/ieee802154/fakehard.c

@@ -26,11 +26,23 @@
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
 
-#include <net/ieee802154/af_ieee802154.h>
-#include <net/ieee802154/netdevice.h>
-#include <net/ieee802154/mac_def.h>
-#include <net/ieee802154/nl802154.h>
+#include <net/af_ieee802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/ieee802154.h>
+#include <net/nl802154.h>
+#include <net/wpan-phy.h>
 
+struct wpan_phy *net_to_phy(struct net_device *dev)
+{
+	return container_of(dev->dev.parent, struct wpan_phy, dev);
+}
+
+/**
+ * fake_get_pan_id - Retrieve the PAN ID of the device.
+ * @dev: The network device to retrieve the PAN of.
+ *
+ * Return the ID of the PAN from the PIB.
+ */
 static u16 fake_get_pan_id(struct net_device *dev)
 {
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -38,6 +50,14 @@ static u16 fake_get_pan_id(struct net_device *dev)
 	return 0xeba1;
 }
 
+/**
+ * fake_get_short_addr - Retrieve the short address of the device.
+ * @dev: The network device to retrieve the short address of.
+ *
+ * Returns the IEEE 802.15.4 short-form address cached for this
+ * device. If the device has not yet had a short address assigned
+ * then this should return 0xFFFF to indicate a lack of association.
+ */
 static u16 fake_get_short_addr(struct net_device *dev)
 {
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -45,6 +65,19 @@ static u16 fake_get_short_addr(struct net_device *dev)
 	return 0x1;
 }
 
+/**
+ * fake_get_dsn - Retrieve the DSN of the device.
+ * @dev: The network device to retrieve the DSN for.
+ *
+ * Returns the IEEE 802.15.4 DSN for the network device.
+ * The DSN is the sequence number which will be added to each
+ * packet or MAC command frame by the MAC during transmission.
+ *
+ * DSN means 'Data Sequence Number'.
+ *
+ * Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
+ *       document.
+ */
 static u8 fake_get_dsn(struct net_device *dev)
 {
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -52,6 +85,19 @@ static u8 fake_get_dsn(struct net_device *dev)
 	return 0x00; /* DSN are implemented in HW, so return just 0 */
 }
 
+/**
+ * fake_get_bsn - Retrieve the BSN of the device.
+ * @dev: The network device to retrieve the BSN for.
+ *
+ * Returns the IEEE 802.15.4 BSN for the network device.
+ * The BSN is the sequence number which will be added to each
+ * beacon frame sent by the MAC.
+ *
+ * BSN means 'Beacon Sequence Number'.
+ *
+ * Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
+ *       document.
+ */
 static u8 fake_get_bsn(struct net_device *dev)
 {
 	BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -59,40 +105,130 @@ static u8 fake_get_bsn(struct net_device *dev)
 	return 0x00; /* BSN are implemented in HW, so return just 0 */
 }
 
+/**
+ * fake_assoc_req - Make an association request to the HW.
+ * @dev: The network device which we are associating to a network.
+ * @addr: The coordinator with which we wish to associate.
+ * @channel: The channel on which to associate.
+ * @cap: The capability information field to use in the association.
+ *
+ * Start an association with a coordinator. The coordinator's address
+ * and PAN ID can be found in @addr.
+ *
+ * Note: This is in section 7.3.1 and 7.5.3.1 of the IEEE
+ *       802.15.4-2006 document.
+ */
 static int fake_assoc_req(struct net_device *dev,
-		struct ieee802154_addr *addr, u8 channel, u8 cap)
+		struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap)
 {
+	struct wpan_phy *phy = net_to_phy(dev);
+
+	mutex_lock(&phy->pib_lock);
+	phy->current_channel = channel;
+	phy->current_page = page;
+	mutex_unlock(&phy->pib_lock);
+
 	/* We simply emulate it here */
 	return ieee802154_nl_assoc_confirm(dev, fake_get_short_addr(dev),
 			IEEE802154_SUCCESS);
 }
 
+/**
+ * fake_assoc_resp - Send an association response to a device.
+ * @dev: The network device on which to send the response.
+ * @addr: The address of the device to respond to.
+ * @short_addr: The assigned short address for the device (if any).
+ * @status: The result of the association request.
+ *
+ * Queue the association response of the coordinator to another
+ * device's attempt to associate with the network which we
+ * coordinate. This is then added to the indirect-send queue to be
+ * transmitted to the end device when it polls for data.
+ *
+ * Note: This is in section 7.3.2 and 7.5.3.1 of the IEEE
+ *       802.15.4-2006 document.
+ */
 static int fake_assoc_resp(struct net_device *dev,
 		struct ieee802154_addr *addr, u16 short_addr, u8 status)
 {
 	return 0;
 }
 
+/**
+ * fake_disassoc_req - Disassociate a device from a network.
+ * @dev: The network device on which we're disassociating a device.
+ * @addr: The device to disassociate from the network.
+ * @reason: The reason to give to the device for being disassociated.
+ *
+ * This sends a disassociation notification to the device being
+ * disassociated from the network.
+ *
+ * Note: This is in section 7.5.3.2 of the IEEE 802.15.4-2006
+ *       document, with the reason described in 7.3.3.2.
+ */
 static int fake_disassoc_req(struct net_device *dev,
 		struct ieee802154_addr *addr, u8 reason)
 {
 	return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS);
 }
 
+/**
+ * fake_start_req - Start an IEEE 802.15.4 PAN.
+ * @dev: The network device on which to start the PAN.
+ * @addr: The coordinator address to use when starting the PAN.
+ * @channel: The channel on which to start the PAN.
+ * @bcn_ord: Beacon order.
+ * @sf_ord: Superframe order.
+ * @pan_coord: Whether or not we are the PAN coordinator or just
+ *             requesting a realignment perhaps?
+ * @blx: Battery Life Extension feature bitfield.
+ * @coord_realign: Something to realign something else.
+ *
+ * If pan_coord is non-zero then this starts a network with the
+ * provided parameters, otherwise it attempts a coordinator
+ * realignment of the stated network instead.
+ *
+ * Note: This is in section 7.5.2.3 of the IEEE 802.15.4-2006
+ * document, with 7.3.8 describing coordinator realignment.
+ */
 static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr,
-				u8 channel,
+				u8 channel, u8 page,
 				u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
 				u8 coord_realign)
 {
+	struct wpan_phy *phy = net_to_phy(dev);
+
+	mutex_lock(&phy->pib_lock);
+	phy->current_channel = channel;
+	phy->current_page = page;
+	mutex_unlock(&phy->pib_lock);
+
+	/* We don't emulate beacons here at all, so START should fail */
+	ieee802154_nl_start_confirm(dev, IEEE802154_INVALID_PARAMETER);
 	return 0;
 }
 
+/**
+ * fake_scan_req - Start a channel scan.
+ * @dev: The network device on which to perform a channel scan.
+ * @type: The type of scan to perform.
+ * @channels: The channel bitmask to scan.
+ * @duration: How long to spend on each channel.
+ *
+ * This starts either a passive (energy) scan or an active (PAN) scan
+ * on the channels indicated in the @channels bitmask. The duration of
+ * the scan is measured in terms of superframe duration. Specifically,
+ * the scan will spend aBaseSuperFrameDuration * ((2^n) + 1) on each
+ * channel.
+ *
+ * Note: This is in section 7.5.2.1 of the IEEE 802.15.4-2006 document.
+ */
 static int fake_scan_req(struct net_device *dev, u8 type, u32 channels,
-		u8 duration)
+		u8 page, u8 duration)
 {
 	u8 edl[27] = {};
 	return ieee802154_nl_scan_confirm(dev, IEEE802154_SUCCESS, type,
-			channels,
+			channels, page,
 			type == IEEE802154_MAC_SCAN_ED ? edl : NULL);
 }
 
@@ -121,7 +257,8 @@ static int ieee802154_fake_close(struct net_device *dev)
 	return 0;
 }
 
-static int ieee802154_fake_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t ieee802154_fake_xmit(struct sk_buff *skb,
+					      struct net_device *dev)
 {
 	skb->iif = dev->ifindex;
 	skb->dev = dev;
@@ -132,7 +269,7 @@ static int ieee802154_fake_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	/* FIXME: do hardware work here ... */
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 
@@ -174,6 +311,14 @@ static const struct net_device_ops fake_ops = {
 	.ndo_set_mac_address	= ieee802154_fake_mac_addr,
 };
 
+static void ieee802154_fake_destruct(struct net_device *dev)
+{
+	struct wpan_phy *phy = net_to_phy(dev);
+
+	wpan_phy_unregister(phy);
+	free_netdev(dev);
+	wpan_phy_free(phy);
+}
 
 static void ieee802154_fake_setup(struct net_device *dev)
 {
@@ -186,22 +331,34 @@ static void ieee802154_fake_setup(struct net_device *dev)
 	dev->type		= ARPHRD_IEEE802154;
 	dev->flags		= IFF_NOARP | IFF_BROADCAST;
 	dev->watchdog_timeo	= 0;
+	dev->destructor		= ieee802154_fake_destruct;
 }
 
 
 static int __devinit ieee802154fake_probe(struct platform_device *pdev)
 {
-	struct net_device *dev =
-		alloc_netdev(0, "hardwpan%d", ieee802154_fake_setup);
+	struct net_device *dev;
+	struct wpan_phy *phy = wpan_phy_alloc(0);
 	int err;
 
-	if (!dev)
+	if (!phy)
 		return -ENOMEM;
 
+	dev = alloc_netdev(0, "hardwpan%d", ieee802154_fake_setup);
+	if (!dev) {
+		wpan_phy_free(phy);
+		return -ENOMEM;
+	}
+
+	phy->dev.platform_data = dev;
+
 	memcpy(dev->dev_addr, "\xba\xbe\xca\xfe\xde\xad\xbe\xef",
 			dev->addr_len);
 	memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
 
+	phy->channels_supported = (1 << 27) - 1;
+	phy->transmit_power = 0xbf;
+
 	dev->netdev_ops = &fake_ops;
 	dev->ml_priv = &fake_mlme;
 
@@ -215,15 +372,18 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev)
 			goto out;
 	}
 
-	SET_NETDEV_DEV(dev, &pdev->dev);
+	SET_NETDEV_DEV(dev, &phy->dev);
 
 	platform_set_drvdata(pdev, dev);
 
+	err = wpan_phy_register(&pdev->dev, phy);
+	if (err)
+		goto out;
+
 	err = register_netdev(dev);
 	if (err < 0)
 		goto out;
 
-
 	dev_info(&pdev->dev, "Added ieee802154 HardMAC hardware\n");
 	return 0;
 
@@ -236,7 +396,6 @@ static int __devexit ieee802154fake_remove(struct platform_device *pdev)
 {
 	struct net_device *dev = platform_get_drvdata(pdev);
 	unregister_netdev(dev);
-	free_netdev(dev);
 	return 0;
 }
 

+ 0 - 1
drivers/infiniband/hw/amso1100/c2.c

@@ -526,7 +526,6 @@ static void c2_rx_interrupt(struct net_device *netdev)
 
 		netif_rx(skb);
 
-		netdev->last_rx = jiffies;
 		netdev->stats.rx_packets++;
 		netdev->stats.rx_bytes += buflen;
 	}

+ 1 - 1
drivers/infiniband/hw/nes/nes_hw.c

@@ -2743,7 +2743,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
 				}
 
 skip_rx_indicate0:
-				nesvnic->netdev->last_rx = jiffies;
+				;
 				/* nesvnic->netstats.rx_packets++; */
 				/* nesvnic->netstats.rx_bytes += rx_pkt_size; */
 			}

+ 1 - 1
drivers/infiniband/hw/nes/nes_nic.c

@@ -1508,7 +1508,7 @@ static int nes_netdev_set_settings(struct net_device *netdev, struct ethtool_cmd
 }
 
 
-static struct ethtool_ops nes_ethtool_ops = {
+static const struct ethtool_ops nes_ethtool_ops = {
 	.get_link = ethtool_op_get_link,
 	.get_settings = nes_netdev_get_settings,
 	.set_settings = nes_netdev_set_settings,

+ 0 - 1
drivers/infiniband/ulp/ipoib/ipoib_cm.c

@@ -662,7 +662,6 @@ copied:
 	skb_reset_mac_header(skb);
 	skb_pull(skb, IPOIB_ENCAP_LEN);
 
-	dev->last_rx = jiffies;
 	++dev->stats.rx_packets;
 	dev->stats.rx_bytes += skb->len;
 

+ 0 - 1
drivers/infiniband/ulp/ipoib/ipoib_ib.c

@@ -276,7 +276,6 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
 	skb_reset_mac_header(skb);
 	skb_pull(skb, IPOIB_ENCAP_LEN);
 
-	dev->last_rx = jiffies;
 	++dev->stats.rx_packets;
 	dev->stats.rx_bytes += skb->len;
 

+ 2 - 4
drivers/isdn/Kconfig

@@ -21,8 +21,6 @@ menuconfig ISDN
 
 if ISDN
 
-source "drivers/isdn/mISDN/Kconfig"
-
 menuconfig ISDN_I4L
 	tristate "Old ISDN4Linux (deprecated)"
 	---help---
@@ -41,9 +39,9 @@ menuconfig ISDN_I4L
 	  It is still available, though, for use with adapters that are not
 	  supported by the new CAPI subsystem yet.
 
-if ISDN_I4L
+source "drivers/isdn/mISDN/Kconfig"
+
 source "drivers/isdn/i4l/Kconfig"
-endif
 
 menuconfig ISDN_CAPI
 	tristate "CAPI 2.0 subsystem"

+ 1 - 2
drivers/isdn/act2000/capi.c

@@ -78,7 +78,6 @@ static actcapi_msgdsc valid_msg[] = {
 #endif
 	{{ 0x00, 0x00}, NULL},
 };
-#define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc))
 #define num_valid_imsg 27 /* MANUFACTURER_IND */
 
 /*
@@ -1025,7 +1024,7 @@ actcapi_debug_msg(struct sk_buff *skb, int direction)
 #ifdef DEBUG_DUMP_SKB
 	dump_skb(skb);
 #endif
-	for (i = 0; i < num_valid_msg; i++)
+	for (i = 0; i < ARRAY_SIZE(valid_msg); i++)
 		if ((msg->hdr.cmd.cmd == valid_msg[i].cmd.cmd) &&
 		    (msg->hdr.cmd.subcmd == valid_msg[i].cmd.subcmd)) {
 			descr = valid_msg[i].description;

+ 15 - 16
drivers/isdn/act2000/module.c

@@ -23,7 +23,6 @@ static unsigned short act2000_isa_ports[] =
         0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380,
         0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60,
 };
-#define ISA_NRPORTS (sizeof(act2000_isa_ports)/sizeof(unsigned short))
 
 static act2000_card *cards = (act2000_card *) NULL;
 
@@ -686,21 +685,21 @@ act2000_addcard(int bus, int port, int irq, char *id)
 		 * This may result in more than one card detected.
 		 */
 		switch (bus) {
-			case ACT2000_BUS_ISA:
-				for (i = 0; i < ISA_NRPORTS; i++)
-					if (act2000_isa_detect(act2000_isa_ports[i])) {
-						printk(KERN_INFO
-						       "act2000: Detected ISA card at port 0x%x\n",
-						       act2000_isa_ports[i]);
-						act2000_alloccard(bus, act2000_isa_ports[i], irq, id);
-					}
-				break;
-			case ACT2000_BUS_MCA:
-			case ACT2000_BUS_PCMCIA:
-			default:
-				printk(KERN_WARNING
-				       "act2000: addcard: Invalid BUS type %d\n",
-				       bus);
+		case ACT2000_BUS_ISA:
+			for (i = 0; i < ARRAY_SIZE(act2000_isa_ports); i++)
+				if (act2000_isa_detect(act2000_isa_ports[i])) {
+					printk(KERN_INFO "act2000: Detected "
+						"ISA card at port 0x%x\n",
+						act2000_isa_ports[i]);
+					act2000_alloccard(bus,
+						act2000_isa_ports[i], irq, id);
+				}
+			break;
+		case ACT2000_BUS_MCA:
+		case ACT2000_BUS_PCMCIA:
+		default:
+			printk(KERN_WARNING
+				"act2000: addcard: Invalid BUS type %d\n", bus);
 		}
 	}
 	if (!cards)

+ 1 - 3
drivers/isdn/hardware/eicon/message.c

@@ -551,9 +551,7 @@ word api_put(APPL   * appl, CAPI_MSG   * msg)
   dbug(1,dprintf("com=%x",msg->header.command));
 
   for(j=0;j<MAX_MSG_PARMS+1;j++) msg_parms[j].length = 0;
-  for(i=0, ret = _BAD_MSG;
-      i<(sizeof(ftable)/sizeof(struct _ftable));
-      i++) {
+  for(i=0, ret = _BAD_MSG; i < ARRAY_SIZE(ftable); i++) {
 
     if(ftable[i].command==msg->header.command) {
       /* break loop if the message is correct, otherwise continue scan  */

+ 1 - 2
drivers/isdn/hardware/eicon/os_4bri.c

@@ -149,8 +149,7 @@ int diva_4bri_init_card(diva_os_xdi_adapter_t * a)
 	diva_os_xdi_adapter_t *diva_current;
 	diva_os_xdi_adapter_t *adapter_list[4];
 	PISDN_ADAPTER Slave;
-	unsigned long bar_length[sizeof(_4bri_bar_length) /
-				 sizeof(_4bri_bar_length[0])];
+	unsigned long bar_length[ARRAY_SIZE(_4bri_bar_length)];
 	int v2 = _4bri_is_rev_2_card(a->CardOrdinal);
 	int tasks = _4bri_is_rev_2_bri_card(a->CardOrdinal) ? 1 : MQ_INSTANCE_COUNT;
 	int factor = (tasks == 1) ? 1 : 2;

+ 51 - 0
drivers/isdn/hardware/mISDN/Kconfig

@@ -39,3 +39,54 @@ config MISDN_HFCUSB
 	  Enable support for USB ISDN TAs with Cologne Chip AG's
 	  HFC-S USB ISDN Controller
 
+config MISDN_AVMFRITZ
+	tristate "Support for AVM FRITZ!CARD PCI"
+	depends on MISDN
+	depends on PCI
+	select MISDN_IPAC
+	help
+	  Enable support for AVMs FRITZ!CARD PCI cards
+
+config MISDN_SPEEDFAX
+	tristate "Support for Sedlbauer Speedfax+"
+	depends on MISDN
+	depends on PCI
+	select MISDN_IPAC
+	select MISDN_ISAR
+	help
+	  Enable support for Sedlbauer Speedfax+.
+
+config MISDN_INFINEON
+	tristate "Support for cards with Infineon chipset"
+	depends on MISDN
+	depends on PCI
+	select MISDN_IPAC
+	help
+	  Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX
+	  chip from Infineon (former manufacturer Siemens).
+
+config MISDN_W6692
+	tristate "Support for cards with Winbond 6692"
+	depends on MISDN
+	depends on PCI
+	help
+	  Enable support for Winbond 6692 PCI chip based cards.
+
+config MISDN_NETJET
+	tristate "Support for NETJet cards"
+	depends on MISDN
+	depends on PCI
+	select MISDN_IPAC
+	select ISDN_HDLC
+	help
+	  Enable support for Traverse Technologies NETJet PCI cards.
+
+
+config MISDN_IPAC
+	tristate
+	depends on MISDN
+
+config MISDN_ISAR
+	tristate
+	depends on MISDN
+

+ 8 - 0
drivers/isdn/hardware/mISDN/Makefile

@@ -6,3 +6,11 @@
 obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
 obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
 obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o
+obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o
+obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o
+obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o
+obj-$(CONFIG_MISDN_W6692) += w6692.o
+obj-$(CONFIG_MISDN_NETJET) += netjet.o
+# chip modules
+obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
+obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o

+ 1152 - 0
drivers/isdn/hardware/mISDN/avmfritz.c

@@ -0,0 +1,1152 @@
+/*
+ * avm_fritz.c    low level stuff for AVM FRITZ!CARD PCI ISDN cards
+ *                Thanks to AVM, Berlin for informations
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <asm/unaligned.h>
+#include "ipac.h"
+
+
+#define AVMFRITZ_REV	"2.1"
+
+static int AVM_cnt;
+static int debug;
+
+enum {
+	AVM_FRITZ_PCI,
+	AVM_FRITZ_PCIV2,
+};
+
+#define HDLC_FIFO		0x0
+#define HDLC_STATUS		0x4
+#define CHIP_WINDOW		0x10
+
+#define CHIP_INDEX		0x4
+#define AVM_HDLC_1		0x00
+#define AVM_HDLC_2		0x01
+#define AVM_ISAC_FIFO		0x02
+#define AVM_ISAC_REG_LOW	0x04
+#define AVM_ISAC_REG_HIGH	0x06
+
+#define AVM_STATUS0_IRQ_ISAC	0x01
+#define AVM_STATUS0_IRQ_HDLC	0x02
+#define AVM_STATUS0_IRQ_TIMER	0x04
+#define AVM_STATUS0_IRQ_MASK	0x07
+
+#define AVM_STATUS0_RESET	0x01
+#define AVM_STATUS0_DIS_TIMER	0x02
+#define AVM_STATUS0_RES_TIMER	0x04
+#define AVM_STATUS0_ENA_IRQ	0x08
+#define AVM_STATUS0_TESTBIT	0x10
+
+#define AVM_STATUS1_INT_SEL	0x0f
+#define AVM_STATUS1_ENA_IOM	0x80
+
+#define HDLC_MODE_ITF_FLG	0x01
+#define HDLC_MODE_TRANS		0x02
+#define HDLC_MODE_CCR_7		0x04
+#define HDLC_MODE_CCR_16	0x08
+#define HDLC_MODE_TESTLOOP	0x80
+
+#define HDLC_INT_XPR		0x80
+#define HDLC_INT_XDU		0x40
+#define HDLC_INT_RPR		0x20
+#define HDLC_INT_MASK		0xE0
+
+#define HDLC_STAT_RME		0x01
+#define HDLC_STAT_RDO		0x10
+#define HDLC_STAT_CRCVFRRAB	0x0E
+#define HDLC_STAT_CRCVFR	0x06
+#define HDLC_STAT_RML_MASK	0x3f00
+
+#define HDLC_CMD_XRS		0x80
+#define HDLC_CMD_XME		0x01
+#define HDLC_CMD_RRS		0x20
+#define HDLC_CMD_XML_MASK	0x3f00
+#define HDLC_FIFO_SIZE		32
+
+/* Fritz PCI v2.0 */
+
+#define AVM_HDLC_FIFO_1		0x10
+#define AVM_HDLC_FIFO_2		0x18
+
+#define AVM_HDLC_STATUS_1	0x14
+#define AVM_HDLC_STATUS_2	0x1c
+
+#define AVM_ISACX_INDEX		0x04
+#define AVM_ISACX_DATA		0x08
+
+/* data struct */
+#define LOG_SIZE		63
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+	u8 fill;
+	u8 mode;
+	u8 xml;
+	u8 cmd;
+#else
+	u8 cmd;
+	u8 xml;
+	u8 mode;
+	u8 fill;
+#endif
+} __attribute__((packed));
+
+struct hdlc_hw {
+	union {
+		u32 ctrl;
+		struct hdlc_stat_reg sr;
+	} ctrl;
+	u32 stat;
+};
+
+struct fritzcard {
+	struct list_head	list;
+	struct pci_dev		*pdev;
+	char			name[MISDN_MAX_IDLEN];
+	u8			type;
+	u8			ctrlreg;
+	u16			irq;
+	u32			irqcnt;
+	u32			addr;
+	spinlock_t		lock; /* hw lock */
+	struct isac_hw		isac;
+	struct hdlc_hw		hdlc[2];
+	struct bchannel		bch[2];
+	char			log[LOG_SIZE + 1];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct fritzcard *card)
+{
+	card->isac.dch.debug = debug;
+	card->bch[0].debug = debug;
+	card->bch[1].debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct fritzcard *card;
+
+	ret = param_set_uint(val, kp);
+	if (!ret) {
+		read_lock(&card_lock);
+		list_for_each_entry(card, &Cards, list)
+			_set_debug(card);
+		read_unlock(&card_lock);
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(AVMFRITZ_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "avmfritz debug mask");
+
+/* Interface functions */
+
+static u8
+ReadISAC_V1(void *p, u8 offset)
+{
+	struct fritzcard *fc = p;
+	u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+	outb(idx, fc->addr + CHIP_INDEX);
+	return inb(fc->addr + CHIP_WINDOW + (offset & 0xf));
+}
+
+static void
+WriteISAC_V1(void *p, u8 offset, u8 value)
+{
+	struct fritzcard *fc = p;
+	u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+	outb(idx, fc->addr + CHIP_INDEX);
+	outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf));
+}
+
+static void
+ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
+{
+	struct fritzcard *fc = p;
+
+	outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
+	insb(fc->addr + CHIP_WINDOW, data, size);
+}
+
+static void
+WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
+{
+	struct fritzcard *fc = p;
+
+	outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
+	outsb(fc->addr + CHIP_WINDOW, data, size);
+}
+
+static u8
+ReadISAC_V2(void *p, u8 offset)
+{
+	struct fritzcard *fc = p;
+
+	outl(offset, fc->addr + AVM_ISACX_INDEX);
+	return 0xff & inl(fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+WriteISAC_V2(void *p, u8 offset, u8 value)
+{
+	struct fritzcard *fc = p;
+
+	outl(offset, fc->addr + AVM_ISACX_INDEX);
+	outl(value, fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
+{
+	struct fritzcard *fc = p;
+	int i;
+
+	outl(off, fc->addr + AVM_ISACX_INDEX);
+	for (i = 0; i < size; i++)
+		data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
+{
+	struct fritzcard *fc = p;
+	int i;
+
+	outl(off, fc->addr + AVM_ISACX_INDEX);
+	for (i = 0; i < size; i++)
+		outl(data[i], fc->addr + AVM_ISACX_DATA);
+}
+
+static struct bchannel *
+Sel_BCS(struct fritzcard *fc, u32 channel)
+{
+	if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) &&
+		(fc->bch[0].nr & channel))
+		return &fc->bch[0];
+	else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) &&
+		(fc->bch[1].nr & channel))
+		return &fc->bch[1];
+	else
+		return NULL;
+}
+
+static inline void
+__write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
+	u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1;
+
+	outl(idx, fc->addr + CHIP_INDEX);
+	outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS);
+}
+
+static inline void
+__write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
+	outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
+		AVM_HDLC_STATUS_1));
+}
+
+void
+write_ctrl(struct bchannel *bch, int which) {
+	struct fritzcard *fc = bch->hw;
+	struct hdlc_hw *hdlc;
+
+	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+	pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr,
+		which, hdlc->ctrl.ctrl);
+	switch (fc->type) {
+	case AVM_FRITZ_PCIV2:
+		__write_ctrl_pciv2(fc, hdlc, bch->nr);
+		break;
+	case AVM_FRITZ_PCI:
+		__write_ctrl_pci(fc, hdlc, bch->nr);
+		break;
+	}
+}
+
+
+static inline u32
+__read_status_pci(u_long addr, u32 channel)
+{
+	outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX);
+	return inl(addr + CHIP_WINDOW + HDLC_STATUS);
+}
+
+static inline u32
+__read_status_pciv2(u_long addr, u32 channel)
+{
+	return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
+		AVM_HDLC_STATUS_1));
+}
+
+
+static u32
+read_status(struct fritzcard *fc, u32 channel)
+{
+	switch (fc->type) {
+	case AVM_FRITZ_PCIV2:
+		return __read_status_pciv2(fc->addr, channel);
+	case AVM_FRITZ_PCI:
+		return __read_status_pci(fc->addr, channel);
+	}
+	/* dummy */
+	return 0;
+}
+
+static void
+enable_hwirq(struct fritzcard *fc)
+{
+	fc->ctrlreg |= AVM_STATUS0_ENA_IRQ;
+	outb(fc->ctrlreg, fc->addr + 2);
+}
+
+static void
+disable_hwirq(struct fritzcard *fc)
+{
+	fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ;
+	outb(fc->ctrlreg, fc->addr + 2);
+}
+
+static int
+modehdlc(struct bchannel *bch, int protocol)
+{
+	struct fritzcard *fc = bch->hw;
+	struct hdlc_hw *hdlc;
+
+	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+	pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name,
+		'@' + bch->nr, bch->state, protocol, bch->nr);
+	hdlc->ctrl.ctrl = 0;
+	switch (protocol) {
+	case -1: /* used for init */
+		bch->state = -1;
+	case ISDN_P_NONE:
+		if (bch->state == ISDN_P_NONE)
+			break;
+		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+		hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
+		write_ctrl(bch, 5);
+		bch->state = ISDN_P_NONE;
+		test_and_clear_bit(FLG_HDLC, &bch->Flags);
+		test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
+		break;
+	case ISDN_P_B_RAW:
+		bch->state = protocol;
+		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+		hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
+		write_ctrl(bch, 5);
+		hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
+		write_ctrl(bch, 1);
+		hdlc->ctrl.sr.cmd = 0;
+		test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
+		break;
+	case ISDN_P_B_HDLC:
+		bch->state = protocol;
+		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+		hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+		write_ctrl(bch, 5);
+		hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
+		write_ctrl(bch, 1);
+		hdlc->ctrl.sr.cmd = 0;
+		test_and_set_bit(FLG_HDLC, &bch->Flags);
+		break;
+	default:
+		pr_info("%s: protocol not known %x\n", fc->name, protocol);
+		return -ENOPROTOOPT;
+	}
+	return 0;
+}
+
+static void
+hdlc_empty_fifo(struct bchannel *bch, int count)
+{
+	u32 *ptr;
+	u8 *p;
+	u32  val, addr;
+	int cnt = 0;
+	struct fritzcard *fc = bch->hw;
+
+	pr_debug("%s: %s %d\n", fc->name, __func__, count);
+	if (!bch->rx_skb) {
+		bch->rx_skb = mI_alloc_skb(bch->maxlen, GFP_ATOMIC);
+		if (!bch->rx_skb) {
+			pr_info("%s: B receive out of memory\n",
+				fc->name);
+			return;
+		}
+	}
+	if ((bch->rx_skb->len + count) > bch->maxlen) {
+		pr_debug("%s: overrun %d\n", fc->name,
+			bch->rx_skb->len + count);
+		return;
+	}
+	p = skb_put(bch->rx_skb, count);
+	ptr = (u32 *)p;
+	if (AVM_FRITZ_PCIV2 == fc->type)
+		addr = fc->addr + (bch->nr == 2 ?
+			AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
+	else {
+		addr = fc->addr + CHIP_WINDOW;
+		outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr);
+	}
+	while (cnt < count) {
+		val = le32_to_cpu(inl(addr));
+		put_unaligned(val, ptr);
+		ptr++;
+		cnt += 4;
+	}
+	if (debug & DEBUG_HW_BFIFO) {
+		snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ",
+			bch->nr, fc->name, count);
+		print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
+	}
+}
+
+static void
+hdlc_fill_fifo(struct bchannel *bch)
+{
+	struct fritzcard *fc = bch->hw;
+	struct hdlc_hw *hdlc;
+	int count, cnt = 0;
+	u8 *p;
+	u32 *ptr, val, addr;
+
+	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+	if (!bch->tx_skb)
+		return;
+	count = bch->tx_skb->len - bch->tx_idx;
+	if (count <= 0)
+		return;
+	p = bch->tx_skb->data + bch->tx_idx;
+	hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME;
+	if (count > HDLC_FIFO_SIZE) {
+		count = HDLC_FIFO_SIZE;
+	} else {
+		if (test_bit(FLG_HDLC, &bch->Flags))
+			hdlc->ctrl.sr.cmd |= HDLC_CMD_XME;
+	}
+	pr_debug("%s: %s %d/%d/%d", fc->name, __func__, count,
+		bch->tx_idx, bch->tx_skb->len);
+	ptr = (u32 *)p;
+	bch->tx_idx += count;
+	hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count);
+	if (AVM_FRITZ_PCIV2 == fc->type) {
+		__write_ctrl_pciv2(fc, hdlc, bch->nr);
+		addr = fc->addr + (bch->nr == 2 ?
+			AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
+	} else {
+		__write_ctrl_pci(fc, hdlc, bch->nr);
+		addr = fc->addr + CHIP_WINDOW;
+	}
+	while (cnt < count) {
+		val = get_unaligned(ptr);
+		outl(cpu_to_le32(val), addr);
+		ptr++;
+		cnt += 4;
+	}
+	if (debug & DEBUG_HW_BFIFO) {
+		snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ",
+			bch->nr, fc->name, count);
+		print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
+	}
+}
+
+static void
+HDLC_irq_xpr(struct bchannel *bch)
+{
+	if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
+		hdlc_fill_fifo(bch);
+	else {
+		if (bch->tx_skb) {
+			/* send confirm, on trans, free on hdlc. */
+			if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+				confirm_Bsend(bch);
+			dev_kfree_skb(bch->tx_skb);
+		}
+		if (get_next_bframe(bch))
+			hdlc_fill_fifo(bch);
+	}
+}
+
+static void
+HDLC_irq(struct bchannel *bch, u32 stat)
+{
+	struct fritzcard *fc = bch->hw;
+	int		len;
+	struct hdlc_hw	*hdlc;
+
+	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+	pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat);
+	if (stat & HDLC_INT_RPR) {
+		if (stat & HDLC_STAT_RDO) {
+			hdlc->ctrl.sr.xml = 0;
+			hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS;
+			write_ctrl(bch, 1);
+			hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+			write_ctrl(bch, 1);
+			if (bch->rx_skb)
+				skb_trim(bch->rx_skb, 0);
+		} else {
+			len = (stat & HDLC_STAT_RML_MASK) >> 8;
+			if (!len)
+				len = 32;
+			hdlc_empty_fifo(bch, len);
+			if (!bch->rx_skb)
+				goto handle_tx;
+			if ((stat & HDLC_STAT_RME) || test_bit(FLG_TRANSPARENT,
+			    &bch->Flags)) {
+				if (((stat & HDLC_STAT_CRCVFRRAB) ==
+				    HDLC_STAT_CRCVFR) ||
+				    test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+					recv_Bchannel(bch, 0);
+				} else {
+					pr_debug("%s: got invalid frame\n",
+						fc->name);
+					skb_trim(bch->rx_skb, 0);
+				}
+			}
+		}
+	}
+handle_tx:
+	if (stat & HDLC_INT_XDU) {
+		/* Here we lost an TX interrupt, so
+		 * restart transmitting the whole frame on HDLC
+		 * in transparent mode we send the next data
+		 */
+		if (bch->tx_skb)
+			pr_debug("%s: ch%d XDU len(%d) idx(%d) Flags(%lx)\n",
+				fc->name, bch->nr, bch->tx_skb->len,
+				bch->tx_idx, bch->Flags);
+		else
+			pr_debug("%s: ch%d XDU no tx_skb Flags(%lx)\n",
+				fc->name, bch->nr, bch->Flags);
+		if (bch->tx_skb && bch->tx_skb->len) {
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+				bch->tx_idx = 0;
+		}
+		hdlc->ctrl.sr.xml = 0;
+		hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS;
+		write_ctrl(bch, 1);
+		hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+		HDLC_irq_xpr(bch);
+		return;
+	} else if (stat & HDLC_INT_XPR)
+		HDLC_irq_xpr(bch);
+}
+
+static inline void
+HDLC_irq_main(struct fritzcard *fc)
+{
+	u32 stat;
+	struct bchannel *bch;
+
+	stat = read_status(fc, 1);
+	if (stat & HDLC_INT_MASK) {
+		bch = Sel_BCS(fc, 1);
+		if (bch)
+			HDLC_irq(bch, stat);
+		else
+			pr_debug("%s: spurious ch1 IRQ\n", fc->name);
+	}
+	stat = read_status(fc, 2);
+	if (stat & HDLC_INT_MASK) {
+		bch = Sel_BCS(fc, 2);
+		if (bch)
+			HDLC_irq(bch, stat);
+		else
+			pr_debug("%s: spurious ch2 IRQ\n", fc->name);
+	}
+}
+
+static irqreturn_t
+avm_fritz_interrupt(int intno, void *dev_id)
+{
+	struct fritzcard *fc = dev_id;
+	u8 val;
+	u8 sval;
+
+	spin_lock(&fc->lock);
+	sval = inb(fc->addr + 2);
+	pr_debug("%s: irq stat0 %x\n", fc->name, sval);
+	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
+		/* shared  IRQ from other HW */
+		spin_unlock(&fc->lock);
+		return IRQ_NONE;
+	}
+	fc->irqcnt++;
+
+	if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+		val = ReadISAC_V1(fc, ISAC_ISTA);
+		mISDNisac_irq(&fc->isac, val);
+	}
+	if (!(sval & AVM_STATUS0_IRQ_HDLC))
+		HDLC_irq_main(fc);
+	spin_unlock(&fc->lock);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+avm_fritzv2_interrupt(int intno, void *dev_id)
+{
+	struct fritzcard *fc = dev_id;
+	u8 val;
+	u8 sval;
+
+	spin_lock(&fc->lock);
+	sval = inb(fc->addr + 2);
+	pr_debug("%s: irq stat0 %x\n", fc->name, sval);
+	if (!(sval & AVM_STATUS0_IRQ_MASK)) {
+		/* shared  IRQ from other HW */
+		spin_unlock(&fc->lock);
+		return IRQ_NONE;
+	}
+	fc->irqcnt++;
+
+	if (sval & AVM_STATUS0_IRQ_HDLC)
+		HDLC_irq_main(fc);
+	if (sval & AVM_STATUS0_IRQ_ISAC) {
+		val = ReadISAC_V2(fc, ISACX_ISTA);
+		mISDNisac_irq(&fc->isac, val);
+	}
+	if (sval & AVM_STATUS0_IRQ_TIMER) {
+		pr_debug("%s: timer irq\n", fc->name);
+		outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2);
+		udelay(1);
+		outb(fc->ctrlreg, fc->addr + 2);
+	}
+	spin_unlock(&fc->lock);
+	return IRQ_HANDLED;
+}
+
+static int
+avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct fritzcard *fc = bch->hw;
+	int ret = -EINVAL;
+	struct mISDNhead *hh = mISDN_HEAD_P(skb);
+	u32 id;
+	u_long flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(&fc->lock, flags);
+		ret = bchannel_senddata(bch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			hdlc_fill_fifo(bch);
+			ret = 0;
+			spin_unlock_irqrestore(&fc->lock, flags);
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(&fc->lock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		spin_lock_irqsave(&fc->lock, flags);
+		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+			ret = modehdlc(bch, ch->protocol);
+		else
+			ret = 0;
+		spin_unlock_irqrestore(&fc->lock, flags);
+		if (!ret)
+			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+				NULL, GFP_KERNEL);
+		break;
+	case PH_DEACTIVATE_REQ:
+		spin_lock_irqsave(&fc->lock, flags);
+		mISDN_clear_bchannel(bch);
+		modehdlc(bch, ISDN_P_NONE);
+		spin_unlock_irqrestore(&fc->lock, flags);
+		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_KERNEL);
+		ret = 0;
+		break;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static void
+inithdlc(struct fritzcard *fc)
+{
+	modehdlc(&fc->bch[0], -1);
+	modehdlc(&fc->bch[1], -1);
+}
+
+void
+clear_pending_hdlc_ints(struct fritzcard *fc)
+{
+	u32 val;
+
+	val = read_status(fc, 1);
+	pr_debug("%s: HDLC 1 STA %x\n", fc->name, val);
+	val = read_status(fc, 2);
+	pr_debug("%s: HDLC 2 STA %x\n", fc->name, val);
+}
+
+static void
+reset_avm(struct fritzcard *fc)
+{
+	switch (fc->type) {
+	case AVM_FRITZ_PCI:
+		fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER;
+		break;
+	case AVM_FRITZ_PCIV2:
+		fc->ctrlreg = AVM_STATUS0_RESET;
+		break;
+	}
+	if (debug & DEBUG_HW)
+		pr_notice("%s: reset\n", fc->name);
+	disable_hwirq(fc);
+	mdelay(5);
+	switch (fc->type) {
+	case AVM_FRITZ_PCI:
+		fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER;
+		disable_hwirq(fc);
+		outb(AVM_STATUS1_ENA_IOM, fc->addr + 3);
+		break;
+	case AVM_FRITZ_PCIV2:
+		fc->ctrlreg = 0;
+		disable_hwirq(fc);
+		break;
+	}
+	mdelay(1);
+	if (debug & DEBUG_HW)
+		pr_notice("%s: S0/S1 %x/%x\n", fc->name,
+			inb(fc->addr + 2), inb(fc->addr + 3));
+}
+
+static int
+init_card(struct fritzcard *fc)
+{
+	int		ret, cnt = 3;
+	u_long		flags;
+
+	reset_avm(fc); /* disable IRQ */
+	if (fc->type == AVM_FRITZ_PCIV2)
+		ret = request_irq(fc->irq, avm_fritzv2_interrupt,
+			IRQF_SHARED, fc->name, fc);
+	else
+		ret = request_irq(fc->irq, avm_fritz_interrupt,
+			IRQF_SHARED, fc->name, fc);
+	if (ret) {
+		pr_info("%s: couldn't get interrupt %d\n",
+			fc->name, fc->irq);
+		return ret;
+	}
+	while (cnt--) {
+		spin_lock_irqsave(&fc->lock, flags);
+		ret = fc->isac.init(&fc->isac);
+		if (ret) {
+			spin_unlock_irqrestore(&fc->lock, flags);
+			pr_info("%s: ISAC init failed with %d\n",
+				fc->name, ret);
+			break;
+		}
+		clear_pending_hdlc_ints(fc);
+		inithdlc(fc);
+		enable_hwirq(fc);
+		/* RESET Receiver and Transmitter */
+		if (AVM_FRITZ_PCIV2 == fc->type) {
+			WriteISAC_V2(fc, ISACX_MASK, 0);
+			WriteISAC_V2(fc, ISACX_CMDRD, 0x41);
+		} else {
+			WriteISAC_V1(fc, ISAC_MASK, 0);
+			WriteISAC_V1(fc, ISAC_CMDR, 0x41);
+		}
+		spin_unlock_irqrestore(&fc->lock, flags);
+		/* Timeout 10ms */
+		msleep_interruptible(10);
+		if (debug & DEBUG_HW)
+			pr_notice("%s: IRQ %d count %d\n", fc->name,
+				fc->irq, fc->irqcnt);
+		if (!fc->irqcnt) {
+			pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
+				fc->name, fc->irq, 3 - cnt);
+			reset_avm(fc);
+		} else
+			return 0;
+	}
+	free_irq(fc->irq, fc);
+	return -EIO;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+	int ret = 0;
+	struct fritzcard *fc = bch->hw;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = 0;
+		break;
+	/* Nothing implemented yet */
+	case MISDN_CTRL_FILL_EMPTY:
+	default:
+		pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct fritzcard *fc = bch->hw;
+	int ret = -EINVAL;
+	u_long flags;
+
+	pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
+	switch (cmd) {
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+			spin_lock_irqsave(&fc->lock, flags);
+			mISDN_freebchannel(bch);
+			test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+			test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+			modehdlc(bch, ISDN_P_NONE);
+			spin_unlock_irqrestore(&fc->lock, flags);
+		}
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(THIS_MODULE);
+		ret = 0;
+		break;
+	case CONTROL_CHANNEL:
+		ret = channel_bctrl(bch, arg);
+		break;
+	default:
+		pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd);
+	}
+	return ret;
+}
+
+static int
+channel_ctrl(struct fritzcard  *fc, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_LOOP;
+		break;
+	case MISDN_CTRL_LOOP:
+		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+		if (cq->channel < 0 || cq->channel > 3) {
+			ret = -EINVAL;
+			break;
+		}
+		ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel);
+		break;
+	default:
+		pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+open_bchannel(struct fritzcard *fc, struct channel_req *rq)
+{
+	struct bchannel		*bch;
+
+	if (rq->adr.channel > 2)
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	bch = &fc->bch[rq->adr.channel - 1];
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+	bch->ch.protocol = rq->protocol;
+	rq->ch = &bch->ch;
+	return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct fritzcard	*fc = dch->hw;
+	struct channel_req	*rq;
+	int			err = 0;
+
+	pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		if (rq->protocol == ISDN_P_TE_S0)
+			err = fc->isac.open(&fc->isac, rq);
+		else
+			err = open_bchannel(fc, rq);
+		if (err)
+			break;
+		if (!try_module_get(THIS_MODULE))
+			pr_info("%s: cannot get module\n", fc->name);
+		break;
+	case CLOSE_CHANNEL:
+		pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id,
+			__builtin_return_address(0));
+		module_put(THIS_MODULE);
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_ctrl(fc, arg);
+		break;
+	default:
+		pr_debug("%s: %s unknown command %x\n",
+			fc->name, __func__, cmd);
+		return -EINVAL;
+	}
+	return err;
+}
+
+int
+setup_fritz(struct fritzcard *fc)
+{
+	u32 val, ver;
+
+	if (!request_region(fc->addr, 32, fc->name)) {
+		pr_info("%s: AVM config port %x-%x already in use\n",
+			fc->name, fc->addr, fc->addr + 31);
+		return -EIO;
+	}
+	switch (fc->type) {
+	case AVM_FRITZ_PCI:
+		val = inl(fc->addr);
+		outl(AVM_HDLC_1, fc->addr + CHIP_INDEX);
+		ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24;
+		if (debug & DEBUG_HW) {
+			pr_notice("%s: PCI stat %#x\n", fc->name, val);
+			pr_notice("%s: PCI Class %X Rev %d\n", fc->name,
+				val & 0xff, (val >> 8) & 0xff);
+			pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
+		}
+		ASSIGN_FUNC(V1, ISAC, fc->isac);
+		fc->isac.type = IPAC_TYPE_ISAC;
+		break;
+	case AVM_FRITZ_PCIV2:
+		val = inl(fc->addr);
+		ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24;
+		if (debug & DEBUG_HW) {
+			pr_notice("%s: PCI V2 stat %#x\n", fc->name, val);
+			pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name,
+				val & 0xff, (val>>8) & 0xff);
+			pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
+		}
+		ASSIGN_FUNC(V2, ISAC, fc->isac);
+		fc->isac.type = IPAC_TYPE_ISACX;
+		break;
+	default:
+		release_region(fc->addr, 32);
+		pr_info("%s: AVM unknown type %d\n", fc->name, fc->type);
+		return -ENODEV;
+	}
+	pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name,
+		(fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" :
+		"AVM Fritz!CARD PCIv2", fc->irq, fc->addr);
+	return 0;
+}
+
+static void
+release_card(struct fritzcard *card)
+{
+	u_long flags;
+
+	disable_hwirq(card);
+	spin_lock_irqsave(&card->lock, flags);
+	modehdlc(&card->bch[0], ISDN_P_NONE);
+	modehdlc(&card->bch[1], ISDN_P_NONE);
+	spin_unlock_irqrestore(&card->lock, flags);
+	card->isac.release(&card->isac);
+	free_irq(card->irq, card);
+	mISDN_freebchannel(&card->bch[1]);
+	mISDN_freebchannel(&card->bch[0]);
+	mISDN_unregister_device(&card->isac.dch.dev);
+	release_region(card->addr, 32);
+	pci_disable_device(card->pdev);
+	pci_set_drvdata(card->pdev, NULL);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	kfree(card);
+	AVM_cnt--;
+}
+
+static int __devinit
+setup_instance(struct fritzcard *card)
+{
+	int i, err;
+	u_long flags;
+
+	snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1);
+	write_lock_irqsave(&card_lock, flags);
+	list_add_tail(&card->list, &Cards);
+	write_unlock_irqrestore(&card_lock, flags);
+
+	_set_debug(card);
+	card->isac.name = card->name;
+	spin_lock_init(&card->lock);
+	card->isac.hwlock = &card->lock;
+	mISDNisac_init(&card->isac, card);
+
+	card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+	    (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	card->isac.dch.dev.D.ctrl = avm_dctrl;
+	for (i = 0; i < 2; i++) {
+		card->bch[i].nr = i + 1;
+		set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+		mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
+		card->bch[i].hw = card;
+		card->bch[i].ch.send = avm_l2l1B;
+		card->bch[i].ch.ctrl = avm_bctrl;
+		card->bch[i].ch.nr = i + 1;
+		list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels);
+	}
+	err = setup_fritz(card);
+	if (err)
+		goto error;
+	err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
+		card->name);
+	if (err)
+		goto error_reg;
+	err = init_card(card);
+	if (!err)  {
+		AVM_cnt++;
+		pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt);
+		return 0;
+	}
+	mISDN_unregister_device(&card->isac.dch.dev);
+error_reg:
+	release_region(card->addr, 32);
+error:
+	card->isac.release(&card->isac);
+	mISDN_freebchannel(&card->bch[1]);
+	mISDN_freebchannel(&card->bch[0]);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	kfree(card);
+	return err;
+}
+
+static int __devinit
+fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int err = -ENOMEM;
+	struct fritzcard *card;
+
+	card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL);
+	if (!card) {
+		pr_info("No kmem for fritzcard\n");
+		return err;
+	}
+	if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2)
+		card->type = AVM_FRITZ_PCIV2;
+	else
+		card->type = AVM_FRITZ_PCI;
+	card->pdev = pdev;
+	err = pci_enable_device(pdev);
+	if (err) {
+		kfree(card);
+		return err;
+	}
+
+	pr_notice("mISDN: found adapter %s at %s\n",
+	       (char *) ent->driver_data, pci_name(pdev));
+
+	card->addr = pci_resource_start(pdev, 1);
+	card->irq = pdev->irq;
+	pci_set_drvdata(pdev, card);
+	err = setup_instance(card);
+	if (err)
+		pci_set_drvdata(pdev, NULL);
+	return err;
+}
+
+static void __devexit
+fritz_remove_pci(struct pci_dev *pdev)
+{
+	struct fritzcard *card = pci_get_drvdata(pdev);
+
+	if (card)
+		release_card(card);
+	else
+		if (debug)
+			pr_info("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id fcpci_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID,
+	  0, 0, (unsigned long) "Fritz!Card PCI"},
+	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID,
+	  0, 0, (unsigned long) "Fritz!Card PCI v2" },
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, fcpci_ids);
+
+static struct pci_driver fcpci_driver = {
+	.name = "fcpci",
+	.probe = fritzpci_probe,
+	.remove = __devexit_p(fritz_remove_pci),
+	.id_table = fcpci_ids,
+};
+
+static int __init AVM_init(void)
+{
+	int err;
+
+	pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV);
+	err = pci_register_driver(&fcpci_driver);
+	return err;
+}
+
+static void __exit AVM_cleanup(void)
+{
+	pci_unregister_driver(&fcpci_driver);
+}
+
+module_init(AVM_init);
+module_exit(AVM_cleanup);

+ 5 - 18
drivers/isdn/hardware/mISDN/hfcmulti.c

@@ -3416,22 +3416,8 @@ deactivate_bchannel(struct bchannel *bch)
 	u_long			flags;
 
 	spin_lock_irqsave(&hc->lock, flags);
-	if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
-		dev_kfree_skb(bch->next_skb);
-		bch->next_skb = NULL;
-	}
-	if (bch->tx_skb) {
-		dev_kfree_skb(bch->tx_skb);
-		bch->tx_skb = NULL;
-	}
-	bch->tx_idx = 0;
-	if (bch->rx_skb) {
-		dev_kfree_skb(bch->rx_skb);
-		bch->rx_skb = NULL;
-	}
+	mISDN_clear_bchannel(bch);
 	hc->chan[bch->slot].coeff_count = 0;
-	test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
-	test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
 	hc->chan[bch->slot].rx_off = 0;
 	hc->chan[bch->slot].conf = -1;
 	mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0);
@@ -5384,9 +5370,10 @@ hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	    ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
 	    ent->device == PCI_DEVICE_ID_CCD_HFCE1)) {
 		printk(KERN_ERR
-		    "Unknown HFC multiport controller (vendor:%x device:%x "
-		    "subvendor:%x subdevice:%x)\n", ent->vendor, ent->device,
-		    ent->subvendor, ent->subdevice);
+		    "Unknown HFC multiport controller (vendor:%04x device:%04x "
+		    "subvendor:%04x subdevice:%04x)\n", pdev->vendor,
+		    pdev->device, pdev->subsystem_vendor,
+		    pdev->subsystem_device);
 		printk(KERN_ERR
 		    "Please contact the driver maintainer for support.\n");
 		return -ENODEV;

+ 1 - 15
drivers/isdn/hardware/mISDN/hfcpci.c

@@ -1522,22 +1522,8 @@ deactivate_bchannel(struct bchannel *bch)
 	u_long		flags;
 
 	spin_lock_irqsave(&hc->lock, flags);
-	if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
-		dev_kfree_skb(bch->next_skb);
-		bch->next_skb = NULL;
-	}
-	if (bch->tx_skb) {
-		dev_kfree_skb(bch->tx_skb);
-		bch->tx_skb = NULL;
-	}
-	bch->tx_idx = 0;
-	if (bch->rx_skb) {
-		dev_kfree_skb(bch->rx_skb);
-		bch->rx_skb = NULL;
-	}
+	mISDN_clear_bchannel(bch);
 	mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
-	test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
-	test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
 	spin_unlock_irqrestore(&hc->lock, flags);
 }
 

+ 1 - 15
drivers/isdn/hardware/mISDN/hfcsusb.c

@@ -1809,21 +1809,7 @@ deactivate_bchannel(struct bchannel *bch)
 		    hw->name, __func__, bch->nr);
 
 	spin_lock_irqsave(&hw->lock, flags);
-	if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
-		dev_kfree_skb(bch->next_skb);
-		bch->next_skb = NULL;
-	}
-	if (bch->tx_skb) {
-		dev_kfree_skb(bch->tx_skb);
-		bch->tx_skb = NULL;
-	}
-	bch->tx_idx = 0;
-	if (bch->rx_skb) {
-		dev_kfree_skb(bch->rx_skb);
-		bch->rx_skb = NULL;
-	}
-	clear_bit(FLG_ACTIVE, &bch->Flags);
-	clear_bit(FLG_TX_BUSY, &bch->Flags);
+	mISDN_clear_bchannel(bch);
 	spin_unlock_irqrestore(&hw->lock, flags);
 	hfcsusb_setup_bch(bch, ISDN_P_NONE);
 	hfcsusb_stop_endpoint(hw, bch->nr);

+ 109 - 0
drivers/isdn/hardware/mISDN/iohelper.h

@@ -0,0 +1,109 @@
+/*
+ * iohelper.h
+ *		helper for define functions to access ISDN hardware
+ *              supported are memory mapped IO
+ *		indirect port IO (one port for address, one for data)
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _IOHELPER_H
+#define _IOHELPER_H
+
+typedef	u8	(read_reg_func)(void *hwp, u8 offset);
+typedef	void	(write_reg_func)(void *hwp, u8 offset, u8 value);
+typedef	void	(fifo_func)(void *hwp, u8 offset, u8 *datap, int size);
+
+struct _ioport {
+	u32	port;
+	u32	ale;
+};
+
+#define IOFUNC_IO(name, hws, ap) \
+	static u8 Read##name##_IO(void *p, u8 off) {\
+		struct hws *hw = p;\
+		return inb(hw->ap.port + off);\
+	} \
+	static void Write##name##_IO(void *p, u8 off, u8 val) {\
+		struct hws *hw = p;\
+		outb(val, hw->ap.port + off);\
+	} \
+	static void ReadFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) {\
+		struct hws *hw = p;\
+		insb(hw->ap.port + off, dp, size);\
+	} \
+	static void WriteFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) {\
+		struct hws *hw = p;\
+		outsb(hw->ap.port + off, dp, size);\
+	}
+
+#define IOFUNC_IND(name, hws, ap) \
+	static u8 Read##name##_IND(void *p, u8 off) {\
+		struct hws *hw = p;\
+		outb(off, hw->ap.ale);\
+		return inb(hw->ap.port);\
+	} \
+	static void Write##name##_IND(void *p, u8 off, u8 val) {\
+		struct hws *hw = p;\
+		outb(off, hw->ap.ale);\
+		outb(val, hw->ap.port);\
+	} \
+	static void ReadFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) {\
+		struct hws *hw = p;\
+		outb(off, hw->ap.ale);\
+		insb(hw->ap.port, dp, size);\
+	} \
+	static void WriteFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) {\
+		struct hws *hw = p;\
+		outb(off, hw->ap.ale);\
+		outsb(hw->ap.port, dp, size);\
+	}
+
+#define IOFUNC_MEMIO(name, hws, typ, adr) \
+	static u8 Read##name##_MIO(void *p, u8 off) {\
+		struct hws *hw = p;\
+		return readb(((typ *)hw->adr) + off);\
+	} \
+	static void Write##name##_MIO(void *p, u8 off, u8 val) {\
+		struct hws *hw = p;\
+		writeb(val, ((typ *)hw->adr) + off);\
+	} \
+	static void ReadFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) {\
+		struct hws *hw = p;\
+		while (size--)\
+			*dp++ = readb(((typ *)hw->adr) + off);\
+	} \
+	static void WriteFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) {\
+		struct hws *hw = p;\
+		while (size--)\
+			writeb(*dp++, ((typ *)hw->adr) + off);\
+	}
+
+#define ASSIGN_FUNC(typ, name, dest)	do {\
+	dest.read_reg = &Read##name##_##typ;\
+	dest.write_reg = &Write##name##_##typ;\
+	dest.read_fifo = &ReadFiFo##name##_##typ;\
+	dest.write_fifo = &WriteFiFo##name##_##typ;\
+	} while (0)
+#define ASSIGN_FUNC_IPAC(typ, target)	do {\
+	ASSIGN_FUNC(typ, ISAC, target.isac);\
+	ASSIGN_FUNC(typ, IPAC, target);\
+	} while (0)
+
+#endif

+ 405 - 0
drivers/isdn/hardware/mISDN/ipac.h

@@ -0,0 +1,405 @@
+/*
+ *
+ * ipac.h	Defines for the Infineon (former Siemens) ISDN
+ *		chip series
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "iohelper.h"
+
+struct isac_hw {
+	struct dchannel		dch;
+	u32			type;
+	u32			off;		/* offset to isac regs */
+	char			*name;
+	spinlock_t		*hwlock;	/* lock HW acccess */
+	read_reg_func		*read_reg;
+	write_reg_func		*write_reg;
+	fifo_func		*read_fifo;
+	fifo_func		*write_fifo;
+	int			(*monitor)(void *, u32, u8 *, int);
+	void			(*release)(struct isac_hw *);
+	int			(*init)(struct isac_hw *);
+	int			(*ctrl)(struct isac_hw *, u32, u_long);
+	int			(*open)(struct isac_hw *, struct channel_req *);
+	u8			*mon_tx;
+	u8			*mon_rx;
+	int			mon_txp;
+	int			mon_txc;
+	int			mon_rxp;
+	struct arcofi_msg	*arcofi_list;
+	struct timer_list	arcofitimer;
+	wait_queue_head_t	arcofi_wait;
+	u8			arcofi_bc;
+	u8			arcofi_state;
+	u8			mocr;
+	u8			adf2;
+	u8			state;
+};
+
+struct ipac_hw;
+
+struct hscx_hw {
+	struct bchannel		bch;
+	struct ipac_hw		*ip;
+	u8			fifo_size;
+	u8			off;	/* offset to ICA or ICB */
+	u8			slot;
+	char			log[64];
+};
+
+struct ipac_hw {
+	struct isac_hw		isac;
+	struct hscx_hw		hscx[2];
+	char			*name;
+	void			*hw;
+	spinlock_t		*hwlock;	/* lock HW acccess */
+	struct module		*owner;
+	u32			type;
+	read_reg_func		*read_reg;
+	write_reg_func		*write_reg;
+	fifo_func		*read_fifo;
+	fifo_func		*write_fifo;
+	void			(*release)(struct ipac_hw *);
+	int			(*init)(struct ipac_hw *);
+	int			(*ctrl)(struct ipac_hw *, u32, u_long);
+	u8			conf;
+};
+
+#define IPAC_TYPE_ISAC		0x0010
+#define IPAC_TYPE_IPAC		0x0020
+#define IPAC_TYPE_ISACX		0x0040
+#define IPAC_TYPE_IPACX		0x0080
+#define IPAC_TYPE_HSCX		0x0100
+
+#define ISAC_USE_ARCOFI		0x1000
+
+/* Monitor functions */
+#define MONITOR_RX_0		0x1000
+#define MONITOR_RX_1		0x1001
+#define MONITOR_TX_0		0x2000
+#define MONITOR_TX_1		0x2001
+
+/* All registers original Siemens Spec  */
+/* IPAC/ISAC registers */
+#define ISAC_MASK		0x20
+#define ISAC_ISTA		0x20
+#define ISAC_STAR		0x21
+#define ISAC_CMDR		0x21
+#define ISAC_EXIR		0x24
+#define ISAC_ADF2		0x39
+#define ISAC_SPCR		0x30
+#define ISAC_ADF1		0x38
+#define ISAC_CIR0		0x31
+#define ISAC_CIX0		0x31
+#define ISAC_CIR1		0x33
+#define ISAC_CIX1		0x33
+#define ISAC_STCR		0x37
+#define ISAC_MODE		0x22
+#define ISAC_RSTA		0x27
+#define ISAC_RBCL		0x25
+#define ISAC_RBCH		0x2A
+#define ISAC_TIMR		0x23
+#define ISAC_SQXR		0x3b
+#define ISAC_SQRR		0x3b
+#define ISAC_MOSR		0x3a
+#define ISAC_MOCR		0x3a
+#define ISAC_MOR0		0x32
+#define ISAC_MOX0		0x32
+#define ISAC_MOR1		0x34
+#define ISAC_MOX1		0x34
+
+#define ISAC_RBCH_XAC		0x80
+
+#define IPAC_D_TIN2		0x01
+
+/* IPAC/HSCX */
+#define IPAC_ISTAB		0x20	/* RD	*/
+#define IPAC_MASKB		0x20	/* WR	*/
+#define IPAC_STARB		0x21	/* RD	*/
+#define IPAC_CMDRB		0x21	/* WR	*/
+#define IPAC_MODEB		0x22	/* R/W	*/
+#define IPAC_EXIRB		0x24	/* RD	*/
+#define IPAC_RBCLB		0x25	/* RD	*/
+#define IPAC_RAH1		0x26	/* WR	*/
+#define IPAC_RAH2		0x27	/* WR	*/
+#define IPAC_RSTAB		0x27	/* RD	*/
+#define IPAC_RAL1		0x28	/* R/W	*/
+#define IPAC_RAL2		0x29	/* WR	*/
+#define IPAC_RHCRB		0x29	/* RD	*/
+#define IPAC_XBCL		0x2A	/* WR	*/
+#define IPAC_CCR2		0x2C	/* R/W	*/
+#define IPAC_RBCHB		0x2D	/* RD	*/
+#define IPAC_XBCH		0x2D	/* WR	*/
+#define HSCX_VSTR		0x2E	/* RD	*/
+#define IPAC_RLCR		0x2E	/* WR	*/
+#define IPAC_CCR1		0x2F	/* R/W	*/
+#define IPAC_TSAX		0x30	/* WR	*/
+#define IPAC_TSAR		0x31	/* WR	*/
+#define IPAC_XCCR		0x32	/* WR	*/
+#define IPAC_RCCR		0x33	/* WR	*/
+
+/* IPAC_ISTAB/IPAC_MASKB bits */
+#define IPAC_B_XPR		0x10
+#define IPAC_B_RPF		0x40
+#define IPAC_B_RME		0x80
+#define IPAC_B_ON		0x2F
+
+/* IPAC_EXIRB bits */
+#define IPAC_B_RFS		0x04
+#define IPAC_B_RFO		0x10
+#define IPAC_B_XDU		0x40
+#define IPAC_B_XMR		0x80
+
+/* IPAC special registers */
+#define IPAC_CONF		0xC0	/* R/W	*/
+#define IPAC_ISTA		0xC1	/* RD	*/
+#define IPAC_MASK		0xC1	/* WR	*/
+#define IPAC_ID			0xC2	/* RD	*/
+#define IPAC_ACFG		0xC3	/* R/W	*/
+#define IPAC_AOE		0xC4	/* R/W	*/
+#define IPAC_ARX		0xC5	/* RD	*/
+#define IPAC_ATX		0xC5	/* WR	*/
+#define IPAC_PITA1		0xC6	/* R/W	*/
+#define IPAC_PITA2		0xC7	/* R/W	*/
+#define IPAC_POTA1		0xC8	/* R/W	*/
+#define IPAC_POTA2		0xC9	/* R/W	*/
+#define IPAC_PCFG		0xCA	/* R/W	*/
+#define IPAC_SCFG		0xCB	/* R/W	*/
+#define IPAC_TIMR2		0xCC	/* R/W	*/
+
+/* IPAC_ISTA/_MASK bits */
+#define IPAC__EXB		0x01
+#define IPAC__ICB		0x02
+#define IPAC__EXA		0x04
+#define IPAC__ICA		0x08
+#define IPAC__EXD		0x10
+#define IPAC__ICD		0x20
+#define IPAC__INT0		0x40
+#define IPAC__INT1		0x80
+#define IPAC__ON		0xC0
+
+/* HSCX ISTA/MASK bits */
+#define HSCX__EXB		0x01
+#define HSCX__EXA		0x02
+#define HSCX__ICA		0x04
+
+/* ISAC/ISACX/IPAC/IPACX L1 commands */
+#define ISAC_CMD_TIM		0x0
+#define ISAC_CMD_RS		0x1
+#define ISAC_CMD_SCZ		0x4
+#define ISAC_CMD_SSZ		0x2
+#define ISAC_CMD_AR8		0x8
+#define ISAC_CMD_AR10		0x9
+#define ISAC_CMD_ARL		0xA
+#define ISAC_CMD_DUI		0xF
+
+/* ISAC/ISACX/IPAC/IPACX L1 indications */
+#define ISAC_IND_RS		0x1
+#define ISAC_IND_PU		0x7
+#define ISAC_IND_DR		0x0
+#define ISAC_IND_SD		0x2
+#define ISAC_IND_DIS		0x3
+#define ISAC_IND_EI		0x6
+#define ISAC_IND_RSY		0x4
+#define ISAC_IND_ARD		0x8
+#define ISAC_IND_TI		0xA
+#define ISAC_IND_ATI		0xB
+#define ISAC_IND_AI8		0xC
+#define ISAC_IND_AI10		0xD
+#define ISAC_IND_DID		0xF
+
+/* the new ISACX / IPACX */
+/* D-channel registers   */
+#define ISACX_RFIFOD		0x00	/* RD	*/
+#define ISACX_XFIFOD		0x00	/* WR	*/
+#define ISACX_ISTAD		0x20	/* RD	*/
+#define ISACX_MASKD		0x20	/* WR	*/
+#define ISACX_STARD		0x21	/* RD	*/
+#define ISACX_CMDRD		0x21	/* WR	*/
+#define ISACX_MODED		0x22	/* R/W	*/
+#define ISACX_EXMD1		0x23	/* R/W	*/
+#define ISACX_TIMR1		0x24	/* R/W	*/
+#define ISACX_SAP1		0x25	/* WR	*/
+#define ISACX_SAP2		0x26	/* WR	*/
+#define ISACX_RBCLD		0x26	/* RD	*/
+#define ISACX_RBCHD		0x27	/* RD	*/
+#define ISACX_TEI1		0x27	/* WR	*/
+#define ISACX_TEI2		0x28	/* WR	*/
+#define ISACX_RSTAD		0x28	/* RD	*/
+#define ISACX_TMD		0x29	/* R/W	*/
+#define ISACX_CIR0		0x2E	/* RD	*/
+#define ISACX_CIX0		0x2E	/* WR	*/
+#define ISACX_CIR1		0x2F	/* RD	*/
+#define ISACX_CIX1		0x2F	/* WR	*/
+
+/* Transceiver registers  */
+#define ISACX_TR_CONF0		0x30	/* R/W	*/
+#define ISACX_TR_CONF1		0x31	/* R/W	*/
+#define ISACX_TR_CONF2		0x32	/* R/W	*/
+#define ISACX_TR_STA		0x33	/* RD	*/
+#define ISACX_TR_CMD		0x34	/* R/W	*/
+#define ISACX_SQRR1		0x35	/* RD	*/
+#define ISACX_SQXR1		0x35	/* WR	*/
+#define ISACX_SQRR2		0x36	/* RD	*/
+#define ISACX_SQXR2		0x36	/* WR	*/
+#define ISACX_SQRR3		0x37	/* RD	*/
+#define ISACX_SQXR3		0x37	/* WR	*/
+#define ISACX_ISTATR		0x38	/* RD	*/
+#define ISACX_MASKTR		0x39	/* R/W	*/
+#define ISACX_TR_MODE		0x3A	/* R/W	*/
+#define ISACX_ACFG1		0x3C	/* R/W	*/
+#define ISACX_ACFG2		0x3D	/* R/W	*/
+#define ISACX_AOE		0x3E	/* R/W	*/
+#define ISACX_ARX		0x3F	/* RD	*/
+#define ISACX_ATX		0x3F	/* WR	*/
+
+/* IOM: Timeslot, DPS, CDA  */
+#define ISACX_CDA10		0x40	/* R/W	*/
+#define ISACX_CDA11		0x41	/* R/W	*/
+#define ISACX_CDA20		0x42	/* R/W	*/
+#define ISACX_CDA21		0x43	/* R/W	*/
+#define ISACX_CDA_TSDP10	0x44	/* R/W	*/
+#define ISACX_CDA_TSDP11	0x45	/* R/W	*/
+#define ISACX_CDA_TSDP20	0x46	/* R/W	*/
+#define ISACX_CDA_TSDP21	0x47	/* R/W	*/
+#define ISACX_BCHA_TSDP_BC1	0x48	/* R/W	*/
+#define ISACX_BCHA_TSDP_BC2	0x49	/* R/W	*/
+#define ISACX_BCHB_TSDP_BC1	0x4A	/* R/W	*/
+#define ISACX_BCHB_TSDP_BC2	0x4B	/* R/W	*/
+#define ISACX_TR_TSDP_BC1	0x4C	/* R/W	*/
+#define ISACX_TR_TSDP_BC2	0x4D	/* R/W	*/
+#define ISACX_CDA1_CR		0x4E	/* R/W	*/
+#define ISACX_CDA2_CR		0x4F	/* R/W	*/
+
+/* IOM: Contol, Sync transfer, Monitor    */
+#define ISACX_TR_CR		0x50	/* R/W	*/
+#define ISACX_TRC_CR		0x50	/* R/W	*/
+#define ISACX_BCHA_CR		0x51	/* R/W	*/
+#define ISACX_BCHB_CR		0x52	/* R/W	*/
+#define ISACX_DCI_CR		0x53	/* R/W	*/
+#define ISACX_DCIC_CR		0x53	/* R/W	*/
+#define ISACX_MON_CR		0x54	/* R/W	*/
+#define ISACX_SDS1_CR		0x55	/* R/W	*/
+#define ISACX_SDS2_CR		0x56	/* R/W	*/
+#define ISACX_IOM_CR		0x57	/* R/W	*/
+#define ISACX_STI		0x58	/* RD	*/
+#define ISACX_ASTI		0x58	/* WR	*/
+#define ISACX_MSTI		0x59	/* R/W	*/
+#define ISACX_SDS_CONF		0x5A	/* R/W	*/
+#define ISACX_MCDA		0x5B	/* RD	*/
+#define ISACX_MOR		0x5C	/* RD	*/
+#define ISACX_MOX		0x5C	/* WR	*/
+#define ISACX_MOSR		0x5D	/* RD	*/
+#define ISACX_MOCR		0x5E	/* R/W	*/
+#define ISACX_MSTA		0x5F	/* RD	*/
+#define ISACX_MCONF		0x5F	/* WR	*/
+
+/* Interrupt and general registers */
+#define ISACX_ISTA		0x60	/* RD	*/
+#define ISACX_MASK		0x60	/* WR	*/
+#define ISACX_AUXI		0x61	/* RD	*/
+#define ISACX_AUXM		0x61	/* WR	*/
+#define ISACX_MODE1		0x62	/* R/W	*/
+#define ISACX_MODE2		0x63	/* R/W	*/
+#define ISACX_ID		0x64	/* RD	*/
+#define ISACX_SRES		0x64	/* WR	*/
+#define ISACX_TIMR2		0x65	/* R/W	*/
+
+/* Register Bits */
+/* ISACX/IPACX _ISTAD (R) and _MASKD (W) */
+#define ISACX_D_XDU		0x04
+#define ISACX_D_XMR		0x08
+#define ISACX_D_XPR		0x10
+#define ISACX_D_RFO		0x20
+#define ISACX_D_RPF		0x40
+#define ISACX_D_RME		0x80
+
+/* ISACX/IPACX _ISTA (R) and _MASK (W) */
+#define ISACX__ICD		0x01
+#define ISACX__MOS		0x02
+#define ISACX__TRAN		0x04
+#define ISACX__AUX		0x08
+#define ISACX__CIC		0x10
+#define ISACX__ST		0x20
+#define IPACX__ICB		0x40
+#define IPACX__ICA		0x80
+#define IPACX__ON		0x2C
+
+/* ISACX/IPACX _CMDRD (W) */
+#define ISACX_CMDRD_XRES	0x01
+#define ISACX_CMDRD_XME		0x02
+#define ISACX_CMDRD_XTF		0x08
+#define ISACX_CMDRD_STI		0x10
+#define ISACX_CMDRD_RRES	0x40
+#define ISACX_CMDRD_RMC		0x80
+
+/* ISACX/IPACX _RSTAD (R) */
+#define ISACX_RSTAD_TA		0x01
+#define ISACX_RSTAD_CR		0x02
+#define ISACX_RSTAD_SA0		0x04
+#define ISACX_RSTAD_SA1		0x08
+#define ISACX_RSTAD_RAB		0x10
+#define ISACX_RSTAD_CRC		0x20
+#define ISACX_RSTAD_RDO		0x40
+#define ISACX_RSTAD_VFR		0x80
+
+/* ISACX/IPACX _CIR0 (R) */
+#define ISACX_CIR0_BAS		0x01
+#define ISACX_CIR0_SG		0x08
+#define ISACX_CIR0_CIC1		0x08
+#define ISACX_CIR0_CIC0		0x08
+
+/* B-channel registers */
+#define IPACX_OFF_ICA		0x70
+#define IPACX_OFF_ICB		0x80
+
+/* ICA: IPACX_OFF_ICA + Reg ICB: IPACX_OFF_ICB + Reg */
+
+#define IPACX_ISTAB		0x00    /* RD	*/
+#define IPACX_MASKB		0x00	/* WR	*/
+#define IPACX_STARB		0x01	/* RD	*/
+#define IPACX_CMDRB		0x01	/* WR	*/
+#define IPACX_MODEB		0x02	/* R/W	*/
+#define IPACX_EXMB		0x03	/* R/W	*/
+#define IPACX_RAH1		0x05	/* WR	*/
+#define IPACX_RAH2		0x06	/* WR	*/
+#define IPACX_RBCLB		0x06	/* RD	*/
+#define IPACX_RBCHB		0x07	/* RD	*/
+#define IPACX_RAL1		0x07	/* WR	*/
+#define IPACX_RAL2		0x08	/* WR	*/
+#define IPACX_RSTAB		0x08	/* RD	*/
+#define IPACX_TMB		0x09	/* R/W	*/
+#define IPACX_RFIFOB		0x0A	/* RD	*/
+#define IPACX_XFIFOB		0x0A	/* WR	*/
+
+/* IPACX_ISTAB / IPACX_MASKB bits */
+#define IPACX_B_XDU		0x04
+#define IPACX_B_XPR		0x10
+#define IPACX_B_RFO		0x20
+#define IPACX_B_RPF		0x40
+#define IPACX_B_RME		0x80
+
+#define IPACX_B_ON		0x0B
+
+extern int mISDNisac_init(struct isac_hw *, void *);
+extern irqreturn_t mISDNisac_irq(struct isac_hw *, u8);
+extern u32 mISDNipac_init(struct ipac_hw *, void *);
+extern irqreturn_t mISDNipac_irq(struct ipac_hw *, int);

+ 269 - 0
drivers/isdn/hardware/mISDN/isar.h

@@ -0,0 +1,269 @@
+/*
+ *
+ * isar.h   ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "iohelper.h"
+
+struct isar_hw;
+
+struct isar_ch {
+	struct bchannel		bch;
+	struct isar_hw		*is;
+	struct timer_list	ftimer;
+	u8			nr;
+	u8			dpath;
+	u8			mml;
+	u8			state;
+	u8			cmd;
+	u8			mod;
+	u8			newcmd;
+	u8			newmod;
+	u8			try_mod;
+	u8			conmsg[16];
+};
+
+struct isar_hw {
+	struct	isar_ch	ch[2];
+	void		*hw;
+	spinlock_t	*hwlock;	/* lock HW acccess */
+	char		*name;
+	struct module	*owner;
+	read_reg_func	*read_reg;
+	write_reg_func	*write_reg;
+	fifo_func	*read_fifo;
+	fifo_func	*write_fifo;
+	int		(*ctrl)(void *, u32, u_long);
+	void		(*release)(struct isar_hw *);
+	int		(*init)(struct isar_hw *);
+	int		(*open)(struct isar_hw *, struct channel_req *);
+	int		(*firmware)(struct isar_hw *, const u8 *, int);
+	unsigned long	Flags;
+	int		version;
+	u8		bstat;
+	u8		iis;
+	u8		cmsb;
+	u8		clsb;
+	u8		buf[256];
+	u8		log[256];
+};
+
+#define ISAR_IRQMSK	0x04
+#define ISAR_IRQSTA	0x04
+#define ISAR_IRQBIT	0x75
+#define ISAR_CTRL_H	0x61
+#define ISAR_CTRL_L	0x60
+#define ISAR_IIS	0x58
+#define ISAR_IIA	0x58
+#define ISAR_HIS	0x50
+#define ISAR_HIA	0x50
+#define ISAR_MBOX	0x4c
+#define ISAR_WADR	0x4a
+#define ISAR_RADR	0x48
+
+#define ISAR_HIS_VNR		0x14
+#define ISAR_HIS_DKEY		0x02
+#define ISAR_HIS_FIRM		0x1e
+#define ISAR_HIS_STDSP		0x08
+#define ISAR_HIS_DIAG		0x05
+#define ISAR_HIS_P0CFG		0x3c
+#define ISAR_HIS_P12CFG		0x24
+#define ISAR_HIS_SARTCFG	0x25
+#define ISAR_HIS_PUMPCFG	0x26
+#define ISAR_HIS_PUMPCTRL	0x2a
+#define ISAR_HIS_IOM2CFG	0x27
+#define ISAR_HIS_IOM2REQ	0x07
+#define ISAR_HIS_IOM2CTRL	0x2b
+#define ISAR_HIS_BSTREQ		0x0c
+#define ISAR_HIS_PSTREQ		0x0e
+#define ISAR_HIS_SDATA		0x20
+#define ISAR_HIS_DPS1		0x40
+#define ISAR_HIS_DPS2		0x80
+#define SET_DPS(x)		((x<<6) & 0xc0)
+
+#define ISAR_IIS_MSCMSD		0x3f
+#define ISAR_IIS_VNR		0x15
+#define ISAR_IIS_DKEY		0x03
+#define ISAR_IIS_FIRM		0x1f
+#define ISAR_IIS_STDSP		0x09
+#define ISAR_IIS_DIAG		0x25
+#define ISAR_IIS_GSTEV		0x00
+#define ISAR_IIS_BSTEV		0x28
+#define ISAR_IIS_BSTRSP		0x2c
+#define ISAR_IIS_PSTRSP		0x2e
+#define ISAR_IIS_PSTEV		0x2a
+#define ISAR_IIS_IOM2RSP	0x27
+#define ISAR_IIS_RDATA		0x20
+#define ISAR_IIS_INVMSG		0x3f
+
+#define ISAR_CTRL_SWVER	0x10
+#define ISAR_CTRL_STST	0x40
+
+#define ISAR_MSG_HWVER	0x20
+
+#define ISAR_DP1_USE	1
+#define ISAR_DP2_USE	2
+#define ISAR_RATE_REQ	3
+
+#define PMOD_DISABLE	0
+#define PMOD_FAX	1
+#define PMOD_DATAMODEM	2
+#define PMOD_HALFDUPLEX	3
+#define PMOD_V110	4
+#define PMOD_DTMF	5
+#define PMOD_DTMF_TRANS	6
+#define PMOD_BYPASS	7
+
+#define PCTRL_ORIG	0x80
+#define PV32P2_V23R	0x40
+#define PV32P2_V22A	0x20
+#define PV32P2_V22B	0x10
+#define PV32P2_V22C	0x08
+#define PV32P2_V21	0x02
+#define PV32P2_BEL	0x01
+
+/* LSB MSB in ISAR doc wrong !!! Arghhh */
+#define PV32P3_AMOD	0x80
+#define PV32P3_V32B	0x02
+#define PV32P3_V23B	0x01
+#define PV32P4_48	0x11
+#define PV32P5_48	0x05
+#define PV32P4_UT48	0x11
+#define PV32P5_UT48	0x0d
+#define PV32P4_96	0x11
+#define PV32P5_96	0x03
+#define PV32P4_UT96	0x11
+#define PV32P5_UT96	0x0f
+#define PV32P4_B96	0x91
+#define PV32P5_B96	0x0b
+#define PV32P4_UTB96	0xd1
+#define PV32P5_UTB96	0x0f
+#define PV32P4_120	0xb1
+#define PV32P5_120	0x09
+#define PV32P4_UT120	0xf1
+#define PV32P5_UT120	0x0f
+#define PV32P4_144	0x99
+#define PV32P5_144	0x09
+#define PV32P4_UT144	0xf9
+#define PV32P5_UT144	0x0f
+#define PV32P6_CTN	0x01
+#define PV32P6_ATN	0x02
+
+#define PFAXP2_CTN	0x01
+#define PFAXP2_ATN	0x04
+
+#define PSEV_10MS_TIMER	0x02
+#define PSEV_CON_ON	0x18
+#define PSEV_CON_OFF	0x19
+#define PSEV_V24_OFF	0x20
+#define PSEV_CTS_ON	0x21
+#define PSEV_CTS_OFF	0x22
+#define PSEV_DCD_ON	0x23
+#define PSEV_DCD_OFF	0x24
+#define PSEV_DSR_ON	0x25
+#define PSEV_DSR_OFF	0x26
+#define PSEV_REM_RET	0xcc
+#define PSEV_REM_REN	0xcd
+#define PSEV_GSTN_CLR	0xd4
+
+#define PSEV_RSP_READY	0xbc
+#define PSEV_LINE_TX_H	0xb3
+#define PSEV_LINE_TX_B	0xb2
+#define PSEV_LINE_RX_H	0xb1
+#define PSEV_LINE_RX_B	0xb0
+#define PSEV_RSP_CONN	0xb5
+#define PSEV_RSP_DISC	0xb7
+#define PSEV_RSP_FCERR	0xb9
+#define PSEV_RSP_SILDET	0xbe
+#define PSEV_RSP_SILOFF	0xab
+#define PSEV_FLAGS_DET	0xba
+
+#define PCTRL_CMD_TDTMF	0x5a
+
+#define PCTRL_CMD_FTH	0xa7
+#define PCTRL_CMD_FRH	0xa5
+#define PCTRL_CMD_FTM	0xa8
+#define PCTRL_CMD_FRM	0xa6
+#define PCTRL_CMD_SILON	0xac
+#define PCTRL_CMD_CONT	0xa2
+#define PCTRL_CMD_ESC	0xa4
+#define PCTRL_CMD_SILOFF 0xab
+#define PCTRL_CMD_HALT	0xa9
+
+#define PCTRL_LOC_RET	0xcf
+#define PCTRL_LOC_REN	0xce
+
+#define SMODE_DISABLE	0
+#define SMODE_V14	2
+#define SMODE_HDLC	3
+#define SMODE_BINARY	4
+#define SMODE_FSK_V14	5
+
+#define SCTRL_HDMC_BOTH	0x00
+#define SCTRL_HDMC_DTX	0x80
+#define SCTRL_HDMC_DRX	0x40
+#define S_P1_OVSP	0x40
+#define S_P1_SNP	0x20
+#define S_P1_EOP	0x10
+#define S_P1_EDP	0x08
+#define S_P1_NSB	0x04
+#define S_P1_CHS_8	0x03
+#define S_P1_CHS_7	0x02
+#define S_P1_CHS_6	0x01
+#define S_P1_CHS_5	0x00
+
+#define S_P2_BFT_DEF	0x10
+
+#define IOM_CTRL_ENA	0x80
+#define IOM_CTRL_NOPCM	0x00
+#define IOM_CTRL_ALAW	0x02
+#define IOM_CTRL_ULAW	0x04
+#define IOM_CTRL_RCV	0x01
+
+#define IOM_P1_TXD	0x10
+
+#define HDLC_FED	0x40
+#define HDLC_FSD	0x20
+#define HDLC_FST	0x20
+#define HDLC_ERROR	0x1c
+#define HDLC_ERR_FAD	0x10
+#define HDLC_ERR_RER	0x08
+#define HDLC_ERR_CER	0x04
+#define SART_NMD	0x01
+
+#define BSTAT_RDM0	0x1
+#define BSTAT_RDM1	0x2
+#define BSTAT_RDM2	0x4
+#define BSTAT_RDM3	0x8
+#define BSTEV_TBO	0x1f
+#define BSTEV_RBO	0x2f
+
+/* FAX State Machine */
+#define STFAX_NULL	0
+#define STFAX_READY	1
+#define STFAX_LINE	2
+#define STFAX_CONT	3
+#define STFAX_ACTIV	4
+#define STFAX_ESCAPE	5
+#define STFAX_SILDET	6
+
+extern u32 mISDNisar_init(struct isar_hw *, void *);
+extern void mISDNisar_irq(struct isar_hw *);

+ 1178 - 0
drivers/isdn/hardware/mISDN/mISDNinfineon.c

@@ -0,0 +1,1178 @@
+/*
+ * mISDNinfineon.c
+ *		Support for cards based on following Infineon ISDN chipsets
+ *		- ISAC + HSCX
+ *		- IPAC and IPAC-X
+ *		- ISAC-SX + HSCX
+ *
+ * Supported cards:
+ *		- Dialogic Diva 2.0
+ *		- Dialogic Diva 2.0U
+ *		- Dialogic Diva 2.01
+ *		- Dialogic Diva 2.02
+ *		- Sedlbauer Speedwin
+ *		- HST Saphir3
+ *		- Develo (former ELSA) Microlink PCI (Quickstep 1000)
+ *		- Develo (former ELSA) Quickstep 3000
+ *		- Berkom Scitel BRIX Quadro
+ *		- Dr.Neuhaus (Sagem) Niccy
+ *
+ *
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include "ipac.h"
+
+#define INFINEON_REV	"1.0"
+
+static int inf_cnt;
+static u32 debug;
+static u32 irqloops = 4;
+
+enum inf_types {
+	INF_NONE,
+	INF_DIVA20,
+	INF_DIVA20U,
+	INF_DIVA201,
+	INF_DIVA202,
+	INF_SPEEDWIN,
+	INF_SAPHIR3,
+	INF_QS1000,
+	INF_QS3000,
+	INF_NICCY,
+	INF_SCT_1,
+	INF_SCT_2,
+	INF_SCT_3,
+	INF_SCT_4,
+	INF_GAZEL_R685,
+	INF_GAZEL_R753
+};
+
+enum addr_mode {
+	AM_NONE = 0,
+	AM_IO,
+	AM_MEMIO,
+	AM_IND_IO,
+};
+
+struct inf_cinfo {
+	enum inf_types	typ;
+	const char	*full;
+	const char	*name;
+	enum addr_mode	cfg_mode;
+	enum addr_mode	addr_mode;
+	u8		cfg_bar;
+	u8		addr_bar;
+	void		*irqfunc;
+};
+
+struct _ioaddr {
+	enum addr_mode	mode;
+	union {
+		void __iomem	*p;
+		struct _ioport	io;
+	} a;
+};
+
+struct _iohandle {
+	enum addr_mode	mode;
+	resource_size_t	size;
+	resource_size_t	start;
+	void __iomem	*p;
+};
+
+struct inf_hw {
+	struct list_head	list;
+	struct pci_dev		*pdev;
+	const struct inf_cinfo	*ci;
+	char			name[MISDN_MAX_IDLEN];
+	u32			irq;
+	u32			irqcnt;
+	struct _iohandle	cfg;
+	struct _iohandle	addr;
+	struct _ioaddr		isac;
+	struct _ioaddr		hscx;
+	spinlock_t		lock;	/* HW access lock */
+	struct ipac_hw		ipac;
+	struct inf_hw		*sc[3];	/* slave cards */
+};
+
+
+#define PCI_SUBVENDOR_HST_SAPHIR3       0x52
+#define PCI_SUBVENDOR_SEDLBAUER_PCI     0x53
+#define PCI_SUB_ID_SEDLBAUER            0x01
+
+static struct pci_device_id infineon_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA20},
+	{ PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20_U,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA20U},
+	{ PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA201,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA201},
+	{ PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA202,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA202},
+	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+	  PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0,
+	  INF_SPEEDWIN},
+	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+	  PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3},
+	{ PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_QS1000},
+	{ PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_QS3000,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_QS3000},
+	{ PCI_VENDOR_ID_SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_NICCY},
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+	  PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0,
+	  INF_SCT_1},
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R685,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R685},
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R753,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_OLITEC,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, infineon_ids);
+
+/* PCI interface specific defines */
+/* Diva 2.0/2.0U */
+#define DIVA_HSCX_PORT		0x00
+#define DIVA_HSCX_ALE		0x04
+#define DIVA_ISAC_PORT		0x08
+#define DIVA_ISAC_ALE		0x0C
+#define DIVA_PCI_CTRL           0x10
+
+/* DIVA_PCI_CTRL bits */
+#define DIVA_IRQ_BIT		0x01
+#define DIVA_RESET_BIT		0x08
+#define DIVA_EEPROM_CLK		0x40
+#define DIVA_LED_A		0x10
+#define DIVA_LED_B		0x20
+#define DIVA_IRQ_CLR		0x80
+
+/* Diva 2.01/2.02 */
+/* Siemens PITA */
+#define PITA_ICR_REG		0x00
+#define PITA_INT0_STATUS	0x02
+
+#define PITA_MISC_REG		0x1c
+#define PITA_PARA_SOFTRESET	0x01000000
+#define PITA_SER_SOFTRESET	0x02000000
+#define PITA_PARA_MPX_MODE	0x04000000
+#define PITA_INT0_ENABLE	0x00020000
+
+/* TIGER 100 Registers */
+#define TIGER_RESET_ADDR	0x00
+#define TIGER_EXTERN_RESET	0x01
+#define TIGER_AUX_CTRL		0x02
+#define TIGER_AUX_DATA		0x03
+#define TIGER_AUX_IRQMASK	0x05
+#define TIGER_AUX_STATUS	0x07
+
+/* Tiger AUX BITs */
+#define TIGER_IOMASK		0xdd	/* 1 and 5 are inputs */
+#define TIGER_IRQ_BIT		0x02
+
+#define TIGER_IPAC_ALE		0xC0
+#define TIGER_IPAC_PORT		0xC8
+
+/* ELSA (now Develo) PCI cards */
+#define ELSA_IRQ_ADDR		0x4c
+#define ELSA_IRQ_MASK		0x04
+#define QS1000_IRQ_OFF		0x01
+#define QS3000_IRQ_OFF		0x03
+#define QS1000_IRQ_ON		0x41
+#define QS3000_IRQ_ON		0x43
+
+/* Dr Neuhaus/Sagem Niccy */
+#define NICCY_ISAC_PORT		0x00
+#define NICCY_HSCX_PORT		0x01
+#define NICCY_ISAC_ALE		0x02
+#define NICCY_HSCX_ALE		0x03
+
+#define NICCY_IRQ_CTRL_REG	0x38
+#define NICCY_IRQ_ENABLE	0x001f00
+#define NICCY_IRQ_DISABLE	0xff0000
+#define NICCY_IRQ_BIT		0x800000
+
+
+/* Scitel PLX */
+#define SCT_PLX_IRQ_ADDR	0x4c
+#define SCT_PLX_RESET_ADDR	0x50
+#define SCT_PLX_IRQ_ENABLE	0x41
+#define SCT_PLX_RESET_BIT	0x04
+
+/* Gazel */
+#define	GAZEL_IPAC_DATA_PORT	0x04
+/* Gazel PLX */
+#define GAZEL_CNTRL		0x50
+#define GAZEL_RESET		0x04
+#define GAZEL_RESET_9050	0x40000000
+#define GAZEL_INCSR		0x4C
+#define GAZEL_ISAC_EN		0x08
+#define GAZEL_INT_ISAC		0x20
+#define GAZEL_HSCX_EN		0x01
+#define GAZEL_INT_HSCX		0x04
+#define GAZEL_PCI_EN		0x40
+#define GAZEL_IPAC_EN		0x03
+
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct inf_hw *card)
+{
+	card->ipac.isac.dch.debug = debug;
+	card->ipac.hscx[0].bch.debug = debug;
+	card->ipac.hscx[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct inf_hw *card;
+
+	ret = param_set_uint(val, kp);
+	if (!ret) {
+		read_lock(&card_lock);
+		list_for_each_entry(card, &Cards, list)
+			_set_debug(card);
+		read_unlock(&card_lock);
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(INFINEON_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "infineon debug mask");
+module_param(irqloops, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)");
+
+/* Interface functions */
+
+IOFUNC_IO(ISAC, inf_hw, isac.a.io)
+IOFUNC_IO(IPAC, inf_hw, hscx.a.io)
+IOFUNC_IND(ISAC, inf_hw, isac.a.io)
+IOFUNC_IND(IPAC, inf_hw, hscx.a.io)
+IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p)
+IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p)
+
+static irqreturn_t
+diva_irq(int intno, void *dev_id)
+{
+	struct inf_hw *hw = dev_id;
+	u8 val;
+
+	spin_lock(&hw->lock);
+	val = inb((u32)hw->cfg.start + DIVA_PCI_CTRL);
+	if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */
+		spin_unlock(&hw->lock);
+		return IRQ_NONE; /* shared */
+	}
+	hw->irqcnt++;
+	mISDNipac_irq(&hw->ipac, irqloops);
+	spin_unlock(&hw->lock);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva20x_irq(int intno, void *dev_id)
+{
+	struct inf_hw *hw = dev_id;
+	u8 val;
+
+	spin_lock(&hw->lock);
+	val = readb(hw->cfg.p);
+	if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */
+		spin_unlock(&hw->lock);
+		return IRQ_NONE; /* shared */
+	}
+	hw->irqcnt++;
+	mISDNipac_irq(&hw->ipac, irqloops);
+	writeb(PITA_INT0_STATUS, hw->cfg.p); /* ACK PITA INT0 */
+	spin_unlock(&hw->lock);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+tiger_irq(int intno, void *dev_id)
+{
+	struct inf_hw *hw = dev_id;
+	u8 val;
+
+	spin_lock(&hw->lock);
+	val = inb((u32)hw->cfg.start + TIGER_AUX_STATUS);
+	if (val & TIGER_IRQ_BIT) { /* for us or shared ? */
+		spin_unlock(&hw->lock);
+		return IRQ_NONE; /* shared */
+	}
+	hw->irqcnt++;
+	mISDNipac_irq(&hw->ipac, irqloops);
+	spin_unlock(&hw->lock);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+elsa_irq(int intno, void *dev_id)
+{
+	struct inf_hw *hw = dev_id;
+	u8 val;
+
+	spin_lock(&hw->lock);
+	val = inb((u32)hw->cfg.start + ELSA_IRQ_ADDR);
+	if (!(val & ELSA_IRQ_MASK)) {
+		spin_unlock(&hw->lock);
+		return IRQ_NONE; /* shared */
+	}
+	hw->irqcnt++;
+	mISDNipac_irq(&hw->ipac, irqloops);
+	spin_unlock(&hw->lock);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+niccy_irq(int intno, void *dev_id)
+{
+	struct inf_hw *hw = dev_id;
+	u32 val;
+
+	spin_lock(&hw->lock);
+	val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+	if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */
+		spin_unlock(&hw->lock);
+		return IRQ_NONE; /* shared */
+	}
+	outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+	hw->irqcnt++;
+	mISDNipac_irq(&hw->ipac, irqloops);
+	spin_unlock(&hw->lock);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+gazel_irq(int intno, void *dev_id)
+{
+	struct inf_hw *hw = dev_id;
+	irqreturn_t ret;
+
+	spin_lock(&hw->lock);
+	ret = mISDNipac_irq(&hw->ipac, irqloops);
+	spin_unlock(&hw->lock);
+	return ret;
+}
+
+static irqreturn_t
+ipac_irq(int intno, void *dev_id)
+{
+	struct inf_hw *hw = dev_id;
+	u8 val;
+
+	spin_lock(&hw->lock);
+	val = hw->ipac.read_reg(hw, IPAC_ISTA);
+	if (!(val & 0x3f)) {
+		spin_unlock(&hw->lock);
+		return IRQ_NONE; /* shared */
+	}
+	hw->irqcnt++;
+	mISDNipac_irq(&hw->ipac, irqloops);
+	spin_unlock(&hw->lock);
+	return IRQ_HANDLED;
+}
+
+static void
+enable_hwirq(struct inf_hw *hw)
+{
+	u16 w;
+	u32 val;
+
+	switch (hw->ci->typ) {
+	case INF_DIVA201:
+	case INF_DIVA202:
+		writel(PITA_INT0_ENABLE, hw->cfg.p);
+		break;
+	case INF_SPEEDWIN:
+	case INF_SAPHIR3:
+		outb(TIGER_IRQ_BIT, (u32)hw->cfg.start + TIGER_AUX_IRQMASK);
+		break;
+	case INF_QS1000:
+		outb(QS1000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+		break;
+	case INF_QS3000:
+		outb(QS3000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+		break;
+	case INF_NICCY:
+		val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+		val |= NICCY_IRQ_ENABLE;;
+		outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+		break;
+	case INF_SCT_1:
+		w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+		w |= SCT_PLX_IRQ_ENABLE;
+		outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+		break;
+	case INF_GAZEL_R685:
+		outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN,
+			(u32)hw->cfg.start + GAZEL_INCSR);
+		break;
+	case INF_GAZEL_R753:
+		outb(GAZEL_IPAC_EN + GAZEL_PCI_EN,
+			(u32)hw->cfg.start + GAZEL_INCSR);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+disable_hwirq(struct inf_hw *hw)
+{
+	u16 w;
+	u32 val;
+
+	switch (hw->ci->typ) {
+	case INF_DIVA201:
+	case INF_DIVA202:
+		writel(0, hw->cfg.p);
+		break;
+	case INF_SPEEDWIN:
+	case INF_SAPHIR3:
+		outb(0, (u32)hw->cfg.start + TIGER_AUX_IRQMASK);
+		break;
+	case INF_QS1000:
+		outb(QS1000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+		break;
+	case INF_QS3000:
+		outb(QS3000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+		break;
+	case INF_NICCY:
+		val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+		val &= NICCY_IRQ_DISABLE;
+		outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+		break;
+	case INF_SCT_1:
+		w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+		w &= (~SCT_PLX_IRQ_ENABLE);
+		outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+		break;
+	case INF_GAZEL_R685:
+	case INF_GAZEL_R753:
+		outb(0, (u32)hw->cfg.start + GAZEL_INCSR);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+ipac_chip_reset(struct inf_hw *hw)
+{
+	hw->ipac.write_reg(hw, IPAC_POTA2, 0x20);
+	mdelay(5);
+	hw->ipac.write_reg(hw, IPAC_POTA2, 0x00);
+	mdelay(5);
+	hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf);
+	hw->ipac.write_reg(hw, IPAC_MASK, 0xc0);
+}
+
+static void
+reset_inf(struct inf_hw *hw)
+{
+	u16 w;
+	u32 val;
+
+	if (debug & DEBUG_HW)
+		pr_notice("%s: resetting card\n", hw->name);
+	switch (hw->ci->typ) {
+	case INF_DIVA20:
+	case INF_DIVA20U:
+		outb(0, (u32)hw->cfg.start + DIVA_PCI_CTRL);
+		mdelay(10);
+		outb(DIVA_RESET_BIT, (u32)hw->cfg.start + DIVA_PCI_CTRL);
+		mdelay(10);
+		/* Workaround PCI9060 */
+		outb(9, (u32)hw->cfg.start + 0x69);
+		outb(DIVA_RESET_BIT | DIVA_LED_A,
+			(u32)hw->cfg.start + DIVA_PCI_CTRL);
+		break;
+	case INF_DIVA201:
+		writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE,
+			hw->cfg.p + PITA_MISC_REG);
+		mdelay(1);
+		writel(PITA_PARA_MPX_MODE, hw->cfg.p + PITA_MISC_REG);
+		mdelay(10);
+		break;
+	case INF_DIVA202:
+		writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE,
+			hw->cfg.p + PITA_MISC_REG);
+		mdelay(1);
+		writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET,
+			hw->cfg.p + PITA_MISC_REG);
+		mdelay(10);
+		break;
+	case INF_SPEEDWIN:
+	case INF_SAPHIR3:
+		ipac_chip_reset(hw);
+		hw->ipac.write_reg(hw, IPAC_ACFG, 0xff);
+		hw->ipac.write_reg(hw, IPAC_AOE, 0x00);
+		hw->ipac.write_reg(hw, IPAC_PCFG, 0x12);
+		break;
+	case INF_QS1000:
+	case INF_QS3000:
+		ipac_chip_reset(hw);
+		hw->ipac.write_reg(hw, IPAC_ACFG, 0x00);
+		hw->ipac.write_reg(hw, IPAC_AOE, 0x3c);
+		hw->ipac.write_reg(hw, IPAC_ATX, 0xff);
+		break;
+	case INF_NICCY:
+		break;
+	case INF_SCT_1:
+		w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+		w &= (~SCT_PLX_RESET_BIT);
+		outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+		mdelay(10);
+		w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+		w |= SCT_PLX_RESET_BIT;
+		outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+		mdelay(10);
+		break;
+	case INF_GAZEL_R685:
+		val = inl((u32)hw->cfg.start + GAZEL_CNTRL);
+		val |= (GAZEL_RESET_9050 + GAZEL_RESET);
+		outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+		val &= ~(GAZEL_RESET_9050 + GAZEL_RESET);
+		mdelay(4);
+		outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+		mdelay(10);
+		hw->ipac.isac.adf2 = 0x87;
+		hw->ipac.hscx[0].slot = 0x1f;
+		hw->ipac.hscx[0].slot = 0x23;
+		break;
+	case INF_GAZEL_R753:
+		val = inl((u32)hw->cfg.start + GAZEL_CNTRL);
+		val |= (GAZEL_RESET_9050 + GAZEL_RESET);
+		outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+		val &= ~(GAZEL_RESET_9050 + GAZEL_RESET);
+		mdelay(4);
+		outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+		mdelay(10);
+		ipac_chip_reset(hw);
+		hw->ipac.write_reg(hw, IPAC_ACFG, 0xff);
+		hw->ipac.write_reg(hw, IPAC_AOE, 0x00);
+		hw->ipac.conf = 0x01; /* IOM off */
+		break;
+	default:
+		return;
+	}
+	enable_hwirq(hw);
+}
+
+static int
+inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case HW_RESET_REQ:
+		reset_inf(hw);
+		break;
+	default:
+		pr_info("%s: %s unknown command %x %lx\n",
+			hw->name, __func__, cmd, arg);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int __devinit
+init_irq(struct inf_hw *hw)
+{
+	int	ret, cnt = 3;
+	u_long	flags;
+
+	if (!hw->ci->irqfunc)
+		return -EINVAL;
+	ret = request_irq(hw->irq, hw->ci->irqfunc, IRQF_SHARED, hw->name, hw);
+	if (ret) {
+		pr_info("%s: couldn't get interrupt %d\n", hw->name, hw->irq);
+		return ret;
+	}
+	while (cnt--) {
+		spin_lock_irqsave(&hw->lock, flags);
+		reset_inf(hw);
+		ret = hw->ipac.init(&hw->ipac);
+		if (ret) {
+			spin_unlock_irqrestore(&hw->lock, flags);
+			pr_info("%s: ISAC init failed with %d\n",
+				hw->name, ret);
+			break;
+		}
+		spin_unlock_irqrestore(&hw->lock, flags);
+		msleep_interruptible(10);
+		if (debug & DEBUG_HW)
+			pr_notice("%s: IRQ %d count %d\n", hw->name,
+				hw->irq, hw->irqcnt);
+		if (!hw->irqcnt) {
+			pr_info("%s: IRQ(%d) got no requests during init %d\n",
+				hw->name, hw->irq, 3 - cnt);
+		} else
+			return 0;
+	}
+	free_irq(hw->irq, hw);
+	return -EIO;
+}
+
+static void
+release_io(struct inf_hw *hw)
+{
+	if (hw->cfg.mode) {
+		if (hw->cfg.p) {
+			release_mem_region(hw->cfg.start, hw->cfg.size);
+			iounmap(hw->cfg.p);
+		} else
+			release_region(hw->cfg.start, hw->cfg.size);
+		hw->cfg.mode = AM_NONE;
+	}
+	if (hw->addr.mode) {
+		if (hw->addr.p) {
+			release_mem_region(hw->addr.start, hw->addr.size);
+			iounmap(hw->addr.p);
+		} else
+			release_region(hw->addr.start, hw->addr.size);
+		hw->addr.mode = AM_NONE;
+	}
+}
+
+static int __devinit
+setup_io(struct inf_hw *hw)
+{
+	int err = 0;
+
+	if (hw->ci->cfg_mode) {
+		hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar);
+		hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar);
+		if (hw->ci->cfg_mode == AM_MEMIO) {
+			if (!request_mem_region(hw->cfg.start, hw->cfg.size,
+			    hw->name))
+				err = -EBUSY;
+		} else {
+			if (!request_region(hw->cfg.start, hw->cfg.size,
+			    hw->name))
+				err = -EBUSY;
+		}
+		if (err) {
+			pr_info("mISDN: %s config port %lx (%lu bytes)"
+				"already in use\n", hw->name,
+				(ulong)hw->cfg.start, (ulong)hw->cfg.size);
+			return err;
+		}
+		if (hw->ci->cfg_mode == AM_MEMIO)
+			hw->cfg.p = ioremap(hw->cfg.start, hw->cfg.size);
+		hw->cfg.mode = hw->ci->cfg_mode;
+		if (debug & DEBUG_HW)
+			pr_notice("%s: IO cfg %lx (%lu bytes) mode%d\n",
+				hw->name, (ulong)hw->cfg.start,
+				(ulong)hw->cfg.size, hw->ci->cfg_mode);
+
+	}
+	if (hw->ci->addr_mode) {
+		hw->addr.start = pci_resource_start(hw->pdev, hw->ci->addr_bar);
+		hw->addr.size = pci_resource_len(hw->pdev, hw->ci->addr_bar);
+		if (hw->ci->addr_mode == AM_MEMIO) {
+			if (!request_mem_region(hw->addr.start, hw->addr.size,
+			    hw->name))
+				err = -EBUSY;
+		} else {
+			if (!request_region(hw->addr.start, hw->addr.size,
+			    hw->name))
+				err = -EBUSY;
+		}
+		if (err) {
+			pr_info("mISDN: %s address port %lx (%lu bytes)"
+				"already in use\n", hw->name,
+				(ulong)hw->addr.start, (ulong)hw->addr.size);
+			return err;
+		}
+		if (hw->ci->addr_mode == AM_MEMIO)
+			hw->addr.p = ioremap(hw->addr.start, hw->addr.size);
+		hw->addr.mode = hw->ci->addr_mode;
+		if (debug & DEBUG_HW)
+			pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n",
+				hw->name, (ulong)hw->addr.start,
+				(ulong)hw->addr.size, hw->ci->addr_mode);
+
+	}
+
+	switch (hw->ci->typ) {
+	case INF_DIVA20:
+	case INF_DIVA20U:
+		hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+		hw->isac.mode = hw->cfg.mode;
+		hw->isac.a.io.ale = (u32)hw->cfg.start + DIVA_ISAC_ALE;
+		hw->isac.a.io.port = (u32)hw->cfg.start + DIVA_ISAC_PORT;
+		hw->hscx.mode = hw->cfg.mode;
+		hw->hscx.a.io.ale = (u32)hw->cfg.start + DIVA_HSCX_ALE;
+		hw->hscx.a.io.port = (u32)hw->cfg.start + DIVA_HSCX_PORT;
+		break;
+	case INF_DIVA201:
+		hw->ipac.type = IPAC_TYPE_IPAC;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.mode = hw->addr.mode;
+		hw->isac.a.p = hw->addr.p;
+		hw->hscx.mode = hw->addr.mode;
+		hw->hscx.a.p = hw->addr.p;
+		break;
+	case INF_DIVA202:
+		hw->ipac.type = IPAC_TYPE_IPACX;
+		hw->isac.mode = hw->addr.mode;
+		hw->isac.a.p = hw->addr.p;
+		hw->hscx.mode = hw->addr.mode;
+		hw->hscx.a.p = hw->addr.p;
+		break;
+	case INF_SPEEDWIN:
+	case INF_SAPHIR3:
+		hw->ipac.type = IPAC_TYPE_IPAC;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.mode = hw->cfg.mode;
+		hw->isac.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE;
+		hw->isac.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT;
+		hw->hscx.mode = hw->cfg.mode;
+		hw->hscx.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE;
+		hw->hscx.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT;
+		outb(0xff, (ulong)hw->cfg.start);
+		mdelay(1);
+		outb(0x00, (ulong)hw->cfg.start);
+		mdelay(1);
+		outb(TIGER_IOMASK, (ulong)hw->cfg.start + TIGER_AUX_CTRL);
+		break;
+	case INF_QS1000:
+	case INF_QS3000:
+		hw->ipac.type = IPAC_TYPE_IPAC;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.a.io.ale = (u32)hw->addr.start;
+		hw->isac.a.io.port = (u32)hw->addr.start + 1;
+		hw->isac.mode = hw->addr.mode;
+		hw->hscx.a.io.ale = (u32)hw->addr.start;
+		hw->hscx.a.io.port = (u32)hw->addr.start + 1;
+		hw->hscx.mode = hw->addr.mode;
+		break;
+	case INF_NICCY:
+		hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+		hw->isac.mode = hw->addr.mode;
+		hw->isac.a.io.ale = (u32)hw->addr.start + NICCY_ISAC_ALE;
+		hw->isac.a.io.port = (u32)hw->addr.start + NICCY_ISAC_PORT;
+		hw->hscx.mode = hw->addr.mode;
+		hw->hscx.a.io.ale = (u32)hw->addr.start + NICCY_HSCX_ALE;
+		hw->hscx.a.io.port = (u32)hw->addr.start + NICCY_HSCX_PORT;
+		break;
+	case INF_SCT_1:
+		hw->ipac.type = IPAC_TYPE_IPAC;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.a.io.ale = (u32)hw->addr.start;
+		hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+		hw->isac.mode = hw->addr.mode;
+		hw->hscx.a.io.ale = hw->isac.a.io.ale;
+		hw->hscx.a.io.port = hw->isac.a.io.port;
+		hw->hscx.mode = hw->addr.mode;
+		break;
+	case INF_SCT_2:
+		hw->ipac.type = IPAC_TYPE_IPAC;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.a.io.ale = (u32)hw->addr.start + 0x08;
+		hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+		hw->isac.mode = hw->addr.mode;
+		hw->hscx.a.io.ale = hw->isac.a.io.ale;
+		hw->hscx.a.io.port = hw->isac.a.io.port;
+		hw->hscx.mode = hw->addr.mode;
+		break;
+	case INF_SCT_3:
+		hw->ipac.type = IPAC_TYPE_IPAC;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.a.io.ale = (u32)hw->addr.start + 0x10;
+		hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+		hw->isac.mode = hw->addr.mode;
+		hw->hscx.a.io.ale = hw->isac.a.io.ale;
+		hw->hscx.a.io.port = hw->isac.a.io.port;
+		hw->hscx.mode = hw->addr.mode;
+		break;
+	case INF_SCT_4:
+		hw->ipac.type = IPAC_TYPE_IPAC;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.a.io.ale = (u32)hw->addr.start + 0x20;
+		hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+		hw->isac.mode = hw->addr.mode;
+		hw->hscx.a.io.ale = hw->isac.a.io.ale;
+		hw->hscx.a.io.port = hw->isac.a.io.port;
+		hw->hscx.mode = hw->addr.mode;
+		break;
+	case INF_GAZEL_R685:
+		hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.mode = hw->addr.mode;
+		hw->isac.a.io.port = (u32)hw->addr.start;
+		hw->hscx.mode = hw->addr.mode;
+		hw->hscx.a.io.port = hw->isac.a.io.port;
+		break;
+	case INF_GAZEL_R753:
+		hw->ipac.type = IPAC_TYPE_IPAC;
+		hw->ipac.isac.off = 0x80;
+		hw->isac.mode = hw->addr.mode;
+		hw->isac.a.io.ale = (u32)hw->addr.start;
+		hw->isac.a.io.port = (u32)hw->addr.start + GAZEL_IPAC_DATA_PORT;
+		hw->hscx.mode = hw->addr.mode;
+		hw->hscx.a.io.ale = hw->isac.a.io.ale;
+		hw->hscx.a.io.port = hw->isac.a.io.port;
+		break;
+	default:
+		return -EINVAL;
+	}
+	switch (hw->isac.mode) {
+	case AM_MEMIO:
+		ASSIGN_FUNC_IPAC(MIO, hw->ipac);
+		break;
+	case AM_IND_IO:
+		ASSIGN_FUNC_IPAC(IND, hw->ipac);
+		break;
+	case AM_IO:
+		ASSIGN_FUNC_IPAC(IO, hw->ipac);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void
+release_card(struct inf_hw *card) {
+	ulong	flags;
+	int	i;
+
+	spin_lock_irqsave(&card->lock, flags);
+	disable_hwirq(card);
+	spin_unlock_irqrestore(&card->lock, flags);
+	card->ipac.isac.release(&card->ipac.isac);
+	free_irq(card->irq, card);
+	mISDN_unregister_device(&card->ipac.isac.dch.dev);
+	release_io(card);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	switch (card->ci->typ) {
+	case INF_SCT_2:
+	case INF_SCT_3:
+	case INF_SCT_4:
+		break;
+	case INF_SCT_1:
+		for (i = 0; i < 3; i++) {
+			if (card->sc[i])
+				release_card(card->sc[i]);
+			card->sc[i] = NULL;
+		}
+	default:
+		pci_disable_device(card->pdev);
+		pci_set_drvdata(card->pdev, NULL);
+		break;
+	}
+	kfree(card);
+	inf_cnt--;
+}
+
+static int __devinit
+setup_instance(struct inf_hw *card)
+{
+	int err;
+	ulong flags;
+
+	snprintf(card->name, MISDN_MAX_IDLEN - 1, "%s.%d", card->ci->name,
+		inf_cnt + 1);
+	write_lock_irqsave(&card_lock, flags);
+	list_add_tail(&card->list, &Cards);
+	write_unlock_irqrestore(&card_lock, flags);
+
+	_set_debug(card);
+	card->ipac.isac.name = card->name;
+	card->ipac.name = card->name;
+	card->ipac.owner = THIS_MODULE;
+	spin_lock_init(&card->lock);
+	card->ipac.isac.hwlock = &card->lock;
+	card->ipac.hwlock = &card->lock;
+	card->ipac.ctrl = (void *)&inf_ctrl;
+
+	err = setup_io(card);
+	if (err)
+		goto error_setup;
+
+	card->ipac.isac.dch.dev.Bprotocols =
+		mISDNipac_init(&card->ipac, card);
+
+	if (card->ipac.isac.dch.dev.Bprotocols == 0)
+		goto error_setup;;
+
+	err = mISDN_register_device(&card->ipac.isac.dch.dev,
+		&card->pdev->dev, card->name);
+	if (err)
+		goto error;
+
+	err = init_irq(card);
+	if (!err)  {
+		inf_cnt++;
+		pr_notice("Infineon %d cards installed\n", inf_cnt);
+		return 0;
+	}
+	mISDN_unregister_device(&card->ipac.isac.dch.dev);
+error:
+	card->ipac.release(&card->ipac);
+error_setup:
+	release_io(card);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	return err;
+}
+
+static const struct inf_cinfo inf_card_info[] = {
+	{
+		INF_DIVA20,
+		"Dialogic Diva 2.0",
+		"diva20",
+		AM_IND_IO, AM_NONE, 2, 0,
+		&diva_irq
+	},
+	{
+		INF_DIVA20U,
+		"Dialogic Diva 2.0U",
+		"diva20U",
+		AM_IND_IO, AM_NONE, 2, 0,
+		&diva_irq
+	},
+	{
+		INF_DIVA201,
+		"Dialogic Diva 2.01",
+		"diva201",
+		AM_MEMIO, AM_MEMIO, 0, 1,
+		&diva20x_irq
+	},
+	{
+		INF_DIVA202,
+		"Dialogic Diva 2.02",
+		"diva202",
+		AM_MEMIO, AM_MEMIO, 0, 1,
+		&diva20x_irq
+	},
+	{
+		INF_SPEEDWIN,
+		"Sedlbauer SpeedWin PCI",
+		"speedwin",
+		AM_IND_IO, AM_NONE, 0, 0,
+		&tiger_irq
+	},
+	{
+		INF_SAPHIR3,
+		"HST Saphir 3",
+		"saphir",
+		AM_IND_IO, AM_NONE, 0, 0,
+		&tiger_irq
+	},
+	{
+		INF_QS1000,
+		"Develo Microlink PCI",
+		"qs1000",
+		AM_IO, AM_IND_IO, 1, 3,
+		&elsa_irq
+	},
+	{
+		INF_QS3000,
+		"Develo QuickStep 3000",
+		"qs3000",
+		AM_IO, AM_IND_IO, 1, 3,
+		&elsa_irq
+	},
+	{
+		INF_NICCY,
+		"Sagem NICCY",
+		"niccy",
+		AM_IO, AM_IND_IO, 0, 1,
+		&niccy_irq
+	},
+	{
+		INF_SCT_1,
+		"SciTel Quadro",
+		"p1_scitel",
+		AM_IO, AM_IND_IO, 1, 5,
+		&ipac_irq
+	},
+	{
+		INF_SCT_2,
+		"SciTel Quadro",
+		"p2_scitel",
+		AM_NONE, AM_IND_IO, 0, 4,
+		&ipac_irq
+	},
+	{
+		INF_SCT_3,
+		"SciTel Quadro",
+		"p3_scitel",
+		AM_NONE, AM_IND_IO, 0, 3,
+		&ipac_irq
+	},
+	{
+		INF_SCT_4,
+		"SciTel Quadro",
+		"p4_scitel",
+		AM_NONE, AM_IND_IO, 0, 2,
+		&ipac_irq
+	},
+	{
+		INF_GAZEL_R685,
+		"Gazel R685",
+		"gazel685",
+		AM_IO, AM_IO, 1, 2,
+		&gazel_irq
+	},
+	{
+		INF_GAZEL_R753,
+		"Gazel R753",
+		"gazel753",
+		AM_IO, AM_IND_IO, 1, 2,
+		&ipac_irq
+	},
+	{
+		INF_NONE,
+	}
+};
+
+static const struct inf_cinfo * __devinit
+get_card_info(enum inf_types typ)
+{
+	const struct inf_cinfo *ci = inf_card_info;
+
+	while (ci->typ != INF_NONE) {
+		if (ci->typ == typ)
+			return ci;
+		ci++;
+	}
+	return NULL;
+}
+
+static int __devinit
+inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int err = -ENOMEM;
+	struct inf_hw *card;
+
+	card = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
+	if (!card) {
+		pr_info("No memory for Infineon ISDN card\n");
+		return err;
+	}
+	card->pdev = pdev;
+	err = pci_enable_device(pdev);
+	if (err) {
+		kfree(card);
+		return err;
+	}
+	card->ci = get_card_info(ent->driver_data);
+	if (!card->ci) {
+		pr_info("mISDN: do not have informations about adapter at %s\n",
+			pci_name(pdev));
+		kfree(card);
+		return -EINVAL;
+	} else
+		pr_notice("mISDN: found adapter %s at %s\n",
+			card->ci->full, pci_name(pdev));
+
+	card->irq = pdev->irq;
+	pci_set_drvdata(pdev, card);
+	err = setup_instance(card);
+	if (err) {
+		pci_disable_device(card->pdev);
+		kfree(card);
+		pci_set_drvdata(pdev, NULL);
+	} else if (ent->driver_data == INF_SCT_1) {
+		int i;
+		struct inf_hw *sc;
+
+		for (i = 1; i < 4; i++) {
+			sc = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
+			if (!sc) {
+				release_card(card);
+				return -ENOMEM;
+			}
+			sc->irq = card->irq;
+			sc->pdev = card->pdev;
+			sc->ci = card->ci + i;
+			err = setup_instance(sc);
+			if (err) {
+				kfree(sc);
+				release_card(card);
+			} else
+				card->sc[i - 1] = sc;
+		}
+	}
+	return err;
+}
+
+static void __devexit
+inf_remove(struct pci_dev *pdev)
+{
+	struct inf_hw	*card = pci_get_drvdata(pdev);
+
+	if (card)
+		release_card(card);
+	else
+		pr_debug("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_driver infineon_driver = {
+	.name = "ISDN Infineon pci",
+	.probe = inf_probe,
+	.remove = __devexit_p(inf_remove),
+	.id_table = infineon_ids,
+};
+
+static int __init
+infineon_init(void)
+{
+	int err;
+
+	pr_notice("Infineon ISDN Driver Rev. %s\n", INFINEON_REV);
+	err = pci_register_driver(&infineon_driver);
+	return err;
+}
+
+static void __exit
+infineon_cleanup(void)
+{
+	pci_unregister_driver(&infineon_driver);
+}
+
+module_init(infineon_init);
+module_exit(infineon_cleanup);

+ 1655 - 0
drivers/isdn/hardware/mISDN/mISDNipac.c

@@ -0,0 +1,1655 @@
+/*
+ * isac.c   ISAC specific routines
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+#include "ipac.h"
+
+
+#define DBUSY_TIMER_VALUE	80
+#define ARCOFI_USE		1
+
+#define ISAC_REV		"2.0"
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_VERSION(ISAC_REV);
+MODULE_LICENSE("GPL v2");
+
+#define ReadISAC(is, o)		(is->read_reg(is->dch.hw, o + is->off))
+#define	WriteISAC(is, o, v)	(is->write_reg(is->dch.hw, o + is->off, v))
+#define ReadHSCX(h, o)		(h->ip->read_reg(h->ip->hw, h->off + o))
+#define WriteHSCX(h, o, v)	(h->ip->write_reg(h->ip->hw, h->off + o, v))
+#define ReadIPAC(ip, o)		(ip->read_reg(ip->hw, o))
+#define WriteIPAC(ip, o, v)	(ip->write_reg(ip->hw, o, v))
+
+static inline void
+ph_command(struct isac_hw *isac, u8 command)
+{
+	pr_debug("%s: ph_command %x\n", isac->name, command);
+	if (isac->type & IPAC_TYPE_ISACX)
+		WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE);
+	else
+		WriteISAC(isac, ISAC_CIX0, (command << 2) | 3);
+}
+
+static void
+isac_ph_state_change(struct isac_hw *isac)
+{
+	switch (isac->state) {
+	case (ISAC_IND_RS):
+	case (ISAC_IND_EI):
+		ph_command(isac, ISAC_CMD_DUI);
+	}
+	schedule_event(&isac->dch, FLG_PHCHANGE);
+}
+
+static void
+isac_ph_state_bh(struct dchannel *dch)
+{
+	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+
+	switch (isac->state) {
+	case ISAC_IND_RS:
+	case ISAC_IND_EI:
+		dch->state = 0;
+		l1_event(dch->l1, HW_RESET_IND);
+		break;
+	case ISAC_IND_DID:
+		dch->state = 3;
+		l1_event(dch->l1, HW_DEACT_CNF);
+		break;
+	case ISAC_IND_DR:
+		dch->state = 3;
+		l1_event(dch->l1, HW_DEACT_IND);
+		break;
+	case ISAC_IND_PU:
+		dch->state = 4;
+		l1_event(dch->l1, HW_POWERUP_IND);
+		break;
+	case ISAC_IND_RSY:
+		if (dch->state <= 5) {
+			dch->state = 5;
+			l1_event(dch->l1, ANYSIGNAL);
+		} else {
+			dch->state = 8;
+			l1_event(dch->l1, LOSTFRAMING);
+		}
+		break;
+	case ISAC_IND_ARD:
+		dch->state = 6;
+		l1_event(dch->l1, INFO2);
+		break;
+	case ISAC_IND_AI8:
+		dch->state = 7;
+		l1_event(dch->l1, INFO4_P8);
+		break;
+	case ISAC_IND_AI10:
+		dch->state = 7;
+		l1_event(dch->l1, INFO4_P10);
+		break;
+	}
+	pr_debug("%s: TE newstate %x\n", isac->name, dch->state);
+}
+
+void
+isac_empty_fifo(struct isac_hw *isac, int count)
+{
+	u8 *ptr;
+
+	pr_debug("%s: %s  %d\n", isac->name, __func__, count);
+
+	if (!isac->dch.rx_skb) {
+		isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC);
+		if (!isac->dch.rx_skb) {
+			pr_info("%s: D receive out of memory\n", isac->name);
+			WriteISAC(isac, ISAC_CMDR, 0x80);
+			return;
+		}
+	}
+	if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) {
+		pr_debug("%s: %s overrun %d\n", isac->name, __func__,
+			    isac->dch.rx_skb->len + count);
+		WriteISAC(isac, ISAC_CMDR, 0x80);
+		return;
+	}
+	ptr = skb_put(isac->dch.rx_skb, count);
+	isac->read_fifo(isac->dch.hw, isac->off, ptr, count);
+	WriteISAC(isac, ISAC_CMDR, 0x80);
+	if (isac->dch.debug & DEBUG_HW_DFIFO) {
+		char	pfx[MISDN_MAX_IDLEN + 16];
+
+		snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ",
+			isac->name, count);
+		print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
+	}
+}
+
+static void
+isac_fill_fifo(struct isac_hw *isac)
+{
+	int count, more;
+	u8 *ptr;
+
+	if (!isac->dch.tx_skb)
+		return;
+	count = isac->dch.tx_skb->len - isac->dch.tx_idx;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	pr_debug("%s: %s  %d\n", isac->name, __func__, count);
+	ptr = isac->dch.tx_skb->data + isac->dch.tx_idx;
+	isac->dch.tx_idx += count;
+	isac->write_fifo(isac->dch.hw, isac->off, ptr, count);
+	WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa);
+	if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
+		pr_debug("%s: %s dbusytimer running\n", isac->name, __func__);
+		del_timer(&isac->dch.timer);
+	}
+	init_timer(&isac->dch.timer);
+	isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&isac->dch.timer);
+	if (isac->dch.debug & DEBUG_HW_DFIFO) {
+		char	pfx[MISDN_MAX_IDLEN + 16];
+
+		snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ",
+			isac->name, count);
+		print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
+	}
+}
+
+static void
+isac_rme_irq(struct isac_hw *isac)
+{
+	u8 val, count;
+
+	val = ReadISAC(isac, ISAC_RSTA);
+	if ((val & 0x70) != 0x20) {
+		if (val & 0x40) {
+			pr_debug("%s: ISAC RDO\n", isac->name);
+#ifdef ERROR_STATISTIC
+			isac->dch.err_rx++;
+#endif
+		}
+		if (!(val & 0x20)) {
+			pr_debug("%s: ISAC CRC error\n", isac->name);
+#ifdef ERROR_STATISTIC
+			isac->dch.err_crc++;
+#endif
+		}
+		WriteISAC(isac, ISAC_CMDR, 0x80);
+		if (isac->dch.rx_skb)
+			dev_kfree_skb(isac->dch.rx_skb);
+		isac->dch.rx_skb = NULL;
+	} else {
+		count = ReadISAC(isac, ISAC_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		isac_empty_fifo(isac, count);
+		recv_Dchannel(&isac->dch);
+	}
+}
+
+static void
+isac_xpr_irq(struct isac_hw *isac)
+{
+	if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
+		del_timer(&isac->dch.timer);
+	if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) {
+		isac_fill_fifo(isac);
+	} else {
+		if (isac->dch.tx_skb)
+			dev_kfree_skb(isac->dch.tx_skb);
+		if (get_next_dframe(&isac->dch))
+			isac_fill_fifo(isac);
+	}
+}
+
+static void
+isac_retransmit(struct isac_hw *isac)
+{
+	if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
+		del_timer(&isac->dch.timer);
+	if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) {
+		/* Restart frame */
+		isac->dch.tx_idx = 0;
+		isac_fill_fifo(isac);
+	} else if (isac->dch.tx_skb) { /* should not happen */
+		pr_info("%s: tx_skb exist but not busy\n", isac->name);
+		test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags);
+		isac->dch.tx_idx = 0;
+		isac_fill_fifo(isac);
+	} else {
+		pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name);
+		if (get_next_dframe(&isac->dch))
+			isac_fill_fifo(isac);
+	}
+}
+
+static void
+isac_mos_irq(struct isac_hw *isac)
+{
+	u8 val;
+	int ret;
+
+	val = ReadISAC(isac, ISAC_MOSR);
+	pr_debug("%s: ISAC MOSR %02x\n", isac->name, val);
+#if ARCOFI_USE
+	if (val & 0x08) {
+		if (!isac->mon_rx) {
+			isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
+			if (!isac->mon_rx) {
+				pr_info("%s: ISAC MON RX out of memory!\n",
+					isac->name);
+				isac->mocr &= 0xf0;
+				isac->mocr |= 0x0a;
+				WriteISAC(isac, ISAC_MOCR, isac->mocr);
+				goto afterMONR0;
+			} else
+				isac->mon_rxp = 0;
+		}
+		if (isac->mon_rxp >= MAX_MON_FRAME) {
+			isac->mocr &= 0xf0;
+			isac->mocr |= 0x0a;
+			WriteISAC(isac, ISAC_MOCR, isac->mocr);
+			isac->mon_rxp = 0;
+			pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
+			goto afterMONR0;
+		}
+		isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0);
+		pr_debug("%s: ISAC MOR0 %02x\n", isac->name,
+			isac->mon_rx[isac->mon_rxp - 1]);
+		if (isac->mon_rxp == 1) {
+			isac->mocr |= 0x04;
+			WriteISAC(isac, ISAC_MOCR, isac->mocr);
+		}
+	}
+afterMONR0:
+	if (val & 0x80) {
+		if (!isac->mon_rx) {
+			isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
+			if (!isac->mon_rx) {
+				pr_info("%s: ISAC MON RX out of memory!\n",
+					isac->name);
+				isac->mocr &= 0x0f;
+				isac->mocr |= 0xa0;
+				WriteISAC(isac, ISAC_MOCR, isac->mocr);
+				goto afterMONR1;
+			} else
+				isac->mon_rxp = 0;
+		}
+		if (isac->mon_rxp >= MAX_MON_FRAME) {
+			isac->mocr &= 0x0f;
+			isac->mocr |= 0xa0;
+			WriteISAC(isac, ISAC_MOCR, isac->mocr);
+			isac->mon_rxp = 0;
+			pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
+			goto afterMONR1;
+		}
+		isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1);
+		pr_debug("%s: ISAC MOR1 %02x\n", isac->name,
+			isac->mon_rx[isac->mon_rxp - 1]);
+		isac->mocr |= 0x40;
+		WriteISAC(isac, ISAC_MOCR, isac->mocr);
+	}
+afterMONR1:
+	if (val & 0x04) {
+		isac->mocr &= 0xf0;
+		WriteISAC(isac, ISAC_MOCR, isac->mocr);
+		isac->mocr |= 0x0a;
+		WriteISAC(isac, ISAC_MOCR, isac->mocr);
+		if (isac->monitor) {
+			ret = isac->monitor(isac->dch.hw, MONITOR_RX_0,
+				isac->mon_rx, isac->mon_rxp);
+			if (ret)
+				kfree(isac->mon_rx);
+		} else {
+			pr_info("%s: MONITOR 0 received %d but no user\n",
+				isac->name, isac->mon_rxp);
+			kfree(isac->mon_rx);
+		}
+		isac->mon_rx = NULL;
+		isac->mon_rxp = 0;
+	}
+	if (val & 0x40) {
+		isac->mocr &= 0x0f;
+		WriteISAC(isac, ISAC_MOCR, isac->mocr);
+		isac->mocr |= 0xa0;
+		WriteISAC(isac, ISAC_MOCR, isac->mocr);
+		if (isac->monitor) {
+			ret = isac->monitor(isac->dch.hw, MONITOR_RX_1,
+				isac->mon_rx, isac->mon_rxp);
+			if (ret)
+				kfree(isac->mon_rx);
+		} else {
+			pr_info("%s: MONITOR 1 received %d but no user\n",
+				isac->name, isac->mon_rxp);
+			kfree(isac->mon_rx);
+		}
+		isac->mon_rx = NULL;
+		isac->mon_rxp = 0;
+	}
+	if (val & 0x02) {
+		if ((!isac->mon_tx) || (isac->mon_txc &&
+			(isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) {
+			isac->mocr &= 0xf0;
+			WriteISAC(isac, ISAC_MOCR, isac->mocr);
+			isac->mocr |= 0x0a;
+			WriteISAC(isac, ISAC_MOCR, isac->mocr);
+			if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+				if (isac->monitor)
+					ret = isac->monitor(isac->dch.hw,
+						MONITOR_TX_0, NULL, 0);
+			}
+			kfree(isac->mon_tx);
+			isac->mon_tx = NULL;
+			isac->mon_txc = 0;
+			isac->mon_txp = 0;
+			goto AfterMOX0;
+		}
+		if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+			if (isac->monitor)
+				ret = isac->monitor(isac->dch.hw,
+					MONITOR_TX_0, NULL, 0);
+			kfree(isac->mon_tx);
+			isac->mon_tx = NULL;
+			isac->mon_txc = 0;
+			isac->mon_txp = 0;
+			goto AfterMOX0;
+		}
+		WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]);
+		pr_debug("%s: ISAC %02x -> MOX0\n", isac->name,
+			isac->mon_tx[isac->mon_txp - 1]);
+	}
+AfterMOX0:
+	if (val & 0x20) {
+		if ((!isac->mon_tx) || (isac->mon_txc &&
+			(isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) {
+			isac->mocr &= 0x0f;
+			WriteISAC(isac, ISAC_MOCR, isac->mocr);
+			isac->mocr |= 0xa0;
+			WriteISAC(isac, ISAC_MOCR, isac->mocr);
+			if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+				if (isac->monitor)
+					ret = isac->monitor(isac->dch.hw,
+						MONITOR_TX_1, NULL, 0);
+			}
+			kfree(isac->mon_tx);
+			isac->mon_tx = NULL;
+			isac->mon_txc = 0;
+			isac->mon_txp = 0;
+			goto AfterMOX1;
+		}
+		if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+			if (isac->monitor)
+				ret = isac->monitor(isac->dch.hw,
+					MONITOR_TX_1, NULL, 0);
+			kfree(isac->mon_tx);
+			isac->mon_tx = NULL;
+			isac->mon_txc = 0;
+			isac->mon_txp = 0;
+			goto AfterMOX1;
+		}
+		WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);
+		pr_debug("%s: ISAC %02x -> MOX1\n", isac->name,
+			isac->mon_tx[isac->mon_txp - 1]);
+	}
+AfterMOX1:
+	val = 0; /* dummy to avoid warning */
+#endif
+}
+
+static void
+isac_cisq_irq(struct isac_hw *isac) {
+	u8 val;
+
+	val = ReadISAC(isac, ISAC_CIR0);
+	pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val);
+	if (val & 2) {
+		pr_debug("%s: ph_state change %x->%x\n", isac->name,
+			isac->state, (val >> 2) & 0xf);
+		isac->state = (val >> 2) & 0xf;
+		isac_ph_state_change(isac);
+	}
+	if (val & 1) {
+		val = ReadISAC(isac, ISAC_CIR1);
+		pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val);
+	}
+}
+
+static void
+isacsx_cic_irq(struct isac_hw *isac)
+{
+	u8 val;
+
+	val = ReadISAC(isac, ISACX_CIR0);
+	pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
+	if (val & ISACX_CIR0_CIC0) {
+		pr_debug("%s: ph_state change %x->%x\n", isac->name,
+			isac->state, val >> 4);
+		isac->state = val >> 4;
+		isac_ph_state_change(isac);
+	}
+}
+
+static void
+isacsx_rme_irq(struct isac_hw *isac)
+{
+	int count;
+	u8 val;
+
+	val = ReadISAC(isac, ISACX_RSTAD);
+	if ((val & (ISACX_RSTAD_VFR |
+		    ISACX_RSTAD_RDO |
+		    ISACX_RSTAD_CRC |
+		    ISACX_RSTAD_RAB))
+	    != (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) {
+		pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val);
+#ifdef ERROR_STATISTIC
+		if (val & ISACX_RSTAD_CRC)
+			isac->dch.err_rx++;
+		else
+			isac->dch.err_crc++;
+#endif
+		WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
+		if (isac->dch.rx_skb)
+			dev_kfree_skb(isac->dch.rx_skb);
+		isac->dch.rx_skb = NULL;
+	} else {
+		count = ReadISAC(isac, ISACX_RBCLD) & 0x1f;
+		if (count == 0)
+			count = 32;
+		isac_empty_fifo(isac, count);
+		if (isac->dch.rx_skb) {
+			skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1);
+			pr_debug("%s: dchannel received %d\n", isac->name,
+				isac->dch.rx_skb->len);
+			recv_Dchannel(&isac->dch);
+		}
+	}
+}
+
+irqreturn_t
+mISDNisac_irq(struct isac_hw *isac, u8 val)
+{
+	if (unlikely(!val))
+		return IRQ_NONE;
+	pr_debug("%s: ISAC interrupt %02x\n", isac->name, val);
+	if (isac->type & IPAC_TYPE_ISACX) {
+		if (val & ISACX__CIC)
+			isacsx_cic_irq(isac);
+		if (val & ISACX__ICD) {
+			val = ReadISAC(isac, ISACX_ISTAD);
+			pr_debug("%s: ISTAD %02x\n", isac->name, val);
+			if (val & ISACX_D_XDU) {
+				pr_debug("%s: ISAC XDU\n", isac->name);
+#ifdef ERROR_STATISTIC
+				isac->dch.err_tx++;
+#endif
+				isac_retransmit(isac);
+			}
+			if (val & ISACX_D_XMR) {
+				pr_debug("%s: ISAC XMR\n", isac->name);
+#ifdef ERROR_STATISTIC
+				isac->dch.err_tx++;
+#endif
+				isac_retransmit(isac);
+			}
+			if (val & ISACX_D_XPR)
+				isac_xpr_irq(isac);
+			if (val & ISACX_D_RFO) {
+				pr_debug("%s: ISAC RFO\n", isac->name);
+				WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
+			}
+			if (val & ISACX_D_RME)
+				isacsx_rme_irq(isac);
+			if (val & ISACX_D_RPF)
+				isac_empty_fifo(isac, 0x20);
+		}
+	} else {
+		if (val & 0x80)	/* RME */
+			isac_rme_irq(isac);
+		if (val & 0x40)	/* RPF */
+			isac_empty_fifo(isac, 32);
+		if (val & 0x10)	/* XPR */
+			isac_xpr_irq(isac);
+		if (val & 0x04)	/* CISQ */
+			isac_cisq_irq(isac);
+		if (val & 0x20)	/* RSC - never */
+			pr_debug("%s: ISAC RSC interrupt\n", isac->name);
+		if (val & 0x02)	/* SIN - never */
+			pr_debug("%s: ISAC SIN interrupt\n", isac->name);
+		if (val & 0x01) {	/* EXI */
+			val = ReadISAC(isac, ISAC_EXIR);
+			pr_debug("%s: ISAC EXIR %02x\n", isac->name, val);
+			if (val & 0x80)	/* XMR */
+				pr_debug("%s: ISAC XMR\n", isac->name);
+			if (val & 0x40) { /* XDU */
+				pr_debug("%s: ISAC XDU\n", isac->name);
+#ifdef ERROR_STATISTIC
+				isac->dch.err_tx++;
+#endif
+				isac_retransmit(isac);
+			}
+			if (val & 0x04)	/* MOS */
+				isac_mos_irq(isac);
+		}
+	}
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(mISDNisac_irq);
+
+static int
+isac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct isac_hw		*isac = container_of(dch, struct isac_hw, dch);
+	int			ret = -EINVAL;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	u32			id;
+	u_long			flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(isac->hwlock, flags);
+		ret = dchannel_senddata(dch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			isac_fill_fifo(isac);
+			ret = 0;
+			spin_unlock_irqrestore(isac->hwlock, flags);
+			queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(isac->hwlock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		ret = l1_event(dch->l1, hh->prim);
+		break;
+	case PH_DEACTIVATE_REQ:
+		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+		ret = l1_event(dch->l1, hh->prim);
+		break;
+	}
+
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+isac_ctrl(struct isac_hw *isac, u32 cmd, u_long para)
+{
+	u8 tl = 0;
+	u_long flags;
+
+	switch (cmd) {
+	case HW_TESTLOOP:
+		spin_lock_irqsave(isac->hwlock, flags);
+		if (!(isac->type & IPAC_TYPE_ISACX)) {
+			/* TODO: implement for IPAC_TYPE_ISACX */
+			if (para & 1) /* B1 */
+				tl |= 0x0c;
+			else if (para & 2) /* B2 */
+				tl |= 0x3;
+			/* we only support IOM2 mode */
+			WriteISAC(isac, ISAC_SPCR, tl);
+			if (tl)
+				WriteISAC(isac, ISAC_ADF1, 0x8);
+			else
+				WriteISAC(isac, ISAC_ADF1, 0x0);
+		}
+		spin_unlock_irqrestore(isac->hwlock, flags);
+		break;
+	default:
+		pr_debug("%s: %s unknown command %x %lx\n", isac->name,
+			__func__, cmd, para);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+isac_l1cmd(struct dchannel *dch, u32 cmd)
+{
+	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+	u_long flags;
+
+	pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state);
+	switch (cmd) {
+	case INFO3_P8:
+		spin_lock_irqsave(isac->hwlock, flags);
+		ph_command(isac, ISAC_CMD_AR8);
+		spin_unlock_irqrestore(isac->hwlock, flags);
+		break;
+	case INFO3_P10:
+		spin_lock_irqsave(isac->hwlock, flags);
+		ph_command(isac, ISAC_CMD_AR10);
+		spin_unlock_irqrestore(isac->hwlock, flags);
+		break;
+	case HW_RESET_REQ:
+		spin_lock_irqsave(isac->hwlock, flags);
+		if ((isac->state == ISAC_IND_EI) ||
+		    (isac->state == ISAC_IND_DR) ||
+		    (isac->state == ISAC_IND_RS))
+			ph_command(isac, ISAC_CMD_TIM);
+		else
+			ph_command(isac, ISAC_CMD_RS);
+		spin_unlock_irqrestore(isac->hwlock, flags);
+		break;
+	case HW_DEACT_REQ:
+		skb_queue_purge(&dch->squeue);
+		if (dch->tx_skb) {
+			dev_kfree_skb(dch->tx_skb);
+			dch->tx_skb = NULL;
+		}
+		dch->tx_idx = 0;
+		if (dch->rx_skb) {
+			dev_kfree_skb(dch->rx_skb);
+			dch->rx_skb = NULL;
+		}
+		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+		if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+			del_timer(&dch->timer);
+		break;
+	case HW_POWERUP_REQ:
+		spin_lock_irqsave(isac->hwlock, flags);
+		ph_command(isac, ISAC_CMD_TIM);
+		spin_unlock_irqrestore(isac->hwlock, flags);
+		break;
+	case PH_ACTIVATE_IND:
+		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	case PH_DEACTIVATE_IND:
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	default:
+		pr_debug("%s: %s unknown command %x\n", isac->name,
+			__func__, cmd);
+		return -1;
+	}
+	return 0;
+}
+
+static void
+isac_release(struct isac_hw *isac)
+{
+	if (isac->type & IPAC_TYPE_ISACX)
+		WriteISAC(isac, ISACX_MASK, 0xff);
+	else
+		WriteISAC(isac, ISAC_MASK, 0xff);
+	if (isac->dch.timer.function != NULL) {
+		del_timer(&isac->dch.timer);
+		isac->dch.timer.function = NULL;
+	}
+	kfree(isac->mon_rx);
+	isac->mon_rx = NULL;
+	kfree(isac->mon_tx);
+	isac->mon_tx = NULL;
+	if (isac->dch.l1)
+		l1_event(isac->dch.l1, CLOSE_CHANNEL);
+	mISDN_freedchannel(&isac->dch);
+}
+
+static void
+dbusy_timer_handler(struct isac_hw *isac)
+{
+	int rbch, star;
+	u_long flags;
+
+	if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
+		spin_lock_irqsave(isac->hwlock, flags);
+		rbch = ReadISAC(isac, ISAC_RBCH);
+		star = ReadISAC(isac, ISAC_STAR);
+		pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
+			isac->name, rbch, star);
+		if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */
+			test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags);
+		else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags);
+			if (isac->dch.tx_idx)
+				isac->dch.tx_idx = 0;
+			else
+				pr_info("%s: ISAC D-Channel Busy no tx_idx\n",
+					isac->name);
+			/* Transmitter reset */
+			WriteISAC(isac, ISAC_CMDR, 0x01);
+		}
+		spin_unlock_irqrestore(isac->hwlock, flags);
+	}
+}
+
+static int
+open_dchannel(struct isac_hw *isac, struct channel_req *rq)
+{
+	pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__,
+		isac->dch.dev.id, __builtin_return_address(1));
+	if (rq->protocol != ISDN_P_TE_S0)
+		return -EINVAL;
+	if (rq->adr.channel == 1)
+		/* E-Channel not supported */
+		return -EINVAL;
+	rq->ch = &isac->dch.dev.D;
+	rq->ch->protocol = rq->protocol;
+	if (isac->dch.state == 7)
+		_queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+		    0, NULL, GFP_KERNEL);
+	return 0;
+}
+
+static const char *ISACVer[] =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+static int
+isac_init(struct isac_hw *isac)
+{
+	u8 val;
+	int err = 0;
+
+	if (!isac->dch.l1) {
+		err = create_l1(&isac->dch, isac_l1cmd);
+		if (err)
+			return err;
+	}
+	isac->mon_tx = NULL;
+	isac->mon_rx = NULL;
+	isac->dch.timer.function = (void *) dbusy_timer_handler;
+	isac->dch.timer.data = (long)isac;
+	init_timer(&isac->dch.timer);
+	isac->mocr = 0xaa;
+	if (isac->type & IPAC_TYPE_ISACX) {
+		/* Disable all IRQ */
+		WriteISAC(isac, ISACX_MASK, 0xff);
+		val = ReadISAC(isac, ISACX_STARD);
+		pr_debug("%s: ISACX STARD %x\n", isac->name, val);
+		val = ReadISAC(isac, ISACX_ISTAD);
+		pr_debug("%s: ISACX ISTAD %x\n", isac->name, val);
+		val = ReadISAC(isac, ISACX_ISTA);
+		pr_debug("%s: ISACX ISTA %x\n", isac->name, val);
+		/* clear LDD */
+		WriteISAC(isac, ISACX_TR_CONF0, 0x00);
+		/* enable transmitter */
+		WriteISAC(isac, ISACX_TR_CONF2, 0x00);
+		/* transparent mode 0, RAC, stop/go */
+		WriteISAC(isac, ISACX_MODED, 0xc9);
+		/* all HDLC IRQ unmasked */
+		val = ReadISAC(isac, ISACX_ID);
+		if (isac->dch.debug & DEBUG_HW)
+			pr_notice("%s: ISACX Design ID %x\n",
+				isac->name, val & 0x3f);
+		val = ReadISAC(isac, ISACX_CIR0);
+		pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
+		isac->state = val >> 4;
+		isac_ph_state_change(isac);
+		ph_command(isac, ISAC_CMD_RS);
+		WriteISAC(isac, ISACX_MASK, IPACX__ON);
+		WriteISAC(isac, ISACX_MASKD, 0x00);
+	} else { /* old isac */
+		WriteISAC(isac, ISAC_MASK, 0xff);
+		val = ReadISAC(isac, ISAC_STAR);
+		pr_debug("%s: ISAC STAR %x\n", isac->name, val);
+		val = ReadISAC(isac, ISAC_MODE);
+		pr_debug("%s: ISAC MODE %x\n", isac->name, val);
+		val = ReadISAC(isac, ISAC_ADF2);
+		pr_debug("%s: ISAC ADF2 %x\n", isac->name, val);
+		val = ReadISAC(isac, ISAC_ISTA);
+		pr_debug("%s: ISAC ISTA %x\n", isac->name, val);
+		if (val & 0x01) {
+			val = ReadISAC(isac, ISAC_EXIR);
+			pr_debug("%s: ISAC EXIR %x\n", isac->name, val);
+		}
+		val = ReadISAC(isac, ISAC_RBCH);
+		if (isac->dch.debug & DEBUG_HW)
+			pr_notice("%s: ISAC version (%x): %s\n", isac->name,
+				val, ISACVer[(val >> 5) & 3]);
+		isac->type |= ((val >> 5) & 3);
+		if (!isac->adf2)
+			isac->adf2 = 0x80;
+		if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */
+			pr_info("%s: only support IOM2 mode but adf2=%02x\n",
+				isac->name, isac->adf2);
+			isac_release(isac);
+			return -EINVAL;
+		}
+		WriteISAC(isac, ISAC_ADF2, isac->adf2);
+		WriteISAC(isac, ISAC_SQXR, 0x2f);
+		WriteISAC(isac, ISAC_SPCR, 0x00);
+		WriteISAC(isac, ISAC_STCR, 0x70);
+		WriteISAC(isac, ISAC_MODE, 0xc9);
+		WriteISAC(isac, ISAC_TIMR, 0x00);
+		WriteISAC(isac, ISAC_ADF1, 0x00);
+		val = ReadISAC(isac, ISAC_CIR0);
+		pr_debug("%s: ISAC CIR0 %x\n", isac->name, val);
+		isac->state = (val >> 2) & 0xf;
+		isac_ph_state_change(isac);
+		ph_command(isac, ISAC_CMD_RS);
+		WriteISAC(isac, ISAC_MASK, 0);
+	}
+	return err;
+}
+
+int
+mISDNisac_init(struct isac_hw *isac, void *hw)
+{
+	mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh);
+	isac->dch.hw = hw;
+	isac->dch.dev.D.send = isac_l1hw;
+	isac->init = isac_init;
+	isac->release = isac_release;
+	isac->ctrl = isac_ctrl;
+	isac->open = open_dchannel;
+	isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
+	isac->dch.dev.nrbchan = 2;
+	return 0;
+}
+EXPORT_SYMBOL(mISDNisac_init);
+
+static void
+waitforCEC(struct hscx_hw *hx)
+{
+	u8 starb, to = 50;
+
+	while (to) {
+		starb = ReadHSCX(hx, IPAC_STARB);
+		if (!(starb & 0x04))
+			break;
+		udelay(1);
+		to--;
+	}
+	if (to < 50)
+		pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr,
+			50 - to);
+	if (!to)
+		pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr);
+}
+
+
+static void
+waitforXFW(struct hscx_hw *hx)
+{
+	u8 starb, to = 50;
+
+	while (to) {
+		starb = ReadHSCX(hx, IPAC_STARB);
+		if ((starb & 0x44) == 0x40)
+			break;
+		udelay(1);
+		to--;
+	}
+	if (to < 50)
+		pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr,
+			50 - to);
+	if (!to)
+		pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr);
+}
+
+static void
+hscx_cmdr(struct hscx_hw *hx, u8 cmd)
+{
+	if (hx->ip->type & IPAC_TYPE_IPACX)
+		WriteHSCX(hx, IPACX_CMDRB, cmd);
+	else {
+		waitforCEC(hx);
+		WriteHSCX(hx, IPAC_CMDRB, cmd);
+	}
+}
+
+static void
+hscx_empty_fifo(struct hscx_hw *hscx, u8 count)
+{
+	u8 *p;
+
+	pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count);
+	if (!hscx->bch.rx_skb) {
+		hscx->bch.rx_skb = mI_alloc_skb(hscx->bch.maxlen, GFP_ATOMIC);
+		if (!hscx->bch.rx_skb) {
+			pr_info("%s: B receive out of memory\n",
+				hscx->ip->name);
+			hscx_cmdr(hscx, 0x80); /* RMC */
+			return;
+		}
+	}
+	if ((hscx->bch.rx_skb->len + count) > hscx->bch.maxlen) {
+		pr_debug("%s: overrun %d\n", hscx->ip->name,
+			hscx->bch.rx_skb->len + count);
+		skb_trim(hscx->bch.rx_skb, 0);
+		hscx_cmdr(hscx, 0x80); /* RMC */
+		return;
+	}
+	p = skb_put(hscx->bch.rx_skb, count);
+
+	if (hscx->ip->type & IPAC_TYPE_IPACX)
+		hscx->ip->read_fifo(hscx->ip->hw,
+			hscx->off + IPACX_RFIFOB, p, count);
+	else
+		hscx->ip->read_fifo(hscx->ip->hw,
+			hscx->off, p, count);
+
+	hscx_cmdr(hscx, 0x80); /* RMC */
+
+	if (hscx->bch.debug & DEBUG_HW_BFIFO) {
+		snprintf(hscx->log, 64, "B%1d-recv %s %d ",
+			hscx->bch.nr, hscx->ip->name, count);
+		print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
+	}
+}
+
+static void
+hscx_fill_fifo(struct hscx_hw *hscx)
+{
+	int count, more;
+	u8 *p;
+
+	if (!hscx->bch.tx_skb)
+		return;
+	count = hscx->bch.tx_skb->len - hscx->bch.tx_idx;
+	if (count <= 0)
+		return;
+	p = hscx->bch.tx_skb->data + hscx->bch.tx_idx;
+
+	more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0;
+	if (count > hscx->fifo_size) {
+		count = hscx->fifo_size;
+		more = 1;
+	}
+	pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr, count,
+		hscx->bch.tx_idx, hscx->bch.tx_skb->len);
+	hscx->bch.tx_idx += count;
+
+	if (hscx->ip->type & IPAC_TYPE_IPACX)
+		hscx->ip->write_fifo(hscx->ip->hw,
+			hscx->off + IPACX_XFIFOB, p, count);
+	else {
+		waitforXFW(hscx);
+		hscx->ip->write_fifo(hscx->ip->hw,
+			hscx->off, p, count);
+	}
+	hscx_cmdr(hscx, more ? 0x08 : 0x0a);
+
+	if (hscx->bch.debug & DEBUG_HW_BFIFO) {
+		snprintf(hscx->log, 64, "B%1d-send %s %d ",
+			hscx->bch.nr, hscx->ip->name, count);
+		print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
+	}
+}
+
+static void
+hscx_xpr(struct hscx_hw *hx)
+{
+	if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len)
+		hscx_fill_fifo(hx);
+	else {
+		if (hx->bch.tx_skb) {
+			/* send confirm, on trans, free on hdlc. */
+			if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags))
+				confirm_Bsend(&hx->bch);
+			dev_kfree_skb(hx->bch.tx_skb);
+		}
+		if (get_next_bframe(&hx->bch))
+			hscx_fill_fifo(hx);
+	}
+}
+
+static void
+ipac_rme(struct hscx_hw *hx)
+{
+	int count;
+	u8 rstab;
+
+	if (hx->ip->type & IPAC_TYPE_IPACX)
+		rstab = ReadHSCX(hx, IPACX_RSTAB);
+	else
+		rstab = ReadHSCX(hx, IPAC_RSTAB);
+	pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab);
+	if ((rstab & 0xf0) != 0xa0) {
+		/* !(VFR && !RDO && CRC && !RAB) */
+		if (!(rstab & 0x80)) {
+			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+				pr_notice("%s: B%1d invalid frame\n",
+					hx->ip->name, hx->bch.nr);
+		}
+		if (rstab & 0x40) {
+			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+				pr_notice("%s: B%1d RDO proto=%x\n",
+					hx->ip->name, hx->bch.nr,
+					hx->bch.state);
+		}
+		if (!(rstab & 0x20)) {
+			if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+				pr_notice("%s: B%1d CRC error\n",
+					hx->ip->name, hx->bch.nr);
+		}
+		hscx_cmdr(hx, 0x80); /* Do RMC */
+		return;
+	}
+	if (hx->ip->type & IPAC_TYPE_IPACX)
+		count = ReadHSCX(hx, IPACX_RBCLB);
+	else
+		count = ReadHSCX(hx, IPAC_RBCLB);
+	count &= (hx->fifo_size - 1);
+	if (count == 0)
+		count = hx->fifo_size;
+	hscx_empty_fifo(hx, count);
+	if (!hx->bch.rx_skb)
+		return;
+	if (hx->bch.rx_skb->len < 2) {
+		pr_debug("%s: B%1d frame to short %d\n",
+			hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len);
+		skb_trim(hx->bch.rx_skb, 0);
+	} else {
+		skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1);
+		recv_Bchannel(&hx->bch, 0);
+	}
+}
+
+static void
+ipac_irq(struct hscx_hw *hx, u8 ista)
+{
+	u8 istab, m, exirb = 0;
+
+	if (hx->ip->type & IPAC_TYPE_IPACX)
+		istab = ReadHSCX(hx, IPACX_ISTAB);
+	else if (hx->ip->type & IPAC_TYPE_IPAC) {
+		istab = ReadHSCX(hx, IPAC_ISTAB);
+		m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB;
+		if (m & ista) {
+			exirb = ReadHSCX(hx, IPAC_EXIRB);
+			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+				hx->bch.nr, exirb);
+		}
+	} else if (hx->bch.nr & 2) { /* HSCX B */
+		if (ista & (HSCX__EXA | HSCX__ICA))
+			ipac_irq(&hx->ip->hscx[0], ista);
+		if (ista & HSCX__EXB) {
+			exirb = ReadHSCX(hx, IPAC_EXIRB);
+			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+				hx->bch.nr, exirb);
+		}
+		istab = ista & 0xF8;
+	} else { /* HSCX A */
+		istab = ReadHSCX(hx, IPAC_ISTAB);
+		if (ista & HSCX__EXA) {
+			exirb = ReadHSCX(hx, IPAC_EXIRB);
+			pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+				hx->bch.nr, exirb);
+		}
+		istab = istab & 0xF8;
+	}
+	if (exirb & IPAC_B_XDU)
+		istab |= IPACX_B_XDU;
+	if (exirb & IPAC_B_RFO)
+		istab |= IPACX_B_RFO;
+	pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab);
+
+	if (!test_bit(FLG_ACTIVE, &hx->bch.Flags))
+		return;
+
+	if (istab & IPACX_B_RME)
+		ipac_rme(hx);
+
+	if (istab & IPACX_B_RPF) {
+		hscx_empty_fifo(hx, hx->fifo_size);
+		if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {
+			/* receive transparent audio data */
+			if (hx->bch.rx_skb)
+				recv_Bchannel(&hx->bch, 0);
+		}
+	}
+
+	if (istab & IPACX_B_RFO) {
+		pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr);
+		hscx_cmdr(hx, 0x40);	/* RRES */
+	}
+
+	if (istab & IPACX_B_XPR)
+		hscx_xpr(hx);
+
+	if (istab & IPACX_B_XDU) {
+		if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {
+			hscx_fill_fifo(hx);
+			return;
+		}
+		pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name,
+			hx->bch.nr, hx->bch.tx_idx);
+		hx->bch.tx_idx = 0;
+		hscx_cmdr(hx, 0x01);	/* XRES */
+	}
+}
+
+irqreturn_t
+mISDNipac_irq(struct ipac_hw *ipac, int maxloop)
+{
+	int cnt = maxloop + 1;
+	u8 ista, istad;
+	struct isac_hw  *isac = &ipac->isac;
+
+	if (ipac->type & IPAC_TYPE_IPACX) {
+		ista = ReadIPAC(ipac, ISACX_ISTA);
+		while (ista && cnt--) {
+			pr_debug("%s: ISTA %02x\n", ipac->name, ista);
+			if (ista & IPACX__ICA)
+				ipac_irq(&ipac->hscx[0], ista);
+			if (ista & IPACX__ICB)
+				ipac_irq(&ipac->hscx[1], ista);
+			if (ista & (ISACX__ICD | ISACX__CIC))
+				mISDNisac_irq(&ipac->isac, ista);
+			ista = ReadIPAC(ipac, ISACX_ISTA);
+		}
+	} else if (ipac->type & IPAC_TYPE_IPAC) {
+		ista = ReadIPAC(ipac, IPAC_ISTA);
+		while (ista && cnt--) {
+			pr_debug("%s: ISTA %02x\n", ipac->name, ista);
+			if (ista & (IPAC__ICD | IPAC__EXD)) {
+				istad = ReadISAC(isac, ISAC_ISTA);
+				pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
+				if (istad & IPAC_D_TIN2)
+					pr_debug("%s TIN2 irq\n", ipac->name);
+				if (ista & IPAC__EXD)
+					istad |= 1; /* ISAC EXI */
+				mISDNisac_irq(isac, istad);
+			}
+			if (ista & (IPAC__ICA | IPAC__EXA))
+				ipac_irq(&ipac->hscx[0], ista);
+			if (ista & (IPAC__ICB | IPAC__EXB))
+				ipac_irq(&ipac->hscx[1], ista);
+			ista = ReadIPAC(ipac, IPAC_ISTA);
+		}
+	} else if (ipac->type & IPAC_TYPE_HSCX) {
+		while (cnt) {
+			ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off);
+			pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista);
+			if (ista)
+				ipac_irq(&ipac->hscx[1], ista);
+			istad = ReadISAC(isac, ISAC_ISTA);
+			pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
+			if (istad)
+				mISDNisac_irq(isac, istad);
+			if (0 == (ista | istad))
+				break;
+			cnt--;
+		}
+	}
+	if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */
+		return IRQ_NONE;
+	if (cnt < maxloop)
+		pr_debug("%s: %d irqloops cpu%d\n", ipac->name,
+			maxloop - cnt, smp_processor_id());
+	if (maxloop && !cnt)
+		pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name,
+			maxloop, smp_processor_id());
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(mISDNipac_irq);
+
+static int
+hscx_mode(struct hscx_hw *hscx, u32 bprotocol)
+{
+	pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name,
+		'@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr);
+	if (hscx->ip->type & IPAC_TYPE_IPACX) {
+		if (hscx->bch.nr & 1) { /* B1 and ICA */
+			WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80);
+			WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88);
+		} else { /* B2 and ICB */
+			WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81);
+			WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88);
+		}
+		switch (bprotocol) {
+		case ISDN_P_NONE: /* init */
+			WriteHSCX(hscx, IPACX_MODEB, 0xC0);	/* rec off */
+			WriteHSCX(hscx, IPACX_EXMB,  0x30);	/* std adj. */
+			WriteHSCX(hscx, IPACX_MASKB, 0xFF);	/* ints off */
+			hscx_cmdr(hscx, 0x41);
+			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+			break;
+		case ISDN_P_B_RAW:
+			WriteHSCX(hscx, IPACX_MODEB, 0x88);	/* ex trans */
+			WriteHSCX(hscx, IPACX_EXMB,  0x00);	/* trans */
+			hscx_cmdr(hscx, 0x41);
+			WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
+			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+			break;
+		case ISDN_P_B_HDLC:
+			WriteHSCX(hscx, IPACX_MODEB, 0xC0);	/* trans */
+			WriteHSCX(hscx, IPACX_EXMB,  0x00);	/* hdlc,crc */
+			hscx_cmdr(hscx, 0x41);
+			WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
+			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+			break;
+		default:
+			pr_info("%s: protocol not known %x\n", hscx->ip->name,
+				bprotocol);
+			return -ENOPROTOOPT;
+		}
+	} else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */
+		WriteHSCX(hscx, IPAC_CCR1, 0x82);
+		WriteHSCX(hscx, IPAC_CCR2, 0x30);
+		WriteHSCX(hscx, IPAC_XCCR, 0x07);
+		WriteHSCX(hscx, IPAC_RCCR, 0x07);
+		WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
+		WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
+		switch (bprotocol) {
+		case ISDN_P_NONE:
+			WriteHSCX(hscx, IPAC_TSAX, 0x1F);
+			WriteHSCX(hscx, IPAC_TSAR, 0x1F);
+			WriteHSCX(hscx, IPAC_MODEB, 0x84);
+			WriteHSCX(hscx, IPAC_CCR1, 0x82);
+			WriteHSCX(hscx, IPAC_MASKB, 0xFF);	/* ints off */
+			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+			break;
+		case ISDN_P_B_RAW:
+			WriteHSCX(hscx, IPAC_MODEB, 0xe4);	/* ex trans */
+			WriteHSCX(hscx, IPAC_CCR1, 0x82);
+			hscx_cmdr(hscx, 0x41);
+			WriteHSCX(hscx, IPAC_MASKB, 0);
+			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+			break;
+		case ISDN_P_B_HDLC:
+			WriteHSCX(hscx, IPAC_MODEB, 0x8c);
+			WriteHSCX(hscx, IPAC_CCR1, 0x8a);
+			hscx_cmdr(hscx, 0x41);
+			WriteHSCX(hscx, IPAC_MASKB, 0);
+			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+			break;
+		default:
+			pr_info("%s: protocol not known %x\n", hscx->ip->name,
+				bprotocol);
+			return -ENOPROTOOPT;
+		}
+	} else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */
+		WriteHSCX(hscx, IPAC_CCR1, 0x85);
+		WriteHSCX(hscx, IPAC_CCR2, 0x30);
+		WriteHSCX(hscx, IPAC_XCCR, 0x07);
+		WriteHSCX(hscx, IPAC_RCCR, 0x07);
+		WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
+		WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
+		switch (bprotocol) {
+		case ISDN_P_NONE:
+			WriteHSCX(hscx, IPAC_TSAX, 0x1F);
+			WriteHSCX(hscx, IPAC_TSAR, 0x1F);
+			WriteHSCX(hscx, IPAC_MODEB, 0x84);
+			WriteHSCX(hscx, IPAC_CCR1, 0x85);
+			WriteHSCX(hscx, IPAC_MASKB, 0xFF);	/* ints off */
+			test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+			test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+			break;
+		case ISDN_P_B_RAW:
+			WriteHSCX(hscx, IPAC_MODEB, 0xe4);	/* ex trans */
+			WriteHSCX(hscx, IPAC_CCR1, 0x85);
+			hscx_cmdr(hscx, 0x41);
+			WriteHSCX(hscx, IPAC_MASKB, 0);
+			test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+			break;
+		case ISDN_P_B_HDLC:
+			WriteHSCX(hscx, IPAC_MODEB, 0x8c);
+			WriteHSCX(hscx, IPAC_CCR1, 0x8d);
+			hscx_cmdr(hscx, 0x41);
+			WriteHSCX(hscx, IPAC_MASKB, 0);
+			test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+			break;
+		default:
+			pr_info("%s: protocol not known %x\n", hscx->ip->name,
+				bprotocol);
+			return -ENOPROTOOPT;
+		}
+	} else
+		return -EINVAL;
+	hscx->bch.state = bprotocol;
+	return 0;
+}
+
+static int
+hscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct hscx_hw	*hx = container_of(bch, struct hscx_hw, bch);
+	int ret = -EINVAL;
+	struct mISDNhead *hh = mISDN_HEAD_P(skb);
+	u32 id;
+	u_long flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(hx->ip->hwlock, flags);
+		ret = bchannel_senddata(bch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			ret = 0;
+			hscx_fill_fifo(hx);
+			spin_unlock_irqrestore(hx->ip->hwlock, flags);
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(hx->ip->hwlock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		spin_lock_irqsave(hx->ip->hwlock, flags);
+		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+			ret = hscx_mode(hx, ch->protocol);
+		else
+			ret = 0;
+		spin_unlock_irqrestore(hx->ip->hwlock, flags);
+		if (!ret)
+			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+				NULL, GFP_KERNEL);
+		break;
+	case PH_DEACTIVATE_REQ:
+		spin_lock_irqsave(hx->ip->hwlock, flags);
+		mISDN_clear_bchannel(bch);
+		hscx_mode(hx, ISDN_P_NONE);
+		spin_unlock_irqrestore(hx->ip->hwlock, flags);
+		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_KERNEL);
+		ret = 0;
+		break;
+	default:
+		pr_info("%s: %s unknown prim(%x,%x)\n",
+			hx->ip->name, __func__, hh->prim, hh->id);
+		ret = -EINVAL;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = 0;
+		break;
+	/* Nothing implemented yet */
+	case MISDN_CTRL_FILL_EMPTY:
+	default:
+		pr_info("%s: unknown Op %x\n", __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct hscx_hw	*hx = container_of(bch, struct hscx_hw, bch);
+	int ret = -EINVAL;
+	u_long flags;
+
+	pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg);
+	switch (cmd) {
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+			spin_lock_irqsave(hx->ip->hwlock, flags);
+			mISDN_freebchannel(bch);
+			hscx_mode(hx, ISDN_P_NONE);
+			spin_unlock_irqrestore(hx->ip->hwlock, flags);
+		} else {
+			skb_queue_purge(&bch->rqueue);
+			bch->rcount = 0;
+		}
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(hx->ip->owner);
+		ret = 0;
+		break;
+	case CONTROL_CHANNEL:
+		ret = channel_bctrl(bch, arg);
+		break;
+	default:
+		pr_info("%s: %s unknown prim(%x)\n",
+			hx->ip->name, __func__, cmd);
+	}
+	return ret;
+}
+
+static void
+free_ipac(struct ipac_hw *ipac)
+{
+	isac_release(&ipac->isac);
+}
+
+static const char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+
+
+static void
+hscx_init(struct hscx_hw *hx)
+{
+	u8 val;
+
+	WriteHSCX(hx, IPAC_RAH2, 0xFF);
+	WriteHSCX(hx, IPAC_XBCH, 0x00);
+	WriteHSCX(hx, IPAC_RLCR, 0x00);
+
+	if (hx->ip->type & IPAC_TYPE_HSCX) {
+		WriteHSCX(hx, IPAC_CCR1, 0x85);
+		val = ReadHSCX(hx, HSCX_VSTR);
+		pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val);
+		if (hx->bch.debug & DEBUG_HW)
+			pr_notice("%s: HSCX version %s\n", hx->ip->name,
+				HSCXVer[val & 0x0f]);
+	} else
+		WriteHSCX(hx, IPAC_CCR1, 0x82);
+	WriteHSCX(hx, IPAC_CCR2, 0x30);
+	WriteHSCX(hx, IPAC_XCCR, 0x07);
+	WriteHSCX(hx, IPAC_RCCR, 0x07);
+}
+
+static int
+ipac_init(struct ipac_hw *ipac)
+{
+	u8 val;
+
+	if (ipac->type & IPAC_TYPE_HSCX) {
+		hscx_init(&ipac->hscx[0]);
+		hscx_init(&ipac->hscx[1]);
+		val = ReadIPAC(ipac, IPAC_ID);
+	} else if (ipac->type & IPAC_TYPE_IPAC) {
+		hscx_init(&ipac->hscx[0]);
+		hscx_init(&ipac->hscx[1]);
+		WriteIPAC(ipac, IPAC_MASK, IPAC__ON);
+		val = ReadIPAC(ipac, IPAC_CONF);
+		/* conf is default 0, but can be overwritten by card setup */
+		pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name,
+			val, ipac->conf);
+		WriteIPAC(ipac, IPAC_CONF, ipac->conf);
+		val = ReadIPAC(ipac, IPAC_ID);
+		if (ipac->hscx[0].bch.debug & DEBUG_HW)
+			pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val);
+	}
+	/* nothing special for IPACX to do here */
+	return isac_init(&ipac->isac);
+}
+
+static int
+open_bchannel(struct ipac_hw *ipac, struct channel_req *rq)
+{
+	struct bchannel		*bch;
+
+	if (rq->adr.channel > 2)
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	bch = &ipac->hscx[rq->adr.channel - 1].bch;
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+	bch->ch.protocol = rq->protocol;
+	rq->ch = &bch->ch;
+	return 0;
+}
+
+static int
+channel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_LOOP;
+		break;
+	case MISDN_CTRL_LOOP:
+		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+		if (cq->channel < 0 || cq->channel > 3) {
+			ret = -EINVAL;
+			break;
+		}
+		ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel);
+		break;
+	default:
+		pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel *dch = container_of(dev, struct dchannel, dev);
+	struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+	struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac);
+	struct channel_req *rq;
+	int err = 0;
+
+	pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		if (rq->protocol == ISDN_P_TE_S0)
+			err = open_dchannel(isac, rq);
+		else
+			err = open_bchannel(ipac, rq);
+		if (err)
+			break;
+		if (!try_module_get(ipac->owner))
+			pr_info("%s: cannot get module\n", ipac->name);
+		break;
+	case CLOSE_CHANNEL:
+		pr_debug("%s: dev(%d) close from %p\n", ipac->name,
+			dch->dev.id, __builtin_return_address(0));
+		module_put(ipac->owner);
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_ctrl(ipac, arg);
+		break;
+	default:
+		pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd);
+		return -EINVAL;
+	}
+	return err;
+}
+
+u32
+mISDNipac_init(struct ipac_hw *ipac, void *hw)
+{
+	u32 ret;
+	u8 i;
+
+	ipac->hw = hw;
+	if (ipac->isac.dch.debug & DEBUG_HW)
+		pr_notice("%s: ipac type %x\n", ipac->name, ipac->type);
+	if (ipac->type & IPAC_TYPE_HSCX) {
+		ipac->isac.type = IPAC_TYPE_ISAC;
+		ipac->hscx[0].off = 0;
+		ipac->hscx[1].off = 0x40;
+		ipac->hscx[0].fifo_size = 32;
+		ipac->hscx[1].fifo_size = 32;
+	} else if (ipac->type & IPAC_TYPE_IPAC) {
+		ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC;
+		ipac->hscx[0].off = 0;
+		ipac->hscx[1].off = 0x40;
+		ipac->hscx[0].fifo_size = 64;
+		ipac->hscx[1].fifo_size = 64;
+	} else if (ipac->type & IPAC_TYPE_IPACX) {
+		ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX;
+		ipac->hscx[0].off = IPACX_OFF_ICA;
+		ipac->hscx[1].off = IPACX_OFF_ICB;
+		ipac->hscx[0].fifo_size = 64;
+		ipac->hscx[1].fifo_size = 64;
+	} else
+		return 0;
+
+	mISDNisac_init(&ipac->isac, hw);
+
+	ipac->isac.dch.dev.D.ctrl = ipac_dctrl;
+
+	for (i = 0; i < 2; i++) {
+		ipac->hscx[i].bch.nr = i + 1;
+		set_channelmap(i + 1, ipac->isac.dch.dev.channelmap);
+		list_add(&ipac->hscx[i].bch.ch.list,
+			&ipac->isac.dch.dev.bchannels);
+		mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM);
+		ipac->hscx[i].bch.ch.nr = i + 1;
+		ipac->hscx[i].bch.ch.send = &hscx_l2l1;
+		ipac->hscx[i].bch.ch.ctrl = hscx_bctrl;
+		ipac->hscx[i].bch.hw = hw;
+		ipac->hscx[i].ip = ipac;
+		/* default values for IOM time slots
+		 * can be overwriten by card */
+		ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03;
+	}
+
+	ipac->init = ipac_init;
+	ipac->release = free_ipac;
+
+	ret =	(1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	return ret;
+}
+EXPORT_SYMBOL(mISDNipac_init);
+
+static int __init
+isac_mod_init(void)
+{
+	pr_notice("mISDNipac module version %s\n", ISAC_REV);
+	return 0;
+}
+
+static void __exit
+isac_mod_cleanup(void)
+{
+	pr_notice("mISDNipac module unloaded\n");
+}
+module_init(isac_mod_init);
+module_exit(isac_mod_cleanup);

+ 1726 - 0
drivers/isdn/hardware/mISDN/mISDNisar.c

@@ -0,0 +1,1726 @@
+/*
+ * mISDNisar.c   ISAR (Siemens PSB 7110) specific functions
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* define this to enable static debug messages, if you kernel supports
+ * dynamic debugging, you should use debugfs for this
+ */
+/* #define DEBUG */
+
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/mISDNhw.h>
+#include "isar.h"
+
+#define ISAR_REV	"2.1"
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(ISAR_REV);
+
+#define DEBUG_HW_FIRMWARE_FIFO	0x10000
+
+static const u8 faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146";
+static const u8 faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121,
+					122, 145, 146};
+#define FAXMODCNT 13
+
+static void isar_setup(struct isar_hw *);
+
+static inline int
+waitforHIA(struct isar_hw *isar, int timeout)
+{
+	int t = timeout;
+	u8 val = isar->read_reg(isar->hw, ISAR_HIA);
+
+	while ((val & 1) && t) {
+		udelay(1);
+		t--;
+		val = isar->read_reg(isar->hw, ISAR_HIA);
+	}
+	pr_debug("%s: HIA after %dus\n", isar->name, timeout - t);
+	return timeout;
+}
+
+/*
+ * send msg to ISAR mailbox
+ * if msg is NULL use isar->buf
+ */
+static int
+send_mbox(struct isar_hw *isar, u8 his, u8 creg, u8 len, u8 *msg)
+{
+	if (!waitforHIA(isar, 1000))
+		return 0;
+	pr_debug("send_mbox(%02x,%02x,%d)\n", his, creg, len);
+	isar->write_reg(isar->hw, ISAR_CTRL_H, creg);
+	isar->write_reg(isar->hw, ISAR_CTRL_L, len);
+	isar->write_reg(isar->hw, ISAR_WADR, 0);
+	if (!msg)
+		msg = isar->buf;
+	if (msg && len) {
+		isar->write_fifo(isar->hw, ISAR_MBOX, msg, len);
+		if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) {
+			int l = 0;
+
+			while (l < (int)len) {
+				hex_dump_to_buffer(msg + l, len - l, 32, 1,
+					isar->log, 256, 1);
+				pr_debug("%s: %s %02x: %s\n", isar->name,
+					__func__, l, isar->log);
+				l += 32;
+			}
+		}
+	}
+	isar->write_reg(isar->hw, ISAR_HIS, his);
+	waitforHIA(isar, 1000);
+	return 1;
+}
+
+/*
+ * receive message from ISAR mailbox
+ * if msg is NULL use isar->buf
+ */
+static void
+rcv_mbox(struct isar_hw *isar, u8 *msg)
+{
+	if (!msg)
+		msg = isar->buf;
+	isar->write_reg(isar->hw, ISAR_RADR, 0);
+	if (msg && isar->clsb) {
+		isar->read_fifo(isar->hw, ISAR_MBOX, msg, isar->clsb);
+		if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) {
+			int l = 0;
+
+			while (l < (int)isar->clsb) {
+				hex_dump_to_buffer(msg + l, isar->clsb - l, 32,
+					1, isar->log, 256, 1);
+				pr_debug("%s: %s %02x: %s\n", isar->name,
+					__func__, l, isar->log);
+				l += 32;
+			}
+		}
+	}
+	isar->write_reg(isar->hw, ISAR_IIA, 0);
+}
+
+static inline void
+get_irq_infos(struct isar_hw *isar)
+{
+	isar->iis = isar->read_reg(isar->hw, ISAR_IIS);
+	isar->cmsb = isar->read_reg(isar->hw, ISAR_CTRL_H);
+	isar->clsb = isar->read_reg(isar->hw, ISAR_CTRL_L);
+	pr_debug("%s: rcv_mbox(%02x,%02x,%d)\n", isar->name,
+		isar->iis, isar->cmsb, isar->clsb);
+}
+
+/*
+ * poll answer message from ISAR mailbox
+ * should be used only with ISAR IRQs disabled before DSP was started
+ *
+ */
+static int
+poll_mbox(struct isar_hw *isar, int maxdelay)
+{
+	int t = maxdelay;
+	u8 irq;
+
+	irq = isar->read_reg(isar->hw, ISAR_IRQBIT);
+	while (t && !(irq & ISAR_IRQSTA)) {
+		udelay(1);
+		t--;
+	}
+	if (t)	{
+		get_irq_infos(isar);
+		rcv_mbox(isar, NULL);
+	}
+	pr_debug("%s: pulled %d bytes after %d us\n",
+		isar->name, isar->clsb, maxdelay - t);
+	return t;
+}
+
+static int
+ISARVersion(struct isar_hw *isar)
+{
+	int ver;
+
+	/* disable ISAR IRQ */
+	isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+	isar->buf[0] = ISAR_MSG_HWVER;
+	isar->buf[1] = 0;
+	isar->buf[2] = 1;
+	if (!send_mbox(isar, ISAR_HIS_VNR, 0, 3, NULL))
+		return -1;
+	if (!poll_mbox(isar, 1000))
+		return -2;
+	if (isar->iis == ISAR_IIS_VNR) {
+		if (isar->clsb == 1) {
+			ver = isar->buf[0] & 0xf;
+			return ver;
+		}
+		return -3;
+	}
+	return -4;
+}
+
+static int
+load_firmware(struct isar_hw *isar, const u8 *buf, int size)
+{
+	u32	saved_debug = isar->ch[0].bch.debug;
+	int	ret, cnt;
+	u8	nom, noc;
+	u16	left, val, *sp = (u16 *)buf;
+	u8	*mp;
+	u_long	flags;
+
+	struct {
+		u16 sadr;
+		u16 len;
+		u16 d_key;
+	} blk_head;
+
+	if (1 != isar->version) {
+		pr_err("%s: ISAR wrong version %d firmware download aborted\n",
+			isar->name, isar->version);
+		return -EINVAL;
+	}
+	if (!(saved_debug & DEBUG_HW_FIRMWARE_FIFO))
+		isar->ch[0].bch.debug &= ~DEBUG_HW_BFIFO;
+	pr_debug("%s: load firmware %d words (%d bytes)\n",
+		isar->name, size/2, size);
+	cnt = 0;
+	size /= 2;
+	/* disable ISAR IRQ */
+	spin_lock_irqsave(isar->hwlock, flags);
+	isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+	spin_unlock_irqrestore(isar->hwlock, flags);
+	while (cnt < size) {
+		blk_head.sadr = le16_to_cpu(*sp++);
+		blk_head.len = le16_to_cpu(*sp++);
+		blk_head.d_key = le16_to_cpu(*sp++);
+		cnt += 3;
+		pr_debug("ISAR firmware block (%#x,%d,%#x)\n",
+			blk_head.sadr, blk_head.len, blk_head.d_key & 0xff);
+		left = blk_head.len;
+		if (cnt + left > size) {
+			pr_info("%s: firmware error have %d need %d words\n",
+				isar->name, size, cnt + left);
+			ret = -EINVAL;
+			goto reterrflg;
+		}
+		spin_lock_irqsave(isar->hwlock, flags);
+		if (!send_mbox(isar, ISAR_HIS_DKEY, blk_head.d_key & 0xff,
+		    0, NULL)) {
+			pr_info("ISAR send_mbox dkey failed\n");
+			ret = -ETIME;
+			goto reterror;
+		}
+		if (!poll_mbox(isar, 1000)) {
+			pr_warning("ISAR poll_mbox dkey failed\n");
+			ret = -ETIME;
+			goto reterror;
+		}
+		spin_unlock_irqrestore(isar->hwlock, flags);
+		if ((isar->iis != ISAR_IIS_DKEY) || isar->cmsb || isar->clsb) {
+			pr_info("ISAR wrong dkey response (%x,%x,%x)\n",
+				isar->iis, isar->cmsb, isar->clsb);
+			ret = 1;
+			goto reterrflg;
+		}
+		while (left > 0) {
+			if (left > 126)
+				noc = 126;
+			else
+				noc = left;
+			nom = (2 * noc) + 3;
+			mp  = isar->buf;
+			/* the ISAR is big endian */
+			*mp++ = blk_head.sadr >> 8;
+			*mp++ = blk_head.sadr & 0xFF;
+			left -= noc;
+			cnt += noc;
+			*mp++ = noc;
+			pr_debug("%s: load %3d words at %04x\n", isar->name,
+				noc, blk_head.sadr);
+			blk_head.sadr += noc;
+			while (noc) {
+				val = le16_to_cpu(*sp++);
+				*mp++ = val >> 8;
+				*mp++ = val & 0xFF;;
+				noc--;
+			}
+			spin_lock_irqsave(isar->hwlock, flags);
+			if (!send_mbox(isar, ISAR_HIS_FIRM, 0, nom, NULL)) {
+				pr_info("ISAR send_mbox prog failed\n");
+				ret = -ETIME;
+				goto reterror;
+			}
+			if (!poll_mbox(isar, 1000)) {
+				pr_info("ISAR poll_mbox prog failed\n");
+				ret = -ETIME;
+				goto reterror;
+			}
+			spin_unlock_irqrestore(isar->hwlock, flags);
+			if ((isar->iis != ISAR_IIS_FIRM) ||
+			    isar->cmsb || isar->clsb) {
+				pr_info("ISAR wrong prog response (%x,%x,%x)\n",
+					isar->iis, isar->cmsb, isar->clsb);
+				ret = -EIO;
+				goto reterrflg;
+			}
+		}
+		pr_debug("%s: ISAR firmware block %d words loaded\n",
+			isar->name, blk_head.len);
+	}
+	isar->ch[0].bch.debug = saved_debug;
+	/* 10ms delay */
+	cnt = 10;
+	while (cnt--)
+		mdelay(1);
+	isar->buf[0] = 0xff;
+	isar->buf[1] = 0xfe;
+	isar->bstat = 0;
+	spin_lock_irqsave(isar->hwlock, flags);
+	if (!send_mbox(isar, ISAR_HIS_STDSP, 0, 2, NULL)) {
+		pr_info("ISAR send_mbox start dsp failed\n");
+		ret = -ETIME;
+		goto reterror;
+	}
+	if (!poll_mbox(isar, 1000)) {
+		pr_info("ISAR poll_mbox start dsp failed\n");
+		ret = -ETIME;
+		goto reterror;
+	}
+	if ((isar->iis != ISAR_IIS_STDSP) || isar->cmsb || isar->clsb) {
+		pr_info("ISAR wrong start dsp response (%x,%x,%x)\n",
+			isar->iis, isar->cmsb, isar->clsb);
+		ret = -EIO;
+		goto reterror;
+	} else
+		pr_debug("%s: ISAR start dsp success\n", isar->name);
+
+	/* NORMAL mode entered */
+	/* Enable IRQs of ISAR */
+	isar->write_reg(isar->hw, ISAR_IRQBIT, ISAR_IRQSTA);
+	spin_unlock_irqrestore(isar->hwlock, flags);
+	cnt = 1000; /* max 1s */
+	while ((!isar->bstat) && cnt) {
+		mdelay(1);
+		cnt--;
+	}
+	if (!cnt) {
+		pr_info("ISAR no general status event received\n");
+		ret = -ETIME;
+		goto reterrflg;
+	} else
+		pr_debug("%s: ISAR general status event %x\n",
+			isar->name, isar->bstat);
+	/* 10ms delay */
+	cnt = 10;
+	while (cnt--)
+		mdelay(1);
+	isar->iis = 0;
+	spin_lock_irqsave(isar->hwlock, flags);
+	if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+		pr_info("ISAR send_mbox self tst failed\n");
+		ret = -ETIME;
+		goto reterror;
+	}
+	spin_unlock_irqrestore(isar->hwlock, flags);
+	cnt = 10000; /* max 100 ms */
+	while ((isar->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	mdelay(1);
+	if (!cnt) {
+		pr_info("ISAR no self tst response\n");
+		ret = -ETIME;
+		goto reterrflg;
+	}
+	if ((isar->cmsb == ISAR_CTRL_STST) && (isar->clsb == 1)
+	    && (isar->buf[0] == 0))
+		pr_debug("%s: ISAR selftest OK\n", isar->name);
+	else {
+		pr_info("ISAR selftest not OK %x/%x/%x\n",
+			isar->cmsb, isar->clsb, isar->buf[0]);
+		ret = -EIO;
+		goto reterrflg;
+	}
+	spin_lock_irqsave(isar->hwlock, flags);
+	isar->iis = 0;
+	if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+		pr_info("ISAR RQST SVN failed\n");
+		ret = -ETIME;
+		goto reterror;
+	}
+	spin_unlock_irqrestore(isar->hwlock, flags);
+	cnt = 30000; /* max 300 ms */
+	while ((isar->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	mdelay(1);
+	if (!cnt) {
+		pr_info("ISAR no SVN response\n");
+		ret = -ETIME;
+		goto reterrflg;
+	} else {
+		if ((isar->cmsb == ISAR_CTRL_SWVER) && (isar->clsb == 1)) {
+			pr_notice("%s: ISAR software version %#x\n",
+				isar->name, isar->buf[0]);
+		} else {
+			pr_info("%s: ISAR wrong swver response (%x,%x)"
+				" cnt(%d)\n", isar->name, isar->cmsb,
+				isar->clsb, cnt);
+			ret = -EIO;
+			goto reterrflg;
+		}
+	}
+	spin_lock_irqsave(isar->hwlock, flags);
+	isar_setup(isar);
+	spin_unlock_irqrestore(isar->hwlock, flags);
+	ret = 0;
+reterrflg:
+	spin_lock_irqsave(isar->hwlock, flags);
+reterror:
+	isar->ch[0].bch.debug = saved_debug;
+	if (ret)
+		/* disable ISAR IRQ */
+		isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+	spin_unlock_irqrestore(isar->hwlock, flags);
+	return ret;
+}
+
+static inline void
+deliver_status(struct isar_ch *ch, int status)
+{
+	pr_debug("%s: HL->LL FAXIND %x\n", ch->is->name, status);
+	_queue_data(&ch->bch.ch, PH_CONTROL_IND, status, 0, NULL, GFP_ATOMIC);
+}
+
+static inline void
+isar_rcv_frame(struct isar_ch *ch)
+{
+	u8		*ptr;
+
+	if (!ch->is->clsb) {
+		pr_debug("%s; ISAR zero len frame\n", ch->is->name);
+		ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+		return;
+	}
+	switch (ch->bch.state) {
+	case ISDN_P_NONE:
+		pr_debug("%s: ISAR protocol 0 spurious IIS_RDATA %x/%x/%x\n",
+			ch->is->name, ch->is->iis, ch->is->cmsb, ch->is->clsb);
+		ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+		break;
+	case ISDN_P_B_RAW:
+	case ISDN_P_B_L2DTMF:
+	case ISDN_P_B_MODEM_ASYNC:
+		if (!ch->bch.rx_skb) {
+			ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+						GFP_ATOMIC);
+			if (unlikely(!ch->bch.rx_skb)) {
+				pr_info("%s: B receive out of memory\n",
+					ch->is->name);
+				ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+				break;
+			}
+		}
+		rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb));
+		recv_Bchannel(&ch->bch, 0);
+		break;
+	case ISDN_P_B_HDLC:
+		if (!ch->bch.rx_skb) {
+			ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+						GFP_ATOMIC);
+			if (unlikely(!ch->bch.rx_skb)) {
+				pr_info("%s: B receive out of memory\n",
+					ch->is->name);
+				ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+				break;
+			}
+		}
+		if ((ch->bch.rx_skb->len + ch->is->clsb) >
+		    (ch->bch.maxlen + 2)) {
+			pr_debug("%s: incoming packet too large\n",
+				ch->is->name);
+			ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+			skb_trim(ch->bch.rx_skb, 0);
+			break;
+		}
+		if (ch->is->cmsb & HDLC_ERROR) {
+			pr_debug("%s: ISAR frame error %x len %d\n",
+				ch->is->name, ch->is->cmsb, ch->is->clsb);
+#ifdef ERROR_STATISTIC
+			if (ch->is->cmsb & HDLC_ERR_RER)
+				ch->bch.err_inv++;
+			if (ch->is->cmsb & HDLC_ERR_CER)
+				ch->bch.err_crc++;
+#endif
+			skb_trim(ch->bch.rx_skb, 0);
+			ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+			break;
+		}
+		if (ch->is->cmsb & HDLC_FSD)
+			skb_trim(ch->bch.rx_skb, 0);
+		ptr = skb_put(ch->bch.rx_skb, ch->is->clsb);
+		rcv_mbox(ch->is, ptr);
+		if (ch->is->cmsb & HDLC_FED) {
+			if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */
+				pr_debug("%s: ISAR frame to short %d\n",
+					ch->is->name, ch->bch.rx_skb->len);
+				skb_trim(ch->bch.rx_skb, 0);
+				break;
+			}
+			skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2);
+			recv_Bchannel(&ch->bch, 0);
+		}
+		break;
+	case ISDN_P_B_T30_FAX:
+		if (ch->state != STFAX_ACTIV) {
+			pr_debug("%s: isar_rcv_frame: not ACTIV\n",
+				ch->is->name);
+			ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+			if (ch->bch.rx_skb)
+				skb_trim(ch->bch.rx_skb, 0);
+			break;
+		}
+		if (!ch->bch.rx_skb) {
+			ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+						GFP_ATOMIC);
+			if (unlikely(!ch->bch.rx_skb)) {
+				pr_info("%s: B receive out of memory\n",
+					__func__);
+				ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+				break;
+			}
+		}
+		if (ch->cmd == PCTRL_CMD_FRM) {
+			rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb));
+			pr_debug("%s: isar_rcv_frame: %d\n",
+				ch->is->name, ch->bch.rx_skb->len);
+			if (ch->is->cmsb & SART_NMD) { /* ABORT */
+				pr_debug("%s: isar_rcv_frame: no more data\n",
+					ch->is->name);
+				ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+				send_mbox(ch->is, SET_DPS(ch->dpath) |
+					ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+					0, NULL);
+				ch->state = STFAX_ESCAPE;
+				/* set_skb_flag(skb, DF_NOMOREDATA); */
+			}
+			recv_Bchannel(&ch->bch, 0);
+			if (ch->is->cmsb & SART_NMD)
+				deliver_status(ch, HW_MOD_NOCARR);
+			break;
+		}
+		if (ch->cmd != PCTRL_CMD_FRH) {
+			pr_debug("%s: isar_rcv_frame: unknown fax mode %x\n",
+				ch->is->name, ch->cmd);
+			ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+			if (ch->bch.rx_skb)
+				skb_trim(ch->bch.rx_skb, 0);
+			break;
+		}
+		/* PCTRL_CMD_FRH */
+		if ((ch->bch.rx_skb->len + ch->is->clsb) >
+		    (ch->bch.maxlen + 2)) {
+			pr_info("%s: %s incoming packet too large\n",
+				ch->is->name, __func__);
+			ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+			skb_trim(ch->bch.rx_skb, 0);
+			break;
+		}  else if (ch->is->cmsb & HDLC_ERROR) {
+			pr_info("%s: ISAR frame error %x len %d\n",
+				ch->is->name, ch->is->cmsb, ch->is->clsb);
+			skb_trim(ch->bch.rx_skb, 0);
+			ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+			break;
+		}
+		if (ch->is->cmsb & HDLC_FSD)
+			skb_trim(ch->bch.rx_skb, 0);
+		ptr = skb_put(ch->bch.rx_skb, ch->is->clsb);
+		rcv_mbox(ch->is, ptr);
+		if (ch->is->cmsb & HDLC_FED) {
+			if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */
+				pr_info("%s: ISAR frame to short %d\n",
+					ch->is->name, ch->bch.rx_skb->len);
+				skb_trim(ch->bch.rx_skb, 0);
+				break;
+			}
+			skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2);
+			recv_Bchannel(&ch->bch, 0);
+		}
+		if (ch->is->cmsb & SART_NMD) { /* ABORT */
+			pr_debug("%s: isar_rcv_frame: no more data\n",
+				ch->is->name);
+			ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+			if (ch->bch.rx_skb)
+				skb_trim(ch->bch.rx_skb, 0);
+			send_mbox(ch->is, SET_DPS(ch->dpath) |
+				ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+			ch->state = STFAX_ESCAPE;
+			deliver_status(ch, HW_MOD_NOCARR);
+		}
+		break;
+	default:
+		pr_info("isar_rcv_frame protocol (%x)error\n", ch->bch.state);
+		ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+		break;
+	}
+}
+
+static void
+isar_fill_fifo(struct isar_ch *ch)
+{
+	int count;
+	u8 msb;
+	u8 *ptr;
+
+	pr_debug("%s: ch%d  tx_skb %p tx_idx %d\n",
+		ch->is->name, ch->bch.nr, ch->bch.tx_skb, ch->bch.tx_idx);
+	if (!ch->bch.tx_skb)
+		return;
+	count = ch->bch.tx_skb->len - ch->bch.tx_idx;
+	if (count <= 0)
+		return;
+	if (!(ch->is->bstat &
+		(ch->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+		return;
+	if (count > ch->mml) {
+		msb = 0;
+		count = ch->mml;
+	} else {
+		msb = HDLC_FED;
+	}
+	ptr = ch->bch.tx_skb->data + ch->bch.tx_idx;
+	if (!ch->bch.tx_idx) {
+		pr_debug("%s: frame start\n", ch->is->name);
+		if ((ch->bch.state == ISDN_P_B_T30_FAX) &&
+			(ch->cmd == PCTRL_CMD_FTH)) {
+			if (count > 1) {
+				if ((ptr[0] == 0xff) && (ptr[1] == 0x13)) {
+					/* last frame */
+					test_and_set_bit(FLG_LASTDATA,
+						&ch->bch.Flags);
+					pr_debug("%s: set LASTDATA\n",
+						ch->is->name);
+					if (msb == HDLC_FED)
+						test_and_set_bit(FLG_DLEETX,
+							&ch->bch.Flags);
+				}
+			}
+		}
+		msb |= HDLC_FST;
+	}
+	ch->bch.tx_idx += count;
+	switch (ch->bch.state) {
+	case ISDN_P_NONE:
+		pr_info("%s: wrong protocol 0\n", __func__);
+		break;
+	case ISDN_P_B_RAW:
+	case ISDN_P_B_L2DTMF:
+	case ISDN_P_B_MODEM_ASYNC:
+		send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+			0, count, ptr);
+		break;
+	case ISDN_P_B_HDLC:
+		send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+			msb, count, ptr);
+		break;
+	case ISDN_P_B_T30_FAX:
+		if (ch->state != STFAX_ACTIV)
+			pr_debug("%s: not ACTIV\n", ch->is->name);
+		else if (ch->cmd == PCTRL_CMD_FTH)
+			send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+				msb, count, ptr);
+		else if (ch->cmd == PCTRL_CMD_FTM)
+			send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+				0, count, ptr);
+		else
+			pr_debug("%s: not FTH/FTM\n", ch->is->name);
+		break;
+	default:
+		pr_info("%s: protocol(%x) error\n",
+			__func__, ch->bch.state);
+		break;
+	}
+}
+
+static inline struct isar_ch *
+sel_bch_isar(struct isar_hw *isar, u8 dpath)
+{
+	struct isar_ch	*base = &isar->ch[0];
+
+	if ((!dpath) || (dpath > 2))
+		return NULL;
+	if (base->dpath == dpath)
+		return base;
+	base++;
+	if (base->dpath == dpath)
+		return base;
+	return NULL;
+}
+
+static void
+send_next(struct isar_ch *ch)
+{
+	pr_debug("%s: %s ch%d tx_skb %p tx_idx %d\n",
+		ch->is->name, __func__, ch->bch.nr,
+		ch->bch.tx_skb, ch->bch.tx_idx);
+	if (ch->bch.state == ISDN_P_B_T30_FAX) {
+		if (ch->cmd == PCTRL_CMD_FTH) {
+			if (test_bit(FLG_LASTDATA, &ch->bch.Flags)) {
+				pr_debug("set NMD_DATA\n");
+				test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags);
+			}
+		} else if (ch->cmd == PCTRL_CMD_FTM) {
+			if (test_bit(FLG_DLEETX, &ch->bch.Flags)) {
+				test_and_set_bit(FLG_LASTDATA, &ch->bch.Flags);
+				test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags);
+			}
+		}
+	}
+	if (ch->bch.tx_skb) {
+		/* send confirm, on trans, free on hdlc. */
+		if (test_bit(FLG_TRANSPARENT, &ch->bch.Flags))
+			confirm_Bsend(&ch->bch);
+		dev_kfree_skb(ch->bch.tx_skb);
+	}
+	if (get_next_bframe(&ch->bch))
+		isar_fill_fifo(ch);
+	else {
+		if (test_and_clear_bit(FLG_DLEETX, &ch->bch.Flags)) {
+			if (test_and_clear_bit(FLG_LASTDATA,
+			    &ch->bch.Flags)) {
+				if (test_and_clear_bit(FLG_NMD_DATA,
+				    &ch->bch.Flags)) {
+					u8 zd = 0;
+					send_mbox(ch->is, SET_DPS(ch->dpath) |
+						ISAR_HIS_SDATA, 0x01, 1, &zd);
+				}
+				test_and_set_bit(FLG_LL_OK, &ch->bch.Flags);
+			} else {
+				deliver_status(ch, HW_MOD_CONNECT);
+			}
+		}
+	}
+}
+
+static void
+check_send(struct isar_hw *isar, u8 rdm)
+{
+	struct isar_ch	*ch;
+
+	pr_debug("%s: rdm %x\n", isar->name, rdm);
+	if (rdm & BSTAT_RDM1) {
+		ch = sel_bch_isar(isar, 1);
+		if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) {
+			if (ch->bch.tx_skb && (ch->bch.tx_skb->len >
+			    ch->bch.tx_idx))
+				isar_fill_fifo(ch);
+			else
+				send_next(ch);
+		}
+	}
+	if (rdm & BSTAT_RDM2) {
+		ch = sel_bch_isar(isar, 2);
+		if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) {
+			if (ch->bch.tx_skb && (ch->bch.tx_skb->len >
+			    ch->bch.tx_idx))
+				isar_fill_fifo(ch);
+			else
+				send_next(ch);
+		}
+	}
+}
+
+const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
+			"300", "600", "1200", "2400", "4800", "7200",
+			"9600nt", "9600t", "12000", "14400", "WRONG"};
+const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21",
+			"Bell103", "V23", "Bell202", "V17", "V29", "V27ter"};
+
+static void
+isar_pump_status_rsp(struct isar_ch *ch) {
+	u8 ril = ch->is->buf[0];
+	u8 rim;
+
+	if (!test_and_clear_bit(ISAR_RATE_REQ, &ch->is->Flags))
+		return;
+	if (ril > 14) {
+		pr_info("%s: wrong pstrsp ril=%d\n", ch->is->name, ril);
+		ril = 15;
+	}
+	switch (ch->is->buf[1]) {
+	case 0:
+		rim = 0;
+		break;
+	case 0x20:
+		rim = 2;
+		break;
+	case 0x40:
+		rim = 3;
+		break;
+	case 0x41:
+		rim = 4;
+		break;
+	case 0x51:
+		rim = 5;
+		break;
+	case 0x61:
+		rim = 6;
+		break;
+	case 0x71:
+		rim = 7;
+		break;
+	case 0x82:
+		rim = 8;
+		break;
+	case 0x92:
+		rim = 9;
+		break;
+	case 0xa2:
+		rim = 10;
+		break;
+	default:
+		rim = 1;
+		break;
+	}
+	sprintf(ch->conmsg, "%s %s", dmril[ril], dmrim[rim]);
+	pr_debug("%s: pump strsp %s\n", ch->is->name, ch->conmsg);
+}
+
+static void
+isar_pump_statev_modem(struct isar_ch *ch, u8 devt) {
+	u8 dps = SET_DPS(ch->dpath);
+
+	switch (devt) {
+	case PSEV_10MS_TIMER:
+		pr_debug("%s: pump stev TIMER\n", ch->is->name);
+		break;
+	case PSEV_CON_ON:
+		pr_debug("%s: pump stev CONNECT\n", ch->is->name);
+		deliver_status(ch, HW_MOD_CONNECT);
+		break;
+	case PSEV_CON_OFF:
+		pr_debug("%s: pump stev NO CONNECT\n", ch->is->name);
+		send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+		deliver_status(ch, HW_MOD_NOCARR);
+		break;
+	case PSEV_V24_OFF:
+		pr_debug("%s: pump stev V24 OFF\n", ch->is->name);
+		break;
+	case PSEV_CTS_ON:
+		pr_debug("%s: pump stev CTS ON\n", ch->is->name);
+		break;
+	case PSEV_CTS_OFF:
+		pr_debug("%s pump stev CTS OFF\n", ch->is->name);
+		break;
+	case PSEV_DCD_ON:
+		pr_debug("%s: pump stev CARRIER ON\n", ch->is->name);
+		test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags);
+		send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+		break;
+	case PSEV_DCD_OFF:
+		pr_debug("%s: pump stev CARRIER OFF\n", ch->is->name);
+		break;
+	case PSEV_DSR_ON:
+		pr_debug("%s: pump stev DSR ON\n", ch->is->name);
+		break;
+	case PSEV_DSR_OFF:
+		pr_debug("%s: pump stev DSR_OFF\n", ch->is->name);
+		break;
+	case PSEV_REM_RET:
+		pr_debug("%s: pump stev REMOTE RETRAIN\n", ch->is->name);
+		break;
+	case PSEV_REM_REN:
+		pr_debug("%s: pump stev REMOTE RENEGOTIATE\n", ch->is->name);
+		break;
+	case PSEV_GSTN_CLR:
+		pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name);
+		break;
+	default:
+		pr_info("u%s: nknown pump stev %x\n", ch->is->name, devt);
+		break;
+	}
+}
+
+static void
+isar_pump_statev_fax(struct isar_ch *ch, u8 devt) {
+	u8 dps = SET_DPS(ch->dpath);
+	u8 p1;
+
+	switch (devt) {
+	case PSEV_10MS_TIMER:
+		pr_debug("%s: pump stev TIMER\n", ch->is->name);
+		break;
+	case PSEV_RSP_READY:
+		pr_debug("%s: pump stev RSP_READY\n", ch->is->name);
+		ch->state = STFAX_READY;
+		deliver_status(ch, HW_MOD_READY);
+#ifdef AUTOCON
+		if (test_bit(BC_FLG_ORIG, &ch->bch.Flags))
+			isar_pump_cmd(bch, HW_MOD_FRH, 3);
+		else
+			isar_pump_cmd(bch, HW_MOD_FTH, 3);
+#endif
+		break;
+	case PSEV_LINE_TX_H:
+		if (ch->state == STFAX_LINE) {
+			pr_debug("%s: pump stev LINE_TX_H\n", ch->is->name);
+			ch->state = STFAX_CONT;
+			send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+				PCTRL_CMD_CONT, 0, NULL);
+		} else {
+			pr_debug("%s: pump stev LINE_TX_H wrong st %x\n",
+				ch->is->name, ch->state);
+		}
+		break;
+	case PSEV_LINE_RX_H:
+		if (ch->state == STFAX_LINE) {
+			pr_debug("%s: pump stev LINE_RX_H\n", ch->is->name);
+			ch->state = STFAX_CONT;
+			send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+				PCTRL_CMD_CONT, 0, NULL);
+		} else {
+			pr_debug("%s: pump stev LINE_RX_H wrong st %x\n",
+				ch->is->name, ch->state);
+		}
+		break;
+	case PSEV_LINE_TX_B:
+		if (ch->state == STFAX_LINE) {
+			pr_debug("%s: pump stev LINE_TX_B\n", ch->is->name);
+			ch->state = STFAX_CONT;
+			send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+				PCTRL_CMD_CONT, 0, NULL);
+		} else {
+			pr_debug("%s: pump stev LINE_TX_B wrong st %x\n",
+				ch->is->name, ch->state);
+		}
+		break;
+	case PSEV_LINE_RX_B:
+		if (ch->state == STFAX_LINE) {
+			pr_debug("%s: pump stev LINE_RX_B\n", ch->is->name);
+			ch->state = STFAX_CONT;
+			send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+				PCTRL_CMD_CONT, 0, NULL);
+		} else {
+			pr_debug("%s: pump stev LINE_RX_B wrong st %x\n",
+				ch->is->name, ch->state);
+		}
+		break;
+	case PSEV_RSP_CONN:
+		if (ch->state == STFAX_CONT) {
+			pr_debug("%s: pump stev RSP_CONN\n", ch->is->name);
+			ch->state = STFAX_ACTIV;
+			test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags);
+			send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+			if (ch->cmd == PCTRL_CMD_FTH) {
+				int delay = (ch->mod == 3) ? 1000 : 200;
+				/* 1s (200 ms) Flags before data */
+				if (test_and_set_bit(FLG_FTI_RUN,
+				    &ch->bch.Flags))
+					del_timer(&ch->ftimer);
+				ch->ftimer.expires =
+					jiffies + ((delay * HZ)/1000);
+				test_and_set_bit(FLG_LL_CONN,
+					&ch->bch.Flags);
+				add_timer(&ch->ftimer);
+			} else {
+				deliver_status(ch, HW_MOD_CONNECT);
+			}
+		} else {
+			pr_debug("%s: pump stev RSP_CONN wrong st %x\n",
+				ch->is->name, ch->state);
+		}
+		break;
+	case PSEV_FLAGS_DET:
+		pr_debug("%s: pump stev FLAGS_DET\n", ch->is->name);
+		break;
+	case PSEV_RSP_DISC:
+		pr_debug("%s: pump stev RSP_DISC state(%d)\n",
+			ch->is->name, ch->state);
+		if (ch->state == STFAX_ESCAPE) {
+			p1 = 5;
+			switch (ch->newcmd) {
+			case 0:
+				ch->state = STFAX_READY;
+				break;
+			case PCTRL_CMD_FTM:
+				p1 = 2;
+			case PCTRL_CMD_FTH:
+				send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+					PCTRL_CMD_SILON, 1, &p1);
+				ch->state = STFAX_SILDET;
+				break;
+			case PCTRL_CMD_FRH:
+			case PCTRL_CMD_FRM:
+				ch->mod = ch->newmod;
+				p1 = ch->newmod;
+				ch->newmod = 0;
+				ch->cmd = ch->newcmd;
+				ch->newcmd = 0;
+				send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+					ch->cmd, 1, &p1);
+				ch->state = STFAX_LINE;
+				ch->try_mod = 3;
+				break;
+			default:
+				pr_debug("%s: RSP_DISC unknown newcmd %x\n",
+					ch->is->name, ch->newcmd);
+				break;
+			}
+		} else if (ch->state == STFAX_ACTIV) {
+			if (test_and_clear_bit(FLG_LL_OK, &ch->bch.Flags))
+				deliver_status(ch, HW_MOD_OK);
+			else if (ch->cmd == PCTRL_CMD_FRM)
+				deliver_status(ch, HW_MOD_NOCARR);
+			else
+				deliver_status(ch, HW_MOD_FCERROR);
+			ch->state = STFAX_READY;
+		} else if (ch->state != STFAX_SILDET) {
+			/* ignore in STFAX_SILDET */
+			ch->state = STFAX_READY;
+			deliver_status(ch, HW_MOD_FCERROR);
+		}
+		break;
+	case PSEV_RSP_SILDET:
+		pr_debug("%s: pump stev RSP_SILDET\n", ch->is->name);
+		if (ch->state == STFAX_SILDET) {
+			ch->mod = ch->newmod;
+			p1 = ch->newmod;
+			ch->newmod = 0;
+			ch->cmd = ch->newcmd;
+			ch->newcmd = 0;
+			send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+				ch->cmd, 1, &p1);
+			ch->state = STFAX_LINE;
+			ch->try_mod = 3;
+		}
+		break;
+	case PSEV_RSP_SILOFF:
+		pr_debug("%s: pump stev RSP_SILOFF\n", ch->is->name);
+		break;
+	case PSEV_RSP_FCERR:
+		if (ch->state == STFAX_LINE) {
+			pr_debug("%s: pump stev RSP_FCERR try %d\n",
+				ch->is->name, ch->try_mod);
+			if (ch->try_mod--) {
+				send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+					ch->cmd, 1, &ch->mod);
+				break;
+			}
+		}
+		pr_debug("%s: pump stev RSP_FCERR\n", ch->is->name);
+		ch->state = STFAX_ESCAPE;
+		send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+			0, NULL);
+		deliver_status(ch, HW_MOD_FCERROR);
+		break;
+	default:
+		break;
+	}
+}
+
+void
+mISDNisar_irq(struct isar_hw *isar)
+{
+	struct isar_ch *ch;
+
+	get_irq_infos(isar);
+	switch (isar->iis & ISAR_IIS_MSCMSD) {
+	case ISAR_IIS_RDATA:
+		ch = sel_bch_isar(isar, isar->iis >> 6);
+		if (ch)
+			isar_rcv_frame(ch);
+		else {
+			pr_debug("%s: ISAR spurious IIS_RDATA %x/%x/%x\n",
+				isar->name, isar->iis, isar->cmsb,
+				isar->clsb);
+			isar->write_reg(isar->hw, ISAR_IIA, 0);
+		}
+		break;
+	case ISAR_IIS_GSTEV:
+		isar->write_reg(isar->hw, ISAR_IIA, 0);
+		isar->bstat |= isar->cmsb;
+		check_send(isar, isar->cmsb);
+		break;
+	case ISAR_IIS_BSTEV:
+#ifdef ERROR_STATISTIC
+		ch = sel_bch_isar(isar, isar->iis >> 6);
+		if (ch) {
+			if (isar->cmsb == BSTEV_TBO)
+				ch->bch.err_tx++;
+			if (isar->cmsb == BSTEV_RBO)
+				ch->bch.err_rdo++;
+		}
+#endif
+		pr_debug("%s: Buffer STEV dpath%d msb(%x)\n",
+			isar->name, isar->iis>>6, isar->cmsb);
+		isar->write_reg(isar->hw, ISAR_IIA, 0);
+		break;
+	case ISAR_IIS_PSTEV:
+		ch = sel_bch_isar(isar, isar->iis >> 6);
+		if (ch) {
+			rcv_mbox(isar, NULL);
+			if (ch->bch.state == ISDN_P_B_MODEM_ASYNC)
+				isar_pump_statev_modem(ch, isar->cmsb);
+			else if (ch->bch.state == ISDN_P_B_T30_FAX)
+				isar_pump_statev_fax(ch, isar->cmsb);
+			else if (ch->bch.state == ISDN_P_B_RAW) {
+				int	tt;
+				tt = isar->cmsb | 0x30;
+				if (tt == 0x3e)
+					tt = '*';
+				else if (tt == 0x3f)
+					tt = '#';
+				else if (tt > '9')
+					tt += 7;
+				tt |= DTMF_TONE_VAL;
+				_queue_data(&ch->bch.ch, PH_CONTROL_IND,
+					MISDN_ID_ANY, sizeof(tt), &tt,
+					GFP_ATOMIC);
+			} else
+				pr_debug("%s: ISAR IIS_PSTEV pm %d sta %x\n",
+					isar->name, ch->bch.state,
+					isar->cmsb);
+		} else {
+			pr_debug("%s: ISAR spurious IIS_PSTEV %x/%x/%x\n",
+				isar->name, isar->iis, isar->cmsb,
+				isar->clsb);
+			isar->write_reg(isar->hw, ISAR_IIA, 0);
+		}
+		break;
+	case ISAR_IIS_PSTRSP:
+		ch = sel_bch_isar(isar, isar->iis >> 6);
+		if (ch) {
+			rcv_mbox(isar, NULL);
+			isar_pump_status_rsp(ch);
+		} else {
+			pr_debug("%s: ISAR spurious IIS_PSTRSP %x/%x/%x\n",
+				isar->name, isar->iis, isar->cmsb,
+				isar->clsb);
+			isar->write_reg(isar->hw, ISAR_IIA, 0);
+		}
+		break;
+	case ISAR_IIS_DIAG:
+	case ISAR_IIS_BSTRSP:
+	case ISAR_IIS_IOM2RSP:
+		rcv_mbox(isar, NULL);
+		break;
+	case ISAR_IIS_INVMSG:
+		rcv_mbox(isar, NULL);
+		pr_debug("%s: invalid msg his:%x\n", isar->name, isar->cmsb);
+		break;
+	default:
+		rcv_mbox(isar, NULL);
+		pr_debug("%s: unhandled msg iis(%x) ctrl(%x/%x)\n",
+			isar->name, isar->iis, isar->cmsb, isar->clsb);
+		break;
+	}
+}
+EXPORT_SYMBOL(mISDNisar_irq);
+
+static void
+ftimer_handler(unsigned long data)
+{
+	struct isar_ch *ch = (struct isar_ch *)data;
+
+	pr_debug("%s: ftimer flags %lx\n", ch->is->name, ch->bch.Flags);
+	test_and_clear_bit(FLG_FTI_RUN, &ch->bch.Flags);
+	if (test_and_clear_bit(FLG_LL_CONN, &ch->bch.Flags))
+		deliver_status(ch, HW_MOD_CONNECT);
+}
+
+static void
+setup_pump(struct isar_ch *ch) {
+	u8 dps = SET_DPS(ch->dpath);
+	u8 ctrl, param[6];
+
+	switch (ch->bch.state) {
+	case ISDN_P_NONE:
+	case ISDN_P_B_RAW:
+	case ISDN_P_B_HDLC:
+		send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL);
+		break;
+	case ISDN_P_B_L2DTMF:
+		if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) {
+			param[0] = 5; /* TOA 5 db */
+			send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG,
+				PMOD_DTMF_TRANS, 1, param);
+		} else {
+			param[0] = 40; /* REL -46 dbm */
+			send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG,
+				PMOD_DTMF, 1, param);
+		}
+	case ISDN_P_B_MODEM_ASYNC:
+		ctrl = PMOD_DATAMODEM;
+		if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) {
+			ctrl |= PCTRL_ORIG;
+			param[5] = PV32P6_CTN;
+		} else {
+			param[5] = PV32P6_ATN;
+		}
+		param[0] = 6; /* 6 db */
+		param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B |
+			PV32P2_V22C | PV32P2_V21 | PV32P2_BEL;
+		param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B;
+		param[3] = PV32P4_UT144;
+		param[4] = PV32P5_UT144;
+		send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param);
+		break;
+	case ISDN_P_B_T30_FAX:
+		ctrl = PMOD_FAX;
+		if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) {
+			ctrl |= PCTRL_ORIG;
+			param[1] = PFAXP2_CTN;
+		} else {
+			param[1] = PFAXP2_ATN;
+		}
+		param[0] = 6; /* 6 db */
+		send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param);
+		ch->state = STFAX_NULL;
+		ch->newcmd = 0;
+		ch->newmod = 0;
+		test_and_set_bit(FLG_FTI_RUN, &ch->bch.Flags);
+		break;
+	}
+	udelay(1000);
+	send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+static void
+setup_sart(struct isar_ch *ch) {
+	u8 dps = SET_DPS(ch->dpath);
+	u8 ctrl, param[2] = {0, 0};
+
+	switch (ch->bch.state) {
+	case ISDN_P_NONE:
+		send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE,
+			0, NULL);
+		break;
+	case ISDN_P_B_RAW:
+	case ISDN_P_B_L2DTMF:
+		send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_BINARY,
+			2, param);
+		break;
+	case ISDN_P_B_HDLC:
+	case ISDN_P_B_T30_FAX:
+		send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_HDLC,
+			1, param);
+		break;
+	case ISDN_P_B_MODEM_ASYNC:
+		ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
+		param[0] = S_P1_CHS_8;
+		param[1] = S_P2_BFT_DEF;
+		send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, ctrl, 2, param);
+		break;
+	}
+	udelay(1000);
+	send_mbox(ch->is, dps | ISAR_HIS_BSTREQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+static void
+setup_iom2(struct isar_ch *ch) {
+	u8 dps = SET_DPS(ch->dpath);
+	u8 cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD, 0, 0, 0, 0};
+
+	if (ch->bch.nr == 2) {
+		msg[1] = 1;
+		msg[3] = 1;
+	}
+	switch (ch->bch.state) {
+	case ISDN_P_NONE:
+		cmsb = 0;
+		/* dummy slot */
+		msg[1] = ch->dpath + 2;
+		msg[3] = ch->dpath + 2;
+		break;
+	case ISDN_P_B_RAW:
+	case ISDN_P_B_HDLC:
+		break;
+	case ISDN_P_B_MODEM_ASYNC:
+	case ISDN_P_B_T30_FAX:
+		cmsb |= IOM_CTRL_RCV;
+	case ISDN_P_B_L2DTMF:
+		if (test_bit(FLG_DTMFSEND, &ch->bch.Flags))
+			cmsb |= IOM_CTRL_RCV;
+		cmsb |= IOM_CTRL_ALAW;
+		break;
+	}
+	send_mbox(ch->is, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg);
+	udelay(1000);
+	send_mbox(ch->is, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+static int
+modeisar(struct isar_ch *ch, u32 bprotocol)
+{
+	/* Here we are selecting the best datapath for requested protocol */
+	if (ch->bch.state == ISDN_P_NONE) { /* New Setup */
+		switch (bprotocol) {
+		case ISDN_P_NONE: /* init */
+			if (!ch->dpath)
+				/* no init for dpath 0 */
+				return 0;
+			test_and_clear_bit(FLG_HDLC, &ch->bch.Flags);
+			test_and_clear_bit(FLG_TRANSPARENT, &ch->bch.Flags);
+			break;
+		case ISDN_P_B_RAW:
+		case ISDN_P_B_HDLC:
+			/* best is datapath 2 */
+			if (!test_and_set_bit(ISAR_DP2_USE, &ch->is->Flags))
+				ch->dpath = 2;
+			else if (!test_and_set_bit(ISAR_DP1_USE,
+			    &ch->is->Flags))
+				ch->dpath = 1;
+			else {
+				pr_info("modeisar both pathes in use\n");
+				return -EBUSY;
+			}
+			if (bprotocol == ISDN_P_B_HDLC)
+				test_and_set_bit(FLG_HDLC, &ch->bch.Flags);
+			else
+				test_and_set_bit(FLG_TRANSPARENT,
+					&ch->bch.Flags);
+			break;
+		case ISDN_P_B_MODEM_ASYNC:
+		case ISDN_P_B_T30_FAX:
+		case ISDN_P_B_L2DTMF:
+			/* only datapath 1 */
+			if (!test_and_set_bit(ISAR_DP1_USE, &ch->is->Flags))
+				ch->dpath = 1;
+			else {
+				pr_info("%s: ISAR modeisar analog functions"
+					"only with DP1\n", ch->is->name);
+				return -EBUSY;
+			}
+			break;
+		default:
+			pr_info("%s: protocol not known %x\n", ch->is->name,
+				bprotocol);
+			return -ENOPROTOOPT;
+		}
+	}
+	pr_debug("%s: ISAR ch%d dp%d protocol %x->%x\n", ch->is->name,
+		ch->bch.nr, ch->dpath, ch->bch.state, bprotocol);
+	ch->bch.state = bprotocol;
+	setup_pump(ch);
+	setup_iom2(ch);
+	setup_sart(ch);
+	if (ch->bch.state == ISDN_P_NONE) {
+		/* Clear resources */
+		if (ch->dpath == 1)
+			test_and_clear_bit(ISAR_DP1_USE, &ch->is->Flags);
+		else if (ch->dpath == 2)
+			test_and_clear_bit(ISAR_DP2_USE, &ch->is->Flags);
+		ch->dpath = 0;
+		ch->is->ctrl(ch->is->hw, HW_DEACT_IND, ch->bch.nr);
+	} else
+		ch->is->ctrl(ch->is->hw, HW_ACTIVATE_IND, ch->bch.nr);
+	return 0;
+}
+
+static void
+isar_pump_cmd(struct isar_ch *ch, u32 cmd, u8 para)
+{
+	u8 dps = SET_DPS(ch->dpath);
+	u8 ctrl = 0, nom = 0, p1 = 0;
+
+	pr_debug("%s: isar_pump_cmd %x/%x state(%x)\n",
+		ch->is->name, cmd, para, ch->bch.state);
+	switch (cmd) {
+	case HW_MOD_FTM:
+		if (ch->state == STFAX_READY) {
+			p1 = para;
+			ctrl = PCTRL_CMD_FTM;
+			nom = 1;
+			ch->state = STFAX_LINE;
+			ch->cmd = ctrl;
+			ch->mod = para;
+			ch->newmod = 0;
+			ch->newcmd = 0;
+			ch->try_mod = 3;
+		} else if ((ch->state == STFAX_ACTIV) &&
+		    (ch->cmd == PCTRL_CMD_FTM) && (ch->mod == para))
+			deliver_status(ch, HW_MOD_CONNECT);
+		else {
+			ch->newmod = para;
+			ch->newcmd = PCTRL_CMD_FTM;
+			nom = 0;
+			ctrl = PCTRL_CMD_ESC;
+			ch->state = STFAX_ESCAPE;
+		}
+		break;
+	case HW_MOD_FTH:
+		if (ch->state == STFAX_READY) {
+			p1 = para;
+			ctrl = PCTRL_CMD_FTH;
+			nom = 1;
+			ch->state = STFAX_LINE;
+			ch->cmd = ctrl;
+			ch->mod = para;
+			ch->newmod = 0;
+			ch->newcmd = 0;
+			ch->try_mod = 3;
+		} else if ((ch->state == STFAX_ACTIV) &&
+		    (ch->cmd == PCTRL_CMD_FTH) && (ch->mod == para))
+				deliver_status(ch, HW_MOD_CONNECT);
+		else {
+			ch->newmod = para;
+			ch->newcmd = PCTRL_CMD_FTH;
+			nom = 0;
+			ctrl = PCTRL_CMD_ESC;
+			ch->state = STFAX_ESCAPE;
+		}
+		break;
+	case HW_MOD_FRM:
+		if (ch->state == STFAX_READY) {
+			p1 = para;
+			ctrl = PCTRL_CMD_FRM;
+			nom = 1;
+			ch->state = STFAX_LINE;
+			ch->cmd = ctrl;
+			ch->mod = para;
+			ch->newmod = 0;
+			ch->newcmd = 0;
+			ch->try_mod = 3;
+		} else if ((ch->state == STFAX_ACTIV) &&
+		    (ch->cmd == PCTRL_CMD_FRM) && (ch->mod == para))
+			deliver_status(ch, HW_MOD_CONNECT);
+		else {
+			ch->newmod = para;
+			ch->newcmd = PCTRL_CMD_FRM;
+			nom = 0;
+			ctrl = PCTRL_CMD_ESC;
+			ch->state = STFAX_ESCAPE;
+		}
+		break;
+	case HW_MOD_FRH:
+		if (ch->state == STFAX_READY) {
+			p1 = para;
+			ctrl = PCTRL_CMD_FRH;
+			nom = 1;
+			ch->state = STFAX_LINE;
+			ch->cmd = ctrl;
+			ch->mod = para;
+			ch->newmod = 0;
+			ch->newcmd = 0;
+			ch->try_mod = 3;
+		} else if ((ch->state == STFAX_ACTIV) &&
+		    (ch->cmd == PCTRL_CMD_FRH) && (ch->mod == para))
+			deliver_status(ch, HW_MOD_CONNECT);
+		else {
+			ch->newmod = para;
+			ch->newcmd = PCTRL_CMD_FRH;
+			nom = 0;
+			ctrl = PCTRL_CMD_ESC;
+			ch->state = STFAX_ESCAPE;
+		}
+		break;
+	case PCTRL_CMD_TDTMF:
+		p1 = para;
+		nom = 1;
+		ctrl = PCTRL_CMD_TDTMF;
+		break;
+	}
+	if (ctrl)
+		send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1);
+}
+
+static void
+isar_setup(struct isar_hw *isar)
+{
+	u8 msg;
+	int i;
+
+	/* Dpath 1, 2 */
+	msg = 61;
+	for (i = 0; i < 2; i++) {
+		/* Buffer Config */
+		send_mbox(isar, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+			ISAR_HIS_P12CFG, 4, 1, &msg);
+		isar->ch[i].mml = msg;
+		isar->ch[i].bch.state = 0;
+		isar->ch[i].dpath = i + 1;
+		modeisar(&isar->ch[i], ISDN_P_NONE);
+	}
+}
+
+static int
+isar_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct isar_ch *ich = container_of(bch, struct isar_ch, bch);
+	int ret = -EINVAL;
+	struct mISDNhead *hh = mISDN_HEAD_P(skb);
+	u32 id, *val;
+	u_long flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(ich->is->hwlock, flags);
+		ret = bchannel_senddata(bch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			ret = 0;
+			isar_fill_fifo(ich);
+			spin_unlock_irqrestore(ich->is->hwlock, flags);
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(ich->is->hwlock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		spin_lock_irqsave(ich->is->hwlock, flags);
+		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+			ret = modeisar(ich, ch->protocol);
+		else
+			ret = 0;
+		spin_unlock_irqrestore(ich->is->hwlock, flags);
+		if (!ret)
+			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+				NULL, GFP_KERNEL);
+		break;
+	case PH_DEACTIVATE_REQ:
+		spin_lock_irqsave(ich->is->hwlock, flags);
+		mISDN_clear_bchannel(bch);
+		modeisar(ich, ISDN_P_NONE);
+		spin_unlock_irqrestore(ich->is->hwlock, flags);
+		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_KERNEL);
+		ret = 0;
+		break;
+	case PH_CONTROL_REQ:
+		val = (u32 *)skb->data;
+		pr_debug("%s: PH_CONTROL | REQUEST %x/%x\n", ich->is->name,
+			hh->id, *val);
+		if ((hh->id == 0) && ((*val & ~DTMF_TONE_MASK) ==
+		    DTMF_TONE_VAL)) {
+			if (bch->state == ISDN_P_B_L2DTMF) {
+				char tt = *val & DTMF_TONE_MASK;
+
+				if (tt == '*')
+					tt = 0x1e;
+				else if (tt == '#')
+					tt = 0x1f;
+				else if (tt > '9')
+					tt -= 7;
+				tt &= 0x1f;
+				spin_lock_irqsave(ich->is->hwlock, flags);
+				isar_pump_cmd(ich, PCTRL_CMD_TDTMF, tt);
+				spin_unlock_irqrestore(ich->is->hwlock, flags);
+			} else {
+				pr_info("%s: DTMF send wrong protocol %x\n",
+					__func__, bch->state);
+				return -EINVAL;
+			}
+		} else if ((hh->id == HW_MOD_FRM) || (hh->id == HW_MOD_FRH) ||
+		    (hh->id == HW_MOD_FTM) || (hh->id == HW_MOD_FTH)) {
+			for (id = 0; id < FAXMODCNT; id++)
+				if (faxmodulation[id] == *val)
+					break;
+			if ((FAXMODCNT > id) &&
+			    test_bit(FLG_INITIALIZED, &bch->Flags)) {
+				pr_debug("%s: isar: new mod\n", ich->is->name);
+				isar_pump_cmd(ich, hh->id, *val);
+				ret = 0;
+			} else {
+				pr_info("%s: wrong modulation\n",
+					ich->is->name);
+				ret = -EINVAL;
+			}
+		} else if (hh->id == HW_MOD_LASTDATA)
+			test_and_set_bit(FLG_DLEETX, &bch->Flags);
+		else {
+			pr_info("%s: unknown PH_CONTROL_REQ %x\n",
+				ich->is->name, hh->id);
+			ret = -EINVAL;
+		}
+	default:
+		pr_info("%s: %s unknown prim(%x,%x)\n",
+			ich->is->name, __func__, hh->prim, hh->id);
+		ret = -EINVAL;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = 0;
+		break;
+	/* Nothing implemented yet */
+	case MISDN_CTRL_FILL_EMPTY:
+	default:
+		pr_info("%s: unknown Op %x\n", __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+isar_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct isar_ch *ich = container_of(bch, struct isar_ch, bch);
+	int ret = -EINVAL;
+	u_long flags;
+
+	pr_debug("%s: %s cmd:%x %p\n", ich->is->name, __func__, cmd, arg);
+	switch (cmd) {
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+			spin_lock_irqsave(ich->is->hwlock, flags);
+			mISDN_freebchannel(bch);
+			modeisar(ich, ISDN_P_NONE);
+			spin_unlock_irqrestore(ich->is->hwlock, flags);
+		} else {
+			skb_queue_purge(&bch->rqueue);
+			bch->rcount = 0;
+		}
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(ich->is->owner);
+		ret = 0;
+		break;
+	case CONTROL_CHANNEL:
+		ret = channel_bctrl(bch, arg);
+		break;
+	default:
+		pr_info("%s: %s unknown prim(%x)\n",
+			ich->is->name, __func__, cmd);
+	}
+	return ret;
+}
+
+static void
+free_isar(struct isar_hw *isar)
+{
+	modeisar(&isar->ch[0], ISDN_P_NONE);
+	modeisar(&isar->ch[1], ISDN_P_NONE);
+	del_timer(&isar->ch[0].ftimer);
+	del_timer(&isar->ch[1].ftimer);
+	test_and_clear_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags);
+	test_and_clear_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags);
+}
+
+static int
+init_isar(struct isar_hw *isar)
+{
+	int	cnt = 3;
+
+	while (cnt--) {
+		isar->version = ISARVersion(isar);
+		if (isar->ch[0].bch.debug & DEBUG_HW)
+			pr_notice("%s: Testing version %d (%d time)\n",
+				isar->name, isar->version, 3 - cnt);
+		if (isar->version == 1)
+			break;
+		isar->ctrl(isar->hw, HW_RESET_REQ, 0);
+	}
+	if (isar->version != 1)
+		return -EINVAL;
+	isar->ch[0].ftimer.function = &ftimer_handler;
+	isar->ch[0].ftimer.data = (long)&isar->ch[0];
+	init_timer(&isar->ch[0].ftimer);
+	test_and_set_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags);
+	isar->ch[1].ftimer.function = &ftimer_handler;
+	isar->ch[1].ftimer.data = (long)&isar->ch[1];
+	init_timer(&isar->ch[1].ftimer);
+	test_and_set_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags);
+	return 0;
+}
+
+static int
+isar_open(struct isar_hw *isar, struct channel_req *rq)
+{
+	struct bchannel		*bch;
+
+	if (rq->adr.channel > 2)
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	bch = &isar->ch[rq->adr.channel - 1].bch;
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+	bch->ch.protocol = rq->protocol;
+	rq->ch = &bch->ch;
+	return 0;
+}
+
+u32
+mISDNisar_init(struct isar_hw *isar, void *hw)
+{
+	u32 ret, i;
+
+	isar->hw = hw;
+	for (i = 0; i < 2; i++) {
+		isar->ch[i].bch.nr = i + 1;
+		mISDN_initbchannel(&isar->ch[i].bch, MAX_DATA_MEM);
+		isar->ch[i].bch.ch.nr = i + 1;
+		isar->ch[i].bch.ch.send = &isar_l2l1;
+		isar->ch[i].bch.ch.ctrl = isar_bctrl;
+		isar->ch[i].bch.hw = hw;
+		isar->ch[i].is = isar;
+	}
+
+	isar->init = &init_isar;
+	isar->release = &free_isar;
+	isar->firmware = &load_firmware;
+	isar->open = &isar_open;
+
+	ret =	(1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)) |
+		(1 << (ISDN_P_B_L2DTMF & ISDN_P_B_MASK)) |
+		(1 << (ISDN_P_B_MODEM_ASYNC & ISDN_P_B_MASK)) |
+		(1 << (ISDN_P_B_T30_FAX & ISDN_P_B_MASK));
+
+	return ret;
+}
+EXPORT_SYMBOL(mISDNisar_init);
+
+static int isar_mod_init(void)
+{
+	pr_notice("mISDN: ISAR driver Rev. %s\n", ISAR_REV);
+	return 0;
+}
+
+static void isar_mod_cleanup(void)
+{
+	pr_notice("mISDN: ISAR module unloaded\n");
+}
+module_init(isar_mod_init);
+module_exit(isar_mod_cleanup);

+ 1156 - 0
drivers/isdn/hardware/mISDN/netjet.c

@@ -0,0 +1,1156 @@
+/*
+ * NETJet mISDN driver
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include "ipac.h"
+#include "iohelper.h"
+#include "netjet.h"
+#include <linux/isdn/hdlc.h>
+
+#define NETJET_REV	"2.0"
+
+enum nj_types {
+	NETJET_S_TJ300,
+	NETJET_S_TJ320,
+	ENTERNOW__TJ320,
+};
+
+struct tiger_dma {
+	size_t		size;
+	u32		*start;
+	int		idx;
+	u32		dmastart;
+	u32		dmairq;
+	u32		dmaend;
+	u32		dmacur;
+};
+
+struct tiger_hw;
+
+struct tiger_ch {
+	struct bchannel		bch;
+	struct tiger_hw		*nj;
+	int			idx;
+	int			free;
+	int			lastrx;
+	u16			rxstate;
+	u16			txstate;
+	struct isdnhdlc_vars	hsend;
+	struct isdnhdlc_vars	hrecv;
+	u8			*hsbuf;
+	u8			*hrbuf;
+};
+
+#define TX_INIT		0x0001
+#define TX_IDLE		0x0002
+#define TX_RUN		0x0004
+#define TX_UNDERRUN	0x0100
+#define RX_OVERRUN	0x0100
+
+#define LOG_SIZE	64
+
+struct tiger_hw {
+	struct list_head	list;
+	struct pci_dev		*pdev;
+	char			name[MISDN_MAX_IDLEN];
+	enum nj_types		typ;
+	int			irq;
+	u32			irqcnt;
+	u32			base;
+	size_t			base_s;
+	dma_addr_t		dma;
+	void			*dma_p;
+	spinlock_t		lock;	/* lock HW */
+	struct isac_hw		isac;
+	struct tiger_dma	send;
+	struct tiger_dma	recv;
+	struct tiger_ch		bc[2];
+	u8			ctrlreg;
+	u8			dmactrl;
+	u8			auxd;
+	u8			last_is0;
+	u8			irqmask0;
+	char			log[LOG_SIZE];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+static u32 debug;
+static int nj_cnt;
+
+static void
+_set_debug(struct tiger_hw *card)
+{
+	card->isac.dch.debug = debug;
+	card->bc[0].bch.debug = debug;
+	card->bc[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct tiger_hw *card;
+
+	ret = param_set_uint(val, kp);
+	if (!ret) {
+		read_lock(&card_lock);
+		list_for_each_entry(card, &Cards, list)
+			_set_debug(card);
+		read_unlock(&card_lock);
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(NETJET_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Netjet debug mask");
+
+static void
+nj_disable_hwirq(struct tiger_hw *card)
+{
+	outb(0, card->base + NJ_IRQMASK0);
+	outb(0, card->base + NJ_IRQMASK1);
+}
+
+
+static u8
+ReadISAC_nj(void *p, u8 offset)
+{
+	struct tiger_hw *card = p;
+	u8 ret;
+
+	card->auxd &= 0xfc;
+	card->auxd |= (offset >> 4) & 3;
+	outb(card->auxd, card->base + NJ_AUXDATA);
+	ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
+	return ret;
+}
+
+static void
+WriteISAC_nj(void *p, u8 offset, u8 value)
+{
+	struct tiger_hw *card = p;
+
+	card->auxd &= 0xfc;
+	card->auxd |= (offset >> 4) & 3;
+	outb(card->auxd, card->base + NJ_AUXDATA);
+	outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
+}
+
+static void
+ReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
+{
+	struct tiger_hw *card = p;
+
+	card->auxd &= 0xfc;
+	outb(card->auxd, card->base + NJ_AUXDATA);
+	insb(card->base + NJ_ISAC_OFF, data, size);
+}
+
+static void
+WriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
+{
+	struct tiger_hw *card = p;
+
+	card->auxd &= 0xfc;
+	outb(card->auxd, card->base + NJ_AUXDATA);
+	outsb(card->base + NJ_ISAC_OFF, data, size);
+}
+
+static void
+fill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill)
+{
+	struct tiger_hw *card = bc->bch.hw;
+	u32 mask = 0xff, val;
+
+	pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name,
+		bc->bch.nr, fill, cnt, idx, card->send.idx);
+	if (bc->bch.nr & 2) {
+		fill  <<= 8;
+		mask <<= 8;
+	}
+	mask ^= 0xffffffff;
+	while (cnt--) {
+		val = card->send.start[idx];
+		val &= mask;
+		val |= fill;
+		card->send.start[idx++] = val;
+		if (idx >= card->send.size)
+			idx = 0;
+	}
+}
+
+static int
+mode_tiger(struct tiger_ch *bc, u32 protocol)
+{
+	struct tiger_hw *card = bc->bch.hw;
+
+	pr_debug("%s: B%1d protocol %x-->%x\n", card->name,
+		bc->bch.nr, bc->bch.state, protocol);
+	switch (protocol) {
+	case ISDN_P_NONE:
+		if (bc->bch.state == ISDN_P_NONE)
+			break;
+		fill_mem(bc, 0, card->send.size, 0xff);
+		bc->bch.state = protocol;
+		/* only stop dma and interrupts if both channels NULL */
+		if ((card->bc[0].bch.state == ISDN_P_NONE) &&
+		    (card->bc[1].bch.state == ISDN_P_NONE)) {
+			card->dmactrl = 0;
+			outb(card->dmactrl, card->base + NJ_DMACTRL);
+			outb(0, card->base + NJ_IRQMASK0);
+		}
+		test_and_clear_bit(FLG_HDLC, &bc->bch.Flags);
+		test_and_clear_bit(FLG_TRANSPARENT, &bc->bch.Flags);
+		bc->txstate = 0;
+		bc->rxstate = 0;
+		bc->lastrx = -1;
+		break;
+	case ISDN_P_B_RAW:
+		test_and_set_bit(FLG_TRANSPARENT, &bc->bch.Flags);
+		bc->bch.state = protocol;
+		bc->idx = 0;
+		bc->free = card->send.size/2;
+		bc->rxstate = 0;
+		bc->txstate = TX_INIT | TX_IDLE;
+		bc->lastrx = -1;
+		if (!card->dmactrl) {
+			card->dmactrl = 1;
+			outb(card->dmactrl, card->base + NJ_DMACTRL);
+			outb(0x0f, card->base + NJ_IRQMASK0);
+		}
+		break;
+	case ISDN_P_B_HDLC:
+		test_and_set_bit(FLG_HDLC, &bc->bch.Flags);
+		bc->bch.state = protocol;
+		bc->idx = 0;
+		bc->free = card->send.size/2;
+		bc->rxstate = 0;
+		bc->txstate = TX_INIT | TX_IDLE;
+		isdnhdlc_rcv_init(&bc->hrecv, 0);
+		isdnhdlc_out_init(&bc->hsend, 0);
+		bc->lastrx = -1;
+		if (!card->dmactrl) {
+			card->dmactrl = 1;
+			outb(card->dmactrl, card->base + NJ_DMACTRL);
+			outb(0x0f, card->base + NJ_IRQMASK0);
+		}
+		break;
+	default:
+		pr_info("%s: %s protocol %x not handled\n", card->name,
+			__func__, protocol);
+		return -ENOPROTOOPT;
+	}
+	card->send.dmacur = inl(card->base + NJ_DMA_READ_ADR);
+	card->recv.dmacur = inl(card->base + NJ_DMA_WRITE_ADR);
+	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+	card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
+	pr_debug("%s: %s ctrl %x irq  %02x/%02x idx %d/%d\n",
+		card->name, __func__,
+		inb(card->base + NJ_DMACTRL),
+		inb(card->base + NJ_IRQMASK0),
+		inb(card->base + NJ_IRQSTAT0),
+		card->send.idx,
+		card->recv.idx);
+	return 0;
+}
+
+static void
+nj_reset(struct tiger_hw *card)
+{
+	outb(0xff, card->base + NJ_CTRL); /* Reset On */
+	mdelay(1);
+
+	/* now edge triggered for TJ320 GE 13/07/00 */
+	/* see comment in IRQ function */
+	if (card->typ == NETJET_S_TJ320) /* TJ320 */
+		card->ctrlreg = 0x40;  /* Reset Off and status read clear */
+	else
+		card->ctrlreg = 0x00;  /* Reset Off and status read clear */
+	outb(card->ctrlreg, card->base + NJ_CTRL);
+	mdelay(10);
+
+	/* configure AUX pins (all output except ISAC IRQ pin) */
+	card->auxd = 0;
+	card->dmactrl = 0;
+	outb(~NJ_ISACIRQ, card->base + NJ_AUXCTRL);
+	outb(NJ_ISACIRQ,  card->base + NJ_IRQMASK1);
+	outb(card->auxd, card->base + NJ_AUXDATA);
+}
+
+static int
+inittiger(struct tiger_hw *card)
+{
+	int i;
+
+	card->dma_p = pci_alloc_consistent(card->pdev, NJ_DMA_SIZE,
+			&card->dma);
+	if (!card->dma_p) {
+		pr_info("%s: No DMA memory\n", card->name);
+		return -ENOMEM;
+	}
+	if ((u64)card->dma > 0xffffffff) {
+		pr_info("%s: DMA outside 32 bit\n", card->name);
+		return -ENOMEM;
+	}
+	for (i = 0; i < 2; i++) {
+		card->bc[i].hsbuf = kmalloc(NJ_DMA_TXSIZE, GFP_KERNEL);
+		if (!card->bc[i].hsbuf) {
+			pr_info("%s: no B%d send buffer\n", card->name, i + 1);
+			return -ENOMEM;
+		}
+		card->bc[i].hrbuf = kmalloc(NJ_DMA_RXSIZE, GFP_KERNEL);
+		if (!card->bc[i].hrbuf) {
+			pr_info("%s: no B%d recv buffer\n", card->name, i + 1);
+			return -ENOMEM;
+		}
+	}
+	memset(card->dma_p, 0xff, NJ_DMA_SIZE);
+
+	card->send.start = card->dma_p;
+	card->send.dmastart = (u32)card->dma;
+	card->send.dmaend = card->send.dmastart +
+		(4 * (NJ_DMA_TXSIZE - 1));
+	card->send.dmairq = card->send.dmastart +
+		(4 * ((NJ_DMA_TXSIZE / 2) - 1));
+	card->send.size = NJ_DMA_TXSIZE;
+
+	if (debug & DEBUG_HW)
+		pr_notice("%s: send buffer phy %#x - %#x - %#x  virt %p"
+			" size %zu u32\n", card->name,
+			card->send.dmastart, card->send.dmairq,
+			card->send.dmaend, card->send.start, card->send.size);
+
+	outl(card->send.dmastart, card->base + NJ_DMA_READ_START);
+	outl(card->send.dmairq, card->base + NJ_DMA_READ_IRQ);
+	outl(card->send.dmaend, card->base + NJ_DMA_READ_END);
+
+	card->recv.start = card->dma_p + (NJ_DMA_SIZE / 2);
+	card->recv.dmastart = (u32)card->dma  + (NJ_DMA_SIZE / 2);
+	card->recv.dmaend = card->recv.dmastart +
+		(4 * (NJ_DMA_RXSIZE - 1));
+	card->recv.dmairq = card->recv.dmastart +
+		(4 * ((NJ_DMA_RXSIZE / 2) - 1));
+	card->recv.size = NJ_DMA_RXSIZE;
+
+	if (debug & DEBUG_HW)
+		pr_notice("%s: recv buffer phy %#x - %#x - %#x  virt %p"
+			" size %zu u32\n", card->name,
+			card->recv.dmastart, card->recv.dmairq,
+			card->recv.dmaend, card->recv.start, card->recv.size);
+
+	outl(card->recv.dmastart, card->base + NJ_DMA_WRITE_START);
+	outl(card->recv.dmairq, card->base + NJ_DMA_WRITE_IRQ);
+	outl(card->recv.dmaend, card->base + NJ_DMA_WRITE_END);
+	return 0;
+}
+
+static void
+read_dma(struct tiger_ch *bc, u32 idx, int cnt)
+{
+	struct tiger_hw *card = bc->bch.hw;
+	int i, stat;
+	u32 val;
+	u8 *p, *pn;
+
+	if (bc->lastrx == idx) {
+		bc->rxstate |= RX_OVERRUN;
+		pr_info("%s: B%1d overrun at idx %d\n", card->name,
+			bc->bch.nr, idx);
+	}
+	bc->lastrx = idx;
+	if (!bc->bch.rx_skb) {
+		bc->bch.rx_skb = mI_alloc_skb(bc->bch.maxlen, GFP_ATOMIC);
+		if (!bc->bch.rx_skb) {
+			pr_info("%s: B%1d receive out of memory\n",
+				card->name, bc->bch.nr);
+			return;
+		}
+	}
+
+	if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) {
+		if ((bc->bch.rx_skb->len + cnt) > bc->bch.maxlen) {
+			pr_debug("%s: B%1d overrun %d\n", card->name,
+				bc->bch.nr, bc->bch.rx_skb->len + cnt);
+			skb_trim(bc->bch.rx_skb, 0);
+			return;
+		}
+		p = skb_put(bc->bch.rx_skb, cnt);
+	} else
+		p = bc->hrbuf;
+
+	for (i = 0; i < cnt; i++) {
+		val = card->recv.start[idx++];
+		if (bc->bch.nr & 2)
+			val >>= 8;
+		if (idx >= card->recv.size)
+			idx = 0;
+		p[i] = val & 0xff;
+	}
+	pn = bc->hrbuf;
+next_frame:
+	if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+		stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i,
+			bc->bch.rx_skb->data, bc->bch.maxlen);
+		if (stat > 0) /* valid frame received */ 
+			p = skb_put(bc->bch.rx_skb, stat);
+		else if (stat == -HDLC_CRC_ERROR)
+			pr_info("%s: B%1d receive frame CRC error\n",
+				card->name, bc->bch.nr);
+		else if (stat == -HDLC_FRAMING_ERROR)
+			pr_info("%s: B%1d receive framing error\n",
+				card->name, bc->bch.nr);
+		else if (stat == -HDLC_LENGTH_ERROR)
+			pr_info("%s: B%1d receive frame too long (> %d)\n",
+				card->name, bc->bch.nr, bc->bch.maxlen);
+	} else
+		stat = cnt;	
+
+	if (stat > 0) {
+		if (debug & DEBUG_HW_BFIFO) {
+			snprintf(card->log, LOG_SIZE, "B%1d-recv %s %d ",
+				bc->bch.nr, card->name, stat);
+			print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET,
+				p, stat);
+		}
+		recv_Bchannel(&bc->bch, 0);
+	}
+	if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+		pn += i;
+		cnt -= i;
+		if (!bc->bch.rx_skb) {
+			bc->bch.rx_skb = mI_alloc_skb(bc->bch.maxlen,
+				GFP_ATOMIC);
+			if (!bc->bch.rx_skb) {
+				pr_info("%s: B%1d receive out of memory\n",
+					card->name, bc->bch.nr);
+				return;
+			}
+		}
+		if (cnt > 0)
+			goto next_frame;
+	}
+}
+
+static void
+recv_tiger(struct tiger_hw *card, u8 irq_stat)
+{
+	u32 idx;
+	int cnt = card->recv.size / 2;
+
+	/* Note receive is via the WRITE DMA channel */
+	card->last_is0 &= ~NJ_IRQM0_WR_MASK;
+	card->last_is0 |= (irq_stat & NJ_IRQM0_WR_MASK);
+
+	if (irq_stat & NJ_IRQM0_WR_END)
+		idx = cnt - 1;
+	else
+		idx = card->recv.size - 1;
+
+	if (test_bit(FLG_ACTIVE, &card->bc[0].bch.Flags))
+		read_dma(&card->bc[0], idx, cnt);
+	if (test_bit(FLG_ACTIVE, &card->bc[1].bch.Flags))
+		read_dma(&card->bc[1], idx, cnt);
+}
+
+/* sync with current DMA address at start or after exception */
+static void
+resync(struct tiger_ch *bc, struct tiger_hw *card)
+{
+	card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
+	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+	if (bc->free > card->send.size / 2)
+		bc->free = card->send.size / 2;
+	/* currently we simple sync to the next complete free area
+	 * this hast the advantage that we have always maximum time to
+	 * handle TX irq
+	 */
+	if (card->send.idx < ((card->send.size / 2) - 1))
+		bc->idx = (card->recv.size / 2) - 1;
+	else
+		bc->idx = card->recv.size - 1;
+	bc->txstate = TX_RUN;
+	pr_debug("%s: %s B%1d free %d idx %d/%d\n", card->name,
+		__func__, bc->bch.nr, bc->free, bc->idx, card->send.idx);
+}
+
+static int bc_next_frame(struct tiger_ch *);
+
+static void
+fill_hdlc_flag(struct tiger_ch *bc)
+{
+	struct tiger_hw *card = bc->bch.hw;
+	int count, i;
+	u32 m, v;
+	u8  *p;
+
+	if (bc->free == 0)
+		return;
+	pr_debug("%s: %s B%1d %d state %x idx %d/%d\n", card->name,
+		__func__, bc->bch.nr, bc->free, bc->txstate,
+		bc->idx, card->send.idx);
+	if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
+		resync(bc, card);
+	count = isdnhdlc_encode(&bc->hsend, NULL, 0, &i,
+			bc->hsbuf, bc->free);
+	pr_debug("%s: B%1d hdlc encoded %d flags\n", card->name,
+			bc->bch.nr, count);
+	bc->free -= count;
+	p = bc->hsbuf;
+	m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
+	for (i = 0; i < count; i++) {
+		if (bc->idx >= card->send.size)
+			bc->idx = 0;
+		v = card->send.start[bc->idx];
+		v &= m;
+		v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8;
+		card->send.start[bc->idx++] = v;
+	}
+	if (debug & DEBUG_HW_BFIFO) {
+		snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
+			bc->bch.nr, card->name, count);
+		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
+	}
+}
+
+static void
+fill_dma(struct tiger_ch *bc)
+{
+	struct tiger_hw *card = bc->bch.hw;
+	int count, i;
+	u32 m, v;
+	u8  *p;
+
+	if (bc->free == 0)
+		return;
+	count = bc->bch.tx_skb->len - bc->bch.tx_idx;
+	if (count <= 0)
+		return;
+	pr_debug("%s: %s B%1d %d/%d/%d/%d state %x idx %d/%d\n", card->name,
+		__func__, bc->bch.nr, count, bc->free, bc->bch.tx_idx,
+		bc->bch.tx_skb->len, bc->txstate, bc->idx, card->send.idx);
+	if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
+		resync(bc, card);
+	p = bc->bch.tx_skb->data + bc->bch.tx_idx;
+	if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+		count = isdnhdlc_encode(&bc->hsend, p, count, &i,
+			bc->hsbuf, bc->free);
+		pr_debug("%s: B%1d hdlc encoded %d in %d\n", card->name,
+			bc->bch.nr, i, count);
+		bc->bch.tx_idx += i;
+		bc->free -= count;
+		p = bc->hsbuf;
+	} else {
+		if (count > bc->free)
+			count = bc->free;
+		bc->bch.tx_idx += count;
+		bc->free -= count;
+	}
+	m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
+	for (i = 0; i < count; i++) {
+		if (bc->idx >= card->send.size)
+			bc->idx = 0;
+		v = card->send.start[bc->idx];
+		v &= m;
+		v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8;
+		card->send.start[bc->idx++] = v;
+	}
+	if (debug & DEBUG_HW_BFIFO) {
+		snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
+			bc->bch.nr, card->name, count);
+		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
+	}
+	if (bc->free)
+		bc_next_frame(bc);
+}
+
+
+static int
+bc_next_frame(struct tiger_ch *bc)
+{
+	if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len)
+		fill_dma(bc);
+	else {
+		if (bc->bch.tx_skb) {
+			/* send confirm, on trans, free on hdlc. */
+			if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags))
+				confirm_Bsend(&bc->bch);
+			dev_kfree_skb(bc->bch.tx_skb);
+		}
+		if (get_next_bframe(&bc->bch))
+			fill_dma(bc);
+		else
+			return 0;
+	}
+	return 1;
+}
+
+static void
+send_tiger_bc(struct tiger_hw *card, struct tiger_ch *bc)
+{
+	int ret;
+
+	bc->free += card->send.size / 2;
+	if (bc->free >= card->send.size) {
+		if (!(bc->txstate & (TX_UNDERRUN | TX_INIT))) {
+			pr_info("%s: B%1d TX underrun state %x\n", card->name,
+				bc->bch.nr, bc->txstate);
+			bc->txstate |= TX_UNDERRUN;
+		}
+		bc->free = card->send.size;
+	}
+	ret = bc_next_frame(bc);
+	if (!ret) {
+		if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+			fill_hdlc_flag(bc);
+			return;
+		}
+		pr_debug("%s: B%1d TX no data free %d idx %d/%d\n", card->name,
+			bc->bch.nr, bc->free, bc->idx, card->send.idx);
+		if (!(bc->txstate & (TX_IDLE | TX_INIT))) {
+			fill_mem(bc, bc->idx, bc->free, 0xff);
+			if (bc->free == card->send.size)
+				bc->txstate |= TX_IDLE;
+		}
+	}
+}
+
+static void
+send_tiger(struct tiger_hw *card, u8 irq_stat)
+{
+	int i;
+
+	/* Note send is via the READ DMA channel */
+	if ((irq_stat & card->last_is0) & NJ_IRQM0_RD_MASK) {
+		pr_info("%s: tiger warn write double dma %x/%x\n",
+			card->name, irq_stat, card->last_is0);
+		return;
+	} else {
+		card->last_is0 &= ~NJ_IRQM0_RD_MASK;
+		card->last_is0 |= (irq_stat & NJ_IRQM0_RD_MASK);
+	}
+	for (i = 0; i < 2; i++) {
+		if (test_bit(FLG_ACTIVE, &card->bc[i].bch.Flags))
+			send_tiger_bc(card, &card->bc[i]);
+	}
+}
+
+static irqreturn_t
+nj_irq(int intno, void *dev_id)
+{
+	struct tiger_hw *card = dev_id;
+	u8 val, s1val, s0val;
+
+	spin_lock(&card->lock);
+	s0val = inb(card->base | NJ_IRQSTAT0);
+	s1val = inb(card->base | NJ_IRQSTAT1);
+	if ((s1val & NJ_ISACIRQ) && (s0val == 0)) {
+		/* shared IRQ */
+		spin_unlock(&card->lock);
+		return IRQ_NONE;
+	}
+	pr_debug("%s: IRQSTAT0 %02x IRQSTAT1 %02x\n", card->name, s0val, s1val);
+	card->irqcnt++;
+	if (!(s1val & NJ_ISACIRQ)) {
+		val = ReadISAC_nj(card, ISAC_ISTA);
+		if (val)
+			mISDNisac_irq(&card->isac, val);
+	}
+
+	if (s0val)
+		/* write to clear */
+		outb(s0val, card->base | NJ_IRQSTAT0);
+	else
+		goto end;
+	s1val = s0val;
+	/* set bits in sval to indicate which page is free */
+	card->recv.dmacur = inl(card->base | NJ_DMA_WRITE_ADR);
+	card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
+	if (card->recv.dmacur < card->recv.dmairq)
+		s0val = 0x08;	/* the 2nd write area is free */
+	else
+		s0val = 0x04;	/* the 1st write area is free */
+
+	card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
+	card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+	if (card->send.dmacur < card->send.dmairq)
+		s0val |= 0x02;	/* the 2nd read area is free */
+	else
+		s0val |= 0x01;	/* the 1st read area is free */
+
+	pr_debug("%s: DMA Status %02x/%02x/%02x %d/%d\n", card->name,
+		s1val, s0val, card->last_is0,
+		card->recv.idx, card->send.idx);
+	/* test if we have a DMA interrupt */
+	if (s0val != card->last_is0) {
+		if ((s0val & NJ_IRQM0_RD_MASK) !=
+		    (card->last_is0 & NJ_IRQM0_RD_MASK))
+			/* got a write dma int */
+			send_tiger(card, s0val);
+		if ((s0val & NJ_IRQM0_WR_MASK) !=
+		    (card->last_is0 & NJ_IRQM0_WR_MASK))
+			/* got a read dma int */
+			recv_tiger(card, s0val);
+	}
+end:
+	spin_unlock(&card->lock);
+	return IRQ_HANDLED;
+}
+
+static int
+nj_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	int ret = -EINVAL;
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
+	struct tiger_hw *card = bch->hw;
+	struct mISDNhead *hh = mISDN_HEAD_P(skb);
+	u32 id;
+	u_long flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		ret = bchannel_senddata(bch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			fill_dma(bc);
+			ret = 0;
+			spin_unlock_irqrestore(&card->lock, flags);
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(&card->lock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+			ret = mode_tiger(bc, ch->protocol);
+		else
+			ret = 0;
+		spin_unlock_irqrestore(&card->lock, flags);
+		if (!ret)
+			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+				NULL, GFP_KERNEL);
+		break;
+	case PH_DEACTIVATE_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		mISDN_clear_bchannel(bch);
+		mode_tiger(bc, ISDN_P_NONE);
+		spin_unlock_irqrestore(&card->lock, flags);
+		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_KERNEL);
+		ret = 0;
+		break;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+channel_bctrl(struct tiger_ch *bc, struct mISDN_ctrl_req *cq)
+{
+	int ret = 0;
+	struct tiger_hw *card  = bc->bch.hw;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = 0;
+		break;
+	/* Nothing implemented yet */
+	case MISDN_CTRL_FILL_EMPTY:
+	default:
+		pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+nj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
+	struct tiger_hw *card  = bch->hw;
+	int ret = -EINVAL;
+	u_long flags;
+
+	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
+	switch (cmd) {
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+			spin_lock_irqsave(&card->lock, flags);
+			mISDN_freebchannel(bch);
+			test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+			test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+			mode_tiger(bc, ISDN_P_NONE);
+			spin_unlock_irqrestore(&card->lock, flags);
+		}
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(THIS_MODULE);
+		ret = 0;
+		break;
+	case CONTROL_CHANNEL:
+		ret = channel_bctrl(bc, arg);
+		break;
+	default:
+		pr_info("%s: %s unknown prim(%x)\n", card->name, __func__, cmd);
+	}
+	return ret;
+}
+
+static int
+channel_ctrl(struct tiger_hw *card, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_LOOP;
+		break;
+	case MISDN_CTRL_LOOP:
+		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+		if (cq->channel < 0 || cq->channel > 3) {
+			ret = -EINVAL;
+			break;
+		}
+		ret = card->isac.ctrl(&card->isac, HW_TESTLOOP, cq->channel);
+		break;
+	default:
+		pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+open_bchannel(struct tiger_hw *card, struct channel_req *rq)
+{
+	struct bchannel *bch;
+
+	if (rq->adr.channel > 2)
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	bch = &card->bc[rq->adr.channel - 1].bch;
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+	bch->ch.protocol = rq->protocol;
+	rq->ch = &bch->ch;
+	return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+nj_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct tiger_hw	*card = dch->hw;
+	struct channel_req	*rq;
+	int			err = 0;
+
+	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		if (rq->protocol == ISDN_P_TE_S0)
+			err = card->isac.open(&card->isac, rq);
+		else
+			err = open_bchannel(card, rq);
+		if (err)
+			break;
+		if (!try_module_get(THIS_MODULE))
+			pr_info("%s: cannot get module\n", card->name);
+		break;
+	case CLOSE_CHANNEL:
+		pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id,
+			__builtin_return_address(0));
+		module_put(THIS_MODULE);
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_ctrl(card, arg);
+		break;
+	default:
+		pr_debug("%s: %s unknown command %x\n",
+			card->name, __func__, cmd);
+		return -EINVAL;
+	}
+	return err;
+}
+
+static int
+nj_init_card(struct tiger_hw *card)
+{
+	u_long flags;
+	int ret;
+
+	spin_lock_irqsave(&card->lock, flags);
+	nj_disable_hwirq(card);
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	card->irq = card->pdev->irq;
+	if (request_irq(card->irq, nj_irq, IRQF_SHARED, card->name, card)) {
+		pr_info("%s: couldn't get interrupt %d\n",
+			card->name, card->irq);
+		card->irq = -1;
+		return -EIO;
+	}
+
+	spin_lock_irqsave(&card->lock, flags);
+	nj_reset(card);
+	ret = card->isac.init(&card->isac);
+	if (ret)
+		goto error;
+	ret = inittiger(card);
+	if (ret)
+		goto error;
+	mode_tiger(&card->bc[0], ISDN_P_NONE);
+	mode_tiger(&card->bc[1], ISDN_P_NONE);
+error:
+	spin_unlock_irqrestore(&card->lock, flags);
+	return ret;
+}
+
+
+static void
+nj_release(struct tiger_hw *card)
+{
+	u_long flags;
+	int i;
+
+	if (card->base_s) {
+		spin_lock_irqsave(&card->lock, flags);
+		nj_disable_hwirq(card);
+		mode_tiger(&card->bc[0], ISDN_P_NONE);
+		mode_tiger(&card->bc[1], ISDN_P_NONE);
+		card->isac.release(&card->isac);
+		spin_unlock_irqrestore(&card->lock, flags);
+		release_region(card->base, card->base_s);
+		card->base_s = 0;
+	}
+	if (card->irq > 0)
+		free_irq(card->irq, card);
+	if (card->isac.dch.dev.dev.class)
+		mISDN_unregister_device(&card->isac.dch.dev);
+	
+	for (i = 0; i < 2; i++) {
+		mISDN_freebchannel(&card->bc[i].bch);
+		kfree(card->bc[i].hsbuf);
+		kfree(card->bc[i].hrbuf);
+	}
+	if (card->dma_p)
+		pci_free_consistent(card->pdev, NJ_DMA_SIZE,
+			card->dma_p, card->dma);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	pci_clear_master(card->pdev);
+	pci_disable_device(card->pdev);
+	pci_set_drvdata(card->pdev, NULL);
+	kfree(card);
+}
+
+
+static int
+nj_setup(struct tiger_hw *card)
+{
+	card->base = pci_resource_start(card->pdev, 0);
+	card->base_s = pci_resource_len(card->pdev, 0);
+	if (!request_region(card->base, card->base_s, card->name)) {
+		pr_info("%s: NETjet config port %#x-%#x already in use\n",
+			card->name, card->base,
+			(u32)(card->base + card->base_s - 1));
+		card->base_s = 0;
+		return -EIO;
+	}
+	ASSIGN_FUNC(nj, ISAC, card->isac);
+	return 0;
+}
+
+
+static int __devinit
+setup_instance(struct tiger_hw *card)
+{
+	int i, err;
+	u_long flags;
+
+	snprintf(card->name, MISDN_MAX_IDLEN - 1, "netjet.%d", nj_cnt + 1);
+	write_lock_irqsave(&card_lock, flags);
+	list_add_tail(&card->list, &Cards);
+	write_unlock_irqrestore(&card_lock, flags);
+
+	_set_debug(card);
+	card->isac.name = card->name;
+	spin_lock_init(&card->lock);
+	card->isac.hwlock = &card->lock;
+	mISDNisac_init(&card->isac, card);
+
+	card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	card->isac.dch.dev.D.ctrl = nj_dctrl;
+	for (i = 0; i < 2; i++) {
+		card->bc[i].bch.nr = i + 1;
+		set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+		mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM);
+		card->bc[i].bch.hw = card;
+		card->bc[i].bch.ch.send = nj_l2l1B;
+		card->bc[i].bch.ch.ctrl = nj_bctrl;
+		card->bc[i].bch.ch.nr = i + 1;
+		list_add(&card->bc[i].bch.ch.list,
+			&card->isac.dch.dev.bchannels);
+		card->bc[i].bch.hw = card;
+	}
+	err = nj_setup(card);
+	if (err)
+		goto error;
+	err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
+		card->name);
+	if (err)
+		goto error;
+	err = nj_init_card(card);
+	if (!err)  {
+		nj_cnt++;
+		pr_notice("Netjet %d cards installed\n", nj_cnt);
+		return 0;
+	}
+error:
+	nj_release(card);
+	return err;
+}
+
+static int __devinit
+nj_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int err = -ENOMEM;
+	int cfg;
+	struct tiger_hw *card;
+
+	if (pdev->subsystem_vendor == 0x8086 &&
+	    pdev->subsystem_device == 0x0003) {
+		pr_notice("Netjet: Digium X100P/X101P not handled\n");
+		return -ENODEV;
+	}
+
+	if (pdev->subsystem_vendor == 0x55 &&
+	    pdev->subsystem_device == 0x02) {
+		pr_notice("Netjet: Enter!Now not handled yet\n");
+		return -ENODEV;
+	}
+
+	card = kzalloc(sizeof(struct tiger_hw), GFP_ATOMIC);
+	if (!card) {
+		pr_info("No kmem for Netjet\n");
+		return err;
+	}
+
+	card->pdev = pdev;
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		kfree(card);
+		return err;
+	}
+
+	printk(KERN_INFO "nj_probe(mISDN): found adapter at %s\n",
+		pci_name(pdev));
+
+	pci_set_master(pdev);
+
+	/* the TJ300 and TJ320 must be detected, the IRQ handling is different
+	 * unfortunately the chips use the same device ID, but the TJ320 has
+	 * the bit20 in status PCI cfg register set
+	 */
+	pci_read_config_dword(pdev, 0x04, &cfg);
+	if (cfg & 0x00100000)
+		card->typ = NETJET_S_TJ320;
+	else
+		card->typ = NETJET_S_TJ300;
+
+	card->base = pci_resource_start(pdev, 0);
+	card->irq = pdev->irq;
+	pci_set_drvdata(pdev, card);
+	err = setup_instance(card);
+	if (err)
+		pci_set_drvdata(pdev, NULL);
+
+	return err;
+}
+
+
+static void __devexit nj_remove(struct pci_dev *pdev)
+{
+	struct tiger_hw *card = pci_get_drvdata(pdev);
+
+	if (card)
+		nj_release(card);
+	else
+		pr_info("%s drvdata already removed\n", __func__);
+}
+
+/* We cannot select cards with PCI_SUB... IDs, since here are cards with
+ * SUB IDs set to PCI_ANY_ID, so we need to match all and reject
+ * known other cards which not work with this driver - see probe function */
+static struct pci_device_id nj_pci_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, nj_pci_ids);
+
+static struct pci_driver nj_driver = {
+	.name = "netjet",
+	.probe = nj_probe,
+	.remove = __devexit_p(nj_remove),
+	.id_table = nj_pci_ids,
+};
+
+static int __init nj_init(void)
+{
+	int err;
+
+	pr_notice("Netjet PCI driver Rev. %s\n", NETJET_REV);
+	err = pci_register_driver(&nj_driver);
+	return err;
+}
+
+static void __exit nj_cleanup(void)
+{
+	pci_unregister_driver(&nj_driver);
+}
+
+module_init(nj_init);
+module_exit(nj_cleanup);

+ 58 - 0
drivers/isdn/hardware/mISDN/netjet.h

@@ -0,0 +1,58 @@
+/*
+ * NETjet common header file
+ *
+ * Author	Karsten Keil
+ *              based on work of Matt Henderson and Daniel Potts,
+ *              Traverse Technologies P/L www.traverse.com.au
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define NJ_CTRL			0x00
+#define NJ_DMACTRL		0x01
+#define NJ_AUXCTRL		0x02
+#define NJ_AUXDATA		0x03
+#define NJ_IRQMASK0		0x04
+#define NJ_IRQMASK1		0x05
+#define NJ_IRQSTAT0		0x06
+#define NJ_IRQSTAT1		0x07
+#define NJ_DMA_READ_START	0x08
+#define NJ_DMA_READ_IRQ		0x0c
+#define NJ_DMA_READ_END		0x10
+#define NJ_DMA_READ_ADR		0x14
+#define NJ_DMA_WRITE_START	0x18
+#define NJ_DMA_WRITE_IRQ	0x1c
+#define NJ_DMA_WRITE_END	0x20
+#define NJ_DMA_WRITE_ADR	0x24
+#define NJ_PULSE_CNT		0x28
+
+#define NJ_ISAC_OFF		0xc0
+#define NJ_ISACIRQ		0x10
+
+#define NJ_IRQM0_RD_MASK	0x03
+#define NJ_IRQM0_RD_IRQ		0x01
+#define NJ_IRQM0_RD_END		0x02
+#define NJ_IRQM0_WR_MASK	0x0c
+#define NJ_IRQM0_WR_IRQ		0x04
+#define NJ_IRQM0_WR_END		0x08
+
+/* one page here is no need to be smaller */
+#define NJ_DMA_SIZE		4096
+/* 2 * 64 byte is a compromise between IRQ count and latency */
+#define NJ_DMA_RXSIZE		128  /* 2 * 64 */
+#define NJ_DMA_TXSIZE		128  /* 2 * 64 */
+

+ 526 - 0
drivers/isdn/hardware/mISDN/speedfax.c

@@ -0,0 +1,526 @@
+/*
+ * speedfax.c	low level stuff for Sedlbauer Speedfax+ cards
+ *		based on the ISAR DSP
+ *		Thanks to Sedlbauer AG for informations and HW
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <linux/firmware.h>
+#include "ipac.h"
+#include "isar.h"
+
+#define SPEEDFAX_REV	"2.0"
+
+#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID	0x51
+#define PCI_SUBVENDOR_SPEEDFAX_PCI	0x54
+#define PCI_SUB_ID_SEDLBAUER		0x01
+
+#define SFAX_PCI_ADDR		0xc8
+#define SFAX_PCI_ISAC		0xd0
+#define SFAX_PCI_ISAR		0xe0
+
+/* TIGER 100 Registers */
+
+#define TIGER_RESET_ADDR	0x00
+#define TIGER_EXTERN_RESET_ON	0x01
+#define TIGER_EXTERN_RESET_OFF	0x00
+#define TIGER_AUX_CTRL		0x02
+#define TIGER_AUX_DATA		0x03
+#define TIGER_AUX_IRQMASK	0x05
+#define TIGER_AUX_STATUS	0x07
+
+/* Tiger AUX BITs */
+#define SFAX_AUX_IOMASK		0xdd	/* 1 and 5 are inputs */
+#define SFAX_ISAR_RESET_BIT_OFF 0x00
+#define SFAX_ISAR_RESET_BIT_ON	0x01
+#define SFAX_TIGER_IRQ_BIT	0x02
+#define SFAX_LED1_BIT		0x08
+#define SFAX_LED2_BIT		0x10
+
+#define SFAX_PCI_RESET_ON	(SFAX_ISAR_RESET_BIT_ON)
+#define SFAX_PCI_RESET_OFF	(SFAX_LED1_BIT | SFAX_LED2_BIT)
+
+static int sfax_cnt;
+static u32 debug;
+static u32 irqloops = 4;
+
+struct sfax_hw {
+	struct list_head	list;
+	struct pci_dev		*pdev;
+	char			name[MISDN_MAX_IDLEN];
+	u32			irq;
+	u32			irqcnt;
+	u32			cfg;
+	struct _ioport		p_isac;
+	struct _ioport		p_isar;
+	u8			aux_data;
+	spinlock_t		lock;	/* HW access lock */
+	struct isac_hw		isac;
+	struct isar_hw		isar;
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct sfax_hw *card)
+{
+	card->isac.dch.debug = debug;
+	card->isar.ch[0].bch.debug = debug;
+	card->isar.ch[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct sfax_hw *card;
+
+	ret = param_set_uint(val, kp);
+	if (!ret) {
+		read_lock(&card_lock);
+		list_for_each_entry(card, &Cards, list)
+			_set_debug(card);
+		read_unlock(&card_lock);
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(SPEEDFAX_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Speedfax debug mask");
+module_param(irqloops, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)");
+
+IOFUNC_IND(ISAC, sfax_hw, p_isac)
+IOFUNC_IND(ISAR, sfax_hw, p_isar)
+
+static irqreturn_t
+speedfax_irq(int intno, void *dev_id)
+{
+	struct sfax_hw	*sf = dev_id;
+	u8 val;
+	int cnt = irqloops;
+
+	spin_lock(&sf->lock);
+	val = inb(sf->cfg + TIGER_AUX_STATUS);
+	if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */
+		spin_unlock(&sf->lock);
+		return IRQ_NONE; /* shared */
+	}
+	sf->irqcnt++;
+	val = ReadISAR_IND(sf, ISAR_IRQBIT);
+Start_ISAR:
+	if (val & ISAR_IRQSTA)
+		mISDNisar_irq(&sf->isar);
+	val = ReadISAC_IND(sf, ISAC_ISTA);
+	if (val)
+		mISDNisac_irq(&sf->isac, val);
+	val = ReadISAR_IND(sf, ISAR_IRQBIT);
+	if ((val & ISAR_IRQSTA) && cnt--)
+		goto Start_ISAR;
+	if (cnt < irqloops)
+		pr_debug("%s: %d irqloops cpu%d\n", sf->name,
+			irqloops - cnt, smp_processor_id());
+	if (irqloops && !cnt)
+		pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name,
+			irqloops, smp_processor_id());
+	spin_unlock(&sf->lock);
+	return IRQ_HANDLED;
+}
+
+static void
+enable_hwirq(struct sfax_hw *sf)
+{
+	WriteISAC_IND(sf, ISAC_MASK, 0);
+	WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK);
+	outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK);
+}
+
+static void
+disable_hwirq(struct sfax_hw *sf)
+{
+	WriteISAC_IND(sf, ISAC_MASK, 0xFF);
+	WriteISAR_IND(sf, ISAR_IRQBIT, 0);
+	outb(0, sf->cfg + TIGER_AUX_IRQMASK);
+}
+
+static void
+reset_speedfax(struct sfax_hw *sf)
+{
+
+	pr_debug("%s: resetting card\n", sf->name);
+	outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR);
+	outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA);
+	mdelay(1);
+	outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR);
+	sf->aux_data = SFAX_PCI_RESET_OFF;
+	outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+	mdelay(1);
+}
+
+static int
+sfax_ctrl(struct sfax_hw  *sf, u32 cmd, u_long arg)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case HW_RESET_REQ:
+		reset_speedfax(sf);
+		break;
+	case HW_ACTIVATE_IND:
+		if (arg & 1)
+			sf->aux_data &= ~SFAX_LED1_BIT;
+		if (arg & 2)
+			sf->aux_data &= ~SFAX_LED2_BIT;
+		outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+		break;
+	case HW_DEACT_IND:
+		if (arg & 1)
+			sf->aux_data |= SFAX_LED1_BIT;
+		if (arg & 2)
+			sf->aux_data |= SFAX_LED2_BIT;
+		outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+		break;
+	default:
+		pr_info("%s: %s unknown command %x %lx\n",
+			sf->name, __func__, cmd, arg);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+channel_ctrl(struct sfax_hw  *sf, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_LOOP;
+		break;
+	case MISDN_CTRL_LOOP:
+		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+		if (cq->channel < 0 || cq->channel > 3) {
+			ret = -EINVAL;
+			break;
+		}
+		ret = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel);
+		break;
+	default:
+		pr_info("%s: unknown Op %x\n", sf->name, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct sfax_hw		*sf = dch->hw;
+	struct channel_req	*rq;
+	int			err = 0;
+
+	pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		if (rq->protocol == ISDN_P_TE_S0)
+			err = sf->isac.open(&sf->isac, rq);
+		else
+			err = sf->isar.open(&sf->isar, rq);
+		if (err)
+			break;
+		if (!try_module_get(THIS_MODULE))
+			pr_info("%s: cannot get module\n", sf->name);
+		break;
+	case CLOSE_CHANNEL:
+		pr_debug("%s: dev(%d) close from %p\n", sf->name,
+			dch->dev.id, __builtin_return_address(0));
+		module_put(THIS_MODULE);
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_ctrl(sf, arg);
+		break;
+	default:
+		pr_debug("%s: unknown command %x\n", sf->name, cmd);
+		return -EINVAL;
+	}
+	return err;
+}
+
+static int __devinit
+init_card(struct sfax_hw *sf)
+{
+	int	ret, cnt = 3;
+	u_long	flags;
+
+	ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf);
+	if (ret) {
+		pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq);
+		return ret;
+	}
+	while (cnt--) {
+		spin_lock_irqsave(&sf->lock, flags);
+		ret = sf->isac.init(&sf->isac);
+		if (ret) {
+			spin_unlock_irqrestore(&sf->lock, flags);
+			pr_info("%s: ISAC init failed with %d\n",
+				sf->name, ret);
+			break;
+		}
+		enable_hwirq(sf);
+		/* RESET Receiver and Transmitter */
+		WriteISAC_IND(sf, ISAC_CMDR, 0x41);
+		spin_unlock_irqrestore(&sf->lock, flags);
+		msleep_interruptible(10);
+		if (debug & DEBUG_HW)
+			pr_notice("%s: IRQ %d count %d\n", sf->name,
+				sf->irq, sf->irqcnt);
+		if (!sf->irqcnt) {
+			pr_info("%s: IRQ(%d) got no requests during init %d\n",
+			       sf->name, sf->irq, 3 - cnt);
+		} else
+			return 0;
+	}
+	free_irq(sf->irq, sf);
+	return -EIO;
+}
+
+
+static int __devinit
+setup_speedfax(struct sfax_hw *sf)
+{
+	u_long flags;
+
+	if (!request_region(sf->cfg, 256, sf->name)) {
+		pr_info("mISDN: %s config port %x-%x already in use\n",
+		       sf->name, sf->cfg, sf->cfg + 255);
+		return -EIO;
+	}
+	outb(0xff, sf->cfg);
+	outb(0, sf->cfg);
+	outb(0xdd, sf->cfg + TIGER_AUX_CTRL);
+	outb(0, sf->cfg + TIGER_AUX_IRQMASK);
+
+	sf->isac.type = IPAC_TYPE_ISAC;
+	sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR;
+	sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC;
+	sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR;
+	sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR;
+	ASSIGN_FUNC(IND, ISAC, sf->isac);
+	ASSIGN_FUNC(IND, ISAR, sf->isar);
+	spin_lock_irqsave(&sf->lock, flags);
+	reset_speedfax(sf);
+	disable_hwirq(sf);
+	spin_unlock_irqrestore(&sf->lock, flags);
+	return 0;
+}
+
+static void
+release_card(struct sfax_hw *card) {
+	u_long	flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	disable_hwirq(card);
+	spin_unlock_irqrestore(&card->lock, flags);
+	card->isac.release(&card->isac);
+	free_irq(card->irq, card);
+	card->isar.release(&card->isar);
+	mISDN_unregister_device(&card->isac.dch.dev);
+	release_region(card->cfg, 256);
+	pci_disable_device(card->pdev);
+	pci_set_drvdata(card->pdev, NULL);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	kfree(card);
+	sfax_cnt--;
+}
+
+static int __devinit
+setup_instance(struct sfax_hw *card)
+{
+	const struct firmware *firmware;
+	int i, err;
+	u_long flags;
+
+	snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1);
+	write_lock_irqsave(&card_lock, flags);
+	list_add_tail(&card->list, &Cards);
+	write_unlock_irqrestore(&card_lock, flags);
+	_set_debug(card);
+	spin_lock_init(&card->lock);
+	card->isac.hwlock = &card->lock;
+	card->isar.hwlock = &card->lock;
+	card->isar.ctrl = (void *)&sfax_ctrl;
+	card->isac.name = card->name;
+	card->isar.name = card->name;
+	card->isar.owner = THIS_MODULE;
+
+	err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev);
+	if (err < 0) {
+		pr_info("%s: firmware request failed %d\n",
+			card->name, err);
+		goto error_fw;
+	}
+	if (debug & DEBUG_HW)
+		pr_notice("%s: got firmware %zu bytes\n",
+			card->name, firmware->size);
+
+	mISDNisac_init(&card->isac, card);
+
+	card->isac.dch.dev.D.ctrl = sfax_dctrl;
+	card->isac.dch.dev.Bprotocols =
+		mISDNisar_init(&card->isar, card);
+	for (i = 0; i < 2; i++) {
+		set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+		list_add(&card->isar.ch[i].bch.ch.list,
+			&card->isac.dch.dev.bchannels);
+	}
+
+	err = setup_speedfax(card);
+	if (err)
+		goto error_setup;
+	err = card->isar.init(&card->isar);
+	if (err)
+		goto error;
+	err = mISDN_register_device(&card->isac.dch.dev,
+		&card->pdev->dev, card->name);
+	if (err)
+		goto error;
+	err = init_card(card);
+	if (err)
+		goto error_init;
+	err = card->isar.firmware(&card->isar, firmware->data, firmware->size);
+	if (!err)  {
+		release_firmware(firmware);
+		sfax_cnt++;
+		pr_notice("SpeedFax %d cards installed\n", sfax_cnt);
+		return 0;
+	}
+	disable_hwirq(card);
+	free_irq(card->irq, card);
+error_init:
+	mISDN_unregister_device(&card->isac.dch.dev);
+error:
+	release_region(card->cfg, 256);
+error_setup:
+	card->isac.release(&card->isac);
+	card->isar.release(&card->isar);
+	release_firmware(firmware);
+error_fw:
+	pci_disable_device(card->pdev);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	kfree(card);
+	return err;
+}
+
+static int __devinit
+sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int err = -ENOMEM;
+	struct sfax_hw *card = kzalloc(sizeof(struct sfax_hw), GFP_KERNEL);
+
+	if (!card) {
+		pr_info("No memory for Speedfax+ PCI\n");
+		return err;
+	}
+	card->pdev = pdev;
+	err = pci_enable_device(pdev);
+	if (err) {
+		kfree(card);
+		return err;
+	}
+
+	pr_notice("mISDN: Speedfax found adapter %s at %s\n",
+		(char *)ent->driver_data, pci_name(pdev));
+
+	card->cfg = pci_resource_start(pdev, 0);
+	card->irq = pdev->irq;
+	pci_set_drvdata(pdev, card);
+	err = setup_instance(card);
+	if (err)
+		pci_set_drvdata(pdev, NULL);
+	return err;
+}
+
+static void __devexit
+sfax_remove_pci(struct pci_dev *pdev)
+{
+	struct sfax_hw	*card = pci_get_drvdata(pdev);
+
+	if (card)
+		release_card(card);
+	else
+		pr_debug("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id sfaxpci_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+	  PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER,
+	  0, 0, (unsigned long) "Pyramid Speedfax + PCI"
+	},
+	{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+	  PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER,
+	  0, 0, (unsigned long) "Sedlbauer Speedfax + PCI"
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, sfaxpci_ids);
+
+static struct pci_driver sfaxpci_driver = {
+	.name = "speedfax+ pci",
+	.probe = sfaxpci_probe,
+	.remove = __devexit_p(sfax_remove_pci),
+	.id_table = sfaxpci_ids,
+};
+
+static int __init
+Speedfax_init(void)
+{
+	int err;
+
+	pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n",
+		SPEEDFAX_REV);
+	err = pci_register_driver(&sfaxpci_driver);
+	return err;
+}
+
+static void __exit
+Speedfax_cleanup(void)
+{
+	pci_unregister_driver(&sfaxpci_driver);
+}
+
+module_init(Speedfax_init);
+module_exit(Speedfax_cleanup);

+ 1440 - 0
drivers/isdn/hardware/mISDN/w6692.c

@@ -0,0 +1,1440 @@
+/*
+ * w6692.c     mISDN driver for Winbond w6692 based cards
+ *
+ * Author      Karsten Keil <kkeil@suse.de>
+ *             based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include "w6692.h"
+
+#define W6692_REV	"2.0"
+
+#define DBUSY_TIMER_VALUE	80
+
+enum {
+	W6692_ASUS,
+	W6692_WINBOND,
+	W6692_USR
+};
+
+/* private data in the PCI devices list */
+struct w6692map {
+	u_int	subtype;
+	char	*name;
+};
+
+static const struct w6692map  w6692_map[] =
+{
+	{W6692_ASUS, "Dynalink/AsusCom IS64PH"},
+	{W6692_WINBOND, "Winbond W6692"},
+	{W6692_USR, "USR W6692"}
+};
+
+#ifndef PCI_VENDOR_ID_USR
+#define PCI_VENDOR_ID_USR	0x16ec
+#define PCI_DEVICE_ID_USR_6692	0x3409
+#endif
+
+struct w6692_ch {
+	struct bchannel		bch;
+	u32			addr;
+	struct timer_list	timer;
+	u8			b_mode;
+};
+
+struct w6692_hw {
+	struct list_head	list;
+	struct pci_dev		*pdev;
+	char			name[MISDN_MAX_IDLEN];
+	u32			irq;
+	u32			irqcnt;
+	u32			addr;
+	u32			fmask;	/* feature mask - bit set per card nr */
+	int			subtype;
+	spinlock_t		lock;	/* hw lock */
+	u8			imask;
+	u8			pctl;
+	u8			xaddr;
+	u8			xdata;
+	u8			state;
+	struct w6692_ch		bc[2];
+	struct dchannel		dch;
+	char			log[64];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static int w6692_cnt;
+static int debug;
+static u32 led;
+static u32 pots;
+
+static void
+_set_debug(struct w6692_hw *card)
+{
+	card->dch.debug = debug;
+	card->bc[0].bch.debug = debug;
+	card->bc[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+	int ret;
+	struct w6692_hw *card;
+
+	ret = param_set_uint(val, kp);
+	if (!ret) {
+		read_lock(&card_lock);
+		list_for_each_entry(card, &Cards, list)
+			_set_debug(card);
+		read_unlock(&card_lock);
+	}
+	return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(W6692_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "W6692 debug mask");
+module_param(led, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)");
+module_param(pots, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)");
+
+static inline u8
+ReadW6692(struct w6692_hw *card, u8 offset)
+{
+	return inb(card->addr + offset);
+}
+
+static inline void
+WriteW6692(struct w6692_hw *card, u8 offset, u8 value)
+{
+	outb(value, card->addr + offset);
+}
+
+static inline u8
+ReadW6692B(struct w6692_ch *bc, u8 offset)
+{
+	return inb(bc->addr + offset);
+}
+
+static inline void
+WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value)
+{
+	outb(value, bc->addr + offset);
+}
+
+static void
+enable_hwirq(struct w6692_hw *card)
+{
+	WriteW6692(card, W_IMASK, card->imask);
+}
+
+static void
+disable_hwirq(struct w6692_hw *card)
+{
+	WriteW6692(card, W_IMASK, 0xff);
+}
+
+static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"};
+
+static void
+W6692Version(struct w6692_hw *card)
+{
+	int val;
+
+	val = ReadW6692(card, W_D_RBCH);
+	pr_notice("%s: Winbond W6692 version: %s\n", card->name,
+		W6692Ver[(val >> 6) & 3]);
+}
+
+static void
+w6692_led_handler(struct w6692_hw *card, int on)
+{
+	if ((!(card->fmask & led)) || card->subtype == W6692_USR)
+		return;
+	if (on) {
+		card->xdata &= 0xfb;	/*  LED ON */
+		WriteW6692(card, W_XDATA, card->xdata);
+	} else {
+		card->xdata |= 0x04;	/*  LED OFF */
+		WriteW6692(card, W_XDATA, card->xdata);
+	}
+}
+
+static void
+ph_command(struct w6692_hw *card, u8 cmd)
+{
+	pr_debug("%s: ph_command %x\n", card->name, cmd);
+	WriteW6692(card, W_CIX, cmd);
+}
+
+static void
+W6692_new_ph(struct w6692_hw *card)
+{
+	if (card->state == W_L1CMD_RST)
+		ph_command(card, W_L1CMD_DRC);
+	schedule_event(&card->dch, FLG_PHCHANGE);
+}
+
+static void
+W6692_ph_bh(struct dchannel *dch)
+{
+	struct w6692_hw *card = dch->hw;
+
+	switch (card->state) {
+	case W_L1CMD_RST:
+		dch->state = 0;
+		l1_event(dch->l1, HW_RESET_IND);
+		break;
+	case W_L1IND_CD:
+		dch->state = 3;
+		l1_event(dch->l1, HW_DEACT_CNF);
+		break;
+	case W_L1IND_DRD:
+		dch->state = 3;
+		l1_event(dch->l1, HW_DEACT_IND);
+		break;
+	case W_L1IND_CE:
+		dch->state = 4;
+		l1_event(dch->l1, HW_POWERUP_IND);
+		break;
+	case W_L1IND_LD:
+		if (dch->state <= 5) {
+			dch->state = 5;
+			l1_event(dch->l1, ANYSIGNAL);
+		} else {
+			dch->state = 8;
+			l1_event(dch->l1, LOSTFRAMING);
+		}
+		break;
+	case W_L1IND_ARD:
+		dch->state = 6;
+		l1_event(dch->l1, INFO2);
+		break;
+	case W_L1IND_AI8:
+		dch->state = 7;
+		l1_event(dch->l1, INFO4_P8);
+		break;
+	case W_L1IND_AI10:
+		dch->state = 7;
+		l1_event(dch->l1, INFO4_P10);
+		break;
+	default:
+		pr_debug("%s: TE unknown state %02x dch state %02x\n",
+			card->name, card->state, dch->state);
+		break;
+	}
+	pr_debug("%s: TE newstate %02x\n", card->name, dch->state);
+}
+
+static void
+W6692_empty_Dfifo(struct w6692_hw *card, int count)
+{
+	struct dchannel *dch = &card->dch;
+	u8 *ptr;
+
+	pr_debug("%s: empty_Dfifo %d\n", card->name, count);
+	if (!dch->rx_skb) {
+		dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC);
+		if (!dch->rx_skb) {
+			pr_info("%s: D receive out of memory\n", card->name);
+			WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+			return;
+		}
+	}
+	if ((dch->rx_skb->len + count) >= dch->maxlen) {
+		pr_debug("%s: empty_Dfifo overrun %d\n", card->name,
+			dch->rx_skb->len + count);
+		WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+		return;
+	}
+	ptr = skb_put(dch->rx_skb, count);
+	insb(card->addr + W_D_RFIFO, ptr, count);
+	WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+	if (debug & DEBUG_HW_DFIFO) {
+		snprintf(card->log, 63, "D-recv %s %d ",
+			card->name, count);
+		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+	}
+}
+
+static void
+W6692_fill_Dfifo(struct w6692_hw *card)
+{
+	struct dchannel *dch = &card->dch;
+	int count;
+	u8 *ptr;
+	u8 cmd = W_D_CMDR_XMS;
+
+	pr_debug("%s: fill_Dfifo\n", card->name);
+	if (!dch->tx_skb)
+		return;
+	count = dch->tx_skb->len - dch->tx_idx;
+	if (count <= 0)
+		return;
+	if (count > W_D_FIFO_THRESH)
+		count = W_D_FIFO_THRESH;
+	else
+		cmd |= W_D_CMDR_XME;
+	ptr = dch->tx_skb->data + dch->tx_idx;
+	dch->tx_idx += count;
+	outsb(card->addr + W_D_XFIFO, ptr, count);
+	WriteW6692(card, W_D_CMDR, cmd);
+	if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) {
+		pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name);
+		del_timer(&dch->timer);
+	}
+	init_timer(&dch->timer);
+	dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&dch->timer);
+	if (debug & DEBUG_HW_DFIFO) {
+		snprintf(card->log, 63, "D-send %s %d ",
+			card->name, count);
+		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+	}
+}
+
+static void
+d_retransmit(struct w6692_hw *card)
+{
+	struct dchannel *dch = &card->dch;
+
+	if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+		del_timer(&dch->timer);
+#ifdef FIXME
+	if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+		dchannel_sched_event(dch, D_CLEARBUSY);
+#endif
+	if (test_bit(FLG_TX_BUSY, &dch->Flags)) {
+		/* Restart frame */
+		dch->tx_idx = 0;
+		W6692_fill_Dfifo(card);
+	} else if (dch->tx_skb) { /* should not happen */
+		pr_info("%s: %s without TX_BUSY\n", card->name, __func__);
+		test_and_set_bit(FLG_TX_BUSY, &dch->Flags);
+		dch->tx_idx = 0;
+		W6692_fill_Dfifo(card);
+	} else {
+		pr_info("%s: XDU no TX_BUSY\n", card->name);
+		if (get_next_dframe(dch))
+			W6692_fill_Dfifo(card);
+	}
+}
+
+static void
+handle_rxD(struct w6692_hw *card) {
+	u8	stat;
+	int	count;
+
+	stat = ReadW6692(card, W_D_RSTA);
+	if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
+		if (stat & W_D_RSTA_RDOV) {
+			pr_debug("%s: D-channel RDOV\n", card->name);
+#ifdef ERROR_STATISTIC
+			card->dch.err_rx++;
+#endif
+		}
+		if (stat & W_D_RSTA_CRCE) {
+			pr_debug("%s: D-channel CRC error\n", card->name);
+#ifdef ERROR_STATISTIC
+			card->dch.err_crc++;
+#endif
+		}
+		if (stat & W_D_RSTA_RMB) {
+			pr_debug("%s: D-channel ABORT\n", card->name);
+#ifdef ERROR_STATISTIC
+			card->dch.err_rx++;
+#endif
+		}
+		if (card->dch.rx_skb)
+			dev_kfree_skb(card->dch.rx_skb);
+		card->dch.rx_skb = NULL;
+		WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
+	} else {
+		count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
+		if (count == 0)
+			count = W_D_FIFO_THRESH;
+		W6692_empty_Dfifo(card, count);
+		recv_Dchannel(&card->dch);
+	}
+}
+
+static void
+handle_txD(struct w6692_hw *card) {
+	if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags))
+		del_timer(&card->dch.timer);
+	if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) {
+		W6692_fill_Dfifo(card);
+	} else {
+		if (card->dch.tx_skb)
+			dev_kfree_skb(card->dch.tx_skb);
+		if (get_next_dframe(&card->dch))
+			W6692_fill_Dfifo(card);
+	}
+}
+
+static void
+handle_statusD(struct w6692_hw *card)
+{
+	struct dchannel *dch = &card->dch;
+	u8 exval, v1, cir;
+
+	exval = ReadW6692(card, W_D_EXIR);
+
+	pr_debug("%s: D_EXIR %02x\n", card->name, exval);
+	if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {
+		/* Transmit underrun/collision */
+		pr_debug("%s: D-channel underrun/collision\n", card->name);
+#ifdef ERROR_STATISTIC
+		dch->err_tx++;
+#endif
+		d_retransmit(card);
+	}
+	if (exval & W_D_EXI_RDOV) {	/* RDOV */
+		pr_debug("%s: D-channel RDOV\n", card->name);
+		WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST);
+	}
+	if (exval & W_D_EXI_TIN2)	/* TIN2 - never */
+		pr_debug("%s: spurious TIN2 interrupt\n", card->name);
+	if (exval & W_D_EXI_MOC) {	/* MOC - not supported */
+		v1 = ReadW6692(card, W_MOSR);
+		pr_debug("%s: spurious MOC interrupt MOSR %02x\n",
+			card->name, v1);
+	}
+	if (exval & W_D_EXI_ISC) {	/* ISC - Level1 change */
+		cir = ReadW6692(card, W_CIR);
+		pr_debug("%s: ISC CIR %02X\n", card->name, cir);
+		if (cir & W_CIR_ICC) {
+			v1 = cir & W_CIR_COD_MASK;
+			pr_debug("%s: ph_state_change %x -> %x\n", card->name,
+				dch->state, v1);
+			card->state = v1;
+			if (card->fmask & led) {
+				switch (v1) {
+				case W_L1IND_AI8:
+				case W_L1IND_AI10:
+					w6692_led_handler(card, 1);
+					break;
+				default:
+					w6692_led_handler(card, 0);
+					break;
+				}
+			}
+			W6692_new_ph(card);
+		}
+		if (cir & W_CIR_SCC) {
+			v1 = ReadW6692(card, W_SQR);
+			pr_debug("%s: SCC SQR %02X\n", card->name, v1);
+		}
+	}
+	if (exval & W_D_EXI_WEXP)
+		pr_debug("%s: spurious WEXP interrupt!\n", card->name);
+	if (exval & W_D_EXI_TEXP)
+		pr_debug("%s: spurious TEXP interrupt!\n", card->name);
+}
+
+static void
+W6692_empty_Bfifo(struct w6692_ch *wch, int count)
+{
+	struct w6692_hw *card = wch->bch.hw;
+	u8 *ptr;
+
+	pr_debug("%s: empty_Bfifo %d\n", card->name, count);
+	if (unlikely(wch->bch.state == ISDN_P_NONE)) {
+		pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name);
+		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+		if (wch->bch.rx_skb)
+			skb_trim(wch->bch.rx_skb, 0);
+		return;
+	}
+	if (!wch->bch.rx_skb) {
+		wch->bch.rx_skb = mI_alloc_skb(wch->bch.maxlen, GFP_ATOMIC);
+		if (unlikely(!wch->bch.rx_skb)) {
+			pr_info("%s: B receive out of memory\n", card->name);
+			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+				W_B_CMDR_RACT);
+			return;
+		}
+	}
+	if (wch->bch.rx_skb->len + count > wch->bch.maxlen) {
+		pr_debug("%s: empty_Bfifo incoming packet too large\n",
+			card->name);
+		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+		skb_trim(wch->bch.rx_skb, 0);
+		return;
+	}
+	ptr = skb_put(wch->bch.rx_skb, count);
+	insb(wch->addr + W_B_RFIFO, ptr, count);
+	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+	if (debug & DEBUG_HW_DFIFO) {
+		snprintf(card->log, 63, "B%1d-recv %s %d ",
+			wch->bch.nr, card->name, count);
+		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+	}
+}
+
+static void
+W6692_fill_Bfifo(struct w6692_ch *wch)
+{
+	struct w6692_hw *card = wch->bch.hw;
+	int count;
+	u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS;
+
+	pr_debug("%s: fill Bfifo\n", card->name);
+	if (!wch->bch.tx_skb)
+		return;
+	count = wch->bch.tx_skb->len - wch->bch.tx_idx;
+	if (count <= 0)
+		return;
+	ptr = wch->bch.tx_skb->data + wch->bch.tx_idx;
+	if (count > W_B_FIFO_THRESH)
+		count = W_B_FIFO_THRESH;
+	else if (test_bit(FLG_HDLC, &wch->bch.Flags))
+		cmd |= W_B_CMDR_XME;
+
+	pr_debug("%s: fill Bfifo%d/%d\n", card->name,
+			count, wch->bch.tx_idx);
+	wch->bch.tx_idx += count;
+	outsb(wch->addr + W_B_XFIFO, ptr, count);
+	WriteW6692B(wch, W_B_CMDR, cmd);
+	if (debug & DEBUG_HW_DFIFO) {
+		snprintf(card->log, 63, "B%1d-send %s %d ",
+			wch->bch.nr, card->name, count);
+		print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+	}
+}
+
+static int
+setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb)
+{
+	struct w6692_hw *card = wch->bch.hw;
+	u16 *vol = (u16 *)skb->data;
+	u8 val;
+
+	if ((!(card->fmask & pots)) ||
+	    !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+		return -ENODEV;
+	if (skb->len < 2)
+		return -EINVAL;
+	if (*vol > 7)
+		return -EINVAL;
+	val = *vol & 7;
+	val = 7 - val;
+	if (mic) {
+		val <<= 3;
+		card->xaddr &= 0xc7;
+	} else {
+		card->xaddr &= 0xf8;
+	}
+	card->xaddr |= val;
+	WriteW6692(card, W_XADDR, card->xaddr);
+	return 0;
+}
+
+static int
+enable_pots(struct w6692_ch *wch)
+{
+	struct w6692_hw *card = wch->bch.hw;
+
+	if ((!(card->fmask & pots)) ||
+	    !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+		return -ENODEV;
+	wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0;
+	WriteW6692B(wch, W_B_MODE, wch->b_mode);
+	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+	card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0);
+	WriteW6692(card, W_PCTL, card->pctl);
+	return 0;
+}
+
+static int
+disable_pots(struct w6692_ch *wch)
+{
+	struct w6692_hw *card = wch->bch.hw;
+
+	if (!(card->fmask & pots))
+		return -ENODEV;
+	wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0);
+	WriteW6692B(wch, W_B_MODE, wch->b_mode);
+	WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+		W_B_CMDR_XRST);
+	return 0;
+}
+
+static int
+w6692_mode(struct w6692_ch *wch, u32 pr)
+{
+	struct w6692_hw	*card;
+
+	card = wch->bch.hw;
+	pr_debug("%s: B%d protocol %x-->%x\n", card->name,
+		wch->bch.nr, wch->bch.state, pr);
+	switch (pr) {
+	case ISDN_P_NONE:
+		if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM))
+			disable_pots(wch);
+		wch->b_mode = 0;
+		mISDN_clear_bchannel(&wch->bch);
+		WriteW6692B(wch, W_B_MODE, wch->b_mode);
+		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+		test_and_clear_bit(FLG_HDLC, &wch->bch.Flags);
+		test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags);
+		break;
+	case ISDN_P_B_RAW:
+		wch->b_mode = W_B_MODE_MMS;
+		WriteW6692B(wch, W_B_MODE, wch->b_mode);
+		WriteW6692B(wch, W_B_EXIM, 0);
+		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+			W_B_CMDR_XRST);
+		test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags);
+		break;
+	case ISDN_P_B_HDLC:
+		wch->b_mode = W_B_MODE_ITF;
+		WriteW6692B(wch, W_B_MODE, wch->b_mode);
+		WriteW6692B(wch, W_B_ADM1, 0xff);
+		WriteW6692B(wch, W_B_ADM2, 0xff);
+		WriteW6692B(wch, W_B_EXIM, 0);
+		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+			W_B_CMDR_XRST);
+		test_and_set_bit(FLG_HDLC, &wch->bch.Flags);
+		break;
+	default:
+		pr_info("%s: protocol %x not known\n", card->name, pr);
+		return -ENOPROTOOPT;
+	}
+	wch->bch.state = pr;
+	return 0;
+}
+
+static void
+send_next(struct w6692_ch *wch)
+{
+	if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len)
+		W6692_fill_Bfifo(wch);
+	else {
+		if (wch->bch.tx_skb) {
+			/* send confirm, on trans, free on hdlc. */
+			if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+				confirm_Bsend(&wch->bch);
+			dev_kfree_skb(wch->bch.tx_skb);
+		}
+		if (get_next_bframe(&wch->bch))
+			W6692_fill_Bfifo(wch);
+	}
+}
+
+static void
+W6692B_interrupt(struct w6692_hw *card, int ch)
+{
+	struct w6692_ch	*wch = &card->bc[ch];
+	int		count;
+	u8		stat, star = 0;
+
+	stat = ReadW6692B(wch, W_B_EXIR);
+	pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat);
+	if (stat & W_B_EXI_RME) {
+		star = ReadW6692B(wch, W_B_STAR);
+		if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
+			if ((star & W_B_STAR_RDOV) &&
+			    test_bit(FLG_ACTIVE, &wch->bch.Flags)) {
+				pr_debug("%s: B%d RDOV proto=%x\n", card->name,
+					wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+				wch->bch.err_rdo++;
+#endif
+			}
+			if (test_bit(FLG_HDLC, &wch->bch.Flags)) {
+				if (star & W_B_STAR_CRCE) {
+					pr_debug("%s: B%d CRC error\n",
+						card->name, wch->bch.nr);
+#ifdef ERROR_STATISTIC
+					wch->bch.err_crc++;
+#endif
+				}
+				if (star & W_B_STAR_RMB) {
+					pr_debug("%s: B%d message abort\n",
+						card->name, wch->bch.nr);
+#ifdef ERROR_STATISTIC
+					wch->bch.err_inv++;
+#endif
+				}
+			}
+			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+				W_B_CMDR_RRST | W_B_CMDR_RACT);
+			if (wch->bch.rx_skb)
+				skb_trim(wch->bch.rx_skb, 0);
+		} else {
+			count = ReadW6692B(wch, W_B_RBCL) &
+				(W_B_FIFO_THRESH - 1);
+			if (count == 0)
+				count = W_B_FIFO_THRESH;
+			W6692_empty_Bfifo(wch, count);
+			recv_Bchannel(&wch->bch, 0);
+		}
+	}
+	if (stat & W_B_EXI_RMR) {
+		if (!(stat & W_B_EXI_RME))
+			star = ReadW6692B(wch, W_B_STAR);
+		if (star & W_B_STAR_RDOV) {
+			pr_debug("%s: B%d RDOV proto=%x\n", card->name,
+				wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+			wch->bch.err_rdo++;
+#endif
+			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+				W_B_CMDR_RRST | W_B_CMDR_RACT);
+		} else {
+			W6692_empty_Bfifo(wch, W_B_FIFO_THRESH);
+			if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags) &&
+			    wch->bch.rx_skb && (wch->bch.rx_skb->len > 0))
+				recv_Bchannel(&wch->bch, 0);
+		}
+	}
+	if (stat & W_B_EXI_RDOV) {
+		/* only if it is not handled yet */
+		if (!(star & W_B_STAR_RDOV)) {
+			pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name,
+				wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+			wch->bch.err_rdo++;
+#endif
+			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+				W_B_CMDR_RRST | W_B_CMDR_RACT);
+		}
+	}
+	if (stat & W_B_EXI_XFR) {
+		if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) {
+			star = ReadW6692B(wch, W_B_STAR);
+			pr_debug("%s: B%d star %02x\n", card->name,
+				wch->bch.nr, star);
+		}
+		if (star & W_B_STAR_XDOW) {
+			pr_debug("%s: B%d XDOW proto=%x\n", card->name,
+				wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+			wch->bch.err_xdu++;
+#endif
+			WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST |
+				W_B_CMDR_RACT);
+			/* resend */
+			if (wch->bch.tx_skb) {
+				if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+					wch->bch.tx_idx = 0;
+			}
+		}
+		send_next(wch);
+		if (stat & W_B_EXI_XDUN)
+			return; /* handle XDOW only once */
+	}
+	if (stat & W_B_EXI_XDUN) {
+		pr_debug("%s: B%d XDUN proto=%x\n", card->name,
+			wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+		wch->bch.err_xdu++;
+#endif
+		WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+		/* resend */
+		if (wch->bch.tx_skb) {
+			if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+				wch->bch.tx_idx = 0;
+		}
+		send_next(wch);
+	}
+}
+
+static irqreturn_t
+w6692_irq(int intno, void *dev_id)
+{
+	struct w6692_hw	*card = dev_id;
+	u8		ista;
+
+	spin_lock(&card->lock);
+	ista = ReadW6692(card, W_ISTA);
+	if ((ista | card->imask) == card->imask) {
+		/* possible a shared  IRQ reqest */
+		spin_unlock(&card->lock);
+		return IRQ_NONE;
+	}
+	card->irqcnt++;
+	pr_debug("%s: ista %02x\n", card->name, ista);
+	ista &= ~card->imask;
+	if (ista & W_INT_B1_EXI)
+		W6692B_interrupt(card, 0);
+	if (ista & W_INT_B2_EXI)
+		W6692B_interrupt(card, 1);
+	if (ista & W_INT_D_RME)
+		handle_rxD(card);
+	if (ista & W_INT_D_RMR)
+		W6692_empty_Dfifo(card, W_D_FIFO_THRESH);
+	if (ista & W_INT_D_XFR)
+		handle_txD(card);
+	if (ista & W_INT_D_EXI)
+		handle_statusD(card);
+	if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */
+		pr_debug("%s: W6692 spurious XINT!\n", card->name);
+/* End IRQ Handler */
+	spin_unlock(&card->lock);
+	return IRQ_HANDLED;
+}
+
+static void
+dbusy_timer_handler(struct dchannel *dch)
+{
+	struct w6692_hw	*card = dch->hw;
+	int		rbch, star;
+	u_long		flags;
+
+	if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) {
+		spin_lock_irqsave(&card->lock, flags);
+		rbch = ReadW6692(card, W_D_RBCH);
+		star = ReadW6692(card, W_D_STAR);
+		pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
+			card->name, rbch, star);
+		if (star & W_D_STAR_XBZ)	/* D-Channel Busy */
+			test_and_set_bit(FLG_L1_BUSY, &dch->Flags);
+		else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags);
+			if (dch->tx_idx)
+				dch->tx_idx = 0;
+			else
+				pr_info("%s: W6692 D-Channel Busy no tx_idx\n",
+					card->name);
+			/* Transmitter reset */
+			WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST);
+		}
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+}
+
+void initW6692(struct w6692_hw *card)
+{
+	u8	val;
+
+	card->dch.timer.function = (void *)dbusy_timer_handler;
+	card->dch.timer.data = (u_long)&card->dch;
+	init_timer(&card->dch.timer);
+	w6692_mode(&card->bc[0], ISDN_P_NONE);
+	w6692_mode(&card->bc[1], ISDN_P_NONE);
+	WriteW6692(card, W_D_CTL, 0x00);
+	disable_hwirq(card);
+	WriteW6692(card, W_D_SAM, 0xff);
+	WriteW6692(card, W_D_TAM, 0xff);
+	WriteW6692(card, W_D_MODE, W_D_MODE_RACT);
+	card->state = W_L1CMD_RST;
+	ph_command(card, W_L1CMD_RST);
+	ph_command(card, W_L1CMD_ECK);
+	/* enable all IRQ but extern */
+	card->imask = 0x18;
+	WriteW6692(card, W_D_EXIM, 0x00);
+	WriteW6692B(&card->bc[0], W_B_EXIM, 0);
+	WriteW6692B(&card->bc[1], W_B_EXIM, 0);
+	/* Reset D-chan receiver and transmitter */
+	WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
+	/* Reset B-chan receiver and transmitter */
+	WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+	WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+	/* enable peripheral */
+	if (card->subtype == W6692_USR) {
+		/* seems that USR implemented some power control features
+		 * Pin 79 is connected to the oscilator circuit so we
+		 * have to handle it here
+		 */
+		card->pctl = 0x80;
+		card->xdata = 0;
+		WriteW6692(card, W_PCTL, card->pctl);
+		WriteW6692(card, W_XDATA, card->xdata);
+	} else {
+		card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 |
+			W_PCTL_OE1 | W_PCTL_OE0;
+		card->xaddr = 0x00;/* all sw off */
+		if (card->fmask & pots)
+			card->xdata |= 0x06;	/*  POWER UP/ LED OFF / ALAW */
+		if (card->fmask & led)
+			card->xdata |= 0x04;	/* LED OFF */
+		if ((card->fmask & pots) || (card->fmask & led)) {
+			WriteW6692(card, W_PCTL, card->pctl);
+			WriteW6692(card, W_XADDR, card->xaddr);
+			WriteW6692(card, W_XDATA, card->xdata);
+			val = ReadW6692(card, W_XADDR);
+			if (debug & DEBUG_HW)
+				pr_notice("%s: W_XADDR=%02x\n",
+					card->name, val);
+		}
+	}
+}
+
+static void
+reset_w6692(struct w6692_hw *card)
+{
+	WriteW6692(card, W_D_CTL, W_D_CTL_SRST);
+	mdelay(10);
+	WriteW6692(card, W_D_CTL, 0);
+}
+
+static int
+init_card(struct w6692_hw *card)
+{
+	int	cnt = 3;
+	u_long	flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	disable_hwirq(card);
+	spin_unlock_irqrestore(&card->lock, flags);
+	if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) {
+		pr_info("%s: couldn't get interrupt %d\n", card->name,
+			card->irq);
+		return -EIO;
+	}
+	while (cnt--) {
+		spin_lock_irqsave(&card->lock, flags);
+		initW6692(card);
+		enable_hwirq(card);
+		spin_unlock_irqrestore(&card->lock, flags);
+		/* Timeout 10ms */
+		msleep_interruptible(10);
+		if (debug & DEBUG_HW)
+			pr_notice("%s: IRQ %d count %d\n", card->name,
+				card->irq, card->irqcnt);
+		if (!card->irqcnt) {
+			pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
+				card->name, card->irq, 3 - cnt);
+			reset_w6692(card);
+		} else
+			return 0;
+	}
+	free_irq(card->irq, card);
+	return -EIO;
+}
+
+static int
+w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct w6692_ch	*bc = container_of(bch, struct w6692_ch, bch);
+	struct w6692_hw *card = bch->hw;
+	int ret = -EINVAL;
+	struct mISDNhead *hh = mISDN_HEAD_P(skb);
+	u32 id;
+	u_long flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		ret = bchannel_senddata(bch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			ret = 0;
+			W6692_fill_Bfifo(bc);
+			spin_unlock_irqrestore(&card->lock, flags);
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(&card->lock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+			ret = w6692_mode(bc, ch->protocol);
+		else
+			ret = 0;
+		spin_unlock_irqrestore(&card->lock, flags);
+		if (!ret)
+			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+				NULL, GFP_KERNEL);
+		break;
+	case PH_DEACTIVATE_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		mISDN_clear_bchannel(bch);
+		w6692_mode(bc, ISDN_P_NONE);
+		spin_unlock_irqrestore(&card->lock, flags);
+		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_KERNEL);
+		ret = 0;
+		break;
+	default:
+		pr_info("%s: %s unknown prim(%x,%x)\n",
+			card->name, __func__, hh->prim, hh->id);
+		ret = -EINVAL;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = 0;
+		break;
+	/* Nothing implemented yet */
+	case MISDN_CTRL_FILL_EMPTY:
+	default:
+		pr_info("%s: unknown Op %x\n", __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+open_bchannel(struct w6692_hw *card, struct channel_req *rq)
+{
+	struct bchannel *bch;
+
+	if (rq->adr.channel > 2)
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	bch = &card->bc[rq->adr.channel - 1].bch;
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+	bch->ch.protocol = rq->protocol;
+	rq->ch = &bch->ch;
+	return 0;
+}
+
+static int
+channel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = 0;
+		break;
+	default:
+		pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct bchannel *bch = container_of(ch, struct bchannel, ch);
+	struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);
+	struct w6692_hw *card = bch->hw;
+	int ret = -EINVAL;
+	u_long flags;
+
+	pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
+	switch (cmd) {
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+			spin_lock_irqsave(&card->lock, flags);
+			mISDN_freebchannel(bch);
+			w6692_mode(bc, ISDN_P_NONE);
+			spin_unlock_irqrestore(&card->lock, flags);
+		} else {
+			skb_queue_purge(&bch->rqueue);
+			bch->rcount = 0;
+		}
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(THIS_MODULE);
+		ret = 0;
+		break;
+	case CONTROL_CHANNEL:
+		ret = channel_bctrl(bch, arg);
+		break;
+	default:
+		pr_info("%s: %s unknown prim(%x)\n",
+			card->name, __func__, cmd);
+	}
+	return ret;
+}
+
+static int
+w6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct w6692_hw		*card = container_of(dch, struct w6692_hw, dch);
+	int			ret = -EINVAL;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	u32			id;
+	u_long			flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		ret = dchannel_senddata(dch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			W6692_fill_Dfifo(card);
+			ret = 0;
+			spin_unlock_irqrestore(&card->lock, flags);
+			queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(&card->lock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		ret = l1_event(dch->l1, hh->prim);
+		break;
+	case PH_DEACTIVATE_REQ:
+		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+		ret = l1_event(dch->l1, hh->prim);
+		break;
+	}
+
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+w6692_l1callback(struct dchannel *dch, u32 cmd)
+{
+	struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
+	u_long flags;
+
+	pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state);
+	switch (cmd) {
+	case INFO3_P8:
+		spin_lock_irqsave(&card->lock, flags);
+		ph_command(card, W_L1CMD_AR8);
+		spin_unlock_irqrestore(&card->lock, flags);
+		break;
+	case INFO3_P10:
+		spin_lock_irqsave(&card->lock, flags);
+		ph_command(card, W_L1CMD_AR10);
+		spin_unlock_irqrestore(&card->lock, flags);
+		break;
+	case HW_RESET_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		if (card->state != W_L1IND_DRD)
+			ph_command(card, W_L1CMD_RST);
+		ph_command(card, W_L1CMD_ECK);
+		spin_unlock_irqrestore(&card->lock, flags);
+		break;
+	case HW_DEACT_REQ:
+		skb_queue_purge(&dch->squeue);
+		if (dch->tx_skb) {
+			dev_kfree_skb(dch->tx_skb);
+			dch->tx_skb = NULL;
+		}
+		dch->tx_idx = 0;
+		if (dch->rx_skb) {
+			dev_kfree_skb(dch->rx_skb);
+			dch->rx_skb = NULL;
+		}
+		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+		if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+			del_timer(&dch->timer);
+		break;
+	case HW_POWERUP_REQ:
+		spin_lock_irqsave(&card->lock, flags);
+		ph_command(card, W_L1CMD_ECK);
+		spin_unlock_irqrestore(&card->lock, flags);
+		break;
+	case PH_ACTIVATE_IND:
+		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	case PH_DEACTIVATE_IND:
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	default:
+		pr_debug("%s: %s unknown command %x\n", card->name,
+			__func__, cmd);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+open_dchannel(struct w6692_hw *card, struct channel_req *rq)
+{
+	pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__,
+		card->dch.dev.id, __builtin_return_address(1));
+	if (rq->protocol != ISDN_P_TE_S0)
+		return -EINVAL;
+	if (rq->adr.channel == 1)
+		/* E-Channel not supported */
+		return -EINVAL;
+	rq->ch = &card->dch.dev.D;
+	rq->ch->protocol = rq->protocol;
+	if (card->dch.state == 7)
+		_queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+		    0, NULL, GFP_KERNEL);
+	return 0;
+}
+
+static int
+w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+	struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel *dch = container_of(dev, struct dchannel, dev);
+	struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
+	struct channel_req *rq;
+	int err = 0;
+
+	pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		if (rq->protocol == ISDN_P_TE_S0)
+			err = open_dchannel(card, rq);
+		else
+			err = open_bchannel(card, rq);
+		if (err)
+			break;
+		if (!try_module_get(THIS_MODULE))
+			pr_info("%s: cannot get module\n", card->name);
+		break;
+	case CLOSE_CHANNEL:
+		pr_debug("%s: dev(%d) close from %p\n", card->name,
+			dch->dev.id, __builtin_return_address(0));
+		module_put(THIS_MODULE);
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_ctrl(card, arg);
+		break;
+	default:
+		pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd);
+		return -EINVAL;
+	}
+	return err;
+}
+
+static int
+setup_w6692(struct w6692_hw *card)
+{
+	u32	val;
+
+	if (!request_region(card->addr, 256, card->name)) {
+		pr_info("%s: config port %x-%x already in use\n", card->name,
+		       card->addr, card->addr + 255);
+		return -EIO;
+	}
+	W6692Version(card);
+	card->bc[0].addr = card->addr;
+	card->bc[1].addr = card->addr + 0x40;
+	val = ReadW6692(card, W_ISTA);
+	if (debug & DEBUG_HW)
+		pr_notice("%s ISTA=%02x\n", card->name, val);
+	val = ReadW6692(card, W_IMASK);
+	if (debug & DEBUG_HW)
+		pr_notice("%s IMASK=%02x\n", card->name, val);
+	val = ReadW6692(card, W_D_EXIR);
+	if (debug & DEBUG_HW)
+		pr_notice("%s D_EXIR=%02x\n", card->name, val);
+	val = ReadW6692(card, W_D_EXIM);
+	if (debug & DEBUG_HW)
+		pr_notice("%s D_EXIM=%02x\n", card->name, val);
+	val = ReadW6692(card, W_D_RSTA);
+	if (debug & DEBUG_HW)
+		pr_notice("%s D_RSTA=%02x\n", card->name, val);
+	return 0;
+}
+
+static void
+release_card(struct w6692_hw *card)
+{
+	u_long	flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	disable_hwirq(card);
+	w6692_mode(&card->bc[0], ISDN_P_NONE);
+	w6692_mode(&card->bc[1], ISDN_P_NONE);
+	if ((card->fmask & led) || card->subtype == W6692_USR) {
+		card->xdata |= 0x04;	/*  LED OFF */
+		WriteW6692(card, W_XDATA, card->xdata);
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+	free_irq(card->irq, card);
+	l1_event(card->dch.l1, CLOSE_CHANNEL);
+	mISDN_unregister_device(&card->dch.dev);
+	release_region(card->addr, 256);
+	mISDN_freebchannel(&card->bc[1].bch);
+	mISDN_freebchannel(&card->bc[0].bch);
+	mISDN_freedchannel(&card->dch);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	pci_disable_device(card->pdev);
+	pci_set_drvdata(card->pdev, NULL);
+	kfree(card);
+}
+
+static int
+setup_instance(struct w6692_hw *card)
+{
+	int		i, err;
+	u_long		flags;
+
+	snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1);
+	write_lock_irqsave(&card_lock, flags);
+	list_add_tail(&card->list, &Cards);
+	write_unlock_irqrestore(&card_lock, flags);
+	card->fmask = (1 << w6692_cnt);
+	_set_debug(card);
+	spin_lock_init(&card->lock);
+	mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh);
+	card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
+	card->dch.dev.D.send = w6692_l2l1D;
+	card->dch.dev.D.ctrl = w6692_dctrl;
+	card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	card->dch.hw = card;
+	card->dch.dev.nrbchan = 2;
+	for (i = 0; i < 2; i++) {
+		mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM);
+		card->bc[i].bch.hw = card;
+		card->bc[i].bch.nr = i + 1;
+		card->bc[i].bch.ch.nr = i + 1;
+		card->bc[i].bch.ch.send = w6692_l2l1B;
+		card->bc[i].bch.ch.ctrl = w6692_bctrl;
+		set_channelmap(i + 1, card->dch.dev.channelmap);
+		list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels);
+	}
+	err = setup_w6692(card);
+	if (err)
+		goto error_setup;
+	err = mISDN_register_device(&card->dch.dev, &card->pdev->dev,
+		card->name);
+	if (err)
+		goto error_reg;
+	err = init_card(card);
+	if (err)
+		goto error_init;
+	err = create_l1(&card->dch, w6692_l1callback);
+	if (!err) {
+		w6692_cnt++;
+		pr_notice("W6692 %d cards installed\n", w6692_cnt);
+		return 0;
+	}
+
+	free_irq(card->irq, card);
+error_init:
+	mISDN_unregister_device(&card->dch.dev);
+error_reg:
+	release_region(card->addr, 256);
+error_setup:
+	mISDN_freebchannel(&card->bc[1].bch);
+	mISDN_freebchannel(&card->bc[0].bch);
+	mISDN_freedchannel(&card->dch);
+	write_lock_irqsave(&card_lock, flags);
+	list_del(&card->list);
+	write_unlock_irqrestore(&card_lock, flags);
+	kfree(card);
+	return err;
+}
+
+static int __devinit
+w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int		err = -ENOMEM;
+	struct w6692_hw	*card;
+	struct w6692map	*m = (struct w6692map *)ent->driver_data;
+
+	card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL);
+	if (!card) {
+		pr_info("No kmem for w6692 card\n");
+		return err;
+	}
+	card->pdev = pdev;
+	card->subtype = m->subtype;
+	err = pci_enable_device(pdev);
+	if (err) {
+		kfree(card);
+		return err;
+	}
+
+	printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
+	       m->name, pci_name(pdev));
+
+	card->addr = pci_resource_start(pdev, 1);
+	card->irq = pdev->irq;
+	pci_set_drvdata(pdev, card);
+	err = setup_instance(card);
+	if (err)
+		pci_set_drvdata(pdev, NULL);
+	return err;
+}
+
+static void __devexit
+w6692_remove_pci(struct pci_dev *pdev)
+{
+	struct w6692_hw	*card = pci_get_drvdata(pdev);
+
+	if (card)
+		release_card(card);
+	else
+		if (debug)
+			pr_notice("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id w6692_ids[] = {
+	{ PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]},
+	{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
+	  PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0,
+	  (ulong)&w6692_map[2]},
+	{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]},
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, w6692_ids);
+
+static struct pci_driver w6692_driver = {
+	.name =  "w6692",
+	.probe = w6692_probe,
+	.remove = __devexit_p(w6692_remove_pci),
+	.id_table = w6692_ids,
+};
+
+static int __init w6692_init(void)
+{
+	int err;
+
+	pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV);
+
+	err = pci_register_driver(&w6692_driver);
+	return err;
+}
+
+static void __exit w6692_cleanup(void)
+{
+	pci_unregister_driver(&w6692_driver);
+}
+
+module_init(w6692_init);
+module_exit(w6692_cleanup);

+ 190 - 0
drivers/isdn/hardware/mISDN/w6692.h

@@ -0,0 +1,190 @@
+/*
+ * Winbond W6692 specific defines
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *		based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Specifications of W6692 registers */
+
+#define W_D_RFIFO	0x00	/* R */
+#define W_D_XFIFO	0x04	/* W */
+#define W_D_CMDR	0x08	/* W */
+#define W_D_MODE	0x0c	/* R/W */
+#define W_D_TIMR	0x10	/* R/W */
+#define W_ISTA		0x14	/* R_clr */
+#define W_IMASK		0x18	/* R/W */
+#define W_D_EXIR	0x1c	/* R_clr */
+#define W_D_EXIM	0x20	/* R/W */
+#define W_D_STAR	0x24	/* R */
+#define W_D_RSTA	0x28	/* R */
+#define W_D_SAM		0x2c	/* R/W */
+#define W_D_SAP1	0x30	/* R/W */
+#define W_D_SAP2	0x34	/* R/W */
+#define W_D_TAM		0x38	/* R/W */
+#define W_D_TEI1	0x3c	/* R/W */
+#define W_D_TEI2	0x40	/* R/W */
+#define W_D_RBCH	0x44	/* R */
+#define W_D_RBCL	0x48	/* R */
+#define W_TIMR2		0x4c	/* W */
+#define W_L1_RC		0x50	/* R/W */
+#define W_D_CTL		0x54	/* R/W */
+#define W_CIR		0x58	/* R */
+#define W_CIX		0x5c	/* W */
+#define W_SQR		0x60	/* R */
+#define W_SQX		0x64	/* W */
+#define W_PCTL		0x68	/* R/W */
+#define W_MOR		0x6c	/* R */
+#define W_MOX		0x70	/* R/W */
+#define W_MOSR		0x74	/* R_clr */
+#define W_MOCR		0x78	/* R/W */
+#define W_GCR		0x7c	/* R/W */
+
+#define	W_B_RFIFO	0x80	/* R */
+#define	W_B_XFIFO	0x84	/* W */
+#define	W_B_CMDR	0x88	/* W */
+#define	W_B_MODE	0x8c	/* R/W */
+#define	W_B_EXIR	0x90	/* R_clr */
+#define	W_B_EXIM	0x94	/* R/W */
+#define	W_B_STAR	0x98	/* R */
+#define	W_B_ADM1	0x9c	/* R/W */
+#define	W_B_ADM2	0xa0	/* R/W */
+#define	W_B_ADR1	0xa4	/* R/W */
+#define	W_B_ADR2	0xa8	/* R/W */
+#define	W_B_RBCL	0xac	/* R */
+#define	W_B_RBCH	0xb0	/* R */
+
+#define W_XADDR		0xf4	/* R/W */
+#define W_XDATA		0xf8	/* R/W */
+#define W_EPCTL		0xfc	/* W */
+
+/* W6692 register bits */
+
+#define	W_D_CMDR_XRST	0x01
+#define	W_D_CMDR_XME	0x02
+#define	W_D_CMDR_XMS	0x08
+#define	W_D_CMDR_STT	0x10
+#define	W_D_CMDR_RRST	0x40
+#define	W_D_CMDR_RACK	0x80
+
+#define	W_D_MODE_RLP	0x01
+#define	W_D_MODE_DLP	0x02
+#define	W_D_MODE_MFD	0x04
+#define	W_D_MODE_TEE	0x08
+#define	W_D_MODE_TMS	0x10
+#define	W_D_MODE_RACT	0x40
+#define	W_D_MODE_MMS	0x80
+
+#define W_INT_B2_EXI	0x01
+#define W_INT_B1_EXI	0x02
+#define W_INT_D_EXI	0x04
+#define W_INT_XINT0	0x08
+#define W_INT_XINT1	0x10
+#define W_INT_D_XFR	0x20
+#define W_INT_D_RME	0x40
+#define W_INT_D_RMR	0x80
+
+#define W_D_EXI_WEXP	0x01
+#define W_D_EXI_TEXP	0x02
+#define W_D_EXI_ISC	0x04
+#define W_D_EXI_MOC	0x08
+#define W_D_EXI_TIN2	0x10
+#define W_D_EXI_XCOL	0x20
+#define W_D_EXI_XDUN	0x40
+#define W_D_EXI_RDOV	0x80
+
+#define	W_D_STAR_DRDY	0x10
+#define	W_D_STAR_XBZ	0x20
+#define	W_D_STAR_XDOW	0x80
+
+#define W_D_RSTA_RMB	0x10
+#define W_D_RSTA_CRCE	0x20
+#define W_D_RSTA_RDOV	0x40
+
+#define W_D_CTL_SRST	0x20
+
+#define W_CIR_SCC	0x80
+#define W_CIR_ICC	0x40
+#define W_CIR_COD_MASK	0x0f
+
+#define W_PCTL_PCX	0x01
+#define W_PCTL_XMODE	0x02
+#define W_PCTL_OE0	0x04
+#define W_PCTL_OE1	0x08
+#define W_PCTL_OE2	0x10
+#define W_PCTL_OE3	0x20
+#define W_PCTL_OE4	0x40
+#define W_PCTL_OE5	0x80
+
+#define	W_B_CMDR_XRST	0x01
+#define	W_B_CMDR_XME	0x02
+#define	W_B_CMDR_XMS	0x04
+#define	W_B_CMDR_RACT	0x20
+#define	W_B_CMDR_RRST	0x40
+#define	W_B_CMDR_RACK	0x80
+
+#define	W_B_MODE_FTS0	0x01
+#define	W_B_MODE_FTS1	0x02
+#define	W_B_MODE_SW56	0x04
+#define	W_B_MODE_BSW0	0x08
+#define	W_B_MODE_BSW1	0x10
+#define	W_B_MODE_EPCM	0x20
+#define	W_B_MODE_ITF	0x40
+#define	W_B_MODE_MMS	0x80
+
+#define	W_B_EXI_XDUN	0x01
+#define	W_B_EXI_XFR	0x02
+#define	W_B_EXI_RDOV	0x10
+#define	W_B_EXI_RME	0x20
+#define	W_B_EXI_RMR	0x40
+
+#define	W_B_STAR_XBZ	0x01
+#define	W_B_STAR_XDOW	0x04
+#define	W_B_STAR_RMB	0x10
+#define	W_B_STAR_CRCE	0x20
+#define	W_B_STAR_RDOV	0x40
+
+#define	W_B_RBCH_LOV	0x20
+
+/* W6692 Layer1 commands */
+
+#define	W_L1CMD_ECK	0x00
+#define W_L1CMD_RST	0x01
+#define W_L1CMD_SCP	0x04
+#define W_L1CMD_SSP	0x02
+#define W_L1CMD_AR8	0x08
+#define W_L1CMD_AR10	0x09
+#define W_L1CMD_EAL	0x0a
+#define W_L1CMD_DRC	0x0f
+
+/* W6692 Layer1 indications */
+
+#define W_L1IND_CE	0x07
+#define W_L1IND_DRD	0x00
+#define W_L1IND_LD	0x04
+#define W_L1IND_ARD	0x08
+#define W_L1IND_TI	0x0a
+#define W_L1IND_ATI	0x0b
+#define W_L1IND_AI8	0x0c
+#define W_L1IND_AI10	0x0d
+#define W_L1IND_CD	0x0f
+
+/* FIFO thresholds */
+#define W_D_FIFO_THRESH	64
+#define W_B_FIFO_THRESH	64

+ 1 - 5
drivers/isdn/hisax/Kconfig

@@ -391,6 +391,7 @@ comment "HiSax sub driver modules"
 config HISAX_ST5481
 	tristate "ST5481 USB ISDN modem (EXPERIMENTAL)"
 	depends on USB && EXPERIMENTAL
+	select ISDN_HDLC
 	select CRC_CCITT
 	select BITREVERSE
 	help
@@ -418,11 +419,6 @@ config HISAX_FRITZ_PCIPNP
 	  (the latter also needs you to select "ISA Plug and Play support"
 	  from the menu "Plug and Play configuration")
 
-config HISAX_HDLC
-	bool
-	depends on HISAX_ST5481
-	default y
-
 config HISAX_AVM_A1_PCMCIA
 	bool
 	depends on HISAX_AVM_A1_CS

+ 0 - 4
drivers/isdn/hisax/Makefile

@@ -16,10 +16,6 @@ obj-$(CONFIG_HISAX_HFCUSB)		+= hfc_usb.o
 obj-$(CONFIG_HISAX_HFC4S8S)		+= hfc4s8s_l1.o
 obj-$(CONFIG_HISAX_FRITZ_PCIPNP)        += hisax_isac.o hisax_fcpcipnp.o
 
-ifdef CONFIG_HISAX_HDLC
-obj-$(CONFIG_ISDN_DRV_HISAX)		+= isdnhdlc.o
-endif
-
 # Multipart objects.
 
 hisax_st5481-y 				:= st5481_init.o st5481_usb.o st5481_d.o \

+ 0 - 2
drivers/isdn/hisax/amd7930_fn.c

@@ -238,8 +238,6 @@ Amd7930_bh(struct work_struct *work)
 		container_of(work, struct IsdnCardState, tqueue);
         struct PStack *stptr;
 
-	if (!cs)
-		return;
 	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
                 if (cs->debug)
 			debugl1(cs, "Amd7930: bh, D-Channel Busy cleared");

+ 1 - 3
drivers/isdn/hisax/callc.c

@@ -833,8 +833,6 @@ static struct FsmNode fnlist[] __initdata =
 };
 /* *INDENT-ON* */
 
-#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
-
 int __init
 CallcNew(void)
 {
@@ -842,7 +840,7 @@ CallcNew(void)
 	callcfsm.event_count = EVENT_COUNT;
 	callcfsm.strEvent = strEvent;
 	callcfsm.strState = strState;
-	return FsmNew(&callcfsm, fnlist, FNCOUNT);
+	return FsmNew(&callcfsm, fnlist, ARRAY_SIZE(fnlist));
 }
 
 void

+ 0 - 2
drivers/isdn/hisax/hfc_pci.c

@@ -1506,8 +1506,6 @@ hfcpci_bh(struct work_struct *work)
 	u_long	flags;
 //      struct PStack *stptr;
 
-	if (!cs)
-		return;
 	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
 		if (!cs->hw.hfcpci.nt_mode)
 			switch (cs->dc.hfcpci.ph_state) {

+ 0 - 2
drivers/isdn/hisax/hfc_sx.c

@@ -1255,8 +1255,6 @@ hfcsx_bh(struct work_struct *work)
 		container_of(work, struct IsdnCardState, tqueue);
 	u_long flags;
 
-	if (!cs)
-		return;
 	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
 		if (!cs->hw.hfcsx.nt_mode)
 			switch (cs->dc.hfcsx.ph_state) {

+ 0 - 2
drivers/isdn/hisax/icc.c

@@ -83,8 +83,6 @@ icc_bh(struct work_struct *work)
 		container_of(work, struct IsdnCardState, tqueue);
 	struct PStack *stptr;
 	
-	if (!cs)
-		return;
 	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
 		if (cs->debug)
 			debugl1(cs, "D-Channel Busy cleared");

+ 0 - 2
drivers/isdn/hisax/isac.c

@@ -86,8 +86,6 @@ isac_bh(struct work_struct *work)
 		container_of(work, struct IsdnCardState, tqueue);
 	struct PStack *stptr;
 	
-	if (!cs)
-		return;
 	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
 		if (cs->debug)
 			debugl1(cs, "D-Channel Busy cleared");

+ 0 - 70
drivers/isdn/hisax/isdnhdlc.h

@@ -1,70 +0,0 @@
-/*
- * isdnhdlc.h  --  General purpose ISDN HDLC decoder.
- *
- * Implementation of a HDLC decoder/encoder in software.
- * Neccessary because some ISDN devices don't have HDLC
- * controllers. Also included: a bit reversal table.
- *
- *Copyright (C) 2002    Wolfgang Mües      <wolfgang@iksw-muees.de>
- *		2001 	Frode Isaksen      <fisaksen@bewan.com>
- *              2001 	Kai Germaschewski  <kai.germaschewski@gmx.de>
- *
- *      This program is free software; you can redistribute it and/or modify
- *      it under the terms of the GNU General Public License as published by
- *      the Free Software Foundation; either version 2 of the License, or
- *      (at your option) any later version.
- *
- *      This program is distributed in the hope that it will be useful,
- *      but WITHOUT ANY WARRANTY; without even the implied warranty of
- *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *      GNU General Public License for more details.
- *
- *      You should have received a copy of the GNU General Public License
- *      along with this program; if not, write to the Free Software
- *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __ISDNHDLC_H__
-#define __ISDNHDLC_H__
-
-struct isdnhdlc_vars {
-	int bit_shift;
-	int hdlc_bits1;
-	int data_bits;
-	int ffbit_shift; 	// encoding only
-	int state;
-	int dstpos;
-
-	unsigned short crc;
-
-	unsigned char cbin;
-	unsigned char shift_reg;
-	unsigned char ffvalue;
-
-	unsigned int data_received:1; 	// set if transferring data
-	unsigned int dchannel:1; 	// set if D channel (send idle instead of flags)
-	unsigned int do_adapt56:1; 	// set if 56K adaptation
-	unsigned int do_closing:1; 	// set if in closing phase (need to send CRC + flag
-};
-
-
-/*
-  The return value from isdnhdlc_decode is
-  the frame length, 0 if no complete frame was decoded,
-  or a negative error number
-*/
-#define HDLC_FRAMING_ERROR     1
-#define HDLC_CRC_ERROR         2
-#define HDLC_LENGTH_ERROR      3
-
-extern void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56);
-
-extern int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src, int slen,int *count,
-	                    unsigned char *dst, int dsize);
-
-extern void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc,int is_d_channel,int do_adapt56);
-
-extern int isdnhdlc_encode (struct isdnhdlc_vars *hdlc,const unsigned char *src,unsigned short slen,int *count,
-	                    unsigned char *dst,int dsize);
-
-#endif /* __ISDNHDLC_H__ */

+ 3 - 9
drivers/isdn/hisax/isdnl1.c

@@ -647,8 +647,6 @@ static struct FsmNode L1SFnList[] __initdata =
 	{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
 };
 
-#define L1S_FN_COUNT (sizeof(L1SFnList)/sizeof(struct FsmNode))
-
 #ifdef HISAX_UINTERFACE
 static void
 l1_deact_req_u(struct FsmInst *fi, int event, void *arg)
@@ -706,8 +704,6 @@ static struct FsmNode L1UFnList[] __initdata =
 	{ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact},
 };
 
-#define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode))
-
 #endif
 
 static void
@@ -754,8 +750,6 @@ static struct FsmNode L1BFnList[] __initdata =
 	{ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact},
 };
 
-#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode))
-
 int __init 
 Isdnl1New(void)
 {
@@ -765,7 +759,7 @@ Isdnl1New(void)
 	l1fsm_s.event_count = L1_EVENT_COUNT;
 	l1fsm_s.strEvent = strL1Event;
 	l1fsm_s.strState = strL1SState;
-	retval = FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT);
+	retval = FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
 	if (retval)
 		return retval;
 
@@ -773,7 +767,7 @@ Isdnl1New(void)
 	l1fsm_b.event_count = L1_EVENT_COUNT;
 	l1fsm_b.strEvent = strL1Event;
 	l1fsm_b.strState = strL1BState;
-	retval = FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
+	retval = FsmNew(&l1fsm_b, L1BFnList, ARRAY_SIZE(L1BFnList));
 	if (retval) {
 		FsmFree(&l1fsm_s);
 		return retval;
@@ -783,7 +777,7 @@ Isdnl1New(void)
 	l1fsm_u.event_count = L1_EVENT_COUNT;
 	l1fsm_u.strEvent = strL1Event;
 	l1fsm_u.strState = strL1UState;
-	retval = FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT);
+	retval = FsmNew(&l1fsm_u, L1UFnList, ARRAY_SIZE(L1UFnList));
 	if (retval) {
 		FsmFree(&l1fsm_s);
 		FsmFree(&l1fsm_b);

+ 1 - 3
drivers/isdn/hisax/isdnl2.c

@@ -1623,8 +1623,6 @@ static struct FsmNode L2FnList[] __initdata =
 	{ST_L2_8, EV_L1_DEACTIVATE, l2_persistent_da},
 };
 
-#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
-
 static void
 isdnl2_l1l2(struct PStack *st, int pr, void *arg)
 {
@@ -1836,7 +1834,7 @@ Isdnl2New(void)
 	l2fsm.event_count = L2_EVENT_COUNT;
 	l2fsm.strEvent = strL2Event;
 	l2fsm.strState = strL2State;
-	return FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+	return FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList));
 }
 
 void

+ 1 - 3
drivers/isdn/hisax/isdnl3.c

@@ -543,8 +543,6 @@ static struct FsmNode L3FnList[] __initdata =
 };
 /* *INDENT-ON* */
 
-#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode))
-
 void
 l3_msg(struct PStack *st, int pr, void *arg)
 {
@@ -587,7 +585,7 @@ Isdnl3New(void)
 	l3fsm.event_count = L3_EVENT_COUNT;
 	l3fsm.strEvent = strL3Event;
 	l3fsm.strState = strL3State;
-	return FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
+	return FsmNew(&l3fsm, L3FnList, ARRAY_SIZE(L3FnList));
 }
 
 void

+ 6 - 14
drivers/isdn/hisax/l3_1tr6.c

@@ -698,9 +698,6 @@ static struct stateentry downstl[] =
 	 CC_T308_2, l3_1tr6_t308_2},
 };
 
-#define DOWNSTL_LEN \
-	(sizeof(downstl) / sizeof(struct stateentry))
-
 static struct stateentry datastln1[] =
 {
 	{SBIT(0),
@@ -735,9 +732,6 @@ static struct stateentry datastln1[] =
 	 MT_N1_REL_ACK, l3_1tr6_rel_ack}
 };
 
-#define DATASTLN1_LEN \
-	(sizeof(datastln1) / sizeof(struct stateentry))
-
 static struct stateentry manstatelist[] =
 {
         {SBIT(2),
@@ -746,8 +740,6 @@ static struct stateentry manstatelist[] =
          DL_RELEASE | INDICATION, l3_1tr6_dl_release},
 };
  
-#define MANSLLEN \
-        (sizeof(manstatelist) / sizeof(struct stateentry))
 /* *INDENT-ON* */
 
 static void
@@ -840,11 +832,11 @@ up1tr6(struct PStack *st, int pr, void *arg)
 				mt = MT_N1_INVALID;
 			}
 		}
-		for (i = 0; i < DATASTLN1_LEN; i++)
+		for (i = 0; i < ARRAY_SIZE(datastln1); i++)
 			if ((mt == datastln1[i].primitive) &&
 			    ((1 << proc->state) & datastln1[i].state))
 				break;
-		if (i == DATASTLN1_LEN) {
+		if (i == ARRAY_SIZE(datastln1)) {
 			dev_kfree_skb(skb);
 			if (st->l3.debug & L3_DEB_STATE) {
 				sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
@@ -892,11 +884,11 @@ down1tr6(struct PStack *st, int pr, void *arg)
 		proc = arg;
 	}
 
-	for (i = 0; i < DOWNSTL_LEN; i++)
+	for (i = 0; i < ARRAY_SIZE(downstl); i++)
 		if ((pr == downstl[i].primitive) &&
 		    ((1 << proc->state) & downstl[i].state))
 			break;
-	if (i == DOWNSTL_LEN) {
+	if (i == ARRAY_SIZE(downstl)) {
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "down1tr6 state %d prim %d unhandled",
 				proc->state, pr);
@@ -922,11 +914,11 @@ man1tr6(struct PStack *st, int pr, void *arg)
                 printk(KERN_ERR "HiSax man1tr6 without proc pr=%04x\n", pr);
                 return;
         }
-        for (i = 0; i < MANSLLEN; i++)
+        for (i = 0; i < ARRAY_SIZE(manstatelist); i++)
                 if ((pr == manstatelist[i].primitive) &&
                     ((1 << proc->state) & manstatelist[i].state))
                         break;
-        if (i == MANSLLEN) {
+        if (i == ARRAY_SIZE(manstatelist)) {
                 if (st->l3.debug & L3_DEB_STATE) {
                         l3_debug(st, "cr %d man1tr6 state %d prim %d unhandled",
                                 proc->callref & 0x7f, proc->state, pr);

部分文件因为文件数量过多而无法显示