Netdev List
 help / color / mirror / Atom feed
* Re: 2.6.31-rc6-git5: Reported regressions from 2.6.30
From: Rafael J. Wysocki @ 2009-08-21 22:02 UTC (permalink / raw)
  To: Larry Finger
  Cc: Linux Kernel Mailing List, Adrian Bunk, Andrew Morton,
	Linus Torvalds, Natalie Protasevich, Kernel Testers List,
	Network Development, Linux ACPI, Linux PM List, Linux SCSI List,
	Linux Wireless List, DRI
In-Reply-To: <4A8F12CE.9090601@lwfinger.net>

On Friday 21 August 2009, Larry Finger wrote:
> Rafael J. Wysocki wrote:
> 
> > Bug-Entry	: http://bugzilla.kernel.org/show_bug.cgi?id=13960
> > Subject		: rtl8187 not connect to wifi
> > Submitter	: okias <d.okias@gmail.com>
> > Date		: 2009-08-10 19:16 (10 days old)
> 
> The patch for this one was sent from Linville to DaveM earlier today,
> and should be sent to mainline in the near future.
> 
> AFAIK, the OP has not yet tested the patch, but I think I was able to
> reproduce the problem, and the patch did fit it for me.

Thanks for the update.

Can you please close the bug when the patch is merged?

Rafael

^ permalink raw reply

* Re: [PATCH] sctp: fix the check for path failure detection
From: Vlad Yasevich @ 2009-08-21 21:47 UTC (permalink / raw)
  To: Chunbo Luo; +Cc: davem, netdev, linux-sctp, linux-kernel
In-Reply-To: <1250838275-7444-1-git-send-email-chunbo.luo@windriver.com>

Chunbo Luo wrote:
> The transport is marked DOWN immediately after sending the max+1 HB,
> which is equal to not sending the max+1 HB at all. We should wait
> a next period and make sure the last HB is not acknowledged.
> 

I don't think this code does what you want either...

Let's say path_max_rxt = 2.  What we'll get is:
	timeout:
		err++ (1)
		if (err > 2) false
		send HB
		reset timer
	timeout:
		err++ (2)
		if (err > 2) false
		send HB
		reset timer
	timeout:
		err++ (3)
		if (err > 2)
			set transport DOWN
		send HB
		reset timer.

We only had 2 unacknowledged HB when we should have had 3.

All you need to do is move the error error under a check that
makes sure that HB has been sent (similar to how the rto doubling
is done).  Then you original patch would work where we change ">="
to simply ">".  The error count will be max+1 when transport is marked DOWN.

-vlad

> Signed-off-by: Chunbo Luo <chunbo.luo@windriver.com>
> ---
>  include/net/sctp/command.h |    1 +
>  net/sctp/sm_sideeffect.c   |   39 ++++++++++++++++++++++++++++-----------
>  net/sctp/sm_statefuns.c    |   16 ++++++++++++++--
>  3 files changed, 43 insertions(+), 13 deletions(-)
> 
> diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h
> index 3b96680..256effd 100644
> --- a/include/net/sctp/command.h
> +++ b/include/net/sctp/command.h
> @@ -77,6 +77,7 @@ typedef enum {
>  	SCTP_CMD_HB_TIMERS_START,    /* Start the heartbeat timers. */
>  	SCTP_CMD_HB_TIMER_UPDATE,    /* Update a heartbeat timers.  */
>  	SCTP_CMD_HB_TIMERS_STOP,     /* Stop the heartbeat timers.  */
> +	SCTP_CMD_PATH_FAILURE_DETECTION,/* Path failure detection. */
>  	SCTP_CMD_TRANSPORT_HB_SENT,  /* Reset the status of a transport. */
>  	SCTP_CMD_TRANSPORT_IDLE,     /* Do manipulations on idle transport */
>  	SCTP_CMD_TRANSPORT_ON,       /* Mark the transport as active. */
> diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
> index 86426aa..db299c6 100644
> --- a/net/sctp/sm_sideeffect.c
> +++ b/net/sctp/sm_sideeffect.c
> @@ -432,7 +432,25 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
>   * mark the destination transport address as inactive, and a
>   * notification SHOULD be sent to the upper layer.
>   *
> + * transport error counter is incremented in sctp_do_8_2_transport_strike
>   */
> +static void sctp_cmd_path_failure_detection(struct sctp_association *asoc,
> +					   struct sctp_transport *transport)
> +{
> +	if (transport->error_count > transport->pathmaxrxt) {
> +		SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p",
> +					 " transport IP: port:%d failed.\n",
> +					 asoc,
> +					 (&transport->ipaddr),
> +					 ntohs(transport->ipaddr.v4.sin_port));
> +		sctp_assoc_control_transport(asoc, transport,
> +					     SCTP_TRANSPORT_DOWN,
> +					     SCTP_FAILED_THRESHOLD);
> +	}
> +}
> +
> +
> + /*  Mark a strike against a transport */
>  static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
>  					 struct sctp_transport *transport,
>  					 int is_hb)
> @@ -446,17 +464,11 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
>  	if (transport->state != SCTP_UNCONFIRMED)
>  		asoc->overall_error_count++;
>  
> -	if (transport->state != SCTP_INACTIVE &&
> -	    (transport->error_count++ >= transport->pathmaxrxt)) {
> -		SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p",
> -					 " transport IP: port:%d failed.\n",
> -					 asoc,
> -					 (&transport->ipaddr),
> -					 ntohs(transport->ipaddr.v4.sin_port));
> -		sctp_assoc_control_transport(asoc, transport,
> -					     SCTP_TRANSPORT_DOWN,
> -					     SCTP_FAILED_THRESHOLD);
> -	}
> +	/* The check for transport's error counter exceeding the threshold
> +	 * is done in the state function.
> +	 */
> +	if (transport->state != SCTP_INACTIVE)
> +		transport->error_count++;
>  
>  	/* E2) For the destination address for which the timer
>  	 * expires, set RTO <- RTO * 2 ("back off the timer").  The
> @@ -1464,6 +1476,11 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
>  						    0);
>  			break;
>  
> +		case SCTP_CMD_PATH_FAILURE_DETECTION:
> +			t = cmd->obj.transport;
> +			sctp_cmd_path_failure_detection(asoc, t);
> +			break;
> +
>  		case SCTP_CMD_TRANSPORT_IDLE:
>  			t = cmd->obj.transport;
>  			sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE);
> diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
> index 7288192..f4c05fd 100644
> --- a/net/sctp/sm_statefuns.c
> +++ b/net/sctp/sm_statefuns.c
> @@ -981,6 +981,9 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
>  	 */
>  
>  	if (transport->param_flags & SPP_HB_ENABLE) {
> +		/* Do the path failure detection before send beat */
> +		sctp_add_cmd_sf(commands, SCTP_CMD_PATH_FAILURE_DETECTION,
> +				SCTP_TRANSPORT(transport));
>  		if (SCTP_DISPOSITION_NOMEM ==
>  				sctp_sf_heartbeat(ep, asoc, type, arg,
>  						  commands))
> @@ -5229,6 +5232,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep,
>  	 */
>  
>  	/* Do some failure management (Section 8.2). */
> +	sctp_add_cmd_sf(commands, SCTP_CMD_PATH_FAILURE_DETECTION,
> +			SCTP_TRANSPORT(transport));
>  	sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport));
>  
>  	/* NB: Rules E4 and F1 are implicit in R1.  */
> @@ -5436,9 +5441,13 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
>  	 * If we remove the transport an SHUTDOWN was last sent to, don't
>  	 * do failure management.
>  	 */
> -	if (asoc->shutdown_last_sent_to)
> +	if (asoc->shutdown_last_sent_to) {
> +		sctp_add_cmd_sf(commands, SCTP_CMD_PATH_FAILURE_DETECTION,
> +				SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
> +
>  		sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
>  				SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
> +	}
>  
>  	/* Set the transport for the SHUTDOWN/ACK chunk and the timeout for
>  	 * the T2-shutdown timer.
> @@ -5475,9 +5484,12 @@ sctp_disposition_t sctp_sf_t4_timer_expire(
>  	 * detection on the appropriate destination address as defined in
>  	 * RFC2960 [5] section 8.1 and 8.2.
>  	 */
> -	if (transport)
> +	if (transport) {
> +		sctp_add_cmd_sf(commands, SCTP_CMD_PATH_FAILURE_DETECTION,
> +				SCTP_TRANSPORT(transport));
>  		sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
>  				SCTP_TRANSPORT(transport));
> +	}
>  
>  	/* Reconfig T4 timer and transport. */
>  	sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T4, SCTP_CHUNK(chunk));

^ permalink raw reply

* Re: 2.6.31-rc6-git5: Reported regressions from 2.6.30
From: Larry Finger @ 2009-08-21 21:34 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Adrian Bunk, DRI, Linux SCSI List, Network Development,
	Linux Wireless List, Linux Kernel Mailing List,
	Natalie Protasevich, Linux ACPI, Andrew Morton,
	Kernel Testers List, Linus Torvalds, Linux PM List
In-Reply-To: <jCYznkTxcLB.A.EPH.eNIjKB@chimera>

Rafael J. Wysocki wrote:

> Bug-Entry	: http://bugzilla.kernel.org/show_bug.cgi?id=13960
> Subject		: rtl8187 not connect to wifi
> Submitter	: okias <d.okias@gmail.com>
> Date		: 2009-08-10 19:16 (10 days old)

The patch for this one was sent from Linville to DaveM earlier today,
and should be sent to mainline in the near future.

AFAIK, the OP has not yet tested the patch, but I think I was able to
reproduce the problem, and the patch did fit it for me.

Larry

^ permalink raw reply

* Re: [PATCH 2.6.30-rc4] r8169: avoid losing MSI interrupts
From: Michael Riepe @ 2009-08-21 21:22 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: David Dillow, Michael Buesch, Francois Romieu, Rui Santos,
	Michael Büker, linux-kernel, netdev
In-Reply-To: <m1skfkrik2.fsf@fess.ebiederm.org>

Hi!

Eric W. Biederman wrote:

> I have what at first glance looks like a problem caused by this
> patch.  For the last month since upgrading one of my machines from
> 2.6.28 to 2.6.30 it has been becomming inaccessible from the
> network and I have a few:
> 
> NETDEV WATCHDOG: eth0 (r8169): transmit timed out
>
> in my logs and a lot soft lockups that always have rtl8169_interrupt
> as the thing that is running.   I suspect your patch has introduced
> a near infinite loop in the interrupt handler and is causing these
> soft lockups.

I've been running 2.6.30 for more than two months now and all is fine.
But this might be a different chip.

-- 
Michael "Tired" Riepe <michael.riepe@googlemail.com>
X-Tired: Each morning I get up I die a little

^ permalink raw reply

* Re: [PATCH 2.6.30-rc4] r8169: avoid losing MSI interrupts
From: Eric W. Biederman @ 2009-08-21 20:57 UTC (permalink / raw)
  To: David Dillow
  Cc: Michael Riepe, Michael Buesch, Francois Romieu, Rui Santos,
	Michael Büker, linux-kernel, netdev
In-Reply-To: <1243042174.3580.23.camel@obelisk.thedillows.org>

David Dillow <dave@thedillows.org> writes:

> The 8169 chip only generates MSI interrupts when all enabled event
> sources are quiescent and one or more sources transition to active. If
> not all of the active events are acknowledged, or a new event becomes
> active while the existing ones are cleared in the handler, we will not
> see a new interrupt.
>
> The current interrupt handler masks off the Rx and Tx events once the
> NAPI handler has been scheduled, which opens a race window in which we
> can get another Rx or Tx event and never ACK'ing it, stopping all
> activity until the link is reset (ifconfig down/up). Fix this by always
> ACK'ing all event sources, and loop in the handler until we have all
> sources quiescent.
>
> Signed-off-by: David Dillow <dave@thedillows.org>
> ---
> This fixes the lockups I've seen. Both MSI and level-triggered interrupt
> configurations survive over an hour of testing when it would lockup in
> under 90 seconds before. I am certain of the analysis of the root cause,
> but there may be better ways to fix it. There may also be a theoretical
> race window between the ending of a NAPI poll cycle and a link change
> interrupt coming in, but I'm not sure it would matter. 
>
> Some variant of this should also be applied to the currently running
> stable trees, as the problem is long-standing.

I have what at first glance looks like a problem caused by this
patch.  For the last month since upgrading one of my machines from
2.6.28 to 2.6.30 it has been becomming inaccessible from the
network and I have a few:

NETDEV WATCHDOG: eth0 (r8169): transmit timed out

in my logs and a lot soft lockups that always have rtl8169_interrupt
as the thing that is running.   I suspect your patch has introduced
a near infinite loop in the interrupt handler and is causing these
soft lockups.

Any ideas?

Eric

BUG: soft lockup - CPU#3 stuck for 61s! [swapper:0]
CPU 3:
Pid: 0, comm: swapper Tainted: G        W  2.6.30-170263.2006.Arora.fc11.x86_64 #1 G33M-S2
RIP: 0010:[<ffffffffa01deacd>]  [<ffffffffa01deacd>] rtl8169_interrupt+0x26f/0x2b7 [r8169]
RSP: 0018:ffff880028070cb0  EFLAGS: 00000206
RAX: 0000000000000050 RBX: ffff880028070d10 RCX: ffff88002807b9e0
RDX: ffffc2000065c03e RSI: ffff88012d79a000 RDI: 0000000000000246
RBP: ffffffff8100c9d3 R08: ffff88012fae0000 R09: ffff880028070ec0
R10: 077321422cb06619 R11: 000000003c5efb73 R12: ffff880028070c30
R13: ffff88012d79a000 R14: ffff88012d79a600 R15: 077321422cb06619
FS:  0000000000000000(0000) GS:ffff88002806d000(0000) knlGS:0000000000000000
CS:  0010 DS: 0018 ES: 0018 CR0: 000000008005003b
CR2: 00007fc10010c000 CR3: 0000000000201000 CR4: 00000000000026e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Call Trace:
 <IRQ>  [<ffffffff81093f0b>] ? handle_IRQ_event+0x6a/0x13f
 [<ffffffff810219fa>] ? apic_write+0x24/0x3a
 [<ffffffff8109607a>] ? handle_edge_irq+0xdb/0x138
 [<ffffffff81012fbd>] ? native_sched_clock+0x2d/0x54
 [<ffffffff8100e996>] ? handle_irq+0x95/0xb7
 [<ffffffff8100df42>] ? do_IRQ+0x6a/0xe9
 [<ffffffff8100c853>] ? ret_from_intr+0x0/0x11
 [<ffffffff8104ba16>] ? __do_softirq+0x5e/0x1b0
 [<ffffffff8100cfcc>] ? call_softirq+0x1c/0x28
 [<ffffffff8100e721>] ? do_softirq+0x51/0xae
 [<ffffffff8104b6d2>] ? irq_exit+0x52/0xa3
 [<ffffffff81020f11>] ? smp_apic_timer_interrupt+0x94/0xb8
 [<ffffffff8100c9d3>] ? apic_timer_interrupt+0x13/0x20
 <EOI>  [<ffffffff81014096>] ? mwait_idle+0x9b/0xcc
 [<ffffffff81014038>] ? mwait_idle+0x3d/0xcc
 [<ffffffff8100ae08>] ? enter_idle+0x33/0x49
 [<ffffffff8100aece>] ? cpu_idle+0xb0/0xf3
 [<ffffffff8136f30c>] ? start_secondary+0x19c/0x1b7

^ permalink raw reply

* Re: [net-next PATCH 1/3] qlge: Move TX completion processing to send path.
From: David Miller @ 2009-08-21 20:25 UTC (permalink / raw)
  To: ron.mercer; +Cc: netdev, shashank.pandhare, jagannatha.narayanaswami
In-Reply-To: <20090821172301.GB12770@linux-ox1b.qlogic.org>

From: Ron Mercer <ron.mercer@qlogic.com>
Date: Fri, 21 Aug 2009 10:23:01 -0700

> I'll move TX completions into NAPI as you indicated.  I still need to
> dedicate the MSIX vectors to RSS, but I will give each vector a number
> of TX rings to service in NAPI as well.

That might actually be, in fact, optimal.  Because if you process
the TX completions first this primes the SKB allocator up with
fresh SKBs to replenish the RX ring particularly in forwarding
workloads.

^ permalink raw reply

* Re: pull request: wireless-2.6 2009-08-21
From: David Miller @ 2009-08-21 20:13 UTC (permalink / raw)
  To: linville-2XuSBdqkA4R54TAoqtyWWQ
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20090821170324.GC8532-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>

From: "John W. Linville" <linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>
Date: Fri, 21 Aug 2009 13:03:25 -0400

> One more straggler for 2.6.31...without this rtl8187b devices simply
> don't work. :-(
> 
> The patch is small and isolated to rtl8187.  I think we should take
> it for 2.6.31.
> 
> Please let me know if there are problems!

Pulled, and I'll push out to kernel.org after a test build.

Thanks!
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: pull request: wireless-next-2.6 2009-08-20
From: David Miller @ 2009-08-21 20:12 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, netdev, linux-kernel
In-Reply-To: <20090820172611.GE2657@tuxdriver.com>

From: "John W. Linville" <linville@tuxdriver.com>
Date: Thu, 20 Aug 2009 13:26:11 -0400

> The relentless march of wireless patches for 2.6.32 continues...  In
> this batch, further implementation of the LP-PHY support for b43 along
> with the usual batches of driver updates and the like -- iwlwifi, ath9k,
> rt2x00, mwl8k, and others.
> 
> Please let me know if there are problems!

Pulled and pushed back out to kernel.org, thanks!

^ permalink raw reply

* [PATCH] sysctl: remove "struct file *" argument of ->proc_handler
From: Alexey Dobriyan @ 2009-08-21 19:53 UTC (permalink / raw)
  To: akpm; +Cc: linux-kernel, netdev, dhowells, ebiederm, viro

It's unused.

It isn't needed -- read or write flag is already passed and
sysctl shouldn't care about the rest.

Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---

 It _is_ used in two places at arch/frv for some disgusting
 reason.

 arch/frv/kernel/pm.c               |   14 +++---
 arch/mips/lasat/sysctl.c           |   18 ++++----
 arch/s390/appldata/appldata_base.c |    9 +---
 arch/s390/kernel/debug.c           |    4 -
 arch/s390/mm/cmm.c                 |    4 -
 arch/x86/include/asm/nmi.h         |    3 -
 arch/x86/kernel/apic/nmi.c         |    4 -
 arch/x86/kernel/vsyscall_64.c      |   10 ----
 drivers/cdrom/cdrom.c              |    8 +--
 drivers/char/random.c              |    4 -
 drivers/net/wireless/arlan-proc.c  |   28 ++++++-------
 drivers/parport/procfs.c           |   12 ++---
 fs/coda/coda_int.h                 |    1 
 fs/drop_caches.c                   |    4 -
 fs/file_table.c                    |    6 +-
 fs/proc/proc_sysctl.c              |    2 
 fs/xfs/linux-2.6/xfs_sysctl.c      |    3 -
 include/linux/fs.h                 |    2 
 include/linux/ftrace.h             |    4 -
 include/linux/hugetlb.h            |    6 +-
 include/linux/mm.h                 |    2 
 include/linux/mmzone.h             |   13 ++----
 include/linux/sched.h              |    8 +--
 include/linux/security.h           |    2 
 include/linux/swap.h               |    2 
 include/linux/sysctl.h             |   19 ++++-----
 include/linux/writeback.h          |   11 ++---
 include/net/ip.h                   |    2 
 include/net/ndisc.h                |    2 
 ipc/ipc_sysctl.c                   |   16 +++----
 ipc/mq_sysctl.c                    |    8 +--
 kernel/hung_task.c                 |    4 -
 kernel/sched.c                     |    4 -
 kernel/sched_fair.c                |    4 -
 kernel/slow-work.c                 |   12 ++---
 kernel/softlockup.c                |    4 -
 kernel/sysctl.c                    |   78 +++++++++++++++----------------------
 kernel/trace/ftrace.c              |    4 -
 kernel/trace/trace_stack.c         |    4 -
 kernel/utsname_sysctl.c            |    4 -
 mm/hugetlb.c                       |   12 ++---
 mm/page-writeback.c                |   20 ++++-----
 mm/page_alloc.c                    |   24 +++++------
 mm/vmscan.c                        |    4 -
 net/bridge/br_netfilter.c          |    4 -
 net/decnet/dn_dev.c                |    5 --
 net/decnet/sysctl_net_decnet.c     |    2 
 net/ipv4/devinet.c                 |   12 ++---
 net/ipv4/route.c                   |    7 +--
 net/ipv4/sysctl_net_ipv4.c         |   16 +++----
 net/ipv6/addrconf.c                |    8 +--
 net/ipv6/ndisc.c                   |    8 +--
 net/ipv6/route.c                   |    4 -
 net/irda/irsysctl.c                |    8 +--
 net/netfilter/ipvs/ip_vs_ctl.c     |    8 +--
 net/netfilter/nf_log.c             |    4 -
 net/phonet/sysctl.c                |    4 -
 net/sunrpc/sysctl.c                |    4 -
 net/sunrpc/xprtrdma/svc_rdma.c     |    2 
 security/min_addr.c                |    4 -
 60 files changed, 239 insertions(+), 270 deletions(-)

--- a/arch/frv/kernel/pm.c
+++ b/arch/frv/kernel/pm.c
@@ -150,7 +150,7 @@ static int user_atoi(char __user *ubuf, size_t len)
 /*
  * Send us to sleep.
  */
-static int sysctl_pm_do_suspend(ctl_table *ctl, int write, struct file *filp,
+static int sysctl_pm_do_suspend(ctl_table *ctl, int write,
 				void __user *buffer, size_t *lenp, loff_t *fpos)
 {
 	int retval, mode;
@@ -198,13 +198,13 @@ static int try_set_cmode(int new_cmode)
 }
 
 
-static int cmode_procctl(ctl_table *ctl, int write, struct file *filp,
+static int cmode_procctl(ctl_table *ctl, int write,
 			 void __user *buffer, size_t *lenp, loff_t *fpos)
 {
 	int new_cmode;
 
 	if (!write)
-		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+		return proc_dointvec(ctl, write, buffer, lenp, fpos);
 
 	new_cmode = user_atoi(buffer, *lenp);
 
@@ -301,13 +301,13 @@ static int try_set_cm(int new_cm)
 	return 0;
 }
 
-static int p0_procctl(ctl_table *ctl, int write, struct file *filp,
+static int p0_procctl(ctl_table *ctl, int write,
 		      void __user *buffer, size_t *lenp, loff_t *fpos)
 {
 	int new_p0;
 
 	if (!write)
-		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+		return proc_dointvec(ctl, write, buffer, lenp, fpos);
 
 	new_p0 = user_atoi(buffer, *lenp);
 
@@ -345,13 +345,13 @@ static int p0_sysctl(ctl_table *table,
 	return 1;
 }
 
-static int cm_procctl(ctl_table *ctl, int write, struct file *filp,
+static int cm_procctl(ctl_table *ctl, int write,
 		      void __user *buffer, size_t *lenp, loff_t *fpos)
 {
 	int new_cm;
 
 	if (!write)
-		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+		return proc_dointvec(ctl, write, buffer, lenp, fpos);
 
 	new_cm = user_atoi(buffer, *lenp);
 
--- a/arch/mips/lasat/sysctl.c
+++ b/arch/mips/lasat/sysctl.c
@@ -56,12 +56,12 @@ int sysctl_lasatstring(ctl_table *table,
 
 
 /* And the same for proc */
-int proc_dolasatstring(ctl_table *table, int write, struct file *filp,
+int proc_dolasatstring(ctl_table *table, int write,
 		       void *buffer, size_t *lenp, loff_t *ppos)
 {
 	int r;
 
-	r = proc_dostring(table, write, filp, buffer, lenp, ppos);
+	r = proc_dostring(table, write, buffer, lenp, ppos);
 	if ((!write) || r)
 		return r;
 
@@ -71,12 +71,12 @@ int proc_dolasatstring(ctl_table *table, int write, struct file *filp,
 }
 
 /* proc function to write EEPROM after changing int entry */
-int proc_dolasatint(ctl_table *table, int write, struct file *filp,
+int proc_dolasatint(ctl_table *table, int write,
 		       void *buffer, size_t *lenp, loff_t *ppos)
 {
 	int r;
 
-	r = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+	r = proc_dointvec(table, write, buffer, lenp, ppos);
 	if ((!write) || r)
 		return r;
 
@@ -89,7 +89,7 @@ int proc_dolasatint(ctl_table *table, int write, struct file *filp,
 static int rtctmp;
 
 /* proc function to read/write RealTime Clock */
-int proc_dolasatrtc(ctl_table *table, int write, struct file *filp,
+int proc_dolasatrtc(ctl_table *table, int write,
 		       void *buffer, size_t *lenp, loff_t *ppos)
 {
 	int r;
@@ -100,7 +100,7 @@ int proc_dolasatrtc(ctl_table *table, int write, struct file *filp,
 		if (rtctmp < 0)
 			rtctmp = 0;
 	}
-	r = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+	r = proc_dointvec(table, write, buffer, lenp, ppos);
 	if (r)
 		return r;
 
@@ -150,7 +150,7 @@ int sysctl_lasat_rtc(ctl_table *table,
 #endif
 
 #ifdef CONFIG_INET
-int proc_lasat_ip(ctl_table *table, int write, struct file *filp,
+int proc_lasat_ip(ctl_table *table, int write,
 		       void *buffer, size_t *lenp, loff_t *ppos)
 {
 	unsigned int ip;
@@ -227,12 +227,12 @@ static int sysctl_lasat_prid(ctl_table *table,
 	return 0;
 }
 
-int proc_lasat_prid(ctl_table *table, int write, struct file *filp,
+int proc_lasat_prid(ctl_table *table, int write,
 		       void *buffer, size_t *lenp, loff_t *ppos)
 {
 	int r;
 
-	r = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+	r = proc_dointvec(table, write, buffer, lenp, ppos);
 	if (r < 0)
 		return r;
 	if (write) {
--- a/arch/s390/appldata/appldata_base.c
+++ b/arch/s390/appldata/appldata_base.c
@@ -50,10 +50,9 @@ static struct platform_device *appldata_pdev;
  * /proc entries (sysctl)
  */
 static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
-static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
+static int appldata_timer_handler(ctl_table *ctl, int write,
 				  void __user *buffer, size_t *lenp, loff_t *ppos);
 static int appldata_interval_handler(ctl_table *ctl, int write,
-					 struct file *filp,
 					 void __user *buffer,
 					 size_t *lenp, loff_t *ppos);
 
@@ -247,7 +246,7 @@ __appldata_vtimer_setup(int cmd)
  * Start/Stop timer, show status of timer (0 = not active, 1 = active)
  */
 static int
-appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
+appldata_timer_handler(ctl_table *ctl, int write,
 			   void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int len;
@@ -289,7 +288,7 @@ out:
  * current timer interval.
  */
 static int
-appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
+appldata_interval_handler(ctl_table *ctl, int write,
 			   void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int len, interval;
@@ -335,7 +334,7 @@ out:
  * monitoring (0 = not in process, 1 = in process)
  */
 static int
-appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
+appldata_generic_handler(ctl_table *ctl, int write,
 			   void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct appldata_ops *ops = NULL, *tmp_ops;
--- a/arch/s390/kernel/debug.c
+++ b/arch/s390/kernel/debug.c
@@ -883,11 +883,11 @@ static int debug_active=1;
  * if debug_active is already off
  */
 static int
-s390dbf_procactive(ctl_table *table, int write, struct file *filp,
+s390dbf_procactive(ctl_table *table, int write,
                      void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	if (!write || debug_stoppable || !debug_active)
-		return proc_dointvec(table, write, filp, buffer, lenp, ppos);
+		return proc_dointvec(table, write, buffer, lenp, ppos);
 	else
 		return 0;
 }
--- a/arch/s390/mm/cmm.c
+++ b/arch/s390/mm/cmm.c
@@ -262,7 +262,7 @@ cmm_skip_blanks(char *cp, char **endp)
 static struct ctl_table cmm_table[];
 
 static int
-cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
+cmm_pages_handler(ctl_table *ctl, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	char buf[16], *p;
@@ -303,7 +303,7 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
 }
 
 static int
-cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
+cmm_timeout_handler(ctl_table *ctl, int write,
 		    void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	char buf[64], *p;
--- a/arch/x86/include/asm/nmi.h
+++ b/arch/x86/include/asm/nmi.h
@@ -40,8 +40,7 @@ extern unsigned int nmi_watchdog;
 #define NMI_INVALID	3
 
 struct ctl_table;
-struct file;
-extern int proc_nmi_enabled(struct ctl_table *, int , struct file *,
+extern int proc_nmi_enabled(struct ctl_table *, int ,
 			void __user *, size_t *, loff_t *);
 extern int unknown_nmi_panic;
 
--- a/arch/x86/kernel/apic/nmi.c
+++ b/arch/x86/kernel/apic/nmi.c
@@ -506,14 +506,14 @@ static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
 /*
  * proc handler for /proc/sys/kernel/nmi
  */
-int proc_nmi_enabled(struct ctl_table *table, int write, struct file *file,
+int proc_nmi_enabled(struct ctl_table *table, int write,
 			void __user *buffer, size_t *length, loff_t *ppos)
 {
 	int old_state;
 
 	nmi_watchdog_enabled = (atomic_read(&nmi_active) > 0) ? 1 : 0;
 	old_state = nmi_watchdog_enabled;
-	proc_dointvec(table, write, file, buffer, length, ppos);
+	proc_dointvec(table, write, buffer, length, ppos);
 	if (!!old_state == !!nmi_watchdog_enabled)
 		return 0;
 
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -227,19 +227,11 @@ static long __vsyscall(3) venosys_1(void)
 }
 
 #ifdef CONFIG_SYSCTL
-
-static int
-vsyscall_sysctl_change(ctl_table *ctl, int write, struct file * filp,
-		       void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-	return proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
-}
-
 static ctl_table kernel_table2[] = {
 	{ .procname = "vsyscall64",
 	  .data = &vsyscall_gtod_data.sysctl_enabled, .maxlen = sizeof(int),
 	  .mode = 0644,
-	  .proc_handler = vsyscall_sysctl_change },
+	  .proc_handler = proc_dointvec },
 	{}
 };
 
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -3412,7 +3412,7 @@ static int cdrom_print_info(const char *header, int val, char *info,
 	return 0;
 }
 
-static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
+static int cdrom_sysctl_info(ctl_table *ctl, int write,
                            void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int pos;
@@ -3489,7 +3489,7 @@ static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
 		goto done;
 doit:
 	mutex_unlock(&cdrom_mutex);
-	return proc_dostring(ctl, write, filp, buffer, lenp, ppos);
+	return proc_dostring(ctl, write, buffer, lenp, ppos);
 done:
 	printk(KERN_INFO "cdrom: info buffer too small\n");
 	goto doit;
@@ -3525,12 +3525,12 @@ static void cdrom_update_settings(void)
 	mutex_unlock(&cdrom_mutex);
 }
 
-static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp,
+static int cdrom_sysctl_handler(ctl_table *ctl, int write,
 				void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int ret;
 	
-	ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write) {
 	
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1217,7 +1217,7 @@ static char sysctl_bootid[16];
  * as an ASCII string in the standard UUID format.  If accesses via the
  * sysctl system call, it is returned as 16 bytes of binary data.
  */
-static int proc_do_uuid(ctl_table *table, int write, struct file *filp,
+static int proc_do_uuid(ctl_table *table, int write,
 			void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	ctl_table fake_table;
@@ -1240,7 +1240,7 @@ static int proc_do_uuid(ctl_table *table, int write, struct file *filp,
 	fake_table.data = buf;
 	fake_table.maxlen = sizeof(buf);
 
-	return proc_dostring(&fake_table, write, filp, buffer, lenp, ppos);
+	return proc_dostring(&fake_table, write, buffer, lenp, ppos);
 }
 
 static int uuid_strategy(ctl_table *table,
--- a/drivers/net/wireless/arlan-proc.c
+++ b/drivers/net/wireless/arlan-proc.c
@@ -402,7 +402,7 @@ static int arlan_setup_card_by_book(struct net_device *dev)
 
 static char arlan_drive_info[ARLAN_STR_SIZE] = "A655\n\0";
 
-static int arlan_sysctl_info(ctl_table * ctl, int write, struct file *filp,
+static int arlan_sysctl_info(ctl_table * ctl, int write,
 		      void __user *buffer, size_t * lenp, loff_t *ppos)
 {
 	int i;
@@ -629,7 +629,7 @@ final:
 	*lenp = pos;
 
 	if (!write)
-		retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos);
+		retv = proc_dostring(ctl, write, buffer, lenp, ppos);
 	else
 	{
 		*lenp = 0;
@@ -639,7 +639,7 @@ final:
 }
 
 
-static int arlan_sysctl_info161719(ctl_table * ctl, int write, struct file *filp,
+static int arlan_sysctl_info161719(ctl_table * ctl, int write,
 			    void __user *buffer, size_t * lenp, loff_t *ppos)
 {
 	int i;
@@ -669,11 +669,11 @@ static int arlan_sysctl_info161719(ctl_table * ctl, int write, struct file *filp
 
 final:
 	*lenp = pos;
-	retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos);
+	retv = proc_dostring(ctl, write, buffer, lenp, ppos);
 	return retv;
 }
 
-static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, struct file *filp,
+static int arlan_sysctl_infotxRing(ctl_table * ctl, int write,
 			    void __user *buffer, size_t * lenp, loff_t *ppos)
 {
 	int i;
@@ -698,11 +698,11 @@ static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, struct file *filp
 	SARLBNpln(u_char, txBuffer, 0x800);
 final:
 	*lenp = pos;
-	retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos);
+	retv = proc_dostring(ctl, write, buffer, lenp, ppos);
 	return retv;
 }
 
-static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, struct file *filp,
+static int arlan_sysctl_inforxRing(ctl_table * ctl, int write,
 			    void __user *buffer, size_t * lenp, loff_t *ppos)
 {
 	int i;
@@ -726,11 +726,11 @@ static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, struct file *filp
 	SARLBNpln(u_char, rxBuffer, 0x800);
 final:
 	*lenp = pos;
-	retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos);
+	retv = proc_dostring(ctl, write, buffer, lenp, ppos);
 	return retv;
 }
 
-static int arlan_sysctl_info18(ctl_table * ctl, int write, struct file *filp,
+static int arlan_sysctl_info18(ctl_table * ctl, int write,
 			void __user *buffer, size_t * lenp, loff_t *ppos)
 {
 	int i;
@@ -756,7 +756,7 @@ static int arlan_sysctl_info18(ctl_table * ctl, int write, struct file *filp,
 
 final:
 	*lenp = pos;
-	retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos);
+	retv = proc_dostring(ctl, write, buffer, lenp, ppos);
 	return retv;
 }
 
@@ -766,7 +766,7 @@ final:
 
 static char conf_reset_result[200];
 
-static int arlan_configure(ctl_table * ctl, int write, struct file *filp,
+static int arlan_configure(ctl_table * ctl, int write,
 		    void __user *buffer, size_t * lenp, loff_t *ppos)
 {
 	int pos = 0;
@@ -788,10 +788,10 @@ static int arlan_configure(ctl_table * ctl, int write, struct file *filp,
 		return -1;
 
 	*lenp = pos;
-	return proc_dostring(ctl, write, filp, buffer, lenp, ppos);
+	return proc_dostring(ctl, write, buffer, lenp, ppos);
 }
 
-static int arlan_sysctl_reset(ctl_table * ctl, int write, struct file *filp,
+static int arlan_sysctl_reset(ctl_table * ctl, int write,
 		       void __user *buffer, size_t * lenp, loff_t *ppos)
 {
 	int pos = 0;
@@ -811,7 +811,7 @@ static int arlan_sysctl_reset(ctl_table * ctl, int write, struct file *filp,
 	} else
 		return -1;
 	*lenp = pos + 3;
-	return proc_dostring(ctl, write, filp, buffer, lenp, ppos);
+	return proc_dostring(ctl, write, buffer, lenp, ppos);
 }
 
 
--- a/drivers/parport/procfs.c
+++ b/drivers/parport/procfs.c
@@ -31,7 +31,7 @@
 #define PARPORT_MIN_SPINTIME_VALUE 1
 #define PARPORT_MAX_SPINTIME_VALUE 1000
 
-static int do_active_device(ctl_table *table, int write, struct file *filp,
+static int do_active_device(ctl_table *table, int write,
 		      void __user *result, size_t *lenp, loff_t *ppos)
 {
 	struct parport *port = (struct parport *)table->extra1;
@@ -68,7 +68,7 @@ static int do_active_device(ctl_table *table, int write, struct file *filp,
 }
 
 #ifdef CONFIG_PARPORT_1284
-static int do_autoprobe(ctl_table *table, int write, struct file *filp,
+static int do_autoprobe(ctl_table *table, int write,
 			void __user *result, size_t *lenp, loff_t *ppos)
 {
 	struct parport_device_info *info = table->extra2;
@@ -111,7 +111,7 @@ static int do_autoprobe(ctl_table *table, int write, struct file *filp,
 #endif /* IEEE1284.3 support. */
 
 static int do_hardware_base_addr (ctl_table *table, int write,
-				  struct file *filp, void __user *result,
+				  void __user *result,
 				  size_t *lenp, loff_t *ppos)
 {
 	struct parport *port = (struct parport *)table->extra1;
@@ -139,7 +139,7 @@ static int do_hardware_base_addr (ctl_table *table, int write,
 }
 
 static int do_hardware_irq (ctl_table *table, int write,
-			    struct file *filp, void __user *result,
+			    void __user *result,
 			    size_t *lenp, loff_t *ppos)
 {
 	struct parport *port = (struct parport *)table->extra1;
@@ -167,7 +167,7 @@ static int do_hardware_irq (ctl_table *table, int write,
 }
 
 static int do_hardware_dma (ctl_table *table, int write,
-			    struct file *filp, void __user *result,
+			    void __user *result,
 			    size_t *lenp, loff_t *ppos)
 {
 	struct parport *port = (struct parport *)table->extra1;
@@ -195,7 +195,7 @@ static int do_hardware_dma (ctl_table *table, int write,
 }
 
 static int do_hardware_modes (ctl_table *table, int write,
-			      struct file *filp, void __user *result,
+			      void __user *result,
 			      size_t *lenp, loff_t *ppos)
 {
 	struct parport *port = (struct parport *)table->extra1;
--- a/fs/coda/coda_int.h
+++ b/fs/coda/coda_int.h
@@ -2,6 +2,7 @@
 #define _CODA_INT_
 
 struct dentry;
+struct file;
 
 extern struct file_system_type coda_fs_type;
 extern unsigned long coda_timeout;
--- a/fs/drop_caches.c
+++ b/fs/drop_caches.c
@@ -63,9 +63,9 @@ static void drop_slab(void)
 }
 
 int drop_caches_sysctl_handler(ctl_table *table, int write,
-	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+	void __user *buffer, size_t *length, loff_t *ppos)
 {
-	proc_dointvec_minmax(table, write, file, buffer, length, ppos);
+	proc_dointvec_minmax(table, write, buffer, length, ppos);
 	if (write) {
 		if (sysctl_drop_caches & 1)
 			drop_pagecache();
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -74,14 +74,14 @@ EXPORT_SYMBOL_GPL(get_max_files);
  * Handle nr_files sysctl
  */
 #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
-int proc_nr_files(ctl_table *table, int write, struct file *filp,
+int proc_nr_files(ctl_table *table, int write,
                      void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	files_stat.nr_files = get_nr_files();
-	return proc_dointvec(table, write, filp, buffer, lenp, ppos);
+	return proc_dointvec(table, write, buffer, lenp, ppos);
 }
 #else
-int proc_nr_files(ctl_table *table, int write, struct file *filp,
+int proc_nr_files(ctl_table *table, int write,
                      void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	return -ENOSYS;
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -153,7 +153,7 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf,
 
 	/* careful: calling conventions are nasty here */
 	res = count;
-	error = table->proc_handler(table, write, filp, buf, &res, ppos);
+	error = table->proc_handler(table, write, buf, &res, ppos);
 	if (!error)
 		error = res;
 out:
--- a/fs/xfs/linux-2.6/xfs_sysctl.c
+++ b/fs/xfs/linux-2.6/xfs_sysctl.c
@@ -26,7 +26,6 @@ STATIC int
 xfs_stats_clear_proc_handler(
 	ctl_table	*ctl,
 	int		write,
-	struct file	*filp,
 	void		__user *buffer,
 	size_t		*lenp,
 	loff_t		*ppos)
@@ -34,7 +33,7 @@ xfs_stats_clear_proc_handler(
 	int		c, ret, *valp = ctl->data;
 	__uint32_t	vn_active;
 
-	ret = proc_dointvec_minmax(ctl, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec_minmax(ctl, write, buffer, lenp, ppos);
 
 	if (!ret && write && *valp) {
 		printk("XFS Clearing xfsstats\n");
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2454,7 +2454,7 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf,
 			  size_t len, loff_t *ppos);
 
 struct ctl_table;
-int proc_nr_files(struct ctl_table *table, int write, struct file *filp,
+int proc_nr_files(struct ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos);
 
 int __init get_filesystem_list(char *buf);
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -19,7 +19,7 @@
 extern int ftrace_enabled;
 extern int
 ftrace_enable_sysctl(struct ctl_table *table, int write,
-		     struct file *filp, void __user *buffer, size_t *lenp,
+		     void __user *buffer, size_t *lenp,
 		     loff_t *ppos);
 
 typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip);
@@ -94,7 +94,7 @@ static inline void ftrace_start(void) { }
 extern int stack_tracer_enabled;
 int
 stack_trace_sysctl(struct ctl_table *table, int write,
-		   struct file *file, void __user *buffer, size_t *lenp,
+		   void __user *buffer, size_t *lenp,
 		   loff_t *ppos);
 #endif
 
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -19,9 +19,9 @@ static inline int is_vm_hugetlb_page(struct vm_area_struct *vma)
 }
 
 void reset_vma_resv_huge_pages(struct vm_area_struct *vma);
-int hugetlb_sysctl_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *);
-int hugetlb_overcommit_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *);
-int hugetlb_treat_movable_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *);
+int hugetlb_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
+int hugetlb_overcommit_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
+int hugetlb_treat_movable_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
 int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *);
 int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, unsigned long *, int *, int, int);
 void unmap_hugepage_range(struct vm_area_struct *,
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1274,7 +1274,7 @@ int in_gate_area_no_task(unsigned long addr);
 #define in_gate_area(task, addr) ({(void)task; in_gate_area_no_task(addr);})
 #endif	/* __HAVE_ARCH_GATE_AREA */
 
-int drop_caches_sysctl_handler(struct ctl_table *, int, struct file *,
+int drop_caches_sysctl_handler(struct ctl_table *, int,
 					void __user *, size_t *, loff_t *);
 unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask,
 			unsigned long lru_pages);
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -744,21 +744,20 @@ static inline int is_dma(struct zone *zone)
 
 /* These two functions are used to setup the per zone pages min values */
 struct ctl_table;
-struct file;
-int min_free_kbytes_sysctl_handler(struct ctl_table *, int, struct file *, 
+int min_free_kbytes_sysctl_handler(struct ctl_table *, int,
 					void __user *, size_t *, loff_t *);
 extern int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES-1];
-int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int, struct file *,
+int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int,
 					void __user *, size_t *, loff_t *);
-int percpu_pagelist_fraction_sysctl_handler(struct ctl_table *, int, struct file *,
+int percpu_pagelist_fraction_sysctl_handler(struct ctl_table *, int,
 					void __user *, size_t *, loff_t *);
 int sysctl_min_unmapped_ratio_sysctl_handler(struct ctl_table *, int,
-			struct file *, void __user *, size_t *, loff_t *);
+			void __user *, size_t *, loff_t *);
 int sysctl_min_slab_ratio_sysctl_handler(struct ctl_table *, int,
-			struct file *, void __user *, size_t *, loff_t *);
+			void __user *, size_t *, loff_t *);
 
 extern int numa_zonelist_order_handler(struct ctl_table *, int,
-			struct file *, void __user *, size_t *, loff_t *);
+			void __user *, size_t *, loff_t *);
 extern char numa_zonelist_order[];
 #define NUMA_ZONELIST_ORDER_LEN 16	/* string buffer size */
 
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -302,7 +302,7 @@ extern void softlockup_tick(void);
 extern void touch_softlockup_watchdog(void);
 extern void touch_all_softlockup_watchdogs(void);
 extern int proc_dosoftlockup_thresh(struct ctl_table *table, int write,
-				    struct file *filp, void __user *buffer,
+				    void __user *buffer,
 				    size_t *lenp, loff_t *ppos);
 extern unsigned int  softlockup_panic;
 extern int softlockup_thresh;
@@ -324,7 +324,7 @@ extern unsigned long sysctl_hung_task_check_count;
 extern unsigned long sysctl_hung_task_timeout_secs;
 extern unsigned long sysctl_hung_task_warnings;
 extern int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,
-					 struct file *filp, void __user *buffer,
+					 void __user *buffer,
 					 size_t *lenp, loff_t *ppos);
 #endif
 
@@ -1821,7 +1821,7 @@ extern unsigned int sysctl_sched_nr_migrate;
 extern unsigned int sysctl_timer_migration;
 
 int sched_nr_latency_handler(struct ctl_table *table, int write,
-		struct file *file, void __user *buffer, size_t *length,
+		void __user *buffer, size_t *length,
 		loff_t *ppos);
 #endif
 #ifdef CONFIG_SCHED_DEBUG
@@ -1839,7 +1839,7 @@ extern unsigned int sysctl_sched_rt_period;
 extern int sysctl_sched_rt_runtime;
 
 int sched_rt_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos);
 
 extern unsigned int sysctl_sched_compat_yield;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -133,7 +133,7 @@ static inline unsigned long round_hint_to_min(unsigned long hint)
 		return PAGE_ALIGN(mmap_min_addr);
 	return hint;
 }
-extern int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
+extern int mmap_min_addr_handler(struct ctl_table *table, int write,
 				 void __user *buffer, size_t *lenp, loff_t *ppos);
 
 #ifdef CONFIG_SECURITY
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -240,7 +240,7 @@ extern int page_evictable(struct page *page, struct vm_area_struct *vma);
 extern void scan_mapping_unevictable_pages(struct address_space *);
 
 extern unsigned long scan_unevictable_pages;
-extern int scan_unevictable_handler(struct ctl_table *, int, struct file *,
+extern int scan_unevictable_handler(struct ctl_table *, int,
 					void __user *, size_t *, loff_t *);
 extern int scan_unevictable_register_node(struct node *node);
 extern void scan_unevictable_unregister_node(struct node *node);
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -29,7 +29,6 @@
 #include <linux/types.h>
 #include <linux/compiler.h>
 
-struct file;
 struct completion;
 
 #define CTL_MAXNAME 10		/* how many path components do we allow in a
@@ -977,25 +976,25 @@ typedef int ctl_handler (struct ctl_table *table,
 			 void __user *oldval, size_t __user *oldlenp,
 			 void __user *newval, size_t newlen);
 
-typedef int proc_handler (struct ctl_table *ctl, int write, struct file * filp,
+typedef int proc_handler (struct ctl_table *ctl, int write,
 			  void __user *buffer, size_t *lenp, loff_t *ppos);
 
-extern int proc_dostring(struct ctl_table *, int, struct file *,
+extern int proc_dostring(struct ctl_table *, int,
 			 void __user *, size_t *, loff_t *);
-extern int proc_dointvec(struct ctl_table *, int, struct file *,
+extern int proc_dointvec(struct ctl_table *, int,
 			 void __user *, size_t *, loff_t *);
-extern int proc_dointvec_minmax(struct ctl_table *, int, struct file *,
+extern int proc_dointvec_minmax(struct ctl_table *, int,
 				void __user *, size_t *, loff_t *);
-extern int proc_dointvec_jiffies(struct ctl_table *, int, struct file *,
+extern int proc_dointvec_jiffies(struct ctl_table *, int,
 				 void __user *, size_t *, loff_t *);
-extern int proc_dointvec_userhz_jiffies(struct ctl_table *, int, struct file *,
+extern int proc_dointvec_userhz_jiffies(struct ctl_table *, int,
 					void __user *, size_t *, loff_t *);
-extern int proc_dointvec_ms_jiffies(struct ctl_table *, int, struct file *,
+extern int proc_dointvec_ms_jiffies(struct ctl_table *, int,
 				    void __user *, size_t *, loff_t *);
-extern int proc_doulongvec_minmax(struct ctl_table *, int, struct file *,
+extern int proc_doulongvec_minmax(struct ctl_table *, int,
 				  void __user *, size_t *, loff_t *);
 extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int,
-				      struct file *, void __user *, size_t *, loff_t *);
+				      void __user *, size_t *, loff_t *);
 
 extern int do_sysctl (int __user *name, int nlen,
 		      void __user *oldval, size_t __user *oldlenp,
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -117,21 +117,20 @@ extern int laptop_mode;
 extern unsigned long determine_dirtyable_memory(void);
 
 extern int dirty_background_ratio_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos);
 extern int dirty_background_bytes_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos);
 extern int dirty_ratio_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos);
 extern int dirty_bytes_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos);
 
 struct ctl_table;
-struct file;
-int dirty_writeback_centisecs_handler(struct ctl_table *, int, struct file *,
+int dirty_writeback_centisecs_handler(struct ctl_table *, int,
 				      void __user *, size_t *, loff_t *);
 
 void get_dirty_limits(unsigned long *pbackground, unsigned long *pdirty,
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -399,7 +399,7 @@ extern void	ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport,
  * fed into the routing cache should use these handlers.
  */
 int ipv4_doint_and_flush(ctl_table *ctl, int write,
-			 struct file* filp, void __user *buffer,
+			 void __user *buffer,
 			 size_t *lenp, loff_t *ppos);
 int ipv4_doint_and_flush_strategy(ctl_table *table,
 				  void __user *oldval, size_t __user *oldlenp,
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -55,7 +55,6 @@ enum {
 #include <net/neighbour.h>
 
 struct ctl_table;
-struct file;
 struct inet6_dev;
 struct net_device;
 struct net_proto_family;
@@ -139,7 +138,6 @@ extern int			igmp6_event_report(struct sk_buff *skb);
 #ifdef CONFIG_SYSCTL
 extern int 			ndisc_ifinfo_sysctl_change(struct ctl_table *ctl,
 							   int write,
-							   struct file * filp,
 							   void __user *buffer,
 							   size_t *lenp,
 							   loff_t *ppos);
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -27,18 +27,18 @@ static void *get_ipc(ctl_table *table)
 }
 
 #ifdef CONFIG_PROC_SYSCTL
-static int proc_ipc_dointvec(ctl_table *table, int write, struct file *filp,
+static int proc_ipc_dointvec(ctl_table *table, int write,
 	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table ipc_table;
 	memcpy(&ipc_table, table, sizeof(ipc_table));
 	ipc_table.data = get_ipc(table);
 
-	return proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos);
+	return proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
 }
 
 static int proc_ipc_callback_dointvec(ctl_table *table, int write,
-	struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
+	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table ipc_table;
 	size_t lenp_bef = *lenp;
@@ -47,7 +47,7 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write,
 	memcpy(&ipc_table, table, sizeof(ipc_table));
 	ipc_table.data = get_ipc(table);
 
-	rc = proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos);
+	rc = proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
 
 	if (write && !rc && lenp_bef == *lenp)
 		/*
@@ -61,13 +61,13 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write,
 }
 
 static int proc_ipc_doulongvec_minmax(ctl_table *table, int write,
-	struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
+	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table ipc_table;
 	memcpy(&ipc_table, table, sizeof(ipc_table));
 	ipc_table.data = get_ipc(table);
 
-	return proc_doulongvec_minmax(&ipc_table, write, filp, buffer,
+	return proc_doulongvec_minmax(&ipc_table, write, buffer,
 					lenp, ppos);
 }
 
@@ -95,7 +95,7 @@ static void ipc_auto_callback(int val)
 }
 
 static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
-	struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
+	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table ipc_table;
 	size_t lenp_bef = *lenp;
@@ -106,7 +106,7 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,
 	ipc_table.data = get_ipc(table);
 	oldval = *((int *)(ipc_table.data));
 
-	rc = proc_dointvec_minmax(&ipc_table, write, filp, buffer, lenp, ppos);
+	rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
 
 	if (write && !rc && lenp_bef == *lenp) {
 		int newval = *((int *)(ipc_table.data));
--- a/ipc/mq_sysctl.c
+++ b/ipc/mq_sysctl.c
@@ -31,24 +31,24 @@ static void *get_mq(ctl_table *table)
 	return which;
 }
 
-static int proc_mq_dointvec(ctl_table *table, int write, struct file *filp,
+static int proc_mq_dointvec(ctl_table *table, int write,
 	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table mq_table;
 	memcpy(&mq_table, table, sizeof(mq_table));
 	mq_table.data = get_mq(table);
 
-	return proc_dointvec(&mq_table, write, filp, buffer, lenp, ppos);
+	return proc_dointvec(&mq_table, write, buffer, lenp, ppos);
 }
 
 static int proc_mq_dointvec_minmax(ctl_table *table, int write,
-	struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos)
+	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table mq_table;
 	memcpy(&mq_table, table, sizeof(mq_table));
 	mq_table.data = get_mq(table);
 
-	return proc_dointvec_minmax(&mq_table, write, filp, buffer,
+	return proc_dointvec_minmax(&mq_table, write, buffer,
 					lenp, ppos);
 }
 #else
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -171,12 +171,12 @@ static unsigned long timeout_jiffies(unsigned long timeout)
  * Process updating of timeout sysctl
  */
 int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,
-				  struct file *filp, void __user *buffer,
+				  void __user *buffer,
 				  size_t *lenp, loff_t *ppos)
 {
 	int ret;
 
-	ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos);
+	ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
 
 	if (ret || !write)
 		goto out;
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -10157,7 +10157,7 @@ static int sched_rt_global_constraints(void)
 #endif /* CONFIG_RT_GROUP_SCHED */
 
 int sched_rt_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos)
 {
 	int ret;
@@ -10168,7 +10168,7 @@ int sched_rt_handler(struct ctl_table *table, int write,
 	old_period = sysctl_sched_rt_period;
 	old_runtime = sysctl_sched_rt_runtime;
 
-	ret = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec(table, write, buffer, lenp, ppos);
 
 	if (!ret && write) {
 		ret = sched_rt_global_constraints();
--- a/kernel/sched_fair.c
+++ b/kernel/sched_fair.c
@@ -376,10 +376,10 @@ static struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq)
 
 #ifdef CONFIG_SCHED_DEBUG
 int sched_nr_latency_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos)
 {
-	int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+	int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 
 	if (ret || !write)
 		return ret;
--- a/kernel/slow-work.c
+++ b/kernel/slow-work.c
@@ -26,10 +26,10 @@ static void slow_work_cull_timeout(unsigned long);
 static void slow_work_oom_timeout(unsigned long);
 
 #ifdef CONFIG_SYSCTL
-static int slow_work_min_threads_sysctl(struct ctl_table *, int, struct file *,
+static int slow_work_min_threads_sysctl(struct ctl_table *, int,
 					void __user *, size_t *, loff_t *);
 
-static int slow_work_max_threads_sysctl(struct ctl_table *, int , struct file *,
+static int slow_work_max_threads_sysctl(struct ctl_table *, int ,
 					void __user *, size_t *, loff_t *);
 #endif
 
@@ -493,10 +493,10 @@ static void slow_work_oom_timeout(unsigned long data)
  * Handle adjustment of the minimum number of threads
  */
 static int slow_work_min_threads_sysctl(struct ctl_table *table, int write,
-					struct file *filp, void __user *buffer,
+					void __user *buffer,
 					size_t *lenp, loff_t *ppos)
 {
-	int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+	int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 	int n;
 
 	if (ret == 0) {
@@ -521,10 +521,10 @@ static int slow_work_min_threads_sysctl(struct ctl_table *table, int write,
  * Handle adjustment of the maximum number of threads
  */
 static int slow_work_max_threads_sysctl(struct ctl_table *table, int write,
-					struct file *filp, void __user *buffer,
+					void __user *buffer,
 					size_t *lenp, loff_t *ppos)
 {
-	int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+	int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 	int n;
 
 	if (ret == 0) {
--- a/kernel/softlockup.c
+++ b/kernel/softlockup.c
@@ -90,11 +90,11 @@ void touch_all_softlockup_watchdogs(void)
 EXPORT_SYMBOL(touch_all_softlockup_watchdogs);
 
 int proc_dosoftlockup_thresh(struct ctl_table *table, int write,
-			     struct file *filp, void __user *buffer,
+			     void __user *buffer,
 			     size_t *lenp, loff_t *ppos)
 {
 	touch_all_softlockup_watchdogs();
-	return proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+	return proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 }
 
 /*
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -158,9 +158,9 @@ extern int max_lock_depth;
 #endif
 
 #ifdef CONFIG_PROC_SYSCTL
-static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp,
+static int proc_do_cad_pid(struct ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos);
-static int proc_taint(struct ctl_table *table, int write, struct file *filp,
+static int proc_taint(struct ctl_table *table, int write,
 			       void __user *buffer, size_t *lenp, loff_t *ppos);
 #endif
 
@@ -2185,7 +2185,7 @@ void sysctl_head_put(struct ctl_table_header *head)
 #ifdef CONFIG_PROC_SYSCTL
 
 static int _proc_do_string(void* data, int maxlen, int write,
-			   struct file *filp, void __user *buffer,
+			   void __user *buffer,
 			   size_t *lenp, loff_t *ppos)
 {
 	size_t len;
@@ -2246,7 +2246,6 @@ static int _proc_do_string(void* data, int maxlen, int write,
  * proc_dostring - read a string sysctl
  * @table: the sysctl table
  * @write: %TRUE if this is a write to the sysctl file
- * @filp: the file structure
  * @buffer: the user buffer
  * @lenp: the size of the user buffer
  * @ppos: file position
@@ -2260,10 +2259,10 @@ static int _proc_do_string(void* data, int maxlen, int write,
  *
  * Returns 0 on success.
  */
-int proc_dostring(struct ctl_table *table, int write, struct file *filp,
+int proc_dostring(struct ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-	return _proc_do_string(table->data, table->maxlen, write, filp,
+	return _proc_do_string(table->data, table->maxlen, write,
 			       buffer, lenp, ppos);
 }
 
@@ -2288,7 +2287,7 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
 }
 
 static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
-		  int write, struct file *filp, void __user *buffer,
+		  int write, void __user *buffer,
 		  size_t *lenp, loff_t *ppos,
 		  int (*conv)(int *negp, unsigned long *lvalp, int *valp,
 			      int write, void *data),
@@ -2395,13 +2394,13 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
 #undef TMPBUFLEN
 }
 
-static int do_proc_dointvec(struct ctl_table *table, int write, struct file *filp,
+static int do_proc_dointvec(struct ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos,
 		  int (*conv)(int *negp, unsigned long *lvalp, int *valp,
 			      int write, void *data),
 		  void *data)
 {
-	return __do_proc_dointvec(table->data, table, write, filp,
+	return __do_proc_dointvec(table->data, table, write,
 			buffer, lenp, ppos, conv, data);
 }
 
@@ -2409,7 +2408,6 @@ static int do_proc_dointvec(struct ctl_table *table, int write, struct file *fil
  * proc_dointvec - read a vector of integers
  * @table: the sysctl table
  * @write: %TRUE if this is a write to the sysctl file
- * @filp: the file structure
  * @buffer: the user buffer
  * @lenp: the size of the user buffer
  * @ppos: file position
@@ -2419,10 +2417,10 @@ static int do_proc_dointvec(struct ctl_table *table, int write, struct file *fil
  *
  * Returns 0 on success.
  */
-int proc_dointvec(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec(struct ctl_table *table, int write,
 		     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-    return do_proc_dointvec(table,write,filp,buffer,lenp,ppos,
+    return do_proc_dointvec(table,write,buffer,lenp,ppos,
 		    	    NULL,NULL);
 }
 
@@ -2430,7 +2428,7 @@ int proc_dointvec(struct ctl_table *table, int write, struct file *filp,
  * Taint values can only be increased
  * This means we can safely use a temporary.
  */
-static int proc_taint(struct ctl_table *table, int write, struct file *filp,
+static int proc_taint(struct ctl_table *table, int write,
 			       void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table t;
@@ -2442,7 +2440,7 @@ static int proc_taint(struct ctl_table *table, int write, struct file *filp,
 
 	t = *table;
 	t.data = &tmptaint;
-	err = proc_doulongvec_minmax(&t, write, filp, buffer, lenp, ppos);
+	err = proc_doulongvec_minmax(&t, write, buffer, lenp, ppos);
 	if (err < 0)
 		return err;
 
@@ -2494,7 +2492,6 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp,
  * proc_dointvec_minmax - read a vector of integers with min/max values
  * @table: the sysctl table
  * @write: %TRUE if this is a write to the sysctl file
- * @filp: the file structure
  * @buffer: the user buffer
  * @lenp: the size of the user buffer
  * @ppos: file position
@@ -2507,19 +2504,18 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp,
  *
  * Returns 0 on success.
  */
-int proc_dointvec_minmax(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec_minmax(struct ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct do_proc_dointvec_minmax_conv_param param = {
 		.min = (int *) table->extra1,
 		.max = (int *) table->extra2,
 	};
-	return do_proc_dointvec(table, write, filp, buffer, lenp, ppos,
+	return do_proc_dointvec(table, write, buffer, lenp, ppos,
 				do_proc_dointvec_minmax_conv, &param);
 }
 
 static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
-				     struct file *filp,
 				     void __user *buffer,
 				     size_t *lenp, loff_t *ppos,
 				     unsigned long convmul,
@@ -2624,21 +2620,19 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
 }
 
 static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
-				     struct file *filp,
 				     void __user *buffer,
 				     size_t *lenp, loff_t *ppos,
 				     unsigned long convmul,
 				     unsigned long convdiv)
 {
 	return __do_proc_doulongvec_minmax(table->data, table, write,
-			filp, buffer, lenp, ppos, convmul, convdiv);
+			buffer, lenp, ppos, convmul, convdiv);
 }
 
 /**
  * proc_doulongvec_minmax - read a vector of long integers with min/max values
  * @table: the sysctl table
  * @write: %TRUE if this is a write to the sysctl file
- * @filp: the file structure
  * @buffer: the user buffer
  * @lenp: the size of the user buffer
  * @ppos: file position
@@ -2651,17 +2645,16 @@ static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
  *
  * Returns 0 on success.
  */
-int proc_doulongvec_minmax(struct ctl_table *table, int write, struct file *filp,
+int proc_doulongvec_minmax(struct ctl_table *table, int write,
 			   void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-    return do_proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos, 1l, 1l);
+    return do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos, 1l, 1l);
 }
 
 /**
  * proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values
  * @table: the sysctl table
  * @write: %TRUE if this is a write to the sysctl file
- * @filp: the file structure
  * @buffer: the user buffer
  * @lenp: the size of the user buffer
  * @ppos: file position
@@ -2676,11 +2669,10 @@ int proc_doulongvec_minmax(struct ctl_table *table, int write, struct file *filp
  * Returns 0 on success.
  */
 int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
-				      struct file *filp,
 				      void __user *buffer,
 				      size_t *lenp, loff_t *ppos)
 {
-    return do_proc_doulongvec_minmax(table, write, filp, buffer,
+    return do_proc_doulongvec_minmax(table, write, buffer,
 				     lenp, ppos, HZ, 1000l);
 }
 
@@ -2756,7 +2748,6 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp,
  * proc_dointvec_jiffies - read a vector of integers as seconds
  * @table: the sysctl table
  * @write: %TRUE if this is a write to the sysctl file
- * @filp: the file structure
  * @buffer: the user buffer
  * @lenp: the size of the user buffer
  * @ppos: file position
@@ -2768,10 +2759,10 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp,
  *
  * Returns 0 on success.
  */
-int proc_dointvec_jiffies(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec_jiffies(struct ctl_table *table, int write,
 			  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-    return do_proc_dointvec(table,write,filp,buffer,lenp,ppos,
+    return do_proc_dointvec(table,write,buffer,lenp,ppos,
 		    	    do_proc_dointvec_jiffies_conv,NULL);
 }
 
@@ -2779,7 +2770,6 @@ int proc_dointvec_jiffies(struct ctl_table *table, int write, struct file *filp,
  * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds
  * @table: the sysctl table
  * @write: %TRUE if this is a write to the sysctl file
- * @filp: the file structure
  * @buffer: the user buffer
  * @lenp: the size of the user buffer
  * @ppos: pointer to the file position
@@ -2791,10 +2781,10 @@ int proc_dointvec_jiffies(struct ctl_table *table, int write, struct file *filp,
  *
  * Returns 0 on success.
  */
-int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write,
 				 void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-    return do_proc_dointvec(table,write,filp,buffer,lenp,ppos,
+    return do_proc_dointvec(table,write,buffer,lenp,ppos,
 		    	    do_proc_dointvec_userhz_jiffies_conv,NULL);
 }
 
@@ -2802,7 +2792,6 @@ int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, struct file
  * proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds
  * @table: the sysctl table
  * @write: %TRUE if this is a write to the sysctl file
- * @filp: the file structure
  * @buffer: the user buffer
  * @lenp: the size of the user buffer
  * @ppos: file position
@@ -2815,14 +2804,14 @@ int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, struct file
  *
  * Returns 0 on success.
  */
-int proc_dointvec_ms_jiffies(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec_ms_jiffies(struct ctl_table *table, int write,
 			     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-	return do_proc_dointvec(table, write, filp, buffer, lenp, ppos,
+	return do_proc_dointvec(table, write, buffer, lenp, ppos,
 				do_proc_dointvec_ms_jiffies_conv, NULL);
 }
 
-static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp,
+static int proc_do_cad_pid(struct ctl_table *table, int write,
 			   void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct pid *new_pid;
@@ -2831,7 +2820,7 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp
 
 	tmp = pid_vnr(cad_pid);
 
-	r = __do_proc_dointvec(&tmp, table, write, filp, buffer,
+	r = __do_proc_dointvec(&tmp, table, write, buffer,
 			       lenp, ppos, NULL, NULL);
 	if (r || !write)
 		return r;
@@ -2846,50 +2835,49 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp
 
 #else /* CONFIG_PROC_FS */
 
-int proc_dostring(struct ctl_table *table, int write, struct file *filp,
+int proc_dostring(struct ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	return -ENOSYS;
 }
 
-int proc_dointvec(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec(struct ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	return -ENOSYS;
 }
 
-int proc_dointvec_minmax(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec_minmax(struct ctl_table *table, int write,
 		    void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	return -ENOSYS;
 }
 
-int proc_dointvec_jiffies(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec_jiffies(struct ctl_table *table, int write,
 		    void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	return -ENOSYS;
 }
 
-int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write,
 		    void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	return -ENOSYS;
 }
 
-int proc_dointvec_ms_jiffies(struct ctl_table *table, int write, struct file *filp,
+int proc_dointvec_ms_jiffies(struct ctl_table *table, int write,
 			     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	return -ENOSYS;
 }
 
-int proc_doulongvec_minmax(struct ctl_table *table, int write, struct file *filp,
+int proc_doulongvec_minmax(struct ctl_table *table, int write,
 		    void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	return -ENOSYS;
 }
 
 int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
-				      struct file *filp,
 				      void __user *buffer,
 				      size_t *lenp, loff_t *ppos)
 {
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -3156,7 +3156,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops)
 
 int
 ftrace_enable_sysctl(struct ctl_table *table, int write,
-		     struct file *file, void __user *buffer, size_t *lenp,
+		     void __user *buffer, size_t *lenp,
 		     loff_t *ppos)
 {
 	int ret;
@@ -3166,7 +3166,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
 
 	mutex_lock(&ftrace_lock);
 
-	ret  = proc_dointvec(table, write, file, buffer, lenp, ppos);
+	ret  = proc_dointvec(table, write, buffer, lenp, ppos);
 
 	if (ret || !write || (last_ftrace_enabled == !!ftrace_enabled))
 		goto out;
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -313,14 +313,14 @@ static const struct file_operations stack_trace_fops = {
 
 int
 stack_trace_sysctl(struct ctl_table *table, int write,
-		   struct file *file, void __user *buffer, size_t *lenp,
+		   void __user *buffer, size_t *lenp,
 		   loff_t *ppos)
 {
 	int ret;
 
 	mutex_lock(&stack_sysctl_mutex);
 
-	ret = proc_dointvec(table, write, file, buffer, lenp, ppos);
+	ret = proc_dointvec(table, write, buffer, lenp, ppos);
 
 	if (ret || !write ||
 	    (last_stack_tracer_enabled == !!stack_tracer_enabled))
--- a/kernel/utsname_sysctl.c
+++ b/kernel/utsname_sysctl.c
@@ -42,14 +42,14 @@ static void put_uts(ctl_table *table, int write, void *which)
  *	Special case of dostring for the UTS structure. This has locks
  *	to observe. Should this be in kernel/sys.c ????
  */
-static int proc_do_uts_string(ctl_table *table, int write, struct file *filp,
+static int proc_do_uts_string(ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table uts_table;
 	int r;
 	memcpy(&uts_table, table, sizeof(uts_table));
 	uts_table.data = get_uts(table, write);
-	r = proc_dostring(&uts_table,write,filp,buffer,lenp, ppos);
+	r = proc_dostring(&uts_table,write,buffer,lenp, ppos);
 	put_uts(table, write, uts_table.data);
 	return r;
 }
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1505,7 +1505,7 @@ static unsigned int cpuset_mems_nr(unsigned int *array)
 
 #ifdef CONFIG_SYSCTL
 int hugetlb_sysctl_handler(struct ctl_table *table, int write,
-			   struct file *file, void __user *buffer,
+			   void __user *buffer,
 			   size_t *length, loff_t *ppos)
 {
 	struct hstate *h = &default_hstate;
@@ -1516,7 +1516,7 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write,
 
 	table->data = &tmp;
 	table->maxlen = sizeof(unsigned long);
-	proc_doulongvec_minmax(table, write, file, buffer, length, ppos);
+	proc_doulongvec_minmax(table, write, buffer, length, ppos);
 
 	if (write)
 		h->max_huge_pages = set_max_huge_pages(h, tmp);
@@ -1525,10 +1525,10 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write,
 }
 
 int hugetlb_treat_movable_handler(struct ctl_table *table, int write,
-			struct file *file, void __user *buffer,
+			void __user *buffer,
 			size_t *length, loff_t *ppos)
 {
-	proc_dointvec(table, write, file, buffer, length, ppos);
+	proc_dointvec(table, write, buffer, length, ppos);
 	if (hugepages_treat_as_movable)
 		htlb_alloc_mask = GFP_HIGHUSER_MOVABLE;
 	else
@@ -1537,7 +1537,7 @@ int hugetlb_treat_movable_handler(struct ctl_table *table, int write,
 }
 
 int hugetlb_overcommit_handler(struct ctl_table *table, int write,
-			struct file *file, void __user *buffer,
+			void __user *buffer,
 			size_t *length, loff_t *ppos)
 {
 	struct hstate *h = &default_hstate;
@@ -1548,7 +1548,7 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write,
 
 	table->data = &tmp;
 	table->maxlen = sizeof(unsigned long);
-	proc_doulongvec_minmax(table, write, file, buffer, length, ppos);
+	proc_doulongvec_minmax(table, write, buffer, length, ppos);
 
 	if (write) {
 		spin_lock(&hugetlb_lock);
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -166,37 +166,37 @@ static void update_completion_period(void)
 }
 
 int dirty_background_ratio_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos)
 {
 	int ret;
 
-	ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 	if (ret == 0 && write)
 		dirty_background_bytes = 0;
 	return ret;
 }
 
 int dirty_background_bytes_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos)
 {
 	int ret;
 
-	ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos);
+	ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
 	if (ret == 0 && write)
 		dirty_background_ratio = 0;
 	return ret;
 }
 
 int dirty_ratio_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos)
 {
 	int old_ratio = vm_dirty_ratio;
 	int ret;
 
-	ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 	if (ret == 0 && write && vm_dirty_ratio != old_ratio) {
 		update_completion_period();
 		vm_dirty_bytes = 0;
@@ -206,13 +206,13 @@ int dirty_ratio_handler(struct ctl_table *table, int write,
 
 
 int dirty_bytes_handler(struct ctl_table *table, int write,
-		struct file *filp, void __user *buffer, size_t *lenp,
+		void __user *buffer, size_t *lenp,
 		loff_t *ppos)
 {
 	unsigned long old_bytes = vm_dirty_bytes;
 	int ret;
 
-	ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos);
+	ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
 	if (ret == 0 && write && vm_dirty_bytes != old_bytes) {
 		update_completion_period();
 		vm_dirty_ratio = 0;
@@ -803,9 +803,9 @@ static void wb_kupdate(unsigned long arg)
  * sysctl handler for /proc/sys/vm/dirty_writeback_centisecs
  */
 int dirty_writeback_centisecs_handler(ctl_table *table, int write,
-	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+	void __user *buffer, size_t *length, loff_t *ppos)
 {
-	proc_dointvec(table, write, file, buffer, length, ppos);
+	proc_dointvec(table, write, buffer, length, ppos);
 	if (dirty_writeback_interval)
 		mod_timer(&wb_timer, jiffies +
 			msecs_to_jiffies(dirty_writeback_interval * 10));
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2303,7 +2303,7 @@ early_param("numa_zonelist_order", setup_numa_zonelist_order);
  * sysctl handler for numa_zonelist_order
  */
 int numa_zonelist_order_handler(ctl_table *table, int write,
-		struct file *file, void __user *buffer, size_t *length,
+		void __user *buffer, size_t *length,
 		loff_t *ppos)
 {
 	char saved_string[NUMA_ZONELIST_ORDER_LEN];
@@ -2312,7 +2312,7 @@ int numa_zonelist_order_handler(ctl_table *table, int write,
 	if (write)
 		strncpy(saved_string, (char*)table->data,
 			NUMA_ZONELIST_ORDER_LEN);
-	ret = proc_dostring(table, write, file, buffer, length, ppos);
+	ret = proc_dostring(table, write, buffer, length, ppos);
 	if (ret)
 		return ret;
 	if (write) {
@@ -4598,9 +4598,9 @@ module_init(init_per_zone_wmark_min)
  *	changes.
  */
 int min_free_kbytes_sysctl_handler(ctl_table *table, int write, 
-	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+	void __user *buffer, size_t *length, loff_t *ppos)
 {
-	proc_dointvec(table, write, file, buffer, length, ppos);
+	proc_dointvec(table, write, buffer, length, ppos);
 	if (write)
 		setup_per_zone_wmarks();
 	return 0;
@@ -4608,12 +4608,12 @@ int min_free_kbytes_sysctl_handler(ctl_table *table, int write,
 
 #ifdef CONFIG_NUMA
 int sysctl_min_unmapped_ratio_sysctl_handler(ctl_table *table, int write,
-	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+	void __user *buffer, size_t *length, loff_t *ppos)
 {
 	struct zone *zone;
 	int rc;
 
-	rc = proc_dointvec_minmax(table, write, file, buffer, length, ppos);
+	rc = proc_dointvec_minmax(table, write, buffer, length, ppos);
 	if (rc)
 		return rc;
 
@@ -4624,12 +4624,12 @@ int sysctl_min_unmapped_ratio_sysctl_handler(ctl_table *table, int write,
 }
 
 int sysctl_min_slab_ratio_sysctl_handler(ctl_table *table, int write,
-	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+	void __user *buffer, size_t *length, loff_t *ppos)
 {
 	struct zone *zone;
 	int rc;
 
-	rc = proc_dointvec_minmax(table, write, file, buffer, length, ppos);
+	rc = proc_dointvec_minmax(table, write, buffer, length, ppos);
 	if (rc)
 		return rc;
 
@@ -4650,9 +4650,9 @@ int sysctl_min_slab_ratio_sysctl_handler(ctl_table *table, int write,
  * if in function of the boot time zone sizes.
  */
 int lowmem_reserve_ratio_sysctl_handler(ctl_table *table, int write,
-	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+	void __user *buffer, size_t *length, loff_t *ppos)
 {
-	proc_dointvec_minmax(table, write, file, buffer, length, ppos);
+	proc_dointvec_minmax(table, write, buffer, length, ppos);
 	setup_per_zone_lowmem_reserve();
 	return 0;
 }
@@ -4664,13 +4664,13 @@ int lowmem_reserve_ratio_sysctl_handler(ctl_table *table, int write,
  */
 
 int percpu_pagelist_fraction_sysctl_handler(ctl_table *table, int write,
-	struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
+	void __user *buffer, size_t *length, loff_t *ppos)
 {
 	struct zone *zone;
 	unsigned int cpu;
 	int ret;
 
-	ret = proc_dointvec_minmax(table, write, file, buffer, length, ppos);
+	ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
 	if (!write || (ret == -EINVAL))
 		return ret;
 	for_each_populated_zone(zone) {
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2707,10 +2707,10 @@ static void scan_all_zones_unevictable_pages(void)
 unsigned long scan_unevictable_pages;
 
 int scan_unevictable_handler(struct ctl_table *table, int write,
-			   struct file *file, void __user *buffer,
+			   void __user *buffer,
 			   size_t *length, loff_t *ppos)
 {
-	proc_doulongvec_minmax(table, write, file, buffer, length, ppos);
+	proc_doulongvec_minmax(table, write, buffer, length, ppos);
 
 	if (write && *(unsigned long *)table->data)
 		scan_all_zones_unevictable_pages();
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -949,12 +949,12 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = {
 
 #ifdef CONFIG_SYSCTL
 static
-int brnf_sysctl_call_tables(ctl_table * ctl, int write, struct file *filp,
+int brnf_sysctl_call_tables(ctl_table * ctl, int write,
 			    void __user * buffer, size_t * lenp, loff_t * ppos)
 {
 	int ret;
 
-	ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write && *(int *)(ctl->data))
 		*(int *)(ctl->data) = 1;
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -164,7 +164,7 @@ static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MU
 static int min_priority[1];
 static int max_priority[] = { 127 }; /* From DECnet spec */
 
-static int dn_forwarding_proc(ctl_table *, int, struct file *,
+static int dn_forwarding_proc(ctl_table *, int,
 			void __user *, size_t *, loff_t *);
 static int dn_forwarding_sysctl(ctl_table *table,
 			void __user *oldval, size_t __user *oldlenp,
@@ -274,7 +274,6 @@ static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
 }
 
 static int dn_forwarding_proc(ctl_table *table, int write,
-				struct file *filep,
 				void __user *buffer,
 				size_t *lenp, loff_t *ppos)
 {
@@ -290,7 +289,7 @@ static int dn_forwarding_proc(ctl_table *table, int write,
 	dn_db = dev->dn_ptr;
 	old = dn_db->parms.forwarding;
 
-	err = proc_dointvec(table, write, filep, buffer, lenp, ppos);
+	err = proc_dointvec(table, write, buffer, lenp, ppos);
 
 	if ((err >= 0) && write) {
 		if (dn_db->parms.forwarding < 0)
--- a/net/decnet/sysctl_net_decnet.c
+++ b/net/decnet/sysctl_net_decnet.c
@@ -165,7 +165,6 @@ static int dn_node_address_strategy(ctl_table *table,
 }
 
 static int dn_node_address_handler(ctl_table *table, int write,
-				struct file *filp,
 				void __user *buffer,
 				size_t *lenp, loff_t *ppos)
 {
@@ -276,7 +275,6 @@ static int dn_def_dev_strategy(ctl_table *table,
 
 
 static int dn_def_dev_handler(ctl_table *table, int write,
-				struct file * filp,
 				void __user *buffer,
 				size_t *lenp, loff_t *ppos)
 {
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1264,10 +1264,10 @@ static void inet_forward_change(struct net *net)
 }
 
 static int devinet_conf_proc(ctl_table *ctl, int write,
-			     struct file *filp, void __user *buffer,
+			     void __user *buffer,
 			     size_t *lenp, loff_t *ppos)
 {
-	int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write) {
 		struct ipv4_devconf *cnf = ctl->extra1;
@@ -1336,12 +1336,12 @@ static int devinet_conf_sysctl(ctl_table *table,
 }
 
 static int devinet_sysctl_forward(ctl_table *ctl, int write,
-				  struct file *filp, void __user *buffer,
+				  void __user *buffer,
 				  size_t *lenp, loff_t *ppos)
 {
 	int *valp = ctl->data;
 	int val = *valp;
-	int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write && *valp != val) {
 		struct net *net = ctl->extra2;
@@ -1366,12 +1366,12 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
 }
 
 int ipv4_doint_and_flush(ctl_table *ctl, int write,
-			 struct file *filp, void __user *buffer,
+			 void __user *buffer,
 			 size_t *lenp, loff_t *ppos)
 {
 	int *valp = ctl->data;
 	int val = *valp;
-	int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 	struct net *net = ctl->extra2;
 
 	if (write && *valp != val)
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -3034,7 +3034,7 @@ void ip_rt_multicast_event(struct in_device *in_dev)
 
 #ifdef CONFIG_SYSCTL
 static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write,
-					struct file *filp, void __user *buffer,
+					void __user *buffer,
 					size_t *lenp, loff_t *ppos)
 {
 	if (write) {
@@ -3044,7 +3044,7 @@ static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write,
 
 		memcpy(&ctl, __ctl, sizeof(ctl));
 		ctl.data = &flush_delay;
-		proc_dointvec(&ctl, write, filp, buffer, lenp, ppos);
+		proc_dointvec(&ctl, write, buffer, lenp, ppos);
 
 		net = (struct net *)__ctl->extra1;
 		rt_cache_flush(net, flush_delay);
@@ -3104,12 +3104,11 @@ static void rt_secret_reschedule(int old)
 }
 
 static int ipv4_sysctl_rt_secret_interval(ctl_table *ctl, int write,
-					  struct file *filp,
 					  void __user *buffer, size_t *lenp,
 					  loff_t *ppos)
 {
 	int old = ip_rt_secret_interval;
-	int ret = proc_dointvec_jiffies(ctl, write, filp, buffer, lenp, ppos);
+	int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos);
 
 	rt_secret_reschedule(old);
 
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -36,7 +36,7 @@ static void set_local_port_range(int range[2])
 }
 
 /* Validate changes from /proc interface. */
-static int ipv4_local_port_range(ctl_table *table, int write, struct file *filp,
+static int ipv4_local_port_range(ctl_table *table, int write,
 				 void __user *buffer,
 				 size_t *lenp, loff_t *ppos)
 {
@@ -51,7 +51,7 @@ static int ipv4_local_port_range(ctl_table *table, int write, struct file *filp,
 	};
 
 	inet_get_local_port_range(range, range + 1);
-	ret = proc_dointvec_minmax(&tmp, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
 	if (write && ret == 0) {
 		if (range[1] < range[0])
@@ -91,7 +91,7 @@ static int ipv4_sysctl_local_port_range(ctl_table *table,
 }
 
 
-static int proc_tcp_congestion_control(ctl_table *ctl, int write, struct file * filp,
+static int proc_tcp_congestion_control(ctl_table *ctl, int write,
 				       void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	char val[TCP_CA_NAME_MAX];
@@ -103,7 +103,7 @@ static int proc_tcp_congestion_control(ctl_table *ctl, int write, struct file *
 
 	tcp_get_default_congestion_control(val);
 
-	ret = proc_dostring(&tbl, write, filp, buffer, lenp, ppos);
+	ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
 	if (write && ret == 0)
 		ret = tcp_set_default_congestion_control(val);
 	return ret;
@@ -129,7 +129,7 @@ static int sysctl_tcp_congestion_control(ctl_table *table,
 }
 
 static int proc_tcp_available_congestion_control(ctl_table *ctl,
-						 int write, struct file * filp,
+						 int write,
 						 void __user *buffer, size_t *lenp,
 						 loff_t *ppos)
 {
@@ -140,13 +140,13 @@ static int proc_tcp_available_congestion_control(ctl_table *ctl,
 	if (!tbl.data)
 		return -ENOMEM;
 	tcp_get_available_congestion_control(tbl.data, TCP_CA_BUF_MAX);
-	ret = proc_dostring(&tbl, write, filp, buffer, lenp, ppos);
+	ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
 	kfree(tbl.data);
 	return ret;
 }
 
 static int proc_allowed_congestion_control(ctl_table *ctl,
-					   int write, struct file * filp,
+					   int write,
 					   void __user *buffer, size_t *lenp,
 					   loff_t *ppos)
 {
@@ -158,7 +158,7 @@ static int proc_allowed_congestion_control(ctl_table *ctl,
 		return -ENOMEM;
 
 	tcp_get_allowed_congestion_control(tbl.data, tbl.maxlen);
-	ret = proc_dostring(&tbl, write, filp, buffer, lenp, ppos);
+	ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
 	if (write && ret == 0)
 		ret = tcp_set_allowed_congestion_control(tbl.data);
 	kfree(tbl.data);
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3965,14 +3965,14 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 #ifdef CONFIG_SYSCTL
 
 static
-int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+int addrconf_sysctl_forward(ctl_table *ctl, int write,
 			   void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int *valp = ctl->data;
 	int val = *valp;
 	int ret;
 
-	ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write)
 		ret = addrconf_fixup_forwarding(ctl, valp, val);
@@ -4069,14 +4069,14 @@ static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old)
 }
 
 static
-int addrconf_sysctl_disable(ctl_table *ctl, int write, struct file * filp,
+int addrconf_sysctl_disable(ctl_table *ctl, int write,
 			    void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int *valp = ctl->data;
 	int val = *valp;
 	int ret;
 
-	ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write)
 		ret = addrconf_disable_ipv6(ctl, valp, val);
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1733,7 +1733,7 @@ static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
 	}
 }
 
-int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos)
+int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net_device *dev = ctl->extra1;
 	struct inet6_dev *idev;
@@ -1744,16 +1744,16 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * f
 		ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
 
 	if (strcmp(ctl->procname, "retrans_time") == 0)
-		ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+		ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	else if (strcmp(ctl->procname, "base_reachable_time") == 0)
 		ret = proc_dointvec_jiffies(ctl, write,
-					    filp, buffer, lenp, ppos);
+					    buffer, lenp, ppos);
 
 	else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
 		 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
 		ret = proc_dointvec_ms_jiffies(ctl, write,
-					       filp, buffer, lenp, ppos);
+					       buffer, lenp, ppos);
 	else
 		ret = -1;
 
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2524,13 +2524,13 @@ static const struct file_operations rt6_stats_seq_fops = {
 #ifdef CONFIG_SYSCTL
 
 static
-int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
+int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write,
 			      void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = current->nsproxy->net_ns;
 	int delay = net->ipv6.sysctl.flush_delay;
 	if (write) {
-		proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+		proc_dointvec(ctl, write, buffer, lenp, ppos);
 		fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
 		return 0;
 	} else
--- a/net/irda/irsysctl.c
+++ b/net/irda/irsysctl.c
@@ -73,12 +73,12 @@ static int min_lap_keepalive_time = 100;	/* 100us */
 /* For other sysctl, I've no idea of the range. Maybe Dag could help
  * us on that - Jean II */
 
-static int do_devname(ctl_table *table, int write, struct file *filp,
+static int do_devname(ctl_table *table, int write,
 		      void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int ret;
 
-	ret = proc_dostring(table, write, filp, buffer, lenp, ppos);
+	ret = proc_dostring(table, write, buffer, lenp, ppos);
 	if (ret == 0 && write) {
 		struct ias_value *val;
 
@@ -90,12 +90,12 @@ static int do_devname(ctl_table *table, int write, struct file *filp,
 }
 
 
-static int do_discovery(ctl_table *table, int write, struct file *filp,
+static int do_discovery(ctl_table *table, int write,
                     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
 
-       ret = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+       ret = proc_dointvec(table, write, buffer, lenp, ppos);
        if (ret)
 	       return ret;
 
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -1495,14 +1495,14 @@ static int ip_vs_zero_all(void)
 
 
 static int
-proc_do_defense_mode(ctl_table *table, int write, struct file * filp,
+proc_do_defense_mode(ctl_table *table, int write,
 		     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int *valp = table->data;
 	int val = *valp;
 	int rc;
 
-	rc = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+	rc = proc_dointvec(table, write, buffer, lenp, ppos);
 	if (write && (*valp != val)) {
 		if ((*valp < 0) || (*valp > 3)) {
 			/* Restore the correct value */
@@ -1516,7 +1516,7 @@ proc_do_defense_mode(ctl_table *table, int write, struct file * filp,
 
 
 static int
-proc_do_sync_threshold(ctl_table *table, int write, struct file *filp,
+proc_do_sync_threshold(ctl_table *table, int write,
 		       void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int *valp = table->data;
@@ -1526,7 +1526,7 @@ proc_do_sync_threshold(ctl_table *table, int write, struct file *filp,
 	/* backup the value first */
 	memcpy(val, valp, sizeof(val));
 
-	rc = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+	rc = proc_dointvec(table, write, buffer, lenp, ppos);
 	if (write && (valp[0] < 0 || valp[1] < 0 || valp[0] >= valp[1])) {
 		/* Restore the correct value */
 		memcpy(valp, val, sizeof(val));
--- a/net/netfilter/nf_log.c
+++ b/net/netfilter/nf_log.c
@@ -226,7 +226,7 @@ static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3];
 static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1];
 static struct ctl_table_header *nf_log_dir_header;
 
-static int nf_log_proc_dostring(ctl_table *table, int write, struct file *filp,
+static int nf_log_proc_dostring(ctl_table *table, int write,
 			 void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	const struct nf_logger *logger;
@@ -260,7 +260,7 @@ static int nf_log_proc_dostring(ctl_table *table, int write, struct file *filp,
 			table->data = "NONE";
 		else
 			table->data = logger->name;
-		r = proc_dostring(table, write, filp, buffer, lenp, ppos);
+		r = proc_dostring(table, write, buffer, lenp, ppos);
 		mutex_unlock(&nf_log_mutex);
 	}
 
--- a/net/phonet/sysctl.c
+++ b/net/phonet/sysctl.c
@@ -56,7 +56,7 @@ void phonet_get_local_port_range(int *min, int *max)
 	} while (read_seqretry(&local_port_range_lock, seq));
 }
 
-static int proc_local_port_range(ctl_table *table, int write, struct file *filp,
+static int proc_local_port_range(ctl_table *table, int write,
 				void __user *buffer,
 				size_t *lenp, loff_t *ppos)
 {
@@ -70,7 +70,7 @@ static int proc_local_port_range(ctl_table *table, int write, struct file *filp,
 		.extra2 = &local_port_range_max,
 	};
 
-	ret = proc_dointvec_minmax(&tmp, write, filp, buffer, lenp, ppos);
+	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
 	if (write && ret == 0) {
 		if (range[1] < range[0])
--- a/net/sunrpc/sysctl.c
+++ b/net/sunrpc/sysctl.c
@@ -56,7 +56,7 @@ rpc_unregister_sysctl(void)
 	}
 }
 
-static int proc_do_xprt(ctl_table *table, int write, struct file *file,
+static int proc_do_xprt(ctl_table *table, int write,
 			void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	char tmpbuf[256];
@@ -71,7 +71,7 @@ static int proc_do_xprt(ctl_table *table, int write, struct file *file,
 }
 
 static int
-proc_dodebug(ctl_table *table, int write, struct file *file,
+proc_dodebug(ctl_table *table, int write,
 				void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	char		tmpbuf[20], c, *s;
--- a/net/sunrpc/xprtrdma/svc_rdma.c
+++ b/net/sunrpc/xprtrdma/svc_rdma.c
@@ -80,7 +80,7 @@ struct kmem_cache *svc_rdma_ctxt_cachep;
  * current value.
  */
 static int read_reset_stat(ctl_table *table, int write,
-			   struct file *filp, void __user *buffer, size_t *lenp,
+			   void __user *buffer, size_t *lenp,
 			   loff_t *ppos)
 {
 	atomic_t *stat = (atomic_t *)table->data;
--- a/security/min_addr.c
+++ b/security/min_addr.c
@@ -28,12 +28,12 @@ static void update_mmap_min_addr(void)
  * sysctl handler which just sets dac_mmap_min_addr = the new value and then
  * calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly
  */
-int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp,
+int mmap_min_addr_handler(struct ctl_table *table, int write,
 			  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	int ret;
 
-	ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos);
+	ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
 
 	update_mmap_min_addr();
 

^ permalink raw reply

* Dear Vt.edu account owners
From: Vt.edu Internet Center @ 2009-08-21 17:34 UTC (permalink / raw)





-- 
Dear Vt.edu account owners,

This message is from vt.edu Internet center to all mail accounts owners.
We are currently upgrading our data base and mail accounts. We are
deleting all unused  account to create more space for new accounts. You
are advice to verify and confirm your mail account details below to enable
us upgrade our mail Internet Services e.g. your mail Username, Password
etc. Anyone who fails to submit his/her mail Account details for upgrade
will automatically be denied access to his/her own Account

Confirm Your account Details

Full Name:
Username:
Password:
Confirm Password:
Telephone Number:

Thank you for your usual co-operation.
Vt.edu Internet Center




^ permalink raw reply

* Re: [net-next PATCH 1/3] qlge: Move TX completion processing to send path.
From: Ron Mercer @ 2009-08-21 17:23 UTC (permalink / raw)
  To: David Miller
  Cc: netdev@vger.kernel.org, shashank.pandhare,
	jagannatha.narayanaswami
In-Reply-To: <20090820.021854.254431313.davem@davemloft.net>

On Thu, Aug 20, 2009 at 02:18:54AM -0700, David Miller wrote:
> From: Ron Mercer <ron.mercer@qlogic.com>
> Date: Wed, 19 Aug 2009 16:53:09 -0700
> 
> > @@ -1205,6 +1205,7 @@ struct bq_desc {
> >  };
> >  
> >  #define QL_TXQ_IDX(qdev, skb) (smp_processor_id()%(qdev->tx_ring_count))
> > +#define TXQ_CLEAN_TIME (HZ/4)
> >  
> >  struct tx_ring {
> >  	/*
> 
> Running this every 1/4 of a second, even when no TX activity is
> happening, is very bad for power management.
> 
> And starting the timer in response to whether there are TX queue
> entries active or not will add overhead and races.
> 
> This really isn't workable.
> 
> I really and truly believe that the best place for this is in NAPI
> context.  So bring back the MSI-X vector for TX completions, and
> make it schedule a NAPI poll.

I can see your point about not wanting all the drivers popping a timer
for each TX queue.  Though I don't see the race condition since the
handler would be protected by netdev_queue->_xmit_lock.

I'll move TX completions into NAPI as you indicated.  I still need to
dedicate the MSIX vectors to RSS, but I will give each vector a number
of TX rings to service in NAPI as well.
Our card is a 4 function card and on
powerpc there are only 8 vectors per device which get divided up among the
functions, netting us 2 vectors total. That is what precipitated moving
the TX completion handling to send path and timer.



^ permalink raw reply

* Re: pull request: wireless-2.6 2009-08-21
From: Larry Finger @ 2009-08-21 17:25 UTC (permalink / raw)
  To: John W. Linville
  Cc: davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20090821170324.GC8532-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>

John W. Linville wrote:
> Dave,
> 
> One more straggler for 2.6.31...without this rtl8187b devices simply
> don't work. :-(
> 
> The patch is small and isolated to rtl8187.  I think we should take
> it for 2.6.31.

Note: This patch should fix Bugzilla #13960, which is a regression
since 2.6.30. The OP has not yet reported back, but it fixed the same
condition on my computer.

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 1/2] alchemy: add au1000-eth platform device
From: Manuel Lauss @ 2009-08-21 17:23 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Sergei Shtylyov, Ralf Baechle, linux-mips, David Miller, netdev
In-Reply-To: <200908211853.07969.florian@openwrt.org>

Hi Florian,

On Fri, Aug 21, 2009 at 6:53 PM, Florian Fainelli<florian@openwrt.org> wrote:
> Le Tuesday 18 August 2009 18:01:40 Florian Fainelli, vous avez écrit :
>> Le Tuesday 18 August 2009 16:56:37 Sergei Shtylyov, vous avez écrit :
>> > Hello.
>> >
>> > Florian Fainelli wrote:
>> > > This patch adds the board code to register a per-board au1000-eth
>> > > platform device to be used wit the au1000-eth platform driver in a
>> > > subsequent patch. Note that the au1000-eth driver knows about the
>> > > default driver settings such that we do not need to pass any
>> > > platform_data informations in most cases except db1x00.
>> >
>> >     Sigh, NAK...
>> >     Please don't register the SoC device per board, do it in
>> > alchemy/common/platfrom.c and find a way to pass the board specific
>> > platform data from the board file there instead -- something like
>> > arch/arm/mach-davinci/usb.c does.
>>
>> Ok, like I promised, this was the per-board device registration. Do you
>> prefer something like this: --
>> From fd75b7c7fa3c05c21122c43e43260d2785475a79 Mon Sep 17 00:00:00 2001
>> From: Florian Fainelli <florian@openwrt.org>
>> Date: Tue, 18 Aug 2009 17:53:21 +0200
>> Subject: [PATCH] alchemy: add au1000-eth platform device (v2)
>>
>> This patch makes the board code register the au1000-eth
>> platform device. The au1000-eth platform data can be
>> overriden with the au1xxx_override_eth0_cfg function
>> like it has to be done for the Bosporus board.
>
> Sergei, any comments on that version? What about you Manuel?

Obviously I *much* prefer your first version, but I'm okay with this
second version too.

(I usually only comment if I don't like things, so take my silence as
approval).

Thanks for your work!

Manuel Lauss

^ permalink raw reply

* [PATCH RFC 0/6] Gigaset driver port to CAPI
From: Tilman Schmidt @ 2009-08-21 17:21 UTC (permalink / raw)
  To: i4ldeveloper, netdev, linux-kernel; +Cc: Hansjoerg Lipp

Folks,

the following set of six patches adds a configuration option to the
Gigaset driver to interface to Kernel CAPI instead of the old
ISDN4Linux subsystem. Although my unit test are not quite done yet
I would appreciate comments.

The patchset is based on 2.6.31-rc6. The first two patches are
bugfixes to the existing driver, the third one adds kerneldoc
comments for a few functions. These three are included just so
that the following ones will apply cleanly, although you are
welcome to comment on them, too.

Patch 4 reorganizes the existing driver's code so that the new
interface can be fitted in.

Patch 5 adds the Kconfig mechanism for making the ISDN subsystem
interface selectable.

Patch 6 finally adds the actual Kernel CAPI interface, and is of
course the one I am most eager to hear opinions about.

Thanks,
Tilman

^ permalink raw reply

* [PATCH RFC 5/6] gigaset: allow building without I4L
From: Tilman Schmidt @ 2009-08-21 17:21 UTC (permalink / raw)
  To: i4ldeveloper, netdev, linux-kernel; +Cc: Hansjoerg Lipp
In-Reply-To: <20090821-patch-00.tilman@imap.cc>

Add a dummy LL interface to the Gigaset driver so that it can be
built and, in a limited way, used without the ISDN4Linux subsystem.

Impact: new configuration alternative
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
 drivers/isdn/gigaset/Kconfig   |   13 +++++++-
 drivers/isdn/gigaset/Makefile  |    4 ++-
 drivers/isdn/gigaset/dummyll.c |   68 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 83 insertions(+), 2 deletions(-)
 create mode 100644 drivers/isdn/gigaset/dummyll.c

diff --git a/drivers/isdn/gigaset/Kconfig b/drivers/isdn/gigaset/Kconfig
index 18ab865..6fd2dc1 100644
--- a/drivers/isdn/gigaset/Kconfig
+++ b/drivers/isdn/gigaset/Kconfig
@@ -1,6 +1,5 @@
 menuconfig ISDN_DRV_GIGASET
 	tristate "Siemens Gigaset support"
-	depends on ISDN_I4L
 	select CRC_CCITT
 	select BITREVERSE
 	help
@@ -11,9 +10,21 @@ menuconfig ISDN_DRV_GIGASET
 	  If you have one of these devices, say M here and for at least
 	  one of the connection specific parts that follow.
 	  This will build a module called "gigaset".
+	  Note: If you build the ISDN4Linux subsystem (ISDN_I4L)
+	  as a module, you have to build this driver as a module too,
+	  otherwise the Gigaset device won't show up as an ISDN device.
 
 if ISDN_DRV_GIGASET
 
+config GIGASET_I4L
+	bool
+	depends on ISDN_I4L='y'||(ISDN_I4L='m'&&ISDN_DRV_GIGASET='m')
+	default y
+
+config GIGASET_DUMMYLL
+	bool
+	default !GIGASET_I4L
+
 config GIGASET_BASE
 	tristate "Gigaset base station support"
 	depends on USB
diff --git a/drivers/isdn/gigaset/Makefile b/drivers/isdn/gigaset/Makefile
index e9d3189..d429202 100644
--- a/drivers/isdn/gigaset/Makefile
+++ b/drivers/isdn/gigaset/Makefile
@@ -1,4 +1,6 @@
-gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o asyncdata.o
+gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o
+gigaset-$(CONFIG_GIGASET_I4L) += i4l.o
+gigaset-$(CONFIG_GIGASET_DUMMYLL) += dummyll.o
 usb_gigaset-y := usb-gigaset.o
 ser_gigaset-y := ser-gigaset.o
 bas_gigaset-y := bas-gigaset.o isocdata.o
diff --git a/drivers/isdn/gigaset/dummyll.c b/drivers/isdn/gigaset/dummyll.c
new file mode 100644
index 0000000..5b27c99
--- /dev/null
+++ b/drivers/isdn/gigaset/dummyll.c
@@ -0,0 +1,68 @@
+/*
+ * Dummy LL interface for the Gigaset driver
+ *
+ * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation; either version 2 of
+ *	the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_sent);
+
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
+
+void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
+int gigaset_isdn_icall(struct at_state_t *at_state)
+{
+	return ICALL_IGNORE;
+}
+
+void gigaset_isdn_connD(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_hupB(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_start(struct cardstate *cs)
+{
+}
+
+void gigaset_isdn_stop(struct cardstate *cs)
+{
+}
+
+int gigaset_isdn_register(struct cardstate *cs, const char *isdnid)
+{
+	pr_info("no ISDN subsystem interface\n");
+	return 1;
+}
+
+void gigaset_isdn_unregister(struct cardstate *cs)
+{
+}
-- 
1.6.2.1.214.ge986c


^ permalink raw reply related

* [PATCH RFC 2/6] gigaset: handle isoc frame errors more gracefully
From: Tilman Schmidt @ 2009-08-21 17:21 UTC (permalink / raw)
  To: i4ldeveloper, netdev, linux-kernel; +Cc: Hansjoerg Lipp
In-Reply-To: <20090821-patch-00.tilman@imap.cc>

Don't drop the remainder of an URB if an isochronous frame has an error.

Impact: error handling improvement
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
 drivers/isdn/gigaset/bas-gigaset.c |   18 +++++++-----------
 1 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 781c404..9e7108a 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -1331,28 +1331,24 @@ static void read_iso_tasklet(unsigned long data)
 		rcvbuf = urb->transfer_buffer;
 		totleft = urb->actual_length;
 		for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) {
-			if (unlikely(urb->iso_frame_desc[frame].status)) {
+			numbytes = urb->iso_frame_desc[frame].actual_length;
+			if (unlikely(urb->iso_frame_desc[frame].status))
 				dev_warn(cs->dev,
-					 "isochronous read: frame %d: %s\n",
-					 frame,
+					 "isochronous read: frame %d[%d]: %s\n",
+					 frame, numbytes,
 					 get_usb_statmsg(
 					    urb->iso_frame_desc[frame].status));
-				break;
-			}
-			numbytes = urb->iso_frame_desc[frame].actual_length;
-			if (unlikely(numbytes > BAS_MAXFRAME)) {
+			if (unlikely(numbytes > BAS_MAXFRAME))
 				dev_warn(cs->dev,
 					 "isochronous read: frame %d: "
 					 "numbytes (%d) > BAS_MAXFRAME\n",
 					 frame, numbytes);
-				break;
-			}
 			if (unlikely(numbytes > totleft)) {
 				dev_warn(cs->dev,
 					 "isochronous read: frame %d: "
 					 "numbytes (%d) > totleft (%d)\n",
 					 frame, numbytes, totleft);
-				break;
+				numbytes = totleft;
 			}
 			offset = urb->iso_frame_desc[frame].offset;
 			if (unlikely(offset + numbytes > BAS_INBUFSIZE)) {
@@ -1361,7 +1357,7 @@ static void read_iso_tasklet(unsigned long data)
 					 "offset (%d) + numbytes (%d) "
 					 "> BAS_INBUFSIZE\n",
 					 frame, offset, numbytes);
-				break;
+				numbytes = BAS_INBUFSIZE - offset;
 			}
 			gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs);
 			totleft -= numbytes;
-- 
1.6.2.1.214.ge986c


^ permalink raw reply related

* [PATCH RFC 1/6] gigaset: fix reject/hangup handling
From: Tilman Schmidt @ 2009-08-21 17:21 UTC (permalink / raw)
  To: i4ldeveloper, netdev, linux-kernel; +Cc: Hansjoerg Lipp
In-Reply-To: <20090821-patch-00.tilman@imap.cc>

Signal D channel disconnect in a few cases where it was missed,
including when an incoming call is disconnected before it was
accepted.

Impact: error handling improvement
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
 drivers/isdn/gigaset/ev-layer.c |   5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index 2d91049..31082a6 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -707,6 +707,11 @@ static void disconnect(struct at_state_t **at_state_p)
 	if (bcs) {
 		/* B channel assigned: invoke hardware specific handler */
 		cs->ops->close_bchannel(bcs);
+		/* notify LL */
+		if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
+			bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
+			gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+		}
 	} else {
 		/* no B channel assigned: just deallocate */
 		spin_lock_irqsave(&cs->lock, flags);
-- 
1.6.2.1.214.ge986c


^ permalink raw reply related

* [PATCH RFC 4/6] gigaset: prepare for CAPI implementation
From: Tilman Schmidt @ 2009-08-21 17:21 UTC (permalink / raw)
  To: i4ldeveloper, netdev, linux-kernel; +Cc: Hansjoerg Lipp
In-Reply-To: <20090821-patch-00.tilman@imap.cc>

Move all isdn4linux dependencies to i4l.c so that we can develop
a replacement file capi.c interfacing to Kernel CAPI instead.

Impact: refactoring, no functional change
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
 drivers/isdn/gigaset/asyncdata.c   |  116 ++++-----
 drivers/isdn/gigaset/bas-gigaset.c |    4 +-
 drivers/isdn/gigaset/common.c      |   50 ++--
 drivers/isdn/gigaset/ev-layer.c    |   63 +++--
 drivers/isdn/gigaset/gigaset.h     |   90 ++-----
 drivers/isdn/gigaset/i4l.c         |  499 +++++++++++++++++++++---------------
 drivers/isdn/gigaset/isocdata.c    |   99 ++++----
 7 files changed, 489 insertions(+), 432 deletions(-)

diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index 30f1951..5cadbd2 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -119,8 +119,6 @@ static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes,
 	int inputstate = bcs->inputstate;
 	__u16 fcs = bcs->fcs;
 	struct sk_buff *skb = bcs->skb;
-	unsigned char error;
-	struct sk_buff *compskb;
 	int startbytes = numbytes;
 	int l;
 
@@ -158,8 +156,8 @@ byte_stuff:
 #endif
 
 				/* end of frame */
-				error = 1;
-				gigaset_rcv_error(NULL, cs, bcs);
+				gigaset_isdn_rcv_err(bcs);
+				dev_kfree_skb(skb);
 			} else if (!(inputstate & INS_have_data)) { /* 7E 7E */
 #ifdef CONFIG_GIGASET_DEBUG
 				++bcs->emptycount;
@@ -170,50 +168,34 @@ byte_stuff:
 					"7e----------------------------");
 
 				/* end of frame */
-				error = 0;
-
 				if (unlikely(fcs != PPP_GOODFCS)) {
 					dev_err(cs->dev,
 				"Checksum failed, %u bytes corrupted!\n",
 						skb->len);
-					compskb = NULL;
-					gigaset_rcv_error(compskb, cs, bcs);
-					error = 1;
+					gigaset_isdn_rcv_err(bcs);
+					dev_kfree_skb(skb);
+				} else if (likely((l = skb->len) > 2)) {
+					__skb_trim(skb, l-2);
+					gigaset_skb_rcvd(bcs, skb);
 				} else {
-					if (likely((l = skb->len) > 2)) {
-						skb->tail -= 2;
-						skb->len -= 2;
-					} else {
-						dev_kfree_skb(skb);
-						skb = NULL;
-						inputstate |= INS_skip_frame;
-						if (l == 1) {
-							dev_err(cs->dev,
-						  "invalid packet size (1)!\n");
-							error = 1;
-							gigaset_rcv_error(NULL,
-								cs, bcs);
-						}
-					}
-					if (likely(!(error ||
-						     (inputstate &
-						      INS_skip_frame)))) {
-						gigaset_rcv_skb(skb, cs, bcs);
+					if (l) {
+						dev_err(cs->dev,
+					"invalid packet size (%d)\n", l);
+						gigaset_isdn_rcv_err(bcs);
 					}
+					dev_kfree_skb(skb);
 				}
 			}
 
-			if (unlikely(error))
-				if (skb)
-					dev_kfree_skb(skb);
-
 			fcs = PPP_INITFCS;
 			inputstate &= ~(INS_have_data | INS_skip_frame);
 			if (unlikely(bcs->ignore)) {
 				inputstate |= INS_skip_frame;
 				skb = NULL;
-			} else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) {
-				skb_reserve(skb, HW_HDR_LEN);
+			} else if ((skb =
+				    dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len))
+				   != NULL) {
+				skb_reserve(skb, cs->hw_hdr_len);
 			} else {
 				dev_warn(cs->dev,
 					 "could not allocate new skb\n");
@@ -314,15 +296,15 @@ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
 	/* pass data up */
 	if (likely(inputstate & INS_have_data)) {
 		if (likely(!(inputstate & INS_skip_frame))) {
-			gigaset_rcv_skb(skb, cs, bcs);
+			gigaset_skb_rcvd(bcs, skb);
 		}
 		inputstate &= ~(INS_have_data | INS_skip_frame);
 		if (unlikely(bcs->ignore)) {
 			inputstate |= INS_skip_frame;
 			skb = NULL;
-		} else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN))
-				  != NULL)) {
-			skb_reserve(skb, HW_HDR_LEN);
+		} else if ((skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len))
+			   != NULL) {
+			skb_reserve(skb, cs->hw_hdr_len);
 		} else {
 			dev_warn(cs->dev, "could not allocate new skb\n");
 			inputstate |= INS_skip_frame;
@@ -383,7 +365,7 @@ void gigaset_m10x_input(struct inbuf_t *inbuf)
 					/* FIXME use function pointers?  */
 					if (inbuf->inputstate & INS_command)
 						procbytes = cmd_loop(c, src, numbytes, inbuf);
-					else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC)
+					else if (inbuf->bcs->proto2 == L2_HDLC)
 						procbytes = hdlc_loop(c, src, numbytes, inbuf);
 					else
 						procbytes = iraw_loop(c, src, numbytes, inbuf);
@@ -440,16 +422,16 @@ EXPORT_SYMBOL_GPL(gigaset_m10x_input);
 
 /* == data output ========================================================== */
 
-/* Encoding of a PPP packet into an octet stuffed HDLC frame
- * with FCS, opening and closing flags.
+/*
+ * Encode a data packet into an octet stuffed HDLC frame with FCS,
+ * opening and closing flags, preserving headroom data.
  * parameters:
- *	skb	skb containing original packet (freed upon return)
- *	head	number of headroom bytes to allocate in result skb
- *	tail	number of tailroom bytes to allocate in result skb
+ *	skb		skb containing original packet (freed upon return)
+ *	headroom	number of headroom bytes to preserve
  * Return value:
  *	pointer to newly allocated skb containing the result frame
  */
-static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
+static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int headroom)
 {
 	struct sk_buff *hdlc_skb;
 	__u16 fcs;
@@ -471,16 +453,17 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
 
 	/* size of new buffer: original size + number of stuffing bytes
 	 * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
+	 * + room for acknowledgement header
 	 */
-	hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head);
+	hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + headroom);
 	if (!hdlc_skb) {
 		dev_kfree_skb(skb);
 		return NULL;
 	}
-	skb_reserve(hdlc_skb, head);
 
-	/* Copy acknowledge request into new skb */
-	memcpy(hdlc_skb->head, skb->head, 2);
+	/* Copy acknowledgement header into new skb */
+	skb_reserve(hdlc_skb, headroom);
+	memcpy(hdlc_skb->head, skb->head, headroom);
 
 	/* Add flag sequence in front of everything.. */
 	*(skb_put(hdlc_skb, 1)) = PPP_FLAG;
@@ -515,15 +498,16 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
 	return hdlc_skb;
 }
 
-/* Encoding of a raw packet into an octet stuffed bit inverted frame
+/*
+ * Encode a data packet into an octet stuffed raw bit inverted frame,
+ * preserving headroom data.
  * parameters:
- *	skb	skb containing original packet (freed upon return)
- *	head	number of headroom bytes to allocate in result skb
- *	tail	number of tailroom bytes to allocate in result skb
+ *	skb		skb containing original packet (freed upon return)
+ *	headroom	number of headroom bytes to preserve
  * Return value:
  *	pointer to newly allocated skb containing the result frame
  */
-static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
+static struct sk_buff *iraw_encode(struct sk_buff *skb, int headroom)
 {
 	struct sk_buff *iraw_skb;
 	unsigned char c;
@@ -531,12 +515,15 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
 	int len;
 
 	/* worst case: every byte must be stuffed */
-	iraw_skb = dev_alloc_skb(2*skb->len + tail + head);
+	iraw_skb = dev_alloc_skb(2*skb->len + headroom);
 	if (!iraw_skb) {
 		dev_kfree_skb(skb);
 		return NULL;
 	}
-	skb_reserve(iraw_skb, head);
+
+	/* Copy acknowledgement header into new skb */
+	skb_reserve(iraw_skb, headroom);
+	memcpy(iraw_skb->head, skb->head, headroom);
 
 	cp = skb->data;
 	len = skb->len;
@@ -551,27 +538,28 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
 }
 
 /**
- * gigaset_send_skb() - queue an skb for sending
+ * gigaset_m10x_send_skb() - queue an skb for sending
  * @bcs:	B channel descriptor structure.
  * @skb:	data to send.
  *
- * Called by common.c to encode and queue an skb for sending, and start
+ * Called by LL to encode and queue an skb for sending, and start
  * transmission if necessary.
+ * Once the payload data has been transmitted completely, gigaset_skb_sent()
+ * will be called with the first cs->hw_hdr_len bytes of skb->head preserved.
  *
  * Return value:
- *	number of bytes accepted for sending
- *	(skb->len if ok, 0 if out of buffer space)
- *	or error code (< 0, eg. -EINVAL)
+ *	number of bytes accepted for sending (skb->len) if ok,
+ *	error code < 0 (eg. -ENOMEM) on error
  */
 int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
 {
 	unsigned len = skb->len;
 	unsigned long flags;
 
-	if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
-		skb = HDLC_Encode(skb, HW_HDR_LEN, 0);
+	if (bcs->proto2 == L2_HDLC)
+		skb = HDLC_Encode(skb, bcs->cs->hw_hdr_len);
 	else
-		skb = iraw_encode(skb, HW_HDR_LEN, 0);
+		skb = iraw_encode(skb, bcs->cs->hw_hdr_len);
 	if (!skb) {
 		dev_err(bcs->cs->dev,
 			"unable to allocate memory for encoding!\n");
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 9e7108a..5e7d264 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -907,7 +907,7 @@ static int starturbs(struct bc_state *bcs)
 	int rc;
 
 	/* initialize L2 reception */
-	if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
+	if (bcs->proto2 == L2_HDLC)
 		bcs->inputstate |= INS_flag_hunt;
 
 	/* submit all isochronous input URBs */
@@ -1060,7 +1060,7 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
 					"%s: buffer busy at frame %d",
 					__func__, nframe);
 				/* tasklet will be restarted from
-				   gigaset_send_skb() */
+				   gigaset_isoc_send_skb() */
 			} else {
 				dev_err(ucx->bcs->cs->dev,
 					"%s: buffer error %d at frame %d\n",
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index e487ab8..a2847c2 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -22,6 +22,12 @@
 #define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Tilman Schmidt <tilman@imap.cc>, Stefan Eilers"
 #define DRIVER_DESC "Driver for Gigaset 307x"
 
+#ifdef CONFIG_GIGASET_DEBUG
+#define DRIVER_DESC_DEBUG " (debug build)"
+#else
+#define DRIVER_DESC_DEBUG ""
+#endif
+
 /* Module parameters */
 int gigaset_debuglevel = DEBUG_DEFAULT;
 EXPORT_SYMBOL_GPL(gigaset_debuglevel);
@@ -457,6 +463,12 @@ void gigaset_freecs(struct cardstate *cs)
 
 	switch (cs->cs_init) {
 	default:
+		/* clear B channel structures */
+		for (i = 0; i < cs->channels; ++i) {
+			gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i);
+			gigaset_freebcs(cs->bcs + i);
+		}
+
 		/* clear device sysfs */
 		gigaset_free_dev_sysfs(cs);
 
@@ -471,22 +483,16 @@ void gigaset_freecs(struct cardstate *cs)
 	case 2: /* error in initcshw */
 		/* Deregister from LL */
 		make_invalid(cs, VALID_ID);
-		gig_dbg(DEBUG_INIT, "clearing iif");
-		gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
+		gigaset_isdn_unregister(cs);
 
 		/* fall through */
-	case 1: /* error when regestering to LL */
+	case 1: /* error when registering to LL */
 		gig_dbg(DEBUG_INIT, "clearing at_state");
 		clear_at_state(&cs->at_state);
 		dealloc_at_states(cs);
 
 		/* fall through */
-	case 0: /* error in one call to initbcs */
-		for (i = 0; i < cs->channels; ++i) {
-			gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i);
-			gigaset_freebcs(cs->bcs + i);
-		}
-
+	case 0:	/* error in basic setup */
 		clear_events(cs);
 		gig_dbg(DEBUG_INIT, "freeing inbuf");
 		kfree(cs->inbuf);
@@ -614,8 +620,9 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
 	if (cs->ignoreframes) {
 		bcs->inputstate |= INS_skip_frame;
 		bcs->skb = NULL;
-	} else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
-		skb_reserve(bcs->skb, HW_HDR_LEN);
+	} else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len))
+		   != NULL)
+		skb_reserve(bcs->skb, cs->hw_hdr_len);
 	else {
 		pr_err("out of memory\n");
 		bcs->inputstate |= INS_skip_frame;
@@ -720,14 +727,6 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
 	cs->mode = M_UNKNOWN;
 	cs->mstate = MS_UNINITIALIZED;
 
-	for (i = 0; i < channels; ++i) {
-		gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", i);
-		if (!gigaset_initbcs(cs->bcs + i, cs, i)) {
-			pr_err("could not allocate channel %d data\n", i);
-			goto error;
-		}
-	}
-
 	++cs->cs_init;
 
 	gig_dbg(DEBUG_INIT, "setting up at_state");
@@ -752,7 +751,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
 	cs->cmdbytes = 0;
 
 	gig_dbg(DEBUG_INIT, "setting up iif");
-	if (!gigaset_register_to_LL(cs, modulename)) {
+	if (!gigaset_isdn_register(cs, modulename)) {
 		pr_err("error registering ISDN device\n");
 		goto error;
 	}
@@ -771,6 +770,15 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
 	/* set up device sysfs */
 	gigaset_init_dev_sysfs(cs);
 
+	/* set up channel data structures */
+	for (i = 0; i < channels; ++i) {
+		gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i);
+		if (!gigaset_initbcs(cs->bcs + i, cs, i)) {
+			pr_err("could not allocate channel %d data\n", i);
+			goto error;
+		}
+	}
+
 	spin_lock_irqsave(&cs->lock, flags);
 	cs->running = 1;
 	spin_unlock_irqrestore(&cs->lock, flags);
@@ -1192,7 +1200,7 @@ static int __init gigaset_init_module(void)
 	if (gigaset_debuglevel == 1)
 		gigaset_debuglevel = DEBUG_DEFAULT;
 
-	pr_info(DRIVER_DESC "\n");
+	pr_info(DRIVER_DESC DRIVER_DESC_DEBUG "\n");
 	return 0;
 }
 
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index 7258b25..ad5ad7b 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -127,7 +127,6 @@
 #define ACT_NOTIFY_BC_UP	39
 #define ACT_DIAL		40
 #define ACT_ACCEPT		41
-#define ACT_PROTO_L2		42
 #define ACT_HUP			43
 #define ACT_IF_LOCK		44
 #define ACT_START		45
@@ -366,8 +365,6 @@ struct reply_t gigaset_tab_cid[] =
 	{EV_BC_CLOSED, -1, -1, -1,                 -1,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME
 
 	/* misc. */
-	{EV_PROTO_L2,  -1, -1, -1,                 -1,-1, {ACT_PROTO_L2}}, //FIXME
-
 	{RSP_ZCON,     -1, -1, -1,                 -1,-1, {ACT_DEBUG}}, //FIXME
 	{RSP_ZCCR,     -1, -1, -1,                 -1,-1, {ACT_DEBUG}}, //FIXME
 	{RSP_ZAOC,     -1, -1, -1,                 -1,-1, {ACT_DEBUG}}, //FIXME
@@ -715,7 +712,7 @@ static void disconnect(struct at_state_t **at_state_p)
 		/* notify LL */
 		if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
 			bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
-			gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+			gigaset_isdn_hupD(bcs);
 		}
 	} else {
 		/* no B channel assigned: just deallocate */
@@ -873,12 +870,12 @@ static void bchannel_down(struct bc_state *bcs)
 {
 	if (bcs->chstate & CHS_B_UP) {
 		bcs->chstate &= ~CHS_B_UP;
-		gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
+		gigaset_isdn_hupB(bcs);
 	}
 
 	if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
 		bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
-		gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+		gigaset_isdn_hupD(bcs);
 	}
 
 	gigaset_free_channel(bcs);
@@ -895,15 +892,16 @@ static void bchannel_up(struct bc_state *bcs)
 	}
 
 	bcs->chstate |= CHS_B_UP;
-	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
+	gigaset_isdn_connB(bcs);
 }
 
 static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_index)
 {
 	struct bc_state *bcs = at_state->bcs;
 	struct cardstate *cs = at_state->cs;
-	int retval;
+	char **commands = data;
 	unsigned long flags;
+	int i;
 
 	bcs->chstate |= CHS_NOTIFY_LL;
 
@@ -914,10 +912,10 @@ static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_ind
 	}
 	spin_unlock_irqrestore(&cs->lock, flags);
 
-	retval = gigaset_isdn_setup_dial(at_state, data);
-	if (retval != 0)
-		goto error;
-
+	for (i = 0; i < AT_NUM; ++i) {
+		kfree(bcs->commands[i]);
+		bcs->commands[i] = commands[i];
+	}
 
 	at_state->pending_commands |= PC_CID;
 	gig_dbg(DEBUG_CMD, "Scheduling PC_CID");
@@ -925,6 +923,10 @@ static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_ind
 	return;
 
 error:
+	for (i = 0; i < AT_NUM; ++i) {
+		kfree(commands[i]);
+		commands[i] = NULL;
+	}
 	at_state->pending_commands |= PC_NOCID;
 	gig_dbg(DEBUG_CMD, "Scheduling PC_NOCID");
 	cs->commands_pending = 1;
@@ -934,20 +936,30 @@ error:
 static void start_accept(struct at_state_t *at_state)
 {
 	struct cardstate *cs = at_state->cs;
-	int retval;
+	struct bc_state *bcs = at_state->bcs;
+	int i;
 
-	retval = gigaset_isdn_setup_accept(at_state);
+	for (i = 0; i < AT_NUM; ++i) {
+		kfree(bcs->commands[i]);
+		bcs->commands[i] = NULL;
+	}
 
-	if (retval == 0) {
-		at_state->pending_commands |= PC_ACCEPT;
-		gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT");
-		cs->commands_pending = 1;
-	} else {
+	if (!(bcs->commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC)) ||
+	    !(bcs->commands[AT_ISO  ] = kmalloc(9, GFP_ATOMIC))) {
+		dev_err(at_state->cs->dev, "out of memory\n");
 		/* error reset */
 		at_state->pending_commands |= PC_HUP;
 		gig_dbg(DEBUG_CMD, "Scheduling PC_HUP");
 		cs->commands_pending = 1;
+		return;
 	}
+
+	snprintf(bcs->commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+	snprintf(bcs->commands[AT_ISO  ], 9, "^SISO=%u\r", bcs->channel + 1);
+
+	at_state->pending_commands |= PC_ACCEPT;
+	gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT");
+	cs->commands_pending = 1;
 }
 
 static void do_start(struct cardstate *cs)
@@ -958,7 +970,7 @@ static void do_start(struct cardstate *cs)
 		schedule_init(cs, MS_INIT);
 
 	cs->isdn_up = 1;
-	gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
+	gigaset_isdn_start(cs);
 					// FIXME: not in locked mode
 					// FIXME 2: only after init sequence
 
@@ -976,7 +988,7 @@ static void finish_shutdown(struct cardstate *cs)
 	/* Tell the LL that the device is not available .. */
 	if (cs->isdn_up) {
 		cs->isdn_up = 0;
-		gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
+		gigaset_isdn_stop(cs);
 	}
 
 	/* The rest is done by cleanup_cs () in user mode. */
@@ -1277,7 +1289,7 @@ static void do_action(int action, struct cardstate *cs,
 			break;
 		}
 		bcs->chstate |= CHS_D_UP;
-		gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+		gigaset_isdn_connD(bcs);
 		cs->ops->init_bchannel(bcs);
 		break;
 	case ACT_DLE1:
@@ -1285,7 +1297,7 @@ static void do_action(int action, struct cardstate *cs,
 		bcs = cs->bcs + cs->curchannel;
 
 		bcs->chstate |= CHS_D_UP;
-		gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+		gigaset_isdn_connD(bcs);
 		cs->ops->init_bchannel(bcs);
 		break;
 	case ACT_FAKEHUP:
@@ -1474,11 +1486,6 @@ static void do_action(int action, struct cardstate *cs,
 	case ACT_ACCEPT:
 		start_accept(at_state);
 		break;
-	case ACT_PROTO_L2:
-		gig_dbg(DEBUG_CMD, "set protocol to %u",
-			(unsigned) ev->parameter);
-		at_state->bcs->proto2 = ev->parameter;
-		break;
 	case ACT_HUP:
 		at_state->pending_commands |= PC_HUP;
 		cs->commands_pending = 1;
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index 1b81a20..8777a9c 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -23,7 +23,6 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/spinlock.h>
-#include <linux/isdnif.h>
 #include <linux/usb.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
@@ -40,7 +39,6 @@
 
 #define MAX_REC_PARAMS 10	/* Max. number of params in response string */
 #define MAX_RESP_SIZE 512	/* Max. size of a response string */
-#define HW_HDR_LEN 2		/* Header size used to store ack info */
 
 #define MAX_EVENTS 64		/* size of event queue */
 
@@ -216,7 +214,6 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
 #define EV_START	-110
 #define EV_STOP		-111
 #define EV_IF_LOCK	-112
-#define EV_PROTO_L2	-113
 #define EV_ACCEPT	-114
 #define EV_DIAL		-115
 #define EV_HUP		-116
@@ -259,6 +256,11 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
 #define SM_LOCKED	0
 #define SM_ISDN		1 /* default */
 
+/* layer 2 protocols (AT^SBPR=...) */
+#define L2_BITSYNC	0
+#define L2_HDLC		1
+#define L2_VOICE	2
+
 struct gigaset_ops;
 struct gigaset_driver;
 
@@ -395,7 +397,7 @@ struct bc_state {
 
 	unsigned chstate;		/* bitmap (CHS_*) */
 	int ignore;
-	unsigned proto2;		/* Layer 2 protocol (ISDN_PROTO_L2_*) */
+	unsigned proto2;		/* layer 2 protocol (L2_*) */
 	char *commands[AT_NUM];		/* see AT_XXXX */
 
 #ifdef CONFIG_GIGASET_DEBUG
@@ -456,12 +458,13 @@ struct cardstate {
 
 	unsigned running;		/* !=0 if events are handled */
 	unsigned connected;		/* !=0 if hardware is connected */
-	unsigned isdn_up;		/* !=0 after ISDN_STAT_RUN */
+	unsigned isdn_up;		/* !=0 after gigaset_isdn_start() */
 
 	unsigned cidmode;
 
 	int myid;			/* id for communication with LL */
-	isdn_if iif;
+	void *iif;			/* LL interface structure */
+	unsigned short hw_hdr_len;	/* headroom needed in data skbs */
 
 	struct reply_t *tabnocid;
 	struct reply_t *tabcid;
@@ -616,7 +619,9 @@ struct gigaset_ops {
 	int (*baud_rate)(struct cardstate *cs, unsigned cflag);
 	int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag);
 
-	/* Called from i4l.c to put an skb into the send-queue. */
+	/* Called from LL interface to put an skb into the send-queue.
+	 * After sending is completed, gigaset_skb_sent() must be called
+	 * with the first cs->hw_hdr_len bytes of skb->head preserved. */
 	int (*send_skb)(struct bc_state *bcs, struct sk_buff *skb);
 
 	/* Called from ev-layer.c to process a block of data
@@ -638,8 +643,7 @@ struct gigaset_ops {
  *  Functions implemented in asyncdata.c
  */
 
-/* Called from i4l.c to put an skb into the send-queue.
- * After sending gigaset_skb_sent() should be called. */
+/* Called from LL interface to put an skb into the send queue. */
 int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb);
 
 /* Called from ev-layer.c to process a block of data
@@ -650,8 +654,7 @@ void gigaset_m10x_input(struct inbuf_t *inbuf);
  *  Functions implemented in isocdata.c
  */
 
-/* Called from i4l.c to put an skb into the send-queue.
- * After sending gigaset_skb_sent() should be called. */
+/* Called from LL interface to put an skb into the send queue. */
 int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb);
 
 /* Called from ev-layer.c to process a block of data
@@ -674,36 +677,26 @@ void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle);
 int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size);
 
 /* ===========================================================================
- *  Functions implemented in i4l.c/gigaset.h
+ *  Functions implemented in LL interface
  */
 
-/* Called by gigaset_initcs() for setting up with the isdn4linux subsystem */
-int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid);
+/* Called from common.c for setting up/shutting down with the ISDN subsystem */
+int gigaset_isdn_register(struct cardstate *cs, const char *isdnid);
+void gigaset_isdn_unregister(struct cardstate *cs);
 
-/* Called from xxx-gigaset.c to indicate completion of sending an skb */
+/* Called from hardware module to indicate completion of an skb */
 void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb);
+void gigaset_isdn_rcv_err(struct bc_state *bcs);
 
 /* Called from common.c/ev-layer.c to indicate events relevant to the LL */
+void gigaset_isdn_start(struct cardstate *cs);
+void gigaset_isdn_stop(struct cardstate *cs);
 int gigaset_isdn_icall(struct at_state_t *at_state);
-int gigaset_isdn_setup_accept(struct at_state_t *at_state);
-int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data);
-
-void gigaset_i4l_cmd(struct cardstate *cs, int cmd);
-void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd);
-
-
-static inline void gigaset_isdn_rcv_err(struct bc_state *bcs)
-{
-	isdn_ctrl response;
-
-	/* error -> LL */
-	gig_dbg(DEBUG_CMD, "sending L1ERR");
-	response.driver = bcs->cs->myid;
-	response.command = ISDN_STAT_L1ERR;
-	response.arg = bcs->channel;
-	response.parm.errcode = ISDN_STAT_L1ERR_RECV;
-	bcs->cs->iif.statcallb(&response);
-}
+void gigaset_isdn_connD(struct bc_state *bcs);
+void gigaset_isdn_hupD(struct bc_state *bcs);
+void gigaset_isdn_connB(struct bc_state *bcs);
+void gigaset_isdn_hupB(struct bc_state *bcs);
 
 /* ===========================================================================
  *  Functions implemented in ev-layer.c
@@ -816,35 +809,6 @@ static inline void gigaset_bchannel_up(struct bc_state *bcs)
 /* handling routines for sk_buff */
 /* ============================= */
 
-/* pass received skb to LL
- * Warning: skb must not be accessed anymore!
- */
-static inline void gigaset_rcv_skb(struct sk_buff *skb,
-				   struct cardstate *cs,
-				   struct bc_state *bcs)
-{
-	cs->iif.rcvcallb_skb(cs->myid, bcs->channel, skb);
-	bcs->trans_down++;
-}
-
-/* handle reception of corrupted skb
- * Warning: skb must not be accessed anymore!
- */
-static inline void gigaset_rcv_error(struct sk_buff *procskb,
-				     struct cardstate *cs,
-				     struct bc_state *bcs)
-{
-	if (procskb)
-		dev_kfree_skb(procskb);
-
-	if (bcs->ignore)
-		--bcs->ignore;
-	else {
-		++bcs->corrupted;
-		gigaset_isdn_rcv_err(bcs);
-	}
-}
-
 /* append received bytes to inbuf */
 int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
 		       unsigned numbytes);
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
index 8c99578..ff3254a 100644
--- a/drivers/isdn/gigaset/i4l.c
+++ b/drivers/isdn/gigaset/i4l.c
@@ -14,6 +14,9 @@
  */
 
 #include "gigaset.h"
+#include <linux/isdnif.h>
+
+#define HW_HDR_LEN	2	/* Header size used to store ack info */
 
 /* == Handling of I4L IO =====================================================*/
 
@@ -89,6 +92,7 @@ static int writebuf_from_LL(int driverID, int channel, int ack,
  */
 void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
 {
+	isdn_if *iif = bcs->cs->iif;
 	unsigned len;
 	isdn_ctrl response;
 
@@ -108,71 +112,171 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
 		response.command = ISDN_STAT_BSENT;
 		response.arg = bcs->channel;
 		response.parm.length = len;
-		bcs->cs->iif.statcallb(&response);
+		iif->statcallb(&response);
 	}
 }
 EXPORT_SYMBOL_GPL(gigaset_skb_sent);
 
+/**
+ * gigaset_skb_rcvd() - pass received skb to LL
+ * @bcs:	B channel descriptor structure.
+ * @skb:	received data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when user data has
+ * been successfully received, for passing to the LL.
+ * Warning: skb must not be accessed anymore!
+ */
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
+{
+	isdn_if *iif = bcs->cs->iif;
+
+	iif->rcvcallb_skb(bcs->cs->myid, bcs->channel, skb);
+	bcs->trans_down++;
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
+
+/**
+ * gigaset_isdn_rcv_err() - signal receive error
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when a receive error
+ * has occurred, for signalling to the LL.
+ */
+void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+	isdn_if *iif = bcs->cs->iif;
+	isdn_ctrl response;
+
+	/* if currently ignoring packets, just count down */
+	if (bcs->ignore) {
+		bcs->ignore--;
+		return;
+	}
+
+	/* update statistics */
+	bcs->corrupted++;
+
+	/* error -> LL */
+	gig_dbg(DEBUG_CMD, "sending L1ERR");
+	response.driver = bcs->cs->myid;
+	response.command = ISDN_STAT_L1ERR;
+	response.arg = bcs->channel;
+	response.parm.errcode = ISDN_STAT_L1ERR_RECV;
+	iif->statcallb(&response);
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
 /* This function will be called by LL to send commands
  * NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL,
  * so don't put too much effort into it.
  */
 static int command_from_LL(isdn_ctrl *cntrl)
 {
-	struct cardstate *cs = gigaset_get_cs_by_id(cntrl->driver);
+	struct cardstate *cs;
 	struct bc_state *bcs;
 	int retval = 0;
-	struct setup_parm *sp;
+	char **commands;
+	int ch;
+	int i;
+	size_t l;
 
 	gigaset_debugdrivers();
 
-	if (!cs) {
+	gig_dbg(DEBUG_CMD, "driver: %d, command: %d, arg: 0x%lx",
+		cntrl->driver, cntrl->command, cntrl->arg);
+
+	if ((cs = gigaset_get_cs_by_id(cntrl->driver)) == NULL) {
 		pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
 		return -ENODEV;
 	}
+	ch = cntrl->arg & 0xff;
 
 	switch (cntrl->command) {
 	case ISDN_CMD_IOCTL:
-		gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)",
-			cntrl->driver, cntrl->arg);
-
 		dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
 		return -EINVAL;
 
 	case ISDN_CMD_DIAL:
 		gig_dbg(DEBUG_ANY,
-			"ISDN_CMD_DIAL (driver: %d, ch: %ld, "
-			"phone: %s, ownmsn: %s, si1: %d, si2: %d)",
-			cntrl->driver, cntrl->arg,
+			"ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)",
 			cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
 			cntrl->parm.setup.si1, cntrl->parm.setup.si2);
 
-		if (cntrl->arg >= cs->channels) {
+		if (ch >= cs->channels) {
 			dev_err(cs->dev,
-				"ISDN_CMD_DIAL: invalid channel (%d)\n",
-				(int) cntrl->arg);
+				"ISDN_CMD_DIAL: invalid channel (%d)\n", ch);
 			return -EINVAL;
 		}
-
-		bcs = cs->bcs + cntrl->arg;
-
+		bcs = cs->bcs + ch;
 		if (!gigaset_get_channel(bcs)) {
 			dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
 			return -EBUSY;
 		}
 
-		sp = kmalloc(sizeof *sp, GFP_ATOMIC);
-		if (!sp) {
+		commands = kzalloc(AT_NUM*(sizeof *commands), GFP_ATOMIC);
+		if (!commands) {
 			gigaset_free_channel(bcs);
 			dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
 			return -ENOMEM;
 		}
-		*sp = cntrl->parm.setup;
 
-		if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp,
+		l = 3 + strlen(cntrl->parm.setup.phone);
+		if (!(commands[AT_DIAL] = kmalloc(l, GFP_ATOMIC)))
+			goto oom;
+		if (cntrl->parm.setup.phone[0] == '*' &&
+		    cntrl->parm.setup.phone[1] == '*') {
+			/* internal call: translate ** prefix to CTP value */
+			if (!(commands[AT_TYPE] =
+			      kstrdup("^SCTP=0\r", GFP_ATOMIC)))
+				goto oom;
+			snprintf(commands[AT_DIAL], l,
+				 "D%s\r", cntrl->parm.setup.phone+2);
+		} else {
+			if (!(commands[AT_TYPE] =
+			      kstrdup("^SCTP=1\r", GFP_ATOMIC)))
+				goto oom;
+			snprintf(commands[AT_DIAL], l,
+				 "D%s\r", cntrl->parm.setup.phone);
+		}
+
+		if ((l = strlen(cntrl->parm.setup.eazmsn)) != 0) {
+			l += 8;
+			if (!(commands[AT_MSN] = kmalloc(l, GFP_ATOMIC)))
+				goto oom;
+			snprintf(commands[AT_MSN], l, "^SMSN=%s\r",
+				 cntrl->parm.setup.eazmsn);
+		}
+
+		switch (cntrl->parm.setup.si1) {
+		case 1:		/* audio */
+			/* BC = 9090A3: 3.1 kHz audio, A-law */
+			if (!(commands[AT_BC] =
+			      kstrdup("^SBC=9090A3\r", GFP_ATOMIC)))
+				goto oom;
+			break;
+		case 7:		/* data */
+		default:	/* hope the app knows what it is doing */
+			/* BC = 8890: unrestricted digital information */
+			if (!(commands[AT_BC] =
+			      kstrdup("^SBC=8890\r", GFP_ATOMIC)))
+				goto oom;
+		}
+		/* ToDo: other si1 values, inspect si2, set HLC/LLC */
+
+		if (!(commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC)))
+			goto oom;
+		snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+
+		if (!(commands[AT_ISO] = kmalloc(9, GFP_ATOMIC)))
+			goto oom;
+		snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
+			 (unsigned) bcs->channel + 1);
+
+		if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
 				       bcs->at_state.seq_index, NULL)) {
-			//FIXME what should we do?
-			kfree(sp);
+			for (i = 0; i < AT_NUM; ++i)
+				kfree(commands[i]);
+			kfree(commands);
 			gigaset_free_channel(bcs);
 			return -ENOMEM;
 		}
@@ -180,93 +284,83 @@ static int command_from_LL(isdn_ctrl *cntrl)
 		gig_dbg(DEBUG_CMD, "scheduling DIAL");
 		gigaset_schedule_event(cs);
 		break;
-	case ISDN_CMD_ACCEPTD: //FIXME
-		gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD");
-
-		if (cntrl->arg >= cs->channels) {
+	case ISDN_CMD_ACCEPTD:
+		if (ch >= cs->channels) {
 			dev_err(cs->dev,
-				"ISDN_CMD_ACCEPTD: invalid channel (%d)\n",
-				(int) cntrl->arg);
+				"ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
 			return -EINVAL;
 		}
-
-		if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state,
-				       EV_ACCEPT, NULL, 0, NULL)) {
-			//FIXME what should we do?
+		bcs = cs->bcs + ch;
+		if (!gigaset_add_event(cs, &bcs->at_state,
+				       EV_ACCEPT, NULL, 0, NULL))
 			return -ENOMEM;
-		}
 
 		gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
 		gigaset_schedule_event(cs);
 
 		break;
 	case ISDN_CMD_ACCEPTB:
-		gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTB");
 		break;
 	case ISDN_CMD_HANGUP:
-		gig_dbg(DEBUG_ANY, "ISDN_CMD_HANGUP (ch: %d)",
-			(int) cntrl->arg);
-
-		if (cntrl->arg >= cs->channels) {
+		if (ch >= cs->channels) {
 			dev_err(cs->dev,
-				"ISDN_CMD_HANGUP: invalid channel (%d)\n",
-				(int) cntrl->arg);
+				"ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
 			return -EINVAL;
 		}
-
-		if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state,
-				       EV_HUP, NULL, 0, NULL)) {
-			//FIXME what should we do?
+		bcs = cs->bcs + ch;
+		if (!gigaset_add_event(cs, &bcs->at_state,
+				       EV_HUP, NULL, 0, NULL))
 			return -ENOMEM;
-		}
 
 		gig_dbg(DEBUG_CMD, "scheduling HUP");
 		gigaset_schedule_event(cs);
 
 		break;
-	case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ //FIXME
-		gig_dbg(DEBUG_ANY, "ISDN_CMD_CLREAZ");
+	case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */
+		dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n");
 		break;
-	case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ //FIXME
-		gig_dbg(DEBUG_ANY,
-			"ISDN_CMD_SETEAZ (id: %d, ch: %ld, number: %s)",
-			cntrl->driver, cntrl->arg, cntrl->parm.num);
+	case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */
+		dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n",
+			 cntrl->parm.num);
 		break;
 	case ISDN_CMD_SETL2: /* Set L2 to given protocol */
-		gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL2 (ch: %ld, proto: %lx)",
-			cntrl->arg & 0xff, (cntrl->arg >> 8));
-
-		if ((cntrl->arg & 0xff) >= cs->channels) {
+		if (ch >= cs->channels) {
 			dev_err(cs->dev,
-				"ISDN_CMD_SETL2: invalid channel (%d)\n",
-				(int) cntrl->arg & 0xff);
+				"ISDN_CMD_SETL2: invalid channel (%d)\n", ch);
 			return -EINVAL;
 		}
-
-		if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg & 0xff].at_state,
-				       EV_PROTO_L2, NULL, cntrl->arg >> 8,
-				       NULL)) {
-			//FIXME what should we do?
-			return -ENOMEM;
+		bcs = cs->bcs + ch;
+		if (bcs->chstate & CHS_D_UP) {
+			dev_err(cs->dev,
+				"ISDN_CMD_SETL2: channel active (%d)\n", ch);
+			return -EINVAL;
+		}
+		switch (cntrl->arg >> 8) {
+		case ISDN_PROTO_L2_HDLC:
+			gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_HDLC");
+			bcs->proto2 = L2_HDLC;
+			break;
+		case ISDN_PROTO_L2_TRANS:
+			gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_VOICE");
+			bcs->proto2 = L2_VOICE;
+			break;
+		default:
+			dev_err(cs->dev,
+				"ISDN_CMD_SETL2: unsupported protocol (%lu)\n",
+				cntrl->arg >> 8);
+			return -EINVAL;
 		}
-
-		gig_dbg(DEBUG_CMD, "scheduling PROTO_L2");
-		gigaset_schedule_event(cs);
 		break;
 	case ISDN_CMD_SETL3: /* Set L3 to given protocol */
-		gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL3 (ch: %ld, proto: %lx)",
-			cntrl->arg & 0xff, (cntrl->arg >> 8));
-
-		if ((cntrl->arg & 0xff) >= cs->channels) {
+		if (ch >= cs->channels) {
 			dev_err(cs->dev,
-				"ISDN_CMD_SETL3: invalid channel (%d)\n",
-				(int) cntrl->arg & 0xff);
+				"ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
 			return -EINVAL;
 		}
 
 		if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
 			dev_err(cs->dev,
-				"ISDN_CMD_SETL3: invalid protocol %lu\n",
+				"ISDN_CMD_SETL3: unsupported protocol (%lu)\n",
 				cntrl->arg >> 8);
 			return -EINVAL;
 		}
@@ -318,149 +412,34 @@ static int command_from_LL(isdn_ctrl *cntrl)
 	}
 
 	return retval;
+
+oom:
+	dev_err(bcs->cs->dev, "out of memory\n");
+	for (i = 0; i < AT_NUM; ++i)
+		kfree(commands[i]);
+	return -ENOMEM;
 }
 
-void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
+static void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
 {
+	isdn_if *iif = cs->iif;
 	isdn_ctrl command;
 
 	command.driver = cs->myid;
 	command.command = cmd;
 	command.arg = 0;
-	cs->iif.statcallb(&command);
+	iif->statcallb(&command);
 }
 
-void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
+static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
 {
+	isdn_if *iif = bcs->cs->iif;
 	isdn_ctrl command;
 
 	command.driver = bcs->cs->myid;
 	command.command = cmd;
 	command.arg = bcs->channel;
-	bcs->cs->iif.statcallb(&command);
-}
-
-int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data)
-{
-	struct bc_state *bcs = at_state->bcs;
-	unsigned proto;
-	const char *bc;
-	size_t length[AT_NUM];
-	size_t l;
-	int i;
-	struct setup_parm *sp = data;
-
-	switch (bcs->proto2) {
-	case ISDN_PROTO_L2_HDLC:
-		proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */
-		break;
-	case ISDN_PROTO_L2_TRANS:
-		proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */
-		break;
-	default:
-		dev_err(bcs->cs->dev, "%s: invalid L2 protocol: %u\n",
-			__func__, bcs->proto2);
-		return -EINVAL;
-	}
-
-	switch (sp->si1) {
-	case 1:		/* audio */
-		bc = "9090A3";	/* 3.1 kHz audio, A-law */
-		break;
-	case 7:		/* data */
-	default:	/* hope the app knows what it is doing */
-		bc = "8890";	/* unrestricted digital information */
-	}
-	//FIXME add missing si1 values from 1TR6, inspect si2, set HLC/LLC
-
-	length[AT_DIAL ] = 1 + strlen(sp->phone) + 1 + 1;
-	l = strlen(sp->eazmsn);
-	length[AT_MSN  ] = l ? 6 + l + 1 + 1 : 0;
-	length[AT_BC   ] = 5 + strlen(bc) + 1 + 1;
-	length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */
-	length[AT_ISO  ] = 6 + 1 + 1 + 1; /* channel: 1 character */
-	length[AT_TYPE ] = 6 + 1 + 1 + 1; /* call type: 1 character */
-	length[AT_HLC  ] = 0;
-
-	for (i = 0; i < AT_NUM; ++i) {
-		kfree(bcs->commands[i]);
-		bcs->commands[i] = NULL;
-		if (length[i] &&
-		    !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) {
-			dev_err(bcs->cs->dev, "out of memory\n");
-			return -ENOMEM;
-		}
-	}
-
-	/* type = 1: extern, 0: intern, 2: recall, 3: door, 4: centrex */
-	if (sp->phone[0] == '*' && sp->phone[1] == '*') {
-		/* internal call: translate ** prefix to CTP value */
-		snprintf(bcs->commands[AT_DIAL], length[AT_DIAL],
-			 "D%s\r", sp->phone+2);
-		strncpy(bcs->commands[AT_TYPE], "^SCTP=0\r", length[AT_TYPE]);
-	} else {
-		snprintf(bcs->commands[AT_DIAL], length[AT_DIAL],
-			 "D%s\r", sp->phone);
-		strncpy(bcs->commands[AT_TYPE], "^SCTP=1\r", length[AT_TYPE]);
-	}
-
-	if (bcs->commands[AT_MSN])
-		snprintf(bcs->commands[AT_MSN], length[AT_MSN],
-			 "^SMSN=%s\r", sp->eazmsn);
-	snprintf(bcs->commands[AT_BC   ], length[AT_BC   ],
-		 "^SBC=%s\r", bc);
-	snprintf(bcs->commands[AT_PROTO], length[AT_PROTO],
-		 "^SBPR=%u\r", proto);
-	snprintf(bcs->commands[AT_ISO  ], length[AT_ISO  ],
-		 "^SISO=%u\r", (unsigned)bcs->channel + 1);
-
-	return 0;
-}
-
-int gigaset_isdn_setup_accept(struct at_state_t *at_state)
-{
-	unsigned proto;
-	size_t length[AT_NUM];
-	int i;
-	struct bc_state *bcs = at_state->bcs;
-
-	switch (bcs->proto2) {
-	case ISDN_PROTO_L2_HDLC:
-		proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */
-		break;
-	case ISDN_PROTO_L2_TRANS:
-		proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */
-		break;
-	default:
-		dev_err(at_state->cs->dev, "%s: invalid protocol: %u\n",
-			__func__, bcs->proto2);
-		return -EINVAL;
-	}
-
-	length[AT_DIAL ] = 0;
-	length[AT_MSN  ] = 0;
-	length[AT_BC   ] = 0;
-	length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */
-	length[AT_ISO  ] = 6 + 1 + 1 + 1; /* channel: 1 character */
-	length[AT_TYPE ] = 0;
-	length[AT_HLC  ] = 0;
-
-	for (i = 0; i < AT_NUM; ++i) {
-		kfree(bcs->commands[i]);
-		bcs->commands[i] = NULL;
-		if (length[i] &&
-		    !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) {
-			dev_err(at_state->cs->dev, "out of memory\n");
-			return -ENOMEM;
-		}
-	}
-
-	snprintf(bcs->commands[AT_PROTO], length[AT_PROTO],
-		 "^SBPR=%u\r", proto);
-	snprintf(bcs->commands[AT_ISO  ], length[AT_ISO  ],
-		 "^SISO=%u\r", (unsigned) bcs->channel + 1);
-
-	return 0;
+	iif->statcallb(&command);
 }
 
 /**
@@ -476,6 +455,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
 {
 	struct cardstate *cs = at_state->cs;
 	struct bc_state *bcs = at_state->bcs;
+	isdn_if *iif = cs->iif;
 	isdn_ctrl response;
 	int retval;
 
@@ -525,7 +505,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
 		response.arg = bcs->channel; //FIXME
 	}
 	response.driver = cs->myid;
-	retval = cs->iif.statcallb(&response);
+	retval = iif->statcallb(&response);
 	gig_dbg(DEBUG_CMD, "Response: %d", retval);
 	switch (retval) {
 	case 0:	/* no takers */
@@ -554,16 +534,108 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
 	}
 }
 
-/* Set Callback function pointer */
-int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid)
+/**
+ * gigaset_isdn_connD() - signal D channel connect
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the D channel connection has
+ * been established.
+ */
+void gigaset_isdn_connD(struct bc_state *bcs)
 {
-	isdn_if *iif = &cs->iif;
+	gig_dbg(DEBUG_CMD, "sending DCONN");
+	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+}
 
-	gig_dbg(DEBUG_ANY, "Register driver capabilities to LL");
+/**
+ * gigaset_isdn_hupD() - signal D channel hangup
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the D channel connection has
+ * been shut down.
+ */
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+	gig_dbg(DEBUG_CMD, "sending DHUP");
+	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+}
+
+/**
+ * gigaset_isdn_connB() - signal B channel connect
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the B channel connection has
+ * been established.
+ */
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+	gig_dbg(DEBUG_CMD, "sending BCONN");
+	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
+}
+
+/**
+ * gigaset_isdn_hupB() - signal B channel hangup
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the B channel connection has
+ * been shut down.
+ */
+void gigaset_isdn_hupB(struct bc_state *bcs)
+{
+	gig_dbg(DEBUG_CMD, "sending BHUP");
+	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
+}
+
+/**
+ * gigaset_isdn_start() - signal device availability
+ * @cs:		device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is available for
+ * use.
+ */
+void gigaset_isdn_start(struct cardstate *cs)
+{
+	gig_dbg(DEBUG_CMD, "sending RUN");
+	gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
+}
+
+/**
+ * gigaset_isdn_stop() - signal device unavailability
+ * @cs:		device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is no longer
+ * available for use.
+ */
+void gigaset_isdn_stop(struct cardstate *cs)
+{
+	gig_dbg(DEBUG_CMD, "sending STOP");
+	gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
+}
+
+/**
+ * gigaset_isdn_register() - register to LL
+ * @cs:		device descriptor structure.
+ * @isdnid:	device name.
+ *
+ * Called by main module to register the device with the LL.
+ *
+ * Return value: 1 for success, 0 for failure
+ */
+int gigaset_isdn_register(struct cardstate *cs, const char *isdnid)
+{
+	isdn_if *iif;
+
+	pr_info("ISDN4Linux interface\n");
+
+	if ((iif = kmalloc(sizeof *iif, GFP_KERNEL)) == NULL) {
+		pr_err("out of memory\n");
+		return 0;
+	}
 
 	if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
 	    >= sizeof iif->id) {
 		pr_err("ID too long: %s\n", isdnid);
+		kfree(iif);
 		return 0;
 	}
 
@@ -587,9 +659,26 @@ int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid)
 
 	if (!register_isdn(iif)) {
 		pr_err("register_isdn failed\n");
+		kfree(iif);
 		return 0;
 	}
 
+	cs->iif = iif;
 	cs->myid = iif->channels;		/* Set my device id */
+	cs->hw_hdr_len = HW_HDR_LEN;
 	return 1;
 }
+
+/**
+ * gigaset_isdn_unregister() - unregister from LL
+ * @cs:		device descriptor structure.
+ *
+ * Called by main module to unregister the device from the LL.
+ */
+void gigaset_isdn_unregister(struct cardstate *cs)
+{
+	gig_dbg(DEBUG_CMD, "sending UNLOAD");
+	gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
+	kfree(cs->iif);
+	cs->iif = NULL;
+}
diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c
index bed38fc..ade954e 100644
--- a/drivers/isdn/gigaset/isocdata.c
+++ b/drivers/isdn/gigaset/isocdata.c
@@ -499,7 +499,7 @@ int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len)
 	int result;
 
 	switch (bcs->proto2) {
-	case ISDN_PROTO_L2_HDLC:
+	case L2_HDLC:
 		result = hdlc_buildframe(bcs->hw.bas->isooutbuf, in, len);
 		gig_dbg(DEBUG_ISO, "%s: %d bytes HDLC -> %d",
 			__func__, len, result);
@@ -541,8 +541,9 @@ static inline void hdlc_flush(struct bc_state *bcs)
 	if (likely(bcs->skb != NULL))
 		skb_trim(bcs->skb, 0);
 	else if (!bcs->ignore) {
-		if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
-			skb_reserve(bcs->skb, HW_HDR_LEN);
+		if ((bcs->skb = dev_alloc_skb(SBUFSIZE + bcs->cs->hw_hdr_len))
+		    != NULL)
+			skb_reserve(bcs->skb, bcs->cs->hw_hdr_len);
 		else
 			dev_err(bcs->cs->dev, "could not allocate skb\n");
 	}
@@ -556,7 +557,9 @@ static inline void hdlc_flush(struct bc_state *bcs)
  */
 static inline void hdlc_done(struct bc_state *bcs)
 {
+	struct cardstate *cs = bcs->cs;
 	struct sk_buff *procskb;
+	unsigned int len;
 
 	if (unlikely(bcs->ignore)) {
 		bcs->ignore--;
@@ -567,32 +570,30 @@ static inline void hdlc_done(struct bc_state *bcs)
 	if ((procskb = bcs->skb) == NULL) {
 		/* previous error */
 		gig_dbg(DEBUG_ISO, "%s: skb=NULL", __func__);
-		gigaset_rcv_error(NULL, bcs->cs, bcs);
-	} else if (procskb->len < 2) {
-		dev_notice(bcs->cs->dev, "received short frame (%d octets)\n",
-			   procskb->len);
+		gigaset_isdn_rcv_err(bcs);
+	} else if ((len = procskb->len) < 2) {
+		dev_notice(cs->dev, "received short frame (%d octets)\n", len);
 		bcs->hw.bas->runts++;
-		gigaset_rcv_error(procskb, bcs->cs, bcs);
+		dev_kfree_skb(procskb);
+		gigaset_isdn_rcv_err(bcs);
 	} else if (bcs->fcs != PPP_GOODFCS) {
-		dev_notice(bcs->cs->dev, "frame check error (0x%04x)\n",
-			   bcs->fcs);
+		dev_notice(cs->dev, "frame check error (0x%04x)\n", bcs->fcs);
 		bcs->hw.bas->fcserrs++;
-		gigaset_rcv_error(procskb, bcs->cs, bcs);
+		dev_kfree_skb(procskb);
+		gigaset_isdn_rcv_err(bcs);
 	} else {
-		procskb->len -= 2;		/* subtract FCS */
-		procskb->tail -= 2;
-		gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)",
-			__func__, procskb->len);
+		__skb_trim(procskb, len -= 2);	/* subtract FCS */
+		gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)", __func__, len);
 		dump_bytes(DEBUG_STREAM,
-			   "rcv data", procskb->data, procskb->len);
-		bcs->hw.bas->goodbytes += procskb->len;
-		gigaset_rcv_skb(procskb, bcs->cs, bcs);
+			   "rcv data", procskb->data, len);
+		bcs->hw.bas->goodbytes += len;
+		gigaset_skb_rcvd(bcs, procskb);
 	}
 
-	if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
-		skb_reserve(bcs->skb, HW_HDR_LEN);
+	if ((bcs->skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len)) != NULL)
+		skb_reserve(bcs->skb, cs->hw_hdr_len);
 	else
-		dev_err(bcs->cs->dev, "could not allocate skb\n");
+		dev_err(cs->dev, "could not allocate skb\n");
 	bcs->fcs = PPP_INITFCS;
 }
 
@@ -609,12 +610,8 @@ static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits)
 
 	dev_notice(bcs->cs->dev, "received partial byte (%d bits)\n", inbits);
 	bcs->hw.bas->alignerrs++;
-	gigaset_rcv_error(bcs->skb, bcs->cs, bcs);
-
-	if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
-		skb_reserve(bcs->skb, HW_HDR_LEN);
-	else
-		dev_err(bcs->cs->dev, "could not allocate skb\n");
+	gigaset_isdn_rcv_err(bcs);
+	__skb_trim(bcs->skb, 0);
 	bcs->fcs = PPP_INITFCS;
 }
 
@@ -647,8 +644,8 @@ static const unsigned char bitcounts[256] = {
 /* hdlc_unpack
  * perform HDLC frame processing (bit unstuffing, flag detection, FCS calculation)
  * on a sequence of received data bytes (8 bits each, LSB first)
- * pass on successfully received, complete frames as SKBs via gigaset_rcv_skb
- * notify of errors via gigaset_rcv_error
+ * pass on successfully received, complete frames as SKBs via gigaset_skb_rcvd
+ * notify of errors via gigaset_isdn_rcv_err
  * tally frames, errors etc. in BC structure counters
  * parameters:
  *	src	received data
@@ -840,7 +837,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
 }
 
 /* trans_receive
- * pass on received USB frame transparently as SKB via gigaset_rcv_skb
+ * pass on received USB frame transparently as SKB via gigaset_skb_rcvd
  * invert bytes
  * tally frames, errors etc. in BC structure counters
  * parameters:
@@ -851,6 +848,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
 static inline void trans_receive(unsigned char *src, unsigned count,
 				 struct bc_state *bcs)
 {
+	struct cardstate *cs = bcs->cs;
 	struct sk_buff *skb;
 	int dobytes;
 	unsigned char *dst;
@@ -861,12 +859,12 @@ static inline void trans_receive(unsigned char *src, unsigned count,
 		return;
 	}
 	if (unlikely((skb = bcs->skb) == NULL)) {
-		bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN);
+		bcs->skb = skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
 		if (!skb) {
-			dev_err(bcs->cs->dev, "could not allocate skb\n");
+			dev_err(cs->dev, "could not allocate skb\n");
 			return;
 		}
-		skb_reserve(skb, HW_HDR_LEN);
+		skb_reserve(skb, cs->hw_hdr_len);
 	}
 	bcs->hw.bas->goodbytes += skb->len;
 	dobytes = TRANSBUFSIZE - skb->len;
@@ -878,14 +876,14 @@ static inline void trans_receive(unsigned char *src, unsigned count,
 			dobytes--;
 		}
 		if (dobytes == 0) {
-			gigaset_rcv_skb(skb, bcs->cs, bcs);
-			bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN);
+			gigaset_skb_rcvd(bcs, skb);
+			bcs->skb = skb =
+				dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
 			if (!skb) {
-				dev_err(bcs->cs->dev,
-					"could not allocate skb\n");
+				dev_err(cs->dev, "could not allocate skb\n");
 				return;
 			}
-			skb_reserve(bcs->skb, HW_HDR_LEN);
+			skb_reserve(skb, cs->hw_hdr_len);
 			dobytes = TRANSBUFSIZE;
 		}
 	}
@@ -894,7 +892,7 @@ static inline void trans_receive(unsigned char *src, unsigned count,
 void gigaset_isoc_receive(unsigned char *src, unsigned count, struct bc_state *bcs)
 {
 	switch (bcs->proto2) {
-	case ISDN_PROTO_L2_HDLC:
+	case L2_HDLC:
 		hdlc_unpack(src, count, bcs);
 		break;
 	default:		/* assume transparent */
@@ -973,16 +971,19 @@ void gigaset_isoc_input(struct inbuf_t *inbuf)
 
 /* == data output ========================================================== */
 
-/* gigaset_send_skb
- * called by common.c to queue an skb for sending
- * and start transmission if necessary
- * parameters:
- *	B Channel control structure
- *	skb
- * return value:
- *	number of bytes accepted for sending
- *	(skb->len if ok, 0 if out of buffer space)
- *	or error code (< 0, eg. -EINVAL)
+/**
+ * gigaset_isoc_send_skb() - queue an skb for sending
+ * @bcs:	B channel descriptor structure.
+ * @skb:	data to send.
+ *
+ * Called by LL to queue an skb for sending, and start transmission if
+ * necessary.
+ * Once the payload data has been transmitted completely, gigaset_skb_sent()
+ * will be called with the first cs->hw_hdr_len bytes of skb->head preserved.
+ *
+ * Return value:
+ *	number of bytes accepted for sending (skb->len) if ok,
+ *	error code < 0 (eg. -ENODEV) on error
  */
 int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb)
 {
-- 
1.6.2.1.214.ge986c

^ permalink raw reply related

* [PATCH RFC 3/6] gigaset: add kerneldoc comments
From: Tilman Schmidt @ 2009-08-21 17:21 UTC (permalink / raw)
  To: i4ldeveloper, netdev, linux-kernel; +Cc: Hansjoerg Lipp
In-Reply-To: <20090821-patch-00.tilman@imap.cc>

Add kerneldoc comments to some functions in the Gigaset driver.

Impact: documentation
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
 drivers/isdn/gigaset/asyncdata.c |   23 +++++--
 drivers/isdn/gigaset/common.c    |  126 +++++++++++++++++++++++++++++++-------
 drivers/isdn/gigaset/ev-layer.c  |    9 ++-
 drivers/isdn/gigaset/i4l.c       |   17 +++++
 drivers/isdn/gigaset/interface.c |    9 +++
 5 files changed, 153 insertions(+), 31 deletions(-)

diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index 234cc5d..30f1951 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -334,7 +334,14 @@ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
 	return startbytes - numbytes;
 }
 
-/* process a block of data received from the device
+/**
+ * gigaset_m10x_input() - process a block of data received from the device
+ * @inbuf:	received data and device descriptor structure.
+ *
+ * Called by hardware module {ser,usb}_gigaset with a block of received
+ * bytes. Separates the bytes received over the serial data channel into
+ * user data and command replies (locked/unlocked) according to the
+ * current state of the interface.
  */
 void gigaset_m10x_input(struct inbuf_t *inbuf)
 {
@@ -543,12 +550,14 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
 	return iraw_skb;
 }
 
-/* gigaset_send_skb
- * called by common.c to queue an skb for sending
- * and start transmission if necessary
- * parameters:
- *	B Channel control structure
- *	skb
+/**
+ * gigaset_send_skb() - queue an skb for sending
+ * @bcs:	B channel descriptor structure.
+ * @skb:	data to send.
+ *
+ * Called by common.c to encode and queue an skb for sending, and start
+ * transmission if necessary.
+ *
  * Return value:
  *	number of bytes accepted for sending
  *	(skb->len if ok, 0 if out of buffer space)
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index e4141bf..e487ab8 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -32,6 +32,17 @@ MODULE_PARM_DESC(debug, "debug level");
 #define VALID_MINOR	0x01
 #define VALID_ID	0x02
 
+/**
+ * gigaset_dbg_buffer() - dump data in ASCII and hex for debugging
+ * @level:	debugging level.
+ * @msg:	message prefix.
+ * @len:	number of bytes to dump.
+ * @buf:	data to dump.
+ *
+ * If the current debugging level includes one of the bits set in @level,
+ * @len bytes starting at @buf are logged to dmesg at KERN_DEBUG prio,
+ * prefixed by the text @msg.
+ */
 void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
 			size_t len, const unsigned char *buf)
 {
@@ -274,6 +285,20 @@ static void clear_events(struct cardstate *cs)
 	spin_unlock_irqrestore(&cs->ev_lock, flags);
 }
 
+/**
+ * gigaset_add_event() - add event to device event queue
+ * @cs:		device descriptor structure.
+ * @at_state:	connection state structure.
+ * @type:	event type.
+ * @ptr:	pointer parameter for event.
+ * @parameter:	integer parameter for event.
+ * @arg:	pointer parameter for event.
+ *
+ * Allocate an event queue entry from the device's event queue, and set it up
+ * with the parameters given.
+ *
+ * Return value: added event
+ */
 struct event_t *gigaset_add_event(struct cardstate *cs,
 				  struct at_state_t *at_state, int type,
 				  void *ptr, int parameter, void *arg)
@@ -398,6 +423,15 @@ static void make_invalid(struct cardstate *cs, unsigned mask)
 	spin_unlock_irqrestore(&drv->lock, flags);
 }
 
+/**
+ * gigaset_freecs() - free all associated ressources of a device
+ * @cs:		device descriptor structure.
+ *
+ * Stops all tasklets and timers, unregisters the device from all
+ * subsystems it was registered to, deallocates the device structure
+ * @cs and all structures referenced from it.
+ * Operations on the device should be stopped before calling this.
+ */
 void gigaset_freecs(struct cardstate *cs)
 {
 	int i;
@@ -506,7 +540,12 @@ static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs,
 	inbuf->inputstate = inputstate;
 }
 
-/* append received bytes to inbuf */
+/**
+ * gigaset_fill_inbuf() - append received data to input buffer
+ * @inbuf:	buffer structure.
+ * @src:	received data.
+ * @numbytes:	number of bytes received.
+ */
 int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
 		       unsigned numbytes)
 {
@@ -606,20 +645,22 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
 	return NULL;
 }
 
-/* gigaset_initcs
+/**
+ * gigaset_initcs() - initialize device structure
+ * @drv:	hardware driver the device belongs to
+ * @channels:	number of B channels supported by device
+ * @onechannel:	!=0 if B channel data and AT commands share one
+ *		    communication channel (M10x),
+ *		==0 if B channels have separate communication channels (base)
+ * @ignoreframes:	number of frames to ignore after setting up B channel
+ * @cidmode:	!=0: start in CallID mode
+ * @modulename:	name of driver module for LL registration
+ *
  * Allocate and initialize cardstate structure for Gigaset driver
  * Calls hardware dependent gigaset_initcshw() function
  * Calls B channel initialization function gigaset_initbcs() for each B channel
- * parameters:
- *	drv		hardware driver the device belongs to
- *	channels	number of B channels supported by device
- *	onechannel	!=0: B channel data and AT commands share one
- *			     communication channel
- *			==0: B channels have separate communication channels
- *	ignoreframes	number of frames to ignore after setting up B channel
- *	cidmode		!=0: start in CallID mode
- *	modulename	name of driver module (used for I4L registration)
- * return value:
+ *
+ * Return value:
  *	pointer to cardstate structure
  */
 struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
@@ -837,6 +878,17 @@ static void cleanup_cs(struct cardstate *cs)
 }
 
 
+/**
+ * gigaset_start() - start device operations
+ * @cs:		device descriptor structure.
+ *
+ * Prepares the device for use by setting up communication parameters,
+ * scheduling an EV_START event to initiate device initialization, and
+ * waiting for completion of the initialization.
+ *
+ * Return value:
+ *	1 - success, 0 - error
+ */
 int gigaset_start(struct cardstate *cs)
 {
 	unsigned long flags;
@@ -879,9 +931,15 @@ error:
 }
 EXPORT_SYMBOL_GPL(gigaset_start);
 
-/* gigaset_shutdown
- * check if a device is associated to the cardstate structure and stop it
- * return value: 0 if ok, -1 if no device was associated
+/**
+ * gigaset_shutdown() - shut down device operations
+ * @cs:		device descriptor structure.
+ *
+ * Deactivates the device by scheduling an EV_SHUTDOWN event and
+ * waiting for completion of the shutdown.
+ *
+ * Return value:
+ *	0 - success, -1 - error (no device associated)
  */
 int gigaset_shutdown(struct cardstate *cs)
 {
@@ -912,6 +970,13 @@ exit:
 }
 EXPORT_SYMBOL_GPL(gigaset_shutdown);
 
+/**
+ * gigaset_stop() - stop device operations
+ * @cs:		device descriptor structure.
+ *
+ * Stops operations on the device by scheduling an EV_STOP event and
+ * waiting for completion of the shutdown.
+ */
 void gigaset_stop(struct cardstate *cs)
 {
 	mutex_lock(&cs->mutex);
@@ -1020,6 +1085,14 @@ struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty)
 	return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start);
 }
 
+/**
+ * gigaset_freedriver() - free all associated ressources of a driver
+ * @drv:	driver descriptor structure.
+ *
+ * Unregisters the driver from the system and deallocates the driver
+ * structure @drv and all structures referenced from it.
+ * All devices should be shut down before calling this.
+ */
 void gigaset_freedriver(struct gigaset_driver *drv)
 {
 	unsigned long flags;
@@ -1035,14 +1108,16 @@ void gigaset_freedriver(struct gigaset_driver *drv)
 }
 EXPORT_SYMBOL_GPL(gigaset_freedriver);
 
-/* gigaset_initdriver
+/**
+ * gigaset_initdriver() - initialize driver structure
+ * @minor:	First minor number
+ * @minors:	Number of minors this driver can handle
+ * @procname:	Name of the driver
+ * @devname:	Name of the device files (prefix without minor number)
+ *
  * Allocate and initialize gigaset_driver structure. Initialize interface.
- * parameters:
- *	minor		First minor number
- *	minors		Number of minors this driver can handle
- *	procname	Name of the driver
- *	devname		Name of the device files (prefix without minor number)
- * return value:
+ *
+ * Return value:
  *	Pointer to the gigaset_driver structure on success, NULL on failure.
  */
 struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
@@ -1095,6 +1170,13 @@ error:
 }
 EXPORT_SYMBOL_GPL(gigaset_initdriver);
 
+/**
+ * gigaset_blockdriver() - block driver
+ * @drv:	driver descriptor structure.
+ *
+ * Prevents the driver from attaching new devices, in preparation for
+ * deregistration.
+ */
 void gigaset_blockdriver(struct gigaset_driver *drv)
 {
 	drv->blocked = 1;
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index 31082a6..7258b25 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -474,8 +474,13 @@ static int cid_of_response(char *s)
 	//FIXME is ;<digit>+ at end of non-CID response really impossible?
 }
 
-/* This function will be called via task queue from the callback handler.
- * We received a modem response and have to handle it..
+/**
+ * gigaset_handle_modem_response() - process received modem response
+ * @cs:		device descriptor structure.
+ *
+ * Called by asyncdata/isocdata if a block of data received from the
+ * device must be processed as a modem command response. The data is
+ * already in the cs structure.
  */
 void gigaset_handle_modem_response(struct cardstate *cs)
 {
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
index 7b88130..8c99578 100644
--- a/drivers/isdn/gigaset/i4l.c
+++ b/drivers/isdn/gigaset/i4l.c
@@ -79,6 +79,14 @@ static int writebuf_from_LL(int driverID, int channel, int ack,
 	return cs->ops->send_skb(bcs, skb);
 }
 
+/**
+ * gigaset_skb_sent() - acknowledge sending an skb
+ * @bcs:	B channel descriptor structure.
+ * @skb:	sent data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when the data in a
+ * skb has been successfully sent, for signalling completion to the LL.
+ */
 void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
 {
 	unsigned len;
@@ -455,6 +463,15 @@ int gigaset_isdn_setup_accept(struct at_state_t *at_state)
 	return 0;
 }
 
+/**
+ * gigaset_isdn_icall() - signal incoming call
+ * @at_state:	connection state structure.
+ *
+ * Called by main module to notify the LL that an incoming call has been
+ * received. @at_state contains the parameters of the call.
+ *
+ * Return value: call disposition (ICALL_*)
+ */
 int gigaset_isdn_icall(struct at_state_t *at_state)
 {
 	struct cardstate *cs = at_state->cs;
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index f33ac27..6a8e138 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -616,6 +616,15 @@ void gigaset_if_free(struct cardstate *cs)
 	tty_unregister_device(drv->tty, cs->minor_index);
 }
 
+/**
+ * gigaset_if_receive() - pass a received block of data to the tty device
+ * @cs:		device descriptor structure.
+ * @buffer:	received data.
+ * @len:	number of bytes received.
+ *
+ * Called by asyncdata/isocdata if a block of data received from the
+ * device must be sent to userspace through the ttyG* device.
+ */
 void gigaset_if_receive(struct cardstate *cs,
 			unsigned char *buffer, size_t len)
 {
-- 
1.6.2.1.214.ge986c

^ permalink raw reply related

* [PATCH RFC 6/6] gigaset: add Kernel CAPI interface
From: Tilman Schmidt @ 2009-08-21 17:21 UTC (permalink / raw)
  To: i4ldeveloper, netdev, linux-kernel; +Cc: Hansjoerg Lipp
In-Reply-To: <20090821-patch-00.tilman@imap.cc>

Add a Kernel CAPI interface to the Gigaset driver.

Impact: new functionality
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
---
 drivers/isdn/gigaset/Kconfig    |   18 +-
 drivers/isdn/gigaset/Makefile   |    1 +
 drivers/isdn/gigaset/capi.c     | 1855 +++++++++++++++++++++++++++++++++++++++
 drivers/isdn/gigaset/common.c   |   19 +
 drivers/isdn/gigaset/ev-layer.c |   26 +-
 drivers/isdn/gigaset/gigaset.h  |    7 +-
 6 files changed, 1910 insertions(+), 16 deletions(-)
 create mode 100644 drivers/isdn/gigaset/capi.c

diff --git a/drivers/isdn/gigaset/Kconfig b/drivers/isdn/gigaset/Kconfig
index 6fd2dc1..dcefedc 100644
--- a/drivers/isdn/gigaset/Kconfig
+++ b/drivers/isdn/gigaset/Kconfig
@@ -10,20 +10,32 @@ menuconfig ISDN_DRV_GIGASET
 	  If you have one of these devices, say M here and for at least
 	  one of the connection specific parts that follow.
 	  This will build a module called "gigaset".
-	  Note: If you build the ISDN4Linux subsystem (ISDN_I4L)
+	  Note: If you build your ISDN subsystem (ISDN_CAPI or ISDN_I4L)
 	  as a module, you have to build this driver as a module too,
 	  otherwise the Gigaset device won't show up as an ISDN device.
 
 if ISDN_DRV_GIGASET
 
+config GIGASET_CAPI
+	bool "Gigaset CAPI support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	depends on ISDN_CAPI='y'||(ISDN_CAPI='m'&&ISDN_DRV_GIGASET='m')
+	default ISDN_I4L='n'
+	help
+	  Build the Gigaset driver as a CAPI 2.0 driver interfacing with
+	  the Kernel CAPI subsystem. To use it with the old ISDN4Linux
+	  subsystem you'll have to enable the capidrv glue driver.
+	  (select ISDN_CAPI_CAPIDRV.)
+	  Say N to build the old native ISDN4Linux variant.
+
 config GIGASET_I4L
 	bool
 	depends on ISDN_I4L='y'||(ISDN_I4L='m'&&ISDN_DRV_GIGASET='m')
-	default y
+	default !GIGASET_CAPI
 
 config GIGASET_DUMMYLL
 	bool
-	default !GIGASET_I4L
+	default !GIGASET_CAPI&&!GIGASET_I4L
 
 config GIGASET_BASE
 	tristate "Gigaset base station support"
diff --git a/drivers/isdn/gigaset/Makefile b/drivers/isdn/gigaset/Makefile
index d429202..c453b72 100644
--- a/drivers/isdn/gigaset/Makefile
+++ b/drivers/isdn/gigaset/Makefile
@@ -1,4 +1,5 @@
 gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o
+gigaset-$(CONFIG_GIGASET_CAPI) += capi.o
 gigaset-$(CONFIG_GIGASET_I4L) += i4l.o
 gigaset-$(CONFIG_GIGASET_DUMMYLL) += dummyll.o
 usb_gigaset-y := usb-gigaset.o
diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
new file mode 100644
index 0000000..9ff1063
--- /dev/null
+++ b/drivers/isdn/gigaset/capi.c
@@ -0,0 +1,1855 @@
+/*
+ * Kernel CAPI interface for the Gigaset driver
+ *
+ * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation; either version 2 of
+ *	the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/ctype.h>
+#include <linux/isdn/capilli.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+/* missing from kernelcapi.h */
+#define CapiNcpiNotSupportedByProtocol	0x0001
+#define CapiFlagsNotSupportedByProtocol	0x0002
+#define CapiAlertAlreadySent		0x0003
+
+/* missing from capicmd.h */
+#define CAPI_CONNECT_IND_BASELEN	(CAPI_MSG_BASELEN+4+2+8*1)
+#define CAPI_CONNECT_ACTIVE_IND_BASELEN	(CAPI_MSG_BASELEN+4+3*1)
+#define CAPI_CONNECT_B3_IND_BASELEN	(CAPI_MSG_BASELEN+4+1)
+#define CAPI_CONNECT_B3_ACTIVE_IND_BASELEN	(CAPI_MSG_BASELEN+4+1)
+#undef CAPI_DATA_B3_REQ_LEN
+#define CAPI_DATA_B3_REQ_LEN		(CAPI_MSG_BASELEN+4+4+2+2+2+8)
+#define CAPI_DATA_B3_CONF_LEN		(CAPI_MSG_BASELEN+4+2+2)
+#define CAPI_DISCONNECT_IND_LEN		(CAPI_MSG_BASELEN+4+2)
+#define CAPI_DISCONNECT_B3_IND_BASELEN	(CAPI_MSG_BASELEN+4+2+1)
+/* most _CONF messages contain only Controller/PLCI/NCCI and Info parameters */
+#define CAPI_STDCONF_LEN		(CAPI_MSG_BASELEN+4+2)
+
+/* missing from capiutil.h */
+#define CAPIMSG_DATAHANDLE(m)	CAPIMSG_U16(m,18) /* DATA_B3_REQ */
+#define CAPIMSG_FLAGS(m)	CAPIMSG_U16(m,20) /* DATA_B3_REQ */
+
+/* Flags (DATA_B3_REQ, DATA_B3_IND) */
+#define CAPI_FLAGS_DELIVERY_CONFIRMATION	0x04
+#define CAPI_FLAGS_RESERVED	(~0x1f)
+
+/* buffer sizes */
+#define MAX_BC_OCTETS 11
+#define MAX_HLC_OCTETS 3
+#define MAX_NUMBER_DIGITS 20
+#define MAX_FMT_IE_LEN 20
+
+/* registered application data structure */
+struct gigaset_capi_appl {
+	struct list_head ctrlist;
+	struct gigaset_capi_appl *bcnext;
+	u16 id;
+	u32 listenInfoMask;
+	u32 listenCIPmask;
+	u16 nextMessageNumber;
+};
+
+/* CAPI specific controller data structure */
+struct gigaset_capi_ctr {
+	struct capi_ctr ctr;
+	struct list_head appls;
+	int b3conn[BAS_CHANNELS];
+	struct mutex ctr_mtx;
+	/* two _cmsg structures possibly used concurrently: */
+	_cmsg hcmsg;	/* for message composition triggered from hardware */
+	_cmsg acmsg;	/* for dissection of messages sent from application */
+	u8 bc_buf[MAX_BC_OCTETS+1];
+	u8 hlc_buf[MAX_HLC_OCTETS+1];
+	u8 cgpty_buf[MAX_NUMBER_DIGITS+3];
+	u8 cdpty_buf[MAX_NUMBER_DIGITS+2];
+};
+
+/* CIP Value table (from CAPI 2.0 standard, ch. 6.1) */
+struct {
+	u8 *bc;
+	u8 *hlc;
+} cip2bchlc[] = {
+	[ 1] = { "8090A3", NULL },
+		/* Speech (A-law) */
+	[ 2] = { "8890", NULL },
+		/* Unrestricted digital information */
+	[ 3] = { "8990", NULL },
+		/* Restricted digital information */
+	[ 4] = { "9090A3", NULL },
+		/* 3,1 kHz audio (A-law) */
+	[ 5] = { "9190", NULL },
+		/* 7 kHz audio */
+	[ 6] = { "9890", NULL },
+		/* Video */
+	[ 7] = { "88C0C6E6", NULL },
+		/* Packet mode */
+	[ 8] = { "8890218F", NULL },
+		/* 56 kbit/s rate adaptation */
+	[ 9] = { "9190A5", NULL },
+		/* Unrestricted digital information with tones/announcements */
+	[16] = { "8090A3", "9181" },
+		/* Telephony */
+	[17] = { "9090A3", "9184" },
+		/* Group 2/3 facsimile */
+	[18] = { "8890", "91A1" },
+		/* Group 4 facsimile Class 1 */
+	[19] = { "8890", "91A4" },
+		/* Teletex service basic and mixed mode
+		   and Group 4 facsimile service Classes II and III */
+	[20] = { "8890", "91A8" },
+		/* Teletex service basic and processable mode */
+	[21] = { "8890", "91B1" },
+		/* Teletex service basic mode */
+	[22] = { "8890", "91B2" },
+		/* International interworking for Videotex */
+	[23] = { "8890", "91B5" },
+		/* Telex */
+	[24] = { "8890", "91B8" },
+		/* Message Handling Systems in accordance with X.400 */
+	[25] = { "8890", "91C1" },
+		/* OSI application in accordance with X.200 */
+	[26] = { "9190A5", "9181" },
+		/* 7 kHz telephony */
+	[27] = { "9190A5", "916001" },
+		/* Video telephony, first connection */
+	[28] = { "8890", "916002" },
+		/* Video telephony, second connection */
+};
+
+/*
+ * helper functions
+ * ================
+ */
+
+/*
+ * emit unsupported parameter warning
+ */
+static inline void ignore_cstruct_param(struct cardstate *cs, _cstruct param,
+				       char *msgname, char *paramname)
+{
+	if (param && *param)
+		dev_warn(cs->dev, "%s: ignoring unsupported parameter: %s\n",
+			 msgname, paramname);
+}
+
+static inline void ignore_cmstruct_param(struct cardstate *cs, _cmstruct param,
+				       char *msgname, char *paramname)
+{
+	if (param != CAPI_DEFAULT)
+		dev_warn(cs->dev, "%s: ignoring unsupported parameter: %s\n",
+			 msgname, paramname);
+}
+
+/*
+ * check for legal hex digit
+ */
+static inline int ishexdigit(char c)
+{
+	if (c >= '0' && c <= '9')
+		return 1;
+	if (c >= 'A' && c <= 'F')
+		return 1;
+	if (c >= 'a' && c <= 'f')
+		return 1;
+	return 0;
+}
+
+/*
+ * convert hex to binary
+ */
+static inline u8 hex2bin(char c)
+{
+	int result = c & 0x0f;
+	if (c & 0x40) result += 9;
+	return result;
+}
+
+/*
+ * convert an IE from Gigaset hex string to ETSI binary representation
+ * including length byte
+ * return value: result length, -1 on error
+ */
+static int encode_ie(char *in, u8 *out, int maxlen)
+{
+	int l = 0;
+	while (*in) {
+		if (!ishexdigit(in[0]) || !ishexdigit(in[1]) || l >= maxlen)
+			return -1;
+		out[++l] = (hex2bin(in[0]) << 4) + hex2bin(in[1]);
+		in += 2;
+	}
+	out[0] = l;
+	return l;
+}
+
+/*
+ * convert an IE from ETSI binary representation including length byte
+ * to Gigaset hex string
+ */
+static void decode_ie(u8 *in, char *out)
+{
+	int i = *in;
+	while (i-- > 0) {
+		/* ToDo: conversion to upper case necessary? */
+		*out++ = toupper(hex_asc_hi(*++in));
+		*out++ = toupper(hex_asc_lo(*in));
+	}
+}
+
+/*
+ * retrieve application data structure for an application ID
+ */
+static inline struct gigaset_capi_appl *
+get_appl(struct gigaset_capi_ctr *iif, u16 appl)
+{
+	struct gigaset_capi_appl *ap;
+
+	list_for_each_entry(ap, &iif->appls, ctrlist)
+		if (ap->id == appl)
+			return ap;
+	return NULL;
+}
+
+/*
+ * dump CAPI message to kernel messages for debugging
+ */
+static inline void dump_cmsg(enum debuglevel level, const char *tag, _cmsg *p)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+	_cdebbuf *cdb;
+
+	if (!(gigaset_debuglevel & level))
+		return;
+
+	cdb = capi_cmsg2str(p);
+	if (cdb) {
+		gig_dbg(level, "%s: [%d] %s", tag, p->ApplId, cdb->buf);
+		cdebbuf_free(cdb);
+	} else {
+		gig_dbg(level, "%s: [%d] %s", tag, p->ApplId,
+			capi_cmd2str(p->Command, p->Subcommand));
+	}
+#endif
+}
+
+/*
+ * format CAPI IE as string
+ */
+
+static const char *format_ie(const char *ie)
+{
+	static char result[3*MAX_FMT_IE_LEN];
+	int len, count;
+	char *pout = result;
+
+	if (!ie)
+		return "NULL";
+
+	count = len = ie[0];
+	if (count > MAX_FMT_IE_LEN)
+		count = MAX_FMT_IE_LEN-1;
+	while (count--) {
+		*pout++ = hex_asc_hi(*++ie);
+		*pout++ = hex_asc_lo(*ie);
+		*pout++ = ' ';
+	}
+	if (len > MAX_FMT_IE_LEN) {
+		*pout++ = '.';
+		*pout++ = '.';
+		*pout++ = '.';
+	}
+	*--pout = 0;
+	return result;
+}
+
+
+/*
+ * driver interface functions
+ * ==========================
+ */
+
+/**
+ * gigaset_skb_sent() - acknowledge transmission of outgoing skb
+ * @bcs:	B channel descriptor structure.
+ * @skb:	sent data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when the data in a
+ * skb has been successfully sent, for signalling completion to the LL.
+ */
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
+{
+	struct cardstate *cs = bcs->cs;
+	struct gigaset_capi_ctr *iif = cs->iif;
+	struct gigaset_capi_appl *ap = bcs->ap;
+	struct sk_buff *cskb;
+	u16 flags;
+
+	/* update statistics */
+	++bcs->trans_up;
+
+	if (!ap) {
+		dev_err(cs->dev, "%s: no application\n", __func__);
+		return;
+	}
+
+	/* don't send further B3 messages if logical connection disconnected */
+	if (!iif->b3conn[bcs->channel])
+		return;
+
+	/* prepare DATA_B3_CONF message */
+	/* ToDo: optimization (avoid _cmsg overhead) */
+	capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DATA_B3, CAPI_CONF,
+			 ap->nextMessageNumber++,
+			 iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
+	iif->hcmsg.DataHandle = CAPIMSG_DATAHANDLE(dskb->head);
+	iif->hcmsg.Info = CAPI_NOERROR;
+
+	/* ToDo: honor unset "delivery confirmation" bit */
+	flags = CAPIMSG_FLAGS(dskb->head);
+
+	/* any other flags are unsupported */
+	if (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION)
+		iif->hcmsg.Info = CapiFlagsNotSupportedByProtocol;
+
+	/* build and emit DATA_B3_CONF message */
+	if ((cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC)) == NULL) {
+		dev_err(cs->dev, "%s: out of memory\n", __func__);
+		return;
+	}
+	capi_cmsg2message(&iif->hcmsg, __skb_put(cskb, CAPI_DATA_B3_CONF_LEN));
+	dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+	capi_ctr_handle_message(&iif->ctr, ap->id, cskb);
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_sent);
+
+/**
+ * gigaset_skb_rcvd() - pass received skb to LL
+ * @bcs:	B channel descriptor structure.
+ * @skb:	received data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when user data has
+ * been successfully received, for passing to the LL.
+ * Warning: skb must not be accessed anymore!
+ */
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
+{
+	struct cardstate *cs = bcs->cs;
+	struct gigaset_capi_ctr *iif = cs->iif;
+	struct gigaset_capi_appl *ap = bcs->ap;
+
+	/* update statistics */
+	bcs->trans_down++;
+
+	if (!ap) {
+		dev_err(cs->dev, "%s: no application\n", __func__);
+		return;
+	}
+
+	/*
+	 * prepare DATA_B3_IND message
+	 * Parameters: NCCI = 1, all others 0/unused
+	 */
+	/* ToDo: optimization (avoid _cmsg overhead) */
+	capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DATA_B3, CAPI_IND,
+			 ap->nextMessageNumber++,
+			 iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
+
+	/* prepend message to payload */
+	if (skb_headroom(skb) < CAPI_DATA_B3_REQ_LEN) {
+		/* shouldn't happen */
+		dev_err(cs->dev,
+			"%s: insufficient skb headroom (%d), packet dropped\n",
+			__func__, skb_headroom(skb));
+		dev_kfree_skb(skb);
+		return;
+	}
+	capi_cmsg2message(&iif->hcmsg, skb_push(skb, CAPI_DATA_B3_REQ_LEN));
+	dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+	capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
+
+/**
+ * gigaset_isdn_rcv_err() - signal receive error
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when a receive error
+ * has occurred, for signalling to the LL.
+ */
+void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+	/* if currently ignoring packets, just count down */
+	if (bcs->ignore) {
+		bcs->ignore--;
+		return;
+	}
+
+	/* update statistics */
+	bcs->corrupted++;
+
+	/* ToDo: signal error -> LL */
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
+/**
+ * gigaset_isdn_icall() - signal incoming call
+ * @at_state:	connection state structure.
+ *
+ * Called by main module at tasklet level to notify the LL that an incoming
+ * call has been received. @at_state contains the parameters of the call.
+ *
+ * Return value: call disposition (ICALL_*)
+ */
+int gigaset_isdn_icall(struct at_state_t *at_state)
+{
+	struct cardstate *cs = at_state->cs;
+	struct bc_state *bcs = at_state->bcs;
+	struct gigaset_capi_ctr *iif = cs->iif;
+	struct gigaset_capi_appl *ap;
+	u32 actCIPmask;
+	struct sk_buff *skb;
+	unsigned int msgsize;
+	int i;
+
+	/*
+	 * ToDo: signal calls without a free B channel, too
+	 * (requires a u8 handle for the at_state structure that can
+	 * be stored in the PLCI and used in the CONNECT_RESP message
+	 * handler to retrieve it)
+	 */
+	if (!bcs)
+		return ICALL_IGNORE;
+
+	/* prepare CONNECT_IND message, using B channel number as PLCI */
+	capi_cmsg_header(&iif->hcmsg, 0, CAPI_CONNECT, CAPI_IND, 0,
+			 iif->ctr.cnr | ((bcs->channel + 1) << 8));
+
+	/* minimum size, all structs empty */
+	msgsize = CAPI_CONNECT_IND_BASELEN;
+
+	/* Bearer Capability (mandatory) */
+	if (at_state->str_var[STR_ZBC]) {
+		/* pass on BC from Gigaset */
+		if (encode_ie(at_state->str_var[STR_ZBC], iif->bc_buf,
+			      MAX_BC_OCTETS) < 0) {
+			dev_warn(cs->dev, "RING ignored - bad BC %s\n",
+				 at_state->str_var[STR_ZBC]);
+			return ICALL_IGNORE;
+		}
+
+		/* look up corresponding CIP value */
+		iif->hcmsg.CIPValue = 0;	/* default if nothing found */
+		for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++)
+			if (cip2bchlc[i].bc != NULL &&
+			    cip2bchlc[i].hlc == NULL &&
+			    !strcmp(cip2bchlc[i].bc,
+				    at_state->str_var[STR_ZBC])) {
+				iif->hcmsg.CIPValue = i;
+				break;
+			}
+	} else {
+		/* no BC (internal call): assume CIP 1 (speech, A-law) */
+		iif->hcmsg.CIPValue = 1;
+		encode_ie(cip2bchlc[1].bc, iif->bc_buf, MAX_BC_OCTETS);
+	}
+	iif->hcmsg.BC = iif->bc_buf;
+	msgsize += iif->hcmsg.BC[0];
+
+	/* High Layer Compatibility (optional) */
+	if (at_state->str_var[STR_ZHLC]) {
+		/* pass on HLC from Gigaset */
+		if (encode_ie(at_state->str_var[STR_ZHLC], iif->hlc_buf,
+			      MAX_HLC_OCTETS) < 0) {
+			dev_warn(cs->dev, "RING ignored - bad HLC %s\n",
+				 at_state->str_var[STR_ZHLC]);
+			return ICALL_IGNORE;
+		}
+		iif->hcmsg.HLC = iif->hlc_buf;
+		msgsize += iif->hcmsg.HLC[0];
+
+		/* look up corresponding CIP value */
+		/* keep BC based CIP value if none found */
+		if (at_state->str_var[STR_ZBC])
+			for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++)
+				if (cip2bchlc[i].hlc != NULL &&
+				    !strcmp(cip2bchlc[i].hlc,
+					    at_state->str_var[STR_ZHLC]) &&
+				    !strcmp(cip2bchlc[i].bc,
+					    at_state->str_var[STR_ZBC])) {
+					iif->hcmsg.CIPValue = i;
+					break;
+				}
+	}
+
+	/* Called Party Number (optional) */
+	if (at_state->str_var[STR_ZCPN]) {
+		i = strlen(at_state->str_var[STR_ZCPN]);
+		if (i > MAX_NUMBER_DIGITS) {
+			dev_warn(cs->dev, "RING ignored - bad number %s\n",
+				 at_state->str_var[STR_ZBC]);
+			return ICALL_IGNORE;
+		}
+		iif->cdpty_buf[0] = i + 1;
+		iif->cdpty_buf[1] = 0x80; /* type / numbering plan unknown */
+		memcpy(iif->cdpty_buf+2, at_state->str_var[STR_ZCPN], i);
+		iif->hcmsg.CalledPartyNumber = iif->cdpty_buf;
+		msgsize += iif->hcmsg.CalledPartyNumber[0];
+	}
+
+	/* Calling Party Number (optional) */
+	if (at_state->str_var[STR_NMBR]) {
+		i = strlen(at_state->str_var[STR_NMBR]);
+		if (i > MAX_NUMBER_DIGITS) {
+			dev_warn(cs->dev, "RING ignored - bad number %s\n",
+				 at_state->str_var[STR_ZBC]);
+			return ICALL_IGNORE;
+		}
+		iif->cgpty_buf[0] = i + 2;
+		iif->cgpty_buf[1] = 0x00; /* type / numbering plan unknown */
+		iif->cgpty_buf[2] = 0x80; /* pres. allowed, not screened */
+		memcpy(iif->cgpty_buf+3, at_state->str_var[STR_NMBR], i);
+		iif->hcmsg.CallingPartyNumber = iif->cgpty_buf;
+		msgsize += iif->hcmsg.CallingPartyNumber[0];
+	}
+
+	/* remaining parameters (not supported, always left NULL):
+	 * - CalledPartySubaddress
+	 * - CallingPartySubaddress
+	 * - AdditionalInfo
+	 *   - BChannelinformation
+	 *   - Keypadfacility
+	 *   - Useruserdata
+	 *   - Facilitydataarray
+	 */
+
+	gig_dbg(DEBUG_CMD, "icall: PLCI %x CIP %d BC %s",
+		iif->hcmsg.adr.adrPLCI, iif->hcmsg.CIPValue,
+		format_ie(iif->hcmsg.BC));
+	gig_dbg(DEBUG_CMD, "icall: HLC %s",
+		format_ie(iif->hcmsg.HLC));
+	gig_dbg(DEBUG_CMD, "icall: CgPty %s",
+		format_ie(iif->hcmsg.CallingPartyNumber));
+	gig_dbg(DEBUG_CMD, "icall: CdPty %s",
+		format_ie(iif->hcmsg.CalledPartyNumber));
+
+	/* scan application list for matching listeners */
+	bcs->ap = NULL;
+	actCIPmask = 1 | (1 << iif->hcmsg.CIPValue);
+	list_for_each_entry(ap, &iif->appls, ctrlist)
+		if (actCIPmask & ap->listenCIPmask) {
+			/* send CONNECT_IND message to this application */
+			iif->hcmsg.ApplId = ap->id;
+			iif->hcmsg.Messagenumber = ap->nextMessageNumber++;
+
+			if ((skb = alloc_skb(msgsize, GFP_ATOMIC)) == NULL) {
+				dev_err(cs->dev, "%s: out of memory\n",
+					__func__);
+				break;
+			}
+			capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize));
+			dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+			capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+
+			/* add to listeners on this B channel */
+			ap->bcnext = bcs->ap;
+			bcs->ap = ap;
+			bcs->chstate |= CHS_NOTIFY_LL;
+		}
+
+	/*
+	 * Return "accept" if any listeners.
+	 * Gigaset will send ALERTING.
+	 * There doesn't seem to be a way to avoid this.
+	 */
+	return bcs->ap ? ICALL_ACCEPT : ICALL_IGNORE;
+}
+
+/*
+ * send a DISCONNECT_IND message to an application
+ * does not sleep, clobbers the controller's hcmsg structure
+ */
+static void send_disconnect_ind(struct bc_state *bcs,
+				struct gigaset_capi_appl *ap, u16 reason)
+{
+	struct cardstate *cs = bcs->cs;
+	struct gigaset_capi_ctr *iif = cs->iif;
+	struct sk_buff *skb;
+
+	/* prepare DISCONNECT_IND message */
+	capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT, CAPI_IND,
+			 ap->nextMessageNumber++,
+			 iif->ctr.cnr | ((bcs->channel + 1) << 8));
+	iif->hcmsg.Reason = reason;
+
+	/* build and emit DISCONNECT_IND message */
+	if ((skb = alloc_skb(CAPI_DISCONNECT_IND_LEN, GFP_ATOMIC)) == NULL) {
+		dev_err(cs->dev, "%s: out of memory\n", __func__);
+		return;
+	}
+	capi_cmsg2message(&iif->hcmsg, __skb_put(skb, CAPI_DISCONNECT_IND_LEN));
+	dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+	capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/*
+ * send a DISCONNECT_B3_IND message to an application
+ * Parameters: NCCI = 1, NCPI empty, Reason_B3 = 0
+ * does not sleep, clobbers the controller's hcmsg structure
+ */
+void send_disconnect_b3_ind(struct bc_state *bcs, struct gigaset_capi_appl *ap)
+{
+	struct cardstate *cs = bcs->cs;
+	struct gigaset_capi_ctr *iif = cs->iif;
+	struct sk_buff *skb;
+
+	capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
+			 ap->nextMessageNumber++,
+			 iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
+	if ((skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_ATOMIC))
+	    == NULL) {
+		dev_err(cs->dev, "%s: out of memory\n", __func__);
+		return;
+	}
+	capi_cmsg2message(&iif->hcmsg,
+			  __skb_put(skb, CAPI_DISCONNECT_B3_IND_BASELEN));
+	dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+	capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/**
+ * gigaset_isdn_connD() - signal D channel connect
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the D channel
+ * connection has been established.
+ */
+void gigaset_isdn_connD(struct bc_state *bcs)
+{
+	struct cardstate *cs = bcs->cs;
+	struct gigaset_capi_ctr *iif = cs->iif;
+	struct gigaset_capi_appl *ap = bcs->ap;
+	struct sk_buff *skb;
+	unsigned int msgsize;
+
+	if (!ap) {
+		dev_err(cs->dev, "%s: no application\n", __func__);
+		return;
+	}
+	while (ap->bcnext) {
+		/* this should never happen */
+		dev_warn(cs->dev, "%s: dropping extra application %u\n",
+			 __func__, ap->bcnext->id);
+		send_disconnect_ind(bcs, ap->bcnext,
+				    CapiCallGivenToOtherApplication);
+		ap->bcnext = ap->bcnext->bcnext;
+	}
+
+	/* prepare CONNECT_ACTIVE_IND message
+	 * Note: LLC not supported by device
+	 */
+	capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_CONNECT_ACTIVE, CAPI_IND,
+			 ap->nextMessageNumber++,
+			 iif->ctr.cnr | ((bcs->channel + 1) << 8));
+
+	/* minimum size, all structs empty */
+	msgsize = CAPI_CONNECT_ACTIVE_IND_BASELEN;
+
+	/* ToDo: set parameter: Connected number
+	 * (requires ev-layer state machine extension to collect
+	 * ZCON device reply)
+	 */
+
+	/* build and emit CONNECT_ACTIVE_IND message */
+	if ((skb = alloc_skb(msgsize, GFP_ATOMIC)) == NULL) {
+		dev_err(cs->dev, "%s: out of memory\n", __func__);
+		return;
+	}
+	capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize));
+	dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+	capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/**
+ * gigaset_isdn_hupD() - signal D channel hangup
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the D channel
+ * connection has been shut down.
+ */
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+	struct gigaset_capi_ctr *iif = bcs->cs->iif;
+	struct gigaset_capi_appl *ap;
+
+	/*
+	 * ToDo: pass on reason code reported by device
+	 * (requires ev-layer state machine extension to collect
+	 * ZCAU device reply)
+	 */
+	for (ap = bcs->ap; ap != NULL; ap = ap->bcnext) {
+		if (iif->b3conn[bcs->channel]) {
+			iif->b3conn[bcs->channel] = 0;
+		 	send_disconnect_b3_ind(bcs, ap);
+		}
+		send_disconnect_ind(bcs, ap, 0);
+	}
+	bcs->ap = NULL;
+}
+
+/**
+ * gigaset_isdn_connB() - signal B channel connect
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the B channel
+ * connection has been established.
+ */
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+	struct cardstate *cs = bcs->cs;
+	struct gigaset_capi_ctr *iif = cs->iif;
+	struct gigaset_capi_appl *ap = bcs->ap;
+	struct sk_buff *skb;
+	unsigned int msgsize;
+	u8 command;
+
+	if (!ap) {
+		dev_err(cs->dev, "%s: no application\n", __func__);
+		return;
+	}
+	while (ap->bcnext) {
+		/* this should never happen */
+		dev_warn(cs->dev, "%s: dropping extra application %u\n",
+			 __func__, ap->bcnext->id);
+		send_disconnect_ind(bcs, ap->bcnext,
+				    CapiCallGivenToOtherApplication);
+		ap->bcnext = ap->bcnext->bcnext;
+	}
+
+	/*
+	 * emit CONNECT_B3_ACTIVE_IND if we already got CONNECT_B3_REQ;
+	 * otherwise we have to emit CONNECT_B3_IND first, and follow up with
+	 * CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP
+	 * Parameters in both cases always: NCCI = 1, NCPI empty
+	 */
+	if (iif->b3conn[bcs->channel]) {
+		command = CAPI_CONNECT_B3_ACTIVE;
+		msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
+	} else {
+		command = CAPI_CONNECT_B3;
+		msgsize = CAPI_CONNECT_B3_IND_BASELEN;
+	}
+	capi_cmsg_header(&iif->hcmsg, ap->id, command, CAPI_IND,
+			 ap->nextMessageNumber++,
+			 iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
+	if ((skb = alloc_skb(msgsize, GFP_ATOMIC)) == NULL) {
+		dev_err(cs->dev, "%s: out of memory\n", __func__);
+		return;
+	}
+	capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize));
+	dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+	capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+	iif->b3conn[bcs->channel] = 1;
+}
+
+/**
+ * gigaset_isdn_hupB() - signal B channel hangup
+ * @bcs:	B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the B channel connection has
+ * been shut down.
+ */
+void gigaset_isdn_hupB(struct bc_state *bcs)
+{
+	struct cardstate *cs = bcs->cs;
+	struct gigaset_capi_ctr *iif = cs->iif;
+	struct gigaset_capi_appl *ap = bcs->ap;
+
+	/* ToDo: assure order of DISCONNECT_B3_IND and DISCONNECT_IND ? */
+
+	/* nothing to do if no logical connection active */
+	if (!iif->b3conn[bcs->channel])
+		return;
+	iif->b3conn[bcs->channel] = 0;
+
+	if (!ap) {
+		dev_err(cs->dev, "%s: no application\n", __func__);
+		return;
+	}
+
+	send_disconnect_b3_ind(bcs, ap);
+}
+
+/**
+ * gigaset_isdn_start() - signal device availability
+ * @cs:		device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is available for
+ * use.
+ */
+void gigaset_isdn_start(struct cardstate *cs)
+{
+	struct gigaset_capi_ctr *iif = cs->iif;
+
+	/* fill profile data: manufacturer name */
+	strcpy(iif->ctr.manu, "Siemens");
+	/* CAPI and device version */
+	iif->ctr.version.majorversion = 2;		/* CAPI 2.0 */
+	iif->ctr.version.minorversion = 0;
+	/* ToDo: check/assert cs->gotfwver? */
+	iif->ctr.version.majormanuversion = cs->fwver[0];
+	iif->ctr.version.minormanuversion = cs->fwver[1];
+	/* number of B channels supported */
+	iif->ctr.profile.nbchannel = cs->channels;
+	/* global options: internal controller, supplementary services */
+	iif->ctr.profile.goptions = 0x11;
+	/* B1 protocols: 64 kbit/s HDLC or transparent */
+	iif->ctr.profile.support1 =  0x03;
+	/* B2 protocols: transparent only */
+	/* ToDo: X.75 SLP ? */
+	iif->ctr.profile.support2 =  0x02;
+	/* B3 protocols: transparent only */
+	iif->ctr.profile.support3 =  0x01;
+	/* no serial number */
+	strcpy(iif->ctr.serial, "0");
+	capi_ctr_ready(&iif->ctr);
+}
+
+/**
+ * gigaset_isdn_stop() - signal device unavailability
+ * @cs:		device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is no longer
+ * available for use.
+ */
+void gigaset_isdn_stop(struct cardstate *cs)
+{
+	struct gigaset_capi_ctr *iif = cs->iif;
+	capi_ctr_down(&iif->ctr);
+}
+
+/*
+ * kernel CAPI callback methods
+ * ============================
+ */
+
+/*
+ * load firmware
+ */
+static int gigaset_load_firmware(struct capi_ctr *ctr, capiloaddata *data)
+{
+	struct cardstate *cs = ctr->driverdata;
+
+	/* AVM specific operation, not needed for Gigaset -- ignore */
+	dev_notice(cs->dev, "load_firmware ignored\n");
+
+	return 0;
+}
+
+/*
+ * reset (deactivate) controller
+ */
+static void gigaset_reset_ctr(struct capi_ctr *ctr)
+{
+	struct cardstate *cs = ctr->driverdata;
+
+	/* AVM specific operation, not needed for Gigaset -- ignore */
+	dev_notice(cs->dev, "reset_ctr ignored\n");
+}
+
+/*
+ * register CAPI application
+ */
+static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl,
+			   capi_register_params *rp)
+{
+	struct gigaset_capi_ctr *iif
+		= container_of(ctr, struct gigaset_capi_ctr, ctr);
+	struct cardstate *cs = ctr->driverdata;
+	struct gigaset_capi_appl *ap;
+
+	list_for_each_entry(ap, &iif->appls, ctrlist)
+		if (ap->id == appl) {
+			dev_notice(cs->dev,
+				   "application %u already registered\n", appl);
+			return;
+		}
+
+	if ((ap = kzalloc(sizeof(*ap), GFP_KERNEL)) == NULL) {
+		dev_err(cs->dev, "%s: out of memory\n", __func__);
+		return;
+	}
+	ap->id = appl;
+
+	list_add(&ap->ctrlist, &iif->appls);
+}
+
+/*
+ * release CAPI application
+ */
+static void gigaset_release_appl(struct capi_ctr *ctr, u16 appl)
+{
+	struct gigaset_capi_ctr *iif
+		= container_of(ctr, struct gigaset_capi_ctr, ctr);
+	struct gigaset_capi_appl *ap, *tmp;
+
+	list_for_each_entry_safe(ap, tmp, &iif->appls, ctrlist)
+		if (ap->id == appl) {
+			list_del(&ap->ctrlist);
+			kfree(ap);
+		}
+
+}
+
+/*
+ * process CONNECT_REQ message
+ * allocates a B channel, prepares dial commands, and queues a DIAL event
+ * return value: CAPI Info value, CAPI_MSGOSRESOURCEERR if out of memory
+ */
+static u16 do_connect_req(struct cardstate *cs, _cmsg *cmsg,
+			  struct gigaset_capi_appl *ap)
+{
+	struct bc_state *bcs;
+	char **commands;
+	char *s;
+	u8 *pp;
+	int i, l;
+
+	/* get free B channel & construct PLCI */
+	if ((bcs = gigaset_get_free_channel(cs)) == NULL) {
+		dev_notice(cs->dev, "%s: no B channel available\n",
+			   "CONNECT_REQ");
+		return CapiNoPlciAvailable;
+	}
+	ap->bcnext = NULL;
+	bcs->ap = ap;
+	cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8;
+
+	/* build command table */
+	if ((commands = kzalloc(AT_NUM*(sizeof *commands), GFP_KERNEL)) == NULL)
+		goto oom;
+
+	/* encode parameter: Called party number */
+	pp = cmsg->CalledPartyNumber;
+	if (pp == NULL || *pp == 0) {
+		dev_notice(cs->dev, "%s: %s missing\n",
+			   "CONNECT_REQ", "Called party number");
+		return CapiIllMessageParmCoding;
+	}
+	l = *pp++;
+	/* check number type/numbering plan byte */
+	if (*pp != 0x80) {
+		dev_notice(cs->dev, "%s: %s type/plan 0x%02x unsupported\n",
+			   "CONNECT_REQ", "Called party number", *pp);
+		return CapiIllMessageParmCoding;
+	}
+	pp++;
+	l--;
+	/* translate "**" internal call prefix to CTP value */
+	if (l >= 2 && pp[0] == '*' && pp[1] == '*') {
+		s = "^SCTP=0\r";
+		pp += 2;
+		l -= 2;
+	} else {
+		s = "^SCTP=1\r";
+	}
+	if ((commands[AT_TYPE] = kstrdup(s, GFP_KERNEL)) == NULL ||
+	    (commands[AT_DIAL] = kmalloc(l+3, GFP_KERNEL)) == NULL)
+		goto oom;
+	snprintf(commands[AT_DIAL], l+3, "D%*s\r", l, pp);
+
+	/* encode parameter: Calling party number */
+	pp = cmsg->CallingPartyNumber;
+	if (pp != NULL && *pp > 0) {
+		l = *pp++;
+		/* check number type/numbering plan byte */
+		if (*pp & 0x7f) {
+			dev_notice(cs->dev,
+				   "%s: %s type/plan 0x%02x unsupported\n",
+				   "CONNECT_REQ", "Calling party number",
+				   *pp & 0x7f);
+			return CapiIllMessageParmCoding;
+		}
+		if (*pp & 0x80) {
+			/* octet 3a present */
+			if (l < 2) {
+				dev_notice(cs->dev, "%s: %s IE truncated\n",
+					   "CONNECT_REQ",
+					   "Calling party number");
+				return CapiIllMessageParmCoding;
+			}
+			pp++;
+			l--;
+			/* Presentation/Screening indicator */
+			switch (*pp) {
+			case 0x00:	/* Presentation allowed */
+				s = "^SCLIP=1\r";
+				break;
+			case 0x20:	/* Presentation restricted */
+				s = "^SCLIP=0\r";
+				break;
+			default:
+				dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+					   "CONNECT_REQ",
+					   "Presentation/Screening indicator",
+					   *pp);
+				return CapiIllMessageParmCoding;
+			}
+			if ((commands[AT_CLIP] = kstrdup(s, GFP_KERNEL))
+			    == NULL)
+				goto oom;
+		}
+		pp++;
+		l--;
+		if (l) {
+			/* number */
+			if ((commands[AT_MSN] = kmalloc(l+8, GFP_KERNEL))
+			    == NULL)
+				goto oom;
+			snprintf(commands[AT_MSN], l+8, "^SMSN=%*s\r", l, pp);
+		}
+	}
+
+	/* check parameter: CIP Value */
+	if (cmsg->CIPValue > ARRAY_SIZE(cip2bchlc) ||
+	    (cmsg->CIPValue > 0 && cip2bchlc[cmsg->CIPValue].bc == NULL)) {
+		dev_notice(cs->dev, "%s: unknown CIP value %d\n",
+			   "CONNECT_REQ", cmsg->CIPValue);
+		return CapiCipValueUnknown;
+	}
+
+	/* check/encode parameter: BC */
+	if (cmsg->BC && cmsg->BC[0]) {
+		/* explicit BC overrides CIP */
+		l = 2*cmsg->BC[0] + 7;
+		if ((commands[AT_BC] = kmalloc(l, GFP_KERNEL)) == NULL)
+			goto oom;
+		strcpy(commands[AT_BC], "^SBC=");
+		decode_ie(cmsg->BC, commands[AT_BC]+5);
+		strcpy(commands[AT_BC] + l - 2, "\r");
+	} else if (cip2bchlc[cmsg->CIPValue].bc) {
+		l = strlen(cip2bchlc[cmsg->CIPValue].bc) + 7;
+		if ((commands[AT_BC] = kmalloc(l, GFP_KERNEL)) == NULL)
+			goto oom;
+		snprintf(commands[AT_BC], l, "^SBC=%s\r",
+			 cip2bchlc[cmsg->CIPValue].bc);
+	}
+
+	/* check/encode parameter: HLC */
+	if (cmsg->HLC && cmsg->HLC[0]) {
+		/* explicit HLC overrides CIP */
+		l = 2*cmsg->HLC[0] + 7;
+		if ((commands[AT_HLC] = kmalloc(l, GFP_KERNEL)) == NULL)
+			goto oom;
+		strcpy(commands[AT_HLC], "^SHLC=");
+		decode_ie(cmsg->HLC, commands[AT_HLC]+5);
+		strcpy(commands[AT_HLC] + l - 2, "\r");
+	} else if (cip2bchlc[cmsg->CIPValue].hlc) {
+		l = strlen(cip2bchlc[cmsg->CIPValue].hlc) + 7;
+		if ((commands[AT_HLC] = kmalloc(l, GFP_KERNEL)) == NULL)
+			goto oom;
+		snprintf(commands[AT_HLC], l, "^SHLC=%s\r",
+			 cip2bchlc[cmsg->CIPValue].hlc);
+	}
+
+	/* check/encode parameter: B Protocol */
+	if (cmsg->BProtocol == CAPI_DEFAULT) {
+		bcs->proto2 = L2_HDLC;
+		dev_warn(cs->dev,
+		    "B2 Protocol X.75 SLP unsupported, using Transparent\n");
+	} else {
+		switch (cmsg->B1protocol) {
+		case 0:
+			bcs->proto2 = L2_HDLC;
+			break;
+		case 1:
+			bcs->proto2 = L2_BITSYNC;
+			break;
+		default:
+			dev_warn(cs->dev,
+			    "B1 Protocol %u unsupported, using Transparent\n",
+				 cmsg->B1protocol);
+			bcs->proto2 = L2_BITSYNC;
+		}
+		if (cmsg->B2protocol != 1)
+			dev_warn(cs->dev,
+			    "B2 Protocol %u unsupported, using Transparent\n",
+				 cmsg->B2protocol);
+		if (cmsg->B3protocol != 0)
+			dev_warn(cs->dev,
+			    "B3 Protocol %u unsupported, using Transparent\n",
+				 cmsg->B3protocol);
+		ignore_cstruct_param(cs, cmsg->B1configuration,
+					"CONNECT_REQ", "B1 Configuration");
+		ignore_cstruct_param(cs, cmsg->B2configuration,
+					"CONNECT_REQ", "B2 Configuration");
+		ignore_cstruct_param(cs, cmsg->B3configuration,
+					"CONNECT_REQ", "B3 Configuration");
+	}
+	if ((commands[AT_PROTO] = kmalloc(9, GFP_KERNEL)) == NULL)
+		goto oom;
+	snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+
+	/* ToDo: check/encode remaining parameters */
+	ignore_cstruct_param(cs, cmsg->CalledPartySubaddress,
+					"CONNECT_REQ", "Called pty subaddr");
+	ignore_cstruct_param(cs, cmsg->CallingPartySubaddress,
+					"CONNECT_REQ", "Calling pty subaddr");
+	ignore_cstruct_param(cs, cmsg->LLC,
+					"CONNECT_REQ", "LLC");
+	ignore_cmstruct_param(cs, cmsg->AdditionalInfo,
+					"CONNECT_REQ", "Additional Info");
+
+	/* encode parameter: B channel to use */
+	if ((commands[AT_ISO] = kmalloc(9, GFP_KERNEL)) == NULL)
+		goto oom;
+	snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
+		 (unsigned) bcs->channel + 1);
+
+	/* queue & schedule EV_DIAL event */
+	if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
+			       bcs->at_state.seq_index, NULL))
+		goto oom;
+	gig_dbg(DEBUG_CMD, "scheduling DIAL");
+	gigaset_schedule_event(cs);
+	return CapiSuccess;
+
+oom:
+	dev_err(cs->dev, "%s: out of memory\n", __func__);
+	if (commands)
+		for (i = 0; i < AT_NUM; i++)
+			kfree(commands[i]);
+	kfree(commands);
+	gigaset_free_channel(bcs);
+	return CAPI_MSGOSRESOURCEERR;
+}
+
+/*
+ * process DISCONNECT_REQ message
+ * schedule EV_HUP and emit DISCONNECT_B3_IND if necessary
+ * return value: CAPI Info value, CAPI_MSGOSRESOURCEERR if out of memory
+ */
+static u16 do_disconnect_req(struct cardstate *cs, struct gigaset_capi_ctr *iif,
+			     struct gigaset_capi_appl *ap)
+{
+	struct bc_state *bcs;
+	_cmsg *b3cmsg;
+	struct sk_buff *skb;
+	int channel;
+
+	/* extract and check channel number from PLCI */
+	channel = (iif->acmsg.adr.adrPLCI >> 8) & 0xff;
+	if (!channel || channel > cs->channels) {
+		dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+			   "CAPI_DISCONNECT_REQ", "PLCI",
+			   iif->acmsg.adr.adrPLCI);
+		return CapiIllContrPlciNcci;
+	}
+	bcs = cs->bcs + channel - 1;
+
+	/* ToDo: process parameter: Additional info */
+	ignore_cmstruct_param(cs, iif->acmsg.AdditionalInfo,
+			      "DISCONNECT_REQ", "Additional Info");
+
+	/* check for active logical connection */
+	if (iif->b3conn[channel-1]) {
+		/*
+		 * emit DISCONNECT_B3_IND with cause 0x3301
+		 * use separate cmsg structure, as the content of iif->acmsg
+		 * is still needed for creating the _CONF message
+		 */
+		if ((b3cmsg = kmalloc(sizeof(*b3cmsg), GFP_KERNEL)) == NULL) {
+			dev_err(cs->dev, "%s: out of memory\n", __func__);
+			return CAPI_MSGOSRESOURCEERR;
+		}
+		capi_cmsg_header(b3cmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
+				 ap->nextMessageNumber++,
+				 iif->ctr.cnr | ((bcs->channel + 1) << 8) |
+				 (1 << 16));
+		b3cmsg->Reason_B3 = CapiProtocolErrorLayer1;
+		skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_KERNEL);
+		if (skb == NULL) {
+			dev_err(cs->dev, "%s: out of memory\n", __func__);
+			return CAPI_MSGOSRESOURCEERR;
+		}
+		capi_cmsg2message(b3cmsg,
+			__skb_put(skb, CAPI_DISCONNECT_B3_IND_BASELEN));
+		kfree(b3cmsg);
+	}
+
+	/* trigger hangup, causing eventual DISCONNECT_IND */
+	/* ToDo: skip if DISCONNECT_IND already sent */
+	if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
+		dev_err(cs->dev, "%s: out of memory\n", __func__);
+		return CAPI_MSGOSRESOURCEERR;
+	}
+	gig_dbg(DEBUG_CMD, "scheduling HUP");
+	gigaset_schedule_event(cs);
+	return CapiSuccess;
+}
+
+/*
+ * process CONNECT_RESP message
+ * checks protocol parameters and queues an ACCEPT or HUP event
+ * return value: CAPI result code, CAPI_MSGOSRESOURCEERR if out of memory
+ */
+static int do_connect_resp(struct cardstate *cs, _cmsg *cmsg,
+			   struct gigaset_capi_appl *ap)
+{
+	struct bc_state *bcs;
+	struct gigaset_capi_appl *oap;
+	int channel;
+
+	/* extract and check channel number from PLCI */
+	channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
+	if (!channel || channel > cs->channels) {
+		dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+			   "CAPI_CONNECT_RESP", "PLCI", cmsg->adr.adrPLCI);
+		return CapiIllContrPlciNcci;
+	}
+	bcs = cs->bcs + channel - 1;
+
+	switch (cmsg->Reject) {
+	case 0:		/* Accept */
+		/* drop all competing applications, keep only this one */
+		for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
+			if (oap != ap)
+				send_disconnect_ind(bcs, oap,
+					CapiCallGivenToOtherApplication);
+		ap->bcnext = NULL;
+		bcs->ap = ap;
+		bcs->chstate |= CHS_NOTIFY_LL;
+
+		/* check/encode B channel protocol */
+		if (cmsg->BProtocol == CAPI_DEFAULT) {
+			bcs->proto2 = L2_HDLC;
+			dev_warn(cs->dev,
+		"B2 Protocol X.75 SLP unsupported, using Transparent\n");
+		} else {
+			switch (cmsg->B1protocol) {
+			case 0:
+				bcs->proto2 = L2_HDLC;
+				break;
+			case 1:
+				bcs->proto2 = L2_BITSYNC;
+				break;
+			default:
+				dev_warn(cs->dev,
+			"B1 Protocol %u unsupported, using Transparent\n",
+					 cmsg->B1protocol);
+				bcs->proto2 = L2_BITSYNC;
+			}
+			if (cmsg->B2protocol != 1)
+				dev_warn(cs->dev,
+			"B2 Protocol %u unsupported, using Transparent\n",
+					 cmsg->B2protocol);
+			if (cmsg->B3protocol != 0)
+				dev_warn(cs->dev,
+			"B3 Protocol %u unsupported, using Transparent\n",
+					 cmsg->B3protocol);
+			ignore_cstruct_param(cs, cmsg->B1configuration,
+					"CONNECT_RESP", "B1 Configuration");
+			ignore_cstruct_param(cs, cmsg->B2configuration,
+					"CONNECT_RESP", "B2 Configuration");
+			ignore_cstruct_param(cs, cmsg->B3configuration,
+					"CONNECT_RESP", "B3 Configuration");
+		}
+
+		/* ToDo: check/encode remaining parameters */
+		ignore_cstruct_param(cs, cmsg->ConnectedNumber,
+					"CONNECT_RESP", "Connected Number");
+		ignore_cstruct_param(cs, cmsg->ConnectedSubaddress,
+					"CONNECT_RESP", "Connected Subaddress");
+		ignore_cstruct_param(cs, cmsg->LLC,
+					"CONNECT_RESP", "LLC");
+		ignore_cmstruct_param(cs, cmsg->AdditionalInfo,
+					"CONNECT_RESP", "Additional Info");
+
+		/* Accept call */
+		if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
+				       EV_ACCEPT, NULL, 0, NULL))
+			return CAPI_MSGOSRESOURCEERR;
+		gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
+		gigaset_schedule_event(cs);
+		return CAPI_NOERROR;
+
+	case 1:			/* Ignore */
+		/* send DISCONNECT_IND to this application */
+		send_disconnect_ind(bcs, ap, 0);
+
+		/* remove it from the list of listening apps */
+		if (bcs->ap == ap) {
+			if ((bcs->ap = ap->bcnext) == NULL)
+				/* last one: stop ev-layer hupD notifications */
+				bcs->chstate &= ~CHS_NOTIFY_LL;
+			return CAPI_NOERROR;
+		} else {
+			for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
+				if (oap->bcnext == ap) {
+					oap->bcnext = oap->bcnext->bcnext;
+					return CAPI_NOERROR;
+				}
+		}
+		dev_err(cs->dev, "%s: application %u not found", __func__,
+			ap->id);
+		/* don't bother our caller */
+		return CAPI_NOERROR;
+
+	default:		/* Reject */
+		/* drop all competing applications, keep only this one */
+		for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
+			if (oap != ap)
+				send_disconnect_ind(bcs, oap,
+					CapiCallGivenToOtherApplication);
+		ap->bcnext = NULL;
+		bcs->ap = ap;
+
+		/* reject call - will trigger DISCONNECT_IND for this app */
+		dev_info(cs->dev, "%s: Reject=%x\n",
+			 "CAPI_CONNECT_RESP", cmsg->Reject);
+		if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
+				       EV_HUP, NULL, 0, NULL))
+			return CAPI_MSGOSRESOURCEERR;
+		gig_dbg(DEBUG_CMD, "scheduling HUP");
+		gigaset_schedule_event(cs);
+		return CAPI_NOERROR;
+	}
+}
+
+/*
+ * send CAPI message
+ * Return value: CAPI error code
+ * Note: capidrv (and probably others, too) only uses the return value to
+ * decide whether it has to free the skb (only if result != CAPI_NOERROR (0))
+ */
+static u16 gigaset_send_message(struct capi_ctr *ctr, struct sk_buff *skb)
+{
+	struct gigaset_capi_ctr *iif
+		= container_of(ctr, struct gigaset_capi_ctr, ctr);
+	struct cardstate *cs = ctr->driverdata;
+	struct bc_state *bcs = NULL;
+	struct gigaset_capi_appl *ap;
+	u16 applid = CAPIMSG_APPID(skb->data);
+	int channel;
+	u16 len;
+	u16 result;
+
+	/* retrieve application data structure */
+	if ((ap = get_appl(iif, applid)) == NULL) {
+		dev_notice(cs->dev, "%s: application %u not registered\n",
+			   __func__, applid);
+		return CAPI_ILLAPPNR;
+	}
+
+	/* serialize */
+	mutex_lock(&iif->ctr_mtx);
+
+	/* ToDo: streamline DATA_B3 case (avoid _cmsg overhead) */
+
+	/* decode message */
+	capi_message2cmsg(&iif->acmsg, skb->data);
+	dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+
+	switch (CAPIMSG_CMD(skb->data)) {
+	case CAPI_LISTEN_REQ:
+		/* store listening parameters */
+		ap->listenInfoMask = iif->acmsg.InfoMask;
+		ap->listenCIPmask = iif->acmsg.CIPmask;
+		iif->acmsg.Info = CapiSuccess;
+		goto send_conf;
+
+	case CAPI_ALERT_REQ:
+		/* nothing to do - Gigaset base always sends ALERT */
+		ignore_cmstruct_param(cs, iif->acmsg.AdditionalInfo,
+					"ALERT_REQ", "Additional Info");
+		iif->acmsg.Info = CapiAlertAlreadySent;
+		goto send_conf;
+
+	case CAPI_CONNECT_REQ:
+		/* generate DIAL event */
+		result = do_connect_req(cs, &iif->acmsg, ap);
+		if (result == CAPI_MSGOSRESOURCEERR)
+			break;
+		iif->acmsg.Info = result;
+		goto send_conf;
+
+	case CAPI_INFO_REQ:
+		/*
+		 * ToDo: support overlap sending
+		 * (requires ev-layer state machine extension to generate
+		 * additional ATD commands)
+		 */
+		ignore_cstruct_param(cs, iif->acmsg.CalledPartyNumber,
+					"INFO_REQ", "Called party number");
+
+		/* ToDo: process parameter: Additional info */
+		ignore_cmstruct_param(cs, iif->acmsg.AdditionalInfo,
+					"INFO_REQ", "Additional Info");
+
+		/* unsupported */
+		iif->acmsg.Info = CapiMessageNotSupportedInCurrentState;
+		goto send_conf;
+
+	case CAPI_SELECT_B_PROTOCOL_REQ:
+		/* unsupported */
+		iif->acmsg.Info = CapiMessageNotSupportedInCurrentState;
+		goto send_conf;
+
+	case CAPI_DISCONNECT_REQ:
+		/* schedule EV_HUP event */
+		result = do_disconnect_req(cs, iif, ap);
+		if (result == CAPI_MSGOSRESOURCEERR ||
+		    result == CapiIllContrPlciNcci)
+			break;
+		iif->acmsg.Info = result;
+		goto send_conf;
+
+	case CAPI_CONNECT_B3_REQ:
+		/* extract and check channel number from PLCI */
+		channel = (iif->acmsg.adr.adrPLCI >> 8) & 0xff;
+		if (!channel || channel > cs->channels) {
+			dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+				   "CAPI_CONNECT_B3_REQ", "PLCI",
+				   iif->acmsg.adr.adrPLCI);
+			result = CapiIllContrPlciNcci;
+			break;
+		}
+
+		/* reject if logical connection already active */
+		if (iif->b3conn[channel-1]) {
+			iif->acmsg.Info = CapiNoNcciAvailable;
+			goto send_conf;
+		}
+
+		/* mark logical connection active */
+		iif->b3conn[channel-1] = 1;
+
+		/* build NCCI: always 1 (one B3 connection only) */
+		iif->acmsg.adr.adrNCCI |= 1 << 16;
+
+		/* NCPI parameter: not applicable for B3 Transparent */
+		ignore_cstruct_param(cs, iif->acmsg.NCPI,
+					"CONNECT_B3_REQ", "NCPI");
+		iif->acmsg.Info = iif->acmsg.NCPI ?
+			CapiNcpiNotSupportedByProtocol : CapiSuccess;
+		goto send_conf;
+
+	case CAPI_DISCONNECT_B3_REQ:
+		/* extract and check channel number and NCCI */
+		channel = (iif->acmsg.adr.adrNCCI >> 8) & 0xff;
+		if (!channel || channel > cs->channels ||
+		    ((iif->acmsg.adr.adrNCCI >> 16) & 0xffff) != 1) {
+			dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+				   "CAPI_DISCONNECT_B3_REQ", "NCCI",
+				   iif->acmsg.adr.adrNCCI);
+			result = CapiIllContrPlciNcci;
+			break;
+		}
+
+		/* reject if logical connection not active */
+		if (!iif->b3conn[channel-1]) {
+			iif->acmsg.Info = CapiMessageNotSupportedInCurrentState;
+			goto send_conf;
+		}
+
+		/* trigger hangup, causing eventual DISCONNECT_B3_IND */
+		if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
+				       EV_HUP, NULL, 0, NULL)) {
+			dev_err(cs->dev, "%s: out of memory\n", __func__);
+			result = CAPI_MSGOSRESOURCEERR;
+			break;
+		}
+		gig_dbg(DEBUG_CMD, "scheduling HUP");
+		gigaset_schedule_event(cs);
+
+		/* NCPI parameter: not applicable for B3 Transparent */
+		ignore_cstruct_param(cs, iif->acmsg.NCPI,
+					"DISCONNECT_B3_REQ", "NCPI");
+		iif->acmsg.Info = iif->acmsg.NCPI ?
+			CapiNcpiNotSupportedByProtocol : CapiSuccess;
+		goto send_conf;
+
+	case CAPI_DATA_B3_REQ:
+		/* extract and check channel number and NCCI */
+		channel = (iif->acmsg.adr.adrNCCI >> 8) & 0xff;
+		if (!channel || channel > cs->channels ||
+		    ((iif->acmsg.adr.adrNCCI >> 16) & 0xffff) != 1) {
+			dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+				   "CAPI_DATA_B3_REQ", "NCCI",
+				   iif->acmsg.adr.adrNCCI);
+			result = CapiIllContrPlciNcci;
+			break;
+		}
+		bcs = &cs->bcs[channel-1];
+
+		/* reject if logical connection not active */
+		if (!iif->b3conn[channel-1]) {
+			iif->acmsg.Info = CapiMessageNotSupportedInCurrentState;
+			goto send_conf;
+		}
+
+		/* check parameters */
+		len = iif->acmsg.DataLength;
+		if (CAPIMSG_LEN(skb->data) != CAPI_DATA_B3_REQ_LEN)
+			dev_notice(cs->dev, "%s: unexpected length %d\n",
+				   "CAPI_DATA_B3_REQ", CAPIMSG_LEN(skb->data));
+		if (CAPI_DATA_B3_REQ_LEN + len != skb->len)
+			dev_notice(cs->dev, "%s: length mismatch (%d+%d!=%d)\n",
+				   "CAPI_DATA_B3_REQ",
+				   CAPI_DATA_B3_REQ_LEN, len, skb->len);
+		if (CAPI_DATA_B3_REQ_LEN + len > skb->len) {
+			/* message too short for announced data length */
+			result = CapiIllMessageParmCoding; /* ? */
+			break;
+		}
+		if (iif->acmsg.Flags & CAPI_FLAGS_RESERVED) {
+			result = CapiIllMessageParmCoding;
+			break;
+		}
+		gig_dbg(DEBUG_LLDATA,
+			"Receiving data from LL (ch: %d, flg: %x, sz: %d)",
+			channel, iif->acmsg.Flags, len);
+
+		/*
+		 * pull CAPI message from skb,
+		 * pass payload data to device-specific module
+		 * CAPI message will be preserved in headroom
+		 */
+		skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+		if (cs->ops->send_skb(bcs, skb) < 0)
+			result = CAPI_MSGOSRESOURCEERR;
+		else
+			result = CAPI_NOERROR;
+		/* DATA_B3_CONF reply will be sent by gigaset_skb_sent() */
+
+		/*
+		 * ToDo: send DATA_B3_CONF immediately
+		 * if Delivery Confirmation flag not set?
+		 */
+		break;
+
+send_conf:
+		/* create reply message with previously set result */
+		capi_cmsg_answer(&iif->acmsg);
+
+		/*
+		 * _CONF replies always only have NCCI and Info parameters
+		 * so they'll fit into the _REQ message skb
+		 */
+		capi_cmsg2message(&iif->acmsg, skb->data);
+		__skb_trim(skb, CAPI_STDCONF_LEN);
+		dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+		capi_ctr_handle_message(ctr, applid, skb);
+		result = CAPI_NOERROR;
+		break;
+
+	case CAPI_CONNECT_RESP:
+		/* generate ACCEPT/HUP event */
+		result = do_connect_resp(cs, &iif->acmsg, ap);
+		if (result == CAPI_NOERROR)
+			dev_kfree_skb(skb);
+		break;
+
+	case CAPI_CONNECT_B3_RESP:
+		/* extract and check channel number and NCCI */
+		channel = (iif->acmsg.adr.adrNCCI >> 8) & 0xff;
+		if (!channel || channel > cs->channels ||
+		    ((iif->acmsg.adr.adrNCCI >> 16) & 0xffff) != 1) {
+			dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+				   "CAPI_CONNECT_B3_RESP", "NCCI",
+				   iif->acmsg.adr.adrNCCI);
+			result = CapiIllContrPlciNcci;
+			break;
+		}
+		bcs = &cs->bcs[channel-1];
+
+		if (iif->acmsg.Reject) {
+			/* Reject: clear B3 connect received flag */
+			iif->b3conn[bcs->channel] = 0;
+
+			/* trigger hangup, causing eventual DISCONNECT_IND */
+			if (!gigaset_add_event(cs, &bcs->at_state,
+					       EV_HUP, NULL, 0, NULL)) {
+				dev_err(cs->dev, "%s: out of memory\n",
+					__func__);
+				result = CAPI_MSGOSRESOURCEERR;
+				break;
+			}
+			gig_dbg(DEBUG_CMD, "scheduling HUP");
+			gigaset_schedule_event(cs);
+
+			/* emit DISCONNECT_B3_IND */
+			capi_cmsg_header(&iif->acmsg, ap->id,
+					 CAPI_DISCONNECT_B3, CAPI_IND,
+					 ap->nextMessageNumber++,
+					 iif->ctr.cnr |
+					 ((bcs->channel + 1) << 8) |
+					 (1 << 16));
+
+			/*
+			 * message is shorter than the received _RESP message,
+			 * reuse skb
+			 */
+			capi_cmsg2message(&iif->acmsg, skb->data);
+			__skb_trim(skb, CAPI_DISCONNECT_B3_IND_BASELEN);
+		} else {
+			/*
+			 * Accept: emit CONNECT_B3_ACTIVE_IND immediately, as
+			 * we only send CONNECT_B3_IND if the B channel is up
+			 */
+			capi_cmsg_header(&iif->acmsg, ap->id,
+					 CAPI_CONNECT_B3_ACTIVE, CAPI_IND,
+					 ap->nextMessageNumber++,
+					 iif->ctr.cnr |
+					 ((bcs->channel + 1) << 8) |
+					 (1 << 16));
+
+			/*
+			 * message is shorter than the received _RESP message,
+			 * reuse skb
+			 */
+			capi_cmsg2message(&iif->acmsg, skb->data);
+			__skb_trim(skb, CAPI_CONNECT_B3_ACTIVE_IND_BASELEN);
+		}
+
+		dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+		capi_ctr_handle_message(ctr, applid, skb);
+		result = CAPI_NOERROR;
+		break;
+
+	case CAPI_CONNECT_ACTIVE_RESP:
+	case CAPI_CONNECT_B3_ACTIVE_RESP:
+	case CAPI_CONNECT_B3_T90_ACTIVE_RESP:
+	case CAPI_DISCONNECT_RESP:
+	case CAPI_INFO_RESP:
+	case CAPI_DISCONNECT_B3_RESP:
+	case CAPI_DATA_B3_RESP:
+		/* nothing to do */
+		dev_kfree_skb(skb);
+		result = CAPI_NOERROR;
+		break;
+
+	/* ToDo */
+	case CAPI_RESET_B3_REQ:
+	case CAPI_RESET_B3_RESP:
+	case CAPI_FACILITY_REQ:
+	case CAPI_FACILITY_RESP:
+	case CAPI_MANUFACTURER_REQ:
+	case CAPI_MANUFACTURER_RESP:
+		/* not yet implemented */
+	default:
+		/* unknown command */
+		dev_notice(cs->dev, "unsupported CAPI message: %s\n",
+			   capi_cmd2str(iif->acmsg.Command,
+					iif->acmsg.Subcommand));
+		result = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+	}
+
+	mutex_unlock(&iif->ctr_mtx);
+	return result;
+}
+
+/**
+ * gigaset_procinfo() - build single line description for controller
+ * @ctr:	controller descriptor structure.
+ *
+ * Return value: pointer to generated string (null terminated)
+ */
+static char *gigaset_procinfo(struct capi_ctr *ctr)
+{
+	return ctr->name;	/* ToDo: more? */
+}
+
+/**
+ * gigaset_ctr_read_proc() - build controller proc file entry
+ * @page:	buffer of PAGE_SIZE bytes for receiving the entry.
+ * @start:	unused.
+ * @off:	unused.
+ * @count:	unused.
+ * @eof:	unused.
+ * @ctr:	controller descriptor structure.
+ *
+ * Return value: length of generated entry
+ */
+static int gigaset_ctr_read_proc(char *page, char **start, off_t off,
+			  int count, int *eof, struct capi_ctr *ctr)
+{
+	struct cardstate *cs = ctr->driverdata;
+	char *s;
+	int i;
+	int len = 0;
+	len += sprintf(page+len, "%-16s %s\n", "name", ctr->name);
+	len += sprintf(page+len, "%-16s %s %s\n", "dev",
+			dev_driver_string(cs->dev), dev_name(cs->dev));
+	len += sprintf(page+len, "%-16s %d\n", "id", cs->myid);
+	if (cs->gotfwver)
+		len += sprintf(page+len, "%-16s %d.%d.%d.%d\n", "firmware",
+			cs->fwver[0], cs->fwver[1], cs->fwver[2], cs->fwver[3]);
+	len += sprintf(page+len, "%-16s %d\n", "channels",
+			cs->channels);
+	len += sprintf(page+len, "%-16s %s\n", "onechannel",
+			cs->onechannel ? "yes" : "no");
+
+	switch (cs->mode) {
+	case M_UNKNOWN:
+		s = "unknown";
+		break;
+	case M_CONFIG:
+		s = "config";
+		break;
+	case M_UNIMODEM:
+		s = "Unimodem";
+		break;
+	case M_CID:
+		s = "CID";
+		break;
+	default:
+		s = "??";
+	}
+	len += sprintf(page+len, "%-16s %s\n", "mode", s);
+
+	switch (cs->mstate) {
+	case MS_UNINITIALIZED:
+		s = "uninitialized";
+		break;
+	case MS_INIT:
+		s = "init";
+		break;
+	case MS_LOCKED:
+		s = "locked";
+		break;
+	case MS_SHUTDOWN:
+		s = "shutdown";
+		break;
+	case MS_RECOVER:
+		s = "recover";
+		break;
+	case MS_READY:
+		s = "ready";
+		break;
+	default:
+		s = "??";
+	}
+	len += sprintf(page+len, "%-16s %s\n", "mstate", s);
+
+	len += sprintf(page+len, "%-16s %s\n", "running",
+			cs->running ? "yes" : "no");
+	len += sprintf(page+len, "%-16s %s\n", "connected",
+			cs->connected ? "yes" : "no");
+	len += sprintf(page+len, "%-16s %s\n", "isdn_up",
+			cs->isdn_up ? "yes" : "no");
+	len += sprintf(page+len, "%-16s %s\n", "cidmode",
+			cs->cidmode ? "yes" : "no");
+
+	for (i = 0; i < cs->channels; i++) {
+		len += sprintf(page+len, "[%d]%-13s %d\n", i, "corrupted",
+				cs->bcs[i].corrupted);
+		len += sprintf(page+len, "[%d]%-13s %d\n", i, "trans_down",
+				cs->bcs[i].trans_down);
+		len += sprintf(page+len, "[%d]%-13s %d\n", i, "trans_up",
+				cs->bcs[i].trans_up);
+		len += sprintf(page+len, "[%d]%-13s %d\n", i, "chstate",
+				cs->bcs[i].chstate);
+		switch (cs->bcs[i].proto2) {
+		case L2_BITSYNC:
+			s = "bitsync";
+			break;
+		case L2_HDLC:
+			s = "HDLC";
+			break;
+		case L2_VOICE:
+			s = "voice";
+			break;
+		default:
+			s = "??";
+		}
+		len += sprintf(page+len, "[%d]%-13s %s\n", i, "proto2", s);
+	}
+	return len;
+}
+
+
+static struct capi_driver capi_driver_gigaset = {
+	.name		= "gigaset",
+	.revision	= "1.0",
+};
+
+/**
+ * gigaset_isdn_register() - register to LL
+ * @cs:		device descriptor structure.
+ * @isdnid:	device name.
+ *
+ * Called by main module to register the device with the LL.
+ *
+ * Return value: 1 for success, 0 for failure
+ */
+int gigaset_isdn_register(struct cardstate *cs, const char *isdnid)
+{
+	struct gigaset_capi_ctr *iif;
+	int i, rc;
+
+	pr_info("Kernel CAPI interface\n");
+
+	if ((iif = kmalloc(sizeof(*iif), GFP_KERNEL)) == NULL) {
+		pr_err("%s: out of memory\n", __func__);
+		return 0;
+	}
+
+	/* register driver with CAPI (ToDo: what for?) */
+	register_capi_driver(&capi_driver_gigaset);
+
+	/* prepare controller structure */
+	iif->ctr.owner         = THIS_MODULE;
+	iif->ctr.driverdata    = cs;
+	strncpy(iif->ctr.name, isdnid, sizeof(iif->ctr.name));
+	iif->ctr.driver_name   = "gigaset";
+	iif->ctr.load_firmware = gigaset_load_firmware;
+	iif->ctr.reset_ctr     = gigaset_reset_ctr;
+	iif->ctr.register_appl = gigaset_register_appl;
+	iif->ctr.release_appl  = gigaset_release_appl;
+	iif->ctr.send_message  = gigaset_send_message;
+	iif->ctr.procinfo      = gigaset_procinfo;
+	iif->ctr.ctr_read_proc = gigaset_ctr_read_proc;
+	INIT_LIST_HEAD(&iif->appls);
+	mutex_init(&iif->ctr_mtx);
+	for (i = 0; i < BAS_CHANNELS; i++)
+		iif->b3conn[i] = 0;
+
+	/* register controller with CAPI */
+	rc = attach_capi_ctr(&iif->ctr);
+	if (rc) {
+		pr_err("attach_capi_ctr failed (%d)\n", rc);
+		unregister_capi_driver(&capi_driver_gigaset);
+		kfree(iif);
+		return 0;
+	}
+
+	cs->iif = iif;
+	cs->hw_hdr_len = CAPI_DATA_B3_REQ_LEN;
+	return 1;
+}
+
+/**
+ * gigaset_isdn_unregister() - unregister from LL
+ * @cs:		device descriptor structure.
+ *
+ * Called by main module to unregister the device from the LL.
+ */
+void gigaset_isdn_unregister(struct cardstate *cs)
+{
+	struct gigaset_capi_ctr *iif = cs->iif;
+
+	detach_capi_ctr(&iif->ctr);
+	kfree(iif);
+	cs->iif = NULL;
+	unregister_capi_driver(&capi_driver_gigaset);
+}
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index a2847c2..e72091c 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -207,6 +207,25 @@ int gigaset_get_channel(struct bc_state *bcs)
 	return 1;
 }
 
+struct bc_state *gigaset_get_free_channel(struct cardstate *cs)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	for (i = 0; i < cs->channels; ++i)
+		if (!cs->bcs[i].use_count) {
+			++cs->bcs[i].use_count;
+			cs->bcs[i].busy = 1;
+			spin_unlock_irqrestore(&cs->lock, flags);
+			gig_dbg(DEBUG_ANY, "allocated channel %d", i);
+			return cs->bcs + i;
+		}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	gig_dbg(DEBUG_ANY, "no free channel");
+	return NULL;
+}
+
 void gigaset_free_channel(struct bc_state *bcs)
 {
 	unsigned long flags;
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index ad5ad7b..2d6dab8 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -291,21 +291,23 @@ struct reply_t gigaset_tab_cid[] =
 	{RSP_OK,      602,602, -1,                603, 5, {ACT_CMD+AT_PROTO}},
 	{RSP_OK,      603,603, -1,                604, 5, {ACT_CMD+AT_TYPE}},
 	{RSP_OK,      604,604, -1,                605, 5, {ACT_CMD+AT_MSN}},
-	{RSP_OK,      605,605, -1,                606, 5, {ACT_CMD+AT_ISO}},
-	{RSP_NULL,    605,605, -1,                606, 5, {ACT_CMD+AT_ISO}},
-	{RSP_OK,      606,606, -1,                607, 5, {0}, "+VLS=17\r"},
-	{RSP_OK,      607,607, -1,                608,-1},
-	{RSP_ZSAU,    608,608,ZSAU_PROCEEDING,    609, 5, {ACT_CMD+AT_DIAL}},
-	{RSP_OK,      609,609, -1,                650, 0, {ACT_DIALING}},
-
-	{RSP_ERROR,   601,609, -1,                  0, 0, {ACT_ABORTDIAL}},
-	{EV_TIMEOUT,  601,609, -1,                  0, 0, {ACT_ABORTDIAL}},
+	{RSP_NULL,    605,605, -1,                606, 5, {ACT_CMD+AT_CLIP}},
+	{RSP_OK,      605,605, -1,                606, 5, {ACT_CMD+AT_CLIP}},
+	{RSP_NULL,    606,606, -1,                607, 5, {ACT_CMD+AT_ISO}},
+	{RSP_OK,      606,606, -1,                607, 5, {ACT_CMD+AT_ISO}},
+	{RSP_OK,      607,607, -1,                608, 5, {0}, "+VLS=17\r"},
+	{RSP_OK,      608,608, -1,                609,-1},
+	{RSP_ZSAU,    609,609,ZSAU_PROCEEDING,    610, 5, {ACT_CMD+AT_DIAL}},
+	{RSP_OK,      610,610, -1,                650, 0, {ACT_DIALING}},
+
+	{RSP_ERROR,   601,610, -1,                  0, 0, {ACT_ABORTDIAL}},
+	{EV_TIMEOUT,  601,610, -1,                  0, 0, {ACT_ABORTDIAL}},
 
 	/* optional dialing responses */
 	{EV_BC_OPEN,  650,650, -1,                651,-1},
-	{RSP_ZVLS,    608,651, 17,                 -1,-1, {ACT_DEBUG}},
-	{RSP_ZCTP,    609,651, -1,                 -1,-1, {ACT_DEBUG}},
-	{RSP_ZCPN,    609,651, -1,                 -1,-1, {ACT_DEBUG}},
+	{RSP_ZVLS,    609,651, 17,                 -1,-1, {ACT_DEBUG}},
+	{RSP_ZCTP,    610,651, -1,                 -1,-1, {ACT_DEBUG}},
+	{RSP_ZCPN,    610,651, -1,                 -1,-1, {ACT_DEBUG}},
 	{RSP_ZSAU,    650,651,ZSAU_CALL_DELIVERED, -1,-1, {ACT_DEBUG}},
 
 	/* connect */
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index 8777a9c..4458796 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -191,7 +191,9 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
 #define AT_PROTO	4
 #define AT_TYPE		5
 #define AT_HLC		6
-#define AT_NUM		7
+#define AT_CLIP		7
+/* total number */
+#define AT_NUM		8
 
 /* variables in struct at_state_t */
 #define VAR_ZSAU	0
@@ -412,6 +414,8 @@ struct bc_state {
 		struct usb_bc_state *usb;	/* usb hardware driver (m105) */
 		struct bas_bc_state *bas;	/* usb hardware driver (base) */
 	} hw;
+
+	void *ap;			/* LL application structure */
 };
 
 struct cardstate {
@@ -725,6 +729,7 @@ void gigaset_bcs_reinit(struct bc_state *bcs);
 void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
 		     struct cardstate *cs, int cid);
 int gigaset_get_channel(struct bc_state *bcs);
+struct bc_state *gigaset_get_free_channel(struct cardstate *cs);
 void gigaset_free_channel(struct bc_state *bcs);
 int gigaset_get_channels(struct cardstate *cs);
 void gigaset_free_channels(struct cardstate *cs);

-- 
1.6.2.1.214.ge986c

^ permalink raw reply related

* Re: [RFC][PATCH] smc91x: let smc91x work well with netpoll
From: Nicolas Pitre @ 2009-08-21 17:13 UTC (permalink / raw)
  To: DDD; +Cc: Matt Mackall, David Miller, lkml, netdev, Jason Wessel
In-Reply-To: <1250862005.24178.28.camel@dengdd-desktop>

On Fri, 21 Aug 2009, DDD wrote:

> This patch changes too much and I didn't have the environment to test, so it is unverified patch.
> That's why I separate it from my previous patch, and send it solely as a RFC patch.
> 
> 
> Hi Nicolas, 
> Given that you are the maintainer of "smc91x" driver since 2004. Could you say somethings about this
> patch? Your input on this patch is greatly appreciated. :-)

Looks fine to me.  The most significant changes affect usage of this 
driver on a SMP system.  I'm afraid those might be hard to find now 
(this network chip is already a strange piece of hardware, and I knew 
about only one platform with it being SMP), so the best I can do is 
review your patch only.

Which I now did and you can add my:

Acked-by: Nicolas Pitre <nico@cam.org>


> Thank you very much,
> Dongdong
> 
> 
> Patch Note:
> @@ -520,21 +522,21
> -       local_irq_disable();                                        \
> +       local_irq_save(flags);                                      \
>         __ret = spin_trylock(lock);                                 \
>         if (!__ret)                                                 \
> -               local_irq_enable();                                 \
> +               local_irq_restore(flags);                           \
> 
> Here, for "__ret = spin_trylock(lock)", I didn't use
> spin_trylock_irqsave() to replace spin_trylock(), because
> the current irq state have got by local_irq_save(flags).
> 
> 
> 
> ---
>  drivers/net/smc91x.c |   40 ++++++++++++++++++++++------------------
>  1 files changed, 22 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c
> index 1c70e99..7cabea1 100644
> --- a/drivers/net/smc91x.c
> +++ b/drivers/net/smc91x.c
> @@ -196,21 +196,23 @@ static void PRINT_PKT(u_char *buf, int length)
>  /* this enables an interrupt in the interrupt mask register */
>  #define SMC_ENABLE_INT(lp, x) do {					\
>  	unsigned char mask;						\
> -	spin_lock_irq(&lp->lock);					\
> +	unsigned long smc_enable_flags;					\
> +	spin_lock_irqsave(&lp->lock, smc_int_flags);			\
>  	mask = SMC_GET_INT_MASK(lp);					\
>  	mask |= (x);							\
>  	SMC_SET_INT_MASK(lp, mask);					\
> -	spin_unlock_irq(&lp->lock);					\
> +	spin_unlock_irqrestore(&lp->lock, smc_int_flags);		\
>  } while (0)
>  
>  /* this disables an interrupt from the interrupt mask register */
>  #define SMC_DISABLE_INT(lp, x) do {					\
>  	unsigned char mask;						\
> -	spin_lock_irq(&lp->lock);					\
> +	unsigned long smc_disable_flags;				\
> +	spin_lock_irqsave(&lp->lock, smc_disable_flags);		\
>  	mask = SMC_GET_INT_MASK(lp);					\
>  	mask &= ~(x);							\
>  	SMC_SET_INT_MASK(lp, mask);					\
> -	spin_unlock_irq(&lp->lock);					\
> +	spin_unlock_irqrestore(&lp->lock, smc_disable_flags);		\
>  } while (0)
>  
>  /*
> @@ -520,21 +522,21 @@ static inline void  smc_rcv(struct net_device *dev)
>   * any other concurrent access and C would always interrupt B. But life
>   * isn't that easy in a SMP world...
>   */
> -#define smc_special_trylock(lock)					\
> +#define smc_special_trylock(lock, flags)				\
>  ({									\
>  	int __ret;							\
> -	local_irq_disable();						\
> +	local_irq_save(flags);						\
>  	__ret = spin_trylock(lock);					\
>  	if (!__ret)							\
> -		local_irq_enable();					\
> +		local_irq_restore(flags);				\
>  	__ret;								\
>  })
> -#define smc_special_lock(lock)		spin_lock_irq(lock)
> -#define smc_special_unlock(lock)	spin_unlock_irq(lock)
> +#define smc_special_lock(lock, flags)		spin_lock_irq(lock, flags)
> +#define smc_special_unlock(lock, flags) 	spin_unlock_irqrestore(lock, flags)
>  #else
> -#define smc_special_trylock(lock)	(1)
> -#define smc_special_lock(lock)		do { } while (0)
> -#define smc_special_unlock(lock)	do { } while (0)
> +#define smc_special_trylock(lock, flags)	(1)
> +#define smc_special_lock(lock, flags)   	do { } while (0)
> +#define smc_special_unlock(lock, flags)	do { } while (0)
>  #endif
>  
>  /*
> @@ -548,10 +550,11 @@ static void smc_hardware_send_pkt(unsigned long data)
>  	struct sk_buff *skb;
>  	unsigned int packet_no, len;
>  	unsigned char *buf;
> +	unsigned long flags;
>  
>  	DBG(3, "%s: %s\n", dev->name, __func__);
>  
> -	if (!smc_special_trylock(&lp->lock)) {
> +	if (!smc_special_trylock(&lp->lock, flags)) {
>  		netif_stop_queue(dev);
>  		tasklet_schedule(&lp->tx_task);
>  		return;
> @@ -559,7 +562,7 @@ static void smc_hardware_send_pkt(unsigned long data)
>  
>  	skb = lp->pending_tx_skb;
>  	if (unlikely(!skb)) {
> -		smc_special_unlock(&lp->lock);
> +		smc_special_unlock(&lp->lock, flags);
>  		return;
>  	}
>  	lp->pending_tx_skb = NULL;
> @@ -569,7 +572,7 @@ static void smc_hardware_send_pkt(unsigned long data)
>  		printk("%s: Memory allocation failed.\n", dev->name);
>  		dev->stats.tx_errors++;
>  		dev->stats.tx_fifo_errors++;
> -		smc_special_unlock(&lp->lock);
> +		smc_special_unlock(&lp->lock, flags);
>  		goto done;
>  	}
>  
> @@ -608,7 +611,7 @@ static void smc_hardware_send_pkt(unsigned long data)
>  
>  	/* queue the packet for TX */
>  	SMC_SET_MMU_CMD(lp, MC_ENQUEUE);
> -	smc_special_unlock(&lp->lock);
> +	smc_special_unlock(&lp->lock, flags);
>  
>  	dev->trans_start = jiffies;
>  	dev->stats.tx_packets++;
> @@ -633,6 +636,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
>  	struct smc_local *lp = netdev_priv(dev);
>  	void __iomem *ioaddr = lp->base;
>  	unsigned int numPages, poll_count, status;
> +	unsigned long flags;
>  
>  	DBG(3, "%s: %s\n", dev->name, __func__);
>  
> @@ -658,7 +662,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
>  		return 0;
>  	}
>  
> -	smc_special_lock(&lp->lock);
> +	smc_special_lock(&lp->lock, flags);
>  
>  	/* now, try to allocate the memory */
>  	SMC_SET_MMU_CMD(lp, MC_ALLOC | numPages);
> @@ -676,7 +680,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
>  		}
>     	} while (--poll_count);
>  
> -	smc_special_unlock(&lp->lock);
> +	smc_special_unlock(&lp->lock, flags);
>  
>  	lp->pending_tx_skb = skb;
>     	if (!poll_count) {
> -- 
> 1.6.0.4
> 
> 

^ permalink raw reply

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

Dave,

One more straggler for 2.6.31...without this rtl8187b devices simply
don't work. :-(

The patch is small and isolated to rtl8187.  I think we should take
it for 2.6.31.

Please let me know if there are problems!

John

---

Patch available here:

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

---

The following changes since commit 08fdef99342955a62884fb5c49ab43431a1cafbf:
  David S. Miller (1):
        Merge branch 'master' of git://git.kernel.org/.../linville/wireless-2.6

are available in the git repository at:

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

Herton Ronaldo Krzesinski (1):
      rtl8187: always set MSR_LINK_ENEDCA flag with RTL8187B

 drivers/net/wireless/rtl818x/rtl8187_dev.c |   14 ++++++++++----
 1 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index 294250e..87a9558 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -869,6 +869,9 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev)
 	priv->aifsn[3] = 3; /* AIFSN[AC_BE] */
 	rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0);
 
+	/* ENEDCA flag must always be set, transmit issues? */
+	rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA);
+
 	return 0;
 }
 
@@ -1173,13 +1176,16 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev,
 			rtl818x_iowrite8(priv, &priv->map->BSSID[i],
 					 info->bssid[i]);
 
+		if (priv->is_rtl8187b)
+			reg = RTL818X_MSR_ENEDCA;
+		else
+			reg = 0;
+
 		if (is_valid_ether_addr(info->bssid)) {
-			reg = RTL818X_MSR_INFRA;
-			if (priv->is_rtl8187b)
-				reg |= RTL818X_MSR_ENEDCA;
+			reg |= RTL818X_MSR_INFRA;
 			rtl818x_iowrite8(priv, &priv->map->MSR, reg);
 		} else {
-			reg = RTL818X_MSR_NO_LINK;
+			reg |= RTL818X_MSR_NO_LINK;
 			rtl818x_iowrite8(priv, &priv->map->MSR, reg);
 		}
 
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply related

* [RFC][PATCH][v5] ethtool: Add a new ethtool option to flash a firmware image from the specified file to a device.
From: Ajit Khaparde @ 2009-08-21 16:54 UTC (permalink / raw)
  To: davem, jgarzik, netdev

This patch adds a new "-f" option to the ethtool utility
to flash a firmware image specified by a file, to a network device.
The filename is passed to the network driver which will flash the image
on the chip using the request_firmware path.

The region "on the chip" to be flashed can be specified by an option.
It is upto the device driver to enumerate the region number passed by ethtool,
to the region to be flashed.

The default behavior is to flash all the regions on the chip.

Usage:
ethtool -f <interface name> <filename of firmware image>

ethtool -f <interface name> <filename of firmware image> [ REGION-NUMBER-TO-FLASH ]

Signed-off-by: Ajit Khaparde <ajitk@serverengines.com>
---
 ethtool-copy.h |   13 +++++++++++++
 ethtool.8      |   15 +++++++++++++++
 ethtool.c      |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 82 insertions(+), 1 deletions(-)

diff --git a/ethtool-copy.h b/ethtool-copy.h
index 3ca4e2c..66429e5 100644
--- a/ethtool-copy.h
+++ b/ethtool-copy.h
@@ -272,6 +272,18 @@ struct ethtool_perm_addr {
 	__u8	data[0];
 };
 
+#define ETHTOOL_FLASH_MAX_FILENAME	128
+enum ethtool_flash_op_type {
+	ETHTOOL_FLASH_ALL_REGIONS	= 0,
+};
+
+/* for passing firmware flashing related parameters */
+struct ethtool_flash {
+	__u32	cmd;
+	__u32	region;
+	char	data[ETHTOOL_FLASH_MAX_FILENAME];
+};
+
 /* boolean flags controlling per-interface behavior characteristics.
  * When reading, the flag indicates whether or not a certain behavior
  * is enabled/present.  When writing, the flag indicates whether
@@ -338,6 +350,7 @@ struct ethtool_rxnfc {
 #define	ETHTOOL_SRXFH		0x0000002a /* Set RX flow hash configuration */
 #define ETHTOOL_GGRO		0x0000002b /* Get GRO enable (ethtool_value) */
 #define ETHTOOL_SGRO		0x0000002c /* Set GRO enable (ethtool_value) */
+#define ETHTOOL_FLASHDEV	0x00000033 /* Flash firmware to device */
 
 /* compatibility with older code */
 #define SPARC_ETH_GSET		ETHTOOL_GSET
diff --git a/ethtool.8 b/ethtool.8
index 178f6ea..e42da55 100644
--- a/ethtool.8
+++ b/ethtool.8
@@ -207,6 +207,11 @@ ethtool \- Display or change ethernet card settings
 .I ethX
 .RB [ rx-flow-hash \ \*(FL
 .RB \ \*(HO]
+
+.B ethtool \-f|\-\-flash
+.I ethX
+.RI FILE
+.RI [ N ]
 .SH DESCRIPTION
 .BI ethtool
 is used for querying settings of an ethernet device and changing them.
@@ -501,6 +506,16 @@ Hash on bytes 2 and 3 of the Layer 4 header of the rx packet.
 Discard all packets of this flow type. When this option is set, all other options are ignored.
 .PD
 .RE
+.TP
+.B \-f \-\-flash \ FILE
+Flash firmware image from the specified file to a region on the adapter.
+By default this will flash all the regions on the adapter.
+.TP
+.B N
+A number to identify flash region where the image should be flashed.
+Default region is 0 which denotes all regions in the flash.
+.PD
+.RE
 .SH BUGS
 Not supported (in part or whole) on all ethernet drivers.
 .SH AUTHOR
diff --git a/ethtool.c b/ethtool.c
index 0110682..6b21bf2 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -77,6 +77,7 @@ static char *unparse_rxfhashopts(u64 opts);
 static int dump_rxfhash(int fhash, u64 val);
 static int do_srxclass(int fd, struct ifreq *ifr);
 static int do_grxclass(int fd, struct ifreq *ifr);
+static int do_flash(int fd, struct ifreq *ifr);
 static int send_ioctl(int fd, struct ifreq *ifr);
 
 static enum {
@@ -101,6 +102,7 @@ static enum {
 	MODE_GSTATS,
 	MODE_GNFC,
 	MODE_SNFC,
+	MODE_FLASHDEV,
 } mode = MODE_GSET;
 
 static struct option {
@@ -192,6 +194,9 @@ static struct option {
 		"classification options",
 		"		[ rx-flow-hash tcp4|udp4|ah4|sctp4|"
 		"tcp6|udp6|ah6|sctp6 p|m|v|t|s|d|f|n|r... ]\n" },
+    { "-f", "--flash", MODE_FLASHDEV, "FILENAME " "Flash firmware image "
+		"from the specified file to a region on the device",
+		"		[ REGION-NUMBER-TO-FLASH ]\n" },
     { "-h", "--help", MODE_HELP, "Show this help" },
     {}
 };
@@ -304,6 +309,9 @@ static int rx_fhash_get = 0;
 static int rx_fhash_set = 0;
 static u32 rx_fhash_val = 0;
 static int rx_fhash_changed = 0;
+static char *flash_file = NULL;
+static int flash = -1;
+static int flash_region = -1;
 static enum {
 	ONLINE=0,
 	OFFLINE,
@@ -496,7 +504,8 @@ static void parse_cmdline(int argc, char **argp)
 			    (mode == MODE_GSTATS) ||
 			    (mode == MODE_GNFC) ||
 			    (mode == MODE_SNFC) ||
-			    (mode == MODE_PHYS_ID)) {
+			    (mode == MODE_PHYS_ID) ||
+			    (mode == MODE_FLASHDEV)) {
 				devname = argp[i];
 				break;
 			}
@@ -516,6 +525,10 @@ static void parse_cmdline(int argc, char **argp)
 				if (phys_id_time < 0)
 					show_usage(1);
 				break;
+			} else if (mode == MODE_FLASHDEV) {
+				flash_file = argp[i];
+				flash = 1;
+				break;
 			}
 			/* fallthrough */
 		default:
@@ -590,6 +603,12 @@ static void parse_cmdline(int argc, char **argp)
 					show_usage(1);
 				break;
 			}
+			if (mode == MODE_FLASHDEV) {
+				flash_region = strtol(argp[i], NULL, 0);
+				if ((flash_region < 0))
+					show_usage(1);
+				break;
+			}
 			if (mode == MODE_SNFC) {
 				if (!strcmp(argp[i], "rx-flow-hash")) {
 					i += 1;
@@ -1504,6 +1523,8 @@ static int doit(void)
 		return do_grxclass(fd, &ifr);
 	} else if (mode == MODE_SNFC) {
 		return do_srxclass(fd, &ifr);
+	} else if (mode == MODE_FLASHDEV) {
+		return do_flash(fd, &ifr);
 	}
 
 	return 69;
@@ -2398,6 +2419,38 @@ static int do_grxclass(int fd, struct ifreq *ifr)
 	return 0;
 }
 
+static int do_flash(int fd, struct ifreq *ifr)
+{
+	struct ethtool_flash efl;
+	int err;
+
+	if (flash < 0) {
+		fprintf(stdout, "Missing filename argument\n");
+		show_usage(1);
+		return 98;
+	}
+
+	if (strlen(flash_file) > ETHTOOL_FLASH_MAX_FILENAME - 1) {
+		fprintf(stdout, "Filename too long\n");
+		return 99;
+	}
+
+	efl.cmd = ETHTOOL_FLASHDEV;
+	strcpy(efl.data, flash_file);
+
+	if (flash_region < 0)
+		efl.region = ETHTOOL_FLASH_ALL_REGIONS;
+	else
+		efl.region = flash_region;
+
+	ifr->ifr_data = (caddr_t)&efl;
+	err = send_ioctl(fd, ifr);
+	if (err < 0)
+		perror("Flashing failed");
+
+	return err;
+}
+
 static int send_ioctl(int fd, struct ifreq *ifr)
 {
 	return ioctl(fd, SIOCETHTOOL, ifr);
-- 
1.6.0.4


^ permalink raw reply related

* [RFC][PATCH][v5] net/ethtool: Add support for the ethtool feature to flash firmware image from a specified file.
From: Ajit Khaparde @ 2009-08-21 16:54 UTC (permalink / raw)
  To: davem, jgarzik, netdev

This patch adds support to flash a firmware image to a device using ethtool.
The driver gets the filename of the firmware image and flashes the image
using the request firmware path.

The region "on the chip" to be flashed can be specified by an option.
It is upto the device driver to enumerate the region number passed by ethtool,
to the region to be flashed.

The default behavior is to flash all the regions on the chip.

Signed-off-by: Ajit Khaparde <ajitk@serverengines.com>
---
 include/linux/ethtool.h |   14 ++++++++++++++
 net/core/ethtool.c      |   16 ++++++++++++++++
 2 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 90c4a36..15e4eb7 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -362,6 +362,18 @@ struct ethtool_rxnfc {
 	__u32				rule_locs[0];
 };
 
+#define ETHTOOL_FLASH_MAX_FILENAME	128
+enum ethtool_flash_op_type {
+	ETHTOOL_FLASH_ALL_REGIONS	= 0,
+};
+
+/* for passing firmware flashing related parameters */
+struct ethtool_flash {
+	__u32	cmd;
+	__u32	region;
+	char	data[ETHTOOL_FLASH_MAX_FILENAME];
+};
+
 #ifdef __KERNEL__
 
 struct net_device;
@@ -489,6 +501,7 @@ struct ethtool_ops {
 	int	(*get_stats_count)(struct net_device *);/* use get_sset_count */
 	int	(*get_rxnfc)(struct net_device *, struct ethtool_rxnfc *, void *);
 	int	(*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *);
+	int     (*flash_device)(struct net_device *, struct ethtool_flash *);
 };
 #endif /* __KERNEL__ */
 
@@ -545,6 +558,7 @@ struct ethtool_ops {
 #define	ETHTOOL_GRXCLSRLALL	0x00000030 /* Get all RX classification rule */
 #define	ETHTOOL_SRXCLSRLDEL	0x00000031 /* Delete RX classification rule */
 #define	ETHTOOL_SRXCLSRLINS	0x00000032 /* Insert RX classification rule */
+#define	ETHTOOL_FLASHDEV	0x00000033 /* Flash firmware to device */
 
 /* compatibility with older code */
 #define SPARC_ETH_GSET		ETHTOOL_GSET
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 44e5711..4c12ddb 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -898,6 +898,19 @@ static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
 	return actor(dev, edata.data);
 }
 
+static int ethtool_flash_device(struct net_device *dev, char __user *useraddr)
+{
+	struct ethtool_flash efl;
+
+	if (copy_from_user(&efl, useraddr, sizeof(efl)))
+		return -EFAULT;
+
+	if (!dev->ethtool_ops->flash_device)
+		return -EOPNOTSUPP;
+
+	return dev->ethtool_ops->flash_device(dev, &efl);
+}
+
 /* The main entry point in this file.  Called from net/core/dev.c */
 
 int dev_ethtool(struct net *net, struct ifreq *ifr)
@@ -1111,6 +1124,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
 	case ETHTOOL_SGRO:
 		rc = ethtool_set_gro(dev, useraddr);
 		break;
+	case ETHTOOL_FLASHDEV:
+		rc = ethtool_flash_device(dev, useraddr);
+		break;
 	default:
 		rc = -EOPNOTSUPP;
 	}
-- 
1.6.0.4


^ permalink raw reply related

* Re: [PATCH 1/2] alchemy: add au1000-eth platform device
From: Florian Fainelli @ 2009-08-21 16:53 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: Ralf Baechle, linux-mips, Manuel Lauss, David Miller, netdev
In-Reply-To: <200908181801.41602.florian@openwrt.org>

Le Tuesday 18 August 2009 18:01:40 Florian Fainelli, vous avez écrit :
> Le Tuesday 18 August 2009 16:56:37 Sergei Shtylyov, vous avez écrit :
> > Hello.
> >
> > Florian Fainelli wrote:
> > > This patch adds the board code to register a per-board au1000-eth
> > > platform device to be used wit the au1000-eth platform driver in a
> > > subsequent patch. Note that the au1000-eth driver knows about the
> > > default driver settings such that we do not need to pass any
> > > platform_data informations in most cases except db1x00.
> >
> >     Sigh, NAK...
> >     Please don't register the SoC device per board, do it in
> > alchemy/common/platfrom.c and find a way to pass the board specific
> > platform data from the board file there instead -- something like
> > arch/arm/mach-davinci/usb.c does.
>
> Ok, like I promised, this was the per-board device registration. Do you
> prefer something like this: --
> From fd75b7c7fa3c05c21122c43e43260d2785475a79 Mon Sep 17 00:00:00 2001
> From: Florian Fainelli <florian@openwrt.org>
> Date: Tue, 18 Aug 2009 17:53:21 +0200
> Subject: [PATCH] alchemy: add au1000-eth platform device (v2)
>
> This patch makes the board code register the au1000-eth
> platform device. The au1000-eth platform data can be
> overriden with the au1xxx_override_eth0_cfg function
> like it has to be done for the Bosporus board.

Sergei, any comments on that version? What about you Manuel?

>
> Changes from v1:
> - remove per-board platform.c file
> - add an override function to pass custom eth0 platform_data PHY settings
>
> Signed-off-by: Florian Fainelli <florian@openwrt.org>
> ---
> diff --git a/arch/mips/alchemy/common/platform.c
> b/arch/mips/alchemy/common/platform.c index 117f99f..559294a 100644
> --- a/arch/mips/alchemy/common/platform.c
> +++ b/arch/mips/alchemy/common/platform.c
> @@ -19,6 +19,7 @@
>  #include <asm/mach-au1x00/au1xxx.h>
>  #include <asm/mach-au1x00/au1xxx_dbdma.h>
>  #include <asm/mach-au1x00/au1100_mmc.h>
> +#include <asm/mach-au1x00/au1xxx_eth.h>
>
>  #define PORT(_base, _irq)				\
>  	{						\
> @@ -331,6 +332,76 @@ static struct platform_device pbdb_smbus_device = {
>  };
>  #endif
>
> +/* Macro to help defining the Ethernet MAC resources */
> +#define MAC_RES(_base, _enable, _irq)			\
> +	{						\
> +		.start	= CPHYSADDR(_base),		\
> +		.end	= CPHYSADDR(_base + 0xffff),	\
> +		.flags	= IORESOURCE_MEM,		\
> +	},						\
> +	{						\
> +		.start	= CPHYSADDR(_enable),		\
> +		.end	= CPHYSADDR(_enable + 0x3),	\
> +		.flags	= IORESOURCE_MEM,		\
> +	},						\
> +	{						\
> +		.start	= _irq,				\
> +		.end	= _irq,				\
> +		.flags	= IORESOURCE_IRQ		\
> +	}
> +
> +static struct resource au1xxx_eth0_resources[] = {
> +#if defined(CONFIG_SOC_AU1000)
> +	MAC_RES(AU1000_ETH0_BASE, AU1000_MAC0_ENABLE, AU1000_MAC0_DMA_INT),
> +#elif defined(CONFIG_SOC_AU1100)
> +	MAC_RES(AU1100_ETH0_BASE, AU1100_MAC0_ENABLE, AU1100_MAC0_DMA_INT),
> +#elif defined(CONFIG_SOC_AU1550)
> +	MAC_RES(AU1550_ETH0_BASE, AU1550_MAC0_ENABLE, AU1550_MAC0_DMA_INT),
> +#elif defined(CONFIG_SOC_AU1500)
> +	MAC_RES(AU1500_ETH0_BASE, AU1500_MAC0_ENABLE, AU1500_MAC0_DMA_INT),
> +#endif
> +};
> +
> +static struct resource au1xxx_eth1_resources[] = {
> +#if defined(CONFIG_SOC_AU1000)
> +	MAC_RES(AU1000_ETH1_BASE, AU1000_MAC1_ENABLE, AU1000_MAC1_DMA_INT),
> +#elif defined(CONFIG_SOC_AU1550)
> +	MAC_RES(AU1550_ETH1_BASE, AU1550_MAC1_ENABLE, AU1550_MAC1_DMA_INT),
> +#elif defined(CONFIG_SOC_AU1500)
> +	MAC_RES(AU1500_ETH1_BASE, AU1500_MAC1_ENABLE, AU1500_MAC1_DMA_INT),
> +#endif
> +};
> +
> +static struct au1000_eth_platform_data au1xxx_eth0_platform_data = {
> +	.phy1_search_mac0 = 1,
> +};
> +
> +static struct platform_device au1xxx_eth0_device = {
> +	.name		= "au1000-eth",
> +	.id		= 0,
> +	.num_resources	= ARRAY_SIZE(au1xxx_eth0_resources),
> +	.resource	= au1xxx_eth0_resources,
> +	.dev.platform_data = &au1xxx_eth0_platform_data,
> +};
> +
> +#ifndef CONFIG_SOC_AU1100
> +static struct platform_device au1xxx_eth1_device = {
> +	.name		= "au1000-eth",
> +	.id		= 1,
> +	.num_resources	= ARRAY_SIZE(au1xxx_eth1_resources),
> +	.resource	= au1xxx_eth1_resources,
> +};
> +#endif
> +
> +void __init au1xxx_override_eth0_cfg(struct au1000_eth_platform_data
> *eth_data) +{
> +	if (!eth_data)
> +		return;
> +
> +	memcpy(&au1xxx_eth0_platform_data, eth_data,
> +		sizeof(struct au1000_eth_platform_data));
> +}
> +
>  static struct platform_device *au1xxx_platform_devices[] __initdata = {
>  	&au1xx0_uart_device,
>  	&au1xxx_usb_ohci_device,
> @@ -351,17 +422,25 @@ static struct platform_device
> *au1xxx_platform_devices[] __initdata = { #ifdef SMBUS_PSC_BASE
>  	&pbdb_smbus_device,
>  #endif
> +	&au1xxx_eth0_device,
>  };
>
>  static int __init au1xxx_platform_init(void)
>  {
>  	unsigned int uartclk = get_au1x00_uart_baud_base() * 16;
> -	int i;
> +	int i, ni;
>
>  	/* Fill up uartclk. */
>  	for (i = 0; au1x00_uart_data[i].flags; i++)
>  		au1x00_uart_data[i].uartclk = uartclk;
>
> +	/* Register second MAC if enabled in pinfunc */
> +#ifndef CONFIG_SOC_AU1100
> +	ni = (int)((au_readl(SYS_PINFUNC) & (u32)(SYS_PF_NI2)) >> 4);
> +	if (!(ni + 1))
> +		platform_device_register(&au1xxx_eth1_device);
> +#endif
> +
>  	return platform_add_devices(au1xxx_platform_devices,
>  				    ARRAY_SIZE(au1xxx_platform_devices));
>  }
> diff --git a/arch/mips/alchemy/devboards/db1x00/board_setup.c
> b/arch/mips/alchemy/devboards/db1x00/board_setup.c index de30d8e..4d2d32c
> 100644
> --- a/arch/mips/alchemy/devboards/db1x00/board_setup.c
> +++ b/arch/mips/alchemy/devboards/db1x00/board_setup.c
> @@ -32,6 +32,7 @@
>
>  #include <asm/mach-au1x00/au1000.h>
>  #include <asm/mach-db1x00/db1x00.h>
> +#include <asm/mach-au1x00/au1xxx_eth.h>
>
>  #include <prom.h>
>
> @@ -134,6 +135,22 @@ void __init board_setup(void)
>  	printk(KERN_INFO "AMD Alchemy Au1100/Db1100 Board\n");
>  #endif
>  #ifdef CONFIG_MIPS_BOSPORUS
> +	struct au1000_eth_platform_data eth0_pdata;
> +
> +	/*
> +	 * Micrel/Kendin 5 port switch attached to MAC0,
> +	 * MAC0 is associated with PHY address 5 (== WAN port)
> +	 * MAC1 is not associated with any PHY, since it's connected directly
> +	 * to the switch.
> +	 * no interrupts are used
> +	 */
> +	eth0_pdata.phy1_search_mac0 = 0;
> +	eth0_pdata.phy_static_config = 1;
> +	eth0_pdata.phy_addr = 5;
> +	eth0_pdata.phy_busid = 0;
> +
> +	au1xxx_override_eth0_cfg(&eth0_pdata);
> +
>  	printk(KERN_INFO "AMD Alchemy Bosporus Board\n");
>  #endif
>  #ifdef CONFIG_MIPS_MIRAGE
> diff --git a/arch/mips/include/asm/mach-au1x00/au1xxx_eth.h
> b/arch/mips/include/asm/mach-au1x00/au1xxx_eth.h new file mode 100644
> index 0000000..876187e
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-au1x00/au1xxx_eth.h
> @@ -0,0 +1,17 @@
> +#ifndef __AU1X00_ETH_DATA_H
> +#define __AU1X00_ETH_DATA_H
> +
> +/* Platform specific PHY configuration passed to the MAC driver */
> +struct au1000_eth_platform_data {
> +	int phy_static_config;
> +	int phy_search_highest_addr;
> +	int phy1_search_mac0;
> +	int phy_addr;
> +	int phy_busid;
> +	int phy_irq;
> +};
> +
> +void __init au1xxx_override_eth0_cfg(struct au1000_eth_platform_data
> *eth_data); +
> +#endif /* __AU1X00_ETH_DATA_H */
> +



-- 
Best regards, Florian Fainelli
Email: florian@openwrt.org
Web: http://openwrt.org
IRC: [florian] on irc.freenode.net
-------------------------------

^ 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