Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v2 0/4] net: Revive fixed link support
From: David Miller @ 2009-07-22 16:20 UTC (permalink / raw)
  To: grant.likely; +Cc: avorontsov, afleming, netdev, linuxppc-dev, leoli
In-Reply-To: <fa686aa40907181137v4a0ed1ednfed3194d1308296a@mail.gmail.com>

From: Grant Likely <grant.likely@secretlab.ca>
Date: Sat, 18 Jul 2009 12:37:34 -0600

> Awesome.  Dave, can you please pick up this series?

Sure thing.

^ permalink raw reply

* Re: [PATCH] be2net: Bug fix in the non-lro path. Size of received packet was not updated in statistics properly.
From: David Miller @ 2009-07-22 16:29 UTC (permalink / raw)
  To: ajitk; +Cc: netdev
In-Reply-To: <20090722111933.GA22465@serverengines.com>

From: Ajit Khaparde <ajitk@serverengines.com>
Date: Wed, 22 Jul 2009 16:49:44 +0530

> This patch fixes a bug in the non-lro path. 
> Wrong size of received packet was being passed for updating receive statistics.
> This patch is against the net-2.6 git.
> 
> Signed-off-by: Ajit Khaparde <ajitk@serverengines.com>

Applied.

^ permalink raw reply

* Re: [PATCH] TCP: Add comments to (near) all functions in tcp_output.c v3
From: David Miller @ 2009-07-22 16:32 UTC (permalink / raw)
  To: andi; +Cc: netdev
In-Reply-To: <20090722090040.GA17474@basil.fritz.box>

From: Andi Kleen <andi@firstfloor.org>
Date: Wed, 22 Jul 2009 11:00:40 +0200

> TCP: Add comments to (near) all functions in tcp_output.c v3
> 
> While looking for something else I spent some time adding
> one liner comments to the tcp_output.c functions that
> didn't have any. That makes the comments more consistent.
> 
> I hope I documented everything right.
> 
> No code changes.
> 
> v2: Incorporated feedback from Ilpo.
> v3: Change style of one liner comments, add a few more comments.
> 
> Signed-off-by: Andi Kleen <ak@linux.intel.com>
> Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>

Applied to net-next-2.6, thanks!


^ permalink raw reply

* Re: [PATCH net-next-2.6] af_packet: style cleanups
From: David Miller @ 2009-07-22 16:32 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev
In-Reply-To: <4A66C687.3050109@gmail.com>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Wed, 22 Jul 2009 09:57:59 +0200

> Some style cleanups to match current code practices.
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>

Applied, thanks.

^ permalink raw reply

* Re: [PATCH] net: KS8851 needs to depend on MII
From: David Miller @ 2009-07-22 16:33 UTC (permalink / raw)
  To: ben; +Cc: a.beregalov, netdev
In-Reply-To: <4A66F5B5.2050606@simtec.co.uk>

From: Ben Dooks <ben@simtec.co.uk>
Date: Wed, 22 Jul 2009 12:19:17 +0100

> Alexander Beregalov wrote:
>> fix this build error when CONFIG_MII is not set
>> drivers/net/ks8851.c:999: undefined reference to `generic_mii_ioctl'
>> drivers/net/ks8851.c:1050: undefined reference to `mii_link_ok'
>> drivers/net/ks8851.c:1056: undefined reference to `mii_nway_restart'
>> drivers/net/ks8851.c:1044: undefined reference to `mii_ethtool_sset'
>> drivers/net/ks8851.c:1038: undefined reference to `mii_ethtool_gset'
> 
> thanks, my test system has an DM9000 so that selected CONFIG_MII for
> me.
> 
>> Signed-off-by: Alexander Beregalov <a.beregalov@gmail.com>
> Acked-by: Ben Dooks <ben@simtec.co.uk>

Applied, thanks.

^ permalink raw reply

* Re: [PATCH v3] net: Rework mdio-ofgpio driver to use of_mdio infrastructure
From: David Miller @ 2009-07-22 16:40 UTC (permalink / raw)
  To: mware; +Cc: grant.likely, netdev, linuxppc-dev
In-Reply-To: <4A66400F.3090309@elphinstone.net>

From: Mark Ware <mware@elphinstone.net>
Date: Wed, 22 Jul 2009 08:24:15 +1000

> Changes to the fs_enet driver
> (aa73832c5a80d6c52c69b18af858d88fa595dd3c) cause kernel crashes when
> using the mdio-ofgpio driver.
> 
> This patch replicates similar changes made to the fs_enet mii-bitbang
> drivers.  It has been tested on a custom mpc8280 based board using an
> NFS mounted root.
> 
> Signed-off-by: Mark Ware <mware@elphinstone.net>
> Acked-by: Grant Likely <grant.likely@secretlab.ca>

Your email client has massively corrupted this patch, I even tried
to fix it up but it's so bad that I just gave up half-way through.

Please use an attachment or whatever is necessary to get this
patch submitted cleanly and without it getting damaged.  Read:

linux/Documentation/email-clients.txt

for tips.

Thanks.

^ permalink raw reply

* Re: [PATCH] sky2: remove unnecessary assignment
From: Stephen Hemminger @ 2009-07-22 16:58 UTC (permalink / raw)
  To: Mike McCormack; +Cc: netdev
In-Reply-To: <392fb48f0907211751k6bbbb6base944c6b7d80a3bf5@mail.gmail.com>

On Wed, 22 Jul 2009 09:51:20 +0900
Mike McCormack <mikem@ring3k.org> wrote:

> Signed-off-by: Mike McCormack <mikem@ring3k.org>
> ---
>  drivers/net/sky2.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
> index 3550c5d..661abd0 100644
> --- a/drivers/net/sky2.c
> +++ b/drivers/net/sky2.c
> @@ -4117,7 +4117,7 @@ static int sky2_debug_show(struct seq_file *seq, void *v)
> 
>  	seq_printf(seq, "\nRx ring hw get=%d put=%d last=%d\n",
>  		   sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_GET_IDX)),
> -		   last = sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
> +		   sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
>  		   sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_LAST_IDX)));
> 
>  	sky2_read32(hw, B0_Y2_SP_LISR);

Acked-by: Stephen Hemminger <shemminger@vyatta.com>

-- 

^ permalink raw reply

* Re: [PATCH] sky2: remove unnecessary assignment
From: Stephen Hemminger @ 2009-07-22 16:59 UTC (permalink / raw)
  To: Mike McCormack; +Cc: netdev
In-Reply-To: <392fb48f0907211751k6bbbb6base944c6b7d80a3bf5@mail.gmail.com>

On Wed, 22 Jul 2009 09:51:20 +0900
Mike McCormack <mikem@ring3k.org> wrote:

> Signed-off-by: Mike McCormack <mikem@ring3k.org>
> ---
>  drivers/net/sky2.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
> index 3550c5d..661abd0 100644
> --- a/drivers/net/sky2.c
> +++ b/drivers/net/sky2.c
> @@ -4117,7 +4117,7 @@ static int sky2_debug_show(struct seq_file *seq, void *v)
> 
>  	seq_printf(seq, "\nRx ring hw get=%d put=%d last=%d\n",
>  		   sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_GET_IDX)),
> -		   last = sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
> +		   sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
>  		   sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_LAST_IDX)));
> 
>  	sky2_read32(hw, B0_Y2_SP_LISR);

Acked-by: Stephen Hemminger <shemminger@vyatta.com>

-- 

^ permalink raw reply

* Re: [PATCH] NET / skge: Enable WoL by default if supported
From: Stephen Hemminger @ 2009-07-22 16:59 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: netdev, pm list, LKML, Michael Guntsche
In-Reply-To: <200907221458.55616.rjw@sisk.pl>

On Wed, 22 Jul 2009 14:58:55 +0200
"Rafael J. Wysocki" <rjw@sisk.pl> wrote:

> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> If skge hardware is capable of waking up the system from sleep,
> enable magic packet WoL during driver initialisation.
> 
> This makes WoL work without calling 'ethtool -s ethX wol g'
> for each adapter.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Tested-by: Michael Guntsche <mike@it-loops.com>
> ---
>  drivers/net/skge.c |    4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> Index: linux-2.6/drivers/net/skge.c
> ===================================================================
> --- linux-2.6.orig/drivers/net/skge.c
> +++ linux-2.6/drivers/net/skge.c
> @@ -3856,8 +3856,10 @@ static struct net_device *skge_devinit(s
>  	skge->speed = -1;
>  	skge->advertising = skge_supported_modes(hw);
>  
> -	if (device_may_wakeup(&hw->pdev->dev))
> +	if (device_can_wakeup(&hw->pdev->dev)) {
>  		skge->wol = wol_supported(hw) & WAKE_MAGIC;
> +		device_set_wakeup_enable(&hw->pdev->dev, skge->wol);
> +	}
>  
>  	hw->dev[port] = dev;
>  

Acked-by: Stephen Hemminger <shemminger@vyatta.com>
-- 

^ permalink raw reply

* Re: the maximal speed of pktgen with 1Gbps NIC
From: Bob Gilligan @ 2009-07-22 17:12 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: Jesper Dangaard Brouer, jon_zhou, netdev
In-Reply-To: <1248275552.4058.0.camel@localhost>

Ben Hutchings wrote:
> On Wed, 2009-07-22 at 14:56 +0200, Jesper Dangaard Brouer wrote:
>> On Wed, 22 Jul 2009, jon_zhou@agilent.com wrote:
>>
>>> anyone knows the maximal speed of pktgen with 1Gbps NIC?
>> You are limited by the number of packets per second (pps) your machine can 
>> generate.  This limitation is given by the CPU speed and the NIC used.
>>
>> The maximum pps I have seen with pktgen at 1Gbit/s is 1200 kpps.
>>
>> 1Gbit/s wire speed pps with 64 bytes packets is approx 1953 kpps.
>> (1000*10^6/(64*8) = 1953125)
> [...]
> 
> You need to take the inter-packet gap into consideration too.

Yes.  Usually when people talk about the "64-byte PPS rate" they are 
referring to the maximum rate of minimum size ethernet frames.  The 
64-bytes includes the src/dst addr fields, type field and CRC, but does 
not include the preamble (7 bytes), SOF (1 byte) or IPG (12 bytes). 
Adding these in  gives a minimum size frame of 84 bytes.  This works out 
to a max rate of ( 1,000,000,000 / (84 * 8) ) = 1,488,095 PPS.

Bob.


^ permalink raw reply

* [PATCH 1/1] IEEE80154: Add documentation to the IEEE80154 netlink and fakehard driver
From: Daniel Silverstone @ 2009-07-22 16:51 UTC (permalink / raw)
  To: netdev; +Cc: Daniel Silverstone

This adds some perfunctory documentation comments to the IEEE 802.15.4
fakehard.c driver (Fake hard MAC) and the nl802154.h (outgoing netlink messages)
header.

These comments are not necessarily complete, but they do reference the
IEEE 802.15.4-2006 document where possible.

Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk>
---
 drivers/ieee802154/fakehard.c     |  120 +++++++++++++++++++++++++++++++++++++
 include/net/ieee802154/nl802154.h |   76 +++++++++++++++++++++++
 2 files changed, 196 insertions(+), 0 deletions(-)

diff --git a/drivers/ieee802154/fakehard.c b/drivers/ieee802154/fakehard.c
index 0384144..9ec07e8 100644
--- a/drivers/ieee802154/fakehard.c
+++ b/drivers/ieee802154/fakehard.c
@@ -31,6 +31,12 @@
 #include <net/ieee802154/mac_def.h>
 #include <net/ieee802154/nl802154.h>
 
+/**
+ * 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 +44,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 +59,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 +79,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,6 +99,19 @@ 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)
 {
@@ -67,18 +120,70 @@ static int fake_assoc_req(struct net_device *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.
+ *
+ * Note: There is currently no way to notify the coordinator userland
+ * program of whether or not the PAN has started successfully. As
+ * such, the coordinator program cannot know when the MAC has
+ * completed starting the network and will simply have to assume
+ * completeness based on some form of time delay.
+ */
 static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr,
 				u8 channel,
 				u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
@@ -87,6 +192,21 @@ static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr,
 	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)
 {
diff --git a/include/net/ieee802154/nl802154.h b/include/net/ieee802154/nl802154.h
index 78efcdf..6096096 100644
--- a/include/net/ieee802154/nl802154.h
+++ b/include/net/ieee802154/nl802154.h
@@ -24,17 +24,93 @@
 struct net_device;
 struct ieee802154_addr;
 
+/**
+ * ieee802154_nl_assoc_indic - Notify userland of an association request.
+ * @dev: The network device on which this association request was
+ *       received.
+ * @addr: The address of the device requesting association.
+ * @cap: The capability information field from the device.
+ *
+ * This informs a userland coordinator of a device requesting to
+ * associate with the PAN controlled by the coordinator.
+ *
+ * Note: This is in section 7.3.1 of the IEEE 802.15.4-2006 document.
+ */
 int ieee802154_nl_assoc_indic(struct net_device *dev,
 		struct ieee802154_addr *addr, u8 cap);
+
+/**
+ * ieee802154_nl_assoc_confirm - Notify userland of association.
+ * @dev: The device which has completed association.
+ * @short_addr: The short address assigned to the device.
+ * @status: The status of the association.
+ *
+ * Inform userland of the result of an association request. If the
+ * association request included asking the coordinator to allocate
+ * a short address then it is returned in @short_addr.
+ *
+ * Note: This is in section 7.3.2 of the IEEE 802.15.4 document.
+ */
 int ieee802154_nl_assoc_confirm(struct net_device *dev,
 		u16 short_addr, u8 status);
+
+/**
+ * ieee802154_nl_disassoc_indic - Notify userland of disassociation.
+ * @dev: The device on which disassociation was indicated.
+ * @addr: The device which is disassociating.
+ * @reason: The reason for the disassociation.
+ *
+ * Inform userland that a device has disassociated from the network.
+ *
+ * Note: This is in section 7.3.3 of the IEEE 802.15.4 document.
+ */
 int ieee802154_nl_disassoc_indic(struct net_device *dev,
 		struct ieee802154_addr *addr, u8 reason);
+
+/**
+ * ieee802154_nl_disassoc_confirm - Notify userland of disassociation
+ * completion.
+ * @dev: The device on which disassociation was ordered.
+ * @status: The result of the disassociation.
+ *
+ * Inform userland of the result of requesting that a device
+ * disassociate, or the result of requesting that we disassociate from
+ * a PAN managed by another coordinator.
+ *
+ * Note: This is in section 7.1.4.3 of the IEEE 802.15.4 document.
+ */
 int ieee802154_nl_disassoc_confirm(struct net_device *dev,
 		u8 status);
+
+/**
+ * ieee802154_nl_scan_confirm - Notify userland of completion of scan.
+ * @dev: The device which was instructed to scan.
+ * @status: The status of the scan operation.
+ * @scan_type: What type of scan was performed.
+ * @unscanned: Any channels that the device was unable to scan.
+ * @edl: The energy levels (if a passive scan).
+ *
+ *
+ * Note: This is in section 7.1.11 of the IEEE 802.15.4 document.
+ * Note: This API does not permit the return of an active scan result.
+ */
 int ieee802154_nl_scan_confirm(struct net_device *dev,
 		u8 status, u8 scan_type, u32 unscanned,
 		u8 *edl/*, struct list_head *pan_desc_list */);
+
+/**
+ * ieee802154_nl_beacon_indic - Notify userland of a received beacon.
+ * @dev: The device on which a beacon was received.
+ * @panid: The PAN of the coordinator.
+ * @coord_addr: The short address of the coordinator on that PAN.
+ *
+ * Note: This is in section 7.1.5 of the IEEE 802.15.4 document.
+ * Note: This API does not provide extended information such as what
+ * channel the PAN is on or what the LQI of the beacon frame was on
+ * receipt.
+ * Note: This API cannot indicate a beacon frame for a coordinator
+ *       operating in long addressing mode.
+ */
 int ieee802154_nl_beacon_indic(struct net_device *dev, u16 panid,
 		u16 coord_addr);
 
-- 
1.5.4.3


^ permalink raw reply related

* Re: the maximal speed of pktgen with 1Gbps NIC
From: Stephen Hemminger @ 2009-07-22 17:25 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: Jesper Dangaard Brouer, jon_zhou, netdev
In-Reply-To: <1248275552.4058.0.camel@localhost>

On Wed, 22 Jul 2009 17:12:32 +0200
Ben Hutchings <bhutchings@solarflare.com> wrote:

> On Wed, 2009-07-22 at 14:56 +0200, Jesper Dangaard Brouer wrote:
> > On Wed, 22 Jul 2009, jon_zhou@agilent.com wrote:
> > 
> > > anyone knows the maximal speed of pktgen with 1Gbps NIC?
> > 
> > You are limited by the number of packets per second (pps) your machine can 
> > generate.  This limitation is given by the CPU speed and the NIC used.
> > 
> > The maximum pps I have seen with pktgen at 1Gbit/s is 1200 kpps.
> > 
> > 1Gbit/s wire speed pps with 64 bytes packets is approx 1953 kpps.
> > (1000*10^6/(64*8) = 1953125)
> [...]
> 
> You need to take the inter-packet gap into consideration too.
> 
> Ben.

Speed	Packets/sec	TCP MByte/sec
 10 M	    14880.95	     1,170,351
100 M	   148809.52	    11,703,511
  1 G	  1488095.24	   123,755,256
 10 G	 14880952.38	 1,237,552,556
 40 G	 59523809.52	 4,950,210,224
100 G	148809523.81	12,375,525,559

* TCP assumes timestamp option and jumbo frames if possible.


http://www.erg.abdn.ac.uk/users/gorry/course/lan-pages/enet-calc.html
-- 

^ permalink raw reply

* Re: [PATCH] register at91_ether using platform_driver_probe
From: David Brownell @ 2009-07-22 17:37 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, David Brownell, netdev, Paulius Zaleckas,
	Marc Pignat, Andrew Victor, Roel Kluin, Andrew Morton,
	Greg Kroah-Hartman
In-Reply-To: <1248207099-14583-1-git-send-email-u.kleine-koenig@pengutronix.de>

On Tuesday 21 July 2009, Uwe Kleine-König wrote:
> at91ether_probe lives in .init.text, so using platform_driver_register
> to register it is wrong because binding a device after the init memory
> is discarded (e.g. via sysfs) results in an oops.
> 
> As requested by David Brownell platform_driver_probe is used instead of
> moving the probe function to .devinit.text as proposed initially.
> This saves some memory, but devices registered after the driver is
> probed are not bound (probably there are none) and binding via sysfs
> isn't possible.

Right, these devices do not hotplug.

> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Cc: David Brownell <dbrownell@users.sourceforge.net>

Acked-by: David Brownell <dbrownell@users.sourceforge.net>

.... and thanks for doing it this way.  :)

> Cc: netdev@vger.kernel.org
> Cc: Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
> Cc: Marc Pignat <marc.pignat@hevs.ch>
> Cc: Andrew Victor <linux@maxim.org.za>
> Cc: Roel Kluin <12o3l@tiscali.nl>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Greg Kroah-Hartman <gregkh@suse.de>
> 
> ---
>  drivers/net/arm/at91_ether.c |    3 +--
>  1 files changed, 1 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c
> index 2e7419a..5041d10 100644
> --- a/drivers/net/arm/at91_ether.c
> +++ b/drivers/net/arm/at91_ether.c
> @@ -1228,7 +1228,6 @@ static int at91ether_resume(struct platform_device *pdev)
>  #endif
>  
>  static struct platform_driver at91ether_driver = {
> -	.probe		= at91ether_probe,
>  	.remove		= __devexit_p(at91ether_remove),
>  	.suspend	= at91ether_suspend,
>  	.resume		= at91ether_resume,
> @@ -1240,7 +1239,7 @@ static struct platform_driver at91ether_driver = {
>  
>  static int __init at91ether_init(void)
>  {
> -	return platform_driver_register(&at91ether_driver);
> +	return platform_driver_probe(&at91ether_driver, at91ether_probe);
>  }
>  
>  static void __exit at91ether_exit(void)
> -- 
> tg: (4b0a840..) t/platsection/at91ether_driver (depends on: linus/master)
> 
> 

^ permalink raw reply

* Re: [PATCH] register at91_ether using platform_driver_probe
From: Andrew Victor @ 2009-07-22 17:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-kernel, David Brownell, netdev, Paulius Zaleckas,
	Marc Pignat, Andrew Victor, Roel Kluin, Andrew Morton,
	Greg Kroah-Hartman
In-Reply-To: <1248207099-14583-1-git-send-email-u.kleine-koenig@pengutronix.de>

hi,

> at91ether_probe lives in .init.text, so using platform_driver_register
> to register it is wrong because binding a device after the init memory
> is discarded (e.g. via sysfs) results in an oops.
>
> As requested by David Brownell platform_driver_probe is used instead of
> moving the probe function to .devinit.text as proposed initially.
> This saves some memory, but devices registered after the driver is
> probed are not bound (probably there are none) and binding via sysfs
> isn't possible.


Acked-by: Andrew Victor <linux@maxim.org.za>

^ permalink raw reply

* pull request: wireless-2.6 2009-07-22
From: John W. Linville @ 2009-07-22 17:47 UTC (permalink / raw)
  To: davem; +Cc: linux-wireless, netdev, linux-kernel

Dave,

Here is a collection of fixes intended for 2.6.31.  I think they mostly
speak for themselves and are uncontroversial.

Please let me know if there are problems!

Thanks,

John

---

Individual patches are available here:

	http://www.kernel.org/pub/linux/kernel/people/linville/wireless-2.6/

---

The following changes since commit 1b0d92244ff2434a98272f6d2525da32fc230f19:
  Wolfgang Grandegger (1):
        can: switch carrier on if device was stopped while in bus-off state

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git master

Alan Jenkins (3):
      rfkill: fix rfkill_set_states() to set the hw state
      rfkill: remove too-strict __must_check
      acer-wmi: fix rfkill conversion

Andy Whitcroft (1):
      rt2x00: Fix chipset detection for rt2500usb

Bob Copeland (1):
      ath5k: temporarily disable crypto for AP mode

Christian Lamparter (1):
      cfg80211: double free in __cfg80211_scan_done

Dan Carpenter (1):
      p54spi: fix potential null deref in p54spi.c

Javier Cardona (1):
      mac80211: use correct address for mesh Path Error

Johannes Berg (4):
      mac80211: disable mesh
      rfkill: allow toggling soft state in sysfs again
      mac80211_hwsim: fix unregistration
      mac80211_hwsim: fix use after free

Larry Finger (1):
      rtl8187: Fix for kernel oops when unloading with LEDs enabled

Luis R. Rodriguez (2):
      ath9k: Tune ANI function processing on AP mode during ANI reset
      ath: add support for special 0x8000 regulatory domain

Marek Vasut (1):
      libertas: Fix problem with broken V4 firmware on CF8381

Niko Jokinen (1):
      nl80211: Memory leak fixed

Pavel Roskin (1):
      mac80211: fix injection in monitor mode

Reinette Chatre (2):
      iwlwifi: only show active power level via sysfs
      iwlwifi: only update byte count table during aggregation

Zhu Yi (1):
      iwmc3200wifi: fix NULL pointer dereference in iwm_if_free

 drivers/net/wireless/ath/ath5k/base.c       |    3 ++
 drivers/net/wireless/ath/ath9k/ani.c        |   12 ++++++++++
 drivers/net/wireless/ath/regd.c             |   17 ++++++++++++++
 drivers/net/wireless/iwlwifi/iwl-agn.c      |    4 +--
 drivers/net/wireless/iwlwifi/iwl-tx.c       |    3 +-
 drivers/net/wireless/iwlwifi/iwl3945-base.c |    4 +--
 drivers/net/wireless/iwmc3200wifi/netdev.c  |    2 +-
 drivers/net/wireless/libertas/cmd.c         |    8 ++++++-
 drivers/net/wireless/libertas/defs.h        |    2 +
 drivers/net/wireless/mac80211_hwsim.c       |    6 ++--
 drivers/net/wireless/p54/p54spi.c           |    2 +-
 drivers/net/wireless/rt2x00/rt2500usb.c     |    4 ++-
 drivers/net/wireless/rtl818x/rtl8187_leds.c |    3 +-
 drivers/platform/x86/acer-wmi.c             |    2 +-
 include/linux/rfkill.h                      |    2 +-
 net/mac80211/Kconfig                        |    1 +
 net/mac80211/mesh_pathtbl.c                 |   11 +++++++--
 net/mac80211/tx.c                           |    2 +-
 net/rfkill/core.c                           |   31 ++++++++++++++++++++-------
 net/wireless/nl80211.c                      |    5 ++-
 net/wireless/scan.c                         |    3 +-
 21 files changed, 94 insertions(+), 33 deletions(-)

diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index ea04515..029c1bc 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -2970,6 +2970,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 	if (modparam_nohwcrypt)
 		return -EOPNOTSUPP;
 
+	if (sc->opmode == NL80211_IFTYPE_AP)
+		return -EOPNOTSUPP;
+
 	switch (key->alg) {
 	case ALG_WEP:
 	case ALG_TKIP:
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index 1aeafb5..aad259b 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -478,6 +478,18 @@ void ath9k_ani_reset(struct ath_hw *ah)
 			"Reset ANI state opmode %u\n", ah->opmode);
 		ah->stats.ast_ani_reset++;
 
+		if (ah->opmode == NL80211_IFTYPE_AP) {
+			/*
+			 * ath9k_hw_ani_control() will only process items set on
+			 * ah->ani_function
+			 */
+			if (IS_CHAN_2GHZ(chan))
+				ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL |
+						    ATH9K_ANI_FIRSTEP_LEVEL);
+			else
+				ah->ani_function = 0;
+		}
+
 		ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
 		ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
 		ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index eef370b..bf3d25b 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -474,6 +474,21 @@ ath_regd_init_wiphy(struct ath_regulatory *reg,
 	return 0;
 }
 
+/*
+ * Some users have reported their EEPROM programmed with
+ * 0x8000 set, this is not a supported regulatory domain
+ * but since we have more than one user with it we need
+ * a solution for them. We default to 0x64, which is the
+ * default Atheros world regulatory domain.
+ */
+static void ath_regd_sanitize(struct ath_regulatory *reg)
+{
+	if (reg->current_rd != COUNTRY_ERD_FLAG)
+		return;
+	printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n");
+	reg->current_rd = 0x64;
+}
+
 int
 ath_regd_init(struct ath_regulatory *reg,
 	      struct wiphy *wiphy,
@@ -486,6 +501,8 @@ ath_regd_init(struct ath_regulatory *reg,
 	if (!reg)
 		return -EINVAL;
 
+	ath_regd_sanitize(reg);
+
 	printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd);
 
 	if (!ath_regd_is_eeprom_valid(reg)) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 6d1519e..355f50e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2675,12 +2675,10 @@ static ssize_t show_power_level(struct device *d,
 				struct device_attribute *attr, char *buf)
 {
 	struct iwl_priv *priv = dev_get_drvdata(d);
-	int mode = priv->power_data.user_power_setting;
 	int level = priv->power_data.power_mode;
 	char *p = buf;
 
-	p += sprintf(p, "INDEX:%d\t", level);
-	p += sprintf(p, "USER:%d\n", mode);
+	p += sprintf(p, "%d\n", level);
 	return p - buf + 1;
 }
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 85ae7a6..9bbeec9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -872,7 +872,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
 	iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len);
 
 	/* Set up entry for this TFD in Tx byte-count array */
-	priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq,
+	if (info->flags & IEEE80211_TX_CTL_AMPDU)
+		priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq,
 						     le16_to_cpu(tx_cmd->len));
 
 	pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys,
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index cb9bd4c..956798f 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -3643,12 +3643,10 @@ static ssize_t show_power_level(struct device *d,
 				struct device_attribute *attr, char *buf)
 {
 	struct iwl_priv *priv = dev_get_drvdata(d);
-	int mode = priv->power_data.user_power_setting;
 	int level = priv->power_data.power_mode;
 	char *p = buf;
 
-	p += sprintf(p, "INDEX:%d\t", level);
-	p += sprintf(p, "USER:%d\n", mode);
+	p += sprintf(p, "%d\n", level);
 	return p - buf + 1;
 }
 
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index aaa20c6..aea5ccf 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -151,8 +151,8 @@ void iwm_if_free(struct iwm_priv *iwm)
 		return;
 
 	free_netdev(iwm_to_ndev(iwm));
-	iwm_wdev_free(iwm);
 	iwm_priv_deinit(iwm);
+	iwm_wdev_free(iwm);
 }
 
 int iwm_if_add(struct iwm_priv *iwm)
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 01db705..6850981 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -135,8 +135,14 @@ int lbs_update_hw_spec(struct lbs_private *priv)
 	/* Clamp region code to 8-bit since FW spec indicates that it should
 	 * only ever be 8-bit, even though the field size is 16-bit.  Some firmware
 	 * returns non-zero high 8 bits here.
+	 *
+	 * Firmware version 4.0.102 used in CF8381 has region code shifted.  We
+	 * need to check for this problem and handle it properly.
 	 */
-	priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
+	if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4)
+		priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF;
+	else
+		priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
 
 	for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
 		/* use the region code to search for the index */
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h
index 48da157..72f3479 100644
--- a/drivers/net/wireless/libertas/defs.h
+++ b/drivers/net/wireless/libertas/defs.h
@@ -234,6 +234,8 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
 /** Mesh enable bit in FW capability */
 #define MESH_CAPINFO_ENABLE_MASK			(1<<16)
 
+/** FW definition from Marvell v4 */
+#define MRVL_FW_V4					(0x04)
 /** FW definition from Marvell v5 */
 #define MRVL_FW_V5					(0x05)
 /** FW definition from Marvell v10 */
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index a111bda..7916ca3 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -709,7 +709,7 @@ static const struct ieee80211_ops mac80211_hwsim_ops =
 static void mac80211_hwsim_free(void)
 {
 	struct list_head tmplist, *i, *tmp;
-	struct mac80211_hwsim_data *data;
+	struct mac80211_hwsim_data *data, *tmpdata;
 
 	INIT_LIST_HEAD(&tmplist);
 
@@ -718,7 +718,7 @@ static void mac80211_hwsim_free(void)
 		list_move(i, &tmplist);
 	spin_unlock_bh(&hwsim_radio_lock);
 
-	list_for_each_entry(data, &tmplist, list) {
+	list_for_each_entry_safe(data, tmpdata, &tmplist, list) {
 		debugfs_remove(data->debugfs_group);
 		debugfs_remove(data->debugfs_ps);
 		debugfs_remove(data->debugfs);
@@ -1167,8 +1167,8 @@ static void __exit exit_mac80211_hwsim(void)
 {
 	printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");
 
-	unregister_netdev(hwsim_mon);
 	mac80211_hwsim_free();
+	unregister_netdev(hwsim_mon);
 }
 
 
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index 83116ba..72c7dbd 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -635,7 +635,7 @@ static int __devinit p54spi_probe(struct spi_device *spi)
 
 	hw = p54_init_common(sizeof(*priv));
 	if (!hw) {
-		dev_err(&priv->spi->dev, "could not alloc ieee80211_hw");
+		dev_err(&spi->dev, "could not alloc ieee80211_hw");
 		return -ENOMEM;
 	}
 
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 66daf68..ce75426 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1550,7 +1550,9 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
 	rt2500usb_register_read(rt2x00dev, MAC_CSR0, &reg);
 	rt2x00_set_chip(rt2x00dev, RT2570, value, reg);
 
-	if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0)) {
+	if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0) ||
+	    rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) {
+
 		ERROR(rt2x00dev, "Invalid RT chipset detected.\n");
 		return -ENODEV;
 	}
diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c
index b442535..cf9f899 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_leds.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c
@@ -208,11 +208,12 @@ void rtl8187_leds_exit(struct ieee80211_hw *dev)
 {
 	struct rtl8187_priv *priv = dev->priv;
 
-	rtl8187_unregister_led(&priv->led_tx);
 	/* turn the LED off before exiting */
 	queue_delayed_work(dev->workqueue, &priv->led_off, 0);
 	cancel_delayed_work_sync(&priv->led_off);
+	cancel_delayed_work_sync(&priv->led_on);
 	rtl8187_unregister_led(&priv->led_rx);
+	rtl8187_unregister_led(&priv->led_tx);
 }
 #endif /* def CONFIG_RTL8187_LED */
 
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index be2fd6f..fb45f5e 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -973,7 +973,7 @@ static int acer_rfkill_set(void *data, bool blocked)
 {
 	acpi_status status;
 	u32 cap = (unsigned long)data;
-	status = set_u32(!!blocked, cap);
+	status = set_u32(!blocked, cap);
 	if (ACPI_FAILURE(status))
 		return -ENODEV;
 	return 0;
diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
index 2ce2983..278777f 100644
--- a/include/linux/rfkill.h
+++ b/include/linux/rfkill.h
@@ -224,7 +224,7 @@ void rfkill_destroy(struct rfkill *rfkill);
  * should be blocked) so that drivers need not keep track of the soft
  * block state -- which they might not be able to.
  */
-bool __must_check rfkill_set_hw_state(struct rfkill *rfkill, bool blocked);
+bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked);
 
 /**
  * rfkill_set_sw_state - Set the internal rfkill software block state
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index ba2643a..7836ee9 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -83,6 +83,7 @@ endmenu
 config MAC80211_MESH
 	bool "Enable mac80211 mesh networking (pre-802.11s) support"
 	depends on MAC80211 && EXPERIMENTAL
+	depends on BROKEN
 	---help---
 	 This options enables support of Draft 802.11s mesh networking.
 	 The implementation is based on Draft 1.08 of the Mesh Networking
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 3c72557..479597e 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -175,6 +175,8 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
 	int err = 0;
 	u32 hash_idx;
 
+	might_sleep();
+
 	if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0)
 		/* never add ourselves as neighbours */
 		return -ENOTSUPP;
@@ -265,6 +267,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
 	int err = 0;
 	u32 hash_idx;
 
+	might_sleep();
 
 	if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0)
 		/* never add ourselves as neighbours */
@@ -491,8 +494,10 @@ void mesh_path_tx_pending(struct mesh_path *mpath)
  * @skb: frame to discard
  * @sdata: network subif the frame was to be sent through
  *
- * If the frame was beign forwarded from another MP, a PERR frame will be sent
- * to the precursor.
+ * If the frame was being forwarded from another MP, a PERR frame will be sent
+ * to the precursor.  The precursor's address (i.e. the previous hop) was saved
+ * in addr1 of the frame-to-be-forwarded, and would only be overwritten once
+ * the destination is successfully resolved.
  *
  * Locking: the function must me called within a rcu_read_lock region
  */
@@ -507,7 +512,7 @@ void mesh_path_discard_frame(struct sk_buff *skb,
 		u8 *ra, *da;
 
 		da = hdr->addr3;
-		ra = hdr->addr2;
+		ra = hdr->addr1;
 		mpath = mesh_path_lookup(da, sdata);
 		if (mpath)
 			dsn = ++mpath->dsn;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index d238a89..3a8922c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1455,7 +1455,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		monitor_iface = UNKNOWN_ADDRESS;
 
 		len_rthdr = ieee80211_get_radiotap_len(skb->data);
-		hdr = (struct ieee80211_hdr *)skb->data + len_rthdr;
+		hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
 		hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
 		/* check the header is complete in the frame */
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 79693fe..2fc4a17 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -549,6 +549,10 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
 	swprev = !!(rfkill->state & RFKILL_BLOCK_SW);
 	hwprev = !!(rfkill->state & RFKILL_BLOCK_HW);
 	__rfkill_set_sw_state(rfkill, sw);
+	if (hw)
+		rfkill->state |= RFKILL_BLOCK_HW;
+	else
+		rfkill->state &= ~RFKILL_BLOCK_HW;
 
 	spin_unlock_irqrestore(&rfkill->lock, flags);
 
@@ -648,15 +652,26 @@ static ssize_t rfkill_state_store(struct device *dev,
 				  struct device_attribute *attr,
 				  const char *buf, size_t count)
 {
-	/*
-	 * The intention was that userspace can only take control over
-	 * a given device when/if rfkill-input doesn't control it due
-	 * to user_claim. Since user_claim is currently unsupported,
-	 * we never support changing the state from userspace -- this
-	 * can be implemented again later.
-	 */
+	struct rfkill *rfkill = to_rfkill(dev);
+	unsigned long state;
+	int err;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	err = strict_strtoul(buf, 0, &state);
+	if (err)
+		return err;
+
+	if (state != RFKILL_USER_STATE_SOFT_BLOCKED &&
+	    state != RFKILL_USER_STATE_UNBLOCKED)
+		return -EINVAL;
+
+	mutex_lock(&rfkill_global_mutex);
+	rfkill_set_block(rfkill, state == RFKILL_USER_STATE_SOFT_BLOCKED);
+	mutex_unlock(&rfkill_global_mutex);
 
-	return -EPERM;
+	return err ?: count;
 }
 
 static ssize_t rfkill_claim_show(struct device *dev,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 43bdb13..634496b 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -997,7 +997,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 
 	if (IS_ERR(hdr)) {
 		err = PTR_ERR(hdr);
-		goto out;
+		goto free_msg;
 	}
 
 	cookie.msg = msg;
@@ -1011,7 +1011,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 				&cookie, get_key_callback);
 
 	if (err)
-		goto out;
+		goto free_msg;
 
 	if (cookie.error)
 		goto nla_put_failure;
@@ -1022,6 +1022,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 
  nla_put_failure:
 	err = -ENOBUFS;
+ free_msg:
 	nlmsg_free(msg);
  out:
 	cfg80211_put_dev(drv);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index f8e71b3..9271118 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -35,8 +35,6 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
 	else
 		nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev);
 
-	wiphy_to_dev(request->wiphy)->scan_req = NULL;
-
 #ifdef CONFIG_WIRELESS_EXT
 	if (!aborted) {
 		memset(&wrqu, 0, sizeof(wrqu));
@@ -48,6 +46,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
 	dev_put(dev);
 
  out:
+	wiphy_to_dev(request->wiphy)->scan_req = NULL;
 	kfree(request);
 }
 EXPORT_SYMBOL(cfg80211_scan_done);
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.
			¡Viva Honduras Libre!

^ permalink raw reply related

* [PATCH 07/13] mISDN: Driver for AVM Fritz!CARD PCI
From: Karsten Keil @ 2009-07-22 17:52 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, David Miller,
	i4ldeveloper-JX7+OpRa80SjiSfgN6Y1Ib39b6g2fGNp
In-Reply-To: <cover.1248546896.git.keil-shG/GajIFYqbacvFa/9K2g@public.gmane.org>

Add mISDN driver for AVM FRITZ!CARD PCI (all versions).

Signed-off-by: Karsten Keil <keil-shG/GajIFYqbacvFa/9K2g@public.gmane.org>
---
 drivers/isdn/hardware/mISDN/Kconfig    |    8 +
 drivers/isdn/hardware/mISDN/Makefile   |    1 +
 drivers/isdn/hardware/mISDN/avmfritz.c | 1152 ++++++++++++++++++++++++++++++++
 3 files changed, 1161 insertions(+), 0 deletions(-)
 create mode 100644 drivers/isdn/hardware/mISDN/avmfritz.c

diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
index a8542b8..212dee6 100644
--- a/drivers/isdn/hardware/mISDN/Kconfig
+++ b/drivers/isdn/hardware/mISDN/Kconfig
@@ -39,6 +39,14 @@ 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_INFINEON
 	tristate "Support for cards with Infineon chipset"
 	depends on MISDN
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
index 2863455..8204d84 100644
--- a/drivers/isdn/hardware/mISDN/Makefile
+++ b/drivers/isdn/hardware/mISDN/Makefile
@@ -6,6 +6,7 @@
 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_INFINEON) += mISDNinfineon.o
 # chip modules
 obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c
new file mode 100644
index 0000000..81ac541
--- /dev/null
+++ b/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-4YnUg6GLQKNUnpShzZ3juw@public.gmane.org>
+ *
+ * Copyright 2009  by Karsten Keil <keil-4YnUg6GLQKNUnpShzZ3juw@public.gmane.org>
+ *
+ * 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);
-- 
1.6.0.2

^ permalink raw reply related

* [PATCH 08/13] mISDN: Add support for Speedfax+ cards
From: Karsten Keil @ 2009-07-22 18:01 UTC (permalink / raw)
  To: linux-kernel; +Cc: netdev, David Miller, i4ldeveloper
In-Reply-To: <cover.1248546896.git.keil@b1-systems.de>

Add support for the Siemens ISAR DSP chip and cards based on it,
including analog modem protocols.

Signed-off-by: Karsten Keil <keil@b1-systems.de>
---
 drivers/isdn/hardware/mISDN/Kconfig     |   13 +
 drivers/isdn/hardware/mISDN/Makefile    |    2 +
 drivers/isdn/hardware/mISDN/isar.h      |  269 +++++
 drivers/isdn/hardware/mISDN/mISDNisar.c | 1726 +++++++++++++++++++++++++++++++
 drivers/isdn/hardware/mISDN/speedfax.c  |  526 ++++++++++
 include/linux/mISDNif.h                 |   16 +-
 6 files changed, 2551 insertions(+), 1 deletions(-)
 create mode 100644 drivers/isdn/hardware/mISDN/isar.h
 create mode 100644 drivers/isdn/hardware/mISDN/mISDNisar.c
 create mode 100644 drivers/isdn/hardware/mISDN/speedfax.c

diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
index 212dee6..2600534 100644
--- a/drivers/isdn/hardware/mISDN/Kconfig
+++ b/drivers/isdn/hardware/mISDN/Kconfig
@@ -47,6 +47,15 @@ config MISDN_AVMFRITZ
 	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
@@ -61,3 +70,7 @@ config MISDN_IPAC
 	tristate
 	depends on MISDN
 
+config MISDN_ISAR
+	tristate
+	depends on MISDN
+
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
index 8204d84..e18f964 100644
--- a/drivers/isdn/hardware/mISDN/Makefile
+++ b/drivers/isdn/hardware/mISDN/Makefile
@@ -7,6 +7,8 @@ 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
 # chip modules
 obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
+obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o
diff --git a/drivers/isdn/hardware/mISDN/isar.h b/drivers/isdn/hardware/mISDN/isar.h
new file mode 100644
index 0000000..092351a
--- /dev/null
+++ b/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_t	*read_reg;
+	write_reg_t	*write_reg;
+	fifo_func_t	*read_fifo;
+	fifo_func_t	*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 *);
diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c
new file mode 100644
index 0000000..de352a1
--- /dev/null
+++ b/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);
diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c
new file mode 100644
index 0000000..ff3a4e2
--- /dev/null
+++ b/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);
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
index 45100b3..536ca12 100644
--- a/include/linux/mISDNif.h
+++ b/include/linux/mISDNif.h
@@ -37,7 +37,7 @@
  */
 #define	MISDN_MAJOR_VERSION	1
 #define	MISDN_MINOR_VERSION	1
-#define MISDN_RELEASE		20
+#define MISDN_RELEASE		21
 
 /* primitives for information exchange
  * generell format
@@ -153,6 +153,18 @@
 #define HFC_VOL_CHANGE_RX	0x2602
 #define HFC_SPL_LOOP_ON		0x2603
 #define HFC_SPL_LOOP_OFF	0x2604
+/* for T30 FAX and analog modem */
+#define HW_MOD_FRM		0x4000
+#define HW_MOD_FRH		0x4001
+#define HW_MOD_FTM		0x4002
+#define HW_MOD_FTH		0x4003
+#define HW_MOD_FTS		0x4004
+#define HW_MOD_CONNECT		0x4010
+#define HW_MOD_OK		0x4011
+#define HW_MOD_NOCARR		0x4012
+#define HW_MOD_FCERROR		0x4013
+#define HW_MOD_READY		0x4014
+#define HW_MOD_LASTDATA		0x4015
 
 /* DSP_TONE_PATT_ON parameter */
 #define TONE_OFF			0x0000
@@ -224,6 +236,8 @@
 #define ISDN_P_B_L2DTMF		0x24
 #define ISDN_P_B_L2DSP		0x25
 #define ISDN_P_B_L2DSPHDLC	0x26
+#define ISDN_P_B_T30_FAX	0x27
+#define ISDN_P_B_MODEM_ASYNC	0x28
 
 #define OPTION_L2_PMX		1
 #define OPTION_L2_PTP		2
-- 
1.6.0.2

^ permalink raw reply related

* [PATCH 09/13] mISDN: Add driver for Winbond cards
From: Karsten Keil @ 2009-07-22 18:06 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, David Miller,
	i4ldeveloper-JX7+OpRa80SjiSfgN6Y1Ib39b6g2fGNp
In-Reply-To: <cover.1248546896.git.keil-shG/GajIFYqbacvFa/9K2g@public.gmane.org>

Add driver for Winbond W6692 based PCI cards.

Signed-off-by: Karsten Keil <keil-shG/GajIFYqbacvFa/9K2g@public.gmane.org>
---
 drivers/isdn/hardware/mISDN/Kconfig  |    7 +
 drivers/isdn/hardware/mISDN/Makefile |    1 +
 drivers/isdn/hardware/mISDN/w6692.c  | 1440 ++++++++++++++++++++++++++++++++++
 drivers/isdn/hardware/mISDN/w6692.h  |  190 +++++
 4 files changed, 1638 insertions(+), 0 deletions(-)
 create mode 100644 drivers/isdn/hardware/mISDN/w6692.c
 create mode 100644 drivers/isdn/hardware/mISDN/w6692.h

diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
index 2600534..d72586e 100644
--- a/drivers/isdn/hardware/mISDN/Kconfig
+++ b/drivers/isdn/hardware/mISDN/Kconfig
@@ -65,6 +65,13 @@ config MISDN_INFINEON
 	  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_IPAC
 	tristate
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
index e18f964..61dd5a5 100644
--- a/drivers/isdn/hardware/mISDN/Makefile
+++ b/drivers/isdn/hardware/mISDN/Makefile
@@ -9,6 +9,7 @@ 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
 # chip modules
 obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
 obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c
new file mode 100644
index 0000000..1b9008f
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/w6692.c
@@ -0,0 +1,1440 @@
+/*
+ * w6692.c     mISDN driver for Winbond w6692 based cards
+ *
+ * Author      Karsten Keil <kkeil-l3A5Bk7waGM@public.gmane.org>
+ *             based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
+ *
+ * Copyright 2009  by Karsten Keil <keil-4YnUg6GLQKNUnpShzZ3juw@public.gmane.org>
+ *
+ * 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;
+}
+
+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);
diff --git a/drivers/isdn/hardware/mISDN/w6692.h b/drivers/isdn/hardware/mISDN/w6692.h
new file mode 100644
index 0000000..f956977
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/w6692.h
@@ -0,0 +1,190 @@
+/*
+ * Winbond W6692 specific defines
+ *
+ * Author       Karsten Keil <keil-4YnUg6GLQKNUnpShzZ3juw@public.gmane.org>
+ *		based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
+ *
+ * Copyright 2009  by Karsten Keil <keil-4YnUg6GLQKNUnpShzZ3juw@public.gmane.org>
+ *
+ * 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.6.0.2

^ permalink raw reply related

* Re: ixgbe: panic in ixgbe_clean_rx_irq()
From: Waskiewicz Jr, Peter P @ 2009-07-22 18:13 UTC (permalink / raw)
  To: Jesper Dangaard Brouer; +Cc: Waskiewicz Jr, Peter P, netdev@vger.kernel.org
In-Reply-To: <1248266678.17215.15.camel@localhost.localdomain>

On Wed, 22 Jul 2009, Jesper Dangaard Brouer wrote:

> Hi Peter,
> 
> Got a panic from the ixgbe driver on my 82599 based NICs, while running
> two pktgen tests (1500 bytes packets) against the machine.
> 
> Kernel: 2.6.31-rc1-net-2.6-00122-ge594e96 with preemption
> 
> The panic occurs in ixgbe_clean_rx_irq+0x319/0x53b
> 
> Using objdump I can see that it seems to occur in an inlined function
> ixgbe_transform_rsc_queue() in objdump "line" "2811:"
> (ixgbe_clean_rx_irq starts 0x24f8 + 0x319 = 0x2811)
> 
> static inline struct sk_buff *ixgbe_transform_rsc_queue(struct sk_buff *skb)
> {
>         unsigned int frag_list_size = 0;
> 
>         while (skb->prev) {
>     2811:       48 8b 7b 08             mov    0x8(%rbx),%rdi
>     2815:       48 85 ff                test   %rdi,%rdi
>     2818:       75 e9                   jne    2803 <ixgbe_clean_rx_irq+0x30b>
>                 frag_list_size += skb->len;
>                 skb->prev = NULL;
>                 skb = prev;
>         }
> 
>         skb_shinfo(skb)->frag_list = skb->next;
>     281a:       48 8b 03                mov    (%rbx),%rax
>     281d:       8b 8b c8 00 00 00       mov    0xc8(%rbx),%ecx
>     2823:       48 8b 93 d0 00 00 00    mov    0xd0(%rbx),%rdx
>     282a:       48 89 44 0a 20          mov    %rax,0x20(%rdx,%rcx,1)
>         skb->next = NULL;
>     282f:       48 c7 03 00 00 00 00    movq   $0x0,(%rbx)
>         skb->len += frag_list_size;
>     2836:       01 73 60                add    %esi,0x60(%rbx)
>         skb->data_len += frag_list_size;
>     2839:       01 73 64                add    %esi,0x64(%rbx)
>         skb->truesize += frag_list_size;
>     283c:       01 b3 e0 00 00 00       add    %esi,0xe0(%rbx)
>                 }
> 
> 
> Let me know if you want the objdump, its quite large.

Thanks for the report Jesper.  Hold onto the objdump for now.  If I need 
it, I'll let you know (and give you a place to upload it to).

For the time being, I have one of my validation guys trying to get a repro 
in our lab right now.  So please consider this being actively worked on 
right now.  Hopefully I'll have something for you soon.

Cheers,
-PJ Waskiewicz

^ permalink raw reply

* Re: pull request: wireless-2.6 2009-07-22
From: David Miller @ 2009-07-22 18:19 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, netdev, linux-kernel
In-Reply-To: <20090722174756.GI5276@tuxdriver.com>

From: "John W. Linville" <linville@tuxdriver.com>
Date: Wed, 22 Jul 2009 13:47:56 -0400

> Here is a collection of fixes intended for 2.6.31.  I think they mostly
> speak for themselves and are uncontroversial.
> 
> Please let me know if there are problems!

Pulled, thanks a lot John.

^ permalink raw reply

* Re: [PATCH] register at91_ether using platform_driver_probe
From: David Miller @ 2009-07-22 18:24 UTC (permalink / raw)
  To: avictor.za
  Cc: u.kleine-koenig, linux-kernel, dbrownell, netdev,
	paulius.zaleckas, marc.pignat, linux, 12o3l, akpm, gregkh
In-Reply-To: <cd73a99e0907221046k5e435fd3m3cb1fd006c0385a7@mail.gmail.com>

From: Andrew Victor <avictor.za@gmail.com>
Date: Wed, 22 Jul 2009 19:46:24 +0200

> hi,
> 
>> at91ether_probe lives in .init.text, so using platform_driver_register
>> to register it is wrong because binding a device after the init memory
>> is discarded (e.g. via sysfs) results in an oops.
>>
>> As requested by David Brownell platform_driver_probe is used instead of
>> moving the probe function to .devinit.text as proposed initially.
>> This saves some memory, but devices registered after the driver is
>> probed are not bound (probably there are none) and binding via sysfs
>> isn't possible.
> 
> 
> Acked-by: Andrew Victor <linux@maxim.org.za>

Applied, thanks everyone.

^ permalink raw reply

* Re: [PATCH] NET / skge: Enable WoL by default if supported
From: David Miller @ 2009-07-22 18:25 UTC (permalink / raw)
  To: shemminger; +Cc: rjw, netdev, linux-pm, linux-kernel, mike
In-Reply-To: <20090722095953.0806bea2@nehalam>

From: Stephen Hemminger <shemminger@vyatta.com>
Date: Wed, 22 Jul 2009 09:59:53 -0700

> On Wed, 22 Jul 2009 14:58:55 +0200
> "Rafael J. Wysocki" <rjw@sisk.pl> wrote:
> 
>> From: Rafael J. Wysocki <rjw@sisk.pl>
>> 
>> If skge hardware is capable of waking up the system from sleep,
>> enable magic packet WoL during driver initialisation.
>> 
>> This makes WoL work without calling 'ethtool -s ethX wol g'
>> for each adapter.
>> 
>> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> Tested-by: Michael Guntsche <mike@it-loops.com>
>> ---
>>  drivers/net/skge.c |    4 +++-
>>  1 file changed, 3 insertions(+), 1 deletion(-)
>> 
>> Index: linux-2.6/drivers/net/skge.c
>> ===================================================================
>> --- linux-2.6.orig/drivers/net/skge.c
>> +++ linux-2.6/drivers/net/skge.c
>> @@ -3856,8 +3856,10 @@ static struct net_device *skge_devinit(s
>>  	skge->speed = -1;
>>  	skge->advertising = skge_supported_modes(hw);
>>  
>> -	if (device_may_wakeup(&hw->pdev->dev))
>> +	if (device_can_wakeup(&hw->pdev->dev)) {
>>  		skge->wol = wol_supported(hw) & WAKE_MAGIC;
>> +		device_set_wakeup_enable(&hw->pdev->dev, skge->wol);
>> +	}
>>  
>>  	hw->dev[port] = dev;
>>  
> 
> Acked-by: Stephen Hemminger <shemminger@vyatta.com>

Applied, thanks.

^ permalink raw reply

* Re: [PATCH] igb/e1000e/e1000/e100: make wol usable
From: Andy Gospodarek @ 2009-07-22 19:34 UTC (permalink / raw)
  To: Alexander Duyck
  Cc: Andy Gospodarek, Ronciak, John, netdev@vger.kernel.org,
	Kirsher, Jeffrey T, Brandeburg, Jesse, Allan, Bruce W,
	Waskiewicz Jr, Peter P, stable@kernel.org
In-Reply-To: <5f2db9d90907212025k31c3a941v5fd1be9ed171e4c1@mail.gmail.com>

On Tue, Jul 21, 2009 at 08:25:31PM -0700, Alexander Duyck wrote:
> My understanding was that the can_wakeup was supposed to be
> initialized by pci_pm_init or platform_pci_wakeup_init based on the
> pci-e capabilities.  Is this not the case?  It seems like this is
> where you should be looking to determine why the the can_wakeup isn't
> being initialized correctly.
> 
> The patch below won't solve the problem either.  The problem is that
> the can_wakeup value is being disabled when the EEPROM doesn't support
> WOL.

The lack of EEPROM support for this was not the problem at all.
Ultimately it turns out that can_wakeup is set in pci_pm_init, so calls
to device_init_wakeup and device_set_wakeup_capable should not be needed
in any driver.

> If you have to do this in the drivers, then my suggestion is to take a
> look at how ixgbe is doing it.  You essentially need to initialize
> can_wakeup to true, and then set the should_wakeup attribute based on
> the EEPROM setting or via ethtool.  This way you can still toggle the
> should_wakeup option without being blocked by the EEPROM disabling it.

The first patch does exactly that (and ixgbe was part of my inspiration
for it).  Since can_wakeup and should_wakeup are both set in
device_init_wakeup it's a suitable alternative in driver probe
functions, but probably not ideal when drivers check for
device_can_wakeup in their ethtool set_wol functions.

Anyway, I knew I would find a bug or unnecessary code between e1000e and
friends or ixgbe, so it looks like ixgbe has an unnecessary call to
device_init_wakeup.  Feel free to consider this patch as an alternative.



[PATCH] ixgbe: remove unnecessary call to device_init_wakeup

Calls to device_init_wakeup should not be necessary in drivers that use
device_set_wakeup_enable since pci_pm_init will set the can_wakeup flag
for the device when initialized.

I can't test this since I don't have any of the 82599 KX4 interfaces
(the only ones capable of WOL), but I did instrument ixgbe_probe and
know that can_wakeup=1 when device_init_wakeup is removed.

Signed-off-by: Andy Gospodarek <andy@greyhouse.net>

---

 ixgbe_main.c |    1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 79f60e8..2f15abf 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -5640,7 +5640,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
 		adapter->wol = 0;
 		break;
 	}
-	device_init_wakeup(&adapter->pdev->dev, true);
 	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
 
 	/* pick up the PCI bus settings for reporting later */


^ permalink raw reply related

* Checksum Question about new sk_buffs
From: Buster Holzbauer @ 2009-07-22 19:42 UTC (permalink / raw)
  To: netdev


Hello,

I am a researcher at a university working on a new protocol based off of TCP, using the 2.6.26.5 kernel on a pair of gentoo machines. I need to send additional sk_buffs containing redundancy information as well as the sk_buffs TCP would normally send containing data. 

The redundancy sk_buffs are constructed by a function called by tcp_push (). I create these sk_buffs by calling sk_stream_alloc_skb(), then updating a few fields in the header (such as sequence number), but I'm clearly doing something wrong or missing a step. I believe this because after adding a printk to the csum_error label in tcp_rcv_established, the receiver-side logs show that these redundancy sk_buffs are triggering this label. The normal data sk_buffs, as expected, do not trigger this printk.

Does anyone have any information about the specific steps to create a new sk_buff, update header fields, and populate it with data so that the receiver processes the sk_buff correctly? 

Thanks in advance for any help you can provide,
Buster Holzbauer




      

^ permalink raw reply

* Re: [PATCH] igb/e1000e/e1000/e100: make wol usable
From: Waskiewicz Jr, Peter P @ 2009-07-22 20:00 UTC (permalink / raw)
  To: Andy Gospodarek
  Cc: Alexander Duyck, Ronciak, John, netdev@vger.kernel.org,
	Kirsher, Jeffrey T, Brandeburg, Jesse, Allan, Bruce W,
	Waskiewicz Jr, Peter P, stable@kernel.org
In-Reply-To: <20090722193400.GN8515@gospo.rdu.redhat.com>

> [PATCH] ixgbe: remove unnecessary call to device_init_wakeup
> 
> Calls to device_init_wakeup should not be necessary in drivers that use
> device_set_wakeup_enable since pci_pm_init will set the can_wakeup flag
> for the device when initialized.
> 
> I can't test this since I don't have any of the 82599 KX4 interfaces
> (the only ones capable of WOL), but I did instrument ixgbe_probe and
> know that can_wakeup=1 when device_init_wakeup is removed.
> 
> Signed-off-by: Andy Gospodarek <andy@greyhouse.net>
> 
> ---
> 
>  ixgbe_main.c |    1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
> index 79f60e8..2f15abf 100644
> --- a/drivers/net/ixgbe/ixgbe_main.c
> +++ b/drivers/net/ixgbe/ixgbe_main.c
> @@ -5640,7 +5640,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
>  		adapter->wol = 0;
>  		break;
>  	}
> -	device_init_wakeup(&adapter->pdev->dev, true);
>  	device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
>  
>  	/* pick up the PCI bus settings for reporting later */

I need to rebuild one of my boxes in the lab that I do WoL testing on for 
ixgbe devices.  That shouldn't take me long, and I'll test this.  If it 
works as expected, I'll send my ack.

Cheers,
-PJ Waskiewicz

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox