irda: move net/irda/ to drivers/staging/irda/net/
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 27 Aug 2017 15:03:31 +0000 (17:03 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Aug 2017 23:42:56 +0000 (16:42 -0700)
It's time to get rid of IRDA.  It's long been broken, and no one seems
to use it anymore.  So move it to staging and after a while, we can
delete it from there.

To start, move the network irda core from net/irda to
drivers/staging/irda/net/

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
106 files changed:
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/irda/net/Kconfig [new file with mode: 0644]
drivers/staging/irda/net/Makefile [new file with mode: 0644]
drivers/staging/irda/net/af_irda.c [new file with mode: 0644]
drivers/staging/irda/net/discovery.c [new file with mode: 0644]
drivers/staging/irda/net/ircomm/Kconfig [new file with mode: 0644]
drivers/staging/irda/net/ircomm/Makefile [new file with mode: 0644]
drivers/staging/irda/net/ircomm/ircomm_core.c [new file with mode: 0644]
drivers/staging/irda/net/ircomm/ircomm_event.c [new file with mode: 0644]
drivers/staging/irda/net/ircomm/ircomm_lmp.c [new file with mode: 0644]
drivers/staging/irda/net/ircomm/ircomm_param.c [new file with mode: 0644]
drivers/staging/irda/net/ircomm/ircomm_ttp.c [new file with mode: 0644]
drivers/staging/irda/net/ircomm/ircomm_tty.c [new file with mode: 0644]
drivers/staging/irda/net/ircomm/ircomm_tty_attach.c [new file with mode: 0644]
drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c [new file with mode: 0644]
drivers/staging/irda/net/irda_device.c [new file with mode: 0644]
drivers/staging/irda/net/iriap.c [new file with mode: 0644]
drivers/staging/irda/net/iriap_event.c [new file with mode: 0644]
drivers/staging/irda/net/irias_object.c [new file with mode: 0644]
drivers/staging/irda/net/irlan/Kconfig [new file with mode: 0644]
drivers/staging/irda/net/irlan/Makefile [new file with mode: 0644]
drivers/staging/irda/net/irlan/irlan_client.c [new file with mode: 0644]
drivers/staging/irda/net/irlan/irlan_client_event.c [new file with mode: 0644]
drivers/staging/irda/net/irlan/irlan_common.c [new file with mode: 0644]
drivers/staging/irda/net/irlan/irlan_eth.c [new file with mode: 0644]
drivers/staging/irda/net/irlan/irlan_event.c [new file with mode: 0644]
drivers/staging/irda/net/irlan/irlan_filter.c [new file with mode: 0644]
drivers/staging/irda/net/irlan/irlan_provider.c [new file with mode: 0644]
drivers/staging/irda/net/irlan/irlan_provider_event.c [new file with mode: 0644]
drivers/staging/irda/net/irlap.c [new file with mode: 0644]
drivers/staging/irda/net/irlap_event.c [new file with mode: 0644]
drivers/staging/irda/net/irlap_frame.c [new file with mode: 0644]
drivers/staging/irda/net/irlmp.c [new file with mode: 0644]
drivers/staging/irda/net/irlmp_event.c [new file with mode: 0644]
drivers/staging/irda/net/irlmp_frame.c [new file with mode: 0644]
drivers/staging/irda/net/irmod.c [new file with mode: 0644]
drivers/staging/irda/net/irnet/Kconfig [new file with mode: 0644]
drivers/staging/irda/net/irnet/Makefile [new file with mode: 0644]
drivers/staging/irda/net/irnet/irnet.h [new file with mode: 0644]
drivers/staging/irda/net/irnet/irnet_irda.c [new file with mode: 0644]
drivers/staging/irda/net/irnet/irnet_irda.h [new file with mode: 0644]
drivers/staging/irda/net/irnet/irnet_ppp.c [new file with mode: 0644]
drivers/staging/irda/net/irnet/irnet_ppp.h [new file with mode: 0644]
drivers/staging/irda/net/irnetlink.c [new file with mode: 0644]
drivers/staging/irda/net/irproc.c [new file with mode: 0644]
drivers/staging/irda/net/irqueue.c [new file with mode: 0644]
drivers/staging/irda/net/irsysctl.c [new file with mode: 0644]
drivers/staging/irda/net/irttp.c [new file with mode: 0644]
drivers/staging/irda/net/parameters.c [new file with mode: 0644]
drivers/staging/irda/net/qos.c [new file with mode: 0644]
drivers/staging/irda/net/timer.c [new file with mode: 0644]
drivers/staging/irda/net/wrapper.c [new file with mode: 0644]
net/Kconfig
net/Makefile
net/irda/Kconfig [deleted file]
net/irda/Makefile [deleted file]
net/irda/af_irda.c [deleted file]
net/irda/discovery.c [deleted file]
net/irda/ircomm/Kconfig [deleted file]
net/irda/ircomm/Makefile [deleted file]
net/irda/ircomm/ircomm_core.c [deleted file]
net/irda/ircomm/ircomm_event.c [deleted file]
net/irda/ircomm/ircomm_lmp.c [deleted file]
net/irda/ircomm/ircomm_param.c [deleted file]
net/irda/ircomm/ircomm_ttp.c [deleted file]
net/irda/ircomm/ircomm_tty.c [deleted file]
net/irda/ircomm/ircomm_tty_attach.c [deleted file]
net/irda/ircomm/ircomm_tty_ioctl.c [deleted file]
net/irda/irda_device.c [deleted file]
net/irda/iriap.c [deleted file]
net/irda/iriap_event.c [deleted file]
net/irda/irias_object.c [deleted file]
net/irda/irlan/Kconfig [deleted file]
net/irda/irlan/Makefile [deleted file]
net/irda/irlan/irlan_client.c [deleted file]
net/irda/irlan/irlan_client_event.c [deleted file]
net/irda/irlan/irlan_common.c [deleted file]
net/irda/irlan/irlan_eth.c [deleted file]
net/irda/irlan/irlan_event.c [deleted file]
net/irda/irlan/irlan_filter.c [deleted file]
net/irda/irlan/irlan_provider.c [deleted file]
net/irda/irlan/irlan_provider_event.c [deleted file]
net/irda/irlap.c [deleted file]
net/irda/irlap_event.c [deleted file]
net/irda/irlap_frame.c [deleted file]
net/irda/irlmp.c [deleted file]
net/irda/irlmp_event.c [deleted file]
net/irda/irlmp_frame.c [deleted file]
net/irda/irmod.c [deleted file]
net/irda/irnet/Kconfig [deleted file]
net/irda/irnet/Makefile [deleted file]
net/irda/irnet/irnet.h [deleted file]
net/irda/irnet/irnet_irda.c [deleted file]
net/irda/irnet/irnet_irda.h [deleted file]
net/irda/irnet/irnet_ppp.c [deleted file]
net/irda/irnet/irnet_ppp.h [deleted file]
net/irda/irnetlink.c [deleted file]
net/irda/irproc.c [deleted file]
net/irda/irqueue.c [deleted file]
net/irda/irsysctl.c [deleted file]
net/irda/irttp.c [deleted file]
net/irda/parameters.c [deleted file]
net/irda/qos.c [deleted file]
net/irda/timer.c [deleted file]
net/irda/wrapper.c [deleted file]

index ef28a1cb64aecf5a0af0afd0d33b3313eaf26013..0c5086d878c62e1dbb97570b88f10120b385a9f3 100644 (file)
@@ -24,6 +24,8 @@ menuconfig STAGING
 
 if STAGING
 
+source "drivers/staging/irda/net/Kconfig"
+
 source "drivers/staging/wlan-ng/Kconfig"
 
 source "drivers/staging/comedi/Kconfig"
index 2918580bdb9e215bfa407d4c38b94a661069c6fa..509fbacf9a7e6eee8f662f589314a22e6bb78b13 100644 (file)
@@ -2,6 +2,7 @@
 
 obj-y                          += media/
 obj-y                          += typec/
+obj-$(CONFIG_IRDA)             += irda/net/
 obj-$(CONFIG_PRISM2_USB)       += wlan-ng/
 obj-$(CONFIG_COMEDI)           += comedi/
 obj-$(CONFIG_FB_OLPC_DCON)     += olpc_dcon/
diff --git a/drivers/staging/irda/net/Kconfig b/drivers/staging/irda/net/Kconfig
new file mode 100644 (file)
index 0000000..cd775e1
--- /dev/null
@@ -0,0 +1,96 @@
+#
+# IrDA protocol configuration
+#
+
+menuconfig IRDA
+       depends on NET && !S390
+       tristate "IrDA (infrared) subsystem support"
+       select CRC_CCITT
+       ---help---
+         Say Y here if you want to build support for the IrDA (TM) protocols.
+         The Infrared Data Associations (tm) specifies standards for wireless
+         infrared communication and is supported by most laptops and PDA's.
+
+         To use Linux support for the IrDA (tm) protocols, you will also need
+         some user-space utilities like irattach.  For more information, see
+         the file <file:Documentation/networking/irda.txt>.  You also want to
+         read the IR-HOWTO, available at
+         <http://www.tldp.org/docs.html#howto>.
+
+         If you want to exchange bits of data (vCal, vCard) with a PDA, you
+         will need to install some OBEX application, such as OpenObex :
+         <http://sourceforge.net/projects/openobex/>
+
+         To compile this support as a module, choose M here: the module will
+         be called irda.
+
+comment "IrDA protocols"
+       depends on IRDA
+
+source "drivers/staging/irda/net/irlan/Kconfig"
+
+source "drivers/staging/irda/net/irnet/Kconfig"
+
+source "drivers/staging/irda/net/ircomm/Kconfig"
+
+config IRDA_ULTRA
+       bool "Ultra (connectionless) protocol"
+       depends on IRDA
+       help
+         Say Y here to support the connectionless Ultra IRDA protocol.
+         Ultra allows to exchange data over IrDA with really simple devices
+         (watch, beacon) without the overhead of the IrDA protocol (no handshaking,
+         no management frames, simple fixed header).
+         Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1);
+
+comment "IrDA options"
+       depends on IRDA
+
+config IRDA_CACHE_LAST_LSAP
+       bool "Cache last LSAP"
+       depends on IRDA
+       help
+         Say Y here if you want IrLMP to cache the last LSAP used.  This
+         makes sense since most frames will be sent/received on the same
+         connection.  Enabling this option will save a hash-lookup per frame.
+
+         If unsure, say Y.
+
+config IRDA_FAST_RR
+       bool "Fast RRs (low latency)"
+       depends on IRDA
+       ---help---
+         Say Y here is you want IrLAP to send fast RR (Receive Ready) frames
+         when acting as a primary station.
+         Disabling this option will make latency over IrDA very bad. Enabling
+         this option will make the IrDA stack send more packet than strictly
+         necessary, thus reduce your battery life (but not that much).
+
+         Fast RR will make IrLAP send out a RR frame immediately when
+         receiving a frame if its own transmit queue is currently empty. This
+         will give a lot of speed improvement when receiving much data since
+         the secondary station will not have to wait the max. turn around
+         time (usually 500ms) before it is allowed to transmit the next time.
+         If the transmit queue of the secondary is also empty, the primary will
+         start backing-off before sending another RR frame, waiting longer
+         each time until the back-off reaches the max. turn around time.
+         This back-off increase in controlled via
+         /proc/sys/net/irda/fast_poll_increase
+
+         If unsure, say Y.
+
+config IRDA_DEBUG
+       bool "Debug information"
+       depends on IRDA
+       help
+         Say Y here if you want the IrDA subsystem to write debug information
+         to your syslog. You can change the debug level in
+         /proc/sys/net/irda/debug .
+         When this option is enabled, the IrDA also perform many extra internal
+         verifications which will usually prevent the kernel to crash in case of
+         bugs.
+
+         If unsure, say Y (since it makes it easier to find the bugs).
+
+source "drivers/net/irda/Kconfig"
+
diff --git a/drivers/staging/irda/net/Makefile b/drivers/staging/irda/net/Makefile
new file mode 100644 (file)
index 0000000..187f6c5
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Makefile for the Linux IrDA protocol layer.
+#
+
+obj-$(CONFIG_IRDA) += irda.o
+obj-$(CONFIG_IRLAN) += irlan/
+obj-$(CONFIG_IRNET) += irnet/
+obj-$(CONFIG_IRCOMM) += ircomm/
+
+irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \
+          irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \
+          irttp.o irda_device.o irias_object.o wrapper.o af_irda.o \
+         discovery.o parameters.o irnetlink.o irmod.o
+irda-$(CONFIG_PROC_FS) += irproc.o
+irda-$(CONFIG_SYSCTL) += irsysctl.o
diff --git a/drivers/staging/irda/net/af_irda.c b/drivers/staging/irda/net/af_irda.c
new file mode 100644 (file)
index 0000000..23fa7c8
--- /dev/null
@@ -0,0 +1,2695 @@
+/*********************************************************************
+ *
+ * Filename:      af_irda.c
+ * Version:       0.9
+ * Description:   IrDA sockets implementation
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun May 31 10:12:43 1998
+ * Modified at:   Sat Dec 25 21:10:23 1999
+ * Modified by:   Dag Brattli <dag@brattli.net>
+ * Sources:       af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
+ *
+ *     Copyright (c) 1999 Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1999-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *     All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *     Linux-IrDA now supports four different types of IrDA sockets:
+ *
+ *     o SOCK_STREAM:    TinyTP connections with SAR disabled. The
+ *                       max SDU size is 0 for conn. of this type
+ *     o SOCK_SEQPACKET: TinyTP connections with SAR enabled. TTP may
+ *                       fragment the messages, but will preserve
+ *                       the message boundaries
+ *     o SOCK_DGRAM:     IRDAPROTO_UNITDATA: TinyTP connections with Unitdata
+ *                       (unreliable) transfers
+ *                       IRDAPROTO_ULTRA: Connectionless and unreliable data
+ *
+ ********************************************************************/
+
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/irda.h>
+#include <linux/poll.h>
+
+#include <asm/ioctls.h>                /* TIOCOUTQ, TIOCINQ */
+#include <linux/uaccess.h>
+
+#include <net/sock.h>
+#include <net/tcp_states.h>
+
+#include <net/irda/af_irda.h>
+
+static int irda_create(struct net *net, struct socket *sock, int protocol, int kern);
+
+static const struct proto_ops irda_stream_ops;
+static const struct proto_ops irda_seqpacket_ops;
+static const struct proto_ops irda_dgram_ops;
+
+#ifdef CONFIG_IRDA_ULTRA
+static const struct proto_ops irda_ultra_ops;
+#define ULTRA_MAX_DATA 382
+#endif /* CONFIG_IRDA_ULTRA */
+
+#define IRDA_MAX_HEADER (TTP_MAX_HEADER)
+
+/*
+ * Function irda_data_indication (instance, sap, skb)
+ *
+ *    Received some data from TinyTP. Just queue it on the receive queue
+ *
+ */
+static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb)
+{
+       struct irda_sock *self;
+       struct sock *sk;
+       int err;
+
+       self = instance;
+       sk = instance;
+
+       err = sock_queue_rcv_skb(sk, skb);
+       if (err) {
+               pr_debug("%s(), error: no more mem!\n", __func__);
+               self->rx_flow = FLOW_STOP;
+
+               /* When we return error, TTP will need to requeue the skb */
+               return err;
+       }
+
+       return 0;
+}
+
+/*
+ * Function irda_disconnect_indication (instance, sap, reason, skb)
+ *
+ *    Connection has been closed. Check reason to find out why
+ *
+ */
+static void irda_disconnect_indication(void *instance, void *sap,
+                                      LM_REASON reason, struct sk_buff *skb)
+{
+       struct irda_sock *self;
+       struct sock *sk;
+
+       self = instance;
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       /* Don't care about it, but let's not leak it */
+       if(skb)
+               dev_kfree_skb(skb);
+
+       sk = instance;
+       if (sk == NULL) {
+               pr_debug("%s(%p) : BUG : sk is NULL\n",
+                        __func__, self);
+               return;
+       }
+
+       /* Prevent race conditions with irda_release() and irda_shutdown() */
+       bh_lock_sock(sk);
+       if (!sock_flag(sk, SOCK_DEAD) && sk->sk_state != TCP_CLOSE) {
+               sk->sk_state     = TCP_CLOSE;
+               sk->sk_shutdown |= SEND_SHUTDOWN;
+
+               sk->sk_state_change(sk);
+
+               /* Close our TSAP.
+                * If we leave it open, IrLMP put it back into the list of
+                * unconnected LSAPs. The problem is that any incoming request
+                * can then be matched to this socket (and it will be, because
+                * it is at the head of the list). This would prevent any
+                * listening socket waiting on the same TSAP to get those
+                * requests. Some apps forget to close sockets, or hang to it
+                * a bit too long, so we may stay in this dead state long
+                * enough to be noticed...
+                * Note : all socket function do check sk->sk_state, so we are
+                * safe...
+                * Jean II
+                */
+               if (self->tsap) {
+                       irttp_close_tsap(self->tsap);
+                       self->tsap = NULL;
+               }
+       }
+       bh_unlock_sock(sk);
+
+       /* Note : once we are there, there is not much you want to do
+        * with the socket anymore, apart from closing it.
+        * For example, bind() and connect() won't reset sk->sk_err,
+        * sk->sk_shutdown and sk->sk_flags to valid values...
+        * Jean II
+        */
+}
+
+/*
+ * Function irda_connect_confirm (instance, sap, qos, max_sdu_size, skb)
+ *
+ *    Connections has been confirmed by the remote device
+ *
+ */
+static void irda_connect_confirm(void *instance, void *sap,
+                                struct qos_info *qos,
+                                __u32 max_sdu_size, __u8 max_header_size,
+                                struct sk_buff *skb)
+{
+       struct irda_sock *self;
+       struct sock *sk;
+
+       self = instance;
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       sk = instance;
+       if (sk == NULL) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       dev_kfree_skb(skb);
+       // Should be ??? skb_queue_tail(&sk->sk_receive_queue, skb);
+
+       /* How much header space do we need to reserve */
+       self->max_header_size = max_header_size;
+
+       /* IrTTP max SDU size in transmit direction */
+       self->max_sdu_size_tx = max_sdu_size;
+
+       /* Find out what the largest chunk of data that we can transmit is */
+       switch (sk->sk_type) {
+       case SOCK_STREAM:
+               if (max_sdu_size != 0) {
+                       net_err_ratelimited("%s: max_sdu_size must be 0\n",
+                                           __func__);
+                       return;
+               }
+               self->max_data_size = irttp_get_max_seg_size(self->tsap);
+               break;
+       case SOCK_SEQPACKET:
+               if (max_sdu_size == 0) {
+                       net_err_ratelimited("%s: max_sdu_size cannot be 0\n",
+                                           __func__);
+                       return;
+               }
+               self->max_data_size = max_sdu_size;
+               break;
+       default:
+               self->max_data_size = irttp_get_max_seg_size(self->tsap);
+       }
+
+       pr_debug("%s(), max_data_size=%d\n", __func__,
+                self->max_data_size);
+
+       memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
+
+       /* We are now connected! */
+       sk->sk_state = TCP_ESTABLISHED;
+       sk->sk_state_change(sk);
+}
+
+/*
+ * Function irda_connect_indication(instance, sap, qos, max_sdu_size, userdata)
+ *
+ *    Incoming connection
+ *
+ */
+static void irda_connect_indication(void *instance, void *sap,
+                                   struct qos_info *qos, __u32 max_sdu_size,
+                                   __u8 max_header_size, struct sk_buff *skb)
+{
+       struct irda_sock *self;
+       struct sock *sk;
+
+       self = instance;
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       sk = instance;
+       if (sk == NULL) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       /* How much header space do we need to reserve */
+       self->max_header_size = max_header_size;
+
+       /* IrTTP max SDU size in transmit direction */
+       self->max_sdu_size_tx = max_sdu_size;
+
+       /* Find out what the largest chunk of data that we can transmit is */
+       switch (sk->sk_type) {
+       case SOCK_STREAM:
+               if (max_sdu_size != 0) {
+                       net_err_ratelimited("%s: max_sdu_size must be 0\n",
+                                           __func__);
+                       kfree_skb(skb);
+                       return;
+               }
+               self->max_data_size = irttp_get_max_seg_size(self->tsap);
+               break;
+       case SOCK_SEQPACKET:
+               if (max_sdu_size == 0) {
+                       net_err_ratelimited("%s: max_sdu_size cannot be 0\n",
+                                           __func__);
+                       kfree_skb(skb);
+                       return;
+               }
+               self->max_data_size = max_sdu_size;
+               break;
+       default:
+               self->max_data_size = irttp_get_max_seg_size(self->tsap);
+       }
+
+       pr_debug("%s(), max_data_size=%d\n", __func__,
+                self->max_data_size);
+
+       memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
+
+       skb_queue_tail(&sk->sk_receive_queue, skb);
+       sk->sk_state_change(sk);
+}
+
+/*
+ * Function irda_connect_response (handle)
+ *
+ *    Accept incoming connection
+ *
+ */
+static void irda_connect_response(struct irda_sock *self)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL);
+       if (skb == NULL) {
+               pr_debug("%s() Unable to allocate sk_buff!\n",
+                        __func__);
+               return;
+       }
+
+       /* Reserve space for MUX_CONTROL and LAP header */
+       skb_reserve(skb, IRDA_MAX_HEADER);
+
+       irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb);
+}
+
+/*
+ * Function irda_flow_indication (instance, sap, flow)
+ *
+ *    Used by TinyTP to tell us if it can accept more data or not
+ *
+ */
+static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
+{
+       struct irda_sock *self;
+       struct sock *sk;
+
+       self = instance;
+       sk = instance;
+       BUG_ON(sk == NULL);
+
+       switch (flow) {
+       case FLOW_STOP:
+               pr_debug("%s(), IrTTP wants us to slow down\n",
+                        __func__);
+               self->tx_flow = flow;
+               break;
+       case FLOW_START:
+               self->tx_flow = flow;
+               pr_debug("%s(), IrTTP wants us to start again\n",
+                        __func__);
+               wake_up_interruptible(sk_sleep(sk));
+               break;
+       default:
+               pr_debug("%s(), Unknown flow command!\n", __func__);
+               /* Unknown flow command, better stop */
+               self->tx_flow = flow;
+               break;
+       }
+}
+
+/*
+ * Function irda_getvalue_confirm (obj_id, value, priv)
+ *
+ *    Got answer from remote LM-IAS, just pass object to requester...
+ *
+ * Note : duplicate from above, but we need our own version that
+ * doesn't touch the dtsap_sel and save the full value structure...
+ */
+static void irda_getvalue_confirm(int result, __u16 obj_id,
+                                 struct ias_value *value, void *priv)
+{
+       struct irda_sock *self;
+
+       self = priv;
+       if (!self) {
+               net_warn_ratelimited("%s: lost myself!\n", __func__);
+               return;
+       }
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       /* We probably don't need to make any more queries */
+       iriap_close(self->iriap);
+       self->iriap = NULL;
+
+       /* Check if request succeeded */
+       if (result != IAS_SUCCESS) {
+               pr_debug("%s(), IAS query failed! (%d)\n", __func__,
+                        result);
+
+               self->errno = result;   /* We really need it later */
+
+               /* Wake up any processes waiting for result */
+               wake_up_interruptible(&self->query_wait);
+
+               return;
+       }
+
+       /* Pass the object to the caller (so the caller must delete it) */
+       self->ias_result = value;
+       self->errno = 0;
+
+       /* Wake up any processes waiting for result */
+       wake_up_interruptible(&self->query_wait);
+}
+
+/*
+ * Function irda_selective_discovery_indication (discovery)
+ *
+ *    Got a selective discovery indication from IrLMP.
+ *
+ * IrLMP is telling us that this node is new and matching our hint bit
+ * filter. Wake up any process waiting for answer...
+ */
+static void irda_selective_discovery_indication(discinfo_t *discovery,
+                                               DISCOVERY_MODE mode,
+                                               void *priv)
+{
+       struct irda_sock *self;
+
+       self = priv;
+       if (!self) {
+               net_warn_ratelimited("%s: lost myself!\n", __func__);
+               return;
+       }
+
+       /* Pass parameter to the caller */
+       self->cachedaddr = discovery->daddr;
+
+       /* Wake up process if its waiting for device to be discovered */
+       wake_up_interruptible(&self->query_wait);
+}
+
+/*
+ * Function irda_discovery_timeout (priv)
+ *
+ *    Timeout in the selective discovery process
+ *
+ * We were waiting for a node to be discovered, but nothing has come up
+ * so far. Wake up the user and tell him that we failed...
+ */
+static void irda_discovery_timeout(u_long priv)
+{
+       struct irda_sock *self;
+
+       self = (struct irda_sock *) priv;
+       BUG_ON(self == NULL);
+
+       /* Nothing for the caller */
+       self->cachelog = NULL;
+       self->cachedaddr = 0;
+       self->errno = -ETIME;
+
+       /* Wake up process if its still waiting... */
+       wake_up_interruptible(&self->query_wait);
+}
+
+/*
+ * Function irda_open_tsap (self)
+ *
+ *    Open local Transport Service Access Point (TSAP)
+ *
+ */
+static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name)
+{
+       notify_t notify;
+
+       if (self->tsap) {
+               pr_debug("%s: busy!\n", __func__);
+               return -EBUSY;
+       }
+
+       /* Initialize callbacks to be used by the IrDA stack */
+       irda_notify_init(&notify);
+       notify.connect_confirm       = irda_connect_confirm;
+       notify.connect_indication    = irda_connect_indication;
+       notify.disconnect_indication = irda_disconnect_indication;
+       notify.data_indication       = irda_data_indication;
+       notify.udata_indication      = irda_data_indication;
+       notify.flow_indication       = irda_flow_indication;
+       notify.instance = self;
+       strncpy(notify.name, name, NOTIFY_MAX_NAME);
+
+       self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT,
+                                    &notify);
+       if (self->tsap == NULL) {
+               pr_debug("%s(), Unable to allocate TSAP!\n",
+                        __func__);
+               return -ENOMEM;
+       }
+       /* Remember which TSAP selector we actually got */
+       self->stsap_sel = self->tsap->stsap_sel;
+
+       return 0;
+}
+
+/*
+ * Function irda_open_lsap (self)
+ *
+ *    Open local Link Service Access Point (LSAP). Used for opening Ultra
+ *    sockets
+ */
+#ifdef CONFIG_IRDA_ULTRA
+static int irda_open_lsap(struct irda_sock *self, int pid)
+{
+       notify_t notify;
+
+       if (self->lsap) {
+               net_warn_ratelimited("%s(), busy!\n", __func__);
+               return -EBUSY;
+       }
+
+       /* Initialize callbacks to be used by the IrDA stack */
+       irda_notify_init(&notify);
+       notify.udata_indication = irda_data_indication;
+       notify.instance = self;
+       strncpy(notify.name, "Ultra", NOTIFY_MAX_NAME);
+
+       self->lsap = irlmp_open_lsap(LSAP_CONNLESS, &notify, pid);
+       if (self->lsap == NULL) {
+               pr_debug("%s(), Unable to allocate LSAP!\n", __func__);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_IRDA_ULTRA */
+
+/*
+ * Function irda_find_lsap_sel (self, name)
+ *
+ *    Try to lookup LSAP selector in remote LM-IAS
+ *
+ * Basically, we start a IAP query, and then go to sleep. When the query
+ * return, irda_getvalue_confirm will wake us up, and we can examine the
+ * result of the query...
+ * Note that in some case, the query fail even before we go to sleep,
+ * creating some races...
+ */
+static int irda_find_lsap_sel(struct irda_sock *self, char *name)
+{
+       pr_debug("%s(%p, %s)\n", __func__, self, name);
+
+       if (self->iriap) {
+               net_warn_ratelimited("%s(): busy with a previous query\n",
+                                    __func__);
+               return -EBUSY;
+       }
+
+       self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+                                irda_getvalue_confirm);
+       if(self->iriap == NULL)
+               return -ENOMEM;
+
+       /* Treat unexpected wakeup as disconnect */
+       self->errno = -EHOSTUNREACH;
+
+       /* Query remote LM-IAS */
+       iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr,
+                                     name, "IrDA:TinyTP:LsapSel");
+
+       /* Wait for answer, if not yet finished (or failed) */
+       if (wait_event_interruptible(self->query_wait, (self->iriap==NULL)))
+               /* Treat signals as disconnect */
+               return -EHOSTUNREACH;
+
+       /* Check what happened */
+       if (self->errno)
+       {
+               /* Requested object/attribute doesn't exist */
+               if((self->errno == IAS_CLASS_UNKNOWN) ||
+                  (self->errno == IAS_ATTRIB_UNKNOWN))
+                       return -EADDRNOTAVAIL;
+               else
+                       return -EHOSTUNREACH;
+       }
+
+       /* Get the remote TSAP selector */
+       switch (self->ias_result->type) {
+       case IAS_INTEGER:
+               pr_debug("%s() int=%d\n",
+                        __func__, self->ias_result->t.integer);
+
+               if (self->ias_result->t.integer != -1)
+                       self->dtsap_sel = self->ias_result->t.integer;
+               else
+                       self->dtsap_sel = 0;
+               break;
+       default:
+               self->dtsap_sel = 0;
+               pr_debug("%s(), bad type!\n", __func__);
+               break;
+       }
+       if (self->ias_result)
+               irias_delete_value(self->ias_result);
+
+       if (self->dtsap_sel)
+               return 0;
+
+       return -EADDRNOTAVAIL;
+}
+
+/*
+ * Function irda_discover_daddr_and_lsap_sel (self, name)
+ *
+ *    This try to find a device with the requested service.
+ *
+ * It basically look into the discovery log. For each address in the list,
+ * it queries the LM-IAS of the device to find if this device offer
+ * the requested service.
+ * If there is more than one node supporting the service, we complain
+ * to the user (it should move devices around).
+ * The, we set both the destination address and the lsap selector to point
+ * on the service on the unique device we have found.
+ *
+ * Note : this function fails if there is more than one device in range,
+ * because IrLMP doesn't disconnect the LAP when the last LSAP is closed.
+ * Moreover, we would need to wait the LAP disconnection...
+ */
+static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
+{
+       discinfo_t *discoveries;        /* Copy of the discovery log */
+       int     number;                 /* Number of nodes in the log */
+       int     i;
+       int     err = -ENETUNREACH;
+       __u32   daddr = DEV_ADDR_ANY;   /* Address we found the service on */
+       __u8    dtsap_sel = 0x0;        /* TSAP associated with it */
+
+       pr_debug("%s(), name=%s\n", __func__, name);
+
+       /* Ask lmp for the current discovery log
+        * Note : we have to use irlmp_get_discoveries(), as opposed
+        * to play with the cachelog directly, because while we are
+        * making our ias query, le log might change... */
+       discoveries = irlmp_get_discoveries(&number, self->mask.word,
+                                           self->nslots);
+       /* Check if the we got some results */
+       if (discoveries == NULL)
+               return -ENETUNREACH;    /* No nodes discovered */
+
+       /*
+        * Now, check all discovered devices (if any), and connect
+        * client only about the services that the client is
+        * interested in...
+        */
+       for(i = 0; i < number; i++) {
+               /* Try the address in the log */
+               self->daddr = discoveries[i].daddr;
+               self->saddr = 0x0;
+               pr_debug("%s(), trying daddr = %08x\n",
+                        __func__, self->daddr);
+
+               /* Query remote LM-IAS for this service */
+               err = irda_find_lsap_sel(self, name);
+               switch (err) {
+               case 0:
+                       /* We found the requested service */
+                       if(daddr != DEV_ADDR_ANY) {
+                               pr_debug("%s(), discovered service ''%s'' in two different devices !!!\n",
+                                        __func__, name);
+                               self->daddr = DEV_ADDR_ANY;
+                               kfree(discoveries);
+                               return -ENOTUNIQ;
+                       }
+                       /* First time we found that one, save it ! */
+                       daddr = self->daddr;
+                       dtsap_sel = self->dtsap_sel;
+                       break;
+               case -EADDRNOTAVAIL:
+                       /* Requested service simply doesn't exist on this node */
+                       break;
+               default:
+                       /* Something bad did happen :-( */
+                       pr_debug("%s(), unexpected IAS query failure\n",
+                                __func__);
+                       self->daddr = DEV_ADDR_ANY;
+                       kfree(discoveries);
+                       return -EHOSTUNREACH;
+               }
+       }
+       /* Cleanup our copy of the discovery log */
+       kfree(discoveries);
+
+       /* Check out what we found */
+       if(daddr == DEV_ADDR_ANY) {
+               pr_debug("%s(), cannot discover service ''%s'' in any device !!!\n",
+                        __func__, name);
+               self->daddr = DEV_ADDR_ANY;
+               return -EADDRNOTAVAIL;
+       }
+
+       /* Revert back to discovered device & service */
+       self->daddr = daddr;
+       self->saddr = 0x0;
+       self->dtsap_sel = dtsap_sel;
+
+       pr_debug("%s(), discovered requested service ''%s'' at address %08x\n",
+                __func__, name, self->daddr);
+
+       return 0;
+}
+
+/*
+ * Function irda_getname (sock, uaddr, uaddr_len, peer)
+ *
+ *    Return the our own, or peers socket address (sockaddr_irda)
+ *
+ */
+static int irda_getname(struct socket *sock, struct sockaddr *uaddr,
+                       int *uaddr_len, int peer)
+{
+       struct sockaddr_irda saddr;
+       struct sock *sk = sock->sk;
+       struct irda_sock *self = irda_sk(sk);
+
+       memset(&saddr, 0, sizeof(saddr));
+       if (peer) {
+               if (sk->sk_state != TCP_ESTABLISHED)
+                       return -ENOTCONN;
+
+               saddr.sir_family = AF_IRDA;
+               saddr.sir_lsap_sel = self->dtsap_sel;
+               saddr.sir_addr = self->daddr;
+       } else {
+               saddr.sir_family = AF_IRDA;
+               saddr.sir_lsap_sel = self->stsap_sel;
+               saddr.sir_addr = self->saddr;
+       }
+
+       pr_debug("%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel);
+       pr_debug("%s(), addr = %08x\n", __func__, saddr.sir_addr);
+
+       /* uaddr_len come to us uninitialised */
+       *uaddr_len = sizeof (struct sockaddr_irda);
+       memcpy(uaddr, &saddr, *uaddr_len);
+
+       return 0;
+}
+
+/*
+ * Function irda_listen (sock, backlog)
+ *
+ *    Just move to the listen state
+ *
+ */
+static int irda_listen(struct socket *sock, int backlog)
+{
+       struct sock *sk = sock->sk;
+       int err = -EOPNOTSUPP;
+
+       lock_sock(sk);
+
+       if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) &&
+           (sk->sk_type != SOCK_DGRAM))
+               goto out;
+
+       if (sk->sk_state != TCP_LISTEN) {
+               sk->sk_max_ack_backlog = backlog;
+               sk->sk_state           = TCP_LISTEN;
+
+               err = 0;
+       }
+out:
+       release_sock(sk);
+
+       return err;
+}
+
+/*
+ * Function irda_bind (sock, uaddr, addr_len)
+ *
+ *    Used by servers to register their well known TSAP
+ *
+ */
+static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+       struct sock *sk = sock->sk;
+       struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
+       struct irda_sock *self = irda_sk(sk);
+       int err;
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       if (addr_len != sizeof(struct sockaddr_irda))
+               return -EINVAL;
+
+       lock_sock(sk);
+#ifdef CONFIG_IRDA_ULTRA
+       /* Special care for Ultra sockets */
+       if ((sk->sk_type == SOCK_DGRAM) &&
+           (sk->sk_protocol == IRDAPROTO_ULTRA)) {
+               self->pid = addr->sir_lsap_sel;
+               err = -EOPNOTSUPP;
+               if (self->pid & 0x80) {
+                       pr_debug("%s(), extension in PID not supp!\n",
+                                __func__);
+                       goto out;
+               }
+               err = irda_open_lsap(self, self->pid);
+               if (err < 0)
+                       goto out;
+
+               /* Pretend we are connected */
+               sock->state = SS_CONNECTED;
+               sk->sk_state   = TCP_ESTABLISHED;
+               err = 0;
+
+               goto out;
+       }
+#endif /* CONFIG_IRDA_ULTRA */
+
+       self->ias_obj = irias_new_object(addr->sir_name, jiffies);
+       err = -ENOMEM;
+       if (self->ias_obj == NULL)
+               goto out;
+
+       err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name);
+       if (err < 0) {
+               irias_delete_object(self->ias_obj);
+               self->ias_obj = NULL;
+               goto out;
+       }
+
+       /*  Register with LM-IAS */
+       irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel",
+                                self->stsap_sel, IAS_KERNEL_ATTR);
+       irias_insert_object(self->ias_obj);
+
+       err = 0;
+out:
+       release_sock(sk);
+       return err;
+}
+
+/*
+ * Function irda_accept (sock, newsock, flags)
+ *
+ *    Wait for incoming connection
+ *
+ */
+static int irda_accept(struct socket *sock, struct socket *newsock, int flags,
+                      bool kern)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *new, *self = irda_sk(sk);
+       struct sock *newsk;
+       struct sk_buff *skb = NULL;
+       int err;
+
+       err = irda_create(sock_net(sk), newsock, sk->sk_protocol, kern);
+       if (err)
+               return err;
+
+       err = -EINVAL;
+
+       lock_sock(sk);
+       if (sock->state != SS_UNCONNECTED)
+               goto out;
+
+       err = -EOPNOTSUPP;
+       if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) &&
+           (sk->sk_type != SOCK_DGRAM))
+               goto out;
+
+       err = -EINVAL;
+       if (sk->sk_state != TCP_LISTEN)
+               goto out;
+
+       /*
+        *      The read queue this time is holding sockets ready to use
+        *      hooked into the SABM we saved
+        */
+
+       /*
+        * We can perform the accept only if there is incoming data
+        * on the listening socket.
+        * So, we will block the caller until we receive any data.
+        * If the caller was waiting on select() or poll() before
+        * calling us, the data is waiting for us ;-)
+        * Jean II
+        */
+       while (1) {
+               skb = skb_dequeue(&sk->sk_receive_queue);
+               if (skb)
+                       break;
+
+               /* Non blocking operation */
+               err = -EWOULDBLOCK;
+               if (flags & O_NONBLOCK)
+                       goto out;
+
+               err = wait_event_interruptible(*(sk_sleep(sk)),
+                                       skb_peek(&sk->sk_receive_queue));
+               if (err)
+                       goto out;
+       }
+
+       newsk = newsock->sk;
+       err = -EIO;
+       if (newsk == NULL)
+               goto out;
+
+       newsk->sk_state = TCP_ESTABLISHED;
+
+       new = irda_sk(newsk);
+
+       /* Now attach up the new socket */
+       new->tsap = irttp_dup(self->tsap, new);
+       err = -EPERM; /* value does not seem to make sense. -arnd */
+       if (!new->tsap) {
+               pr_debug("%s(), dup failed!\n", __func__);
+               goto out;
+       }
+
+       new->stsap_sel = new->tsap->stsap_sel;
+       new->dtsap_sel = new->tsap->dtsap_sel;
+       new->saddr = irttp_get_saddr(new->tsap);
+       new->daddr = irttp_get_daddr(new->tsap);
+
+       new->max_sdu_size_tx = self->max_sdu_size_tx;
+       new->max_sdu_size_rx = self->max_sdu_size_rx;
+       new->max_data_size   = self->max_data_size;
+       new->max_header_size = self->max_header_size;
+
+       memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info));
+
+       /* Clean up the original one to keep it in listen state */
+       irttp_listen(self->tsap);
+
+       sk->sk_ack_backlog--;
+
+       newsock->state = SS_CONNECTED;
+
+       irda_connect_response(new);
+       err = 0;
+out:
+       kfree_skb(skb);
+       release_sock(sk);
+       return err;
+}
+
+/*
+ * Function irda_connect (sock, uaddr, addr_len, flags)
+ *
+ *    Connect to a IrDA device
+ *
+ * The main difference with a "standard" connect is that with IrDA we need
+ * to resolve the service name into a TSAP selector (in TCP, port number
+ * doesn't have to be resolved).
+ * Because of this service name resolution, we can offer "auto-connect",
+ * where we connect to a service without specifying a destination address.
+ *
+ * Note : by consulting "errno", the user space caller may learn the cause
+ * of the failure. Most of them are visible in the function, others may come
+ * from subroutines called and are listed here :
+ *     o EBUSY : already processing a connect
+ *     o EHOSTUNREACH : bad addr->sir_addr argument
+ *     o EADDRNOTAVAIL : bad addr->sir_name argument
+ *     o ENOTUNIQ : more than one node has addr->sir_name (auto-connect)
+ *     o ENETUNREACH : no node found on the network (auto-connect)
+ */
+static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
+                       int addr_len, int flags)
+{
+       struct sock *sk = sock->sk;
+       struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
+       struct irda_sock *self = irda_sk(sk);
+       int err;
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       lock_sock(sk);
+       /* Don't allow connect for Ultra sockets */
+       err = -ESOCKTNOSUPPORT;
+       if ((sk->sk_type == SOCK_DGRAM) && (sk->sk_protocol == IRDAPROTO_ULTRA))
+               goto out;
+
+       if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
+               sock->state = SS_CONNECTED;
+               err = 0;
+               goto out;   /* Connect completed during a ERESTARTSYS event */
+       }
+
+       if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) {
+               sock->state = SS_UNCONNECTED;
+               err = -ECONNREFUSED;
+               goto out;
+       }
+
+       err = -EISCONN;      /* No reconnect on a seqpacket socket */
+       if (sk->sk_state == TCP_ESTABLISHED)
+               goto out;
+
+       sk->sk_state   = TCP_CLOSE;
+       sock->state = SS_UNCONNECTED;
+
+       err = -EINVAL;
+       if (addr_len != sizeof(struct sockaddr_irda))
+               goto out;
+
+       /* Check if user supplied any destination device address */
+       if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) {
+               /* Try to find one suitable */
+               err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name);
+               if (err) {
+                       pr_debug("%s(), auto-connect failed!\n", __func__);
+                       goto out;
+               }
+       } else {
+               /* Use the one provided by the user */
+               self->daddr = addr->sir_addr;
+               pr_debug("%s(), daddr = %08x\n", __func__, self->daddr);
+
+               /* If we don't have a valid service name, we assume the
+                * user want to connect on a specific LSAP. Prevent
+                * the use of invalid LSAPs (IrLMP 1.1 p10). Jean II */
+               if((addr->sir_name[0] != '\0') ||
+                  (addr->sir_lsap_sel >= 0x70)) {
+                       /* Query remote LM-IAS using service name */
+                       err = irda_find_lsap_sel(self, addr->sir_name);
+                       if (err) {
+                               pr_debug("%s(), connect failed!\n", __func__);
+                               goto out;
+                       }
+               } else {
+                       /* Directly connect to the remote LSAP
+                        * specified by the sir_lsap field.
+                        * Please use with caution, in IrDA LSAPs are
+                        * dynamic and there is no "well-known" LSAP. */
+                       self->dtsap_sel = addr->sir_lsap_sel;
+               }
+       }
+
+       /* Check if we have opened a local TSAP */
+       if (!self->tsap) {
+               err = irda_open_tsap(self, LSAP_ANY, addr->sir_name);
+               if (err)
+                       goto out;
+       }
+
+       /* Move to connecting socket, start sending Connect Requests */
+       sock->state = SS_CONNECTING;
+       sk->sk_state   = TCP_SYN_SENT;
+
+       /* Connect to remote device */
+       err = irttp_connect_request(self->tsap, self->dtsap_sel,
+                                   self->saddr, self->daddr, NULL,
+                                   self->max_sdu_size_rx, NULL);
+       if (err) {
+               pr_debug("%s(), connect failed!\n", __func__);
+               goto out;
+       }
+
+       /* Now the loop */
+       err = -EINPROGRESS;
+       if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+               goto out;
+
+       err = -ERESTARTSYS;
+       if (wait_event_interruptible(*(sk_sleep(sk)),
+                                    (sk->sk_state != TCP_SYN_SENT)))
+               goto out;
+
+       if (sk->sk_state != TCP_ESTABLISHED) {
+               sock->state = SS_UNCONNECTED;
+               err = sock_error(sk);
+               if (!err)
+                       err = -ECONNRESET;
+               goto out;
+       }
+
+       sock->state = SS_CONNECTED;
+
+       /* At this point, IrLMP has assigned our source address */
+       self->saddr = irttp_get_saddr(self->tsap);
+       err = 0;
+out:
+       release_sock(sk);
+       return err;
+}
+
+static struct proto irda_proto = {
+       .name     = "IRDA",
+       .owner    = THIS_MODULE,
+       .obj_size = sizeof(struct irda_sock),
+};
+
+/*
+ * Function irda_create (sock, protocol)
+ *
+ *    Create IrDA socket
+ *
+ */
+static int irda_create(struct net *net, struct socket *sock, int protocol,
+                      int kern)
+{
+       struct sock *sk;
+       struct irda_sock *self;
+
+       if (protocol < 0 || protocol > SK_PROTOCOL_MAX)
+               return -EINVAL;
+
+       if (net != &init_net)
+               return -EAFNOSUPPORT;
+
+       /* Check for valid socket type */
+       switch (sock->type) {
+       case SOCK_STREAM:     /* For TTP connections with SAR disabled */
+       case SOCK_SEQPACKET:  /* For TTP connections with SAR enabled */
+       case SOCK_DGRAM:      /* For TTP Unitdata or LMP Ultra transfers */
+               break;
+       default:
+               return -ESOCKTNOSUPPORT;
+       }
+
+       /* Allocate networking socket */
+       sk = sk_alloc(net, PF_IRDA, GFP_KERNEL, &irda_proto, kern);
+       if (sk == NULL)
+               return -ENOMEM;
+
+       self = irda_sk(sk);
+       pr_debug("%s() : self is %p\n", __func__, self);
+
+       init_waitqueue_head(&self->query_wait);
+
+       switch (sock->type) {
+       case SOCK_STREAM:
+               sock->ops = &irda_stream_ops;
+               self->max_sdu_size_rx = TTP_SAR_DISABLE;
+               break;
+       case SOCK_SEQPACKET:
+               sock->ops = &irda_seqpacket_ops;
+               self->max_sdu_size_rx = TTP_SAR_UNBOUND;
+               break;
+       case SOCK_DGRAM:
+               switch (protocol) {
+#ifdef CONFIG_IRDA_ULTRA
+               case IRDAPROTO_ULTRA:
+                       sock->ops = &irda_ultra_ops;
+                       /* Initialise now, because we may send on unbound
+                        * sockets. Jean II */
+                       self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER;
+                       self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER;
+                       break;
+#endif /* CONFIG_IRDA_ULTRA */
+               case IRDAPROTO_UNITDATA:
+                       sock->ops = &irda_dgram_ops;
+                       /* We let Unitdata conn. be like seqpack conn. */
+                       self->max_sdu_size_rx = TTP_SAR_UNBOUND;
+                       break;
+               default:
+                       sk_free(sk);
+                       return -ESOCKTNOSUPPORT;
+               }
+               break;
+       default:
+               sk_free(sk);
+               return -ESOCKTNOSUPPORT;
+       }
+
+       /* Initialise networking socket struct */
+       sock_init_data(sock, sk);       /* Note : set sk->sk_refcnt to 1 */
+       sk->sk_family = PF_IRDA;
+       sk->sk_protocol = protocol;
+
+       /* Register as a client with IrLMP */
+       self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
+       self->mask.word = 0xffff;
+       self->rx_flow = self->tx_flow = FLOW_START;
+       self->nslots = DISCOVERY_DEFAULT_SLOTS;
+       self->daddr = DEV_ADDR_ANY;     /* Until we get connected */
+       self->saddr = 0x0;              /* so IrLMP assign us any link */
+       return 0;
+}
+
+/*
+ * Function irda_destroy_socket (self)
+ *
+ *    Destroy socket
+ *
+ */
+static void irda_destroy_socket(struct irda_sock *self)
+{
+       pr_debug("%s(%p)\n", __func__, self);
+
+       /* Unregister with IrLMP */
+       irlmp_unregister_client(self->ckey);
+       irlmp_unregister_service(self->skey);
+
+       /* Unregister with LM-IAS */
+       if (self->ias_obj) {
+               irias_delete_object(self->ias_obj);
+               self->ias_obj = NULL;
+       }
+
+       if (self->iriap) {
+               iriap_close(self->iriap);
+               self->iriap = NULL;
+       }
+
+       if (self->tsap) {
+               irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
+               irttp_close_tsap(self->tsap);
+               self->tsap = NULL;
+       }
+#ifdef CONFIG_IRDA_ULTRA
+       if (self->lsap) {
+               irlmp_close_lsap(self->lsap);
+               self->lsap = NULL;
+       }
+#endif /* CONFIG_IRDA_ULTRA */
+}
+
+/*
+ * Function irda_release (sock)
+ */
+static int irda_release(struct socket *sock)
+{
+       struct sock *sk = sock->sk;
+
+       if (sk == NULL)
+               return 0;
+
+       lock_sock(sk);
+       sk->sk_state       = TCP_CLOSE;
+       sk->sk_shutdown   |= SEND_SHUTDOWN;
+       sk->sk_state_change(sk);
+
+       /* Destroy IrDA socket */
+       irda_destroy_socket(irda_sk(sk));
+
+       sock_orphan(sk);
+       sock->sk   = NULL;
+       release_sock(sk);
+
+       /* Purge queues (see sock_init_data()) */
+       skb_queue_purge(&sk->sk_receive_queue);
+
+       /* Destroy networking socket if we are the last reference on it,
+        * i.e. if(sk->sk_refcnt == 0) -> sk_free(sk) */
+       sock_put(sk);
+
+       /* Notes on socket locking and deallocation... - Jean II
+        * In theory we should put pairs of sock_hold() / sock_put() to
+        * prevent the socket to be destroyed whenever there is an
+        * outstanding request or outstanding incoming packet or event.
+        *
+        * 1) This may include IAS request, both in connect and getsockopt.
+        * Unfortunately, the situation is a bit more messy than it looks,
+        * because we close iriap and kfree(self) above.
+        *
+        * 2) This may include selective discovery in getsockopt.
+        * Same stuff as above, irlmp registration and self are gone.
+        *
+        * Probably 1 and 2 may not matter, because it's all triggered
+        * by a process and the socket layer already prevent the
+        * socket to go away while a process is holding it, through
+        * sockfd_put() and fput()...
+        *
+        * 3) This may include deferred TSAP closure. In particular,
+        * we may receive a late irda_disconnect_indication()
+        * Fortunately, (tsap_cb *)->close_pend should protect us
+        * from that.
+        *
+        * I did some testing on SMP, and it looks solid. And the socket
+        * memory leak is now gone... - Jean II
+        */
+
+       return 0;
+}
+
+/*
+ * Function irda_sendmsg (sock, msg, len)
+ *
+ *    Send message down to TinyTP. This function is used for both STREAM and
+ *    SEQPACK services. This is possible since it forces the client to
+ *    fragment the message if necessary
+ */
+static int irda_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self;
+       struct sk_buff *skb;
+       int err = -EPIPE;
+
+       pr_debug("%s(), len=%zd\n", __func__, len);
+
+       /* Note : socket.c set MSG_EOR on SEQPACKET sockets */
+       if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT |
+                              MSG_NOSIGNAL)) {
+               return -EINVAL;
+       }
+
+       lock_sock(sk);
+
+       if (sk->sk_shutdown & SEND_SHUTDOWN)
+               goto out_err;
+
+       if (sk->sk_state != TCP_ESTABLISHED) {
+               err = -ENOTCONN;
+               goto out;
+       }
+
+       self = irda_sk(sk);
+
+       /* Check if IrTTP is wants us to slow down */
+
+       if (wait_event_interruptible(*(sk_sleep(sk)),
+           (self->tx_flow != FLOW_STOP  ||  sk->sk_state != TCP_ESTABLISHED))) {
+               err = -ERESTARTSYS;
+               goto out;
+       }
+
+       /* Check if we are still connected */
+       if (sk->sk_state != TCP_ESTABLISHED) {
+               err = -ENOTCONN;
+               goto out;
+       }
+
+       /* Check that we don't send out too big frames */
+       if (len > self->max_data_size) {
+               pr_debug("%s(), Chopping frame from %zd to %d bytes!\n",
+                        __func__, len, self->max_data_size);
+               len = self->max_data_size;
+       }
+
+       skb = sock_alloc_send_skb(sk, len + self->max_header_size + 16,
+                                 msg->msg_flags & MSG_DONTWAIT, &err);
+       if (!skb)
+               goto out_err;
+
+       skb_reserve(skb, self->max_header_size + 16);
+       skb_reset_transport_header(skb);
+       skb_put(skb, len);
+       err = memcpy_from_msg(skb_transport_header(skb), msg, len);
+       if (err) {
+               kfree_skb(skb);
+               goto out_err;
+       }
+
+       /*
+        * Just send the message to TinyTP, and let it deal with possible
+        * errors. No need to duplicate all that here
+        */
+       err = irttp_data_request(self->tsap, skb);
+       if (err) {
+               pr_debug("%s(), err=%d\n", __func__, err);
+               goto out_err;
+       }
+
+       release_sock(sk);
+       /* Tell client how much data we actually sent */
+       return len;
+
+out_err:
+       err = sk_stream_error(sk, msg->msg_flags, err);
+out:
+       release_sock(sk);
+       return err;
+
+}
+
+/*
+ * Function irda_recvmsg_dgram (sock, msg, size, flags)
+ *
+ *    Try to receive message and copy it to user. The frame is discarded
+ *    after being read, regardless of how much the user actually read
+ */
+static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg,
+                             size_t size, int flags)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self = irda_sk(sk);
+       struct sk_buff *skb;
+       size_t copied;
+       int err;
+
+       skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
+                               flags & MSG_DONTWAIT, &err);
+       if (!skb)
+               return err;
+
+       skb_reset_transport_header(skb);
+       copied = skb->len;
+
+       if (copied > size) {
+               pr_debug("%s(), Received truncated frame (%zd < %zd)!\n",
+                        __func__, copied, size);
+               copied = size;
+               msg->msg_flags |= MSG_TRUNC;
+       }
+       skb_copy_datagram_msg(skb, 0, msg, copied);
+
+       skb_free_datagram(sk, skb);
+
+       /*
+        *  Check if we have previously stopped IrTTP and we know
+        *  have more free space in our rx_queue. If so tell IrTTP
+        *  to start delivering frames again before our rx_queue gets
+        *  empty
+        */
+       if (self->rx_flow == FLOW_STOP) {
+               if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) {
+                       pr_debug("%s(), Starting IrTTP\n", __func__);
+                       self->rx_flow = FLOW_START;
+                       irttp_flow_request(self->tsap, FLOW_START);
+               }
+       }
+
+       return copied;
+}
+
+/*
+ * Function irda_recvmsg_stream (sock, msg, size, flags)
+ */
+static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg,
+                              size_t size, int flags)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self = irda_sk(sk);
+       int noblock = flags & MSG_DONTWAIT;
+       size_t copied = 0;
+       int target, err;
+       long timeo;
+
+       if ((err = sock_error(sk)) < 0)
+               return err;
+
+       if (sock->flags & __SO_ACCEPTCON)
+               return -EINVAL;
+
+       err =-EOPNOTSUPP;
+       if (flags & MSG_OOB)
+               return -EOPNOTSUPP;
+
+       err = 0;
+       target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
+       timeo = sock_rcvtimeo(sk, noblock);
+
+       do {
+               int chunk;
+               struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
+
+               if (skb == NULL) {
+                       DEFINE_WAIT(wait);
+                       err = 0;
+
+                       if (copied >= target)
+                               break;
+
+                       prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+
+                       /*
+                        *      POSIX 1003.1g mandates this order.
+                        */
+                       err = sock_error(sk);
+                       if (err)
+                               ;
+                       else if (sk->sk_shutdown & RCV_SHUTDOWN)
+                               ;
+                       else if (noblock)
+                               err = -EAGAIN;
+                       else if (signal_pending(current))
+                               err = sock_intr_errno(timeo);
+                       else if (sk->sk_state != TCP_ESTABLISHED)
+                               err = -ENOTCONN;
+                       else if (skb_peek(&sk->sk_receive_queue) == NULL)
+                               /* Wait process until data arrives */
+                               schedule();
+
+                       finish_wait(sk_sleep(sk), &wait);
+
+                       if (err)
+                               return err;
+                       if (sk->sk_shutdown & RCV_SHUTDOWN)
+                               break;
+
+                       continue;
+               }
+
+               chunk = min_t(unsigned int, skb->len, size);
+               if (memcpy_to_msg(msg, skb->data, chunk)) {
+                       skb_queue_head(&sk->sk_receive_queue, skb);
+                       if (copied == 0)
+                               copied = -EFAULT;
+                       break;
+               }
+               copied += chunk;
+               size -= chunk;
+
+               /* Mark read part of skb as used */
+               if (!(flags & MSG_PEEK)) {
+                       skb_pull(skb, chunk);
+
+                       /* put the skb back if we didn't use it up.. */
+                       if (skb->len) {
+                               pr_debug("%s(), back on q!\n",
+                                        __func__);
+                               skb_queue_head(&sk->sk_receive_queue, skb);
+                               break;
+                       }
+
+                       kfree_skb(skb);
+               } else {
+                       pr_debug("%s() questionable!?\n", __func__);
+
+                       /* put message back and return */
+                       skb_queue_head(&sk->sk_receive_queue, skb);
+                       break;
+               }
+       } while (size);
+
+       /*
+        *  Check if we have previously stopped IrTTP and we know
+        *  have more free space in our rx_queue. If so tell IrTTP
+        *  to start delivering frames again before our rx_queue gets
+        *  empty
+        */
+       if (self->rx_flow == FLOW_STOP) {
+               if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) {
+                       pr_debug("%s(), Starting IrTTP\n", __func__);
+                       self->rx_flow = FLOW_START;
+                       irttp_flow_request(self->tsap, FLOW_START);
+               }
+       }
+
+       return copied;
+}
+
+/*
+ * Function irda_sendmsg_dgram (sock, msg, len)
+ *
+ *    Send message down to TinyTP for the unreliable sequenced
+ *    packet service...
+ *
+ */
+static int irda_sendmsg_dgram(struct socket *sock, struct msghdr *msg,
+                             size_t len)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self;
+       struct sk_buff *skb;
+       int err;
+
+       pr_debug("%s(), len=%zd\n", __func__, len);
+
+       if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
+               return -EINVAL;
+
+       lock_sock(sk);
+
+       if (sk->sk_shutdown & SEND_SHUTDOWN) {
+               send_sig(SIGPIPE, current, 0);
+               err = -EPIPE;
+               goto out;
+       }
+
+       err = -ENOTCONN;
+       if (sk->sk_state != TCP_ESTABLISHED)
+               goto out;
+
+       self = irda_sk(sk);
+
+       /*
+        * Check that we don't send out too big frames. This is an unreliable
+        * service, so we have no fragmentation and no coalescence
+        */
+       if (len > self->max_data_size) {
+               pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n",
+                        __func__, len, self->max_data_size);
+               len = self->max_data_size;
+       }
+
+       skb = sock_alloc_send_skb(sk, len + self->max_header_size,
+                                 msg->msg_flags & MSG_DONTWAIT, &err);
+       err = -ENOBUFS;
+       if (!skb)
+               goto out;
+
+       skb_reserve(skb, self->max_header_size);
+       skb_reset_transport_header(skb);
+
+       pr_debug("%s(), appending user data\n", __func__);
+       skb_put(skb, len);
+       err = memcpy_from_msg(skb_transport_header(skb), msg, len);
+       if (err) {
+               kfree_skb(skb);
+               goto out;
+       }
+
+       /*
+        * Just send the message to TinyTP, and let it deal with possible
+        * errors. No need to duplicate all that here
+        */
+       err = irttp_udata_request(self->tsap, skb);
+       if (err) {
+               pr_debug("%s(), err=%d\n", __func__, err);
+               goto out;
+       }
+
+       release_sock(sk);
+       return len;
+
+out:
+       release_sock(sk);
+       return err;
+}
+
+/*
+ * Function irda_sendmsg_ultra (sock, msg, len)
+ *
+ *    Send message down to IrLMP for the unreliable Ultra
+ *    packet service...
+ */
+#ifdef CONFIG_IRDA_ULTRA
+static int irda_sendmsg_ultra(struct socket *sock, struct msghdr *msg,
+                             size_t len)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self;
+       __u8 pid = 0;
+       int bound = 0;
+       struct sk_buff *skb;
+       int err;
+
+       pr_debug("%s(), len=%zd\n", __func__, len);
+
+       err = -EINVAL;
+       if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
+               return -EINVAL;
+
+       lock_sock(sk);
+
+       err = -EPIPE;
+       if (sk->sk_shutdown & SEND_SHUTDOWN) {
+               send_sig(SIGPIPE, current, 0);
+               goto out;
+       }
+
+       self = irda_sk(sk);
+
+       /* Check if an address was specified with sendto. Jean II */
+       if (msg->msg_name) {
+               DECLARE_SOCKADDR(struct sockaddr_irda *, addr, msg->msg_name);
+               err = -EINVAL;
+               /* Check address, extract pid. Jean II */
+               if (msg->msg_namelen < sizeof(*addr))
+                       goto out;
+               if (addr->sir_family != AF_IRDA)
+                       goto out;
+
+               pid = addr->sir_lsap_sel;
+               if (pid & 0x80) {
+                       pr_debug("%s(), extension in PID not supp!\n",
+                                __func__);
+                       err = -EOPNOTSUPP;
+                       goto out;
+               }
+       } else {
+               /* Check that the socket is properly bound to an Ultra
+                * port. Jean II */
+               if ((self->lsap == NULL) ||
+                   (sk->sk_state != TCP_ESTABLISHED)) {
+                       pr_debug("%s(), socket not bound to Ultra PID.\n",
+                                __func__);
+                       err = -ENOTCONN;
+                       goto out;
+               }
+               /* Use PID from socket */
+               bound = 1;
+       }
+
+       /*
+        * Check that we don't send out too big frames. This is an unreliable
+        * service, so we have no fragmentation and no coalescence
+        */
+       if (len > self->max_data_size) {
+               pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n",
+                        __func__, len, self->max_data_size);
+               len = self->max_data_size;
+       }
+
+       skb = sock_alloc_send_skb(sk, len + self->max_header_size,
+                                 msg->msg_flags & MSG_DONTWAIT, &err);
+       err = -ENOBUFS;
+       if (!skb)
+               goto out;
+
+       skb_reserve(skb, self->max_header_size);
+       skb_reset_transport_header(skb);
+
+       pr_debug("%s(), appending user data\n", __func__);
+       skb_put(skb, len);
+       err = memcpy_from_msg(skb_transport_header(skb), msg, len);
+       if (err) {
+               kfree_skb(skb);
+               goto out;
+       }
+
+       err = irlmp_connless_data_request((bound ? self->lsap : NULL),
+                                         skb, pid);
+       if (err)
+               pr_debug("%s(), err=%d\n", __func__, err);
+out:
+       release_sock(sk);
+       return err ? : len;
+}
+#endif /* CONFIG_IRDA_ULTRA */
+
+/*
+ * Function irda_shutdown (sk, how)
+ */
+static int irda_shutdown(struct socket *sock, int how)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self = irda_sk(sk);
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       lock_sock(sk);
+
+       sk->sk_state       = TCP_CLOSE;
+       sk->sk_shutdown   |= SEND_SHUTDOWN;
+       sk->sk_state_change(sk);
+
+       if (self->iriap) {
+               iriap_close(self->iriap);
+               self->iriap = NULL;
+       }
+
+       if (self->tsap) {
+               irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
+               irttp_close_tsap(self->tsap);
+               self->tsap = NULL;
+       }
+
+       /* A few cleanup so the socket look as good as new... */
+       self->rx_flow = self->tx_flow = FLOW_START;     /* needed ??? */
+       self->daddr = DEV_ADDR_ANY;     /* Until we get re-connected */
+       self->saddr = 0x0;              /* so IrLMP assign us any link */
+
+       release_sock(sk);
+
+       return 0;
+}
+
+/*
+ * Function irda_poll (file, sock, wait)
+ */
+static unsigned int irda_poll(struct file * file, struct socket *sock,
+                             poll_table *wait)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self = irda_sk(sk);
+       unsigned int mask;
+
+       poll_wait(file, sk_sleep(sk), wait);
+       mask = 0;
+
+       /* Exceptional events? */
+       if (sk->sk_err)
+               mask |= POLLERR;
+       if (sk->sk_shutdown & RCV_SHUTDOWN) {
+               pr_debug("%s(), POLLHUP\n", __func__);
+               mask |= POLLHUP;
+       }
+
+       /* Readable? */
+       if (!skb_queue_empty(&sk->sk_receive_queue)) {
+               pr_debug("Socket is readable\n");
+               mask |= POLLIN | POLLRDNORM;
+       }
+
+       /* Connection-based need to check for termination and startup */
+       switch (sk->sk_type) {
+       case SOCK_STREAM:
+               if (sk->sk_state == TCP_CLOSE) {
+                       pr_debug("%s(), POLLHUP\n", __func__);
+                       mask |= POLLHUP;
+               }
+
+               if (sk->sk_state == TCP_ESTABLISHED) {
+                       if ((self->tx_flow == FLOW_START) &&
+                           sock_writeable(sk))
+                       {
+                               mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+                       }
+               }
+               break;
+       case SOCK_SEQPACKET:
+               if ((self->tx_flow == FLOW_START) &&
+                   sock_writeable(sk))
+               {
+                       mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+               }
+               break;
+       case SOCK_DGRAM:
+               if (sock_writeable(sk))
+                       mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+               break;
+       default:
+               break;
+       }
+
+       return mask;
+}
+
+/*
+ * Function irda_ioctl (sock, cmd, arg)
+ */
+static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+       struct sock *sk = sock->sk;
+       int err;
+
+       pr_debug("%s(), cmd=%#x\n", __func__, cmd);
+
+       err = -EINVAL;
+       switch (cmd) {
+       case TIOCOUTQ: {
+               long amount;
+
+               amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
+               if (amount < 0)
+                       amount = 0;
+               err = put_user(amount, (unsigned int __user *)arg);
+               break;
+       }
+
+       case TIOCINQ: {
+               struct sk_buff *skb;
+               long amount = 0L;
+               /* These two are safe on a single CPU system as only user tasks fiddle here */
+               if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
+                       amount = skb->len;
+               err = put_user(amount, (unsigned int __user *)arg);
+               break;
+       }
+
+       case SIOCGSTAMP:
+               if (sk != NULL)
+                       err = sock_get_timestamp(sk, (struct timeval __user *)arg);
+               break;
+
+       case SIOCGIFADDR:
+       case SIOCSIFADDR:
+       case SIOCGIFDSTADDR:
+       case SIOCSIFDSTADDR:
+       case SIOCGIFBRDADDR:
+       case SIOCSIFBRDADDR:
+       case SIOCGIFNETMASK:
+       case SIOCSIFNETMASK:
+       case SIOCGIFMETRIC:
+       case SIOCSIFMETRIC:
+               break;
+       default:
+               pr_debug("%s(), doing device ioctl!\n", __func__);
+               err = -ENOIOCTLCMD;
+       }
+
+       return err;
+}
+
+#ifdef CONFIG_COMPAT
+/*
+ * Function irda_ioctl (sock, cmd, arg)
+ */
+static int irda_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+       /*
+        * All IRDA's ioctl are standard ones.
+        */
+       return -ENOIOCTLCMD;
+}
+#endif
+
+/*
+ * Function irda_setsockopt (sock, level, optname, optval, optlen)
+ *
+ *    Set some options for the socket
+ *
+ */
+static int irda_setsockopt(struct socket *sock, int level, int optname,
+                          char __user *optval, unsigned int optlen)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self = irda_sk(sk);
+       struct irda_ias_set    *ias_opt;
+       struct ias_object      *ias_obj;
+       struct ias_attrib *     ias_attr;       /* Attribute in IAS object */
+       int opt, free_ias = 0, err = 0;
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       if (level != SOL_IRLMP)
+               return -ENOPROTOOPT;
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case IRLMP_IAS_SET:
+               /* The user want to add an attribute to an existing IAS object
+                * (in the IAS database) or to create a new object with this
+                * attribute.
+                * We first query IAS to know if the object exist, and then
+                * create the right attribute...
+                */
+
+               if (optlen != sizeof(struct irda_ias_set)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Copy query to the driver. */
+               ias_opt = memdup_user(optval, optlen);
+               if (IS_ERR(ias_opt)) {
+                       err = PTR_ERR(ias_opt);
+                       goto out;
+               }
+
+               /* Find the object we target.
+                * If the user gives us an empty string, we use the object
+                * associated with this socket. This will workaround
+                * duplicated class name - Jean II */
+               if(ias_opt->irda_class_name[0] == '\0') {
+                       if(self->ias_obj == NULL) {
+                               kfree(ias_opt);
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       ias_obj = self->ias_obj;
+               } else
+                       ias_obj = irias_find_object(ias_opt->irda_class_name);
+
+               /* Only ROOT can mess with the global IAS database.
+                * Users can only add attributes to the object associated
+                * with the socket they own - Jean II */
+               if((!capable(CAP_NET_ADMIN)) &&
+                  ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
+                       kfree(ias_opt);
+                       err = -EPERM;
+                       goto out;
+               }
+
+               /* If the object doesn't exist, create it */
+               if(ias_obj == (struct ias_object *) NULL) {
+                       /* Create a new object */
+                       ias_obj = irias_new_object(ias_opt->irda_class_name,
+                                                  jiffies);
+                       if (ias_obj == NULL) {
+                               kfree(ias_opt);
+                               err = -ENOMEM;
+                               goto out;
+                       }
+                       free_ias = 1;
+               }
+
+               /* Do we have the attribute already ? */
+               if(irias_find_attrib(ias_obj, ias_opt->irda_attrib_name)) {
+                       kfree(ias_opt);
+                       if (free_ias) {
+                               kfree(ias_obj->name);
+                               kfree(ias_obj);
+                       }
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Look at the type */
+               switch(ias_opt->irda_attrib_type) {
+               case IAS_INTEGER:
+                       /* Add an integer attribute */
+                       irias_add_integer_attrib(
+                               ias_obj,
+                               ias_opt->irda_attrib_name,
+                               ias_opt->attribute.irda_attrib_int,
+                               IAS_USER_ATTR);
+                       break;
+               case IAS_OCT_SEQ:
+                       /* Check length */
+                       if(ias_opt->attribute.irda_attrib_octet_seq.len >
+                          IAS_MAX_OCTET_STRING) {
+                               kfree(ias_opt);
+                               if (free_ias) {
+                                       kfree(ias_obj->name);
+                                       kfree(ias_obj);
+                               }
+
+                               err = -EINVAL;
+                               goto out;
+                       }
+                       /* Add an octet sequence attribute */
+                       irias_add_octseq_attrib(
+                             ias_obj,
+                             ias_opt->irda_attrib_name,
+                             ias_opt->attribute.irda_attrib_octet_seq.octet_seq,
+                             ias_opt->attribute.irda_attrib_octet_seq.len,
+                             IAS_USER_ATTR);
+                       break;
+               case IAS_STRING:
+                       /* Should check charset & co */
+                       /* Check length */
+                       /* The length is encoded in a __u8, and
+                        * IAS_MAX_STRING == 256, so there is no way
+                        * userspace can pass us a string too large.
+                        * Jean II */
+                       /* NULL terminate the string (avoid troubles) */
+                       ias_opt->attribute.irda_attrib_string.string[ias_opt->attribute.irda_attrib_string.len] = '\0';
+                       /* Add a string attribute */
+                       irias_add_string_attrib(
+                               ias_obj,
+                               ias_opt->irda_attrib_name,
+                               ias_opt->attribute.irda_attrib_string.string,
+                               IAS_USER_ATTR);
+                       break;
+               default :
+                       kfree(ias_opt);
+                       if (free_ias) {
+                               kfree(ias_obj->name);
+                               kfree(ias_obj);
+                       }
+                       err = -EINVAL;
+                       goto out;
+               }
+               irias_insert_object(ias_obj);
+               kfree(ias_opt);
+               break;
+       case IRLMP_IAS_DEL:
+               /* The user want to delete an object from our local IAS
+                * database. We just need to query the IAS, check is the
+                * object is not owned by the kernel and delete it.
+                */
+
+               if (optlen != sizeof(struct irda_ias_set)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Copy query to the driver. */
+               ias_opt = memdup_user(optval, optlen);
+               if (IS_ERR(ias_opt)) {
+                       err = PTR_ERR(ias_opt);
+                       goto out;
+               }
+
+               /* Find the object we target.
+                * If the user gives us an empty string, we use the object
+                * associated with this socket. This will workaround
+                * duplicated class name - Jean II */
+               if(ias_opt->irda_class_name[0] == '\0')
+                       ias_obj = self->ias_obj;
+               else
+                       ias_obj = irias_find_object(ias_opt->irda_class_name);
+               if(ias_obj == (struct ias_object *) NULL) {
+                       kfree(ias_opt);
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Only ROOT can mess with the global IAS database.
+                * Users can only del attributes from the object associated
+                * with the socket they own - Jean II */
+               if((!capable(CAP_NET_ADMIN)) &&
+                  ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
+                       kfree(ias_opt);
+                       err = -EPERM;
+                       goto out;
+               }
+
+               /* Find the attribute (in the object) we target */
+               ias_attr = irias_find_attrib(ias_obj,
+                                            ias_opt->irda_attrib_name);
+               if(ias_attr == (struct ias_attrib *) NULL) {
+                       kfree(ias_opt);
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Check is the user space own the object */
+               if(ias_attr->value->owner != IAS_USER_ATTR) {
+                       pr_debug("%s(), attempting to delete a kernel attribute\n",
+                                __func__);
+                       kfree(ias_opt);
+                       err = -EPERM;
+                       goto out;
+               }
+
+               /* Remove the attribute (and maybe the object) */
+               irias_delete_attrib(ias_obj, ias_attr, 1);
+               kfree(ias_opt);
+               break;
+       case IRLMP_MAX_SDU_SIZE:
+               if (optlen < sizeof(int)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               if (get_user(opt, (int __user *)optval)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               /* Only possible for a seqpacket service (TTP with SAR) */
+               if (sk->sk_type != SOCK_SEQPACKET) {
+                       pr_debug("%s(), setting max_sdu_size = %d\n",
+                                __func__, opt);
+                       self->max_sdu_size_rx = opt;
+               } else {
+                       net_warn_ratelimited("%s: not allowed to set MAXSDUSIZE for this socket type!\n",
+                                            __func__);
+                       err = -ENOPROTOOPT;
+                       goto out;
+               }
+               break;
+       case IRLMP_HINTS_SET:
+               if (optlen < sizeof(int)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* The input is really a (__u8 hints[2]), easier as an int */
+               if (get_user(opt, (int __user *)optval)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               /* Unregister any old registration */
+               irlmp_unregister_service(self->skey);
+
+               self->skey = irlmp_register_service((__u16) opt);
+               break;
+       case IRLMP_HINT_MASK_SET:
+               /* As opposed to the previous case which set the hint bits
+                * that we advertise, this one set the filter we use when
+                * making a discovery (nodes which don't match any hint
+                * bit in the mask are not reported).
+                */
+               if (optlen < sizeof(int)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* The input is really a (__u8 hints[2]), easier as an int */
+               if (get_user(opt, (int __user *)optval)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               /* Set the new hint mask */
+               self->mask.word = (__u16) opt;
+               /* Mask out extension bits */
+               self->mask.word &= 0x7f7f;
+               /* Check if no bits */
+               if(!self->mask.word)
+                       self->mask.word = 0xFFFF;
+
+               break;
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+
+out:
+       release_sock(sk);
+
+       return err;
+}
+
+/*
+ * Function irda_extract_ias_value(ias_opt, ias_value)
+ *
+ *    Translate internal IAS value structure to the user space representation
+ *
+ * The external representation of IAS values, as we exchange them with
+ * user space program is quite different from the internal representation,
+ * as stored in the IAS database (because we need a flat structure for
+ * crossing kernel boundary).
+ * This function transform the former in the latter. We also check
+ * that the value type is valid.
+ */
+static int irda_extract_ias_value(struct irda_ias_set *ias_opt,
+                                 struct ias_value *ias_value)
+{
+       /* Look at the type */
+       switch (ias_value->type) {
+       case IAS_INTEGER:
+               /* Copy the integer */
+               ias_opt->attribute.irda_attrib_int = ias_value->t.integer;
+               break;
+       case IAS_OCT_SEQ:
+               /* Set length */
+               ias_opt->attribute.irda_attrib_octet_seq.len = ias_value->len;
+               /* Copy over */
+               memcpy(ias_opt->attribute.irda_attrib_octet_seq.octet_seq,
+                      ias_value->t.oct_seq, ias_value->len);
+               break;
+       case IAS_STRING:
+               /* Set length */
+               ias_opt->attribute.irda_attrib_string.len = ias_value->len;
+               ias_opt->attribute.irda_attrib_string.charset = ias_value->charset;
+               /* Copy over */
+               memcpy(ias_opt->attribute.irda_attrib_string.string,
+                      ias_value->t.string, ias_value->len);
+               /* NULL terminate the string (avoid troubles) */
+               ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0';
+               break;
+       case IAS_MISSING:
+       default :
+               return -EINVAL;
+       }
+
+       /* Copy type over */
+       ias_opt->irda_attrib_type = ias_value->type;
+
+       return 0;
+}
+
+/*
+ * Function irda_getsockopt (sock, level, optname, optval, optlen)
+ */
+static int irda_getsockopt(struct socket *sock, int level, int optname,
+                          char __user *optval, int __user *optlen)
+{
+       struct sock *sk = sock->sk;
+       struct irda_sock *self = irda_sk(sk);
+       struct irda_device_list list = { 0 };
+       struct irda_device_info *discoveries;
+       struct irda_ias_set *   ias_opt;        /* IAS get/query params */
+       struct ias_object *     ias_obj;        /* Object in IAS */
+       struct ias_attrib *     ias_attr;       /* Attribute in IAS object */
+       int daddr = DEV_ADDR_ANY;       /* Dest address for IAS queries */
+       int val = 0;
+       int len = 0;
+       int err = 0;
+       int offset, total;
+
+       pr_debug("%s(%p)\n", __func__, self);
+
+       if (level != SOL_IRLMP)
+               return -ENOPROTOOPT;
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       if(len < 0)
+               return -EINVAL;
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case IRLMP_ENUMDEVICES:
+
+               /* Offset to first device entry */
+               offset = sizeof(struct irda_device_list) -
+                       sizeof(struct irda_device_info);
+
+               if (len < offset) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Ask lmp for the current discovery log */
+               discoveries = irlmp_get_discoveries(&list.len, self->mask.word,
+                                                   self->nslots);
+               /* Check if the we got some results */
+               if (discoveries == NULL) {
+                       err = -EAGAIN;
+                       goto out;               /* Didn't find any devices */
+               }
+
+               /* Write total list length back to client */
+               if (copy_to_user(optval, &list, offset))
+                       err = -EFAULT;
+
+               /* Copy the list itself - watch for overflow */
+               if (list.len > 2048) {
+                       err = -EINVAL;
+                       goto bed;
+               }
+               total = offset + (list.len * sizeof(struct irda_device_info));
+               if (total > len)
+                       total = len;
+               if (copy_to_user(optval+offset, discoveries, total - offset))
+                       err = -EFAULT;
+
+               /* Write total number of bytes used back to client */
+               if (put_user(total, optlen))
+                       err = -EFAULT;
+bed:
+               /* Free up our buffer */
+               kfree(discoveries);
+               break;
+       case IRLMP_MAX_SDU_SIZE:
+               val = self->max_data_size;
+               len = sizeof(int);
+               if (put_user(len, optlen)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               if (copy_to_user(optval, &val, len)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               break;
+       case IRLMP_IAS_GET:
+               /* The user want an object from our local IAS database.
+                * We just need to query the IAS and return the value
+                * that we found */
+
+               /* Check that the user has allocated the right space for us */
+               if (len != sizeof(struct irda_ias_set)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Copy query to the driver. */
+               ias_opt = memdup_user(optval, len);
+               if (IS_ERR(ias_opt)) {
+                       err = PTR_ERR(ias_opt);
+                       goto out;
+               }
+
+               /* Find the object we target.
+                * If the user gives us an empty string, we use the object
+                * associated with this socket. This will workaround
+                * duplicated class name - Jean II */
+               if(ias_opt->irda_class_name[0] == '\0')
+                       ias_obj = self->ias_obj;
+               else
+                       ias_obj = irias_find_object(ias_opt->irda_class_name);
+               if(ias_obj == (struct ias_object *) NULL) {
+                       kfree(ias_opt);
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Find the attribute (in the object) we target */
+               ias_attr = irias_find_attrib(ias_obj,
+                                            ias_opt->irda_attrib_name);
+               if(ias_attr == (struct ias_attrib *) NULL) {
+                       kfree(ias_opt);
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Translate from internal to user structure */
+               err = irda_extract_ias_value(ias_opt, ias_attr->value);
+               if(err) {
+                       kfree(ias_opt);
+                       goto out;
+               }
+
+               /* Copy reply to the user */
+               if (copy_to_user(optval, ias_opt,
+                                sizeof(struct irda_ias_set))) {
+                       kfree(ias_opt);
+                       err = -EFAULT;
+                       goto out;
+               }
+               /* Note : don't need to put optlen, we checked it */
+               kfree(ias_opt);
+               break;
+       case IRLMP_IAS_QUERY:
+               /* The user want an object from a remote IAS database.
+                * We need to use IAP to query the remote database and
+                * then wait for the answer to come back. */
+
+               /* Check that the user has allocated the right space for us */
+               if (len != sizeof(struct irda_ias_set)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Copy query to the driver. */
+               ias_opt = memdup_user(optval, len);
+               if (IS_ERR(ias_opt)) {
+                       err = PTR_ERR(ias_opt);
+                       goto out;
+               }
+
+               /* At this point, there are two cases...
+                * 1) the socket is connected - that's the easy case, we
+                *      just query the device we are connected to...
+                * 2) the socket is not connected - the user doesn't want
+                *      to connect and/or may not have a valid service name
+                *      (so can't create a fake connection). In this case,
+                *      we assume that the user pass us a valid destination
+                *      address in the requesting structure...
+                */
+               if(self->daddr != DEV_ADDR_ANY) {
+                       /* We are connected - reuse known daddr */
+                       daddr = self->daddr;
+               } else {
+                       /* We are not connected, we must specify a valid
+                        * destination address */
+                       daddr = ias_opt->daddr;
+                       if((!daddr) || (daddr == DEV_ADDR_ANY)) {
+                               kfree(ias_opt);
+                               err = -EINVAL;
+                               goto out;
+                       }
+               }
+
+               /* Check that we can proceed with IAP */
+               if (self->iriap) {
+                       net_warn_ratelimited("%s: busy with a previous query\n",
+                                            __func__);
+                       kfree(ias_opt);
+                       err = -EBUSY;
+                       goto out;
+               }
+
+               self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+                                        irda_getvalue_confirm);
+
+               if (self->iriap == NULL) {
+                       kfree(ias_opt);
+                       err = -ENOMEM;
+                       goto out;
+               }
+
+               /* Treat unexpected wakeup as disconnect */
+               self->errno = -EHOSTUNREACH;
+
+               /* Query remote LM-IAS */
+               iriap_getvaluebyclass_request(self->iriap,
+                                             self->saddr, daddr,
+                                             ias_opt->irda_class_name,
+                                             ias_opt->irda_attrib_name);
+
+               /* Wait for answer, if not yet finished (or failed) */
+               if (wait_event_interruptible(self->query_wait,
+                                            (self->iriap == NULL))) {
+                       /* pending request uses copy of ias_opt-content
+                        * we can free it regardless! */
+                       kfree(ias_opt);
+                       /* Treat signals as disconnect */
+                       err = -EHOSTUNREACH;
+                       goto out;
+               }
+
+               /* Check what happened */
+               if (self->errno)
+               {
+                       kfree(ias_opt);
+                       /* Requested object/attribute doesn't exist */
+                       if((self->errno == IAS_CLASS_UNKNOWN) ||
+                          (self->errno == IAS_ATTRIB_UNKNOWN))
+                               err = -EADDRNOTAVAIL;
+                       else
+                               err = -EHOSTUNREACH;
+
+                       goto out;
+               }
+
+               /* Translate from internal to user structure */
+               err = irda_extract_ias_value(ias_opt, self->ias_result);
+               if (self->ias_result)
+                       irias_delete_value(self->ias_result);
+               if (err) {
+                       kfree(ias_opt);
+                       goto out;
+               }
+
+               /* Copy reply to the user */
+               if (copy_to_user(optval, ias_opt,
+                                sizeof(struct irda_ias_set))) {
+                       kfree(ias_opt);
+                       err = -EFAULT;
+                       goto out;
+               }
+               /* Note : don't need to put optlen, we checked it */
+               kfree(ias_opt);
+               break;
+       case IRLMP_WAITDEVICE:
+               /* This function is just another way of seeing life ;-)
+                * IRLMP_ENUMDEVICES assumes that you have a static network,
+                * and that you just want to pick one of the devices present.
+                * On the other hand, in here we assume that no device is
+                * present and that at some point in the future a device will
+                * come into range. When this device arrive, we just wake
+                * up the caller, so that he has time to connect to it before
+                * the device goes away...
+                * Note : once the node has been discovered for more than a
+                * few second, it won't trigger this function, unless it
+                * goes away and come back changes its hint bits (so we
+                * might call it IRLMP_WAITNEWDEVICE).
+                */
+
+               /* Check that the user is passing us an int */
+               if (len != sizeof(int)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+               /* Get timeout in ms (max time we block the caller) */
+               if (get_user(val, (int __user *)optval)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               /* Tell IrLMP we want to be notified */
+               irlmp_update_client(self->ckey, self->mask.word,
+                                   irda_selective_discovery_indication,
+                                   NULL, (void *) self);
+
+               /* Do some discovery (and also return cached results) */
+               irlmp_discovery_request(self->nslots);
+
+               /* Wait until a node is discovered */
+               if (!self->cachedaddr) {
+                       pr_debug("%s(), nothing discovered yet, going to sleep...\n",
+                                __func__);
+
+                       /* Set watchdog timer to expire in <val> ms. */
+                       self->errno = 0;
+                       setup_timer(&self->watchdog, irda_discovery_timeout,
+                                       (unsigned long)self);
+                       mod_timer(&self->watchdog,
+                                 jiffies + msecs_to_jiffies(val));
+
+                       /* Wait for IR-LMP to call us back */
+                       err = __wait_event_interruptible(self->query_wait,
+                             (self->cachedaddr != 0 || self->errno == -ETIME));
+
+                       /* If watchdog is still activated, kill it! */
+                       del_timer(&(self->watchdog));
+
+                       pr_debug("%s(), ...waking up !\n", __func__);
+
+                       if (err != 0)
+                               goto out;
+               }
+               else
+                       pr_debug("%s(), found immediately !\n",
+                                __func__);
+
+               /* Tell IrLMP that we have been notified */
+               irlmp_update_client(self->ckey, self->mask.word,
+                                   NULL, NULL, NULL);
+
+               /* Check if the we got some results */
+               if (!self->cachedaddr) {
+                       err = -EAGAIN;          /* Didn't find any devices */
+                       goto out;
+               }
+               daddr = self->cachedaddr;
+               /* Cleanup */
+               self->cachedaddr = 0;
+
+               /* We return the daddr of the device that trigger the
+                * wakeup. As irlmp pass us only the new devices, we
+                * are sure that it's not an old device.
+                * If the user want more details, he should query
+                * the whole discovery log and pick one device...
+                */
+               if (put_user(daddr, (int __user *)optval)) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
+               break;
+       default:
+               err = -ENOPROTOOPT;
+       }
+
+out:
+
+       release_sock(sk);
+
+       return err;
+}
+
+static const struct net_proto_family irda_family_ops = {
+       .family = PF_IRDA,
+       .create = irda_create,
+       .owner  = THIS_MODULE,
+};
+
+static const struct proto_ops irda_stream_ops = {
+       .family =       PF_IRDA,
+       .owner =        THIS_MODULE,
+       .release =      irda_release,
+       .bind =         irda_bind,
+       .connect =      irda_connect,
+       .socketpair =   sock_no_socketpair,
+       .accept =       irda_accept,
+       .getname =      irda_getname,
+       .poll =         irda_poll,
+       .ioctl =        irda_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = irda_compat_ioctl,
+#endif
+       .listen =       irda_listen,
+       .shutdown =     irda_shutdown,
+       .setsockopt =   irda_setsockopt,
+       .getsockopt =   irda_getsockopt,
+       .sendmsg =      irda_sendmsg,
+       .recvmsg =      irda_recvmsg_stream,
+       .mmap =         sock_no_mmap,
+       .sendpage =     sock_no_sendpage,
+};
+
+static const struct proto_ops irda_seqpacket_ops = {
+       .family =       PF_IRDA,
+       .owner =        THIS_MODULE,
+       .release =      irda_release,
+       .bind =         irda_bind,
+       .connect =      irda_connect,
+       .socketpair =   sock_no_socketpair,
+       .accept =       irda_accept,
+       .getname =      irda_getname,
+       .poll =         datagram_poll,
+       .ioctl =        irda_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = irda_compat_ioctl,
+#endif
+       .listen =       irda_listen,
+       .shutdown =     irda_shutdown,
+       .setsockopt =   irda_setsockopt,
+       .getsockopt =   irda_getsockopt,
+       .sendmsg =      irda_sendmsg,
+       .recvmsg =      irda_recvmsg_dgram,
+       .mmap =         sock_no_mmap,
+       .sendpage =     sock_no_sendpage,
+};
+
+static const struct proto_ops irda_dgram_ops = {
+       .family =       PF_IRDA,
+       .owner =        THIS_MODULE,
+       .release =      irda_release,
+       .bind =         irda_bind,
+       .connect =      irda_connect,
+       .socketpair =   sock_no_socketpair,
+       .accept =       irda_accept,
+       .getname =      irda_getname,
+       .poll =         datagram_poll,
+       .ioctl =        irda_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = irda_compat_ioctl,
+#endif
+       .listen =       irda_listen,
+       .shutdown =     irda_shutdown,
+       .setsockopt =   irda_setsockopt,
+       .getsockopt =   irda_getsockopt,
+       .sendmsg =      irda_sendmsg_dgram,
+       .recvmsg =      irda_recvmsg_dgram,
+       .mmap =         sock_no_mmap,
+       .sendpage =     sock_no_sendpage,
+};
+
+#ifdef CONFIG_IRDA_ULTRA
+static const struct proto_ops irda_ultra_ops = {
+       .family =       PF_IRDA,
+       .owner =        THIS_MODULE,
+       .release =      irda_release,
+       .bind =         irda_bind,
+       .connect =      sock_no_connect,
+       .socketpair =   sock_no_socketpair,
+       .accept =       sock_no_accept,
+       .getname =      irda_getname,
+       .poll =         datagram_poll,
+       .ioctl =        irda_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = irda_compat_ioctl,
+#endif
+       .listen =       sock_no_listen,
+       .shutdown =     irda_shutdown,
+       .setsockopt =   irda_setsockopt,
+       .getsockopt =   irda_getsockopt,
+       .sendmsg =      irda_sendmsg_ultra,
+       .recvmsg =      irda_recvmsg_dgram,
+       .mmap =         sock_no_mmap,
+       .sendpage =     sock_no_sendpage,
+};
+#endif /* CONFIG_IRDA_ULTRA */
+
+/*
+ * Function irsock_init (pro)
+ *
+ *    Initialize IrDA protocol
+ *
+ */
+int __init irsock_init(void)
+{
+       int rc = proto_register(&irda_proto, 0);
+
+       if (rc == 0)
+               rc = sock_register(&irda_family_ops);
+
+       return rc;
+}
+
+/*
+ * Function irsock_cleanup (void)
+ *
+ *    Remove IrDA protocol
+ *
+ */
+void irsock_cleanup(void)
+{
+       sock_unregister(PF_IRDA);
+       proto_unregister(&irda_proto);
+}
diff --git a/drivers/staging/irda/net/discovery.c b/drivers/staging/irda/net/discovery.c
new file mode 100644 (file)
index 0000000..364d70a
--- /dev/null
@@ -0,0 +1,417 @@
+/*********************************************************************
+ *
+ * Filename:      discovery.c
+ * Version:       0.1
+ * Description:   Routines for handling discoveries at the IrLMP layer
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Tue Apr  6 15:33:50 1999
+ * Modified at:   Sat Oct  9 17:11:31 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Modified at:   Fri May 28  3:11 CST 1999
+ * Modified by:   Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
+ *
+ *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlmp.h>
+
+#include <net/irda/discovery.h>
+
+#include <asm/unaligned.h>
+
+/*
+ * Function irlmp_add_discovery (cachelog, discovery)
+ *
+ *    Add a new discovery to the cachelog, and remove any old discoveries
+ *    from the same device
+ *
+ * Note : we try to preserve the time this device was *first* discovered
+ * (as opposed to the time of last discovery used for cleanup). This is
+ * used by clients waiting for discovery events to tell if the device
+ * discovered is "new" or just the same old one. They can't rely there
+ * on a binary flag (new/old), because not all discovery events are
+ * propagated to them, and they might not always listen, so they would
+ * miss some new devices popping up...
+ * Jean II
+ */
+void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
+{
+       discovery_t *discovery, *node;
+       unsigned long flags;
+
+       /* Set time of first discovery if node is new (see below) */
+       new->firststamp = new->timestamp;
+
+       spin_lock_irqsave(&cachelog->hb_spinlock, flags);
+
+       /*
+        * Remove all discoveries of devices that has previously been
+        * discovered on the same link with the same name (info), or the
+        * same daddr. We do this since some devices (mostly PDAs) change
+        * their device address between every discovery.
+        */
+       discovery = (discovery_t *) hashbin_get_first(cachelog);
+       while (discovery != NULL ) {
+               node = discovery;
+
+               /* Be sure to stay one item ahead */
+               discovery = (discovery_t *) hashbin_get_next(cachelog);
+
+               if ((node->data.saddr == new->data.saddr) &&
+                   ((node->data.daddr == new->data.daddr) ||
+                    (strcmp(node->data.info, new->data.info) == 0)))
+               {
+                       /* This discovery is a previous discovery
+                        * from the same device, so just remove it
+                        */
+                       hashbin_remove_this(cachelog, (irda_queue_t *) node);
+                       /* Check if hints bits are unchanged */
+                       if (get_unaligned((__u16 *)node->data.hints) == get_unaligned((__u16 *)new->data.hints))
+                               /* Set time of first discovery for this node */
+                               new->firststamp = node->firststamp;
+                       kfree(node);
+               }
+       }
+
+       /* Insert the new and updated version */
+       hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL);
+
+       spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
+}
+
+/*
+ * Function irlmp_add_discovery_log (cachelog, log)
+ *
+ *    Merge a disovery log into the cachelog.
+ *
+ */
+void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
+{
+       discovery_t *discovery;
+
+       /*
+        *  If log is missing this means that IrLAP was unable to perform the
+        *  discovery, so restart discovery again with just the half timeout
+        *  of the normal one.
+        */
+       /* Well... It means that there was nobody out there - Jean II */
+       if (log == NULL) {
+               /* irlmp_start_discovery_timer(irlmp, 150); */
+               return;
+       }
+
+       /*
+        * Locking : we are the only owner of this discovery log, so
+        * no need to lock it.
+        * We just need to lock the global log in irlmp_add_discovery().
+        */
+       discovery = (discovery_t *) hashbin_remove_first(log);
+       while (discovery != NULL) {
+               irlmp_add_discovery(cachelog, discovery);
+
+               discovery = (discovery_t *) hashbin_remove_first(log);
+       }
+
+       /* Delete the now empty log */
+       hashbin_delete(log, (FREE_FUNC) kfree);
+}
+
+/*
+ * Function irlmp_expire_discoveries (log, saddr, force)
+ *
+ *    Go through all discoveries and expire all that has stayed too long
+ *
+ * Note : this assume that IrLAP won't change its saddr, which
+ * currently is a valid assumption...
+ */
+void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
+{
+       discovery_t *           discovery;
+       discovery_t *           curr;
+       unsigned long           flags;
+       discinfo_t *            buffer = NULL;
+       int                     n;              /* Size of the full log */
+       int                     i = 0;          /* How many we expired */
+
+       IRDA_ASSERT(log != NULL, return;);
+       spin_lock_irqsave(&log->hb_spinlock, flags);
+
+       discovery = (discovery_t *) hashbin_get_first(log);
+       while (discovery != NULL) {
+               /* Be sure to be one item ahead */
+               curr = discovery;
+               discovery = (discovery_t *) hashbin_get_next(log);
+
+               /* Test if it's time to expire this discovery */
+               if ((curr->data.saddr == saddr) &&
+                   (force ||
+                    ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT)))
+               {
+                       /* Create buffer as needed.
+                        * As this function get called a lot and most time
+                        * we don't have anything to put in the log (we are
+                        * quite picky), we can save a lot of overhead
+                        * by not calling kmalloc. Jean II */
+                       if(buffer == NULL) {
+                               /* Create the client specific buffer */
+                               n = HASHBIN_GET_SIZE(log);
+                               buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
+                               if (buffer == NULL) {
+                                       spin_unlock_irqrestore(&log->hb_spinlock, flags);
+                                       return;
+                               }
+
+                       }
+
+                       /* Copy discovery information */
+                       memcpy(&(buffer[i]), &(curr->data),
+                              sizeof(discinfo_t));
+                       i++;
+
+                       /* Remove it from the log */
+                       curr = hashbin_remove_this(log, (irda_queue_t *) curr);
+                       kfree(curr);
+               }
+       }
+
+       /* Drop the spinlock before calling the higher layers, as
+        * we can't guarantee they won't call us back and create a
+        * deadlock. We will work on our own private data, so we
+        * don't care to be interrupted. - Jean II */
+       spin_unlock_irqrestore(&log->hb_spinlock, flags);
+
+       if(buffer == NULL)
+               return;
+
+       /* Tell IrLMP and registered clients about it */
+       irlmp_discovery_expiry(buffer, i);
+
+       /* Free up our buffer */
+       kfree(buffer);
+}
+
+#if 0
+/*
+ * Function irlmp_dump_discoveries (log)
+ *
+ *    Print out all discoveries in log
+ *
+ */
+void irlmp_dump_discoveries(hashbin_t *log)
+{
+       discovery_t *discovery;
+
+       IRDA_ASSERT(log != NULL, return;);
+
+       discovery = (discovery_t *) hashbin_get_first(log);
+       while (discovery != NULL) {
+               pr_debug("Discovery:\n");
+               pr_debug("  daddr=%08x\n", discovery->data.daddr);
+               pr_debug("  saddr=%08x\n", discovery->data.saddr);
+               pr_debug("  nickname=%s\n", discovery->data.info);
+
+               discovery = (discovery_t *) hashbin_get_next(log);
+       }
+}
+#endif
+
+/*
+ * Function irlmp_copy_discoveries (log, pn, mask)
+ *
+ *    Copy all discoveries in a buffer
+ *
+ * This function implement a safe way for lmp clients to access the
+ * discovery log. The basic problem is that we don't want the log
+ * to change (add/remove) while the client is reading it. If the
+ * lmp client manipulate directly the hashbin, he is sure to get
+ * into troubles...
+ * The idea is that we copy all the current discovery log in a buffer
+ * which is specific to the client and pass this copy to him. As we
+ * do this operation with the spinlock grabbed, we are safe...
+ * Note : we don't want those clients to grab the spinlock, because
+ * we have no control on how long they will hold it...
+ * Note : we choose to copy the log in "struct irda_device_info" to
+ * save space...
+ * Note : the client must kfree himself() the log...
+ * Jean II
+ */
+struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
+                                               __u16 mask, int old_entries)
+{
+       discovery_t *           discovery;
+       unsigned long           flags;
+       discinfo_t *            buffer = NULL;
+       int                     j_timeout = (sysctl_discovery_timeout * HZ);
+       int                     n;              /* Size of the full log */
+       int                     i = 0;          /* How many we picked */
+
+       IRDA_ASSERT(pn != NULL, return NULL;);
+       IRDA_ASSERT(log != NULL, return NULL;);
+
+       /* Save spin lock */
+       spin_lock_irqsave(&log->hb_spinlock, flags);
+
+       discovery = (discovery_t *) hashbin_get_first(log);
+       while (discovery != NULL) {
+               /* Mask out the ones we don't want :
+                * We want to match the discovery mask, and to get only
+                * the most recent one (unless we want old ones) */
+               if ((get_unaligned((__u16 *)discovery->data.hints) & mask) &&
+                   ((old_entries) ||
+                    ((jiffies - discovery->firststamp) < j_timeout))) {
+                       /* Create buffer as needed.
+                        * As this function get called a lot and most time
+                        * we don't have anything to put in the log (we are
+                        * quite picky), we can save a lot of overhead
+                        * by not calling kmalloc. Jean II */
+                       if(buffer == NULL) {
+                               /* Create the client specific buffer */
+                               n = HASHBIN_GET_SIZE(log);
+                               buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
+                               if (buffer == NULL) {
+                                       spin_unlock_irqrestore(&log->hb_spinlock, flags);
+                                       return NULL;
+                               }
+
+                       }
+
+                       /* Copy discovery information */
+                       memcpy(&(buffer[i]), &(discovery->data),
+                              sizeof(discinfo_t));
+                       i++;
+               }
+               discovery = (discovery_t *) hashbin_get_next(log);
+       }
+
+       spin_unlock_irqrestore(&log->hb_spinlock, flags);
+
+       /* Get the actual number of device in the buffer and return */
+       *pn = i;
+       return buffer;
+}
+
+#ifdef CONFIG_PROC_FS
+static inline discovery_t *discovery_seq_idx(loff_t pos)
+
+{
+       discovery_t *discovery;
+
+       for (discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog);
+            discovery != NULL;
+            discovery = (discovery_t *) hashbin_get_next(irlmp->cachelog)) {
+               if (pos-- == 0)
+                       break;
+       }
+
+       return discovery;
+}
+
+static void *discovery_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       spin_lock_irq(&irlmp->cachelog->hb_spinlock);
+       return *pos ? discovery_seq_idx(*pos - 1) : SEQ_START_TOKEN;
+}
+
+static void *discovery_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       ++*pos;
+       return (v == SEQ_START_TOKEN)
+               ? (void *) hashbin_get_first(irlmp->cachelog)
+               : (void *) hashbin_get_next(irlmp->cachelog);
+}
+
+static void discovery_seq_stop(struct seq_file *seq, void *v)
+{
+       spin_unlock_irq(&irlmp->cachelog->hb_spinlock);
+}
+
+static int discovery_seq_show(struct seq_file *seq, void *v)
+{
+       if (v == SEQ_START_TOKEN)
+               seq_puts(seq, "IrLMP: Discovery log:\n\n");
+       else {
+               const discovery_t *discovery = v;
+
+               seq_printf(seq, "nickname: %s, hint: 0x%02x%02x",
+                          discovery->data.info,
+                          discovery->data.hints[0],
+                          discovery->data.hints[1]);
+#if 0
+               if ( discovery->data.hints[0] & HINT_PNP)
+                       seq_puts(seq, "PnP Compatible ");
+               if ( discovery->data.hints[0] & HINT_PDA)
+                       seq_puts(seq, "PDA/Palmtop ");
+               if ( discovery->data.hints[0] & HINT_COMPUTER)
+                       seq_puts(seq, "Computer ");
+               if ( discovery->data.hints[0] & HINT_PRINTER)
+                       seq_puts(seq, "Printer ");
+               if ( discovery->data.hints[0] & HINT_MODEM)
+                       seq_puts(seq, "Modem ");
+               if ( discovery->data.hints[0] & HINT_FAX)
+                       seq_puts(seq, "Fax ");
+               if ( discovery->data.hints[0] & HINT_LAN)
+                       seq_puts(seq, "LAN Access ");
+
+               if ( discovery->data.hints[1] & HINT_TELEPHONY)
+                       seq_puts(seq, "Telephony ");
+               if ( discovery->data.hints[1] & HINT_FILE_SERVER)
+                       seq_puts(seq, "File Server ");
+               if ( discovery->data.hints[1] & HINT_COMM)
+                       seq_puts(seq, "IrCOMM ");
+               if ( discovery->data.hints[1] & HINT_OBEX)
+                       seq_puts(seq, "IrOBEX ");
+#endif
+               seq_printf(seq,", saddr: 0x%08x, daddr: 0x%08x\n\n",
+                              discovery->data.saddr,
+                              discovery->data.daddr);
+
+               seq_putc(seq, '\n');
+       }
+       return 0;
+}
+
+static const struct seq_operations discovery_seq_ops = {
+       .start  = discovery_seq_start,
+       .next   = discovery_seq_next,
+       .stop   = discovery_seq_stop,
+       .show   = discovery_seq_show,
+};
+
+static int discovery_seq_open(struct inode *inode, struct file *file)
+{
+       IRDA_ASSERT(irlmp != NULL, return -EINVAL;);
+
+       return seq_open(file, &discovery_seq_ops);
+}
+
+const struct file_operations discovery_seq_fops = {
+       .owner          = THIS_MODULE,
+       .open           = discovery_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+#endif
diff --git a/drivers/staging/irda/net/ircomm/Kconfig b/drivers/staging/irda/net/ircomm/Kconfig
new file mode 100644 (file)
index 0000000..19492c1
--- /dev/null
@@ -0,0 +1,12 @@
+config IRCOMM
+       tristate "IrCOMM protocol"
+       depends on IRDA && TTY
+       help
+         Say Y here if you want to build support for the IrCOMM protocol.
+         To compile it as modules, choose M here: the modules will be
+         called ircomm and ircomm_tty.
+         IrCOMM implements serial port emulation, and makes it possible to
+         use all existing applications that understands TTY's with an
+         infrared link.  Thus you should be able to use application like PPP,
+         minicom and others.
+
diff --git a/drivers/staging/irda/net/ircomm/Makefile b/drivers/staging/irda/net/ircomm/Makefile
new file mode 100644 (file)
index 0000000..ab23b5b
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for the Linux IrDA IrCOMM protocol layer.
+#
+
+obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
+
+ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
+ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
diff --git a/drivers/staging/irda/net/ircomm/ircomm_core.c b/drivers/staging/irda/net/ircomm/ircomm_core.c
new file mode 100644 (file)
index 0000000..3af2195
--- /dev/null
@@ -0,0 +1,563 @@
+/*********************************************************************
+ *
+ * Filename:      ircomm_core.c
+ * Version:       1.0
+ * Description:   IrCOMM service interface
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Jun  6 20:37:34 1999
+ * Modified at:   Tue Dec 21 13:26:41 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irias_object.h>
+
+#include <net/irda/ircomm_event.h>
+#include <net/irda/ircomm_lmp.h>
+#include <net/irda/ircomm_ttp.h>
+#include <net/irda/ircomm_param.h>
+#include <net/irda/ircomm_core.h>
+
+static int __ircomm_close(struct ircomm_cb *self);
+static void ircomm_control_indication(struct ircomm_cb *self,
+                                     struct sk_buff *skb, int clen);
+
+#ifdef CONFIG_PROC_FS
+extern struct proc_dir_entry *proc_irda;
+static int ircomm_seq_open(struct inode *, struct file *);
+
+static const struct file_operations ircomm_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = ircomm_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+#endif /* CONFIG_PROC_FS */
+
+hashbin_t *ircomm = NULL;
+
+static int __init ircomm_init(void)
+{
+       ircomm = hashbin_new(HB_LOCK);
+       if (ircomm == NULL) {
+               net_err_ratelimited("%s(), can't allocate hashbin!\n",
+                                   __func__);
+               return -ENOMEM;
+       }
+
+#ifdef CONFIG_PROC_FS
+       { struct proc_dir_entry *ent;
+       ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops);
+       if (!ent) {
+               printk(KERN_ERR "ircomm_init: can't create /proc entry!\n");
+               return -ENODEV;
+       }
+       }
+#endif /* CONFIG_PROC_FS */
+
+       net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n");
+
+       return 0;
+}
+
+static void __exit ircomm_cleanup(void)
+{
+       hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
+
+#ifdef CONFIG_PROC_FS
+       remove_proc_entry("ircomm", proc_irda);
+#endif /* CONFIG_PROC_FS */
+}
+
+/*
+ * Function ircomm_open (client_notify)
+ *
+ *    Start a new IrCOMM instance
+ *
+ */
+struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
+{
+       struct ircomm_cb *self = NULL;
+       int ret;
+
+       pr_debug("%s(), service_type=0x%02x\n", __func__ ,
+                service_type);
+
+       IRDA_ASSERT(ircomm != NULL, return NULL;);
+
+       self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL);
+       if (self == NULL)
+               return NULL;
+
+       self->notify = *notify;
+       self->magic = IRCOMM_MAGIC;
+
+       /* Check if we should use IrLMP or IrTTP */
+       if (service_type & IRCOMM_3_WIRE_RAW) {
+               self->flow_status = FLOW_START;
+               ret = ircomm_open_lsap(self);
+       } else
+               ret = ircomm_open_tsap(self);
+
+       if (ret < 0) {
+               kfree(self);
+               return NULL;
+       }
+
+       self->service_type = service_type;
+       self->line = line;
+
+       hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
+
+       ircomm_next_state(self, IRCOMM_IDLE);
+
+       return self;
+}
+
+EXPORT_SYMBOL(ircomm_open);
+
+/*
+ * Function ircomm_close_instance (self)
+ *
+ *    Remove IrCOMM instance
+ *
+ */
+static int __ircomm_close(struct ircomm_cb *self)
+{
+       /* Disconnect link if any */
+       ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
+
+       /* Remove TSAP */
+       if (self->tsap) {
+               irttp_close_tsap(self->tsap);
+               self->tsap = NULL;
+       }
+
+       /* Remove LSAP */
+       if (self->lsap) {
+               irlmp_close_lsap(self->lsap);
+               self->lsap = NULL;
+       }
+       self->magic = 0;
+
+       kfree(self);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_close (self)
+ *
+ *    Closes and removes the specified IrCOMM instance
+ *
+ */
+int ircomm_close(struct ircomm_cb *self)
+{
+       struct ircomm_cb *entry;
+
+       IRDA_ASSERT(self != NULL, return -EIO;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
+
+       entry = hashbin_remove(ircomm, self->line, NULL);
+
+       IRDA_ASSERT(entry == self, return -1;);
+
+       return __ircomm_close(self);
+}
+
+EXPORT_SYMBOL(ircomm_close);
+
+/*
+ * Function ircomm_connect_request (self, service_type)
+ *
+ *    Impl. of this function is differ from one of the reference. This
+ *    function does discovery as well as sending connect request
+ *
+ */
+int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
+                          __u32 saddr, __u32 daddr, struct sk_buff *skb,
+                          __u8 service_type)
+{
+       struct ircomm_info info;
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+
+       self->service_type= service_type;
+
+       info.dlsap_sel = dlsap_sel;
+       info.saddr = saddr;
+       info.daddr = daddr;
+
+       ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info);
+
+       return ret;
+}
+
+EXPORT_SYMBOL(ircomm_connect_request);
+
+/*
+ * Function ircomm_connect_indication (self, qos, skb)
+ *
+ *    Notify user layer about the incoming connection
+ *
+ */
+void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
+                              struct ircomm_info *info)
+{
+       /*
+        * If there are any data hiding in the control channel, we must
+        * deliver it first. The side effect is that the control channel
+        * will be removed from the skb
+        */
+       if (self->notify.connect_indication)
+               self->notify.connect_indication(self->notify.instance, self,
+                                               info->qos, info->max_data_size,
+                                               info->max_header_size, skb);
+       else {
+               pr_debug("%s(), missing handler\n", __func__);
+       }
+}
+
+/*
+ * Function ircomm_connect_response (self, userdata, max_sdu_size)
+ *
+ *    User accepts connection
+ *
+ */
+int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
+{
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+
+       ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
+
+       return ret;
+}
+
+EXPORT_SYMBOL(ircomm_connect_response);
+
+/*
+ * Function connect_confirm (self, skb)
+ *
+ *    Notify user layer that the link is now connected
+ *
+ */
+void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
+                           struct ircomm_info *info)
+{
+       if (self->notify.connect_confirm )
+               self->notify.connect_confirm(self->notify.instance,
+                                            self, info->qos,
+                                            info->max_data_size,
+                                            info->max_header_size, skb);
+       else {
+               pr_debug("%s(), missing handler\n", __func__);
+       }
+}
+
+/*
+ * Function ircomm_data_request (self, userdata)
+ *
+ *    Send IrCOMM data to peer device
+ *
+ */
+int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb)
+{
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -EFAULT;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
+       IRDA_ASSERT(skb != NULL, return -EFAULT;);
+
+       ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
+
+       return ret;
+}
+
+EXPORT_SYMBOL(ircomm_data_request);
+
+/*
+ * Function ircomm_data_indication (self, skb)
+ *
+ *    Data arrived, so deliver it to user
+ *
+ */
+void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
+{
+       IRDA_ASSERT(skb->len > 0, return;);
+
+       if (self->notify.data_indication)
+               self->notify.data_indication(self->notify.instance, self, skb);
+       else {
+               pr_debug("%s(), missing handler\n", __func__);
+       }
+}
+
+/*
+ * Function ircomm_process_data (self, skb)
+ *
+ *    Data arrived which may contain control channel data
+ *
+ */
+void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
+{
+       int clen;
+
+       IRDA_ASSERT(skb->len > 0, return;);
+
+       clen = skb->data[0];
+
+       /*
+        * Input validation check: a stir4200/mcp2150 combinations sometimes
+        * results in frames with clen > remaining packet size. These are
+        * illegal; if we throw away just this frame then it seems to carry on
+        * fine
+        */
+       if (unlikely(skb->len < (clen + 1))) {
+               pr_debug("%s() throwing away illegal frame\n",
+                        __func__);
+               return;
+       }
+
+       /*
+        * If there are any data hiding in the control channel, we must
+        * deliver it first. The side effect is that the control channel
+        * will be removed from the skb
+        */
+       if (clen > 0)
+               ircomm_control_indication(self, skb, clen);
+
+       /* Remove control channel from data channel */
+       skb_pull(skb, clen+1);
+
+       if (skb->len)
+               ircomm_data_indication(self, skb);
+       else {
+               pr_debug("%s(), data was control info only!\n",
+                        __func__);
+       }
+}
+
+/*
+ * Function ircomm_control_request (self, params)
+ *
+ *    Send control data to peer device
+ *
+ */
+int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb)
+{
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -EFAULT;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
+       IRDA_ASSERT(skb != NULL, return -EFAULT;);
+
+       ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
+
+       return ret;
+}
+
+EXPORT_SYMBOL(ircomm_control_request);
+
+/*
+ * Function ircomm_control_indication (self, skb)
+ *
+ *    Data has arrived on the control channel
+ *
+ */
+static void ircomm_control_indication(struct ircomm_cb *self,
+                                     struct sk_buff *skb, int clen)
+{
+       /* Use udata for delivering data on the control channel */
+       if (self->notify.udata_indication) {
+               struct sk_buff *ctrl_skb;
+
+               /* We don't own the skb, so clone it */
+               ctrl_skb = skb_clone(skb, GFP_ATOMIC);
+               if (!ctrl_skb)
+                       return;
+
+               /* Remove data channel from control channel */
+               skb_trim(ctrl_skb, clen+1);
+
+               self->notify.udata_indication(self->notify.instance, self,
+                                             ctrl_skb);
+
+               /* Drop reference count -
+                * see ircomm_tty_control_indication(). */
+               dev_kfree_skb(ctrl_skb);
+       } else {
+               pr_debug("%s(), missing handler\n", __func__);
+       }
+}
+
+/*
+ * Function ircomm_disconnect_request (self, userdata, priority)
+ *
+ *    User layer wants to disconnect the IrCOMM connection
+ *
+ */
+int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata)
+{
+       struct ircomm_info info;
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+
+       ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
+                             &info);
+       return ret;
+}
+
+EXPORT_SYMBOL(ircomm_disconnect_request);
+
+/*
+ * Function disconnect_indication (self, skb)
+ *
+ *    Tell user that the link has been disconnected
+ *
+ */
+void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
+                                 struct ircomm_info *info)
+{
+       IRDA_ASSERT(info != NULL, return;);
+
+       if (self->notify.disconnect_indication) {
+               self->notify.disconnect_indication(self->notify.instance, self,
+                                                  info->reason, skb);
+       } else {
+               pr_debug("%s(), missing handler\n", __func__);
+       }
+}
+
+/*
+ * Function ircomm_flow_request (self, flow)
+ *
+ *
+ *
+ */
+void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+       if (self->service_type == IRCOMM_3_WIRE_RAW)
+               return;
+
+       irttp_flow_request(self->tsap, flow);
+}
+
+EXPORT_SYMBOL(ircomm_flow_request);
+
+#ifdef CONFIG_PROC_FS
+static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       struct ircomm_cb *self;
+       loff_t off = 0;
+
+       spin_lock_irq(&ircomm->hb_spinlock);
+
+       for (self = (struct ircomm_cb *) hashbin_get_first(ircomm);
+            self != NULL;
+            self = (struct ircomm_cb *) hashbin_get_next(ircomm)) {
+               if (off++ == *pos)
+                       break;
+
+       }
+       return self;
+}
+
+static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       ++*pos;
+
+       return (void *) hashbin_get_next(ircomm);
+}
+
+static void ircomm_seq_stop(struct seq_file *seq, void *v)
+{
+       spin_unlock_irq(&ircomm->hb_spinlock);
+}
+
+static int ircomm_seq_show(struct seq_file *seq, void *v)
+{
+       const struct ircomm_cb *self = v;
+
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; );
+
+       if(self->line < 0x10)
+               seq_printf(seq, "ircomm%d", self->line);
+       else
+               seq_printf(seq, "irlpt%d", self->line - 0x10);
+
+       seq_printf(seq,
+                  " state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
+                  ircomm_state[ self->state],
+                  self->slsap_sel, self->dlsap_sel);
+
+       if(self->service_type & IRCOMM_3_WIRE_RAW)
+               seq_printf(seq, " 3-wire-raw");
+       if(self->service_type & IRCOMM_3_WIRE)
+               seq_printf(seq, " 3-wire");
+       if(self->service_type & IRCOMM_9_WIRE)
+               seq_printf(seq, " 9-wire");
+       if(self->service_type & IRCOMM_CENTRONICS)
+               seq_printf(seq, " Centronics");
+       seq_putc(seq, '\n');
+
+       return 0;
+}
+
+static const struct seq_operations ircomm_seq_ops = {
+       .start  = ircomm_seq_start,
+       .next   = ircomm_seq_next,
+       .stop   = ircomm_seq_stop,
+       .show   = ircomm_seq_show,
+};
+
+static int ircomm_seq_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &ircomm_seq_ops);
+}
+#endif /* CONFIG_PROC_FS */
+
+MODULE_AUTHOR("Dag Brattli <dag@brattli.net>");
+MODULE_DESCRIPTION("IrCOMM protocol");
+MODULE_LICENSE("GPL");
+
+module_init(ircomm_init);
+module_exit(ircomm_cleanup);
diff --git a/drivers/staging/irda/net/ircomm/ircomm_event.c b/drivers/staging/irda/net/ircomm/ircomm_event.c
new file mode 100644 (file)
index 0000000..b0730ac
--- /dev/null
@@ -0,0 +1,246 @@
+/*********************************************************************
+ *
+ * Filename:      ircomm_event.c
+ * Version:       1.0
+ * Description:   IrCOMM layer state machine
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Jun  6 20:33:11 1999
+ * Modified at:   Sun Dec 12 13:44:32 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irias_object.h>
+
+#include <net/irda/ircomm_core.h>
+#include <net/irda/ircomm_event.h>
+
+static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
+                            struct sk_buff *skb, struct ircomm_info *info);
+static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
+                             struct sk_buff *skb, struct ircomm_info *info);
+static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
+                             struct sk_buff *skb, struct ircomm_info *info);
+static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
+                            struct sk_buff *skb, struct ircomm_info *info);
+
+const char *const ircomm_state[] = {
+       "IRCOMM_IDLE",
+       "IRCOMM_WAITI",
+       "IRCOMM_WAITR",
+       "IRCOMM_CONN",
+};
+
+static const char *const ircomm_event[] __maybe_unused = {
+       "IRCOMM_CONNECT_REQUEST",
+       "IRCOMM_CONNECT_RESPONSE",
+       "IRCOMM_TTP_CONNECT_INDICATION",
+       "IRCOMM_LMP_CONNECT_INDICATION",
+       "IRCOMM_TTP_CONNECT_CONFIRM",
+       "IRCOMM_LMP_CONNECT_CONFIRM",
+
+       "IRCOMM_LMP_DISCONNECT_INDICATION",
+       "IRCOMM_TTP_DISCONNECT_INDICATION",
+       "IRCOMM_DISCONNECT_REQUEST",
+
+       "IRCOMM_TTP_DATA_INDICATION",
+       "IRCOMM_LMP_DATA_INDICATION",
+       "IRCOMM_DATA_REQUEST",
+       "IRCOMM_CONTROL_REQUEST",
+       "IRCOMM_CONTROL_INDICATION",
+};
+
+static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event,
+                     struct sk_buff *skb, struct ircomm_info *info) =
+{
+       ircomm_state_idle,
+       ircomm_state_waiti,
+       ircomm_state_waitr,
+       ircomm_state_conn,
+};
+
+/*
+ * Function ircomm_state_idle (self, event, skb)
+ *
+ *    IrCOMM is currently idle
+ *
+ */
+static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
+                            struct sk_buff *skb, struct ircomm_info *info)
+{
+       int ret = 0;
+
+       switch (event) {
+       case IRCOMM_CONNECT_REQUEST:
+               ircomm_next_state(self, IRCOMM_WAITI);
+               ret = self->issue.connect_request(self, skb, info);
+               break;
+       case IRCOMM_TTP_CONNECT_INDICATION:
+       case IRCOMM_LMP_CONNECT_INDICATION:
+               ircomm_next_state(self, IRCOMM_WAITR);
+               ircomm_connect_indication(self, skb, info);
+               break;
+       default:
+               pr_debug("%s(), unknown event: %s\n", __func__ ,
+                        ircomm_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_state_waiti (self, event, skb)
+ *
+ *    The IrCOMM user has requested an IrCOMM connection to the remote
+ *    device and is awaiting confirmation
+ */
+static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
+                             struct sk_buff *skb, struct ircomm_info *info)
+{
+       int ret = 0;
+
+       switch (event) {
+       case IRCOMM_TTP_CONNECT_CONFIRM:
+       case IRCOMM_LMP_CONNECT_CONFIRM:
+               ircomm_next_state(self, IRCOMM_CONN);
+               ircomm_connect_confirm(self, skb, info);
+               break;
+       case IRCOMM_TTP_DISCONNECT_INDICATION:
+       case IRCOMM_LMP_DISCONNECT_INDICATION:
+               ircomm_next_state(self, IRCOMM_IDLE);
+               ircomm_disconnect_indication(self, skb, info);
+               break;
+       default:
+               pr_debug("%s(), unknown event: %s\n", __func__ ,
+                        ircomm_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_state_waitr (self, event, skb)
+ *
+ *    IrCOMM has received an incoming connection request and is awaiting
+ *    response from the user
+ */
+static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
+                             struct sk_buff *skb, struct ircomm_info *info)
+{
+       int ret = 0;
+
+       switch (event) {
+       case IRCOMM_CONNECT_RESPONSE:
+               ircomm_next_state(self, IRCOMM_CONN);
+               ret = self->issue.connect_response(self, skb);
+               break;
+       case IRCOMM_DISCONNECT_REQUEST:
+               ircomm_next_state(self, IRCOMM_IDLE);
+               ret = self->issue.disconnect_request(self, skb, info);
+               break;
+       case IRCOMM_TTP_DISCONNECT_INDICATION:
+       case IRCOMM_LMP_DISCONNECT_INDICATION:
+               ircomm_next_state(self, IRCOMM_IDLE);
+               ircomm_disconnect_indication(self, skb, info);
+               break;
+       default:
+               pr_debug("%s(), unknown event = %s\n", __func__ ,
+                        ircomm_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_state_conn (self, event, skb)
+ *
+ *    IrCOMM is connected to the peer IrCOMM device
+ *
+ */
+static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
+                            struct sk_buff *skb, struct ircomm_info *info)
+{
+       int ret = 0;
+
+       switch (event) {
+       case IRCOMM_DATA_REQUEST:
+               ret = self->issue.data_request(self, skb, 0);
+               break;
+       case IRCOMM_TTP_DATA_INDICATION:
+               ircomm_process_data(self, skb);
+               break;
+       case IRCOMM_LMP_DATA_INDICATION:
+               ircomm_data_indication(self, skb);
+               break;
+       case IRCOMM_CONTROL_REQUEST:
+               /* Just send a separate frame for now */
+               ret = self->issue.data_request(self, skb, skb->len);
+               break;
+       case IRCOMM_TTP_DISCONNECT_INDICATION:
+       case IRCOMM_LMP_DISCONNECT_INDICATION:
+               ircomm_next_state(self, IRCOMM_IDLE);
+               ircomm_disconnect_indication(self, skb, info);
+               break;
+       case IRCOMM_DISCONNECT_REQUEST:
+               ircomm_next_state(self, IRCOMM_IDLE);
+               ret = self->issue.disconnect_request(self, skb, info);
+               break;
+       default:
+               pr_debug("%s(), unknown event = %s\n", __func__ ,
+                        ircomm_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_do_event (self, event, skb)
+ *
+ *    Process event
+ *
+ */
+int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
+                   struct sk_buff *skb, struct ircomm_info *info)
+{
+       pr_debug("%s: state=%s, event=%s\n", __func__ ,
+                ircomm_state[self->state], ircomm_event[event]);
+
+       return (*state[self->state])(self, event, skb, info);
+}
+
+/*
+ * Function ircomm_next_state (self, state)
+ *
+ *    Switch state
+ *
+ */
+void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state)
+{
+       self->state = state;
+
+       pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
+                ircomm_state[self->state], self->service_type);
+}
diff --git a/drivers/staging/irda/net/ircomm/ircomm_lmp.c b/drivers/staging/irda/net/ircomm/ircomm_lmp.c
new file mode 100644 (file)
index 0000000..e4cc847
--- /dev/null
@@ -0,0 +1,350 @@
+/*********************************************************************
+ *
+ * Filename:      ircomm_lmp.c
+ * Version:       1.0
+ * Description:   Interface between IrCOMM and IrLMP
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Jun  6 20:48:27 1999
+ * Modified at:   Sun Dec 12 13:44:17 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Sources:       Previous IrLPT work by Thomas Davis
+ *
+ *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/init.h>
+#include <linux/gfp.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irda_device.h>      /* struct irda_skb_cb */
+
+#include <net/irda/ircomm_event.h>
+#include <net/irda/ircomm_lmp.h>
+
+
+/*
+ * Function ircomm_lmp_connect_request (self, userdata)
+ *
+ *
+ *
+ */
+static int ircomm_lmp_connect_request(struct ircomm_cb *self,
+                                     struct sk_buff *userdata,
+                                     struct ircomm_info *info)
+{
+       int ret = 0;
+
+       /* Don't forget to refcount it - should be NULL anyway */
+       if(userdata)
+               skb_get(userdata);
+
+       ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
+                                   info->saddr, info->daddr, NULL, userdata);
+       return ret;
+}
+
+/*
+ * Function ircomm_lmp_connect_response (self, skb)
+ *
+ *
+ *
+ */
+static int ircomm_lmp_connect_response(struct ircomm_cb *self,
+                                      struct sk_buff *userdata)
+{
+       struct sk_buff *tx_skb;
+
+       /* Any userdata supplied? */
+       if (userdata == NULL) {
+               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
+               if (!tx_skb)
+                       return -ENOMEM;
+
+               /* Reserve space for MUX and LAP header */
+               skb_reserve(tx_skb, LMP_MAX_HEADER);
+       } else {
+               /*
+                *  Check that the client has reserved enough space for
+                *  headers
+                */
+               IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER,
+                           return -1;);
+
+               /* Don't forget to refcount it - should be NULL anyway */
+               skb_get(userdata);
+               tx_skb = userdata;
+       }
+
+       return irlmp_connect_response(self->lsap, tx_skb);
+}
+
+static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
+                                        struct sk_buff *userdata,
+                                        struct ircomm_info *info)
+{
+       struct sk_buff *tx_skb;
+       int ret;
+
+       if (!userdata) {
+               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
+               if (!tx_skb)
+                       return -ENOMEM;
+
+               /*  Reserve space for MUX and LAP header */
+               skb_reserve(tx_skb, LMP_MAX_HEADER);
+               userdata = tx_skb;
+       } else {
+               /* Don't forget to refcount it - should be NULL anyway */
+               skb_get(userdata);
+       }
+
+       ret = irlmp_disconnect_request(self->lsap, userdata);
+
+       return ret;
+}
+
+/*
+ * Function ircomm_lmp_flow_control (skb)
+ *
+ *    This function is called when a data frame we have sent to IrLAP has
+ *    been deallocated. We do this to make sure we don't flood IrLAP with
+ *    frames, since we are not using the IrTTP flow control mechanism
+ */
+static void ircomm_lmp_flow_control(struct sk_buff *skb)
+{
+       struct irda_skb_cb *cb;
+       struct ircomm_cb *self;
+       int line;
+
+       IRDA_ASSERT(skb != NULL, return;);
+
+       cb = (struct irda_skb_cb *) skb->cb;
+
+       line = cb->line;
+
+       self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
+       if (!self) {
+               pr_debug("%s(), didn't find myself\n", __func__);
+               return;
+       }
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+       self->pkt_count--;
+
+       if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
+               pr_debug("%s(), asking TTY to start again!\n", __func__);
+               self->flow_status = FLOW_START;
+               if (self->notify.flow_indication)
+                       self->notify.flow_indication(self->notify.instance,
+                                                    self, FLOW_START);
+       }
+}
+
+/*
+ * Function ircomm_lmp_data_request (self, userdata)
+ *
+ *    Send data frame to peer device
+ *
+ */
+static int ircomm_lmp_data_request(struct ircomm_cb *self,
+                                  struct sk_buff *skb,
+                                  int not_used)
+{
+       struct irda_skb_cb *cb;
+       int ret;
+
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       cb = (struct irda_skb_cb *) skb->cb;
+
+       cb->line = self->line;
+
+       pr_debug("%s(), sending frame\n", __func__);
+
+       /* Don't forget to refcount it - see ircomm_tty_do_softint() */
+       skb_get(skb);
+
+       skb_orphan(skb);
+       skb->destructor = ircomm_lmp_flow_control;
+
+       if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
+               pr_debug("%s(), asking TTY to slow down!\n", __func__);
+               self->flow_status = FLOW_STOP;
+               if (self->notify.flow_indication)
+                       self->notify.flow_indication(self->notify.instance,
+                                                    self, FLOW_STOP);
+       }
+       ret = irlmp_data_request(self->lsap, skb);
+       if (ret) {
+               net_err_ratelimited("%s(), failed\n", __func__);
+               /* irlmp_data_request already free the packet */
+       }
+
+       return ret;
+}
+
+/*
+ * Function ircomm_lmp_data_indication (instance, sap, skb)
+ *
+ *    Incoming data which we must deliver to the state machine, to check
+ *    we are still connected.
+ */
+static int ircomm_lmp_data_indication(void *instance, void *sap,
+                                     struct sk_buff *skb)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
+
+       /* Drop reference count - see ircomm_tty_data_indication(). */
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
+ *                                       max_header_size, skb)
+ *
+ *    Connection has been confirmed by peer device
+ *
+ */
+static void ircomm_lmp_connect_confirm(void *instance, void *sap,
+                                      struct qos_info *qos,
+                                      __u32 max_seg_size,
+                                      __u8 max_header_size,
+                                      struct sk_buff *skb)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *) instance;
+       struct ircomm_info info;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(qos != NULL, return;);
+
+       info.max_data_size = max_seg_size;
+       info.max_header_size = max_header_size;
+       info.qos = qos;
+
+       ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info);
+
+       /* Drop reference count - see ircomm_tty_connect_confirm(). */
+       dev_kfree_skb(skb);
+}
+
+/*
+ * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
+ *                                         max_header_size, skb)
+ *
+ *    Peer device wants to make a connection with us
+ *
+ */
+static void ircomm_lmp_connect_indication(void *instance, void *sap,
+                                         struct qos_info *qos,
+                                         __u32 max_seg_size,
+                                         __u8 max_header_size,
+                                         struct sk_buff *skb)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *)instance;
+       struct ircomm_info info;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(qos != NULL, return;);
+
+       info.max_data_size = max_seg_size;
+       info.max_header_size = max_header_size;
+       info.qos = qos;
+
+       ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info);
+
+       /* Drop reference count - see ircomm_tty_connect_indication(). */
+       dev_kfree_skb(skb);
+}
+
+/*
+ * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
+ *
+ *    Peer device has closed the connection, or the link went down for some
+ *    other reason
+ */
+static void ircomm_lmp_disconnect_indication(void *instance, void *sap,
+                                            LM_REASON reason,
+                                            struct sk_buff *skb)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *) instance;
+       struct ircomm_info info;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+       info.reason = reason;
+
+       ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
+
+       /* Drop reference count - see ircomm_tty_disconnect_indication(). */
+       if(skb)
+               dev_kfree_skb(skb);
+}
+/*
+ * Function ircomm_open_lsap (self)
+ *
+ *    Open LSAP. This function will only be used when using "raw" services
+ *
+ */
+int ircomm_open_lsap(struct ircomm_cb *self)
+{
+       notify_t notify;
+
+       /* Register callbacks */
+       irda_notify_init(&notify);
+       notify.data_indication       = ircomm_lmp_data_indication;
+       notify.connect_confirm       = ircomm_lmp_connect_confirm;
+       notify.connect_indication    = ircomm_lmp_connect_indication;
+       notify.disconnect_indication = ircomm_lmp_disconnect_indication;
+       notify.instance = self;
+       strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
+
+       self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
+       if (!self->lsap) {
+               pr_debug("%sfailed to allocate tsap\n", __func__);
+               return -1;
+       }
+       self->slsap_sel = self->lsap->slsap_sel;
+
+       /*
+        *  Initialize the call-table for issuing commands
+        */
+       self->issue.data_request       = ircomm_lmp_data_request;
+       self->issue.connect_request    = ircomm_lmp_connect_request;
+       self->issue.connect_response   = ircomm_lmp_connect_response;
+       self->issue.disconnect_request = ircomm_lmp_disconnect_request;
+
+       return 0;
+}
diff --git a/drivers/staging/irda/net/ircomm/ircomm_param.c b/drivers/staging/irda/net/ircomm/ircomm_param.c
new file mode 100644 (file)
index 0000000..5728e76
--- /dev/null
@@ -0,0 +1,501 @@
+/*********************************************************************
+ *
+ * Filename:      ircomm_param.c
+ * Version:       1.0
+ * Description:   Parameter handling for the IrCOMM protocol
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Mon Jun  7 10:25:11 1999
+ * Modified at:   Sun Jan 30 14:32:03 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/gfp.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/parameters.h>
+
+#include <net/irda/ircomm_core.h>
+#include <net/irda/ircomm_tty_attach.h>
+#include <net/irda/ircomm_tty.h>
+
+#include <net/irda/ircomm_param.h>
+
+static int ircomm_param_service_type(void *instance, irda_param_t *param,
+                                    int get);
+static int ircomm_param_port_type(void *instance, irda_param_t *param,
+                                 int get);
+static int ircomm_param_port_name(void *instance, irda_param_t *param,
+                                 int get);
+static int ircomm_param_service_type(void *instance, irda_param_t *param,
+                                    int get);
+static int ircomm_param_data_rate(void *instance, irda_param_t *param,
+                                 int get);
+static int ircomm_param_data_format(void *instance, irda_param_t *param,
+                                   int get);
+static int ircomm_param_flow_control(void *instance, irda_param_t *param,
+                                    int get);
+static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
+static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
+static int ircomm_param_line_status(void *instance, irda_param_t *param,
+                                   int get);
+static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
+static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
+static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
+
+static const pi_minor_info_t pi_minor_call_table_common[] = {
+       { ircomm_param_service_type, PV_INT_8_BITS },
+       { ircomm_param_port_type,    PV_INT_8_BITS },
+       { ircomm_param_port_name,    PV_STRING }
+};
+static const pi_minor_info_t pi_minor_call_table_non_raw[] = {
+       { ircomm_param_data_rate,    PV_INT_32_BITS | PV_BIG_ENDIAN },
+       { ircomm_param_data_format,  PV_INT_8_BITS },
+       { ircomm_param_flow_control, PV_INT_8_BITS },
+       { ircomm_param_xon_xoff,     PV_INT_16_BITS },
+       { ircomm_param_enq_ack,      PV_INT_16_BITS },
+       { ircomm_param_line_status,  PV_INT_8_BITS }
+};
+static const pi_minor_info_t pi_minor_call_table_9_wire[] = {
+       { ircomm_param_dte,          PV_INT_8_BITS },
+       { ircomm_param_dce,          PV_INT_8_BITS },
+       { ircomm_param_poll,         PV_NO_VALUE },
+};
+
+static const pi_major_info_t pi_major_call_table[] = {
+       { pi_minor_call_table_common,  3 },
+       { pi_minor_call_table_non_raw, 6 },
+       { pi_minor_call_table_9_wire,  3 }
+/*     { pi_minor_call_table_centronics }  */
+};
+
+pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 };
+
+/*
+ * Function ircomm_param_request (self, pi, flush)
+ *
+ *    Queue a parameter for the control channel
+ *
+ */
+int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
+{
+       unsigned long flags;
+       struct sk_buff *skb;
+       int count;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       /* Make sure we don't send parameters for raw mode */
+       if (self->service_type == IRCOMM_3_WIRE_RAW)
+               return 0;
+
+       spin_lock_irqsave(&self->spinlock, flags);
+
+       skb = self->ctrl_skb;
+       if (!skb) {
+               skb = alloc_skb(256, GFP_ATOMIC);
+               if (!skb) {
+                       spin_unlock_irqrestore(&self->spinlock, flags);
+                       return -ENOMEM;
+               }
+
+               skb_reserve(skb, self->max_header_size);
+               self->ctrl_skb = skb;
+       }
+       /*
+        * Inserting is a little bit tricky since we don't know how much
+        * room we will need. But this should hopefully work OK
+        */
+       count = irda_param_insert(self, pi, skb_tail_pointer(skb),
+                                 skb_tailroom(skb), &ircomm_param_info);
+       if (count < 0) {
+               net_warn_ratelimited("%s(), no room for parameter!\n",
+                                    __func__);
+               spin_unlock_irqrestore(&self->spinlock, flags);
+               return -1;
+       }
+       skb_put(skb, count);
+       pr_debug("%s(), skb->len=%d\n", __func__, skb->len);
+
+       spin_unlock_irqrestore(&self->spinlock, flags);
+
+       if (flush) {
+               /* ircomm_tty_do_softint will take care of the rest */
+               schedule_work(&self->tqueue);
+       }
+
+       return count;
+}
+
+/*
+ * Function ircomm_param_service_type (self, buf, len)
+ *
+ *    Handle service type, this function will both be called after the LM-IAS
+ *    query and then the remote device sends its initial parameters
+ *
+ */
+static int ircomm_param_service_type(void *instance, irda_param_t *param,
+                                    int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       __u8 service_type = (__u8) param->pv.i;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get) {
+               param->pv.i = self->settings.service_type;
+               return 0;
+       }
+
+       /* Find all common service types */
+       service_type &= self->service_type;
+       if (!service_type) {
+               pr_debug("%s(), No common service type to use!\n", __func__);
+               return -1;
+       }
+       pr_debug("%s(), services in common=%02x\n", __func__ ,
+                service_type);
+
+       /*
+        * Now choose a preferred service type of those available
+        */
+       if (service_type & IRCOMM_CENTRONICS)
+               self->settings.service_type = IRCOMM_CENTRONICS;
+       else if (service_type & IRCOMM_9_WIRE)
+               self->settings.service_type = IRCOMM_9_WIRE;
+       else if (service_type & IRCOMM_3_WIRE)
+               self->settings.service_type = IRCOMM_3_WIRE;
+       else if (service_type & IRCOMM_3_WIRE_RAW)
+               self->settings.service_type = IRCOMM_3_WIRE_RAW;
+
+       pr_debug("%s(), resulting service type=0x%02x\n", __func__ ,
+                self->settings.service_type);
+
+       /*
+        * Now the line is ready for some communication. Check if we are a
+        * server, and send over some initial parameters.
+        * Client do it in ircomm_tty_state_setup().
+        * Note : we may get called from ircomm_tty_getvalue_confirm(),
+        * therefore before we even have open any socket. And self->client
+        * is initialised to TRUE only later. So, we check if the link is
+        * really initialised. - Jean II
+        */
+       if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) &&
+           (!self->client) &&
+           (self->settings.service_type != IRCOMM_3_WIRE_RAW))
+       {
+               /* Init connection */
+               ircomm_tty_send_initial_parameters(self);
+               ircomm_tty_link_established(self);
+       }
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_port_type (self, param)
+ *
+ *    The port type parameter tells if the devices are serial or parallel.
+ *    Since we only advertise serial service, this parameter should only
+ *    be equal to IRCOMM_SERIAL.
+ */
+static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = IRCOMM_SERIAL;
+       else {
+               self->settings.port_type = (__u8) param->pv.i;
+
+               pr_debug("%s(), port type=%d\n", __func__ ,
+                        self->settings.port_type);
+       }
+       return 0;
+}
+
+/*
+ * Function ircomm_param_port_name (self, param)
+ *
+ *    Exchange port name
+ *
+ */
+static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get) {
+               pr_debug("%s(), not imp!\n", __func__);
+       } else {
+               pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c);
+               strncpy(self->settings.port_name, param->pv.c, 32);
+       }
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_data_rate (self, param)
+ *
+ *    Exchange data rate to be used in this settings
+ *
+ */
+static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->settings.data_rate;
+       else
+               self->settings.data_rate = param->pv.i;
+
+       pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_data_format (self, param)
+ *
+ *    Exchange data format to be used in this settings
+ *
+ */
+static int ircomm_param_data_format(void *instance, irda_param_t *param,
+                                   int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->settings.data_format;
+       else
+               self->settings.data_format = (__u8) param->pv.i;
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_flow_control (self, param)
+ *
+ *    Exchange flow control settings to be used in this settings
+ *
+ */
+static int ircomm_param_flow_control(void *instance, irda_param_t *param,
+                                    int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->settings.flow_control;
+       else
+               self->settings.flow_control = (__u8) param->pv.i;
+
+       pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_xon_xoff (self, param)
+ *
+ *    Exchange XON/XOFF characters
+ *
+ */
+static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get) {
+               param->pv.i = self->settings.xonxoff[0];
+               param->pv.i |= self->settings.xonxoff[1] << 8;
+       } else {
+               self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff;
+               self->settings.xonxoff[1] = (__u16) param->pv.i >> 8;
+       }
+
+       pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ ,
+                param->pv.i & 0xff, param->pv.i >> 8);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_enq_ack (self, param)
+ *
+ *    Exchange ENQ/ACK characters
+ *
+ */
+static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get) {
+               param->pv.i = self->settings.enqack[0];
+               param->pv.i |= self->settings.enqack[1] << 8;
+       } else {
+               self->settings.enqack[0] = (__u16) param->pv.i & 0xff;
+               self->settings.enqack[1] = (__u16) param->pv.i >> 8;
+       }
+
+       pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ ,
+                param->pv.i & 0xff, param->pv.i >> 8);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_line_status (self, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_line_status(void *instance, irda_param_t *param,
+                                   int get)
+{
+       pr_debug("%s(), not impl.\n", __func__);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_dte (instance, param)
+ *
+ *    If we get here, there must be some sort of null-modem connection, and
+ *    we are probably working in server mode as well.
+ */
+static int ircomm_param_dte(void *instance, irda_param_t *param, int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       __u8 dte;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->settings.dte;
+       else {
+               dte = (__u8) param->pv.i;
+
+               self->settings.dce = 0;
+
+               if (dte & IRCOMM_DELTA_DTR)
+                       self->settings.dce |= (IRCOMM_DELTA_DSR|
+                                             IRCOMM_DELTA_RI |
+                                             IRCOMM_DELTA_CD);
+               if (dte & IRCOMM_DTR)
+                       self->settings.dce |= (IRCOMM_DSR|
+                                             IRCOMM_RI |
+                                             IRCOMM_CD);
+
+               if (dte & IRCOMM_DELTA_RTS)
+                       self->settings.dce |= IRCOMM_DELTA_CTS;
+               if (dte & IRCOMM_RTS)
+                       self->settings.dce |= IRCOMM_CTS;
+
+               /* Take appropriate actions */
+               ircomm_tty_check_modem_status(self);
+
+               /* Null modem cable emulator */
+               self->settings.null_modem = TRUE;
+       }
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_dce (instance, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       __u8 dce;
+
+       pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i);
+
+       dce = (__u8) param->pv.i;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       self->settings.dce = dce;
+
+       /* Check if any of the settings have changed */
+       if (dce & 0x0f) {
+               if (dce & IRCOMM_DELTA_CTS) {
+                       pr_debug("%s(), CTS\n", __func__);
+               }
+       }
+
+       ircomm_tty_check_modem_status(self);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_param_poll (instance, param)
+ *
+ *    Called when the peer device is polling for the line settings
+ *
+ */
+static int ircomm_param_poll(void *instance, irda_param_t *param, int get)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       /* Poll parameters are always of length 0 (just a signal) */
+       if (!get) {
+               /* Respond with DTE line settings */
+               ircomm_param_request(self, IRCOMM_DTE, TRUE);
+       }
+       return 0;
+}
+
+
+
+
+
diff --git a/drivers/staging/irda/net/ircomm/ircomm_ttp.c b/drivers/staging/irda/net/ircomm/ircomm_ttp.c
new file mode 100644 (file)
index 0000000..4b81e09
--- /dev/null
@@ -0,0 +1,350 @@
+/*********************************************************************
+ *
+ * Filename:      ircomm_ttp.c
+ * Version:       1.0
+ * Description:   Interface between IrCOMM and IrTTP
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Jun  6 20:48:27 1999
+ * Modified at:   Mon Dec 13 11:35:13 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irttp.h>
+
+#include <net/irda/ircomm_event.h>
+#include <net/irda/ircomm_ttp.h>
+
+static int ircomm_ttp_data_indication(void *instance, void *sap,
+                                     struct sk_buff *skb);
+static void ircomm_ttp_connect_confirm(void *instance, void *sap,
+                                      struct qos_info *qos,
+                                      __u32 max_sdu_size,
+                                      __u8 max_header_size,
+                                      struct sk_buff *skb);
+static void ircomm_ttp_connect_indication(void *instance, void *sap,
+                                         struct qos_info *qos,
+                                         __u32 max_sdu_size,
+                                         __u8 max_header_size,
+                                         struct sk_buff *skb);
+static void ircomm_ttp_flow_indication(void *instance, void *sap,
+                                      LOCAL_FLOW cmd);
+static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
+                                            LM_REASON reason,
+                                            struct sk_buff *skb);
+static int ircomm_ttp_data_request(struct ircomm_cb *self,
+                                  struct sk_buff *skb,
+                                  int clen);
+static int ircomm_ttp_connect_request(struct ircomm_cb *self,
+                                     struct sk_buff *userdata,
+                                     struct ircomm_info *info);
+static int ircomm_ttp_connect_response(struct ircomm_cb *self,
+                                      struct sk_buff *userdata);
+static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
+                                        struct sk_buff *userdata,
+                                        struct ircomm_info *info);
+
+/*
+ * Function ircomm_open_tsap (self)
+ *
+ *
+ *
+ */
+int ircomm_open_tsap(struct ircomm_cb *self)
+{
+       notify_t notify;
+
+       /* Register callbacks */
+       irda_notify_init(&notify);
+       notify.data_indication       = ircomm_ttp_data_indication;
+       notify.connect_confirm       = ircomm_ttp_connect_confirm;
+       notify.connect_indication    = ircomm_ttp_connect_indication;
+       notify.flow_indication       = ircomm_ttp_flow_indication;
+       notify.disconnect_indication = ircomm_ttp_disconnect_indication;
+       notify.instance = self;
+       strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
+
+       self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
+                                    &notify);
+       if (!self->tsap) {
+               pr_debug("%sfailed to allocate tsap\n", __func__);
+               return -1;
+       }
+       self->slsap_sel = self->tsap->stsap_sel;
+
+       /*
+        *  Initialize the call-table for issuing commands
+        */
+       self->issue.data_request       = ircomm_ttp_data_request;
+       self->issue.connect_request    = ircomm_ttp_connect_request;
+       self->issue.connect_response   = ircomm_ttp_connect_response;
+       self->issue.disconnect_request = ircomm_ttp_disconnect_request;
+
+       return 0;
+}
+
+/*
+ * Function ircomm_ttp_connect_request (self, userdata)
+ *
+ *
+ *
+ */
+static int ircomm_ttp_connect_request(struct ircomm_cb *self,
+                                     struct sk_buff *userdata,
+                                     struct ircomm_info *info)
+{
+       int ret = 0;
+
+       /* Don't forget to refcount it - should be NULL anyway */
+       if(userdata)
+               skb_get(userdata);
+
+       ret = irttp_connect_request(self->tsap, info->dlsap_sel,
+                                   info->saddr, info->daddr, NULL,
+                                   TTP_SAR_DISABLE, userdata);
+
+       return ret;
+}
+
+/*
+ * Function ircomm_ttp_connect_response (self, skb)
+ *
+ *
+ *
+ */
+static int ircomm_ttp_connect_response(struct ircomm_cb *self,
+                                      struct sk_buff *userdata)
+{
+       int ret;
+
+       /* Don't forget to refcount it - should be NULL anyway */
+       if(userdata)
+               skb_get(userdata);
+
+       ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata);
+
+       return ret;
+}
+
+/*
+ * Function ircomm_ttp_data_request (self, userdata)
+ *
+ *    Send IrCOMM data to IrTTP layer. Currently we do not try to combine
+ *    control data with pure data, so they will be sent as separate frames.
+ *    Should not be a big problem though, since control frames are rare. But
+ *    some of them are sent after connection establishment, so this can
+ *    increase the latency a bit.
+ */
+static int ircomm_ttp_data_request(struct ircomm_cb *self,
+                                  struct sk_buff *skb,
+                                  int clen)
+{
+       int ret;
+
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       pr_debug("%s(), clen=%d\n", __func__ , clen);
+
+       /*
+        * Insert clen field, currently we either send data only, or control
+        * only frames, to make things easier and avoid queueing
+        */
+       IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;);
+
+       /* Don't forget to refcount it - see ircomm_tty_do_softint() */
+       skb_get(skb);
+
+       skb_push(skb, IRCOMM_HEADER_SIZE);
+
+       skb->data[0] = clen;
+
+       ret = irttp_data_request(self->tsap, skb);
+       if (ret) {
+               net_err_ratelimited("%s(), failed\n", __func__);
+               /* irttp_data_request already free the packet */
+       }
+
+       return ret;
+}
+
+/*
+ * Function ircomm_ttp_data_indication (instance, sap, skb)
+ *
+ *    Incoming data
+ *
+ */
+static int ircomm_ttp_data_indication(void *instance, void *sap,
+                                     struct sk_buff *skb)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL);
+
+       /* Drop reference count - see ircomm_tty_data_indication(). */
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+static void ircomm_ttp_connect_confirm(void *instance, void *sap,
+                                      struct qos_info *qos,
+                                      __u32 max_sdu_size,
+                                      __u8 max_header_size,
+                                      struct sk_buff *skb)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *) instance;
+       struct ircomm_info info;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(qos != NULL, goto out;);
+
+       if (max_sdu_size != TTP_SAR_DISABLE) {
+               net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
+                                   __func__);
+               goto out;
+       }
+
+       info.max_data_size = irttp_get_max_seg_size(self->tsap)
+               - IRCOMM_HEADER_SIZE;
+       info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
+       info.qos = qos;
+
+       ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info);
+
+out:
+       /* Drop reference count - see ircomm_tty_connect_confirm(). */
+       dev_kfree_skb(skb);
+}
+
+/*
+ * Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size,
+ *                                         max_header_size, skb)
+ *
+ *
+ *
+ */
+static void ircomm_ttp_connect_indication(void *instance, void *sap,
+                                         struct qos_info *qos,
+                                         __u32 max_sdu_size,
+                                         __u8 max_header_size,
+                                         struct sk_buff *skb)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *)instance;
+       struct ircomm_info info;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(qos != NULL, goto out;);
+
+       if (max_sdu_size != TTP_SAR_DISABLE) {
+               net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
+                                   __func__);
+               goto out;
+       }
+
+       info.max_data_size = irttp_get_max_seg_size(self->tsap)
+               - IRCOMM_HEADER_SIZE;
+       info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
+       info.qos = qos;
+
+       ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info);
+
+out:
+       /* Drop reference count - see ircomm_tty_connect_indication(). */
+       dev_kfree_skb(skb);
+}
+
+/*
+ * Function ircomm_ttp_disconnect_request (self, userdata, info)
+ *
+ *
+ *
+ */
+static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
+                                        struct sk_buff *userdata,
+                                        struct ircomm_info *info)
+{
+       int ret;
+
+       /* Don't forget to refcount it - should be NULL anyway */
+       if(userdata)
+               skb_get(userdata);
+
+       ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL);
+
+       return ret;
+}
+
+/*
+ * Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb)
+ *
+ *
+ *
+ */
+static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
+                                            LM_REASON reason,
+                                            struct sk_buff *skb)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *) instance;
+       struct ircomm_info info;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+       info.reason = reason;
+
+       ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info);
+
+       /* Drop reference count - see ircomm_tty_disconnect_indication(). */
+       if(skb)
+               dev_kfree_skb(skb);
+}
+
+/*
+ * Function ircomm_ttp_flow_indication (instance, sap, cmd)
+ *
+ *    Layer below is telling us to start or stop the flow of data
+ *
+ */
+static void ircomm_ttp_flow_indication(void *instance, void *sap,
+                                      LOCAL_FLOW cmd)
+{
+       struct ircomm_cb *self = (struct ircomm_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+       if (self->notify.flow_indication)
+               self->notify.flow_indication(self->notify.instance, self, cmd);
+}
+
+
diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty.c b/drivers/staging/irda/net/ircomm/ircomm_tty.c
new file mode 100644 (file)
index 0000000..ec157c3
--- /dev/null
@@ -0,0 +1,1329 @@
+/*********************************************************************
+ *
+ * Filename:      ircomm_tty.c
+ * Version:       1.0
+ * Description:   IrCOMM serial TTY driver
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Jun  6 21:00:56 1999
+ * Modified at:   Wed Feb 23 00:09:02 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Sources:       serial.c and previous IrCOMM work by Takahide Higuchi
+ *
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+#include <linux/seq_file.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>              /* for MODULE_ALIAS_CHARDEV_MAJOR */
+
+#include <linux/uaccess.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+
+#include <net/irda/ircomm_core.h>
+#include <net/irda/ircomm_param.h>
+#include <net/irda/ircomm_tty_attach.h>
+#include <net/irda/ircomm_tty.h>
+
+static int ircomm_tty_install(struct tty_driver *driver,
+               struct tty_struct *tty);
+static int  ircomm_tty_open(struct tty_struct *tty, struct file *filp);
+static void ircomm_tty_close(struct tty_struct * tty, struct file *filp);
+static int  ircomm_tty_write(struct tty_struct * tty,
+                            const unsigned char *buf, int count);
+static int  ircomm_tty_write_room(struct tty_struct *tty);
+static void ircomm_tty_throttle(struct tty_struct *tty);
+static void ircomm_tty_unthrottle(struct tty_struct *tty);
+static int  ircomm_tty_chars_in_buffer(struct tty_struct *tty);
+static void ircomm_tty_flush_buffer(struct tty_struct *tty);
+static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch);
+static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout);
+static void ircomm_tty_hangup(struct tty_struct *tty);
+static void ircomm_tty_do_softint(struct work_struct *work);
+static void ircomm_tty_shutdown(struct ircomm_tty_cb *self);
+static void ircomm_tty_stop(struct tty_struct *tty);
+
+static int ircomm_tty_data_indication(void *instance, void *sap,
+                                     struct sk_buff *skb);
+static int ircomm_tty_control_indication(void *instance, void *sap,
+                                        struct sk_buff *skb);
+static void ircomm_tty_flow_indication(void *instance, void *sap,
+                                      LOCAL_FLOW cmd);
+#ifdef CONFIG_PROC_FS
+static const struct file_operations ircomm_tty_proc_fops;
+#endif /* CONFIG_PROC_FS */
+static struct tty_driver *driver;
+
+static hashbin_t *ircomm_tty = NULL;
+
+static const struct tty_operations ops = {
+       .install         = ircomm_tty_install,
+       .open            = ircomm_tty_open,
+       .close           = ircomm_tty_close,
+       .write           = ircomm_tty_write,
+       .write_room      = ircomm_tty_write_room,
+       .chars_in_buffer = ircomm_tty_chars_in_buffer,
+       .flush_buffer    = ircomm_tty_flush_buffer,
+       .ioctl           = ircomm_tty_ioctl,    /* ircomm_tty_ioctl.c */
+       .tiocmget        = ircomm_tty_tiocmget, /* ircomm_tty_ioctl.c */
+       .tiocmset        = ircomm_tty_tiocmset, /* ircomm_tty_ioctl.c */
+       .throttle        = ircomm_tty_throttle,
+       .unthrottle      = ircomm_tty_unthrottle,
+       .send_xchar      = ircomm_tty_send_xchar,
+       .set_termios     = ircomm_tty_set_termios,
+       .stop            = ircomm_tty_stop,
+       .start           = ircomm_tty_start,
+       .hangup          = ircomm_tty_hangup,
+       .wait_until_sent = ircomm_tty_wait_until_sent,
+#ifdef CONFIG_PROC_FS
+       .proc_fops       = &ircomm_tty_proc_fops,
+#endif /* CONFIG_PROC_FS */
+};
+
+static void ircomm_port_raise_dtr_rts(struct tty_port *port, int raise)
+{
+       struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb,
+                       port);
+       /*
+        * Here, we use to lock those two guys, but as ircomm_param_request()
+        * does it itself, I don't see the point (and I see the deadlock).
+        * Jean II
+        */
+       if (raise)
+               self->settings.dte |= IRCOMM_RTS | IRCOMM_DTR;
+       else
+               self->settings.dte &= ~(IRCOMM_RTS | IRCOMM_DTR);
+
+       ircomm_param_request(self, IRCOMM_DTE, TRUE);
+}
+
+static int ircomm_port_carrier_raised(struct tty_port *port)
+{
+       struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb,
+                       port);
+       return self->settings.dce & IRCOMM_CD;
+}
+
+static const struct tty_port_operations ircomm_port_ops = {
+       .dtr_rts = ircomm_port_raise_dtr_rts,
+       .carrier_raised = ircomm_port_carrier_raised,
+};
+
+/*
+ * Function ircomm_tty_init()
+ *
+ *    Init IrCOMM TTY layer/driver
+ *
+ */
+static int __init ircomm_tty_init(void)
+{
+       driver = alloc_tty_driver(IRCOMM_TTY_PORTS);
+       if (!driver)
+               return -ENOMEM;
+       ircomm_tty = hashbin_new(HB_LOCK);
+       if (ircomm_tty == NULL) {
+               net_err_ratelimited("%s(), can't allocate hashbin!\n",
+                                   __func__);
+               put_tty_driver(driver);
+               return -ENOMEM;
+       }
+
+       driver->driver_name     = "ircomm";
+       driver->name            = "ircomm";
+       driver->major           = IRCOMM_TTY_MAJOR;
+       driver->minor_start     = IRCOMM_TTY_MINOR;
+       driver->type            = TTY_DRIVER_TYPE_SERIAL;
+       driver->subtype         = SERIAL_TYPE_NORMAL;
+       driver->init_termios    = tty_std_termios;
+       driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       driver->flags           = TTY_DRIVER_REAL_RAW;
+       tty_set_operations(driver, &ops);
+       if (tty_register_driver(driver)) {
+               net_err_ratelimited("%s(): Couldn't register serial driver\n",
+                                   __func__);
+               put_tty_driver(driver);
+               return -1;
+       }
+       return 0;
+}
+
+static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       ircomm_tty_shutdown(self);
+
+       self->magic = 0;
+       tty_port_destroy(&self->port);
+       kfree(self);
+}
+
+/*
+ * Function ircomm_tty_cleanup ()
+ *
+ *    Remove IrCOMM TTY layer/driver
+ *
+ */
+static void __exit ircomm_tty_cleanup(void)
+{
+       int ret;
+
+       ret = tty_unregister_driver(driver);
+       if (ret) {
+               net_err_ratelimited("%s(), failed to unregister driver\n",
+                                   __func__);
+               return;
+       }
+
+       hashbin_delete(ircomm_tty, (FREE_FUNC) __ircomm_tty_cleanup);
+       put_tty_driver(driver);
+}
+
+/*
+ * Function ircomm_startup (self)
+ *
+ *
+ *
+ */
+static int ircomm_tty_startup(struct ircomm_tty_cb *self)
+{
+       notify_t notify;
+       int ret = -ENODEV;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       /* Check if already open */
+       if (tty_port_initialized(&self->port)) {
+               pr_debug("%s(), already open so break out!\n", __func__);
+               return 0;
+       }
+       tty_port_set_initialized(&self->port, 1);
+
+       /* Register with IrCOMM */
+       irda_notify_init(&notify);
+       /* These callbacks we must handle ourselves */
+       notify.data_indication       = ircomm_tty_data_indication;
+       notify.udata_indication      = ircomm_tty_control_indication;
+       notify.flow_indication       = ircomm_tty_flow_indication;
+
+       /* Use the ircomm_tty interface for these ones */
+       notify.disconnect_indication = ircomm_tty_disconnect_indication;
+       notify.connect_confirm       = ircomm_tty_connect_confirm;
+       notify.connect_indication    = ircomm_tty_connect_indication;
+       strlcpy(notify.name, "ircomm_tty", sizeof(notify.name));
+       notify.instance = self;
+
+       if (!self->ircomm) {
+               self->ircomm = ircomm_open(&notify, self->service_type,
+                                          self->line);
+       }
+       if (!self->ircomm)
+               goto err;
+
+       self->slsap_sel = self->ircomm->slsap_sel;
+
+       /* Connect IrCOMM link with remote device */
+       ret = ircomm_tty_attach_cable(self);
+       if (ret < 0) {
+               net_err_ratelimited("%s(), error attaching cable!\n", __func__);
+               goto err;
+       }
+
+       return 0;
+err:
+       tty_port_set_initialized(&self->port, 0);
+       return ret;
+}
+
+/*
+ * Function ircomm_block_til_ready (self, filp)
+ *
+ *
+ *
+ */
+static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
+               struct tty_struct *tty, struct file *filp)
+{
+       struct tty_port *port = &self->port;
+       DECLARE_WAITQUEUE(wait, current);
+       int             retval;
+       int             do_clocal = 0;
+       unsigned long   flags;
+
+       /*
+        * If non-blocking mode is set, or the port is not enabled,
+        * then make the check up front and then exit.
+        */
+       if (tty_io_error(tty)) {
+               tty_port_set_active(port, 1);
+               return 0;
+       }
+
+       if (filp->f_flags & O_NONBLOCK) {
+               /* nonblock mode is set */
+               if (C_BAUD(tty))
+                       tty_port_raise_dtr_rts(port);
+               tty_port_set_active(port, 1);
+               pr_debug("%s(), O_NONBLOCK requested!\n", __func__);
+               return 0;
+       }
+
+       if (C_CLOCAL(tty)) {
+               pr_debug("%s(), doing CLOCAL!\n", __func__);
+               do_clocal = 1;
+       }
+
+       /* Wait for carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, port->count is dropped by one, so that
+        * mgsl_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+
+       retval = 0;
+       add_wait_queue(&port->open_wait, &wait);
+
+       pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n",
+                __FILE__, __LINE__, tty->driver->name, port->count);
+
+       spin_lock_irqsave(&port->lock, flags);
+       port->count--;
+       port->blocked_open++;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       while (1) {
+               if (C_BAUD(tty) && tty_port_initialized(port))
+                       tty_port_raise_dtr_rts(port);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (tty_hung_up_p(filp) || !tty_port_initialized(port)) {
+                       retval = (port->flags & ASYNC_HUP_NOTIFY) ?
+                                       -EAGAIN : -ERESTARTSYS;
+                       break;
+               }
+
+               /*
+                * Check if link is ready now. Even if CLOCAL is
+                * specified, we cannot return before the IrCOMM link is
+                * ready
+                */
+               if ((do_clocal || tty_port_carrier_raised(port)) &&
+                   self->state == IRCOMM_TTY_READY)
+               {
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+
+               pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n",
+                        __FILE__, __LINE__, tty->driver->name, port->count);
+
+               schedule();
+       }
+
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(&port->open_wait, &wait);
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (!tty_hung_up_p(filp))
+               port->count++;
+       port->blocked_open--;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n",
+                __FILE__, __LINE__, tty->driver->name, port->count);
+
+       if (!retval)
+               tty_port_set_active(port, 1);
+
+       return retval;
+}
+
+
+static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self;
+       unsigned int line = tty->index;
+
+       /* Check if instance already exists */
+       self = hashbin_lock_find(ircomm_tty, line, NULL);
+       if (!self) {
+               /* No, so make new instance */
+               self = kzalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL);
+               if (self == NULL)
+                       return -ENOMEM;
+
+               tty_port_init(&self->port);
+               self->port.ops = &ircomm_port_ops;
+               self->magic = IRCOMM_TTY_MAGIC;
+               self->flow = FLOW_STOP;
+
+               self->line = line;
+               INIT_WORK(&self->tqueue, ircomm_tty_do_softint);
+               self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED;
+               self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED;
+
+               /* Init some important stuff */
+               init_timer(&self->watchdog_timer);
+               spin_lock_init(&self->spinlock);
+
+               /*
+                * Force TTY into raw mode by default which is usually what
+                * we want for IrCOMM and IrLPT. This way applications will
+                * not have to twiddle with printcap etc.
+                *
+                * Note this is completely usafe and doesn't work properly
+                */
+               tty->termios.c_iflag = 0;
+               tty->termios.c_oflag = 0;
+
+               /* Insert into hash */
+               hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL);
+       }
+
+       tty->driver_data = self;
+
+       return tty_port_install(&self->port, driver, tty);
+}
+
+/*
+ * Function ircomm_tty_open (tty, filp)
+ *
+ *    This routine is called when a particular tty device is opened. This
+ *    routine is mandatory; if this routine is not filled in, the attempted
+ *    open will fail with ENODEV.
+ */
+static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+       struct ircomm_tty_cb *self = tty->driver_data;
+       unsigned long   flags;
+       int ret;
+
+       /* ++ is not atomic, so this should be protected - Jean II */
+       spin_lock_irqsave(&self->port.lock, flags);
+       self->port.count++;
+       spin_unlock_irqrestore(&self->port.lock, flags);
+       tty_port_tty_set(&self->port, tty);
+
+       pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name,
+                self->line, self->port.count);
+
+       /* Not really used by us, but lets do it anyway */
+       self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       /* Check if this is a "normal" ircomm device, or an irlpt device */
+       if (self->line < 0x10) {
+               self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE;
+               self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */
+               /* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */
+               self->settings.dce = IRCOMM_CTS | IRCOMM_CD | IRCOMM_DSR | IRCOMM_RI; /* Default line settings */
+               pr_debug("%s(), IrCOMM device\n", __func__);
+       } else {
+               pr_debug("%s(), IrLPT device\n", __func__);
+               self->service_type = IRCOMM_3_WIRE_RAW;
+               self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */
+       }
+
+       ret = ircomm_tty_startup(self);
+       if (ret)
+               return ret;
+
+       ret = ircomm_tty_block_til_ready(self, tty, filp);
+       if (ret) {
+               pr_debug("%s(), returning after block_til_ready with %d\n",
+                        __func__, ret);
+
+               return ret;
+       }
+       return 0;
+}
+
+/*
+ * Function ircomm_tty_close (tty, filp)
+ *
+ *    This routine is called when a particular tty device is closed.
+ *
+ */
+static void ircomm_tty_close(struct tty_struct *tty, struct file *filp)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       struct tty_port *port = &self->port;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       if (tty_port_close_start(port, tty, filp) == 0)
+               return;
+
+       ircomm_tty_shutdown(self);
+
+       tty_driver_flush_buffer(tty);
+
+       tty_port_close_end(port, tty);
+       tty_port_tty_set(port, NULL);
+}
+
+/*
+ * Function ircomm_tty_flush_buffer (tty)
+ *
+ *
+ *
+ */
+static void ircomm_tty_flush_buffer(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       /*
+        * Let do_softint() do this to avoid race condition with
+        * do_softint() ;-)
+        */
+       schedule_work(&self->tqueue);
+}
+
+/*
+ * Function ircomm_tty_do_softint (work)
+ *
+ *    We use this routine to give the write wakeup to the user at at a
+ *    safe time (as fast as possible after write have completed). This
+ *    can be compared to the Tx interrupt.
+ */
+static void ircomm_tty_do_softint(struct work_struct *work)
+{
+       struct ircomm_tty_cb *self =
+               container_of(work, struct ircomm_tty_cb, tqueue);
+       struct tty_struct *tty;
+       unsigned long flags;
+       struct sk_buff *skb, *ctrl_skb;
+
+       if (!self || self->magic != IRCOMM_TTY_MAGIC)
+               return;
+
+       tty = tty_port_tty_get(&self->port);
+       if (!tty)
+               return;
+
+       /* Unlink control buffer */
+       spin_lock_irqsave(&self->spinlock, flags);
+
+       ctrl_skb = self->ctrl_skb;
+       self->ctrl_skb = NULL;
+
+       spin_unlock_irqrestore(&self->spinlock, flags);
+
+       /* Flush control buffer if any */
+       if(ctrl_skb) {
+               if(self->flow == FLOW_START)
+                       ircomm_control_request(self->ircomm, ctrl_skb);
+               /* Drop reference count - see ircomm_ttp_data_request(). */
+               dev_kfree_skb(ctrl_skb);
+       }
+
+       if (tty->hw_stopped)
+               goto put;
+
+       /* Unlink transmit buffer */
+       spin_lock_irqsave(&self->spinlock, flags);
+
+       skb = self->tx_skb;
+       self->tx_skb = NULL;
+
+       spin_unlock_irqrestore(&self->spinlock, flags);
+
+       /* Flush transmit buffer if any */
+       if (skb) {
+               ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL);
+               /* Drop reference count - see ircomm_ttp_data_request(). */
+               dev_kfree_skb(skb);
+       }
+
+       /* Check if user (still) wants to be waken up */
+       tty_wakeup(tty);
+put:
+       tty_kref_put(tty);
+}
+
+/*
+ * Function ircomm_tty_write (tty, buf, count)
+ *
+ *    This routine is called by the kernel to write a series of characters
+ *    to the tty device. The characters may come from user space or kernel
+ *    space. This routine will return the number of characters actually
+ *    accepted for writing. This routine is mandatory.
+ */
+static int ircomm_tty_write(struct tty_struct *tty,
+                           const unsigned char *buf, int count)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       unsigned long flags;
+       struct sk_buff *skb;
+       int tailroom = 0;
+       int len = 0;
+       int size;
+
+       pr_debug("%s(), count=%d, hw_stopped=%d\n", __func__ , count,
+                tty->hw_stopped);
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       /* We may receive packets from the TTY even before we have finished
+        * our setup. Not cool.
+        * The problem is that we don't know the final header and data size
+        * to create the proper skb, so any skb we would create would have
+        * bogus header and data size, so need care.
+        * We use a bogus header size to safely detect this condition.
+        * Another problem is that hw_stopped was set to 0 way before it
+        * should be, so we would drop this skb. It should now be fixed.
+        * One option is to not accept data until we are properly setup.
+        * But, I suspect that when it happens, the ppp line discipline
+        * just "drops" the data, which might screw up connect scripts.
+        * The second option is to create a "safe skb", with large header
+        * and small size (see ircomm_tty_open() for values).
+        * We just need to make sure that when the real values get filled,
+        * we don't mess up the original "safe skb" (see tx_data_size).
+        * Jean II */
+       if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) {
+               pr_debug("%s() : not initialised\n", __func__);
+#ifdef IRCOMM_NO_TX_BEFORE_INIT
+               /* We didn't consume anything, TTY will retry */
+               return 0;
+#endif
+       }
+
+       if (count < 1)
+               return 0;
+
+       /* Protect our manipulation of self->tx_skb and related */
+       spin_lock_irqsave(&self->spinlock, flags);
+
+       /* Fetch current transmit buffer */
+       skb = self->tx_skb;
+
+       /*
+        * Send out all the data we get, possibly as multiple fragmented
+        * frames, but this will only happen if the data is larger than the
+        * max data size. The normal case however is just the opposite, and
+        * this function may be called multiple times, and will then actually
+        * defragment the data and send it out as one packet as soon as
+        * possible, but at a safer point in time
+        */
+       while (count) {
+               size = count;
+
+               /* Adjust data size to the max data size */
+               if (size > self->max_data_size)
+                       size = self->max_data_size;
+
+               /*
+                * Do we already have a buffer ready for transmit, or do
+                * we need to allocate a new frame
+                */
+               if (skb) {
+                       /*
+                        * Any room for more data at the end of the current
+                        * transmit buffer? Cannot use skb_tailroom, since
+                        * dev_alloc_skb gives us a larger skb than we
+                        * requested
+                        * Note : use tx_data_size, because max_data_size
+                        * may have changed and we don't want to overwrite
+                        * the skb. - Jean II
+                        */
+                       if ((tailroom = (self->tx_data_size - skb->len)) > 0) {
+                               /* Adjust data to tailroom */
+                               if (size > tailroom)
+                                       size = tailroom;
+                       } else {
+                               /*
+                                * Current transmit frame is full, so break
+                                * out, so we can send it as soon as possible
+                                */
+                               break;
+                       }
+               } else {
+                       /* Prepare a full sized frame */
+                       skb = alloc_skb(self->max_data_size+
+                                       self->max_header_size,
+                                       GFP_ATOMIC);
+                       if (!skb) {
+                               spin_unlock_irqrestore(&self->spinlock, flags);
+                               return -ENOBUFS;
+                       }
+                       skb_reserve(skb, self->max_header_size);
+                       self->tx_skb = skb;
+                       /* Remember skb size because max_data_size may
+                        * change later on - Jean II */
+                       self->tx_data_size = self->max_data_size;
+               }
+
+               /* Copy data */
+               skb_put_data(skb, buf + len, size);
+
+               count -= size;
+               len += size;
+       }
+
+       spin_unlock_irqrestore(&self->spinlock, flags);
+
+       /*
+        * Schedule a new thread which will transmit the frame as soon
+        * as possible, but at a safe point in time. We do this so the
+        * "user" can give us data multiple times, as PPP does (because of
+        * its 256 byte tx buffer). We will then defragment and send out
+        * all this data as one single packet.
+        */
+       schedule_work(&self->tqueue);
+
+       return len;
+}
+
+/*
+ * Function ircomm_tty_write_room (tty)
+ *
+ *    This routine returns the numbers of characters the tty driver will
+ *    accept for queuing to be written. This number is subject to change as
+ *    output buffers get emptied, or if the output flow control is acted.
+ */
+static int ircomm_tty_write_room(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       unsigned long flags;
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+#ifdef IRCOMM_NO_TX_BEFORE_INIT
+       /* max_header_size tells us if the channel is initialised or not. */
+       if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED)
+               /* Don't bother us yet */
+               return 0;
+#endif
+
+       /* Check if we are allowed to transmit any data.
+        * hw_stopped is the regular flow control.
+        * Jean II */
+       if (tty->hw_stopped)
+               ret = 0;
+       else {
+               spin_lock_irqsave(&self->spinlock, flags);
+               if (self->tx_skb)
+                       ret = self->tx_data_size - self->tx_skb->len;
+               else
+                       ret = self->max_data_size;
+               spin_unlock_irqrestore(&self->spinlock, flags);
+       }
+       pr_debug("%s(), ret=%d\n", __func__ , ret);
+
+       return ret;
+}
+
+/*
+ * Function ircomm_tty_wait_until_sent (tty, timeout)
+ *
+ *    This routine waits until the device has written out all of the
+ *    characters in its transmitter FIFO.
+ */
+static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       unsigned long orig_jiffies, poll_time;
+       unsigned long flags;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       orig_jiffies = jiffies;
+
+       /* Set poll time to 200 ms */
+       poll_time = msecs_to_jiffies(200);
+       if (timeout)
+               poll_time = min_t(unsigned long, timeout, poll_time);
+
+       spin_lock_irqsave(&self->spinlock, flags);
+       while (self->tx_skb && self->tx_skb->len) {
+               spin_unlock_irqrestore(&self->spinlock, flags);
+               schedule_timeout_interruptible(poll_time);
+               spin_lock_irqsave(&self->spinlock, flags);
+               if (signal_pending(current))
+                       break;
+               if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                       break;
+       }
+       spin_unlock_irqrestore(&self->spinlock, flags);
+       __set_current_state(TASK_RUNNING);
+}
+
+/*
+ * Function ircomm_tty_throttle (tty)
+ *
+ *    This routine notifies the tty driver that input buffers for the line
+ *    discipline are close to full, and it should somehow signal that no
+ *    more characters should be sent to the tty.
+ */
+static void ircomm_tty_throttle(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       /* Software flow control? */
+       if (I_IXOFF(tty))
+               ircomm_tty_send_xchar(tty, STOP_CHAR(tty));
+
+       /* Hardware flow control? */
+       if (C_CRTSCTS(tty)) {
+               self->settings.dte &= ~IRCOMM_RTS;
+               self->settings.dte |= IRCOMM_DELTA_RTS;
+
+               ircomm_param_request(self, IRCOMM_DTE, TRUE);
+       }
+
+       ircomm_flow_request(self->ircomm, FLOW_STOP);
+}
+
+/*
+ * Function ircomm_tty_unthrottle (tty)
+ *
+ *    This routine notifies the tty drivers that it should signals that
+ *    characters can now be sent to the tty without fear of overrunning the
+ *    input buffers of the line disciplines.
+ */
+static void ircomm_tty_unthrottle(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       /* Using software flow control? */
+       if (I_IXOFF(tty))
+               ircomm_tty_send_xchar(tty, START_CHAR(tty));
+
+       /* Using hardware flow control? */
+       if (C_CRTSCTS(tty)) {
+               self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS);
+
+               ircomm_param_request(self, IRCOMM_DTE, TRUE);
+               pr_debug("%s(), FLOW_START\n", __func__);
+       }
+       ircomm_flow_request(self->ircomm, FLOW_START);
+}
+
+/*
+ * Function ircomm_tty_chars_in_buffer (tty)
+ *
+ *    Indicates if there are any data in the buffer
+ *
+ */
+static int ircomm_tty_chars_in_buffer(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       unsigned long flags;
+       int len = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       spin_lock_irqsave(&self->spinlock, flags);
+
+       if (self->tx_skb)
+               len = self->tx_skb->len;
+
+       spin_unlock_irqrestore(&self->spinlock, flags);
+
+       return len;
+}
+
+static void ircomm_tty_shutdown(struct ircomm_tty_cb *self)
+{
+       unsigned long flags;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       if (!tty_port_initialized(&self->port))
+               return;
+       tty_port_set_initialized(&self->port, 0);
+
+       ircomm_tty_detach_cable(self);
+
+       spin_lock_irqsave(&self->spinlock, flags);
+
+       del_timer(&self->watchdog_timer);
+
+       /* Free parameter buffer */
+       if (self->ctrl_skb) {
+               dev_kfree_skb(self->ctrl_skb);
+               self->ctrl_skb = NULL;
+       }
+
+       /* Free transmit buffer */
+       if (self->tx_skb) {
+               dev_kfree_skb(self->tx_skb);
+               self->tx_skb = NULL;
+       }
+
+       if (self->ircomm) {
+               ircomm_close(self->ircomm);
+               self->ircomm = NULL;
+       }
+
+       spin_unlock_irqrestore(&self->spinlock, flags);
+}
+
+/*
+ * Function ircomm_tty_hangup (tty)
+ *
+ *    This routine notifies the tty driver that it should hangup the tty
+ *    device.
+ *
+ */
+static void ircomm_tty_hangup(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       struct tty_port *port = &self->port;
+       unsigned long   flags;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       /* ircomm_tty_flush_buffer(tty); */
+       ircomm_tty_shutdown(self);
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (port->tty) {
+               set_bit(TTY_IO_ERROR, &port->tty->flags);
+               tty_kref_put(port->tty);
+       }
+       port->tty = NULL;
+       port->count = 0;
+       spin_unlock_irqrestore(&port->lock, flags);
+       tty_port_set_active(port, 0);
+
+       wake_up_interruptible(&port->open_wait);
+}
+
+/*
+ * Function ircomm_tty_send_xchar (tty, ch)
+ *
+ *    This routine is used to send a high-priority XON/XOFF character to
+ *    the device.
+ */
+static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+       pr_debug("%s(), not impl\n", __func__);
+}
+
+/*
+ * Function ircomm_tty_start (tty)
+ *
+ *    This routine notifies the tty driver that it resume sending
+ *    characters to the tty device.
+ */
+void ircomm_tty_start(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+       ircomm_flow_request(self->ircomm, FLOW_START);
+}
+
+/*
+ * Function ircomm_tty_stop (tty)
+ *
+ *     This routine notifies the tty driver that it should stop outputting
+ *     characters to the tty device.
+ */
+static void ircomm_tty_stop(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       ircomm_flow_request(self->ircomm, FLOW_STOP);
+}
+
+/*
+ * Function ircomm_check_modem_status (self)
+ *
+ *    Check for any changes in the DCE's line settings. This function should
+ *    be called whenever the dce parameter settings changes, to update the
+ *    flow control settings and other things
+ */
+void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
+{
+       struct tty_struct *tty;
+       int status;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       tty = tty_port_tty_get(&self->port);
+
+       status = self->settings.dce;
+
+       if (status & IRCOMM_DCE_DELTA_ANY) {
+               /*wake_up_interruptible(&self->delta_msr_wait);*/
+       }
+       if (tty_port_check_carrier(&self->port) && (status & IRCOMM_DELTA_CD)) {
+               pr_debug("%s(), ircomm%d CD now %s...\n", __func__ , self->line,
+                        (status & IRCOMM_CD) ? "on" : "off");
+
+               if (status & IRCOMM_CD) {
+                       wake_up_interruptible(&self->port.open_wait);
+               } else {
+                       pr_debug("%s(), Doing serial hangup..\n", __func__);
+                       if (tty)
+                               tty_hangup(tty);
+
+                       /* Hangup will remote the tty, so better break out */
+                       goto put;
+               }
+       }
+       if (tty && tty_port_cts_enabled(&self->port)) {
+               if (tty->hw_stopped) {
+                       if (status & IRCOMM_CTS) {
+                               pr_debug("%s(), CTS tx start...\n", __func__);
+                               tty->hw_stopped = 0;
+
+                               /* Wake up processes blocked on open */
+                               wake_up_interruptible(&self->port.open_wait);
+
+                               schedule_work(&self->tqueue);
+                               goto put;
+                       }
+               } else {
+                       if (!(status & IRCOMM_CTS)) {
+                               pr_debug("%s(), CTS tx stop...\n", __func__);
+                               tty->hw_stopped = 1;
+                       }
+               }
+       }
+put:
+       tty_kref_put(tty);
+}
+
+/*
+ * Function ircomm_tty_data_indication (instance, sap, skb)
+ *
+ *    Handle incoming data, and deliver it to the line discipline
+ *
+ */
+static int ircomm_tty_data_indication(void *instance, void *sap,
+                                     struct sk_buff *skb)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       struct tty_struct *tty;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       tty = tty_port_tty_get(&self->port);
+       if (!tty) {
+               pr_debug("%s(), no tty!\n", __func__);
+               return 0;
+       }
+
+       /*
+        * If we receive data when hardware is stopped then something is wrong.
+        * We try to poll the peers line settings to check if we are up todate.
+        * Devices like WinCE can do this, and since they don't send any
+        * params, we can just as well declare the hardware for running.
+        */
+       if (tty->hw_stopped && (self->flow == FLOW_START)) {
+               pr_debug("%s(), polling for line settings!\n", __func__);
+               ircomm_param_request(self, IRCOMM_POLL, TRUE);
+
+               /* We can just as well declare the hardware for running */
+               ircomm_tty_send_initial_parameters(self);
+               ircomm_tty_link_established(self);
+       }
+       tty_kref_put(tty);
+
+       /*
+        * Use flip buffer functions since the code may be called from interrupt
+        * context
+        */
+       tty_insert_flip_string(&self->port, skb->data, skb->len);
+       tty_flip_buffer_push(&self->port);
+
+       /* No need to kfree_skb - see ircomm_ttp_data_indication() */
+
+       return 0;
+}
+
+/*
+ * Function ircomm_tty_control_indication (instance, sap, skb)
+ *
+ *    Parse all incoming parameters (easy!)
+ *
+ */
+static int ircomm_tty_control_indication(void *instance, void *sap,
+                                        struct sk_buff *skb)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       int clen;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       clen = skb->data[0];
+
+       irda_param_extract_all(self, skb->data+1, IRDA_MIN(skb->len-1, clen),
+                              &ircomm_param_info);
+
+       /* No need to kfree_skb - see ircomm_control_indication() */
+
+       return 0;
+}
+
+/*
+ * Function ircomm_tty_flow_indication (instance, sap, cmd)
+ *
+ *    This function is called by IrTTP when it wants us to slow down the
+ *    transmission of data. We just mark the hardware as stopped, and wait
+ *    for IrTTP to notify us that things are OK again.
+ */
+static void ircomm_tty_flow_indication(void *instance, void *sap,
+                                      LOCAL_FLOW cmd)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       struct tty_struct *tty;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       tty = tty_port_tty_get(&self->port);
+
+       switch (cmd) {
+       case FLOW_START:
+               pr_debug("%s(), hw start!\n", __func__);
+               if (tty)
+                       tty->hw_stopped = 0;
+
+               /* ircomm_tty_do_softint will take care of the rest */
+               schedule_work(&self->tqueue);
+               break;
+       default:  /* If we get here, something is very wrong, better stop */
+       case FLOW_STOP:
+               pr_debug("%s(), hw stopped!\n", __func__);
+               if (tty)
+                       tty->hw_stopped = 1;
+               break;
+       }
+
+       tty_kref_put(tty);
+       self->flow = cmd;
+}
+
+#ifdef CONFIG_PROC_FS
+static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m)
+{
+       struct tty_struct *tty;
+       char sep;
+
+       seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]);
+
+       seq_puts(m, "Service type: ");
+       if (self->service_type & IRCOMM_9_WIRE)
+               seq_puts(m, "9_WIRE");
+       else if (self->service_type & IRCOMM_3_WIRE)
+               seq_puts(m, "3_WIRE");
+       else if (self->service_type & IRCOMM_3_WIRE_RAW)
+               seq_puts(m, "3_WIRE_RAW");
+       else
+               seq_puts(m, "No common service type!\n");
+       seq_putc(m, '\n');
+
+       seq_printf(m, "Port name: %s\n", self->settings.port_name);
+
+       seq_printf(m, "DTE status:");
+       sep = ' ';
+       if (self->settings.dte & IRCOMM_RTS) {
+               seq_printf(m, "%cRTS", sep);
+               sep = '|';
+       }
+       if (self->settings.dte & IRCOMM_DTR) {
+               seq_printf(m, "%cDTR", sep);
+               sep = '|';
+       }
+       seq_putc(m, '\n');
+
+       seq_puts(m, "DCE status:");
+       sep = ' ';
+       if (self->settings.dce & IRCOMM_CTS) {
+               seq_printf(m, "%cCTS", sep);
+               sep = '|';
+       }
+       if (self->settings.dce & IRCOMM_DSR) {
+               seq_printf(m, "%cDSR", sep);
+               sep = '|';
+       }
+       if (self->settings.dce & IRCOMM_CD) {
+               seq_printf(m, "%cCD", sep);
+               sep = '|';
+       }
+       if (self->settings.dce & IRCOMM_RI) {
+               seq_printf(m, "%cRI", sep);
+               sep = '|';
+       }
+       seq_putc(m, '\n');
+
+       seq_puts(m, "Configuration: ");
+       if (!self->settings.null_modem)
+               seq_puts(m, "DTE <-> DCE\n");
+       else
+               seq_puts(m, "DTE <-> DTE (null modem emulation)\n");
+
+       seq_printf(m, "Data rate: %d\n", self->settings.data_rate);
+
+       seq_puts(m, "Flow control:");
+       sep = ' ';
+       if (self->settings.flow_control & IRCOMM_XON_XOFF_IN) {
+               seq_printf(m, "%cXON_XOFF_IN", sep);
+               sep = '|';
+       }
+       if (self->settings.flow_control & IRCOMM_XON_XOFF_OUT) {
+               seq_printf(m, "%cXON_XOFF_OUT", sep);
+               sep = '|';
+       }
+       if (self->settings.flow_control & IRCOMM_RTS_CTS_IN) {
+               seq_printf(m, "%cRTS_CTS_IN", sep);
+               sep = '|';
+       }
+       if (self->settings.flow_control & IRCOMM_RTS_CTS_OUT) {
+               seq_printf(m, "%cRTS_CTS_OUT", sep);
+               sep = '|';
+       }
+       if (self->settings.flow_control & IRCOMM_DSR_DTR_IN) {
+               seq_printf(m, "%cDSR_DTR_IN", sep);
+               sep = '|';
+       }
+       if (self->settings.flow_control & IRCOMM_DSR_DTR_OUT) {
+               seq_printf(m, "%cDSR_DTR_OUT", sep);
+               sep = '|';
+       }
+       if (self->settings.flow_control & IRCOMM_ENQ_ACK_IN) {
+               seq_printf(m, "%cENQ_ACK_IN", sep);
+               sep = '|';
+       }
+       if (self->settings.flow_control & IRCOMM_ENQ_ACK_OUT) {
+               seq_printf(m, "%cENQ_ACK_OUT", sep);
+               sep = '|';
+       }
+       seq_putc(m, '\n');
+
+       seq_puts(m, "Flags:");
+       sep = ' ';
+       if (tty_port_cts_enabled(&self->port)) {
+               seq_printf(m, "%cASYNC_CTS_FLOW", sep);
+               sep = '|';
+       }
+       if (tty_port_check_carrier(&self->port)) {
+               seq_printf(m, "%cASYNC_CHECK_CD", sep);
+               sep = '|';
+       }
+       if (tty_port_initialized(&self->port)) {
+               seq_printf(m, "%cASYNC_INITIALIZED", sep);
+               sep = '|';
+       }
+       if (self->port.flags & ASYNC_LOW_LATENCY) {
+               seq_printf(m, "%cASYNC_LOW_LATENCY", sep);
+               sep = '|';
+       }
+       if (tty_port_active(&self->port)) {
+               seq_printf(m, "%cASYNC_NORMAL_ACTIVE", sep);
+               sep = '|';
+       }
+       seq_putc(m, '\n');
+
+       seq_printf(m, "Role: %s\n", self->client ? "client" : "server");
+       seq_printf(m, "Open count: %d\n", self->port.count);
+       seq_printf(m, "Max data size: %d\n", self->max_data_size);
+       seq_printf(m, "Max header size: %d\n", self->max_header_size);
+
+       tty = tty_port_tty_get(&self->port);
+       if (tty) {
+               seq_printf(m, "Hardware: %s\n",
+                              tty->hw_stopped ? "Stopped" : "Running");
+               tty_kref_put(tty);
+       }
+}
+
+static int ircomm_tty_proc_show(struct seq_file *m, void *v)
+{
+       struct ircomm_tty_cb *self;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ircomm_tty->hb_spinlock, flags);
+
+       self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty);
+       while (self != NULL) {
+               if (self->magic != IRCOMM_TTY_MAGIC)
+                       break;
+
+               ircomm_tty_line_info(self, m);
+               self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty);
+       }
+       spin_unlock_irqrestore(&ircomm_tty->hb_spinlock, flags);
+       return 0;
+}
+
+static int ircomm_tty_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ircomm_tty_proc_show, NULL);
+}
+
+static const struct file_operations ircomm_tty_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = ircomm_tty_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+#endif /* CONFIG_PROC_FS */
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("IrCOMM serial TTY driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(IRCOMM_TTY_MAJOR);
+
+module_init(ircomm_tty_init);
+module_exit(ircomm_tty_cleanup);
diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c b/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c
new file mode 100644 (file)
index 0000000..0a41101
--- /dev/null
@@ -0,0 +1,987 @@
+/*********************************************************************
+ *
+ * Filename:      ircomm_tty_attach.c
+ * Version:
+ * Description:   Code for attaching the serial driver to IrCOMM
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sat Jun  5 17:42:00 1999
+ * Modified at:   Tue Jan  4 14:20:49 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/init.h>
+#include <linux/sched.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irias_object.h>
+#include <net/irda/parameters.h>
+
+#include <net/irda/ircomm_core.h>
+#include <net/irda/ircomm_param.h>
+#include <net/irda/ircomm_event.h>
+
+#include <net/irda/ircomm_tty.h>
+#include <net/irda/ircomm_tty_attach.h>
+
+static void ircomm_tty_ias_register(struct ircomm_tty_cb *self);
+static void ircomm_tty_discovery_indication(discinfo_t *discovery,
+                                           DISCOVERY_MODE mode,
+                                           void *priv);
+static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
+                                       struct ias_value *value, void *priv);
+static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
+                                           int timeout);
+static void ircomm_tty_watchdog_timer_expired(void *data);
+
+static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
+                                IRCOMM_TTY_EVENT event,
+                                struct sk_buff *skb,
+                                struct ircomm_tty_info *info);
+static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
+                                  IRCOMM_TTY_EVENT event,
+                                  struct sk_buff *skb,
+                                  struct ircomm_tty_info *info);
+static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
+                                            IRCOMM_TTY_EVENT event,
+                                            struct sk_buff *skb,
+                                            struct ircomm_tty_info *info);
+static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
+                                          IRCOMM_TTY_EVENT event,
+                                          struct sk_buff *skb,
+                                          struct ircomm_tty_info *info);
+static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
+                                 IRCOMM_TTY_EVENT event,
+                                 struct sk_buff *skb,
+                                 struct ircomm_tty_info *info);
+static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
+                                 IRCOMM_TTY_EVENT event,
+                                 struct sk_buff *skb,
+                                 struct ircomm_tty_info *info);
+
+const char *const ircomm_tty_state[] = {
+       "IRCOMM_TTY_IDLE",
+       "IRCOMM_TTY_SEARCH",
+       "IRCOMM_TTY_QUERY_PARAMETERS",
+       "IRCOMM_TTY_QUERY_LSAP_SEL",
+       "IRCOMM_TTY_SETUP",
+       "IRCOMM_TTY_READY",
+       "*** ERROR *** ",
+};
+
+static const char *const ircomm_tty_event[] __maybe_unused = {
+       "IRCOMM_TTY_ATTACH_CABLE",
+       "IRCOMM_TTY_DETACH_CABLE",
+       "IRCOMM_TTY_DATA_REQUEST",
+       "IRCOMM_TTY_DATA_INDICATION",
+       "IRCOMM_TTY_DISCOVERY_REQUEST",
+       "IRCOMM_TTY_DISCOVERY_INDICATION",
+       "IRCOMM_TTY_CONNECT_CONFIRM",
+       "IRCOMM_TTY_CONNECT_INDICATION",
+       "IRCOMM_TTY_DISCONNECT_REQUEST",
+       "IRCOMM_TTY_DISCONNECT_INDICATION",
+       "IRCOMM_TTY_WD_TIMER_EXPIRED",
+       "IRCOMM_TTY_GOT_PARAMETERS",
+       "IRCOMM_TTY_GOT_LSAPSEL",
+       "*** ERROR ****",
+};
+
+static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
+                     struct sk_buff *skb, struct ircomm_tty_info *info) =
+{
+       ircomm_tty_state_idle,
+       ircomm_tty_state_search,
+       ircomm_tty_state_query_parameters,
+       ircomm_tty_state_query_lsap_sel,
+       ircomm_tty_state_setup,
+       ircomm_tty_state_ready,
+};
+
+/*
+ * Function ircomm_tty_attach_cable (driver)
+ *
+ *    Try to attach cable (IrCOMM link). This function will only return
+ *    when the link has been connected, or if an error condition occurs.
+ *    If success, the return value is the resulting service type.
+ */
+int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
+{
+       struct tty_struct *tty;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       /* Check if somebody has already connected to us */
+       if (ircomm_is_connected(self->ircomm)) {
+               pr_debug("%s(), already connected!\n", __func__);
+               return 0;
+       }
+
+       /* Make sure nobody tries to write before the link is up */
+       tty = tty_port_tty_get(&self->port);
+       if (tty) {
+               tty->hw_stopped = 1;
+               tty_kref_put(tty);
+       }
+
+       ircomm_tty_ias_register(self);
+
+       ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_detach_cable (driver)
+ *
+ *    Detach cable, or cable has been detached by peer
+ *
+ */
+void ircomm_tty_detach_cable(struct ircomm_tty_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       del_timer(&self->watchdog_timer);
+
+       /* Remove discovery handler */
+       if (self->ckey) {
+               irlmp_unregister_client(self->ckey);
+               self->ckey = NULL;
+       }
+       /* Remove IrCOMM hint bits */
+       if (self->skey) {
+               irlmp_unregister_service(self->skey);
+               self->skey = NULL;
+       }
+
+       if (self->iriap) {
+               iriap_close(self->iriap);
+               self->iriap = NULL;
+       }
+
+       /* Remove LM-IAS object */
+       if (self->obj) {
+               irias_delete_object(self->obj);
+               self->obj = NULL;
+       }
+
+       ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL);
+
+       /* Reset some values */
+       self->daddr = self->saddr = 0;
+       self->dlsap_sel = self->slsap_sel = 0;
+
+       memset(&self->settings, 0, sizeof(struct ircomm_params));
+}
+
+/*
+ * Function ircomm_tty_ias_register (self)
+ *
+ *    Register with LM-IAS depending on which service type we are
+ *
+ */
+static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
+{
+       __u8 oct_seq[6];
+       __u16 hints;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       /* Compute hint bits based on service */
+       hints = irlmp_service_to_hint(S_COMM);
+       if (self->service_type & IRCOMM_3_WIRE_RAW)
+               hints |= irlmp_service_to_hint(S_PRINTER);
+
+       /* Advertise IrCOMM hint bit in discovery */
+       if (!self->skey)
+               self->skey = irlmp_register_service(hints);
+       /* Set up a discovery handler */
+       if (!self->ckey)
+               self->ckey = irlmp_register_client(hints,
+                                                  ircomm_tty_discovery_indication,
+                                                  NULL, (void *) self);
+
+       /* If already done, no need to do it again */
+       if (self->obj)
+               return;
+
+       if (self->service_type & IRCOMM_3_WIRE_RAW) {
+               /* Register IrLPT with LM-IAS */
+               self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID);
+               irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel",
+                                        self->slsap_sel, IAS_KERNEL_ATTR);
+       } else {
+               /* Register IrCOMM with LM-IAS */
+               self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID);
+               irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel",
+                                        self->slsap_sel, IAS_KERNEL_ATTR);
+
+               /* Code the parameters into the buffer */
+               irda_param_pack(oct_seq, "bbbbbb",
+                               IRCOMM_SERVICE_TYPE, 1, self->service_type,
+                               IRCOMM_PORT_TYPE,    1, IRCOMM_SERIAL);
+
+               /* Register parameters with LM-IAS */
+               irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6,
+                                       IAS_KERNEL_ATTR);
+       }
+       irias_insert_object(self->obj);
+}
+
+/*
+ * Function ircomm_tty_ias_unregister (self)
+ *
+ *    Remove our IAS object and client hook while connected.
+ *
+ */
+static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self)
+{
+       /* Remove LM-IAS object now so it is not reused.
+        * IrCOMM deals very poorly with multiple incoming connections.
+        * It should looks a lot more like IrNET, and "dup" a server TSAP
+        * to the application TSAP (based on various rules).
+        * This is a cheap workaround allowing multiple clients to
+        * connect to us. It will not always work.
+        * Each IrCOMM socket has an IAS entry. Incoming connection will
+        * pick the first one found. So, when we are fully connected,
+        * we remove our IAS entries so that the next IAS entry is used.
+        * We do that for *both* client and server, because a server
+        * can also create client instances.
+        * Jean II */
+       if (self->obj) {
+               irias_delete_object(self->obj);
+               self->obj = NULL;
+       }
+
+#if 0
+       /* Remove discovery handler.
+        * While we are connected, we no longer need to receive
+        * discovery events. This would be the case if there is
+        * multiple IrLAP interfaces. Jean II */
+       if (self->ckey) {
+               irlmp_unregister_client(self->ckey);
+               self->ckey = NULL;
+       }
+#endif
+}
+
+/*
+ * Function ircomm_send_initial_parameters (self)
+ *
+ *    Send initial parameters to the remote IrCOMM device. These parameters
+ *    must be sent before any data.
+ */
+int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (self->service_type & IRCOMM_3_WIRE_RAW)
+               return 0;
+
+       /*
+        * Set default values, but only if the application for some reason
+        * haven't set them already
+        */
+       pr_debug("%s(), data-rate = %d\n", __func__ ,
+                self->settings.data_rate);
+       if (!self->settings.data_rate)
+               self->settings.data_rate = 9600;
+       pr_debug("%s(), data-format = %d\n", __func__ ,
+                self->settings.data_format);
+       if (!self->settings.data_format)
+               self->settings.data_format = IRCOMM_WSIZE_8;  /* 8N1 */
+
+       pr_debug("%s(), flow-control = %d\n", __func__ ,
+                self->settings.flow_control);
+       /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
+
+       /* Do not set delta values for the initial parameters */
+       self->settings.dte = IRCOMM_DTR | IRCOMM_RTS;
+
+       /* Only send service type parameter when we are the client */
+       if (self->client)
+               ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE);
+       ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
+       ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
+
+       /* For a 3 wire service, we just flush the last parameter and return */
+       if (self->settings.service_type == IRCOMM_3_WIRE) {
+               ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
+               return 0;
+       }
+
+       /* Only 9-wire service types continue here */
+       ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE);
+#if 0
+       ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE);
+       ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE);
+#endif
+       /* Notify peer that we are ready to receive data */
+       ircomm_param_request(self, IRCOMM_DTE, TRUE);
+
+       return 0;
+}
+
+/*
+ * Function ircomm_tty_discovery_indication (discovery)
+ *
+ *    Remote device is discovered, try query the remote IAS to see which
+ *    device it is, and which services it has.
+ *
+ */
+static void ircomm_tty_discovery_indication(discinfo_t *discovery,
+                                           DISCOVERY_MODE mode,
+                                           void *priv)
+{
+       struct ircomm_tty_cb *self;
+       struct ircomm_tty_info info;
+
+       /* Important note :
+        * We need to drop all passive discoveries.
+        * The LSAP management of IrComm is deficient and doesn't deal
+        * with the case of two instance connecting to each other
+        * simultaneously (it will deadlock in LMP).
+        * The proper fix would be to use the same technique as in IrNET,
+        * to have one server socket and separate instances for the
+        * connecting/connected socket.
+        * The workaround is to drop passive discovery, which drastically
+        * reduce the probability of this happening.
+        * Jean II */
+       if(mode == DISCOVERY_PASSIVE)
+               return;
+
+       info.daddr = discovery->daddr;
+       info.saddr = discovery->saddr;
+
+       self = priv;
+       ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
+                           NULL, &info);
+}
+
+/*
+ * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb)
+ *
+ *    Link disconnected
+ *
+ */
+void ircomm_tty_disconnect_indication(void *instance, void *sap,
+                                     LM_REASON reason,
+                                     struct sk_buff *skb)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       struct tty_struct *tty;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       tty = tty_port_tty_get(&self->port);
+       if (!tty)
+               return;
+
+       /* This will stop control data transfers */
+       self->flow = FLOW_STOP;
+
+       /* Stop data transfers */
+       tty->hw_stopped = 1;
+
+       ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
+                           NULL);
+       tty_kref_put(tty);
+}
+
+/*
+ * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
+ *
+ *    Got result from the IAS query we make
+ *
+ */
+static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
+                                       struct ias_value *value,
+                                       void *priv)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       /* We probably don't need to make any more queries */
+       iriap_close(self->iriap);
+       self->iriap = NULL;
+
+       /* Check if request succeeded */
+       if (result != IAS_SUCCESS) {
+               pr_debug("%s(), got NULL value!\n", __func__);
+               return;
+       }
+
+       switch (value->type) {
+       case IAS_OCT_SEQ:
+               pr_debug("%s(), got octet sequence\n", __func__);
+
+               irda_param_extract_all(self, value->t.oct_seq, value->len,
+                                      &ircomm_param_info);
+
+               ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
+                                   NULL);
+               break;
+       case IAS_INTEGER:
+               /* Got LSAP selector */
+               pr_debug("%s(), got lsapsel = %d\n", __func__ ,
+                        value->t.integer);
+
+               if (value->t.integer == -1) {
+                       pr_debug("%s(), invalid value!\n", __func__);
+               } else
+                       self->dlsap_sel = value->t.integer;
+
+               ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
+               break;
+       case IAS_MISSING:
+               pr_debug("%s(), got IAS_MISSING\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), got unknown type!\n", __func__);
+               break;
+       }
+       irias_delete_value(value);
+}
+
+/*
+ * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
+ *
+ *    Connection confirmed
+ *
+ */
+void ircomm_tty_connect_confirm(void *instance, void *sap,
+                               struct qos_info *qos,
+                               __u32 max_data_size,
+                               __u8 max_header_size,
+                               struct sk_buff *skb)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       self->client = TRUE;
+       self->max_data_size = max_data_size;
+       self->max_header_size = max_header_size;
+       self->flow = FLOW_START;
+
+       ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
+
+       /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
+}
+
+/*
+ * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
+ *                                         skb)
+ *
+ *    we are discovered and being requested to connect by remote device !
+ *
+ */
+void ircomm_tty_connect_indication(void *instance, void *sap,
+                                  struct qos_info *qos,
+                                  __u32 max_data_size,
+                                  __u8 max_header_size,
+                                  struct sk_buff *skb)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+       int clen;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       self->client = FALSE;
+       self->max_data_size = max_data_size;
+       self->max_header_size = max_header_size;
+       self->flow = FLOW_START;
+
+       clen = skb->data[0];
+       if (clen)
+               irda_param_extract_all(self, skb->data+1,
+                                      IRDA_MIN(skb->len, clen),
+                                      &ircomm_param_info);
+
+       ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
+
+       /* No need to kfree_skb - see ircomm_ttp_connect_indication() */
+}
+
+/*
+ * Function ircomm_tty_link_established (self)
+ *
+ *    Called when the IrCOMM link is established
+ *
+ */
+void ircomm_tty_link_established(struct ircomm_tty_cb *self)
+{
+       struct tty_struct *tty;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       tty = tty_port_tty_get(&self->port);
+       if (!tty)
+               return;
+
+       del_timer(&self->watchdog_timer);
+
+       /*
+        * IrCOMM link is now up, and if we are not using hardware
+        * flow-control, then declare the hardware as running. Otherwise we
+        * will have to wait for the peer device (DCE) to raise the CTS
+        * line.
+        */
+       if (tty_port_cts_enabled(&self->port) &&
+                       ((self->settings.dce & IRCOMM_CTS) == 0)) {
+               pr_debug("%s(), waiting for CTS ...\n", __func__);
+               goto put;
+       } else {
+               pr_debug("%s(), starting hardware!\n", __func__);
+
+               tty->hw_stopped = 0;
+
+               /* Wake up processes blocked on open */
+               wake_up_interruptible(&self->port.open_wait);
+       }
+
+       schedule_work(&self->tqueue);
+put:
+       tty_kref_put(tty);
+}
+
+/*
+ * Function ircomm_tty_start_watchdog_timer (self, timeout)
+ *
+ *    Start the watchdog timer. This timer is used to make sure that any
+ *    connection attempt is successful, and if not, we will retry after
+ *    the timeout
+ */
+static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
+                                           int timeout)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
+                        ircomm_tty_watchdog_timer_expired);
+}
+
+/*
+ * Function ircomm_tty_watchdog_timer_expired (data)
+ *
+ *    Called when the connect procedure have taken to much time.
+ *
+ */
+static void ircomm_tty_watchdog_timer_expired(void *data)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
+}
+
+
+/*
+ * Function ircomm_tty_do_event (self, event, skb)
+ *
+ *    Process event
+ *
+ */
+int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
+                       struct sk_buff *skb, struct ircomm_tty_info *info)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       pr_debug("%s: state=%s, event=%s\n", __func__ ,
+                ircomm_tty_state[self->state], ircomm_tty_event[event]);
+
+       return (*state[self->state])(self, event, skb, info);
+}
+
+/*
+ * Function ircomm_tty_next_state (self, state)
+ *
+ *    Switch state
+ *
+ */
+static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
+{
+       /*
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+       pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
+       ircomm_tty_state[self->state], self->service_type);
+       */
+       self->state = state;
+}
+
+/*
+ * Function ircomm_tty_state_idle (self, event, skb, info)
+ *
+ *    Just hanging around
+ *
+ */
+static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
+                                IRCOMM_TTY_EVENT event,
+                                struct sk_buff *skb,
+                                struct ircomm_tty_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s: state=%s, event=%s\n", __func__ ,
+                ircomm_tty_state[self->state], ircomm_tty_event[event]);
+       switch (event) {
+       case IRCOMM_TTY_ATTACH_CABLE:
+               /* Try to discover any remote devices */
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
+
+               irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
+               break;
+       case IRCOMM_TTY_DISCOVERY_INDICATION:
+               self->daddr = info->daddr;
+               self->saddr = info->saddr;
+
+               if (self->iriap) {
+                       net_warn_ratelimited("%s(), busy with a previous query\n",
+                                            __func__);
+                       return -EBUSY;
+               }
+
+               self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+                                        ircomm_tty_getvalue_confirm);
+
+               iriap_getvaluebyclass_request(self->iriap,
+                                             self->saddr, self->daddr,
+                                             "IrDA:IrCOMM", "Parameters");
+
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
+               break;
+       case IRCOMM_TTY_CONNECT_INDICATION:
+               del_timer(&self->watchdog_timer);
+
+               /* Accept connection */
+               ircomm_connect_response(self->ircomm, NULL);
+               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+               break;
+       case IRCOMM_TTY_WD_TIMER_EXPIRED:
+               /* Just stay idle */
+               break;
+       case IRCOMM_TTY_DETACH_CABLE:
+               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+               break;
+       default:
+               pr_debug("%s(), unknown event: %s\n", __func__ ,
+                        ircomm_tty_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_tty_state_search (self, event, skb, info)
+ *
+ *    Trying to discover an IrCOMM device
+ *
+ */
+static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
+                                  IRCOMM_TTY_EVENT event,
+                                  struct sk_buff *skb,
+                                  struct ircomm_tty_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s: state=%s, event=%s\n", __func__ ,
+                ircomm_tty_state[self->state], ircomm_tty_event[event]);
+
+       switch (event) {
+       case IRCOMM_TTY_DISCOVERY_INDICATION:
+               self->daddr = info->daddr;
+               self->saddr = info->saddr;
+
+               if (self->iriap) {
+                       net_warn_ratelimited("%s(), busy with a previous query\n",
+                                            __func__);
+                       return -EBUSY;
+               }
+
+               self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+                                        ircomm_tty_getvalue_confirm);
+
+               if (self->service_type == IRCOMM_3_WIRE_RAW) {
+                       iriap_getvaluebyclass_request(self->iriap, self->saddr,
+                                                     self->daddr, "IrLPT",
+                                                     "IrDA:IrLMP:LsapSel");
+                       ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
+               } else {
+                       iriap_getvaluebyclass_request(self->iriap, self->saddr,
+                                                     self->daddr,
+                                                     "IrDA:IrCOMM",
+                                                     "Parameters");
+
+                       ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
+               }
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               break;
+       case IRCOMM_TTY_CONNECT_INDICATION:
+               del_timer(&self->watchdog_timer);
+               ircomm_tty_ias_unregister(self);
+
+               /* Accept connection */
+               ircomm_connect_response(self->ircomm, NULL);
+               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+               break;
+       case IRCOMM_TTY_WD_TIMER_EXPIRED:
+#if 1
+               /* Give up */
+#else
+               /* Try to discover any remote devices */
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
+#endif
+               break;
+       case IRCOMM_TTY_DETACH_CABLE:
+               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+               break;
+       default:
+               pr_debug("%s(), unknown event: %s\n", __func__ ,
+                        ircomm_tty_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_tty_state_query (self, event, skb, info)
+ *
+ *    Querying the remote LM-IAS for IrCOMM parameters
+ *
+ */
+static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
+                                            IRCOMM_TTY_EVENT event,
+                                            struct sk_buff *skb,
+                                            struct ircomm_tty_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s: state=%s, event=%s\n", __func__ ,
+                ircomm_tty_state[self->state], ircomm_tty_event[event]);
+
+       switch (event) {
+       case IRCOMM_TTY_GOT_PARAMETERS:
+               if (self->iriap) {
+                       net_warn_ratelimited("%s(), busy with a previous query\n",
+                                            __func__);
+                       return -EBUSY;
+               }
+
+               self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+                                        ircomm_tty_getvalue_confirm);
+
+               iriap_getvaluebyclass_request(self->iriap, self->saddr,
+                                             self->daddr, "IrDA:IrCOMM",
+                                             "IrDA:TinyTP:LsapSel");
+
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
+               break;
+       case IRCOMM_TTY_WD_TIMER_EXPIRED:
+               /* Go back to search mode */
+               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               break;
+       case IRCOMM_TTY_CONNECT_INDICATION:
+               del_timer(&self->watchdog_timer);
+               ircomm_tty_ias_unregister(self);
+
+               /* Accept connection */
+               ircomm_connect_response(self->ircomm, NULL);
+               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+               break;
+       case IRCOMM_TTY_DETACH_CABLE:
+               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+               break;
+       default:
+               pr_debug("%s(), unknown event: %s\n", __func__ ,
+                        ircomm_tty_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info)
+ *
+ *    Query remote LM-IAS for the LSAP selector which we can connect to
+ *
+ */
+static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
+                                          IRCOMM_TTY_EVENT event,
+                                          struct sk_buff *skb,
+                                          struct ircomm_tty_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s: state=%s, event=%s\n", __func__ ,
+                ircomm_tty_state[self->state], ircomm_tty_event[event]);
+
+       switch (event) {
+       case IRCOMM_TTY_GOT_LSAPSEL:
+               /* Connect to remote device */
+               ret = ircomm_connect_request(self->ircomm, self->dlsap_sel,
+                                            self->saddr, self->daddr,
+                                            NULL, self->service_type);
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               ircomm_tty_next_state(self, IRCOMM_TTY_SETUP);
+               break;
+       case IRCOMM_TTY_WD_TIMER_EXPIRED:
+               /* Go back to search mode */
+               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               break;
+       case IRCOMM_TTY_CONNECT_INDICATION:
+               del_timer(&self->watchdog_timer);
+               ircomm_tty_ias_unregister(self);
+
+               /* Accept connection */
+               ircomm_connect_response(self->ircomm, NULL);
+               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+               break;
+       case IRCOMM_TTY_DETACH_CABLE:
+               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+               break;
+       default:
+               pr_debug("%s(), unknown event: %s\n", __func__ ,
+                        ircomm_tty_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_tty_state_setup (self, event, skb, info)
+ *
+ *    Trying to connect
+ *
+ */
+static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
+                                 IRCOMM_TTY_EVENT event,
+                                 struct sk_buff *skb,
+                                 struct ircomm_tty_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s: state=%s, event=%s\n", __func__ ,
+                ircomm_tty_state[self->state], ircomm_tty_event[event]);
+
+       switch (event) {
+       case IRCOMM_TTY_CONNECT_CONFIRM:
+               del_timer(&self->watchdog_timer);
+               ircomm_tty_ias_unregister(self);
+
+               /*
+                * Send initial parameters. This will also send out queued
+                * parameters waiting for the connection to come up
+                */
+               ircomm_tty_send_initial_parameters(self);
+               ircomm_tty_link_established(self);
+               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+               break;
+       case IRCOMM_TTY_CONNECT_INDICATION:
+               del_timer(&self->watchdog_timer);
+               ircomm_tty_ias_unregister(self);
+
+               /* Accept connection */
+               ircomm_connect_response(self->ircomm, NULL);
+               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+               break;
+       case IRCOMM_TTY_WD_TIMER_EXPIRED:
+               /* Go back to search mode */
+               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+               break;
+       case IRCOMM_TTY_DETACH_CABLE:
+               /* ircomm_disconnect_request(self->ircomm, NULL); */
+               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+               break;
+       default:
+               pr_debug("%s(), unknown event: %s\n", __func__ ,
+                        ircomm_tty_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Function ircomm_tty_state_ready (self, event, skb, info)
+ *
+ *    IrCOMM is now connected
+ *
+ */
+static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
+                                 IRCOMM_TTY_EVENT event,
+                                 struct sk_buff *skb,
+                                 struct ircomm_tty_info *info)
+{
+       int ret = 0;
+
+       switch (event) {
+       case IRCOMM_TTY_DATA_REQUEST:
+               ret = ircomm_data_request(self->ircomm, skb);
+               break;
+       case IRCOMM_TTY_DETACH_CABLE:
+               ircomm_disconnect_request(self->ircomm, NULL);
+               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+               break;
+       case IRCOMM_TTY_DISCONNECT_INDICATION:
+               ircomm_tty_ias_register(self);
+               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
+               ircomm_tty_start_watchdog_timer(self, 3*HZ);
+
+               if (tty_port_check_carrier(&self->port)) {
+                       /* Drop carrier */
+                       self->settings.dce = IRCOMM_DELTA_CD;
+                       ircomm_tty_check_modem_status(self);
+               } else {
+                       pr_debug("%s(), hanging up!\n", __func__);
+                       tty_port_tty_hangup(&self->port, false);
+               }
+               break;
+       default:
+               pr_debug("%s(), unknown event: %s\n", __func__ ,
+                        ircomm_tty_event[event]);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c b/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c
new file mode 100644 (file)
index 0000000..171c3de
--- /dev/null
@@ -0,0 +1,291 @@
+/*********************************************************************
+ *
+ * Filename:      ircomm_tty_ioctl.c
+ * Version:
+ * Description:
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Thu Jun 10 14:39:09 1999
+ * Modified at:   Wed Jan  5 14:45:43 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+
+#include <linux/uaccess.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+
+#include <net/irda/ircomm_core.h>
+#include <net/irda/ircomm_param.h>
+#include <net/irda/ircomm_tty_attach.h>
+#include <net/irda/ircomm_tty.h>
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+/*
+ * Function ircomm_tty_change_speed (driver)
+ *
+ *    Change speed of the driver. If the remote device is a DCE, then this
+ *    should make it change the speed of its serial port
+ */
+static void ircomm_tty_change_speed(struct ircomm_tty_cb *self,
+               struct tty_struct *tty)
+{
+       unsigned int cflag, cval;
+       int baud;
+
+       if (!self->ircomm)
+               return;
+
+       cflag = tty->termios.c_cflag;
+
+       /*  byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS5: cval = IRCOMM_WSIZE_5; break;
+       case CS6: cval = IRCOMM_WSIZE_6; break;
+       case CS7: cval = IRCOMM_WSIZE_7; break;
+       case CS8: cval = IRCOMM_WSIZE_8; break;
+       default:  cval = IRCOMM_WSIZE_5; break;
+       }
+       if (cflag & CSTOPB)
+               cval |= IRCOMM_2_STOP_BIT;
+
+       if (cflag & PARENB)
+               cval |= IRCOMM_PARITY_ENABLE;
+       if (!(cflag & PARODD))
+               cval |= IRCOMM_PARITY_EVEN;
+
+       /* Determine divisor based on baud rate */
+       baud = tty_get_baud_rate(tty);
+       if (!baud)
+               baud = 9600;    /* B0 transition handled in rs_set_termios */
+
+       self->settings.data_rate = baud;
+       ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
+
+       /* CTS flow control flag and modem status interrupts */
+       tty_port_set_cts_flow(&self->port, cflag & CRTSCTS);
+       if (cflag & CRTSCTS) {
+               self->settings.flow_control |= IRCOMM_RTS_CTS_IN;
+               /* This got me. Bummer. Jean II */
+               if (self->service_type == IRCOMM_3_WIRE_RAW)
+                       net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n",
+                                            __func__);
+       } else {
+               self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
+       }
+       tty_port_set_check_carrier(&self->port, ~cflag & CLOCAL);
+
+       self->settings.data_format = cval;
+
+       ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
+       ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
+}
+
+/*
+ * Function ircomm_tty_set_termios (tty, old_termios)
+ *
+ *    This routine allows the tty driver to be notified when device's
+ *    termios settings have changed.  Note that a well-designed tty driver
+ *    should be prepared to accept the case where old == NULL, and try to
+ *    do something rational.
+ */
+void ircomm_tty_set_termios(struct tty_struct *tty,
+                           struct ktermios *old_termios)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       unsigned int cflag = tty->termios.c_cflag;
+
+       if ((cflag == old_termios->c_cflag) &&
+           (RELEVANT_IFLAG(tty->termios.c_iflag) ==
+            RELEVANT_IFLAG(old_termios->c_iflag)))
+       {
+               return;
+       }
+
+       ircomm_tty_change_speed(self, tty);
+
+       /* Handle transition to B0 status */
+       if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) {
+               self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS);
+               ircomm_param_request(self, IRCOMM_DTE, TRUE);
+       }
+
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
+               self->settings.dte |= IRCOMM_DTR;
+               if (!C_CRTSCTS(tty) || !tty_throttled(tty))
+                       self->settings.dte |= IRCOMM_RTS;
+               ircomm_param_request(self, IRCOMM_DTE, TRUE);
+       }
+
+       /* Handle turning off CRTSCTS */
+       if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty))
+       {
+               tty->hw_stopped = 0;
+               ircomm_tty_start(tty);
+       }
+}
+
+/*
+ * Function ircomm_tty_tiocmget (tty)
+ *
+ *
+ *
+ */
+int ircomm_tty_tiocmget(struct tty_struct *tty)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       unsigned int result;
+
+       if (tty_io_error(tty))
+               return -EIO;
+
+       result =  ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
+               | ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
+               | ((self->settings.dce & IRCOMM_CD)  ? TIOCM_CAR : 0)
+               | ((self->settings.dce & IRCOMM_RI)  ? TIOCM_RNG : 0)
+               | ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
+               | ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
+       return result;
+}
+
+/*
+ * Function ircomm_tty_tiocmset (tty, set, clear)
+ *
+ *
+ *
+ */
+int ircomm_tty_tiocmset(struct tty_struct *tty,
+                       unsigned int set, unsigned int clear)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+       if (tty_io_error(tty))
+               return -EIO;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+       if (set & TIOCM_RTS)
+               self->settings.dte |= IRCOMM_RTS;
+       if (set & TIOCM_DTR)
+               self->settings.dte |= IRCOMM_DTR;
+
+       if (clear & TIOCM_RTS)
+               self->settings.dte &= ~IRCOMM_RTS;
+       if (clear & TIOCM_DTR)
+               self->settings.dte &= ~IRCOMM_DTR;
+
+       if ((set|clear) & TIOCM_RTS)
+               self->settings.dte |= IRCOMM_DELTA_RTS;
+       if ((set|clear) & TIOCM_DTR)
+               self->settings.dte |= IRCOMM_DELTA_DTR;
+
+       ircomm_param_request(self, IRCOMM_DTE, TRUE);
+
+       return 0;
+}
+
+/*
+ * Function get_serial_info (driver, retinfo)
+ *
+ *
+ *
+ */
+static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self,
+                                     struct serial_struct __user *retinfo)
+{
+       struct serial_struct info;
+
+       memset(&info, 0, sizeof(info));
+       info.line = self->line;
+       info.flags = self->port.flags;
+       info.baud_base = self->settings.data_rate;
+       info.close_delay = self->port.close_delay;
+       info.closing_wait = self->port.closing_wait;
+
+       /* For compatibility  */
+       info.type = PORT_16550A;
+
+       if (copy_to_user(retinfo, &info, sizeof(*retinfo)))
+               return -EFAULT;
+
+       return 0;
+}
+
+/*
+ * Function set_serial_info (driver, new_info)
+ *
+ *
+ *
+ */
+static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self,
+                                     struct serial_struct __user *new_info)
+{
+       return 0;
+}
+
+/*
+ * Function ircomm_tty_ioctl (tty, cmd, arg)
+ *
+ *
+ *
+ */
+int ircomm_tty_ioctl(struct tty_struct *tty,
+                    unsigned int cmd, unsigned long arg)
+{
+       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+       int ret = 0;
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+               if (tty_io_error(tty))
+                   return -EIO;
+       }
+
+       switch (cmd) {
+       case TIOCGSERIAL:
+               ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg);
+               break;
+       case TIOCSSERIAL:
+               ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg);
+               break;
+       case TIOCMIWAIT:
+               pr_debug("(), TIOCMIWAIT, not impl!\n");
+               break;
+
+       case TIOCGICOUNT:
+               pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__);
+               return 0;
+       default:
+               ret = -ENOIOCTLCMD;  /* ioctls which we must ignore */
+       }
+       return ret;
+}
+
+
+
diff --git a/drivers/staging/irda/net/irda_device.c b/drivers/staging/irda/net/irda_device.c
new file mode 100644 (file)
index 0000000..890b90d
--- /dev/null
@@ -0,0 +1,316 @@
+/*********************************************************************
+ *
+ * Filename:      irda_device.c
+ * Version:       0.9
+ * Description:   Utility functions used by the device drivers
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sat Oct  9 09:22:27 1999
+ * Modified at:   Sun Jan 23 17:41:24 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/capability.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/kmod.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include <asm/ioctls.h>
+#include <linux/uaccess.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+
+#include <net/irda/irda_device.h>
+#include <net/irda/irlap.h>
+#include <net/irda/timer.h>
+#include <net/irda/wrapper.h>
+
+static void __irda_task_delete(struct irda_task *task);
+
+static hashbin_t *dongles = NULL;
+static hashbin_t *tasks = NULL;
+
+static void irda_task_timer_expired(void *data);
+
+int __init irda_device_init( void)
+{
+       dongles = hashbin_new(HB_NOLOCK);
+       if (dongles == NULL) {
+               net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n");
+               return -ENOMEM;
+       }
+       spin_lock_init(&dongles->hb_spinlock);
+
+       tasks = hashbin_new(HB_LOCK);
+       if (tasks == NULL) {
+               net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n");
+               hashbin_delete(dongles, NULL);
+               return -ENOMEM;
+       }
+
+       /* We no longer initialise the driver ourselves here, we let
+        * the system do it for us... - Jean II */
+
+       return 0;
+}
+
+static void leftover_dongle(void *arg)
+{
+       struct dongle_reg *reg = arg;
+       net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n",
+                            reg->type);
+}
+
+void irda_device_cleanup(void)
+{
+       hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete);
+
+       hashbin_delete(dongles, leftover_dongle);
+}
+
+/*
+ * Function irda_device_set_media_busy (self, status)
+ *
+ *    Called when we have detected that another station is transmitting
+ *    in contention mode.
+ */
+void irda_device_set_media_busy(struct net_device *dev, int status)
+{
+       struct irlap_cb *self;
+
+       pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE");
+
+       self = (struct irlap_cb *) dev->atalk_ptr;
+
+       /* Some drivers may enable the receive interrupt before calling
+        * irlap_open(), or they may disable the receive interrupt
+        * after calling irlap_close().
+        * The IrDA stack is protected from this in irlap_driver_rcv().
+        * However, the driver calls directly the wrapper, that calls
+        * us directly. Make sure we protect ourselves.
+        * Jean II */
+       if (!self || self->magic != LAP_MAGIC)
+               return;
+
+       if (status) {
+               self->media_busy = TRUE;
+               if (status == SMALL)
+                       irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT);
+               else
+                       irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT);
+               pr_debug("Media busy!\n");
+       } else {
+               self->media_busy = FALSE;
+               irlap_stop_mbusy_timer(self);
+       }
+}
+EXPORT_SYMBOL(irda_device_set_media_busy);
+
+
+/*
+ * Function irda_device_is_receiving (dev)
+ *
+ *    Check if the device driver is currently receiving data
+ *
+ */
+int irda_device_is_receiving(struct net_device *dev)
+{
+       struct if_irda_req req;
+       int ret;
+
+       if (!dev->netdev_ops->ndo_do_ioctl) {
+               net_err_ratelimited("%s: do_ioctl not impl. by device driver\n",
+                                   __func__);
+               return -1;
+       }
+
+       ret = (dev->netdev_ops->ndo_do_ioctl)(dev, (struct ifreq *) &req,
+                                             SIOCGRECEIVING);
+       if (ret < 0)
+               return ret;
+
+       return req.ifr_receiving;
+}
+
+static void __irda_task_delete(struct irda_task *task)
+{
+       del_timer(&task->timer);
+
+       kfree(task);
+}
+
+static void irda_task_delete(struct irda_task *task)
+{
+       /* Unregister task */
+       hashbin_remove(tasks, (long) task, NULL);
+
+       __irda_task_delete(task);
+}
+
+/*
+ * Function irda_task_kick (task)
+ *
+ *    Tries to execute a task possible multiple times until the task is either
+ *    finished, or askes for a timeout. When a task is finished, we do post
+ *    processing, and notify the parent task, that is waiting for this task
+ *    to complete.
+ */
+static int irda_task_kick(struct irda_task *task)
+{
+       int finished = TRUE;
+       int count = 0;
+       int timeout;
+
+       IRDA_ASSERT(task != NULL, return -1;);
+       IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;);
+
+       /* Execute task until it's finished, or askes for a timeout */
+       do {
+               timeout = task->function(task);
+               if (count++ > 100) {
+                       net_err_ratelimited("%s: error in task handler!\n",
+                                           __func__);
+                       irda_task_delete(task);
+                       return TRUE;
+               }
+       } while ((timeout == 0) && (task->state != IRDA_TASK_DONE));
+
+       if (timeout < 0) {
+               net_err_ratelimited("%s: Error executing task!\n", __func__);
+               irda_task_delete(task);
+               return TRUE;
+       }
+
+       /* Check if we are finished */
+       if (task->state == IRDA_TASK_DONE) {
+               del_timer(&task->timer);
+
+               /* Do post processing */
+               if (task->finished)
+                       task->finished(task);
+
+               /* Notify parent */
+               if (task->parent) {
+                       /* Check if parent is waiting for us to complete */
+                       if (task->parent->state == IRDA_TASK_CHILD_WAIT) {
+                               task->parent->state = IRDA_TASK_CHILD_DONE;
+
+                               /* Stop timer now that we are here */
+                               del_timer(&task->parent->timer);
+
+                               /* Kick parent task */
+                               irda_task_kick(task->parent);
+                       }
+               }
+               irda_task_delete(task);
+       } else if (timeout > 0) {
+               irda_start_timer(&task->timer, timeout, (void *) task,
+                                irda_task_timer_expired);
+               finished = FALSE;
+       } else {
+               pr_debug("%s(), not finished, and no timeout!\n",
+                        __func__);
+               finished = FALSE;
+       }
+
+       return finished;
+}
+
+/*
+ * Function irda_task_timer_expired (data)
+ *
+ *    Task time has expired. We now try to execute task (again), and restart
+ *    the timer if the task has not finished yet
+ */
+static void irda_task_timer_expired(void *data)
+{
+       struct irda_task *task;
+
+       task = data;
+
+       irda_task_kick(task);
+}
+
+/*
+ * Function irda_device_setup (dev)
+ *
+ *    This function should be used by low level device drivers in a similar way
+ *    as ether_setup() is used by normal network device drivers
+ */
+static void irda_device_setup(struct net_device *dev)
+{
+       dev->hard_header_len = 0;
+       dev->addr_len        = LAP_ALEN;
+
+       dev->type            = ARPHRD_IRDA;
+       dev->tx_queue_len    = 8; /* Window size + 1 s-frame */
+
+       memset(dev->broadcast, 0xff, LAP_ALEN);
+
+       dev->mtu = 2048;
+       dev->flags = IFF_NOARP;
+}
+
+/*
+ * Funciton  alloc_irdadev
+ *     Allocates and sets up an IRDA device in a manner similar to
+ *     alloc_etherdev.
+ */
+struct net_device *alloc_irdadev(int sizeof_priv)
+{
+       return alloc_netdev(sizeof_priv, "irda%d", NET_NAME_UNKNOWN,
+                           irda_device_setup);
+}
+EXPORT_SYMBOL(alloc_irdadev);
+
+#ifdef CONFIG_ISA_DMA_API
+/*
+ * Function setup_dma (idev, buffer, count, mode)
+ *
+ *    Setup the DMA channel. Commonly used by LPC FIR drivers
+ *
+ */
+void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode)
+{
+       unsigned long flags;
+
+       flags = claim_dma_lock();
+
+       disable_dma(channel);
+       clear_dma_ff(channel);
+       set_dma_mode(channel, mode);
+       set_dma_addr(channel, buffer);
+       set_dma_count(channel, count);
+       enable_dma(channel);
+
+       release_dma_lock(flags);
+}
+EXPORT_SYMBOL(irda_setup_dma);
+#endif
diff --git a/drivers/staging/irda/net/iriap.c b/drivers/staging/irda/net/iriap.c
new file mode 100644 (file)
index 0000000..1138eaf
--- /dev/null
@@ -0,0 +1,1085 @@
+/*********************************************************************
+ *
+ * Filename:      iriap.c
+ * Version:       0.8
+ * Description:   Information Access Protocol (IAP)
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Thu Aug 21 00:02:07 1997
+ * Modified at:   Sat Dec 25 16:42:42 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irias_object.h>
+#include <net/irda/iriap_event.h>
+#include <net/irda/iriap.h>
+
+/* FIXME: This one should go in irlmp.c */
+static const char *const ias_charset_types[] __maybe_unused = {
+       "CS_ASCII",
+       "CS_ISO_8859_1",
+       "CS_ISO_8859_2",
+       "CS_ISO_8859_3",
+       "CS_ISO_8859_4",
+       "CS_ISO_8859_5",
+       "CS_ISO_8859_6",
+       "CS_ISO_8859_7",
+       "CS_ISO_8859_8",
+       "CS_ISO_8859_9",
+       "CS_UNICODE"
+};
+
+static hashbin_t *iriap = NULL;
+static void *service_handle;
+
+static void __iriap_close(struct iriap_cb *self);
+static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode);
+static void iriap_disconnect_indication(void *instance, void *sap,
+                                       LM_REASON reason, struct sk_buff *skb);
+static void iriap_connect_indication(void *instance, void *sap,
+                                    struct qos_info *qos, __u32 max_sdu_size,
+                                    __u8 max_header_size,
+                                    struct sk_buff *skb);
+static void iriap_connect_confirm(void *instance, void *sap,
+                                 struct qos_info *qos,
+                                 __u32 max_sdu_size, __u8 max_header_size,
+                                 struct sk_buff *skb);
+static int iriap_data_indication(void *instance, void *sap,
+                                struct sk_buff *skb);
+
+static void iriap_watchdog_timer_expired(void *data);
+
+static inline void iriap_start_watchdog_timer(struct iriap_cb *self,
+                                             int timeout)
+{
+       irda_start_timer(&self->watchdog_timer, timeout, self,
+                        iriap_watchdog_timer_expired);
+}
+
+static struct lock_class_key irias_objects_key;
+
+/*
+ * Function iriap_init (void)
+ *
+ *    Initializes the IrIAP layer, called by the module initialization code
+ *    in irmod.c
+ */
+int __init iriap_init(void)
+{
+       struct ias_object *obj;
+       struct iriap_cb *server;
+       __u8 oct_seq[6];
+       __u16 hints;
+
+       /* Allocate master array */
+       iriap = hashbin_new(HB_LOCK);
+       if (!iriap)
+               return -ENOMEM;
+
+       /* Object repository - defined in irias_object.c */
+       irias_objects = hashbin_new(HB_LOCK);
+       if (!irias_objects) {
+               net_warn_ratelimited("%s: Can't allocate irias_objects hashbin!\n",
+                                    __func__);
+               hashbin_delete(iriap, NULL);
+               return -ENOMEM;
+       }
+
+       lockdep_set_class_and_name(&irias_objects->hb_spinlock, &irias_objects_key,
+                                  "irias_objects");
+
+       /*
+        *  Register some default services for IrLMP
+        */
+       hints  = irlmp_service_to_hint(S_COMPUTER);
+       service_handle = irlmp_register_service(hints);
+
+       /* Register the Device object with LM-IAS */
+       obj = irias_new_object("Device", IAS_DEVICE_ID);
+       irias_add_string_attrib(obj, "DeviceName", "Linux", IAS_KERNEL_ATTR);
+
+       oct_seq[0] = 0x01;  /* Version 1 */
+       oct_seq[1] = 0x00;  /* IAS support bits */
+       oct_seq[2] = 0x00;  /* LM-MUX support bits */
+#ifdef CONFIG_IRDA_ULTRA
+       oct_seq[2] |= 0x04; /* Connectionless Data support */
+#endif
+       irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3,
+                               IAS_KERNEL_ATTR);
+       irias_insert_object(obj);
+
+       /*
+        *  Register server support with IrLMP so we can accept incoming
+        *  connections
+        */
+       server = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
+       if (!server) {
+               pr_debug("%s(), unable to open server\n", __func__);
+               return -1;
+       }
+       iriap_register_lsap(server, LSAP_IAS, IAS_SERVER);
+
+       return 0;
+}
+
+/*
+ * Function iriap_cleanup (void)
+ *
+ *    Initializes the IrIAP layer, called by the module cleanup code in
+ *    irmod.c
+ */
+void iriap_cleanup(void)
+{
+       irlmp_unregister_service(service_handle);
+
+       hashbin_delete(iriap, (FREE_FUNC) __iriap_close);
+       hashbin_delete(irias_objects, (FREE_FUNC) __irias_delete_object);
+}
+
+/*
+ * Function iriap_open (void)
+ *
+ *    Opens an instance of the IrIAP layer, and registers with IrLMP
+ */
+struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv,
+                           CONFIRM_CALLBACK callback)
+{
+       struct iriap_cb *self;
+
+       self = kzalloc(sizeof(*self), GFP_ATOMIC);
+       if (!self)
+               return NULL;
+
+       /*
+        *  Initialize instance
+        */
+
+       self->magic = IAS_MAGIC;
+       self->mode = mode;
+       if (mode == IAS_CLIENT) {
+               if (iriap_register_lsap(self, slsap_sel, mode)) {
+                       kfree(self);
+                       return NULL;
+               }
+       }
+
+       self->confirm = callback;
+       self->priv = priv;
+
+       /* iriap_getvaluebyclass_request() will construct packets before
+        * we connect, so this must have a sane value... Jean II */
+       self->max_header_size = LMP_MAX_HEADER;
+
+       init_timer(&self->watchdog_timer);
+
+       hashbin_insert(iriap, (irda_queue_t *) self, (long) self, NULL);
+
+       /* Initialize state machines */
+       iriap_next_client_state(self, S_DISCONNECT);
+       iriap_next_call_state(self, S_MAKE_CALL);
+       iriap_next_server_state(self, R_DISCONNECT);
+       iriap_next_r_connect_state(self, R_WAITING);
+
+       return self;
+}
+EXPORT_SYMBOL(iriap_open);
+
+/*
+ * Function __iriap_close (self)
+ *
+ *    Removes (deallocates) the IrIAP instance
+ *
+ */
+static void __iriap_close(struct iriap_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       del_timer(&self->watchdog_timer);
+
+       if (self->request_skb)
+               dev_kfree_skb(self->request_skb);
+
+       self->magic = 0;
+
+       kfree(self);
+}
+
+/*
+ * Function iriap_close (void)
+ *
+ *    Closes IrIAP and deregisters with IrLMP
+ */
+void iriap_close(struct iriap_cb *self)
+{
+       struct iriap_cb *entry;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       if (self->lsap) {
+               irlmp_close_lsap(self->lsap);
+               self->lsap = NULL;
+       }
+
+       entry = (struct iriap_cb *) hashbin_remove(iriap, (long) self, NULL);
+       IRDA_ASSERT(entry == self, return;);
+
+       __iriap_close(self);
+}
+EXPORT_SYMBOL(iriap_close);
+
+static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode)
+{
+       notify_t notify;
+
+       irda_notify_init(&notify);
+       notify.connect_confirm       = iriap_connect_confirm;
+       notify.connect_indication    = iriap_connect_indication;
+       notify.disconnect_indication = iriap_disconnect_indication;
+       notify.data_indication       = iriap_data_indication;
+       notify.instance = self;
+       if (mode == IAS_CLIENT)
+               strcpy(notify.name, "IrIAS cli");
+       else
+               strcpy(notify.name, "IrIAS srv");
+
+       self->lsap = irlmp_open_lsap(slsap_sel, &notify, 0);
+       if (self->lsap == NULL) {
+               net_err_ratelimited("%s: Unable to allocated LSAP!\n",
+                                   __func__);
+               return -1;
+       }
+       self->slsap_sel = self->lsap->slsap_sel;
+
+       return 0;
+}
+
+/*
+ * Function iriap_disconnect_indication (handle, reason)
+ *
+ *    Got disconnect, so clean up everything associated with this connection
+ *
+ */
+static void iriap_disconnect_indication(void *instance, void *sap,
+                                       LM_REASON reason,
+                                       struct sk_buff *skb)
+{
+       struct iriap_cb *self;
+
+       pr_debug("%s(), reason=%s [%d]\n", __func__,
+                irlmp_reason_str(reason), reason);
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       IRDA_ASSERT(iriap != NULL, return;);
+
+       del_timer(&self->watchdog_timer);
+
+       /* Not needed */
+       if (skb)
+               dev_kfree_skb(skb);
+
+       if (self->mode == IAS_CLIENT) {
+               pr_debug("%s(), disconnect as client\n", __func__);
+
+
+               iriap_do_client_event(self, IAP_LM_DISCONNECT_INDICATION,
+                                     NULL);
+               /*
+                * Inform service user that the request failed by sending
+                * it a NULL value. Warning, the client might close us, so
+                * remember no to use self anymore after calling confirm
+                */
+               if (self->confirm)
+                       self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
+       } else {
+               pr_debug("%s(), disconnect as server\n", __func__);
+               iriap_do_server_event(self, IAP_LM_DISCONNECT_INDICATION,
+                                     NULL);
+               iriap_close(self);
+       }
+}
+
+/*
+ * Function iriap_disconnect_request (handle)
+ */
+static void iriap_disconnect_request(struct iriap_cb *self)
+{
+       struct sk_buff *tx_skb;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
+       if (tx_skb == NULL) {
+               pr_debug("%s(), Could not allocate an sk_buff of length %d\n",
+                        __func__, LMP_MAX_HEADER);
+               return;
+       }
+
+       /*
+        *  Reserve space for MUX control and LAP header
+        */
+       skb_reserve(tx_skb, LMP_MAX_HEADER);
+
+       irlmp_disconnect_request(self->lsap, tx_skb);
+}
+
+/*
+ * Function iriap_getvaluebyclass (addr, name, attr)
+ *
+ *    Retrieve all values from attribute in all objects with given class
+ *    name
+ */
+int iriap_getvaluebyclass_request(struct iriap_cb *self,
+                                 __u32 saddr, __u32 daddr,
+                                 char *name, char *attr)
+{
+       struct sk_buff *tx_skb;
+       int name_len, attr_len, skb_len;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return -1;);
+
+       /* Client must supply the destination device address */
+       if (!daddr)
+               return -1;
+
+       self->daddr = daddr;
+       self->saddr = saddr;
+
+       /*
+        *  Save operation, so we know what the later indication is about
+        */
+       self->operation = GET_VALUE_BY_CLASS;
+
+       /* Give ourselves 10 secs to finish this operation */
+       iriap_start_watchdog_timer(self, 10*HZ);
+
+       name_len = strlen(name);        /* Up to IAS_MAX_CLASSNAME = 60 */
+       attr_len = strlen(attr);        /* Up to IAS_MAX_ATTRIBNAME = 60 */
+
+       skb_len = self->max_header_size+2+name_len+1+attr_len+4;
+       tx_skb = alloc_skb(skb_len, GFP_ATOMIC);
+       if (!tx_skb)
+               return -ENOMEM;
+
+       /* Reserve space for MUX and LAP header */
+       skb_reserve(tx_skb, self->max_header_size);
+       skb_put(tx_skb, 3+name_len+attr_len);
+       frame = tx_skb->data;
+
+       /* Build frame */
+       frame[0] = IAP_LST | GET_VALUE_BY_CLASS;
+       frame[1] = name_len;                       /* Insert length of name */
+       memcpy(frame+2, name, name_len);           /* Insert name */
+       frame[2+name_len] = attr_len;              /* Insert length of attr */
+       memcpy(frame+3+name_len, attr, attr_len);  /* Insert attr */
+
+       iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, tx_skb);
+
+       /* Drop reference count - see state_s_disconnect(). */
+       dev_kfree_skb(tx_skb);
+
+       return 0;
+}
+EXPORT_SYMBOL(iriap_getvaluebyclass_request);
+
+/*
+ * Function iriap_getvaluebyclass_confirm (self, skb)
+ *
+ *    Got result from GetValueByClass command. Parse it and return result
+ *    to service user.
+ *
+ */
+static void iriap_getvaluebyclass_confirm(struct iriap_cb *self,
+                                         struct sk_buff *skb)
+{
+       struct ias_value *value;
+       int charset;
+       __u32 value_len;
+       __u32 tmp_cpu32;
+       __u16 obj_id;
+       __u16 len;
+       __u8  type;
+       __u8 *fp;
+       int n;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       /* Initialize variables */
+       fp = skb->data;
+       n = 2;
+
+       /* Get length, MSB first */
+       len = get_unaligned_be16(fp + n);
+       n += 2;
+
+       pr_debug("%s(), len=%d\n", __func__, len);
+
+       /* Get object ID, MSB first */
+       obj_id = get_unaligned_be16(fp + n);
+       n += 2;
+
+       type = fp[n++];
+       pr_debug("%s(), Value type = %d\n", __func__, type);
+
+       switch (type) {
+       case IAS_INTEGER:
+               memcpy(&tmp_cpu32, fp+n, 4); n += 4;
+               be32_to_cpus(&tmp_cpu32);
+               value = irias_new_integer_value(tmp_cpu32);
+
+               /*  Legal values restricted to 0x01-0x6f, page 15 irttp */
+               pr_debug("%s(), lsap=%d\n", __func__, value->t.integer);
+               break;
+       case IAS_STRING:
+               charset = fp[n++];
+
+               switch (charset) {
+               case CS_ASCII:
+                       break;
+/*             case CS_ISO_8859_1: */
+/*             case CS_ISO_8859_2: */
+/*             case CS_ISO_8859_3: */
+/*             case CS_ISO_8859_4: */
+/*             case CS_ISO_8859_5: */
+/*             case CS_ISO_8859_6: */
+/*             case CS_ISO_8859_7: */
+/*             case CS_ISO_8859_8: */
+/*             case CS_ISO_8859_9: */
+/*             case CS_UNICODE: */
+               default:
+                       pr_debug("%s(), charset [%d] %s, not supported\n",
+                                __func__, charset,
+                                charset < ARRAY_SIZE(ias_charset_types) ?
+                                ias_charset_types[charset] :
+                                "(unknown)");
+
+                       /* Aborting, close connection! */
+                       iriap_disconnect_request(self);
+                       return;
+                       /* break; */
+               }
+               value_len = fp[n++];
+               pr_debug("%s(), strlen=%d\n", __func__, value_len);
+
+               /* Make sure the string is null-terminated */
+               if (n + value_len < skb->len)
+                       fp[n + value_len] = 0x00;
+               pr_debug("Got string %s\n", fp+n);
+
+               /* Will truncate to IAS_MAX_STRING bytes */
+               value = irias_new_string_value(fp+n);
+               break;
+       case IAS_OCT_SEQ:
+               value_len = get_unaligned_be16(fp + n);
+               n += 2;
+
+               /* Will truncate to IAS_MAX_OCTET_STRING bytes */
+               value = irias_new_octseq_value(fp+n, value_len);
+               break;
+       default:
+               value = irias_new_missing_value();
+               break;
+       }
+
+       /* Finished, close connection! */
+       iriap_disconnect_request(self);
+
+       /* Warning, the client might close us, so remember no to use self
+        * anymore after calling confirm
+        */
+       if (self->confirm)
+               self->confirm(IAS_SUCCESS, obj_id, value, self->priv);
+       else {
+               pr_debug("%s(), missing handler!\n", __func__);
+               irias_delete_value(value);
+       }
+}
+
+/*
+ * Function iriap_getvaluebyclass_response ()
+ *
+ *    Send answer back to remote LM-IAS
+ *
+ */
+static void iriap_getvaluebyclass_response(struct iriap_cb *self,
+                                          __u16 obj_id,
+                                          __u8 ret_code,
+                                          struct ias_value *value)
+{
+       struct sk_buff *tx_skb;
+       int n;
+       __be32 tmp_be32;
+       __be16 tmp_be16;
+       __u8 *fp;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+       IRDA_ASSERT(value != NULL, return;);
+       IRDA_ASSERT(value->len <= 1024, return;);
+
+       /* Initialize variables */
+       n = 0;
+
+       /*
+        *  We must adjust the size of the response after the length of the
+        *  value. We add 32 bytes because of the 6 bytes for the frame and
+        *  max 5 bytes for the value coding.
+        */
+       tx_skb = alloc_skb(value->len + self->max_header_size + 32,
+                          GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       /* Reserve space for MUX and LAP header */
+       skb_reserve(tx_skb, self->max_header_size);
+       skb_put(tx_skb, 6);
+
+       fp = tx_skb->data;
+
+       /* Build frame */
+       fp[n++] = GET_VALUE_BY_CLASS | IAP_LST;
+       fp[n++] = ret_code;
+
+       /* Insert list length (MSB first) */
+       tmp_be16 = htons(0x0001);
+       memcpy(fp+n, &tmp_be16, 2);  n += 2;
+
+       /* Insert object identifier ( MSB first) */
+       tmp_be16 = cpu_to_be16(obj_id);
+       memcpy(fp+n, &tmp_be16, 2); n += 2;
+
+       switch (value->type) {
+       case IAS_STRING:
+               skb_put(tx_skb, 3 + value->len);
+               fp[n++] = value->type;
+               fp[n++] = 0; /* ASCII */
+               fp[n++] = (__u8) value->len;
+               memcpy(fp+n, value->t.string, value->len); n+=value->len;
+               break;
+       case IAS_INTEGER:
+               skb_put(tx_skb, 5);
+               fp[n++] = value->type;
+
+               tmp_be32 = cpu_to_be32(value->t.integer);
+               memcpy(fp+n, &tmp_be32, 4); n += 4;
+               break;
+       case IAS_OCT_SEQ:
+               skb_put(tx_skb, 3 + value->len);
+               fp[n++] = value->type;
+
+               tmp_be16 = cpu_to_be16(value->len);
+               memcpy(fp+n, &tmp_be16, 2); n += 2;
+               memcpy(fp+n, value->t.oct_seq, value->len); n+=value->len;
+               break;
+       case IAS_MISSING:
+               pr_debug("%s: sending IAS_MISSING\n", __func__);
+               skb_put(tx_skb, 1);
+               fp[n++] = value->type;
+               break;
+       default:
+               pr_debug("%s(), type not implemented!\n", __func__);
+               break;
+       }
+       iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb);
+
+       /* Drop reference count - see state_r_execute(). */
+       dev_kfree_skb(tx_skb);
+}
+
+/*
+ * Function iriap_getvaluebyclass_indication (self, skb)
+ *
+ *    getvaluebyclass is requested from peer LM-IAS
+ *
+ */
+static void iriap_getvaluebyclass_indication(struct iriap_cb *self,
+                                            struct sk_buff *skb)
+{
+       struct ias_object *obj;
+       struct ias_attrib *attrib;
+       int name_len;
+       int attr_len;
+       char name[IAS_MAX_CLASSNAME + 1];       /* 60 bytes */
+       char attr[IAS_MAX_ATTRIBNAME + 1];      /* 60 bytes */
+       __u8 *fp;
+       int n;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       fp = skb->data;
+       n = 1;
+
+       name_len = fp[n++];
+
+       IRDA_ASSERT(name_len < IAS_MAX_CLASSNAME + 1, return;);
+
+       memcpy(name, fp+n, name_len); n+=name_len;
+       name[name_len] = '\0';
+
+       attr_len = fp[n++];
+
+       IRDA_ASSERT(attr_len < IAS_MAX_ATTRIBNAME + 1, return;);
+
+       memcpy(attr, fp+n, attr_len); n+=attr_len;
+       attr[attr_len] = '\0';
+
+       pr_debug("LM-IAS: Looking up %s: %s\n", name, attr);
+       obj = irias_find_object(name);
+
+       if (obj == NULL) {
+               pr_debug("LM-IAS: Object %s not found\n", name);
+               iriap_getvaluebyclass_response(self, 0x1235, IAS_CLASS_UNKNOWN,
+                                              &irias_missing);
+               return;
+       }
+       pr_debug("LM-IAS: found %s, id=%d\n", obj->name, obj->id);
+
+       attrib = irias_find_attrib(obj, attr);
+       if (attrib == NULL) {
+               pr_debug("LM-IAS: Attribute %s not found\n", attr);
+               iriap_getvaluebyclass_response(self, obj->id,
+                                              IAS_ATTRIB_UNKNOWN,
+                                              &irias_missing);
+               return;
+       }
+
+       /* We have a match; send the value.  */
+       iriap_getvaluebyclass_response(self, obj->id, IAS_SUCCESS,
+                                      attrib->value);
+}
+
+/*
+ * Function iriap_send_ack (void)
+ *
+ *    Currently not used
+ *
+ */
+void iriap_send_ack(struct iriap_cb *self)
+{
+       struct sk_buff *tx_skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       tx_skb = alloc_skb(LMP_MAX_HEADER + 1, GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       /* Reserve space for MUX and LAP header */
+       skb_reserve(tx_skb, self->max_header_size);
+       skb_put(tx_skb, 1);
+       frame = tx_skb->data;
+
+       /* Build frame */
+       frame[0] = IAP_LST | IAP_ACK | self->operation;
+
+       irlmp_data_request(self->lsap, tx_skb);
+}
+
+void iriap_connect_request(struct iriap_cb *self)
+{
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       ret = irlmp_connect_request(self->lsap, LSAP_IAS,
+                                   self->saddr, self->daddr,
+                                   NULL, NULL);
+       if (ret < 0) {
+               pr_debug("%s(), connect failed!\n", __func__);
+               self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
+       }
+}
+
+/*
+ * Function iriap_connect_confirm (handle, skb)
+ *
+ *    LSAP connection confirmed!
+ *
+ */
+static void iriap_connect_confirm(void *instance, void *sap,
+                                 struct qos_info *qos, __u32 max_seg_size,
+                                 __u8 max_header_size,
+                                 struct sk_buff *skb)
+{
+       struct iriap_cb *self;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       self->max_data_size = max_seg_size;
+       self->max_header_size = max_header_size;
+
+       del_timer(&self->watchdog_timer);
+
+       iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, skb);
+
+       /* Drop reference count - see state_s_make_call(). */
+       dev_kfree_skb(skb);
+}
+
+/*
+ * Function iriap_connect_indication ( handle, skb)
+ *
+ *    Remote LM-IAS is requesting connection
+ *
+ */
+static void iriap_connect_indication(void *instance, void *sap,
+                                    struct qos_info *qos, __u32 max_seg_size,
+                                    __u8 max_header_size,
+                                    struct sk_buff *skb)
+{
+       struct iriap_cb *self, *new;
+
+       self = instance;
+
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(self != NULL, goto out;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;);
+
+       /* Start new server */
+       new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
+       if (!new) {
+               pr_debug("%s(), open failed\n", __func__);
+               goto out;
+       }
+
+       /* Now attach up the new "socket" */
+       new->lsap = irlmp_dup(self->lsap, new);
+       if (!new->lsap) {
+               pr_debug("%s(), dup failed!\n", __func__);
+               goto out;
+       }
+
+       new->max_data_size = max_seg_size;
+       new->max_header_size = max_header_size;
+
+       /* Clean up the original one to keep it in listen state */
+       irlmp_listen(self->lsap);
+
+       iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, skb);
+
+out:
+       /* Drop reference count - see state_r_disconnect(). */
+       dev_kfree_skb(skb);
+}
+
+/*
+ * Function iriap_data_indication (handle, skb)
+ *
+ *    Receives data from connection identified by handle from IrLMP
+ *
+ */
+static int iriap_data_indication(void *instance, void *sap,
+                                struct sk_buff *skb)
+{
+       struct iriap_cb *self;
+       __u8  *frame;
+       __u8  opcode;
+
+       self = instance;
+
+       IRDA_ASSERT(skb != NULL, return 0;);
+       IRDA_ASSERT(self != NULL, goto out;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;);
+
+       frame = skb->data;
+
+       if (self->mode == IAS_SERVER) {
+               /* Call server */
+               pr_debug("%s(), Calling server!\n", __func__);
+               iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb);
+               goto out;
+       }
+       opcode = frame[0];
+       if (~opcode & IAP_LST) {
+               net_warn_ratelimited("%s:, IrIAS multiframe commands or results is not implemented yet!\n",
+                                    __func__);
+               goto out;
+       }
+
+       /* Check for ack frames since they don't contain any data */
+       if (opcode & IAP_ACK) {
+               pr_debug("%s() Got ack frame!\n", __func__);
+               goto out;
+       }
+
+       opcode &= ~IAP_LST; /* Mask away LST bit */
+
+       switch (opcode) {
+       case GET_INFO_BASE:
+               pr_debug("IrLMP GetInfoBaseDetails not implemented!\n");
+               break;
+       case GET_VALUE_BY_CLASS:
+               iriap_do_call_event(self, IAP_RECV_F_LST, NULL);
+
+               switch (frame[1]) {
+               case IAS_SUCCESS:
+                       iriap_getvaluebyclass_confirm(self, skb);
+                       break;
+               case IAS_CLASS_UNKNOWN:
+                       pr_debug("%s(), No such class!\n", __func__);
+                       /* Finished, close connection! */
+                       iriap_disconnect_request(self);
+
+                       /*
+                        * Warning, the client might close us, so remember
+                        * no to use self anymore after calling confirm
+                        */
+                       if (self->confirm)
+                               self->confirm(IAS_CLASS_UNKNOWN, 0, NULL,
+                                             self->priv);
+                       break;
+               case IAS_ATTRIB_UNKNOWN:
+                       pr_debug("%s(), No such attribute!\n", __func__);
+                       /* Finished, close connection! */
+                       iriap_disconnect_request(self);
+
+                       /*
+                        * Warning, the client might close us, so remember
+                        * no to use self anymore after calling confirm
+                        */
+                       if (self->confirm)
+                               self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL,
+                                             self->priv);
+                       break;
+               }
+               break;
+       default:
+               pr_debug("%s(), Unknown op-code: %02x\n", __func__,
+                        opcode);
+               break;
+       }
+
+out:
+       /* Cleanup - sub-calls will have done skb_get() as needed. */
+       dev_kfree_skb(skb);
+       return 0;
+}
+
+/*
+ * Function iriap_call_indication (self, skb)
+ *
+ *    Received call to server from peer LM-IAS
+ *
+ */
+void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb)
+{
+       __u8 *fp;
+       __u8 opcode;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       fp = skb->data;
+
+       opcode = fp[0];
+       if (~opcode & 0x80) {
+               net_warn_ratelimited("%s: IrIAS multiframe commands or results is not implemented yet!\n",
+                                    __func__);
+               return;
+       }
+       opcode &= 0x7f; /* Mask away LST bit */
+
+       switch (opcode) {
+       case GET_INFO_BASE:
+               net_warn_ratelimited("%s: GetInfoBaseDetails not implemented yet!\n",
+                                    __func__);
+               break;
+       case GET_VALUE_BY_CLASS:
+               iriap_getvaluebyclass_indication(self, skb);
+               break;
+       }
+       /* skb will be cleaned up in iriap_data_indication */
+}
+
+/*
+ * Function iriap_watchdog_timer_expired (data)
+ *
+ *    Query has taken too long time, so abort
+ *
+ */
+static void iriap_watchdog_timer_expired(void *data)
+{
+       struct iriap_cb *self = (struct iriap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       /* iriap_close(self); */
+}
+
+#ifdef CONFIG_PROC_FS
+
+static const char *const ias_value_types[] = {
+       "IAS_MISSING",
+       "IAS_INTEGER",
+       "IAS_OCT_SEQ",
+       "IAS_STRING"
+};
+
+static inline struct ias_object *irias_seq_idx(loff_t pos)
+{
+       struct ias_object *obj;
+
+       for (obj = (struct ias_object *) hashbin_get_first(irias_objects);
+            obj; obj = (struct ias_object *) hashbin_get_next(irias_objects)) {
+               if (pos-- == 0)
+                       break;
+       }
+
+       return obj;
+}
+
+static void *irias_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       spin_lock_irq(&irias_objects->hb_spinlock);
+
+       return *pos ? irias_seq_idx(*pos - 1) : SEQ_START_TOKEN;
+}
+
+static void *irias_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       ++*pos;
+
+       return (v == SEQ_START_TOKEN)
+               ? (void *) hashbin_get_first(irias_objects)
+               : (void *) hashbin_get_next(irias_objects);
+}
+
+static void irias_seq_stop(struct seq_file *seq, void *v)
+{
+       spin_unlock_irq(&irias_objects->hb_spinlock);
+}
+
+static int irias_seq_show(struct seq_file *seq, void *v)
+{
+       if (v == SEQ_START_TOKEN)
+               seq_puts(seq, "LM-IAS Objects:\n");
+       else {
+               struct ias_object *obj = v;
+               struct ias_attrib *attrib;
+
+               IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -EINVAL;);
+
+               seq_printf(seq, "name: %s, id=%d\n",
+                          obj->name, obj->id);
+
+               /* Careful for priority inversions here !
+                * All other uses of attrib spinlock are independent of
+                * the object spinlock, so we are safe. Jean II */
+               spin_lock(&obj->attribs->hb_spinlock);
+
+               /* List all attributes for this object */
+               for (attrib = (struct ias_attrib *) hashbin_get_first(obj->attribs);
+                    attrib != NULL;
+                    attrib = (struct ias_attrib *) hashbin_get_next(obj->attribs)) {
+
+                       IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC,
+                                   goto outloop; );
+
+                       seq_printf(seq, " - Attribute name: \"%s\", ",
+                                  attrib->name);
+                       seq_printf(seq, "value[%s]: ",
+                                  ias_value_types[attrib->value->type]);
+
+                       switch (attrib->value->type) {
+                       case IAS_INTEGER:
+                               seq_printf(seq, "%d\n",
+                                          attrib->value->t.integer);
+                               break;
+                       case IAS_STRING:
+                               seq_printf(seq, "\"%s\"\n",
+                                          attrib->value->t.string);
+                               break;
+                       case IAS_OCT_SEQ:
+                               seq_printf(seq, "octet sequence (%d bytes)\n",
+                                          attrib->value->len);
+                               break;
+                       case IAS_MISSING:
+                               seq_puts(seq, "missing\n");
+                               break;
+                       default:
+                               seq_printf(seq, "type %d?\n",
+                                          attrib->value->type);
+                       }
+                       seq_putc(seq, '\n');
+
+               }
+       IRDA_ASSERT_LABEL(outloop:)
+               spin_unlock(&obj->attribs->hb_spinlock);
+       }
+
+       return 0;
+}
+
+static const struct seq_operations irias_seq_ops = {
+       .start  = irias_seq_start,
+       .next   = irias_seq_next,
+       .stop   = irias_seq_stop,
+       .show   = irias_seq_show,
+};
+
+static int irias_seq_open(struct inode *inode, struct file *file)
+{
+       IRDA_ASSERT( irias_objects != NULL, return -EINVAL;);
+
+       return seq_open(file, &irias_seq_ops);
+}
+
+const struct file_operations irias_seq_fops = {
+       .owner          = THIS_MODULE,
+       .open           = irias_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+#endif /* PROC_FS */
diff --git a/drivers/staging/irda/net/iriap_event.c b/drivers/staging/irda/net/iriap_event.c
new file mode 100644 (file)
index 0000000..e6098b2
--- /dev/null
@@ -0,0 +1,496 @@
+/*********************************************************************
+ *
+ * Filename:      iriap_event.c
+ * Version:       0.1
+ * Description:   IAP Finite State Machine
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Thu Aug 21 00:02:07 1997
+ * Modified at:   Wed Mar  1 11:28:34 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1997, 1999-2000 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/slab.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+#include <net/irda/iriap_event.h>
+
+static void state_s_disconnect   (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_s_connecting   (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_s_call         (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+
+static void state_s_make_call    (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_s_calling      (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_s_outstanding  (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_s_replying     (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_s_wait_active  (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+
+static void state_r_disconnect   (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_r_call         (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_r_waiting      (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_r_wait_active  (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_r_receiving    (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_r_execute      (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+static void state_r_returning    (struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb);
+
+static void (*iriap_state[])(struct iriap_cb *self, IRIAP_EVENT event,
+                            struct sk_buff *skb) = {
+       /* Client FSM */
+       state_s_disconnect,
+       state_s_connecting,
+       state_s_call,
+
+       /* S-Call FSM */
+       state_s_make_call,
+       state_s_calling,
+       state_s_outstanding,
+       state_s_replying,
+       state_s_wait_for_call,
+       state_s_wait_active,
+
+       /* Server FSM */
+       state_r_disconnect,
+       state_r_call,
+
+       /* R-Connect FSM */
+       state_r_waiting,
+       state_r_wait_active,
+       state_r_receiving,
+       state_r_execute,
+       state_r_returning,
+};
+
+void iriap_next_client_state(struct iriap_cb *self, IRIAP_STATE state)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       self->client_state = state;
+}
+
+void iriap_next_call_state(struct iriap_cb *self, IRIAP_STATE state)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       self->call_state = state;
+}
+
+void iriap_next_server_state(struct iriap_cb *self, IRIAP_STATE state)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       self->server_state = state;
+}
+
+void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       self->r_connect_state = state;
+}
+
+void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event,
+                          struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       (*iriap_state[ self->client_state]) (self, event, skb);
+}
+
+void iriap_do_call_event(struct iriap_cb *self, IRIAP_EVENT event,
+                        struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       (*iriap_state[ self->call_state]) (self, event, skb);
+}
+
+void iriap_do_server_event(struct iriap_cb *self, IRIAP_EVENT event,
+                          struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       (*iriap_state[ self->server_state]) (self, event, skb);
+}
+
+void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event,
+                             struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       (*iriap_state[ self->r_connect_state]) (self, event, skb);
+}
+
+
+/*
+ * Function state_s_disconnect (event, skb)
+ *
+ *    S-Disconnect, The device has no LSAP connection to a particular
+ *    remote device.
+ */
+static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
+                              struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       switch (event) {
+       case IAP_CALL_REQUEST_GVBC:
+               iriap_next_client_state(self, S_CONNECTING);
+               IRDA_ASSERT(self->request_skb == NULL, return;);
+               /* Don't forget to refcount it -
+                * see iriap_getvaluebyclass_request(). */
+               skb_get(skb);
+               self->request_skb = skb;
+               iriap_connect_request(self);
+               break;
+       case IAP_LM_DISCONNECT_INDICATION:
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__, event);
+               break;
+       }
+}
+
+/*
+ * Function state_s_connecting (self, event, skb)
+ *
+ *    S-Connecting
+ *
+ */
+static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event,
+                              struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       switch (event) {
+       case IAP_LM_CONNECT_CONFIRM:
+               /*
+                *  Jump to S-Call FSM
+                */
+               iriap_do_call_event(self, IAP_CALL_REQUEST, skb);
+               /* iriap_call_request(self, 0,0,0); */
+               iriap_next_client_state(self, S_CALL);
+               break;
+       case IAP_LM_DISCONNECT_INDICATION:
+               /* Abort calls */
+               iriap_next_call_state(self, S_MAKE_CALL);
+               iriap_next_client_state(self, S_DISCONNECT);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__, event);
+               break;
+       }
+}
+
+/*
+ * Function state_s_call (self, event, skb)
+ *
+ *    S-Call, The device can process calls to a specific remote
+ *    device. Whenever the LSAP connection is disconnected, this state
+ *    catches that event and clears up
+ */
+static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event,
+                        struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+
+       switch (event) {
+       case IAP_LM_DISCONNECT_INDICATION:
+               /* Abort calls */
+               iriap_next_call_state(self, S_MAKE_CALL);
+               iriap_next_client_state(self, S_DISCONNECT);
+               break;
+       default:
+               pr_debug("state_s_call: Unknown event %d\n", event);
+               break;
+       }
+}
+
+/*
+ * Function state_s_make_call (event, skb)
+ *
+ *    S-Make-Call
+ *
+ */
+static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event,
+                             struct sk_buff *skb)
+{
+       struct sk_buff *tx_skb;
+
+       IRDA_ASSERT(self != NULL, return;);
+
+       switch (event) {
+       case IAP_CALL_REQUEST:
+               /* Already refcounted - see state_s_disconnect() */
+               tx_skb = self->request_skb;
+               self->request_skb = NULL;
+
+               irlmp_data_request(self->lsap, tx_skb);
+               iriap_next_call_state(self, S_OUTSTANDING);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__, event);
+               break;
+       }
+}
+
+/*
+ * Function state_s_calling (event, skb)
+ *
+ *    S-Calling
+ *
+ */
+static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event,
+                           struct sk_buff *skb)
+{
+       pr_debug("%s(), Not implemented\n", __func__);
+}
+
+/*
+ * Function state_s_outstanding (event, skb)
+ *
+ *    S-Outstanding, The device is waiting for a response to a command
+ *
+ */
+static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event,
+                               struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+
+       switch (event) {
+       case IAP_RECV_F_LST:
+               /*iriap_send_ack(self);*/
+               /*LM_Idle_request(idle); */
+
+               iriap_next_call_state(self, S_WAIT_FOR_CALL);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__, event);
+               break;
+       }
+}
+
+/*
+ * Function state_s_replying (event, skb)
+ *
+ *    S-Replying, The device is collecting a multiple part response
+ */
+static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event,
+                            struct sk_buff *skb)
+{
+       pr_debug("%s(), Not implemented\n", __func__);
+}
+
+/*
+ * Function state_s_wait_for_call (event, skb)
+ *
+ *    S-Wait-for-Call
+ *
+ */
+static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
+                                 struct sk_buff *skb)
+{
+       pr_debug("%s(), Not implemented\n", __func__);
+}
+
+
+/*
+ * Function state_s_wait_active (event, skb)
+ *
+ *    S-Wait-Active
+ *
+ */
+static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
+                               struct sk_buff *skb)
+{
+       pr_debug("%s(), Not implemented\n", __func__);
+}
+
+/**************************************************************************
+ *
+ *  Server FSM
+ *
+ **************************************************************************/
+
+/*
+ * Function state_r_disconnect (self, event, skb)
+ *
+ *    LM-IAS server is disconnected (not processing any requests!)
+ *
+ */
+static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
+                              struct sk_buff *skb)
+{
+       struct sk_buff *tx_skb;
+
+       switch (event) {
+       case IAP_LM_CONNECT_INDICATION:
+               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
+               if (tx_skb == NULL)
+                       return;
+
+               /* Reserve space for MUX_CONTROL and LAP header */
+               skb_reserve(tx_skb, LMP_MAX_HEADER);
+
+               irlmp_connect_response(self->lsap, tx_skb);
+               /*LM_Idle_request(idle); */
+
+               iriap_next_server_state(self, R_CALL);
+
+               /*
+                *  Jump to R-Connect FSM, we skip R-Waiting since we do not
+                *  care about LM_Idle_request()!
+                */
+               iriap_next_r_connect_state(self, R_RECEIVING);
+               break;
+       default:
+               pr_debug("%s(), unknown event %d\n", __func__, event);
+               break;
+       }
+}
+
+/*
+ * Function state_r_call (self, event, skb)
+ */
+static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event,
+                        struct sk_buff *skb)
+{
+       switch (event) {
+       case IAP_LM_DISCONNECT_INDICATION:
+               /* Abort call */
+               iriap_next_server_state(self, R_DISCONNECT);
+               iriap_next_r_connect_state(self, R_WAITING);
+               break;
+       default:
+               pr_debug("%s(), unknown event!\n", __func__);
+               break;
+       }
+}
+
+/*
+ *  R-Connect FSM
+ */
+
+/*
+ * Function state_r_waiting (self, event, skb)
+ */
+static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event,
+                           struct sk_buff *skb)
+{
+       pr_debug("%s(), Not implemented\n", __func__);
+}
+
+static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
+                               struct sk_buff *skb)
+{
+       pr_debug("%s(), Not implemented\n", __func__);
+}
+
+/*
+ * Function state_r_receiving (self, event, skb)
+ *
+ *    We are receiving a command
+ *
+ */
+static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event,
+                             struct sk_buff *skb)
+{
+       switch (event) {
+       case IAP_RECV_F_LST:
+               iriap_next_r_connect_state(self, R_EXECUTE);
+
+               iriap_call_indication(self, skb);
+               break;
+       default:
+               pr_debug("%s(), unknown event!\n", __func__);
+               break;
+       }
+}
+
+/*
+ * Function state_r_execute (self, event, skb)
+ *
+ *    The server is processing the request
+ *
+ */
+static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
+                           struct sk_buff *skb)
+{
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
+
+       switch (event) {
+       case IAP_CALL_RESPONSE:
+               /*
+                *  Since we don't implement the Waiting state, we return
+                *  to state Receiving instead, DB.
+                */
+               iriap_next_r_connect_state(self, R_RECEIVING);
+
+               /* Don't forget to refcount it - see
+                * iriap_getvaluebyclass_response(). */
+               skb_get(skb);
+
+               irlmp_data_request(self->lsap, skb);
+               break;
+       default:
+               pr_debug("%s(), unknown event!\n", __func__);
+               break;
+       }
+}
+
+static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event,
+                             struct sk_buff *skb)
+{
+       pr_debug("%s(), event=%d\n", __func__, event);
+
+       switch (event) {
+       case IAP_RECV_F_LST:
+               break;
+       default:
+               break;
+       }
+}
diff --git a/drivers/staging/irda/net/irias_object.c b/drivers/staging/irda/net/irias_object.c
new file mode 100644 (file)
index 0000000..53b86d0
--- /dev/null
@@ -0,0 +1,555 @@
+/*********************************************************************
+ *
+ * Filename:      irias_object.c
+ * Version:       0.3
+ * Description:   IAS object database and functions
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Thu Oct  1 22:50:04 1998
+ * Modified at:   Wed Dec 15 11:23:16 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/module.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irias_object.h>
+
+hashbin_t *irias_objects;
+
+/*
+ *  Used when a missing value needs to be returned
+ */
+struct ias_value irias_missing = { IAS_MISSING, 0, 0, 0, {0}};
+
+
+/*
+ * Function ias_new_object (name, id)
+ *
+ *    Create a new IAS object
+ *
+ */
+struct ias_object *irias_new_object( char *name, int id)
+{
+       struct ias_object *obj;
+
+       obj = kzalloc(sizeof(struct ias_object), GFP_ATOMIC);
+       if (obj == NULL) {
+               net_warn_ratelimited("%s(), Unable to allocate object!\n",
+                                    __func__);
+               return NULL;
+       }
+
+       obj->magic = IAS_OBJECT_MAGIC;
+       obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC);
+       if (!obj->name) {
+               net_warn_ratelimited("%s(), Unable to allocate name!\n",
+                                    __func__);
+               kfree(obj);
+               return NULL;
+       }
+       obj->id = id;
+
+       /* Locking notes : the attrib spinlock has lower precendence
+        * than the objects spinlock. Never grap the objects spinlock
+        * while holding any attrib spinlock (risk of deadlock). Jean II */
+       obj->attribs = hashbin_new(HB_LOCK);
+
+       if (obj->attribs == NULL) {
+               net_warn_ratelimited("%s(), Unable to allocate attribs!\n",
+                                    __func__);
+               kfree(obj->name);
+               kfree(obj);
+               return NULL;
+       }
+
+       return obj;
+}
+EXPORT_SYMBOL(irias_new_object);
+
+/*
+ * Function irias_delete_attrib (attrib)
+ *
+ *    Delete given attribute and deallocate all its memory
+ *
+ */
+static void __irias_delete_attrib(struct ias_attrib *attrib)
+{
+       IRDA_ASSERT(attrib != NULL, return;);
+       IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
+
+       kfree(attrib->name);
+
+       irias_delete_value(attrib->value);
+       attrib->magic = ~IAS_ATTRIB_MAGIC;
+
+       kfree(attrib);
+}
+
+void __irias_delete_object(struct ias_object *obj)
+{
+       IRDA_ASSERT(obj != NULL, return;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
+
+       kfree(obj->name);
+
+       hashbin_delete(obj->attribs, (FREE_FUNC) __irias_delete_attrib);
+
+       obj->magic = ~IAS_OBJECT_MAGIC;
+
+       kfree(obj);
+}
+
+/*
+ * Function irias_delete_object (obj)
+ *
+ *    Remove object from hashbin and deallocate all attributes associated with
+ *    with this object and the object itself
+ *
+ */
+int irias_delete_object(struct ias_object *obj)
+{
+       struct ias_object *node;
+
+       IRDA_ASSERT(obj != NULL, return -1;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
+
+       /* Remove from list */
+       node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj);
+       if (!node)
+               pr_debug("%s(), object already removed!\n",
+                        __func__);
+
+       /* Destroy */
+       __irias_delete_object(obj);
+
+       return 0;
+}
+EXPORT_SYMBOL(irias_delete_object);
+
+/*
+ * Function irias_delete_attrib (obj)
+ *
+ *    Remove attribute from hashbin and, if it was the last attribute of
+ *    the object, remove the object as well.
+ *
+ */
+int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib,
+                       int cleanobject)
+{
+       struct ias_attrib *node;
+
+       IRDA_ASSERT(obj != NULL, return -1;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
+       IRDA_ASSERT(attrib != NULL, return -1;);
+
+       /* Remove attribute from object */
+       node = hashbin_remove_this(obj->attribs, (irda_queue_t *) attrib);
+       if (!node)
+               return 0; /* Already removed or non-existent */
+
+       /* Deallocate attribute */
+       __irias_delete_attrib(node);
+
+       /* Check if object has still some attributes, destroy it if none.
+        * At first glance, this look dangerous, as the kernel reference
+        * various IAS objects. However, we only use this function on
+        * user attributes, not kernel attributes, so there is no risk
+        * of deleting a kernel object this way. Jean II */
+       node = (struct ias_attrib *) hashbin_get_first(obj->attribs);
+       if (cleanobject && !node)
+               irias_delete_object(obj);
+
+       return 0;
+}
+
+/*
+ * Function irias_insert_object (obj)
+ *
+ *    Insert an object into the LM-IAS database
+ *
+ */
+void irias_insert_object(struct ias_object *obj)
+{
+       IRDA_ASSERT(obj != NULL, return;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
+
+       hashbin_insert(irias_objects, (irda_queue_t *) obj, 0, obj->name);
+}
+EXPORT_SYMBOL(irias_insert_object);
+
+/*
+ * Function irias_find_object (name)
+ *
+ *    Find object with given name
+ *
+ */
+struct ias_object *irias_find_object(char *name)
+{
+       IRDA_ASSERT(name != NULL, return NULL;);
+
+       /* Unsafe (locking), object might change */
+       return hashbin_lock_find(irias_objects, 0, name);
+}
+EXPORT_SYMBOL(irias_find_object);
+
+/*
+ * Function irias_find_attrib (obj, name)
+ *
+ *    Find named attribute in object
+ *
+ */
+struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name)
+{
+       struct ias_attrib *attrib;
+
+       IRDA_ASSERT(obj != NULL, return NULL;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;);
+       IRDA_ASSERT(name != NULL, return NULL;);
+
+       attrib = hashbin_lock_find(obj->attribs, 0, name);
+       if (attrib == NULL)
+               return NULL;
+
+       /* Unsafe (locking), attrib might change */
+       return attrib;
+}
+
+/*
+ * Function irias_add_attribute (obj, attrib)
+ *
+ *    Add attribute to object
+ *
+ */
+static void irias_add_attrib(struct ias_object *obj, struct ias_attrib *attrib,
+                            int owner)
+{
+       IRDA_ASSERT(obj != NULL, return;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
+
+       IRDA_ASSERT(attrib != NULL, return;);
+       IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
+
+       /* Set if attrib is owned by kernel or user space */
+       attrib->value->owner = owner;
+
+       hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name);
+}
+
+/*
+ * Function irias_object_change_attribute (obj_name, attrib_name, new_value)
+ *
+ *    Change the value of an objects attribute.
+ *
+ */
+int irias_object_change_attribute(char *obj_name, char *attrib_name,
+                                 struct ias_value *new_value)
+{
+       struct ias_object *obj;
+       struct ias_attrib *attrib;
+       unsigned long flags;
+
+       /* Find object */
+       obj = hashbin_lock_find(irias_objects, 0, obj_name);
+       if (obj == NULL) {
+               net_warn_ratelimited("%s: Unable to find object: %s\n",
+                                    __func__, obj_name);
+               return -1;
+       }
+
+       /* Slightly unsafe (obj might get removed under us) */
+       spin_lock_irqsave(&obj->attribs->hb_spinlock, flags);
+
+       /* Find attribute */
+       attrib = hashbin_find(obj->attribs, 0, attrib_name);
+       if (attrib == NULL) {
+               net_warn_ratelimited("%s: Unable to find attribute: %s\n",
+                                    __func__, attrib_name);
+               spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
+               return -1;
+       }
+
+       if ( attrib->value->type != new_value->type) {
+               pr_debug("%s(), changing value type not allowed!\n",
+                        __func__);
+               spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
+               return -1;
+       }
+
+       /* Delete old value */
+       irias_delete_value(attrib->value);
+
+       /* Insert new value */
+       attrib->value = new_value;
+
+       /* Success */
+       spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(irias_object_change_attribute);
+
+/*
+ * Function irias_object_add_integer_attrib (obj, name, value)
+ *
+ *    Add an integer attribute to an LM-IAS object
+ *
+ */
+void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
+                             int owner)
+{
+       struct ias_attrib *attrib;
+
+       IRDA_ASSERT(obj != NULL, return;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
+       IRDA_ASSERT(name != NULL, return;);
+
+       attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
+       if (attrib == NULL) {
+               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+                                    __func__);
+               return;
+       }
+
+       attrib->magic = IAS_ATTRIB_MAGIC;
+       attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
+
+       /* Insert value */
+       attrib->value = irias_new_integer_value(value);
+       if (!attrib->name || !attrib->value) {
+               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+                                    __func__);
+               if (attrib->value)
+                       irias_delete_value(attrib->value);
+               kfree(attrib->name);
+               kfree(attrib);
+               return;
+       }
+
+       irias_add_attrib(obj, attrib, owner);
+}
+EXPORT_SYMBOL(irias_add_integer_attrib);
+
+ /*
+ * Function irias_add_octseq_attrib (obj, name, octet_seq, len)
+ *
+ *    Add a octet sequence attribute to an LM-IAS object
+ *
+ */
+
+void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
+                            int len, int owner)
+{
+       struct ias_attrib *attrib;
+
+       IRDA_ASSERT(obj != NULL, return;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
+
+       IRDA_ASSERT(name != NULL, return;);
+       IRDA_ASSERT(octets != NULL, return;);
+
+       attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
+       if (attrib == NULL) {
+               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+                                    __func__);
+               return;
+       }
+
+       attrib->magic = IAS_ATTRIB_MAGIC;
+       attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
+
+       attrib->value = irias_new_octseq_value( octets, len);
+       if (!attrib->name || !attrib->value) {
+               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+                                    __func__);
+               if (attrib->value)
+                       irias_delete_value(attrib->value);
+               kfree(attrib->name);
+               kfree(attrib);
+               return;
+       }
+
+       irias_add_attrib(obj, attrib, owner);
+}
+EXPORT_SYMBOL(irias_add_octseq_attrib);
+
+/*
+ * Function irias_object_add_string_attrib (obj, string)
+ *
+ *    Add a string attribute to an LM-IAS object
+ *
+ */
+void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
+                            int owner)
+{
+       struct ias_attrib *attrib;
+
+       IRDA_ASSERT(obj != NULL, return;);
+       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
+
+       IRDA_ASSERT(name != NULL, return;);
+       IRDA_ASSERT(value != NULL, return;);
+
+       attrib = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC);
+       if (attrib == NULL) {
+               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+                                    __func__);
+               return;
+       }
+
+       attrib->magic = IAS_ATTRIB_MAGIC;
+       attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
+
+       attrib->value = irias_new_string_value(value);
+       if (!attrib->name || !attrib->value) {
+               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+                                    __func__);
+               if (attrib->value)
+                       irias_delete_value(attrib->value);
+               kfree(attrib->name);
+               kfree(attrib);
+               return;
+       }
+
+       irias_add_attrib(obj, attrib, owner);
+}
+EXPORT_SYMBOL(irias_add_string_attrib);
+
+/*
+ * Function irias_new_integer_value (integer)
+ *
+ *    Create new IAS integer value
+ *
+ */
+struct ias_value *irias_new_integer_value(int integer)
+{
+       struct ias_value *value;
+
+       value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
+       if (value == NULL)
+               return NULL;
+
+       value->type = IAS_INTEGER;
+       value->len = 4;
+       value->t.integer = integer;
+
+       return value;
+}
+EXPORT_SYMBOL(irias_new_integer_value);
+
+/*
+ * Function irias_new_string_value (string)
+ *
+ *    Create new IAS string value
+ *
+ * Per IrLMP 1.1, 4.3.3.2, strings are up to 256 chars - Jean II
+ */
+struct ias_value *irias_new_string_value(char *string)
+{
+       struct ias_value *value;
+
+       value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
+       if (value == NULL)
+               return NULL;
+
+       value->type = IAS_STRING;
+       value->charset = CS_ASCII;
+       value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC);
+       if (!value->t.string) {
+               net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__);
+               kfree(value);
+               return NULL;
+       }
+
+       value->len = strlen(value->t.string);
+
+       return value;
+}
+
+/*
+ * Function irias_new_octseq_value (octets, len)
+ *
+ *    Create new IAS octet-sequence value
+ *
+ * Per IrLMP 1.1, 4.3.3.2, octet-sequence are up to 1024 bytes - Jean II
+ */
+struct ias_value *irias_new_octseq_value(__u8 *octseq , int len)
+{
+       struct ias_value *value;
+
+       value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
+       if (value == NULL)
+               return NULL;
+
+       value->type = IAS_OCT_SEQ;
+       /* Check length */
+       if(len > IAS_MAX_OCTET_STRING)
+               len = IAS_MAX_OCTET_STRING;
+       value->len = len;
+
+       value->t.oct_seq = kmemdup(octseq, len, GFP_ATOMIC);
+       if (value->t.oct_seq == NULL){
+               net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__);
+               kfree(value);
+               return NULL;
+       }
+       return value;
+}
+
+struct ias_value *irias_new_missing_value(void)
+{
+       struct ias_value *value;
+
+       value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
+       if (value == NULL)
+               return NULL;
+
+       value->type = IAS_MISSING;
+
+       return value;
+}
+
+/*
+ * Function irias_delete_value (value)
+ *
+ *    Delete IAS value
+ *
+ */
+void irias_delete_value(struct ias_value *value)
+{
+       IRDA_ASSERT(value != NULL, return;);
+
+       switch (value->type) {
+       case IAS_INTEGER: /* Fallthrough */
+       case IAS_MISSING:
+               /* No need to deallocate */
+               break;
+       case IAS_STRING:
+               /* Deallocate string */
+               kfree(value->t.string);
+               break;
+       case IAS_OCT_SEQ:
+               /* Deallocate byte stream */
+                kfree(value->t.oct_seq);
+                break;
+       default:
+               pr_debug("%s(), Unknown value type!\n", __func__);
+               break;
+       }
+       kfree(value);
+}
+EXPORT_SYMBOL(irias_delete_value);
diff --git a/drivers/staging/irda/net/irlan/Kconfig b/drivers/staging/irda/net/irlan/Kconfig
new file mode 100644 (file)
index 0000000..951abc2
--- /dev/null
@@ -0,0 +1,14 @@
+config IRLAN
+       tristate "IrLAN protocol"
+       depends on IRDA
+       help
+         Say Y here if you want to build support for the IrLAN protocol.
+         To compile it as a module, choose M here: the module will be called
+         irlan.  IrLAN emulates an Ethernet and makes it possible to put up
+         a wireless LAN using infrared beams.
+
+         The IrLAN protocol can be used to talk with infrared access points
+         like the HP NetbeamIR, or the ESI JetEye NET.  You can also connect
+         to another Linux machine running the IrLAN protocol for ad-hoc
+         networking!
+
diff --git a/drivers/staging/irda/net/irlan/Makefile b/drivers/staging/irda/net/irlan/Makefile
new file mode 100644 (file)
index 0000000..94eefbc
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux IrDA IrLAN protocol layer.
+#
+
+obj-$(CONFIG_IRLAN) += irlan.o
+
+irlan-y := irlan_common.o irlan_eth.o irlan_event.o irlan_client.o irlan_provider.o irlan_filter.o irlan_provider_event.o irlan_client_event.o
diff --git a/drivers/staging/irda/net/irlan/irlan_client.c b/drivers/staging/irda/net/irlan/irlan_client.c
new file mode 100644 (file)
index 0000000..c5837a4
--- /dev/null
@@ -0,0 +1,559 @@
+/*********************************************************************
+ *
+ * Filename:      irlan_client.c
+ * Version:       0.9
+ * Description:   IrDA LAN Access Protocol (IrLAN) Client
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Aug 31 20:14:37 1997
+ * Modified at:   Tue Dec 14 15:47:02 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
+ *                slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
+ *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/bitops.h>
+#include <net/arp.h>
+
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irias_object.h>
+#include <net/irda/iriap.h>
+#include <net/irda/timer.h>
+
+#include <net/irda/irlan_common.h>
+#include <net/irda/irlan_event.h>
+#include <net/irda/irlan_eth.h>
+#include <net/irda/irlan_provider.h>
+#include <net/irda/irlan_client.h>
+
+#undef CONFIG_IRLAN_GRATUITOUS_ARP
+
+static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
+                                                   LM_REASON reason,
+                                                   struct sk_buff *);
+static int irlan_client_ctrl_data_indication(void *instance, void *sap,
+                                            struct sk_buff *skb);
+static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
+                                             struct qos_info *qos,
+                                             __u32 max_sdu_size,
+                                             __u8 max_header_size,
+                                             struct sk_buff *);
+static void irlan_check_response_param(struct irlan_cb *self, char *param,
+                                      char *value, int val_len);
+static void irlan_client_open_ctrl_tsap(struct irlan_cb *self);
+
+static void irlan_client_kick_timer_expired(void *data)
+{
+       struct irlan_cb *self = (struct irlan_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /*
+        * If we are in peer mode, the client may not have got the discovery
+        * indication it needs to make progress. If the client is still in
+        * IDLE state, we must kick it to, but only if the provider is not IDLE
+        */
+       if ((self->provider.access_type == ACCESS_PEER) &&
+           (self->client.state == IRLAN_IDLE) &&
+           (self->provider.state != IRLAN_IDLE)) {
+               irlan_client_wakeup(self, self->saddr, self->daddr);
+       }
+}
+
+static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout)
+{
+       irda_start_timer(&self->client.kick_timer, timeout, (void *) self,
+                        irlan_client_kick_timer_expired);
+}
+
+/*
+ * Function irlan_client_wakeup (self, saddr, daddr)
+ *
+ *    Wake up client
+ *
+ */
+void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /*
+        * Check if we are already awake, or if we are a provider in direct
+        * mode (in that case we must leave the client idle
+        */
+       if ((self->client.state != IRLAN_IDLE) ||
+           (self->provider.access_type == ACCESS_DIRECT))
+       {
+               pr_debug("%s(), already awake!\n", __func__);
+                       return;
+       }
+
+       /* Addresses may have changed! */
+       self->saddr = saddr;
+       self->daddr = daddr;
+
+       if (self->disconnect_reason == LM_USER_REQUEST) {
+               pr_debug("%s(), still stopped by user\n", __func__);
+                       return;
+       }
+
+       /* Open TSAPs */
+       irlan_client_open_ctrl_tsap(self);
+       irlan_open_data_tsap(self);
+
+       irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL);
+
+       /* Start kick timer */
+       irlan_client_start_kick_timer(self, 2*HZ);
+}
+
+/*
+ * Function irlan_discovery_indication (daddr)
+ *
+ *    Remote device with IrLAN server support discovered
+ *
+ */
+void irlan_client_discovery_indication(discinfo_t *discovery,
+                                      DISCOVERY_MODE mode,
+                                      void *priv)
+{
+       struct irlan_cb *self;
+       __u32 saddr, daddr;
+
+       IRDA_ASSERT(discovery != NULL, return;);
+
+       /*
+        * I didn't check it, but I bet that IrLAN suffer from the same
+        * deficiency as IrComm and doesn't handle two instances
+        * simultaneously connecting to each other.
+        * Same workaround, drop passive discoveries.
+        * Jean II */
+       if(mode == DISCOVERY_PASSIVE)
+               return;
+
+       saddr = discovery->saddr;
+       daddr = discovery->daddr;
+
+       /* Find instance */
+       rcu_read_lock();
+       self = irlan_get_any();
+       if (self) {
+               IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;);
+
+               pr_debug("%s(), Found instance (%08x)!\n", __func__ ,
+                        daddr);
+
+               irlan_client_wakeup(self, saddr, daddr);
+       }
+IRDA_ASSERT_LABEL(out:)
+       rcu_read_unlock();
+}
+
+/*
+ * Function irlan_client_data_indication (handle, skb)
+ *
+ *    This function gets the data that is received on the control channel
+ *
+ */
+static int irlan_client_ctrl_data_indication(void *instance, void *sap,
+                                            struct sk_buff *skb)
+{
+       struct irlan_cb *self;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb);
+
+       /* Ready for a new command */
+       pr_debug("%s(), clearing tx_busy\n", __func__);
+       self->client.tx_busy = FALSE;
+
+       /* Check if we have some queued commands waiting to be sent */
+       irlan_run_ctrl_tx_queue(self);
+
+       return 0;
+}
+
+static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
+                                                   LM_REASON reason,
+                                                   struct sk_buff *userdata)
+{
+       struct irlan_cb *self;
+       struct tsap_cb *tsap;
+       struct sk_buff *skb;
+
+       pr_debug("%s(), reason=%d\n", __func__ , reason);
+
+       self = instance;
+       tsap = sap;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+       IRDA_ASSERT(tsap != NULL, return;);
+       IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
+
+       IRDA_ASSERT(tsap == self->client.tsap_ctrl, return;);
+
+       /* Remove frames queued on the control channel */
+       while ((skb = skb_dequeue(&self->client.txq)) != NULL) {
+               dev_kfree_skb(skb);
+       }
+       self->client.tx_busy = FALSE;
+
+       irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
+}
+
+/*
+ * Function irlan_client_open_tsaps (self)
+ *
+ *    Initialize callbacks and open IrTTP TSAPs
+ *
+ */
+static void irlan_client_open_ctrl_tsap(struct irlan_cb *self)
+{
+       struct tsap_cb *tsap;
+       notify_t notify;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /* Check if already open */
+       if (self->client.tsap_ctrl)
+               return;
+
+       irda_notify_init(&notify);
+
+       /* Set up callbacks */
+       notify.data_indication       = irlan_client_ctrl_data_indication;
+       notify.connect_confirm       = irlan_client_ctrl_connect_confirm;
+       notify.disconnect_indication = irlan_client_ctrl_disconnect_indication;
+       notify.instance = self;
+       strlcpy(notify.name, "IrLAN ctrl (c)", sizeof(notify.name));
+
+       tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
+       if (!tsap) {
+               pr_debug("%s(), Got no tsap!\n", __func__);
+               return;
+       }
+       self->client.tsap_ctrl = tsap;
+}
+
+/*
+ * Function irlan_client_connect_confirm (handle, skb)
+ *
+ *    Connection to peer IrLAN laye confirmed
+ *
+ */
+static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
+                                             struct qos_info *qos,
+                                             __u32 max_sdu_size,
+                                             __u8 max_header_size,
+                                             struct sk_buff *skb)
+{
+       struct irlan_cb *self;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       self->client.max_sdu_size = max_sdu_size;
+       self->client.max_header_size = max_header_size;
+
+       /* TODO: we could set the MTU depending on the max_sdu_size */
+
+       irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL);
+}
+
+/*
+ * Function print_ret_code (code)
+ *
+ *    Print return code of request to peer IrLAN layer.
+ *
+ */
+static void print_ret_code(__u8 code)
+{
+       switch(code) {
+       case 0:
+               printk(KERN_INFO "Success\n");
+               break;
+       case 1:
+               net_warn_ratelimited("IrLAN: Insufficient resources\n");
+               break;
+       case 2:
+               net_warn_ratelimited("IrLAN: Invalid command format\n");
+               break;
+       case 3:
+               net_warn_ratelimited("IrLAN: Command not supported\n");
+               break;
+       case 4:
+               net_warn_ratelimited("IrLAN: Parameter not supported\n");
+               break;
+       case 5:
+               net_warn_ratelimited("IrLAN: Value not supported\n");
+               break;
+       case 6:
+               net_warn_ratelimited("IrLAN: Not open\n");
+               break;
+       case 7:
+               net_warn_ratelimited("IrLAN: Authentication required\n");
+               break;
+       case 8:
+               net_warn_ratelimited("IrLAN: Invalid password\n");
+               break;
+       case 9:
+               net_warn_ratelimited("IrLAN: Protocol error\n");
+               break;
+       case 255:
+               net_warn_ratelimited("IrLAN: Asynchronous status\n");
+               break;
+       }
+}
+
+/*
+ * Function irlan_client_parse_response (self, skb)
+ *
+ *    Extract all parameters from received buffer, then feed them to
+ *    check_params for parsing
+ */
+void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb)
+{
+       __u8 *frame;
+       __u8 *ptr;
+       int count;
+       int ret;
+       __u16 val_len;
+       int i;
+       char *name;
+       char *value;
+
+       IRDA_ASSERT(skb != NULL, return;);
+
+       pr_debug("%s() skb->len=%d\n", __func__ , (int)skb->len);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       if (!skb) {
+               net_err_ratelimited("%s(), Got NULL skb!\n", __func__);
+               return;
+       }
+       frame = skb->data;
+
+       /*
+        *  Check return code and print it if not success
+        */
+       if (frame[0]) {
+               print_ret_code(frame[0]);
+               return;
+       }
+
+       name = kmalloc(255, GFP_ATOMIC);
+       if (!name)
+               return;
+       value = kmalloc(1016, GFP_ATOMIC);
+       if (!value) {
+               kfree(name);
+               return;
+       }
+
+       /* How many parameters? */
+       count = frame[1];
+
+       pr_debug("%s(), got %d parameters\n", __func__ , count);
+
+       ptr = frame+2;
+
+       /* For all parameters */
+       for (i=0; i<count;i++) {
+               ret = irlan_extract_param(ptr, name, value, &val_len);
+               if (ret < 0) {
+                       pr_debug("%s(), IrLAN, Error!\n", __func__);
+                       break;
+               }
+               ptr += ret;
+               irlan_check_response_param(self, name, value, val_len);
+       }
+       /* Cleanup */
+       kfree(name);
+       kfree(value);
+}
+
+/*
+ * Function irlan_check_response_param (self, param, value, val_len)
+ *
+ *     Check which parameter is received and update local variables
+ *
+ */
+static void irlan_check_response_param(struct irlan_cb *self, char *param,
+                                      char *value, int val_len)
+{
+       __u16 tmp_cpu; /* Temporary value in host order */
+       __u8 *bytes;
+       int i;
+
+       pr_debug("%s(), parm=%s\n", __func__ , param);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /* Media type */
+       if (strcmp(param, "MEDIA") == 0) {
+               if (strcmp(value, "802.3") == 0)
+                       self->media = MEDIA_802_3;
+               else
+                       self->media = MEDIA_802_5;
+               return;
+       }
+       if (strcmp(param, "FILTER_TYPE") == 0) {
+               if (strcmp(value, "DIRECTED") == 0)
+                       self->client.filter_type |= IRLAN_DIRECTED;
+               else if (strcmp(value, "FUNCTIONAL") == 0)
+                       self->client.filter_type |= IRLAN_FUNCTIONAL;
+               else if (strcmp(value, "GROUP") == 0)
+                       self->client.filter_type |= IRLAN_GROUP;
+               else if (strcmp(value, "MAC_FRAME") == 0)
+                       self->client.filter_type |= IRLAN_MAC_FRAME;
+               else if (strcmp(value, "MULTICAST") == 0)
+                       self->client.filter_type |= IRLAN_MULTICAST;
+               else if (strcmp(value, "BROADCAST") == 0)
+                       self->client.filter_type |= IRLAN_BROADCAST;
+               else if (strcmp(value, "IPX_SOCKET") == 0)
+                       self->client.filter_type |= IRLAN_IPX_SOCKET;
+
+       }
+       if (strcmp(param, "ACCESS_TYPE") == 0) {
+               if (strcmp(value, "DIRECT") == 0)
+                       self->client.access_type = ACCESS_DIRECT;
+               else if (strcmp(value, "PEER") == 0)
+                       self->client.access_type = ACCESS_PEER;
+               else if (strcmp(value, "HOSTED") == 0)
+                       self->client.access_type = ACCESS_HOSTED;
+               else {
+                       pr_debug("%s(), unknown access type!\n", __func__);
+               }
+       }
+       /* IRLAN version */
+       if (strcmp(param, "IRLAN_VER") == 0) {
+               pr_debug("IrLAN version %d.%d\n", (__u8)value[0],
+                        (__u8)value[1]);
+
+               self->version[0] = value[0];
+               self->version[1] = value[1];
+               return;
+       }
+       /* Which remote TSAP to use for data channel */
+       if (strcmp(param, "DATA_CHAN") == 0) {
+               self->dtsap_sel_data = value[0];
+               pr_debug("Data TSAP = %02x\n", self->dtsap_sel_data);
+               return;
+       }
+       if (strcmp(param, "CON_ARB") == 0) {
+               memcpy(&tmp_cpu, value, 2); /* Align value */
+               le16_to_cpus(&tmp_cpu);     /* Convert to host order */
+               self->client.recv_arb_val = tmp_cpu;
+               pr_debug("%s(), receive arb val=%d\n", __func__ ,
+                        self->client.recv_arb_val);
+       }
+       if (strcmp(param, "MAX_FRAME") == 0) {
+               memcpy(&tmp_cpu, value, 2); /* Align value */
+               le16_to_cpus(&tmp_cpu);     /* Convert to host order */
+               self->client.max_frame = tmp_cpu;
+               pr_debug("%s(), max frame=%d\n", __func__ ,
+                        self->client.max_frame);
+       }
+
+       /* RECONNECT_KEY, in case the link goes down! */
+       if (strcmp(param, "RECONNECT_KEY") == 0) {
+               pr_debug("Got reconnect key: ");
+               /* for (i = 0; i < val_len; i++) */
+/*                     printk("%02x", value[i]); */
+               memcpy(self->client.reconnect_key, value, val_len);
+               self->client.key_len = val_len;
+               pr_debug("\n");
+       }
+       /* FILTER_ENTRY, have we got an ethernet address? */
+       if (strcmp(param, "FILTER_ENTRY") == 0) {
+               bytes = value;
+               pr_debug("Ethernet address = %pM\n", bytes);
+               for (i = 0; i < 6; i++)
+                       self->dev->dev_addr[i] = bytes[i];
+       }
+}
+
+/*
+ * Function irlan_client_get_value_confirm (obj_id, value)
+ *
+ *    Got results from remote LM-IAS
+ *
+ */
+void irlan_client_get_value_confirm(int result, __u16 obj_id,
+                                   struct ias_value *value, void *priv)
+{
+       struct irlan_cb *self;
+
+       IRDA_ASSERT(priv != NULL, return;);
+
+       self = priv;
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /* We probably don't need to make any more queries */
+       iriap_close(self->client.iriap);
+       self->client.iriap = NULL;
+
+       /* Check if request succeeded */
+       if (result != IAS_SUCCESS) {
+               pr_debug("%s(), got NULL value!\n", __func__);
+               irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL,
+                                     NULL);
+               return;
+       }
+
+       switch (value->type) {
+       case IAS_INTEGER:
+               self->dtsap_sel_ctrl = value->t.integer;
+
+               if (value->t.integer != -1) {
+                       irlan_do_client_event(self, IRLAN_IAS_PROVIDER_AVAIL,
+                                             NULL);
+                       return;
+               }
+               irias_delete_value(value);
+               break;
+       default:
+               pr_debug("%s(), unknown type!\n", __func__);
+               break;
+       }
+       irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL);
+}
diff --git a/drivers/staging/irda/net/irlan/irlan_client_event.c b/drivers/staging/irda/net/irlan/irlan_client_event.c
new file mode 100644 (file)
index 0000000..cc93fab
--- /dev/null
@@ -0,0 +1,511 @@
+/*********************************************************************
+ *
+ * Filename:      irlan_client_event.c
+ * Version:       0.9
+ * Description:   IrLAN client state machine
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Aug 31 20:14:37 1997
+ * Modified at:   Sun Dec 26 21:52:24 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/skbuff.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/timer.h>
+#include <net/irda/irmod.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irttp.h>
+
+#include <net/irda/irlan_common.h>
+#include <net/irda/irlan_client.h>
+#include <net/irda/irlan_event.h>
+
+static int irlan_client_state_idle (struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_conn (struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_info (struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_open (struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_wait (struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_arb  (struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_data (struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+static int irlan_client_state_sync (struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb);
+
+static int (*state[])(struct irlan_cb *, IRLAN_EVENT event, struct sk_buff *) =
+{
+       irlan_client_state_idle,
+       irlan_client_state_query,
+       irlan_client_state_conn,
+       irlan_client_state_info,
+       irlan_client_state_media,
+       irlan_client_state_open,
+       irlan_client_state_wait,
+       irlan_client_state_arb,
+       irlan_client_state_data,
+       irlan_client_state_close,
+       irlan_client_state_sync
+};
+
+void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event,
+                          struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       (*state[ self->client.state]) (self, event, skb);
+}
+
+/*
+ * Function irlan_client_state_idle (event, skb, info)
+ *
+ *    IDLE, We are waiting for an indication that there is a provider
+ *    available.
+ */
+static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
+                                  struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
+
+       switch (event) {
+       case IRLAN_DISCOVERY_INDICATION:
+               if (self->client.iriap) {
+                       net_warn_ratelimited("%s(), busy with a previous query\n",
+                                            __func__);
+                       return -EBUSY;
+               }
+
+               self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+                                               irlan_client_get_value_confirm);
+               /* Get some values from peer IAS */
+               irlan_next_client_state(self, IRLAN_QUERY);
+               iriap_getvaluebyclass_request(self->client.iriap,
+                                             self->saddr, self->daddr,
+                                             "IrLAN", "IrDA:TinyTP:LsapSel");
+               break;
+       case IRLAN_WATCHDOG_TIMEOUT:
+               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_query (event, skb, info)
+ *
+ *    QUERY, We have queryed the remote IAS and is ready to connect
+ *    to provider, just waiting for the confirm.
+ *
+ */
+static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
+
+       switch(event) {
+       case IRLAN_IAS_PROVIDER_AVAIL:
+               IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;);
+
+               self->client.open_retries = 0;
+
+               irttp_connect_request(self->client.tsap_ctrl,
+                                     self->dtsap_sel_ctrl,
+                                     self->saddr, self->daddr, NULL,
+                                     IRLAN_MTU, NULL);
+               irlan_next_client_state(self, IRLAN_CONN);
+               break;
+       case IRLAN_IAS_PROVIDER_NOT_AVAIL:
+               pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__);
+               irlan_next_client_state(self, IRLAN_IDLE);
+
+               /* Give the client a kick! */
+               if ((self->provider.access_type == ACCESS_PEER) &&
+                   (self->provider.state != IRLAN_IDLE))
+                       irlan_client_wakeup(self, self->saddr, self->daddr);
+               break;
+       case IRLAN_LMP_DISCONNECT:
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_client_state(self, IRLAN_IDLE);
+               break;
+       case IRLAN_WATCHDOG_TIMEOUT:
+               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_conn (event, skb, info)
+ *
+ *    CONN, We have connected to a provider but has not issued any
+ *    commands yet.
+ *
+ */
+static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
+                                  struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch (event) {
+       case IRLAN_CONNECT_COMPLETE:
+               /* Send getinfo cmd */
+               irlan_get_provider_info(self);
+               irlan_next_client_state(self, IRLAN_INFO);
+               break;
+       case IRLAN_LMP_DISCONNECT:
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_client_state(self, IRLAN_IDLE);
+               break;
+       case IRLAN_WATCHDOG_TIMEOUT:
+               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_info (self, event, skb, info)
+ *
+ *    INFO, We have issued a GetInfo command and is awaiting a reply.
+ */
+static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
+                                  struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch (event) {
+       case IRLAN_DATA_INDICATION:
+               IRDA_ASSERT(skb != NULL, return -1;);
+
+               irlan_client_parse_response(self, skb);
+
+               irlan_next_client_state(self, IRLAN_MEDIA);
+
+               irlan_get_media_char(self);
+               break;
+
+       case IRLAN_LMP_DISCONNECT:
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_client_state(self, IRLAN_IDLE);
+               break;
+       case IRLAN_WATCHDOG_TIMEOUT:
+               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_media (self, event, skb, info)
+ *
+ *    MEDIA, The irlan_client has issued a GetMedia command and is awaiting a
+ *    reply.
+ *
+ */
+static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch(event) {
+       case IRLAN_DATA_INDICATION:
+               irlan_client_parse_response(self, skb);
+               irlan_open_data_channel(self);
+               irlan_next_client_state(self, IRLAN_OPEN);
+               break;
+       case IRLAN_LMP_DISCONNECT:
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_client_state(self, IRLAN_IDLE);
+               break;
+       case IRLAN_WATCHDOG_TIMEOUT:
+               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_open (self, event, skb, info)
+ *
+ *    OPEN, The irlan_client has issued a OpenData command and is awaiting a
+ *    reply
+ *
+ */
+static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
+                                  struct sk_buff *skb)
+{
+       struct qos_info qos;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch(event) {
+       case IRLAN_DATA_INDICATION:
+               irlan_client_parse_response(self, skb);
+
+               /*
+                *  Check if we have got the remote TSAP for data
+                *  communications
+                */
+               IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;);
+
+               /* Check which access type we are dealing with */
+               switch (self->client.access_type) {
+               case ACCESS_PEER:
+                   if (self->provider.state == IRLAN_OPEN) {
+
+                           irlan_next_client_state(self, IRLAN_ARB);
+                           irlan_do_client_event(self, IRLAN_CHECK_CON_ARB,
+                                                 NULL);
+                   } else {
+
+                           irlan_next_client_state(self, IRLAN_WAIT);
+                   }
+                   break;
+               case ACCESS_DIRECT:
+               case ACCESS_HOSTED:
+                       qos.link_disc_time.bits = 0x01; /* 3 secs */
+
+                       irttp_connect_request(self->tsap_data,
+                                             self->dtsap_sel_data,
+                                             self->saddr, self->daddr, &qos,
+                                             IRLAN_MTU, NULL);
+
+                       irlan_next_client_state(self, IRLAN_DATA);
+                       break;
+               default:
+                       pr_debug("%s(), unknown access type!\n", __func__);
+                       break;
+               }
+               break;
+       case IRLAN_LMP_DISCONNECT:
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_client_state(self, IRLAN_IDLE);
+               break;
+       case IRLAN_WATCHDOG_TIMEOUT:
+               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_wait (self, event, skb, info)
+ *
+ *    WAIT, The irlan_client is waiting for the local provider to enter the
+ *    provider OPEN state.
+ *
+ */
+static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event,
+                                  struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch(event) {
+       case IRLAN_PROVIDER_SIGNAL:
+               irlan_next_client_state(self, IRLAN_ARB);
+               irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL);
+               break;
+       case IRLAN_LMP_DISCONNECT:
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_client_state(self, IRLAN_IDLE);
+               break;
+       case IRLAN_WATCHDOG_TIMEOUT:
+               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
+                                 struct sk_buff *skb)
+{
+       struct qos_info qos;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch(event) {
+       case IRLAN_CHECK_CON_ARB:
+               if (self->client.recv_arb_val == self->provider.send_arb_val) {
+                       irlan_next_client_state(self, IRLAN_CLOSE);
+                       irlan_close_data_channel(self);
+               } else if (self->client.recv_arb_val <
+                          self->provider.send_arb_val)
+               {
+                       qos.link_disc_time.bits = 0x01; /* 3 secs */
+
+                       irlan_next_client_state(self, IRLAN_DATA);
+                       irttp_connect_request(self->tsap_data,
+                                             self->dtsap_sel_data,
+                                             self->saddr, self->daddr, &qos,
+                                             IRLAN_MTU, NULL);
+               } else if (self->client.recv_arb_val >
+                          self->provider.send_arb_val)
+               {
+                       pr_debug("%s(), lost the battle :-(\n", __func__);
+               }
+               break;
+       case IRLAN_DATA_CONNECT_INDICATION:
+               irlan_next_client_state(self, IRLAN_DATA);
+               break;
+       case IRLAN_LMP_DISCONNECT:
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_client_state(self, IRLAN_IDLE);
+               break;
+       case IRLAN_WATCHDOG_TIMEOUT:
+               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_data (self, event, skb, info)
+ *
+ *    DATA, The data channel is connected, allowing data transfers between
+ *    the local and remote machines.
+ *
+ */
+static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
+                                  struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
+
+       switch(event) {
+       case IRLAN_DATA_INDICATION:
+               irlan_client_parse_response(self, skb);
+               break;
+       case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_client_state(self, IRLAN_IDLE);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_close (self, event, skb, info)
+ *
+ *
+ *
+ */
+static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
+                                   struct sk_buff *skb)
+{
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_client_state_sync (self, event, skb, info)
+ *
+ *
+ *
+ */
+static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event,
+                                  struct sk_buff *skb)
+{
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drivers/staging/irda/net/irlan/irlan_common.c b/drivers/staging/irda/net/irlan/irlan_common.c
new file mode 100644 (file)
index 0000000..481bbc2
--- /dev/null
@@ -0,0 +1,1176 @@
+/*********************************************************************
+ *
+ * Filename:      irlan_common.c
+ * Version:       0.9
+ * Description:   IrDA LAN Access Protocol Implementation
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Aug 31 20:14:37 1997
+ * Modified at:   Sun Dec 26 21:53:10 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/random.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/moduleparam.h>
+#include <linux/bitops.h>
+
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+#include <net/irda/timer.h>
+
+#include <net/irda/irlan_common.h>
+#include <net/irda/irlan_client.h>
+#include <net/irda/irlan_provider.h>
+#include <net/irda/irlan_eth.h>
+#include <net/irda/irlan_filter.h>
+
+
+/* extern char sysctl_devname[]; */
+
+/*
+ *  Master structure
+ */
+static LIST_HEAD(irlans);
+
+static void *ckey;
+static void *skey;
+
+/* Module parameters */
+static bool eth;   /* Use "eth" or "irlan" name for devices */
+static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */
+
+#ifdef CONFIG_PROC_FS
+static const char *const irlan_access[] = {
+       "UNKNOWN",
+       "DIRECT",
+       "PEER",
+       "HOSTED"
+};
+
+static const char *const irlan_media[] = {
+       "UNKNOWN",
+       "802.3",
+       "802.5"
+};
+
+extern struct proc_dir_entry *proc_irda;
+
+static int irlan_seq_open(struct inode *inode, struct file *file);
+
+static const struct file_operations irlan_fops = {
+       .owner   = THIS_MODULE,
+       .open    = irlan_seq_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release,
+};
+
+extern struct proc_dir_entry *proc_irda;
+#endif /* CONFIG_PROC_FS */
+
+static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr);
+static void __irlan_close(struct irlan_cb *self);
+static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
+                               __u8 value_byte, __u16 value_short,
+                               __u8 *value_array, __u16 value_len);
+static void irlan_open_unicast_addr(struct irlan_cb *self);
+static void irlan_get_unicast_addr(struct irlan_cb *self);
+void irlan_close_tsaps(struct irlan_cb *self);
+
+/*
+ * Function irlan_init (void)
+ *
+ *    Initialize IrLAN layer
+ *
+ */
+static int __init irlan_init(void)
+{
+       struct irlan_cb *new;
+       __u16 hints;
+
+#ifdef CONFIG_PROC_FS
+       { struct proc_dir_entry *proc;
+       proc = proc_create("irlan", 0, proc_irda, &irlan_fops);
+       if (!proc) {
+               printk(KERN_ERR "irlan_init: can't create /proc entry!\n");
+               return -ENODEV;
+       }
+       }
+#endif /* CONFIG_PROC_FS */
+
+       hints = irlmp_service_to_hint(S_LAN);
+
+       /* Register with IrLMP as a client */
+       ckey = irlmp_register_client(hints, &irlan_client_discovery_indication,
+                                    NULL, NULL);
+       if (!ckey)
+               goto err_ckey;
+
+       /* Register with IrLMP as a service */
+       skey = irlmp_register_service(hints);
+       if (!skey)
+               goto err_skey;
+
+       /* Start the master IrLAN instance (the only one for now) */
+       new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY);
+       if (!new)
+               goto err_open;
+
+       /* The master will only open its (listen) control TSAP */
+       irlan_provider_open_ctrl_tsap(new);
+
+       /* Do some fast discovery! */
+       irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
+
+       return 0;
+
+err_open:
+       irlmp_unregister_service(skey);
+err_skey:
+       irlmp_unregister_client(ckey);
+err_ckey:
+#ifdef CONFIG_PROC_FS
+       remove_proc_entry("irlan", proc_irda);
+#endif /* CONFIG_PROC_FS */
+
+       return -ENOMEM;
+}
+
+static void __exit irlan_cleanup(void)
+{
+       struct irlan_cb *self, *next;
+
+       irlmp_unregister_client(ckey);
+       irlmp_unregister_service(skey);
+
+#ifdef CONFIG_PROC_FS
+       remove_proc_entry("irlan", proc_irda);
+#endif /* CONFIG_PROC_FS */
+
+       /* Cleanup any leftover network devices */
+       rtnl_lock();
+       list_for_each_entry_safe(self, next, &irlans, dev_list) {
+               __irlan_close(self);
+       }
+       rtnl_unlock();
+}
+
+/*
+ * Function irlan_open (void)
+ *
+ *    Open new instance of a client/provider, we should only register the
+ *    network device if this instance is ment for a particular client/provider
+ */
+static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr)
+{
+       struct net_device *dev;
+       struct irlan_cb *self;
+
+       /* Create network device with irlan */
+       dev = alloc_irlandev(eth ? "eth%d" : "irlan%d");
+       if (!dev)
+               return NULL;
+
+       self = netdev_priv(dev);
+       self->dev = dev;
+
+       /*
+        *  Initialize local device structure
+        */
+       self->magic = IRLAN_MAGIC;
+       self->saddr = saddr;
+       self->daddr = daddr;
+
+       /* Provider access can only be PEER, DIRECT, or HOSTED */
+       self->provider.access_type = access;
+       if (access == ACCESS_DIRECT) {
+               /*
+                * Since we are emulating an IrLAN sever we will have to
+                * give ourself an ethernet address!
+                */
+               dev->dev_addr[0] = 0x40;
+               dev->dev_addr[1] = 0x00;
+               dev->dev_addr[2] = 0x00;
+               dev->dev_addr[3] = 0x00;
+               get_random_bytes(dev->dev_addr+4, 1);
+               get_random_bytes(dev->dev_addr+5, 1);
+       }
+
+       self->media = MEDIA_802_3;
+       self->disconnect_reason = LM_USER_REQUEST;
+       init_timer(&self->watchdog_timer);
+       init_timer(&self->client.kick_timer);
+       init_waitqueue_head(&self->open_wait);
+
+       skb_queue_head_init(&self->client.txq);
+
+       irlan_next_client_state(self, IRLAN_IDLE);
+       irlan_next_provider_state(self, IRLAN_IDLE);
+
+       if (register_netdev(dev)) {
+               pr_debug("%s(), register_netdev() failed!\n",
+                        __func__);
+               self = NULL;
+               free_netdev(dev);
+       } else {
+               rtnl_lock();
+               list_add_rcu(&self->dev_list, &irlans);
+               rtnl_unlock();
+       }
+
+       return self;
+}
+/*
+ * Function __irlan_close (self)
+ *
+ *    This function closes and deallocates the IrLAN client instances. Be
+ *    aware that other functions which calls client_close() must
+ *    remove self from irlans list first.
+ */
+static void __irlan_close(struct irlan_cb *self)
+{
+       ASSERT_RTNL();
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       del_timer_sync(&self->watchdog_timer);
+       del_timer_sync(&self->client.kick_timer);
+
+       /* Close all open connections and remove TSAPs */
+       irlan_close_tsaps(self);
+
+       if (self->client.iriap)
+               iriap_close(self->client.iriap);
+
+       /* Remove frames queued on the control channel */
+       skb_queue_purge(&self->client.txq);
+
+       /* Unregister and free self via destructor */
+       unregister_netdevice(self->dev);
+}
+
+/* Find any instance of irlan, used for client discovery wakeup */
+struct irlan_cb *irlan_get_any(void)
+{
+       struct irlan_cb *self;
+
+       list_for_each_entry_rcu(self, &irlans, dev_list) {
+               return self;
+       }
+       return NULL;
+}
+
+/*
+ * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb)
+ *
+ *    Here we receive the connect indication for the data channel
+ *
+ */
+static void irlan_connect_indication(void *instance, void *sap,
+                                    struct qos_info *qos,
+                                    __u32 max_sdu_size,
+                                    __u8 max_header_size,
+                                    struct sk_buff *skb)
+{
+       struct irlan_cb *self;
+       struct tsap_cb *tsap;
+
+       self = instance;
+       tsap = sap;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+       IRDA_ASSERT(tsap == self->tsap_data,return;);
+
+       self->max_sdu_size = max_sdu_size;
+       self->max_header_size = max_header_size;
+
+       pr_debug("%s: We are now connected!\n", __func__);
+
+       del_timer(&self->watchdog_timer);
+
+       /* If you want to pass the skb to *both* state machines, you will
+        * need to skb_clone() it, so that you don't free it twice.
+        * As the state machines don't need it, git rid of it here...
+        * Jean II */
+       if (skb)
+               dev_kfree_skb(skb);
+
+       irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
+       irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
+
+       if (self->provider.access_type == ACCESS_PEER) {
+               /*
+                * Data channel is open, so we are now allowed to
+                * configure the remote filter
+                */
+               irlan_get_unicast_addr(self);
+               irlan_open_unicast_addr(self);
+       }
+       /* Ready to transfer Ethernet frames (at last) */
+       netif_start_queue(self->dev); /* Clear reason */
+}
+
+static void irlan_connect_confirm(void *instance, void *sap,
+                                 struct qos_info *qos,
+                                 __u32 max_sdu_size,
+                                 __u8 max_header_size,
+                                 struct sk_buff *skb)
+{
+       struct irlan_cb *self;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       self->max_sdu_size = max_sdu_size;
+       self->max_header_size = max_header_size;
+
+       /* TODO: we could set the MTU depending on the max_sdu_size */
+
+       pr_debug("%s: We are now connected!\n", __func__);
+       del_timer(&self->watchdog_timer);
+
+       /*
+        * Data channel is open, so we are now allowed to configure the remote
+        * filter
+        */
+       irlan_get_unicast_addr(self);
+       irlan_open_unicast_addr(self);
+
+       /* Open broadcast and multicast filter by default */
+       irlan_set_broadcast_filter(self, TRUE);
+       irlan_set_multicast_filter(self, TRUE);
+
+       /* Ready to transfer Ethernet frames */
+       netif_start_queue(self->dev);
+       self->disconnect_reason = 0; /* Clear reason */
+       wake_up_interruptible(&self->open_wait);
+}
+
+/*
+ * Function irlan_client_disconnect_indication (handle)
+ *
+ *    Callback function for the IrTTP layer. Indicates a disconnection of
+ *    the specified connection (handle)
+ */
+static void irlan_disconnect_indication(void *instance,
+                                       void *sap, LM_REASON reason,
+                                       struct sk_buff *userdata)
+{
+       struct irlan_cb *self;
+       struct tsap_cb *tsap;
+
+       pr_debug("%s(), reason=%d\n", __func__ , reason);
+
+       self = instance;
+       tsap = sap;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+       IRDA_ASSERT(tsap != NULL, return;);
+       IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
+
+       IRDA_ASSERT(tsap == self->tsap_data, return;);
+
+       pr_debug("IrLAN, data channel disconnected by peer!\n");
+
+       /* Save reason so we know if we should try to reconnect or not */
+       self->disconnect_reason = reason;
+
+       switch (reason) {
+       case LM_USER_REQUEST: /* User request */
+               pr_debug("%s(), User requested\n", __func__);
+               break;
+       case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */
+               pr_debug("%s(), Unexpected IrLAP disconnect\n", __func__);
+               break;
+       case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */
+               pr_debug("%s(), IrLAP connect failed\n", __func__);
+               break;
+       case LM_LAP_RESET:  /* IrLAP reset */
+               pr_debug("%s(), IrLAP reset\n", __func__);
+               break;
+       case LM_INIT_DISCONNECT:
+               pr_debug("%s(), IrLMP connect failed\n", __func__);
+               break;
+       default:
+               net_err_ratelimited("%s(), Unknown disconnect reason\n",
+                                   __func__);
+               break;
+       }
+
+       /* If you want to pass the skb to *both* state machines, you will
+        * need to skb_clone() it, so that you don't free it twice.
+        * As the state machines don't need it, git rid of it here...
+        * Jean II */
+       if (userdata)
+               dev_kfree_skb(userdata);
+
+       irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
+       irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
+
+       wake_up_interruptible(&self->open_wait);
+}
+
+void irlan_open_data_tsap(struct irlan_cb *self)
+{
+       struct tsap_cb *tsap;
+       notify_t notify;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /* Check if already open */
+       if (self->tsap_data)
+               return;
+
+       irda_notify_init(&notify);
+
+       notify.data_indication       = irlan_eth_receive;
+       notify.udata_indication      = irlan_eth_receive;
+       notify.connect_indication    = irlan_connect_indication;
+       notify.connect_confirm       = irlan_connect_confirm;
+       notify.flow_indication       = irlan_eth_flow_indication;
+       notify.disconnect_indication = irlan_disconnect_indication;
+       notify.instance              = self;
+       strlcpy(notify.name, "IrLAN data", sizeof(notify.name));
+
+       tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
+       if (!tsap) {
+               pr_debug("%s(), Got no tsap!\n", __func__);
+               return;
+       }
+       self->tsap_data = tsap;
+
+       /*
+        *  This is the data TSAP selector which we will pass to the client
+        *  when the client ask for it.
+        */
+       self->stsap_sel_data = self->tsap_data->stsap_sel;
+}
+
+void irlan_close_tsaps(struct irlan_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /* Disconnect and close all open TSAP connections */
+       if (self->tsap_data) {
+               irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL);
+               irttp_close_tsap(self->tsap_data);
+               self->tsap_data = NULL;
+       }
+       if (self->client.tsap_ctrl) {
+               irttp_disconnect_request(self->client.tsap_ctrl, NULL,
+                                        P_NORMAL);
+               irttp_close_tsap(self->client.tsap_ctrl);
+               self->client.tsap_ctrl = NULL;
+       }
+       if (self->provider.tsap_ctrl) {
+               irttp_disconnect_request(self->provider.tsap_ctrl, NULL,
+                                        P_NORMAL);
+               irttp_close_tsap(self->provider.tsap_ctrl);
+               self->provider.tsap_ctrl = NULL;
+       }
+       self->disconnect_reason = LM_USER_REQUEST;
+}
+
+/*
+ * Function irlan_ias_register (self, tsap_sel)
+ *
+ *    Register with LM-IAS
+ *
+ */
+void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel)
+{
+       struct ias_object *obj;
+       struct ias_value *new_value;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /*
+        * Check if object has already been registered by a previous provider.
+        * If that is the case, we just change the value of the attribute
+        */
+       if (!irias_find_object("IrLAN")) {
+               obj = irias_new_object("IrLAN", IAS_IRLAN_ID);
+               irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel,
+                                        IAS_KERNEL_ATTR);
+               irias_insert_object(obj);
+       } else {
+               new_value = irias_new_integer_value(tsap_sel);
+               irias_object_change_attribute("IrLAN", "IrDA:TinyTP:LsapSel",
+                                             new_value);
+       }
+
+       /* Register PnP object only if not registered before */
+       if (!irias_find_object("PnP")) {
+               obj = irias_new_object("PnP", IAS_PNP_ID);
+#if 0
+               irias_add_string_attrib(obj, "Name", sysctl_devname,
+                                       IAS_KERNEL_ATTR);
+#else
+               irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR);
+#endif
+               irias_add_string_attrib(obj, "DeviceID", "HWP19F0",
+                                       IAS_KERNEL_ATTR);
+               irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR);
+               if (self->provider.access_type == ACCESS_PEER)
+                       irias_add_string_attrib(obj, "Comp#01", "PNP8389",
+                                               IAS_KERNEL_ATTR);
+               else
+                       irias_add_string_attrib(obj, "Comp#01", "PNP8294",
+                                               IAS_KERNEL_ATTR);
+
+               irias_add_string_attrib(obj, "Manufacturer",
+                                       "Linux-IrDA Project", IAS_KERNEL_ATTR);
+               irias_insert_object(obj);
+       }
+}
+
+/*
+ * Function irlan_run_ctrl_tx_queue (self)
+ *
+ *    Try to send the next command in the control transmit queue
+ *
+ */
+int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
+{
+       struct sk_buff *skb;
+
+       if (irda_lock(&self->client.tx_busy) == FALSE)
+               return -EBUSY;
+
+       skb = skb_dequeue(&self->client.txq);
+       if (!skb) {
+               self->client.tx_busy = FALSE;
+               return 0;
+       }
+
+       /* Check that it's really possible to send commands */
+       if ((self->client.tsap_ctrl == NULL) ||
+           (self->client.state == IRLAN_IDLE))
+       {
+               self->client.tx_busy = FALSE;
+               dev_kfree_skb(skb);
+               return -1;
+       }
+       pr_debug("%s(), sending ...\n", __func__);
+
+       return irttp_data_request(self->client.tsap_ctrl, skb);
+}
+
+/*
+ * Function irlan_ctrl_data_request (self, skb)
+ *
+ *    This function makes sure that commands on the control channel is being
+ *    sent in a command/response fashion
+ */
+static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb)
+{
+       /* Queue command */
+       skb_queue_tail(&self->client.txq, skb);
+
+       /* Try to send command */
+       irlan_run_ctrl_tx_queue(self);
+}
+
+/*
+ * Function irlan_get_provider_info (self)
+ *
+ *    Send Get Provider Information command to peer IrLAN layer
+ *
+ */
+void irlan_get_provider_info(struct irlan_cb *self)
+{
+       struct sk_buff *skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER,
+                       GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       /* Reserve space for TTP, LMP, and LAP header */
+       skb_reserve(skb, self->client.max_header_size);
+       skb_put(skb, 2);
+
+       frame = skb->data;
+
+       frame[0] = CMD_GET_PROVIDER_INFO;
+       frame[1] = 0x00;                 /* Zero parameters */
+
+       irlan_ctrl_data_request(self, skb);
+}
+
+/*
+ * Function irlan_open_data_channel (self)
+ *
+ *    Send an Open Data Command to provider
+ *
+ */
+void irlan_open_data_channel(struct irlan_cb *self)
+{
+       struct sk_buff *skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
+                       IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3") +
+                       IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "DIRECT"),
+                       GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       skb_reserve(skb, self->client.max_header_size);
+       skb_put(skb, 2);
+
+       frame = skb->data;
+
+       /* Build frame */
+       frame[0] = CMD_OPEN_DATA_CHANNEL;
+       frame[1] = 0x02; /* Two parameters */
+
+       irlan_insert_string_param(skb, "MEDIA", "802.3");
+       irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT");
+       /* irlan_insert_string_param(skb, "MODE", "UNRELIABLE"); */
+
+/*     self->use_udata = TRUE; */
+
+       irlan_ctrl_data_request(self, skb);
+}
+
+void irlan_close_data_channel(struct irlan_cb *self)
+{
+       struct sk_buff *skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /* Check if the TSAP is still there */
+       if (self->client.tsap_ctrl == NULL)
+               return;
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
+                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN"),
+                       GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       skb_reserve(skb, self->client.max_header_size);
+       skb_put(skb, 2);
+
+       frame = skb->data;
+
+       /* Build frame */
+       frame[0] = CMD_CLOSE_DATA_CHAN;
+       frame[1] = 0x01; /* One parameter */
+
+       irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
+
+       irlan_ctrl_data_request(self, skb);
+}
+
+/*
+ * Function irlan_open_unicast_addr (self)
+ *
+ *    Make IrLAN provider accept ethernet frames addressed to the unicast
+ *    address.
+ *
+ */
+static void irlan_open_unicast_addr(struct irlan_cb *self)
+{
+       struct sk_buff *skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
+                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"),
+                       GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       /* Reserve space for TTP, LMP, and LAP header */
+       skb_reserve(skb, self->max_header_size);
+       skb_put(skb, 2);
+
+       frame = skb->data;
+
+       frame[0] = CMD_FILTER_OPERATION;
+       frame[1] = 0x03;                 /* Three parameters */
+       irlan_insert_byte_param(skb, "DATA_CHAN" , self->dtsap_sel_data);
+       irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
+       irlan_insert_string_param(skb, "FILTER_MODE", "FILTER");
+
+       irlan_ctrl_data_request(self, skb);
+}
+
+/*
+ * Function irlan_set_broadcast_filter (self, status)
+ *
+ *    Make IrLAN provider accept ethernet frames addressed to the broadcast
+ *    address. Be careful with the use of this one, since there may be a lot
+ *    of broadcast traffic out there. We can still function without this
+ *    one but then _we_ have to initiate all communication with other
+ *    hosts, since ARP request for this host will not be answered.
+ */
+void irlan_set_broadcast_filter(struct irlan_cb *self, int status)
+{
+       struct sk_buff *skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
+                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") +
+                       /* We may waste one byte here...*/
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"),
+                       GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       /* Reserve space for TTP, LMP, and LAP header */
+       skb_reserve(skb, self->client.max_header_size);
+       skb_put(skb, 2);
+
+       frame = skb->data;
+
+       frame[0] = CMD_FILTER_OPERATION;
+       frame[1] = 0x03;                 /* Three parameters */
+       irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
+       irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST");
+       if (status)
+               irlan_insert_string_param(skb, "FILTER_MODE", "FILTER");
+       else
+               irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
+
+       irlan_ctrl_data_request(self, skb);
+}
+
+/*
+ * Function irlan_set_multicast_filter (self, status)
+ *
+ *    Make IrLAN provider accept ethernet frames addressed to the multicast
+ *    address.
+ *
+ */
+void irlan_set_multicast_filter(struct irlan_cb *self, int status)
+{
+       struct sk_buff *skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
+                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") +
+                       /* We may waste one byte here...*/
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "NONE"),
+                       GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       /* Reserve space for TTP, LMP, and LAP header */
+       skb_reserve(skb, self->client.max_header_size);
+       skb_put(skb, 2);
+
+       frame = skb->data;
+
+       frame[0] = CMD_FILTER_OPERATION;
+       frame[1] = 0x03;                 /* Three parameters */
+       irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
+       irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST");
+       if (status)
+               irlan_insert_string_param(skb, "FILTER_MODE", "ALL");
+       else
+               irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
+
+       irlan_ctrl_data_request(self, skb);
+}
+
+/*
+ * Function irlan_get_unicast_addr (self)
+ *
+ *    Retrieves the unicast address from the IrLAN provider. This address
+ *    will be inserted into the devices structure, so the ethernet layer
+ *    can construct its packets.
+ *
+ */
+static void irlan_get_unicast_addr(struct irlan_cb *self)
+{
+       struct sk_buff *skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
+                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_OPERATION",
+                                                  "DYNAMIC"),
+                       GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       /* Reserve space for TTP, LMP, and LAP header */
+       skb_reserve(skb, self->client.max_header_size);
+       skb_put(skb, 2);
+
+       frame = skb->data;
+
+       frame[0] = CMD_FILTER_OPERATION;
+       frame[1] = 0x03;                 /* Three parameters */
+       irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
+       irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
+       irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC");
+
+       irlan_ctrl_data_request(self, skb);
+}
+
+/*
+ * Function irlan_get_media_char (self)
+ *
+ *
+ *
+ */
+void irlan_get_media_char(struct irlan_cb *self)
+{
+       struct sk_buff *skb;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
+                       IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3"),
+                       GFP_ATOMIC);
+
+       if (!skb)
+               return;
+
+       /* Reserve space for TTP, LMP, and LAP header */
+       skb_reserve(skb, self->client.max_header_size);
+       skb_put(skb, 2);
+
+       frame = skb->data;
+
+       /* Build frame */
+       frame[0] = CMD_GET_MEDIA_CHAR;
+       frame[1] = 0x01; /* One parameter */
+
+       irlan_insert_string_param(skb, "MEDIA", "802.3");
+       irlan_ctrl_data_request(self, skb);
+}
+
+/*
+ * Function insert_byte_param (skb, param, value)
+ *
+ *    Insert byte parameter into frame
+ *
+ */
+int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value)
+{
+       return __irlan_insert_param(skb, param, IRLAN_BYTE, value, 0, NULL, 0);
+}
+
+int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value)
+{
+       return __irlan_insert_param(skb, param, IRLAN_SHORT, 0, value, NULL, 0);
+}
+
+/*
+ * Function insert_string (skb, param, value)
+ *
+ *    Insert string parameter into frame
+ *
+ */
+int irlan_insert_string_param(struct sk_buff *skb, char *param, char *string)
+{
+       int string_len = strlen(string);
+
+       return __irlan_insert_param(skb, param, IRLAN_ARRAY, 0, 0, string,
+                                   string_len);
+}
+
+/*
+ * Function insert_array_param(skb, param, value, len_value)
+ *
+ *    Insert array parameter into frame
+ *
+ */
+int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *array,
+                            __u16 array_len)
+{
+       return __irlan_insert_param(skb, name, IRLAN_ARRAY, 0, 0, array,
+                                   array_len);
+}
+
+/*
+ * Function insert_param (skb, param, value, byte)
+ *
+ *    Insert parameter at end of buffer, structure of a parameter is:
+ *
+ *    -----------------------------------------------------------------------
+ *    | Name Length[1] | Param Name[1..255] | Val Length[2] | Value[0..1016]|
+ *    -----------------------------------------------------------------------
+ */
+static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
+                               __u8 value_byte, __u16 value_short,
+                               __u8 *value_array, __u16 value_len)
+{
+       __u8 *frame;
+       __u8 param_len;
+       __le16 tmp_le; /* Temporary value in little endian format */
+       int n=0;
+
+       if (skb == NULL) {
+               pr_debug("%s(), Got NULL skb\n", __func__);
+               return 0;
+       }
+
+       param_len = strlen(param);
+       switch (type) {
+       case IRLAN_BYTE:
+               value_len = 1;
+               break;
+       case IRLAN_SHORT:
+               value_len = 2;
+               break;
+       case IRLAN_ARRAY:
+               IRDA_ASSERT(value_array != NULL, return 0;);
+               IRDA_ASSERT(value_len > 0, return 0;);
+               break;
+       default:
+               pr_debug("%s(), Unknown parameter type!\n", __func__);
+               return 0;
+       }
+
+       /* Insert at end of sk-buffer */
+       frame = skb_tail_pointer(skb);
+
+       /* Make space for data */
+       if (skb_tailroom(skb) < (param_len+value_len+3)) {
+               pr_debug("%s(), No more space at end of skb\n", __func__);
+               return 0;
+       }
+       skb_put(skb, param_len+value_len+3);
+
+       /* Insert parameter length */
+       frame[n++] = param_len;
+
+       /* Insert parameter */
+       memcpy(frame+n, param, param_len); n += param_len;
+
+       /* Insert value length (2 byte little endian format, LSB first) */
+       tmp_le = cpu_to_le16(value_len);
+       memcpy(frame+n, &tmp_le, 2); n += 2; /* To avoid alignment problems */
+
+       /* Insert value */
+       switch (type) {
+       case IRLAN_BYTE:
+               frame[n++] = value_byte;
+               break;
+       case IRLAN_SHORT:
+               tmp_le = cpu_to_le16(value_short);
+               memcpy(frame+n, &tmp_le, 2); n += 2;
+               break;
+       case IRLAN_ARRAY:
+               memcpy(frame+n, value_array, value_len); n+=value_len;
+               break;
+       default:
+               break;
+       }
+       IRDA_ASSERT(n == (param_len+value_len+3), return 0;);
+
+       return param_len+value_len+3;
+}
+
+/*
+ * Function irlan_extract_param (buf, name, value, len)
+ *
+ *    Extracts a single parameter name/value pair from buffer and updates
+ *    the buffer pointer to point to the next name/value pair.
+ */
+int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len)
+{
+       __u8 name_len;
+       __u16 val_len;
+       int n=0;
+
+       /* get length of parameter name (1 byte) */
+       name_len = buf[n++];
+
+       if (name_len > 254) {
+               pr_debug("%s(), name_len > 254\n", __func__);
+               return -RSP_INVALID_COMMAND_FORMAT;
+       }
+
+       /* get parameter name */
+       memcpy(name, buf+n, name_len);
+       name[name_len] = '\0';
+       n+=name_len;
+
+       /*
+        *  Get length of parameter value (2 bytes in little endian
+        *  format)
+        */
+       memcpy(&val_len, buf+n, 2); /* To avoid alignment problems */
+       le16_to_cpus(&val_len); n+=2;
+
+       if (val_len >= 1016) {
+               pr_debug("%s(), parameter length to long\n", __func__);
+               return -RSP_INVALID_COMMAND_FORMAT;
+       }
+       *len = val_len;
+
+       /* get parameter value */
+       memcpy(value, buf+n, val_len);
+       value[val_len] = '\0';
+       n+=val_len;
+
+       pr_debug("Parameter: %s ", name);
+       pr_debug("Value: %s\n", value);
+
+       return n;
+}
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Start of reading /proc entries.
+ * Return entry at pos,
+ *     or start_token to indicate print header line
+ *     or NULL if end of file
+ */
+static void *irlan_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       rcu_read_lock();
+       return seq_list_start_head(&irlans, *pos);
+}
+
+/* Return entry after v, and increment pos */
+static void *irlan_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       return seq_list_next(v, &irlans, pos);
+}
+
+/* End of reading /proc file */
+static void irlan_seq_stop(struct seq_file *seq, void *v)
+{
+       rcu_read_unlock();
+}
+
+
+/*
+ * Show one entry in /proc file.
+ */
+static int irlan_seq_show(struct seq_file *seq, void *v)
+{
+       if (v == &irlans)
+               seq_puts(seq, "IrLAN instances:\n");
+       else {
+               struct irlan_cb *self = list_entry(v, struct irlan_cb, dev_list);
+
+               IRDA_ASSERT(self != NULL, return -1;);
+               IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
+
+               seq_printf(seq,"ifname: %s,\n",
+                              self->dev->name);
+               seq_printf(seq,"client state: %s, ",
+                              irlan_state[ self->client.state]);
+               seq_printf(seq,"provider state: %s,\n",
+                              irlan_state[ self->provider.state]);
+               seq_printf(seq,"saddr: %#08x, ",
+                              self->saddr);
+               seq_printf(seq,"daddr: %#08x\n",
+                              self->daddr);
+               seq_printf(seq,"version: %d.%d,\n",
+                              self->version[1], self->version[0]);
+               seq_printf(seq,"access type: %s\n",
+                              irlan_access[self->client.access_type]);
+               seq_printf(seq,"media: %s\n",
+                              irlan_media[self->media]);
+
+               seq_printf(seq,"local filter:\n");
+               seq_printf(seq,"remote filter: ");
+               irlan_print_filter(seq, self->client.filter_type);
+               seq_printf(seq,"tx busy: %s\n",
+                              netif_queue_stopped(self->dev) ? "TRUE" : "FALSE");
+
+               seq_putc(seq,'\n');
+       }
+       return 0;
+}
+
+static const struct seq_operations irlan_seq_ops = {
+       .start = irlan_seq_start,
+       .next  = irlan_seq_next,
+       .stop  = irlan_seq_stop,
+       .show  = irlan_seq_show,
+};
+
+static int irlan_seq_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &irlan_seq_ops);
+}
+#endif
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("The Linux IrDA LAN protocol");
+MODULE_LICENSE("GPL");
+
+module_param(eth, bool, 0);
+MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)");
+module_param(access, int, 0);
+MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3");
+
+module_init(irlan_init);
+module_exit(irlan_cleanup);
+
diff --git a/drivers/staging/irda/net/irlan/irlan_eth.c b/drivers/staging/irda/net/irlan/irlan_eth.c
new file mode 100644 (file)
index 0000000..3be8528
--- /dev/null
@@ -0,0 +1,340 @@
+/*********************************************************************
+ *
+ * Filename:      irlan_eth.c
+ * Version:
+ * Description:
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Thu Oct 15 08:37:58 1998
+ * Modified at:   Tue Mar 21 09:06:41 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
+ *                slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
+ *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ *     Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <net/arp.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irlan_common.h>
+#include <net/irda/irlan_client.h>
+#include <net/irda/irlan_event.h>
+#include <net/irda/irlan_eth.h>
+
+static int  irlan_eth_open(struct net_device *dev);
+static int  irlan_eth_close(struct net_device *dev);
+static netdev_tx_t  irlan_eth_xmit(struct sk_buff *skb,
+                                        struct net_device *dev);
+static void irlan_eth_set_multicast_list(struct net_device *dev);
+
+static const struct net_device_ops irlan_eth_netdev_ops = {
+       .ndo_open               = irlan_eth_open,
+       .ndo_stop               = irlan_eth_close,
+       .ndo_start_xmit         = irlan_eth_xmit,
+       .ndo_set_rx_mode        = irlan_eth_set_multicast_list,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+/*
+ * Function irlan_eth_setup (dev)
+ *
+ *    The network device initialization function.
+ *
+ */
+static void irlan_eth_setup(struct net_device *dev)
+{
+       ether_setup(dev);
+
+       dev->netdev_ops         = &irlan_eth_netdev_ops;
+       dev->needs_free_netdev  = true;
+       dev->min_mtu            = 0;
+       dev->max_mtu            = ETH_MAX_MTU;
+
+       /*
+        * Lets do all queueing in IrTTP instead of this device driver.
+        * Queueing here as well can introduce some strange latency
+        * problems, which we will avoid by setting the queue size to 0.
+        */
+       /*
+        * The bugs in IrTTP and IrLAN that created this latency issue
+        * have now been fixed, and we can propagate flow control properly
+        * to the network layer. However, this requires a minimal queue of
+        * packets for the device.
+        * Without flow control, the Tx Queue is 14 (ttp) + 0 (dev) = 14
+        * With flow control, the Tx Queue is 7 (ttp) + 4 (dev) = 11
+        * See irlan_eth_flow_indication()...
+        * Note : this number was randomly selected and would need to
+        * be adjusted.
+        * Jean II */
+       dev->tx_queue_len = 4;
+}
+
+/*
+ * Function alloc_irlandev
+ *
+ *    Allocate network device and control block
+ *
+ */
+struct net_device *alloc_irlandev(const char *name)
+{
+       return alloc_netdev(sizeof(struct irlan_cb), name, NET_NAME_UNKNOWN,
+                           irlan_eth_setup);
+}
+
+/*
+ * Function irlan_eth_open (dev)
+ *
+ *    Network device has been opened by user
+ *
+ */
+static int irlan_eth_open(struct net_device *dev)
+{
+       struct irlan_cb *self = netdev_priv(dev);
+
+       /* Ready to play! */
+       netif_stop_queue(dev); /* Wait until data link is ready */
+
+       /* We are now open, so time to do some work */
+       self->disconnect_reason = 0;
+       irlan_client_wakeup(self, self->saddr, self->daddr);
+
+       /* Make sure we have a hardware address before we return,
+          so DHCP clients gets happy */
+       return wait_event_interruptible(self->open_wait,
+                                       !self->tsap_data->connected);
+}
+
+/*
+ * Function irlan_eth_close (dev)
+ *
+ *    Stop the ether network device, his function will usually be called by
+ *    ifconfig down. We should now disconnect the link, We start the
+ *    close timer, so that the instance will be removed if we are unable
+ *    to discover the remote device after the disconnect.
+ */
+static int irlan_eth_close(struct net_device *dev)
+{
+       struct irlan_cb *self = netdev_priv(dev);
+
+       /* Stop device */
+       netif_stop_queue(dev);
+
+       irlan_close_data_channel(self);
+       irlan_close_tsaps(self);
+
+       irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
+       irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
+
+       /* Remove frames queued on the control channel */
+       skb_queue_purge(&self->client.txq);
+
+       self->client.tx_busy = 0;
+
+       return 0;
+}
+
+/*
+ * Function irlan_eth_tx (skb)
+ *
+ *    Transmits ethernet frames over IrDA link.
+ *
+ */
+static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb,
+                                       struct net_device *dev)
+{
+       struct irlan_cb *self = netdev_priv(dev);
+       int ret;
+       unsigned int len;
+
+       /* skb headroom large enough to contain all IrDA-headers? */
+       if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) {
+               struct sk_buff *new_skb =
+                       skb_realloc_headroom(skb, self->max_header_size);
+
+               /*  We have to free the original skb anyway */
+               dev_kfree_skb(skb);
+
+               /* Did the realloc succeed? */
+               if (new_skb == NULL)
+                       return NETDEV_TX_OK;
+
+               /* Use the new skb instead */
+               skb = new_skb;
+       }
+
+       netif_trans_update(dev);
+
+       len = skb->len;
+       /* Now queue the packet in the transport layer */
+       if (self->use_udata)
+               ret = irttp_udata_request(self->tsap_data, skb);
+       else
+               ret = irttp_data_request(self->tsap_data, skb);
+
+       if (ret < 0) {
+               /*
+                * IrTTPs tx queue is full, so we just have to
+                * drop the frame! You might think that we should
+                * just return -1 and don't deallocate the frame,
+                * but that is dangerous since it's possible that
+                * we have replaced the original skb with a new
+                * one with larger headroom, and that would really
+                * confuse do_dev_queue_xmit() in dev.c! I have
+                * tried :-) DB
+                */
+               /* irttp_data_request already free the packet */
+               dev->stats.tx_dropped++;
+       } else {
+               dev->stats.tx_packets++;
+               dev->stats.tx_bytes += len;
+       }
+
+       return NETDEV_TX_OK;
+}
+
+/*
+ * Function irlan_eth_receive (handle, skb)
+ *
+ *    This function gets the data that is received on the data channel
+ *
+ */
+int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb)
+{
+       struct irlan_cb *self = instance;
+       struct net_device *dev = self->dev;
+
+       if (skb == NULL) {
+               dev->stats.rx_dropped++;
+               return 0;
+       }
+       if (skb->len < ETH_HLEN) {
+               pr_debug("%s() : IrLAN frame too short (%d)\n",
+                        __func__, skb->len);
+               dev->stats.rx_dropped++;
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       /*
+        * Adopt this frame! Important to set all these fields since they
+        * might have been previously set by the low level IrDA network
+        * device driver
+        */
+       skb->protocol = eth_type_trans(skb, dev); /* Remove eth header */
+
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += skb->len;
+
+       netif_rx(skb);   /* Eat it! */
+
+       return 0;
+}
+
+/*
+ * Function irlan_eth_flow (status)
+ *
+ *    Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by
+ *    controlling the queue stop/start.
+ *
+ * The IrDA link layer has the advantage to have flow control, and
+ * IrTTP now properly handles that. Flow controlling the higher layers
+ * prevent us to drop Tx packets in here (up to 15% for a TCP socket,
+ * more for UDP socket).
+ * Also, this allow us to reduce the overall transmit queue, which means
+ * less latency in case of mixed traffic.
+ * Jean II
+ */
+void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
+{
+       struct irlan_cb *self;
+       struct net_device *dev;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       dev = self->dev;
+
+       IRDA_ASSERT(dev != NULL, return;);
+
+       pr_debug("%s() : flow %s ; running %d\n", __func__,
+                flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START",
+                netif_running(dev));
+
+       switch (flow) {
+       case FLOW_STOP:
+               /* IrTTP is full, stop higher layers */
+               netif_stop_queue(dev);
+               break;
+       case FLOW_START:
+       default:
+               /* Tell upper layers that its time to transmit frames again */
+               /* Schedule network layer */
+               netif_wake_queue(dev);
+               break;
+       }
+}
+
+/*
+ * Function set_multicast_list (dev)
+ *
+ *    Configure the filtering of the device
+ *
+ */
+#define HW_MAX_ADDRS 4 /* Must query to get it! */
+static void irlan_eth_set_multicast_list(struct net_device *dev)
+{
+       struct irlan_cb *self = netdev_priv(dev);
+
+       /* Check if data channel has been connected yet */
+       if (self->client.state != IRLAN_DATA) {
+               pr_debug("%s(), delaying!\n", __func__);
+               return;
+       }
+
+       if (dev->flags & IFF_PROMISC) {
+               /* Enable promiscuous mode */
+               net_warn_ratelimited("Promiscuous mode not implemented by IrLAN!\n");
+       } else if ((dev->flags & IFF_ALLMULTI) ||
+                netdev_mc_count(dev) > HW_MAX_ADDRS) {
+               /* Disable promiscuous mode, use normal mode. */
+               pr_debug("%s(), Setting multicast filter\n", __func__);
+               /* hardware_set_filter(NULL); */
+
+               irlan_set_multicast_filter(self, TRUE);
+       } else if (!netdev_mc_empty(dev)) {
+               pr_debug("%s(), Setting multicast filter\n", __func__);
+               /* Walk the address list, and load the filter */
+               /* hardware_set_filter(dev->mc_list); */
+
+               irlan_set_multicast_filter(self, TRUE);
+       } else {
+               pr_debug("%s(), Clearing multicast filter\n", __func__);
+               irlan_set_multicast_filter(self, FALSE);
+       }
+
+       if (dev->flags & IFF_BROADCAST)
+               irlan_set_broadcast_filter(self, TRUE);
+       else
+               irlan_set_broadcast_filter(self, FALSE);
+}
diff --git a/drivers/staging/irda/net/irlan/irlan_event.c b/drivers/staging/irda/net/irlan/irlan_event.c
new file mode 100644 (file)
index 0000000..9a1cc11
--- /dev/null
@@ -0,0 +1,60 @@
+/*********************************************************************
+ *
+ * Filename:      irlan_event.c
+ * Version:
+ * Description:
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Tue Oct 20 09:10:16 1998
+ * Modified at:   Sat Oct 30 12:59:01 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <net/irda/irlan_event.h>
+
+const char * const irlan_state[] = {
+       "IRLAN_IDLE",
+       "IRLAN_QUERY",
+       "IRLAN_CONN",
+       "IRLAN_INFO",
+       "IRLAN_MEDIA",
+       "IRLAN_OPEN",
+       "IRLAN_WAIT",
+       "IRLAN_ARB",
+       "IRLAN_DATA",
+       "IRLAN_CLOSE",
+       "IRLAN_SYNC",
+};
+
+void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state)
+{
+       pr_debug("%s(), %s\n", __func__ , irlan_state[state]);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       self->client.state = state;
+}
+
+void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state)
+{
+       pr_debug("%s(), %s\n", __func__ , irlan_state[state]);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       self->provider.state = state;
+}
+
diff --git a/drivers/staging/irda/net/irlan/irlan_filter.c b/drivers/staging/irda/net/irlan/irlan_filter.c
new file mode 100644 (file)
index 0000000..e755e90
--- /dev/null
@@ -0,0 +1,240 @@
+/*********************************************************************
+ *
+ * Filename:      irlan_filter.c
+ * Version:
+ * Description:
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Fri Jan 29 11:16:38 1999
+ * Modified at:   Sat Oct 30 12:58:45 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/skbuff.h>
+#include <linux/random.h>
+#include <linux/seq_file.h>
+
+#include <net/irda/irlan_common.h>
+#include <net/irda/irlan_filter.h>
+
+/*
+ * Function irlan_filter_request (self, skb)
+ *
+ *    Handle filter request from client peer device
+ *
+ */
+void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       if ((self->provider.filter_type == IRLAN_DIRECTED) &&
+           (self->provider.filter_operation == DYNAMIC))
+       {
+               pr_debug("Giving peer a dynamic Ethernet address\n");
+               self->provider.mac_address[0] = 0x40;
+               self->provider.mac_address[1] = 0x00;
+               self->provider.mac_address[2] = 0x00;
+               self->provider.mac_address[3] = 0x00;
+
+               /* Use arbitration value to generate MAC address */
+               if (self->provider.access_type == ACCESS_PEER) {
+                       self->provider.mac_address[4] =
+                               self->provider.send_arb_val & 0xff;
+                       self->provider.mac_address[5] =
+                               (self->provider.send_arb_val >> 8) & 0xff;
+               } else {
+                       /* Just generate something for now */
+                       get_random_bytes(self->provider.mac_address+4, 1);
+                       get_random_bytes(self->provider.mac_address+5, 1);
+               }
+
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x03;
+               irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
+               irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001);
+               irlan_insert_array_param(skb, "FILTER_ENTRY",
+                                        self->provider.mac_address, 6);
+               return;
+       }
+
+       if ((self->provider.filter_type == IRLAN_DIRECTED) &&
+           (self->provider.filter_mode == FILTER))
+       {
+               pr_debug("Directed filter on\n");
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x00;
+               return;
+       }
+       if ((self->provider.filter_type == IRLAN_DIRECTED) &&
+           (self->provider.filter_mode == NONE))
+       {
+               pr_debug("Directed filter off\n");
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x00;
+               return;
+       }
+
+       if ((self->provider.filter_type == IRLAN_BROADCAST) &&
+           (self->provider.filter_mode == FILTER))
+       {
+               pr_debug("Broadcast filter on\n");
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x00;
+               return;
+       }
+       if ((self->provider.filter_type == IRLAN_BROADCAST) &&
+           (self->provider.filter_mode == NONE))
+       {
+               pr_debug("Broadcast filter off\n");
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x00;
+               return;
+       }
+       if ((self->provider.filter_type == IRLAN_MULTICAST) &&
+           (self->provider.filter_mode == FILTER))
+       {
+               pr_debug("Multicast filter on\n");
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x00;
+               return;
+       }
+       if ((self->provider.filter_type == IRLAN_MULTICAST) &&
+           (self->provider.filter_mode == NONE))
+       {
+               pr_debug("Multicast filter off\n");
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x00;
+               return;
+       }
+       if ((self->provider.filter_type == IRLAN_MULTICAST) &&
+           (self->provider.filter_operation == GET))
+       {
+               pr_debug("Multicast filter get\n");
+               skb->data[0] = 0x00; /* Success? */
+               skb->data[1] = 0x02;
+               irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
+               irlan_insert_short_param(skb, "MAX_ENTRY", 16);
+               return;
+       }
+       skb->data[0] = 0x00; /* Command not supported */
+       skb->data[1] = 0x00;
+
+       pr_debug("Not implemented!\n");
+}
+
+/*
+ * Function check_request_param (self, param, value)
+ *
+ *    Check parameters in request from peer device
+ *
+ */
+void irlan_check_command_param(struct irlan_cb *self, char *param, char *value)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       pr_debug("%s, %s\n", param, value);
+
+       /*
+        *  This is experimental!! DB.
+        */
+        if (strcmp(param, "MODE") == 0) {
+               self->use_udata = TRUE;
+               return;
+       }
+
+       /*
+        *  FILTER_TYPE
+        */
+       if (strcmp(param, "FILTER_TYPE") == 0) {
+               if (strcmp(value, "DIRECTED") == 0) {
+                       self->provider.filter_type = IRLAN_DIRECTED;
+                       return;
+               }
+               if (strcmp(value, "MULTICAST") == 0) {
+                       self->provider.filter_type = IRLAN_MULTICAST;
+                       return;
+               }
+               if (strcmp(value, "BROADCAST") == 0) {
+                       self->provider.filter_type = IRLAN_BROADCAST;
+                       return;
+               }
+       }
+       /*
+        *  FILTER_MODE
+        */
+       if (strcmp(param, "FILTER_MODE") == 0) {
+               if (strcmp(value, "ALL") == 0) {
+                       self->provider.filter_mode = ALL;
+                       return;
+               }
+               if (strcmp(value, "FILTER") == 0) {
+                       self->provider.filter_mode = FILTER;
+                       return;
+               }
+               if (strcmp(value, "NONE") == 0) {
+                       self->provider.filter_mode = FILTER;
+                       return;
+               }
+       }
+       /*
+        *  FILTER_OPERATION
+        */
+       if (strcmp(param, "FILTER_OPERATION") == 0) {
+               if (strcmp(value, "DYNAMIC") == 0) {
+                       self->provider.filter_operation = DYNAMIC;
+                       return;
+               }
+               if (strcmp(value, "GET") == 0) {
+                       self->provider.filter_operation = GET;
+                       return;
+               }
+       }
+}
+
+/*
+ * Function irlan_print_filter (filter_type, buf)
+ *
+ *    Print status of filter. Used by /proc file system
+ *
+ */
+#ifdef CONFIG_PROC_FS
+#define MASK2STR(m,s)  { .mask = m, .str = s }
+
+void irlan_print_filter(struct seq_file *seq, int filter_type)
+{
+       static struct {
+               int mask;
+               const char *str;
+       } filter_mask2str[] = {
+               MASK2STR(IRLAN_DIRECTED,        "DIRECTED"),
+               MASK2STR(IRLAN_FUNCTIONAL,      "FUNCTIONAL"),
+               MASK2STR(IRLAN_GROUP,           "GROUP"),
+               MASK2STR(IRLAN_MAC_FRAME,       "MAC_FRAME"),
+               MASK2STR(IRLAN_MULTICAST,       "MULTICAST"),
+               MASK2STR(IRLAN_BROADCAST,       "BROADCAST"),
+               MASK2STR(IRLAN_IPX_SOCKET,      "IPX_SOCKET"),
+               MASK2STR(0,                     NULL)
+       }, *p;
+
+       for (p = filter_mask2str; p->str; p++) {
+               if (filter_type & p->mask)
+                       seq_printf(seq, "%s ", p->str);
+       }
+       seq_putc(seq, '\n');
+}
+#undef MASK2STR
+#endif
diff --git a/drivers/staging/irda/net/irlan/irlan_provider.c b/drivers/staging/irda/net/irlan/irlan_provider.c
new file mode 100644 (file)
index 0000000..15c292c
--- /dev/null
@@ -0,0 +1,408 @@
+/*********************************************************************
+ *
+ * Filename:      irlan_provider.c
+ * Version:       0.9
+ * Description:   IrDA LAN Access Protocol Implementation
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Aug 31 20:14:37 1997
+ * Modified at:   Sat Oct 30 12:52:10 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
+ *                slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
+ *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/random.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irias_object.h>
+#include <net/irda/iriap.h>
+#include <net/irda/timer.h>
+
+#include <net/irda/irlan_common.h>
+#include <net/irda/irlan_eth.h>
+#include <net/irda/irlan_event.h>
+#include <net/irda/irlan_provider.h>
+#include <net/irda/irlan_filter.h>
+#include <net/irda/irlan_client.h>
+
+static void irlan_provider_connect_indication(void *instance, void *sap,
+                                             struct qos_info *qos,
+                                             __u32 max_sdu_size,
+                                             __u8 max_header_size,
+                                             struct sk_buff *skb);
+
+/*
+ * Function irlan_provider_control_data_indication (handle, skb)
+ *
+ *    This function gets the data that is received on the control channel
+ *
+ */
+static int irlan_provider_data_indication(void *instance, void *sap,
+                                         struct sk_buff *skb)
+{
+       struct irlan_cb *self;
+       __u8 code;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
+
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       code = skb->data[0];
+       switch(code) {
+       case CMD_GET_PROVIDER_INFO:
+               pr_debug("Got GET_PROVIDER_INFO command!\n");
+               irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb);
+               break;
+
+       case CMD_GET_MEDIA_CHAR:
+               pr_debug("Got GET_MEDIA_CHAR command!\n");
+               irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb);
+               break;
+       case CMD_OPEN_DATA_CHANNEL:
+               pr_debug("Got OPEN_DATA_CHANNEL command!\n");
+               irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb);
+               break;
+       case CMD_FILTER_OPERATION:
+               pr_debug("Got FILTER_OPERATION command!\n");
+               irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb);
+               break;
+       case CMD_RECONNECT_DATA_CHAN:
+               pr_debug("%s(), Got RECONNECT_DATA_CHAN command\n", __func__);
+               pr_debug("%s(), NOT IMPLEMENTED\n", __func__);
+               break;
+       case CMD_CLOSE_DATA_CHAN:
+               pr_debug("Got CLOSE_DATA_CHAN command!\n");
+               pr_debug("%s(), NOT IMPLEMENTED\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown command!\n", __func__);
+               break;
+       }
+       return 0;
+}
+
+/*
+ * Function irlan_provider_connect_indication (handle, skb, priv)
+ *
+ *    Got connection from peer IrLAN client
+ *
+ */
+static void irlan_provider_connect_indication(void *instance, void *sap,
+                                             struct qos_info *qos,
+                                             __u32 max_sdu_size,
+                                             __u8 max_header_size,
+                                             struct sk_buff *skb)
+{
+       struct irlan_cb *self;
+       struct tsap_cb *tsap;
+
+       self = instance;
+       tsap = sap;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;);
+       IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;);
+
+       self->provider.max_sdu_size = max_sdu_size;
+       self->provider.max_header_size = max_header_size;
+
+       irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL);
+
+       /*
+        * If we are in peer mode, the client may not have got the discovery
+        * indication it needs to make progress. If the client is still in
+        * IDLE state, we must kick it.
+        */
+       if ((self->provider.access_type == ACCESS_PEER) &&
+           (self->client.state == IRLAN_IDLE))
+       {
+               irlan_client_wakeup(self, self->saddr, self->daddr);
+       }
+}
+
+/*
+ * Function irlan_provider_connect_response (handle)
+ *
+ *    Accept incoming connection
+ *
+ */
+void irlan_provider_connect_response(struct irlan_cb *self,
+                                    struct tsap_cb *tsap)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       /* Just accept */
+       irttp_connect_response(tsap, IRLAN_MTU, NULL);
+}
+
+static void irlan_provider_disconnect_indication(void *instance, void *sap,
+                                                LM_REASON reason,
+                                                struct sk_buff *userdata)
+{
+       struct irlan_cb *self;
+       struct tsap_cb *tsap;
+
+       pr_debug("%s(), reason=%d\n", __func__ , reason);
+
+       self = instance;
+       tsap = sap;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+       IRDA_ASSERT(tsap != NULL, return;);
+       IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
+
+       IRDA_ASSERT(tsap == self->provider.tsap_ctrl, return;);
+
+       irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
+}
+
+/*
+ * Function irlan_parse_open_data_cmd (self, skb)
+ *
+ *
+ *
+ */
+int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb)
+{
+       int ret;
+
+       ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb);
+
+       /* Open data channel */
+       irlan_open_data_tsap(self);
+
+       return ret;
+}
+
+/*
+ * Function parse_command (skb)
+ *
+ *    Extract all parameters from received buffer, then feed them to
+ *    check_params for parsing
+ *
+ */
+int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
+                                struct sk_buff *skb)
+{
+       __u8 *frame;
+       __u8 *ptr;
+       int count;
+       __u16 val_len;
+       int i;
+       char *name;
+       char *value;
+       int ret = RSP_SUCCESS;
+
+       IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;);
+
+       pr_debug("%s(), skb->len=%d\n", __func__ , (int)skb->len);
+
+       IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;);
+
+       if (!skb)
+               return -RSP_PROTOCOL_ERROR;
+
+       frame = skb->data;
+
+       name = kmalloc(255, GFP_ATOMIC);
+       if (!name)
+               return -RSP_INSUFFICIENT_RESOURCES;
+       value = kmalloc(1016, GFP_ATOMIC);
+       if (!value) {
+               kfree(name);
+               return -RSP_INSUFFICIENT_RESOURCES;
+       }
+
+       /* How many parameters? */
+       count = frame[1];
+
+       pr_debug("Got %d parameters\n", count);
+
+       ptr = frame+2;
+
+       /* For all parameters */
+       for (i=0; i<count;i++) {
+               ret = irlan_extract_param(ptr, name, value, &val_len);
+               if (ret < 0) {
+                       pr_debug("%s(), IrLAN, Error!\n", __func__);
+                       break;
+               }
+               ptr+=ret;
+               ret = RSP_SUCCESS;
+               irlan_check_command_param(self, name, value);
+       }
+       /* Cleanup */
+       kfree(name);
+       kfree(value);
+
+       return ret;
+}
+
+/*
+ * Function irlan_provider_send_reply (self, info)
+ *
+ *    Send reply to query to peer IrLAN layer
+ *
+ */
+void irlan_provider_send_reply(struct irlan_cb *self, int command,
+                              int ret_code)
+{
+       struct sk_buff *skb;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
+                       /* Bigger param length comes from CMD_GET_MEDIA_CHAR */
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") +
+                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") +
+                       IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "HOSTED"),
+                       GFP_ATOMIC);
+
+       if (!skb)
+               return;
+
+       /* Reserve space for TTP, LMP, and LAP header */
+       skb_reserve(skb, self->provider.max_header_size);
+       skb_put(skb, 2);
+
+       switch (command) {
+       case CMD_GET_PROVIDER_INFO:
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x02; /* 2 parameters */
+               switch (self->media) {
+               case MEDIA_802_3:
+                       irlan_insert_string_param(skb, "MEDIA", "802.3");
+                       break;
+               case MEDIA_802_5:
+                       irlan_insert_string_param(skb, "MEDIA", "802.5");
+                       break;
+               default:
+                       pr_debug("%s(), unknown media type!\n", __func__);
+                       break;
+               }
+               irlan_insert_short_param(skb, "IRLAN_VER", 0x0101);
+               break;
+
+       case CMD_GET_MEDIA_CHAR:
+               skb->data[0] = 0x00; /* Success */
+               skb->data[1] = 0x05; /* 5 parameters */
+               irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
+               irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST");
+               irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST");
+
+               switch (self->provider.access_type) {
+               case ACCESS_DIRECT:
+                       irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT");
+                       break;
+               case ACCESS_PEER:
+                       irlan_insert_string_param(skb, "ACCESS_TYPE", "PEER");
+                       break;
+               case ACCESS_HOSTED:
+                       irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED");
+                       break;
+               default:
+                       pr_debug("%s(), Unknown access type\n", __func__);
+                       break;
+               }
+               irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee);
+               break;
+       case CMD_OPEN_DATA_CHANNEL:
+               skb->data[0] = 0x00; /* Success */
+               if (self->provider.send_arb_val) {
+                       skb->data[1] = 0x03; /* 3 parameters */
+                       irlan_insert_short_param(skb, "CON_ARB",
+                                                self->provider.send_arb_val);
+               } else
+                       skb->data[1] = 0x02; /* 2 parameters */
+               irlan_insert_byte_param(skb, "DATA_CHAN", self->stsap_sel_data);
+               irlan_insert_string_param(skb, "RECONNECT_KEY", "LINUX RULES!");
+               break;
+       case CMD_FILTER_OPERATION:
+               irlan_filter_request(self, skb);
+               break;
+       default:
+               pr_debug("%s(), Unknown command!\n", __func__);
+               break;
+       }
+
+       irttp_data_request(self->provider.tsap_ctrl, skb);
+}
+
+/*
+ * Function irlan_provider_register(void)
+ *
+ *    Register provider support so we can accept incoming connections.
+ *
+ */
+int irlan_provider_open_ctrl_tsap(struct irlan_cb *self)
+{
+       struct tsap_cb *tsap;
+       notify_t notify;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
+
+       /* Check if already open */
+       if (self->provider.tsap_ctrl)
+               return -1;
+
+       /*
+        *  First register well known control TSAP
+        */
+       irda_notify_init(&notify);
+       notify.data_indication       = irlan_provider_data_indication;
+       notify.connect_indication    = irlan_provider_connect_indication;
+       notify.disconnect_indication = irlan_provider_disconnect_indication;
+       notify.instance = self;
+       strlcpy(notify.name, "IrLAN ctrl (p)", sizeof(notify.name));
+
+       tsap = irttp_open_tsap(LSAP_ANY, 1, &notify);
+       if (!tsap) {
+               pr_debug("%s(), Got no tsap!\n", __func__);
+               return -1;
+       }
+       self->provider.tsap_ctrl = tsap;
+
+       /* Register with LM-IAS */
+       irlan_ias_register(self, tsap->stsap_sel);
+
+       return 0;
+}
+
diff --git a/drivers/staging/irda/net/irlan/irlan_provider_event.c b/drivers/staging/irda/net/irlan/irlan_provider_event.c
new file mode 100644 (file)
index 0000000..9c4f7f5
--- /dev/null
@@ -0,0 +1,233 @@
+/*********************************************************************
+ *
+ * Filename:      irlan_provider_event.c
+ * Version:       0.9
+ * Description:   IrLAN provider state machine)
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Aug 31 20:14:37 1997
+ * Modified at:   Sat Oct 30 12:52:41 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <net/irda/irda.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irttp.h>
+
+#include <net/irda/irlan_provider.h>
+#include <net/irda/irlan_event.h>
+
+static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
+                                    struct sk_buff *skb);
+static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
+                                    struct sk_buff *skb);
+static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
+                                    struct sk_buff *skb);
+static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
+                                    struct sk_buff *skb);
+
+static int (*state[])(struct irlan_cb *self, IRLAN_EVENT event,
+                     struct sk_buff *skb) =
+{
+       irlan_provider_state_idle,
+       NULL, /* Query */
+       NULL, /* Info */
+       irlan_provider_state_info,
+       NULL, /* Media */
+       irlan_provider_state_open,
+       NULL, /* Wait */
+       NULL, /* Arb */
+       irlan_provider_state_data,
+       NULL, /* Close */
+       NULL, /* Sync */
+};
+
+void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event,
+                            struct sk_buff *skb)
+{
+       IRDA_ASSERT(*state[ self->provider.state] != NULL, return;);
+
+       (*state[self->provider.state]) (self, event, skb);
+}
+
+/*
+ * Function irlan_provider_state_idle (event, skb, info)
+ *
+ *    IDLE, We are waiting for an indication that there is a provider
+ *    available.
+ */
+static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
+                                    struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch(event) {
+       case IRLAN_CONNECT_INDICATION:
+            irlan_provider_connect_response( self, self->provider.tsap_ctrl);
+            irlan_next_provider_state( self, IRLAN_INFO);
+            break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_provider_state_info (self, event, skb, info)
+ *
+ *    INFO, We have issued a GetInfo command and is awaiting a reply.
+ */
+static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
+                                    struct sk_buff *skb)
+{
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch(event) {
+       case IRLAN_GET_INFO_CMD:
+               /* Be sure to use 802.3 in case of peer mode */
+               if (self->provider.access_type == ACCESS_PEER) {
+                       self->media = MEDIA_802_3;
+
+                       /* Check if client has started yet */
+                       if (self->client.state == IRLAN_IDLE) {
+                               /* This should get the client going */
+                               irlmp_discovery_request(8);
+                       }
+               }
+
+               irlan_provider_send_reply(self, CMD_GET_PROVIDER_INFO,
+                                         RSP_SUCCESS);
+               /* Keep state */
+               break;
+       case IRLAN_GET_MEDIA_CMD:
+               irlan_provider_send_reply(self, CMD_GET_MEDIA_CHAR,
+                                         RSP_SUCCESS);
+               /* Keep state */
+               break;
+       case IRLAN_OPEN_DATA_CMD:
+               ret = irlan_parse_open_data_cmd(self, skb);
+               if (self->provider.access_type == ACCESS_PEER) {
+                       /* FIXME: make use of random functions! */
+                       self->provider.send_arb_val = (jiffies & 0xffff);
+               }
+               irlan_provider_send_reply(self, CMD_OPEN_DATA_CHANNEL, ret);
+
+               if (ret == RSP_SUCCESS) {
+                       irlan_next_provider_state(self, IRLAN_OPEN);
+
+                       /* Signal client that we are now open */
+                       irlan_do_client_event(self, IRLAN_PROVIDER_SIGNAL, NULL);
+               }
+               break;
+       case IRLAN_LMP_DISCONNECT:  /* FALLTHROUGH */
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_provider_state(self, IRLAN_IDLE);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_provider_state_open (self, event, skb, info)
+ *
+ *    OPEN, The client has issued a OpenData command and is awaiting a
+ *    reply
+ *
+ */
+static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
+                                    struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+
+       switch(event) {
+       case IRLAN_FILTER_CONFIG_CMD:
+               irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
+               irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
+                                         RSP_SUCCESS);
+               /* Keep state */
+               break;
+       case IRLAN_DATA_CONNECT_INDICATION:
+               irlan_next_provider_state(self, IRLAN_DATA);
+               irlan_provider_connect_response(self, self->tsap_data);
+               break;
+       case IRLAN_LMP_DISCONNECT:  /* FALLTHROUGH */
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_provider_state(self, IRLAN_IDLE);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irlan_provider_state_data (self, event, skb, info)
+ *
+ *    DATA, The data channel is connected, allowing data transfers between
+ *    the local and remote machines.
+ *
+ */
+static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
+                                    struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
+
+       switch(event) {
+       case IRLAN_FILTER_CONFIG_CMD:
+               irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
+               irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
+                                         RSP_SUCCESS);
+               break;
+       case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
+       case IRLAN_LAP_DISCONNECT:
+               irlan_next_provider_state(self, IRLAN_IDLE);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__ , event);
+               break;
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/drivers/staging/irda/net/irlap.c b/drivers/staging/irda/net/irlap.c
new file mode 100644 (file)
index 0000000..1cde711
--- /dev/null
@@ -0,0 +1,1207 @@
+/*********************************************************************
+ *
+ * Filename:      irlap.c
+ * Version:       1.0
+ * Description:   IrLAP implementation for Linux
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Mon Aug  4 20:40:53 1997
+ * Modified at:   Tue Dec 14 09:26:44 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/random.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/irqueue.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irlmp_frame.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/irlap.h>
+#include <net/irda/timer.h>
+#include <net/irda/qos.h>
+
+static hashbin_t *irlap = NULL;
+int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ;
+
+/* This is the delay of missed pf period before generating an event
+ * to the application. The spec mandate 3 seconds, but in some cases
+ * it's way too long. - Jean II */
+int sysctl_warn_noreply_time = 3;
+
+extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
+static void __irlap_close(struct irlap_cb *self);
+static void irlap_init_qos_capabilities(struct irlap_cb *self,
+                                       struct qos_info *qos_user);
+
+static const char *const lap_reasons[] __maybe_unused = {
+       "ERROR, NOT USED",
+       "LAP_DISC_INDICATION",
+       "LAP_NO_RESPONSE",
+       "LAP_RESET_INDICATION",
+       "LAP_FOUND_NONE",
+       "LAP_MEDIA_BUSY",
+       "LAP_PRIMARY_CONFLICT",
+       "ERROR, NOT USED",
+};
+
+int __init irlap_init(void)
+{
+       /* Check if the compiler did its job properly.
+        * May happen on some ARM configuration, check with Russell King. */
+       IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;);
+       IRDA_ASSERT(sizeof(struct test_frame) == 10, ;);
+       IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;);
+       IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;);
+
+       /* Allocate master array */
+       irlap = hashbin_new(HB_LOCK);
+       if (irlap == NULL) {
+               net_err_ratelimited("%s: can't allocate irlap hashbin!\n",
+                                   __func__);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+void irlap_cleanup(void)
+{
+       IRDA_ASSERT(irlap != NULL, return;);
+
+       hashbin_delete(irlap, (FREE_FUNC) __irlap_close);
+}
+
+/*
+ * Function irlap_open (driver)
+ *
+ *    Initialize IrLAP layer
+ *
+ */
+struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
+                           const char *hw_name)
+{
+       struct irlap_cb *self;
+
+       /* Initialize the irlap structure. */
+       self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL);
+       if (self == NULL)
+               return NULL;
+
+       self->magic = LAP_MAGIC;
+
+       /* Make a binding between the layers */
+       self->netdev = dev;
+       self->qos_dev = qos;
+       /* Copy hardware name */
+       if(hw_name != NULL) {
+               strlcpy(self->hw_name, hw_name, sizeof(self->hw_name));
+       } else {
+               self->hw_name[0] = '\0';
+       }
+
+       /* FIXME: should we get our own field? */
+       dev->atalk_ptr = self;
+
+       self->state = LAP_OFFLINE;
+
+       /* Initialize transmit queue */
+       skb_queue_head_init(&self->txq);
+       skb_queue_head_init(&self->txq_ultra);
+       skb_queue_head_init(&self->wx_list);
+
+       /* My unique IrLAP device address! */
+       /* We don't want the broadcast address, neither the NULL address
+        * (most often used to signify "invalid"), and we don't want an
+        * address already in use (otherwise connect won't be able
+        * to select the proper link). - Jean II */
+       do {
+               get_random_bytes(&self->saddr, sizeof(self->saddr));
+       } while ((self->saddr == 0x0) || (self->saddr == BROADCAST) ||
+                (hashbin_lock_find(irlap, self->saddr, NULL)) );
+       /* Copy to the driver */
+       memcpy(dev->dev_addr, &self->saddr, 4);
+
+       init_timer(&self->slot_timer);
+       init_timer(&self->query_timer);
+       init_timer(&self->discovery_timer);
+       init_timer(&self->final_timer);
+       init_timer(&self->poll_timer);
+       init_timer(&self->wd_timer);
+       init_timer(&self->backoff_timer);
+       init_timer(&self->media_busy_timer);
+
+       irlap_apply_default_connection_parameters(self);
+
+       self->N3 = 3; /* # connections attempts to try before giving up */
+
+       self->state = LAP_NDM;
+
+       hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL);
+
+       irlmp_register_link(self, self->saddr, &self->notify);
+
+       return self;
+}
+EXPORT_SYMBOL(irlap_open);
+
+/*
+ * Function __irlap_close (self)
+ *
+ *    Remove IrLAP and all allocated memory. Stop any pending timers.
+ *
+ */
+static void __irlap_close(struct irlap_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* Stop timers */
+       del_timer(&self->slot_timer);
+       del_timer(&self->query_timer);
+       del_timer(&self->discovery_timer);
+       del_timer(&self->final_timer);
+       del_timer(&self->poll_timer);
+       del_timer(&self->wd_timer);
+       del_timer(&self->backoff_timer);
+       del_timer(&self->media_busy_timer);
+
+       irlap_flush_all_queues(self);
+
+       self->magic = 0;
+
+       kfree(self);
+}
+
+/*
+ * Function irlap_close (self)
+ *
+ *    Remove IrLAP instance
+ *
+ */
+void irlap_close(struct irlap_cb *self)
+{
+       struct irlap_cb *lap;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* We used to send a LAP_DISC_INDICATION here, but this was
+        * racy. This has been move within irlmp_unregister_link()
+        * itself. Jean II */
+
+       /* Kill the LAP and all LSAPs on top of it */
+       irlmp_unregister_link(self->saddr);
+       self->notify.instance = NULL;
+
+       /* Be sure that we manage to remove ourself from the hash */
+       lap = hashbin_remove(irlap, self->saddr, NULL);
+       if (!lap) {
+               pr_debug("%s(), Didn't find myself!\n", __func__);
+               return;
+       }
+       __irlap_close(lap);
+}
+EXPORT_SYMBOL(irlap_close);
+
+/*
+ * Function irlap_connect_indication (self, skb)
+ *
+ *    Another device is attempting to make a connection
+ *
+ */
+void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       irlap_init_qos_capabilities(self, NULL); /* No user QoS! */
+
+       irlmp_link_connect_indication(self->notify.instance, self->saddr,
+                                     self->daddr, &self->qos_tx, skb);
+}
+
+/*
+ * Function irlap_connect_response (self, skb)
+ *
+ *    Service user has accepted incoming connection
+ *
+ */
+void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata)
+{
+       irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL);
+}
+
+/*
+ * Function irlap_connect_request (self, daddr, qos_user, sniff)
+ *
+ *    Request connection with another device, sniffing is not implemented
+ *    yet.
+ *
+ */
+void irlap_connect_request(struct irlap_cb *self, __u32 daddr,
+                          struct qos_info *qos_user, int sniff)
+{
+       pr_debug("%s(), daddr=0x%08x\n", __func__, daddr);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       self->daddr = daddr;
+
+       /*
+        *  If the service user specifies QoS values for this connection,
+        *  then use them
+        */
+       irlap_init_qos_capabilities(self, qos_user);
+
+       if ((self->state == LAP_NDM) && !self->media_busy)
+               irlap_do_event(self, CONNECT_REQUEST, NULL, NULL);
+       else
+               self->connect_pending = TRUE;
+}
+
+/*
+ * Function irlap_connect_confirm (self, skb)
+ *
+ *    Connection request has been accepted
+ *
+ */
+void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb);
+}
+
+/*
+ * Function irlap_data_indication (self, skb)
+ *
+ *    Received data frames from IR-port, so we just pass them up to
+ *    IrLMP for further processing
+ *
+ */
+void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb,
+                          int unreliable)
+{
+       /* Hide LAP header from IrLMP layer */
+       skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
+
+       irlmp_link_data_indication(self->notify.instance, skb, unreliable);
+}
+
+
+/*
+ * Function irlap_data_request (self, skb)
+ *
+ *    Queue data for transmission, must wait until XMIT state
+ *
+ */
+void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb,
+                       int unreliable)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER),
+                   return;);
+       skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
+
+       /*
+        *  Must set frame format now so that the rest of the code knows
+        *  if its dealing with an I or an UI frame
+        */
+       if (unreliable)
+               skb->data[1] = UI_FRAME;
+       else
+               skb->data[1] = I_FRAME;
+
+       /* Don't forget to refcount it - see irlmp_connect_request(). */
+       skb_get(skb);
+
+       /* Add at the end of the queue (keep ordering) - Jean II */
+       skb_queue_tail(&self->txq, skb);
+
+       /*
+        *  Send event if this frame only if we are in the right state
+        *  FIXME: udata should be sent first! (skb_queue_head?)
+        */
+       if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) {
+               /* If we are not already processing the Tx queue, trigger
+                * transmission immediately - Jean II */
+               if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy))
+                       irlap_do_event(self, DATA_REQUEST, skb, NULL);
+               /* Otherwise, the packets will be sent normally at the
+                * next pf-poll - Jean II */
+       }
+}
+
+/*
+ * Function irlap_unitdata_request (self, skb)
+ *
+ *    Send Ultra data. This is data that must be sent outside any connection
+ *
+ */
+#ifdef CONFIG_IRDA_ULTRA
+void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER),
+              return;);
+       skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
+
+       skb->data[0] = CBROADCAST;
+       skb->data[1] = UI_FRAME;
+
+       /* Don't need to refcount, see irlmp_connless_data_request() */
+
+       skb_queue_tail(&self->txq_ultra, skb);
+
+       irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);
+}
+#endif /*CONFIG_IRDA_ULTRA */
+
+/*
+ * Function irlap_udata_indication (self, skb)
+ *
+ *    Receive Ultra data. This is data that is received outside any connection
+ *
+ */
+#ifdef CONFIG_IRDA_ULTRA
+void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       /* Hide LAP header from IrLMP layer */
+       skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
+
+       irlmp_link_unitdata_indication(self->notify.instance, skb);
+}
+#endif /* CONFIG_IRDA_ULTRA */
+
+/*
+ * Function irlap_disconnect_request (void)
+ *
+ *    Request to disconnect connection by service user
+ */
+void irlap_disconnect_request(struct irlap_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* Don't disconnect until all data frames are successfully sent */
+       if (!skb_queue_empty(&self->txq)) {
+               self->disconnect_pending = TRUE;
+               return;
+       }
+
+       /* Check if we are in the right state for disconnecting */
+       switch (self->state) {
+       case LAP_XMIT_P:        /* FALLTHROUGH */
+       case LAP_XMIT_S:        /* FALLTHROUGH */
+       case LAP_CONN:          /* FALLTHROUGH */
+       case LAP_RESET_WAIT:    /* FALLTHROUGH */
+       case LAP_RESET_CHECK:
+               irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL);
+               break;
+       default:
+               pr_debug("%s(), disconnect pending!\n", __func__);
+               self->disconnect_pending = TRUE;
+               break;
+       }
+}
+
+/*
+ * Function irlap_disconnect_indication (void)
+ *
+ *    Disconnect request from other device
+ *
+ */
+void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason)
+{
+       pr_debug("%s(), reason=%s\n", __func__, lap_reasons[reason]);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* Flush queues */
+       irlap_flush_all_queues(self);
+
+       switch (reason) {
+       case LAP_RESET_INDICATION:
+               pr_debug("%s(), Sending reset request!\n", __func__);
+               irlap_do_event(self, RESET_REQUEST, NULL, NULL);
+               break;
+       case LAP_NO_RESPONSE:      /* FALLTHROUGH */
+       case LAP_DISC_INDICATION:  /* FALLTHROUGH */
+       case LAP_FOUND_NONE:       /* FALLTHROUGH */
+       case LAP_MEDIA_BUSY:
+               irlmp_link_disconnect_indication(self->notify.instance, self,
+                                                reason, NULL);
+               break;
+       default:
+               net_err_ratelimited("%s: Unknown reason %d\n",
+                                   __func__, reason);
+       }
+}
+
+/*
+ * Function irlap_discovery_request (gen_addr_bit)
+ *
+ *    Start one single discovery operation.
+ *
+ */
+void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
+{
+       struct irlap_info info;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+       IRDA_ASSERT(discovery != NULL, return;);
+
+       pr_debug("%s(), nslots = %d\n", __func__, discovery->nslots);
+
+       IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) ||
+                   (discovery->nslots == 8) || (discovery->nslots == 16),
+                   return;);
+
+       /* Discovery is only possible in NDM mode */
+       if (self->state != LAP_NDM) {
+               pr_debug("%s(), discovery only possible in NDM mode\n",
+                        __func__);
+               irlap_discovery_confirm(self, NULL);
+               /* Note : in theory, if we are not in NDM, we could postpone
+                * the discovery like we do for connection request.
+                * In practice, it's not worth it. If the media was busy,
+                * it's likely next time around it won't be busy. If we are
+                * in REPLY state, we will get passive discovery info & event.
+                * Jean II */
+               return;
+       }
+
+       /* Check if last discovery request finished in time, or if
+        * it was aborted due to the media busy flag. */
+       if (self->discovery_log != NULL) {
+               hashbin_delete(self->discovery_log, (FREE_FUNC) kfree);
+               self->discovery_log = NULL;
+       }
+
+       /* All operations will occur at predictable time, no need to lock */
+       self->discovery_log = hashbin_new(HB_NOLOCK);
+
+       if (self->discovery_log == NULL) {
+               net_warn_ratelimited("%s(), Unable to allocate discovery log!\n",
+                                    __func__);
+               return;
+       }
+
+       info.S = discovery->nslots; /* Number of slots */
+       info.s = 0; /* Current slot */
+
+       self->discovery_cmd = discovery;
+       info.discovery = discovery;
+
+       /* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */
+       self->slot_timeout = msecs_to_jiffies(sysctl_slot_timeout);
+
+       irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info);
+}
+
+/*
+ * Function irlap_discovery_confirm (log)
+ *
+ *    A device has been discovered in front of this station, we
+ *    report directly to LMP.
+ */
+void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       IRDA_ASSERT(self->notify.instance != NULL, return;);
+
+       /*
+        * Check for successful discovery, since we are then allowed to clear
+        * the media busy condition (IrLAP 6.13.4 - p.94). This should allow
+        * us to make connection attempts much faster and easier (i.e. no
+        * collisions).
+        * Setting media busy to false will also generate an event allowing
+        * to process pending events in NDM state machine.
+        * Note : the spec doesn't define what's a successful discovery is.
+        * If we want Ultra to work, it's successful even if there is
+        * nobody discovered - Jean II
+        */
+       if (discovery_log)
+               irda_device_set_media_busy(self->netdev, FALSE);
+
+       /* Inform IrLMP */
+       irlmp_link_discovery_confirm(self->notify.instance, discovery_log);
+}
+
+/*
+ * Function irlap_discovery_indication (log)
+ *
+ *    Somebody is trying to discover us!
+ *
+ */
+void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+       IRDA_ASSERT(discovery != NULL, return;);
+
+       IRDA_ASSERT(self->notify.instance != NULL, return;);
+
+       /* A device is very likely to connect immediately after it performs
+        * a successful discovery. This means that in our case, we are much
+        * more likely to receive a connection request over the medium.
+        * So, we backoff to avoid collisions.
+        * IrLAP spec 6.13.4 suggest 100ms...
+        * Note : this little trick actually make a *BIG* difference. If I set
+        * my Linux box with discovery enabled and one Ultra frame sent every
+        * second, my Palm has no trouble connecting to it every time !
+        * Jean II */
+       irda_device_set_media_busy(self->netdev, SMALL);
+
+       irlmp_link_discovery_indication(self->notify.instance, discovery);
+}
+
+/*
+ * Function irlap_status_indication (quality_of_link)
+ */
+void irlap_status_indication(struct irlap_cb *self, int quality_of_link)
+{
+       switch (quality_of_link) {
+       case STATUS_NO_ACTIVITY:
+               net_info_ratelimited("IrLAP, no activity on link!\n");
+               break;
+       case STATUS_NOISY:
+               net_info_ratelimited("IrLAP, noisy link!\n");
+               break;
+       default:
+               break;
+       }
+       irlmp_status_indication(self->notify.instance,
+                               quality_of_link, LOCK_NO_CHANGE);
+}
+
+/*
+ * Function irlap_reset_indication (void)
+ */
+void irlap_reset_indication(struct irlap_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       if (self->state == LAP_RESET_WAIT)
+               irlap_do_event(self, RESET_REQUEST, NULL, NULL);
+       else
+               irlap_do_event(self, RESET_RESPONSE, NULL, NULL);
+}
+
+/*
+ * Function irlap_reset_confirm (void)
+ */
+void irlap_reset_confirm(void)
+{
+}
+
+/*
+ * Function irlap_generate_rand_time_slot (S, s)
+ *
+ *    Generate a random time slot between s and S-1 where
+ *    S = Number of slots (0 -> S-1)
+ *    s = Current slot
+ */
+int irlap_generate_rand_time_slot(int S, int s)
+{
+       static int rand;
+       int slot;
+
+       IRDA_ASSERT((S - s) > 0, return 0;);
+
+       rand += jiffies;
+       rand ^= (rand << 12);
+       rand ^= (rand >> 20);
+
+       slot = s + rand % (S-s);
+
+       IRDA_ASSERT((slot >= s) || (slot < S), return 0;);
+
+       return slot;
+}
+
+/*
+ * Function irlap_update_nr_received (nr)
+ *
+ *    Remove all acknowledged frames in current window queue. This code is
+ *    not intuitive and you should not try to change it. If you think it
+ *    contains bugs, please mail a patch to the author instead.
+ */
+void irlap_update_nr_received(struct irlap_cb *self, int nr)
+{
+       struct sk_buff *skb = NULL;
+       int count = 0;
+
+       /*
+        * Remove all the ack-ed frames from the window queue.
+        */
+
+       /*
+        *  Optimize for the common case. It is most likely that the receiver
+        *  will acknowledge all the frames we have sent! So in that case we
+        *  delete all frames stored in window.
+        */
+       if (nr == self->vs) {
+               while ((skb = skb_dequeue(&self->wx_list)) != NULL) {
+                       dev_kfree_skb(skb);
+               }
+               /* The last acked frame is the next to send minus one */
+               self->va = nr - 1;
+       } else {
+               /* Remove all acknowledged frames in current window */
+               while ((skb_peek(&self->wx_list) != NULL) &&
+                      (((self->va+1) % 8) != nr))
+               {
+                       skb = skb_dequeue(&self->wx_list);
+                       dev_kfree_skb(skb);
+
+                       self->va = (self->va + 1) % 8;
+                       count++;
+               }
+       }
+
+       /* Advance window */
+       self->window = self->window_size - skb_queue_len(&self->wx_list);
+}
+
+/*
+ * Function irlap_validate_ns_received (ns)
+ *
+ *    Validate the next to send (ns) field from received frame.
+ */
+int irlap_validate_ns_received(struct irlap_cb *self, int ns)
+{
+       /*  ns as expected?  */
+       if (ns == self->vr)
+               return NS_EXPECTED;
+       /*
+        *  Stations are allowed to treat invalid NS as unexpected NS
+        *  IrLAP, Recv ... with-invalid-Ns. p. 84
+        */
+       return NS_UNEXPECTED;
+
+       /* return NR_INVALID; */
+}
+/*
+ * Function irlap_validate_nr_received (nr)
+ *
+ *    Validate the next to receive (nr) field from received frame.
+ *
+ */
+int irlap_validate_nr_received(struct irlap_cb *self, int nr)
+{
+       /*  nr as expected?  */
+       if (nr == self->vs) {
+               pr_debug("%s(), expected!\n", __func__);
+               return NR_EXPECTED;
+       }
+
+       /*
+        *  unexpected nr? (but within current window), first we check if the
+        *  ns numbers of the frames in the current window wrap.
+        */
+       if (self->va < self->vs) {
+               if ((nr >= self->va) && (nr <= self->vs))
+                       return NR_UNEXPECTED;
+       } else {
+               if ((nr >= self->va) || (nr <= self->vs))
+                       return NR_UNEXPECTED;
+       }
+
+       /* Invalid nr!  */
+       return NR_INVALID;
+}
+
+/*
+ * Function irlap_initiate_connection_state ()
+ *
+ *    Initialize the connection state parameters
+ *
+ */
+void irlap_initiate_connection_state(struct irlap_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* Next to send and next to receive */
+       self->vs = self->vr = 0;
+
+       /* Last frame which got acked (0 - 1) % 8 */
+       self->va = 7;
+
+       self->window = 1;
+
+       self->remote_busy = FALSE;
+       self->retry_count = 0;
+}
+
+/*
+ * Function irlap_wait_min_turn_around (self, qos)
+ *
+ *    Wait negotiated minimum turn around time, this function actually sets
+ *    the number of BOS's that must be sent before the next transmitted
+ *    frame in order to delay for the specified amount of time. This is
+ *    done to avoid using timers, and the forbidden udelay!
+ */
+void irlap_wait_min_turn_around(struct irlap_cb *self, struct qos_info *qos)
+{
+       __u32 min_turn_time;
+       __u32 speed;
+
+       /* Get QoS values.  */
+       speed = qos->baud_rate.value;
+       min_turn_time = qos->min_turn_time.value;
+
+       /* No need to calculate XBOFs for speeds over 115200 bps */
+       if (speed > 115200) {
+               self->mtt_required = min_turn_time;
+               return;
+       }
+
+       /*
+        *  Send additional BOF's for the next frame for the requested
+        *  min turn time, so now we must calculate how many chars (XBOF's) we
+        *  must send for the requested time period (min turn time)
+        */
+       self->xbofs_delay = irlap_min_turn_time_in_bytes(speed, min_turn_time);
+}
+
+/*
+ * Function irlap_flush_all_queues (void)
+ *
+ *    Flush all queues
+ *
+ */
+void irlap_flush_all_queues(struct irlap_cb *self)
+{
+       struct sk_buff* skb;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* Free transmission queue */
+       while ((skb = skb_dequeue(&self->txq)) != NULL)
+               dev_kfree_skb(skb);
+
+       while ((skb = skb_dequeue(&self->txq_ultra)) != NULL)
+               dev_kfree_skb(skb);
+
+       /* Free sliding window buffered packets */
+       while ((skb = skb_dequeue(&self->wx_list)) != NULL)
+               dev_kfree_skb(skb);
+}
+
+/*
+ * Function irlap_setspeed (self, speed)
+ *
+ *    Change the speed of the IrDA port
+ *
+ */
+static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now)
+{
+       struct sk_buff *skb;
+
+       pr_debug("%s(), setting speed to %d\n", __func__, speed);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       self->speed = speed;
+
+       /* Change speed now, or just piggyback speed on frames */
+       if (now) {
+               /* Send down empty frame to trigger speed change */
+               skb = alloc_skb(0, GFP_ATOMIC);
+               if (skb)
+                       irlap_queue_xmit(self, skb);
+       }
+}
+
+/*
+ * Function irlap_init_qos_capabilities (self, qos)
+ *
+ *    Initialize QoS for this IrLAP session, What we do is to compute the
+ *    intersection of the QoS capabilities for the user, driver and for
+ *    IrLAP itself. Normally, IrLAP will not specify any values, but it can
+ *    be used to restrict certain values.
+ */
+static void irlap_init_qos_capabilities(struct irlap_cb *self,
+                                       struct qos_info *qos_user)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+       IRDA_ASSERT(self->netdev != NULL, return;);
+
+       /* Start out with the maximum QoS support possible */
+       irda_init_max_qos_capabilies(&self->qos_rx);
+
+       /* Apply drivers QoS capabilities */
+       irda_qos_compute_intersection(&self->qos_rx, self->qos_dev);
+
+       /*
+        *  Check for user supplied QoS parameters. The service user is only
+        *  allowed to supply these values. We check each parameter since the
+        *  user may not have set all of them.
+        */
+       if (qos_user) {
+               pr_debug("%s(), Found user specified QoS!\n", __func__);
+
+               if (qos_user->baud_rate.bits)
+                       self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits;
+
+               if (qos_user->max_turn_time.bits)
+                       self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits;
+               if (qos_user->data_size.bits)
+                       self->qos_rx.data_size.bits &= qos_user->data_size.bits;
+
+               if (qos_user->link_disc_time.bits)
+                       self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits;
+       }
+
+       /* Use 500ms in IrLAP for now */
+       self->qos_rx.max_turn_time.bits &= 0x01;
+
+       /* Set data size */
+       /*self->qos_rx.data_size.bits &= 0x03;*/
+
+       irda_qos_bits_to_value(&self->qos_rx);
+}
+
+/*
+ * Function irlap_apply_default_connection_parameters (void, now)
+ *
+ *    Use the default connection and transmission parameters
+ */
+void irlap_apply_default_connection_parameters(struct irlap_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* xbofs : Default value in NDM */
+       self->next_bofs   = 12;
+       self->bofs_count  = 12;
+
+       /* NDM Speed is 9600 */
+       irlap_change_speed(self, 9600, TRUE);
+
+       /* Set mbusy when going to NDM state */
+       irda_device_set_media_busy(self->netdev, TRUE);
+
+       /*
+        * Generate random connection address for this session, which must
+        * be 7 bits wide and different from 0x00 and 0xfe
+        */
+       while ((self->caddr == 0x00) || (self->caddr == 0xfe)) {
+               get_random_bytes(&self->caddr, sizeof(self->caddr));
+               self->caddr &= 0xfe;
+       }
+
+       /* Use default values until connection has been negitiated */
+       self->slot_timeout = sysctl_slot_timeout;
+       self->final_timeout = FINAL_TIMEOUT;
+       self->poll_timeout = POLL_TIMEOUT;
+       self->wd_timeout = WD_TIMEOUT;
+
+       /* Set some default values */
+       self->qos_tx.baud_rate.value = 9600;
+       self->qos_rx.baud_rate.value = 9600;
+       self->qos_tx.max_turn_time.value = 0;
+       self->qos_rx.max_turn_time.value = 0;
+       self->qos_tx.min_turn_time.value = 0;
+       self->qos_rx.min_turn_time.value = 0;
+       self->qos_tx.data_size.value = 64;
+       self->qos_rx.data_size.value = 64;
+       self->qos_tx.window_size.value = 1;
+       self->qos_rx.window_size.value = 1;
+       self->qos_tx.additional_bofs.value = 12;
+       self->qos_rx.additional_bofs.value = 12;
+       self->qos_tx.link_disc_time.value = 0;
+       self->qos_rx.link_disc_time.value = 0;
+
+       irlap_flush_all_queues(self);
+
+       self->disconnect_pending = FALSE;
+       self->connect_pending = FALSE;
+}
+
+/*
+ * Function irlap_apply_connection_parameters (qos, now)
+ *
+ *    Initialize IrLAP with the negotiated QoS values
+ *
+ * If 'now' is false, the speed and xbofs will be changed after the next
+ * frame is sent.
+ * If 'now' is true, the speed and xbofs is changed immediately
+ */
+void irlap_apply_connection_parameters(struct irlap_cb *self, int now)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* Set the negotiated xbofs value */
+       self->next_bofs   = self->qos_tx.additional_bofs.value;
+       if (now)
+               self->bofs_count = self->next_bofs;
+
+       /* Set the negotiated link speed (may need the new xbofs value) */
+       irlap_change_speed(self, self->qos_tx.baud_rate.value, now);
+
+       self->window_size = self->qos_tx.window_size.value;
+       self->window      = self->qos_tx.window_size.value;
+
+#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
+       /*
+        *  Calculate how many bytes it is possible to transmit before the
+        *  link must be turned around
+        */
+       self->line_capacity =
+               irlap_max_line_capacity(self->qos_tx.baud_rate.value,
+                                       self->qos_tx.max_turn_time.value);
+       self->bytes_left = self->line_capacity;
+#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+
+
+       /*
+        *  Initialize timeout values, some of the rules are listed on
+        *  page 92 in IrLAP.
+        */
+       IRDA_ASSERT(self->qos_tx.max_turn_time.value != 0, return;);
+       IRDA_ASSERT(self->qos_rx.max_turn_time.value != 0, return;);
+       /* The poll timeout applies only to the primary station.
+        * It defines the maximum time the primary stay in XMIT mode
+        * before timeout and turning the link around (sending a RR).
+        * Or, this is how much we can keep the pf bit in primary mode.
+        * Therefore, it must be lower or equal than our *OWN* max turn around.
+        * Jean II */
+       self->poll_timeout = msecs_to_jiffies(
+                               self->qos_tx.max_turn_time.value);
+       /* The Final timeout applies only to the primary station.
+        * It defines the maximum time the primary wait (mostly in RECV mode)
+        * for an answer from the secondary station before polling it again.
+        * Therefore, it must be greater or equal than our *PARTNER*
+        * max turn around time - Jean II */
+       self->final_timeout = msecs_to_jiffies(
+                               self->qos_rx.max_turn_time.value);
+       /* The Watchdog Bit timeout applies only to the secondary station.
+        * It defines the maximum time the secondary wait (mostly in RECV mode)
+        * for poll from the primary station before getting annoyed.
+        * Therefore, it must be greater or equal than our *PARTNER*
+        * max turn around time - Jean II */
+       self->wd_timeout = self->final_timeout * 2;
+
+       /*
+        * N1 and N2 are maximum retry count for *both* the final timer
+        * and the wd timer (with a factor 2) as defined above.
+        * After N1 retry of a timer, we give a warning to the user.
+        * After N2 retry, we consider the link dead and disconnect it.
+        * Jean II
+        */
+
+       /*
+        *  Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to
+        *  3 seconds otherwise. See page 71 in IrLAP for more details.
+        *  Actually, it's not always 3 seconds, as we allow to set
+        *  it via sysctl... Max maxtt is 500ms, and N1 need to be multiple
+        *  of 2, so 1 second is minimum we can allow. - Jean II
+        */
+       if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time)
+               /*
+                * If we set N1 to 0, it will trigger immediately, which is
+                * not what we want. What we really want is to disable it,
+                * Jean II
+                */
+               self->N1 = -2; /* Disable - Need to be multiple of 2*/
+       else
+               self->N1 = sysctl_warn_noreply_time * 1000 /
+                 self->qos_rx.max_turn_time.value;
+
+       pr_debug("Setting N1 = %d\n", self->N1);
+
+       /* Set N2 to match our own disconnect time */
+       self->N2 = self->qos_tx.link_disc_time.value * 1000 /
+               self->qos_rx.max_turn_time.value;
+       pr_debug("Setting N2 = %d\n", self->N2);
+}
+
+#ifdef CONFIG_PROC_FS
+struct irlap_iter_state {
+       int id;
+};
+
+static void *irlap_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       struct irlap_iter_state *iter = seq->private;
+       struct irlap_cb *self;
+
+       /* Protect our access to the tsap list */
+       spin_lock_irq(&irlap->hb_spinlock);
+       iter->id = 0;
+
+       for (self = (struct irlap_cb *) hashbin_get_first(irlap);
+            self; self = (struct irlap_cb *) hashbin_get_next(irlap)) {
+               if (iter->id == *pos)
+                       break;
+               ++iter->id;
+       }
+
+       return self;
+}
+
+static void *irlap_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       struct irlap_iter_state *iter = seq->private;
+
+       ++*pos;
+       ++iter->id;
+       return (void *) hashbin_get_next(irlap);
+}
+
+static void irlap_seq_stop(struct seq_file *seq, void *v)
+{
+       spin_unlock_irq(&irlap->hb_spinlock);
+}
+
+static int irlap_seq_show(struct seq_file *seq, void *v)
+{
+       const struct irlap_iter_state *iter = seq->private;
+       const struct irlap_cb *self = v;
+
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -EINVAL;);
+
+       seq_printf(seq, "irlap%d ", iter->id);
+       seq_printf(seq, "state: %s\n",
+                  irlap_state[self->state]);
+
+       seq_printf(seq, "  device name: %s, ",
+                  (self->netdev) ? self->netdev->name : "bug");
+       seq_printf(seq, "hardware name: %s\n", self->hw_name);
+
+       seq_printf(seq, "  caddr: %#02x, ", self->caddr);
+       seq_printf(seq, "saddr: %#08x, ", self->saddr);
+       seq_printf(seq, "daddr: %#08x\n", self->daddr);
+
+       seq_printf(seq, "  win size: %d, ",
+                  self->window_size);
+       seq_printf(seq, "win: %d, ", self->window);
+#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
+       seq_printf(seq, "line capacity: %d, ",
+                  self->line_capacity);
+       seq_printf(seq, "bytes left: %d\n", self->bytes_left);
+#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+       seq_printf(seq, "  tx queue len: %d ",
+                  skb_queue_len(&self->txq));
+       seq_printf(seq, "win queue len: %d ",
+                  skb_queue_len(&self->wx_list));
+       seq_printf(seq, "rbusy: %s", self->remote_busy ?
+                  "TRUE" : "FALSE");
+       seq_printf(seq, " mbusy: %s\n", self->media_busy ?
+                  "TRUE" : "FALSE");
+
+       seq_printf(seq, "  retrans: %d ", self->retry_count);
+       seq_printf(seq, "vs: %d ", self->vs);
+       seq_printf(seq, "vr: %d ", self->vr);
+       seq_printf(seq, "va: %d\n", self->va);
+
+       seq_printf(seq, "  qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n");
+
+       seq_printf(seq, "  tx\t%d\t",
+                  self->qos_tx.baud_rate.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_tx.max_turn_time.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_tx.data_size.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_tx.window_size.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_tx.additional_bofs.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_tx.min_turn_time.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_tx.link_disc_time.value);
+       seq_printf(seq, "\n");
+
+       seq_printf(seq, "  rx\t%d\t",
+                  self->qos_rx.baud_rate.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_rx.max_turn_time.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_rx.data_size.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_rx.window_size.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_rx.additional_bofs.value);
+       seq_printf(seq, "%d\t",
+                  self->qos_rx.min_turn_time.value);
+       seq_printf(seq, "%d\n",
+                  self->qos_rx.link_disc_time.value);
+
+       return 0;
+}
+
+static const struct seq_operations irlap_seq_ops = {
+       .start  = irlap_seq_start,
+       .next   = irlap_seq_next,
+       .stop   = irlap_seq_stop,
+       .show   = irlap_seq_show,
+};
+
+static int irlap_seq_open(struct inode *inode, struct file *file)
+{
+       if (irlap == NULL)
+               return -EINVAL;
+
+       return seq_open_private(file, &irlap_seq_ops,
+                       sizeof(struct irlap_iter_state));
+}
+
+const struct file_operations irlap_seq_fops = {
+       .owner          = THIS_MODULE,
+       .open           = irlap_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release_private,
+};
+
+#endif /* CONFIG_PROC_FS */
diff --git a/drivers/staging/irda/net/irlap_event.c b/drivers/staging/irda/net/irlap_event.c
new file mode 100644 (file)
index 0000000..0e1b4d7
--- /dev/null
@@ -0,0 +1,2316 @@
+/*********************************************************************
+ *
+ * Filename:      irlap_event.c
+ * Version:       0.9
+ * Description:   IrLAP state machine implementation
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dag@brattli.net>
+ * Created at:    Sat Aug 16 00:59:29 1997
+ * Modified at:   Sat Dec 25 21:07:57 1999
+ * Modified by:   Dag Brattli <dag@brattli.net>
+ *
+ *     Copyright (c) 1998-2000 Dag Brattli <dag@brattli.net>,
+ *     Copyright (c) 1998      Thomas Davis <ratbert@radiks.net>
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlap_event.h>
+
+#include <net/irda/timer.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/qos.h>
+#include <net/irda/parameters.h>
+#include <net/irda/irlmp.h>            /* irlmp_flow_indication(), ... */
+
+#include <net/irda/irda_device.h>
+
+#ifdef CONFIG_IRDA_FAST_RR
+int sysctl_fast_poll_increase = 50;
+#endif
+
+static int irlap_state_ndm    (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_query  (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reply  (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_conn   (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_setup  (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_xmit_p (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_pclose (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_nrm_p  (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
+                                 struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reset  (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_nrm_s  (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_xmit_s (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info);
+static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event,
+                                  struct sk_buff *, struct irlap_info *);
+
+static const char *const irlap_event[] __maybe_unused = {
+       "DISCOVERY_REQUEST",
+       "CONNECT_REQUEST",
+       "CONNECT_RESPONSE",
+       "DISCONNECT_REQUEST",
+       "DATA_REQUEST",
+       "RESET_REQUEST",
+       "RESET_RESPONSE",
+       "SEND_I_CMD",
+       "SEND_UI_FRAME",
+       "RECV_DISCOVERY_XID_CMD",
+       "RECV_DISCOVERY_XID_RSP",
+       "RECV_SNRM_CMD",
+       "RECV_TEST_CMD",
+       "RECV_TEST_RSP",
+       "RECV_UA_RSP",
+       "RECV_DM_RSP",
+       "RECV_RD_RSP",
+       "RECV_I_CMD",
+       "RECV_I_RSP",
+       "RECV_UI_FRAME",
+       "RECV_FRMR_RSP",
+       "RECV_RR_CMD",
+       "RECV_RR_RSP",
+       "RECV_RNR_CMD",
+       "RECV_RNR_RSP",
+       "RECV_REJ_CMD",
+       "RECV_REJ_RSP",
+       "RECV_SREJ_CMD",
+       "RECV_SREJ_RSP",
+       "RECV_DISC_CMD",
+       "SLOT_TIMER_EXPIRED",
+       "QUERY_TIMER_EXPIRED",
+       "FINAL_TIMER_EXPIRED",
+       "POLL_TIMER_EXPIRED",
+       "DISCOVERY_TIMER_EXPIRED",
+       "WD_TIMER_EXPIRED",
+       "BACKOFF_TIMER_EXPIRED",
+       "MEDIA_BUSY_TIMER_EXPIRED",
+};
+
+const char *const irlap_state[] = {
+       "LAP_NDM",
+       "LAP_QUERY",
+       "LAP_REPLY",
+       "LAP_CONN",
+       "LAP_SETUP",
+       "LAP_OFFLINE",
+       "LAP_XMIT_P",
+       "LAP_PCLOSE",
+       "LAP_NRM_P",
+       "LAP_RESET_WAIT",
+       "LAP_RESET",
+       "LAP_NRM_S",
+       "LAP_XMIT_S",
+       "LAP_SCLOSE",
+       "LAP_RESET_CHECK",
+};
+
+static int (*state[])(struct irlap_cb *self, IRLAP_EVENT event,
+                     struct sk_buff *skb, struct irlap_info *info) =
+{
+       irlap_state_ndm,
+       irlap_state_query,
+       irlap_state_reply,
+       irlap_state_conn,
+       irlap_state_setup,
+       irlap_state_offline,
+       irlap_state_xmit_p,
+       irlap_state_pclose,
+       irlap_state_nrm_p,
+       irlap_state_reset_wait,
+       irlap_state_reset,
+       irlap_state_nrm_s,
+       irlap_state_xmit_s,
+       irlap_state_sclose,
+       irlap_state_reset_check,
+};
+
+/*
+ * Function irda_poll_timer_expired (data)
+ *
+ *    Poll timer has expired. Normally we must now send a RR frame to the
+ *    remote device
+ */
+static void irlap_poll_timer_expired(void *data)
+{
+       struct irlap_cb *self = (struct irlap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);
+}
+
+/*
+ * Calculate and set time before we will have to send back the pf bit
+ * to the peer. Use in primary.
+ * Make sure that state is XMIT_P/XMIT_S when calling this function
+ * (and that nobody messed up with the state). - Jean II
+ */
+static void irlap_start_poll_timer(struct irlap_cb *self, int timeout)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+#ifdef CONFIG_IRDA_FAST_RR
+       /*
+        * Send out the RR frames faster if our own transmit queue is empty, or
+        * if the peer is busy. The effect is a much faster conversation
+        */
+       if (skb_queue_empty(&self->txq) || self->remote_busy) {
+               if (self->fast_RR == TRUE) {
+                       /*
+                        *  Assert that the fast poll timer has not reached the
+                        *  normal poll timer yet
+                        */
+                       if (self->fast_RR_timeout < timeout) {
+                               /*
+                                *  FIXME: this should be a more configurable
+                                *         function
+                                */
+                               self->fast_RR_timeout +=
+                                       (sysctl_fast_poll_increase * HZ/1000);
+
+                               /* Use this fast(er) timeout instead */
+                               timeout = self->fast_RR_timeout;
+                       }
+               } else {
+                       self->fast_RR = TRUE;
+
+                       /* Start with just 0 ms */
+                       self->fast_RR_timeout = 0;
+                       timeout = 0;
+               }
+       } else
+               self->fast_RR = FALSE;
+
+       pr_debug("%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies);
+#endif /* CONFIG_IRDA_FAST_RR */
+
+       if (timeout == 0)
+               irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);
+       else
+               irda_start_timer(&self->poll_timer, timeout, self,
+                                irlap_poll_timer_expired);
+}
+
+/*
+ * Function irlap_do_event (event, skb, info)
+ *
+ *    Rushes through the state machine without any delay. If state == XMIT
+ *    then send queued data frames.
+ */
+void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
+                   struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret;
+
+       if (!self || self->magic != LAP_MAGIC)
+               return;
+
+       pr_debug("%s(), event = %s, state = %s\n", __func__,
+                irlap_event[event], irlap_state[self->state]);
+
+       ret = (*state[self->state])(self, event, skb, info);
+
+       /*
+        *  Check if there are any pending events that needs to be executed
+        */
+       switch (self->state) {
+       case LAP_XMIT_P: /* FALLTHROUGH */
+       case LAP_XMIT_S:
+               /*
+                * We just received the pf bit and are at the beginning
+                * of a new LAP transmit window.
+                * Check if there are any queued data frames, and do not
+                * try to disconnect link if we send any data frames, since
+                * that will change the state away form XMIT
+                */
+               pr_debug("%s() : queue len = %d\n", __func__,
+                        skb_queue_len(&self->txq));
+
+               if (!skb_queue_empty(&self->txq)) {
+                       /* Prevent race conditions with irlap_data_request() */
+                       self->local_busy = TRUE;
+
+                       /* Theory of operation.
+                        * We send frames up to when we fill the window or
+                        * reach line capacity. Those frames will queue up
+                        * in the device queue, and the driver will slowly
+                        * send them.
+                        * After each frame that we send, we poll the higher
+                        * layer for more data. It's the right time to do
+                        * that because the link layer need to perform the mtt
+                        * and then send the first frame, so we can afford
+                        * to send a bit of time in kernel space.
+                        * The explicit flow indication allow to minimise
+                        * buffers (== lower latency), to avoid higher layer
+                        * polling via timers (== less context switches) and
+                        * to implement a crude scheduler - Jean II */
+
+                       /* Try to send away all queued data frames */
+                       while ((skb = skb_dequeue(&self->txq)) != NULL) {
+                               /* Send one frame */
+                               ret = (*state[self->state])(self, SEND_I_CMD,
+                                                           skb, NULL);
+                               /* Drop reference count.
+                                * It will be increase as needed in
+                                * irlap_send_data_xxx() */
+                               kfree_skb(skb);
+
+                               /* Poll the higher layers for one more frame */
+                               irlmp_flow_indication(self->notify.instance,
+                                                     FLOW_START);
+
+                               if (ret == -EPROTO)
+                                       break; /* Try again later! */
+                       }
+                       /* Finished transmitting */
+                       self->local_busy = FALSE;
+               } else if (self->disconnect_pending) {
+                       self->disconnect_pending = FALSE;
+
+                       ret = (*state[self->state])(self, DISCONNECT_REQUEST,
+                                                   NULL, NULL);
+               }
+               break;
+/*     case LAP_NDM: */
+/*     case LAP_CONN: */
+/*     case LAP_RESET_WAIT: */
+/*     case LAP_RESET_CHECK: */
+       default:
+               break;
+       }
+}
+
+/*
+ * Function irlap_state_ndm (event, skb, frame)
+ *
+ *    NDM (Normal Disconnected Mode) state
+ *
+ */
+static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
+                          struct sk_buff *skb, struct irlap_info *info)
+{
+       discovery_t *discovery_rsp;
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case CONNECT_REQUEST:
+               IRDA_ASSERT(self->netdev != NULL, return -1;);
+
+               if (self->media_busy) {
+                       /* Note : this will never happen, because we test
+                        * media busy in irlap_connect_request() and
+                        * postpone the event... - Jean II */
+                       pr_debug("%s(), CONNECT_REQUEST: media busy!\n",
+                                __func__);
+
+                       /* Always switch state before calling upper layers */
+                       irlap_next_state(self, LAP_NDM);
+
+                       irlap_disconnect_indication(self, LAP_MEDIA_BUSY);
+               } else {
+                       irlap_send_snrm_frame(self, &self->qos_rx);
+
+                       /* Start Final-bit timer */
+                       irlap_start_final_timer(self, self->final_timeout);
+
+                       self->retry_count = 0;
+                       irlap_next_state(self, LAP_SETUP);
+               }
+               break;
+       case RECV_SNRM_CMD:
+               /* Check if the frame contains and I field */
+               if (info) {
+                       self->daddr = info->daddr;
+                       self->caddr = info->caddr;
+
+                       irlap_next_state(self, LAP_CONN);
+
+                       irlap_connect_indication(self, skb);
+               } else {
+                       pr_debug("%s(), SNRM frame does not contain an I field!\n",
+                                __func__);
+               }
+               break;
+       case DISCOVERY_REQUEST:
+               IRDA_ASSERT(info != NULL, return -1;);
+
+               if (self->media_busy) {
+                       pr_debug("%s(), DISCOVERY_REQUEST: media busy!\n",
+                                __func__);
+                       /* irlap->log.condition = MEDIA_BUSY; */
+
+                       /* This will make IrLMP try again */
+                       irlap_discovery_confirm(self, NULL);
+                       /* Note : the discovery log is not cleaned up here,
+                        * it will be done in irlap_discovery_request()
+                        * Jean II */
+                       return 0;
+               }
+
+               self->S = info->S;
+               self->s = info->s;
+               irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE,
+                                              info->discovery);
+               self->frame_sent = FALSE;
+               self->s++;
+
+               irlap_start_slot_timer(self, self->slot_timeout);
+               irlap_next_state(self, LAP_QUERY);
+               break;
+       case RECV_DISCOVERY_XID_CMD:
+               IRDA_ASSERT(info != NULL, return -1;);
+
+               /* Assert that this is not the final slot */
+               if (info->s <= info->S) {
+                       self->slot = irlap_generate_rand_time_slot(info->S,
+                                                                  info->s);
+                       if (self->slot == info->s) {
+                               discovery_rsp = irlmp_get_discovery_response();
+                               discovery_rsp->data.daddr = info->daddr;
+
+                               irlap_send_discovery_xid_frame(self, info->S,
+                                                              self->slot,
+                                                              FALSE,
+                                                              discovery_rsp);
+                               self->frame_sent = TRUE;
+                       } else
+                               self->frame_sent = FALSE;
+
+                       /*
+                        * Go to reply state until end of discovery to
+                        * inhibit our own transmissions. Set the timer
+                        * to not stay forever there... Jean II
+                        */
+                       irlap_start_query_timer(self, info->S, info->s);
+                       irlap_next_state(self, LAP_REPLY);
+               } else {
+               /* This is the final slot. How is it possible ?
+                * This would happen is both discoveries are just slightly
+                * offset (if they are in sync, all packets are lost).
+                * Most often, all the discovery requests will be received
+                * in QUERY state (see my comment there), except for the
+                * last frame that will come here.
+                * The big trouble when it happen is that active discovery
+                * doesn't happen, because nobody answer the discoveries
+                * frame of the other guy, so the log shows up empty.
+                * What should we do ?
+                * Not much. It's too late to answer those discovery frames,
+                * so we just pass the info to IrLMP who will put it in the
+                * log (and post an event).
+                * Another cause would be devices that do discovery much
+                * slower than us, however the latest fixes should minimise
+                * those cases...
+                * Jean II
+                */
+                       pr_debug("%s(), Receiving final discovery request, missed the discovery slots :-(\n",
+                                __func__);
+
+                       /* Last discovery request -> in the log */
+                       irlap_discovery_indication(self, info->discovery);
+               }
+               break;
+       case MEDIA_BUSY_TIMER_EXPIRED:
+               /* A bunch of events may be postponed because the media is
+                * busy (usually immediately after we close a connection),
+                * or while we are doing discovery (state query/reply).
+                * In all those cases, the media busy flag will be cleared
+                * when it's OK for us to process those postponed events.
+                * This event is not mentioned in the state machines in the
+                * IrLAP spec. It's because they didn't consider Ultra and
+                * postponing connection request is optional.
+                * Jean II */
+#ifdef CONFIG_IRDA_ULTRA
+               /* Send any pending Ultra frames if any */
+               if (!skb_queue_empty(&self->txq_ultra)) {
+                       /* We don't send the frame, just post an event.
+                        * Also, previously this code was in timer.c...
+                        * Jean II */
+                       ret = (*state[self->state])(self, SEND_UI_FRAME,
+                                                   NULL, NULL);
+               }
+#endif /* CONFIG_IRDA_ULTRA */
+               /* Check if we should try to connect.
+                * This code was previously in irlap_do_event() */
+               if (self->connect_pending) {
+                       self->connect_pending = FALSE;
+
+                       /* This one *should* not pend in this state, except
+                        * if a socket try to connect and immediately
+                        * disconnect. - clear - Jean II */
+                       if (self->disconnect_pending)
+                               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+                       else
+                               ret = (*state[self->state])(self,
+                                                           CONNECT_REQUEST,
+                                                           NULL, NULL);
+                       self->disconnect_pending = FALSE;
+               }
+               /* Note : one way to test if this code works well (including
+                * media busy and small busy) is to create a user space
+                * application generating an Ultra packet every 3.05 sec (or
+                * 2.95 sec) and to see how it interact with discovery.
+                * It's fairly easy to check that no packet is lost, that the
+                * packets are postponed during discovery and that after
+                * discovery indication you have a 100ms "gap".
+                * As connection request and Ultra are now processed the same
+                * way, this avoid the tedious job of trying IrLAP connection
+                * in all those cases...
+                * Jean II */
+               break;
+#ifdef CONFIG_IRDA_ULTRA
+       case SEND_UI_FRAME:
+       {
+               int i;
+               /* Only allowed to repeat an operation twice */
+               for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) {
+                       skb = skb_dequeue(&self->txq_ultra);
+                       if (skb)
+                               irlap_send_ui_frame(self, skb, CBROADCAST,
+                                                   CMD_FRAME);
+                       else
+                               break;
+                       /* irlap_send_ui_frame() won't increase skb reference
+                        * count, so no dev_kfree_skb() - Jean II */
+               }
+               if (i == 2) {
+                       /* Force us to listen 500 ms again */
+                       irda_device_set_media_busy(self->netdev, TRUE);
+               }
+               break;
+       }
+       case RECV_UI_FRAME:
+               /* Only accept broadcast frames in NDM mode */
+               if (info->caddr != CBROADCAST) {
+                       pr_debug("%s(), not a broadcast frame!\n",
+                                __func__);
+               } else
+                       irlap_unitdata_indication(self, skb);
+               break;
+#endif /* CONFIG_IRDA_ULTRA */
+       case RECV_TEST_CMD:
+               /* Remove test frame header */
+               skb_pull(skb, sizeof(struct test_frame));
+
+               /*
+                * Send response. This skb will not be sent out again, and
+                * will only be used to send out the same info as the cmd
+                */
+               irlap_send_test_frame(self, CBROADCAST, info->daddr, skb);
+               break;
+       case RECV_TEST_RSP:
+               pr_debug("%s() not implemented!\n", __func__);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n", __func__,
+                        irlap_event[event]);
+
+               ret = -1;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_query (event, skb, info)
+ *
+ *    QUERY state
+ *
+ */
+static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
+                            struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case RECV_DISCOVERY_XID_RSP:
+               IRDA_ASSERT(info != NULL, return -1;);
+               IRDA_ASSERT(info->discovery != NULL, return -1;);
+
+               pr_debug("%s(), daddr=%08x\n", __func__,
+                        info->discovery->data.daddr);
+
+               if (!self->discovery_log) {
+                       net_warn_ratelimited("%s: discovery log is gone! maybe the discovery timeout has been set too short?\n",
+                                            __func__);
+                       break;
+               }
+               hashbin_insert(self->discovery_log,
+                              (irda_queue_t *) info->discovery,
+                              info->discovery->data.daddr, NULL);
+
+               /* Keep state */
+               /* irlap_next_state(self, LAP_QUERY);  */
+
+               break;
+       case RECV_DISCOVERY_XID_CMD:
+               /* Yes, it is possible to receive those frames in this mode.
+                * Note that most often the last discovery request won't
+                * occur here but in NDM state (see my comment there).
+                * What should we do ?
+                * Not much. We are currently performing our own discovery,
+                * therefore we can't answer those frames. We don't want
+                * to change state either. We just pass the info to
+                * IrLMP who will put it in the log (and post an event).
+                * Jean II
+                */
+
+               IRDA_ASSERT(info != NULL, return -1;);
+
+               pr_debug("%s(), Receiving discovery request (s = %d) while performing discovery :-(\n",
+                        __func__, info->s);
+
+               /* Last discovery request ? */
+               if (info->s == 0xff)
+                       irlap_discovery_indication(self, info->discovery);
+               break;
+       case SLOT_TIMER_EXPIRED:
+               /*
+                * Wait a little longer if we detect an incoming frame. This
+                * is not mentioned in the spec, but is a good thing to do,
+                * since we want to work even with devices that violate the
+                * timing requirements.
+                */
+               if (irda_device_is_receiving(self->netdev) && !self->add_wait) {
+                       pr_debug("%s(), device is slow to answer, waiting some more!\n",
+                                __func__);
+                       irlap_start_slot_timer(self, msecs_to_jiffies(10));
+                       self->add_wait = TRUE;
+                       return ret;
+               }
+               self->add_wait = FALSE;
+
+               if (self->s < self->S) {
+                       irlap_send_discovery_xid_frame(self, self->S,
+                                                      self->s, TRUE,
+                                                      self->discovery_cmd);
+                       self->s++;
+                       irlap_start_slot_timer(self, self->slot_timeout);
+
+                       /* Keep state */
+                       irlap_next_state(self, LAP_QUERY);
+               } else {
+                       /* This is the final slot! */
+                       irlap_send_discovery_xid_frame(self, self->S, 0xff,
+                                                      TRUE,
+                                                      self->discovery_cmd);
+
+                       /* Always switch state before calling upper layers */
+                       irlap_next_state(self, LAP_NDM);
+
+                       /*
+                        *  We are now finished with the discovery procedure,
+                        *  so now we must return the results
+                        */
+                       irlap_discovery_confirm(self, self->discovery_log);
+
+                       /* IrLMP should now have taken care of the log */
+                       self->discovery_log = NULL;
+               }
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n", __func__,
+                        irlap_event[event]);
+
+               ret = -1;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_reply (self, event, skb, info)
+ *
+ *    REPLY, we have received a XID discovery frame from a device and we
+ *    are waiting for the right time slot to send a response XID frame
+ *
+ */
+static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
+                            struct sk_buff *skb, struct irlap_info *info)
+{
+       discovery_t *discovery_rsp;
+       int ret=0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case QUERY_TIMER_EXPIRED:
+               pr_debug("%s(), QUERY_TIMER_EXPIRED <%ld>\n",
+                        __func__, jiffies);
+               irlap_next_state(self, LAP_NDM);
+               break;
+       case RECV_DISCOVERY_XID_CMD:
+               IRDA_ASSERT(info != NULL, return -1;);
+               /* Last frame? */
+               if (info->s == 0xff) {
+                       del_timer(&self->query_timer);
+
+                       /* info->log.condition = REMOTE; */
+
+                       /* Always switch state before calling upper layers */
+                       irlap_next_state(self, LAP_NDM);
+
+                       irlap_discovery_indication(self, info->discovery);
+               } else {
+                       /* If it's our slot, send our reply */
+                       if ((info->s >= self->slot) && (!self->frame_sent)) {
+                               discovery_rsp = irlmp_get_discovery_response();
+                               discovery_rsp->data.daddr = info->daddr;
+
+                               irlap_send_discovery_xid_frame(self, info->S,
+                                                              self->slot,
+                                                              FALSE,
+                                                              discovery_rsp);
+
+                               self->frame_sent = TRUE;
+                       }
+                       /* Readjust our timer to accommodate devices
+                        * doing faster or slower discovery than us...
+                        * Jean II */
+                       irlap_start_query_timer(self, info->S, info->s);
+
+                       /* Keep state */
+                       //irlap_next_state(self, LAP_REPLY);
+               }
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d, %s\n", __func__,
+                        event, irlap_event[event]);
+
+               ret = -1;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_conn (event, skb, info)
+ *
+ *    CONN, we have received a SNRM command and is waiting for the upper
+ *    layer to accept or refuse connection
+ *
+ */
+static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event,
+                           struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case CONNECT_RESPONSE:
+               skb_pull(skb, sizeof(struct snrm_frame));
+
+               IRDA_ASSERT(self->netdev != NULL, return -1;);
+
+               irlap_qos_negotiate(self, skb);
+
+               irlap_initiate_connection_state(self);
+
+               /*
+                * Applying the parameters now will make sure we change speed
+                * *after* we have sent the next frame
+                */
+               irlap_apply_connection_parameters(self, FALSE);
+
+               /*
+                * Sending this frame will force a speed change after it has
+                * been sent (i.e. the frame will be sent at 9600).
+                */
+               irlap_send_ua_response_frame(self, &self->qos_rx);
+
+#if 0
+               /*
+                * We are allowed to send two frames, but this may increase
+                * the connect latency, so lets not do it for now.
+                */
+               /* This is full of good intentions, but doesn't work in
+                * practice.
+                * After sending the first UA response, we switch the
+                * dongle to the negotiated speed, which is usually
+                * different than 9600 kb/s.
+                * From there, there is two solutions :
+                * 1) The other end has received the first UA response :
+                * it will set up the connection, move to state LAP_NRM_P,
+                * and will ignore and drop the second UA response.
+                * Actually, it's even worse : the other side will almost
+                * immediately send a RR that will likely collide with the
+                * UA response (depending on negotiated turnaround).
+                * 2) The other end has not received the first UA response,
+                * will stay at 9600 and will never see the second UA response.
+                * Jean II */
+               irlap_send_ua_response_frame(self, &self->qos_rx);
+#endif
+
+               /*
+                *  The WD-timer could be set to the duration of the P-timer
+                *  for this case, but it is recommended to use twice the
+                *  value (note 3 IrLAP p. 60).
+                */
+               irlap_start_wd_timer(self, self->wd_timeout);
+               irlap_next_state(self, LAP_NRM_S);
+
+               break;
+       case RECV_DISCOVERY_XID_CMD:
+               pr_debug("%s(), event RECV_DISCOVER_XID_CMD!\n",
+                        __func__);
+               irlap_next_state(self, LAP_NDM);
+
+               break;
+       case DISCONNECT_REQUEST:
+               pr_debug("%s(), Disconnect request!\n", __func__);
+               irlap_send_dm_frame(self);
+               irlap_next_state( self, LAP_NDM);
+               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d, %s\n", __func__,
+                        event, irlap_event[event]);
+
+               ret = -1;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Function irlap_state_setup (event, skb, frame)
+ *
+ *    SETUP state, The local layer has transmitted a SNRM command frame to
+ *    a remote peer layer and is awaiting a reply .
+ *
+ */
+static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
+                            struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case FINAL_TIMER_EXPIRED:
+               if (self->retry_count < self->N3) {
+/*
+ *  Perform random backoff, Wait a random number of time units, minimum
+ *  duration half the time taken to transmitt a SNRM frame, maximum duration
+ *  1.5 times the time taken to transmit a SNRM frame. So this time should
+ *  between 15 msecs and 45 msecs.
+ */
+                       irlap_start_backoff_timer(self, msecs_to_jiffies(20 +
+                                                       (jiffies % 30)));
+               } else {
+                       /* Always switch state before calling upper layers */
+                       irlap_next_state(self, LAP_NDM);
+
+                       irlap_disconnect_indication(self, LAP_FOUND_NONE);
+               }
+               break;
+       case BACKOFF_TIMER_EXPIRED:
+               irlap_send_snrm_frame(self, &self->qos_rx);
+               irlap_start_final_timer(self, self->final_timeout);
+               self->retry_count++;
+               break;
+       case RECV_SNRM_CMD:
+               pr_debug("%s(), SNRM battle!\n", __func__);
+
+               IRDA_ASSERT(skb != NULL, return 0;);
+               IRDA_ASSERT(info != NULL, return 0;);
+
+               /*
+                *  The device with the largest device address wins the battle
+                *  (both have sent a SNRM command!)
+                */
+               if (info &&(info->daddr > self->saddr)) {
+                       del_timer(&self->final_timer);
+                       irlap_initiate_connection_state(self);
+
+                       IRDA_ASSERT(self->netdev != NULL, return -1;);
+
+                       skb_pull(skb, sizeof(struct snrm_frame));
+
+                       irlap_qos_negotiate(self, skb);
+
+                       /* Send UA frame and then change link settings */
+                       irlap_apply_connection_parameters(self, FALSE);
+                       irlap_send_ua_response_frame(self, &self->qos_rx);
+
+                       irlap_next_state(self, LAP_NRM_S);
+                       irlap_connect_confirm(self, skb);
+
+                       /*
+                        *  The WD-timer could be set to the duration of the
+                        *  P-timer for this case, but it is recommended
+                        *  to use twice the value (note 3 IrLAP p. 60).
+                        */
+                       irlap_start_wd_timer(self, self->wd_timeout);
+               } else {
+                       /* We just ignore the other device! */
+                       irlap_next_state(self, LAP_SETUP);
+               }
+               break;
+       case RECV_UA_RSP:
+               /* Stop F-timer */
+               del_timer(&self->final_timer);
+
+               /* Initiate connection state */
+               irlap_initiate_connection_state(self);
+
+               /* Negotiate connection parameters */
+               IRDA_ASSERT(skb->len > 10, return -1;);
+
+               skb_pull(skb, sizeof(struct ua_frame));
+
+               IRDA_ASSERT(self->netdev != NULL, return -1;);
+
+               irlap_qos_negotiate(self, skb);
+
+               /* Set the new link setting *now* (before the rr frame) */
+               irlap_apply_connection_parameters(self, TRUE);
+               self->retry_count = 0;
+
+               /* Wait for turnaround time to give a chance to the other
+                * device to be ready to receive us.
+                * Note : the time to switch speed is typically larger
+                * than the turnaround time, but as we don't have the other
+                * side speed switch time, that's our best guess...
+                * Jean II */
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+
+               /* This frame will actually be sent at the new speed */
+               irlap_send_rr_frame(self, CMD_FRAME);
+
+               /* The timer is set to half the normal timer to quickly
+                * detect a failure to negotiate the new connection
+                * parameters. IrLAP 6.11.3.2, note 3.
+                * Note that currently we don't process this failure
+                * properly, as we should do a quick disconnect.
+                * Jean II */
+               irlap_start_final_timer(self, self->final_timeout/2);
+               irlap_next_state(self, LAP_NRM_P);
+
+               irlap_connect_confirm(self, skb);
+               break;
+       case RECV_DM_RSP:     /* FALLTHROUGH */
+       case RECV_DISC_CMD:
+               del_timer(&self->final_timer);
+               irlap_next_state(self, LAP_NDM);
+
+               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d, %s\n", __func__,
+                        event, irlap_event[event]);
+
+               ret = -1;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_offline (self, event, skb, info)
+ *
+ *    OFFLINE state, not used for now!
+ *
+ */
+static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event,
+                              struct sk_buff *skb, struct irlap_info *info)
+{
+       pr_debug("%s(), Unknown event\n", __func__);
+
+       return -1;
+}
+
+/*
+ * Function irlap_state_xmit_p (self, event, skb, info)
+ *
+ *    XMIT, Only the primary station has right to transmit, and we
+ *    therefore do not expect to receive any transmissions from other
+ *    stations.
+ *
+ */
+static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
+                             struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+
+       switch (event) {
+       case SEND_I_CMD:
+               /*
+                *  Only send frame if send-window > 0.
+                */
+               if ((self->window > 0) && (!self->remote_busy)) {
+                       int nextfit;
+#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
+                       struct sk_buff *skb_next;
+
+                       /* With DYNAMIC_WINDOW, we keep the window size
+                        * maximum, and adapt on the packets we are sending.
+                        * At 115k, we can send only 2 packets of 2048 bytes
+                        * in a 500 ms turnaround. Without this option, we
+                        * would always limit the window to 2. With this
+                        * option, if we send smaller packets, we can send
+                        * up to 7 of them (always depending on QoS).
+                        * Jean II */
+
+                       /* Look at the next skb. This is safe, as we are
+                        * the only consumer of the Tx queue (if we are not,
+                        * we have other problems) - Jean II */
+                       skb_next = skb_peek(&self->txq);
+
+                       /* Check if a subsequent skb exist and would fit in
+                        * the current window (with respect to turnaround
+                        * time).
+                        * This allow us to properly mark the current packet
+                        * with the pf bit, to avoid falling back on the
+                        * second test below, and avoid waiting the
+                        * end of the window and sending a extra RR.
+                        * Note : (skb_next != NULL) <=> (skb_queue_len() > 0)
+                        * Jean II */
+                       nextfit = ((skb_next != NULL) &&
+                                  ((skb_next->len + skb->len) <=
+                                   self->bytes_left));
+
+                       /*
+                        * The current packet may not fit ! Because of test
+                        * above, this should not happen any more !!!
+                        *  Test if we have transmitted more bytes over the
+                        *  link than its possible to do with the current
+                        *  speed and turn-around-time.
+                        */
+                       if((!nextfit) && (skb->len > self->bytes_left)) {
+                               pr_debug("%s(), Not allowed to transmit more bytes!\n",
+                                        __func__);
+                               /* Requeue the skb */
+                               skb_queue_head(&self->txq, skb_get(skb));
+                               /*
+                                *  We should switch state to LAP_NRM_P, but
+                                *  that is not possible since we must be sure
+                                *  that we poll the other side. Since we have
+                                *  used up our time, the poll timer should
+                                *  trigger anyway now, so we just wait for it
+                                *  DB
+                                */
+                               /*
+                                * Sorry, but that's not totally true. If
+                                * we send 2000B packets, we may wait another
+                                * 1000B until our turnaround expire. That's
+                                * why we need to be proactive in avoiding
+                                * coming here. - Jean II
+                                */
+                               return -EPROTO;
+                       }
+
+                       /* Subtract space used by this skb */
+                       self->bytes_left -= skb->len;
+#else  /* CONFIG_IRDA_DYNAMIC_WINDOW */
+                       /* Window has been adjusted for the max packet
+                        * size, so much simpler... - Jean II */
+                       nextfit = !skb_queue_empty(&self->txq);
+#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+                       /*
+                        *  Send data with poll bit cleared only if window > 1
+                        *  and there is more frames after this one to be sent
+                        */
+                       if ((self->window > 1) && (nextfit)) {
+                               /* More packet to send in current window */
+                               irlap_send_data_primary(self, skb);
+                               irlap_next_state(self, LAP_XMIT_P);
+                       } else {
+                               /* Final packet of window */
+                               irlap_send_data_primary_poll(self, skb);
+
+                               /*
+                                * Make sure state machine does not try to send
+                                * any more frames
+                                */
+                               ret = -EPROTO;
+                       }
+#ifdef CONFIG_IRDA_FAST_RR
+                       /* Peer may want to reply immediately */
+                       self->fast_RR = FALSE;
+#endif /* CONFIG_IRDA_FAST_RR */
+               } else {
+                       pr_debug("%s(), Unable to send! remote busy?\n",
+                                __func__);
+                       skb_queue_head(&self->txq, skb_get(skb));
+
+                       /*
+                        *  The next ret is important, because it tells
+                        *  irlap_next_state _not_ to deliver more frames
+                        */
+                       ret = -EPROTO;
+               }
+               break;
+       case POLL_TIMER_EXPIRED:
+               pr_debug("%s(), POLL_TIMER_EXPIRED <%ld>\n",
+                        __func__, jiffies);
+               irlap_send_rr_frame(self, CMD_FRAME);
+               /* Return to NRM properly - Jean II  */
+               self->window = self->window_size;
+#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
+               /* Allowed to transmit a maximum number of bytes again. */
+               self->bytes_left = self->line_capacity;
+#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+               irlap_start_final_timer(self, self->final_timeout);
+               irlap_next_state(self, LAP_NRM_P);
+               break;
+       case DISCONNECT_REQUEST:
+               del_timer(&self->poll_timer);
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+               irlap_send_disc_frame(self);
+               irlap_flush_all_queues(self);
+               irlap_start_final_timer(self, self->final_timeout);
+               self->retry_count = 0;
+               irlap_next_state(self, LAP_PCLOSE);
+               break;
+       case DATA_REQUEST:
+               /* Nothing to do, irlap_do_event() will send the packet
+                * when we return... - Jean II */
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n",
+                        __func__, irlap_event[event]);
+
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_pclose (event, skb, info)
+ *
+ *    PCLOSE state
+ */
+static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event,
+                             struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case RECV_UA_RSP: /* FALLTHROUGH */
+       case RECV_DM_RSP:
+               del_timer(&self->final_timer);
+
+               /* Set new link parameters */
+               irlap_apply_default_connection_parameters(self);
+
+               /* Always switch state before calling upper layers */
+               irlap_next_state(self, LAP_NDM);
+
+               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+               break;
+       case FINAL_TIMER_EXPIRED:
+               if (self->retry_count < self->N3) {
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_disc_frame(self);
+                       irlap_start_final_timer(self, self->final_timeout);
+                       self->retry_count++;
+                       /* Keep state */
+               } else {
+                       irlap_apply_default_connection_parameters(self);
+
+                       /*  Always switch state before calling upper layers */
+                       irlap_next_state(self, LAP_NDM);
+
+                       irlap_disconnect_indication(self, LAP_NO_RESPONSE);
+               }
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d\n", __func__, event);
+
+               ret = -1;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_nrm_p (self, event, skb, info)
+ *
+ *   NRM_P (Normal Response Mode as Primary), The primary station has given
+ *   permissions to a secondary station to transmit IrLAP resonse frames
+ *   (by sending a frame with the P bit set). The primary station will not
+ *   transmit any frames and is expecting to receive frames only from the
+ *   secondary to which transmission permissions has been given.
+ */
+static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
+                            struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+       int ns_status;
+       int nr_status;
+
+       switch (event) {
+       case RECV_I_RSP: /* Optimize for the common case */
+               if (unlikely(skb->len <= LAP_ADDR_HEADER + LAP_CTRL_HEADER)) {
+                       /*
+                        * Input validation check: a stir4200/mcp2150
+                        * combination sometimes results in an empty i:rsp.
+                        * This makes no sense; we can just ignore the frame
+                        * and send an rr:cmd immediately. This happens before
+                        * changing nr or ns so triggers a retransmit
+                        */
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_rr_frame(self, CMD_FRAME);
+                       /* Keep state */
+                       break;
+               }
+               /* FIXME: must check for remote_busy below */
+#ifdef CONFIG_IRDA_FAST_RR
+               /*
+                *  Reset the fast_RR so we can use the fast RR code with
+                *  full speed the next time since peer may have more frames
+                *  to transmitt
+                */
+               self->fast_RR = FALSE;
+#endif /* CONFIG_IRDA_FAST_RR */
+               IRDA_ASSERT( info != NULL, return -1;);
+
+               ns_status = irlap_validate_ns_received(self, info->ns);
+               nr_status = irlap_validate_nr_received(self, info->nr);
+
+               /*
+                *  Check for expected I(nformation) frame
+                */
+               if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) {
+
+                       /* Update Vr (next frame for us to receive) */
+                       self->vr = (self->vr + 1) % 8;
+
+                       /* Update Nr received, cleanup our retry queue */
+                       irlap_update_nr_received(self, info->nr);
+
+                       /*
+                        *  Got expected NR, so reset the
+                        *  retry_count. This is not done by IrLAP spec,
+                        *  which is strange!
+                        */
+                       self->retry_count = 0;
+                       self->ack_required = TRUE;
+
+                       /*  poll bit cleared?  */
+                       if (!info->pf) {
+                               /* Keep state, do not move this line */
+                               irlap_next_state(self, LAP_NRM_P);
+
+                               irlap_data_indication(self, skb, FALSE);
+                       } else {
+                               /* No longer waiting for pf */
+                               del_timer(&self->final_timer);
+
+                               irlap_wait_min_turn_around(self, &self->qos_tx);
+
+                               /* Call higher layer *before* changing state
+                                * to give them a chance to send data in the
+                                * next LAP frame.
+                                * Jean II */
+                               irlap_data_indication(self, skb, FALSE);
+
+                               /* XMIT states are the most dangerous state
+                                * to be in, because user requests are
+                                * processed directly and may change state.
+                                * On the other hand, in NDM_P, those
+                                * requests are queued and we will process
+                                * them when we return to irlap_do_event().
+                                * Jean II
+                                */
+                               irlap_next_state(self, LAP_XMIT_P);
+
+                               /* This is the last frame.
+                                * Make sure it's always called in XMIT state.
+                                * - Jean II */
+                               irlap_start_poll_timer(self, self->poll_timeout);
+                       }
+                       break;
+
+               }
+               /* Unexpected next to send (Ns) */
+               if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED))
+               {
+                       if (!info->pf) {
+                               irlap_update_nr_received(self, info->nr);
+
+                               /*
+                                *  Wait until the last frame before doing
+                                *  anything
+                                */
+
+                               /* Keep state */
+                               irlap_next_state(self, LAP_NRM_P);
+                       } else {
+                               pr_debug("%s(), missing or duplicate frame!\n",
+                                        __func__);
+
+                               /* Update Nr received */
+                               irlap_update_nr_received(self, info->nr);
+
+                               irlap_wait_min_turn_around(self, &self->qos_tx);
+                               irlap_send_rr_frame(self, CMD_FRAME);
+
+                               self->ack_required = FALSE;
+
+                               irlap_start_final_timer(self, self->final_timeout);
+                               irlap_next_state(self, LAP_NRM_P);
+                       }
+                       break;
+               }
+               /*
+                *  Unexpected next to receive (Nr)
+                */
+               if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
+               {
+                       if (info->pf) {
+                               self->vr = (self->vr + 1) % 8;
+
+                               /* Update Nr received */
+                               irlap_update_nr_received(self, info->nr);
+
+                               /* Resend rejected frames */
+                               irlap_resend_rejected_frames(self, CMD_FRAME);
+
+                               self->ack_required = FALSE;
+
+                               /* Make sure we account for the time
+                                * to transmit our frames. See comemnts
+                                * in irlap_send_data_primary_poll().
+                                * Jean II */
+                               irlap_start_final_timer(self, 2 * self->final_timeout);
+
+                               /* Keep state, do not move this line */
+                               irlap_next_state(self, LAP_NRM_P);
+
+                               irlap_data_indication(self, skb, FALSE);
+                       } else {
+                               /*
+                                *  Do not resend frames until the last
+                                *  frame has arrived from the other
+                                *  device. This is not documented in
+                                *  IrLAP!!
+                                */
+                               self->vr = (self->vr + 1) % 8;
+
+                               /* Update Nr received */
+                               irlap_update_nr_received(self, info->nr);
+
+                               self->ack_required = FALSE;
+
+                               /* Keep state, do not move this line!*/
+                               irlap_next_state(self, LAP_NRM_P);
+
+                               irlap_data_indication(self, skb, FALSE);
+                       }
+                       break;
+               }
+               /*
+                *  Unexpected next to send (Ns) and next to receive (Nr)
+                *  Not documented by IrLAP!
+                */
+               if ((ns_status == NS_UNEXPECTED) &&
+                   (nr_status == NR_UNEXPECTED))
+               {
+                       pr_debug("%s(), unexpected nr and ns!\n",
+                                __func__);
+                       if (info->pf) {
+                               /* Resend rejected frames */
+                               irlap_resend_rejected_frames(self, CMD_FRAME);
+
+                               /* Give peer some time to retransmit!
+                                * But account for our own Tx. */
+                               irlap_start_final_timer(self, 2 * self->final_timeout);
+
+                               /* Keep state, do not move this line */
+                               irlap_next_state(self, LAP_NRM_P);
+                       } else {
+                               /* Update Nr received */
+                               /* irlap_update_nr_received( info->nr); */
+
+                               self->ack_required = FALSE;
+                       }
+                       break;
+               }
+
+               /*
+                *  Invalid NR or NS
+                */
+               if ((nr_status == NR_INVALID) || (ns_status == NS_INVALID)) {
+                       if (info->pf) {
+                               del_timer(&self->final_timer);
+
+                               irlap_next_state(self, LAP_RESET_WAIT);
+
+                               irlap_disconnect_indication(self, LAP_RESET_INDICATION);
+                               self->xmitflag = TRUE;
+                       } else {
+                               del_timer(&self->final_timer);
+
+                               irlap_disconnect_indication(self, LAP_RESET_INDICATION);
+
+                               self->xmitflag = FALSE;
+                       }
+                       break;
+               }
+               pr_debug("%s(), Not implemented!\n", __func__);
+               pr_debug("%s(), event=%s, ns_status=%d, nr_status=%d\n",
+                        __func__, irlap_event[event], ns_status, nr_status);
+               break;
+       case RECV_UI_FRAME:
+               /* Poll bit cleared? */
+               if (!info->pf) {
+                       irlap_data_indication(self, skb, TRUE);
+                       irlap_next_state(self, LAP_NRM_P);
+               } else {
+                       del_timer(&self->final_timer);
+                       irlap_data_indication(self, skb, TRUE);
+                       irlap_next_state(self, LAP_XMIT_P);
+                       pr_debug("%s: RECV_UI_FRAME: next state %s\n",
+                                __func__, irlap_state[self->state]);
+                       irlap_start_poll_timer(self, self->poll_timeout);
+               }
+               break;
+       case RECV_RR_RSP:
+               /*
+                *  If you get a RR, the remote isn't busy anymore,
+                *  no matter what the NR
+                */
+               self->remote_busy = FALSE;
+
+               /* Stop final timer */
+               del_timer(&self->final_timer);
+
+               /*
+                *  Nr as expected?
+                */
+               ret = irlap_validate_nr_received(self, info->nr);
+               if (ret == NR_EXPECTED) {
+                       /* Update Nr received */
+                       irlap_update_nr_received(self, info->nr);
+
+                       /*
+                        *  Got expected NR, so reset the retry_count. This
+                        *  is not done by the IrLAP standard , which is
+                        *  strange! DB.
+                        */
+                       self->retry_count = 0;
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+
+                       irlap_next_state(self, LAP_XMIT_P);
+
+                       /* Start poll timer */
+                       irlap_start_poll_timer(self, self->poll_timeout);
+               } else if (ret == NR_UNEXPECTED) {
+                       IRDA_ASSERT(info != NULL, return -1;);
+                       /*
+                        *  Unexpected nr!
+                        */
+
+                       /* Update Nr received */
+                       irlap_update_nr_received(self, info->nr);
+
+                       pr_debug("RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n",
+                                self->retry_count, info->nr, self->va,
+                                self->vs, self->vr);
+
+                       /* Resend rejected frames */
+                       irlap_resend_rejected_frames(self, CMD_FRAME);
+                       irlap_start_final_timer(self, self->final_timeout * 2);
+
+                       irlap_next_state(self, LAP_NRM_P);
+               } else if (ret == NR_INVALID) {
+                       pr_debug("%s(), Received RR with invalid nr !\n",
+                                __func__);
+
+                       irlap_next_state(self, LAP_RESET_WAIT);
+
+                       irlap_disconnect_indication(self, LAP_RESET_INDICATION);
+                       self->xmitflag = TRUE;
+               }
+               break;
+       case RECV_RNR_RSP:
+               IRDA_ASSERT(info != NULL, return -1;);
+
+               /* Stop final timer */
+               del_timer(&self->final_timer);
+               self->remote_busy = TRUE;
+
+               /* Update Nr received */
+               irlap_update_nr_received(self, info->nr);
+               irlap_next_state(self, LAP_XMIT_P);
+
+               /* Start poll timer */
+               irlap_start_poll_timer(self, self->poll_timeout);
+               break;
+       case RECV_FRMR_RSP:
+               del_timer(&self->final_timer);
+               self->xmitflag = TRUE;
+               irlap_next_state(self, LAP_RESET_WAIT);
+               irlap_reset_indication(self);
+               break;
+       case FINAL_TIMER_EXPIRED:
+               /*
+                *  We are allowed to wait for additional 300 ms if
+                *  final timer expires when we are in the middle
+                *  of receiving a frame (page 45, IrLAP). Check that
+                *  we only do this once for each frame.
+                */
+               if (irda_device_is_receiving(self->netdev) && !self->add_wait) {
+                       pr_debug("FINAL_TIMER_EXPIRED when receiving a frame! Waiting a little bit more!\n");
+                       irlap_start_final_timer(self, msecs_to_jiffies(300));
+
+                       /*
+                        *  Don't allow this to happen one more time in a row,
+                        *  or else we can get a pretty tight loop here if
+                        *  if we only receive half a frame. DB.
+                        */
+                       self->add_wait = TRUE;
+                       break;
+               }
+               self->add_wait = FALSE;
+
+               /* N2 is the disconnect timer. Until we reach it, we retry */
+               if (self->retry_count < self->N2) {
+                       if (skb_peek(&self->wx_list) == NULL) {
+                               /* Retry sending the pf bit to the secondary */
+                               pr_debug("nrm_p: resending rr");
+                               irlap_wait_min_turn_around(self, &self->qos_tx);
+                               irlap_send_rr_frame(self, CMD_FRAME);
+                       } else {
+                               pr_debug("nrm_p: resend frames");
+                               irlap_resend_rejected_frames(self, CMD_FRAME);
+                       }
+
+                       irlap_start_final_timer(self, self->final_timeout);
+                       self->retry_count++;
+                       pr_debug("irlap_state_nrm_p: FINAL_TIMER_EXPIRED: retry_count=%d\n",
+                                self->retry_count);
+
+                       /* Early warning event. I'm using a pretty liberal
+                        * interpretation of the spec and generate an event
+                        * every time the timer is multiple of N1 (and not
+                        * only the first time). This allow application
+                        * to know precisely if connectivity restart...
+                        * Jean II */
+                       if((self->retry_count % self->N1) == 0)
+                               irlap_status_indication(self,
+                                                       STATUS_NO_ACTIVITY);
+
+                       /* Keep state */
+               } else {
+                       irlap_apply_default_connection_parameters(self);
+
+                       /* Always switch state before calling upper layers */
+                       irlap_next_state(self, LAP_NDM);
+                       irlap_disconnect_indication(self, LAP_NO_RESPONSE);
+               }
+               break;
+       case RECV_REJ_RSP:
+               irlap_update_nr_received(self, info->nr);
+               if (self->remote_busy) {
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_rr_frame(self, CMD_FRAME);
+               } else
+                       irlap_resend_rejected_frames(self, CMD_FRAME);
+               irlap_start_final_timer(self, 2 * self->final_timeout);
+               break;
+       case RECV_SREJ_RSP:
+               irlap_update_nr_received(self, info->nr);
+               if (self->remote_busy) {
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_rr_frame(self, CMD_FRAME);
+               } else
+                       irlap_resend_rejected_frame(self, CMD_FRAME);
+               irlap_start_final_timer(self, 2 * self->final_timeout);
+               break;
+       case RECV_RD_RSP:
+               pr_debug("%s(), RECV_RD_RSP\n", __func__);
+
+               irlap_flush_all_queues(self);
+               irlap_next_state(self, LAP_XMIT_P);
+               /* Call back the LAP state machine to do a proper disconnect */
+               irlap_disconnect_request(self);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n",
+                        __func__, irlap_event[event]);
+
+               ret = -1;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_reset_wait (event, skb, info)
+ *
+ *    We have informed the service user of a reset condition, and is
+ *    awaiting reset of disconnect request.
+ *
+ */
+static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
+                                 struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s(), event = %s\n", __func__, irlap_event[event]);
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case RESET_REQUEST:
+               if (self->xmitflag) {
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_snrm_frame(self, NULL);
+                       irlap_start_final_timer(self, self->final_timeout);
+                       irlap_next_state(self, LAP_RESET);
+               } else {
+                       irlap_start_final_timer(self, self->final_timeout);
+                       irlap_next_state(self, LAP_RESET);
+               }
+               break;
+       case DISCONNECT_REQUEST:
+               irlap_wait_min_turn_around( self, &self->qos_tx);
+               irlap_send_disc_frame( self);
+               irlap_flush_all_queues( self);
+               irlap_start_final_timer( self, self->final_timeout);
+               self->retry_count = 0;
+               irlap_next_state( self, LAP_PCLOSE);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n", __func__,
+                        irlap_event[event]);
+
+               ret = -1;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_reset (self, event, skb, info)
+ *
+ *    We have sent a SNRM reset command to the peer layer, and is awaiting
+ *    reply.
+ *
+ */
+static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
+                            struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s(), event = %s\n", __func__, irlap_event[event]);
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case RECV_DISC_CMD:
+               del_timer(&self->final_timer);
+
+               irlap_apply_default_connection_parameters(self);
+
+               /* Always switch state before calling upper layers */
+               irlap_next_state(self, LAP_NDM);
+
+               irlap_disconnect_indication(self, LAP_NO_RESPONSE);
+
+               break;
+       case RECV_UA_RSP:
+               del_timer(&self->final_timer);
+
+               /* Initiate connection state */
+               irlap_initiate_connection_state(self);
+
+               irlap_reset_confirm();
+
+               self->remote_busy = FALSE;
+
+               irlap_next_state(self, LAP_XMIT_P);
+
+               irlap_start_poll_timer(self, self->poll_timeout);
+
+               break;
+       case FINAL_TIMER_EXPIRED:
+               if (self->retry_count < 3) {
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+
+                       IRDA_ASSERT(self->netdev != NULL, return -1;);
+                       irlap_send_snrm_frame(self, self->qos_dev);
+
+                       self->retry_count++; /* Experimental!! */
+
+                       irlap_start_final_timer(self, self->final_timeout);
+                       irlap_next_state(self, LAP_RESET);
+               } else if (self->retry_count >= self->N3) {
+                       irlap_apply_default_connection_parameters(self);
+
+                       /* Always switch state before calling upper layers */
+                       irlap_next_state(self, LAP_NDM);
+
+                       irlap_disconnect_indication(self, LAP_NO_RESPONSE);
+               }
+               break;
+       case RECV_SNRM_CMD:
+               /*
+                * SNRM frame is not allowed to contain an I-field in this
+                * state
+                */
+               if (!info) {
+                       pr_debug("%s(), RECV_SNRM_CMD\n", __func__);
+                       irlap_initiate_connection_state(self);
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_ua_response_frame(self, &self->qos_rx);
+                       irlap_reset_confirm();
+                       irlap_start_wd_timer(self, self->wd_timeout);
+                       irlap_next_state(self, LAP_NDM);
+               } else {
+                       pr_debug("%s(), SNRM frame contained an I field!\n",
+                                __func__);
+               }
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n",
+                        __func__, irlap_event[event]);
+
+               ret = -1;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_xmit_s (event, skb, info)
+ *
+ *   XMIT_S, The secondary station has been given the right to transmit,
+ *   and we therefore do not expect to receive any transmissions from other
+ *   stations.
+ */
+static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
+                             struct sk_buff *skb, struct irlap_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
+
+       IRDA_ASSERT(self != NULL, return -ENODEV;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
+
+       switch (event) {
+       case SEND_I_CMD:
+               /*
+                *  Send frame only if send window > 0
+                */
+               if ((self->window > 0) && (!self->remote_busy)) {
+                       int nextfit;
+#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
+                       struct sk_buff *skb_next;
+
+                       /*
+                        * Same deal as in irlap_state_xmit_p(), so see
+                        * the comments at that point.
+                        * We are the secondary, so there are only subtle
+                        * differences. - Jean II
+                        */
+
+                       /* Check if a subsequent skb exist and would fit in
+                        * the current window (with respect to turnaround
+                        * time). - Jean II */
+                       skb_next = skb_peek(&self->txq);
+                       nextfit = ((skb_next != NULL) &&
+                                  ((skb_next->len + skb->len) <=
+                                   self->bytes_left));
+
+                       /*
+                        *  Test if we have transmitted more bytes over the
+                        *  link than its possible to do with the current
+                        *  speed and turn-around-time.
+                        */
+                       if((!nextfit) && (skb->len > self->bytes_left)) {
+                               pr_debug("%s(), Not allowed to transmit more bytes!\n",
+                                        __func__);
+                               /* Requeue the skb */
+                               skb_queue_head(&self->txq, skb_get(skb));
+
+                               /*
+                                *  Switch to NRM_S, this is only possible
+                                *  when we are in secondary mode, since we
+                                *  must be sure that we don't miss any RR
+                                *  frames
+                                */
+                               self->window = self->window_size;
+                               self->bytes_left = self->line_capacity;
+                               irlap_start_wd_timer(self, self->wd_timeout);
+
+                               irlap_next_state(self, LAP_NRM_S);
+                               /* Slight difference with primary :
+                                * here we would wait for the other side to
+                                * expire the turnaround. - Jean II */
+
+                               return -EPROTO; /* Try again later */
+                       }
+                       /* Subtract space used by this skb */
+                       self->bytes_left -= skb->len;
+#else  /* CONFIG_IRDA_DYNAMIC_WINDOW */
+                       /* Window has been adjusted for the max packet
+                        * size, so much simpler... - Jean II */
+                       nextfit = !skb_queue_empty(&self->txq);
+#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+                       /*
+                        *  Send data with final bit cleared only if window > 1
+                        *  and there is more frames to be sent
+                        */
+                       if ((self->window > 1) && (nextfit)) {
+                               irlap_send_data_secondary(self, skb);
+                               irlap_next_state(self, LAP_XMIT_S);
+                       } else {
+                               irlap_send_data_secondary_final(self, skb);
+                               irlap_next_state(self, LAP_NRM_S);
+
+                               /*
+                                * Make sure state machine does not try to send
+                                * any more frames
+                                */
+                               ret = -EPROTO;
+                       }
+               } else {
+                       pr_debug("%s(), Unable to send!\n", __func__);
+                       skb_queue_head(&self->txq, skb_get(skb));
+                       ret = -EPROTO;
+               }
+               break;
+       case DISCONNECT_REQUEST:
+               irlap_send_rd_frame(self);
+               irlap_flush_all_queues(self);
+               irlap_start_wd_timer(self, self->wd_timeout);
+               irlap_next_state(self, LAP_SCLOSE);
+               break;
+       case DATA_REQUEST:
+               /* Nothing to do, irlap_do_event() will send the packet
+                * when we return... - Jean II */
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n", __func__,
+                        irlap_event[event]);
+
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_nrm_s (event, skb, info)
+ *
+ *    NRM_S (Normal Response Mode as Secondary) state, in this state we are
+ *    expecting to receive frames from the primary station
+ *
+ */
+static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
+                            struct sk_buff *skb, struct irlap_info *info)
+{
+       int ns_status;
+       int nr_status;
+       int ret = 0;
+
+       pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       switch (event) {
+       case RECV_I_CMD: /* Optimize for the common case */
+               /* FIXME: must check for remote_busy below */
+               pr_debug("%s(), event=%s nr=%d, vs=%d, ns=%d, vr=%d, pf=%d\n",
+                        __func__, irlap_event[event], info->nr,
+                        self->vs, info->ns, self->vr, info->pf);
+
+               self->retry_count = 0;
+
+               ns_status = irlap_validate_ns_received(self, info->ns);
+               nr_status = irlap_validate_nr_received(self, info->nr);
+               /*
+                *  Check for expected I(nformation) frame
+                */
+               if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) {
+
+                       /* Update Vr (next frame for us to receive) */
+                       self->vr = (self->vr + 1) % 8;
+
+                       /* Update Nr received */
+                       irlap_update_nr_received(self, info->nr);
+
+                       /*
+                        *  poll bit cleared?
+                        */
+                       if (!info->pf) {
+
+                               self->ack_required = TRUE;
+
+                               /*
+                                *  Starting WD-timer here is optional, but
+                                *  not recommended. Note 6 IrLAP p. 83
+                                */
+#if 0
+                               irda_start_timer(WD_TIMER, self->wd_timeout);
+#endif
+                               /* Keep state, do not move this line */
+                               irlap_next_state(self, LAP_NRM_S);
+
+                               irlap_data_indication(self, skb, FALSE);
+                               break;
+                       } else {
+                               /*
+                                *  We should wait before sending RR, and
+                                *  also before changing to XMIT_S
+                                *  state. (note 1, IrLAP p. 82)
+                                */
+                               irlap_wait_min_turn_around(self, &self->qos_tx);
+
+                               /*
+                                * Give higher layers a chance to
+                                * immediately reply with some data before
+                                * we decide if we should send a RR frame
+                                * or not
+                                */
+                               irlap_data_indication(self, skb, FALSE);
+
+                               /* Any pending data requests?  */
+                               if (!skb_queue_empty(&self->txq) &&
+                                   (self->window > 0))
+                               {
+                                       self->ack_required = TRUE;
+
+                                       del_timer(&self->wd_timer);
+
+                                       irlap_next_state(self, LAP_XMIT_S);
+                               } else {
+                                       irlap_send_rr_frame(self, RSP_FRAME);
+                                       irlap_start_wd_timer(self,
+                                                            self->wd_timeout);
+
+                                       /* Keep the state */
+                                       irlap_next_state(self, LAP_NRM_S);
+                               }
+                               break;
+                       }
+               }
+               /*
+                *  Check for Unexpected next to send (Ns)
+                */
+               if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED))
+               {
+                       /* Unexpected next to send, with final bit cleared */
+                       if (!info->pf) {
+                               irlap_update_nr_received(self, info->nr);
+
+                               irlap_start_wd_timer(self, self->wd_timeout);
+                       } else {
+                               /* Update Nr received */
+                               irlap_update_nr_received(self, info->nr);
+
+                               irlap_wait_min_turn_around(self, &self->qos_tx);
+                               irlap_send_rr_frame(self, RSP_FRAME);
+
+                               irlap_start_wd_timer(self, self->wd_timeout);
+                       }
+                       break;
+               }
+
+               /*
+                *  Unexpected Next to Receive(NR) ?
+                */
+               if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
+               {
+                       if (info->pf) {
+                               pr_debug("RECV_I_RSP: frame(s) lost\n");
+
+                               self->vr = (self->vr + 1) % 8;
+
+                               /* Update Nr received */
+                               irlap_update_nr_received(self, info->nr);
+
+                               /* Resend rejected frames */
+                               irlap_resend_rejected_frames(self, RSP_FRAME);
+
+                               /* Keep state, do not move this line */
+                               irlap_next_state(self, LAP_NRM_S);
+
+                               irlap_data_indication(self, skb, FALSE);
+                               irlap_start_wd_timer(self, self->wd_timeout);
+                               break;
+                       }
+                       /*
+                        *  This is not documented in IrLAP!! Unexpected NR
+                        *  with poll bit cleared
+                        */
+                       if (!info->pf) {
+                               self->vr = (self->vr + 1) % 8;
+
+                               /* Update Nr received */
+                               irlap_update_nr_received(self, info->nr);
+
+                               /* Keep state, do not move this line */
+                               irlap_next_state(self, LAP_NRM_S);
+
+                               irlap_data_indication(self, skb, FALSE);
+                               irlap_start_wd_timer(self, self->wd_timeout);
+                       }
+                       break;
+               }
+
+               if (ret == NR_INVALID) {
+                       pr_debug("NRM_S, NR_INVALID not implemented!\n");
+               }
+               if (ret == NS_INVALID) {
+                       pr_debug("NRM_S, NS_INVALID not implemented!\n");
+               }
+               break;
+       case RECV_UI_FRAME:
+               /*
+                *  poll bit cleared?
+                */
+               if (!info->pf) {
+                       irlap_data_indication(self, skb, TRUE);
+                       irlap_next_state(self, LAP_NRM_S); /* Keep state */
+               } else {
+                       /*
+                        *  Any pending data requests?
+                        */
+                       if (!skb_queue_empty(&self->txq) &&
+                           (self->window > 0) && !self->remote_busy)
+                       {
+                               irlap_data_indication(self, skb, TRUE);
+
+                               del_timer(&self->wd_timer);
+
+                               irlap_next_state(self, LAP_XMIT_S);
+                       } else {
+                               irlap_data_indication(self, skb, TRUE);
+
+                               irlap_wait_min_turn_around(self, &self->qos_tx);
+
+                               irlap_send_rr_frame(self, RSP_FRAME);
+                               self->ack_required = FALSE;
+
+                               irlap_start_wd_timer(self, self->wd_timeout);
+
+                               /* Keep the state */
+                               irlap_next_state(self, LAP_NRM_S);
+                       }
+               }
+               break;
+       case RECV_RR_CMD:
+               self->retry_count = 0;
+
+               /*
+                *  Nr as expected?
+                */
+               nr_status = irlap_validate_nr_received(self, info->nr);
+               if (nr_status == NR_EXPECTED) {
+                       if (!skb_queue_empty(&self->txq) &&
+                           (self->window > 0)) {
+                               self->remote_busy = FALSE;
+
+                               /* Update Nr received */
+                               irlap_update_nr_received(self, info->nr);
+                               del_timer(&self->wd_timer);
+
+                               irlap_wait_min_turn_around(self, &self->qos_tx);
+                               irlap_next_state(self, LAP_XMIT_S);
+                       } else {
+                               self->remote_busy = FALSE;
+                               /* Update Nr received */
+                               irlap_update_nr_received(self, info->nr);
+                               irlap_wait_min_turn_around(self, &self->qos_tx);
+                               irlap_start_wd_timer(self, self->wd_timeout);
+
+                               /* Note : if the link is idle (this case),
+                                * we never go in XMIT_S, so we never get a
+                                * chance to process any DISCONNECT_REQUEST.
+                                * Do it now ! - Jean II */
+                               if (self->disconnect_pending) {
+                                       /* Disconnect */
+                                       irlap_send_rd_frame(self);
+                                       irlap_flush_all_queues(self);
+
+                                       irlap_next_state(self, LAP_SCLOSE);
+                               } else {
+                                       /* Just send back pf bit */
+                                       irlap_send_rr_frame(self, RSP_FRAME);
+
+                                       irlap_next_state(self, LAP_NRM_S);
+                               }
+                       }
+               } else if (nr_status == NR_UNEXPECTED) {
+                       self->remote_busy = FALSE;
+                       irlap_update_nr_received(self, info->nr);
+                       irlap_resend_rejected_frames(self, RSP_FRAME);
+
+                       irlap_start_wd_timer(self, self->wd_timeout);
+
+                       /* Keep state */
+                       irlap_next_state(self, LAP_NRM_S);
+               } else {
+                       pr_debug("%s(), invalid nr not implemented!\n",
+                                __func__);
+               }
+               break;
+       case RECV_SNRM_CMD:
+               /* SNRM frame is not allowed to contain an I-field */
+               if (!info) {
+                       del_timer(&self->wd_timer);
+                       pr_debug("%s(), received SNRM cmd\n", __func__);
+                       irlap_next_state(self, LAP_RESET_CHECK);
+
+                       irlap_reset_indication(self);
+               } else {
+                       pr_debug("%s(), SNRM frame contained an I-field!\n",
+                                __func__);
+
+               }
+               break;
+       case RECV_REJ_CMD:
+               irlap_update_nr_received(self, info->nr);
+               if (self->remote_busy) {
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_rr_frame(self, RSP_FRAME);
+               } else
+                       irlap_resend_rejected_frames(self, RSP_FRAME);
+               irlap_start_wd_timer(self, self->wd_timeout);
+               break;
+       case RECV_SREJ_CMD:
+               irlap_update_nr_received(self, info->nr);
+               if (self->remote_busy) {
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_rr_frame(self, RSP_FRAME);
+               } else
+                       irlap_resend_rejected_frame(self, RSP_FRAME);
+               irlap_start_wd_timer(self, self->wd_timeout);
+               break;
+       case WD_TIMER_EXPIRED:
+               /*
+                *  Wait until retry_count * n matches negotiated threshold/
+                *  disconnect time (note 2 in IrLAP p. 82)
+                *
+                * Similar to irlap_state_nrm_p() -> FINAL_TIMER_EXPIRED
+                * Note : self->wd_timeout = (self->final_timeout * 2),
+                *   which explain why we use (self->N2 / 2) here !!!
+                * Jean II
+                */
+               pr_debug("%s(), retry_count = %d\n", __func__,
+                        self->retry_count);
+
+               if (self->retry_count < (self->N2 / 2)) {
+                       /* No retry, just wait for primary */
+                       irlap_start_wd_timer(self, self->wd_timeout);
+                       self->retry_count++;
+
+                       if((self->retry_count % (self->N1 / 2)) == 0)
+                               irlap_status_indication(self,
+                                                       STATUS_NO_ACTIVITY);
+               } else {
+                       irlap_apply_default_connection_parameters(self);
+
+                       /* Always switch state before calling upper layers */
+                       irlap_next_state(self, LAP_NDM);
+                       irlap_disconnect_indication(self, LAP_NO_RESPONSE);
+               }
+               break;
+       case RECV_DISC_CMD:
+               /* Always switch state before calling upper layers */
+               irlap_next_state(self, LAP_NDM);
+
+               /* Send disconnect response */
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+               irlap_send_ua_response_frame(self, NULL);
+
+               del_timer(&self->wd_timer);
+               irlap_flush_all_queues(self);
+               /* Set default link parameters */
+               irlap_apply_default_connection_parameters(self);
+
+               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+               break;
+       case RECV_DISCOVERY_XID_CMD:
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+               irlap_send_rr_frame(self, RSP_FRAME);
+               self->ack_required = TRUE;
+               irlap_start_wd_timer(self, self->wd_timeout);
+               irlap_next_state(self, LAP_NRM_S);
+
+               break;
+       case RECV_TEST_CMD:
+               /* Remove test frame header (only LAP header in NRM) */
+               skb_pull(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
+
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+               irlap_start_wd_timer(self, self->wd_timeout);
+
+               /* Send response (info will be copied) */
+               irlap_send_test_frame(self, self->caddr, info->daddr, skb);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
+                        event, irlap_event[event]);
+
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlap_state_sclose (self, event, skb, info)
+ */
+static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event,
+                             struct sk_buff *skb, struct irlap_info *info)
+{
+       IRDA_ASSERT(self != NULL, return -ENODEV;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
+
+       switch (event) {
+       case RECV_DISC_CMD:
+               /* Always switch state before calling upper layers */
+               irlap_next_state(self, LAP_NDM);
+
+               /* Send disconnect response */
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+               irlap_send_ua_response_frame(self, NULL);
+
+               del_timer(&self->wd_timer);
+               /* Set default link parameters */
+               irlap_apply_default_connection_parameters(self);
+
+               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+               break;
+       case RECV_DM_RSP:
+               /* IrLAP-1.1 p.82: in SCLOSE, S and I type RSP frames
+                * shall take us down into default NDM state, like DM_RSP
+                */
+       case RECV_RR_RSP:
+       case RECV_RNR_RSP:
+       case RECV_REJ_RSP:
+       case RECV_SREJ_RSP:
+       case RECV_I_RSP:
+               /* Always switch state before calling upper layers */
+               irlap_next_state(self, LAP_NDM);
+
+               del_timer(&self->wd_timer);
+               irlap_apply_default_connection_parameters(self);
+
+               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+               break;
+       case WD_TIMER_EXPIRED:
+               /* Always switch state before calling upper layers */
+               irlap_next_state(self, LAP_NDM);
+
+               irlap_apply_default_connection_parameters(self);
+
+               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+               break;
+       default:
+               /* IrLAP-1.1 p.82: in SCLOSE, basically any received frame
+                * with pf=1 shall restart the wd-timer and resend the rd:rsp
+                */
+               if (info != NULL  &&  info->pf) {
+                       del_timer(&self->wd_timer);
+                       irlap_wait_min_turn_around(self, &self->qos_tx);
+                       irlap_send_rd_frame(self);
+                       irlap_start_wd_timer(self, self->wd_timeout);
+                       break;          /* stay in SCLOSE */
+               }
+
+               pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
+                        event, irlap_event[event]);
+
+               break;
+       }
+
+       return -1;
+}
+
+static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event,
+                                  struct sk_buff *skb,
+                                  struct irlap_info *info)
+{
+       int ret = 0;
+
+       pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
+
+       IRDA_ASSERT(self != NULL, return -ENODEV;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
+
+       switch (event) {
+       case RESET_RESPONSE:
+               irlap_send_ua_response_frame(self, &self->qos_rx);
+               irlap_initiate_connection_state(self);
+               irlap_start_wd_timer(self, WD_TIMEOUT);
+               irlap_flush_all_queues(self);
+
+               irlap_next_state(self, LAP_NRM_S);
+               break;
+       case DISCONNECT_REQUEST:
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+               irlap_send_rd_frame(self);
+               irlap_start_wd_timer(self, WD_TIMEOUT);
+               irlap_next_state(self, LAP_SCLOSE);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
+                        event, irlap_event[event]);
+
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
diff --git a/drivers/staging/irda/net/irlap_frame.c b/drivers/staging/irda/net/irlap_frame.c
new file mode 100644 (file)
index 0000000..debda3d
--- /dev/null
@@ -0,0 +1,1407 @@
+/*********************************************************************
+ *
+ * Filename:      irlap_frame.c
+ * Version:       1.0
+ * Description:   Build and transmit IrLAP frames
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Tue Aug 19 10:27:26 1997
+ * Modified at:   Wed Jan  5 08:59:04 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/skbuff.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/irda.h>
+#include <linux/slab.h>
+
+#include <net/pkt_sched.h>
+#include <net/sock.h>
+
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/irlap.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/timer.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/qos.h>
+
+static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb,
+                              int command);
+
+/*
+ * Function irlap_insert_info (self, skb)
+ *
+ *    Insert minimum turnaround time and speed information into the skb. We
+ *    need to do this since it's per packet relevant information. Safe to
+ *    have this function inlined since it's only called from one place
+ */
+static inline void irlap_insert_info(struct irlap_cb *self,
+                                    struct sk_buff *skb)
+{
+       struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
+
+       /*
+        * Insert MTT (min. turn time) and speed into skb, so that the
+        * device driver knows which settings to use
+        */
+       cb->magic = LAP_MAGIC;
+       cb->mtt = self->mtt_required;
+       cb->next_speed = self->speed;
+
+       /* Reset */
+       self->mtt_required = 0;
+
+       /*
+        * Delay equals negotiated BOFs count, plus the number of BOFs to
+        * force the negotiated minimum turnaround time
+        */
+       cb->xbofs = self->bofs_count;
+       cb->next_xbofs = self->next_bofs;
+       cb->xbofs_delay = self->xbofs_delay;
+
+       /* Reset XBOF's delay (used only for getting min turn time) */
+       self->xbofs_delay = 0;
+       /* Put the correct xbofs value for the next packet */
+       self->bofs_count = self->next_bofs;
+}
+
+/*
+ * Function irlap_queue_xmit (self, skb)
+ *
+ *    A little wrapper for dev_queue_xmit, so we can insert some common
+ *    code into it.
+ */
+void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb)
+{
+       /* Some common init stuff */
+       skb->dev = self->netdev;
+       skb_reset_mac_header(skb);
+       skb_reset_network_header(skb);
+       skb_reset_transport_header(skb);
+       skb->protocol = htons(ETH_P_IRDA);
+       skb->priority = TC_PRIO_BESTEFFORT;
+
+       irlap_insert_info(self, skb);
+
+       if (unlikely(self->mode & IRDA_MODE_MONITOR)) {
+               pr_debug("%s(): %s is in monitor mode\n", __func__,
+                        self->netdev->name);
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       dev_queue_xmit(skb);
+}
+
+/*
+ * Function irlap_send_snrm_cmd (void)
+ *
+ *    Transmits a connect SNRM command frame
+ */
+void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos)
+{
+       struct sk_buff *tx_skb;
+       struct snrm_frame *frame;
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* Allocate frame */
+       tx_skb = alloc_skb(sizeof(struct snrm_frame) +
+                          IRLAP_NEGOCIATION_PARAMS_LEN,
+                          GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       frame = skb_put(tx_skb, 2);
+
+       /* Insert connection address field */
+       if (qos)
+               frame->caddr = CMD_FRAME | CBROADCAST;
+       else
+               frame->caddr = CMD_FRAME | self->caddr;
+
+       /* Insert control field */
+       frame->control = SNRM_CMD | PF_BIT;
+
+       /*
+        *  If we are establishing a connection then insert QoS parameters
+        */
+       if (qos) {
+               skb_put(tx_skb, 9); /* 25 left */
+               frame->saddr = cpu_to_le32(self->saddr);
+               frame->daddr = cpu_to_le32(self->daddr);
+
+               frame->ncaddr = self->caddr;
+
+               ret = irlap_insert_qos_negotiation_params(self, tx_skb);
+               if (ret < 0) {
+                       dev_kfree_skb(tx_skb);
+                       return;
+               }
+       }
+       irlap_queue_xmit(self, tx_skb);
+}
+
+/*
+ * Function irlap_recv_snrm_cmd (skb, info)
+ *
+ *    Received SNRM (Set Normal Response Mode) command frame
+ *
+ */
+static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
+                               struct irlap_info *info)
+{
+       struct snrm_frame *frame;
+
+       if (pskb_may_pull(skb,sizeof(struct snrm_frame))) {
+               frame = (struct snrm_frame *) skb->data;
+
+               /* Copy the new connection address ignoring the C/R bit */
+               info->caddr = frame->ncaddr & 0xFE;
+
+               /* Check if the new connection address is valid */
+               if ((info->caddr == 0x00) || (info->caddr == 0xfe)) {
+                       pr_debug("%s(), invalid connection address!\n",
+                                __func__);
+                       return;
+               }
+
+               /* Copy peer device address */
+               info->daddr = le32_to_cpu(frame->saddr);
+               info->saddr = le32_to_cpu(frame->daddr);
+
+               /* Only accept if addressed directly to us */
+               if (info->saddr != self->saddr) {
+                       pr_debug("%s(), not addressed to us!\n",
+                                __func__);
+                       return;
+               }
+               irlap_do_event(self, RECV_SNRM_CMD, skb, info);
+       } else {
+               /* Signal that this SNRM frame does not contain and I-field */
+               irlap_do_event(self, RECV_SNRM_CMD, skb, NULL);
+       }
+}
+
+/*
+ * Function irlap_send_ua_response_frame (qos)
+ *
+ *    Send UA (Unnumbered Acknowledgement) frame
+ *
+ */
+void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos)
+{
+       struct sk_buff *tx_skb;
+       struct ua_frame *frame;
+       int ret;
+
+       pr_debug("%s() <%ld>\n", __func__, jiffies);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /* Allocate frame */
+       tx_skb = alloc_skb(sizeof(struct ua_frame) +
+                          IRLAP_NEGOCIATION_PARAMS_LEN,
+                          GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       frame = skb_put(tx_skb, 10);
+
+       /* Build UA response */
+       frame->caddr = self->caddr;
+       frame->control = UA_RSP | PF_BIT;
+
+       frame->saddr = cpu_to_le32(self->saddr);
+       frame->daddr = cpu_to_le32(self->daddr);
+
+       /* Should we send QoS negotiation parameters? */
+       if (qos) {
+               ret = irlap_insert_qos_negotiation_params(self, tx_skb);
+               if (ret < 0) {
+                       dev_kfree_skb(tx_skb);
+                       return;
+               }
+       }
+
+       irlap_queue_xmit(self, tx_skb);
+}
+
+
+/*
+ * Function irlap_send_dm_frame (void)
+ *
+ *    Send disconnected mode (DM) frame
+ *
+ */
+void irlap_send_dm_frame( struct irlap_cb *self)
+{
+       struct sk_buff *tx_skb = NULL;
+       struct dm_frame *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       tx_skb = alloc_skb(sizeof(struct dm_frame), GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       frame = skb_put(tx_skb, 2);
+
+       if (self->state == LAP_NDM)
+               frame->caddr = CBROADCAST;
+       else
+               frame->caddr = self->caddr;
+
+       frame->control = DM_RSP | PF_BIT;
+
+       irlap_queue_xmit(self, tx_skb);
+}
+
+/*
+ * Function irlap_send_disc_frame (void)
+ *
+ *    Send disconnect (DISC) frame
+ *
+ */
+void irlap_send_disc_frame(struct irlap_cb *self)
+{
+       struct sk_buff *tx_skb = NULL;
+       struct disc_frame *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       tx_skb = alloc_skb(sizeof(struct disc_frame), GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       frame = skb_put(tx_skb, 2);
+
+       frame->caddr = self->caddr | CMD_FRAME;
+       frame->control = DISC_CMD | PF_BIT;
+
+       irlap_queue_xmit(self, tx_skb);
+}
+
+/*
+ * Function irlap_send_discovery_xid_frame (S, s, command)
+ *
+ *    Build and transmit a XID (eXchange station IDentifier) discovery
+ *    frame.
+ */
+void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
+                                   __u8 command, discovery_t *discovery)
+{
+       struct sk_buff *tx_skb = NULL;
+       struct xid_frame *frame;
+       __u32 bcast = BROADCAST;
+       __u8 *info;
+
+       pr_debug("%s(), s=%d, S=%d, command=%d\n", __func__,
+                s, S, command);
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+       IRDA_ASSERT(discovery != NULL, return;);
+
+       tx_skb = alloc_skb(sizeof(struct xid_frame) + IRLAP_DISCOVERY_INFO_LEN,
+                          GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       skb_put(tx_skb, 14);
+       frame = (struct xid_frame *) tx_skb->data;
+
+       if (command) {
+               frame->caddr = CBROADCAST | CMD_FRAME;
+               frame->control =  XID_CMD | PF_BIT;
+       } else {
+               frame->caddr = CBROADCAST;
+               frame->control =  XID_RSP | PF_BIT;
+       }
+       frame->ident = XID_FORMAT;
+
+       frame->saddr = cpu_to_le32(self->saddr);
+
+       if (command)
+               frame->daddr = cpu_to_le32(bcast);
+       else
+               frame->daddr = cpu_to_le32(discovery->data.daddr);
+
+       switch (S) {
+       case 1:
+               frame->flags = 0x00;
+               break;
+       case 6:
+               frame->flags = 0x01;
+               break;
+       case 8:
+               frame->flags = 0x02;
+               break;
+       case 16:
+               frame->flags = 0x03;
+               break;
+       default:
+               frame->flags = 0x02;
+               break;
+       }
+
+       frame->slotnr = s;
+       frame->version = 0x00;
+
+       /*
+        *  Provide info for final slot only in commands, and for all
+        *  responses. Send the second byte of the hint only if the
+        *  EXTENSION bit is set in the first byte.
+        */
+       if (!command || (frame->slotnr == 0xff)) {
+               int len;
+
+               if (discovery->data.hints[0] & HINT_EXTENSION) {
+                       info = skb_put(tx_skb, 2);
+                       info[0] = discovery->data.hints[0];
+                       info[1] = discovery->data.hints[1];
+               } else {
+                       info = skb_put(tx_skb, 1);
+                       info[0] = discovery->data.hints[0];
+               }
+               info = skb_put(tx_skb, 1);
+               info[0] = discovery->data.charset;
+
+               len = IRDA_MIN(discovery->name_len, skb_tailroom(tx_skb));
+               skb_put_data(tx_skb, discovery->data.info, len);
+       }
+       irlap_queue_xmit(self, tx_skb);
+}
+
+/*
+ * Function irlap_recv_discovery_xid_rsp (skb, info)
+ *
+ *    Received a XID discovery response
+ *
+ */
+static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
+                                        struct sk_buff *skb,
+                                        struct irlap_info *info)
+{
+       struct xid_frame *xid;
+       discovery_t *discovery = NULL;
+       __u8 *discovery_info;
+       char *text;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
+               net_err_ratelimited("%s: frame too short!\n", __func__);
+               return;
+       }
+
+       xid = (struct xid_frame *) skb->data;
+
+       info->daddr = le32_to_cpu(xid->saddr);
+       info->saddr = le32_to_cpu(xid->daddr);
+
+       /* Make sure frame is addressed to us */
+       if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
+               pr_debug("%s(), frame is not addressed to us!\n",
+                        __func__);
+               return;
+       }
+
+       if ((discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) {
+               net_warn_ratelimited("%s: kmalloc failed!\n", __func__);
+               return;
+       }
+
+       discovery->data.daddr = info->daddr;
+       discovery->data.saddr = self->saddr;
+       discovery->timestamp = jiffies;
+
+       pr_debug("%s(), daddr=%08x\n", __func__,
+                discovery->data.daddr);
+
+       discovery_info = skb_pull(skb, sizeof(struct xid_frame));
+
+       /* Get info returned from peer */
+       discovery->data.hints[0] = discovery_info[0];
+       if (discovery_info[0] & HINT_EXTENSION) {
+               pr_debug("EXTENSION\n");
+               discovery->data.hints[1] = discovery_info[1];
+               discovery->data.charset = discovery_info[2];
+               text = (char *) &discovery_info[3];
+       } else {
+               discovery->data.hints[1] = 0;
+               discovery->data.charset = discovery_info[1];
+               text = (char *) &discovery_info[2];
+       }
+       /*
+        *  Terminate info string, should be safe since this is where the
+        *  FCS bytes resides.
+        */
+       skb->data[skb->len] = '\0';
+       strncpy(discovery->data.info, text, NICKNAME_MAX_LEN);
+       discovery->name_len = strlen(discovery->data.info);
+
+       info->discovery = discovery;
+
+       irlap_do_event(self, RECV_DISCOVERY_XID_RSP, skb, info);
+}
+
+/*
+ * Function irlap_recv_discovery_xid_cmd (skb, info)
+ *
+ *    Received a XID discovery command
+ *
+ */
+static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
+                                        struct sk_buff *skb,
+                                        struct irlap_info *info)
+{
+       struct xid_frame *xid;
+       discovery_t *discovery = NULL;
+       __u8 *discovery_info;
+       char *text;
+
+       if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
+               net_err_ratelimited("%s: frame too short!\n", __func__);
+               return;
+       }
+
+       xid = (struct xid_frame *) skb->data;
+
+       info->daddr = le32_to_cpu(xid->saddr);
+       info->saddr = le32_to_cpu(xid->daddr);
+
+       /* Make sure frame is addressed to us */
+       if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
+               pr_debug("%s(), frame is not addressed to us!\n",
+                        __func__);
+               return;
+       }
+
+       switch (xid->flags & 0x03) {
+       case 0x00:
+               info->S = 1;
+               break;
+       case 0x01:
+               info->S = 6;
+               break;
+       case 0x02:
+               info->S = 8;
+               break;
+       case 0x03:
+               info->S = 16;
+               break;
+       default:
+               /* Error!! */
+               return;
+       }
+       info->s = xid->slotnr;
+
+       discovery_info = skb_pull(skb, sizeof(struct xid_frame));
+
+       /*
+        *  Check if last frame
+        */
+       if (info->s == 0xff) {
+               /* Check if things are sane at this point... */
+               if((discovery_info == NULL) ||
+                  !pskb_may_pull(skb, 3)) {
+                       net_err_ratelimited("%s: discovery frame too short!\n",
+                                           __func__);
+                       return;
+               }
+
+               /*
+                *  We now have some discovery info to deliver!
+                */
+               discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC);
+               if (!discovery)
+                       return;
+
+               discovery->data.daddr = info->daddr;
+               discovery->data.saddr = self->saddr;
+               discovery->timestamp = jiffies;
+
+               discovery->data.hints[0] = discovery_info[0];
+               if (discovery_info[0] & HINT_EXTENSION) {
+                       discovery->data.hints[1] = discovery_info[1];
+                       discovery->data.charset = discovery_info[2];
+                       text = (char *) &discovery_info[3];
+               } else {
+                       discovery->data.hints[1] = 0;
+                       discovery->data.charset = discovery_info[1];
+                       text = (char *) &discovery_info[2];
+               }
+               /*
+                *  Terminate string, should be safe since this is where the
+                *  FCS bytes resides.
+                */
+               skb->data[skb->len] = '\0';
+               strncpy(discovery->data.info, text, NICKNAME_MAX_LEN);
+               discovery->name_len = strlen(discovery->data.info);
+
+               info->discovery = discovery;
+       } else
+               info->discovery = NULL;
+
+       irlap_do_event(self, RECV_DISCOVERY_XID_CMD, skb, info);
+}
+
+/*
+ * Function irlap_send_rr_frame (self, command)
+ *
+ *    Build and transmit RR (Receive Ready) frame. Notice that it is currently
+ *    only possible to send RR frames with the poll bit set.
+ */
+void irlap_send_rr_frame(struct irlap_cb *self, int command)
+{
+       struct sk_buff *tx_skb;
+       struct rr_frame *frame;
+
+       tx_skb = alloc_skb(sizeof(struct rr_frame), GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       frame = skb_put(tx_skb, 2);
+
+       frame->caddr = self->caddr;
+       frame->caddr |= (command) ? CMD_FRAME : 0;
+
+       frame->control = RR | PF_BIT | (self->vr << 5);
+
+       irlap_queue_xmit(self, tx_skb);
+}
+
+/*
+ * Function irlap_send_rd_frame (self)
+ *
+ *    Request disconnect. Used by a secondary station to request the
+ *    disconnection of the link.
+ */
+void irlap_send_rd_frame(struct irlap_cb *self)
+{
+       struct sk_buff *tx_skb;
+       struct rd_frame *frame;
+
+       tx_skb = alloc_skb(sizeof(struct rd_frame), GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       frame = skb_put(tx_skb, 2);
+
+       frame->caddr = self->caddr;
+       frame->control = RD_RSP | PF_BIT;
+
+       irlap_queue_xmit(self, tx_skb);
+}
+
+/*
+ * Function irlap_recv_rr_frame (skb, info)
+ *
+ *    Received RR (Receive Ready) frame from peer station, no harm in
+ *    making it inline since its called only from one single place
+ *    (irlap_driver_rcv).
+ */
+static inline void irlap_recv_rr_frame(struct irlap_cb *self,
+                                      struct sk_buff *skb,
+                                      struct irlap_info *info, int command)
+{
+       info->nr = skb->data[1] >> 5;
+
+       /* Check if this is a command or a response frame */
+       if (command)
+               irlap_do_event(self, RECV_RR_CMD, skb, info);
+       else
+               irlap_do_event(self, RECV_RR_RSP, skb, info);
+}
+
+/*
+ * Function irlap_recv_rnr_frame (self, skb, info)
+ *
+ *    Received RNR (Receive Not Ready) frame from peer station
+ *
+ */
+static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                struct irlap_info *info, int command)
+{
+       info->nr = skb->data[1] >> 5;
+
+       pr_debug("%s(), nr=%d, %ld\n", __func__, info->nr, jiffies);
+
+       if (command)
+               irlap_do_event(self, RECV_RNR_CMD, skb, info);
+       else
+               irlap_do_event(self, RECV_RNR_RSP, skb, info);
+}
+
+static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                struct irlap_info *info, int command)
+{
+       info->nr = skb->data[1] >> 5;
+
+       /* Check if this is a command or a response frame */
+       if (command)
+               irlap_do_event(self, RECV_REJ_CMD, skb, info);
+       else
+               irlap_do_event(self, RECV_REJ_RSP, skb, info);
+}
+
+static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                 struct irlap_info *info, int command)
+{
+       info->nr = skb->data[1] >> 5;
+
+       /* Check if this is a command or a response frame */
+       if (command)
+               irlap_do_event(self, RECV_SREJ_CMD, skb, info);
+       else
+               irlap_do_event(self, RECV_SREJ_RSP, skb, info);
+}
+
+static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                 struct irlap_info *info, int command)
+{
+       /* Check if this is a command or a response frame */
+       if (command)
+               irlap_do_event(self, RECV_DISC_CMD, skb, info);
+       else
+               irlap_do_event(self, RECV_RD_RSP, skb, info);
+}
+
+/*
+ * Function irlap_recv_ua_frame (skb, frame)
+ *
+ *    Received UA (Unnumbered Acknowledgement) frame
+ *
+ */
+static inline void irlap_recv_ua_frame(struct irlap_cb *self,
+                                      struct sk_buff *skb,
+                                      struct irlap_info *info)
+{
+       irlap_do_event(self, RECV_UA_RSP, skb, info);
+}
+
+/*
+ * Function irlap_send_data_primary(self, skb)
+ *
+ *    Send I-frames as the primary station but without the poll bit set
+ *
+ */
+void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb)
+{
+       struct sk_buff *tx_skb;
+
+       if (skb->data[1] == I_FRAME) {
+
+               /*
+                *  Insert frame sequence number (Vs) in control field before
+                *  inserting into transmit window queue.
+                */
+               skb->data[1] = I_FRAME | (self->vs << 1);
+
+               /*
+                *  Insert frame in store, in case of retransmissions
+                *  Increase skb reference count, see irlap_do_event()
+                */
+               skb_get(skb);
+               skb_queue_tail(&self->wx_list, skb);
+
+               /* Copy buffer */
+               tx_skb = skb_clone(skb, GFP_ATOMIC);
+               if (tx_skb == NULL) {
+                       return;
+               }
+
+               self->vs = (self->vs + 1) % 8;
+               self->ack_required = FALSE;
+               self->window -= 1;
+
+               irlap_send_i_frame( self, tx_skb, CMD_FRAME);
+       } else {
+               pr_debug("%s(), sending unreliable frame\n", __func__);
+               irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
+               self->window -= 1;
+       }
+}
+/*
+ * Function irlap_send_data_primary_poll (self, skb)
+ *
+ *    Send I(nformation) frame as primary with poll bit set
+ */
+void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
+{
+       struct sk_buff *tx_skb;
+       int transmission_time;
+
+       /* Stop P timer */
+       del_timer(&self->poll_timer);
+
+       /* Is this reliable or unreliable data? */
+       if (skb->data[1] == I_FRAME) {
+
+               /*
+                *  Insert frame sequence number (Vs) in control field before
+                *  inserting into transmit window queue.
+                */
+               skb->data[1] = I_FRAME | (self->vs << 1);
+
+               /*
+                *  Insert frame in store, in case of retransmissions
+                *  Increase skb reference count, see irlap_do_event()
+                */
+               skb_get(skb);
+               skb_queue_tail(&self->wx_list, skb);
+
+               /* Copy buffer */
+               tx_skb = skb_clone(skb, GFP_ATOMIC);
+               if (tx_skb == NULL) {
+                       return;
+               }
+
+               /*
+                *  Set poll bit if necessary. We do this to the copied
+                *  skb, since retransmitted need to set or clear the poll
+                *  bit depending on when they are sent.
+                */
+               tx_skb->data[1] |= PF_BIT;
+
+               self->vs = (self->vs + 1) % 8;
+               self->ack_required = FALSE;
+
+               irlap_next_state(self, LAP_NRM_P);
+               irlap_send_i_frame(self, tx_skb, CMD_FRAME);
+       } else {
+               pr_debug("%s(), sending unreliable frame\n", __func__);
+
+               if (self->ack_required) {
+                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
+                       irlap_next_state(self, LAP_NRM_P);
+                       irlap_send_rr_frame(self, CMD_FRAME);
+                       self->ack_required = FALSE;
+               } else {
+                       skb->data[1] |= PF_BIT;
+                       irlap_next_state(self, LAP_NRM_P);
+                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
+               }
+       }
+
+       /* How much time we took for transmission of all frames.
+        * We don't know, so let assume we used the full window. Jean II */
+       transmission_time = self->final_timeout;
+
+       /* Reset parameter so that we can fill next window */
+       self->window = self->window_size;
+
+#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
+       /* Remove what we have not used. Just do a prorata of the
+        * bytes left in window to window capacity.
+        * See max_line_capacities[][] in qos.c for details. Jean II */
+       transmission_time -= (self->final_timeout * self->bytes_left
+                             / self->line_capacity);
+       pr_debug("%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n",
+                __func__, self->final_timeout, self->bytes_left,
+                self->line_capacity, transmission_time);
+
+       /* We are allowed to transmit a maximum number of bytes again. */
+       self->bytes_left = self->line_capacity;
+#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+
+       /*
+        * The network layer has a intermediate buffer between IrLAP
+        * and the IrDA driver which can contain 8 frames. So, even
+        * though IrLAP is currently sending the *last* frame of the
+        * tx-window, the driver most likely has only just started
+        * sending the *first* frame of the same tx-window.
+        * I.e. we are always at the very beginning of or Tx window.
+        * Now, we are supposed to set the final timer from the end
+        * of our tx-window to let the other peer reply. So, we need
+        * to add extra time to compensate for the fact that we
+        * are really at the start of tx-window, otherwise the final timer
+        * might expire before he can answer...
+        * Jean II
+        */
+       irlap_start_final_timer(self, self->final_timeout + transmission_time);
+
+       /*
+        * The clever amongst you might ask why we do this adjustement
+        * only here, and not in all the other cases in irlap_event.c.
+        * In all those other case, we only send a very short management
+        * frame (few bytes), so the adjustement would be lost in the
+        * noise...
+        * The exception of course is irlap_resend_rejected_frame().
+        * Jean II */
+}
+
+/*
+ * Function irlap_send_data_secondary_final (self, skb)
+ *
+ *    Send I(nformation) frame as secondary with final bit set
+ *
+ */
+void irlap_send_data_secondary_final(struct irlap_cb *self,
+                                    struct sk_buff *skb)
+{
+       struct sk_buff *tx_skb = NULL;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       /* Is this reliable or unreliable data? */
+       if (skb->data[1] == I_FRAME) {
+
+               /*
+                *  Insert frame sequence number (Vs) in control field before
+                *  inserting into transmit window queue.
+                */
+               skb->data[1] = I_FRAME | (self->vs << 1);
+
+               /*
+                *  Insert frame in store, in case of retransmissions
+                *  Increase skb reference count, see irlap_do_event()
+                */
+               skb_get(skb);
+               skb_queue_tail(&self->wx_list, skb);
+
+               tx_skb = skb_clone(skb, GFP_ATOMIC);
+               if (tx_skb == NULL) {
+                       return;
+               }
+
+               tx_skb->data[1] |= PF_BIT;
+
+               self->vs = (self->vs + 1) % 8;
+               self->ack_required = FALSE;
+
+               irlap_send_i_frame(self, tx_skb, RSP_FRAME);
+       } else {
+               if (self->ack_required) {
+                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
+                       irlap_send_rr_frame(self, RSP_FRAME);
+                       self->ack_required = FALSE;
+               } else {
+                       skb->data[1] |= PF_BIT;
+                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
+               }
+       }
+
+       self->window = self->window_size;
+#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
+       /* We are allowed to transmit a maximum number of bytes again. */
+       self->bytes_left = self->line_capacity;
+#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+
+       irlap_start_wd_timer(self, self->wd_timeout);
+}
+
+/*
+ * Function irlap_send_data_secondary (self, skb)
+ *
+ *    Send I(nformation) frame as secondary without final bit set
+ *
+ */
+void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb)
+{
+       struct sk_buff *tx_skb = NULL;
+
+       /* Is this reliable or unreliable data? */
+       if (skb->data[1] == I_FRAME) {
+
+               /*
+                *  Insert frame sequence number (Vs) in control field before
+                *  inserting into transmit window queue.
+                */
+               skb->data[1] = I_FRAME | (self->vs << 1);
+
+               /*
+                *  Insert frame in store, in case of retransmissions
+                *  Increase skb reference count, see irlap_do_event()
+                */
+               skb_get(skb);
+               skb_queue_tail(&self->wx_list, skb);
+
+               tx_skb = skb_clone(skb, GFP_ATOMIC);
+               if (tx_skb == NULL) {
+                       return;
+               }
+
+               self->vs = (self->vs + 1) % 8;
+               self->ack_required = FALSE;
+               self->window -= 1;
+
+               irlap_send_i_frame(self, tx_skb, RSP_FRAME);
+       } else {
+               irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
+               self->window -= 1;
+       }
+}
+
+/*
+ * Function irlap_resend_rejected_frames (nr)
+ *
+ *    Resend frames which has not been acknowledged. Should be safe to
+ *    traverse the list without locking it since this function will only be
+ *    called from interrupt context (BH)
+ */
+void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
+{
+       struct sk_buff *tx_skb;
+       struct sk_buff *skb;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /*  Resend unacknowledged frame(s) */
+       skb_queue_walk(&self->wx_list, skb) {
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+
+               /* We copy the skb to be retransmitted since we will have to
+                * modify it. Cloning will confuse packet sniffers
+                */
+               /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
+               tx_skb = skb_copy(skb, GFP_ATOMIC);
+               if (!tx_skb) {
+                       pr_debug("%s(), unable to copy\n", __func__);
+                       return;
+               }
+
+               /* Clear old Nr field + poll bit */
+               tx_skb->data[1] &= 0x0f;
+
+               /*
+                *  Set poll bit on the last frame retransmitted
+                */
+               if (skb_queue_is_last(&self->wx_list, skb))
+                       tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
+               else
+                       tx_skb->data[1] &= ~PF_BIT; /* Clear p/f bit */
+
+               irlap_send_i_frame(self, tx_skb, command);
+       }
+#if 0 /* Not yet */
+       /*
+        *  We can now fill the window with additional data frames
+        */
+       while (!skb_queue_empty(&self->txq)) {
+
+               pr_debug("%s(), sending additional frames!\n", __func__);
+               if (self->window > 0) {
+                       skb = skb_dequeue( &self->txq);
+                       IRDA_ASSERT(skb != NULL, return;);
+
+                       /*
+                        *  If send window > 1 then send frame with pf
+                        *  bit cleared
+                        */
+                       if ((self->window > 1) &&
+                           !skb_queue_empty(&self->txq)) {
+                               irlap_send_data_primary(self, skb);
+                       } else {
+                               irlap_send_data_primary_poll(self, skb);
+                       }
+                       kfree_skb(skb);
+               }
+       }
+#endif
+}
+
+void irlap_resend_rejected_frame(struct irlap_cb *self, int command)
+{
+       struct sk_buff *tx_skb;
+       struct sk_buff *skb;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       /*  Resend unacknowledged frame(s) */
+       skb = skb_peek(&self->wx_list);
+       if (skb != NULL) {
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+
+               /* We copy the skb to be retransmitted since we will have to
+                * modify it. Cloning will confuse packet sniffers
+                */
+               /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
+               tx_skb = skb_copy(skb, GFP_ATOMIC);
+               if (!tx_skb) {
+                       pr_debug("%s(), unable to copy\n", __func__);
+                       return;
+               }
+
+               /* Clear old Nr field + poll bit */
+               tx_skb->data[1] &= 0x0f;
+
+               /*  Set poll/final bit */
+               tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
+
+               irlap_send_i_frame(self, tx_skb, command);
+       }
+}
+
+/*
+ * Function irlap_send_ui_frame (self, skb, command)
+ *
+ *    Contruct and transmit an Unnumbered Information (UI) frame
+ *
+ */
+void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
+                        __u8 caddr, int command)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       /* Insert connection address */
+       skb->data[0] = caddr | ((command) ? CMD_FRAME : 0);
+
+       irlap_queue_xmit(self, skb);
+}
+
+/*
+ * Function irlap_send_i_frame (skb)
+ *
+ *    Contruct and transmit Information (I) frame
+ */
+static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb,
+                              int command)
+{
+       /* Insert connection address */
+       skb->data[0] = self->caddr;
+       skb->data[0] |= (command) ? CMD_FRAME : 0;
+
+       /* Insert next to receive (Vr) */
+       skb->data[1] |= (self->vr << 5);  /* insert nr */
+
+       irlap_queue_xmit(self, skb);
+}
+
+/*
+ * Function irlap_recv_i_frame (skb, frame)
+ *
+ *    Receive and parse an I (Information) frame, no harm in making it inline
+ *    since it's called only from one single place (irlap_driver_rcv).
+ */
+static inline void irlap_recv_i_frame(struct irlap_cb *self,
+                                     struct sk_buff *skb,
+                                     struct irlap_info *info, int command)
+{
+       info->nr = skb->data[1] >> 5;          /* Next to receive */
+       info->pf = skb->data[1] & PF_BIT;      /* Final bit */
+       info->ns = (skb->data[1] >> 1) & 0x07; /* Next to send */
+
+       /* Check if this is a command or a response frame */
+       if (command)
+               irlap_do_event(self, RECV_I_CMD, skb, info);
+       else
+               irlap_do_event(self, RECV_I_RSP, skb, info);
+}
+
+/*
+ * Function irlap_recv_ui_frame (self, skb, info)
+ *
+ *    Receive and parse an Unnumbered Information (UI) frame
+ *
+ */
+static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
+                               struct irlap_info *info)
+{
+       info->pf = skb->data[1] & PF_BIT;      /* Final bit */
+
+       irlap_do_event(self, RECV_UI_FRAME, skb, info);
+}
+
+/*
+ * Function irlap_recv_frmr_frame (skb, frame)
+ *
+ *    Received Frame Reject response.
+ *
+ */
+static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                 struct irlap_info *info)
+{
+       __u8 *frame;
+       int w, x, y, z;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(info != NULL, return;);
+
+       if (!pskb_may_pull(skb, 4)) {
+               net_err_ratelimited("%s: frame too short!\n", __func__);
+               return;
+       }
+
+       frame = skb->data;
+
+       info->nr = frame[2] >> 5;          /* Next to receive */
+       info->pf = frame[2] & PF_BIT;      /* Final bit */
+       info->ns = (frame[2] >> 1) & 0x07; /* Next to send */
+
+       w = frame[3] & 0x01;
+       x = frame[3] & 0x02;
+       y = frame[3] & 0x04;
+       z = frame[3] & 0x08;
+
+       if (w) {
+               pr_debug("Rejected control field is undefined or not implemented\n");
+       }
+       if (x) {
+               pr_debug("Rejected control field was invalid because it contained a non permitted I field\n");
+       }
+       if (y) {
+               pr_debug("Received I field exceeded the maximum negotiated for the existing connection or exceeded the maximum this station supports if no connection exists\n");
+       }
+       if (z) {
+               pr_debug("Rejected control field control field contained an invalid Nr count\n");
+       }
+       irlap_do_event(self, RECV_FRMR_RSP, skb, info);
+}
+
+/*
+ * Function irlap_send_test_frame (self, daddr)
+ *
+ *    Send a test frame response
+ *
+ */
+void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
+                          struct sk_buff *cmd)
+{
+       struct sk_buff *tx_skb;
+       struct test_frame *frame;
+
+       tx_skb = alloc_skb(cmd->len + sizeof(struct test_frame), GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       /* Broadcast frames must include saddr and daddr fields */
+       if (caddr == CBROADCAST) {
+               frame = skb_put(tx_skb, sizeof(struct test_frame));
+
+               /* Insert the swapped addresses */
+               frame->saddr = cpu_to_le32(self->saddr);
+               frame->daddr = cpu_to_le32(daddr);
+       } else
+               frame = skb_put(tx_skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
+
+       frame->caddr = caddr;
+       frame->control = TEST_RSP | PF_BIT;
+
+       /* Copy info */
+       skb_put_data(tx_skb, cmd->data, cmd->len);
+
+       /* Return to sender */
+       irlap_wait_min_turn_around(self, &self->qos_tx);
+       irlap_queue_xmit(self, tx_skb);
+}
+
+/*
+ * Function irlap_recv_test_frame (self, skb)
+ *
+ *    Receive a test frame
+ *
+ */
+static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                 struct irlap_info *info, int command)
+{
+       struct test_frame *frame;
+
+       if (!pskb_may_pull(skb, sizeof(*frame))) {
+               net_err_ratelimited("%s: frame too short!\n", __func__);
+               return;
+       }
+       frame = (struct test_frame *) skb->data;
+
+       /* Broadcast frames must carry saddr and daddr fields */
+       if (info->caddr == CBROADCAST) {
+               if (skb->len < sizeof(struct test_frame)) {
+                       pr_debug("%s() test frame too short!\n",
+                                __func__);
+                       return;
+               }
+
+               /* Read and swap addresses */
+               info->daddr = le32_to_cpu(frame->saddr);
+               info->saddr = le32_to_cpu(frame->daddr);
+
+               /* Make sure frame is addressed to us */
+               if ((info->saddr != self->saddr) &&
+                   (info->saddr != BROADCAST)) {
+                       return;
+               }
+       }
+
+       if (command)
+               irlap_do_event(self, RECV_TEST_CMD, skb, info);
+       else
+               irlap_do_event(self, RECV_TEST_RSP, skb, info);
+}
+
+/*
+ * Function irlap_driver_rcv (skb, netdev, ptype)
+ *
+ *    Called when a frame is received. Dispatches the right receive function
+ *    for processing of the frame.
+ *
+ * Note on skb management :
+ * After calling the higher layers of the IrDA stack, we always
+ * kfree() the skb, which drop the reference count (and potentially
+ * destroy it).
+ * If a higher layer of the stack want to keep the skb around (to put
+ * in a queue or pass it to the higher layer), it will need to use
+ * skb_get() to keep a reference on it. This is usually done at the
+ * LMP level in irlmp.c.
+ * Jean II
+ */
+int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
+                    struct packet_type *ptype, struct net_device *orig_dev)
+{
+       struct irlap_info info;
+       struct irlap_cb *self;
+       int command;
+       __u8 control;
+       int ret = -1;
+
+       if (!net_eq(dev_net(dev), &init_net))
+               goto out;
+
+       /* FIXME: should we get our own field? */
+       self = (struct irlap_cb *) dev->atalk_ptr;
+
+       /* If the net device is down, then IrLAP is gone! */
+       if (!self || self->magic != LAP_MAGIC)
+               goto err;
+
+       /* We are no longer an "old" protocol, so we need to handle
+        * share and non linear skbs. This should never happen, so
+        * we don't need to be clever about it. Jean II */
+       if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
+               net_err_ratelimited("%s: can't clone shared skb!\n", __func__);
+               goto err;
+       }
+
+       /* Check if frame is large enough for parsing */
+       if (!pskb_may_pull(skb, 2)) {
+               net_err_ratelimited("%s: frame too short!\n", __func__);
+               goto err;
+       }
+
+       command    = skb->data[0] & CMD_FRAME;
+       info.caddr = skb->data[0] & CBROADCAST;
+
+       info.pf      = skb->data[1] &  PF_BIT;
+       info.control = skb->data[1] & ~PF_BIT; /* Mask away poll/final bit */
+
+       control = info.control;
+
+       /*  First we check if this frame has a valid connection address */
+       if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) {
+               pr_debug("%s(), wrong connection address!\n",
+                        __func__);
+               goto out;
+       }
+       /*
+        *  Optimize for the common case and check if the frame is an
+        *  I(nformation) frame. Only I-frames have bit 0 set to 0
+        */
+       if (~control & 0x01) {
+               irlap_recv_i_frame(self, skb, &info, command);
+               goto out;
+       }
+       /*
+        *  We now check is the frame is an S(upervisory) frame. Only
+        *  S-frames have bit 0 set to 1 and bit 1 set to 0
+        */
+       if (~control & 0x02) {
+               /*
+                *  Received S(upervisory) frame, check which frame type it is
+                *  only the first nibble is of interest
+                */
+               switch (control & 0x0f) {
+               case RR:
+                       irlap_recv_rr_frame(self, skb, &info, command);
+                       break;
+               case RNR:
+                       irlap_recv_rnr_frame(self, skb, &info, command);
+                       break;
+               case REJ:
+                       irlap_recv_rej_frame(self, skb, &info, command);
+                       break;
+               case SREJ:
+                       irlap_recv_srej_frame(self, skb, &info, command);
+                       break;
+               default:
+                       net_warn_ratelimited("%s: Unknown S-frame %02x received!\n",
+                                            __func__, info.control);
+                       break;
+               }
+               goto out;
+       }
+       /*
+        *  This must be a C(ontrol) frame
+        */
+       switch (control) {
+       case XID_RSP:
+               irlap_recv_discovery_xid_rsp(self, skb, &info);
+               break;
+       case XID_CMD:
+               irlap_recv_discovery_xid_cmd(self, skb, &info);
+               break;
+       case SNRM_CMD:
+               irlap_recv_snrm_cmd(self, skb, &info);
+               break;
+       case DM_RSP:
+               irlap_do_event(self, RECV_DM_RSP, skb, &info);
+               break;
+       case DISC_CMD: /* And RD_RSP since they have the same value */
+               irlap_recv_disc_frame(self, skb, &info, command);
+               break;
+       case TEST_CMD:
+               irlap_recv_test_frame(self, skb, &info, command);
+               break;
+       case UA_RSP:
+               irlap_recv_ua_frame(self, skb, &info);
+               break;
+       case FRMR_RSP:
+               irlap_recv_frmr_frame(self, skb, &info);
+               break;
+       case UI_FRAME:
+               irlap_recv_ui_frame(self, skb, &info);
+               break;
+       default:
+               net_warn_ratelimited("%s: Unknown frame %02x received!\n",
+                                    __func__, info.control);
+               break;
+       }
+out:
+       ret = 0;
+err:
+       /* Always drop our reference on the skb */
+       dev_kfree_skb(skb);
+       return ret;
+}
diff --git a/drivers/staging/irda/net/irlmp.c b/drivers/staging/irda/net/irlmp.c
new file mode 100644 (file)
index 0000000..4396459
--- /dev/null
@@ -0,0 +1,1996 @@
+/*********************************************************************
+ *
+ * Filename:      irlmp.c
+ * Version:       1.0
+ * Description:   IrDA Link Management Protocol (LMP) layer
+ * Status:        Stable.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Aug 17 20:54:32 1997
+ * Modified at:   Wed Jan  5 11:26:03 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/random.h>
+#include <linux/seq_file.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/timer.h>
+#include <net/irda/qos.h>
+#include <net/irda/irlap.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irlmp_frame.h>
+
+#include <asm/unaligned.h>
+
+static __u8 irlmp_find_free_slsap(void);
+static int irlmp_slsap_inuse(__u8 slsap_sel);
+
+/* Master structure */
+struct irlmp_cb *irlmp = NULL;
+
+/* These can be altered by the sysctl interface */
+int  sysctl_discovery         = 0;
+int  sysctl_discovery_timeout = 3; /* 3 seconds by default */
+int  sysctl_discovery_slots   = 6; /* 6 slots by default */
+int  sysctl_lap_keepalive_time = LM_IDLE_TIMEOUT * 1000 / HZ;
+char sysctl_devname[65];
+
+static const char *irlmp_reasons[] = {
+       "ERROR, NOT USED",
+       "LM_USER_REQUEST",
+       "LM_LAP_DISCONNECT",
+       "LM_CONNECT_FAILURE",
+       "LM_LAP_RESET",
+       "LM_INIT_DISCONNECT",
+       "ERROR, NOT USED",
+       "UNKNOWN",
+};
+
+const char *irlmp_reason_str(LM_REASON reason)
+{
+       reason = min_t(size_t, reason, ARRAY_SIZE(irlmp_reasons) - 1);
+       return irlmp_reasons[reason];
+}
+
+/*
+ * Function irlmp_init (void)
+ *
+ *    Create (allocate) the main IrLMP structure
+ *
+ */
+int __init irlmp_init(void)
+{
+       /* Initialize the irlmp structure. */
+       irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL);
+       if (irlmp == NULL)
+               return -ENOMEM;
+
+       irlmp->magic = LMP_MAGIC;
+
+       irlmp->clients = hashbin_new(HB_LOCK);
+       irlmp->services = hashbin_new(HB_LOCK);
+       irlmp->links = hashbin_new(HB_LOCK);
+       irlmp->unconnected_lsaps = hashbin_new(HB_LOCK);
+       irlmp->cachelog = hashbin_new(HB_NOLOCK);
+
+       if ((irlmp->clients == NULL) ||
+           (irlmp->services == NULL) ||
+           (irlmp->links == NULL) ||
+           (irlmp->unconnected_lsaps == NULL) ||
+           (irlmp->cachelog == NULL)) {
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&irlmp->cachelog->hb_spinlock);
+
+       irlmp->last_lsap_sel = 0x0f; /* Reserved 0x00-0x0f */
+       strcpy(sysctl_devname, "Linux");
+
+       init_timer(&irlmp->discovery_timer);
+
+       /* Do discovery every 3 seconds, conditionally */
+       if (sysctl_discovery)
+               irlmp_start_discovery_timer(irlmp,
+                                           sysctl_discovery_timeout*HZ);
+
+       return 0;
+}
+
+/*
+ * Function irlmp_cleanup (void)
+ *
+ *    Remove IrLMP layer
+ *
+ */
+void irlmp_cleanup(void)
+{
+       /* Check for main structure */
+       IRDA_ASSERT(irlmp != NULL, return;);
+       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;);
+
+       del_timer(&irlmp->discovery_timer);
+
+       hashbin_delete(irlmp->links, (FREE_FUNC) kfree);
+       hashbin_delete(irlmp->unconnected_lsaps, (FREE_FUNC) kfree);
+       hashbin_delete(irlmp->clients, (FREE_FUNC) kfree);
+       hashbin_delete(irlmp->services, (FREE_FUNC) kfree);
+       hashbin_delete(irlmp->cachelog, (FREE_FUNC) kfree);
+
+       /* De-allocate main structure */
+       kfree(irlmp);
+       irlmp = NULL;
+}
+
+/*
+ * Function irlmp_open_lsap (slsap, notify)
+ *
+ *   Register with IrLMP and create a local LSAP,
+ *   returns handle to LSAP.
+ */
+struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid)
+{
+       struct lsap_cb *self;
+
+       IRDA_ASSERT(notify != NULL, return NULL;);
+       IRDA_ASSERT(irlmp != NULL, return NULL;);
+       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return NULL;);
+       IRDA_ASSERT(notify->instance != NULL, return NULL;);
+
+       /*  Does the client care which Source LSAP selector it gets?  */
+       if (slsap_sel == LSAP_ANY) {
+               slsap_sel = irlmp_find_free_slsap();
+               if (!slsap_sel)
+                       return NULL;
+       } else if (irlmp_slsap_inuse(slsap_sel))
+               return NULL;
+
+       /* Allocate new instance of a LSAP connection */
+       self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC);
+       if (self == NULL)
+               return NULL;
+
+       self->magic = LMP_LSAP_MAGIC;
+       self->slsap_sel = slsap_sel;
+
+       /* Fix connectionless LSAP's */
+       if (slsap_sel == LSAP_CONNLESS) {
+#ifdef CONFIG_IRDA_ULTRA
+               self->dlsap_sel = LSAP_CONNLESS;
+               self->pid = pid;
+#endif /* CONFIG_IRDA_ULTRA */
+       } else
+               self->dlsap_sel = LSAP_ANY;
+       /* self->connected = FALSE; -> already NULL via memset() */
+
+       init_timer(&self->watchdog_timer);
+
+       self->notify = *notify;
+
+       self->lsap_state = LSAP_DISCONNECTED;
+
+       /* Insert into queue of unconnected LSAPs */
+       hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self,
+                      (long) self, NULL);
+
+       return self;
+}
+EXPORT_SYMBOL(irlmp_open_lsap);
+
+/*
+ * Function __irlmp_close_lsap (self)
+ *
+ *    Remove an instance of LSAP
+ */
+static void __irlmp_close_lsap(struct lsap_cb *self)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+
+       /*
+        *  Set some of the variables to preset values
+        */
+       self->magic = 0;
+       del_timer(&self->watchdog_timer); /* Important! */
+
+       if (self->conn_skb)
+               dev_kfree_skb(self->conn_skb);
+
+       kfree(self);
+}
+
+/*
+ * Function irlmp_close_lsap (self)
+ *
+ *    Close and remove LSAP
+ *
+ */
+void irlmp_close_lsap(struct lsap_cb *self)
+{
+       struct lap_cb *lap;
+       struct lsap_cb *lsap = NULL;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+
+       /*
+        *  Find out if we should remove this LSAP from a link or from the
+        *  list of unconnected lsaps (not associated with a link)
+        */
+       lap = self->lap;
+       if (lap) {
+               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
+               /* We might close a LSAP before it has completed the
+                * connection setup. In those case, higher layers won't
+                * send a proper disconnect request. Harmless, except
+                * that we will forget to close LAP... - Jean II */
+               if(self->lsap_state != LSAP_DISCONNECTED) {
+                       self->lsap_state = LSAP_DISCONNECTED;
+                       irlmp_do_lap_event(self->lap,
+                                          LM_LAP_DISCONNECT_REQUEST, NULL);
+               }
+               /* Now, remove from the link */
+               lsap = hashbin_remove(lap->lsaps, (long) self, NULL);
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+               lap->cache.valid = FALSE;
+#endif
+       }
+       self->lap = NULL;
+       /* Check if we found the LSAP! If not then try the unconnected lsaps */
+       if (!lsap) {
+               lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self,
+                                     NULL);
+       }
+       if (!lsap) {
+               pr_debug("%s(), Looks like somebody has removed me already!\n",
+                        __func__);
+               return;
+       }
+       __irlmp_close_lsap(self);
+}
+EXPORT_SYMBOL(irlmp_close_lsap);
+
+/*
+ * Function irlmp_register_irlap (saddr, notify)
+ *
+ *    Register IrLAP layer with IrLMP. There is possible to have multiple
+ *    instances of the IrLAP layer, each connected to different IrDA ports
+ *
+ */
+void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify)
+{
+       struct lap_cb *lap;
+
+       IRDA_ASSERT(irlmp != NULL, return;);
+       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;);
+       IRDA_ASSERT(notify != NULL, return;);
+
+       /*
+        *  Allocate new instance of a LSAP connection
+        */
+       lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL);
+       if (lap == NULL)
+               return;
+
+       lap->irlap = irlap;
+       lap->magic = LMP_LAP_MAGIC;
+       lap->saddr = saddr;
+       lap->daddr = DEV_ADDR_ANY;
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+       lap->cache.valid = FALSE;
+#endif
+       lap->lsaps = hashbin_new(HB_LOCK);
+       if (lap->lsaps == NULL) {
+               net_warn_ratelimited("%s(), unable to kmalloc lsaps\n",
+                                    __func__);
+               kfree(lap);
+               return;
+       }
+
+       lap->lap_state = LAP_STANDBY;
+
+       init_timer(&lap->idle_timer);
+
+       /*
+        *  Insert into queue of LMP links
+        */
+       hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL);
+
+       /*
+        *  We set only this variable so IrLAP can tell us on which link the
+        *  different events happened on
+        */
+       irda_notify_init(notify);
+       notify->instance = lap;
+}
+
+/*
+ * Function irlmp_unregister_irlap (saddr)
+ *
+ *    IrLAP layer has been removed!
+ *
+ */
+void irlmp_unregister_link(__u32 saddr)
+{
+       struct lap_cb *link;
+
+       /* We must remove ourselves from the hashbin *first*. This ensure
+        * that no more LSAPs will be open on this link and no discovery
+        * will be triggered anymore. Jean II */
+       link = hashbin_remove(irlmp->links, saddr, NULL);
+       if (link) {
+               IRDA_ASSERT(link->magic == LMP_LAP_MAGIC, return;);
+
+               /* Kill all the LSAPs on this link. Jean II */
+               link->reason = LAP_DISC_INDICATION;
+               link->daddr = DEV_ADDR_ANY;
+               irlmp_do_lap_event(link, LM_LAP_DISCONNECT_INDICATION, NULL);
+
+               /* Remove all discoveries discovered at this link */
+               irlmp_expire_discoveries(irlmp->cachelog, link->saddr, TRUE);
+
+               /* Final cleanup */
+               del_timer(&link->idle_timer);
+               link->magic = 0;
+               hashbin_delete(link->lsaps, (FREE_FUNC) __irlmp_close_lsap);
+               kfree(link);
+       }
+}
+
+/*
+ * Function irlmp_connect_request (handle, dlsap, userdata)
+ *
+ *    Connect with a peer LSAP
+ *
+ */
+int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
+                         __u32 saddr, __u32 daddr,
+                         struct qos_info *qos, struct sk_buff *userdata)
+{
+       struct sk_buff *tx_skb = userdata;
+       struct lap_cb *lap;
+       struct lsap_cb *lsap;
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -EBADR;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;);
+
+       pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n",
+                __func__, self->slsap_sel, dlsap_sel, saddr, daddr);
+
+       if (test_bit(0, &self->connected)) {
+               ret = -EISCONN;
+               goto err;
+       }
+
+       /* Client must supply destination device address */
+       if (!daddr) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* Any userdata? */
+       if (tx_skb == NULL) {
+               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
+               if (!tx_skb)
+                       return -ENOMEM;
+
+               skb_reserve(tx_skb, LMP_MAX_HEADER);
+       }
+
+       /* Make room for MUX control header (3 bytes) */
+       IRDA_ASSERT(skb_headroom(tx_skb) >= LMP_CONTROL_HEADER, return -1;);
+       skb_push(tx_skb, LMP_CONTROL_HEADER);
+
+       self->dlsap_sel = dlsap_sel;
+
+       /*
+        * Find the link to where we should try to connect since there may
+        * be more than one IrDA port on this machine. If the client has
+        * passed us the saddr (and already knows which link to use), then
+        * we use that to find the link, if not then we have to look in the
+        * discovery log and check if any of the links has discovered a
+        * device with the given daddr
+        */
+       if ((!saddr) || (saddr == DEV_ADDR_ANY)) {
+               discovery_t *discovery;
+               unsigned long flags;
+
+               spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags);
+               if (daddr != DEV_ADDR_ANY)
+                       discovery = hashbin_find(irlmp->cachelog, daddr, NULL);
+               else {
+                       pr_debug("%s(), no daddr\n", __func__);
+                       discovery = (discovery_t *)
+                               hashbin_get_first(irlmp->cachelog);
+               }
+
+               if (discovery) {
+                       saddr = discovery->data.saddr;
+                       daddr = discovery->data.daddr;
+               }
+               spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags);
+       }
+       lap = hashbin_lock_find(irlmp->links, saddr, NULL);
+       if (lap == NULL) {
+               pr_debug("%s(), Unable to find a usable link!\n", __func__);
+               ret = -EHOSTUNREACH;
+               goto err;
+       }
+
+       /* Check if LAP is disconnected or already connected */
+       if (lap->daddr == DEV_ADDR_ANY)
+               lap->daddr = daddr;
+       else if (lap->daddr != daddr) {
+               /* Check if some LSAPs are active on this LAP */
+               if (HASHBIN_GET_SIZE(lap->lsaps) == 0) {
+                       /* No active connection, but LAP hasn't been
+                        * disconnected yet (waiting for timeout in LAP).
+                        * Maybe we could give LAP a bit of help in this case.
+                        */
+                       pr_debug("%s(), sorry, but I'm waiting for LAP to timeout!\n",
+                                __func__);
+                       ret = -EAGAIN;
+                       goto err;
+               }
+
+               /* LAP is already connected to a different node, and LAP
+                * can only talk to one node at a time */
+               pr_debug("%s(), sorry, but link is busy!\n", __func__);
+               ret = -EBUSY;
+               goto err;
+       }
+
+       self->lap = lap;
+
+       /*
+        *  Remove LSAP from list of unconnected LSAPs and insert it into the
+        *  list of connected LSAPs for the particular link
+        */
+       lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, NULL);
+
+       IRDA_ASSERT(lsap != NULL, return -1;);
+       IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;);
+       IRDA_ASSERT(lsap->lap != NULL, return -1;);
+       IRDA_ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;);
+
+       hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (long) self,
+                      NULL);
+
+       set_bit(0, &self->connected);   /* TRUE */
+
+       /*
+        *  User supplied qos specifications?
+        */
+       if (qos)
+               self->qos = *qos;
+
+       irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, tx_skb);
+
+       /* Drop reference count - see irlap_data_request(). */
+       dev_kfree_skb(tx_skb);
+
+       return 0;
+
+err:
+       /* Cleanup */
+       if(tx_skb)
+               dev_kfree_skb(tx_skb);
+       return ret;
+}
+EXPORT_SYMBOL(irlmp_connect_request);
+
+/*
+ * Function irlmp_connect_indication (self)
+ *
+ *    Incoming connection
+ *
+ */
+void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb)
+{
+       int max_seg_size;
+       int lap_header_size;
+       int max_header_size;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(self->lap != NULL, return;);
+
+       pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
+                __func__, self->slsap_sel, self->dlsap_sel);
+
+       /* Note : self->lap is set in irlmp_link_data_indication(),
+        * (case CONNECT_CMD:) because we have no way to set it here.
+        * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap().
+        * Jean II */
+
+       self->qos = *self->lap->qos;
+
+       max_seg_size = self->lap->qos->data_size.value-LMP_HEADER;
+       lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap);
+       max_header_size = LMP_HEADER + lap_header_size;
+
+       /* Hide LMP_CONTROL_HEADER header from layer above */
+       skb_pull(skb, LMP_CONTROL_HEADER);
+
+       if (self->notify.connect_indication) {
+               /* Don't forget to refcount it - see irlap_driver_rcv(). */
+               skb_get(skb);
+               self->notify.connect_indication(self->notify.instance, self,
+                                               &self->qos, max_seg_size,
+                                               max_header_size, skb);
+       }
+}
+
+/*
+ * Function irlmp_connect_response (handle, userdata)
+ *
+ *    Service user is accepting connection
+ *
+ */
+int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+       IRDA_ASSERT(userdata != NULL, return -1;);
+
+       /* We set the connected bit and move the lsap to the connected list
+        * in the state machine itself. Jean II */
+
+       pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
+                __func__, self->slsap_sel, self->dlsap_sel);
+
+       /* Make room for MUX control header (3 bytes) */
+       IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;);
+       skb_push(userdata, LMP_CONTROL_HEADER);
+
+       irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata);
+
+       /* Drop reference count - see irlap_data_request(). */
+       dev_kfree_skb(userdata);
+
+       return 0;
+}
+EXPORT_SYMBOL(irlmp_connect_response);
+
+/*
+ * Function irlmp_connect_confirm (handle, skb)
+ *
+ *    LSAP connection confirmed peer device!
+ */
+void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb)
+{
+       int max_header_size;
+       int lap_header_size;
+       int max_seg_size;
+
+       IRDA_ASSERT(skb != NULL, return;);
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+       IRDA_ASSERT(self->lap != NULL, return;);
+
+       self->qos = *self->lap->qos;
+
+       max_seg_size    = self->lap->qos->data_size.value-LMP_HEADER;
+       lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap);
+       max_header_size = LMP_HEADER + lap_header_size;
+
+       pr_debug("%s(), max_header_size=%d\n",
+                __func__, max_header_size);
+
+       /* Hide LMP_CONTROL_HEADER header from layer above */
+       skb_pull(skb, LMP_CONTROL_HEADER);
+
+       if (self->notify.connect_confirm) {
+               /* Don't forget to refcount it - see irlap_driver_rcv() */
+               skb_get(skb);
+               self->notify.connect_confirm(self->notify.instance, self,
+                                            &self->qos, max_seg_size,
+                                            max_header_size, skb);
+       }
+}
+
+/*
+ * Function irlmp_dup (orig, instance)
+ *
+ *    Duplicate LSAP, can be used by servers to confirm a connection on a
+ *    new LSAP so it can keep listening on the old one.
+ *
+ */
+struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
+{
+       struct lsap_cb *new;
+       unsigned long flags;
+
+       spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
+
+       /* Only allowed to duplicate unconnected LSAP's, and only LSAPs
+        * that have received a connect indication. Jean II */
+       if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) ||
+           (orig->lap == NULL)) {
+               pr_debug("%s(), invalid LSAP (wrong state)\n",
+                        __func__);
+               spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
+                                      flags);
+               return NULL;
+       }
+
+       /* Allocate a new instance */
+       new = kmemdup(orig, sizeof(*new), GFP_ATOMIC);
+       if (!new)  {
+               pr_debug("%s(), unable to kmalloc\n", __func__);
+               spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
+                                      flags);
+               return NULL;
+       }
+       /* new->lap = orig->lap; => done in the memcpy() */
+       /* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */
+       new->conn_skb = NULL;
+
+       spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
+
+       /* Not everything is the same */
+       new->notify.instance = instance;
+
+       init_timer(&new->watchdog_timer);
+
+       hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new,
+                      (long) new, NULL);
+
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+       /* Make sure that we invalidate the LSAP cache */
+       new->lap->cache.valid = FALSE;
+#endif /* CONFIG_IRDA_CACHE_LAST_LSAP */
+
+       return new;
+}
+
+/*
+ * Function irlmp_disconnect_request (handle, userdata)
+ *
+ *    The service user is requesting disconnection, this will not remove the
+ *    LSAP, but only mark it as disconnected
+ */
+int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata)
+{
+       struct lsap_cb *lsap;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+       IRDA_ASSERT(userdata != NULL, return -1;);
+
+       /* Already disconnected ?
+        * There is a race condition between irlmp_disconnect_indication()
+        * and us that might mess up the hashbins below. This fixes it.
+        * Jean II */
+       if (! test_and_clear_bit(0, &self->connected)) {
+               pr_debug("%s(), already disconnected!\n", __func__);
+               dev_kfree_skb(userdata);
+               return -1;
+       }
+
+       skb_push(userdata, LMP_CONTROL_HEADER);
+
+       /*
+        *  Do the event before the other stuff since we must know
+        *  which lap layer that the frame should be transmitted on
+        */
+       irlmp_do_lsap_event(self, LM_DISCONNECT_REQUEST, userdata);
+
+       /* Drop reference count - see irlap_data_request(). */
+       dev_kfree_skb(userdata);
+
+       /*
+        *  Remove LSAP from list of connected LSAPs for the particular link
+        *  and insert it into the list of unconnected LSAPs
+        */
+       IRDA_ASSERT(self->lap != NULL, return -1;);
+       IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
+       IRDA_ASSERT(self->lap->lsaps != NULL, return -1;);
+
+       lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL);
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+       self->lap->cache.valid = FALSE;
+#endif
+
+       IRDA_ASSERT(lsap != NULL, return -1;);
+       IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;);
+       IRDA_ASSERT(lsap == self, return -1;);
+
+       hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self,
+                      (long) self, NULL);
+
+       /* Reset some values */
+       self->dlsap_sel = LSAP_ANY;
+       self->lap = NULL;
+
+       return 0;
+}
+EXPORT_SYMBOL(irlmp_disconnect_request);
+
+/*
+ * Function irlmp_disconnect_indication (reason, userdata)
+ *
+ *    LSAP is being closed!
+ */
+void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
+                                struct sk_buff *skb)
+{
+       struct lsap_cb *lsap;
+
+       pr_debug("%s(), reason=%s [%d]\n", __func__,
+                irlmp_reason_str(reason), reason);
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+
+       pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
+                __func__, self->slsap_sel, self->dlsap_sel);
+
+       /* Already disconnected ?
+        * There is a race condition between irlmp_disconnect_request()
+        * and us that might mess up the hashbins below. This fixes it.
+        * Jean II */
+       if (! test_and_clear_bit(0, &self->connected)) {
+               pr_debug("%s(), already disconnected!\n", __func__);
+               return;
+       }
+
+       /*
+        *  Remove association between this LSAP and the link it used
+        */
+       IRDA_ASSERT(self->lap != NULL, return;);
+       IRDA_ASSERT(self->lap->lsaps != NULL, return;);
+
+       lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL);
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+       self->lap->cache.valid = FALSE;
+#endif
+
+       IRDA_ASSERT(lsap != NULL, return;);
+       IRDA_ASSERT(lsap == self, return;);
+       hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap,
+                      (long) lsap, NULL);
+
+       self->dlsap_sel = LSAP_ANY;
+       self->lap = NULL;
+
+       /*
+        *  Inform service user
+        */
+       if (self->notify.disconnect_indication) {
+               /* Don't forget to refcount it - see irlap_driver_rcv(). */
+               if(skb)
+                       skb_get(skb);
+               self->notify.disconnect_indication(self->notify.instance,
+                                                  self, reason, skb);
+       } else {
+               pr_debug("%s(), no handler\n", __func__);
+       }
+}
+
+/*
+ * Function irlmp_do_expiry (void)
+ *
+ *    Do a cleanup of the discovery log (remove old entries)
+ *
+ * Note : separate from irlmp_do_discovery() so that we can handle
+ * passive discovery properly.
+ */
+void irlmp_do_expiry(void)
+{
+       struct lap_cb *lap;
+
+       /*
+        * Expire discovery on all links which are *not* connected.
+        * On links which are connected, we can't do discovery
+        * anymore and can't refresh the log, so we freeze the
+        * discovery log to keep info about the device we are
+        * connected to.
+        * This info is mandatory if we want irlmp_connect_request()
+        * to work properly. - Jean II
+        */
+       lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
+       while (lap != NULL) {
+               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
+
+               if (lap->lap_state == LAP_STANDBY) {
+                       /* Expire discoveries discovered on this link */
+                       irlmp_expire_discoveries(irlmp->cachelog, lap->saddr,
+                                                FALSE);
+               }
+               lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
+       }
+}
+
+/*
+ * Function irlmp_do_discovery (nslots)
+ *
+ *    Do some discovery on all links
+ *
+ * Note : log expiry is done above.
+ */
+void irlmp_do_discovery(int nslots)
+{
+       struct lap_cb *lap;
+       __u16 *data_hintsp;
+
+       /* Make sure the value is sane */
+       if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){
+               net_warn_ratelimited("%s: invalid value for number of slots!\n",
+                                    __func__);
+               nslots = sysctl_discovery_slots = 8;
+       }
+
+       /* Construct new discovery info to be used by IrLAP, */
+       data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints;
+       put_unaligned(irlmp->hints.word, data_hintsp);
+
+       /*
+        *  Set character set for device name (we use ASCII), and
+        *  copy device name. Remember to make room for a \0 at the
+        *  end
+        */
+       irlmp->discovery_cmd.data.charset = CS_ASCII;
+       strncpy(irlmp->discovery_cmd.data.info, sysctl_devname,
+               NICKNAME_MAX_LEN);
+       irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.data.info);
+       irlmp->discovery_cmd.nslots = nslots;
+
+       /*
+        * Try to send discovery packets on all links
+        */
+       lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
+       while (lap != NULL) {
+               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
+
+               if (lap->lap_state == LAP_STANDBY) {
+                       /* Try to discover */
+                       irlmp_do_lap_event(lap, LM_LAP_DISCOVERY_REQUEST,
+                                          NULL);
+               }
+               lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
+       }
+}
+
+/*
+ * Function irlmp_discovery_request (nslots)
+ *
+ *    Do a discovery of devices in front of the computer
+ *
+ * If the caller has registered a client discovery callback, this
+ * allow him to receive the full content of the discovery log through
+ * this callback (as normally he will receive only new discoveries).
+ */
+void irlmp_discovery_request(int nslots)
+{
+       /* Return current cached discovery log (in full) */
+       irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_LOG);
+
+       /*
+        * Start a single discovery operation if discovery is not already
+        * running
+        */
+       if (!sysctl_discovery) {
+               /* Check if user wants to override the default */
+               if (nslots == DISCOVERY_DEFAULT_SLOTS)
+                       nslots = sysctl_discovery_slots;
+
+               irlmp_do_discovery(nslots);
+               /* Note : we never do expiry here. Expiry will run on the
+                * discovery timer regardless of the state of sysctl_discovery
+                * Jean II */
+       }
+}
+EXPORT_SYMBOL(irlmp_discovery_request);
+
+/*
+ * Function irlmp_get_discoveries (pn, mask, slots)
+ *
+ *    Return the current discovery log
+ *
+ * If discovery is not enabled, you should call this function again
+ * after 1 or 2 seconds (i.e. after discovery has been done).
+ */
+struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots)
+{
+       /* If discovery is not enabled, it's likely that the discovery log
+        * will be empty. So, we trigger a single discovery, so that next
+        * time the user call us there might be some results in the log.
+        * Jean II
+        */
+       if (!sysctl_discovery) {
+               /* Check if user wants to override the default */
+               if (nslots == DISCOVERY_DEFAULT_SLOTS)
+                       nslots = sysctl_discovery_slots;
+
+               /* Start discovery - will complete sometime later */
+               irlmp_do_discovery(nslots);
+               /* Note : we never do expiry here. Expiry will run on the
+                * discovery timer regardless of the state of sysctl_discovery
+                * Jean II */
+       }
+
+       /* Return current cached discovery log */
+       return irlmp_copy_discoveries(irlmp->cachelog, pn, mask, TRUE);
+}
+EXPORT_SYMBOL(irlmp_get_discoveries);
+
+/*
+ * Function irlmp_notify_client (log)
+ *
+ *    Notify all about discovered devices
+ *
+ * Clients registered with IrLMP are :
+ *     o IrComm
+ *     o IrLAN
+ *     o Any socket (in any state - ouch, that may be a lot !)
+ * The client may have defined a callback to be notified in case of
+ * partial/selective discovery based on the hints that it passed to IrLMP.
+ */
+static inline void
+irlmp_notify_client(irlmp_client_t *client,
+                   hashbin_t *log, DISCOVERY_MODE mode)
+{
+       discinfo_t *discoveries;        /* Copy of the discovery log */
+       int     number;                 /* Number of nodes in the log */
+       int     i;
+
+       /* Check if client wants or not partial/selective log (optimisation) */
+       if (!client->disco_callback)
+               return;
+
+       /*
+        * Locking notes :
+        * the old code was manipulating the log directly, which was
+        * very racy. Now, we use copy_discoveries, that protects
+        * itself while dumping the log for us.
+        * The overhead of the copy is compensated by the fact that
+        * we only pass new discoveries in normal mode and don't
+        * pass the same old entry every 3s to the caller as we used
+        * to do (virtual function calling is expensive).
+        * Jean II
+        */
+
+       /*
+        * Now, check all discovered devices (if any), and notify client
+        * only about the services that the client is interested in
+        * We also notify only about the new devices unless the caller
+        * explicitly request a dump of the log. Jean II
+        */
+       discoveries = irlmp_copy_discoveries(log, &number,
+                                            client->hint_mask.word,
+                                            (mode == DISCOVERY_LOG));
+       /* Check if the we got some results */
+       if (discoveries == NULL)
+               return; /* No nodes discovered */
+
+       /* Pass all entries to the listener */
+       for(i = 0; i < number; i++)
+               client->disco_callback(&(discoveries[i]), mode, client->priv);
+
+       /* Free up our buffer */
+       kfree(discoveries);
+}
+
+/*
+ * Function irlmp_discovery_confirm ( self, log)
+ *
+ *    Some device(s) answered to our discovery request! Check to see which
+ *    device it is, and give indication to the client(s)
+ *
+ */
+void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
+{
+       irlmp_client_t *client;
+       irlmp_client_t *client_next;
+
+       IRDA_ASSERT(log != NULL, return;);
+
+       if (!(HASHBIN_GET_SIZE(log)))
+               return;
+
+       /* For each client - notify callback may touch client list */
+       client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
+       while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
+                                        (void *) &client_next) ) {
+               /* Check if we should notify client */
+               irlmp_notify_client(client, log, mode);
+
+               client = client_next;
+       }
+}
+
+/*
+ * Function irlmp_discovery_expiry (expiry)
+ *
+ *     This device is no longer been discovered, and therefore it is being
+ *     purged from the discovery log. Inform all clients who have
+ *     registered for this event...
+ *
+ *     Note : called exclusively from discovery.c
+ *     Note : this is no longer called under discovery spinlock, so the
+ *             client can do whatever he wants in the callback.
+ */
+void irlmp_discovery_expiry(discinfo_t *expiries, int number)
+{
+       irlmp_client_t *client;
+       irlmp_client_t *client_next;
+       int             i;
+
+       IRDA_ASSERT(expiries != NULL, return;);
+
+       /* For each client - notify callback may touch client list */
+       client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
+       while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
+                                        (void *) &client_next) ) {
+
+               /* Pass all entries to the listener */
+               for(i = 0; i < number; i++) {
+                       /* Check if we should notify client */
+                       if ((client->expir_callback) &&
+                           (client->hint_mask.word &
+                            get_unaligned((__u16 *)expiries[i].hints)
+                            & 0x7f7f) )
+                               client->expir_callback(&(expiries[i]),
+                                                      EXPIRY_TIMEOUT,
+                                                      client->priv);
+               }
+
+               /* Next client */
+               client = client_next;
+       }
+}
+
+/*
+ * Function irlmp_get_discovery_response ()
+ *
+ *    Used by IrLAP to get the discovery info it needs when answering
+ *    discovery requests by other devices.
+ */
+discovery_t *irlmp_get_discovery_response(void)
+{
+       IRDA_ASSERT(irlmp != NULL, return NULL;);
+
+       put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints);
+
+       /*
+        *  Set character set for device name (we use ASCII), and
+        *  copy device name. Remember to make room for a \0 at the
+        *  end
+        */
+       irlmp->discovery_rsp.data.charset = CS_ASCII;
+
+       strncpy(irlmp->discovery_rsp.data.info, sysctl_devname,
+               NICKNAME_MAX_LEN);
+       irlmp->discovery_rsp.name_len = strlen(irlmp->discovery_rsp.data.info);
+
+       return &irlmp->discovery_rsp;
+}
+
+/*
+ * Function irlmp_data_request (self, skb)
+ *
+ *    Send some data to peer device
+ *
+ * Note on skb management :
+ * After calling the lower layers of the IrDA stack, we always
+ * kfree() the skb, which drop the reference count (and potentially
+ * destroy it).
+ * IrLMP and IrLAP may queue the packet, and in those cases will need
+ * to use skb_get() to keep it around.
+ * Jean II
+ */
+int irlmp_data_request(struct lsap_cb *self, struct sk_buff *userdata)
+{
+       int     ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+
+       /* Make room for MUX header */
+       IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;);
+       skb_push(userdata, LMP_HEADER);
+
+       ret = irlmp_do_lsap_event(self, LM_DATA_REQUEST, userdata);
+
+       /* Drop reference count - see irlap_data_request(). */
+       dev_kfree_skb(userdata);
+
+       return ret;
+}
+EXPORT_SYMBOL(irlmp_data_request);
+
+/*
+ * Function irlmp_data_indication (handle, skb)
+ *
+ *    Got data from LAP layer so pass it up to upper layer
+ *
+ */
+void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb)
+{
+       /* Hide LMP header from layer above */
+       skb_pull(skb, LMP_HEADER);
+
+       if (self->notify.data_indication) {
+               /* Don't forget to refcount it - see irlap_driver_rcv(). */
+               skb_get(skb);
+               self->notify.data_indication(self->notify.instance, self, skb);
+       }
+}
+
+/*
+ * Function irlmp_udata_request (self, skb)
+ */
+int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata)
+{
+       int     ret;
+
+       IRDA_ASSERT(userdata != NULL, return -1;);
+
+       /* Make room for MUX header */
+       IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;);
+       skb_push(userdata, LMP_HEADER);
+
+       ret = irlmp_do_lsap_event(self, LM_UDATA_REQUEST, userdata);
+
+       /* Drop reference count - see irlap_data_request(). */
+       dev_kfree_skb(userdata);
+
+       return ret;
+}
+
+/*
+ * Function irlmp_udata_indication (self, skb)
+ *
+ *    Send unreliable data (but still within the connection)
+ *
+ */
+void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       /* Hide LMP header from layer above */
+       skb_pull(skb, LMP_HEADER);
+
+       if (self->notify.udata_indication) {
+               /* Don't forget to refcount it - see irlap_driver_rcv(). */
+               skb_get(skb);
+               self->notify.udata_indication(self->notify.instance, self,
+                                             skb);
+       }
+}
+
+/*
+ * Function irlmp_connless_data_request (self, skb)
+ */
+#ifdef CONFIG_IRDA_ULTRA
+int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata,
+                               __u8 pid)
+{
+       struct sk_buff *clone_skb;
+       struct lap_cb *lap;
+
+       IRDA_ASSERT(userdata != NULL, return -1;);
+
+       /* Make room for MUX and PID header */
+       IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER+LMP_PID_HEADER,
+                   return -1;);
+
+       /* Insert protocol identifier */
+       skb_push(userdata, LMP_PID_HEADER);
+       if(self != NULL)
+         userdata->data[0] = self->pid;
+       else
+         userdata->data[0] = pid;
+
+       /* Connectionless sockets must use 0x70 */
+       skb_push(userdata, LMP_HEADER);
+       userdata->data[0] = userdata->data[1] = LSAP_CONNLESS;
+
+       /* Try to send Connectionless  packets out on all links */
+       lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
+       while (lap != NULL) {
+               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return -1;);
+
+               clone_skb = skb_clone(userdata, GFP_ATOMIC);
+               if (!clone_skb) {
+                       dev_kfree_skb(userdata);
+                       return -ENOMEM;
+               }
+
+               irlap_unitdata_request(lap->irlap, clone_skb);
+               /* irlap_unitdata_request() don't increase refcount,
+                * so no dev_kfree_skb() - Jean II */
+
+               lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
+       }
+       dev_kfree_skb(userdata);
+
+       return 0;
+}
+#endif /* CONFIG_IRDA_ULTRA */
+
+/*
+ * Function irlmp_connless_data_indication (self, skb)
+ *
+ *    Receive unreliable data outside any connection. Mostly used by Ultra
+ *
+ */
+#ifdef CONFIG_IRDA_ULTRA
+void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       /* Hide LMP and PID header from layer above */
+       skb_pull(skb, LMP_HEADER+LMP_PID_HEADER);
+
+       if (self->notify.udata_indication) {
+               /* Don't forget to refcount it - see irlap_driver_rcv(). */
+               skb_get(skb);
+               self->notify.udata_indication(self->notify.instance, self,
+                                             skb);
+       }
+}
+#endif /* CONFIG_IRDA_ULTRA */
+
+/*
+ * Propagate status indication from LAP to LSAPs (via LMP)
+ * This don't trigger any change of state in lap_cb, lmp_cb or lsap_cb,
+ * and the event is stateless, therefore we can bypass both state machines
+ * and send the event direct to the LSAP user.
+ * Jean II
+ */
+void irlmp_status_indication(struct lap_cb *self,
+                            LINK_STATUS link, LOCK_STATUS lock)
+{
+       struct lsap_cb *next;
+       struct lsap_cb *curr;
+
+       /* Send status_indication to all LSAPs using this link */
+       curr = (struct lsap_cb *) hashbin_get_first( self->lsaps);
+       while (NULL != hashbin_find_next(self->lsaps, (long) curr, NULL,
+                                        (void *) &next) ) {
+               IRDA_ASSERT(curr->magic == LMP_LSAP_MAGIC, return;);
+               /*
+                *  Inform service user if he has requested it
+                */
+               if (curr->notify.status_indication != NULL)
+                       curr->notify.status_indication(curr->notify.instance,
+                                                      link, lock);
+               else
+                       pr_debug("%s(), no handler\n", __func__);
+
+               curr = next;
+       }
+}
+
+/*
+ * Receive flow control indication from LAP.
+ * LAP want us to send it one more frame. We implement a simple round
+ * robin scheduler between the active sockets so that we get a bit of
+ * fairness. Note that the round robin is far from perfect, but it's
+ * better than nothing.
+ * We then poll the selected socket so that we can do synchronous
+ * refilling of IrLAP (which allow to minimise the number of buffers).
+ * Jean II
+ */
+void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow)
+{
+       struct lsap_cb *next;
+       struct lsap_cb *curr;
+       int     lsap_todo;
+
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+       IRDA_ASSERT(flow == FLOW_START, return;);
+
+       /* Get the number of lsap. That's the only safe way to know
+        * that we have looped around... - Jean II */
+       lsap_todo = HASHBIN_GET_SIZE(self->lsaps);
+       pr_debug("%s() : %d lsaps to scan\n", __func__, lsap_todo);
+
+       /* Poll lsap in order until the queue is full or until we
+        * tried them all.
+        * Most often, the current LSAP will have something to send,
+        * so we will go through this loop only once. - Jean II */
+       while((lsap_todo--) &&
+             (IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) {
+               /* Try to find the next lsap we should poll. */
+               next = self->flow_next;
+               /* If we have no lsap, restart from first one */
+               if(next == NULL)
+                       next = (struct lsap_cb *) hashbin_get_first(self->lsaps);
+               /* Verify current one and find the next one */
+               curr = hashbin_find_next(self->lsaps, (long) next, NULL,
+                                        (void *) &self->flow_next);
+               /* Uh-oh... Paranoia */
+               if(curr == NULL)
+                       break;
+               pr_debug("%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n",
+                        __func__, curr, next, self->flow_next, lsap_todo,
+                        IRLAP_GET_TX_QUEUE_LEN(self->irlap));
+
+               /* Inform lsap user that it can send one more packet. */
+               if (curr->notify.flow_indication != NULL)
+                       curr->notify.flow_indication(curr->notify.instance,
+                                                    curr, flow);
+               else
+                       pr_debug("%s(), no handler\n", __func__);
+       }
+}
+
+#if 0
+/*
+ * Function irlmp_hint_to_service (hint)
+ *
+ *    Returns a list of all servics contained in the given hint bits. This
+ *    function assumes that the hint bits have the size of two bytes only
+ */
+__u8 *irlmp_hint_to_service(__u8 *hint)
+{
+       __u8 *service;
+       int i = 0;
+
+       /*
+        * Allocate array to store services in. 16 entries should be safe
+        * since we currently only support 2 hint bytes
+        */
+       service = kmalloc(16, GFP_ATOMIC);
+       if (!service)
+               return NULL;
+
+       if (!hint[0]) {
+               pr_debug("<None>\n");
+               kfree(service);
+               return NULL;
+       }
+       if (hint[0] & HINT_PNP)
+               pr_debug("PnP Compatible ");
+       if (hint[0] & HINT_PDA)
+               pr_debug("PDA/Palmtop ");
+       if (hint[0] & HINT_COMPUTER)
+               pr_debug("Computer ");
+       if (hint[0] & HINT_PRINTER) {
+               pr_debug("Printer ");
+               service[i++] = S_PRINTER;
+       }
+       if (hint[0] & HINT_MODEM)
+               pr_debug("Modem ");
+       if (hint[0] & HINT_FAX)
+               pr_debug("Fax ");
+       if (hint[0] & HINT_LAN) {
+               pr_debug("LAN Access ");
+               service[i++] = S_LAN;
+       }
+       /*
+        *  Test if extension byte exists. This byte will usually be
+        *  there, but this is not really required by the standard.
+        *  (IrLMP p. 29)
+        */
+       if (hint[0] & HINT_EXTENSION) {
+               if (hint[1] & HINT_TELEPHONY) {
+                       pr_debug("Telephony ");
+                       service[i++] = S_TELEPHONY;
+               }
+               if (hint[1] & HINT_FILE_SERVER)
+                       pr_debug("File Server ");
+
+               if (hint[1] & HINT_COMM) {
+                       pr_debug("IrCOMM ");
+                       service[i++] = S_COMM;
+               }
+               if (hint[1] & HINT_OBEX) {
+                       pr_debug("IrOBEX ");
+                       service[i++] = S_OBEX;
+               }
+       }
+       pr_debug("\n");
+
+       /* So that client can be notified about any discovery */
+       service[i++] = S_ANY;
+
+       service[i] = S_END;
+
+       return service;
+}
+#endif
+
+static const __u16 service_hint_mapping[S_END][2] = {
+       { HINT_PNP,             0 },                    /* S_PNP */
+       { HINT_PDA,             0 },                    /* S_PDA */
+       { HINT_COMPUTER,        0 },                    /* S_COMPUTER */
+       { HINT_PRINTER,         0 },                    /* S_PRINTER */
+       { HINT_MODEM,           0 },                    /* S_MODEM */
+       { HINT_FAX,             0 },                    /* S_FAX */
+       { HINT_LAN,             0 },                    /* S_LAN */
+       { HINT_EXTENSION,       HINT_TELEPHONY },       /* S_TELEPHONY */
+       { HINT_EXTENSION,       HINT_COMM },            /* S_COMM */
+       { HINT_EXTENSION,       HINT_OBEX },            /* S_OBEX */
+       { 0xFF,                 0xFF },                 /* S_ANY */
+};
+
+/*
+ * Function irlmp_service_to_hint (service)
+ *
+ *    Converts a service type, to a hint bit
+ *
+ *    Returns: a 16 bit hint value, with the service bit set
+ */
+__u16 irlmp_service_to_hint(int service)
+{
+       __u16_host_order hint;
+
+       hint.byte[0] = service_hint_mapping[service][0];
+       hint.byte[1] = service_hint_mapping[service][1];
+
+       return hint.word;
+}
+EXPORT_SYMBOL(irlmp_service_to_hint);
+
+/*
+ * Function irlmp_register_service (service)
+ *
+ *    Register local service with IrLMP
+ *
+ */
+void *irlmp_register_service(__u16 hints)
+{
+       irlmp_service_t *service;
+
+       pr_debug("%s(), hints = %04x\n", __func__, hints);
+
+       /* Make a new registration */
+       service = kmalloc(sizeof(irlmp_service_t), GFP_ATOMIC);
+       if (!service)
+               return NULL;
+
+       service->hints.word = hints;
+       hashbin_insert(irlmp->services, (irda_queue_t *) service,
+                      (long) service, NULL);
+
+       irlmp->hints.word |= hints;
+
+       return (void *)service;
+}
+EXPORT_SYMBOL(irlmp_register_service);
+
+/*
+ * Function irlmp_unregister_service (handle)
+ *
+ *    Unregister service with IrLMP.
+ *
+ *    Returns: 0 on success, -1 on error
+ */
+int irlmp_unregister_service(void *handle)
+{
+       irlmp_service_t *service;
+       unsigned long flags;
+
+       if (!handle)
+               return -1;
+
+       /* Caller may call with invalid handle (it's legal) - Jean II */
+       service = hashbin_lock_find(irlmp->services, (long) handle, NULL);
+       if (!service) {
+               pr_debug("%s(), Unknown service!\n", __func__);
+               return -1;
+       }
+
+       hashbin_remove_this(irlmp->services, (irda_queue_t *) service);
+       kfree(service);
+
+       /* Remove old hint bits */
+       irlmp->hints.word = 0;
+
+       /* Refresh current hint bits */
+       spin_lock_irqsave(&irlmp->services->hb_spinlock, flags);
+       service = (irlmp_service_t *) hashbin_get_first(irlmp->services);
+       while (service) {
+               irlmp->hints.word |= service->hints.word;
+
+               service = (irlmp_service_t *)hashbin_get_next(irlmp->services);
+       }
+       spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(irlmp_unregister_service);
+
+/*
+ * Function irlmp_register_client (hint_mask, callback1, callback2)
+ *
+ *    Register a local client with IrLMP
+ *     First callback is selective discovery (based on hints)
+ *     Second callback is for selective discovery expiries
+ *
+ *    Returns: handle > 0 on success, 0 on error
+ */
+void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
+                           DISCOVERY_CALLBACK2 expir_clb, void *priv)
+{
+       irlmp_client_t *client;
+
+       IRDA_ASSERT(irlmp != NULL, return NULL;);
+
+       /* Make a new registration */
+       client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC);
+       if (!client)
+               return NULL;
+
+       /* Register the details */
+       client->hint_mask.word = hint_mask;
+       client->disco_callback = disco_clb;
+       client->expir_callback = expir_clb;
+       client->priv = priv;
+
+       hashbin_insert(irlmp->clients, (irda_queue_t *) client,
+                      (long) client, NULL);
+
+       return (void *) client;
+}
+EXPORT_SYMBOL(irlmp_register_client);
+
+/*
+ * Function irlmp_update_client (handle, hint_mask, callback1, callback2)
+ *
+ *    Updates specified client (handle) with possibly new hint_mask and
+ *    callback
+ *
+ *    Returns: 0 on success, -1 on error
+ */
+int irlmp_update_client(void *handle, __u16 hint_mask,
+                       DISCOVERY_CALLBACK1 disco_clb,
+                       DISCOVERY_CALLBACK2 expir_clb, void *priv)
+{
+       irlmp_client_t *client;
+
+       if (!handle)
+               return -1;
+
+       client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
+       if (!client) {
+               pr_debug("%s(), Unknown client!\n", __func__);
+               return -1;
+       }
+
+       client->hint_mask.word = hint_mask;
+       client->disco_callback = disco_clb;
+       client->expir_callback = expir_clb;
+       client->priv = priv;
+
+       return 0;
+}
+EXPORT_SYMBOL(irlmp_update_client);
+
+/*
+ * Function irlmp_unregister_client (handle)
+ *
+ *    Returns: 0 on success, -1 on error
+ *
+ */
+int irlmp_unregister_client(void *handle)
+{
+       struct irlmp_client *client;
+
+       if (!handle)
+               return -1;
+
+       /* Caller may call with invalid handle (it's legal) - Jean II */
+       client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
+       if (!client) {
+               pr_debug("%s(), Unknown client!\n", __func__);
+               return -1;
+       }
+
+       pr_debug("%s(), removing client!\n", __func__);
+       hashbin_remove_this(irlmp->clients, (irda_queue_t *) client);
+       kfree(client);
+
+       return 0;
+}
+EXPORT_SYMBOL(irlmp_unregister_client);
+
+/*
+ * Function irlmp_slsap_inuse (slsap)
+ *
+ *    Check if the given source LSAP selector is in use
+ *
+ * This function is clearly not very efficient. On the mitigating side, the
+ * stack make sure that in 99% of the cases, we are called only once
+ * for each socket allocation. We could probably keep a bitmap
+ * of the allocated LSAP, but I'm not sure the complexity is worth it.
+ * Jean II
+ */
+static int irlmp_slsap_inuse(__u8 slsap_sel)
+{
+       struct lsap_cb *self;
+       struct lap_cb *lap;
+       unsigned long flags;
+
+       IRDA_ASSERT(irlmp != NULL, return TRUE;);
+       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;);
+       IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;);
+
+#ifdef CONFIG_IRDA_ULTRA
+       /* Accept all bindings to the connectionless LSAP */
+       if (slsap_sel == LSAP_CONNLESS)
+               return FALSE;
+#endif /* CONFIG_IRDA_ULTRA */
+
+       /* Valid values are between 0 and 127 (0x0-0x6F) */
+       if (slsap_sel > LSAP_MAX)
+               return TRUE;
+
+       /*
+        *  Check if slsap is already in use. To do this we have to loop over
+        *  every IrLAP connection and check every LSAP associated with each
+        *  the connection.
+        */
+       spin_lock_irqsave_nested(&irlmp->links->hb_spinlock, flags,
+                       SINGLE_DEPTH_NESTING);
+       lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
+       while (lap != NULL) {
+               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, goto errlap;);
+
+               /* Careful for priority inversions here !
+                * irlmp->links is never taken while another IrDA
+                * spinlock is held, so we are safe. Jean II */
+               spin_lock(&lap->lsaps->hb_spinlock);
+
+               /* For this IrLAP, check all the LSAPs */
+               self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
+               while (self != NULL) {
+                       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC,
+                                   goto errlsap;);
+
+                       if ((self->slsap_sel == slsap_sel)) {
+                               pr_debug("Source LSAP selector=%02x in use\n",
+                                        self->slsap_sel);
+                               goto errlsap;
+                       }
+                       self = (struct lsap_cb*) hashbin_get_next(lap->lsaps);
+               }
+               spin_unlock(&lap->lsaps->hb_spinlock);
+
+               /* Next LAP */
+               lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
+       }
+       spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
+
+       /*
+        * Server sockets are typically waiting for connections and
+        * therefore reside in the unconnected list. We don't want
+        * to give out their LSAPs for obvious reasons...
+        * Jean II
+        */
+       spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
+
+       self = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
+       while (self != NULL) {
+               IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;);
+               if ((self->slsap_sel == slsap_sel)) {
+                       pr_debug("Source LSAP selector=%02x in use (unconnected)\n",
+                                self->slsap_sel);
+                       goto erruncon;
+               }
+               self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps);
+       }
+       spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
+
+       return FALSE;
+
+       /* Error exit from within one of the two nested loops.
+        * Make sure we release the right spinlock in the righ order.
+        * Jean II */
+errlsap:
+       spin_unlock(&lap->lsaps->hb_spinlock);
+IRDA_ASSERT_LABEL(errlap:)
+       spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
+       return TRUE;
+
+       /* Error exit from within the unconnected loop.
+        * Just one spinlock to release... Jean II */
+erruncon:
+       spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
+       return TRUE;
+}
+
+/*
+ * Function irlmp_find_free_slsap ()
+ *
+ *    Find a free source LSAP to use. This function is called if the service
+ *    user has requested a source LSAP equal to LM_ANY
+ */
+static __u8 irlmp_find_free_slsap(void)
+{
+       __u8 lsap_sel;
+       int wrapped = 0;
+
+       IRDA_ASSERT(irlmp != NULL, return -1;);
+       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return -1;);
+
+       /* Most users don't really care which LSAPs they are given,
+        * and therefore we automatically give them a free LSAP.
+        * This function try to find a suitable LSAP, i.e. which is
+        * not in use and is within the acceptable range. Jean II */
+
+       do {
+               /* Always increment to LSAP number before using it.
+                * In theory, we could reuse the last LSAP number, as long
+                * as it is no longer in use. Some IrDA stack do that.
+                * However, the previous socket may be half closed, i.e.
+                * we closed it, we think it's no longer in use, but the
+                * other side did not receive our close and think it's
+                * active and still send data on it.
+                * This is similar to what is done with PIDs and TCP ports.
+                * Also, this reduce the number of calls to irlmp_slsap_inuse()
+                * which is an expensive function to call.
+                * Jean II */
+               irlmp->last_lsap_sel++;
+
+               /* Check if we need to wraparound (0x70-0x7f are reserved) */
+               if (irlmp->last_lsap_sel > LSAP_MAX) {
+                       /* 0x00-0x10 are also reserved for well know ports */
+                       irlmp->last_lsap_sel = 0x10;
+
+                       /* Make sure we terminate the loop */
+                       if (wrapped++) {
+                               net_err_ratelimited("%s: no more free LSAPs !\n",
+                                                   __func__);
+                               return 0;
+                       }
+               }
+
+               /* If the LSAP is in use, try the next one.
+                * Despite the autoincrement, we need to check if the lsap
+                * is really in use or not, first because LSAP may be
+                * directly allocated in irlmp_open_lsap(), and also because
+                * we may wraparound on old sockets. Jean II */
+       } while (irlmp_slsap_inuse(irlmp->last_lsap_sel));
+
+       /* Got it ! */
+       lsap_sel = irlmp->last_lsap_sel;
+       pr_debug("%s(), found free lsap_sel=%02x\n",
+                __func__, lsap_sel);
+
+       return lsap_sel;
+}
+
+/*
+ * Function irlmp_convert_lap_reason (lap_reason)
+ *
+ *    Converts IrLAP disconnect reason codes to IrLMP disconnect reason
+ *    codes
+ *
+ */
+LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason)
+{
+       int reason = LM_LAP_DISCONNECT;
+
+       switch (lap_reason) {
+       case LAP_DISC_INDICATION: /* Received a disconnect request from peer */
+               pr_debug("%s(), LAP_DISC_INDICATION\n", __func__);
+               reason = LM_USER_REQUEST;
+               break;
+       case LAP_NO_RESPONSE:    /* To many retransmits without response */
+               pr_debug("%s(), LAP_NO_RESPONSE\n", __func__);
+               reason = LM_LAP_DISCONNECT;
+               break;
+       case LAP_RESET_INDICATION:
+               pr_debug("%s(), LAP_RESET_INDICATION\n", __func__);
+               reason = LM_LAP_RESET;
+               break;
+       case LAP_FOUND_NONE:
+       case LAP_MEDIA_BUSY:
+       case LAP_PRIMARY_CONFLICT:
+               pr_debug("%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n",
+                        __func__);
+               reason = LM_CONNECT_FAILURE;
+               break;
+       default:
+               pr_debug("%s(), Unknown IrLAP disconnect reason %d!\n",
+                        __func__, lap_reason);
+               reason = LM_LAP_DISCONNECT;
+               break;
+       }
+
+       return reason;
+}
+
+#ifdef CONFIG_PROC_FS
+
+struct irlmp_iter_state {
+       hashbin_t *hashbin;
+};
+
+#define LSAP_START_TOKEN       ((void *)1)
+#define LINK_START_TOKEN       ((void *)2)
+
+static void *irlmp_seq_hb_idx(struct irlmp_iter_state *iter, loff_t *off)
+{
+       void *element;
+
+       spin_lock_irq(&iter->hashbin->hb_spinlock);
+       for (element = hashbin_get_first(iter->hashbin);
+            element != NULL;
+            element = hashbin_get_next(iter->hashbin)) {
+               if (!off || (*off)-- == 0) {
+                       /* NB: hashbin left locked */
+                       return element;
+               }
+       }
+       spin_unlock_irq(&iter->hashbin->hb_spinlock);
+       iter->hashbin = NULL;
+       return NULL;
+}
+
+
+static void *irlmp_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       struct irlmp_iter_state *iter = seq->private;
+       void *v;
+       loff_t off = *pos;
+
+       iter->hashbin = NULL;
+       if (off-- == 0)
+               return LSAP_START_TOKEN;
+
+       iter->hashbin = irlmp->unconnected_lsaps;
+       v = irlmp_seq_hb_idx(iter, &off);
+       if (v)
+               return v;
+
+       if (off-- == 0)
+               return LINK_START_TOKEN;
+
+       iter->hashbin = irlmp->links;
+       return irlmp_seq_hb_idx(iter, &off);
+}
+
+static void *irlmp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       struct irlmp_iter_state *iter = seq->private;
+
+       ++*pos;
+
+       if (v == LSAP_START_TOKEN) {            /* start of list of lsaps */
+               iter->hashbin = irlmp->unconnected_lsaps;
+               v = irlmp_seq_hb_idx(iter, NULL);
+               return v ? v : LINK_START_TOKEN;
+       }
+
+       if (v == LINK_START_TOKEN) {            /* start of list of links */
+               iter->hashbin = irlmp->links;
+               return irlmp_seq_hb_idx(iter, NULL);
+       }
+
+       v = hashbin_get_next(iter->hashbin);
+
+       if (v == NULL) {                        /* no more in this hash bin */
+               spin_unlock_irq(&iter->hashbin->hb_spinlock);
+
+               if (iter->hashbin == irlmp->unconnected_lsaps)
+                       v =  LINK_START_TOKEN;
+
+               iter->hashbin = NULL;
+       }
+       return v;
+}
+
+static void irlmp_seq_stop(struct seq_file *seq, void *v)
+{
+       struct irlmp_iter_state *iter = seq->private;
+
+       if (iter->hashbin)
+               spin_unlock_irq(&iter->hashbin->hb_spinlock);
+}
+
+static int irlmp_seq_show(struct seq_file *seq, void *v)
+{
+       const struct irlmp_iter_state *iter = seq->private;
+       struct lsap_cb *self = v;
+
+       if (v == LSAP_START_TOKEN)
+               seq_puts(seq, "Unconnected LSAPs:\n");
+       else if (v == LINK_START_TOKEN)
+               seq_puts(seq, "\nRegistered Link Layers:\n");
+       else if (iter->hashbin == irlmp->unconnected_lsaps) {
+               self = v;
+               IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EINVAL; );
+               seq_printf(seq, "lsap state: %s, ",
+                          irlsap_state[ self->lsap_state]);
+               seq_printf(seq,
+                          "slsap_sel: %#02x, dlsap_sel: %#02x, ",
+                          self->slsap_sel, self->dlsap_sel);
+               seq_printf(seq, "(%s)", self->notify.name);
+               seq_printf(seq, "\n");
+       } else if (iter->hashbin == irlmp->links) {
+               struct lap_cb *lap = v;
+
+               seq_printf(seq, "lap state: %s, ",
+                          irlmp_state[lap->lap_state]);
+
+               seq_printf(seq, "saddr: %#08x, daddr: %#08x, ",
+                          lap->saddr, lap->daddr);
+               seq_printf(seq, "num lsaps: %d",
+                          HASHBIN_GET_SIZE(lap->lsaps));
+               seq_printf(seq, "\n");
+
+               /* Careful for priority inversions here !
+                * All other uses of attrib spinlock are independent of
+                * the object spinlock, so we are safe. Jean II */
+               spin_lock(&lap->lsaps->hb_spinlock);
+
+               seq_printf(seq, "\n  Connected LSAPs:\n");
+               for (self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
+                    self != NULL;
+                    self = (struct lsap_cb *)hashbin_get_next(lap->lsaps)) {
+                       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC,
+                                   goto outloop;);
+                       seq_printf(seq, "  lsap state: %s, ",
+                                  irlsap_state[ self->lsap_state]);
+                       seq_printf(seq,
+                                  "slsap_sel: %#02x, dlsap_sel: %#02x, ",
+                                  self->slsap_sel, self->dlsap_sel);
+                       seq_printf(seq, "(%s)", self->notify.name);
+                       seq_putc(seq, '\n');
+
+               }
+       IRDA_ASSERT_LABEL(outloop:)
+               spin_unlock(&lap->lsaps->hb_spinlock);
+               seq_putc(seq, '\n');
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct seq_operations irlmp_seq_ops = {
+       .start  = irlmp_seq_start,
+       .next   = irlmp_seq_next,
+       .stop   = irlmp_seq_stop,
+       .show   = irlmp_seq_show,
+};
+
+static int irlmp_seq_open(struct inode *inode, struct file *file)
+{
+       IRDA_ASSERT(irlmp != NULL, return -EINVAL;);
+
+       return seq_open_private(file, &irlmp_seq_ops,
+                       sizeof(struct irlmp_iter_state));
+}
+
+const struct file_operations irlmp_seq_fops = {
+       .owner          = THIS_MODULE,
+       .open           = irlmp_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release_private,
+};
+
+#endif /* PROC_FS */
diff --git a/drivers/staging/irda/net/irlmp_event.c b/drivers/staging/irda/net/irlmp_event.c
new file mode 100644 (file)
index 0000000..e306cf2
--- /dev/null
@@ -0,0 +1,886 @@
+/*********************************************************************
+ *
+ * Filename:      irlmp_event.c
+ * Version:       0.8
+ * Description:   An IrDA LMP event driver for Linux
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Mon Aug  4 20:40:53 1997
+ * Modified at:   Tue Dec 14 23:04:16 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/kernel.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/timer.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irlmp_frame.h>
+#include <net/irda/irlmp_event.h>
+
+const char *const irlmp_state[] = {
+       "LAP_STANDBY",
+       "LAP_U_CONNECT",
+       "LAP_ACTIVE",
+};
+
+const char *const irlsap_state[] = {
+       "LSAP_DISCONNECTED",
+       "LSAP_CONNECT",
+       "LSAP_CONNECT_PEND",
+       "LSAP_DATA_TRANSFER_READY",
+       "LSAP_SETUP",
+       "LSAP_SETUP_PEND",
+};
+
+static const char *const irlmp_event[] __maybe_unused = {
+       "LM_CONNECT_REQUEST",
+       "LM_CONNECT_CONFIRM",
+       "LM_CONNECT_RESPONSE",
+       "LM_CONNECT_INDICATION",
+
+       "LM_DISCONNECT_INDICATION",
+       "LM_DISCONNECT_REQUEST",
+
+       "LM_DATA_REQUEST",
+       "LM_UDATA_REQUEST",
+       "LM_DATA_INDICATION",
+       "LM_UDATA_INDICATION",
+
+       "LM_WATCHDOG_TIMEOUT",
+
+       /* IrLAP events */
+       "LM_LAP_CONNECT_REQUEST",
+       "LM_LAP_CONNECT_INDICATION",
+       "LM_LAP_CONNECT_CONFIRM",
+       "LM_LAP_DISCONNECT_INDICATION",
+       "LM_LAP_DISCONNECT_REQUEST",
+       "LM_LAP_DISCOVERY_REQUEST",
+       "LM_LAP_DISCOVERY_CONFIRM",
+       "LM_LAP_IDLE_TIMEOUT",
+};
+
+/* LAP Connection control proto declarations */
+static void irlmp_state_standby  (struct lap_cb *, IRLMP_EVENT,
+                                 struct sk_buff *);
+static void irlmp_state_u_connect(struct lap_cb *, IRLMP_EVENT,
+                                 struct sk_buff *);
+static void irlmp_state_active   (struct lap_cb *, IRLMP_EVENT,
+                                 struct sk_buff *);
+
+/* LSAP Connection control proto declarations */
+static int irlmp_state_disconnected(struct lsap_cb *, IRLMP_EVENT,
+                                   struct sk_buff *);
+static int irlmp_state_connect     (struct lsap_cb *, IRLMP_EVENT,
+                                   struct sk_buff *);
+static int irlmp_state_connect_pend(struct lsap_cb *, IRLMP_EVENT,
+                                   struct sk_buff *);
+static int irlmp_state_dtr         (struct lsap_cb *, IRLMP_EVENT,
+                                   struct sk_buff *);
+static int irlmp_state_setup       (struct lsap_cb *, IRLMP_EVENT,
+                                   struct sk_buff *);
+static int irlmp_state_setup_pend  (struct lsap_cb *, IRLMP_EVENT,
+                                   struct sk_buff *);
+
+static void (*lap_state[]) (struct lap_cb *, IRLMP_EVENT, struct sk_buff *) =
+{
+       irlmp_state_standby,
+       irlmp_state_u_connect,
+       irlmp_state_active,
+};
+
+static int (*lsap_state[])( struct lsap_cb *, IRLMP_EVENT, struct sk_buff *) =
+{
+       irlmp_state_disconnected,
+       irlmp_state_connect,
+       irlmp_state_connect_pend,
+       irlmp_state_dtr,
+       irlmp_state_setup,
+       irlmp_state_setup_pend
+};
+
+static inline void irlmp_next_lap_state(struct lap_cb *self,
+                                       IRLMP_STATE state)
+{
+       /*
+         pr_debug("%s(), LMP LAP = %s\n", __func__, irlmp_state[state]);
+       */
+       self->lap_state = state;
+}
+
+static inline void irlmp_next_lsap_state(struct lsap_cb *self,
+                                        LSAP_STATE state)
+{
+       /*
+       IRDA_ASSERT(self != NULL, return;);
+       pr_debug("%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]);
+       */
+       self->lsap_state = state;
+}
+
+/* Do connection control events */
+int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
+                       struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+
+       pr_debug("%s(), EVENT = %s, STATE = %s\n",
+                __func__, irlmp_event[event], irlsap_state[self->lsap_state]);
+
+       return (*lsap_state[self->lsap_state]) (self, event, skb);
+}
+
+/*
+ * Function do_lap_event (event, skb, info)
+ *
+ *    Do IrLAP control events
+ *
+ */
+void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
+                       struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+
+       pr_debug("%s(), EVENT = %s, STATE = %s\n", __func__,
+                irlmp_event[event],
+                irlmp_state[self->lap_state]);
+
+       (*lap_state[self->lap_state]) (self, event, skb);
+}
+
+void irlmp_discovery_timer_expired(void *data)
+{
+       /* We always cleanup the log (active & passive discovery) */
+       irlmp_do_expiry();
+
+       irlmp_do_discovery(sysctl_discovery_slots);
+
+       /* Restart timer */
+       irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout * HZ);
+}
+
+void irlmp_watchdog_timer_expired(void *data)
+{
+       struct lsap_cb *self = (struct lsap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+
+       irlmp_do_lsap_event(self, LM_WATCHDOG_TIMEOUT, NULL);
+}
+
+void irlmp_idle_timer_expired(void *data)
+{
+       struct lap_cb *self = (struct lap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+
+       irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL);
+}
+
+/*
+ * Send an event on all LSAPs attached to this LAP.
+ */
+static inline void
+irlmp_do_all_lsap_event(hashbin_t *    lsap_hashbin,
+                       IRLMP_EVENT     event)
+{
+       struct lsap_cb *lsap;
+       struct lsap_cb *lsap_next;
+
+       /* Note : this function use the new hashbin_find_next()
+        * function, instead of the old hashbin_get_next().
+        * This make sure that we are always pointing one lsap
+        * ahead, so that if the current lsap is removed as the
+        * result of sending the event, we don't care.
+        * Also, as we store the context ourselves, if an enumeration
+        * of the same lsap hashbin happens as the result of sending the
+        * event, we don't care.
+        * The only problem is if the next lsap is removed. In that case,
+        * hashbin_find_next() will return NULL and we will abort the
+        * enumeration. - Jean II */
+
+       /* Also : we don't accept any skb in input. We can *NOT* pass
+        * the same skb to multiple clients safely, we would need to
+        * skb_clone() it. - Jean II */
+
+       lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin);
+
+       while (NULL != hashbin_find_next(lsap_hashbin,
+                                        (long) lsap,
+                                        NULL,
+                                        (void *) &lsap_next) ) {
+               irlmp_do_lsap_event(lsap, event, NULL);
+               lsap = lsap_next;
+       }
+}
+
+/*********************************************************************
+ *
+ *    LAP connection control states
+ *
+ ********************************************************************/
+
+/*
+ * Function irlmp_state_standby (event, skb, info)
+ *
+ *    STANDBY, The IrLAP connection does not exist.
+ *
+ */
+static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
+                               struct sk_buff *skb)
+{
+       IRDA_ASSERT(self->irlap != NULL, return;);
+
+       switch (event) {
+       case LM_LAP_DISCOVERY_REQUEST:
+               /* irlmp_next_station_state( LMP_DISCOVER); */
+
+               irlap_discovery_request(self->irlap, &irlmp->discovery_cmd);
+               break;
+       case LM_LAP_CONNECT_INDICATION:
+               /*  It's important to switch state first, to avoid IrLMP to
+                *  think that the link is free since IrLMP may then start
+                *  discovery before the connection is properly set up. DB.
+                */
+               irlmp_next_lap_state(self, LAP_ACTIVE);
+
+               /* Just accept connection TODO, this should be fixed */
+               irlap_connect_response(self->irlap, skb);
+               break;
+       case LM_LAP_CONNECT_REQUEST:
+               pr_debug("%s() LS_CONNECT_REQUEST\n", __func__);
+
+               irlmp_next_lap_state(self, LAP_U_CONNECT);
+
+               /* FIXME: need to set users requested QoS */
+               irlap_connect_request(self->irlap, self->daddr, NULL, 0);
+               break;
+       case LM_LAP_DISCONNECT_INDICATION:
+               pr_debug("%s(), Error LM_LAP_DISCONNECT_INDICATION\n",
+                        __func__);
+
+               irlmp_next_lap_state(self, LAP_STANDBY);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n",
+                        __func__, irlmp_event[event]);
+               break;
+       }
+}
+
+/*
+ * Function irlmp_state_u_connect (event, skb, info)
+ *
+ *    U_CONNECT, The layer above has tried to open an LSAP connection but
+ *    since the IrLAP connection does not exist, we must first start an
+ *    IrLAP connection. We are now waiting response from IrLAP.
+ * */
+static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
+                                 struct sk_buff *skb)
+{
+       pr_debug("%s(), event=%s\n", __func__, irlmp_event[event]);
+
+       switch (event) {
+       case LM_LAP_CONNECT_INDICATION:
+               /*  It's important to switch state first, to avoid IrLMP to
+                *  think that the link is free since IrLMP may then start
+                *  discovery before the connection is properly set up. DB.
+                */
+               irlmp_next_lap_state(self, LAP_ACTIVE);
+
+               /* Just accept connection TODO, this should be fixed */
+               irlap_connect_response(self->irlap, skb);
+
+               /* Tell LSAPs that they can start sending data */
+               irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
+
+               /* Note : by the time we get there (LAP retries and co),
+                * the lsaps may already have gone. This avoid getting stuck
+                * forever in LAP_ACTIVE state - Jean II */
+               if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
+                       pr_debug("%s() NO LSAPs !\n",  __func__);
+                       irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
+               }
+               break;
+       case LM_LAP_CONNECT_REQUEST:
+               /* Already trying to connect */
+               break;
+       case LM_LAP_CONNECT_CONFIRM:
+               /* For all lsap_ce E Associated do LS_Connect_confirm */
+               irlmp_next_lap_state(self, LAP_ACTIVE);
+
+               /* Tell LSAPs that they can start sending data */
+               irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
+
+               /* Note : by the time we get there (LAP retries and co),
+                * the lsaps may already have gone. This avoid getting stuck
+                * forever in LAP_ACTIVE state - Jean II */
+               if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
+                       pr_debug("%s() NO LSAPs !\n",  __func__);
+                       irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
+               }
+               break;
+       case LM_LAP_DISCONNECT_INDICATION:
+               pr_debug("%s(), LM_LAP_DISCONNECT_INDICATION\n",  __func__);
+               irlmp_next_lap_state(self, LAP_STANDBY);
+
+               /* Send disconnect event to all LSAPs using this link */
+               irlmp_do_all_lsap_event(self->lsaps,
+                                       LM_LAP_DISCONNECT_INDICATION);
+               break;
+       case LM_LAP_DISCONNECT_REQUEST:
+               pr_debug("%s(), LM_LAP_DISCONNECT_REQUEST\n",  __func__);
+
+               /* One of the LSAP did timeout or was closed, if it was
+                * the last one, try to get out of here - Jean II */
+               if (HASHBIN_GET_SIZE(self->lsaps) <= 1) {
+                       irlap_disconnect_request(self->irlap);
+               }
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n",
+                        __func__, irlmp_event[event]);
+               break;
+       }
+}
+
+/*
+ * Function irlmp_state_active (event, skb, info)
+ *
+ *    ACTIVE, IrLAP connection is active
+ *
+ */
+static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
+                              struct sk_buff *skb)
+{
+       switch (event) {
+       case LM_LAP_CONNECT_REQUEST:
+               pr_debug("%s(), LS_CONNECT_REQUEST\n", __func__);
+
+               /*
+                * IrLAP may have a pending disconnect. We tried to close
+                * IrLAP, but it was postponed because the link was
+                * busy or we were still sending packets. As we now
+                * need it, make sure it stays on. Jean II
+                */
+               irlap_clear_disconnect(self->irlap);
+
+               /*
+                *  LAP connection already active, just bounce back! Since we
+                *  don't know which LSAP that tried to do this, we have to
+                *  notify all LSAPs using this LAP, but that should be safe to
+                *  do anyway.
+                */
+               irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
+
+               /* Needed by connect indication */
+               irlmp_do_all_lsap_event(irlmp->unconnected_lsaps,
+                                       LM_LAP_CONNECT_CONFIRM);
+               /* Keep state */
+               break;
+       case LM_LAP_DISCONNECT_REQUEST:
+               /*
+                *  Need to find out if we should close IrLAP or not. If there
+                *  is only one LSAP connection left on this link, that LSAP
+                *  must be the one that tries to close IrLAP. It will be
+                *  removed later and moved to the list of unconnected LSAPs
+                */
+               if (HASHBIN_GET_SIZE(self->lsaps) > 0) {
+                       /* Timer value is checked in irsysctl - Jean II */
+                       irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000);
+               } else {
+                       /* No more connections, so close IrLAP */
+
+                       /* We don't want to change state just yet, because
+                        * we want to reflect accurately the real state of
+                        * the LAP, not the state we wish it was in,
+                        * so that we don't lose LM_LAP_CONNECT_REQUEST.
+                        * In some cases, IrLAP won't close the LAP
+                        * immediately. For example, it might still be
+                        * retrying packets or waiting for the pf bit.
+                        * As the LAP always send a DISCONNECT_INDICATION
+                        * in PCLOSE or SCLOSE, just change state on that.
+                        * Jean II */
+                       irlap_disconnect_request(self->irlap);
+               }
+               break;
+       case LM_LAP_IDLE_TIMEOUT:
+               if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
+                       /* Same reasoning as above - keep state */
+                       irlap_disconnect_request(self->irlap);
+               }
+               break;
+       case LM_LAP_DISCONNECT_INDICATION:
+               irlmp_next_lap_state(self, LAP_STANDBY);
+
+               /* In some case, at this point our side has already closed
+                * all lsaps, and we are waiting for the idle_timer to
+                * expire. If another device reconnect immediately, the
+                * idle timer will expire in the midle of the connection
+                * initialisation, screwing up things a lot...
+                * Therefore, we must stop the timer... */
+               irlmp_stop_idle_timer(self);
+
+               /*
+                *  Inform all connected LSAP's using this link
+                */
+               irlmp_do_all_lsap_event(self->lsaps,
+                                       LM_LAP_DISCONNECT_INDICATION);
+
+               /* Force an expiry of the discovery log.
+                * Now that the LAP is free, the system may attempt to
+                * connect to another device. Unfortunately, our entries
+                * are stale. There is a small window (<3s) before the
+                * normal discovery will run and where irlmp_connect_request()
+                * can get the wrong info, so make sure things get
+                * cleaned *NOW* ;-) - Jean II */
+               irlmp_do_expiry();
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s\n",
+                        __func__, irlmp_event[event]);
+               break;
+       }
+}
+
+/*********************************************************************
+ *
+ *    LSAP connection control states
+ *
+ ********************************************************************/
+
+/*
+ * Function irlmp_state_disconnected (event, skb, info)
+ *
+ *    DISCONNECTED
+ *
+ */
+static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
+                                   struct sk_buff *skb)
+{
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+
+       switch (event) {
+#ifdef CONFIG_IRDA_ULTRA
+       case LM_UDATA_INDICATION:
+               /* This is most bizarre. Those packets are  aka unreliable
+                * connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA.
+                * Why do we pass them as Ultra ??? Jean II */
+               irlmp_connless_data_indication(self, skb);
+               break;
+#endif /* CONFIG_IRDA_ULTRA */
+       case LM_CONNECT_REQUEST:
+               pr_debug("%s(), LM_CONNECT_REQUEST\n", __func__);
+
+               if (self->conn_skb) {
+                       net_warn_ratelimited("%s: busy with another request!\n",
+                                            __func__);
+                       return -EBUSY;
+               }
+               /* Don't forget to refcount it (see irlmp_connect_request()) */
+               skb_get(skb);
+               self->conn_skb = skb;
+
+               irlmp_next_lsap_state(self, LSAP_SETUP_PEND);
+
+               /* Start watchdog timer (5 secs for now) */
+               irlmp_start_watchdog_timer(self, 5*HZ);
+
+               irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
+               break;
+       case LM_CONNECT_INDICATION:
+               if (self->conn_skb) {
+                       net_warn_ratelimited("%s: busy with another request!\n",
+                                            __func__);
+                       return -EBUSY;
+               }
+               /* Don't forget to refcount it (see irlap_driver_rcv()) */
+               skb_get(skb);
+               self->conn_skb = skb;
+
+               irlmp_next_lsap_state(self, LSAP_CONNECT_PEND);
+
+               /* Start watchdog timer
+                * This is not mentionned in the spec, but there is a rare
+                * race condition that can get the socket stuck.
+                * If we receive this event while our LAP is closing down,
+                * the LM_LAP_CONNECT_REQUEST get lost and we get stuck in
+                * CONNECT_PEND state forever.
+                * The other cause of getting stuck down there is if the
+                * higher layer never reply to the CONNECT_INDICATION.
+                * Anyway, it make sense to make sure that we always have
+                * a backup plan. 1 second is plenty (should be immediate).
+                * Jean II */
+               irlmp_start_watchdog_timer(self, 1*HZ);
+
+               irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+                        __func__, irlmp_event[event], self->slsap_sel);
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlmp_state_connect (self, event, skb)
+ *
+ *    CONNECT
+ *
+ */
+static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
+                               struct sk_buff *skb)
+{
+       struct lsap_cb *lsap;
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+
+       switch (event) {
+       case LM_CONNECT_RESPONSE:
+               /*
+                *  Bind this LSAP to the IrLAP link where the connect was
+                *  received
+                */
+               lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self,
+                                     NULL);
+
+               IRDA_ASSERT(lsap == self, return -1;);
+               IRDA_ASSERT(self->lap != NULL, return -1;);
+               IRDA_ASSERT(self->lap->lsaps != NULL, return -1;);
+
+               hashbin_insert(self->lap->lsaps, (irda_queue_t *) self,
+                              (long) self, NULL);
+
+               set_bit(0, &self->connected);   /* TRUE */
+
+               irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
+                                  self->slsap_sel, CONNECT_CNF, skb);
+
+               del_timer(&self->watchdog_timer);
+
+               irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
+               break;
+       case LM_WATCHDOG_TIMEOUT:
+               /* May happen, who knows...
+                * Jean II */
+               pr_debug("%s() WATCHDOG_TIMEOUT!\n",  __func__);
+
+               /* Disconnect, get out... - Jean II */
+               self->lap = NULL;
+               self->dlsap_sel = LSAP_ANY;
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+               break;
+       default:
+               /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
+                * are *not* yet bound to the IrLAP link. Jean II */
+               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+                        __func__, irlmp_event[event], self->slsap_sel);
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlmp_state_connect_pend (event, skb, info)
+ *
+ *    CONNECT_PEND
+ *
+ */
+static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
+                                   struct sk_buff *skb)
+{
+       struct sk_buff *tx_skb;
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+
+       switch (event) {
+       case LM_CONNECT_REQUEST:
+               /* Keep state */
+               break;
+       case LM_CONNECT_RESPONSE:
+               pr_debug("%s(), LM_CONNECT_RESPONSE, no indication issued yet\n",
+                        __func__);
+               /* Keep state */
+               break;
+       case LM_DISCONNECT_REQUEST:
+               pr_debug("%s(), LM_DISCONNECT_REQUEST, not yet bound to IrLAP connection\n",
+                        __func__);
+               /* Keep state */
+               break;
+       case LM_LAP_CONNECT_CONFIRM:
+               pr_debug("%s(), LS_CONNECT_CONFIRM\n",  __func__);
+               irlmp_next_lsap_state(self, LSAP_CONNECT);
+
+               tx_skb = self->conn_skb;
+               self->conn_skb = NULL;
+
+               irlmp_connect_indication(self, tx_skb);
+               /* Drop reference count - see irlmp_connect_indication(). */
+               dev_kfree_skb(tx_skb);
+               break;
+       case LM_WATCHDOG_TIMEOUT:
+               /* Will happen in some rare cases because of a race condition.
+                * Just make sure we don't stay there forever...
+                * Jean II */
+               pr_debug("%s() WATCHDOG_TIMEOUT!\n",  __func__);
+
+               /* Go back to disconnected mode, keep the socket waiting */
+               self->lap = NULL;
+               self->dlsap_sel = LSAP_ANY;
+               if(self->conn_skb)
+                       dev_kfree_skb(self->conn_skb);
+               self->conn_skb = NULL;
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+               break;
+       default:
+               /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
+                * are *not* yet bound to the IrLAP link. Jean II */
+               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+                        __func__, irlmp_event[event], self->slsap_sel);
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlmp_state_dtr (self, event, skb)
+ *
+ *    DATA_TRANSFER_READY
+ *
+ */
+static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
+                          struct sk_buff *skb)
+{
+       LM_REASON reason;
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+       IRDA_ASSERT(self->lap != NULL, return -1;);
+
+       switch (event) {
+       case LM_DATA_REQUEST: /* Optimize for the common case */
+               irlmp_send_data_pdu(self->lap, self->dlsap_sel,
+                                   self->slsap_sel, FALSE, skb);
+               break;
+       case LM_DATA_INDICATION: /* Optimize for the common case */
+               irlmp_data_indication(self, skb);
+               break;
+       case LM_UDATA_REQUEST:
+               IRDA_ASSERT(skb != NULL, return -1;);
+               irlmp_send_data_pdu(self->lap, self->dlsap_sel,
+                                   self->slsap_sel, TRUE, skb);
+               break;
+       case LM_UDATA_INDICATION:
+               irlmp_udata_indication(self, skb);
+               break;
+       case LM_CONNECT_REQUEST:
+               pr_debug("%s(), LM_CONNECT_REQUEST, error, LSAP already connected\n",
+                        __func__);
+               /* Keep state */
+               break;
+       case LM_CONNECT_RESPONSE:
+               pr_debug("%s(), LM_CONNECT_RESPONSE, error, LSAP already connected\n",
+                        __func__);
+               /* Keep state */
+               break;
+       case LM_DISCONNECT_REQUEST:
+               irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, self->slsap_sel,
+                                  DISCONNECT, skb);
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+               /* Called only from irlmp_disconnect_request(), will
+                * unbind from LAP over there. Jean II */
+
+               /* Try to close the LAP connection if its still there */
+               if (self->lap) {
+                       pr_debug("%s(), trying to close IrLAP\n",
+                                __func__);
+                       irlmp_do_lap_event(self->lap,
+                                          LM_LAP_DISCONNECT_REQUEST,
+                                          NULL);
+               }
+               break;
+       case LM_LAP_DISCONNECT_INDICATION:
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+
+               reason = irlmp_convert_lap_reason(self->lap->reason);
+
+               irlmp_disconnect_indication(self, reason, NULL);
+               break;
+       case LM_DISCONNECT_INDICATION:
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+
+               IRDA_ASSERT(self->lap != NULL, return -1;);
+               IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
+
+               IRDA_ASSERT(skb != NULL, return -1;);
+               IRDA_ASSERT(skb->len > 3, return -1;);
+               reason = skb->data[3];
+
+                /* Try to close the LAP connection */
+               pr_debug("%s(), trying to close IrLAP\n", __func__);
+               irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
+
+               irlmp_disconnect_indication(self, reason, skb);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+                        __func__, irlmp_event[event], self->slsap_sel);
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlmp_state_setup (event, skb, info)
+ *
+ *    SETUP, Station Control has set up the underlying IrLAP connection.
+ *    An LSAP connection request has been transmitted to the peer
+ *    LSAP-Connection Control FSM and we are awaiting reply.
+ */
+static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
+                            struct sk_buff *skb)
+{
+       LM_REASON reason;
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+
+       switch (event) {
+       case LM_CONNECT_CONFIRM:
+               irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
+
+               del_timer(&self->watchdog_timer);
+
+               irlmp_connect_confirm(self, skb);
+               break;
+       case LM_DISCONNECT_INDICATION:
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+
+               IRDA_ASSERT(self->lap != NULL, return -1;);
+               IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
+
+               IRDA_ASSERT(skb != NULL, return -1;);
+               IRDA_ASSERT(skb->len > 3, return -1;);
+               reason = skb->data[3];
+
+                /* Try to close the LAP connection */
+               pr_debug("%s(), trying to close IrLAP\n",  __func__);
+               irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
+
+               irlmp_disconnect_indication(self, reason, skb);
+               break;
+       case LM_LAP_DISCONNECT_INDICATION:
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+
+               del_timer(&self->watchdog_timer);
+
+               IRDA_ASSERT(self->lap != NULL, return -1;);
+               IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
+
+               reason = irlmp_convert_lap_reason(self->lap->reason);
+
+               irlmp_disconnect_indication(self, reason, skb);
+               break;
+       case LM_WATCHDOG_TIMEOUT:
+               pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__);
+
+               IRDA_ASSERT(self->lap != NULL, return -1;);
+               irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+
+               irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+                        __func__, irlmp_event[event], self->slsap_sel);
+               break;
+       }
+       return ret;
+}
+
+/*
+ * Function irlmp_state_setup_pend (event, skb, info)
+ *
+ *    SETUP_PEND, An LM_CONNECT_REQUEST has been received from the service
+ *    user to set up an LSAP connection. A request has been sent to the
+ *    LAP FSM to set up the underlying IrLAP connection, and we
+ *    are awaiting confirm.
+ */
+static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
+                                 struct sk_buff *skb)
+{
+       struct sk_buff *tx_skb;
+       LM_REASON reason;
+       int ret = 0;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(irlmp != NULL, return -1;);
+
+       switch (event) {
+       case LM_LAP_CONNECT_CONFIRM:
+               IRDA_ASSERT(self->conn_skb != NULL, return -1;);
+
+               tx_skb = self->conn_skb;
+               self->conn_skb = NULL;
+
+               irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
+                                  self->slsap_sel, CONNECT_CMD, tx_skb);
+               /* Drop reference count - see irlap_data_request(). */
+               dev_kfree_skb(tx_skb);
+
+               irlmp_next_lsap_state(self, LSAP_SETUP);
+               break;
+       case LM_WATCHDOG_TIMEOUT:
+               pr_debug("%s() : WATCHDOG_TIMEOUT !\n",  __func__);
+
+               IRDA_ASSERT(self->lap != NULL, return -1;);
+               irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+
+               irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
+               break;
+       case LM_LAP_DISCONNECT_INDICATION: /* LS_Disconnect.indication */
+               del_timer( &self->watchdog_timer);
+
+               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+
+               reason = irlmp_convert_lap_reason(self->lap->reason);
+
+               irlmp_disconnect_indication(self, reason, NULL);
+               break;
+       default:
+               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+                        __func__, irlmp_event[event], self->slsap_sel);
+               break;
+       }
+       return ret;
+}
diff --git a/drivers/staging/irda/net/irlmp_frame.c b/drivers/staging/irda/net/irlmp_frame.c
new file mode 100644 (file)
index 0000000..38b0f99
--- /dev/null
@@ -0,0 +1,476 @@
+/*********************************************************************
+ *
+ * Filename:      irlmp_frame.c
+ * Version:       0.9
+ * Description:   IrLMP frame implementation
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Tue Aug 19 02:09:59 1997
+ * Modified at:   Mon Dec 13 13:41:12 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/skbuff.h>
+#include <linux/kernel.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlap.h>
+#include <net/irda/timer.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irlmp_frame.h>
+#include <net/irda/discovery.h>
+
+static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap,
+                                      __u8 slsap, int status, hashbin_t *);
+
+inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
+                               int expedited, struct sk_buff *skb)
+{
+       skb->data[0] = dlsap;
+       skb->data[1] = slsap;
+
+       if (expedited) {
+               pr_debug("%s(), sending expedited data\n", __func__);
+               irlap_data_request(self->irlap, skb, TRUE);
+       } else
+               irlap_data_request(self->irlap, skb, FALSE);
+}
+
+/*
+ * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb)
+ *
+ *    Send Link Control Frame to IrLAP
+ */
+void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
+                       __u8 opcode, struct sk_buff *skb)
+{
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       frame = skb->data;
+
+       frame[0] = dlsap | CONTROL_BIT;
+       frame[1] = slsap;
+
+       frame[2] = opcode;
+
+       if (opcode == DISCONNECT)
+               frame[3] = 0x01; /* Service user request */
+       else
+               frame[3] = 0x00; /* rsvd */
+
+       irlap_data_request(self->irlap, skb, FALSE);
+}
+
+/*
+ * Function irlmp_input (skb)
+ *
+ *    Used by IrLAP to pass received data frames to IrLMP layer
+ *
+ */
+void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
+                               int unreliable)
+{
+       struct lsap_cb *lsap;
+       __u8   slsap_sel;   /* Source (this) LSAP address */
+       __u8   dlsap_sel;   /* Destination LSAP address */
+       __u8   *fp;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+       IRDA_ASSERT(skb->len > 2, return;);
+
+       fp = skb->data;
+
+       /*
+        *  The next statements may be confusing, but we do this so that
+        *  destination LSAP of received frame is source LSAP in our view
+        */
+       slsap_sel = fp[0] & LSAP_MASK;
+       dlsap_sel = fp[1];
+
+       /*
+        *  Check if this is an incoming connection, since we must deal with
+        *  it in a different way than other established connections.
+        */
+       if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) {
+               pr_debug("%s(), incoming connection, source LSAP=%d, dest LSAP=%d\n",
+                        __func__, slsap_sel, dlsap_sel);
+
+               /* Try to find LSAP among the unconnected LSAPs */
+               lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD,
+                                      irlmp->unconnected_lsaps);
+
+               /* Maybe LSAP was already connected, so try one more time */
+               if (!lsap) {
+                       pr_debug("%s(), incoming connection for LSAP already connected\n",
+                                __func__);
+                       lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
+                                              self->lsaps);
+               }
+       } else
+               lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
+                                      self->lsaps);
+
+       if (lsap == NULL) {
+               pr_debug("IrLMP, Sorry, no LSAP for received frame!\n");
+               pr_debug("%s(), slsap_sel = %02x, dlsap_sel = %02x\n",
+                        __func__, slsap_sel, dlsap_sel);
+               if (fp[0] & CONTROL_BIT) {
+                       pr_debug("%s(), received control frame %02x\n",
+                                __func__, fp[2]);
+               } else {
+                       pr_debug("%s(), received data frame\n", __func__);
+               }
+               return;
+       }
+
+       /*
+        *  Check if we received a control frame?
+        */
+       if (fp[0] & CONTROL_BIT) {
+               switch (fp[2]) {
+               case CONNECT_CMD:
+                       lsap->lap = self;
+                       irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb);
+                       break;
+               case CONNECT_CNF:
+                       irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb);
+                       break;
+               case DISCONNECT:
+                       pr_debug("%s(), Disconnect indication!\n",
+                                __func__);
+                       irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION,
+                                           skb);
+                       break;
+               case ACCESSMODE_CMD:
+                       pr_debug("Access mode cmd not implemented!\n");
+                       break;
+               case ACCESSMODE_CNF:
+                       pr_debug("Access mode cnf not implemented!\n");
+                       break;
+               default:
+                       pr_debug("%s(), Unknown control frame %02x\n",
+                                __func__, fp[2]);
+                       break;
+               }
+       } else if (unreliable) {
+               /* Optimize and bypass the state machine if possible */
+               if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
+                       irlmp_udata_indication(lsap, skb);
+               else
+                       irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb);
+       } else {
+               /* Optimize and bypass the state machine if possible */
+               if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
+                       irlmp_data_indication(lsap, skb);
+               else
+                       irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb);
+       }
+}
+
+/*
+ * Function irlmp_link_unitdata_indication (self, skb)
+ *
+ *
+ *
+ */
+#ifdef CONFIG_IRDA_ULTRA
+void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
+{
+       struct lsap_cb *lsap;
+       __u8   slsap_sel;   /* Source (this) LSAP address */
+       __u8   dlsap_sel;   /* Destination LSAP address */
+       __u8   pid;         /* Protocol identifier */
+       __u8   *fp;
+       unsigned long flags;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+       IRDA_ASSERT(skb->len > 2, return;);
+
+       fp = skb->data;
+
+       /*
+        *  The next statements may be confusing, but we do this so that
+        *  destination LSAP of received frame is source LSAP in our view
+        */
+       slsap_sel = fp[0] & LSAP_MASK;
+       dlsap_sel = fp[1];
+       pid       = fp[2];
+
+       if (pid & 0x80) {
+               pr_debug("%s(), extension in PID not supp!\n",
+                        __func__);
+               return;
+       }
+
+       /* Check if frame is addressed to the connectionless LSAP */
+       if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
+               pr_debug("%s(), dropping frame!\n", __func__);
+               return;
+       }
+
+       /* Search the connectionless LSAP */
+       spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
+       lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
+       while (lsap != NULL) {
+               /*
+                *  Check if source LSAP and dest LSAP selectors and PID match.
+                */
+               if ((lsap->slsap_sel == slsap_sel) &&
+                   (lsap->dlsap_sel == dlsap_sel) &&
+                   (lsap->pid == pid))
+               {
+                       break;
+               }
+               lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
+       }
+       spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
+
+       if (lsap)
+               irlmp_connless_data_indication(lsap, skb);
+       else {
+               pr_debug("%s(), found no matching LSAP!\n", __func__);
+       }
+}
+#endif /* CONFIG_IRDA_ULTRA */
+
+/*
+ * Function irlmp_link_disconnect_indication (reason, userdata)
+ *
+ *    IrLAP has disconnected
+ *
+ */
+void irlmp_link_disconnect_indication(struct lap_cb *lap,
+                                     struct irlap_cb *irlap,
+                                     LAP_REASON reason,
+                                     struct sk_buff *skb)
+{
+       IRDA_ASSERT(lap != NULL, return;);
+       IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
+
+       lap->reason = reason;
+       lap->daddr = DEV_ADDR_ANY;
+
+       /* FIXME: must do something with the skb if any */
+
+       /*
+        *  Inform station state machine
+        */
+       irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL);
+}
+
+/*
+ * Function irlmp_link_connect_indication (qos)
+ *
+ *    Incoming LAP connection!
+ *
+ */
+void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr,
+                                  __u32 daddr, struct qos_info *qos,
+                                  struct sk_buff *skb)
+{
+       /* Copy QoS settings for this session */
+       self->qos = qos;
+
+       /* Update destination device address */
+       self->daddr = daddr;
+       IRDA_ASSERT(self->saddr == saddr, return;);
+
+       irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb);
+}
+
+/*
+ * Function irlmp_link_connect_confirm (qos)
+ *
+ *    LAP connection confirmed!
+ *
+ */
+void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
+                               struct sk_buff *skb)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+       IRDA_ASSERT(qos != NULL, return;);
+
+       /* Don't need use the skb for now */
+
+       /* Copy QoS settings for this session */
+       self->qos = qos;
+
+       irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL);
+}
+
+/*
+ * Function irlmp_link_discovery_indication (self, log)
+ *
+ *    Device is discovering us
+ *
+ * It's not an answer to our own discoveries, just another device trying
+ * to perform discovery, but we don't want to miss the opportunity
+ * to exploit this information, because :
+ *     o We may not actively perform discovery (just passive discovery)
+ *     o This type of discovery is much more reliable. In some cases, it
+ *       seem that less than 50% of our discoveries get an answer, while
+ *       we always get ~100% of these.
+ *     o Make faster discovery, statistically divide time of discovery
+ *       events by 2 (important for the latency aspect and user feel)
+ *     o Even is we do active discovery, the other node might not
+ *       answer our discoveries (ex: Palm). The Palm will just perform
+ *       one active discovery and connect directly to us.
+ *
+ * However, when both devices discover each other, they might attempt to
+ * connect to each other following the discovery event, and it would create
+ * collisions on the medium (SNRM battle).
+ * The "fix" for that is to disable all connection requests in IrLAP
+ * for 100ms after a discovery indication by setting the media_busy flag.
+ * Previously, we used to postpone the event which was quite ugly. Now
+ * that IrLAP takes care of this problem, just pass the event up...
+ *
+ * Jean II
+ */
+void irlmp_link_discovery_indication(struct lap_cb *self,
+                                    discovery_t *discovery)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+
+       /* Add to main log, cleanup */
+       irlmp_add_discovery(irlmp->cachelog, discovery);
+
+       /* Just handle it the same way as a discovery confirm,
+        * bypass the LM_LAP state machine (see below) */
+       irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE);
+}
+
+/*
+ * Function irlmp_link_discovery_confirm (self, log)
+ *
+ *    Called by IrLAP with a list of discoveries after the discovery
+ *    request has been carried out. A NULL log is received if IrLAP
+ *    was unable to carry out the discovery request
+ *
+ */
+void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+
+       /* Add to main log, cleanup */
+       irlmp_add_discovery_log(irlmp->cachelog, log);
+
+       /* Propagate event to various LSAPs registered for it.
+        * We bypass the LM_LAP state machine because
+        *      1) We do it regardless of the LM_LAP state
+        *      2) It doesn't affect the LM_LAP state
+        *      3) Faster, slimer, simpler, ...
+        * Jean II */
+       irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE);
+}
+
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+static inline void irlmp_update_cache(struct lap_cb *lap,
+                                     struct lsap_cb *lsap)
+{
+       /* Prevent concurrent read to get garbage */
+       lap->cache.valid = FALSE;
+       /* Update cache entry */
+       lap->cache.dlsap_sel = lsap->dlsap_sel;
+       lap->cache.slsap_sel = lsap->slsap_sel;
+       lap->cache.lsap = lsap;
+       lap->cache.valid = TRUE;
+}
+#endif
+
+/*
+ * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
+ *
+ *    Find handle associated with destination and source LSAP
+ *
+ * Any IrDA connection (LSAP/TSAP) is uniquely identified by
+ * 3 parameters, the local lsap, the remote lsap and the remote address.
+ * We may initiate multiple connections to the same remote service
+ * (they will have different local lsap), a remote device may initiate
+ * multiple connections to the same local service (they will have
+ * different remote lsap), or multiple devices may connect to the same
+ * service and may use the same remote lsap (and they will have
+ * different remote address).
+ * So, where is the remote address ? Each LAP connection is made with
+ * a single remote device, so imply a specific remote address.
+ * Jean II
+ */
+static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
+                                      __u8 slsap_sel, int status,
+                                      hashbin_t *queue)
+{
+       struct lsap_cb *lsap;
+       unsigned long flags;
+
+       /*
+        *  Optimize for the common case. We assume that the last frame
+        *  received is in the same connection as the last one, so check in
+        *  cache first to avoid the linear search
+        */
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+       if ((self->cache.valid) &&
+           (self->cache.slsap_sel == slsap_sel) &&
+           (self->cache.dlsap_sel == dlsap_sel))
+       {
+               return self->cache.lsap;
+       }
+#endif
+
+       spin_lock_irqsave(&queue->hb_spinlock, flags);
+
+       lsap = (struct lsap_cb *) hashbin_get_first(queue);
+       while (lsap != NULL) {
+               /*
+                *  If this is an incoming connection, then the destination
+                *  LSAP selector may have been specified as LM_ANY so that
+                *  any client can connect. In that case we only need to check
+                *  if the source LSAP (in our view!) match!
+                */
+               if ((status == CONNECT_CMD) &&
+                   (lsap->slsap_sel == slsap_sel) &&
+                   (lsap->dlsap_sel == LSAP_ANY)) {
+                       /* This is where the dest lsap sel is set on incoming
+                        * lsaps */
+                       lsap->dlsap_sel = dlsap_sel;
+                       break;
+               }
+               /*
+                *  Check if source LSAP and dest LSAP selectors match.
+                */
+               if ((lsap->slsap_sel == slsap_sel) &&
+                   (lsap->dlsap_sel == dlsap_sel))
+                       break;
+
+               lsap = (struct lsap_cb *) hashbin_get_next(queue);
+       }
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+       if(lsap)
+               irlmp_update_cache(self, lsap);
+#endif
+       spin_unlock_irqrestore(&queue->hb_spinlock, flags);
+
+       /* Return what we've found or NULL */
+       return lsap;
+}
diff --git a/drivers/staging/irda/net/irmod.c b/drivers/staging/irda/net/irmod.c
new file mode 100644 (file)
index 0000000..c5e35b8
--- /dev/null
@@ -0,0 +1,199 @@
+/*********************************************************************
+ *
+ * Filename:      irmod.c
+ * Version:       0.9
+ * Description:   IrDA stack main entry points
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Mon Dec 15 13:55:39 1997
+ * Modified at:   Wed Jan  5 15:12:41 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1997, 1999-2000 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2004 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+/*
+ * This file contains the main entry points of the IrDA stack.
+ * They are in this file and not af_irda.c because some developpers
+ * are using the IrDA stack without the socket API (compiling out
+ * af_irda.c).
+ * Jean II
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>            /* notify_t */
+#include <net/irda/irlap.h>            /* irlap_init */
+#include <net/irda/irlmp.h>            /* irlmp_init */
+#include <net/irda/iriap.h>            /* iriap_init */
+#include <net/irda/irttp.h>            /* irttp_init */
+#include <net/irda/irda_device.h>      /* irda_device_init */
+
+/* Packet type handler.
+ * Tell the kernel how IrDA packets should be handled.
+ */
+static struct packet_type irda_packet_type __read_mostly = {
+       .type   = cpu_to_be16(ETH_P_IRDA),
+       .func   = irlap_driver_rcv,     /* Packet type handler irlap_frame.c */
+};
+
+/*
+ * Function irda_notify_init (notify)
+ *
+ *    Used for initializing the notify structure
+ *
+ */
+void irda_notify_init(notify_t *notify)
+{
+       notify->data_indication = NULL;
+       notify->udata_indication = NULL;
+       notify->connect_confirm = NULL;
+       notify->connect_indication = NULL;
+       notify->disconnect_indication = NULL;
+       notify->flow_indication = NULL;
+       notify->status_indication = NULL;
+       notify->instance = NULL;
+       strlcpy(notify->name, "Unknown", sizeof(notify->name));
+}
+EXPORT_SYMBOL(irda_notify_init);
+
+/*
+ * Function irda_init (void)
+ *
+ *  Protocol stack initialisation entry point.
+ *  Initialise the various components of the IrDA stack
+ */
+static int __init irda_init(void)
+{
+       int ret = 0;
+
+       /* Lower layer of the stack */
+       irlmp_init();
+       irlap_init();
+
+       /* Driver/dongle support */
+       irda_device_init();
+
+       /* Higher layers of the stack */
+       iriap_init();
+       irttp_init();
+       ret = irsock_init();
+       if (ret < 0)
+               goto out_err_1;
+
+       /* Add IrDA packet type (Start receiving packets) */
+       dev_add_pack(&irda_packet_type);
+
+       /* External APIs */
+#ifdef CONFIG_PROC_FS
+       irda_proc_register();
+#endif
+#ifdef CONFIG_SYSCTL
+       ret = irda_sysctl_register();
+       if (ret < 0)
+               goto out_err_2;
+#endif
+
+       ret = irda_nl_register();
+       if (ret < 0)
+               goto out_err_3;
+
+       return 0;
+
+ out_err_3:
+#ifdef CONFIG_SYSCTL
+       irda_sysctl_unregister();
+ out_err_2:
+#endif
+#ifdef CONFIG_PROC_FS
+       irda_proc_unregister();
+#endif
+
+       /* Remove IrDA packet type (stop receiving packets) */
+       dev_remove_pack(&irda_packet_type);
+
+       /* Remove higher layers */
+       irsock_cleanup();
+ out_err_1:
+       irttp_cleanup();
+       iriap_cleanup();
+
+       /* Remove lower layers */
+       irda_device_cleanup();
+       irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */
+
+       /* Remove middle layer */
+       irlmp_cleanup();
+
+
+       return ret;
+}
+
+/*
+ * Function irda_cleanup (void)
+ *
+ *  Protocol stack cleanup/removal entry point.
+ *  Cleanup the various components of the IrDA stack
+ */
+static void __exit irda_cleanup(void)
+{
+       /* Remove External APIs */
+       irda_nl_unregister();
+
+#ifdef CONFIG_SYSCTL
+       irda_sysctl_unregister();
+#endif
+#ifdef CONFIG_PROC_FS
+       irda_proc_unregister();
+#endif
+
+       /* Remove IrDA packet type (stop receiving packets) */
+       dev_remove_pack(&irda_packet_type);
+
+       /* Remove higher layers */
+       irsock_cleanup();
+       irttp_cleanup();
+       iriap_cleanup();
+
+       /* Remove lower layers */
+       irda_device_cleanup();
+       irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */
+
+       /* Remove middle layer */
+       irlmp_cleanup();
+}
+
+/*
+ * The IrDA stack must be initialised *before* drivers get initialised,
+ * and *before* higher protocols (IrLAN/IrCOMM/IrNET) get initialised,
+ * otherwise bad things will happen (hashbins will be NULL for example).
+ * Those modules are at module_init()/device_initcall() level.
+ *
+ * On the other hand, it needs to be initialised *after* the basic
+ * networking, the /proc/net filesystem and sysctl module. Those are
+ * currently initialised in .../init/main.c (before initcalls).
+ * Also, IrDA drivers needs to be initialised *after* the random number
+ * generator (main stack and higher layer init don't need it anymore).
+ *
+ * Jean II
+ */
+subsys_initcall(irda_init);
+module_exit(irda_cleanup);
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> & Jean Tourrilhes <jt@hpl.hp.com>");
+MODULE_DESCRIPTION("The Linux IrDA Protocol Stack");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_IRDA);
diff --git a/drivers/staging/irda/net/irnet/Kconfig b/drivers/staging/irda/net/irnet/Kconfig
new file mode 100644 (file)
index 0000000..28c557f
--- /dev/null
@@ -0,0 +1,13 @@
+config IRNET
+       tristate "IrNET protocol"
+       depends on IRDA && PPP
+       help
+         Say Y here if you want to build support for the IrNET protocol.
+         To compile it as a module, choose M here: the module will be
+         called irnet.  IrNET is a PPP driver, so you will also need a
+         working PPP subsystem (driver, daemon and config)...
+
+         IrNET is an alternate way to transfer TCP/IP traffic over IrDA.  It
+         uses synchronous PPP over a set of point to point IrDA sockets.  You
+         can use it between Linux machine or with W2k.
+
diff --git a/drivers/staging/irda/net/irnet/Makefile b/drivers/staging/irda/net/irnet/Makefile
new file mode 100644 (file)
index 0000000..61c365c
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux IrDA IrNET protocol layer.
+#
+
+obj-$(CONFIG_IRNET) += irnet.o
+
+irnet-y := irnet_ppp.o irnet_irda.o
diff --git a/drivers/staging/irda/net/irnet/irnet.h b/drivers/staging/irda/net/irnet/irnet.h
new file mode 100644 (file)
index 0000000..9d451f8
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+ *     IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ *             Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file contains definitions and declarations global to the IrNET module,
+ * all grouped in one place...
+ * This file is a *private* header, so other modules don't want to know
+ * what's in there...
+ *
+ * Note : as most part of the Linux kernel, this module is available
+ * under the GNU General Public License (GPL).
+ */
+
+#ifndef IRNET_H
+#define IRNET_H
+
+/************************** DOCUMENTATION ***************************/
+/*
+ * What is IrNET
+ * -------------
+ * IrNET is a protocol allowing to carry TCP/IP traffic between two
+ * IrDA peers in an efficient fashion. It is a thin layer, passing PPP
+ * packets to IrTTP and vice versa. It uses PPP in synchronous mode,
+ * because IrTTP offer a reliable sequenced packet service (as opposed
+ * to a byte stream). In fact, you could see IrNET as carrying TCP/IP
+ * in a IrDA socket, using PPP to provide the glue.
+ *
+ * The main difference with traditional PPP over IrCOMM is that we
+ * avoid the framing and serial emulation which are a performance
+ * bottleneck. It also allows multipoint communications in a sensible
+ * fashion.
+ *
+ * The main difference with IrLAN is that we use PPP for the link
+ * management, which is more standard, interoperable and flexible than
+ * the IrLAN protocol. For example, PPP adds authentication,
+ * encryption, compression, header compression and automated routing
+ * setup. And, as IrNET let PPP do the hard work, the implementation
+ * is much simpler than IrLAN.
+ *
+ * The Linux implementation
+ * ------------------------
+ * IrNET is written on top of the Linux-IrDA stack, and interface with
+ * the generic Linux PPP driver. Because IrNET depend on recent
+ * changes of the PPP driver interface, IrNET will work only with very
+ * recent kernel (2.3.99-pre6 and up).
+ *
+ * The present implementation offer the following features :
+ *     o simple user interface using pppd
+ *     o efficient implementation (interface directly to PPP and IrTTP)
+ *     o addressing (you can specify the name of the IrNET recipient)
+ *     o multipoint operation (limited by IrLAP specification)
+ *     o information in /proc/net/irda/irnet
+ *     o IrNET events on /dev/irnet (for user space daemon)
+ *     o IrNET daemon (irnetd) to automatically handle incoming requests
+ *     o Windows 2000 compatibility (tested, but need more work)
+ * Currently missing :
+ *     o Lot's of testing (that's your job)
+ *     o Connection retries (may be too hard to do)
+ *     o Check pppd persist mode
+ *     o User space daemon (to automatically handle incoming requests)
+ *
+ * The setup is not currently the most easy, but this should get much
+ * better when everything will get integrated...
+ *
+ * Acknowledgements
+ * ----------------
+ * This module is based on :
+ *     o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras
+ *     o The IrLAN protocol (irlan_common/XXX) by Dag Brattli
+ *     o The IrSock interface (af_irda) by Dag Brattli
+ *     o Some other bits from the kernel and my drivers...
+ * Infinite thanks to those brave souls for providing the infrastructure
+ * upon which IrNET is built.
+ *
+ * Thanks to all my colleagues in HP for helping me. In particular,
+ * thanks to Salil Pradhan and Bill Serra for W2k testing...
+ * Thanks to Luiz Magalhaes for irnetd and much testing...
+ *
+ * Thanks to Alan Cox for answering lot's of my stupid questions, and
+ * to Paul Mackerras answering my questions on how to best integrate
+ * IrNET and pppd.
+ *
+ * Jean II
+ *
+ * Note on some implementations choices...
+ * ------------------------------------
+ *     1) Direct interface vs tty/socket
+ * I could have used a tty interface to hook to ppp and use the full
+ * socket API to connect to IrDA. The code would have been easier to
+ * maintain, and maybe the code would have been smaller...
+ * Instead, we hook directly to ppp_generic and to IrTTP, which make
+ * things more complicated...
+ *
+ * The first reason is flexibility : this allow us to create IrNET
+ * instances on demand (no /dev/ircommX crap) and to allow linkname
+ * specification on pppd command line...
+ *
+ * Second reason is speed optimisation. If you look closely at the
+ * transmit and receive paths, you will notice that they are "super lean"
+ * (that's why they look ugly), with no function calls and as little data
+ * copy and modification as I could...
+ *
+ *     2) irnetd in user space
+ * irnetd is implemented in user space, which is necessary to call pppd.
+ * This also give maximum benefits in term of flexibility and customability,
+ * and allow to offer the event channel, useful for other stuff like debug.
+ *
+ * On the other hand, this require a loose coordination between the
+ * present module and irnetd. One critical area is how incoming request
+ * are handled.
+ * When irnet receive an incoming request, it send an event to irnetd and
+ * drop the incoming IrNET socket.
+ * irnetd start a pppd instance, which create a new IrNET socket. This new
+ * socket is then connected in the originating node to the pppd instance.
+ * At this point, in the originating node, the first socket is closed.
+ *
+ * I admit, this is a bit messy and waste some resources. The alternative
+ * is caching incoming socket, and that's also quite messy and waste
+ * resources.
+ * We also make connection time slower. For example, on a 115 kb/s link it
+ * adds 60ms to the connection time (770 ms). However, this is slower than
+ * the time it takes to fire up pppd on my P133...
+ *
+ *
+ * History :
+ * -------
+ *
+ * v1 - 15.5.00 - Jean II
+ *     o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint)
+ *     o control channel on /dev/irnet (set name/address)
+ *     o event channel on /dev/irnet (for user space daemon)
+ *
+ * v2 - 5.6.00 - Jean II
+ *     o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness...
+ *     o Add DISCONNECT_TO event and rename DISCONNECT_FROM.
+ *     o Set official device number alloaction on /dev/irnet
+ *
+ * v3 - 30.8.00 - Jean II
+ *     o Update to latest Linux-IrDA changes :
+ *             - queue_t => irda_queue_t
+ *     o Update to ppp-2.4.0 :
+ *             - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD
+ *     o Add EXPIRE event (depend on new IrDA-Linux patch)
+ *     o Switch from `hashbin_remove' to `hashbin_remove_this' to fix
+ *       a multilink bug... (depend on new IrDA-Linux patch)
+ *     o fix a self->daddr to self->raddr in irda_irnet_connect to fix
+ *       another multilink bug (darn !)
+ *     o Remove LINKNAME_IOCTL cruft
+ *
+ * v3b - 31.8.00 - Jean II
+ *     o Dump discovery log at event channel startup
+ *
+ * v4 - 28.9.00 - Jean II
+ *     o Fix interaction between poll/select and dump discovery log
+ *     o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch)
+ *     o Add IRNET_NOANSWER_FROM event (mostly to help support)
+ *     o Release flow control in disconnect_indication
+ *     o Block packets while connecting (speed up connections)
+ *
+ * v5 - 11.01.01 - Jean II
+ *     o Init self->max_header_size, just in case...
+ *     o Set up ap->chan.hdrlen, to get zero copy on tx side working.
+ *     o avoid tx->ttp->flow->ppp->tx->... loop, by checking flow state
+ *             Thanks to Christian Gennerat for finding this bug !
+ *     ---
+ *     o Declare the proper MTU/MRU that we can support
+ *             (but PPP doesn't read the MTU value :-()
+ *     o Declare hashbin HB_NOLOCK instead of HB_LOCAL to avoid
+ *             disabling and enabling irq twice
+ *
+ * v6 - 31.05.01 - Jean II
+ *     o Print source address in Found, Discovery, Expiry & Request events
+ *     o Print requested source address in /proc/net/irnet
+ *     o Change control channel input. Allow multiple commands in one line.
+ *     o Add saddr command to change ap->rsaddr (and use that in IrDA)
+ *     ---
+ *     o Make the IrDA connection procedure totally asynchronous.
+ *       Heavy rewrite of the IAS query code and the whole connection
+ *       procedure. Now, irnet_connect() no longer need to be called from
+ *       a process context...
+ *     o Enable IrDA connect retries in ppp_irnet_send(). The good thing
+ *       is that IrDA connect retries are directly driven by PPP LCP
+ *       retries (we retry for each LCP packet), so that everything
+ *       is transparently controlled from pppd lcp-max-configure.
+ *     o Add ttp_connect flag to prevent rentry on the connect procedure
+ *     o Test and fixups to eliminate side effects of retries
+ *
+ * v7 - 22.08.01 - Jean II
+ *     o Cleanup : Change "saddr = 0x0" to "saddr = DEV_ADDR_ANY"
+ *     o Fix bug in BLOCK_WHEN_CONNECT introduced in v6 : due to the
+ *       asynchronous IAS query, self->tsap is NULL when PPP send the
+ *       first packet.  This was preventing "connect-delay 0" to work.
+ *       Change the test in ppp_irnet_send() to self->ttp_connect.
+ *
+ * v8 - 1.11.01 - Jean II
+ *     o Tighten the use of self->ttp_connect and self->ttp_open to
+ *       prevent various race conditions.
+ *     o Avoid leaking discovery log and skb
+ *     o Replace "self" with "server" in irnet_connect_indication() to
+ *       better detect cut'n'paste error ;-)
+ *
+ * v9 - 29.11.01 - Jean II
+ *     o Fix event generation in disconnect indication that I broke in v8
+ *       It was always generation "No-Answer" because I was testing ttp_open
+ *       just after clearing it. *blush*.
+ *     o Use newly created irttp_listen() to fix potential crash when LAP
+ *       destroyed before irnet module removed.
+ *
+ * v10 - 4.3.2 - Jean II
+ *     o When receiving a disconnect indication, don't reenable the
+ *       PPP Tx queue, this will trigger a reconnect. Instead, close
+ *       the channel, which will kill pppd...
+ *
+ * v11 - 20.3.02 - Jean II
+ *     o Oops ! v10 fix disabled IrNET retries and passive behaviour.
+ *       Better fix in irnet_disconnect_indication() :
+ *       - if connected, kill pppd via hangup.
+ *       - if not connected, reenable ppp Tx, which trigger IrNET retry.
+ *
+ * v12 - 10.4.02 - Jean II
+ *     o Fix race condition in irnet_connect_indication().
+ *       If the socket was already trying to connect, drop old connection
+ *       and use new one only if acting as primary. See comments.
+ *
+ * v13 - 30.5.02 - Jean II
+ *     o Update module init code
+ *
+ * v14 - 20.2.03 - Jean II
+ *     o Add discovery hint bits in the control channel.
+ *     o Remove obsolete MOD_INC/DEC_USE_COUNT in favor of .owner
+ *
+ * v15 - 7.4.03 - Jean II
+ *     o Replace spin_lock_irqsave() with spin_lock_bh() so that we can
+ *       use ppp_unit_number(). It's probably also better overall...
+ *     o Disable call to ppp_unregister_channel(), because we can't do it.
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/tty.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+#include <linux/capability.h>
+#include <linux/ctype.h>       /* isspace() */
+#include <linux/string.h>      /* skip_spaces() */
+#include <linux/uaccess.h>
+#include <linux/init.h>
+
+#include <linux/ppp_defs.h>
+#include <linux/ppp-ioctl.h>
+#include <linux/ppp_channel.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irias_object.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irttp.h>
+#include <net/irda/discovery.h>
+
+/***************************** OPTIONS *****************************/
+/*
+ * Define or undefine to compile or not some optional part of the
+ * IrNET driver...
+ * Note : the present defaults make sense, play with that at your
+ * own risk...
+ */
+/* IrDA side of the business... */
+#define DISCOVERY_NOMASK       /* To enable W2k compatibility... */
+#define ADVERTISE_HINT         /* Advertise IrLAN hint bit */
+#define ALLOW_SIMULT_CONNECT   /* This seem to work, cross fingers... */
+#define DISCOVERY_EVENTS       /* Query the discovery log to post events */
+#define INITIAL_DISCOVERY      /* Dump current discovery log as events */
+#undef STREAM_COMPAT           /* Not needed - potentially messy */
+#undef CONNECT_INDIC_KICK      /* Might mess IrDA, not needed */
+#undef FAIL_SEND_DISCONNECT    /* Might mess IrDA, not needed */
+#undef PASS_CONNECT_PACKETS    /* Not needed ? Safe */
+#undef MISSING_PPP_API         /* Stuff I wish I could do */
+
+/* PPP side of the business */
+#define BLOCK_WHEN_CONNECT     /* Block packets when connecting */
+#define CONNECT_IN_SEND                /* Retry IrDA connection procedure */
+#undef FLUSH_TO_PPP            /* Not sure about this one, let's play safe */
+#undef SECURE_DEVIRNET         /* Bah... */
+
+/****************************** DEBUG ******************************/
+
+/*
+ * This set of flags enable and disable all the various warning,
+ * error and debug message of this driver.
+ * Each section can be enabled and disabled independently
+ */
+/* In the PPP part */
+#define DEBUG_CTRL_TRACE       0       /* Control channel */
+#define DEBUG_CTRL_INFO                0       /* various info */
+#define DEBUG_CTRL_ERROR       1       /* problems */
+#define DEBUG_FS_TRACE         0       /* filesystem callbacks */
+#define DEBUG_FS_INFO          0       /* various info */
+#define DEBUG_FS_ERROR         1       /* problems */
+#define DEBUG_PPP_TRACE                0       /* PPP related functions */
+#define DEBUG_PPP_INFO         0       /* various info */
+#define DEBUG_PPP_ERROR                1       /* problems */
+#define DEBUG_MODULE_TRACE     0       /* module insertion/removal */
+#define DEBUG_MODULE_ERROR     1       /* problems */
+
+/* In the IrDA part */
+#define DEBUG_IRDA_SR_TRACE    0       /* IRDA subroutines */
+#define DEBUG_IRDA_SR_INFO     0       /* various info */
+#define DEBUG_IRDA_SR_ERROR    1       /* problems */
+#define DEBUG_IRDA_SOCK_TRACE  0       /* IRDA main socket functions */
+#define DEBUG_IRDA_SOCK_INFO   0       /* various info */
+#define DEBUG_IRDA_SOCK_ERROR  1       /* problems */
+#define DEBUG_IRDA_SERV_TRACE  0       /* The IrNET server */
+#define DEBUG_IRDA_SERV_INFO   0       /* various info */
+#define DEBUG_IRDA_SERV_ERROR  1       /* problems */
+#define DEBUG_IRDA_TCB_TRACE   0       /* IRDA IrTTP callbacks */
+#define DEBUG_IRDA_CB_INFO     0       /* various info */
+#define DEBUG_IRDA_CB_ERROR    1       /* problems */
+#define DEBUG_IRDA_OCB_TRACE   0       /* IRDA other callbacks */
+#define DEBUG_IRDA_OCB_INFO    0       /* various info */
+#define DEBUG_IRDA_OCB_ERROR   1       /* problems */
+
+#define DEBUG_ASSERT           0       /* Verify all assertions */
+
+/*
+ * These are the macros we are using to actually print the debug
+ * statements. Don't look at it, it's ugly...
+ *
+ * One of the trick is that, as the DEBUG_XXX are constant, the
+ * compiler will optimise away the if() in all cases.
+ */
+/* All error messages (will show up in the normal logs) */
+#define DERROR(dbg, format, args...) \
+       {if(DEBUG_##dbg) \
+               printk(KERN_INFO "irnet: %s(): " format, __func__ , ##args);}
+
+/* Normal debug message (will show up in /var/log/debug) */
+#define DEBUG(dbg, format, args...) \
+       {if(DEBUG_##dbg) \
+               printk(KERN_DEBUG "irnet: %s(): " format, __func__ , ##args);}
+
+/* Entering a function (trace) */
+#define DENTER(dbg, format, args...) \
+       {if(DEBUG_##dbg) \
+               printk(KERN_DEBUG "irnet: -> %s" format, __func__ , ##args);}
+
+/* Entering and exiting a function in one go (trace) */
+#define DPASS(dbg, format, args...) \
+       {if(DEBUG_##dbg) \
+               printk(KERN_DEBUG "irnet: <>%s" format, __func__ , ##args);}
+
+/* Exiting a function (trace) */
+#define DEXIT(dbg, format, args...) \
+       {if(DEBUG_##dbg) \
+               printk(KERN_DEBUG "irnet: <-%s()" format, __func__ , ##args);}
+
+/* Exit a function with debug */
+#define DRETURN(ret, dbg, args...) \
+       {DEXIT(dbg, ": " args);\
+       return ret; }
+
+/* Exit a function on failed condition */
+#define DABORT(cond, ret, dbg, args...) \
+       {if(cond) {\
+               DERROR(dbg, args);\
+               return ret; }}
+
+/* Invalid assertion, print out an error and exit... */
+#define DASSERT(cond, ret, dbg, args...) \
+       {if((DEBUG_ASSERT) && !(cond)) {\
+               DERROR(dbg, "Invalid assertion: " args);\
+               return ret; }}
+
+/************************ CONSTANTS & MACROS ************************/
+
+/* Paranoia */
+#define IRNET_MAGIC    0xB00754
+
+/* Number of control events in the control channel buffer... */
+#define IRNET_MAX_EVENTS       8       /* Should be more than enough... */
+
+/****************************** TYPES ******************************/
+
+/*
+ * This is the main structure where we store all the data pertaining to
+ * one instance of irnet.
+ * Note : in irnet functions, a pointer this structure is usually called
+ * "ap" or "self". If the code is borrowed from the IrDA stack, it tend
+ * to be called "self", and if it is borrowed from the PPP driver it is
+ * "ap". Apart from that, it's exactly the same structure ;-)
+ */
+typedef struct irnet_socket
+{
+  /* ------------------- Instance management ------------------- */
+  /* We manage a linked list of IrNET socket instances */
+  irda_queue_t         q;              /* Must be first - for hasbin */
+  int                  magic;          /* Paranoia */
+
+  /* --------------------- FileSystem part --------------------- */
+  /* "pppd" interact directly with us on a /dev/ file */
+  struct file *                file;           /* File descriptor of this instance */
+  /* TTY stuff - to keep "pppd" happy */
+  struct ktermios      termios;        /* Various tty flags */
+  /* Stuff for the control channel */
+  int                  event_index;    /* Last read in the event log */
+
+  /* ------------------------- PPP part ------------------------- */
+  /* We interface directly to the ppp_generic driver in the kernel */
+  int                  ppp_open;       /* registered with ppp_generic */
+  struct ppp_channel   chan;           /* Interface to generic ppp layer */
+
+  int                  mru;            /* Max size of PPP payload */
+  u32                  xaccm[8];       /* Asynchronous character map (just */
+  u32                  raccm;          /* to please pppd - dummy) */
+  unsigned int         flags;          /* PPP flags (compression, ...) */
+  unsigned int         rbits;          /* Unused receive flags ??? */
+  struct work_struct disconnect_work;   /* Process context disconnection */
+  /* ------------------------ IrTTP part ------------------------ */
+  /* We create a pseudo "socket" over the IrDA tranport */
+  unsigned long                ttp_open;       /* Set when IrTTP is ready */
+  unsigned long                ttp_connect;    /* Set when IrTTP is connecting */
+  struct tsap_cb *     tsap;           /* IrTTP instance (the connection) */
+
+  char                 rname[NICKNAME_MAX_LEN + 1];
+                                       /* IrDA nickname of destination */
+  __u32                        rdaddr;         /* Requested peer IrDA address */
+  __u32                        rsaddr;         /* Requested local IrDA address */
+  __u32                        daddr;          /* actual peer IrDA address */
+  __u32                        saddr;          /* my local IrDA address */
+  __u8                 dtsap_sel;      /* Remote TSAP selector */
+  __u8                 stsap_sel;      /* Local TSAP selector */
+
+  __u32                        max_sdu_size_rx;/* Socket parameters used for IrTTP */
+  __u32                        max_sdu_size_tx;
+  __u32                        max_data_size;
+  __u8                 max_header_size;
+  LOCAL_FLOW           tx_flow;        /* State of the Tx path in IrTTP */
+
+  /* ------------------- IrLMP and IrIAS part ------------------- */
+  /* Used for IrDA Discovery and socket name resolution */
+  void *               ckey;           /* IrLMP client handle */
+  __u16                        mask;           /* Hint bits mask (filter discov.)*/
+  int                  nslots;         /* Number of slots for discovery */
+
+  struct iriap_cb *    iriap;          /* Used to query remote IAS */
+  int                  errno;          /* status of the IAS query */
+
+  /* -------------------- Discovery log part -------------------- */
+  /* Used by initial discovery on the control channel
+   * and by irnet_discover_daddr_and_lsap_sel() */
+  struct irda_device_info *discoveries;        /* Copy of the discovery log */
+  int                  disco_index;    /* Last read in the discovery log */
+  int                  disco_number;   /* Size of the discovery log */
+
+  struct mutex         lock;
+
+} irnet_socket;
+
+/*
+ * This is the various event that we will generate on the control channel
+ */
+typedef enum irnet_event
+{
+  IRNET_DISCOVER,              /* New IrNET node discovered */
+  IRNET_EXPIRE,                        /* IrNET node expired */
+  IRNET_CONNECT_TO,            /* IrNET socket has connected to other node */
+  IRNET_CONNECT_FROM,          /* Other node has connected to IrNET socket */
+  IRNET_REQUEST_FROM,          /* Non satisfied connection request */
+  IRNET_NOANSWER_FROM,         /* Failed connection request */
+  IRNET_BLOCKED_LINK,          /* Link (IrLAP) is blocked for > 3s */
+  IRNET_DISCONNECT_FROM,       /* IrNET socket has disconnected */
+  IRNET_DISCONNECT_TO          /* Closing IrNET socket */
+} irnet_event;
+
+/*
+ * This is the storage for an event and its arguments
+ */
+typedef struct irnet_log
+{
+  irnet_event  event;
+  int          unit;
+  __u32                saddr;
+  __u32                daddr;
+  char         name[NICKNAME_MAX_LEN + 1];     /* 21 + 1 */
+  __u16_host_order hints;                      /* Discovery hint bits */
+} irnet_log;
+
+/*
+ * This is the storage for all events and related stuff...
+ */
+typedef struct irnet_ctrl_channel
+{
+  irnet_log    log[IRNET_MAX_EVENTS];  /* Event log */
+  int          index;          /* Current index in log */
+  spinlock_t   spinlock;       /* Serialize access to the event log */
+  wait_queue_head_t    rwait;  /* processes blocked on read (or poll) */
+} irnet_ctrl_channel;
+
+/**************************** PROTOTYPES ****************************/
+/*
+ * Global functions of the IrNET module
+ * Note : we list here also functions called from one file to the other.
+ */
+
+/* -------------------------- IRDA PART -------------------------- */
+int irda_irnet_create(irnet_socket *); /* Initialise an IrNET socket */
+int irda_irnet_connect(irnet_socket *);        /* Try to connect over IrDA */
+void irda_irnet_destroy(irnet_socket *);       /* Teardown an IrNET socket */
+int irda_irnet_init(void);             /* Initialise IrDA part of IrNET */
+void irda_irnet_cleanup(void);         /* Teardown IrDA part of IrNET */
+
+/**************************** VARIABLES ****************************/
+
+/* Control channel stuff - allocated in irnet_irda.h */
+extern struct irnet_ctrl_channel       irnet_events;
+
+#endif /* IRNET_H */
diff --git a/drivers/staging/irda/net/irnet/irnet_irda.c b/drivers/staging/irda/net/irnet/irnet_irda.c
new file mode 100644 (file)
index 0000000..e390bce
--- /dev/null
@@ -0,0 +1,1885 @@
+/*
+ *     IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ *             Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file implement the IRDA interface of IrNET.
+ * Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly,
+ * and exchange frames with IrTTP.
+ */
+
+#include "irnet_irda.h"                /* Private header */
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+/*
+ * PPP disconnect work: we need to make sure we're in
+ * process context when calling ppp_unregister_channel().
+ */
+static void irnet_ppp_disconnect(struct work_struct *work)
+{
+       irnet_socket * self =
+               container_of(work, irnet_socket, disconnect_work);
+
+       if (self == NULL)
+               return;
+       /*
+        * If we were connected, cleanup & close the PPP
+        * channel, which will kill pppd (hangup) and the rest.
+        */
+       if (self->ppp_open && !self->ttp_open && !self->ttp_connect) {
+               ppp_unregister_channel(&self->chan);
+               self->ppp_open = 0;
+       }
+}
+
+/************************* CONTROL CHANNEL *************************/
+/*
+ * When ppp is not active, /dev/irnet act as a control channel.
+ * Writing allow to set up the IrDA destination of the IrNET channel,
+ * and any application may be read events happening on IrNET...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Post an event to the control channel...
+ * Put the event in the log, and then wait all process blocked on read
+ * so they can read the log...
+ */
+static void
+irnet_post_event(irnet_socket *        ap,
+                irnet_event    event,
+                __u32          saddr,
+                __u32          daddr,
+                char *         name,
+                __u16          hints)
+{
+  int                  index;          /* In the log */
+
+  DENTER(CTRL_TRACE, "(ap=0x%p, event=%d, daddr=%08x, name=``%s'')\n",
+        ap, event, daddr, name);
+
+  /* Protect this section via spinlock.
+   * Note : as we are the only event producer, we only need to exclude
+   * ourself when touching the log, which is nice and easy.
+   */
+  spin_lock_bh(&irnet_events.spinlock);
+
+  /* Copy the event in the log */
+  index = irnet_events.index;
+  irnet_events.log[index].event = event;
+  irnet_events.log[index].daddr = daddr;
+  irnet_events.log[index].saddr = saddr;
+  /* Try to copy IrDA nickname */
+  if(name)
+    strcpy(irnet_events.log[index].name, name);
+  else
+    irnet_events.log[index].name[0] = '\0';
+  /* Copy hints */
+  irnet_events.log[index].hints.word = hints;
+  /* Try to get ppp unit number */
+  if((ap != (irnet_socket *) NULL) && (ap->ppp_open))
+    irnet_events.log[index].unit = ppp_unit_number(&ap->chan);
+  else
+    irnet_events.log[index].unit = -1;
+
+  /* Increment the index
+   * Note that we increment the index only after the event is written,
+   * to make sure that the readers don't get garbage... */
+  irnet_events.index = (index + 1) % IRNET_MAX_EVENTS;
+
+  DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index);
+
+  /* Spin lock end */
+  spin_unlock_bh(&irnet_events.spinlock);
+
+  /* Now : wake up everybody waiting for events... */
+  wake_up_interruptible_all(&irnet_events.rwait);
+
+  DEXIT(CTRL_TRACE, "\n");
+}
+
+/************************* IRDA SUBROUTINES *************************/
+/*
+ * These are a bunch of subroutines called from other functions
+ * down there, mostly common code or to improve readability...
+ *
+ * Note : we duplicate quite heavily some routines of af_irda.c,
+ * because our input structure (self) is quite different
+ * (struct irnet instead of struct irda_sock), which make sharing
+ * the same code impossible (at least, without templates).
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_open_tsap (self)
+ *
+ *    Open local Transport Service Access Point (TSAP)
+ *
+ * Create a IrTTP instance for us and set all the IrTTP callbacks.
+ */
+static inline int
+irnet_open_tsap(irnet_socket * self)
+{
+  notify_t     notify;         /* Callback structure */
+
+  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
+
+  DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n");
+
+  /* Initialize IrTTP callbacks to be used by the IrDA stack */
+  irda_notify_init(&notify);
+  notify.connect_confirm       = irnet_connect_confirm;
+  notify.connect_indication    = irnet_connect_indication;
+  notify.disconnect_indication = irnet_disconnect_indication;
+  notify.data_indication       = irnet_data_indication;
+  /*notify.udata_indication    = NULL;*/
+  notify.flow_indication       = irnet_flow_indication;
+  notify.status_indication     = irnet_status_indication;
+  notify.instance              = self;
+  strlcpy(notify.name, IRNET_NOTIFY_NAME, sizeof(notify.name));
+
+  /* Open an IrTTP instance */
+  self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
+                              &notify);
+  DABORT(self->tsap == NULL, -ENOMEM,
+        IRDA_SR_ERROR, "Unable to allocate TSAP !\n");
+
+  /* Remember which TSAP selector we actually got */
+  self->stsap_sel = self->tsap->stsap_sel;
+
+  DEXIT(IRDA_SR_TRACE, " - tsap=0x%p, sel=0x%X\n",
+       self->tsap, self->stsap_sel);
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_ias_to_tsap (self, result, value)
+ *
+ *    Examine an IAS object and extract TSAP
+ *
+ * We do an IAP query to find the TSAP associated with the IrNET service.
+ * When IrIAP pass us the result of the query, this function look at
+ * the return values to check for failures and extract the TSAP if
+ * possible.
+ * Also deallocate value
+ * The failure is in self->errno
+ * Return TSAP or -1
+ */
+static inline __u8
+irnet_ias_to_tsap(irnet_socket *       self,
+                 int                   result,
+                 struct ias_value *    value)
+{
+  __u8 dtsap_sel = 0;          /* TSAP we are looking for */
+
+  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
+
+  /* By default, no error */
+  self->errno = 0;
+
+  /* Check if request succeeded */
+  switch(result)
+    {
+      /* Standard errors : service not available */
+    case IAS_CLASS_UNKNOWN:
+    case IAS_ATTRIB_UNKNOWN:
+      DEBUG(IRDA_SR_INFO, "IAS object doesn't exist ! (%d)\n", result);
+      self->errno = -EADDRNOTAVAIL;
+      break;
+
+      /* Other errors, most likely IrDA stack failure */
+    default :
+      DEBUG(IRDA_SR_INFO, "IAS query failed ! (%d)\n", result);
+      self->errno = -EHOSTUNREACH;
+      break;
+
+      /* Success : we got what we wanted */
+    case IAS_SUCCESS:
+      break;
+    }
+
+  /* Check what was returned to us */
+  if(value != NULL)
+    {
+      /* What type of argument have we got ? */
+      switch(value->type)
+       {
+       case IAS_INTEGER:
+         DEBUG(IRDA_SR_INFO, "result=%d\n", value->t.integer);
+         if(value->t.integer != -1)
+           /* Get the remote TSAP selector */
+           dtsap_sel = value->t.integer;
+         else
+           self->errno = -EADDRNOTAVAIL;
+         break;
+       default:
+         self->errno = -EADDRNOTAVAIL;
+         DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", value->type);
+         break;
+       }
+
+      /* Cleanup */
+      irias_delete_value(value);
+    }
+  else /* value == NULL */
+    {
+      /* Nothing returned to us - usually result != SUCCESS */
+      if(!(self->errno))
+       {
+         DERROR(IRDA_SR_ERROR,
+                "IrDA bug : result == SUCCESS && value == NULL\n");
+         self->errno = -EHOSTUNREACH;
+       }
+    }
+  DEXIT(IRDA_SR_TRACE, "\n");
+
+  /* Return the TSAP */
+  return dtsap_sel;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_find_lsap_sel (self)
+ *
+ *    Try to lookup LSAP selector in remote LM-IAS
+ *
+ * Basically, we start a IAP query, and then go to sleep. When the query
+ * return, irnet_getvalue_confirm will wake us up, and we can examine the
+ * result of the query...
+ * Note that in some case, the query fail even before we go to sleep,
+ * creating some races...
+ */
+static inline int
+irnet_find_lsap_sel(irnet_socket *     self)
+{
+  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
+
+  /* This should not happen */
+  DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n");
+
+  /* Create an IAP instance, will be closed in irnet_getvalue_confirm() */
+  self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+                          irnet_getvalue_confirm);
+
+  /* Treat unexpected signals as disconnect */
+  self->errno = -EHOSTUNREACH;
+
+  /* Query remote LM-IAS */
+  iriap_getvaluebyclass_request(self->iriap, self->rsaddr, self->daddr,
+                               IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
+
+  /* The above request is non-blocking.
+   * After a while, IrDA will call us back in irnet_getvalue_confirm()
+   * We will then call irnet_ias_to_tsap() and finish the
+   * connection procedure */
+
+  DEXIT(IRDA_SR_TRACE, "\n");
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_connect_tsap (self)
+ *
+ *    Initialise the TTP socket and initiate TTP connection
+ *
+ */
+static inline int
+irnet_connect_tsap(irnet_socket *      self)
+{
+  int          err;
+
+  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
+
+  /* Open a local TSAP (an IrTTP instance) */
+  err = irnet_open_tsap(self);
+  if(err != 0)
+    {
+      clear_bit(0, &self->ttp_connect);
+      DERROR(IRDA_SR_ERROR, "connect aborted!\n");
+      return err;
+    }
+
+  /* Connect to remote device */
+  err = irttp_connect_request(self->tsap, self->dtsap_sel,
+                             self->rsaddr, self->daddr, NULL,
+                             self->max_sdu_size_rx, NULL);
+  if(err != 0)
+    {
+      clear_bit(0, &self->ttp_connect);
+      DERROR(IRDA_SR_ERROR, "connect aborted!\n");
+      return err;
+    }
+
+  /* The above call is non-blocking.
+   * After a while, the IrDA stack will either call us back in
+   * irnet_connect_confirm() or irnet_disconnect_indication()
+   * See you there ;-) */
+
+  DEXIT(IRDA_SR_TRACE, "\n");
+  return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_discover_next_daddr (self)
+ *
+ *    Query the IrNET TSAP of the next device in the log.
+ *
+ * Used in the TSAP discovery procedure.
+ */
+static inline int
+irnet_discover_next_daddr(irnet_socket *       self)
+{
+  /* Close the last instance of IrIAP, and open a new one.
+   * We can't reuse the IrIAP instance in the IrIAP callback */
+  if(self->iriap)
+    {
+      iriap_close(self->iriap);
+      self->iriap = NULL;
+    }
+  /* Create a new IAP instance */
+  self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
+                          irnet_discovervalue_confirm);
+  if(self->iriap == NULL)
+    return -ENOMEM;
+
+  /* Next discovery - before the call to avoid races */
+  self->disco_index++;
+
+  /* Check if we have one more address to try */
+  if(self->disco_index < self->disco_number)
+    {
+      /* Query remote LM-IAS */
+      iriap_getvaluebyclass_request(self->iriap,
+                                   self->discoveries[self->disco_index].saddr,
+                                   self->discoveries[self->disco_index].daddr,
+                                   IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
+      /* The above request is non-blocking.
+       * After a while, IrDA will call us back in irnet_discovervalue_confirm()
+       * We will then call irnet_ias_to_tsap() and come back here again... */
+      return 0;
+    }
+  else
+    return 1;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_discover_daddr_and_lsap_sel (self)
+ *
+ *    This try to find a device with the requested service.
+ *
+ * Initiate a TSAP discovery procedure.
+ * It basically look into the discovery log. For each address in the list,
+ * it queries the LM-IAS of the device to find if this device offer
+ * the requested service.
+ * If there is more than one node supporting the service, we complain
+ * to the user (it should move devices around).
+ * If we find one node which have the requested TSAP, we connect to it.
+ *
+ * This function just start the whole procedure. It request the discovery
+ * log and submit the first IAS query.
+ * The bulk of the job is handled in irnet_discovervalue_confirm()
+ *
+ * Note : this procedure fails if there is more than one device in range
+ * on the same dongle, because IrLMP doesn't disconnect the LAP when the
+ * last LSAP is closed. Moreover, we would need to wait the LAP
+ * disconnection...
+ */
+static inline int
+irnet_discover_daddr_and_lsap_sel(irnet_socket *       self)
+{
+  int  ret;
+
+  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
+
+  /* Ask lmp for the current discovery log */
+  self->discoveries = irlmp_get_discoveries(&self->disco_number, self->mask,
+                                           DISCOVERY_DEFAULT_SLOTS);
+
+  /* Check if the we got some results */
+  if(self->discoveries == NULL)
+    {
+      self->disco_number = -1;
+      clear_bit(0, &self->ttp_connect);
+      DRETURN(-ENETUNREACH, IRDA_SR_INFO, "No Cachelog...\n");
+    }
+  DEBUG(IRDA_SR_INFO, "Got the log (0x%p), size is %d\n",
+       self->discoveries, self->disco_number);
+
+  /* Start with the first discovery */
+  self->disco_index = -1;
+  self->daddr = DEV_ADDR_ANY;
+
+  /* This will fail if the log is empty - this is non-blocking */
+  ret = irnet_discover_next_daddr(self);
+  if(ret)
+    {
+      /* Close IAP */
+      if(self->iriap)
+       iriap_close(self->iriap);
+      self->iriap = NULL;
+
+      /* Cleanup our copy of the discovery log */
+      kfree(self->discoveries);
+      self->discoveries = NULL;
+
+      clear_bit(0, &self->ttp_connect);
+      DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
+    }
+
+  /* Follow me in irnet_discovervalue_confirm() */
+
+  DEXIT(IRDA_SR_TRACE, "\n");
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_dname_to_daddr (self)
+ *
+ *    Convert an IrDA nickname to a valid IrDA address
+ *
+ * It basically look into the discovery log until there is a match.
+ */
+static inline int
+irnet_dname_to_daddr(irnet_socket *    self)
+{
+  struct irda_device_info *discoveries;        /* Copy of the discovery log */
+  int  number;                 /* Number of nodes in the log */
+  int  i;
+
+  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
+
+  /* Ask lmp for the current discovery log */
+  discoveries = irlmp_get_discoveries(&number, 0xffff,
+                                     DISCOVERY_DEFAULT_SLOTS);
+  /* Check if the we got some results */
+  if(discoveries == NULL)
+    DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
+
+  /*
+   * Now, check all discovered devices (if any), and connect
+   * client only about the services that the client is
+   * interested in...
+   */
+  for(i = 0; i < number; i++)
+    {
+      /* Does the name match ? */
+      if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN))
+       {
+         /* Yes !!! Get it.. */
+         self->daddr = discoveries[i].daddr;
+         DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n",
+               self->rname, self->daddr);
+         kfree(discoveries);
+         DEXIT(IRDA_SR_TRACE, "\n");
+         return 0;
+       }
+    }
+  /* No luck ! */
+  DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname);
+  kfree(discoveries);
+  return -EADDRNOTAVAIL;
+}
+
+
+/************************* SOCKET ROUTINES *************************/
+/*
+ * This are the main operations on IrNET sockets, basically to create
+ * and destroy IrNET sockets. These are called from the PPP part...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Create a IrNET instance : just initialise some parameters...
+ */
+int
+irda_irnet_create(irnet_socket *       self)
+{
+  DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
+
+  self->magic = IRNET_MAGIC;   /* Paranoia */
+
+  self->ttp_open = 0;          /* Prevent higher layer from accessing IrTTP */
+  self->ttp_connect = 0;       /* Not connecting yet */
+  self->rname[0] = '\0';       /* May be set via control channel */
+  self->rdaddr = DEV_ADDR_ANY; /* May be set via control channel */
+  self->rsaddr = DEV_ADDR_ANY; /* May be set via control channel */
+  self->daddr = DEV_ADDR_ANY;  /* Until we get connected */
+  self->saddr = DEV_ADDR_ANY;  /* Until we get connected */
+  self->max_sdu_size_rx = TTP_SAR_UNBOUND;
+
+  /* Register as a client with IrLMP */
+  self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
+#ifdef DISCOVERY_NOMASK
+  self->mask = 0xffff;         /* For W2k compatibility */
+#else /* DISCOVERY_NOMASK */
+  self->mask = irlmp_service_to_hint(S_LAN);
+#endif /* DISCOVERY_NOMASK */
+  self->tx_flow = FLOW_START;  /* Flow control from IrTTP */
+
+  INIT_WORK(&self->disconnect_work, irnet_ppp_disconnect);
+
+  DEXIT(IRDA_SOCK_TRACE, "\n");
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Connect to the other side :
+ *     o convert device name to an address
+ *     o find the socket number (dlsap)
+ *     o Establish the connection
+ *
+ * Note : We no longer mimic af_irda. The IAS query for finding the TSAP
+ * is done asynchronously, like the TTP connection. This allow us to
+ * call this function from any context (not only process).
+ * The downside is that following what's happening in there is tricky
+ * because it involve various functions all over the place...
+ */
+int
+irda_irnet_connect(irnet_socket *      self)
+{
+  int          err;
+
+  DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
+
+  /* Check if we are already trying to connect.
+   * Because irda_irnet_connect() can be called directly by pppd plus
+   * packet retries in ppp_generic and connect may take time, plus we may
+   * race with irnet_connect_indication(), we need to be careful there... */
+  if(test_and_set_bit(0, &self->ttp_connect))
+    DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n");
+  if((self->iriap != NULL) || (self->tsap != NULL))
+    DERROR(IRDA_SOCK_ERROR, "Socket not cleaned up...\n");
+
+  /* Insert ourselves in the hashbin so that the IrNET server can find us.
+   * Notes : 4th arg is string of 32 char max and must be null terminated
+   *        When 4th arg is used (string), 3rd arg isn't (int)
+   *        Can't re-insert (MUST remove first) so check for that... */
+  if((irnet_server.running) && (self->q.q_next == NULL))
+    {
+      spin_lock_bh(&irnet_server.spinlock);
+      hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname);
+      spin_unlock_bh(&irnet_server.spinlock);
+      DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname);
+    }
+
+  /* If we don't have anything (no address, no name) */
+  if((self->rdaddr == DEV_ADDR_ANY) && (self->rname[0] == '\0'))
+    {
+      /* Try to find a suitable address */
+      if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0)
+       DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n");
+      /* In most cases, the call above is non-blocking */
+    }
+  else
+    {
+      /* If we have only the name (no address), try to get an address */
+      if(self->rdaddr == DEV_ADDR_ANY)
+       {
+         if((err = irnet_dname_to_daddr(self)) != 0)
+           DRETURN(err, IRDA_SOCK_INFO, "name connect failed!\n");
+       }
+      else
+       /* Use the requested destination address */
+       self->daddr = self->rdaddr;
+
+      /* Query remote LM-IAS to find LSAP selector */
+      irnet_find_lsap_sel(self);
+      /* The above call is non blocking */
+    }
+
+  /* At this point, we are waiting for the IrDA stack to call us back,
+   * or we have already failed.
+   * We will finish the connection procedure in irnet_connect_tsap().
+   */
+  DEXIT(IRDA_SOCK_TRACE, "\n");
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_irnet_destroy(self)
+ *
+ *    Destroy irnet instance
+ *
+ * Note : this need to be called from a process context.
+ */
+void
+irda_irnet_destroy(irnet_socket *      self)
+{
+  DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
+  if(self == NULL)
+    return;
+
+  /* Remove ourselves from hashbin (if we are queued in hashbin)
+   * Note : `irnet_server.running' protect us from calls in hashbin_delete() */
+  if((irnet_server.running) && (self->q.q_next != NULL))
+    {
+      struct irnet_socket *    entry;
+      DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n");
+      spin_lock_bh(&irnet_server.spinlock);
+      entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self);
+      self->q.q_next = NULL;
+      spin_unlock_bh(&irnet_server.spinlock);
+      DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n");
+    }
+
+  /* If we were connected, post a message */
+  if(test_bit(0, &self->ttp_open))
+    {
+      /* Note : as the disconnect comes from ppp_generic, the unit number
+       * doesn't exist anymore when we post the event, so we need to pass
+       * NULL as the first arg... */
+      irnet_post_event(NULL, IRNET_DISCONNECT_TO,
+                      self->saddr, self->daddr, self->rname, 0);
+    }
+
+  /* Prevent various IrDA callbacks from messing up things
+   * Need to be first */
+  clear_bit(0, &self->ttp_connect);
+
+  /* Prevent higher layer from accessing IrTTP */
+  clear_bit(0, &self->ttp_open);
+
+  /* Unregister with IrLMP */
+  irlmp_unregister_client(self->ckey);
+
+  /* Unregister with LM-IAS */
+  if(self->iriap)
+    {
+      iriap_close(self->iriap);
+      self->iriap = NULL;
+    }
+
+  /* Cleanup eventual discoveries from connection attempt or control channel */
+  if(self->discoveries != NULL)
+    {
+      /* Cleanup our copy of the discovery log */
+      kfree(self->discoveries);
+      self->discoveries = NULL;
+    }
+
+  /* Close our IrTTP connection */
+  if(self->tsap)
+    {
+      DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n");
+      irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
+      irttp_close_tsap(self->tsap);
+      self->tsap = NULL;
+    }
+  self->stsap_sel = 0;
+
+  DEXIT(IRDA_SOCK_TRACE, "\n");
+}
+
+
+/************************** SERVER SOCKET **************************/
+/*
+ * The IrNET service is composed of one server socket and a variable
+ * number of regular IrNET sockets. The server socket is supposed to
+ * handle incoming connections and redirect them to one IrNET sockets.
+ * It's a superset of the regular IrNET socket, but has a very distinct
+ * behaviour...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_daddr_to_dname (self)
+ *
+ *    Convert an IrDA address to a IrDA nickname
+ *
+ * It basically look into the discovery log until there is a match.
+ */
+static inline int
+irnet_daddr_to_dname(irnet_socket *    self)
+{
+  struct irda_device_info *discoveries;        /* Copy of the discovery log */
+  int  number;                 /* Number of nodes in the log */
+  int  i;
+
+  DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
+
+  /* Ask lmp for the current discovery log */
+  discoveries = irlmp_get_discoveries(&number, 0xffff,
+                                     DISCOVERY_DEFAULT_SLOTS);
+  /* Check if the we got some results */
+  if (discoveries == NULL)
+    DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n");
+
+  /* Now, check all discovered devices (if any) */
+  for(i = 0; i < number; i++)
+    {
+      /* Does the name match ? */
+      if(discoveries[i].daddr == self->daddr)
+       {
+         /* Yes !!! Get it.. */
+         strlcpy(self->rname, discoveries[i].info, sizeof(self->rname));
+         self->rname[sizeof(self->rname) - 1] = '\0';
+         DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n",
+               self->daddr, self->rname);
+         kfree(discoveries);
+         DEXIT(IRDA_SERV_TRACE, "\n");
+         return 0;
+       }
+    }
+  /* No luck ! */
+  DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr);
+  kfree(discoveries);
+  return -EADDRNOTAVAIL;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_find_socket (self)
+ *
+ *    Find the correct IrNET socket
+ *
+ * Look into the list of IrNET sockets and finds one with the right
+ * properties...
+ */
+static inline irnet_socket *
+irnet_find_socket(irnet_socket *       self)
+{
+  irnet_socket *       new = (irnet_socket *) NULL;
+  int                  err;
+
+  DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
+
+  /* Get the addresses of the requester */
+  self->daddr = irttp_get_daddr(self->tsap);
+  self->saddr = irttp_get_saddr(self->tsap);
+
+  /* Try to get the IrDA nickname of the requester */
+  err = irnet_daddr_to_dname(self);
+
+  /* Protect access to the instance list */
+  spin_lock_bh(&irnet_server.spinlock);
+
+  /* So now, try to get an socket having specifically
+   * requested that nickname */
+  if(err == 0)
+    {
+      new = (irnet_socket *) hashbin_find(irnet_server.list,
+                                         0, self->rname);
+      if(new)
+       DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches rname ``%s''.\n",
+             new, new->rname);
+    }
+
+  /* If no name matches, try to find an socket by the destination address */
+  /* It can be either the requested destination address (set via the
+   * control channel), or the current destination address if the
+   * socket is in the middle of a connection request */
+  if(new == (irnet_socket *) NULL)
+    {
+      new = (irnet_socket *) hashbin_get_first(irnet_server.list);
+      while(new !=(irnet_socket *) NULL)
+       {
+         /* Does it have the same address ? */
+         if((new->rdaddr == self->daddr) || (new->daddr == self->daddr))
+           {
+             /* Yes !!! Get it.. */
+             DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches daddr %#08x.\n",
+                   new, self->daddr);
+             break;
+           }
+         new = (irnet_socket *) hashbin_get_next(irnet_server.list);
+       }
+    }
+
+  /* If we don't have any socket, get the first unconnected socket */
+  if(new == (irnet_socket *) NULL)
+    {
+      new = (irnet_socket *) hashbin_get_first(irnet_server.list);
+      while(new !=(irnet_socket *) NULL)
+       {
+         /* Is it available ? */
+         if(!(test_bit(0, &new->ttp_open)) && (new->rdaddr == DEV_ADDR_ANY) &&
+            (new->rname[0] == '\0') && (new->ppp_open))
+           {
+             /* Yes !!! Get it.. */
+             DEBUG(IRDA_SERV_INFO, "Socket 0x%p is free.\n",
+                   new);
+             break;
+           }
+         new = (irnet_socket *) hashbin_get_next(irnet_server.list);
+       }
+    }
+
+  /* Spin lock end */
+  spin_unlock_bh(&irnet_server.spinlock);
+
+  DEXIT(IRDA_SERV_TRACE, " - new = 0x%p\n", new);
+  return new;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_connect_socket (self)
+ *
+ *    Connect an incoming connection to the socket
+ *
+ */
+static inline int
+irnet_connect_socket(irnet_socket *    server,
+                    irnet_socket *     new,
+                    struct qos_info *  qos,
+                    __u32              max_sdu_size,
+                    __u8               max_header_size)
+{
+  DENTER(IRDA_SERV_TRACE, "(server=0x%p, new=0x%p)\n",
+        server, new);
+
+  /* Now attach up the new socket */
+  new->tsap = irttp_dup(server->tsap, new);
+  DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n");
+
+  /* Set up all the relevant parameters on the new socket */
+  new->stsap_sel = new->tsap->stsap_sel;
+  new->dtsap_sel = new->tsap->dtsap_sel;
+  new->saddr = irttp_get_saddr(new->tsap);
+  new->daddr = irttp_get_daddr(new->tsap);
+
+  new->max_header_size = max_header_size;
+  new->max_sdu_size_tx = max_sdu_size;
+  new->max_data_size   = max_sdu_size;
+#ifdef STREAM_COMPAT
+  /* If we want to receive "stream sockets" */
+  if(max_sdu_size == 0)
+    new->max_data_size = irttp_get_max_seg_size(new->tsap);
+#endif /* STREAM_COMPAT */
+
+  /* Clean up the original one to keep it in listen state */
+  irttp_listen(server->tsap);
+
+  /* Send a connection response on the new socket */
+  irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL);
+
+  /* Allow PPP to send its junk over the new socket... */
+  set_bit(0, &new->ttp_open);
+
+  /* Not connecting anymore, and clean up last possible remains
+   * of connection attempts on the socket */
+  clear_bit(0, &new->ttp_connect);
+  if(new->iriap)
+    {
+      iriap_close(new->iriap);
+      new->iriap = NULL;
+    }
+  if(new->discoveries != NULL)
+    {
+      kfree(new->discoveries);
+      new->discoveries = NULL;
+    }
+
+#ifdef CONNECT_INDIC_KICK
+  /* As currently we don't block packets in ppp_irnet_send() while passive,
+   * this is not really needed...
+   * Also, not doing it give IrDA a chance to finish the setup properly
+   * before being swamped with packets... */
+  ppp_output_wakeup(&new->chan);
+#endif /* CONNECT_INDIC_KICK */
+
+  /* Notify the control channel */
+  irnet_post_event(new, IRNET_CONNECT_FROM,
+                  new->saddr, new->daddr, server->rname, 0);
+
+  DEXIT(IRDA_SERV_TRACE, "\n");
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_disconnect_server (self)
+ *
+ *    Cleanup the server socket when the incoming connection abort
+ *
+ */
+static inline void
+irnet_disconnect_server(irnet_socket * self,
+                       struct sk_buff *skb)
+{
+  DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
+
+  /* Put the received packet in the black hole */
+  kfree_skb(skb);
+
+#ifdef FAIL_SEND_DISCONNECT
+  /* Tell the other party we don't want to be connected */
+  /* Hum... Is it the right thing to do ? And do we need to send
+   * a connect response before ? It looks ok without this... */
+  irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
+#endif /* FAIL_SEND_DISCONNECT */
+
+  /* Notify the control channel (see irnet_find_socket()) */
+  irnet_post_event(NULL, IRNET_REQUEST_FROM,
+                  self->saddr, self->daddr, self->rname, 0);
+
+  /* Clean up the server to keep it in listen state */
+  irttp_listen(self->tsap);
+
+  DEXIT(IRDA_SERV_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_setup_server (self)
+ *
+ *    Create a IrTTP server and set it up...
+ *
+ * Register the IrLAN hint bit, create a IrTTP instance for us,
+ * set all the IrTTP callbacks and create an IrIAS entry...
+ */
+static inline int
+irnet_setup_server(void)
+{
+  __u16                hints;
+
+  DENTER(IRDA_SERV_TRACE, "()\n");
+
+  /* Initialise the regular socket part of the server */
+  irda_irnet_create(&irnet_server.s);
+
+  /* Open a local TSAP (an IrTTP instance) for the server */
+  irnet_open_tsap(&irnet_server.s);
+
+  /* PPP part setup */
+  irnet_server.s.ppp_open = 0;
+  irnet_server.s.chan.private = NULL;
+  irnet_server.s.file = NULL;
+
+  /* Get the hint bit corresponding to IrLAN */
+  /* Note : we overload the IrLAN hint bit. As it is only a "hint", and as
+   * we provide roughly the same functionality as IrLAN, this is ok.
+   * In fact, the situation is similar as JetSend overloading the Obex hint
+   */
+  hints = irlmp_service_to_hint(S_LAN);
+
+#ifdef ADVERTISE_HINT
+  /* Register with IrLMP as a service (advertise our hint bit) */
+  irnet_server.skey = irlmp_register_service(hints);
+#endif /* ADVERTISE_HINT */
+
+  /* Register with LM-IAS (so that people can connect to us) */
+  irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies);
+  irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE,
+                          irnet_server.s.stsap_sel, IAS_KERNEL_ATTR);
+  irias_insert_object(irnet_server.ias_obj);
+
+#ifdef DISCOVERY_EVENTS
+  /* Tell IrLMP we want to be notified of newly discovered nodes */
+  irlmp_update_client(irnet_server.s.ckey, hints,
+                     irnet_discovery_indication, irnet_expiry_indication,
+                     (void *) &irnet_server.s);
+#endif
+
+  DEXIT(IRDA_SERV_TRACE, " - self=0x%p\n", &irnet_server.s);
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irda_destroy_server (self)
+ *
+ *    Destroy the IrTTP server...
+ *
+ * Reverse of the previous function...
+ */
+static inline void
+irnet_destroy_server(void)
+{
+  DENTER(IRDA_SERV_TRACE, "()\n");
+
+#ifdef ADVERTISE_HINT
+  /* Unregister with IrLMP */
+  irlmp_unregister_service(irnet_server.skey);
+#endif /* ADVERTISE_HINT */
+
+  /* Unregister with LM-IAS */
+  if(irnet_server.ias_obj)
+    irias_delete_object(irnet_server.ias_obj);
+
+  /* Cleanup the socket part */
+  irda_irnet_destroy(&irnet_server.s);
+
+  DEXIT(IRDA_SERV_TRACE, "\n");
+}
+
+
+/************************ IRDA-TTP CALLBACKS ************************/
+/*
+ * When we create a IrTTP instance, we pass to it a set of callbacks
+ * that IrTTP will call in case of various events.
+ * We take care of those events here.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_data_indication (instance, sap, skb)
+ *
+ *    Received some data from TinyTP. Just queue it on the receive queue
+ *
+ */
+static int
+irnet_data_indication(void *   instance,
+                     void *    sap,
+                     struct sk_buff *skb)
+{
+  irnet_socket *       ap = (irnet_socket *) instance;
+  unsigned char *      p;
+  int                  code = 0;
+
+  DENTER(IRDA_TCB_TRACE, "(self/ap=0x%p, skb=0x%p)\n",
+        ap, skb);
+  DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n");
+
+  /* Check is ppp is ready to receive our packet */
+  if(!ap->ppp_open)
+    {
+      DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n");
+      /* When we return error, TTP will need to requeue the skb and
+       * will stop the sender. IrTTP will stall until we send it a
+       * flow control request... */
+      return -ENOMEM;
+    }
+
+  /* strip address/control field if present */
+  p = skb->data;
+  if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI))
+    {
+      /* chop off address/control */
+      if(skb->len < 3)
+       goto err_exit;
+      p = skb_pull(skb, 2);
+    }
+
+  /* decompress protocol field if compressed */
+  if(p[0] & 1)
+    {
+      /* protocol is compressed */
+      *(u8 *)skb_push(skb, 1) = 0;
+    }
+  else
+    if(skb->len < 2)
+      goto err_exit;
+
+  /* pass to generic ppp layer */
+  /* Note : how do I know if ppp can accept or not the packet ? This is
+   * essential if I want to manage flow control smoothly... */
+  ppp_input(&ap->chan, skb);
+
+  DEXIT(IRDA_TCB_TRACE, "\n");
+  return 0;
+
+ err_exit:
+  DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n");
+  kfree_skb(skb);
+  ppp_input_error(&ap->chan, code);
+  return 0;    /* Don't return an error code, only for flow control... */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_disconnect_indication (instance, sap, reason, skb)
+ *
+ *    Connection has been closed. Chech reason to find out why
+ *
+ * Note : there are many cases where we come here :
+ *     o attempted to connect, timeout
+ *     o connected, link is broken, LAP has timeout
+ *     o connected, other side close the link
+ *     o connection request on the server not handled
+ */
+static void
+irnet_disconnect_indication(void *     instance,
+                           void *      sap,
+                           LM_REASON   reason,
+                           struct sk_buff *skb)
+{
+  irnet_socket *       self = (irnet_socket *) instance;
+  int                  test_open;
+  int                  test_connect;
+
+  DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
+  DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
+
+  /* Don't care about it, but let's not leak it */
+  if(skb)
+    dev_kfree_skb(skb);
+
+  /* Prevent higher layer from accessing IrTTP */
+  test_open = test_and_clear_bit(0, &self->ttp_open);
+  /* Not connecting anymore...
+   * (note : TSAP is open, so IAP callbacks are no longer pending...) */
+  test_connect = test_and_clear_bit(0, &self->ttp_connect);
+
+  /* If both self->ttp_open and self->ttp_connect are NULL, it mean that we
+   * have a race condition with irda_irnet_destroy() or
+   * irnet_connect_indication(), so don't mess up tsap...
+   */
+  if(!(test_open || test_connect))
+    {
+      DERROR(IRDA_CB_ERROR, "Race condition detected...\n");
+      return;
+    }
+
+  /* If we were active, notify the control channel */
+  if(test_open)
+    irnet_post_event(self, IRNET_DISCONNECT_FROM,
+                    self->saddr, self->daddr, self->rname, 0);
+  else
+    /* If we were trying to connect, notify the control channel */
+    if((self->tsap) && (self != &irnet_server.s))
+      irnet_post_event(self, IRNET_NOANSWER_FROM,
+                      self->saddr, self->daddr, self->rname, 0);
+
+  /* Close our IrTTP connection, cleanup tsap */
+  if((self->tsap) && (self != &irnet_server.s))
+    {
+      DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n");
+      irttp_close_tsap(self->tsap);
+      self->tsap = NULL;
+    }
+  /* Cleanup the socket in case we want to reconnect in ppp_output_wakeup() */
+  self->stsap_sel = 0;
+  self->daddr = DEV_ADDR_ANY;
+  self->tx_flow = FLOW_START;
+
+  /* Deal with the ppp instance if it's still alive */
+  if(self->ppp_open)
+    {
+      if(test_open)
+       {
+         /* ppp_unregister_channel() wants a user context. */
+         schedule_work(&self->disconnect_work);
+       }
+      else
+       {
+         /* If we were trying to connect, flush (drain) ppp_generic
+          * Tx queue (most often we have blocked it), which will
+          * trigger an other attempt to connect. If we are passive,
+          * this will empty the Tx queue after last try. */
+         ppp_output_wakeup(&self->chan);
+       }
+    }
+
+  DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb)
+ *
+ *    Connections has been confirmed by the remote device
+ *
+ */
+static void
+irnet_connect_confirm(void *   instance,
+                     void *    sap,
+                     struct qos_info *qos,
+                     __u32     max_sdu_size,
+                     __u8      max_header_size,
+                     struct sk_buff *skb)
+{
+  irnet_socket *       self = (irnet_socket *) instance;
+
+  DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
+
+  /* Check if socket is closing down (via irda_irnet_destroy()) */
+  if(! test_bit(0, &self->ttp_connect))
+    {
+      DERROR(IRDA_CB_ERROR, "Socket no longer connecting. Ouch !\n");
+      return;
+    }
+
+  /* How much header space do we need to reserve */
+  self->max_header_size = max_header_size;
+
+  /* IrTTP max SDU size in transmit direction */
+  self->max_sdu_size_tx = max_sdu_size;
+  self->max_data_size = max_sdu_size;
+#ifdef STREAM_COMPAT
+  if(max_sdu_size == 0)
+    self->max_data_size = irttp_get_max_seg_size(self->tsap);
+#endif /* STREAM_COMPAT */
+
+  /* At this point, IrLMP has assigned our source address */
+  self->saddr = irttp_get_saddr(self->tsap);
+
+  /* Allow higher layer to access IrTTP */
+  set_bit(0, &self->ttp_open);
+  clear_bit(0, &self->ttp_connect);    /* Not racy, IrDA traffic is serial */
+  /* Give a kick in the ass of ppp_generic so that he sends us some data */
+  ppp_output_wakeup(&self->chan);
+
+  /* Check size of received packet */
+  if(skb->len > 0)
+    {
+#ifdef PASS_CONNECT_PACKETS
+      DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
+      /* Try to pass it to PPP */
+      irnet_data_indication(instance, sap, skb);
+#else /* PASS_CONNECT_PACKETS */
+      DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
+      kfree_skb(skb);  /* Note : will be optimised with other kfree... */
+#endif /* PASS_CONNECT_PACKETS */
+    }
+  else
+    kfree_skb(skb);
+
+  /* Notify the control channel */
+  irnet_post_event(self, IRNET_CONNECT_TO,
+                  self->saddr, self->daddr, self->rname, 0);
+
+  DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_flow_indication (instance, sap, flow)
+ *
+ *    Used by TinyTP to tell us if it can accept more data or not
+ *
+ */
+static void
+irnet_flow_indication(void *   instance,
+                     void *    sap,
+                     LOCAL_FLOW flow)
+{
+  irnet_socket *       self = (irnet_socket *) instance;
+  LOCAL_FLOW           oldflow = self->tx_flow;
+
+  DENTER(IRDA_TCB_TRACE, "(self=0x%p, flow=%d)\n", self, flow);
+
+  /* Update our state */
+  self->tx_flow = flow;
+
+  /* Check what IrTTP want us to do... */
+  switch(flow)
+    {
+    case FLOW_START:
+      DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n");
+      /* Check if we really need to wake up PPP */
+      if(oldflow == FLOW_STOP)
+       ppp_output_wakeup(&self->chan);
+      else
+       DEBUG(IRDA_CB_INFO, "But we were already transmitting !!!\n");
+      break;
+    case FLOW_STOP:
+      DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n");
+      break;
+    default:
+      DEBUG(IRDA_CB_INFO, "Unknown flow command!\n");
+      break;
+    }
+
+  DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_status_indication (instance, sap, reason, skb)
+ *
+ *    Link (IrLAP) status report.
+ *
+ */
+static void
+irnet_status_indication(void * instance,
+                       LINK_STATUS link,
+                       LOCK_STATUS lock)
+{
+  irnet_socket *       self = (irnet_socket *) instance;
+
+  DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
+  DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
+
+  /* We can only get this event if we are connected */
+  switch(link)
+    {
+    case STATUS_NO_ACTIVITY:
+      irnet_post_event(self, IRNET_BLOCKED_LINK,
+                      self->saddr, self->daddr, self->rname, 0);
+      break;
+    default:
+      DEBUG(IRDA_CB_INFO, "Unknown status...\n");
+    }
+
+  DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata)
+ *
+ *    Incoming connection
+ *
+ * In theory, this function is called only on the server socket.
+ * Some other node is attempting to connect to the IrNET service, and has
+ * sent a connection request on our server socket.
+ * We just redirect the connection to the relevant IrNET socket.
+ *
+ * Note : we also make sure that between 2 irnet nodes, there can
+ * exist only one irnet connection.
+ */
+static void
+irnet_connect_indication(void *                instance,
+                        void *         sap,
+                        struct qos_info *qos,
+                        __u32          max_sdu_size,
+                        __u8           max_header_size,
+                        struct sk_buff *skb)
+{
+  irnet_socket *       server = &irnet_server.s;
+  irnet_socket *       new = (irnet_socket *) NULL;
+
+  DENTER(IRDA_TCB_TRACE, "(server=0x%p)\n", server);
+  DASSERT(instance == &irnet_server, , IRDA_CB_ERROR,
+         "Invalid instance (0x%p) !!!\n", instance);
+  DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n");
+
+  /* Try to find the most appropriate IrNET socket */
+  new = irnet_find_socket(server);
+
+  /* After all this hard work, do we have an socket ? */
+  if(new == (irnet_socket *) NULL)
+    {
+      DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n");
+      irnet_disconnect_server(server, skb);
+      return;
+    }
+
+  /* Is the socket already busy ? */
+  if(test_bit(0, &new->ttp_open))
+    {
+      DEXIT(IRDA_CB_INFO, ": Socket already connected.\n");
+      irnet_disconnect_server(server, skb);
+      return;
+    }
+
+  /* The following code is a bit tricky, so need comments ;-)
+   */
+  /* If ttp_connect is set, the socket is trying to connect to the other
+   * end and may have sent a IrTTP connection request and is waiting for
+   * a connection response (that may never come).
+   * Now, the pain is that the socket may have opened a tsap and is
+   * waiting on it, while the other end is trying to connect to it on
+   * another tsap.
+   * Because IrNET can be peer to peer, we need to workaround this.
+   * Furthermore, the way the irnetd script is implemented, the
+   * target will create a second IrNET connection back to the
+   * originator and expect the originator to bind this new connection
+   * to the original PPPD instance.
+   * And of course, if we don't use irnetd, we can have a race when
+   * both side try to connect simultaneously, which could leave both
+   * connections half closed (yuck).
+   * Conclusions :
+   *   1) The "originator" must accept the new connection and get rid
+   *      of the old one so that irnetd works
+   *   2) One side must deny the new connection to avoid races,
+   *      but both side must agree on which side it is...
+   * Most often, the originator is primary at the LAP layer.
+   * Jean II
+   */
+  /* Now, let's look at the way I wrote the test...
+   * We need to clear up the ttp_connect flag atomically to prevent
+   * irnet_disconnect_indication() to mess up the tsap we are going to close.
+   * We want to clear the ttp_connect flag only if we close the tsap,
+   * otherwise we will never close it, so we need to check for primary
+   * *before* doing the test on the flag.
+   * And of course, ALLOW_SIMULT_CONNECT can disable this entirely...
+   * Jean II
+   */
+
+  /* Socket already connecting ? On primary ? */
+  if(0
+#ifdef ALLOW_SIMULT_CONNECT
+     || ((irttp_is_primary(server->tsap) == 1) &&      /* primary */
+        (test_and_clear_bit(0, &new->ttp_connect)))
+#endif /* ALLOW_SIMULT_CONNECT */
+     )
+    {
+      DERROR(IRDA_CB_ERROR, "Socket already connecting, but going to reuse it !\n");
+
+      /* Cleanup the old TSAP if necessary - IrIAP will be cleaned up later */
+      if(new->tsap != NULL)
+       {
+         /* Close the old connection the new socket was attempting,
+          * so that we can hook it up to the new connection.
+          * It's now safe to do it... */
+         irttp_close_tsap(new->tsap);
+         new->tsap = NULL;
+       }
+    }
+  else
+    {
+      /* Three options :
+       * 1) socket was not connecting or connected : ttp_connect should be 0.
+       * 2) we don't want to connect the socket because we are secondary or
+       * ALLOW_SIMULT_CONNECT is undefined. ttp_connect should be 1.
+       * 3) we are half way in irnet_disconnect_indication(), and it's a
+       * nice race condition... Fortunately, we can detect that by checking
+       * if tsap is still alive. On the other hand, we can't be in
+       * irda_irnet_destroy() otherwise we would not have found this
+       * socket in the hashbin.
+       * Jean II */
+      if((test_bit(0, &new->ttp_connect)) || (new->tsap != NULL))
+       {
+         /* Don't mess this socket, somebody else in in charge... */
+         DERROR(IRDA_CB_ERROR, "Race condition detected, socket in use, abort connect...\n");
+         irnet_disconnect_server(server, skb);
+         return;
+       }
+    }
+
+  /* So : at this point, we have a socket, and it is idle. Good ! */
+  irnet_connect_socket(server, new, qos, max_sdu_size, max_header_size);
+
+  /* Check size of received packet */
+  if(skb->len > 0)
+    {
+#ifdef PASS_CONNECT_PACKETS
+      DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
+      /* Try to pass it to PPP */
+      irnet_data_indication(new, new->tsap, skb);
+#else /* PASS_CONNECT_PACKETS */
+      DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
+      kfree_skb(skb);  /* Note : will be optimised with other kfree... */
+#endif /* PASS_CONNECT_PACKETS */
+    }
+  else
+    kfree_skb(skb);
+
+  DEXIT(IRDA_TCB_TRACE, "\n");
+}
+
+
+/********************** IRDA-IAS/LMP CALLBACKS **********************/
+/*
+ * These are the callbacks called by other layers of the IrDA stack,
+ * mainly LMP for discovery and IAS for name queries.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_getvalue_confirm (result, obj_id, value, priv)
+ *
+ *    Got answer from remote LM-IAS, just connect
+ *
+ * This is the reply to a IAS query we were doing to find the TSAP of
+ * the device we want to connect to.
+ * If we have found a valid TSAP, just initiate the TTP connection
+ * on this TSAP.
+ */
+static void
+irnet_getvalue_confirm(int     result,
+                      __u16    obj_id,
+                      struct ias_value *value,
+                      void *   priv)
+{
+  irnet_socket *       self = (irnet_socket *) priv;
+
+  DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
+  DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n");
+
+  /* Check if already connected (via irnet_connect_socket())
+   * or socket is closing down (via irda_irnet_destroy()) */
+  if(! test_bit(0, &self->ttp_connect))
+    {
+      DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n");
+      return;
+    }
+
+  /* We probably don't need to make any more queries */
+  iriap_close(self->iriap);
+  self->iriap = NULL;
+
+  /* Post process the IAS reply */
+  self->dtsap_sel = irnet_ias_to_tsap(self, result, value);
+
+  /* If error, just go out */
+  if(self->errno)
+    {
+      clear_bit(0, &self->ttp_connect);
+      DERROR(IRDA_OCB_ERROR, "IAS connect failed ! (0x%X)\n", self->errno);
+      return;
+    }
+
+  DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
+       self->daddr, self->dtsap_sel);
+
+  /* Start up TTP - non blocking */
+  irnet_connect_tsap(self);
+
+  DEXIT(IRDA_OCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_discovervalue_confirm (result, obj_id, value, priv)
+ *
+ *    Handle the TSAP discovery procedure state machine.
+ *    Got answer from remote LM-IAS, try next device
+ *
+ * We are doing a  TSAP discovery procedure, and we got an answer to
+ * a IAS query we were doing to find the TSAP on one of the address
+ * in the discovery log.
+ *
+ * If we have found a valid TSAP for the first time, save it. If it's
+ * not the first time we found one, complain.
+ *
+ * If we have more addresses in the log, just initiate a new query.
+ * Note that those query may fail (see irnet_discover_daddr_and_lsap_sel())
+ *
+ * Otherwise, wrap up the procedure (cleanup), check if we have found
+ * any device and connect to it.
+ */
+static void
+irnet_discovervalue_confirm(int                result,
+                           __u16       obj_id,
+                           struct ias_value *value,
+                           void *      priv)
+{
+  irnet_socket *       self = (irnet_socket *) priv;
+  __u8                 dtsap_sel;              /* TSAP we are looking for */
+
+  DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
+  DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n");
+
+  /* Check if already connected (via irnet_connect_socket())
+   * or socket is closing down (via irda_irnet_destroy()) */
+  if(! test_bit(0, &self->ttp_connect))
+    {
+      DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n");
+      return;
+    }
+
+  /* Post process the IAS reply */
+  dtsap_sel = irnet_ias_to_tsap(self, result, value);
+
+  /* Have we got something ? */
+  if(self->errno == 0)
+    {
+      /* We found the requested service */
+      if(self->daddr != DEV_ADDR_ANY)
+       {
+         DERROR(IRDA_OCB_ERROR, "More than one device in range supports IrNET...\n");
+       }
+      else
+       {
+         /* First time we found that one, save it ! */
+         self->daddr = self->discoveries[self->disco_index].daddr;
+         self->dtsap_sel = dtsap_sel;
+       }
+    }
+
+  /* If no failure */
+  if((self->errno == -EADDRNOTAVAIL) || (self->errno == 0))
+    {
+      int      ret;
+
+      /* Search the next node */
+      ret = irnet_discover_next_daddr(self);
+      if(!ret)
+       {
+         /* In this case, the above request was non-blocking.
+          * We will return here after a while... */
+         return;
+       }
+      /* In this case, we have processed the last discovery item */
+    }
+
+  /* No more queries to be done (failure or last one) */
+
+  /* We probably don't need to make any more queries */
+  iriap_close(self->iriap);
+  self->iriap = NULL;
+
+  /* No more items : remove the log and signal termination */
+  DEBUG(IRDA_OCB_INFO, "Cleaning up log (0x%p)\n",
+       self->discoveries);
+  if(self->discoveries != NULL)
+    {
+      /* Cleanup our copy of the discovery log */
+      kfree(self->discoveries);
+      self->discoveries = NULL;
+    }
+  self->disco_number = -1;
+
+  /* Check out what we found */
+  if(self->daddr == DEV_ADDR_ANY)
+    {
+      self->daddr = DEV_ADDR_ANY;
+      clear_bit(0, &self->ttp_connect);
+      DEXIT(IRDA_OCB_TRACE, ": cannot discover IrNET in any device !!!\n");
+      return;
+    }
+
+  /* We have a valid address - just connect */
+
+  DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
+       self->daddr, self->dtsap_sel);
+
+  /* Start up TTP - non blocking */
+  irnet_connect_tsap(self);
+
+  DEXIT(IRDA_OCB_TRACE, "\n");
+}
+
+#ifdef DISCOVERY_EVENTS
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_discovery_indication (discovery)
+ *
+ *    Got a discovery indication from IrLMP, post an event
+ *
+ * Note : IrLMP take care of matching the hint mask for us, and also
+ * check if it is a "new" node for us...
+ *
+ * As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET
+ * nodes, so it's only at connection time that we will know if the
+ * node support IrNET, IrLAN or both. The other solution is to check
+ * in IAS the PNP ids and service name.
+ * Note : even if a node support IrNET (or IrLAN), it's no guarantee
+ * that we will be able to connect to it, the node might already be
+ * busy...
+ *
+ * One last thing : in some case, this function will trigger duplicate
+ * discovery events. On the other hand, we should catch all
+ * discoveries properly (i.e. not miss one). Filtering duplicate here
+ * is to messy, so we leave that to user space...
+ */
+static void
+irnet_discovery_indication(discinfo_t *                discovery,
+                          DISCOVERY_MODE       mode,
+                          void *               priv)
+{
+  irnet_socket *       self = &irnet_server.s;
+
+  DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
+  DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR,
+         "Invalid instance (0x%p) !!!\n", priv);
+
+  DEBUG(IRDA_OCB_INFO, "Discovered new IrNET/IrLAN node %s...\n",
+       discovery->info);
+
+  /* Notify the control channel */
+  irnet_post_event(NULL, IRNET_DISCOVER,
+                  discovery->saddr, discovery->daddr, discovery->info,
+                  get_unaligned((__u16 *)discovery->hints));
+
+  DEXIT(IRDA_OCB_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_expiry_indication (expiry)
+ *
+ *    Got a expiry indication from IrLMP, post an event
+ *
+ * Note : IrLMP take care of matching the hint mask for us, we only
+ * check if it is a "new" node...
+ */
+static void
+irnet_expiry_indication(discinfo_t *   expiry,
+                       DISCOVERY_MODE  mode,
+                       void *          priv)
+{
+  irnet_socket *       self = &irnet_server.s;
+
+  DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
+  DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR,
+         "Invalid instance (0x%p) !!!\n", priv);
+
+  DEBUG(IRDA_OCB_INFO, "IrNET/IrLAN node %s expired...\n",
+       expiry->info);
+
+  /* Notify the control channel */
+  irnet_post_event(NULL, IRNET_EXPIRE,
+                  expiry->saddr, expiry->daddr, expiry->info,
+                  get_unaligned((__u16 *)expiry->hints));
+
+  DEXIT(IRDA_OCB_TRACE, "\n");
+}
+#endif /* DISCOVERY_EVENTS */
+
+
+/*********************** PROC ENTRY CALLBACKS ***********************/
+/*
+ * We create a instance in the /proc filesystem, and here we take care
+ * of that...
+ */
+
+#ifdef CONFIG_PROC_FS
+static int
+irnet_proc_show(struct seq_file *m, void *v)
+{
+  irnet_socket *       self;
+  char *               state;
+  int                  i = 0;
+
+  /* Get the IrNET server information... */
+  seq_printf(m, "IrNET server - ");
+  seq_printf(m, "IrDA state: %s, ",
+                (irnet_server.running ? "running" : "dead"));
+  seq_printf(m, "stsap_sel: %02x, ", irnet_server.s.stsap_sel);
+  seq_printf(m, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel);
+
+  /* Do we need to continue ? */
+  if(!irnet_server.running)
+    return 0;
+
+  /* Protect access to the instance list */
+  spin_lock_bh(&irnet_server.spinlock);
+
+  /* Get the sockets one by one... */
+  self = (irnet_socket *) hashbin_get_first(irnet_server.list);
+  while(self != NULL)
+    {
+      /* Start printing info about the socket. */
+      seq_printf(m, "\nIrNET socket %d - ", i++);
+
+      /* First, get the requested configuration */
+      seq_printf(m, "Requested IrDA name: \"%s\", ", self->rname);
+      seq_printf(m, "daddr: %08x, ", self->rdaddr);
+      seq_printf(m, "saddr: %08x\n", self->rsaddr);
+
+      /* Second, get all the PPP info */
+      seq_printf(m, "  PPP state: %s",
+                (self->ppp_open ? "registered" : "unregistered"));
+      if(self->ppp_open)
+       {
+         seq_printf(m, ", unit: ppp%d",
+                        ppp_unit_number(&self->chan));
+         seq_printf(m, ", channel: %d",
+                        ppp_channel_index(&self->chan));
+         seq_printf(m, ", mru: %d",
+                        self->mru);
+         /* Maybe add self->flags ? Later... */
+       }
+
+      /* Then, get all the IrDA specific info... */
+      if(self->ttp_open)
+       state = "connected";
+      else
+       if(self->tsap != NULL)
+         state = "connecting";
+       else
+         if(self->iriap != NULL)
+           state = "searching";
+         else
+           if(self->ttp_connect)
+             state = "weird";
+           else
+             state = "idle";
+      seq_printf(m, "\n        IrDA state: %s, ", state);
+      seq_printf(m, "daddr: %08x, ", self->daddr);
+      seq_printf(m, "stsap_sel: %02x, ", self->stsap_sel);
+      seq_printf(m, "dtsap_sel: %02x\n", self->dtsap_sel);
+
+      /* Next socket, please... */
+      self = (irnet_socket *) hashbin_get_next(irnet_server.list);
+    }
+
+  /* Spin lock end */
+  spin_unlock_bh(&irnet_server.spinlock);
+
+  return 0;
+}
+
+static int irnet_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, irnet_proc_show, NULL);
+}
+
+static const struct file_operations irnet_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = irnet_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+#endif /* PROC_FS */
+
+
+/********************** CONFIGURATION/CLEANUP **********************/
+/*
+ * Initialisation and teardown of the IrDA part, called at module
+ * insertion and removal...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare the IrNET layer for operation...
+ */
+int __init
+irda_irnet_init(void)
+{
+  int          err = 0;
+
+  DENTER(MODULE_TRACE, "()\n");
+
+  /* Pure paranoia - should be redundant */
+  memset(&irnet_server, 0, sizeof(struct irnet_root));
+
+  /* Setup start of irnet instance list */
+  irnet_server.list = hashbin_new(HB_NOLOCK);
+  DABORT(irnet_server.list == NULL, -ENOMEM,
+        MODULE_ERROR, "Can't allocate hashbin!\n");
+  /* Init spinlock for instance list */
+  spin_lock_init(&irnet_server.spinlock);
+
+  /* Initialise control channel */
+  init_waitqueue_head(&irnet_events.rwait);
+  irnet_events.index = 0;
+  /* Init spinlock for event logging */
+  spin_lock_init(&irnet_events.spinlock);
+
+#ifdef CONFIG_PROC_FS
+  /* Add a /proc file for irnet infos */
+  proc_create("irnet", 0, proc_irda, &irnet_proc_fops);
+#endif /* CONFIG_PROC_FS */
+
+  /* Setup the IrNET server */
+  err = irnet_setup_server();
+
+  if(!err)
+    /* We are no longer functional... */
+    irnet_server.running = 1;
+
+  DEXIT(MODULE_TRACE, "\n");
+  return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Cleanup at exit...
+ */
+void __exit
+irda_irnet_cleanup(void)
+{
+  DENTER(MODULE_TRACE, "()\n");
+
+  /* We are no longer there... */
+  irnet_server.running = 0;
+
+#ifdef CONFIG_PROC_FS
+  /* Remove our /proc file */
+  remove_proc_entry("irnet", proc_irda);
+#endif /* CONFIG_PROC_FS */
+
+  /* Remove our IrNET server from existence */
+  irnet_destroy_server();
+
+  /* Remove all instances of IrNET socket still present */
+  hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy);
+
+  DEXIT(MODULE_TRACE, "\n");
+}
diff --git a/drivers/staging/irda/net/irnet/irnet_irda.h b/drivers/staging/irda/net/irnet/irnet_irda.h
new file mode 100644 (file)
index 0000000..3e40895
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ *     IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ *             Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file contains all definitions and declarations necessary for the
+ * IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co).
+ * This file is a private header, so other modules don't want to know
+ * what's in there...
+ */
+
+#ifndef IRNET_IRDA_H
+#define IRNET_IRDA_H
+
+/***************************** INCLUDES *****************************/
+/* Please add other headers in irnet.h */
+
+#include "irnet.h"             /* Module global include */
+
+/************************ CONSTANTS & MACROS ************************/
+
+/*
+ * Name of the service (socket name) used by IrNET
+ */
+/* IAS object name (or part of it) */
+#define IRNET_SERVICE_NAME     "IrNetv1"
+/* IAS attribute */
+#define IRNET_IAS_VALUE                "IrDA:TinyTP:LsapSel"
+/* LMP notify name for client (only for /proc/net/irda/irlmp) */
+#define IRNET_NOTIFY_NAME      "IrNET socket"
+/* LMP notify name for server (only for /proc/net/irda/irlmp) */
+#define IRNET_NOTIFY_NAME_SERV "IrNET server"
+
+/****************************** TYPES ******************************/
+
+/*
+ * This is the main structure where we store all the data pertaining to
+ * the IrNET server (listen for connection requests) and the root
+ * of the IrNET socket list
+ */
+typedef struct irnet_root
+{
+  irnet_socket         s;              /* To pretend we are a client... */
+
+  /* Generic stuff */
+  int                  magic;          /* Paranoia */
+  int                  running;        /* Are we operational ? */
+
+  /* Link list of all IrNET instances opened */
+  hashbin_t *          list;
+  spinlock_t           spinlock;       /* Serialize access to the list */
+  /* Note : the way hashbin has been designed is absolutely not
+   * reentrant, beware... So, we blindly protect all with spinlock */
+
+  /* Handle for the hint bit advertised in IrLMP */
+  void *               skey;
+
+  /* Server socket part */
+  struct ias_object *  ias_obj;        /* Our service name + lsap in IAS */
+
+} irnet_root;
+
+
+/**************************** PROTOTYPES ****************************/
+
+/* ----------------------- CONTROL CHANNEL ----------------------- */
+static void
+       irnet_post_event(irnet_socket *,
+                        irnet_event,
+                        __u32,
+                        __u32,
+                        char *,
+                        __u16);
+/* ----------------------- IRDA SUBROUTINES ----------------------- */
+static inline int
+       irnet_open_tsap(irnet_socket *);
+static inline __u8
+       irnet_ias_to_tsap(irnet_socket *,
+                         int,
+                         struct ias_value *);
+static inline int
+       irnet_find_lsap_sel(irnet_socket *);
+static inline int
+       irnet_connect_tsap(irnet_socket *);
+static inline int
+       irnet_discover_next_daddr(irnet_socket *);
+static inline int
+       irnet_discover_daddr_and_lsap_sel(irnet_socket *);
+static inline int
+       irnet_dname_to_daddr(irnet_socket *);
+/* ------------------------ SERVER SOCKET ------------------------ */
+static inline int
+       irnet_daddr_to_dname(irnet_socket *);
+static inline irnet_socket *
+       irnet_find_socket(irnet_socket *);
+static inline int
+       irnet_connect_socket(irnet_socket *,
+                            irnet_socket *,
+                            struct qos_info *,
+                            __u32,
+                            __u8);
+static inline void
+       irnet_disconnect_server(irnet_socket *,
+                               struct sk_buff *);
+static inline int
+       irnet_setup_server(void);
+static inline void
+       irnet_destroy_server(void);
+/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */
+static int
+       irnet_data_indication(void *,           /* instance */
+                             void *,           /* sap */
+                             struct sk_buff *);
+static void
+       irnet_disconnect_indication(void *,
+                                   void *,
+                                   LM_REASON,
+                                   struct sk_buff *);
+static void
+       irnet_connect_confirm(void *,
+                             void *,
+                             struct qos_info *,
+                             __u32,
+                             __u8,
+                             struct sk_buff *);
+static void
+       irnet_flow_indication(void *,
+                             void *,
+                             LOCAL_FLOW);
+static void
+       irnet_status_indication(void *,
+                               LINK_STATUS,
+                               LOCK_STATUS);
+static void
+       irnet_connect_indication(void *,
+                                void *,
+                                struct qos_info *,
+                                __u32,
+                                __u8,
+                                struct sk_buff *);
+/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */
+static void
+       irnet_getvalue_confirm(int,
+                              __u16,
+                              struct ias_value *,
+                              void *);
+static void
+       irnet_discovervalue_confirm(int,
+                                   __u16,
+                                   struct ias_value *,
+                                   void *);
+#ifdef DISCOVERY_EVENTS
+static void
+       irnet_discovery_indication(discinfo_t *,
+                                  DISCOVERY_MODE,
+                                  void *);
+static void
+       irnet_expiry_indication(discinfo_t *,
+                               DISCOVERY_MODE,
+                               void *);
+#endif
+
+/**************************** VARIABLES ****************************/
+
+/*
+ * The IrNET server. Listen to connection requests and co...
+ */
+static struct irnet_root       irnet_server;
+
+/* Control channel stuff (note : extern) */
+struct irnet_ctrl_channel      irnet_events;
+
+/* The /proc/net/irda directory, defined elsewhere... */
+#ifdef CONFIG_PROC_FS
+extern struct proc_dir_entry *proc_irda;
+#endif /* CONFIG_PROC_FS */
+
+#endif /* IRNET_IRDA_H */
diff --git a/drivers/staging/irda/net/irnet/irnet_ppp.c b/drivers/staging/irda/net/irnet/irnet_ppp.c
new file mode 100644 (file)
index 0000000..7025dcb
--- /dev/null
@@ -0,0 +1,1189 @@
+/*
+ *     IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ *             Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file implement the PPP interface and /dev/irnet character device.
+ * The PPP interface hook to the ppp_generic module, handle all our
+ *     relationship to the PPP code in the kernel (and by extension to pppd),
+ *     and exchange PPP frames with this module (send/receive).
+ * The /dev/irnet device is used primarily for 2 functions :
+ *     1) as a stub for pppd (the ppp daemon), so that we can appropriately
+ *     generate PPP sessions (we pretend we are a tty).
+ *     2) as a control channel (write commands, read events)
+ */
+
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+
+#include "irnet_ppp.h"         /* Private header */
+/* Please put other headers in irnet.h - Thanks */
+
+/* Generic PPP callbacks (to call us) */
+static const struct ppp_channel_ops irnet_ppp_ops = {
+       .start_xmit = ppp_irnet_send,
+       .ioctl = ppp_irnet_ioctl
+};
+
+/************************* CONTROL CHANNEL *************************/
+/*
+ * When a pppd instance is not active on /dev/irnet, it acts as a control
+ * channel.
+ * Writing allow to set up the IrDA destination of the IrNET channel,
+ * and any application may be read events happening in IrNET...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write is used to send a command to configure a IrNET channel
+ * before it is open by pppd. The syntax is : "command argument"
+ * Currently there is only two defined commands :
+ *     o name : set the requested IrDA nickname of the IrNET peer.
+ *     o addr : set the requested IrDA address of the IrNET peer.
+ * Note : the code is crude, but effective...
+ */
+static inline ssize_t
+irnet_ctrl_write(irnet_socket *        ap,
+                const char __user *buf,
+                size_t         count)
+{
+  char         command[IRNET_MAX_COMMAND];
+  char *       start;          /* Current command being processed */
+  char *       next;           /* Next command to process */
+  int          length;         /* Length of current command */
+
+  DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count);
+
+  /* Check for overflow... */
+  DABORT(count >= IRNET_MAX_COMMAND, -ENOMEM,
+        CTRL_ERROR, "Too much data !!!\n");
+
+  /* Get the data in the driver */
+  if(copy_from_user(command, buf, count))
+    {
+      DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
+      return -EFAULT;
+    }
+
+  /* Safe terminate the string */
+  command[count] = '\0';
+  DEBUG(CTRL_INFO, "Command line received is ``%s'' (%zd).\n",
+       command, count);
+
+  /* Check every commands in the command line */
+  next = command;
+  while(next != NULL)
+    {
+      /* Look at the next command */
+      start = next;
+
+       /* Scrap whitespaces before the command */
+       start = skip_spaces(start);
+
+      /* ',' is our command separator */
+      next = strchr(start, ',');
+      if(next)
+       {
+         *next = '\0';                 /* Terminate command */
+         length = next - start;        /* Length */
+         next++;                       /* Skip the '\0' */
+       }
+      else
+       length = strlen(start);
+
+      DEBUG(CTRL_INFO, "Found command ``%s'' (%d).\n", start, length);
+
+      /* Check if we recognised one of the known command
+       * We can't use "switch" with strings, so hack with "continue" */
+
+      /* First command : name -> Requested IrDA nickname */
+      if(!strncmp(start, "name", 4))
+       {
+         /* Copy the name only if is included and not "any" */
+         if((length > 5) && (strcmp(start + 5, "any")))
+           {
+             /* Strip out trailing whitespaces */
+             while(isspace(start[length - 1]))
+               length--;
+
+             DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5,
+                    -EINVAL, CTRL_ERROR, "Invalid nickname.\n");
+
+             /* Copy the name for later reuse */
+             memcpy(ap->rname, start + 5, length - 5);
+             ap->rname[length - 5] = '\0';
+           }
+         else
+           ap->rname[0] = '\0';
+         DEBUG(CTRL_INFO, "Got rname = ``%s''\n", ap->rname);
+
+         /* Restart the loop */
+         continue;
+       }
+
+      /* Second command : addr, daddr -> Requested IrDA destination address
+       * Also process : saddr -> Requested IrDA source address */
+      if((!strncmp(start, "addr", 4)) ||
+        (!strncmp(start, "daddr", 5)) ||
+        (!strncmp(start, "saddr", 5)))
+       {
+         __u32         addr = DEV_ADDR_ANY;
+
+         /* Copy the address only if is included and not "any" */
+         if((length > 5) && (strcmp(start + 5, "any")))
+           {
+             char *    begp = start + 5;
+             char *    endp;
+
+             /* Scrap whitespaces before the command */
+             begp = skip_spaces(begp);
+
+             /* Convert argument to a number (last arg is the base) */
+             addr = simple_strtoul(begp, &endp, 16);
+             /* Has it worked  ? (endp should be start + length) */
+             DABORT(endp <= (start + 5), -EINVAL,
+                    CTRL_ERROR, "Invalid address.\n");
+           }
+         /* Which type of address ? */
+         if(start[0] == 's')
+           {
+             /* Save it */
+             ap->rsaddr = addr;
+             DEBUG(CTRL_INFO, "Got rsaddr = %08x\n", ap->rsaddr);
+           }
+         else
+           {
+             /* Save it */
+             ap->rdaddr = addr;
+             DEBUG(CTRL_INFO, "Got rdaddr = %08x\n", ap->rdaddr);
+           }
+
+         /* Restart the loop */
+         continue;
+       }
+
+      /* Other possible command : connect N (number of retries) */
+
+      /* No command matched -> Failed... */
+      DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n");
+    }
+
+  /* Success : we have parsed all commands successfully */
+  return count;
+}
+
+#ifdef INITIAL_DISCOVERY
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_get_discovery_log (self)
+ *
+ *    Query the content on the discovery log if not done
+ *
+ * This function query the current content of the discovery log
+ * at the startup of the event channel and save it in the internal struct.
+ */
+static void
+irnet_get_discovery_log(irnet_socket * ap)
+{
+  __u16                mask = irlmp_service_to_hint(S_LAN);
+
+  /* Ask IrLMP for the current discovery log */
+  ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask,
+                                         DISCOVERY_DEFAULT_SLOTS);
+
+  /* Check if the we got some results */
+  if(ap->discoveries == NULL)
+    ap->disco_number = -1;
+
+  DEBUG(CTRL_INFO, "Got the log (0x%p), size is %d\n",
+       ap->discoveries, ap->disco_number);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Function irnet_read_discovery_log (self, event)
+ *
+ *    Read the content on the discovery log
+ *
+ * This function dump the current content of the discovery log
+ * at the startup of the event channel.
+ * Return 1 if wrote an event on the control channel...
+ *
+ * State of the ap->disco_XXX variables :
+ * Socket creation :  discoveries = NULL ; disco_index = 0 ; disco_number = 0
+ * While reading :    discoveries = ptr  ; disco_index = X ; disco_number = Y
+ * After reading :    discoveries = NULL ; disco_index = Y ; disco_number = -1
+ */
+static inline int
+irnet_read_discovery_log(irnet_socket *ap, char *event, int buf_size)
+{
+  int          done_event = 0;
+
+  DENTER(CTRL_TRACE, "(ap=0x%p, event=0x%p)\n",
+        ap, event);
+
+  /* Test if we have some work to do or we have already finished */
+  if(ap->disco_number == -1)
+    {
+      DEBUG(CTRL_INFO, "Already done\n");
+      return 0;
+    }
+
+  /* Test if it's the first time and therefore we need to get the log */
+  if(ap->discoveries == NULL)
+    irnet_get_discovery_log(ap);
+
+  /* Check if we have more item to dump */
+  if(ap->disco_index < ap->disco_number)
+    {
+      /* Write an event */
+      snprintf(event, buf_size,
+              "Found %08x (%s) behind %08x {hints %02X-%02X}\n",
+              ap->discoveries[ap->disco_index].daddr,
+              ap->discoveries[ap->disco_index].info,
+              ap->discoveries[ap->disco_index].saddr,
+              ap->discoveries[ap->disco_index].hints[0],
+              ap->discoveries[ap->disco_index].hints[1]);
+      DEBUG(CTRL_INFO, "Writing discovery %d : %s\n",
+           ap->disco_index, ap->discoveries[ap->disco_index].info);
+
+      /* We have an event */
+      done_event = 1;
+      /* Next discovery */
+      ap->disco_index++;
+    }
+
+  /* Check if we have done the last item */
+  if(ap->disco_index >= ap->disco_number)
+    {
+      /* No more items : remove the log and signal termination */
+      DEBUG(CTRL_INFO, "Cleaning up log (0x%p)\n",
+           ap->discoveries);
+      if(ap->discoveries != NULL)
+       {
+         /* Cleanup our copy of the discovery log */
+         kfree(ap->discoveries);
+         ap->discoveries = NULL;
+       }
+      ap->disco_number = -1;
+    }
+
+  return done_event;
+}
+#endif /* INITIAL_DISCOVERY */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read is used to get IrNET events
+ */
+static inline ssize_t
+irnet_ctrl_read(irnet_socket * ap,
+               struct file *   file,
+               char __user *   buf,
+               size_t          count)
+{
+  DECLARE_WAITQUEUE(wait, current);
+  char         event[75];
+  ssize_t      ret = 0;
+
+  DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count);
+
+#ifdef INITIAL_DISCOVERY
+  /* Check if we have read the log */
+  if (irnet_read_discovery_log(ap, event, sizeof(event)))
+    {
+      count = min(strlen(event), count);
+      if (copy_to_user(buf, event, count))
+       {
+         DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
+         return -EFAULT;
+       }
+
+      DEXIT(CTRL_TRACE, "\n");
+      return count;
+    }
+#endif /* INITIAL_DISCOVERY */
+
+  /* Put ourselves on the wait queue to be woken up */
+  add_wait_queue(&irnet_events.rwait, &wait);
+  set_current_state(TASK_INTERRUPTIBLE);
+  for(;;)
+    {
+      /* If there is unread events */
+      ret = 0;
+      if(ap->event_index != irnet_events.index)
+       break;
+      ret = -EAGAIN;
+      if(file->f_flags & O_NONBLOCK)
+       break;
+      ret = -ERESTARTSYS;
+      if(signal_pending(current))
+       break;
+      /* Yield and wait to be woken up */
+      schedule();
+    }
+  __set_current_state(TASK_RUNNING);
+  remove_wait_queue(&irnet_events.rwait, &wait);
+
+  /* Did we got it ? */
+  if(ret != 0)
+    {
+      /* No, return the error code */
+      DEXIT(CTRL_TRACE, " - ret %zd\n", ret);
+      return ret;
+    }
+
+  /* Which event is it ? */
+  switch(irnet_events.log[ap->event_index].event)
+    {
+    case IRNET_DISCOVER:
+      snprintf(event, sizeof(event),
+              "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name,
+              irnet_events.log[ap->event_index].saddr,
+              irnet_events.log[ap->event_index].hints.byte[0],
+              irnet_events.log[ap->event_index].hints.byte[1]);
+      break;
+    case IRNET_EXPIRE:
+      snprintf(event, sizeof(event),
+              "Expired %08x (%s) behind %08x {hints %02X-%02X}\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name,
+              irnet_events.log[ap->event_index].saddr,
+              irnet_events.log[ap->event_index].hints.byte[0],
+              irnet_events.log[ap->event_index].hints.byte[1]);
+      break;
+    case IRNET_CONNECT_TO:
+      snprintf(event, sizeof(event), "Connected to %08x (%s) on ppp%d\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name,
+              irnet_events.log[ap->event_index].unit);
+      break;
+    case IRNET_CONNECT_FROM:
+      snprintf(event, sizeof(event), "Connection from %08x (%s) on ppp%d\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name,
+              irnet_events.log[ap->event_index].unit);
+      break;
+    case IRNET_REQUEST_FROM:
+      snprintf(event, sizeof(event), "Request from %08x (%s) behind %08x\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name,
+              irnet_events.log[ap->event_index].saddr);
+      break;
+    case IRNET_NOANSWER_FROM:
+      snprintf(event, sizeof(event), "No-answer from %08x (%s) on ppp%d\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name,
+              irnet_events.log[ap->event_index].unit);
+      break;
+    case IRNET_BLOCKED_LINK:
+      snprintf(event, sizeof(event), "Blocked link with %08x (%s) on ppp%d\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name,
+              irnet_events.log[ap->event_index].unit);
+      break;
+    case IRNET_DISCONNECT_FROM:
+      snprintf(event, sizeof(event), "Disconnection from %08x (%s) on ppp%d\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name,
+              irnet_events.log[ap->event_index].unit);
+      break;
+    case IRNET_DISCONNECT_TO:
+      snprintf(event, sizeof(event), "Disconnected to %08x (%s)\n",
+              irnet_events.log[ap->event_index].daddr,
+              irnet_events.log[ap->event_index].name);
+      break;
+    default:
+      snprintf(event, sizeof(event), "Bug\n");
+    }
+  /* Increment our event index */
+  ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS;
+
+  DEBUG(CTRL_INFO, "Event is :%s", event);
+
+  count = min(strlen(event), count);
+  if (copy_to_user(buf, event, count))
+    {
+      DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
+      return -EFAULT;
+    }
+
+  DEXIT(CTRL_TRACE, "\n");
+  return count;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Poll : called when someone do a select on /dev/irnet.
+ * Just check if there are new events...
+ */
+static inline unsigned int
+irnet_ctrl_poll(irnet_socket * ap,
+               struct file *   file,
+               poll_table *    wait)
+{
+  unsigned int mask;
+
+  DENTER(CTRL_TRACE, "(ap=0x%p)\n", ap);
+
+  poll_wait(file, &irnet_events.rwait, wait);
+  mask = POLLOUT | POLLWRNORM;
+  /* If there is unread events */
+  if(ap->event_index != irnet_events.index)
+    mask |= POLLIN | POLLRDNORM;
+#ifdef INITIAL_DISCOVERY
+  if(ap->disco_number != -1)
+    {
+      /* Test if it's the first time and therefore we need to get the log */
+      if(ap->discoveries == NULL)
+       irnet_get_discovery_log(ap);
+      /* Recheck */
+      if(ap->disco_number != -1)
+       mask |= POLLIN | POLLRDNORM;
+    }
+#endif /* INITIAL_DISCOVERY */
+
+  DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask);
+  return mask;
+}
+
+
+/*********************** FILESYSTEM CALLBACKS ***********************/
+/*
+ * Implement the usual open, read, write functions that will be called
+ * by the file system when some action is performed on /dev/irnet.
+ * Most of those actions will in fact be performed by "pppd" or
+ * the control channel, we just act as a redirector...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Open : when somebody open /dev/irnet
+ * We basically create a new instance of irnet and initialise it.
+ */
+static int
+dev_irnet_open(struct inode *  inode,
+              struct file *    file)
+{
+  struct irnet_socket *        ap;
+  int                  err;
+
+  DENTER(FS_TRACE, "(file=0x%p)\n", file);
+
+#ifdef SECURE_DEVIRNET
+  /* This could (should?) be enforced by the permissions on /dev/irnet. */
+  if(!capable(CAP_NET_ADMIN))
+    return -EPERM;
+#endif /* SECURE_DEVIRNET */
+
+  /* Allocate a private structure for this IrNET instance */
+  ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+  DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n");
+
+  /* initialize the irnet structure */
+  ap->file = file;
+
+  /* PPP channel setup */
+  ap->ppp_open = 0;
+  ap->chan.private = ap;
+  ap->chan.ops = &irnet_ppp_ops;
+  ap->chan.mtu = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN);
+  ap->chan.hdrlen = 2 + TTP_MAX_HEADER;                /* for A/C + Max IrDA hdr */
+  /* PPP parameters */
+  ap->mru = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN);
+  ap->xaccm[0] = ~0U;
+  ap->xaccm[3] = 0x60000000U;
+  ap->raccm = ~0U;
+
+  /* Setup the IrDA part... */
+  err = irda_irnet_create(ap);
+  if(err)
+    {
+      DERROR(FS_ERROR, "Can't setup IrDA link...\n");
+      kfree(ap);
+
+      return err;
+    }
+
+  /* For the control channel */
+  ap->event_index = irnet_events.index;        /* Cancel all past events */
+
+  mutex_init(&ap->lock);
+
+  /* Put our stuff where we will be able to find it later */
+  file->private_data = ap;
+
+  DEXIT(FS_TRACE, " - ap=0x%p\n", ap);
+
+  return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Close : when somebody close /dev/irnet
+ * Destroy the instance of /dev/irnet
+ */
+static int
+dev_irnet_close(struct inode * inode,
+               struct file *   file)
+{
+  irnet_socket *       ap = file->private_data;
+
+  DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n",
+        file, ap);
+  DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n");
+
+  /* Detach ourselves */
+  file->private_data = NULL;
+
+  /* Close IrDA stuff */
+  irda_irnet_destroy(ap);
+
+  /* Disconnect from the generic PPP layer if not already done */
+  if(ap->ppp_open)
+    {
+      DERROR(FS_ERROR, "Channel still registered - deregistering !\n");
+      ap->ppp_open = 0;
+      ppp_unregister_channel(&ap->chan);
+    }
+
+  kfree(ap);
+
+  DEXIT(FS_TRACE, "\n");
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Write does nothing.
+ * (we receive packet from ppp_generic through ppp_irnet_send())
+ */
+static ssize_t
+dev_irnet_write(struct file *  file,
+               const char __user *buf,
+               size_t          count,
+               loff_t *        ppos)
+{
+  irnet_socket *       ap = file->private_data;
+
+  DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n",
+       file, ap, count);
+  DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n");
+
+  /* If we are connected to ppp_generic, let it handle the job */
+  if(ap->ppp_open)
+    return -EAGAIN;
+  else
+    return irnet_ctrl_write(ap, buf, count);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read doesn't do much either.
+ * (pppd poll us, but ultimately reads through /dev/ppp)
+ */
+static ssize_t
+dev_irnet_read(struct file *   file,
+              char __user *    buf,
+              size_t           count,
+              loff_t *         ppos)
+{
+  irnet_socket *       ap = file->private_data;
+
+  DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n",
+       file, ap, count);
+  DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n");
+
+  /* If we are connected to ppp_generic, let it handle the job */
+  if(ap->ppp_open)
+    return -EAGAIN;
+  else
+    return irnet_ctrl_read(ap, file, buf, count);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Poll : called when someone do a select on /dev/irnet
+ */
+static unsigned int
+dev_irnet_poll(struct file *   file,
+              poll_table *     wait)
+{
+  irnet_socket *       ap = file->private_data;
+  unsigned int         mask;
+
+  DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n",
+        file, ap);
+
+  mask = POLLOUT | POLLWRNORM;
+  DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n");
+
+  /* If we are connected to ppp_generic, let it handle the job */
+  if(!ap->ppp_open)
+    mask |= irnet_ctrl_poll(ap, file, wait);
+
+  DEXIT(FS_TRACE, " - mask=0x%X\n", mask);
+  return mask;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * IOCtl : Called when someone does some ioctls on /dev/irnet
+ * This is the way pppd configure us and control us while the PPP
+ * instance is active.
+ */
+static long
+dev_irnet_ioctl(
+               struct file *   file,
+               unsigned int    cmd,
+               unsigned long   arg)
+{
+  irnet_socket *       ap = file->private_data;
+  int                  err;
+  int                  val;
+  void __user *argp = (void __user *)arg;
+
+  DENTER(FS_TRACE, "(file=0x%p, ap=0x%p, cmd=0x%X)\n",
+        file, ap, cmd);
+
+  /* Basic checks... */
+  DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");
+#ifdef SECURE_DEVIRNET
+  if(!capable(CAP_NET_ADMIN))
+    return -EPERM;
+#endif /* SECURE_DEVIRNET */
+
+  err = -EFAULT;
+  switch(cmd)
+    {
+      /* Set discipline (should be N_SYNC_PPP or N_TTY) */
+    case TIOCSETD:
+      if(get_user(val, (int __user *)argp))
+       break;
+      if((val == N_SYNC_PPP) || (val == N_PPP))
+       {
+         DEBUG(FS_INFO, "Entering PPP discipline.\n");
+         /* PPP channel setup (ap->chan in configured in dev_irnet_open())*/
+         if (mutex_lock_interruptible(&ap->lock))
+                 return -EINTR;
+
+         err = ppp_register_channel(&ap->chan);
+         if(err == 0)
+           {
+             /* Our ppp side is active */
+             ap->ppp_open = 1;
+
+             DEBUG(FS_INFO, "Trying to establish a connection.\n");
+             /* Setup the IrDA link now - may fail... */
+             irda_irnet_connect(ap);
+           }
+         else
+           DERROR(FS_ERROR, "Can't setup PPP channel...\n");
+
+          mutex_unlock(&ap->lock);
+       }
+      else
+       {
+         /* In theory, should be N_TTY */
+         DEBUG(FS_INFO, "Exiting PPP discipline.\n");
+         /* Disconnect from the generic PPP layer */
+         if (mutex_lock_interruptible(&ap->lock))
+                 return -EINTR;
+
+         if(ap->ppp_open)
+           {
+             ap->ppp_open = 0;
+             ppp_unregister_channel(&ap->chan);
+           }
+         else
+           DERROR(FS_ERROR, "Channel not registered !\n");
+         err = 0;
+
+         mutex_unlock(&ap->lock);
+       }
+      break;
+
+      /* Query PPP channel and unit number */
+    case PPPIOCGCHAN:
+      if (mutex_lock_interruptible(&ap->lock))
+             return -EINTR;
+
+      if(ap->ppp_open && !put_user(ppp_channel_index(&ap->chan),
+                                               (int __user *)argp))
+       err = 0;
+
+      mutex_unlock(&ap->lock);
+      break;
+    case PPPIOCGUNIT:
+      if (mutex_lock_interruptible(&ap->lock))
+             return -EINTR;
+
+      if(ap->ppp_open && !put_user(ppp_unit_number(&ap->chan),
+                                               (int __user *)argp))
+        err = 0;
+
+      mutex_unlock(&ap->lock);
+      break;
+
+      /* All these ioctls can be passed both directly and from ppp_generic,
+       * so we just deal with them in one place...
+       */
+    case PPPIOCGFLAGS:
+    case PPPIOCSFLAGS:
+    case PPPIOCGASYNCMAP:
+    case PPPIOCSASYNCMAP:
+    case PPPIOCGRASYNCMAP:
+    case PPPIOCSRASYNCMAP:
+    case PPPIOCGXASYNCMAP:
+    case PPPIOCSXASYNCMAP:
+    case PPPIOCGMRU:
+    case PPPIOCSMRU:
+      DEBUG(FS_INFO, "Standard PPP ioctl.\n");
+      if(!capable(CAP_NET_ADMIN))
+       err = -EPERM;
+      else {
+       if (mutex_lock_interruptible(&ap->lock))
+             return -EINTR;
+
+       err = ppp_irnet_ioctl(&ap->chan, cmd, arg);
+
+       mutex_unlock(&ap->lock);
+      }
+      break;
+
+      /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */
+      /* Get termios */
+    case TCGETS:
+      DEBUG(FS_INFO, "Get termios.\n");
+      if (mutex_lock_interruptible(&ap->lock))
+             return -EINTR;
+
+#ifndef TCGETS2
+      if(!kernel_termios_to_user_termios((struct termios __user *)argp, &ap->termios))
+       err = 0;
+#else
+      if(kernel_termios_to_user_termios_1((struct termios __user *)argp, &ap->termios))
+       err = 0;
+#endif
+
+      mutex_unlock(&ap->lock);
+      break;
+      /* Set termios */
+    case TCSETSF:
+      DEBUG(FS_INFO, "Set termios.\n");
+      if (mutex_lock_interruptible(&ap->lock))
+             return -EINTR;
+
+#ifndef TCGETS2
+      if(!user_termios_to_kernel_termios(&ap->termios, (struct termios __user *)argp))
+       err = 0;
+#else
+      if(!user_termios_to_kernel_termios_1(&ap->termios, (struct termios __user *)argp))
+       err = 0;
+#endif
+
+      mutex_unlock(&ap->lock);
+      break;
+
+      /* Set DTR/RTS */
+    case TIOCMBIS:
+    case TIOCMBIC:
+      /* Set exclusive/non-exclusive mode */
+    case TIOCEXCL:
+    case TIOCNXCL:
+      DEBUG(FS_INFO, "TTY compatibility.\n");
+      err = 0;
+      break;
+
+    case TCGETA:
+      DEBUG(FS_INFO, "TCGETA\n");
+      break;
+
+    case TCFLSH:
+      DEBUG(FS_INFO, "TCFLSH\n");
+      /* Note : this will flush buffers in PPP, so it *must* be done
+       * We should also worry that we don't accept junk here and that
+       * we get rid of our own buffers */
+#ifdef FLUSH_TO_PPP
+      if (mutex_lock_interruptible(&ap->lock))
+             return -EINTR;
+      ppp_output_wakeup(&ap->chan);
+      mutex_unlock(&ap->lock);
+#endif /* FLUSH_TO_PPP */
+      err = 0;
+      break;
+
+    case FIONREAD:
+      DEBUG(FS_INFO, "FIONREAD\n");
+      val = 0;
+      if(put_user(val, (int __user *)argp))
+       break;
+      err = 0;
+      break;
+
+    default:
+      DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd);
+      err = -ENOTTY;
+    }
+
+  DEXIT(FS_TRACE, " - err = 0x%X\n", err);
+  return err;
+}
+
+/************************** PPP CALLBACKS **************************/
+/*
+ * This are the functions that the generic PPP driver in the kernel
+ * will call to communicate to us.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare the ppp frame for transmission over the IrDA socket.
+ * We make sure that the header space is enough, and we change ppp header
+ * according to flags passed by pppd.
+ * This is not a callback, but just a helper function used in ppp_irnet_send()
+ */
+static inline struct sk_buff *
+irnet_prepare_skb(irnet_socket *       ap,
+                 struct sk_buff *      skb)
+{
+  unsigned char *      data;
+  int                  proto;          /* PPP protocol */
+  int                  islcp;          /* Protocol == LCP */
+  int                  needaddr;       /* Need PPP address */
+
+  DENTER(PPP_TRACE, "(ap=0x%p, skb=0x%p)\n",
+        ap, skb);
+
+  /* Extract PPP protocol from the frame */
+  data  = skb->data;
+  proto = (data[0] << 8) + data[1];
+
+  /* LCP packets with codes between 1 (configure-request)
+   * and 7 (code-reject) must be sent as though no options
+   * have been negotiated. */
+  islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7);
+
+  /* compress protocol field if option enabled */
+  if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp))
+    skb_pull(skb,1);
+
+  /* Check if we need address/control fields */
+  needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp);
+
+  /* Is the skb headroom large enough to contain all IrDA-headers? */
+  if((skb_headroom(skb) < (ap->max_header_size + needaddr)) ||
+      (skb_shared(skb)))
+    {
+      struct sk_buff * new_skb;
+
+      DEBUG(PPP_INFO, "Reallocating skb\n");
+
+      /* Create a new skb */
+      new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr);
+
+      /* We have to free the original skb anyway */
+      dev_kfree_skb(skb);
+
+      /* Did the realloc succeed ? */
+      DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n");
+
+      /* Use the new skb instead */
+      skb = new_skb;
+    }
+
+  /* prepend address/control fields if necessary */
+  if(needaddr)
+    {
+      skb_push(skb, 2);
+      skb->data[0] = PPP_ALLSTATIONS;
+      skb->data[1] = PPP_UI;
+    }
+
+  DEXIT(PPP_TRACE, "\n");
+
+  return skb;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Send a packet to the peer over the IrTTP connection.
+ * Returns 1 iff the packet was accepted.
+ * Returns 0 iff packet was not consumed.
+ * If the packet was not accepted, we will call ppp_output_wakeup
+ * at some later time to reactivate flow control in ppp_generic.
+ */
+static int
+ppp_irnet_send(struct ppp_channel *    chan,
+              struct sk_buff *         skb)
+{
+  irnet_socket *       self = (struct irnet_socket *) chan->private;
+  int                  ret;
+
+  DENTER(PPP_TRACE, "(channel=0x%p, ap/self=0x%p)\n",
+        chan, self);
+
+  /* Check if things are somewhat valid... */
+  DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n");
+
+  /* Check if we are connected */
+  if(!(test_bit(0, &self->ttp_open)))
+    {
+#ifdef CONNECT_IN_SEND
+      /* Let's try to connect one more time... */
+      /* Note : we won't be connected after this call, but we should be
+       * ready for next packet... */
+      /* If we are already connecting, this will fail */
+      irda_irnet_connect(self);
+#endif /* CONNECT_IN_SEND */
+
+      DEBUG(PPP_INFO, "IrTTP not ready ! (%ld-%ld)\n",
+           self->ttp_open, self->ttp_connect);
+
+      /* Note : we can either drop the packet or block the packet.
+       *
+       * Blocking the packet allow us a better connection time,
+       * because by calling ppp_output_wakeup() we can have
+       * ppp_generic resending the LCP request immediately to us,
+       * rather than waiting for one of pppd periodic transmission of
+       * LCP request.
+       *
+       * On the other hand, if we block all packet, all those periodic
+       * transmissions of pppd accumulate in ppp_generic, creating a
+       * backlog of LCP request. When we eventually connect later on,
+       * we have to transmit all this backlog before we can connect
+       * proper (if we don't timeout before).
+       *
+       * The current strategy is as follow :
+       * While we are attempting to connect, we block packets to get
+       * a better connection time.
+       * If we fail to connect, we drain the queue and start dropping packets
+       */
+#ifdef BLOCK_WHEN_CONNECT
+      /* If we are attempting to connect */
+      if(test_bit(0, &self->ttp_connect))
+       {
+         /* Blocking packet, ppp_generic will retry later */
+         return 0;
+       }
+#endif /* BLOCK_WHEN_CONNECT */
+
+      /* Dropping packet, pppd will retry later */
+      dev_kfree_skb(skb);
+      return 1;
+    }
+
+  /* Check if the queue can accept any packet, otherwise block */
+  if(self->tx_flow != FLOW_START)
+    DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n",
+           skb_queue_len(&self->tsap->tx_queue));
+
+  /* Prepare ppp frame for transmission */
+  skb = irnet_prepare_skb(self, skb);
+  DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n");
+
+  /* Send the packet to IrTTP */
+  ret = irttp_data_request(self->tsap, skb);
+  if(ret < 0)
+    {
+      /*
+       * > IrTTPs tx queue is full, so we just have to
+       * > drop the frame! You might think that we should
+       * > just return -1 and don't deallocate the frame,
+       * > but that is dangerous since it's possible that
+       * > we have replaced the original skb with a new
+       * > one with larger headroom, and that would really
+       * > confuse do_dev_queue_xmit() in dev.c! I have
+       * > tried :-) DB
+       * Correction : we verify the flow control above (self->tx_flow),
+       * so we come here only if IrTTP doesn't like the packet (empty,
+       * too large, IrTTP not connected). In those rare cases, it's ok
+       * to drop it, we don't want to see it here again...
+       * Jean II
+       */
+      DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret);
+      /* irttp_data_request already free the packet */
+    }
+
+  DEXIT(PPP_TRACE, "\n");
+  return 1;    /* Packet has been consumed */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Take care of the ioctls that ppp_generic doesn't want to deal with...
+ * Note : we are also called from dev_irnet_ioctl().
+ */
+static int
+ppp_irnet_ioctl(struct ppp_channel *   chan,
+               unsigned int            cmd,
+               unsigned long           arg)
+{
+  irnet_socket *       ap = (struct irnet_socket *) chan->private;
+  int                  err;
+  int                  val;
+  u32                  accm[8];
+  void __user *argp = (void __user *)arg;
+
+  DENTER(PPP_TRACE, "(channel=0x%p, ap=0x%p, cmd=0x%X)\n",
+        chan, ap, cmd);
+
+  /* Basic checks... */
+  DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");
+
+  err = -EFAULT;
+  switch(cmd)
+    {
+      /* PPP flags */
+    case PPPIOCGFLAGS:
+      val = ap->flags | ap->rbits;
+      if(put_user(val, (int __user *) argp))
+       break;
+      err = 0;
+      break;
+    case PPPIOCSFLAGS:
+      if(get_user(val, (int __user *) argp))
+       break;
+      ap->flags = val & ~SC_RCV_BITS;
+      ap->rbits = val & SC_RCV_BITS;
+      err = 0;
+      break;
+
+      /* Async map stuff - all dummy to please pppd */
+    case PPPIOCGASYNCMAP:
+      if(put_user(ap->xaccm[0], (u32 __user *) argp))
+       break;
+      err = 0;
+      break;
+    case PPPIOCSASYNCMAP:
+      if(get_user(ap->xaccm[0], (u32 __user *) argp))
+       break;
+      err = 0;
+      break;
+    case PPPIOCGRASYNCMAP:
+      if(put_user(ap->raccm, (u32 __user *) argp))
+       break;
+      err = 0;
+      break;
+    case PPPIOCSRASYNCMAP:
+      if(get_user(ap->raccm, (u32 __user *) argp))
+       break;
+      err = 0;
+      break;
+    case PPPIOCGXASYNCMAP:
+      if(copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
+       break;
+      err = 0;
+      break;
+    case PPPIOCSXASYNCMAP:
+      if(copy_from_user(accm, argp, sizeof(accm)))
+       break;
+      accm[2] &= ~0x40000000U;         /* can't escape 0x5e */
+      accm[3] |= 0x60000000U;          /* must escape 0x7d, 0x7e */
+      memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+      err = 0;
+      break;
+
+      /* Max PPP frame size */
+    case PPPIOCGMRU:
+      if(put_user(ap->mru, (int __user *) argp))
+       break;
+      err = 0;
+      break;
+    case PPPIOCSMRU:
+      if(get_user(val, (int __user *) argp))
+       break;
+      if(val < PPP_MRU)
+       val = PPP_MRU;
+      ap->mru = val;
+      err = 0;
+      break;
+
+    default:
+      DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd);
+      err = -ENOIOCTLCMD;
+    }
+
+  DEXIT(PPP_TRACE, " - err = 0x%X\n", err);
+  return err;
+}
+
+/************************** INITIALISATION **************************/
+/*
+ * Module initialisation and all that jazz...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Hook our device callbacks in the filesystem, to connect our code
+ * to /dev/irnet
+ */
+static inline int __init
+ppp_irnet_init(void)
+{
+  int err = 0;
+
+  DENTER(MODULE_TRACE, "()\n");
+
+  /* Allocate ourselves as a minor in the misc range */
+  err = misc_register(&irnet_misc_device);
+
+  DEXIT(MODULE_TRACE, "\n");
+  return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Cleanup at exit...
+ */
+static inline void __exit
+ppp_irnet_cleanup(void)
+{
+  DENTER(MODULE_TRACE, "()\n");
+
+  /* De-allocate /dev/irnet minor in misc range */
+  misc_deregister(&irnet_misc_device);
+
+  DEXIT(MODULE_TRACE, "\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Module main entry point
+ */
+static int __init
+irnet_init(void)
+{
+  int err;
+
+  /* Initialise both parts... */
+  err = irda_irnet_init();
+  if(!err)
+    err = ppp_irnet_init();
+  return err;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Module exit
+ */
+static void __exit
+irnet_cleanup(void)
+{
+  irda_irnet_cleanup();
+  ppp_irnet_cleanup();
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Module magic
+ */
+module_init(irnet_init);
+module_exit(irnet_cleanup);
+MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
+MODULE_DESCRIPTION("IrNET : Synchronous PPP over IrDA");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV(10, 187);
diff --git a/drivers/staging/irda/net/irnet/irnet_ppp.h b/drivers/staging/irda/net/irnet/irnet_ppp.h
new file mode 100644 (file)
index 0000000..3206144
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ *     IrNET protocol module : Synchronous PPP over an IrDA socket.
+ *
+ *             Jean II - HPL `00 - <jt@hpl.hp.com>
+ *
+ * This file contains all definitions and declarations necessary for the
+ * PPP part of the IrNET module.
+ * This file is a private header, so other modules don't want to know
+ * what's in there...
+ */
+
+#ifndef IRNET_PPP_H
+#define IRNET_PPP_H
+
+/***************************** INCLUDES *****************************/
+
+#include "irnet.h"             /* Module global include */
+#include <linux/miscdevice.h>
+
+/************************ CONSTANTS & MACROS ************************/
+
+/* IrNET control channel stuff */
+#define IRNET_MAX_COMMAND      256     /* Max length of a command line */
+
+/* PPP hardcore stuff */
+
+/* Bits in rbits (PPP flags in irnet struct) */
+#define SC_RCV_BITS    (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
+
+/* Bit numbers in busy */
+#define XMIT_BUSY      0
+#define RECV_BUSY      1
+#define XMIT_WAKEUP    2
+#define XMIT_FULL      3
+
+/* Queue management */
+#define PPPSYNC_MAX_RQLEN      32      /* arbitrary */
+
+/****************************** TYPES ******************************/
+
+
+/**************************** PROTOTYPES ****************************/
+
+/* ----------------------- CONTROL CHANNEL ----------------------- */
+static inline ssize_t
+       irnet_ctrl_write(irnet_socket *,
+                        const char *,
+                        size_t);
+static inline ssize_t
+       irnet_ctrl_read(irnet_socket *,
+                       struct file *,
+                       char *,
+                       size_t);
+static inline unsigned int
+       irnet_ctrl_poll(irnet_socket *,
+                       struct file *,
+                       poll_table *);
+/* ----------------------- CHARACTER DEVICE ----------------------- */
+static int
+       dev_irnet_open(struct inode *,  /* fs callback : open */
+                      struct file *),
+       dev_irnet_close(struct inode *,
+                       struct file *);
+static ssize_t
+       dev_irnet_write(struct file *,
+                       const char __user *,
+                       size_t,
+                       loff_t *),
+       dev_irnet_read(struct file *,
+                      char __user *,
+                      size_t,
+                      loff_t *);
+static unsigned int
+       dev_irnet_poll(struct file *,
+                      poll_table *);
+static long
+       dev_irnet_ioctl(struct file *,
+                       unsigned int,
+                       unsigned long);
+/* ------------------------ PPP INTERFACE ------------------------ */
+static inline struct sk_buff *
+       irnet_prepare_skb(irnet_socket *,
+                         struct sk_buff *);
+static int
+       ppp_irnet_send(struct ppp_channel *,
+                     struct sk_buff *);
+static int
+       ppp_irnet_ioctl(struct ppp_channel *,
+                       unsigned int,
+                       unsigned long);
+
+/**************************** VARIABLES ****************************/
+
+/* Filesystem callbacks (to call us) */
+static const struct file_operations irnet_device_fops =
+{
+       .owner          = THIS_MODULE,
+       .read           = dev_irnet_read,
+       .write          = dev_irnet_write,
+       .poll           = dev_irnet_poll,
+       .unlocked_ioctl = dev_irnet_ioctl,
+       .open           = dev_irnet_open,
+       .release        = dev_irnet_close,
+       .llseek         = noop_llseek,
+  /* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */
+};
+
+/* Structure so that the misc major (drivers/char/misc.c) take care of us... */
+static struct miscdevice irnet_misc_device =
+{
+       .minor = IRNET_MINOR,
+       .name = "irnet",
+       .fops = &irnet_device_fops
+};
+
+#endif /* IRNET_PPP_H */
diff --git a/drivers/staging/irda/net/irnetlink.c b/drivers/staging/irda/net/irnetlink.c
new file mode 100644 (file)
index 0000000..7fc340e
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * IrDA netlink layer, for stack configuration.
+ *
+ * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org>
+ *
+ * Partly based on the 802.11 nelink implementation
+ * (see net/wireless/nl80211.c) which is:
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/socket.h>
+#include <linux/irda.h>
+#include <linux/gfp.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+#include <net/irda/irda.h>
+#include <net/irda/irlap.h>
+#include <net/genetlink.h>
+
+
+
+static struct genl_family irda_nl_family;
+
+static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *info)
+{
+       char * ifname;
+
+       if (!info->attrs[IRDA_NL_ATTR_IFNAME])
+               return NULL;
+
+       ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]);
+
+       pr_debug("%s(): Looking for %s\n", __func__, ifname);
+
+       return dev_get_by_name(net, ifname);
+}
+
+static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info)
+{
+       struct net_device * dev;
+       struct irlap_cb * irlap;
+       u32 mode;
+
+       if (!info->attrs[IRDA_NL_ATTR_MODE])
+               return -EINVAL;
+
+       mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]);
+
+       pr_debug("%s(): Switching to mode: %d\n", __func__, mode);
+
+       dev = ifname_to_netdev(&init_net, info);
+       if (!dev)
+               return -ENODEV;
+
+       irlap = (struct irlap_cb *)dev->atalk_ptr;
+       if (!irlap) {
+               dev_put(dev);
+               return -ENODEV;
+       }
+
+       irlap->mode = mode;
+
+       dev_put(dev);
+
+       return 0;
+}
+
+static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info)
+{
+       struct net_device * dev;
+       struct irlap_cb * irlap;
+       struct sk_buff *msg;
+       void *hdr;
+       int ret = -ENOBUFS;
+
+       dev = ifname_to_netdev(&init_net, info);
+       if (!dev)
+               return -ENODEV;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg) {
+               dev_put(dev);
+               return -ENOMEM;
+       }
+
+       irlap = (struct irlap_cb *)dev->atalk_ptr;
+       if (!irlap) {
+               ret = -ENODEV;
+               goto err_out;
+       }
+
+       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
+                         &irda_nl_family, 0,  IRDA_NL_CMD_GET_MODE);
+       if (hdr == NULL) {
+               ret = -EMSGSIZE;
+               goto err_out;
+       }
+
+       if(nla_put_string(msg, IRDA_NL_ATTR_IFNAME,
+                         dev->name))
+               goto err_out;
+
+       if(nla_put_u32(msg, IRDA_NL_ATTR_MODE, irlap->mode))
+               goto err_out;
+
+       genlmsg_end(msg, hdr);
+
+       return genlmsg_reply(msg, info);
+
+ err_out:
+       nlmsg_free(msg);
+       dev_put(dev);
+
+       return ret;
+}
+
+static const struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = {
+       [IRDA_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING,
+                                 .len = IFNAMSIZ-1 },
+       [IRDA_NL_ATTR_MODE] = { .type = NLA_U32 },
+};
+
+static const struct genl_ops irda_nl_ops[] = {
+       {
+               .cmd = IRDA_NL_CMD_SET_MODE,
+               .doit = irda_nl_set_mode,
+               .policy = irda_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = IRDA_NL_CMD_GET_MODE,
+               .doit = irda_nl_get_mode,
+               .policy = irda_nl_policy,
+               /* can be retrieved by unprivileged users */
+       },
+
+};
+
+static struct genl_family irda_nl_family __ro_after_init = {
+       .name = IRDA_NL_NAME,
+       .hdrsize = 0,
+       .version = IRDA_NL_VERSION,
+       .maxattr = IRDA_NL_CMD_MAX,
+       .module = THIS_MODULE,
+       .ops = irda_nl_ops,
+       .n_ops = ARRAY_SIZE(irda_nl_ops),
+};
+
+int __init irda_nl_register(void)
+{
+       return genl_register_family(&irda_nl_family);
+}
+
+void irda_nl_unregister(void)
+{
+       genl_unregister_family(&irda_nl_family);
+}
diff --git a/drivers/staging/irda/net/irproc.c b/drivers/staging/irda/net/irproc.c
new file mode 100644 (file)
index 0000000..77cfdde
--- /dev/null
@@ -0,0 +1,96 @@
+/*********************************************************************
+ *
+ * Filename:      irproc.c
+ * Version:       1.0
+ * Description:   Various entries in the /proc file system
+ * Status:        Experimental.
+ * Author:        Thomas Davis, <ratbert@radiks.net>
+ * Created at:    Sat Feb 21 21:33:24 1998
+ * Modified at:   Sun Nov 14 08:54:54 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-1999, Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>,
+ *     All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     I, Thomas Davis, provide no warranty for any of this software.
+ *     This material is provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <net/net_namespace.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irlmp.h>
+
+extern const struct file_operations discovery_seq_fops;
+extern const struct file_operations irlap_seq_fops;
+extern const struct file_operations irlmp_seq_fops;
+extern const struct file_operations irttp_seq_fops;
+extern const struct file_operations irias_seq_fops;
+
+struct irda_entry {
+       const char *name;
+       const struct file_operations *fops;
+};
+
+struct proc_dir_entry *proc_irda;
+EXPORT_SYMBOL(proc_irda);
+
+static const struct irda_entry irda_dirs[] = {
+       {"discovery",   &discovery_seq_fops},
+       {"irttp",       &irttp_seq_fops},
+       {"irlmp",       &irlmp_seq_fops},
+       {"irlap",       &irlap_seq_fops},
+       {"irias",       &irias_seq_fops},
+};
+
+/*
+ * Function irda_proc_register (void)
+ *
+ *    Register irda entry in /proc file system
+ *
+ */
+void __init irda_proc_register(void)
+{
+       int i;
+
+       proc_irda = proc_mkdir("irda", init_net.proc_net);
+       if (proc_irda == NULL)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(irda_dirs); i++)
+               (void) proc_create(irda_dirs[i].name, 0, proc_irda,
+                                  irda_dirs[i].fops);
+}
+
+/*
+ * Function irda_proc_unregister (void)
+ *
+ *    Unregister irda entry in /proc file system
+ *
+ */
+void irda_proc_unregister(void)
+{
+       int i;
+
+       if (proc_irda) {
+               for (i=0; i<ARRAY_SIZE(irda_dirs); i++)
+                       remove_proc_entry(irda_dirs[i].name, proc_irda);
+
+               remove_proc_entry("irda", init_net.proc_net);
+               proc_irda = NULL;
+       }
+}
+
+
diff --git a/drivers/staging/irda/net/irqueue.c b/drivers/staging/irda/net/irqueue.c
new file mode 100644 (file)
index 0000000..160dc89
--- /dev/null
@@ -0,0 +1,911 @@
+/*********************************************************************
+ *
+ * Filename:      irqueue.c
+ * Version:       0.3
+ * Description:   General queue implementation
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Tue Jun  9 13:29:31 1998
+ * Modified at:   Sun Dec 12 13:48:22 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Modified at:   Thu Jan  4 14:29:10 CET 2001
+ * Modified by:   Marc Zyngier <mzyngier@freesurf.fr>
+ *
+ *     Copyright (C) 1998-1999, Aage Kvalnes <aage@cs.uit.no>
+ *     Copyright (C) 1998, Dag Brattli,
+ *     All Rights Reserved.
+ *
+ *     This code is taken from the Vortex Operating System written by Aage
+ *     Kvalnes. Aage has agreed that this code can use the GPL licence,
+ *     although he does not use that licence in his own code.
+ *
+ *     This copyright does however _not_ include the ELF hash() function
+ *     which I currently don't know which licence or copyright it
+ *     has. Please inform me if you know.
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+/*
+ * NOTE :
+ * There are various problems with this package :
+ *     o the hash function for ints is pathetic (but could be changed)
+ *     o locking is sometime suspicious (especially during enumeration)
+ *     o most users have only a few elements (== overhead)
+ *     o most users never use search, so don't benefit from hashing
+ * Problem already fixed :
+ *     o not 64 bit compliant (most users do hashv = (int) self)
+ *     o hashbin_remove() is broken => use hashbin_remove_this()
+ * I think most users would be better served by a simple linked list
+ * (like include/linux/list.h) with a global spinlock per list.
+ * Jean II
+ */
+
+/*
+ * Notes on the concurrent access to hashbin and other SMP issues
+ * -------------------------------------------------------------
+ *     Hashbins are very often in the IrDA stack a global repository of
+ * information, and therefore used in a very asynchronous manner following
+ * various events (driver calls, timers, user calls...).
+ *     Therefore, very often it is highly important to consider the
+ * management of concurrent access to the hashbin and how to guarantee the
+ * consistency of the operations on it.
+ *
+ *     First, we need to define the objective of locking :
+ *             1) Protect user data (content pointed by the hashbin)
+ *             2) Protect hashbin structure itself (linked list in each bin)
+ *
+ *                          OLD LOCKING
+ *                          -----------
+ *
+ *     The previous locking strategy, either HB_LOCAL or HB_GLOBAL were
+ * both inadequate in *both* aspect.
+ *             o HB_GLOBAL was using a spinlock for each bin (local locking).
+ *             o HB_LOCAL was disabling irq on *all* CPUs, so use a single
+ *               global semaphore.
+ *     The problems were :
+ *             A) Global irq disabling is no longer supported by the kernel
+ *             B) No protection for the hashbin struct global data
+ *                     o hashbin_delete()
+ *                     o hb_current
+ *             C) No protection for user data in some cases
+ *
+ *     A) HB_LOCAL use global irq disabling, so doesn't work on kernel
+ * 2.5.X. Even when it is supported (kernel 2.4.X and earlier), its
+ * performance is not satisfactory on SMP setups. Most hashbins were
+ * HB_LOCAL, so (A) definitely need fixing.
+ *     B) HB_LOCAL could be modified to fix (B). However, because HB_GLOBAL
+ * lock only the individual bins, it will never be able to lock the
+ * global data, so can't do (B).
+ *     C) Some functions return pointer to data that is still in the
+ * hashbin :
+ *             o hashbin_find()
+ *             o hashbin_get_first()
+ *             o hashbin_get_next()
+ *     As the data is still in the hashbin, it may be changed or free'd
+ * while the caller is examinimg the data. In those case, locking can't
+ * be done within the hashbin, but must include use of the data within
+ * the caller.
+ *     The caller can easily do this with HB_LOCAL (just disable irqs).
+ * However, this is impossible with HB_GLOBAL because the caller has no
+ * way to know the proper bin, so don't know which spinlock to use.
+ *
+ *     Quick summary : can no longer use HB_LOCAL, and HB_GLOBAL is
+ * fundamentally broken and will never work.
+ *
+ *                          NEW LOCKING
+ *                          -----------
+ *
+ *     To fix those problems, I've introduce a few changes in the
+ * hashbin locking :
+ *             1) New HB_LOCK scheme
+ *             2) hashbin->hb_spinlock
+ *             3) New hashbin usage policy
+ *
+ * HB_LOCK :
+ * -------
+ *     HB_LOCK is a locking scheme intermediate between the old HB_LOCAL
+ * and HB_GLOBAL. It uses a single spinlock to protect the whole content
+ * of the hashbin. As it is a single spinlock, it can protect the global
+ * data of the hashbin and not only the bins themselves.
+ *     HB_LOCK can only protect some of the hashbin calls, so it only lock
+ * call that can be made 100% safe and leave other call unprotected.
+ *     HB_LOCK in theory is slower than HB_GLOBAL, but as the hashbin
+ * content is always small contention is not high, so it doesn't matter
+ * much. HB_LOCK is probably faster than HB_LOCAL.
+ *
+ * hashbin->hb_spinlock :
+ * --------------------
+ *     The spinlock that HB_LOCK uses is available for caller, so that
+ * the caller can protect unprotected calls (see below).
+ *     If the caller want to do entirely its own locking (HB_NOLOCK), he
+ * can do so and may use safely this spinlock.
+ *     Locking is done like this :
+ *             spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+ *     Releasing the lock :
+ *             spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+ *
+ * Safe & Protected calls :
+ * ----------------------
+ *     The following calls are safe or protected via HB_LOCK :
+ *             o hashbin_new()         -> safe
+ *             o hashbin_delete()
+ *             o hashbin_insert()
+ *             o hashbin_remove_first()
+ *             o hashbin_remove()
+ *             o hashbin_remove_this()
+ *             o HASHBIN_GET_SIZE()    -> atomic
+ *
+ *     The following calls only protect the hashbin itself :
+ *             o hashbin_lock_find()
+ *             o hashbin_find_next()
+ *
+ * Unprotected calls :
+ * -----------------
+ *     The following calls need to be protected by the caller :
+ *             o hashbin_find()
+ *             o hashbin_get_first()
+ *             o hashbin_get_next()
+ *
+ * Locking Policy :
+ * --------------
+ *     If the hashbin is used only in a single thread of execution
+ * (explicitly or implicitely), you can use HB_NOLOCK
+ *     If the calling module already provide concurrent access protection,
+ * you may use HB_NOLOCK.
+ *
+ *     In all other cases, you need to use HB_LOCK and lock the hashbin
+ * every time before calling one of the unprotected calls. You also must
+ * use the pointer returned by the unprotected call within the locked
+ * region.
+ *
+ * Extra care for enumeration :
+ * --------------------------
+ *     hashbin_get_first() and hashbin_get_next() use the hashbin to
+ * store the current position, in hb_current.
+ *     As long as the hashbin remains locked, this is safe. If you unlock
+ * the hashbin, the current position may change if anybody else modify
+ * or enumerate the hashbin.
+ *     Summary : do the full enumeration while locked.
+ *
+ *     Alternatively, you may use hashbin_find_next(). But, this will
+ * be slower, is more complex to use and doesn't protect the hashbin
+ * content. So, care is needed here as well.
+ *
+ * Other issues :
+ * ------------
+ *     I believe that we are overdoing it by using spin_lock_irqsave()
+ * and we should use only spin_lock_bh() or similar. But, I don't have
+ * the balls to try it out.
+ *     Don't believe that because hashbin are now (somewhat) SMP safe
+ * that the rest of the code is. Higher layers tend to be safest,
+ * but LAP and LMP would need some serious dedicated love.
+ *
+ * Jean II
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irqueue.h>
+
+/************************ QUEUE SUBROUTINES ************************/
+
+/*
+ * Hashbin
+ */
+#define GET_HASHBIN(x) ( x & HASHBIN_MASK )
+
+/*
+ * Function hash (name)
+ *
+ *    This function hash the input string 'name' using the ELF hash
+ *    function for strings.
+ */
+static __u32 hash( const char* name)
+{
+       __u32 h = 0;
+       __u32 g;
+
+       while(*name) {
+               h = (h<<4) + *name++;
+               if ((g = (h & 0xf0000000)))
+                       h ^=g>>24;
+               h &=~g;
+       }
+       return h;
+}
+
+/*
+ * Function enqueue_first (queue, proc)
+ *
+ *    Insert item first in queue.
+ *
+ */
+static void enqueue_first(irda_queue_t **queue, irda_queue_t* element)
+{
+
+       /*
+        * Check if queue is empty.
+        */
+       if ( *queue == NULL ) {
+               /*
+                * Queue is empty.  Insert one element into the queue.
+                */
+               element->q_next = element->q_prev = *queue = element;
+
+       } else {
+               /*
+                * Queue is not empty.  Insert element into front of queue.
+                */
+               element->q_next          = (*queue);
+               (*queue)->q_prev->q_next = element;
+               element->q_prev          = (*queue)->q_prev;
+               (*queue)->q_prev         = element;
+               (*queue)                 = element;
+       }
+}
+
+
+/*
+ * Function dequeue (queue)
+ *
+ *    Remove first entry in queue
+ *
+ */
+static irda_queue_t *dequeue_first(irda_queue_t **queue)
+{
+       irda_queue_t *ret;
+
+       pr_debug("dequeue_first()\n");
+
+       /*
+        * Set return value
+        */
+       ret =  *queue;
+
+       if ( *queue == NULL ) {
+               /*
+                * Queue was empty.
+                */
+       } else if ( (*queue)->q_next == *queue ) {
+               /*
+                *  Queue only contained a single element. It will now be
+                *  empty.
+                */
+               *queue = NULL;
+       } else {
+               /*
+                * Queue contained several element.  Remove the first one.
+                */
+               (*queue)->q_prev->q_next = (*queue)->q_next;
+               (*queue)->q_next->q_prev = (*queue)->q_prev;
+               *queue = (*queue)->q_next;
+       }
+
+       /*
+        * Return the removed entry (or NULL of queue was empty).
+        */
+       return ret;
+}
+
+/*
+ * Function dequeue_general (queue, element)
+ *
+ *
+ */
+static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element)
+{
+       irda_queue_t *ret;
+
+       pr_debug("dequeue_general()\n");
+
+       /*
+        * Set return value
+        */
+       ret =  *queue;
+
+       if ( *queue == NULL ) {
+               /*
+                * Queue was empty.
+                */
+       } else if ( (*queue)->q_next == *queue ) {
+               /*
+                *  Queue only contained a single element. It will now be
+                *  empty.
+                */
+               *queue = NULL;
+
+       } else {
+               /*
+                *  Remove specific element.
+                */
+               element->q_prev->q_next = element->q_next;
+               element->q_next->q_prev = element->q_prev;
+               if ( (*queue) == element)
+                       (*queue) = element->q_next;
+       }
+
+       /*
+        * Return the removed entry (or NULL of queue was empty).
+        */
+       return ret;
+}
+
+/************************ HASHBIN MANAGEMENT ************************/
+
+/*
+ * Function hashbin_create ( type, name )
+ *
+ *    Create hashbin!
+ *
+ */
+hashbin_t *hashbin_new(int type)
+{
+       hashbin_t* hashbin;
+
+       /*
+        * Allocate new hashbin
+        */
+       hashbin = kzalloc(sizeof(*hashbin), GFP_ATOMIC);
+       if (!hashbin)
+               return NULL;
+
+       /*
+        * Initialize structure
+        */
+       hashbin->hb_type = type;
+       hashbin->magic = HB_MAGIC;
+       //hashbin->hb_current = NULL;
+
+       /* Make sure all spinlock's are unlocked */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_lock_init(&hashbin->hb_spinlock);
+       }
+
+       return hashbin;
+}
+EXPORT_SYMBOL(hashbin_new);
+
+
+/*
+ * Function hashbin_delete (hashbin, free_func)
+ *
+ *    Destroy hashbin, the free_func can be a user supplied special routine
+ *    for deallocating this structure if it's complex. If not the user can
+ *    just supply kfree, which should take care of the job.
+ */
+int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
+{
+       irda_queue_t* queue;
+       unsigned long flags = 0;
+       int i;
+
+       IRDA_ASSERT(hashbin != NULL, return -1;);
+       IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;);
+
+       /* Synchronize */
+       if (hashbin->hb_type & HB_LOCK)
+               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+
+       /*
+        *  Free the entries in the hashbin, TODO: use hashbin_clear when
+        *  it has been shown to work
+        */
+       for (i = 0; i < HASHBIN_SIZE; i ++ ) {
+               while (1) {
+                       queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
+
+                       if (!queue)
+                               break;
+
+                       if (free_func) {
+                               if (hashbin->hb_type & HB_LOCK)
+                                       spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+                               free_func(queue);
+                               if (hashbin->hb_type & HB_LOCK)
+                                       spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+                       }
+               }
+       }
+
+       /* Cleanup local data */
+       hashbin->hb_current = NULL;
+       hashbin->magic = ~HB_MAGIC;
+
+       /* Release lock */
+       if (hashbin->hb_type & HB_LOCK)
+               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+
+       /*
+        *  Free the hashbin structure
+        */
+       kfree(hashbin);
+
+       return 0;
+}
+EXPORT_SYMBOL(hashbin_delete);
+
+/********************* HASHBIN LIST OPERATIONS *********************/
+
+/*
+ * Function hashbin_insert (hashbin, entry, name)
+ *
+ *    Insert an entry into the hashbin
+ *
+ */
+void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
+                   const char* name)
+{
+       unsigned long flags = 0;
+       int bin;
+
+       IRDA_ASSERT( hashbin != NULL, return;);
+       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;);
+
+       /*
+        * Locate hashbin
+        */
+       if ( name )
+               hashv = hash( name );
+       bin = GET_HASHBIN( hashv );
+
+       /* Synchronize */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+       } /* Default is no-lock  */
+
+       /*
+        * Store name and key
+        */
+       entry->q_hash = hashv;
+       if ( name )
+               strlcpy( entry->q_name, name, sizeof(entry->q_name));
+
+       /*
+        * Insert new entry first
+        */
+       enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ],
+                      entry);
+       hashbin->hb_size++;
+
+       /* Release lock */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+       } /* Default is no-lock  */
+}
+EXPORT_SYMBOL(hashbin_insert);
+
+/*
+ *  Function hashbin_remove_first (hashbin)
+ *
+ *    Remove first entry of the hashbin
+ *
+ * Note : this function no longer use hashbin_remove(), but does things
+ * similar to hashbin_remove_this(), so can be considered safe.
+ * Jean II
+ */
+void *hashbin_remove_first( hashbin_t *hashbin)
+{
+       unsigned long flags = 0;
+       irda_queue_t *entry = NULL;
+
+       /* Synchronize */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+       } /* Default is no-lock  */
+
+       entry = hashbin_get_first( hashbin);
+       if ( entry != NULL) {
+               int     bin;
+               long    hashv;
+               /*
+                * Locate hashbin
+                */
+               hashv = entry->q_hash;
+               bin = GET_HASHBIN( hashv );
+
+               /*
+                * Dequeue the entry...
+                */
+               dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
+                                entry);
+               hashbin->hb_size--;
+               entry->q_next = NULL;
+               entry->q_prev = NULL;
+
+               /*
+                *  Check if this item is the currently selected item, and in
+                *  that case we must reset hb_current
+                */
+               if ( entry == hashbin->hb_current)
+                       hashbin->hb_current = NULL;
+       }
+
+       /* Release lock */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+       } /* Default is no-lock  */
+
+       return entry;
+}
+
+
+/*
+ *  Function hashbin_remove (hashbin, hashv, name)
+ *
+ *    Remove entry with the given name
+ *
+ *  The use of this function is highly discouraged, because the whole
+ *  concept behind hashbin_remove() is broken. In many cases, it's not
+ *  possible to guarantee the unicity of the index (either hashv or name),
+ *  leading to removing the WRONG entry.
+ *  The only simple safe use is :
+ *             hashbin_remove(hasbin, (int) self, NULL);
+ *  In other case, you must think hard to guarantee unicity of the index.
+ *  Jean II
+ */
+void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name)
+{
+       int bin, found = FALSE;
+       unsigned long flags = 0;
+       irda_queue_t* entry;
+
+       IRDA_ASSERT( hashbin != NULL, return NULL;);
+       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+
+       /*
+        * Locate hashbin
+        */
+       if ( name )
+               hashv = hash( name );
+       bin = GET_HASHBIN( hashv );
+
+       /* Synchronize */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+       } /* Default is no-lock  */
+
+       /*
+        * Search for entry
+        */
+       entry = hashbin->hb_queue[ bin ];
+       if ( entry ) {
+               do {
+                       /*
+                        * Check for key
+                        */
+                       if ( entry->q_hash == hashv ) {
+                               /*
+                                * Name compare too?
+                                */
+                               if ( name ) {
+                                       if ( strcmp( entry->q_name, name) == 0)
+                                       {
+                                               found = TRUE;
+                                               break;
+                                       }
+                               } else {
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+                       entry = entry->q_next;
+               } while ( entry != hashbin->hb_queue[ bin ] );
+       }
+
+       /*
+        * If entry was found, dequeue it
+        */
+       if ( found ) {
+               dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
+                                entry);
+               hashbin->hb_size--;
+
+               /*
+                *  Check if this item is the currently selected item, and in
+                *  that case we must reset hb_current
+                */
+               if ( entry == hashbin->hb_current)
+                       hashbin->hb_current = NULL;
+       }
+
+       /* Release lock */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+       } /* Default is no-lock  */
+
+
+       /* Return */
+       if ( found )
+               return entry;
+       else
+               return NULL;
+
+}
+EXPORT_SYMBOL(hashbin_remove);
+
+/*
+ *  Function hashbin_remove_this (hashbin, entry)
+ *
+ *    Remove entry with the given name
+ *
+ * In some cases, the user of hashbin can't guarantee the unicity
+ * of either the hashv or name.
+ * In those cases, using the above function is guaranteed to cause troubles,
+ * so we use this one instead...
+ * And by the way, it's also faster, because we skip the search phase ;-)
+ */
+void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
+{
+       unsigned long flags = 0;
+       int     bin;
+       long    hashv;
+
+       IRDA_ASSERT( hashbin != NULL, return NULL;);
+       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+       IRDA_ASSERT( entry != NULL, return NULL;);
+
+       /* Synchronize */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+       } /* Default is no-lock  */
+
+       /* Check if valid and not already removed... */
+       if((entry->q_next == NULL) || (entry->q_prev == NULL)) {
+               entry = NULL;
+               goto out;
+       }
+
+       /*
+        * Locate hashbin
+        */
+       hashv = entry->q_hash;
+       bin = GET_HASHBIN( hashv );
+
+       /*
+        * Dequeue the entry...
+        */
+       dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
+                        entry);
+       hashbin->hb_size--;
+       entry->q_next = NULL;
+       entry->q_prev = NULL;
+
+       /*
+        *  Check if this item is the currently selected item, and in
+        *  that case we must reset hb_current
+        */
+       if ( entry == hashbin->hb_current)
+               hashbin->hb_current = NULL;
+out:
+       /* Release lock */
+       if ( hashbin->hb_type & HB_LOCK ) {
+               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+       } /* Default is no-lock  */
+
+       return entry;
+}
+EXPORT_SYMBOL(hashbin_remove_this);
+
+/*********************** HASHBIN ENUMERATION ***********************/
+
+/*
+ * Function hashbin_common_find (hashbin, hashv, name)
+ *
+ *    Find item with the given hashv or name
+ *
+ */
+void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name )
+{
+       int bin;
+       irda_queue_t* entry;
+
+       pr_debug("hashbin_find()\n");
+
+       IRDA_ASSERT( hashbin != NULL, return NULL;);
+       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+
+       /*
+        * Locate hashbin
+        */
+       if ( name )
+               hashv = hash( name );
+       bin = GET_HASHBIN( hashv );
+
+       /*
+        * Search for entry
+        */
+       entry = hashbin->hb_queue[ bin];
+       if ( entry ) {
+               do {
+                       /*
+                        * Check for key
+                        */
+                       if ( entry->q_hash == hashv ) {
+                               /*
+                                * Name compare too?
+                                */
+                               if ( name ) {
+                                       if ( strcmp( entry->q_name, name ) == 0 ) {
+                                               return entry;
+                                       }
+                               } else {
+                                       return entry;
+                               }
+                       }
+                       entry = entry->q_next;
+               } while ( entry != hashbin->hb_queue[ bin ] );
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(hashbin_find);
+
+/*
+ * Function hashbin_lock_find (hashbin, hashv, name)
+ *
+ *    Find item with the given hashv or name
+ *
+ * Same, but with spinlock protection...
+ * I call it safe, but it's only safe with respect to the hashbin, not its
+ * content. - Jean II
+ */
+void* hashbin_lock_find( hashbin_t* hashbin, long hashv, const char* name )
+{
+       unsigned long flags = 0;
+       irda_queue_t* entry;
+
+       /* Synchronize */
+       spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+
+       /*
+        * Search for entry
+        */
+       entry = hashbin_find(hashbin, hashv, name);
+
+       /* Release lock */
+       spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+
+       return entry;
+}
+EXPORT_SYMBOL(hashbin_lock_find);
+
+/*
+ * Function hashbin_find (hashbin, hashv, name, pnext)
+ *
+ *    Find an item with the given hashv or name, and its successor
+ *
+ * This function allow to do concurrent enumerations without the
+ * need to lock over the whole session, because the caller keep the
+ * context of the search. On the other hand, it might fail and return
+ * NULL if the entry is removed. - Jean II
+ */
+void* hashbin_find_next( hashbin_t* hashbin, long hashv, const char* name,
+                        void ** pnext)
+{
+       unsigned long flags = 0;
+       irda_queue_t* entry;
+
+       /* Synchronize */
+       spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+
+       /*
+        * Search for current entry
+        * This allow to check if the current item is still in the
+        * hashbin or has been removed.
+        */
+       entry = hashbin_find(hashbin, hashv, name);
+
+       /*
+        * Trick hashbin_get_next() to return what we want
+        */
+       if(entry) {
+               hashbin->hb_current = entry;
+               *pnext = hashbin_get_next( hashbin );
+       } else
+               *pnext = NULL;
+
+       /* Release lock */
+       spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+
+       return entry;
+}
+
+/*
+ * Function hashbin_get_first (hashbin)
+ *
+ *    Get a pointer to first element in hashbin, this function must be
+ *    called before any calls to hashbin_get_next()!
+ *
+ */
+irda_queue_t *hashbin_get_first( hashbin_t* hashbin)
+{
+       irda_queue_t *entry;
+       int i;
+
+       IRDA_ASSERT( hashbin != NULL, return NULL;);
+       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+
+       if ( hashbin == NULL)
+               return NULL;
+
+       for ( i = 0; i < HASHBIN_SIZE; i ++ ) {
+               entry = hashbin->hb_queue[ i];
+               if ( entry) {
+                       hashbin->hb_current = entry;
+                       return entry;
+               }
+       }
+       /*
+        *  Did not find any item in hashbin
+        */
+       return NULL;
+}
+EXPORT_SYMBOL(hashbin_get_first);
+
+/*
+ * Function hashbin_get_next (hashbin)
+ *
+ *    Get next item in hashbin. A series of hashbin_get_next() calls must
+ *    be started by a call to hashbin_get_first(). The function returns
+ *    NULL when all items have been traversed
+ *
+ * The context of the search is stored within the hashbin, so you must
+ * protect yourself from concurrent enumerations. - Jean II
+ */
+irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
+{
+       irda_queue_t* entry;
+       int bin;
+       int i;
+
+       IRDA_ASSERT( hashbin != NULL, return NULL;);
+       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+
+       if ( hashbin->hb_current == NULL) {
+               IRDA_ASSERT( hashbin->hb_current != NULL, return NULL;);
+               return NULL;
+       }
+       entry = hashbin->hb_current->q_next;
+       bin = GET_HASHBIN( entry->q_hash);
+
+       /*
+        *  Make sure that we are not back at the beginning of the queue
+        *  again
+        */
+       if ( entry != hashbin->hb_queue[ bin ]) {
+               hashbin->hb_current = entry;
+
+               return entry;
+       }
+
+       /*
+        *  Check that this is not the last queue in hashbin
+        */
+       if ( bin >= HASHBIN_SIZE)
+               return NULL;
+
+       /*
+        *  Move to next queue in hashbin
+        */
+       bin++;
+       for ( i = bin; i < HASHBIN_SIZE; i++ ) {
+               entry = hashbin->hb_queue[ i];
+               if ( entry) {
+                       hashbin->hb_current = entry;
+
+                       return entry;
+               }
+       }
+       return NULL;
+}
+EXPORT_SYMBOL(hashbin_get_next);
diff --git a/drivers/staging/irda/net/irsysctl.c b/drivers/staging/irda/net/irsysctl.c
new file mode 100644 (file)
index 0000000..873da5e
--- /dev/null
@@ -0,0 +1,258 @@
+/*********************************************************************
+ *
+ * Filename:      irsysctl.c
+ * Version:       1.0
+ * Description:   Sysctl interface for IrDA
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun May 24 22:12:06 1998
+ * Modified at:   Fri Jun  4 02:50:15 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/mm.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>             /* irda_debug */
+#include <net/irda/irlmp.h>
+#include <net/irda/timer.h>
+#include <net/irda/irias_object.h>
+
+extern int  sysctl_discovery;
+extern int  sysctl_discovery_slots;
+extern int  sysctl_discovery_timeout;
+extern int  sysctl_slot_timeout;
+extern int  sysctl_fast_poll_increase;
+extern char sysctl_devname[];
+extern int  sysctl_max_baud_rate;
+extern unsigned int sysctl_min_tx_turn_time;
+extern unsigned int sysctl_max_tx_data_size;
+extern unsigned int sysctl_max_tx_window;
+extern int  sysctl_max_noreply_time;
+extern int  sysctl_warn_noreply_time;
+extern int  sysctl_lap_keepalive_time;
+
+extern struct irlmp_cb *irlmp;
+
+/* this is needed for the proc_dointvec_minmax - Jean II */
+static int max_discovery_slots = 16;           /* ??? */
+static int min_discovery_slots = 1;
+/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices
+ * seems to require it. (from Dag's comment) */
+static int max_slot_timeout = 160;
+static int min_slot_timeout = 20;
+static int max_max_baud_rate = 16000000;       /* See qos.c - IrLAP spec */
+static int min_max_baud_rate = 2400;
+static int max_min_tx_turn_time = 10000;       /* See qos.c - IrLAP spec */
+static int min_min_tx_turn_time;
+static int max_max_tx_data_size = 2048;                /* See qos.c - IrLAP spec */
+static int min_max_tx_data_size = 64;
+static int max_max_tx_window = 7;              /* See qos.c - IrLAP spec */
+static int min_max_tx_window = 1;
+static int max_max_noreply_time = 40;          /* See qos.c - IrLAP spec */
+static int min_max_noreply_time = 3;
+static int max_warn_noreply_time = 3;          /* 3s == standard */
+static int min_warn_noreply_time = 1;          /* 1s == min WD_TIMER */
+static int max_lap_keepalive_time = 10000;     /* 10s */
+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(struct ctl_table *table, int write,
+                     void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int ret;
+
+       ret = proc_dostring(table, write, buffer, lenp, ppos);
+       if (ret == 0 && write) {
+               struct ias_value *val;
+
+               val = irias_new_string_value(sysctl_devname);
+               if (val)
+                       irias_object_change_attribute("Device", "DeviceName", val);
+       }
+       return ret;
+}
+
+
+static int do_discovery(struct ctl_table *table, int write,
+                    void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int ret;
+
+       ret = proc_dointvec(table, write, buffer, lenp, ppos);
+       if (ret)
+              return ret;
+
+       if (irlmp == NULL)
+              return -ENODEV;
+
+       if (sysctl_discovery)
+              irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout*HZ);
+       else
+              del_timer_sync(&irlmp->discovery_timer);
+
+       return ret;
+}
+
+/* One file */
+static struct ctl_table irda_table[] = {
+       {
+               .procname       = "discovery",
+               .data           = &sysctl_discovery,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = do_discovery,
+       },
+       {
+               .procname       = "devname",
+               .data           = sysctl_devname,
+               .maxlen         = 65,
+               .mode           = 0644,
+               .proc_handler   = do_devname,
+       },
+#ifdef CONFIG_IRDA_FAST_RR
+       {
+               .procname       = "fast_poll_increase",
+               .data           = &sysctl_fast_poll_increase,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
+#endif
+       {
+               .procname       = "discovery_slots",
+               .data           = &sysctl_discovery_slots,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_discovery_slots,
+               .extra2         = &max_discovery_slots
+       },
+       {
+               .procname       = "discovery_timeout",
+               .data           = &sysctl_discovery_timeout,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
+       {
+               .procname       = "slot_timeout",
+               .data           = &sysctl_slot_timeout,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_slot_timeout,
+               .extra2         = &max_slot_timeout
+       },
+       {
+               .procname       = "max_baud_rate",
+               .data           = &sysctl_max_baud_rate,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_max_baud_rate,
+               .extra2         = &max_max_baud_rate
+       },
+       {
+               .procname       = "min_tx_turn_time",
+               .data           = &sysctl_min_tx_turn_time,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_min_tx_turn_time,
+               .extra2         = &max_min_tx_turn_time
+       },
+       {
+               .procname       = "max_tx_data_size",
+               .data           = &sysctl_max_tx_data_size,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_max_tx_data_size,
+               .extra2         = &max_max_tx_data_size
+       },
+       {
+               .procname       = "max_tx_window",
+               .data           = &sysctl_max_tx_window,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_max_tx_window,
+               .extra2         = &max_max_tx_window
+       },
+       {
+               .procname       = "max_noreply_time",
+               .data           = &sysctl_max_noreply_time,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_max_noreply_time,
+               .extra2         = &max_max_noreply_time
+       },
+       {
+               .procname       = "warn_noreply_time",
+               .data           = &sysctl_warn_noreply_time,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_warn_noreply_time,
+               .extra2         = &max_warn_noreply_time
+       },
+       {
+               .procname       = "lap_keepalive_time",
+               .data           = &sysctl_lap_keepalive_time,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_lap_keepalive_time,
+               .extra2         = &max_lap_keepalive_time
+       },
+       { }
+};
+
+static struct ctl_table_header *irda_table_header;
+
+/*
+ * Function irda_sysctl_register (void)
+ *
+ *    Register our sysctl interface
+ *
+ */
+int __init irda_sysctl_register(void)
+{
+       irda_table_header = register_net_sysctl(&init_net, "net/irda", irda_table);
+       if (!irda_table_header)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/*
+ * Function irda_sysctl_unregister (void)
+ *
+ *    Unregister our sysctl interface
+ *
+ */
+void irda_sysctl_unregister(void)
+{
+       unregister_net_sysctl_table(irda_table_header);
+}
+
+
+
diff --git a/drivers/staging/irda/net/irttp.c b/drivers/staging/irda/net/irttp.c
new file mode 100644 (file)
index 0000000..b6ab41d
--- /dev/null
@@ -0,0 +1,1891 @@
+/*********************************************************************
+ *
+ * Filename:      irttp.c
+ * Version:       1.2
+ * Description:   Tiny Transport Protocol (TTP) implementation
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sun Aug 31 20:14:31 1997
+ * Modified at:   Wed Jan  5 11:31:27 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/parameters.h>
+#include <net/irda/irttp.h>
+
+static struct irttp_cb *irttp;
+
+static void __irttp_close_tsap(struct tsap_cb *self);
+
+static int irttp_data_indication(void *instance, void *sap,
+                                struct sk_buff *skb);
+static int irttp_udata_indication(void *instance, void *sap,
+                                 struct sk_buff *skb);
+static void irttp_disconnect_indication(void *instance, void *sap,
+                                       LM_REASON reason, struct sk_buff *);
+static void irttp_connect_indication(void *instance, void *sap,
+                                    struct qos_info *qos, __u32 max_sdu_size,
+                                    __u8 header_size, struct sk_buff *skb);
+static void irttp_connect_confirm(void *instance, void *sap,
+                                 struct qos_info *qos, __u32 max_sdu_size,
+                                 __u8 header_size, struct sk_buff *skb);
+static void irttp_run_tx_queue(struct tsap_cb *self);
+static void irttp_run_rx_queue(struct tsap_cb *self);
+
+static void irttp_flush_queues(struct tsap_cb *self);
+static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb);
+static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
+static void irttp_todo_expired(unsigned long data);
+static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
+                                   int get);
+
+static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow);
+static void irttp_status_indication(void *instance,
+                                   LINK_STATUS link, LOCK_STATUS lock);
+
+/* Information for parsing parameters in IrTTP */
+static const pi_minor_info_t pi_minor_call_table[] = {
+       { NULL, 0 },                                             /* 0x00 */
+       { irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */
+};
+static const pi_major_info_t pi_major_call_table[] = {
+       { pi_minor_call_table, 2 }
+};
+static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 };
+
+/************************ GLOBAL PROCEDURES ************************/
+
+/*
+ * Function irttp_init (void)
+ *
+ *    Initialize the IrTTP layer. Called by module initialization code
+ *
+ */
+int __init irttp_init(void)
+{
+       irttp = kzalloc(sizeof(struct irttp_cb), GFP_KERNEL);
+       if (irttp == NULL)
+               return -ENOMEM;
+
+       irttp->magic = TTP_MAGIC;
+
+       irttp->tsaps = hashbin_new(HB_LOCK);
+       if (!irttp->tsaps) {
+               net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n",
+                                   __func__);
+               kfree(irttp);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/*
+ * Function irttp_cleanup (void)
+ *
+ *    Called by module destruction/cleanup code
+ *
+ */
+void irttp_cleanup(void)
+{
+       /* Check for main structure */
+       IRDA_ASSERT(irttp->magic == TTP_MAGIC, return;);
+
+       /*
+        *  Delete hashbin and close all TSAP instances in it
+        */
+       hashbin_delete(irttp->tsaps, (FREE_FUNC) __irttp_close_tsap);
+
+       irttp->magic = 0;
+
+       /* De-allocate main structure */
+       kfree(irttp);
+
+       irttp = NULL;
+}
+
+/*************************** SUBROUTINES ***************************/
+
+/*
+ * Function irttp_start_todo_timer (self, timeout)
+ *
+ *    Start todo timer.
+ *
+ * Made it more effient and unsensitive to race conditions - Jean II
+ */
+static inline void irttp_start_todo_timer(struct tsap_cb *self, int timeout)
+{
+       /* Set new value for timer */
+       mod_timer(&self->todo_timer, jiffies + timeout);
+}
+
+/*
+ * Function irttp_todo_expired (data)
+ *
+ *    Todo timer has expired!
+ *
+ * One of the restriction of the timer is that it is run only on the timer
+ * interrupt which run every 10ms. This mean that even if you set the timer
+ * with a delay of 0, it may take up to 10ms before it's run.
+ * So, to minimise latency and keep cache fresh, we try to avoid using
+ * it as much as possible.
+ * Note : we can't use tasklets, because they can't be asynchronously
+ * killed (need user context), and we can't guarantee that here...
+ * Jean II
+ */
+static void irttp_todo_expired(unsigned long data)
+{
+       struct tsap_cb *self = (struct tsap_cb *) data;
+
+       /* Check that we still exist */
+       if (!self || self->magic != TTP_TSAP_MAGIC)
+               return;
+
+       pr_debug("%s(instance=%p)\n", __func__, self);
+
+       /* Try to make some progress, especially on Tx side - Jean II */
+       irttp_run_rx_queue(self);
+       irttp_run_tx_queue(self);
+
+       /* Check if time for disconnect */
+       if (test_bit(0, &self->disconnect_pend)) {
+               /* Check if it's possible to disconnect yet */
+               if (skb_queue_empty(&self->tx_queue)) {
+                       /* Make sure disconnect is not pending anymore */
+                       clear_bit(0, &self->disconnect_pend);   /* FALSE */
+
+                       /* Note : self->disconnect_skb may be NULL */
+                       irttp_disconnect_request(self, self->disconnect_skb,
+                                                P_NORMAL);
+                       self->disconnect_skb = NULL;
+               } else {
+                       /* Try again later */
+                       irttp_start_todo_timer(self, HZ/10);
+
+                       /* No reason to try and close now */
+                       return;
+               }
+       }
+
+       /* Check if it's closing time */
+       if (self->close_pend)
+               /* Finish cleanup */
+               irttp_close_tsap(self);
+}
+
+/*
+ * Function irttp_flush_queues (self)
+ *
+ *     Flushes (removes all frames) in transitt-buffer (tx_list)
+ */
+static void irttp_flush_queues(struct tsap_cb *self)
+{
+       struct sk_buff *skb;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+       /* Deallocate frames waiting to be sent */
+       while ((skb = skb_dequeue(&self->tx_queue)) != NULL)
+               dev_kfree_skb(skb);
+
+       /* Deallocate received frames */
+       while ((skb = skb_dequeue(&self->rx_queue)) != NULL)
+               dev_kfree_skb(skb);
+
+       /* Deallocate received fragments */
+       while ((skb = skb_dequeue(&self->rx_fragments)) != NULL)
+               dev_kfree_skb(skb);
+}
+
+/*
+ * Function irttp_reassemble (self)
+ *
+ *    Makes a new (continuous) skb of all the fragments in the fragment
+ *    queue
+ *
+ */
+static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
+{
+       struct sk_buff *skb, *frag;
+       int n = 0;  /* Fragment index */
+
+       IRDA_ASSERT(self != NULL, return NULL;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;);
+
+       pr_debug("%s(), self->rx_sdu_size=%d\n", __func__,
+                self->rx_sdu_size);
+
+       skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size);
+       if (!skb)
+               return NULL;
+
+       /*
+        * Need to reserve space for TTP header in case this skb needs to
+        * be requeued in case delivery failes
+        */
+       skb_reserve(skb, TTP_HEADER);
+       skb_put(skb, self->rx_sdu_size);
+
+       /*
+        *  Copy all fragments to a new buffer
+        */
+       while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) {
+               skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len);
+               n += frag->len;
+
+               dev_kfree_skb(frag);
+       }
+
+       pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n",
+                __func__, n, self->rx_sdu_size, self->rx_max_sdu_size);
+       /* Note : irttp_run_rx_queue() calculate self->rx_sdu_size
+        * by summing the size of all fragments, so we should always
+        * have n == self->rx_sdu_size, except in cases where we
+        * droped the last fragment (when self->rx_sdu_size exceed
+        * self->rx_max_sdu_size), where n < self->rx_sdu_size.
+        * Jean II */
+       IRDA_ASSERT(n <= self->rx_sdu_size, n = self->rx_sdu_size;);
+
+       /* Set the new length */
+       skb_trim(skb, n);
+
+       self->rx_sdu_size = 0;
+
+       return skb;
+}
+
+/*
+ * Function irttp_fragment_skb (skb)
+ *
+ *    Fragments a frame and queues all the fragments for transmission
+ *
+ */
+static inline void irttp_fragment_skb(struct tsap_cb *self,
+                                     struct sk_buff *skb)
+{
+       struct sk_buff *frag;
+       __u8 *frame;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       /*
+        *  Split frame into a number of segments
+        */
+       while (skb->len > self->max_seg_size) {
+               pr_debug("%s(), fragmenting ...\n", __func__);
+
+               /* Make new segment */
+               frag = alloc_skb(self->max_seg_size+self->max_header_size,
+                                GFP_ATOMIC);
+               if (!frag)
+                       return;
+
+               skb_reserve(frag, self->max_header_size);
+
+               /* Copy data from the original skb into this fragment. */
+               skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size),
+                             self->max_seg_size);
+
+               /* Insert TTP header, with the more bit set */
+               frame = skb_push(frag, TTP_HEADER);
+               frame[0] = TTP_MORE;
+
+               /* Hide the copied data from the original skb */
+               skb_pull(skb, self->max_seg_size);
+
+               /* Queue fragment */
+               skb_queue_tail(&self->tx_queue, frag);
+       }
+       /* Queue what is left of the original skb */
+       pr_debug("%s(), queuing last segment\n", __func__);
+
+       frame = skb_push(skb, TTP_HEADER);
+       frame[0] = 0x00; /* Clear more bit */
+
+       /* Queue fragment */
+       skb_queue_tail(&self->tx_queue, skb);
+}
+
+/*
+ * Function irttp_param_max_sdu_size (self, param)
+ *
+ *    Handle the MaxSduSize parameter in the connect frames, this function
+ *    will be called both when this parameter needs to be inserted into, and
+ *    extracted from the connect frames
+ */
+static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
+                                   int get)
+{
+       struct tsap_cb *self;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->tx_max_sdu_size;
+       else
+               self->tx_max_sdu_size = param->pv.i;
+
+       pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i);
+
+       return 0;
+}
+
+/*************************** CLIENT CALLS ***************************/
+/************************** LMP CALLBACKS **************************/
+/* Everything is happily mixed up. Waiting for next clean up - Jean II */
+
+/*
+ * Initialization, that has to be done on new tsap
+ * instance allocation and on duplication
+ */
+static void irttp_init_tsap(struct tsap_cb *tsap)
+{
+       spin_lock_init(&tsap->lock);
+       init_timer(&tsap->todo_timer);
+
+       skb_queue_head_init(&tsap->rx_queue);
+       skb_queue_head_init(&tsap->tx_queue);
+       skb_queue_head_init(&tsap->rx_fragments);
+}
+
+/*
+ * Function irttp_open_tsap (stsap, notify)
+ *
+ *    Create TSAP connection endpoint,
+ */
+struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
+{
+       struct tsap_cb *self;
+       struct lsap_cb *lsap;
+       notify_t ttp_notify;
+
+       IRDA_ASSERT(irttp->magic == TTP_MAGIC, return NULL;);
+
+       /* The IrLMP spec (IrLMP 1.1 p10) says that we have the right to
+        * use only 0x01-0x6F. Of course, we can use LSAP_ANY as well.
+        * JeanII */
+       if ((stsap_sel != LSAP_ANY) &&
+          ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) {
+               pr_debug("%s(), invalid tsap!\n", __func__);
+               return NULL;
+       }
+
+       self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC);
+       if (self == NULL)
+               return NULL;
+
+       /* Initialize internal objects */
+       irttp_init_tsap(self);
+
+       /* Initialise todo timer */
+       self->todo_timer.data     = (unsigned long) self;
+       self->todo_timer.function = &irttp_todo_expired;
+
+       /* Initialize callbacks for IrLMP to use */
+       irda_notify_init(&ttp_notify);
+       ttp_notify.connect_confirm = irttp_connect_confirm;
+       ttp_notify.connect_indication = irttp_connect_indication;
+       ttp_notify.disconnect_indication = irttp_disconnect_indication;
+       ttp_notify.data_indication = irttp_data_indication;
+       ttp_notify.udata_indication = irttp_udata_indication;
+       ttp_notify.flow_indication = irttp_flow_indication;
+       if (notify->status_indication != NULL)
+               ttp_notify.status_indication = irttp_status_indication;
+       ttp_notify.instance = self;
+       strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME);
+
+       self->magic = TTP_TSAP_MAGIC;
+       self->connected = FALSE;
+
+       /*
+        *  Create LSAP at IrLMP layer
+        */
+       lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0);
+       if (lsap == NULL) {
+               pr_debug("%s: unable to allocate LSAP!!\n", __func__);
+               __irttp_close_tsap(self);
+               return NULL;
+       }
+
+       /*
+        *  If user specified LSAP_ANY as source TSAP selector, then IrLMP
+        *  will replace it with whatever source selector which is free, so
+        *  the stsap_sel we have might not be valid anymore
+        */
+       self->stsap_sel = lsap->slsap_sel;
+       pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel);
+
+       self->notify = *notify;
+       self->lsap = lsap;
+
+       hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (long) self, NULL);
+
+       if (credit > TTP_RX_MAX_CREDIT)
+               self->initial_credit = TTP_RX_MAX_CREDIT;
+       else
+               self->initial_credit = credit;
+
+       return self;
+}
+EXPORT_SYMBOL(irttp_open_tsap);
+
+/*
+ * Function irttp_close (handle)
+ *
+ *    Remove an instance of a TSAP. This function should only deal with the
+ *    deallocation of the TSAP, and resetting of the TSAPs values;
+ *
+ */
+static void __irttp_close_tsap(struct tsap_cb *self)
+{
+       /* First make sure we're connected. */
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+       irttp_flush_queues(self);
+
+       del_timer(&self->todo_timer);
+
+       /* This one won't be cleaned up if we are disconnect_pend + close_pend
+        * and we receive a disconnect_indication */
+       if (self->disconnect_skb)
+               dev_kfree_skb(self->disconnect_skb);
+
+       self->connected = FALSE;
+       self->magic = ~TTP_TSAP_MAGIC;
+
+       kfree(self);
+}
+
+/*
+ * Function irttp_close (self)
+ *
+ *    Remove TSAP from list of all TSAPs and then deallocate all resources
+ *    associated with this TSAP
+ *
+ * Note : because we *free* the tsap structure, it is the responsibility
+ * of the caller to make sure we are called only once and to deal with
+ * possible race conditions. - Jean II
+ */
+int irttp_close_tsap(struct tsap_cb *self)
+{
+       struct tsap_cb *tsap;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
+
+       /* Make sure tsap has been disconnected */
+       if (self->connected) {
+               /* Check if disconnect is not pending */
+               if (!test_bit(0, &self->disconnect_pend)) {
+                       net_warn_ratelimited("%s: TSAP still connected!\n",
+                                            __func__);
+                       irttp_disconnect_request(self, NULL, P_NORMAL);
+               }
+               self->close_pend = TRUE;
+               irttp_start_todo_timer(self, HZ/10);
+
+               return 0; /* Will be back! */
+       }
+
+       tsap = hashbin_remove(irttp->tsaps, (long) self, NULL);
+
+       IRDA_ASSERT(tsap == self, return -1;);
+
+       /* Close corresponding LSAP */
+       if (self->lsap) {
+               irlmp_close_lsap(self->lsap);
+               self->lsap = NULL;
+       }
+
+       __irttp_close_tsap(self);
+
+       return 0;
+}
+EXPORT_SYMBOL(irttp_close_tsap);
+
+/*
+ * Function irttp_udata_request (self, skb)
+ *
+ *    Send unreliable data on this TSAP
+ *
+ */
+int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb)
+{
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       /* Take shortcut on zero byte packets */
+       if (skb->len == 0) {
+               ret = 0;
+               goto err;
+       }
+
+       /* Check that nothing bad happens */
+       if (!self->connected) {
+               net_warn_ratelimited("%s(), Not connected\n", __func__);
+               ret = -ENOTCONN;
+               goto err;
+       }
+
+       if (skb->len > self->max_seg_size) {
+               net_err_ratelimited("%s(), UData is too large for IrLAP!\n",
+                                   __func__);
+               ret = -EMSGSIZE;
+               goto err;
+       }
+
+       irlmp_udata_request(self->lsap, skb);
+       self->stats.tx_packets++;
+
+       return 0;
+
+err:
+       dev_kfree_skb(skb);
+       return ret;
+}
+EXPORT_SYMBOL(irttp_udata_request);
+
+
+/*
+ * Function irttp_data_request (handle, skb)
+ *
+ *    Queue frame for transmission. If SAR is enabled, fragement the frame
+ *    and queue the fragments for transmission
+ */
+int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
+{
+       __u8 *frame;
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       pr_debug("%s() : queue len = %d\n", __func__,
+                skb_queue_len(&self->tx_queue));
+
+       /* Take shortcut on zero byte packets */
+       if (skb->len == 0) {
+               ret = 0;
+               goto err;
+       }
+
+       /* Check that nothing bad happens */
+       if (!self->connected) {
+               net_warn_ratelimited("%s: Not connected\n", __func__);
+               ret = -ENOTCONN;
+               goto err;
+       }
+
+       /*
+        *  Check if SAR is disabled, and the frame is larger than what fits
+        *  inside an IrLAP frame
+        */
+       if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) {
+               net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n",
+                                   __func__);
+               ret = -EMSGSIZE;
+               goto err;
+       }
+
+       /*
+        *  Check if SAR is enabled, and the frame is larger than the
+        *  TxMaxSduSize
+        */
+       if ((self->tx_max_sdu_size != 0) &&
+           (self->tx_max_sdu_size != TTP_SAR_UNBOUND) &&
+           (skb->len > self->tx_max_sdu_size)) {
+               net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n",
+                                   __func__);
+               ret = -EMSGSIZE;
+               goto err;
+       }
+       /*
+        *  Check if transmit queue is full
+        */
+       if (skb_queue_len(&self->tx_queue) >= TTP_TX_MAX_QUEUE) {
+               /*
+                *  Give it a chance to empty itself
+                */
+               irttp_run_tx_queue(self);
+
+               /* Drop packet. This error code should trigger the caller
+                * to resend the data in the client code - Jean II */
+               ret = -ENOBUFS;
+               goto err;
+       }
+
+       /* Queue frame, or queue frame segments */
+       if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) {
+               /* Queue frame */
+               IRDA_ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;);
+               frame = skb_push(skb, TTP_HEADER);
+               frame[0] = 0x00; /* Clear more bit */
+
+               skb_queue_tail(&self->tx_queue, skb);
+       } else {
+               /*
+                *  Fragment the frame, this function will also queue the
+                *  fragments, we don't care about the fact the transmit
+                *  queue may be overfilled by all the segments for a little
+                *  while
+                */
+               irttp_fragment_skb(self, skb);
+       }
+
+       /* Check if we can accept more data from client */
+       if ((!self->tx_sdu_busy) &&
+           (skb_queue_len(&self->tx_queue) > TTP_TX_HIGH_THRESHOLD)) {
+               /* Tx queue filling up, so stop client. */
+               if (self->notify.flow_indication) {
+                       self->notify.flow_indication(self->notify.instance,
+                                                    self, FLOW_STOP);
+               }
+               /* self->tx_sdu_busy is the state of the client.
+                * Update state after notifying client to avoid
+                * race condition with irttp_flow_indication().
+                * If the queue empty itself after our test but before
+                * we set the flag, we will fix ourselves below in
+                * irttp_run_tx_queue().
+                * Jean II */
+               self->tx_sdu_busy = TRUE;
+       }
+
+       /* Try to make some progress */
+       irttp_run_tx_queue(self);
+
+       return 0;
+
+err:
+       dev_kfree_skb(skb);
+       return ret;
+}
+EXPORT_SYMBOL(irttp_data_request);
+
+/*
+ * Function irttp_run_tx_queue (self)
+ *
+ *    Transmit packets queued for transmission (if possible)
+ *
+ */
+static void irttp_run_tx_queue(struct tsap_cb *self)
+{
+       struct sk_buff *skb;
+       unsigned long flags;
+       int n;
+
+       pr_debug("%s() : send_credit = %d, queue_len = %d\n",
+                __func__,
+                self->send_credit, skb_queue_len(&self->tx_queue));
+
+       /* Get exclusive access to the tx queue, otherwise don't touch it */
+       if (irda_lock(&self->tx_queue_lock) == FALSE)
+               return;
+
+       /* Try to send out frames as long as we have credits
+        * and as long as LAP is not full. If LAP is full, it will
+        * poll us through irttp_flow_indication() - Jean II */
+       while ((self->send_credit > 0) &&
+              (!irlmp_lap_tx_queue_full(self->lsap)) &&
+              (skb = skb_dequeue(&self->tx_queue))) {
+               /*
+                *  Since we can transmit and receive frames concurrently,
+                *  the code below is a critical region and we must assure that
+                *  nobody messes with the credits while we update them.
+                */
+               spin_lock_irqsave(&self->lock, flags);
+
+               n = self->avail_credit;
+               self->avail_credit = 0;
+
+               /* Only room for 127 credits in frame */
+               if (n > 127) {
+                       self->avail_credit = n-127;
+                       n = 127;
+               }
+               self->remote_credit += n;
+               self->send_credit--;
+
+               spin_unlock_irqrestore(&self->lock, flags);
+
+               /*
+                *  More bit must be set by the data_request() or fragment()
+                *  functions
+                */
+               skb->data[0] |= (n & 0x7f);
+
+               /* Detach from socket.
+                * The current skb has a reference to the socket that sent
+                * it (skb->sk). When we pass it to IrLMP, the skb will be
+                * stored in in IrLAP (self->wx_list). When we are within
+                * IrLAP, we lose the notion of socket, so we should not
+                * have a reference to a socket. So, we drop it here.
+                *
+                * Why does it matter ?
+                * When the skb is freed (kfree_skb), if it is associated
+                * with a socket, it release buffer space on the socket
+                * (through sock_wfree() and sock_def_write_space()).
+                * If the socket no longer exist, we may crash. Hard.
+                * When we close a socket, we make sure that associated packets
+                * in IrTTP are freed. However, we have no way to cancel
+                * the packet that we have passed to IrLAP. So, if a packet
+                * remains in IrLAP (retry on the link or else) after we
+                * close the socket, we are dead !
+                * Jean II */
+               if (skb->sk != NULL) {
+                       /* IrSOCK application, IrOBEX, ... */
+                       skb_orphan(skb);
+               }
+                       /* IrCOMM over IrTTP, IrLAN, ... */
+
+               /* Pass the skb to IrLMP - done */
+               irlmp_data_request(self->lsap, skb);
+               self->stats.tx_packets++;
+       }
+
+       /* Check if we can accept more frames from client.
+        * We don't want to wait until the todo timer to do that, and we
+        * can't use tasklets (grr...), so we are obliged to give control
+        * to client. That's ok, this test will be true not too often
+        * (max once per LAP window) and we are called from places
+        * where we can spend a bit of time doing stuff. - Jean II */
+       if ((self->tx_sdu_busy) &&
+           (skb_queue_len(&self->tx_queue) < TTP_TX_LOW_THRESHOLD) &&
+           (!self->close_pend)) {
+               if (self->notify.flow_indication)
+                       self->notify.flow_indication(self->notify.instance,
+                                                    self, FLOW_START);
+
+               /* self->tx_sdu_busy is the state of the client.
+                * We don't really have a race here, but it's always safer
+                * to update our state after the client - Jean II */
+               self->tx_sdu_busy = FALSE;
+       }
+
+       /* Reset lock */
+       self->tx_queue_lock = 0;
+}
+
+/*
+ * Function irttp_give_credit (self)
+ *
+ *    Send a dataless flowdata TTP-PDU and give available credit to peer
+ *    TSAP
+ */
+static inline void irttp_give_credit(struct tsap_cb *self)
+{
+       struct sk_buff *tx_skb = NULL;
+       unsigned long flags;
+       int n;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+       pr_debug("%s() send=%d,avail=%d,remote=%d\n",
+                __func__,
+                self->send_credit, self->avail_credit, self->remote_credit);
+
+       /* Give credit to peer */
+       tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC);
+       if (!tx_skb)
+               return;
+
+       /* Reserve space for LMP, and LAP header */
+       skb_reserve(tx_skb, LMP_MAX_HEADER);
+
+       /*
+        *  Since we can transmit and receive frames concurrently,
+        *  the code below is a critical region and we must assure that
+        *  nobody messes with the credits while we update them.
+        */
+       spin_lock_irqsave(&self->lock, flags);
+
+       n = self->avail_credit;
+       self->avail_credit = 0;
+
+       /* Only space for 127 credits in frame */
+       if (n > 127) {
+               self->avail_credit = n - 127;
+               n = 127;
+       }
+       self->remote_credit += n;
+
+       spin_unlock_irqrestore(&self->lock, flags);
+
+       skb_put(tx_skb, 1);
+       tx_skb->data[0] = (__u8) (n & 0x7f);
+
+       irlmp_data_request(self->lsap, tx_skb);
+       self->stats.tx_packets++;
+}
+
+/*
+ * Function irttp_udata_indication (instance, sap, skb)
+ *
+ *    Received some unit-data (unreliable)
+ *
+ */
+static int irttp_udata_indication(void *instance, void *sap,
+                                 struct sk_buff *skb)
+{
+       struct tsap_cb *self;
+       int err;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
+       IRDA_ASSERT(skb != NULL, return -1;);
+
+       self->stats.rx_packets++;
+
+       /* Just pass data to layer above */
+       if (self->notify.udata_indication) {
+               err = self->notify.udata_indication(self->notify.instance,
+                                                   self, skb);
+               /* Same comment as in irttp_do_data_indication() */
+               if (!err)
+                       return 0;
+       }
+       /* Either no handler, or handler returns an error */
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+/*
+ * Function irttp_data_indication (instance, sap, skb)
+ *
+ *    Receive segment from IrLMP.
+ *
+ */
+static int irttp_data_indication(void *instance, void *sap,
+                                struct sk_buff *skb)
+{
+       struct tsap_cb *self;
+       unsigned long flags;
+       int n;
+
+       self = instance;
+
+       n = skb->data[0] & 0x7f;     /* Extract the credits */
+
+       self->stats.rx_packets++;
+
+       /*  Deal with inbound credit
+        *  Since we can transmit and receive frames concurrently,
+        *  the code below is a critical region and we must assure that
+        *  nobody messes with the credits while we update them.
+        */
+       spin_lock_irqsave(&self->lock, flags);
+       self->send_credit += n;
+       if (skb->len > 1)
+               self->remote_credit--;
+       spin_unlock_irqrestore(&self->lock, flags);
+
+       /*
+        *  Data or dataless packet? Dataless frames contains only the
+        *  TTP_HEADER.
+        */
+       if (skb->len > 1) {
+               /*
+                *  We don't remove the TTP header, since we must preserve the
+                *  more bit, so the defragment routing knows what to do
+                */
+               skb_queue_tail(&self->rx_queue, skb);
+       } else {
+               /* Dataless flowdata TTP-PDU */
+               dev_kfree_skb(skb);
+       }
+
+
+       /* Push data to the higher layer.
+        * We do it synchronously because running the todo timer for each
+        * receive packet would be too much overhead and latency.
+        * By passing control to the higher layer, we run the risk that
+        * it may take time or grab a lock. Most often, the higher layer
+        * will only put packet in a queue.
+        * Anyway, packets are only dripping through the IrDA, so we can
+        * have time before the next packet.
+        * Further, we are run from NET_BH, so the worse that can happen is
+        * us missing the optimal time to send back the PF bit in LAP.
+        * Jean II */
+       irttp_run_rx_queue(self);
+
+       /* We now give credits to peer in irttp_run_rx_queue().
+        * We need to send credit *NOW*, otherwise we are going
+        * to miss the next Tx window. The todo timer may take
+        * a while before it's run... - Jean II */
+
+       /*
+        * If the peer device has given us some credits and we didn't have
+        * anyone from before, then we need to shedule the tx queue.
+        * We need to do that because our Tx have stopped (so we may not
+        * get any LAP flow indication) and the user may be stopped as
+        * well. - Jean II
+        */
+       if (self->send_credit == n) {
+               /* Restart pushing stuff to LAP */
+               irttp_run_tx_queue(self);
+               /* Note : we don't want to schedule the todo timer
+                * because it has horrible latency. No tasklets
+                * because the tasklet API is broken. - Jean II */
+       }
+
+       return 0;
+}
+
+/*
+ * Function irttp_status_indication (self, reason)
+ *
+ *    Status_indication, just pass to the higher layer...
+ *
+ */
+static void irttp_status_indication(void *instance,
+                                   LINK_STATUS link, LOCK_STATUS lock)
+{
+       struct tsap_cb *self;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+       /* Check if client has already closed the TSAP and gone away */
+       if (self->close_pend)
+               return;
+
+       /*
+        *  Inform service user if he has requested it
+        */
+       if (self->notify.status_indication != NULL)
+               self->notify.status_indication(self->notify.instance,
+                                              link, lock);
+       else
+               pr_debug("%s(), no handler\n", __func__);
+}
+
+/*
+ * Function irttp_flow_indication (self, reason)
+ *
+ *    Flow_indication : IrLAP tells us to send more data.
+ *
+ */
+static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
+{
+       struct tsap_cb *self;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+       pr_debug("%s(instance=%p)\n", __func__, self);
+
+       /* We are "polled" directly from LAP, and the LAP want to fill
+        * its Tx window. We want to do our best to send it data, so that
+        * we maximise the window. On the other hand, we want to limit the
+        * amount of work here so that LAP doesn't hang forever waiting
+        * for packets. - Jean II */
+
+       /* Try to send some packets. Currently, LAP calls us every time
+        * there is one free slot, so we will send only one packet.
+        * This allow the scheduler to do its round robin - Jean II */
+       irttp_run_tx_queue(self);
+
+       /* Note regarding the interraction with higher layer.
+        * irttp_run_tx_queue() may call the client when its queue
+        * start to empty, via notify.flow_indication(). Initially.
+        * I wanted this to happen in a tasklet, to avoid client
+        * grabbing the CPU, but we can't use tasklets safely. And timer
+        * is definitely too slow.
+        * This will happen only once per LAP window, and usually at
+        * the third packet (unless window is smaller). LAP is still
+        * doing mtt and sending first packet so it's sort of OK
+        * to do that. Jean II */
+
+       /* If we need to send disconnect. try to do it now */
+       if (self->disconnect_pend)
+               irttp_start_todo_timer(self, 0);
+}
+
+/*
+ * Function irttp_flow_request (self, command)
+ *
+ *    This function could be used by the upper layers to tell IrTTP to stop
+ *    delivering frames if the receive queues are starting to get full, or
+ *    to tell IrTTP to start delivering frames again.
+ */
+void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow)
+{
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+       switch (flow) {
+       case FLOW_STOP:
+               pr_debug("%s(), flow stop\n", __func__);
+               self->rx_sdu_busy = TRUE;
+               break;
+       case FLOW_START:
+               pr_debug("%s(), flow start\n", __func__);
+               self->rx_sdu_busy = FALSE;
+
+               /* Client say he can accept more data, try to free our
+                * queues ASAP - Jean II */
+               irttp_run_rx_queue(self);
+
+               break;
+       default:
+               pr_debug("%s(), Unknown flow command!\n", __func__);
+       }
+}
+EXPORT_SYMBOL(irttp_flow_request);
+
+/*
+ * Function irttp_connect_request (self, dtsap_sel, daddr, qos)
+ *
+ *    Try to connect to remote destination TSAP selector
+ *
+ */
+int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
+                         __u32 saddr, __u32 daddr,
+                         struct qos_info *qos, __u32 max_sdu_size,
+                         struct sk_buff *userdata)
+{
+       struct sk_buff *tx_skb;
+       __u8 *frame;
+       __u8 n;
+
+       pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size);
+
+       IRDA_ASSERT(self != NULL, return -EBADR;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;);
+
+       if (self->connected) {
+               if (userdata)
+                       dev_kfree_skb(userdata);
+               return -EISCONN;
+       }
+
+       /* Any userdata supplied? */
+       if (userdata == NULL) {
+               tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER,
+                                  GFP_ATOMIC);
+               if (!tx_skb)
+                       return -ENOMEM;
+
+               /* Reserve space for MUX_CONTROL and LAP header */
+               skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER);
+       } else {
+               tx_skb = userdata;
+               /*
+                *  Check that the client has reserved enough space for
+                *  headers
+                */
+               IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER,
+                       { dev_kfree_skb(userdata); return -1; });
+       }
+
+       /* Initialize connection parameters */
+       self->connected = FALSE;
+       self->avail_credit = 0;
+       self->rx_max_sdu_size = max_sdu_size;
+       self->rx_sdu_size = 0;
+       self->rx_sdu_busy = FALSE;
+       self->dtsap_sel = dtsap_sel;
+
+       n = self->initial_credit;
+
+       self->remote_credit = 0;
+       self->send_credit = 0;
+
+       /*
+        *  Give away max 127 credits for now
+        */
+       if (n > 127) {
+               self->avail_credit = n - 127;
+               n = 127;
+       }
+
+       self->remote_credit = n;
+
+       /* SAR enabled? */
+       if (max_sdu_size > 0) {
+               IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER),
+                       { dev_kfree_skb(tx_skb); return -1; });
+
+               /* Insert SAR parameters */
+               frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER);
+
+               frame[0] = TTP_PARAMETERS | n;
+               frame[1] = 0x04; /* Length */
+               frame[2] = 0x01; /* MaxSduSize */
+               frame[3] = 0x02; /* Value length */
+
+               put_unaligned(cpu_to_be16((__u16) max_sdu_size),
+                             (__be16 *)(frame+4));
+       } else {
+               /* Insert plain TTP header */
+               frame = skb_push(tx_skb, TTP_HEADER);
+
+               /* Insert initial credit in frame */
+               frame[0] = n & 0x7f;
+       }
+
+       /* Connect with IrLMP. No QoS parameters for now */
+       return irlmp_connect_request(self->lsap, dtsap_sel, saddr, daddr, qos,
+                                    tx_skb);
+}
+EXPORT_SYMBOL(irttp_connect_request);
+
+/*
+ * Function irttp_connect_confirm (handle, qos, skb)
+ *
+ *    Service user confirms TSAP connection with peer.
+ *
+ */
+static void irttp_connect_confirm(void *instance, void *sap,
+                                 struct qos_info *qos, __u32 max_seg_size,
+                                 __u8 max_header_size, struct sk_buff *skb)
+{
+       struct tsap_cb *self;
+       int parameters;
+       int ret;
+       __u8 plen;
+       __u8 n;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       self->max_seg_size = max_seg_size - TTP_HEADER;
+       self->max_header_size = max_header_size + TTP_HEADER;
+
+       /*
+        *  Check if we have got some QoS parameters back! This should be the
+        *  negotiated QoS for the link.
+        */
+       if (qos) {
+               pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n",
+                        qos->baud_rate.bits);
+               pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n",
+                        qos->baud_rate.value);
+       }
+
+       n = skb->data[0] & 0x7f;
+
+       pr_debug("%s(), Initial send_credit=%d\n", __func__, n);
+
+       self->send_credit = n;
+       self->tx_max_sdu_size = 0;
+       self->connected = TRUE;
+
+       parameters = skb->data[0] & 0x80;
+
+       IRDA_ASSERT(skb->len >= TTP_HEADER, return;);
+       skb_pull(skb, TTP_HEADER);
+
+       if (parameters) {
+               plen = skb->data[0];
+
+               ret = irda_param_extract_all(self, skb->data+1,
+                                            IRDA_MIN(skb->len-1, plen),
+                                            &param_info);
+
+               /* Any errors in the parameter list? */
+               if (ret < 0) {
+                       net_warn_ratelimited("%s: error extracting parameters\n",
+                                            __func__);
+                       dev_kfree_skb(skb);
+
+                       /* Do not accept this connection attempt */
+                       return;
+               }
+               /* Remove parameters */
+               skb_pull(skb, IRDA_MIN(skb->len, plen+1));
+       }
+
+       pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__,
+                self->send_credit, self->avail_credit, self->remote_credit);
+
+       pr_debug("%s(), MaxSduSize=%d\n", __func__,
+                self->tx_max_sdu_size);
+
+       if (self->notify.connect_confirm) {
+               self->notify.connect_confirm(self->notify.instance, self, qos,
+                                            self->tx_max_sdu_size,
+                                            self->max_header_size, skb);
+       } else
+               dev_kfree_skb(skb);
+}
+
+/*
+ * Function irttp_connect_indication (handle, skb)
+ *
+ *    Some other device is connecting to this TSAP
+ *
+ */
+static void irttp_connect_indication(void *instance, void *sap,
+               struct qos_info *qos, __u32 max_seg_size, __u8 max_header_size,
+               struct sk_buff *skb)
+{
+       struct tsap_cb *self;
+       struct lsap_cb *lsap;
+       int parameters;
+       int ret;
+       __u8 plen;
+       __u8 n;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+       IRDA_ASSERT(skb != NULL, return;);
+
+       lsap = sap;
+
+       self->max_seg_size = max_seg_size - TTP_HEADER;
+       self->max_header_size = max_header_size+TTP_HEADER;
+
+       pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel);
+
+       /* Need to update dtsap_sel if its equal to LSAP_ANY */
+       self->dtsap_sel = lsap->dlsap_sel;
+
+       n = skb->data[0] & 0x7f;
+
+       self->send_credit = n;
+       self->tx_max_sdu_size = 0;
+
+       parameters = skb->data[0] & 0x80;
+
+       IRDA_ASSERT(skb->len >= TTP_HEADER, return;);
+       skb_pull(skb, TTP_HEADER);
+
+       if (parameters) {
+               plen = skb->data[0];
+
+               ret = irda_param_extract_all(self, skb->data+1,
+                                            IRDA_MIN(skb->len-1, plen),
+                                            &param_info);
+
+               /* Any errors in the parameter list? */
+               if (ret < 0) {
+                       net_warn_ratelimited("%s: error extracting parameters\n",
+                                            __func__);
+                       dev_kfree_skb(skb);
+
+                       /* Do not accept this connection attempt */
+                       return;
+               }
+
+               /* Remove parameters */
+               skb_pull(skb, IRDA_MIN(skb->len, plen+1));
+       }
+
+       if (self->notify.connect_indication) {
+               self->notify.connect_indication(self->notify.instance, self,
+                                               qos, self->tx_max_sdu_size,
+                                               self->max_header_size, skb);
+       } else
+               dev_kfree_skb(skb);
+}
+
+/*
+ * Function irttp_connect_response (handle, userdata)
+ *
+ *    Service user is accepting the connection, just pass it down to
+ *    IrLMP!
+ *
+ */
+int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
+                          struct sk_buff *userdata)
+{
+       struct sk_buff *tx_skb;
+       __u8 *frame;
+       int ret;
+       __u8 n;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
+
+       pr_debug("%s(), Source TSAP selector=%02x\n", __func__,
+                self->stsap_sel);
+
+       /* Any userdata supplied? */
+       if (userdata == NULL) {
+               tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER,
+                                  GFP_ATOMIC);
+               if (!tx_skb)
+                       return -ENOMEM;
+
+               /* Reserve space for MUX_CONTROL and LAP header */
+               skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER);
+       } else {
+               tx_skb = userdata;
+               /*
+                *  Check that the client has reserved enough space for
+                *  headers
+                */
+               IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER,
+                       { dev_kfree_skb(userdata); return -1; });
+       }
+
+       self->avail_credit = 0;
+       self->remote_credit = 0;
+       self->rx_max_sdu_size = max_sdu_size;
+       self->rx_sdu_size = 0;
+       self->rx_sdu_busy = FALSE;
+
+       n = self->initial_credit;
+
+       /* Frame has only space for max 127 credits (7 bits) */
+       if (n > 127) {
+               self->avail_credit = n - 127;
+               n = 127;
+       }
+
+       self->remote_credit = n;
+       self->connected = TRUE;
+
+       /* SAR enabled? */
+       if (max_sdu_size > 0) {
+               IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER),
+                       { dev_kfree_skb(tx_skb); return -1; });
+
+               /* Insert TTP header with SAR parameters */
+               frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER);
+
+               frame[0] = TTP_PARAMETERS | n;
+               frame[1] = 0x04; /* Length */
+
+               /* irda_param_insert(self, IRTTP_MAX_SDU_SIZE, frame+1,  */
+/*                               TTP_SAR_HEADER, &param_info) */
+
+               frame[2] = 0x01; /* MaxSduSize */
+               frame[3] = 0x02; /* Value length */
+
+               put_unaligned(cpu_to_be16((__u16) max_sdu_size),
+                             (__be16 *)(frame+4));
+       } else {
+               /* Insert TTP header */
+               frame = skb_push(tx_skb, TTP_HEADER);
+
+               frame[0] = n & 0x7f;
+       }
+
+       ret = irlmp_connect_response(self->lsap, tx_skb);
+
+       return ret;
+}
+EXPORT_SYMBOL(irttp_connect_response);
+
+/*
+ * Function irttp_dup (self, instance)
+ *
+ *    Duplicate TSAP, can be used by servers to confirm a connection on a
+ *    new TSAP so it can keep listening on the old one.
+ */
+struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
+{
+       struct tsap_cb *new;
+       unsigned long flags;
+
+       /* Protect our access to the old tsap instance */
+       spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags);
+
+       /* Find the old instance */
+       if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) {
+               pr_debug("%s(), unable to find TSAP\n", __func__);
+               spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
+               return NULL;
+       }
+
+       /* Allocate a new instance */
+       new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC);
+       if (!new) {
+               pr_debug("%s(), unable to kmalloc\n", __func__);
+               spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
+               return NULL;
+       }
+       spin_lock_init(&new->lock);
+
+       /* We don't need the old instance any more */
+       spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
+
+       /* Try to dup the LSAP (may fail if we were too slow) */
+       new->lsap = irlmp_dup(orig->lsap, new);
+       if (!new->lsap) {
+               pr_debug("%s(), dup failed!\n", __func__);
+               kfree(new);
+               return NULL;
+       }
+
+       /* Not everything should be copied */
+       new->notify.instance = instance;
+
+       /* Initialize internal objects */
+       irttp_init_tsap(new);
+
+       /* This is locked */
+       hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL);
+
+       return new;
+}
+EXPORT_SYMBOL(irttp_dup);
+
+/*
+ * Function irttp_disconnect_request (self)
+ *
+ *    Close this connection please! If priority is high, the queued data
+ *    segments, if any, will be deallocated first
+ *
+ */
+int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
+                            int priority)
+{
+       int ret;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
+
+       /* Already disconnected? */
+       if (!self->connected) {
+               pr_debug("%s(), already disconnected!\n", __func__);
+               if (userdata)
+                       dev_kfree_skb(userdata);
+               return -1;
+       }
+
+       /* Disconnect already pending ?
+        * We need to use an atomic operation to prevent reentry. This
+        * function may be called from various context, like user, timer
+        * for following a disconnect_indication() (i.e. net_bh).
+        * Jean II */
+       if (test_and_set_bit(0, &self->disconnect_pend)) {
+               pr_debug("%s(), disconnect already pending\n",
+                        __func__);
+               if (userdata)
+                       dev_kfree_skb(userdata);
+
+               /* Try to make some progress */
+               irttp_run_tx_queue(self);
+               return -1;
+       }
+
+       /*
+        *  Check if there is still data segments in the transmit queue
+        */
+       if (!skb_queue_empty(&self->tx_queue)) {
+               if (priority == P_HIGH) {
+                       /*
+                        *  No need to send the queued data, if we are
+                        *  disconnecting right now since the data will
+                        *  not have any usable connection to be sent on
+                        */
+                       pr_debug("%s(): High priority!!()\n", __func__);
+                       irttp_flush_queues(self);
+               } else if (priority == P_NORMAL) {
+                       /*
+                        *  Must delay disconnect until after all data segments
+                        *  have been sent and the tx_queue is empty
+                        */
+                       /* We'll reuse this one later for the disconnect */
+                       self->disconnect_skb = userdata;  /* May be NULL */
+
+                       irttp_run_tx_queue(self);
+
+                       irttp_start_todo_timer(self, HZ/10);
+                       return -1;
+               }
+       }
+       /* Note : we don't need to check if self->rx_queue is full and the
+        * state of self->rx_sdu_busy because the disconnect response will
+        * be sent at the LMP level (so even if the peer has its Tx queue
+        * full of data). - Jean II */
+
+       pr_debug("%s(), Disconnecting ...\n", __func__);
+       self->connected = FALSE;
+
+       if (!userdata) {
+               struct sk_buff *tx_skb;
+               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
+               if (!tx_skb)
+                       return -ENOMEM;
+
+               /*
+                *  Reserve space for MUX and LAP header
+                */
+               skb_reserve(tx_skb, LMP_MAX_HEADER);
+
+               userdata = tx_skb;
+       }
+       ret = irlmp_disconnect_request(self->lsap, userdata);
+
+       /* The disconnect is no longer pending */
+       clear_bit(0, &self->disconnect_pend);   /* FALSE */
+
+       return ret;
+}
+EXPORT_SYMBOL(irttp_disconnect_request);
+
+/*
+ * Function irttp_disconnect_indication (self, reason)
+ *
+ *    Disconnect indication, TSAP disconnected by peer?
+ *
+ */
+static void irttp_disconnect_indication(void *instance, void *sap,
+               LM_REASON reason, struct sk_buff *skb)
+{
+       struct tsap_cb *self;
+
+       self = instance;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+       /* Prevent higher layer to send more data */
+       self->connected = FALSE;
+
+       /* Check if client has already tried to close the TSAP */
+       if (self->close_pend) {
+               /* In this case, the higher layer is probably gone. Don't
+                * bother it and clean up the remains - Jean II */
+               if (skb)
+                       dev_kfree_skb(skb);
+               irttp_close_tsap(self);
+               return;
+       }
+
+       /* If we are here, we assume that is the higher layer is still
+        * waiting for the disconnect notification and able to process it,
+        * even if he tried to disconnect. Otherwise, it would have already
+        * attempted to close the tsap and self->close_pend would be TRUE.
+        * Jean II */
+
+       /* No need to notify the client if has already tried to disconnect */
+       if (self->notify.disconnect_indication)
+               self->notify.disconnect_indication(self->notify.instance, self,
+                                                  reason, skb);
+       else
+               if (skb)
+                       dev_kfree_skb(skb);
+}
+
+/*
+ * Function irttp_do_data_indication (self, skb)
+ *
+ *    Try to deliver reassembled skb to layer above, and requeue it if that
+ *    for some reason should fail. We mark rx sdu as busy to apply back
+ *    pressure is necessary.
+ */
+static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb)
+{
+       int err;
+
+       /* Check if client has already closed the TSAP and gone away */
+       if (self->close_pend) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       err = self->notify.data_indication(self->notify.instance, self, skb);
+
+       /* Usually the layer above will notify that it's input queue is
+        * starting to get filled by using the flow request, but this may
+        * be difficult, so it can instead just refuse to eat it and just
+        * give an error back
+        */
+       if (err) {
+               pr_debug("%s() requeueing skb!\n", __func__);
+
+               /* Make sure we take a break */
+               self->rx_sdu_busy = TRUE;
+
+               /* Need to push the header in again */
+               skb_push(skb, TTP_HEADER);
+               skb->data[0] = 0x00; /* Make sure MORE bit is cleared */
+
+               /* Put skb back on queue */
+               skb_queue_head(&self->rx_queue, skb);
+       }
+}
+
+/*
+ * Function irttp_run_rx_queue (self)
+ *
+ *     Check if we have any frames to be transmitted, or if we have any
+ *     available credit to give away.
+ */
+static void irttp_run_rx_queue(struct tsap_cb *self)
+{
+       struct sk_buff *skb;
+       int more = 0;
+
+       pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__,
+                self->send_credit, self->avail_credit, self->remote_credit);
+
+       /* Get exclusive access to the rx queue, otherwise don't touch it */
+       if (irda_lock(&self->rx_queue_lock) == FALSE)
+               return;
+
+       /*
+        *  Reassemble all frames in receive queue and deliver them
+        */
+       while (!self->rx_sdu_busy && (skb = skb_dequeue(&self->rx_queue))) {
+               /* This bit will tell us if it's the last fragment or not */
+               more = skb->data[0] & 0x80;
+
+               /* Remove TTP header */
+               skb_pull(skb, TTP_HEADER);
+
+               /* Add the length of the remaining data */
+               self->rx_sdu_size += skb->len;
+
+               /*
+                * If SAR is disabled, or user has requested no reassembly
+                * of received fragments then we just deliver them
+                * immediately. This can be requested by clients that
+                * implements byte streams without any message boundaries
+                */
+               if (self->rx_max_sdu_size == TTP_SAR_DISABLE) {
+                       irttp_do_data_indication(self, skb);
+                       self->rx_sdu_size = 0;
+
+                       continue;
+               }
+
+               /* Check if this is a fragment, and not the last fragment */
+               if (more) {
+                       /*
+                        *  Queue the fragment if we still are within the
+                        *  limits of the maximum size of the rx_sdu
+                        */
+                       if (self->rx_sdu_size <= self->rx_max_sdu_size) {
+                               pr_debug("%s(), queueing frag\n",
+                                        __func__);
+                               skb_queue_tail(&self->rx_fragments, skb);
+                       } else {
+                               /* Free the part of the SDU that is too big */
+                               dev_kfree_skb(skb);
+                       }
+                       continue;
+               }
+               /*
+                *  This is the last fragment, so time to reassemble!
+                */
+               if ((self->rx_sdu_size <= self->rx_max_sdu_size) ||
+                   (self->rx_max_sdu_size == TTP_SAR_UNBOUND)) {
+                       /*
+                        * A little optimizing. Only queue the fragment if
+                        * there are other fragments. Since if this is the
+                        * last and only fragment, there is no need to
+                        * reassemble :-)
+                        */
+                       if (!skb_queue_empty(&self->rx_fragments)) {
+                               skb_queue_tail(&self->rx_fragments,
+                                              skb);
+
+                               skb = irttp_reassemble_skb(self);
+                       }
+
+                       /* Now we can deliver the reassembled skb */
+                       irttp_do_data_indication(self, skb);
+               } else {
+                       pr_debug("%s(), Truncated frame\n", __func__);
+
+                       /* Free the part of the SDU that is too big */
+                       dev_kfree_skb(skb);
+
+                       /* Deliver only the valid but truncated part of SDU */
+                       skb = irttp_reassemble_skb(self);
+
+                       irttp_do_data_indication(self, skb);
+               }
+               self->rx_sdu_size = 0;
+       }
+
+       /*
+        * It's not trivial to keep track of how many credits are available
+        * by incrementing at each packet, because delivery may fail
+        * (irttp_do_data_indication() may requeue the frame) and because
+        * we need to take care of fragmentation.
+        * We want the other side to send up to initial_credit packets.
+        * We have some frames in our queues, and we have already allowed it
+        * to send remote_credit.
+        * No need to spinlock, write is atomic and self correcting...
+        * Jean II
+        */
+       self->avail_credit = (self->initial_credit -
+                             (self->remote_credit +
+                              skb_queue_len(&self->rx_queue) +
+                              skb_queue_len(&self->rx_fragments)));
+
+       /* Do we have too much credits to send to peer ? */
+       if ((self->remote_credit <= TTP_RX_MIN_CREDIT) &&
+           (self->avail_credit > 0)) {
+               /* Send explicit credit frame */
+               irttp_give_credit(self);
+               /* Note : do *NOT* check if tx_queue is non-empty, that
+                * will produce deadlocks. I repeat : send a credit frame
+                * even if we have something to send in our Tx queue.
+                * If we have credits, it means that our Tx queue is blocked.
+                *
+                * Let's suppose the peer can't keep up with our Tx. He will
+                * flow control us by not sending us any credits, and we
+                * will stop Tx and start accumulating credits here.
+                * Up to the point where the peer will stop its Tx queue,
+                * for lack of credits.
+                * Let's assume the peer application is single threaded.
+                * It will block on Tx and never consume any Rx buffer.
+                * Deadlock. Guaranteed. - Jean II
+                */
+       }
+
+       /* Reset lock */
+       self->rx_queue_lock = 0;
+}
+
+#ifdef CONFIG_PROC_FS
+struct irttp_iter_state {
+       int id;
+};
+
+static void *irttp_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       struct irttp_iter_state *iter = seq->private;
+       struct tsap_cb *self;
+
+       /* Protect our access to the tsap list */
+       spin_lock_irq(&irttp->tsaps->hb_spinlock);
+       iter->id = 0;
+
+       for (self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps);
+            self != NULL;
+            self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps)) {
+               if (iter->id == *pos)
+                       break;
+               ++iter->id;
+       }
+
+       return self;
+}
+
+static void *irttp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       struct irttp_iter_state *iter = seq->private;
+
+       ++*pos;
+       ++iter->id;
+       return (void *) hashbin_get_next(irttp->tsaps);
+}
+
+static void irttp_seq_stop(struct seq_file *seq, void *v)
+{
+       spin_unlock_irq(&irttp->tsaps->hb_spinlock);
+}
+
+static int irttp_seq_show(struct seq_file *seq, void *v)
+{
+       const struct irttp_iter_state *iter = seq->private;
+       const struct tsap_cb *self = v;
+
+       seq_printf(seq, "TSAP %d, ", iter->id);
+       seq_printf(seq, "stsap_sel: %02x, ",
+                  self->stsap_sel);
+       seq_printf(seq, "dtsap_sel: %02x\n",
+                  self->dtsap_sel);
+       seq_printf(seq, "  connected: %s, ",
+                  self->connected ? "TRUE" : "FALSE");
+       seq_printf(seq, "avail credit: %d, ",
+                  self->avail_credit);
+       seq_printf(seq, "remote credit: %d, ",
+                  self->remote_credit);
+       seq_printf(seq, "send credit: %d\n",
+                  self->send_credit);
+       seq_printf(seq, "  tx packets: %lu, ",
+                  self->stats.tx_packets);
+       seq_printf(seq, "rx packets: %lu, ",
+                  self->stats.rx_packets);
+       seq_printf(seq, "tx_queue len: %u ",
+                  skb_queue_len(&self->tx_queue));
+       seq_printf(seq, "rx_queue len: %u\n",
+                  skb_queue_len(&self->rx_queue));
+       seq_printf(seq, "  tx_sdu_busy: %s, ",
+                  self->tx_sdu_busy ? "TRUE" : "FALSE");
+       seq_printf(seq, "rx_sdu_busy: %s\n",
+                  self->rx_sdu_busy ? "TRUE" : "FALSE");
+       seq_printf(seq, "  max_seg_size: %u, ",
+                  self->max_seg_size);
+       seq_printf(seq, "tx_max_sdu_size: %u, ",
+                  self->tx_max_sdu_size);
+       seq_printf(seq, "rx_max_sdu_size: %u\n",
+                  self->rx_max_sdu_size);
+
+       seq_printf(seq, "  Used by (%s)\n\n",
+                  self->notify.name);
+       return 0;
+}
+
+static const struct seq_operations irttp_seq_ops = {
+       .start  = irttp_seq_start,
+       .next   = irttp_seq_next,
+       .stop   = irttp_seq_stop,
+       .show   = irttp_seq_show,
+};
+
+static int irttp_seq_open(struct inode *inode, struct file *file)
+{
+       return seq_open_private(file, &irttp_seq_ops,
+                       sizeof(struct irttp_iter_state));
+}
+
+const struct file_operations irttp_seq_fops = {
+       .owner          = THIS_MODULE,
+       .open           = irttp_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release_private,
+};
+
+#endif /* PROC_FS */
diff --git a/drivers/staging/irda/net/parameters.c b/drivers/staging/irda/net/parameters.c
new file mode 100644 (file)
index 0000000..16ce32f
--- /dev/null
@@ -0,0 +1,584 @@
+/*********************************************************************
+ *
+ * Filename:      parameters.c
+ * Version:       1.0
+ * Description:   A more general way to handle (pi,pl,pv) parameters
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Mon Jun  7 10:25:11 1999
+ * Modified at:   Sun Jan 30 14:08:39 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/types.h>
+#include <linux/module.h>
+
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/parameters.h>
+
+static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
+                               PV_TYPE type, PI_HANDLER func);
+static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
+                              PV_TYPE type, PI_HANDLER func);
+static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
+                              PV_TYPE type, PI_HANDLER func);
+static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi,
+                                PV_TYPE type, PI_HANDLER func);
+
+static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
+                              PV_TYPE type, PI_HANDLER func);
+static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
+                               PV_TYPE type, PI_HANDLER func);
+
+static int irda_param_unpack(__u8 *buf, char *fmt, ...);
+
+/* Parameter value call table. Must match PV_TYPE */
+static const PV_HANDLER pv_extract_table[] = {
+       irda_extract_integer, /* Handler for any length integers */
+       irda_extract_integer, /* Handler for 8  bits integers */
+       irda_extract_integer, /* Handler for 16 bits integers */
+       irda_extract_string,  /* Handler for strings */
+       irda_extract_integer, /* Handler for 32 bits integers */
+       irda_extract_octseq,  /* Handler for octet sequences */
+       irda_extract_no_value /* Handler for no value parameters */
+};
+
+static const PV_HANDLER pv_insert_table[] = {
+       irda_insert_integer, /* Handler for any length integers */
+       irda_insert_integer, /* Handler for 8  bits integers */
+       irda_insert_integer, /* Handler for 16 bits integers */
+       NULL,                /* Handler for strings */
+       irda_insert_integer, /* Handler for 32 bits integers */
+       NULL,                /* Handler for octet sequences */
+       irda_insert_no_value /* Handler for no value parameters */
+};
+
+/*
+ * Function irda_insert_no_value (self, buf, len, pi, type, func)
+ */
+static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
+                               PV_TYPE type, PI_HANDLER func)
+{
+       irda_param_t p;
+       int ret;
+
+       p.pi = pi;
+       p.pl = 0;
+
+       /* Call handler for this parameter */
+       ret = (*func)(self, &p, PV_GET);
+
+       /* Extract values anyway, since handler may need them */
+       irda_param_pack(buf, "bb", p.pi, p.pl);
+
+       if (ret < 0)
+               return ret;
+
+       return 2; /* Inserted pl+2 bytes */
+}
+
+/*
+ * Function irda_extract_no_value (self, buf, len, type, func)
+ *
+ *    Extracts a parameter without a pv field (pl=0)
+ *
+ */
+static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi,
+                                PV_TYPE type, PI_HANDLER func)
+{
+       irda_param_t p;
+       int ret;
+
+       /* Extract values anyway, since handler may need them */
+       irda_param_unpack(buf, "bb", &p.pi, &p.pl);
+
+       /* Call handler for this parameter */
+       ret = (*func)(self, &p, PV_PUT);
+
+       if (ret < 0)
+               return ret;
+
+       return 2; /* Extracted pl+2 bytes */
+}
+
+/*
+ * Function irda_insert_integer (self, buf, len, pi, type, func)
+ */
+static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
+                              PV_TYPE type, PI_HANDLER func)
+{
+       irda_param_t p;
+       int n = 0;
+       int err;
+
+       p.pi = pi;             /* In case handler needs to know */
+       p.pl = type & PV_MASK; /* The integer type codes the length as well */
+       p.pv.i = 0;            /* Clear value */
+
+       /* Call handler for this parameter */
+       err = (*func)(self, &p, PV_GET);
+       if (err < 0)
+               return err;
+
+       /*
+        * If parameter length is still 0, then (1) this is an any length
+        * integer, and (2) the handler function does not care which length
+        * we choose to use, so we pick the one the gives the fewest bytes.
+        */
+       if (p.pl == 0) {
+               if (p.pv.i < 0xff) {
+                       pr_debug("%s(), using 1 byte\n", __func__);
+                       p.pl = 1;
+               } else if (p.pv.i < 0xffff) {
+                       pr_debug("%s(), using 2 bytes\n", __func__);
+                       p.pl = 2;
+               } else {
+                       pr_debug("%s(), using 4 bytes\n", __func__);
+                       p.pl = 4; /* Default length */
+               }
+       }
+       /* Check if buffer is long enough for insertion */
+       if (len < (2+p.pl)) {
+               net_warn_ratelimited("%s: buffer too short for insertion!\n",
+                                    __func__);
+               return -1;
+       }
+       pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
+                p.pi, p.pl, p.pv.i);
+       switch (p.pl) {
+       case 1:
+               n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i);
+               break;
+       case 2:
+               if (type & PV_BIG_ENDIAN)
+                       p.pv.i = cpu_to_be16((__u16) p.pv.i);
+               else
+                       p.pv.i = cpu_to_le16((__u16) p.pv.i);
+               n += irda_param_pack(buf, "bbs", p.pi, p.pl, (__u16) p.pv.i);
+               break;
+       case 4:
+               if (type & PV_BIG_ENDIAN)
+                       cpu_to_be32s(&p.pv.i);
+               else
+                       cpu_to_le32s(&p.pv.i);
+               n += irda_param_pack(buf, "bbi", p.pi, p.pl, p.pv.i);
+
+               break;
+       default:
+               net_warn_ratelimited("%s: length %d not supported\n",
+                                    __func__, p.pl);
+               /* Skip parameter */
+               return -1;
+       }
+
+       return p.pl+2; /* Inserted pl+2 bytes */
+}
+
+/*
+ * Function irda_extract integer (self, buf, len, pi, type, func)
+ *
+ *    Extract a possibly variable length integer from buffer, and call
+ *    handler for processing of the parameter
+ */
+static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
+                               PV_TYPE type, PI_HANDLER func)
+{
+       irda_param_t p;
+       int n = 0;
+       int extract_len;        /* Real length we extract */
+       int err;
+
+       p.pi = pi;     /* In case handler needs to know */
+       p.pl = buf[1]; /* Extract length of value */
+       p.pv.i = 0;    /* Clear value */
+       extract_len = p.pl;     /* Default : extract all */
+
+       /* Check if buffer is long enough for parsing */
+       if (len < (2+p.pl)) {
+               net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
+                                    __func__, p.pl, len);
+               return -1;
+       }
+
+       /*
+        * Check that the integer length is what we expect it to be. If the
+        * handler want a 16 bits integer then a 32 bits is not good enough
+        * PV_INTEGER means that the handler is flexible.
+        */
+       if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) {
+               net_err_ratelimited("%s: invalid parameter length! Expected %d bytes, but value had %d bytes!\n",
+                                   __func__, type & PV_MASK, p.pl);
+
+               /* Most parameters are bit/byte fields or little endian,
+                * so it's ok to only extract a subset of it (the subset
+                * that the handler expect). This is necessary, as some
+                * broken implementations seems to add extra undefined bits.
+                * If the parameter is shorter than we expect or is big
+                * endian, we can't play those tricks. Jean II */
+               if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) {
+                       /* Skip parameter */
+                       return p.pl+2;
+               } else {
+                       /* Extract subset of it, fallthrough */
+                       extract_len = type & PV_MASK;
+               }
+       }
+
+
+       switch (extract_len) {
+       case 1:
+               n += irda_param_unpack(buf+2, "b", &p.pv.i);
+               break;
+       case 2:
+               n += irda_param_unpack(buf+2, "s", &p.pv.i);
+               if (type & PV_BIG_ENDIAN)
+                       p.pv.i = be16_to_cpu((__u16) p.pv.i);
+               else
+                       p.pv.i = le16_to_cpu((__u16) p.pv.i);
+               break;
+       case 4:
+               n += irda_param_unpack(buf+2, "i", &p.pv.i);
+               if (type & PV_BIG_ENDIAN)
+                       be32_to_cpus(&p.pv.i);
+               else
+                       le32_to_cpus(&p.pv.i);
+               break;
+       default:
+               net_warn_ratelimited("%s: length %d not supported\n",
+                                    __func__, p.pl);
+
+               /* Skip parameter */
+               return p.pl+2;
+       }
+
+       pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
+                p.pi, p.pl, p.pv.i);
+       /* Call handler for this parameter */
+       err = (*func)(self, &p, PV_PUT);
+       if (err < 0)
+               return err;
+
+       return p.pl+2; /* Extracted pl+2 bytes */
+}
+
+/*
+ * Function irda_extract_string (self, buf, len, type, func)
+ */
+static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
+                              PV_TYPE type, PI_HANDLER func)
+{
+       char str[33];
+       irda_param_t p;
+       int err;
+
+       p.pi = pi;     /* In case handler needs to know */
+       p.pl = buf[1]; /* Extract length of value */
+       if (p.pl > 32)
+               p.pl = 32;
+
+       pr_debug("%s(), pi=%#x, pl=%d\n", __func__,
+                p.pi, p.pl);
+
+       /* Check if buffer is long enough for parsing */
+       if (len < (2+p.pl)) {
+               net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
+                                    __func__, p.pl, len);
+               return -1;
+       }
+
+       /* Should be safe to copy string like this since we have already
+        * checked that the buffer is long enough */
+       strncpy(str, buf+2, p.pl);
+
+       pr_debug("%s(), str=0x%02x 0x%02x\n",
+                __func__, (__u8)str[0], (__u8)str[1]);
+
+       /* Null terminate string */
+       str[p.pl] = '\0';
+
+       p.pv.c = str; /* Handler will need to take a copy */
+
+       /* Call handler for this parameter */
+       err = (*func)(self, &p, PV_PUT);
+       if (err < 0)
+               return err;
+
+       return p.pl+2; /* Extracted pl+2 bytes */
+}
+
+/*
+ * Function irda_extract_octseq (self, buf, len, type, func)
+ */
+static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
+                              PV_TYPE type, PI_HANDLER func)
+{
+       irda_param_t p;
+
+       p.pi = pi;     /* In case handler needs to know */
+       p.pl = buf[1]; /* Extract length of value */
+
+       /* Check if buffer is long enough for parsing */
+       if (len < (2+p.pl)) {
+               net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
+                                    __func__, p.pl, len);
+               return -1;
+       }
+
+       pr_debug("%s(), not impl\n", __func__);
+
+       return p.pl+2; /* Extracted pl+2 bytes */
+}
+
+/*
+ * Function irda_param_pack (skb, fmt, ...)
+ *
+ *    Format:
+ *        'i' = 32 bits integer
+ *        's' = string
+ *
+ */
+int irda_param_pack(__u8 *buf, char *fmt, ...)
+{
+       irda_pv_t arg;
+       va_list args;
+       char *p;
+       int n = 0;
+
+       va_start(args, fmt);
+
+       for (p = fmt; *p != '\0'; p++) {
+               switch (*p) {
+               case 'b':  /* 8 bits unsigned byte */
+                       buf[n++] = (__u8)va_arg(args, int);
+                       break;
+               case 's':  /* 16 bits unsigned short */
+                       arg.i = (__u16)va_arg(args, int);
+                       put_unaligned((__u16)arg.i, (__u16 *)(buf+n)); n+=2;
+                       break;
+               case 'i':  /* 32 bits unsigned integer */
+                       arg.i = va_arg(args, __u32);
+                       put_unaligned(arg.i, (__u32 *)(buf+n)); n+=4;
+                       break;
+#if 0
+               case 'c': /* \0 terminated string */
+                       arg.c = va_arg(args, char *);
+                       strcpy(buf+n, arg.c);
+                       n += strlen(arg.c) + 1;
+                       break;
+#endif
+               default:
+                       va_end(args);
+                       return -1;
+               }
+       }
+       va_end(args);
+
+       return 0;
+}
+EXPORT_SYMBOL(irda_param_pack);
+
+/*
+ * Function irda_param_unpack (skb, fmt, ...)
+ */
+static int irda_param_unpack(__u8 *buf, char *fmt, ...)
+{
+       irda_pv_t arg;
+       va_list args;
+       char *p;
+       int n = 0;
+
+       va_start(args, fmt);
+
+       for (p = fmt; *p != '\0'; p++) {
+               switch (*p) {
+               case 'b':  /* 8 bits byte */
+                       arg.ip = va_arg(args, __u32 *);
+                       *arg.ip = buf[n++];
+                       break;
+               case 's':  /* 16 bits short */
+                       arg.ip = va_arg(args, __u32 *);
+                       *arg.ip = get_unaligned((__u16 *)(buf+n)); n+=2;
+                       break;
+               case 'i':  /* 32 bits unsigned integer */
+                       arg.ip = va_arg(args, __u32 *);
+                       *arg.ip = get_unaligned((__u32 *)(buf+n)); n+=4;
+                       break;
+#if 0
+               case 'c':   /* \0 terminated string */
+                       arg.c = va_arg(args, char *);
+                       strcpy(arg.c, buf+n);
+                       n += strlen(arg.c) + 1;
+                       break;
+#endif
+               default:
+                       va_end(args);
+                       return -1;
+               }
+
+       }
+       va_end(args);
+
+       return 0;
+}
+
+/*
+ * Function irda_param_insert (self, pi, buf, len, info)
+ *
+ *    Insert the specified parameter (pi) into buffer. Returns number of
+ *    bytes inserted
+ */
+int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
+                     pi_param_info_t *info)
+{
+       const pi_minor_info_t *pi_minor_info;
+       __u8 pi_minor;
+       __u8 pi_major;
+       int type;
+       int ret = -1;
+       int n = 0;
+
+       IRDA_ASSERT(buf != NULL, return ret;);
+       IRDA_ASSERT(info != NULL, return ret;);
+
+       pi_minor = pi & info->pi_mask;
+       pi_major = pi >> info->pi_major_offset;
+
+       /* Check if the identifier value (pi) is valid */
+       if ((pi_major > info->len-1) ||
+           (pi_minor > info->tables[pi_major].len-1))
+       {
+               pr_debug("%s(), no handler for parameter=0x%02x\n",
+                        __func__, pi);
+
+               /* Skip this parameter */
+               return -1;
+       }
+
+       /* Lookup the info on how to parse this parameter */
+       pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor];
+
+       /* Find expected data type for this parameter identifier (pi)*/
+       type = pi_minor_info->type;
+
+       /*  Check if handler has been implemented */
+       if (!pi_minor_info->func) {
+               net_info_ratelimited("%s: no handler for pi=%#x\n",
+                                    __func__, pi);
+               /* Skip this parameter */
+               return -1;
+       }
+
+       /* Insert parameter value */
+       ret = (*pv_insert_table[type & PV_MASK])(self, buf+n, len, pi, type,
+                                                pi_minor_info->func);
+       return ret;
+}
+EXPORT_SYMBOL(irda_param_insert);
+
+/*
+ * Function irda_param_extract (self, buf, len, info)
+ *
+ *    Parse all parameters. If len is correct, then everything should be
+ *    safe. Returns the number of bytes that was parsed
+ *
+ */
+static int irda_param_extract(void *self, __u8 *buf, int len,
+                             pi_param_info_t *info)
+{
+       const pi_minor_info_t *pi_minor_info;
+       __u8 pi_minor;
+       __u8 pi_major;
+       int type;
+       int ret = -1;
+       int n = 0;
+
+       IRDA_ASSERT(buf != NULL, return ret;);
+       IRDA_ASSERT(info != NULL, return ret;);
+
+       pi_minor = buf[n] & info->pi_mask;
+       pi_major = buf[n] >> info->pi_major_offset;
+
+       /* Check if the identifier value (pi) is valid */
+       if ((pi_major > info->len-1) ||
+           (pi_minor > info->tables[pi_major].len-1))
+       {
+               pr_debug("%s(), no handler for parameter=0x%02x\n",
+                        __func__, buf[0]);
+
+               /* Skip this parameter */
+               return 2 + buf[n + 1];  /* Continue */
+       }
+
+       /* Lookup the info on how to parse this parameter */
+       pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor];
+
+       /* Find expected data type for this parameter identifier (pi)*/
+       type = pi_minor_info->type;
+
+       pr_debug("%s(), pi=[%d,%d], type=%d\n", __func__,
+                pi_major, pi_minor, type);
+
+       /*  Check if handler has been implemented */
+       if (!pi_minor_info->func) {
+               net_info_ratelimited("%s: no handler for pi=%#x\n",
+                                    __func__, buf[n]);
+               /* Skip this parameter */
+               return 2 + buf[n + 1]; /* Continue */
+       }
+
+       /* Parse parameter value */
+       ret = (*pv_extract_table[type & PV_MASK])(self, buf+n, len, buf[n],
+                                                 type, pi_minor_info->func);
+       return ret;
+}
+
+/*
+ * Function irda_param_extract_all (self, buf, len, info)
+ *
+ *    Parse all parameters. If len is correct, then everything should be
+ *    safe. Returns the number of bytes that was parsed
+ *
+ */
+int irda_param_extract_all(void *self, __u8 *buf, int len,
+                          pi_param_info_t *info)
+{
+       int ret = -1;
+       int n = 0;
+
+       IRDA_ASSERT(buf != NULL, return ret;);
+       IRDA_ASSERT(info != NULL, return ret;);
+
+       /*
+        * Parse all parameters. Each parameter must be at least two bytes
+        * long or else there is no point in trying to parse it
+        */
+       while (len > 2) {
+               ret = irda_param_extract(self, buf+n, len, info);
+               if (ret < 0)
+                       return ret;
+
+               n += ret;
+               len -= ret;
+       }
+       return n;
+}
+EXPORT_SYMBOL(irda_param_extract_all);
diff --git a/drivers/staging/irda/net/qos.c b/drivers/staging/irda/net/qos.c
new file mode 100644 (file)
index 0000000..25ba850
--- /dev/null
@@ -0,0 +1,771 @@
+/*********************************************************************
+ *
+ * Filename:      qos.c
+ * Version:       1.0
+ * Description:   IrLAP QoS parameter negotiation
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Tue Sep  9 00:00:26 1997
+ * Modified at:   Sun Jan 30 14:29:16 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ ********************************************************************/
+
+#include <linux/export.h>
+
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/parameters.h>
+#include <net/irda/qos.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irlap_frame.h>
+
+/*
+ * Maximum values of the baud rate we negotiate with the other end.
+ * Most often, you don't have to change that, because Linux-IrDA will
+ * use the maximum offered by the link layer, which usually works fine.
+ * In some very rare cases, you may want to limit it to lower speeds...
+ */
+int sysctl_max_baud_rate = 16000000;
+/*
+ * Maximum value of the lap disconnect timer we negotiate with the other end.
+ * Most often, the value below represent the best compromise, but some user
+ * may want to keep the LAP alive longer or shorter in case of link failure.
+ * Remember that the threshold time (early warning) is fixed to 3s...
+ */
+int sysctl_max_noreply_time = 12;
+/*
+ * Minimum turn time to be applied before transmitting to the peer.
+ * Nonzero values (usec) are used as lower limit to the per-connection
+ * mtt value which was announced by the other end during negotiation.
+ * Might be helpful if the peer device provides too short mtt.
+ * Default is 10us which means using the unmodified value given by the
+ * peer except if it's 0 (0 is likely a bug in the other stack).
+ */
+unsigned int sysctl_min_tx_turn_time = 10;
+/*
+ * Maximum data size to be used in transmission in payload of LAP frame.
+ * There is a bit of confusion in the IrDA spec :
+ * The LAP spec defines the payload of a LAP frame (I field) to be
+ * 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40).
+ * On the other hand, the PHY mention frames of 2048 bytes max (IrPHY
+ * 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header
+ * (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP
+ * payload), that's only 2042 bytes. Oups !
+ * My nsc-ircc hardware has troubles receiving 2048 bytes frames at 4 Mb/s,
+ * so adjust to 2042... I don't know if this bug applies only for 2048
+ * bytes frames or all negotiated frame sizes, but you can use the sysctl
+ * to play with this value anyway.
+ * Jean II */
+unsigned int sysctl_max_tx_data_size = 2042;
+/*
+ * Maximum transmit window, i.e. number of LAP frames between turn-around.
+ * This allow to override what the peer told us. Some peers are buggy and
+ * don't always support what they tell us.
+ * Jean II */
+unsigned int sysctl_max_tx_window = 7;
+
+static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
+static int irlap_param_link_disconnect(void *instance, irda_param_t *parm,
+                                      int get);
+static int irlap_param_max_turn_time(void *instance, irda_param_t *param,
+                                    int get);
+static int irlap_param_data_size(void *instance, irda_param_t *param, int get);
+static int irlap_param_window_size(void *instance, irda_param_t *param,
+                                  int get);
+static int irlap_param_additional_bofs(void *instance, irda_param_t *parm,
+                                      int get);
+static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
+                                    int get);
+
+#ifndef CONFIG_IRDA_DYNAMIC_WINDOW
+static __u32 irlap_requested_line_capacity(struct qos_info *qos);
+#endif
+
+static __u32 min_turn_times[]  = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */
+static __u32 baud_rates[]      = { 2400, 9600, 19200, 38400, 57600, 115200, 576000,
+                                  1152000, 4000000, 16000000 };           /* bps */
+static __u32 data_sizes[]      = { 64, 128, 256, 512, 1024, 2048 };        /* bytes */
+static __u32 add_bofs[]        = { 48, 24, 12, 5, 3, 2, 1, 0 };            /* bytes */
+static __u32 max_turn_times[]  = { 500, 250, 100, 50 };                    /* ms */
+static __u32 link_disc_times[] = { 3, 8, 12, 16, 20, 25, 30, 40 };         /* secs */
+
+static __u32 max_line_capacities[10][4] = {
+       /* 500 ms     250 ms  100 ms  50 ms (max turn time) */
+       {    100,      0,      0,     0 }, /*     2400 bps */
+       {    400,      0,      0,     0 }, /*     9600 bps */
+       {    800,      0,      0,     0 }, /*    19200 bps */
+       {   1600,      0,      0,     0 }, /*    38400 bps */
+       {   2360,      0,      0,     0 }, /*    57600 bps */
+       {   4800,   2400,    960,   480 }, /*   115200 bps */
+       {  28800,  11520,   5760,  2880 }, /*   576000 bps */
+       {  57600,  28800,  11520,  5760 }, /*  1152000 bps */
+       { 200000, 100000,  40000, 20000 }, /*  4000000 bps */
+       { 800000, 400000, 160000, 80000 }, /* 16000000 bps */
+};
+
+static const pi_minor_info_t pi_minor_call_table_type_0[] = {
+       { NULL, 0 },
+/* 01 */{ irlap_param_baud_rate,       PV_INTEGER | PV_LITTLE_ENDIAN },
+       { NULL, 0 },
+       { NULL, 0 },
+       { NULL, 0 },
+       { NULL, 0 },
+       { NULL, 0 },
+       { NULL, 0 },
+/* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS }
+};
+
+static const pi_minor_info_t pi_minor_call_table_type_1[] = {
+       { NULL, 0 },
+       { NULL, 0 },
+/* 82 */{ irlap_param_max_turn_time,   PV_INT_8_BITS },
+/* 83 */{ irlap_param_data_size,       PV_INT_8_BITS },
+/* 84 */{ irlap_param_window_size,     PV_INT_8_BITS },
+/* 85 */{ irlap_param_additional_bofs, PV_INT_8_BITS },
+/* 86 */{ irlap_param_min_turn_time,   PV_INT_8_BITS },
+};
+
+static const pi_major_info_t pi_major_call_table[] = {
+       { pi_minor_call_table_type_0, 9 },
+       { pi_minor_call_table_type_1, 7 },
+};
+
+static pi_param_info_t irlap_param_info = { pi_major_call_table, 2, 0x7f, 7 };
+
+/* ---------------------- LOCAL SUBROUTINES ---------------------- */
+/* Note : we start with a bunch of local subroutines.
+ * As the compiler is "one pass", this is the only way to get them to
+ * inline properly...
+ * Jean II
+ */
+/*
+ * Function value_index (value, array, size)
+ *
+ *    Returns the index to the value in the specified array
+ */
+static inline int value_index(__u32 value, __u32 *array, int size)
+{
+       int i;
+
+       for (i=0; i < size; i++)
+               if (array[i] == value)
+                       break;
+       return i;
+}
+
+/*
+ * Function index_value (index, array)
+ *
+ *    Returns value to index in array, easy!
+ *
+ */
+static inline __u32 index_value(int index, __u32 *array)
+{
+       return array[index];
+}
+
+/*
+ * Function msb_index (word)
+ *
+ *    Returns index to most significant bit (MSB) in word
+ *
+ */
+static int msb_index (__u16 word)
+{
+       __u16 msb = 0x8000;
+       int index = 15;   /* Current MSB */
+
+       /* Check for buggy peers.
+        * Note : there is a small probability that it could be us, but I
+        * would expect driver authors to catch that pretty early and be
+        * able to check precisely what's going on. If a end user sees this,
+        * it's very likely the peer. - Jean II */
+       if (word == 0) {
+               net_warn_ratelimited("%s(), Detected buggy peer, adjust null PV to 0x1!\n",
+                                    __func__);
+               /* The only safe choice (we don't know the array size) */
+               word = 0x1;
+       }
+
+       while (msb) {
+               if (word & msb)
+                       break;   /* Found it! */
+               msb >>=1;
+               index--;
+       }
+       return index;
+}
+
+/*
+ * Function value_lower_bits (value, array)
+ *
+ *    Returns a bit field marking all possibility lower than value.
+ */
+static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field)
+{
+       int     i;
+       __u16   mask = 0x1;
+       __u16   result = 0x0;
+
+       for (i=0; i < size; i++) {
+               /* Add the current value to the bit field, shift mask */
+               result |= mask;
+               mask <<= 1;
+               /* Finished ? */
+               if (array[i] >= value)
+                       break;
+       }
+       /* Send back a valid index */
+       if(i >= size)
+         i = size - 1; /* Last item */
+       *field = result;
+       return i;
+}
+
+/*
+ * Function value_highest_bit (value, array)
+ *
+ *    Returns a bit field marking the highest possibility lower than value.
+ */
+static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field)
+{
+       int     i;
+       __u16   mask = 0x1;
+       __u16   result = 0x0;
+
+       for (i=0; i < size; i++) {
+               /* Finished ? */
+               if (array[i] <= value)
+                       break;
+               /* Shift mask */
+               mask <<= 1;
+       }
+       /* Set the current value to the bit field */
+       result |= mask;
+       /* Send back a valid index */
+       if(i >= size)
+         i = size - 1; /* Last item */
+       *field = result;
+       return i;
+}
+
+/* -------------------------- MAIN CALLS -------------------------- */
+
+/*
+ * Function irda_qos_compute_intersection (qos, new)
+ *
+ *    Compute the intersection of the old QoS capabilities with new ones
+ *
+ */
+void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new)
+{
+       IRDA_ASSERT(qos != NULL, return;);
+       IRDA_ASSERT(new != NULL, return;);
+
+       /* Apply */
+       qos->baud_rate.bits       &= new->baud_rate.bits;
+       qos->window_size.bits     &= new->window_size.bits;
+       qos->min_turn_time.bits   &= new->min_turn_time.bits;
+       qos->max_turn_time.bits   &= new->max_turn_time.bits;
+       qos->data_size.bits       &= new->data_size.bits;
+       qos->link_disc_time.bits  &= new->link_disc_time.bits;
+       qos->additional_bofs.bits &= new->additional_bofs.bits;
+
+       irda_qos_bits_to_value(qos);
+}
+
+/*
+ * Function irda_init_max_qos_capabilies (qos)
+ *
+ *    The purpose of this function is for layers and drivers to be able to
+ *    set the maximum QoS possible and then "and in" their own limitations
+ *
+ */
+void irda_init_max_qos_capabilies(struct qos_info *qos)
+{
+       int i;
+       /*
+        *  These are the maximum supported values as specified on pages
+        *  39-43 in IrLAP
+        */
+
+       /* Use sysctl to set some configurable values... */
+       /* Set configured max speed */
+       i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10,
+                            &qos->baud_rate.bits);
+       sysctl_max_baud_rate = index_value(i, baud_rates);
+
+       /* Set configured max disc time */
+       i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8,
+                            &qos->link_disc_time.bits);
+       sysctl_max_noreply_time = index_value(i, link_disc_times);
+
+       /* LSB is first byte, MSB is second byte */
+       qos->baud_rate.bits    &= 0x03ff;
+
+       qos->window_size.bits     = 0x7f;
+       qos->min_turn_time.bits   = 0xff;
+       qos->max_turn_time.bits   = 0x0f;
+       qos->data_size.bits       = 0x3f;
+       qos->link_disc_time.bits &= 0xff;
+       qos->additional_bofs.bits = 0xff;
+}
+EXPORT_SYMBOL(irda_init_max_qos_capabilies);
+
+/*
+ * Function irlap_adjust_qos_settings (qos)
+ *
+ *     Adjust QoS settings in case some values are not possible to use because
+ *     of other settings
+ */
+static void irlap_adjust_qos_settings(struct qos_info *qos)
+{
+       __u32 line_capacity;
+       int index;
+
+       /*
+        * Make sure the mintt is sensible.
+        * Main culprit : Ericsson T39. - Jean II
+        */
+       if (sysctl_min_tx_turn_time > qos->min_turn_time.value) {
+               int i;
+
+               net_warn_ratelimited("%s(), Detected buggy peer, adjust mtt to %dus!\n",
+                                    __func__, sysctl_min_tx_turn_time);
+
+               /* We don't really need bits, but easier this way */
+               i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times,
+                                     8, &qos->min_turn_time.bits);
+               sysctl_min_tx_turn_time = index_value(i, min_turn_times);
+               qos->min_turn_time.value = sysctl_min_tx_turn_time;
+       }
+
+       /*
+        * Not allowed to use a max turn time less than 500 ms if the baudrate
+        * is less than 115200
+        */
+       if ((qos->baud_rate.value < 115200) &&
+           (qos->max_turn_time.value < 500))
+       {
+               pr_debug("%s(), adjusting max turn time from %d to 500 ms\n",
+                        __func__, qos->max_turn_time.value);
+               qos->max_turn_time.value = 500;
+       }
+
+       /*
+        * The data size must be adjusted according to the baud rate and max
+        * turn time
+        */
+       index = value_index(qos->data_size.value, data_sizes, 6);
+       line_capacity = irlap_max_line_capacity(qos->baud_rate.value,
+                                               qos->max_turn_time.value);
+
+#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
+       while ((qos->data_size.value > line_capacity) && (index > 0)) {
+               qos->data_size.value = data_sizes[index--];
+               pr_debug("%s(), reducing data size to %d\n",
+                        __func__, qos->data_size.value);
+       }
+#else /* Use method described in section 6.6.11 of IrLAP */
+       while (irlap_requested_line_capacity(qos) > line_capacity) {
+               IRDA_ASSERT(index != 0, return;);
+
+               /* Must be able to send at least one frame */
+               if (qos->window_size.value > 1) {
+                       qos->window_size.value--;
+                       pr_debug("%s(), reducing window size to %d\n",
+                                __func__, qos->window_size.value);
+               } else if (index > 1) {
+                       qos->data_size.value = data_sizes[index--];
+                       pr_debug("%s(), reducing data size to %d\n",
+                                __func__, qos->data_size.value);
+               } else {
+                       net_warn_ratelimited("%s(), nothing more we can do!\n",
+                                            __func__);
+               }
+       }
+#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
+       /*
+        * Fix tx data size according to user limits - Jean II
+        */
+       if (qos->data_size.value > sysctl_max_tx_data_size)
+               /* Allow non discrete adjustement to avoid losing capacity */
+               qos->data_size.value = sysctl_max_tx_data_size;
+       /*
+        * Override Tx window if user request it. - Jean II
+        */
+       if (qos->window_size.value > sysctl_max_tx_window)
+               qos->window_size.value = sysctl_max_tx_window;
+}
+
+/*
+ * Function irlap_negotiate (qos_device, qos_session, skb)
+ *
+ *    Negotiate QoS values, not really that much negotiation :-)
+ *    We just set the QoS capabilities for the peer station
+ *
+ */
+int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb)
+{
+       int ret;
+
+       ret = irda_param_extract_all(self, skb->data, skb->len,
+                                    &irlap_param_info);
+
+       /* Convert the negotiated bits to values */
+       irda_qos_bits_to_value(&self->qos_tx);
+       irda_qos_bits_to_value(&self->qos_rx);
+
+       irlap_adjust_qos_settings(&self->qos_tx);
+
+       pr_debug("Setting BAUD_RATE to %d bps.\n",
+                self->qos_tx.baud_rate.value);
+       pr_debug("Setting DATA_SIZE to %d bytes\n",
+                self->qos_tx.data_size.value);
+       pr_debug("Setting WINDOW_SIZE to %d\n",
+                self->qos_tx.window_size.value);
+       pr_debug("Setting XBOFS to %d\n",
+                self->qos_tx.additional_bofs.value);
+       pr_debug("Setting MAX_TURN_TIME to %d ms.\n",
+                self->qos_tx.max_turn_time.value);
+       pr_debug("Setting MIN_TURN_TIME to %d usecs.\n",
+                self->qos_tx.min_turn_time.value);
+       pr_debug("Setting LINK_DISC to %d secs.\n",
+                self->qos_tx.link_disc_time.value);
+       return ret;
+}
+
+/*
+ * Function irlap_insert_negotiation_params (qos, fp)
+ *
+ *    Insert QoS negotiaion pararameters into frame
+ *
+ */
+int irlap_insert_qos_negotiation_params(struct irlap_cb *self,
+                                       struct sk_buff *skb)
+{
+       int ret;
+
+       /* Insert data rate */
+       ret = irda_param_insert(self, PI_BAUD_RATE, skb_tail_pointer(skb),
+                               skb_tailroom(skb), &irlap_param_info);
+       if (ret < 0)
+               return ret;
+       skb_put(skb, ret);
+
+       /* Insert max turnaround time */
+       ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb_tail_pointer(skb),
+                               skb_tailroom(skb), &irlap_param_info);
+       if (ret < 0)
+               return ret;
+       skb_put(skb, ret);
+
+       /* Insert data size */
+       ret = irda_param_insert(self, PI_DATA_SIZE, skb_tail_pointer(skb),
+                               skb_tailroom(skb), &irlap_param_info);
+       if (ret < 0)
+               return ret;
+       skb_put(skb, ret);
+
+       /* Insert window size */
+       ret = irda_param_insert(self, PI_WINDOW_SIZE, skb_tail_pointer(skb),
+                               skb_tailroom(skb), &irlap_param_info);
+       if (ret < 0)
+               return ret;
+       skb_put(skb, ret);
+
+       /* Insert additional BOFs */
+       ret = irda_param_insert(self, PI_ADD_BOFS, skb_tail_pointer(skb),
+                               skb_tailroom(skb), &irlap_param_info);
+       if (ret < 0)
+               return ret;
+       skb_put(skb, ret);
+
+       /* Insert minimum turnaround time */
+       ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb_tail_pointer(skb),
+                               skb_tailroom(skb), &irlap_param_info);
+       if (ret < 0)
+               return ret;
+       skb_put(skb, ret);
+
+       /* Insert link disconnect/threshold time */
+       ret = irda_param_insert(self, PI_LINK_DISC, skb_tail_pointer(skb),
+                               skb_tailroom(skb), &irlap_param_info);
+       if (ret < 0)
+               return ret;
+       skb_put(skb, ret);
+
+       return 0;
+}
+
+/*
+ * Function irlap_param_baud_rate (instance, param, get)
+ *
+ *    Negotiate data-rate
+ *
+ */
+static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get)
+{
+       __u16 final;
+
+       struct irlap_cb *self = (struct irlap_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       if (get) {
+               param->pv.i = self->qos_rx.baud_rate.bits;
+               pr_debug("%s(), baud rate = 0x%02x\n",
+                        __func__, param->pv.i);
+       } else {
+               /*
+                *  Stations must agree on baud rate, so calculate
+                *  intersection
+                */
+               pr_debug("Requested BAUD_RATE: 0x%04x\n", (__u16)param->pv.i);
+               final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits;
+
+               pr_debug("Final BAUD_RATE: 0x%04x\n", final);
+               self->qos_tx.baud_rate.bits = final;
+               self->qos_rx.baud_rate.bits = final;
+       }
+
+       return 0;
+}
+
+/*
+ * Function irlap_param_link_disconnect (instance, param, get)
+ *
+ *    Negotiate link disconnect/threshold time.
+ *
+ */
+static int irlap_param_link_disconnect(void *instance, irda_param_t *param,
+                                      int get)
+{
+       __u16 final;
+
+       struct irlap_cb *self = (struct irlap_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->qos_rx.link_disc_time.bits;
+       else {
+               /*
+                *  Stations must agree on link disconnect/threshold
+                *  time.
+                */
+               pr_debug("LINK_DISC: %02x\n", (__u8)param->pv.i);
+               final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits;
+
+               pr_debug("Final LINK_DISC: %02x\n", final);
+               self->qos_tx.link_disc_time.bits = final;
+               self->qos_rx.link_disc_time.bits = final;
+       }
+       return 0;
+}
+
+/*
+ * Function irlap_param_max_turn_time (instance, param, get)
+ *
+ *    Negotiate the maximum turnaround time. This is a type 1 parameter and
+ *    will be negotiated independently for each station
+ *
+ */
+static int irlap_param_max_turn_time(void *instance, irda_param_t *param,
+                                    int get)
+{
+       struct irlap_cb *self = (struct irlap_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->qos_rx.max_turn_time.bits;
+       else
+               self->qos_tx.max_turn_time.bits = (__u8) param->pv.i;
+
+       return 0;
+}
+
+/*
+ * Function irlap_param_data_size (instance, param, get)
+ *
+ *    Negotiate the data size. This is a type 1 parameter and
+ *    will be negotiated independently for each station
+ *
+ */
+static int irlap_param_data_size(void *instance, irda_param_t *param, int get)
+{
+       struct irlap_cb *self = (struct irlap_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->qos_rx.data_size.bits;
+       else
+               self->qos_tx.data_size.bits = (__u8) param->pv.i;
+
+       return 0;
+}
+
+/*
+ * Function irlap_param_window_size (instance, param, get)
+ *
+ *    Negotiate the window size. This is a type 1 parameter and
+ *    will be negotiated independently for each station
+ *
+ */
+static int irlap_param_window_size(void *instance, irda_param_t *param,
+                                  int get)
+{
+       struct irlap_cb *self = (struct irlap_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->qos_rx.window_size.bits;
+       else
+               self->qos_tx.window_size.bits = (__u8) param->pv.i;
+
+       return 0;
+}
+
+/*
+ * Function irlap_param_additional_bofs (instance, param, get)
+ *
+ *    Negotiate additional BOF characters. This is a type 1 parameter and
+ *    will be negotiated independently for each station.
+ */
+static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get)
+{
+       struct irlap_cb *self = (struct irlap_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->qos_rx.additional_bofs.bits;
+       else
+               self->qos_tx.additional_bofs.bits = (__u8) param->pv.i;
+
+       return 0;
+}
+
+/*
+ * Function irlap_param_min_turn_time (instance, param, get)
+ *
+ *    Negotiate the minimum turn around time. This is a type 1 parameter and
+ *    will be negotiated independently for each station
+ */
+static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
+                                    int get)
+{
+       struct irlap_cb *self = (struct irlap_cb *) instance;
+
+       IRDA_ASSERT(self != NULL, return -1;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
+
+       if (get)
+               param->pv.i = self->qos_rx.min_turn_time.bits;
+       else
+               self->qos_tx.min_turn_time.bits = (__u8) param->pv.i;
+
+       return 0;
+}
+
+/*
+ * Function irlap_max_line_capacity (speed, max_turn_time, min_turn_time)
+ *
+ *    Calculate the maximum line capacity
+ *
+ */
+__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time)
+{
+       __u32 line_capacity;
+       int i,j;
+
+       pr_debug("%s(), speed=%d, max_turn_time=%d\n",
+                __func__, speed, max_turn_time);
+
+       i = value_index(speed, baud_rates, 10);
+       j = value_index(max_turn_time, max_turn_times, 4);
+
+       IRDA_ASSERT(((i >=0) && (i <10)), return 0;);
+       IRDA_ASSERT(((j >=0) && (j <4)), return 0;);
+
+       line_capacity = max_line_capacities[i][j];
+
+       pr_debug("%s(), line capacity=%d bytes\n",
+                __func__, line_capacity);
+
+       return line_capacity;
+}
+
+#ifndef CONFIG_IRDA_DYNAMIC_WINDOW
+static __u32 irlap_requested_line_capacity(struct qos_info *qos)
+{
+       __u32 line_capacity;
+
+       line_capacity = qos->window_size.value *
+               (qos->data_size.value + 6 + qos->additional_bofs.value) +
+               irlap_min_turn_time_in_bytes(qos->baud_rate.value,
+                                            qos->min_turn_time.value);
+
+       pr_debug("%s(), requested line capacity=%d\n",
+                __func__, line_capacity);
+
+       return line_capacity;
+}
+#endif
+
+void irda_qos_bits_to_value(struct qos_info *qos)
+{
+       int index;
+
+       IRDA_ASSERT(qos != NULL, return;);
+
+       index = msb_index(qos->baud_rate.bits);
+       qos->baud_rate.value = baud_rates[index];
+
+       index = msb_index(qos->data_size.bits);
+       qos->data_size.value = data_sizes[index];
+
+       index = msb_index(qos->window_size.bits);
+       qos->window_size.value = index+1;
+
+       index = msb_index(qos->min_turn_time.bits);
+       qos->min_turn_time.value = min_turn_times[index];
+
+       index = msb_index(qos->max_turn_time.bits);
+       qos->max_turn_time.value = max_turn_times[index];
+
+       index = msb_index(qos->link_disc_time.bits);
+       qos->link_disc_time.value = link_disc_times[index];
+
+       index = msb_index(qos->additional_bofs.bits);
+       qos->additional_bofs.value = add_bofs[index];
+}
+EXPORT_SYMBOL(irda_qos_bits_to_value);
diff --git a/drivers/staging/irda/net/timer.c b/drivers/staging/irda/net/timer.c
new file mode 100644 (file)
index 0000000..f2280f7
--- /dev/null
@@ -0,0 +1,231 @@
+/*********************************************************************
+ *
+ * Filename:      timer.c
+ * Version:
+ * Description:
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Sat Aug 16 00:59:29 1997
+ * Modified at:   Wed Dec  8 12:50:34 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ *
+ *     Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/delay.h>
+
+#include <net/irda/timer.h>
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irlmp.h>
+
+extern int  sysctl_slot_timeout;
+
+static void irlap_slot_timer_expired(void* data);
+static void irlap_query_timer_expired(void* data);
+static void irlap_final_timer_expired(void* data);
+static void irlap_wd_timer_expired(void* data);
+static void irlap_backoff_timer_expired(void* data);
+static void irlap_media_busy_expired(void* data);
+
+void irlap_start_slot_timer(struct irlap_cb *self, int timeout)
+{
+       irda_start_timer(&self->slot_timer, timeout, (void *) self,
+                        irlap_slot_timer_expired);
+}
+
+void irlap_start_query_timer(struct irlap_cb *self, int S, int s)
+{
+       int timeout;
+
+       /* Calculate when the peer discovery should end. Normally, we
+        * get the end-of-discovery frame, so this is just in case
+        * we miss it.
+        * Basically, we multiply the number of remaining slots by our
+        * slot time, plus add some extra time to properly receive the last
+        * discovery packet (which is longer due to extra discovery info),
+        * to avoid messing with for incoming connections requests and
+        * to accommodate devices that perform discovery slower than us.
+        * Jean II */
+       timeout = msecs_to_jiffies(sysctl_slot_timeout) * (S - s)
+                  + XIDEXTRA_TIMEOUT + SMALLBUSY_TIMEOUT;
+
+       /* Set or re-set the timer. We reset the timer for each received
+        * discovery query, which allow us to automatically adjust to
+        * the speed of the peer discovery (faster or slower). Jean II */
+       irda_start_timer( &self->query_timer, timeout, (void *) self,
+                         irlap_query_timer_expired);
+}
+
+void irlap_start_final_timer(struct irlap_cb *self, int timeout)
+{
+       irda_start_timer(&self->final_timer, timeout, (void *) self,
+                        irlap_final_timer_expired);
+}
+
+void irlap_start_wd_timer(struct irlap_cb *self, int timeout)
+{
+       irda_start_timer(&self->wd_timer, timeout, (void *) self,
+                        irlap_wd_timer_expired);
+}
+
+void irlap_start_backoff_timer(struct irlap_cb *self, int timeout)
+{
+       irda_start_timer(&self->backoff_timer, timeout, (void *) self,
+                        irlap_backoff_timer_expired);
+}
+
+void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout)
+{
+       irda_start_timer(&self->media_busy_timer, timeout,
+                        (void *) self, irlap_media_busy_expired);
+}
+
+void irlap_stop_mbusy_timer(struct irlap_cb *self)
+{
+       /* If timer is activated, kill it! */
+       del_timer(&self->media_busy_timer);
+
+       /* If we are in NDM, there is a bunch of events in LAP that
+        * that be pending due to the media_busy condition, such as
+        * CONNECT_REQUEST and SEND_UI_FRAME. If we don't generate
+        * an event, they will wait forever...
+        * Jean II */
+       if (self->state == LAP_NDM)
+               irlap_do_event(self, MEDIA_BUSY_TIMER_EXPIRED, NULL, NULL);
+}
+
+void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout)
+{
+       irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
+                        irlmp_watchdog_timer_expired);
+}
+
+void irlmp_start_discovery_timer(struct irlmp_cb *self, int timeout)
+{
+       irda_start_timer(&self->discovery_timer, timeout, (void *) self,
+                        irlmp_discovery_timer_expired);
+}
+
+void irlmp_start_idle_timer(struct lap_cb *self, int timeout)
+{
+       irda_start_timer(&self->idle_timer, timeout, (void *) self,
+                        irlmp_idle_timer_expired);
+}
+
+void irlmp_stop_idle_timer(struct lap_cb *self)
+{
+       /* If timer is activated, kill it! */
+       del_timer(&self->idle_timer);
+}
+
+/*
+ * Function irlap_slot_timer_expired (data)
+ *
+ *    IrLAP slot timer has expired
+ *
+ */
+static void irlap_slot_timer_expired(void *data)
+{
+       struct irlap_cb *self = (struct irlap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       irlap_do_event(self, SLOT_TIMER_EXPIRED, NULL, NULL);
+}
+
+/*
+ * Function irlap_query_timer_expired (data)
+ *
+ *    IrLAP query timer has expired
+ *
+ */
+static void irlap_query_timer_expired(void *data)
+{
+       struct irlap_cb *self = (struct irlap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       irlap_do_event(self, QUERY_TIMER_EXPIRED, NULL, NULL);
+}
+
+/*
+ * Function irda_final_timer_expired (data)
+ *
+ *
+ *
+ */
+static void irlap_final_timer_expired(void *data)
+{
+       struct irlap_cb *self = (struct irlap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       irlap_do_event(self, FINAL_TIMER_EXPIRED, NULL, NULL);
+}
+
+/*
+ * Function irda_wd_timer_expired (data)
+ *
+ *
+ *
+ */
+static void irlap_wd_timer_expired(void *data)
+{
+       struct irlap_cb *self = (struct irlap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       irlap_do_event(self, WD_TIMER_EXPIRED, NULL, NULL);
+}
+
+/*
+ * Function irda_backoff_timer_expired (data)
+ *
+ *
+ *
+ */
+static void irlap_backoff_timer_expired(void *data)
+{
+       struct irlap_cb *self = (struct irlap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
+
+       irlap_do_event(self, BACKOFF_TIMER_EXPIRED, NULL, NULL);
+}
+
+
+/*
+ * Function irtty_media_busy_expired (data)
+ *
+ *
+ */
+static void irlap_media_busy_expired(void *data)
+{
+       struct irlap_cb *self = (struct irlap_cb *) data;
+
+       IRDA_ASSERT(self != NULL, return;);
+
+       irda_device_set_media_busy(self->netdev, FALSE);
+       /* Note : the LAP event will be send in irlap_stop_mbusy_timer(),
+       * to catch other cases where the flag is cleared (for example
+       * after a discovery) - Jean II */
+}
diff --git a/drivers/staging/irda/net/wrapper.c b/drivers/staging/irda/net/wrapper.c
new file mode 100644 (file)
index 0000000..40a0f99
--- /dev/null
@@ -0,0 +1,492 @@
+/*********************************************************************
+ *
+ * Filename:      wrapper.c
+ * Version:       1.2
+ * Description:   IrDA SIR async wrapper layer
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Mon Aug  4 20:40:53 1997
+ * Modified at:   Fri Jan 28 13:21:09 2000
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * Modified at:   Fri May 28  3:11 CST 1999
+ * Modified by:   Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
+ *
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
+ *     All Rights Reserved.
+ *     Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *     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.
+ *
+ *     Neither Dag Brattli nor University of Tromsø admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/crc.h>
+#include <net/irda/irlap.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/irda_device.h>
+
+/************************** FRAME WRAPPING **************************/
+/*
+ * Unwrap and unstuff SIR frames
+ *
+ * Note : at FIR and MIR, HDLC framing is used and usually handled
+ * by the controller, so we come here only for SIR... Jean II
+ */
+
+/*
+ * Function stuff_byte (byte, buf)
+ *
+ *    Byte stuff one single byte and put the result in buffer pointed to by
+ *    buf. The buffer must at all times be able to have two bytes inserted.
+ *
+ * This is in a tight loop, better inline it, so need to be prior to callers.
+ * (2000 bytes on P6 200MHz, non-inlined ~370us, inline ~170us) - Jean II
+ */
+static inline int stuff_byte(__u8 byte, __u8 *buf)
+{
+       switch (byte) {
+       case BOF: /* FALLTHROUGH */
+       case EOF: /* FALLTHROUGH */
+       case CE:
+               /* Insert transparently coded */
+               buf[0] = CE;               /* Send link escape */
+               buf[1] = byte^IRDA_TRANS;    /* Complement bit 5 */
+               return 2;
+               /* break; */
+       default:
+                /* Non-special value, no transparency required */
+               buf[0] = byte;
+               return 1;
+               /* break; */
+       }
+}
+
+/*
+ * Function async_wrap (skb, *tx_buff, buffsize)
+ *
+ *    Makes a new buffer with wrapping and stuffing, should check that
+ *    we don't get tx buffer overflow.
+ */
+int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
+{
+       struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
+       int xbofs;
+       int i;
+       int n;
+       union {
+               __u16 value;
+               __u8 bytes[2];
+       } fcs;
+
+       /* Initialize variables */
+       fcs.value = INIT_FCS;
+       n = 0;
+
+       /*
+        *  Send  XBOF's for required min. turn time and for the negotiated
+        *  additional XBOFS
+        */
+
+       if (cb->magic != LAP_MAGIC) {
+               /*
+                * This will happen for all frames sent from user-space.
+                * Nothing to worry about, but we set the default number of
+                * BOF's
+                */
+               pr_debug("%s(), wrong magic in skb!\n", __func__);
+               xbofs = 10;
+       } else
+               xbofs = cb->xbofs + cb->xbofs_delay;
+
+       pr_debug("%s(), xbofs=%d\n", __func__, xbofs);
+
+       /* Check that we never use more than 115 + 48 xbofs */
+       if (xbofs > 163) {
+               pr_debug("%s(), too many xbofs (%d)\n", __func__,
+                        xbofs);
+               xbofs = 163;
+       }
+
+       memset(tx_buff + n, XBOF, xbofs);
+       n += xbofs;
+
+       /* Start of packet character BOF */
+       tx_buff[n++] = BOF;
+
+       /* Insert frame and calc CRC */
+       for (i=0; i < skb->len; i++) {
+               /*
+                *  Check for the possibility of tx buffer overflow. We use
+                *  bufsize-5 since the maximum number of bytes that can be
+                *  transmitted after this point is 5.
+                */
+               if(n >= (buffsize-5)) {
+                       net_err_ratelimited("%s(), tx buffer overflow (n=%d)\n",
+                                           __func__, n);
+                       return n;
+               }
+
+               n += stuff_byte(skb->data[i], tx_buff+n);
+               fcs.value = irda_fcs(fcs.value, skb->data[i]);
+       }
+
+       /* Insert CRC in little endian format (LSB first) */
+       fcs.value = ~fcs.value;
+#ifdef __LITTLE_ENDIAN
+       n += stuff_byte(fcs.bytes[0], tx_buff+n);
+       n += stuff_byte(fcs.bytes[1], tx_buff+n);
+#else /* ifdef __BIG_ENDIAN */
+       n += stuff_byte(fcs.bytes[1], tx_buff+n);
+       n += stuff_byte(fcs.bytes[0], tx_buff+n);
+#endif
+       tx_buff[n++] = EOF;
+
+       return n;
+}
+EXPORT_SYMBOL(async_wrap_skb);
+
+/************************* FRAME UNWRAPPING *************************/
+/*
+ * Unwrap and unstuff SIR frames
+ *
+ * Complete rewrite by Jean II :
+ * More inline, faster, more compact, more logical. Jean II
+ * (16 bytes on P6 200MHz, old 5 to 7 us, new 4 to 6 us)
+ * (24 bytes on P6 200MHz, old 9 to 10 us, new 7 to 8 us)
+ * (for reference, 115200 b/s is 1 byte every 69 us)
+ * And reduce wrapper.o by ~900B in the process ;-)
+ *
+ * Then, we have the addition of ZeroCopy, which is optional
+ * (i.e. the driver must initiate it) and improve final processing.
+ * (2005 B frame + EOF on P6 200MHz, without 30 to 50 us, with 10 to 25 us)
+ *
+ * Note : at FIR and MIR, HDLC framing is used and usually handled
+ * by the controller, so we come here only for SIR... Jean II
+ */
+
+/*
+ * We can also choose where we want to do the CRC calculation. We can
+ * do it "inline", as we receive the bytes, or "postponed", when
+ * receiving the End-Of-Frame.
+ * (16 bytes on P6 200MHz, inlined 4 to 6 us, postponed 4 to 5 us)
+ * (24 bytes on P6 200MHz, inlined 7 to 8 us, postponed 5 to 7 us)
+ * With ZeroCopy :
+ * (2005 B frame on P6 200MHz, inlined 10 to 25 us, postponed 140 to 180 us)
+ * Without ZeroCopy :
+ * (2005 B frame on P6 200MHz, inlined 30 to 50 us, postponed 150 to 180 us)
+ * (Note : numbers taken with irq disabled)
+ *
+ * From those numbers, it's not clear which is the best strategy, because
+ * we end up running through a lot of data one way or another (i.e. cache
+ * misses). I personally prefer to avoid the huge latency spike of the
+ * "postponed" solution, because it come just at the time when we have
+ * lot's of protocol processing to do and it will hurt our ability to
+ * reach low link turnaround times... Jean II
+ */
+//#define POSTPONE_RX_CRC
+
+/*
+ * Function async_bump (buf, len, stats)
+ *
+ *    Got a frame, make a copy of it, and pass it up the stack! We can try
+ *    to inline it since it's only called from state_inside_frame
+ */
+static inline void
+async_bump(struct net_device *dev,
+          struct net_device_stats *stats,
+          iobuff_t *rx_buff)
+{
+       struct sk_buff *newskb;
+       struct sk_buff *dataskb;
+       int             docopy;
+
+       /* Check if we need to copy the data to a new skb or not.
+        * If the driver doesn't use ZeroCopy Rx, we have to do it.
+        * With ZeroCopy Rx, the rx_buff already point to a valid
+        * skb. But, if the frame is small, it is more efficient to
+        * copy it to save memory (copy will be fast anyway - that's
+        * called Rx-copy-break). Jean II */
+       docopy = ((rx_buff->skb == NULL) ||
+                 (rx_buff->len < IRDA_RX_COPY_THRESHOLD));
+
+       /* Allocate a new skb */
+       newskb = dev_alloc_skb(docopy ? rx_buff->len + 1 : rx_buff->truesize);
+       if (!newskb)  {
+               stats->rx_dropped++;
+               /* We could deliver the current skb if doing ZeroCopy Rx,
+                * but this would stall the Rx path. Better drop the
+                * packet... Jean II */
+               return;
+       }
+
+       /* Align IP header to 20 bytes (i.e. increase skb->data)
+        * Note this is only useful with IrLAN, as PPP has a variable
+        * header size (2 or 1 bytes) - Jean II */
+       skb_reserve(newskb, 1);
+
+       if(docopy) {
+               /* Copy data without CRC (length already checked) */
+               skb_copy_to_linear_data(newskb, rx_buff->data,
+                                       rx_buff->len - 2);
+               /* Deliver this skb */
+               dataskb = newskb;
+       } else {
+               /* We are using ZeroCopy. Deliver old skb */
+               dataskb = rx_buff->skb;
+               /* And hook the new skb to the rx_buff */
+               rx_buff->skb = newskb;
+               rx_buff->head = newskb->data;   /* NOT newskb->head */
+               //printk(KERN_DEBUG "ZeroCopy : len = %d, dataskb = %p, newskb = %p\n", rx_buff->len, dataskb, newskb);
+       }
+
+       /* Set proper length on skb (without CRC) */
+       skb_put(dataskb, rx_buff->len - 2);
+
+       /* Feed it to IrLAP layer */
+       dataskb->dev = dev;
+       skb_reset_mac_header(dataskb);
+       dataskb->protocol = htons(ETH_P_IRDA);
+
+       netif_rx(dataskb);
+
+       stats->rx_packets++;
+       stats->rx_bytes += rx_buff->len;
+
+       /* Clean up rx_buff (redundant with async_unwrap_bof() ???) */
+       rx_buff->data = rx_buff->head;
+       rx_buff->len = 0;
+}
+
+/*
+ * Function async_unwrap_bof(dev, byte)
+ *
+ *    Handle Beginning Of Frame character received within a frame
+ *
+ */
+static inline void
+async_unwrap_bof(struct net_device *dev,
+                struct net_device_stats *stats,
+                iobuff_t *rx_buff, __u8 byte)
+{
+       switch(rx_buff->state) {
+       case LINK_ESCAPE:
+       case INSIDE_FRAME:
+               /* Not supposed to happen, the previous frame is not
+                * finished - Jean II */
+               pr_debug("%s(), Discarding incomplete frame\n",
+                        __func__);
+               stats->rx_errors++;
+               stats->rx_missed_errors++;
+               irda_device_set_media_busy(dev, TRUE);
+               break;
+
+       case OUTSIDE_FRAME:
+       case BEGIN_FRAME:
+       default:
+               /* We may receive multiple BOF at the start of frame */
+               break;
+       }
+
+       /* Now receiving frame */
+       rx_buff->state = BEGIN_FRAME;
+       rx_buff->in_frame = TRUE;
+
+       /* Time to initialize receive buffer */
+       rx_buff->data = rx_buff->head;
+       rx_buff->len = 0;
+       rx_buff->fcs = INIT_FCS;
+}
+
+/*
+ * Function async_unwrap_eof(dev, byte)
+ *
+ *    Handle End Of Frame character received within a frame
+ *
+ */
+static inline void
+async_unwrap_eof(struct net_device *dev,
+                struct net_device_stats *stats,
+                iobuff_t *rx_buff, __u8 byte)
+{
+#ifdef POSTPONE_RX_CRC
+       int     i;
+#endif
+
+       switch(rx_buff->state) {
+       case OUTSIDE_FRAME:
+               /* Probably missed the BOF */
+               stats->rx_errors++;
+               stats->rx_missed_errors++;
+               irda_device_set_media_busy(dev, TRUE);
+               break;
+
+       case BEGIN_FRAME:
+       case LINK_ESCAPE:
+       case INSIDE_FRAME:
+       default:
+               /* Note : in the case of BEGIN_FRAME and LINK_ESCAPE,
+                * the fcs will most likely not match and generate an
+                * error, as expected - Jean II */
+               rx_buff->state = OUTSIDE_FRAME;
+               rx_buff->in_frame = FALSE;
+
+#ifdef POSTPONE_RX_CRC
+               /* If we haven't done the CRC as we receive bytes, we
+                * must do it now... Jean II */
+               for(i = 0; i < rx_buff->len; i++)
+                       rx_buff->fcs = irda_fcs(rx_buff->fcs,
+                                               rx_buff->data[i]);
+#endif
+
+               /* Test FCS and signal success if the frame is good */
+               if (rx_buff->fcs == GOOD_FCS) {
+                       /* Deliver frame */
+                       async_bump(dev, stats, rx_buff);
+                       break;
+               } else {
+                       /* Wrong CRC, discard frame!  */
+                       irda_device_set_media_busy(dev, TRUE);
+
+                       pr_debug("%s(), crc error\n", __func__);
+                       stats->rx_errors++;
+                       stats->rx_crc_errors++;
+               }
+               break;
+       }
+}
+
+/*
+ * Function async_unwrap_ce(dev, byte)
+ *
+ *    Handle Character Escape character received within a frame
+ *
+ */
+static inline void
+async_unwrap_ce(struct net_device *dev,
+                struct net_device_stats *stats,
+                iobuff_t *rx_buff, __u8 byte)
+{
+       switch(rx_buff->state) {
+       case OUTSIDE_FRAME:
+               /* Activate carrier sense */
+               irda_device_set_media_busy(dev, TRUE);
+               break;
+
+       case LINK_ESCAPE:
+               net_warn_ratelimited("%s: state not defined\n", __func__);
+               break;
+
+       case BEGIN_FRAME:
+       case INSIDE_FRAME:
+       default:
+               /* Stuffed byte coming */
+               rx_buff->state = LINK_ESCAPE;
+               break;
+       }
+}
+
+/*
+ * Function async_unwrap_other(dev, byte)
+ *
+ *    Handle other characters received within a frame
+ *
+ */
+static inline void
+async_unwrap_other(struct net_device *dev,
+                  struct net_device_stats *stats,
+                  iobuff_t *rx_buff, __u8 byte)
+{
+       switch(rx_buff->state) {
+               /* This is on the critical path, case are ordered by
+                * probability (most frequent first) - Jean II */
+       case INSIDE_FRAME:
+               /* Must be the next byte of the frame */
+               if (rx_buff->len < rx_buff->truesize)  {
+                       rx_buff->data[rx_buff->len++] = byte;
+#ifndef POSTPONE_RX_CRC
+                       rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
+#endif
+               } else {
+                       pr_debug("%s(), Rx buffer overflow, aborting\n",
+                                __func__);
+                       rx_buff->state = OUTSIDE_FRAME;
+               }
+               break;
+
+       case LINK_ESCAPE:
+               /*
+                *  Stuffed char, complement bit 5 of byte
+                *  following CE, IrLAP p.114
+                */
+               byte ^= IRDA_TRANS;
+               if (rx_buff->len < rx_buff->truesize)  {
+                       rx_buff->data[rx_buff->len++] = byte;
+#ifndef POSTPONE_RX_CRC
+                       rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
+#endif
+                       rx_buff->state = INSIDE_FRAME;
+               } else {
+                       pr_debug("%s(), Rx buffer overflow, aborting\n",
+                                __func__);
+                       rx_buff->state = OUTSIDE_FRAME;
+               }
+               break;
+
+       case OUTSIDE_FRAME:
+               /* Activate carrier sense */
+               if(byte != XBOF)
+                       irda_device_set_media_busy(dev, TRUE);
+               break;
+
+       case BEGIN_FRAME:
+       default:
+               rx_buff->data[rx_buff->len++] = byte;
+#ifndef POSTPONE_RX_CRC
+               rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
+#endif
+               rx_buff->state = INSIDE_FRAME;
+               break;
+       }
+}
+
+/*
+ * Function async_unwrap_char (dev, rx_buff, byte)
+ *
+ *    Parse and de-stuff frame received from the IrDA-port
+ *
+ * This is the main entry point for SIR drivers.
+ */
+void async_unwrap_char(struct net_device *dev,
+                      struct net_device_stats *stats,
+                      iobuff_t *rx_buff, __u8 byte)
+{
+       switch(byte) {
+       case CE:
+               async_unwrap_ce(dev, stats, rx_buff, byte);
+               break;
+       case BOF:
+               async_unwrap_bof(dev, stats, rx_buff, byte);
+               break;
+       case EOF:
+               async_unwrap_eof(dev, stats, rx_buff, byte);
+               break;
+       default:
+               async_unwrap_other(dev, stats, rx_buff, byte);
+               break;
+       }
+}
+EXPORT_SYMBOL(async_unwrap_char);
+
index 17ca213e1f85d458c502cb6c4ebb5f7857d22ec4..8614a9ccfbefac9c591546ce59a5455a75728355 100644 (file)
@@ -376,7 +376,6 @@ endmenu
 
 source "net/ax25/Kconfig"
 source "net/can/Kconfig"
-source "net/irda/Kconfig"
 source "net/bluetooth/Kconfig"
 source "net/rxrpc/Kconfig"
 source "net/kcm/Kconfig"
index bed80fa398b7f888ae80da7b4e7c15f424be70e2..3d3feff3643bb2449e69bbc105dbe0c5fa361c7c 100644 (file)
@@ -31,7 +31,6 @@ obj-$(CONFIG_NETROM)          += netrom/
 obj-$(CONFIG_ROSE)             += rose/
 obj-$(CONFIG_AX25)             += ax25/
 obj-$(CONFIG_CAN)              += can/
-obj-$(CONFIG_IRDA)             += irda/
 obj-$(CONFIG_BT)               += bluetooth/
 obj-$(CONFIG_SUNRPC)           += sunrpc/
 obj-$(CONFIG_AF_RXRPC)         += rxrpc/
diff --git a/net/irda/Kconfig b/net/irda/Kconfig
deleted file mode 100644 (file)
index c8671a7..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#
-# IrDA protocol configuration
-#
-
-menuconfig IRDA
-       depends on NET && !S390
-       tristate "IrDA (infrared) subsystem support"
-       select CRC_CCITT
-       ---help---
-         Say Y here if you want to build support for the IrDA (TM) protocols.
-         The Infrared Data Associations (tm) specifies standards for wireless
-         infrared communication and is supported by most laptops and PDA's.
-
-         To use Linux support for the IrDA (tm) protocols, you will also need
-         some user-space utilities like irattach.  For more information, see
-         the file <file:Documentation/networking/irda.txt>.  You also want to
-         read the IR-HOWTO, available at
-         <http://www.tldp.org/docs.html#howto>.
-
-         If you want to exchange bits of data (vCal, vCard) with a PDA, you
-         will need to install some OBEX application, such as OpenObex :
-         <http://sourceforge.net/projects/openobex/>
-
-         To compile this support as a module, choose M here: the module will
-         be called irda.
-
-comment "IrDA protocols"
-       depends on IRDA
-
-source "net/irda/irlan/Kconfig"
-
-source "net/irda/irnet/Kconfig"
-
-source "net/irda/ircomm/Kconfig"
-
-config IRDA_ULTRA
-       bool "Ultra (connectionless) protocol"
-       depends on IRDA
-       help
-         Say Y here to support the connectionless Ultra IRDA protocol.
-         Ultra allows to exchange data over IrDA with really simple devices
-         (watch, beacon) without the overhead of the IrDA protocol (no handshaking,
-         no management frames, simple fixed header).
-         Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1);
-
-comment "IrDA options"
-       depends on IRDA
-
-config IRDA_CACHE_LAST_LSAP
-       bool "Cache last LSAP"
-       depends on IRDA
-       help
-         Say Y here if you want IrLMP to cache the last LSAP used.  This
-         makes sense since most frames will be sent/received on the same
-         connection.  Enabling this option will save a hash-lookup per frame.
-
-         If unsure, say Y.
-
-config IRDA_FAST_RR
-       bool "Fast RRs (low latency)"
-       depends on IRDA
-       ---help---
-         Say Y here is you want IrLAP to send fast RR (Receive Ready) frames
-         when acting as a primary station.
-         Disabling this option will make latency over IrDA very bad. Enabling
-         this option will make the IrDA stack send more packet than strictly
-         necessary, thus reduce your battery life (but not that much).
-
-         Fast RR will make IrLAP send out a RR frame immediately when
-         receiving a frame if its own transmit queue is currently empty. This
-         will give a lot of speed improvement when receiving much data since
-         the secondary station will not have to wait the max. turn around
-         time (usually 500ms) before it is allowed to transmit the next time.
-         If the transmit queue of the secondary is also empty, the primary will
-         start backing-off before sending another RR frame, waiting longer
-         each time until the back-off reaches the max. turn around time.
-         This back-off increase in controlled via
-         /proc/sys/net/irda/fast_poll_increase
-
-         If unsure, say Y.
-
-config IRDA_DEBUG
-       bool "Debug information"
-       depends on IRDA
-       help
-         Say Y here if you want the IrDA subsystem to write debug information
-         to your syslog. You can change the debug level in
-         /proc/sys/net/irda/debug .
-         When this option is enabled, the IrDA also perform many extra internal
-         verifications which will usually prevent the kernel to crash in case of
-         bugs.
-
-         If unsure, say Y (since it makes it easier to find the bugs).
-
-source "drivers/net/irda/Kconfig"
-
diff --git a/net/irda/Makefile b/net/irda/Makefile
deleted file mode 100644 (file)
index 187f6c5..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# Makefile for the Linux IrDA protocol layer.
-#
-
-obj-$(CONFIG_IRDA) += irda.o
-obj-$(CONFIG_IRLAN) += irlan/
-obj-$(CONFIG_IRNET) += irnet/
-obj-$(CONFIG_IRCOMM) += ircomm/
-
-irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \
-          irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \
-          irttp.o irda_device.o irias_object.o wrapper.o af_irda.o \
-         discovery.o parameters.o irnetlink.o irmod.o
-irda-$(CONFIG_PROC_FS) += irproc.o
-irda-$(CONFIG_SYSCTL) += irsysctl.o
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
deleted file mode 100644 (file)
index 23fa7c8..0000000
+++ /dev/null
@@ -1,2695 +0,0 @@
-/*********************************************************************
- *
- * Filename:      af_irda.c
- * Version:       0.9
- * Description:   IrDA sockets implementation
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun May 31 10:12:43 1998
- * Modified at:   Sat Dec 25 21:10:23 1999
- * Modified by:   Dag Brattli <dag@brattli.net>
- * Sources:       af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
- *
- *     Copyright (c) 1999 Dag Brattli <dagb@cs.uit.no>
- *     Copyright (c) 1999-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *     All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- *     Linux-IrDA now supports four different types of IrDA sockets:
- *
- *     o SOCK_STREAM:    TinyTP connections with SAR disabled. The
- *                       max SDU size is 0 for conn. of this type
- *     o SOCK_SEQPACKET: TinyTP connections with SAR enabled. TTP may
- *                       fragment the messages, but will preserve
- *                       the message boundaries
- *     o SOCK_DGRAM:     IRDAPROTO_UNITDATA: TinyTP connections with Unitdata
- *                       (unreliable) transfers
- *                       IRDAPROTO_ULTRA: Connectionless and unreliable data
- *
- ********************************************************************/
-
-#include <linux/capability.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/slab.h>
-#include <linux/sched/signal.h>
-#include <linux/init.h>
-#include <linux/net.h>
-#include <linux/irda.h>
-#include <linux/poll.h>
-
-#include <asm/ioctls.h>                /* TIOCOUTQ, TIOCINQ */
-#include <linux/uaccess.h>
-
-#include <net/sock.h>
-#include <net/tcp_states.h>
-
-#include <net/irda/af_irda.h>
-
-static int irda_create(struct net *net, struct socket *sock, int protocol, int kern);
-
-static const struct proto_ops irda_stream_ops;
-static const struct proto_ops irda_seqpacket_ops;
-static const struct proto_ops irda_dgram_ops;
-
-#ifdef CONFIG_IRDA_ULTRA
-static const struct proto_ops irda_ultra_ops;
-#define ULTRA_MAX_DATA 382
-#endif /* CONFIG_IRDA_ULTRA */
-
-#define IRDA_MAX_HEADER (TTP_MAX_HEADER)
-
-/*
- * Function irda_data_indication (instance, sap, skb)
- *
- *    Received some data from TinyTP. Just queue it on the receive queue
- *
- */
-static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb)
-{
-       struct irda_sock *self;
-       struct sock *sk;
-       int err;
-
-       self = instance;
-       sk = instance;
-
-       err = sock_queue_rcv_skb(sk, skb);
-       if (err) {
-               pr_debug("%s(), error: no more mem!\n", __func__);
-               self->rx_flow = FLOW_STOP;
-
-               /* When we return error, TTP will need to requeue the skb */
-               return err;
-       }
-
-       return 0;
-}
-
-/*
- * Function irda_disconnect_indication (instance, sap, reason, skb)
- *
- *    Connection has been closed. Check reason to find out why
- *
- */
-static void irda_disconnect_indication(void *instance, void *sap,
-                                      LM_REASON reason, struct sk_buff *skb)
-{
-       struct irda_sock *self;
-       struct sock *sk;
-
-       self = instance;
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       /* Don't care about it, but let's not leak it */
-       if(skb)
-               dev_kfree_skb(skb);
-
-       sk = instance;
-       if (sk == NULL) {
-               pr_debug("%s(%p) : BUG : sk is NULL\n",
-                        __func__, self);
-               return;
-       }
-
-       /* Prevent race conditions with irda_release() and irda_shutdown() */
-       bh_lock_sock(sk);
-       if (!sock_flag(sk, SOCK_DEAD) && sk->sk_state != TCP_CLOSE) {
-               sk->sk_state     = TCP_CLOSE;
-               sk->sk_shutdown |= SEND_SHUTDOWN;
-
-               sk->sk_state_change(sk);
-
-               /* Close our TSAP.
-                * If we leave it open, IrLMP put it back into the list of
-                * unconnected LSAPs. The problem is that any incoming request
-                * can then be matched to this socket (and it will be, because
-                * it is at the head of the list). This would prevent any
-                * listening socket waiting on the same TSAP to get those
-                * requests. Some apps forget to close sockets, or hang to it
-                * a bit too long, so we may stay in this dead state long
-                * enough to be noticed...
-                * Note : all socket function do check sk->sk_state, so we are
-                * safe...
-                * Jean II
-                */
-               if (self->tsap) {
-                       irttp_close_tsap(self->tsap);
-                       self->tsap = NULL;
-               }
-       }
-       bh_unlock_sock(sk);
-
-       /* Note : once we are there, there is not much you want to do
-        * with the socket anymore, apart from closing it.
-        * For example, bind() and connect() won't reset sk->sk_err,
-        * sk->sk_shutdown and sk->sk_flags to valid values...
-        * Jean II
-        */
-}
-
-/*
- * Function irda_connect_confirm (instance, sap, qos, max_sdu_size, skb)
- *
- *    Connections has been confirmed by the remote device
- *
- */
-static void irda_connect_confirm(void *instance, void *sap,
-                                struct qos_info *qos,
-                                __u32 max_sdu_size, __u8 max_header_size,
-                                struct sk_buff *skb)
-{
-       struct irda_sock *self;
-       struct sock *sk;
-
-       self = instance;
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       sk = instance;
-       if (sk == NULL) {
-               dev_kfree_skb(skb);
-               return;
-       }
-
-       dev_kfree_skb(skb);
-       // Should be ??? skb_queue_tail(&sk->sk_receive_queue, skb);
-
-       /* How much header space do we need to reserve */
-       self->max_header_size = max_header_size;
-
-       /* IrTTP max SDU size in transmit direction */
-       self->max_sdu_size_tx = max_sdu_size;
-
-       /* Find out what the largest chunk of data that we can transmit is */
-       switch (sk->sk_type) {
-       case SOCK_STREAM:
-               if (max_sdu_size != 0) {
-                       net_err_ratelimited("%s: max_sdu_size must be 0\n",
-                                           __func__);
-                       return;
-               }
-               self->max_data_size = irttp_get_max_seg_size(self->tsap);
-               break;
-       case SOCK_SEQPACKET:
-               if (max_sdu_size == 0) {
-                       net_err_ratelimited("%s: max_sdu_size cannot be 0\n",
-                                           __func__);
-                       return;
-               }
-               self->max_data_size = max_sdu_size;
-               break;
-       default:
-               self->max_data_size = irttp_get_max_seg_size(self->tsap);
-       }
-
-       pr_debug("%s(), max_data_size=%d\n", __func__,
-                self->max_data_size);
-
-       memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
-
-       /* We are now connected! */
-       sk->sk_state = TCP_ESTABLISHED;
-       sk->sk_state_change(sk);
-}
-
-/*
- * Function irda_connect_indication(instance, sap, qos, max_sdu_size, userdata)
- *
- *    Incoming connection
- *
- */
-static void irda_connect_indication(void *instance, void *sap,
-                                   struct qos_info *qos, __u32 max_sdu_size,
-                                   __u8 max_header_size, struct sk_buff *skb)
-{
-       struct irda_sock *self;
-       struct sock *sk;
-
-       self = instance;
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       sk = instance;
-       if (sk == NULL) {
-               dev_kfree_skb(skb);
-               return;
-       }
-
-       /* How much header space do we need to reserve */
-       self->max_header_size = max_header_size;
-
-       /* IrTTP max SDU size in transmit direction */
-       self->max_sdu_size_tx = max_sdu_size;
-
-       /* Find out what the largest chunk of data that we can transmit is */
-       switch (sk->sk_type) {
-       case SOCK_STREAM:
-               if (max_sdu_size != 0) {
-                       net_err_ratelimited("%s: max_sdu_size must be 0\n",
-                                           __func__);
-                       kfree_skb(skb);
-                       return;
-               }
-               self->max_data_size = irttp_get_max_seg_size(self->tsap);
-               break;
-       case SOCK_SEQPACKET:
-               if (max_sdu_size == 0) {
-                       net_err_ratelimited("%s: max_sdu_size cannot be 0\n",
-                                           __func__);
-                       kfree_skb(skb);
-                       return;
-               }
-               self->max_data_size = max_sdu_size;
-               break;
-       default:
-               self->max_data_size = irttp_get_max_seg_size(self->tsap);
-       }
-
-       pr_debug("%s(), max_data_size=%d\n", __func__,
-                self->max_data_size);
-
-       memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
-
-       skb_queue_tail(&sk->sk_receive_queue, skb);
-       sk->sk_state_change(sk);
-}
-
-/*
- * Function irda_connect_response (handle)
- *
- *    Accept incoming connection
- *
- */
-static void irda_connect_response(struct irda_sock *self)
-{
-       struct sk_buff *skb;
-
-       skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL);
-       if (skb == NULL) {
-               pr_debug("%s() Unable to allocate sk_buff!\n",
-                        __func__);
-               return;
-       }
-
-       /* Reserve space for MUX_CONTROL and LAP header */
-       skb_reserve(skb, IRDA_MAX_HEADER);
-
-       irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb);
-}
-
-/*
- * Function irda_flow_indication (instance, sap, flow)
- *
- *    Used by TinyTP to tell us if it can accept more data or not
- *
- */
-static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
-{
-       struct irda_sock *self;
-       struct sock *sk;
-
-       self = instance;
-       sk = instance;
-       BUG_ON(sk == NULL);
-
-       switch (flow) {
-       case FLOW_STOP:
-               pr_debug("%s(), IrTTP wants us to slow down\n",
-                        __func__);
-               self->tx_flow = flow;
-               break;
-       case FLOW_START:
-               self->tx_flow = flow;
-               pr_debug("%s(), IrTTP wants us to start again\n",
-                        __func__);
-               wake_up_interruptible(sk_sleep(sk));
-               break;
-       default:
-               pr_debug("%s(), Unknown flow command!\n", __func__);
-               /* Unknown flow command, better stop */
-               self->tx_flow = flow;
-               break;
-       }
-}
-
-/*
- * Function irda_getvalue_confirm (obj_id, value, priv)
- *
- *    Got answer from remote LM-IAS, just pass object to requester...
- *
- * Note : duplicate from above, but we need our own version that
- * doesn't touch the dtsap_sel and save the full value structure...
- */
-static void irda_getvalue_confirm(int result, __u16 obj_id,
-                                 struct ias_value *value, void *priv)
-{
-       struct irda_sock *self;
-
-       self = priv;
-       if (!self) {
-               net_warn_ratelimited("%s: lost myself!\n", __func__);
-               return;
-       }
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       /* We probably don't need to make any more queries */
-       iriap_close(self->iriap);
-       self->iriap = NULL;
-
-       /* Check if request succeeded */
-       if (result != IAS_SUCCESS) {
-               pr_debug("%s(), IAS query failed! (%d)\n", __func__,
-                        result);
-
-               self->errno = result;   /* We really need it later */
-
-               /* Wake up any processes waiting for result */
-               wake_up_interruptible(&self->query_wait);
-
-               return;
-       }
-
-       /* Pass the object to the caller (so the caller must delete it) */
-       self->ias_result = value;
-       self->errno = 0;
-
-       /* Wake up any processes waiting for result */
-       wake_up_interruptible(&self->query_wait);
-}
-
-/*
- * Function irda_selective_discovery_indication (discovery)
- *
- *    Got a selective discovery indication from IrLMP.
- *
- * IrLMP is telling us that this node is new and matching our hint bit
- * filter. Wake up any process waiting for answer...
- */
-static void irda_selective_discovery_indication(discinfo_t *discovery,
-                                               DISCOVERY_MODE mode,
-                                               void *priv)
-{
-       struct irda_sock *self;
-
-       self = priv;
-       if (!self) {
-               net_warn_ratelimited("%s: lost myself!\n", __func__);
-               return;
-       }
-
-       /* Pass parameter to the caller */
-       self->cachedaddr = discovery->daddr;
-
-       /* Wake up process if its waiting for device to be discovered */
-       wake_up_interruptible(&self->query_wait);
-}
-
-/*
- * Function irda_discovery_timeout (priv)
- *
- *    Timeout in the selective discovery process
- *
- * We were waiting for a node to be discovered, but nothing has come up
- * so far. Wake up the user and tell him that we failed...
- */
-static void irda_discovery_timeout(u_long priv)
-{
-       struct irda_sock *self;
-
-       self = (struct irda_sock *) priv;
-       BUG_ON(self == NULL);
-
-       /* Nothing for the caller */
-       self->cachelog = NULL;
-       self->cachedaddr = 0;
-       self->errno = -ETIME;
-
-       /* Wake up process if its still waiting... */
-       wake_up_interruptible(&self->query_wait);
-}
-
-/*
- * Function irda_open_tsap (self)
- *
- *    Open local Transport Service Access Point (TSAP)
- *
- */
-static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name)
-{
-       notify_t notify;
-
-       if (self->tsap) {
-               pr_debug("%s: busy!\n", __func__);
-               return -EBUSY;
-       }
-
-       /* Initialize callbacks to be used by the IrDA stack */
-       irda_notify_init(&notify);
-       notify.connect_confirm       = irda_connect_confirm;
-       notify.connect_indication    = irda_connect_indication;
-       notify.disconnect_indication = irda_disconnect_indication;
-       notify.data_indication       = irda_data_indication;
-       notify.udata_indication      = irda_data_indication;
-       notify.flow_indication       = irda_flow_indication;
-       notify.instance = self;
-       strncpy(notify.name, name, NOTIFY_MAX_NAME);
-
-       self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT,
-                                    &notify);
-       if (self->tsap == NULL) {
-               pr_debug("%s(), Unable to allocate TSAP!\n",
-                        __func__);
-               return -ENOMEM;
-       }
-       /* Remember which TSAP selector we actually got */
-       self->stsap_sel = self->tsap->stsap_sel;
-
-       return 0;
-}
-
-/*
- * Function irda_open_lsap (self)
- *
- *    Open local Link Service Access Point (LSAP). Used for opening Ultra
- *    sockets
- */
-#ifdef CONFIG_IRDA_ULTRA
-static int irda_open_lsap(struct irda_sock *self, int pid)
-{
-       notify_t notify;
-
-       if (self->lsap) {
-               net_warn_ratelimited("%s(), busy!\n", __func__);
-               return -EBUSY;
-       }
-
-       /* Initialize callbacks to be used by the IrDA stack */
-       irda_notify_init(&notify);
-       notify.udata_indication = irda_data_indication;
-       notify.instance = self;
-       strncpy(notify.name, "Ultra", NOTIFY_MAX_NAME);
-
-       self->lsap = irlmp_open_lsap(LSAP_CONNLESS, &notify, pid);
-       if (self->lsap == NULL) {
-               pr_debug("%s(), Unable to allocate LSAP!\n", __func__);
-               return -ENOMEM;
-       }
-
-       return 0;
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irda_find_lsap_sel (self, name)
- *
- *    Try to lookup LSAP selector in remote LM-IAS
- *
- * Basically, we start a IAP query, and then go to sleep. When the query
- * return, irda_getvalue_confirm will wake us up, and we can examine the
- * result of the query...
- * Note that in some case, the query fail even before we go to sleep,
- * creating some races...
- */
-static int irda_find_lsap_sel(struct irda_sock *self, char *name)
-{
-       pr_debug("%s(%p, %s)\n", __func__, self, name);
-
-       if (self->iriap) {
-               net_warn_ratelimited("%s(): busy with a previous query\n",
-                                    __func__);
-               return -EBUSY;
-       }
-
-       self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
-                                irda_getvalue_confirm);
-       if(self->iriap == NULL)
-               return -ENOMEM;
-
-       /* Treat unexpected wakeup as disconnect */
-       self->errno = -EHOSTUNREACH;
-
-       /* Query remote LM-IAS */
-       iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr,
-                                     name, "IrDA:TinyTP:LsapSel");
-
-       /* Wait for answer, if not yet finished (or failed) */
-       if (wait_event_interruptible(self->query_wait, (self->iriap==NULL)))
-               /* Treat signals as disconnect */
-               return -EHOSTUNREACH;
-
-       /* Check what happened */
-       if (self->errno)
-       {
-               /* Requested object/attribute doesn't exist */
-               if((self->errno == IAS_CLASS_UNKNOWN) ||
-                  (self->errno == IAS_ATTRIB_UNKNOWN))
-                       return -EADDRNOTAVAIL;
-               else
-                       return -EHOSTUNREACH;
-       }
-
-       /* Get the remote TSAP selector */
-       switch (self->ias_result->type) {
-       case IAS_INTEGER:
-               pr_debug("%s() int=%d\n",
-                        __func__, self->ias_result->t.integer);
-
-               if (self->ias_result->t.integer != -1)
-                       self->dtsap_sel = self->ias_result->t.integer;
-               else
-                       self->dtsap_sel = 0;
-               break;
-       default:
-               self->dtsap_sel = 0;
-               pr_debug("%s(), bad type!\n", __func__);
-               break;
-       }
-       if (self->ias_result)
-               irias_delete_value(self->ias_result);
-
-       if (self->dtsap_sel)
-               return 0;
-
-       return -EADDRNOTAVAIL;
-}
-
-/*
- * Function irda_discover_daddr_and_lsap_sel (self, name)
- *
- *    This try to find a device with the requested service.
- *
- * It basically look into the discovery log. For each address in the list,
- * it queries the LM-IAS of the device to find if this device offer
- * the requested service.
- * If there is more than one node supporting the service, we complain
- * to the user (it should move devices around).
- * The, we set both the destination address and the lsap selector to point
- * on the service on the unique device we have found.
- *
- * Note : this function fails if there is more than one device in range,
- * because IrLMP doesn't disconnect the LAP when the last LSAP is closed.
- * Moreover, we would need to wait the LAP disconnection...
- */
-static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
-{
-       discinfo_t *discoveries;        /* Copy of the discovery log */
-       int     number;                 /* Number of nodes in the log */
-       int     i;
-       int     err = -ENETUNREACH;
-       __u32   daddr = DEV_ADDR_ANY;   /* Address we found the service on */
-       __u8    dtsap_sel = 0x0;        /* TSAP associated with it */
-
-       pr_debug("%s(), name=%s\n", __func__, name);
-
-       /* Ask lmp for the current discovery log
-        * Note : we have to use irlmp_get_discoveries(), as opposed
-        * to play with the cachelog directly, because while we are
-        * making our ias query, le log might change... */
-       discoveries = irlmp_get_discoveries(&number, self->mask.word,
-                                           self->nslots);
-       /* Check if the we got some results */
-       if (discoveries == NULL)
-               return -ENETUNREACH;    /* No nodes discovered */
-
-       /*
-        * Now, check all discovered devices (if any), and connect
-        * client only about the services that the client is
-        * interested in...
-        */
-       for(i = 0; i < number; i++) {
-               /* Try the address in the log */
-               self->daddr = discoveries[i].daddr;
-               self->saddr = 0x0;
-               pr_debug("%s(), trying daddr = %08x\n",
-                        __func__, self->daddr);
-
-               /* Query remote LM-IAS for this service */
-               err = irda_find_lsap_sel(self, name);
-               switch (err) {
-               case 0:
-                       /* We found the requested service */
-                       if(daddr != DEV_ADDR_ANY) {
-                               pr_debug("%s(), discovered service ''%s'' in two different devices !!!\n",
-                                        __func__, name);
-                               self->daddr = DEV_ADDR_ANY;
-                               kfree(discoveries);
-                               return -ENOTUNIQ;
-                       }
-                       /* First time we found that one, save it ! */
-                       daddr = self->daddr;
-                       dtsap_sel = self->dtsap_sel;
-                       break;
-               case -EADDRNOTAVAIL:
-                       /* Requested service simply doesn't exist on this node */
-                       break;
-               default:
-                       /* Something bad did happen :-( */
-                       pr_debug("%s(), unexpected IAS query failure\n",
-                                __func__);
-                       self->daddr = DEV_ADDR_ANY;
-                       kfree(discoveries);
-                       return -EHOSTUNREACH;
-               }
-       }
-       /* Cleanup our copy of the discovery log */
-       kfree(discoveries);
-
-       /* Check out what we found */
-       if(daddr == DEV_ADDR_ANY) {
-               pr_debug("%s(), cannot discover service ''%s'' in any device !!!\n",
-                        __func__, name);
-               self->daddr = DEV_ADDR_ANY;
-               return -EADDRNOTAVAIL;
-       }
-
-       /* Revert back to discovered device & service */
-       self->daddr = daddr;
-       self->saddr = 0x0;
-       self->dtsap_sel = dtsap_sel;
-
-       pr_debug("%s(), discovered requested service ''%s'' at address %08x\n",
-                __func__, name, self->daddr);
-
-       return 0;
-}
-
-/*
- * Function irda_getname (sock, uaddr, uaddr_len, peer)
- *
- *    Return the our own, or peers socket address (sockaddr_irda)
- *
- */
-static int irda_getname(struct socket *sock, struct sockaddr *uaddr,
-                       int *uaddr_len, int peer)
-{
-       struct sockaddr_irda saddr;
-       struct sock *sk = sock->sk;
-       struct irda_sock *self = irda_sk(sk);
-
-       memset(&saddr, 0, sizeof(saddr));
-       if (peer) {
-               if (sk->sk_state != TCP_ESTABLISHED)
-                       return -ENOTCONN;
-
-               saddr.sir_family = AF_IRDA;
-               saddr.sir_lsap_sel = self->dtsap_sel;
-               saddr.sir_addr = self->daddr;
-       } else {
-               saddr.sir_family = AF_IRDA;
-               saddr.sir_lsap_sel = self->stsap_sel;
-               saddr.sir_addr = self->saddr;
-       }
-
-       pr_debug("%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel);
-       pr_debug("%s(), addr = %08x\n", __func__, saddr.sir_addr);
-
-       /* uaddr_len come to us uninitialised */
-       *uaddr_len = sizeof (struct sockaddr_irda);
-       memcpy(uaddr, &saddr, *uaddr_len);
-
-       return 0;
-}
-
-/*
- * Function irda_listen (sock, backlog)
- *
- *    Just move to the listen state
- *
- */
-static int irda_listen(struct socket *sock, int backlog)
-{
-       struct sock *sk = sock->sk;
-       int err = -EOPNOTSUPP;
-
-       lock_sock(sk);
-
-       if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) &&
-           (sk->sk_type != SOCK_DGRAM))
-               goto out;
-
-       if (sk->sk_state != TCP_LISTEN) {
-               sk->sk_max_ack_backlog = backlog;
-               sk->sk_state           = TCP_LISTEN;
-
-               err = 0;
-       }
-out:
-       release_sock(sk);
-
-       return err;
-}
-
-/*
- * Function irda_bind (sock, uaddr, addr_len)
- *
- *    Used by servers to register their well known TSAP
- *
- */
-static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
-{
-       struct sock *sk = sock->sk;
-       struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
-       struct irda_sock *self = irda_sk(sk);
-       int err;
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       if (addr_len != sizeof(struct sockaddr_irda))
-               return -EINVAL;
-
-       lock_sock(sk);
-#ifdef CONFIG_IRDA_ULTRA
-       /* Special care for Ultra sockets */
-       if ((sk->sk_type == SOCK_DGRAM) &&
-           (sk->sk_protocol == IRDAPROTO_ULTRA)) {
-               self->pid = addr->sir_lsap_sel;
-               err = -EOPNOTSUPP;
-               if (self->pid & 0x80) {
-                       pr_debug("%s(), extension in PID not supp!\n",
-                                __func__);
-                       goto out;
-               }
-               err = irda_open_lsap(self, self->pid);
-               if (err < 0)
-                       goto out;
-
-               /* Pretend we are connected */
-               sock->state = SS_CONNECTED;
-               sk->sk_state   = TCP_ESTABLISHED;
-               err = 0;
-
-               goto out;
-       }
-#endif /* CONFIG_IRDA_ULTRA */
-
-       self->ias_obj = irias_new_object(addr->sir_name, jiffies);
-       err = -ENOMEM;
-       if (self->ias_obj == NULL)
-               goto out;
-
-       err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name);
-       if (err < 0) {
-               irias_delete_object(self->ias_obj);
-               self->ias_obj = NULL;
-               goto out;
-       }
-
-       /*  Register with LM-IAS */
-       irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel",
-                                self->stsap_sel, IAS_KERNEL_ATTR);
-       irias_insert_object(self->ias_obj);
-
-       err = 0;
-out:
-       release_sock(sk);
-       return err;
-}
-
-/*
- * Function irda_accept (sock, newsock, flags)
- *
- *    Wait for incoming connection
- *
- */
-static int irda_accept(struct socket *sock, struct socket *newsock, int flags,
-                      bool kern)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *new, *self = irda_sk(sk);
-       struct sock *newsk;
-       struct sk_buff *skb = NULL;
-       int err;
-
-       err = irda_create(sock_net(sk), newsock, sk->sk_protocol, kern);
-       if (err)
-               return err;
-
-       err = -EINVAL;
-
-       lock_sock(sk);
-       if (sock->state != SS_UNCONNECTED)
-               goto out;
-
-       err = -EOPNOTSUPP;
-       if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) &&
-           (sk->sk_type != SOCK_DGRAM))
-               goto out;
-
-       err = -EINVAL;
-       if (sk->sk_state != TCP_LISTEN)
-               goto out;
-
-       /*
-        *      The read queue this time is holding sockets ready to use
-        *      hooked into the SABM we saved
-        */
-
-       /*
-        * We can perform the accept only if there is incoming data
-        * on the listening socket.
-        * So, we will block the caller until we receive any data.
-        * If the caller was waiting on select() or poll() before
-        * calling us, the data is waiting for us ;-)
-        * Jean II
-        */
-       while (1) {
-               skb = skb_dequeue(&sk->sk_receive_queue);
-               if (skb)
-                       break;
-
-               /* Non blocking operation */
-               err = -EWOULDBLOCK;
-               if (flags & O_NONBLOCK)
-                       goto out;
-
-               err = wait_event_interruptible(*(sk_sleep(sk)),
-                                       skb_peek(&sk->sk_receive_queue));
-               if (err)
-                       goto out;
-       }
-
-       newsk = newsock->sk;
-       err = -EIO;
-       if (newsk == NULL)
-               goto out;
-
-       newsk->sk_state = TCP_ESTABLISHED;
-
-       new = irda_sk(newsk);
-
-       /* Now attach up the new socket */
-       new->tsap = irttp_dup(self->tsap, new);
-       err = -EPERM; /* value does not seem to make sense. -arnd */
-       if (!new->tsap) {
-               pr_debug("%s(), dup failed!\n", __func__);
-               goto out;
-       }
-
-       new->stsap_sel = new->tsap->stsap_sel;
-       new->dtsap_sel = new->tsap->dtsap_sel;
-       new->saddr = irttp_get_saddr(new->tsap);
-       new->daddr = irttp_get_daddr(new->tsap);
-
-       new->max_sdu_size_tx = self->max_sdu_size_tx;
-       new->max_sdu_size_rx = self->max_sdu_size_rx;
-       new->max_data_size   = self->max_data_size;
-       new->max_header_size = self->max_header_size;
-
-       memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info));
-
-       /* Clean up the original one to keep it in listen state */
-       irttp_listen(self->tsap);
-
-       sk->sk_ack_backlog--;
-
-       newsock->state = SS_CONNECTED;
-
-       irda_connect_response(new);
-       err = 0;
-out:
-       kfree_skb(skb);
-       release_sock(sk);
-       return err;
-}
-
-/*
- * Function irda_connect (sock, uaddr, addr_len, flags)
- *
- *    Connect to a IrDA device
- *
- * The main difference with a "standard" connect is that with IrDA we need
- * to resolve the service name into a TSAP selector (in TCP, port number
- * doesn't have to be resolved).
- * Because of this service name resolution, we can offer "auto-connect",
- * where we connect to a service without specifying a destination address.
- *
- * Note : by consulting "errno", the user space caller may learn the cause
- * of the failure. Most of them are visible in the function, others may come
- * from subroutines called and are listed here :
- *     o EBUSY : already processing a connect
- *     o EHOSTUNREACH : bad addr->sir_addr argument
- *     o EADDRNOTAVAIL : bad addr->sir_name argument
- *     o ENOTUNIQ : more than one node has addr->sir_name (auto-connect)
- *     o ENETUNREACH : no node found on the network (auto-connect)
- */
-static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
-                       int addr_len, int flags)
-{
-       struct sock *sk = sock->sk;
-       struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
-       struct irda_sock *self = irda_sk(sk);
-       int err;
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       lock_sock(sk);
-       /* Don't allow connect for Ultra sockets */
-       err = -ESOCKTNOSUPPORT;
-       if ((sk->sk_type == SOCK_DGRAM) && (sk->sk_protocol == IRDAPROTO_ULTRA))
-               goto out;
-
-       if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
-               sock->state = SS_CONNECTED;
-               err = 0;
-               goto out;   /* Connect completed during a ERESTARTSYS event */
-       }
-
-       if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) {
-               sock->state = SS_UNCONNECTED;
-               err = -ECONNREFUSED;
-               goto out;
-       }
-
-       err = -EISCONN;      /* No reconnect on a seqpacket socket */
-       if (sk->sk_state == TCP_ESTABLISHED)
-               goto out;
-
-       sk->sk_state   = TCP_CLOSE;
-       sock->state = SS_UNCONNECTED;
-
-       err = -EINVAL;
-       if (addr_len != sizeof(struct sockaddr_irda))
-               goto out;
-
-       /* Check if user supplied any destination device address */
-       if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) {
-               /* Try to find one suitable */
-               err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name);
-               if (err) {
-                       pr_debug("%s(), auto-connect failed!\n", __func__);
-                       goto out;
-               }
-       } else {
-               /* Use the one provided by the user */
-               self->daddr = addr->sir_addr;
-               pr_debug("%s(), daddr = %08x\n", __func__, self->daddr);
-
-               /* If we don't have a valid service name, we assume the
-                * user want to connect on a specific LSAP. Prevent
-                * the use of invalid LSAPs (IrLMP 1.1 p10). Jean II */
-               if((addr->sir_name[0] != '\0') ||
-                  (addr->sir_lsap_sel >= 0x70)) {
-                       /* Query remote LM-IAS using service name */
-                       err = irda_find_lsap_sel(self, addr->sir_name);
-                       if (err) {
-                               pr_debug("%s(), connect failed!\n", __func__);
-                               goto out;
-                       }
-               } else {
-                       /* Directly connect to the remote LSAP
-                        * specified by the sir_lsap field.
-                        * Please use with caution, in IrDA LSAPs are
-                        * dynamic and there is no "well-known" LSAP. */
-                       self->dtsap_sel = addr->sir_lsap_sel;
-               }
-       }
-
-       /* Check if we have opened a local TSAP */
-       if (!self->tsap) {
-               err = irda_open_tsap(self, LSAP_ANY, addr->sir_name);
-               if (err)
-                       goto out;
-       }
-
-       /* Move to connecting socket, start sending Connect Requests */
-       sock->state = SS_CONNECTING;
-       sk->sk_state   = TCP_SYN_SENT;
-
-       /* Connect to remote device */
-       err = irttp_connect_request(self->tsap, self->dtsap_sel,
-                                   self->saddr, self->daddr, NULL,
-                                   self->max_sdu_size_rx, NULL);
-       if (err) {
-               pr_debug("%s(), connect failed!\n", __func__);
-               goto out;
-       }
-
-       /* Now the loop */
-       err = -EINPROGRESS;
-       if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
-               goto out;
-
-       err = -ERESTARTSYS;
-       if (wait_event_interruptible(*(sk_sleep(sk)),
-                                    (sk->sk_state != TCP_SYN_SENT)))
-               goto out;
-
-       if (sk->sk_state != TCP_ESTABLISHED) {
-               sock->state = SS_UNCONNECTED;
-               err = sock_error(sk);
-               if (!err)
-                       err = -ECONNRESET;
-               goto out;
-       }
-
-       sock->state = SS_CONNECTED;
-
-       /* At this point, IrLMP has assigned our source address */
-       self->saddr = irttp_get_saddr(self->tsap);
-       err = 0;
-out:
-       release_sock(sk);
-       return err;
-}
-
-static struct proto irda_proto = {
-       .name     = "IRDA",
-       .owner    = THIS_MODULE,
-       .obj_size = sizeof(struct irda_sock),
-};
-
-/*
- * Function irda_create (sock, protocol)
- *
- *    Create IrDA socket
- *
- */
-static int irda_create(struct net *net, struct socket *sock, int protocol,
-                      int kern)
-{
-       struct sock *sk;
-       struct irda_sock *self;
-
-       if (protocol < 0 || protocol > SK_PROTOCOL_MAX)
-               return -EINVAL;
-
-       if (net != &init_net)
-               return -EAFNOSUPPORT;
-
-       /* Check for valid socket type */
-       switch (sock->type) {
-       case SOCK_STREAM:     /* For TTP connections with SAR disabled */
-       case SOCK_SEQPACKET:  /* For TTP connections with SAR enabled */
-       case SOCK_DGRAM:      /* For TTP Unitdata or LMP Ultra transfers */
-               break;
-       default:
-               return -ESOCKTNOSUPPORT;
-       }
-
-       /* Allocate networking socket */
-       sk = sk_alloc(net, PF_IRDA, GFP_KERNEL, &irda_proto, kern);
-       if (sk == NULL)
-               return -ENOMEM;
-
-       self = irda_sk(sk);
-       pr_debug("%s() : self is %p\n", __func__, self);
-
-       init_waitqueue_head(&self->query_wait);
-
-       switch (sock->type) {
-       case SOCK_STREAM:
-               sock->ops = &irda_stream_ops;
-               self->max_sdu_size_rx = TTP_SAR_DISABLE;
-               break;
-       case SOCK_SEQPACKET:
-               sock->ops = &irda_seqpacket_ops;
-               self->max_sdu_size_rx = TTP_SAR_UNBOUND;
-               break;
-       case SOCK_DGRAM:
-               switch (protocol) {
-#ifdef CONFIG_IRDA_ULTRA
-               case IRDAPROTO_ULTRA:
-                       sock->ops = &irda_ultra_ops;
-                       /* Initialise now, because we may send on unbound
-                        * sockets. Jean II */
-                       self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER;
-                       self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER;
-                       break;
-#endif /* CONFIG_IRDA_ULTRA */
-               case IRDAPROTO_UNITDATA:
-                       sock->ops = &irda_dgram_ops;
-                       /* We let Unitdata conn. be like seqpack conn. */
-                       self->max_sdu_size_rx = TTP_SAR_UNBOUND;
-                       break;
-               default:
-                       sk_free(sk);
-                       return -ESOCKTNOSUPPORT;
-               }
-               break;
-       default:
-               sk_free(sk);
-               return -ESOCKTNOSUPPORT;
-       }
-
-       /* Initialise networking socket struct */
-       sock_init_data(sock, sk);       /* Note : set sk->sk_refcnt to 1 */
-       sk->sk_family = PF_IRDA;
-       sk->sk_protocol = protocol;
-
-       /* Register as a client with IrLMP */
-       self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
-       self->mask.word = 0xffff;
-       self->rx_flow = self->tx_flow = FLOW_START;
-       self->nslots = DISCOVERY_DEFAULT_SLOTS;
-       self->daddr = DEV_ADDR_ANY;     /* Until we get connected */
-       self->saddr = 0x0;              /* so IrLMP assign us any link */
-       return 0;
-}
-
-/*
- * Function irda_destroy_socket (self)
- *
- *    Destroy socket
- *
- */
-static void irda_destroy_socket(struct irda_sock *self)
-{
-       pr_debug("%s(%p)\n", __func__, self);
-
-       /* Unregister with IrLMP */
-       irlmp_unregister_client(self->ckey);
-       irlmp_unregister_service(self->skey);
-
-       /* Unregister with LM-IAS */
-       if (self->ias_obj) {
-               irias_delete_object(self->ias_obj);
-               self->ias_obj = NULL;
-       }
-
-       if (self->iriap) {
-               iriap_close(self->iriap);
-               self->iriap = NULL;
-       }
-
-       if (self->tsap) {
-               irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
-               irttp_close_tsap(self->tsap);
-               self->tsap = NULL;
-       }
-#ifdef CONFIG_IRDA_ULTRA
-       if (self->lsap) {
-               irlmp_close_lsap(self->lsap);
-               self->lsap = NULL;
-       }
-#endif /* CONFIG_IRDA_ULTRA */
-}
-
-/*
- * Function irda_release (sock)
- */
-static int irda_release(struct socket *sock)
-{
-       struct sock *sk = sock->sk;
-
-       if (sk == NULL)
-               return 0;
-
-       lock_sock(sk);
-       sk->sk_state       = TCP_CLOSE;
-       sk->sk_shutdown   |= SEND_SHUTDOWN;
-       sk->sk_state_change(sk);
-
-       /* Destroy IrDA socket */
-       irda_destroy_socket(irda_sk(sk));
-
-       sock_orphan(sk);
-       sock->sk   = NULL;
-       release_sock(sk);
-
-       /* Purge queues (see sock_init_data()) */
-       skb_queue_purge(&sk->sk_receive_queue);
-
-       /* Destroy networking socket if we are the last reference on it,
-        * i.e. if(sk->sk_refcnt == 0) -> sk_free(sk) */
-       sock_put(sk);
-
-       /* Notes on socket locking and deallocation... - Jean II
-        * In theory we should put pairs of sock_hold() / sock_put() to
-        * prevent the socket to be destroyed whenever there is an
-        * outstanding request or outstanding incoming packet or event.
-        *
-        * 1) This may include IAS request, both in connect and getsockopt.
-        * Unfortunately, the situation is a bit more messy than it looks,
-        * because we close iriap and kfree(self) above.
-        *
-        * 2) This may include selective discovery in getsockopt.
-        * Same stuff as above, irlmp registration and self are gone.
-        *
-        * Probably 1 and 2 may not matter, because it's all triggered
-        * by a process and the socket layer already prevent the
-        * socket to go away while a process is holding it, through
-        * sockfd_put() and fput()...
-        *
-        * 3) This may include deferred TSAP closure. In particular,
-        * we may receive a late irda_disconnect_indication()
-        * Fortunately, (tsap_cb *)->close_pend should protect us
-        * from that.
-        *
-        * I did some testing on SMP, and it looks solid. And the socket
-        * memory leak is now gone... - Jean II
-        */
-
-       return 0;
-}
-
-/*
- * Function irda_sendmsg (sock, msg, len)
- *
- *    Send message down to TinyTP. This function is used for both STREAM and
- *    SEQPACK services. This is possible since it forces the client to
- *    fragment the message if necessary
- */
-static int irda_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self;
-       struct sk_buff *skb;
-       int err = -EPIPE;
-
-       pr_debug("%s(), len=%zd\n", __func__, len);
-
-       /* Note : socket.c set MSG_EOR on SEQPACKET sockets */
-       if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT |
-                              MSG_NOSIGNAL)) {
-               return -EINVAL;
-       }
-
-       lock_sock(sk);
-
-       if (sk->sk_shutdown & SEND_SHUTDOWN)
-               goto out_err;
-
-       if (sk->sk_state != TCP_ESTABLISHED) {
-               err = -ENOTCONN;
-               goto out;
-       }
-
-       self = irda_sk(sk);
-
-       /* Check if IrTTP is wants us to slow down */
-
-       if (wait_event_interruptible(*(sk_sleep(sk)),
-           (self->tx_flow != FLOW_STOP  ||  sk->sk_state != TCP_ESTABLISHED))) {
-               err = -ERESTARTSYS;
-               goto out;
-       }
-
-       /* Check if we are still connected */
-       if (sk->sk_state != TCP_ESTABLISHED) {
-               err = -ENOTCONN;
-               goto out;
-       }
-
-       /* Check that we don't send out too big frames */
-       if (len > self->max_data_size) {
-               pr_debug("%s(), Chopping frame from %zd to %d bytes!\n",
-                        __func__, len, self->max_data_size);
-               len = self->max_data_size;
-       }
-
-       skb = sock_alloc_send_skb(sk, len + self->max_header_size + 16,
-                                 msg->msg_flags & MSG_DONTWAIT, &err);
-       if (!skb)
-               goto out_err;
-
-       skb_reserve(skb, self->max_header_size + 16);
-       skb_reset_transport_header(skb);
-       skb_put(skb, len);
-       err = memcpy_from_msg(skb_transport_header(skb), msg, len);
-       if (err) {
-               kfree_skb(skb);
-               goto out_err;
-       }
-
-       /*
-        * Just send the message to TinyTP, and let it deal with possible
-        * errors. No need to duplicate all that here
-        */
-       err = irttp_data_request(self->tsap, skb);
-       if (err) {
-               pr_debug("%s(), err=%d\n", __func__, err);
-               goto out_err;
-       }
-
-       release_sock(sk);
-       /* Tell client how much data we actually sent */
-       return len;
-
-out_err:
-       err = sk_stream_error(sk, msg->msg_flags, err);
-out:
-       release_sock(sk);
-       return err;
-
-}
-
-/*
- * Function irda_recvmsg_dgram (sock, msg, size, flags)
- *
- *    Try to receive message and copy it to user. The frame is discarded
- *    after being read, regardless of how much the user actually read
- */
-static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg,
-                             size_t size, int flags)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self = irda_sk(sk);
-       struct sk_buff *skb;
-       size_t copied;
-       int err;
-
-       skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
-                               flags & MSG_DONTWAIT, &err);
-       if (!skb)
-               return err;
-
-       skb_reset_transport_header(skb);
-       copied = skb->len;
-
-       if (copied > size) {
-               pr_debug("%s(), Received truncated frame (%zd < %zd)!\n",
-                        __func__, copied, size);
-               copied = size;
-               msg->msg_flags |= MSG_TRUNC;
-       }
-       skb_copy_datagram_msg(skb, 0, msg, copied);
-
-       skb_free_datagram(sk, skb);
-
-       /*
-        *  Check if we have previously stopped IrTTP and we know
-        *  have more free space in our rx_queue. If so tell IrTTP
-        *  to start delivering frames again before our rx_queue gets
-        *  empty
-        */
-       if (self->rx_flow == FLOW_STOP) {
-               if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) {
-                       pr_debug("%s(), Starting IrTTP\n", __func__);
-                       self->rx_flow = FLOW_START;
-                       irttp_flow_request(self->tsap, FLOW_START);
-               }
-       }
-
-       return copied;
-}
-
-/*
- * Function irda_recvmsg_stream (sock, msg, size, flags)
- */
-static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg,
-                              size_t size, int flags)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self = irda_sk(sk);
-       int noblock = flags & MSG_DONTWAIT;
-       size_t copied = 0;
-       int target, err;
-       long timeo;
-
-       if ((err = sock_error(sk)) < 0)
-               return err;
-
-       if (sock->flags & __SO_ACCEPTCON)
-               return -EINVAL;
-
-       err =-EOPNOTSUPP;
-       if (flags & MSG_OOB)
-               return -EOPNOTSUPP;
-
-       err = 0;
-       target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
-       timeo = sock_rcvtimeo(sk, noblock);
-
-       do {
-               int chunk;
-               struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);
-
-               if (skb == NULL) {
-                       DEFINE_WAIT(wait);
-                       err = 0;
-
-                       if (copied >= target)
-                               break;
-
-                       prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
-
-                       /*
-                        *      POSIX 1003.1g mandates this order.
-                        */
-                       err = sock_error(sk);
-                       if (err)
-                               ;
-                       else if (sk->sk_shutdown & RCV_SHUTDOWN)
-                               ;
-                       else if (noblock)
-                               err = -EAGAIN;
-                       else if (signal_pending(current))
-                               err = sock_intr_errno(timeo);
-                       else if (sk->sk_state != TCP_ESTABLISHED)
-                               err = -ENOTCONN;
-                       else if (skb_peek(&sk->sk_receive_queue) == NULL)
-                               /* Wait process until data arrives */
-                               schedule();
-
-                       finish_wait(sk_sleep(sk), &wait);
-
-                       if (err)
-                               return err;
-                       if (sk->sk_shutdown & RCV_SHUTDOWN)
-                               break;
-
-                       continue;
-               }
-
-               chunk = min_t(unsigned int, skb->len, size);
-               if (memcpy_to_msg(msg, skb->data, chunk)) {
-                       skb_queue_head(&sk->sk_receive_queue, skb);
-                       if (copied == 0)
-                               copied = -EFAULT;
-                       break;
-               }
-               copied += chunk;
-               size -= chunk;
-
-               /* Mark read part of skb as used */
-               if (!(flags & MSG_PEEK)) {
-                       skb_pull(skb, chunk);
-
-                       /* put the skb back if we didn't use it up.. */
-                       if (skb->len) {
-                               pr_debug("%s(), back on q!\n",
-                                        __func__);
-                               skb_queue_head(&sk->sk_receive_queue, skb);
-                               break;
-                       }
-
-                       kfree_skb(skb);
-               } else {
-                       pr_debug("%s() questionable!?\n", __func__);
-
-                       /* put message back and return */
-                       skb_queue_head(&sk->sk_receive_queue, skb);
-                       break;
-               }
-       } while (size);
-
-       /*
-        *  Check if we have previously stopped IrTTP and we know
-        *  have more free space in our rx_queue. If so tell IrTTP
-        *  to start delivering frames again before our rx_queue gets
-        *  empty
-        */
-       if (self->rx_flow == FLOW_STOP) {
-               if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) {
-                       pr_debug("%s(), Starting IrTTP\n", __func__);
-                       self->rx_flow = FLOW_START;
-                       irttp_flow_request(self->tsap, FLOW_START);
-               }
-       }
-
-       return copied;
-}
-
-/*
- * Function irda_sendmsg_dgram (sock, msg, len)
- *
- *    Send message down to TinyTP for the unreliable sequenced
- *    packet service...
- *
- */
-static int irda_sendmsg_dgram(struct socket *sock, struct msghdr *msg,
-                             size_t len)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self;
-       struct sk_buff *skb;
-       int err;
-
-       pr_debug("%s(), len=%zd\n", __func__, len);
-
-       if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
-               return -EINVAL;
-
-       lock_sock(sk);
-
-       if (sk->sk_shutdown & SEND_SHUTDOWN) {
-               send_sig(SIGPIPE, current, 0);
-               err = -EPIPE;
-               goto out;
-       }
-
-       err = -ENOTCONN;
-       if (sk->sk_state != TCP_ESTABLISHED)
-               goto out;
-
-       self = irda_sk(sk);
-
-       /*
-        * Check that we don't send out too big frames. This is an unreliable
-        * service, so we have no fragmentation and no coalescence
-        */
-       if (len > self->max_data_size) {
-               pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n",
-                        __func__, len, self->max_data_size);
-               len = self->max_data_size;
-       }
-
-       skb = sock_alloc_send_skb(sk, len + self->max_header_size,
-                                 msg->msg_flags & MSG_DONTWAIT, &err);
-       err = -ENOBUFS;
-       if (!skb)
-               goto out;
-
-       skb_reserve(skb, self->max_header_size);
-       skb_reset_transport_header(skb);
-
-       pr_debug("%s(), appending user data\n", __func__);
-       skb_put(skb, len);
-       err = memcpy_from_msg(skb_transport_header(skb), msg, len);
-       if (err) {
-               kfree_skb(skb);
-               goto out;
-       }
-
-       /*
-        * Just send the message to TinyTP, and let it deal with possible
-        * errors. No need to duplicate all that here
-        */
-       err = irttp_udata_request(self->tsap, skb);
-       if (err) {
-               pr_debug("%s(), err=%d\n", __func__, err);
-               goto out;
-       }
-
-       release_sock(sk);
-       return len;
-
-out:
-       release_sock(sk);
-       return err;
-}
-
-/*
- * Function irda_sendmsg_ultra (sock, msg, len)
- *
- *    Send message down to IrLMP for the unreliable Ultra
- *    packet service...
- */
-#ifdef CONFIG_IRDA_ULTRA
-static int irda_sendmsg_ultra(struct socket *sock, struct msghdr *msg,
-                             size_t len)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self;
-       __u8 pid = 0;
-       int bound = 0;
-       struct sk_buff *skb;
-       int err;
-
-       pr_debug("%s(), len=%zd\n", __func__, len);
-
-       err = -EINVAL;
-       if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
-               return -EINVAL;
-
-       lock_sock(sk);
-
-       err = -EPIPE;
-       if (sk->sk_shutdown & SEND_SHUTDOWN) {
-               send_sig(SIGPIPE, current, 0);
-               goto out;
-       }
-
-       self = irda_sk(sk);
-
-       /* Check if an address was specified with sendto. Jean II */
-       if (msg->msg_name) {
-               DECLARE_SOCKADDR(struct sockaddr_irda *, addr, msg->msg_name);
-               err = -EINVAL;
-               /* Check address, extract pid. Jean II */
-               if (msg->msg_namelen < sizeof(*addr))
-                       goto out;
-               if (addr->sir_family != AF_IRDA)
-                       goto out;
-
-               pid = addr->sir_lsap_sel;
-               if (pid & 0x80) {
-                       pr_debug("%s(), extension in PID not supp!\n",
-                                __func__);
-                       err = -EOPNOTSUPP;
-                       goto out;
-               }
-       } else {
-               /* Check that the socket is properly bound to an Ultra
-                * port. Jean II */
-               if ((self->lsap == NULL) ||
-                   (sk->sk_state != TCP_ESTABLISHED)) {
-                       pr_debug("%s(), socket not bound to Ultra PID.\n",
-                                __func__);
-                       err = -ENOTCONN;
-                       goto out;
-               }
-               /* Use PID from socket */
-               bound = 1;
-       }
-
-       /*
-        * Check that we don't send out too big frames. This is an unreliable
-        * service, so we have no fragmentation and no coalescence
-        */
-       if (len > self->max_data_size) {
-               pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n",
-                        __func__, len, self->max_data_size);
-               len = self->max_data_size;
-       }
-
-       skb = sock_alloc_send_skb(sk, len + self->max_header_size,
-                                 msg->msg_flags & MSG_DONTWAIT, &err);
-       err = -ENOBUFS;
-       if (!skb)
-               goto out;
-
-       skb_reserve(skb, self->max_header_size);
-       skb_reset_transport_header(skb);
-
-       pr_debug("%s(), appending user data\n", __func__);
-       skb_put(skb, len);
-       err = memcpy_from_msg(skb_transport_header(skb), msg, len);
-       if (err) {
-               kfree_skb(skb);
-               goto out;
-       }
-
-       err = irlmp_connless_data_request((bound ? self->lsap : NULL),
-                                         skb, pid);
-       if (err)
-               pr_debug("%s(), err=%d\n", __func__, err);
-out:
-       release_sock(sk);
-       return err ? : len;
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irda_shutdown (sk, how)
- */
-static int irda_shutdown(struct socket *sock, int how)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self = irda_sk(sk);
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       lock_sock(sk);
-
-       sk->sk_state       = TCP_CLOSE;
-       sk->sk_shutdown   |= SEND_SHUTDOWN;
-       sk->sk_state_change(sk);
-
-       if (self->iriap) {
-               iriap_close(self->iriap);
-               self->iriap = NULL;
-       }
-
-       if (self->tsap) {
-               irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
-               irttp_close_tsap(self->tsap);
-               self->tsap = NULL;
-       }
-
-       /* A few cleanup so the socket look as good as new... */
-       self->rx_flow = self->tx_flow = FLOW_START;     /* needed ??? */
-       self->daddr = DEV_ADDR_ANY;     /* Until we get re-connected */
-       self->saddr = 0x0;              /* so IrLMP assign us any link */
-
-       release_sock(sk);
-
-       return 0;
-}
-
-/*
- * Function irda_poll (file, sock, wait)
- */
-static unsigned int irda_poll(struct file * file, struct socket *sock,
-                             poll_table *wait)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self = irda_sk(sk);
-       unsigned int mask;
-
-       poll_wait(file, sk_sleep(sk), wait);
-       mask = 0;
-
-       /* Exceptional events? */
-       if (sk->sk_err)
-               mask |= POLLERR;
-       if (sk->sk_shutdown & RCV_SHUTDOWN) {
-               pr_debug("%s(), POLLHUP\n", __func__);
-               mask |= POLLHUP;
-       }
-
-       /* Readable? */
-       if (!skb_queue_empty(&sk->sk_receive_queue)) {
-               pr_debug("Socket is readable\n");
-               mask |= POLLIN | POLLRDNORM;
-       }
-
-       /* Connection-based need to check for termination and startup */
-       switch (sk->sk_type) {
-       case SOCK_STREAM:
-               if (sk->sk_state == TCP_CLOSE) {
-                       pr_debug("%s(), POLLHUP\n", __func__);
-                       mask |= POLLHUP;
-               }
-
-               if (sk->sk_state == TCP_ESTABLISHED) {
-                       if ((self->tx_flow == FLOW_START) &&
-                           sock_writeable(sk))
-                       {
-                               mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
-                       }
-               }
-               break;
-       case SOCK_SEQPACKET:
-               if ((self->tx_flow == FLOW_START) &&
-                   sock_writeable(sk))
-               {
-                       mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
-               }
-               break;
-       case SOCK_DGRAM:
-               if (sock_writeable(sk))
-                       mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
-               break;
-       default:
-               break;
-       }
-
-       return mask;
-}
-
-/*
- * Function irda_ioctl (sock, cmd, arg)
- */
-static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
-       struct sock *sk = sock->sk;
-       int err;
-
-       pr_debug("%s(), cmd=%#x\n", __func__, cmd);
-
-       err = -EINVAL;
-       switch (cmd) {
-       case TIOCOUTQ: {
-               long amount;
-
-               amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
-               if (amount < 0)
-                       amount = 0;
-               err = put_user(amount, (unsigned int __user *)arg);
-               break;
-       }
-
-       case TIOCINQ: {
-               struct sk_buff *skb;
-               long amount = 0L;
-               /* These two are safe on a single CPU system as only user tasks fiddle here */
-               if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
-                       amount = skb->len;
-               err = put_user(amount, (unsigned int __user *)arg);
-               break;
-       }
-
-       case SIOCGSTAMP:
-               if (sk != NULL)
-                       err = sock_get_timestamp(sk, (struct timeval __user *)arg);
-               break;
-
-       case SIOCGIFADDR:
-       case SIOCSIFADDR:
-       case SIOCGIFDSTADDR:
-       case SIOCSIFDSTADDR:
-       case SIOCGIFBRDADDR:
-       case SIOCSIFBRDADDR:
-       case SIOCGIFNETMASK:
-       case SIOCSIFNETMASK:
-       case SIOCGIFMETRIC:
-       case SIOCSIFMETRIC:
-               break;
-       default:
-               pr_debug("%s(), doing device ioctl!\n", __func__);
-               err = -ENOIOCTLCMD;
-       }
-
-       return err;
-}
-
-#ifdef CONFIG_COMPAT
-/*
- * Function irda_ioctl (sock, cmd, arg)
- */
-static int irda_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
-       /*
-        * All IRDA's ioctl are standard ones.
-        */
-       return -ENOIOCTLCMD;
-}
-#endif
-
-/*
- * Function irda_setsockopt (sock, level, optname, optval, optlen)
- *
- *    Set some options for the socket
- *
- */
-static int irda_setsockopt(struct socket *sock, int level, int optname,
-                          char __user *optval, unsigned int optlen)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self = irda_sk(sk);
-       struct irda_ias_set    *ias_opt;
-       struct ias_object      *ias_obj;
-       struct ias_attrib *     ias_attr;       /* Attribute in IAS object */
-       int opt, free_ias = 0, err = 0;
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       if (level != SOL_IRLMP)
-               return -ENOPROTOOPT;
-
-       lock_sock(sk);
-
-       switch (optname) {
-       case IRLMP_IAS_SET:
-               /* The user want to add an attribute to an existing IAS object
-                * (in the IAS database) or to create a new object with this
-                * attribute.
-                * We first query IAS to know if the object exist, and then
-                * create the right attribute...
-                */
-
-               if (optlen != sizeof(struct irda_ias_set)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Copy query to the driver. */
-               ias_opt = memdup_user(optval, optlen);
-               if (IS_ERR(ias_opt)) {
-                       err = PTR_ERR(ias_opt);
-                       goto out;
-               }
-
-               /* Find the object we target.
-                * If the user gives us an empty string, we use the object
-                * associated with this socket. This will workaround
-                * duplicated class name - Jean II */
-               if(ias_opt->irda_class_name[0] == '\0') {
-                       if(self->ias_obj == NULL) {
-                               kfree(ias_opt);
-                               err = -EINVAL;
-                               goto out;
-                       }
-                       ias_obj = self->ias_obj;
-               } else
-                       ias_obj = irias_find_object(ias_opt->irda_class_name);
-
-               /* Only ROOT can mess with the global IAS database.
-                * Users can only add attributes to the object associated
-                * with the socket they own - Jean II */
-               if((!capable(CAP_NET_ADMIN)) &&
-                  ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
-                       kfree(ias_opt);
-                       err = -EPERM;
-                       goto out;
-               }
-
-               /* If the object doesn't exist, create it */
-               if(ias_obj == (struct ias_object *) NULL) {
-                       /* Create a new object */
-                       ias_obj = irias_new_object(ias_opt->irda_class_name,
-                                                  jiffies);
-                       if (ias_obj == NULL) {
-                               kfree(ias_opt);
-                               err = -ENOMEM;
-                               goto out;
-                       }
-                       free_ias = 1;
-               }
-
-               /* Do we have the attribute already ? */
-               if(irias_find_attrib(ias_obj, ias_opt->irda_attrib_name)) {
-                       kfree(ias_opt);
-                       if (free_ias) {
-                               kfree(ias_obj->name);
-                               kfree(ias_obj);
-                       }
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Look at the type */
-               switch(ias_opt->irda_attrib_type) {
-               case IAS_INTEGER:
-                       /* Add an integer attribute */
-                       irias_add_integer_attrib(
-                               ias_obj,
-                               ias_opt->irda_attrib_name,
-                               ias_opt->attribute.irda_attrib_int,
-                               IAS_USER_ATTR);
-                       break;
-               case IAS_OCT_SEQ:
-                       /* Check length */
-                       if(ias_opt->attribute.irda_attrib_octet_seq.len >
-                          IAS_MAX_OCTET_STRING) {
-                               kfree(ias_opt);
-                               if (free_ias) {
-                                       kfree(ias_obj->name);
-                                       kfree(ias_obj);
-                               }
-
-                               err = -EINVAL;
-                               goto out;
-                       }
-                       /* Add an octet sequence attribute */
-                       irias_add_octseq_attrib(
-                             ias_obj,
-                             ias_opt->irda_attrib_name,
-                             ias_opt->attribute.irda_attrib_octet_seq.octet_seq,
-                             ias_opt->attribute.irda_attrib_octet_seq.len,
-                             IAS_USER_ATTR);
-                       break;
-               case IAS_STRING:
-                       /* Should check charset & co */
-                       /* Check length */
-                       /* The length is encoded in a __u8, and
-                        * IAS_MAX_STRING == 256, so there is no way
-                        * userspace can pass us a string too large.
-                        * Jean II */
-                       /* NULL terminate the string (avoid troubles) */
-                       ias_opt->attribute.irda_attrib_string.string[ias_opt->attribute.irda_attrib_string.len] = '\0';
-                       /* Add a string attribute */
-                       irias_add_string_attrib(
-                               ias_obj,
-                               ias_opt->irda_attrib_name,
-                               ias_opt->attribute.irda_attrib_string.string,
-                               IAS_USER_ATTR);
-                       break;
-               default :
-                       kfree(ias_opt);
-                       if (free_ias) {
-                               kfree(ias_obj->name);
-                               kfree(ias_obj);
-                       }
-                       err = -EINVAL;
-                       goto out;
-               }
-               irias_insert_object(ias_obj);
-               kfree(ias_opt);
-               break;
-       case IRLMP_IAS_DEL:
-               /* The user want to delete an object from our local IAS
-                * database. We just need to query the IAS, check is the
-                * object is not owned by the kernel and delete it.
-                */
-
-               if (optlen != sizeof(struct irda_ias_set)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Copy query to the driver. */
-               ias_opt = memdup_user(optval, optlen);
-               if (IS_ERR(ias_opt)) {
-                       err = PTR_ERR(ias_opt);
-                       goto out;
-               }
-
-               /* Find the object we target.
-                * If the user gives us an empty string, we use the object
-                * associated with this socket. This will workaround
-                * duplicated class name - Jean II */
-               if(ias_opt->irda_class_name[0] == '\0')
-                       ias_obj = self->ias_obj;
-               else
-                       ias_obj = irias_find_object(ias_opt->irda_class_name);
-               if(ias_obj == (struct ias_object *) NULL) {
-                       kfree(ias_opt);
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Only ROOT can mess with the global IAS database.
-                * Users can only del attributes from the object associated
-                * with the socket they own - Jean II */
-               if((!capable(CAP_NET_ADMIN)) &&
-                  ((ias_obj == NULL) || (ias_obj != self->ias_obj))) {
-                       kfree(ias_opt);
-                       err = -EPERM;
-                       goto out;
-               }
-
-               /* Find the attribute (in the object) we target */
-               ias_attr = irias_find_attrib(ias_obj,
-                                            ias_opt->irda_attrib_name);
-               if(ias_attr == (struct ias_attrib *) NULL) {
-                       kfree(ias_opt);
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Check is the user space own the object */
-               if(ias_attr->value->owner != IAS_USER_ATTR) {
-                       pr_debug("%s(), attempting to delete a kernel attribute\n",
-                                __func__);
-                       kfree(ias_opt);
-                       err = -EPERM;
-                       goto out;
-               }
-
-               /* Remove the attribute (and maybe the object) */
-               irias_delete_attrib(ias_obj, ias_attr, 1);
-               kfree(ias_opt);
-               break;
-       case IRLMP_MAX_SDU_SIZE:
-               if (optlen < sizeof(int)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               if (get_user(opt, (int __user *)optval)) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
-               /* Only possible for a seqpacket service (TTP with SAR) */
-               if (sk->sk_type != SOCK_SEQPACKET) {
-                       pr_debug("%s(), setting max_sdu_size = %d\n",
-                                __func__, opt);
-                       self->max_sdu_size_rx = opt;
-               } else {
-                       net_warn_ratelimited("%s: not allowed to set MAXSDUSIZE for this socket type!\n",
-                                            __func__);
-                       err = -ENOPROTOOPT;
-                       goto out;
-               }
-               break;
-       case IRLMP_HINTS_SET:
-               if (optlen < sizeof(int)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* The input is really a (__u8 hints[2]), easier as an int */
-               if (get_user(opt, (int __user *)optval)) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
-               /* Unregister any old registration */
-               irlmp_unregister_service(self->skey);
-
-               self->skey = irlmp_register_service((__u16) opt);
-               break;
-       case IRLMP_HINT_MASK_SET:
-               /* As opposed to the previous case which set the hint bits
-                * that we advertise, this one set the filter we use when
-                * making a discovery (nodes which don't match any hint
-                * bit in the mask are not reported).
-                */
-               if (optlen < sizeof(int)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* The input is really a (__u8 hints[2]), easier as an int */
-               if (get_user(opt, (int __user *)optval)) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
-               /* Set the new hint mask */
-               self->mask.word = (__u16) opt;
-               /* Mask out extension bits */
-               self->mask.word &= 0x7f7f;
-               /* Check if no bits */
-               if(!self->mask.word)
-                       self->mask.word = 0xFFFF;
-
-               break;
-       default:
-               err = -ENOPROTOOPT;
-               break;
-       }
-
-out:
-       release_sock(sk);
-
-       return err;
-}
-
-/*
- * Function irda_extract_ias_value(ias_opt, ias_value)
- *
- *    Translate internal IAS value structure to the user space representation
- *
- * The external representation of IAS values, as we exchange them with
- * user space program is quite different from the internal representation,
- * as stored in the IAS database (because we need a flat structure for
- * crossing kernel boundary).
- * This function transform the former in the latter. We also check
- * that the value type is valid.
- */
-static int irda_extract_ias_value(struct irda_ias_set *ias_opt,
-                                 struct ias_value *ias_value)
-{
-       /* Look at the type */
-       switch (ias_value->type) {
-       case IAS_INTEGER:
-               /* Copy the integer */
-               ias_opt->attribute.irda_attrib_int = ias_value->t.integer;
-               break;
-       case IAS_OCT_SEQ:
-               /* Set length */
-               ias_opt->attribute.irda_attrib_octet_seq.len = ias_value->len;
-               /* Copy over */
-               memcpy(ias_opt->attribute.irda_attrib_octet_seq.octet_seq,
-                      ias_value->t.oct_seq, ias_value->len);
-               break;
-       case IAS_STRING:
-               /* Set length */
-               ias_opt->attribute.irda_attrib_string.len = ias_value->len;
-               ias_opt->attribute.irda_attrib_string.charset = ias_value->charset;
-               /* Copy over */
-               memcpy(ias_opt->attribute.irda_attrib_string.string,
-                      ias_value->t.string, ias_value->len);
-               /* NULL terminate the string (avoid troubles) */
-               ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0';
-               break;
-       case IAS_MISSING:
-       default :
-               return -EINVAL;
-       }
-
-       /* Copy type over */
-       ias_opt->irda_attrib_type = ias_value->type;
-
-       return 0;
-}
-
-/*
- * Function irda_getsockopt (sock, level, optname, optval, optlen)
- */
-static int irda_getsockopt(struct socket *sock, int level, int optname,
-                          char __user *optval, int __user *optlen)
-{
-       struct sock *sk = sock->sk;
-       struct irda_sock *self = irda_sk(sk);
-       struct irda_device_list list = { 0 };
-       struct irda_device_info *discoveries;
-       struct irda_ias_set *   ias_opt;        /* IAS get/query params */
-       struct ias_object *     ias_obj;        /* Object in IAS */
-       struct ias_attrib *     ias_attr;       /* Attribute in IAS object */
-       int daddr = DEV_ADDR_ANY;       /* Dest address for IAS queries */
-       int val = 0;
-       int len = 0;
-       int err = 0;
-       int offset, total;
-
-       pr_debug("%s(%p)\n", __func__, self);
-
-       if (level != SOL_IRLMP)
-               return -ENOPROTOOPT;
-
-       if (get_user(len, optlen))
-               return -EFAULT;
-
-       if(len < 0)
-               return -EINVAL;
-
-       lock_sock(sk);
-
-       switch (optname) {
-       case IRLMP_ENUMDEVICES:
-
-               /* Offset to first device entry */
-               offset = sizeof(struct irda_device_list) -
-                       sizeof(struct irda_device_info);
-
-               if (len < offset) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Ask lmp for the current discovery log */
-               discoveries = irlmp_get_discoveries(&list.len, self->mask.word,
-                                                   self->nslots);
-               /* Check if the we got some results */
-               if (discoveries == NULL) {
-                       err = -EAGAIN;
-                       goto out;               /* Didn't find any devices */
-               }
-
-               /* Write total list length back to client */
-               if (copy_to_user(optval, &list, offset))
-                       err = -EFAULT;
-
-               /* Copy the list itself - watch for overflow */
-               if (list.len > 2048) {
-                       err = -EINVAL;
-                       goto bed;
-               }
-               total = offset + (list.len * sizeof(struct irda_device_info));
-               if (total > len)
-                       total = len;
-               if (copy_to_user(optval+offset, discoveries, total - offset))
-                       err = -EFAULT;
-
-               /* Write total number of bytes used back to client */
-               if (put_user(total, optlen))
-                       err = -EFAULT;
-bed:
-               /* Free up our buffer */
-               kfree(discoveries);
-               break;
-       case IRLMP_MAX_SDU_SIZE:
-               val = self->max_data_size;
-               len = sizeof(int);
-               if (put_user(len, optlen)) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
-               if (copy_to_user(optval, &val, len)) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
-               break;
-       case IRLMP_IAS_GET:
-               /* The user want an object from our local IAS database.
-                * We just need to query the IAS and return the value
-                * that we found */
-
-               /* Check that the user has allocated the right space for us */
-               if (len != sizeof(struct irda_ias_set)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Copy query to the driver. */
-               ias_opt = memdup_user(optval, len);
-               if (IS_ERR(ias_opt)) {
-                       err = PTR_ERR(ias_opt);
-                       goto out;
-               }
-
-               /* Find the object we target.
-                * If the user gives us an empty string, we use the object
-                * associated with this socket. This will workaround
-                * duplicated class name - Jean II */
-               if(ias_opt->irda_class_name[0] == '\0')
-                       ias_obj = self->ias_obj;
-               else
-                       ias_obj = irias_find_object(ias_opt->irda_class_name);
-               if(ias_obj == (struct ias_object *) NULL) {
-                       kfree(ias_opt);
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Find the attribute (in the object) we target */
-               ias_attr = irias_find_attrib(ias_obj,
-                                            ias_opt->irda_attrib_name);
-               if(ias_attr == (struct ias_attrib *) NULL) {
-                       kfree(ias_opt);
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Translate from internal to user structure */
-               err = irda_extract_ias_value(ias_opt, ias_attr->value);
-               if(err) {
-                       kfree(ias_opt);
-                       goto out;
-               }
-
-               /* Copy reply to the user */
-               if (copy_to_user(optval, ias_opt,
-                                sizeof(struct irda_ias_set))) {
-                       kfree(ias_opt);
-                       err = -EFAULT;
-                       goto out;
-               }
-               /* Note : don't need to put optlen, we checked it */
-               kfree(ias_opt);
-               break;
-       case IRLMP_IAS_QUERY:
-               /* The user want an object from a remote IAS database.
-                * We need to use IAP to query the remote database and
-                * then wait for the answer to come back. */
-
-               /* Check that the user has allocated the right space for us */
-               if (len != sizeof(struct irda_ias_set)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               /* Copy query to the driver. */
-               ias_opt = memdup_user(optval, len);
-               if (IS_ERR(ias_opt)) {
-                       err = PTR_ERR(ias_opt);
-                       goto out;
-               }
-
-               /* At this point, there are two cases...
-                * 1) the socket is connected - that's the easy case, we
-                *      just query the device we are connected to...
-                * 2) the socket is not connected - the user doesn't want
-                *      to connect and/or may not have a valid service name
-                *      (so can't create a fake connection). In this case,
-                *      we assume that the user pass us a valid destination
-                *      address in the requesting structure...
-                */
-               if(self->daddr != DEV_ADDR_ANY) {
-                       /* We are connected - reuse known daddr */
-                       daddr = self->daddr;
-               } else {
-                       /* We are not connected, we must specify a valid
-                        * destination address */
-                       daddr = ias_opt->daddr;
-                       if((!daddr) || (daddr == DEV_ADDR_ANY)) {
-                               kfree(ias_opt);
-                               err = -EINVAL;
-                               goto out;
-                       }
-               }
-
-               /* Check that we can proceed with IAP */
-               if (self->iriap) {
-                       net_warn_ratelimited("%s: busy with a previous query\n",
-                                            __func__);
-                       kfree(ias_opt);
-                       err = -EBUSY;
-                       goto out;
-               }
-
-               self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
-                                        irda_getvalue_confirm);
-
-               if (self->iriap == NULL) {
-                       kfree(ias_opt);
-                       err = -ENOMEM;
-                       goto out;
-               }
-
-               /* Treat unexpected wakeup as disconnect */
-               self->errno = -EHOSTUNREACH;
-
-               /* Query remote LM-IAS */
-               iriap_getvaluebyclass_request(self->iriap,
-                                             self->saddr, daddr,
-                                             ias_opt->irda_class_name,
-                                             ias_opt->irda_attrib_name);
-
-               /* Wait for answer, if not yet finished (or failed) */
-               if (wait_event_interruptible(self->query_wait,
-                                            (self->iriap == NULL))) {
-                       /* pending request uses copy of ias_opt-content
-                        * we can free it regardless! */
-                       kfree(ias_opt);
-                       /* Treat signals as disconnect */
-                       err = -EHOSTUNREACH;
-                       goto out;
-               }
-
-               /* Check what happened */
-               if (self->errno)
-               {
-                       kfree(ias_opt);
-                       /* Requested object/attribute doesn't exist */
-                       if((self->errno == IAS_CLASS_UNKNOWN) ||
-                          (self->errno == IAS_ATTRIB_UNKNOWN))
-                               err = -EADDRNOTAVAIL;
-                       else
-                               err = -EHOSTUNREACH;
-
-                       goto out;
-               }
-
-               /* Translate from internal to user structure */
-               err = irda_extract_ias_value(ias_opt, self->ias_result);
-               if (self->ias_result)
-                       irias_delete_value(self->ias_result);
-               if (err) {
-                       kfree(ias_opt);
-                       goto out;
-               }
-
-               /* Copy reply to the user */
-               if (copy_to_user(optval, ias_opt,
-                                sizeof(struct irda_ias_set))) {
-                       kfree(ias_opt);
-                       err = -EFAULT;
-                       goto out;
-               }
-               /* Note : don't need to put optlen, we checked it */
-               kfree(ias_opt);
-               break;
-       case IRLMP_WAITDEVICE:
-               /* This function is just another way of seeing life ;-)
-                * IRLMP_ENUMDEVICES assumes that you have a static network,
-                * and that you just want to pick one of the devices present.
-                * On the other hand, in here we assume that no device is
-                * present and that at some point in the future a device will
-                * come into range. When this device arrive, we just wake
-                * up the caller, so that he has time to connect to it before
-                * the device goes away...
-                * Note : once the node has been discovered for more than a
-                * few second, it won't trigger this function, unless it
-                * goes away and come back changes its hint bits (so we
-                * might call it IRLMP_WAITNEWDEVICE).
-                */
-
-               /* Check that the user is passing us an int */
-               if (len != sizeof(int)) {
-                       err = -EINVAL;
-                       goto out;
-               }
-               /* Get timeout in ms (max time we block the caller) */
-               if (get_user(val, (int __user *)optval)) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
-               /* Tell IrLMP we want to be notified */
-               irlmp_update_client(self->ckey, self->mask.word,
-                                   irda_selective_discovery_indication,
-                                   NULL, (void *) self);
-
-               /* Do some discovery (and also return cached results) */
-               irlmp_discovery_request(self->nslots);
-
-               /* Wait until a node is discovered */
-               if (!self->cachedaddr) {
-                       pr_debug("%s(), nothing discovered yet, going to sleep...\n",
-                                __func__);
-
-                       /* Set watchdog timer to expire in <val> ms. */
-                       self->errno = 0;
-                       setup_timer(&self->watchdog, irda_discovery_timeout,
-                                       (unsigned long)self);
-                       mod_timer(&self->watchdog,
-                                 jiffies + msecs_to_jiffies(val));
-
-                       /* Wait for IR-LMP to call us back */
-                       err = __wait_event_interruptible(self->query_wait,
-                             (self->cachedaddr != 0 || self->errno == -ETIME));
-
-                       /* If watchdog is still activated, kill it! */
-                       del_timer(&(self->watchdog));
-
-                       pr_debug("%s(), ...waking up !\n", __func__);
-
-                       if (err != 0)
-                               goto out;
-               }
-               else
-                       pr_debug("%s(), found immediately !\n",
-                                __func__);
-
-               /* Tell IrLMP that we have been notified */
-               irlmp_update_client(self->ckey, self->mask.word,
-                                   NULL, NULL, NULL);
-
-               /* Check if the we got some results */
-               if (!self->cachedaddr) {
-                       err = -EAGAIN;          /* Didn't find any devices */
-                       goto out;
-               }
-               daddr = self->cachedaddr;
-               /* Cleanup */
-               self->cachedaddr = 0;
-
-               /* We return the daddr of the device that trigger the
-                * wakeup. As irlmp pass us only the new devices, we
-                * are sure that it's not an old device.
-                * If the user want more details, he should query
-                * the whole discovery log and pick one device...
-                */
-               if (put_user(daddr, (int __user *)optval)) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
-               break;
-       default:
-               err = -ENOPROTOOPT;
-       }
-
-out:
-
-       release_sock(sk);
-
-       return err;
-}
-
-static const struct net_proto_family irda_family_ops = {
-       .family = PF_IRDA,
-       .create = irda_create,
-       .owner  = THIS_MODULE,
-};
-
-static const struct proto_ops irda_stream_ops = {
-       .family =       PF_IRDA,
-       .owner =        THIS_MODULE,
-       .release =      irda_release,
-       .bind =         irda_bind,
-       .connect =      irda_connect,
-       .socketpair =   sock_no_socketpair,
-       .accept =       irda_accept,
-       .getname =      irda_getname,
-       .poll =         irda_poll,
-       .ioctl =        irda_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl = irda_compat_ioctl,
-#endif
-       .listen =       irda_listen,
-       .shutdown =     irda_shutdown,
-       .setsockopt =   irda_setsockopt,
-       .getsockopt =   irda_getsockopt,
-       .sendmsg =      irda_sendmsg,
-       .recvmsg =      irda_recvmsg_stream,
-       .mmap =         sock_no_mmap,
-       .sendpage =     sock_no_sendpage,
-};
-
-static const struct proto_ops irda_seqpacket_ops = {
-       .family =       PF_IRDA,
-       .owner =        THIS_MODULE,
-       .release =      irda_release,
-       .bind =         irda_bind,
-       .connect =      irda_connect,
-       .socketpair =   sock_no_socketpair,
-       .accept =       irda_accept,
-       .getname =      irda_getname,
-       .poll =         datagram_poll,
-       .ioctl =        irda_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl = irda_compat_ioctl,
-#endif
-       .listen =       irda_listen,
-       .shutdown =     irda_shutdown,
-       .setsockopt =   irda_setsockopt,
-       .getsockopt =   irda_getsockopt,
-       .sendmsg =      irda_sendmsg,
-       .recvmsg =      irda_recvmsg_dgram,
-       .mmap =         sock_no_mmap,
-       .sendpage =     sock_no_sendpage,
-};
-
-static const struct proto_ops irda_dgram_ops = {
-       .family =       PF_IRDA,
-       .owner =        THIS_MODULE,
-       .release =      irda_release,
-       .bind =         irda_bind,
-       .connect =      irda_connect,
-       .socketpair =   sock_no_socketpair,
-       .accept =       irda_accept,
-       .getname =      irda_getname,
-       .poll =         datagram_poll,
-       .ioctl =        irda_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl = irda_compat_ioctl,
-#endif
-       .listen =       irda_listen,
-       .shutdown =     irda_shutdown,
-       .setsockopt =   irda_setsockopt,
-       .getsockopt =   irda_getsockopt,
-       .sendmsg =      irda_sendmsg_dgram,
-       .recvmsg =      irda_recvmsg_dgram,
-       .mmap =         sock_no_mmap,
-       .sendpage =     sock_no_sendpage,
-};
-
-#ifdef CONFIG_IRDA_ULTRA
-static const struct proto_ops irda_ultra_ops = {
-       .family =       PF_IRDA,
-       .owner =        THIS_MODULE,
-       .release =      irda_release,
-       .bind =         irda_bind,
-       .connect =      sock_no_connect,
-       .socketpair =   sock_no_socketpair,
-       .accept =       sock_no_accept,
-       .getname =      irda_getname,
-       .poll =         datagram_poll,
-       .ioctl =        irda_ioctl,
-#ifdef CONFIG_COMPAT
-       .compat_ioctl = irda_compat_ioctl,
-#endif
-       .listen =       sock_no_listen,
-       .shutdown =     irda_shutdown,
-       .setsockopt =   irda_setsockopt,
-       .getsockopt =   irda_getsockopt,
-       .sendmsg =      irda_sendmsg_ultra,
-       .recvmsg =      irda_recvmsg_dgram,
-       .mmap =         sock_no_mmap,
-       .sendpage =     sock_no_sendpage,
-};
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irsock_init (pro)
- *
- *    Initialize IrDA protocol
- *
- */
-int __init irsock_init(void)
-{
-       int rc = proto_register(&irda_proto, 0);
-
-       if (rc == 0)
-               rc = sock_register(&irda_family_ops);
-
-       return rc;
-}
-
-/*
- * Function irsock_cleanup (void)
- *
- *    Remove IrDA protocol
- *
- */
-void irsock_cleanup(void)
-{
-       sock_unregister(PF_IRDA);
-       proto_unregister(&irda_proto);
-}
diff --git a/net/irda/discovery.c b/net/irda/discovery.c
deleted file mode 100644 (file)
index 364d70a..0000000
+++ /dev/null
@@ -1,417 +0,0 @@
-/*********************************************************************
- *
- * Filename:      discovery.c
- * Version:       0.1
- * Description:   Routines for handling discoveries at the IrLMP layer
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Tue Apr  6 15:33:50 1999
- * Modified at:   Sat Oct  9 17:11:31 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Modified at:   Fri May 28  3:11 CST 1999
- * Modified by:   Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
- *
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/string.h>
-#include <linux/socket.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-
-#include <net/irda/discovery.h>
-
-#include <asm/unaligned.h>
-
-/*
- * Function irlmp_add_discovery (cachelog, discovery)
- *
- *    Add a new discovery to the cachelog, and remove any old discoveries
- *    from the same device
- *
- * Note : we try to preserve the time this device was *first* discovered
- * (as opposed to the time of last discovery used for cleanup). This is
- * used by clients waiting for discovery events to tell if the device
- * discovered is "new" or just the same old one. They can't rely there
- * on a binary flag (new/old), because not all discovery events are
- * propagated to them, and they might not always listen, so they would
- * miss some new devices popping up...
- * Jean II
- */
-void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
-{
-       discovery_t *discovery, *node;
-       unsigned long flags;
-
-       /* Set time of first discovery if node is new (see below) */
-       new->firststamp = new->timestamp;
-
-       spin_lock_irqsave(&cachelog->hb_spinlock, flags);
-
-       /*
-        * Remove all discoveries of devices that has previously been
-        * discovered on the same link with the same name (info), or the
-        * same daddr. We do this since some devices (mostly PDAs) change
-        * their device address between every discovery.
-        */
-       discovery = (discovery_t *) hashbin_get_first(cachelog);
-       while (discovery != NULL ) {
-               node = discovery;
-
-               /* Be sure to stay one item ahead */
-               discovery = (discovery_t *) hashbin_get_next(cachelog);
-
-               if ((node->data.saddr == new->data.saddr) &&
-                   ((node->data.daddr == new->data.daddr) ||
-                    (strcmp(node->data.info, new->data.info) == 0)))
-               {
-                       /* This discovery is a previous discovery
-                        * from the same device, so just remove it
-                        */
-                       hashbin_remove_this(cachelog, (irda_queue_t *) node);
-                       /* Check if hints bits are unchanged */
-                       if (get_unaligned((__u16 *)node->data.hints) == get_unaligned((__u16 *)new->data.hints))
-                               /* Set time of first discovery for this node */
-                               new->firststamp = node->firststamp;
-                       kfree(node);
-               }
-       }
-
-       /* Insert the new and updated version */
-       hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL);
-
-       spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
-}
-
-/*
- * Function irlmp_add_discovery_log (cachelog, log)
- *
- *    Merge a disovery log into the cachelog.
- *
- */
-void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
-{
-       discovery_t *discovery;
-
-       /*
-        *  If log is missing this means that IrLAP was unable to perform the
-        *  discovery, so restart discovery again with just the half timeout
-        *  of the normal one.
-        */
-       /* Well... It means that there was nobody out there - Jean II */
-       if (log == NULL) {
-               /* irlmp_start_discovery_timer(irlmp, 150); */
-               return;
-       }
-
-       /*
-        * Locking : we are the only owner of this discovery log, so
-        * no need to lock it.
-        * We just need to lock the global log in irlmp_add_discovery().
-        */
-       discovery = (discovery_t *) hashbin_remove_first(log);
-       while (discovery != NULL) {
-               irlmp_add_discovery(cachelog, discovery);
-
-               discovery = (discovery_t *) hashbin_remove_first(log);
-       }
-
-       /* Delete the now empty log */
-       hashbin_delete(log, (FREE_FUNC) kfree);
-}
-
-/*
- * Function irlmp_expire_discoveries (log, saddr, force)
- *
- *    Go through all discoveries and expire all that has stayed too long
- *
- * Note : this assume that IrLAP won't change its saddr, which
- * currently is a valid assumption...
- */
-void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
-{
-       discovery_t *           discovery;
-       discovery_t *           curr;
-       unsigned long           flags;
-       discinfo_t *            buffer = NULL;
-       int                     n;              /* Size of the full log */
-       int                     i = 0;          /* How many we expired */
-
-       IRDA_ASSERT(log != NULL, return;);
-       spin_lock_irqsave(&log->hb_spinlock, flags);
-
-       discovery = (discovery_t *) hashbin_get_first(log);
-       while (discovery != NULL) {
-               /* Be sure to be one item ahead */
-               curr = discovery;
-               discovery = (discovery_t *) hashbin_get_next(log);
-
-               /* Test if it's time to expire this discovery */
-               if ((curr->data.saddr == saddr) &&
-                   (force ||
-                    ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT)))
-               {
-                       /* Create buffer as needed.
-                        * As this function get called a lot and most time
-                        * we don't have anything to put in the log (we are
-                        * quite picky), we can save a lot of overhead
-                        * by not calling kmalloc. Jean II */
-                       if(buffer == NULL) {
-                               /* Create the client specific buffer */
-                               n = HASHBIN_GET_SIZE(log);
-                               buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
-                               if (buffer == NULL) {
-                                       spin_unlock_irqrestore(&log->hb_spinlock, flags);
-                                       return;
-                               }
-
-                       }
-
-                       /* Copy discovery information */
-                       memcpy(&(buffer[i]), &(curr->data),
-                              sizeof(discinfo_t));
-                       i++;
-
-                       /* Remove it from the log */
-                       curr = hashbin_remove_this(log, (irda_queue_t *) curr);
-                       kfree(curr);
-               }
-       }
-
-       /* Drop the spinlock before calling the higher layers, as
-        * we can't guarantee they won't call us back and create a
-        * deadlock. We will work on our own private data, so we
-        * don't care to be interrupted. - Jean II */
-       spin_unlock_irqrestore(&log->hb_spinlock, flags);
-
-       if(buffer == NULL)
-               return;
-
-       /* Tell IrLMP and registered clients about it */
-       irlmp_discovery_expiry(buffer, i);
-
-       /* Free up our buffer */
-       kfree(buffer);
-}
-
-#if 0
-/*
- * Function irlmp_dump_discoveries (log)
- *
- *    Print out all discoveries in log
- *
- */
-void irlmp_dump_discoveries(hashbin_t *log)
-{
-       discovery_t *discovery;
-
-       IRDA_ASSERT(log != NULL, return;);
-
-       discovery = (discovery_t *) hashbin_get_first(log);
-       while (discovery != NULL) {
-               pr_debug("Discovery:\n");
-               pr_debug("  daddr=%08x\n", discovery->data.daddr);
-               pr_debug("  saddr=%08x\n", discovery->data.saddr);
-               pr_debug("  nickname=%s\n", discovery->data.info);
-
-               discovery = (discovery_t *) hashbin_get_next(log);
-       }
-}
-#endif
-
-/*
- * Function irlmp_copy_discoveries (log, pn, mask)
- *
- *    Copy all discoveries in a buffer
- *
- * This function implement a safe way for lmp clients to access the
- * discovery log. The basic problem is that we don't want the log
- * to change (add/remove) while the client is reading it. If the
- * lmp client manipulate directly the hashbin, he is sure to get
- * into troubles...
- * The idea is that we copy all the current discovery log in a buffer
- * which is specific to the client and pass this copy to him. As we
- * do this operation with the spinlock grabbed, we are safe...
- * Note : we don't want those clients to grab the spinlock, because
- * we have no control on how long they will hold it...
- * Note : we choose to copy the log in "struct irda_device_info" to
- * save space...
- * Note : the client must kfree himself() the log...
- * Jean II
- */
-struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
-                                               __u16 mask, int old_entries)
-{
-       discovery_t *           discovery;
-       unsigned long           flags;
-       discinfo_t *            buffer = NULL;
-       int                     j_timeout = (sysctl_discovery_timeout * HZ);
-       int                     n;              /* Size of the full log */
-       int                     i = 0;          /* How many we picked */
-
-       IRDA_ASSERT(pn != NULL, return NULL;);
-       IRDA_ASSERT(log != NULL, return NULL;);
-
-       /* Save spin lock */
-       spin_lock_irqsave(&log->hb_spinlock, flags);
-
-       discovery = (discovery_t *) hashbin_get_first(log);
-       while (discovery != NULL) {
-               /* Mask out the ones we don't want :
-                * We want to match the discovery mask, and to get only
-                * the most recent one (unless we want old ones) */
-               if ((get_unaligned((__u16 *)discovery->data.hints) & mask) &&
-                   ((old_entries) ||
-                    ((jiffies - discovery->firststamp) < j_timeout))) {
-                       /* Create buffer as needed.
-                        * As this function get called a lot and most time
-                        * we don't have anything to put in the log (we are
-                        * quite picky), we can save a lot of overhead
-                        * by not calling kmalloc. Jean II */
-                       if(buffer == NULL) {
-                               /* Create the client specific buffer */
-                               n = HASHBIN_GET_SIZE(log);
-                               buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
-                               if (buffer == NULL) {
-                                       spin_unlock_irqrestore(&log->hb_spinlock, flags);
-                                       return NULL;
-                               }
-
-                       }
-
-                       /* Copy discovery information */
-                       memcpy(&(buffer[i]), &(discovery->data),
-                              sizeof(discinfo_t));
-                       i++;
-               }
-               discovery = (discovery_t *) hashbin_get_next(log);
-       }
-
-       spin_unlock_irqrestore(&log->hb_spinlock, flags);
-
-       /* Get the actual number of device in the buffer and return */
-       *pn = i;
-       return buffer;
-}
-
-#ifdef CONFIG_PROC_FS
-static inline discovery_t *discovery_seq_idx(loff_t pos)
-
-{
-       discovery_t *discovery;
-
-       for (discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog);
-            discovery != NULL;
-            discovery = (discovery_t *) hashbin_get_next(irlmp->cachelog)) {
-               if (pos-- == 0)
-                       break;
-       }
-
-       return discovery;
-}
-
-static void *discovery_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       spin_lock_irq(&irlmp->cachelog->hb_spinlock);
-       return *pos ? discovery_seq_idx(*pos - 1) : SEQ_START_TOKEN;
-}
-
-static void *discovery_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-       ++*pos;
-       return (v == SEQ_START_TOKEN)
-               ? (void *) hashbin_get_first(irlmp->cachelog)
-               : (void *) hashbin_get_next(irlmp->cachelog);
-}
-
-static void discovery_seq_stop(struct seq_file *seq, void *v)
-{
-       spin_unlock_irq(&irlmp->cachelog->hb_spinlock);
-}
-
-static int discovery_seq_show(struct seq_file *seq, void *v)
-{
-       if (v == SEQ_START_TOKEN)
-               seq_puts(seq, "IrLMP: Discovery log:\n\n");
-       else {
-               const discovery_t *discovery = v;
-
-               seq_printf(seq, "nickname: %s, hint: 0x%02x%02x",
-                          discovery->data.info,
-                          discovery->data.hints[0],
-                          discovery->data.hints[1]);
-#if 0
-               if ( discovery->data.hints[0] & HINT_PNP)
-                       seq_puts(seq, "PnP Compatible ");
-               if ( discovery->data.hints[0] & HINT_PDA)
-                       seq_puts(seq, "PDA/Palmtop ");
-               if ( discovery->data.hints[0] & HINT_COMPUTER)
-                       seq_puts(seq, "Computer ");
-               if ( discovery->data.hints[0] & HINT_PRINTER)
-                       seq_puts(seq, "Printer ");
-               if ( discovery->data.hints[0] & HINT_MODEM)
-                       seq_puts(seq, "Modem ");
-               if ( discovery->data.hints[0] & HINT_FAX)
-                       seq_puts(seq, "Fax ");
-               if ( discovery->data.hints[0] & HINT_LAN)
-                       seq_puts(seq, "LAN Access ");
-
-               if ( discovery->data.hints[1] & HINT_TELEPHONY)
-                       seq_puts(seq, "Telephony ");
-               if ( discovery->data.hints[1] & HINT_FILE_SERVER)
-                       seq_puts(seq, "File Server ");
-               if ( discovery->data.hints[1] & HINT_COMM)
-                       seq_puts(seq, "IrCOMM ");
-               if ( discovery->data.hints[1] & HINT_OBEX)
-                       seq_puts(seq, "IrOBEX ");
-#endif
-               seq_printf(seq,", saddr: 0x%08x, daddr: 0x%08x\n\n",
-                              discovery->data.saddr,
-                              discovery->data.daddr);
-
-               seq_putc(seq, '\n');
-       }
-       return 0;
-}
-
-static const struct seq_operations discovery_seq_ops = {
-       .start  = discovery_seq_start,
-       .next   = discovery_seq_next,
-       .stop   = discovery_seq_stop,
-       .show   = discovery_seq_show,
-};
-
-static int discovery_seq_open(struct inode *inode, struct file *file)
-{
-       IRDA_ASSERT(irlmp != NULL, return -EINVAL;);
-
-       return seq_open(file, &discovery_seq_ops);
-}
-
-const struct file_operations discovery_seq_fops = {
-       .owner          = THIS_MODULE,
-       .open           = discovery_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release,
-};
-#endif
diff --git a/net/irda/ircomm/Kconfig b/net/irda/ircomm/Kconfig
deleted file mode 100644 (file)
index 19492c1..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-config IRCOMM
-       tristate "IrCOMM protocol"
-       depends on IRDA && TTY
-       help
-         Say Y here if you want to build support for the IrCOMM protocol.
-         To compile it as modules, choose M here: the modules will be
-         called ircomm and ircomm_tty.
-         IrCOMM implements serial port emulation, and makes it possible to
-         use all existing applications that understands TTY's with an
-         infrared link.  Thus you should be able to use application like PPP,
-         minicom and others.
-
diff --git a/net/irda/ircomm/Makefile b/net/irda/ircomm/Makefile
deleted file mode 100644 (file)
index ab23b5b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for the Linux IrDA IrCOMM protocol layer.
-#
-
-obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
-
-ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
-ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c
deleted file mode 100644 (file)
index 3af2195..0000000
+++ /dev/null
@@ -1,563 +0,0 @@
-/*********************************************************************
- *
- * Filename:      ircomm_core.c
- * Version:       1.0
- * Description:   IrCOMM service interface
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Jun  6 20:37:34 1999
- * Modified at:   Tue Dec 21 13:26:41 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irias_object.h>
-
-#include <net/irda/ircomm_event.h>
-#include <net/irda/ircomm_lmp.h>
-#include <net/irda/ircomm_ttp.h>
-#include <net/irda/ircomm_param.h>
-#include <net/irda/ircomm_core.h>
-
-static int __ircomm_close(struct ircomm_cb *self);
-static void ircomm_control_indication(struct ircomm_cb *self,
-                                     struct sk_buff *skb, int clen);
-
-#ifdef CONFIG_PROC_FS
-extern struct proc_dir_entry *proc_irda;
-static int ircomm_seq_open(struct inode *, struct file *);
-
-static const struct file_operations ircomm_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = ircomm_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release,
-};
-#endif /* CONFIG_PROC_FS */
-
-hashbin_t *ircomm = NULL;
-
-static int __init ircomm_init(void)
-{
-       ircomm = hashbin_new(HB_LOCK);
-       if (ircomm == NULL) {
-               net_err_ratelimited("%s(), can't allocate hashbin!\n",
-                                   __func__);
-               return -ENOMEM;
-       }
-
-#ifdef CONFIG_PROC_FS
-       { struct proc_dir_entry *ent;
-       ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops);
-       if (!ent) {
-               printk(KERN_ERR "ircomm_init: can't create /proc entry!\n");
-               return -ENODEV;
-       }
-       }
-#endif /* CONFIG_PROC_FS */
-
-       net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n");
-
-       return 0;
-}
-
-static void __exit ircomm_cleanup(void)
-{
-       hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
-
-#ifdef CONFIG_PROC_FS
-       remove_proc_entry("ircomm", proc_irda);
-#endif /* CONFIG_PROC_FS */
-}
-
-/*
- * Function ircomm_open (client_notify)
- *
- *    Start a new IrCOMM instance
- *
- */
-struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
-{
-       struct ircomm_cb *self = NULL;
-       int ret;
-
-       pr_debug("%s(), service_type=0x%02x\n", __func__ ,
-                service_type);
-
-       IRDA_ASSERT(ircomm != NULL, return NULL;);
-
-       self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL);
-       if (self == NULL)
-               return NULL;
-
-       self->notify = *notify;
-       self->magic = IRCOMM_MAGIC;
-
-       /* Check if we should use IrLMP or IrTTP */
-       if (service_type & IRCOMM_3_WIRE_RAW) {
-               self->flow_status = FLOW_START;
-               ret = ircomm_open_lsap(self);
-       } else
-               ret = ircomm_open_tsap(self);
-
-       if (ret < 0) {
-               kfree(self);
-               return NULL;
-       }
-
-       self->service_type = service_type;
-       self->line = line;
-
-       hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
-
-       ircomm_next_state(self, IRCOMM_IDLE);
-
-       return self;
-}
-
-EXPORT_SYMBOL(ircomm_open);
-
-/*
- * Function ircomm_close_instance (self)
- *
- *    Remove IrCOMM instance
- *
- */
-static int __ircomm_close(struct ircomm_cb *self)
-{
-       /* Disconnect link if any */
-       ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
-
-       /* Remove TSAP */
-       if (self->tsap) {
-               irttp_close_tsap(self->tsap);
-               self->tsap = NULL;
-       }
-
-       /* Remove LSAP */
-       if (self->lsap) {
-               irlmp_close_lsap(self->lsap);
-               self->lsap = NULL;
-       }
-       self->magic = 0;
-
-       kfree(self);
-
-       return 0;
-}
-
-/*
- * Function ircomm_close (self)
- *
- *    Closes and removes the specified IrCOMM instance
- *
- */
-int ircomm_close(struct ircomm_cb *self)
-{
-       struct ircomm_cb *entry;
-
-       IRDA_ASSERT(self != NULL, return -EIO;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
-
-       entry = hashbin_remove(ircomm, self->line, NULL);
-
-       IRDA_ASSERT(entry == self, return -1;);
-
-       return __ircomm_close(self);
-}
-
-EXPORT_SYMBOL(ircomm_close);
-
-/*
- * Function ircomm_connect_request (self, service_type)
- *
- *    Impl. of this function is differ from one of the reference. This
- *    function does discovery as well as sending connect request
- *
- */
-int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
-                          __u32 saddr, __u32 daddr, struct sk_buff *skb,
-                          __u8 service_type)
-{
-       struct ircomm_info info;
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
-
-       self->service_type= service_type;
-
-       info.dlsap_sel = dlsap_sel;
-       info.saddr = saddr;
-       info.daddr = daddr;
-
-       ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info);
-
-       return ret;
-}
-
-EXPORT_SYMBOL(ircomm_connect_request);
-
-/*
- * Function ircomm_connect_indication (self, qos, skb)
- *
- *    Notify user layer about the incoming connection
- *
- */
-void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
-                              struct ircomm_info *info)
-{
-       /*
-        * If there are any data hiding in the control channel, we must
-        * deliver it first. The side effect is that the control channel
-        * will be removed from the skb
-        */
-       if (self->notify.connect_indication)
-               self->notify.connect_indication(self->notify.instance, self,
-                                               info->qos, info->max_data_size,
-                                               info->max_header_size, skb);
-       else {
-               pr_debug("%s(), missing handler\n", __func__);
-       }
-}
-
-/*
- * Function ircomm_connect_response (self, userdata, max_sdu_size)
- *
- *    User accepts connection
- *
- */
-int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
-{
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
-
-       ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
-
-       return ret;
-}
-
-EXPORT_SYMBOL(ircomm_connect_response);
-
-/*
- * Function connect_confirm (self, skb)
- *
- *    Notify user layer that the link is now connected
- *
- */
-void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
-                           struct ircomm_info *info)
-{
-       if (self->notify.connect_confirm )
-               self->notify.connect_confirm(self->notify.instance,
-                                            self, info->qos,
-                                            info->max_data_size,
-                                            info->max_header_size, skb);
-       else {
-               pr_debug("%s(), missing handler\n", __func__);
-       }
-}
-
-/*
- * Function ircomm_data_request (self, userdata)
- *
- *    Send IrCOMM data to peer device
- *
- */
-int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb)
-{
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -EFAULT;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
-       IRDA_ASSERT(skb != NULL, return -EFAULT;);
-
-       ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
-
-       return ret;
-}
-
-EXPORT_SYMBOL(ircomm_data_request);
-
-/*
- * Function ircomm_data_indication (self, skb)
- *
- *    Data arrived, so deliver it to user
- *
- */
-void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
-{
-       IRDA_ASSERT(skb->len > 0, return;);
-
-       if (self->notify.data_indication)
-               self->notify.data_indication(self->notify.instance, self, skb);
-       else {
-               pr_debug("%s(), missing handler\n", __func__);
-       }
-}
-
-/*
- * Function ircomm_process_data (self, skb)
- *
- *    Data arrived which may contain control channel data
- *
- */
-void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
-{
-       int clen;
-
-       IRDA_ASSERT(skb->len > 0, return;);
-
-       clen = skb->data[0];
-
-       /*
-        * Input validation check: a stir4200/mcp2150 combinations sometimes
-        * results in frames with clen > remaining packet size. These are
-        * illegal; if we throw away just this frame then it seems to carry on
-        * fine
-        */
-       if (unlikely(skb->len < (clen + 1))) {
-               pr_debug("%s() throwing away illegal frame\n",
-                        __func__);
-               return;
-       }
-
-       /*
-        * If there are any data hiding in the control channel, we must
-        * deliver it first. The side effect is that the control channel
-        * will be removed from the skb
-        */
-       if (clen > 0)
-               ircomm_control_indication(self, skb, clen);
-
-       /* Remove control channel from data channel */
-       skb_pull(skb, clen+1);
-
-       if (skb->len)
-               ircomm_data_indication(self, skb);
-       else {
-               pr_debug("%s(), data was control info only!\n",
-                        __func__);
-       }
-}
-
-/*
- * Function ircomm_control_request (self, params)
- *
- *    Send control data to peer device
- *
- */
-int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb)
-{
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -EFAULT;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
-       IRDA_ASSERT(skb != NULL, return -EFAULT;);
-
-       ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
-
-       return ret;
-}
-
-EXPORT_SYMBOL(ircomm_control_request);
-
-/*
- * Function ircomm_control_indication (self, skb)
- *
- *    Data has arrived on the control channel
- *
- */
-static void ircomm_control_indication(struct ircomm_cb *self,
-                                     struct sk_buff *skb, int clen)
-{
-       /* Use udata for delivering data on the control channel */
-       if (self->notify.udata_indication) {
-               struct sk_buff *ctrl_skb;
-
-               /* We don't own the skb, so clone it */
-               ctrl_skb = skb_clone(skb, GFP_ATOMIC);
-               if (!ctrl_skb)
-                       return;
-
-               /* Remove data channel from control channel */
-               skb_trim(ctrl_skb, clen+1);
-
-               self->notify.udata_indication(self->notify.instance, self,
-                                             ctrl_skb);
-
-               /* Drop reference count -
-                * see ircomm_tty_control_indication(). */
-               dev_kfree_skb(ctrl_skb);
-       } else {
-               pr_debug("%s(), missing handler\n", __func__);
-       }
-}
-
-/*
- * Function ircomm_disconnect_request (self, userdata, priority)
- *
- *    User layer wants to disconnect the IrCOMM connection
- *
- */
-int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata)
-{
-       struct ircomm_info info;
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
-
-       ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
-                             &info);
-       return ret;
-}
-
-EXPORT_SYMBOL(ircomm_disconnect_request);
-
-/*
- * Function disconnect_indication (self, skb)
- *
- *    Tell user that the link has been disconnected
- *
- */
-void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
-                                 struct ircomm_info *info)
-{
-       IRDA_ASSERT(info != NULL, return;);
-
-       if (self->notify.disconnect_indication) {
-               self->notify.disconnect_indication(self->notify.instance, self,
-                                                  info->reason, skb);
-       } else {
-               pr_debug("%s(), missing handler\n", __func__);
-       }
-}
-
-/*
- * Function ircomm_flow_request (self, flow)
- *
- *
- *
- */
-void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
-       if (self->service_type == IRCOMM_3_WIRE_RAW)
-               return;
-
-       irttp_flow_request(self->tsap, flow);
-}
-
-EXPORT_SYMBOL(ircomm_flow_request);
-
-#ifdef CONFIG_PROC_FS
-static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       struct ircomm_cb *self;
-       loff_t off = 0;
-
-       spin_lock_irq(&ircomm->hb_spinlock);
-
-       for (self = (struct ircomm_cb *) hashbin_get_first(ircomm);
-            self != NULL;
-            self = (struct ircomm_cb *) hashbin_get_next(ircomm)) {
-               if (off++ == *pos)
-                       break;
-
-       }
-       return self;
-}
-
-static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-       ++*pos;
-
-       return (void *) hashbin_get_next(ircomm);
-}
-
-static void ircomm_seq_stop(struct seq_file *seq, void *v)
-{
-       spin_unlock_irq(&ircomm->hb_spinlock);
-}
-
-static int ircomm_seq_show(struct seq_file *seq, void *v)
-{
-       const struct ircomm_cb *self = v;
-
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; );
-
-       if(self->line < 0x10)
-               seq_printf(seq, "ircomm%d", self->line);
-       else
-               seq_printf(seq, "irlpt%d", self->line - 0x10);
-
-       seq_printf(seq,
-                  " state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
-                  ircomm_state[ self->state],
-                  self->slsap_sel, self->dlsap_sel);
-
-       if(self->service_type & IRCOMM_3_WIRE_RAW)
-               seq_printf(seq, " 3-wire-raw");
-       if(self->service_type & IRCOMM_3_WIRE)
-               seq_printf(seq, " 3-wire");
-       if(self->service_type & IRCOMM_9_WIRE)
-               seq_printf(seq, " 9-wire");
-       if(self->service_type & IRCOMM_CENTRONICS)
-               seq_printf(seq, " Centronics");
-       seq_putc(seq, '\n');
-
-       return 0;
-}
-
-static const struct seq_operations ircomm_seq_ops = {
-       .start  = ircomm_seq_start,
-       .next   = ircomm_seq_next,
-       .stop   = ircomm_seq_stop,
-       .show   = ircomm_seq_show,
-};
-
-static int ircomm_seq_open(struct inode *inode, struct file *file)
-{
-       return seq_open(file, &ircomm_seq_ops);
-}
-#endif /* CONFIG_PROC_FS */
-
-MODULE_AUTHOR("Dag Brattli <dag@brattli.net>");
-MODULE_DESCRIPTION("IrCOMM protocol");
-MODULE_LICENSE("GPL");
-
-module_init(ircomm_init);
-module_exit(ircomm_cleanup);
diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c
deleted file mode 100644 (file)
index b0730ac..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-/*********************************************************************
- *
- * Filename:      ircomm_event.c
- * Version:       1.0
- * Description:   IrCOMM layer state machine
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Jun  6 20:33:11 1999
- * Modified at:   Sun Dec 12 13:44:32 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irias_object.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_event.h>
-
-static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
-                            struct sk_buff *skb, struct ircomm_info *info);
-static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
-                             struct sk_buff *skb, struct ircomm_info *info);
-static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
-                             struct sk_buff *skb, struct ircomm_info *info);
-static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
-                            struct sk_buff *skb, struct ircomm_info *info);
-
-const char *const ircomm_state[] = {
-       "IRCOMM_IDLE",
-       "IRCOMM_WAITI",
-       "IRCOMM_WAITR",
-       "IRCOMM_CONN",
-};
-
-static const char *const ircomm_event[] __maybe_unused = {
-       "IRCOMM_CONNECT_REQUEST",
-       "IRCOMM_CONNECT_RESPONSE",
-       "IRCOMM_TTP_CONNECT_INDICATION",
-       "IRCOMM_LMP_CONNECT_INDICATION",
-       "IRCOMM_TTP_CONNECT_CONFIRM",
-       "IRCOMM_LMP_CONNECT_CONFIRM",
-
-       "IRCOMM_LMP_DISCONNECT_INDICATION",
-       "IRCOMM_TTP_DISCONNECT_INDICATION",
-       "IRCOMM_DISCONNECT_REQUEST",
-
-       "IRCOMM_TTP_DATA_INDICATION",
-       "IRCOMM_LMP_DATA_INDICATION",
-       "IRCOMM_DATA_REQUEST",
-       "IRCOMM_CONTROL_REQUEST",
-       "IRCOMM_CONTROL_INDICATION",
-};
-
-static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event,
-                     struct sk_buff *skb, struct ircomm_info *info) =
-{
-       ircomm_state_idle,
-       ircomm_state_waiti,
-       ircomm_state_waitr,
-       ircomm_state_conn,
-};
-
-/*
- * Function ircomm_state_idle (self, event, skb)
- *
- *    IrCOMM is currently idle
- *
- */
-static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
-                            struct sk_buff *skb, struct ircomm_info *info)
-{
-       int ret = 0;
-
-       switch (event) {
-       case IRCOMM_CONNECT_REQUEST:
-               ircomm_next_state(self, IRCOMM_WAITI);
-               ret = self->issue.connect_request(self, skb, info);
-               break;
-       case IRCOMM_TTP_CONNECT_INDICATION:
-       case IRCOMM_LMP_CONNECT_INDICATION:
-               ircomm_next_state(self, IRCOMM_WAITR);
-               ircomm_connect_indication(self, skb, info);
-               break;
-       default:
-               pr_debug("%s(), unknown event: %s\n", __func__ ,
-                        ircomm_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_state_waiti (self, event, skb)
- *
- *    The IrCOMM user has requested an IrCOMM connection to the remote
- *    device and is awaiting confirmation
- */
-static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
-                             struct sk_buff *skb, struct ircomm_info *info)
-{
-       int ret = 0;
-
-       switch (event) {
-       case IRCOMM_TTP_CONNECT_CONFIRM:
-       case IRCOMM_LMP_CONNECT_CONFIRM:
-               ircomm_next_state(self, IRCOMM_CONN);
-               ircomm_connect_confirm(self, skb, info);
-               break;
-       case IRCOMM_TTP_DISCONNECT_INDICATION:
-       case IRCOMM_LMP_DISCONNECT_INDICATION:
-               ircomm_next_state(self, IRCOMM_IDLE);
-               ircomm_disconnect_indication(self, skb, info);
-               break;
-       default:
-               pr_debug("%s(), unknown event: %s\n", __func__ ,
-                        ircomm_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_state_waitr (self, event, skb)
- *
- *    IrCOMM has received an incoming connection request and is awaiting
- *    response from the user
- */
-static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
-                             struct sk_buff *skb, struct ircomm_info *info)
-{
-       int ret = 0;
-
-       switch (event) {
-       case IRCOMM_CONNECT_RESPONSE:
-               ircomm_next_state(self, IRCOMM_CONN);
-               ret = self->issue.connect_response(self, skb);
-               break;
-       case IRCOMM_DISCONNECT_REQUEST:
-               ircomm_next_state(self, IRCOMM_IDLE);
-               ret = self->issue.disconnect_request(self, skb, info);
-               break;
-       case IRCOMM_TTP_DISCONNECT_INDICATION:
-       case IRCOMM_LMP_DISCONNECT_INDICATION:
-               ircomm_next_state(self, IRCOMM_IDLE);
-               ircomm_disconnect_indication(self, skb, info);
-               break;
-       default:
-               pr_debug("%s(), unknown event = %s\n", __func__ ,
-                        ircomm_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_state_conn (self, event, skb)
- *
- *    IrCOMM is connected to the peer IrCOMM device
- *
- */
-static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
-                            struct sk_buff *skb, struct ircomm_info *info)
-{
-       int ret = 0;
-
-       switch (event) {
-       case IRCOMM_DATA_REQUEST:
-               ret = self->issue.data_request(self, skb, 0);
-               break;
-       case IRCOMM_TTP_DATA_INDICATION:
-               ircomm_process_data(self, skb);
-               break;
-       case IRCOMM_LMP_DATA_INDICATION:
-               ircomm_data_indication(self, skb);
-               break;
-       case IRCOMM_CONTROL_REQUEST:
-               /* Just send a separate frame for now */
-               ret = self->issue.data_request(self, skb, skb->len);
-               break;
-       case IRCOMM_TTP_DISCONNECT_INDICATION:
-       case IRCOMM_LMP_DISCONNECT_INDICATION:
-               ircomm_next_state(self, IRCOMM_IDLE);
-               ircomm_disconnect_indication(self, skb, info);
-               break;
-       case IRCOMM_DISCONNECT_REQUEST:
-               ircomm_next_state(self, IRCOMM_IDLE);
-               ret = self->issue.disconnect_request(self, skb, info);
-               break;
-       default:
-               pr_debug("%s(), unknown event = %s\n", __func__ ,
-                        ircomm_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_do_event (self, event, skb)
- *
- *    Process event
- *
- */
-int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
-                   struct sk_buff *skb, struct ircomm_info *info)
-{
-       pr_debug("%s: state=%s, event=%s\n", __func__ ,
-                ircomm_state[self->state], ircomm_event[event]);
-
-       return (*state[self->state])(self, event, skb, info);
-}
-
-/*
- * Function ircomm_next_state (self, state)
- *
- *    Switch state
- *
- */
-void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state)
-{
-       self->state = state;
-
-       pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
-                ircomm_state[self->state], self->service_type);
-}
diff --git a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c
deleted file mode 100644 (file)
index e4cc847..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-/*********************************************************************
- *
- * Filename:      ircomm_lmp.c
- * Version:       1.0
- * Description:   Interface between IrCOMM and IrLMP
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Jun  6 20:48:27 1999
- * Modified at:   Sun Dec 12 13:44:17 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Sources:       Previous IrLPT work by Thomas Davis
- *
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-#include <linux/gfp.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irda_device.h>      /* struct irda_skb_cb */
-
-#include <net/irda/ircomm_event.h>
-#include <net/irda/ircomm_lmp.h>
-
-
-/*
- * Function ircomm_lmp_connect_request (self, userdata)
- *
- *
- *
- */
-static int ircomm_lmp_connect_request(struct ircomm_cb *self,
-                                     struct sk_buff *userdata,
-                                     struct ircomm_info *info)
-{
-       int ret = 0;
-
-       /* Don't forget to refcount it - should be NULL anyway */
-       if(userdata)
-               skb_get(userdata);
-
-       ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
-                                   info->saddr, info->daddr, NULL, userdata);
-       return ret;
-}
-
-/*
- * Function ircomm_lmp_connect_response (self, skb)
- *
- *
- *
- */
-static int ircomm_lmp_connect_response(struct ircomm_cb *self,
-                                      struct sk_buff *userdata)
-{
-       struct sk_buff *tx_skb;
-
-       /* Any userdata supplied? */
-       if (userdata == NULL) {
-               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
-               if (!tx_skb)
-                       return -ENOMEM;
-
-               /* Reserve space for MUX and LAP header */
-               skb_reserve(tx_skb, LMP_MAX_HEADER);
-       } else {
-               /*
-                *  Check that the client has reserved enough space for
-                *  headers
-                */
-               IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER,
-                           return -1;);
-
-               /* Don't forget to refcount it - should be NULL anyway */
-               skb_get(userdata);
-               tx_skb = userdata;
-       }
-
-       return irlmp_connect_response(self->lsap, tx_skb);
-}
-
-static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
-                                        struct sk_buff *userdata,
-                                        struct ircomm_info *info)
-{
-       struct sk_buff *tx_skb;
-       int ret;
-
-       if (!userdata) {
-               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
-               if (!tx_skb)
-                       return -ENOMEM;
-
-               /*  Reserve space for MUX and LAP header */
-               skb_reserve(tx_skb, LMP_MAX_HEADER);
-               userdata = tx_skb;
-       } else {
-               /* Don't forget to refcount it - should be NULL anyway */
-               skb_get(userdata);
-       }
-
-       ret = irlmp_disconnect_request(self->lsap, userdata);
-
-       return ret;
-}
-
-/*
- * Function ircomm_lmp_flow_control (skb)
- *
- *    This function is called when a data frame we have sent to IrLAP has
- *    been deallocated. We do this to make sure we don't flood IrLAP with
- *    frames, since we are not using the IrTTP flow control mechanism
- */
-static void ircomm_lmp_flow_control(struct sk_buff *skb)
-{
-       struct irda_skb_cb *cb;
-       struct ircomm_cb *self;
-       int line;
-
-       IRDA_ASSERT(skb != NULL, return;);
-
-       cb = (struct irda_skb_cb *) skb->cb;
-
-       line = cb->line;
-
-       self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
-       if (!self) {
-               pr_debug("%s(), didn't find myself\n", __func__);
-               return;
-       }
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
-       self->pkt_count--;
-
-       if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
-               pr_debug("%s(), asking TTY to start again!\n", __func__);
-               self->flow_status = FLOW_START;
-               if (self->notify.flow_indication)
-                       self->notify.flow_indication(self->notify.instance,
-                                                    self, FLOW_START);
-       }
-}
-
-/*
- * Function ircomm_lmp_data_request (self, userdata)
- *
- *    Send data frame to peer device
- *
- */
-static int ircomm_lmp_data_request(struct ircomm_cb *self,
-                                  struct sk_buff *skb,
-                                  int not_used)
-{
-       struct irda_skb_cb *cb;
-       int ret;
-
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       cb = (struct irda_skb_cb *) skb->cb;
-
-       cb->line = self->line;
-
-       pr_debug("%s(), sending frame\n", __func__);
-
-       /* Don't forget to refcount it - see ircomm_tty_do_softint() */
-       skb_get(skb);
-
-       skb_orphan(skb);
-       skb->destructor = ircomm_lmp_flow_control;
-
-       if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
-               pr_debug("%s(), asking TTY to slow down!\n", __func__);
-               self->flow_status = FLOW_STOP;
-               if (self->notify.flow_indication)
-                       self->notify.flow_indication(self->notify.instance,
-                                                    self, FLOW_STOP);
-       }
-       ret = irlmp_data_request(self->lsap, skb);
-       if (ret) {
-               net_err_ratelimited("%s(), failed\n", __func__);
-               /* irlmp_data_request already free the packet */
-       }
-
-       return ret;
-}
-
-/*
- * Function ircomm_lmp_data_indication (instance, sap, skb)
- *
- *    Incoming data which we must deliver to the state machine, to check
- *    we are still connected.
- */
-static int ircomm_lmp_data_indication(void *instance, void *sap,
-                                     struct sk_buff *skb)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
-
-       /* Drop reference count - see ircomm_tty_data_indication(). */
-       dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
- *                                       max_header_size, skb)
- *
- *    Connection has been confirmed by peer device
- *
- */
-static void ircomm_lmp_connect_confirm(void *instance, void *sap,
-                                      struct qos_info *qos,
-                                      __u32 max_seg_size,
-                                      __u8 max_header_size,
-                                      struct sk_buff *skb)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *) instance;
-       struct ircomm_info info;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(qos != NULL, return;);
-
-       info.max_data_size = max_seg_size;
-       info.max_header_size = max_header_size;
-       info.qos = qos;
-
-       ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info);
-
-       /* Drop reference count - see ircomm_tty_connect_confirm(). */
-       dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
- *                                         max_header_size, skb)
- *
- *    Peer device wants to make a connection with us
- *
- */
-static void ircomm_lmp_connect_indication(void *instance, void *sap,
-                                         struct qos_info *qos,
-                                         __u32 max_seg_size,
-                                         __u8 max_header_size,
-                                         struct sk_buff *skb)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *)instance;
-       struct ircomm_info info;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(qos != NULL, return;);
-
-       info.max_data_size = max_seg_size;
-       info.max_header_size = max_header_size;
-       info.qos = qos;
-
-       ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info);
-
-       /* Drop reference count - see ircomm_tty_connect_indication(). */
-       dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
- *
- *    Peer device has closed the connection, or the link went down for some
- *    other reason
- */
-static void ircomm_lmp_disconnect_indication(void *instance, void *sap,
-                                            LM_REASON reason,
-                                            struct sk_buff *skb)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *) instance;
-       struct ircomm_info info;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
-       info.reason = reason;
-
-       ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
-
-       /* Drop reference count - see ircomm_tty_disconnect_indication(). */
-       if(skb)
-               dev_kfree_skb(skb);
-}
-/*
- * Function ircomm_open_lsap (self)
- *
- *    Open LSAP. This function will only be used when using "raw" services
- *
- */
-int ircomm_open_lsap(struct ircomm_cb *self)
-{
-       notify_t notify;
-
-       /* Register callbacks */
-       irda_notify_init(&notify);
-       notify.data_indication       = ircomm_lmp_data_indication;
-       notify.connect_confirm       = ircomm_lmp_connect_confirm;
-       notify.connect_indication    = ircomm_lmp_connect_indication;
-       notify.disconnect_indication = ircomm_lmp_disconnect_indication;
-       notify.instance = self;
-       strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
-
-       self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
-       if (!self->lsap) {
-               pr_debug("%sfailed to allocate tsap\n", __func__);
-               return -1;
-       }
-       self->slsap_sel = self->lsap->slsap_sel;
-
-       /*
-        *  Initialize the call-table for issuing commands
-        */
-       self->issue.data_request       = ircomm_lmp_data_request;
-       self->issue.connect_request    = ircomm_lmp_connect_request;
-       self->issue.connect_response   = ircomm_lmp_connect_response;
-       self->issue.disconnect_request = ircomm_lmp_disconnect_request;
-
-       return 0;
-}
diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c
deleted file mode 100644 (file)
index 5728e76..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-/*********************************************************************
- *
- * Filename:      ircomm_param.c
- * Version:       1.0
- * Description:   Parameter handling for the IrCOMM protocol
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Mon Jun  7 10:25:11 1999
- * Modified at:   Sun Jan 30 14:32:03 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/gfp.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/parameters.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_tty_attach.h>
-#include <net/irda/ircomm_tty.h>
-
-#include <net/irda/ircomm_param.h>
-
-static int ircomm_param_service_type(void *instance, irda_param_t *param,
-                                    int get);
-static int ircomm_param_port_type(void *instance, irda_param_t *param,
-                                 int get);
-static int ircomm_param_port_name(void *instance, irda_param_t *param,
-                                 int get);
-static int ircomm_param_service_type(void *instance, irda_param_t *param,
-                                    int get);
-static int ircomm_param_data_rate(void *instance, irda_param_t *param,
-                                 int get);
-static int ircomm_param_data_format(void *instance, irda_param_t *param,
-                                   int get);
-static int ircomm_param_flow_control(void *instance, irda_param_t *param,
-                                    int get);
-static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
-static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
-static int ircomm_param_line_status(void *instance, irda_param_t *param,
-                                   int get);
-static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
-static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
-static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
-
-static const pi_minor_info_t pi_minor_call_table_common[] = {
-       { ircomm_param_service_type, PV_INT_8_BITS },
-       { ircomm_param_port_type,    PV_INT_8_BITS },
-       { ircomm_param_port_name,    PV_STRING }
-};
-static const pi_minor_info_t pi_minor_call_table_non_raw[] = {
-       { ircomm_param_data_rate,    PV_INT_32_BITS | PV_BIG_ENDIAN },
-       { ircomm_param_data_format,  PV_INT_8_BITS },
-       { ircomm_param_flow_control, PV_INT_8_BITS },
-       { ircomm_param_xon_xoff,     PV_INT_16_BITS },
-       { ircomm_param_enq_ack,      PV_INT_16_BITS },
-       { ircomm_param_line_status,  PV_INT_8_BITS }
-};
-static const pi_minor_info_t pi_minor_call_table_9_wire[] = {
-       { ircomm_param_dte,          PV_INT_8_BITS },
-       { ircomm_param_dce,          PV_INT_8_BITS },
-       { ircomm_param_poll,         PV_NO_VALUE },
-};
-
-static const pi_major_info_t pi_major_call_table[] = {
-       { pi_minor_call_table_common,  3 },
-       { pi_minor_call_table_non_raw, 6 },
-       { pi_minor_call_table_9_wire,  3 }
-/*     { pi_minor_call_table_centronics }  */
-};
-
-pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 };
-
-/*
- * Function ircomm_param_request (self, pi, flush)
- *
- *    Queue a parameter for the control channel
- *
- */
-int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
-{
-       unsigned long flags;
-       struct sk_buff *skb;
-       int count;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       /* Make sure we don't send parameters for raw mode */
-       if (self->service_type == IRCOMM_3_WIRE_RAW)
-               return 0;
-
-       spin_lock_irqsave(&self->spinlock, flags);
-
-       skb = self->ctrl_skb;
-       if (!skb) {
-               skb = alloc_skb(256, GFP_ATOMIC);
-               if (!skb) {
-                       spin_unlock_irqrestore(&self->spinlock, flags);
-                       return -ENOMEM;
-               }
-
-               skb_reserve(skb, self->max_header_size);
-               self->ctrl_skb = skb;
-       }
-       /*
-        * Inserting is a little bit tricky since we don't know how much
-        * room we will need. But this should hopefully work OK
-        */
-       count = irda_param_insert(self, pi, skb_tail_pointer(skb),
-                                 skb_tailroom(skb), &ircomm_param_info);
-       if (count < 0) {
-               net_warn_ratelimited("%s(), no room for parameter!\n",
-                                    __func__);
-               spin_unlock_irqrestore(&self->spinlock, flags);
-               return -1;
-       }
-       skb_put(skb, count);
-       pr_debug("%s(), skb->len=%d\n", __func__, skb->len);
-
-       spin_unlock_irqrestore(&self->spinlock, flags);
-
-       if (flush) {
-               /* ircomm_tty_do_softint will take care of the rest */
-               schedule_work(&self->tqueue);
-       }
-
-       return count;
-}
-
-/*
- * Function ircomm_param_service_type (self, buf, len)
- *
- *    Handle service type, this function will both be called after the LM-IAS
- *    query and then the remote device sends its initial parameters
- *
- */
-static int ircomm_param_service_type(void *instance, irda_param_t *param,
-                                    int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-       __u8 service_type = (__u8) param->pv.i;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get) {
-               param->pv.i = self->settings.service_type;
-               return 0;
-       }
-
-       /* Find all common service types */
-       service_type &= self->service_type;
-       if (!service_type) {
-               pr_debug("%s(), No common service type to use!\n", __func__);
-               return -1;
-       }
-       pr_debug("%s(), services in common=%02x\n", __func__ ,
-                service_type);
-
-       /*
-        * Now choose a preferred service type of those available
-        */
-       if (service_type & IRCOMM_CENTRONICS)
-               self->settings.service_type = IRCOMM_CENTRONICS;
-       else if (service_type & IRCOMM_9_WIRE)
-               self->settings.service_type = IRCOMM_9_WIRE;
-       else if (service_type & IRCOMM_3_WIRE)
-               self->settings.service_type = IRCOMM_3_WIRE;
-       else if (service_type & IRCOMM_3_WIRE_RAW)
-               self->settings.service_type = IRCOMM_3_WIRE_RAW;
-
-       pr_debug("%s(), resulting service type=0x%02x\n", __func__ ,
-                self->settings.service_type);
-
-       /*
-        * Now the line is ready for some communication. Check if we are a
-        * server, and send over some initial parameters.
-        * Client do it in ircomm_tty_state_setup().
-        * Note : we may get called from ircomm_tty_getvalue_confirm(),
-        * therefore before we even have open any socket. And self->client
-        * is initialised to TRUE only later. So, we check if the link is
-        * really initialised. - Jean II
-        */
-       if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) &&
-           (!self->client) &&
-           (self->settings.service_type != IRCOMM_3_WIRE_RAW))
-       {
-               /* Init connection */
-               ircomm_tty_send_initial_parameters(self);
-               ircomm_tty_link_established(self);
-       }
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_port_type (self, param)
- *
- *    The port type parameter tells if the devices are serial or parallel.
- *    Since we only advertise serial service, this parameter should only
- *    be equal to IRCOMM_SERIAL.
- */
-static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = IRCOMM_SERIAL;
-       else {
-               self->settings.port_type = (__u8) param->pv.i;
-
-               pr_debug("%s(), port type=%d\n", __func__ ,
-                        self->settings.port_type);
-       }
-       return 0;
-}
-
-/*
- * Function ircomm_param_port_name (self, param)
- *
- *    Exchange port name
- *
- */
-static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get) {
-               pr_debug("%s(), not imp!\n", __func__);
-       } else {
-               pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c);
-               strncpy(self->settings.port_name, param->pv.c, 32);
-       }
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_data_rate (self, param)
- *
- *    Exchange data rate to be used in this settings
- *
- */
-static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->settings.data_rate;
-       else
-               self->settings.data_rate = param->pv.i;
-
-       pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i);
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_data_format (self, param)
- *
- *    Exchange data format to be used in this settings
- *
- */
-static int ircomm_param_data_format(void *instance, irda_param_t *param,
-                                   int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->settings.data_format;
-       else
-               self->settings.data_format = (__u8) param->pv.i;
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_flow_control (self, param)
- *
- *    Exchange flow control settings to be used in this settings
- *
- */
-static int ircomm_param_flow_control(void *instance, irda_param_t *param,
-                                    int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->settings.flow_control;
-       else
-               self->settings.flow_control = (__u8) param->pv.i;
-
-       pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i);
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_xon_xoff (self, param)
- *
- *    Exchange XON/XOFF characters
- *
- */
-static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get) {
-               param->pv.i = self->settings.xonxoff[0];
-               param->pv.i |= self->settings.xonxoff[1] << 8;
-       } else {
-               self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff;
-               self->settings.xonxoff[1] = (__u16) param->pv.i >> 8;
-       }
-
-       pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ ,
-                param->pv.i & 0xff, param->pv.i >> 8);
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_enq_ack (self, param)
- *
- *    Exchange ENQ/ACK characters
- *
- */
-static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get) {
-               param->pv.i = self->settings.enqack[0];
-               param->pv.i |= self->settings.enqack[1] << 8;
-       } else {
-               self->settings.enqack[0] = (__u16) param->pv.i & 0xff;
-               self->settings.enqack[1] = (__u16) param->pv.i >> 8;
-       }
-
-       pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ ,
-                param->pv.i & 0xff, param->pv.i >> 8);
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_line_status (self, param)
- *
- *
- *
- */
-static int ircomm_param_line_status(void *instance, irda_param_t *param,
-                                   int get)
-{
-       pr_debug("%s(), not impl.\n", __func__);
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_dte (instance, param)
- *
- *    If we get here, there must be some sort of null-modem connection, and
- *    we are probably working in server mode as well.
- */
-static int ircomm_param_dte(void *instance, irda_param_t *param, int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-       __u8 dte;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->settings.dte;
-       else {
-               dte = (__u8) param->pv.i;
-
-               self->settings.dce = 0;
-
-               if (dte & IRCOMM_DELTA_DTR)
-                       self->settings.dce |= (IRCOMM_DELTA_DSR|
-                                             IRCOMM_DELTA_RI |
-                                             IRCOMM_DELTA_CD);
-               if (dte & IRCOMM_DTR)
-                       self->settings.dce |= (IRCOMM_DSR|
-                                             IRCOMM_RI |
-                                             IRCOMM_CD);
-
-               if (dte & IRCOMM_DELTA_RTS)
-                       self->settings.dce |= IRCOMM_DELTA_CTS;
-               if (dte & IRCOMM_RTS)
-                       self->settings.dce |= IRCOMM_CTS;
-
-               /* Take appropriate actions */
-               ircomm_tty_check_modem_status(self);
-
-               /* Null modem cable emulator */
-               self->settings.null_modem = TRUE;
-       }
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_dce (instance, param)
- *
- *
- *
- */
-static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-       __u8 dce;
-
-       pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i);
-
-       dce = (__u8) param->pv.i;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       self->settings.dce = dce;
-
-       /* Check if any of the settings have changed */
-       if (dce & 0x0f) {
-               if (dce & IRCOMM_DELTA_CTS) {
-                       pr_debug("%s(), CTS\n", __func__);
-               }
-       }
-
-       ircomm_tty_check_modem_status(self);
-
-       return 0;
-}
-
-/*
- * Function ircomm_param_poll (instance, param)
- *
- *    Called when the peer device is polling for the line settings
- *
- */
-static int ircomm_param_poll(void *instance, irda_param_t *param, int get)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       /* Poll parameters are always of length 0 (just a signal) */
-       if (!get) {
-               /* Respond with DTE line settings */
-               ircomm_param_request(self, IRCOMM_DTE, TRUE);
-       }
-       return 0;
-}
-
-
-
-
-
diff --git a/net/irda/ircomm/ircomm_ttp.c b/net/irda/ircomm/ircomm_ttp.c
deleted file mode 100644 (file)
index 4b81e09..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-/*********************************************************************
- *
- * Filename:      ircomm_ttp.c
- * Version:       1.0
- * Description:   Interface between IrCOMM and IrTTP
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Jun  6 20:48:27 1999
- * Modified at:   Mon Dec 13 11:35:13 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-
-#include <net/irda/ircomm_event.h>
-#include <net/irda/ircomm_ttp.h>
-
-static int ircomm_ttp_data_indication(void *instance, void *sap,
-                                     struct sk_buff *skb);
-static void ircomm_ttp_connect_confirm(void *instance, void *sap,
-                                      struct qos_info *qos,
-                                      __u32 max_sdu_size,
-                                      __u8 max_header_size,
-                                      struct sk_buff *skb);
-static void ircomm_ttp_connect_indication(void *instance, void *sap,
-                                         struct qos_info *qos,
-                                         __u32 max_sdu_size,
-                                         __u8 max_header_size,
-                                         struct sk_buff *skb);
-static void ircomm_ttp_flow_indication(void *instance, void *sap,
-                                      LOCAL_FLOW cmd);
-static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
-                                            LM_REASON reason,
-                                            struct sk_buff *skb);
-static int ircomm_ttp_data_request(struct ircomm_cb *self,
-                                  struct sk_buff *skb,
-                                  int clen);
-static int ircomm_ttp_connect_request(struct ircomm_cb *self,
-                                     struct sk_buff *userdata,
-                                     struct ircomm_info *info);
-static int ircomm_ttp_connect_response(struct ircomm_cb *self,
-                                      struct sk_buff *userdata);
-static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
-                                        struct sk_buff *userdata,
-                                        struct ircomm_info *info);
-
-/*
- * Function ircomm_open_tsap (self)
- *
- *
- *
- */
-int ircomm_open_tsap(struct ircomm_cb *self)
-{
-       notify_t notify;
-
-       /* Register callbacks */
-       irda_notify_init(&notify);
-       notify.data_indication       = ircomm_ttp_data_indication;
-       notify.connect_confirm       = ircomm_ttp_connect_confirm;
-       notify.connect_indication    = ircomm_ttp_connect_indication;
-       notify.flow_indication       = ircomm_ttp_flow_indication;
-       notify.disconnect_indication = ircomm_ttp_disconnect_indication;
-       notify.instance = self;
-       strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
-
-       self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
-                                    &notify);
-       if (!self->tsap) {
-               pr_debug("%sfailed to allocate tsap\n", __func__);
-               return -1;
-       }
-       self->slsap_sel = self->tsap->stsap_sel;
-
-       /*
-        *  Initialize the call-table for issuing commands
-        */
-       self->issue.data_request       = ircomm_ttp_data_request;
-       self->issue.connect_request    = ircomm_ttp_connect_request;
-       self->issue.connect_response   = ircomm_ttp_connect_response;
-       self->issue.disconnect_request = ircomm_ttp_disconnect_request;
-
-       return 0;
-}
-
-/*
- * Function ircomm_ttp_connect_request (self, userdata)
- *
- *
- *
- */
-static int ircomm_ttp_connect_request(struct ircomm_cb *self,
-                                     struct sk_buff *userdata,
-                                     struct ircomm_info *info)
-{
-       int ret = 0;
-
-       /* Don't forget to refcount it - should be NULL anyway */
-       if(userdata)
-               skb_get(userdata);
-
-       ret = irttp_connect_request(self->tsap, info->dlsap_sel,
-                                   info->saddr, info->daddr, NULL,
-                                   TTP_SAR_DISABLE, userdata);
-
-       return ret;
-}
-
-/*
- * Function ircomm_ttp_connect_response (self, skb)
- *
- *
- *
- */
-static int ircomm_ttp_connect_response(struct ircomm_cb *self,
-                                      struct sk_buff *userdata)
-{
-       int ret;
-
-       /* Don't forget to refcount it - should be NULL anyway */
-       if(userdata)
-               skb_get(userdata);
-
-       ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata);
-
-       return ret;
-}
-
-/*
- * Function ircomm_ttp_data_request (self, userdata)
- *
- *    Send IrCOMM data to IrTTP layer. Currently we do not try to combine
- *    control data with pure data, so they will be sent as separate frames.
- *    Should not be a big problem though, since control frames are rare. But
- *    some of them are sent after connection establishment, so this can
- *    increase the latency a bit.
- */
-static int ircomm_ttp_data_request(struct ircomm_cb *self,
-                                  struct sk_buff *skb,
-                                  int clen)
-{
-       int ret;
-
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       pr_debug("%s(), clen=%d\n", __func__ , clen);
-
-       /*
-        * Insert clen field, currently we either send data only, or control
-        * only frames, to make things easier and avoid queueing
-        */
-       IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;);
-
-       /* Don't forget to refcount it - see ircomm_tty_do_softint() */
-       skb_get(skb);
-
-       skb_push(skb, IRCOMM_HEADER_SIZE);
-
-       skb->data[0] = clen;
-
-       ret = irttp_data_request(self->tsap, skb);
-       if (ret) {
-               net_err_ratelimited("%s(), failed\n", __func__);
-               /* irttp_data_request already free the packet */
-       }
-
-       return ret;
-}
-
-/*
- * Function ircomm_ttp_data_indication (instance, sap, skb)
- *
- *    Incoming data
- *
- */
-static int ircomm_ttp_data_indication(void *instance, void *sap,
-                                     struct sk_buff *skb)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL);
-
-       /* Drop reference count - see ircomm_tty_data_indication(). */
-       dev_kfree_skb(skb);
-
-       return 0;
-}
-
-static void ircomm_ttp_connect_confirm(void *instance, void *sap,
-                                      struct qos_info *qos,
-                                      __u32 max_sdu_size,
-                                      __u8 max_header_size,
-                                      struct sk_buff *skb)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *) instance;
-       struct ircomm_info info;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(qos != NULL, goto out;);
-
-       if (max_sdu_size != TTP_SAR_DISABLE) {
-               net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
-                                   __func__);
-               goto out;
-       }
-
-       info.max_data_size = irttp_get_max_seg_size(self->tsap)
-               - IRCOMM_HEADER_SIZE;
-       info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
-       info.qos = qos;
-
-       ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info);
-
-out:
-       /* Drop reference count - see ircomm_tty_connect_confirm(). */
-       dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size,
- *                                         max_header_size, skb)
- *
- *
- *
- */
-static void ircomm_ttp_connect_indication(void *instance, void *sap,
-                                         struct qos_info *qos,
-                                         __u32 max_sdu_size,
-                                         __u8 max_header_size,
-                                         struct sk_buff *skb)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *)instance;
-       struct ircomm_info info;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(qos != NULL, goto out;);
-
-       if (max_sdu_size != TTP_SAR_DISABLE) {
-               net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
-                                   __func__);
-               goto out;
-       }
-
-       info.max_data_size = irttp_get_max_seg_size(self->tsap)
-               - IRCOMM_HEADER_SIZE;
-       info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
-       info.qos = qos;
-
-       ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info);
-
-out:
-       /* Drop reference count - see ircomm_tty_connect_indication(). */
-       dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_ttp_disconnect_request (self, userdata, info)
- *
- *
- *
- */
-static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
-                                        struct sk_buff *userdata,
-                                        struct ircomm_info *info)
-{
-       int ret;
-
-       /* Don't forget to refcount it - should be NULL anyway */
-       if(userdata)
-               skb_get(userdata);
-
-       ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL);
-
-       return ret;
-}
-
-/*
- * Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb)
- *
- *
- *
- */
-static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
-                                            LM_REASON reason,
-                                            struct sk_buff *skb)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *) instance;
-       struct ircomm_info info;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
-       info.reason = reason;
-
-       ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info);
-
-       /* Drop reference count - see ircomm_tty_disconnect_indication(). */
-       if(skb)
-               dev_kfree_skb(skb);
-}
-
-/*
- * Function ircomm_ttp_flow_indication (instance, sap, cmd)
- *
- *    Layer below is telling us to start or stop the flow of data
- *
- */
-static void ircomm_ttp_flow_indication(void *instance, void *sap,
-                                      LOCAL_FLOW cmd)
-{
-       struct ircomm_cb *self = (struct ircomm_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
-       if (self->notify.flow_indication)
-               self->notify.flow_indication(self->notify.instance, self, cmd);
-}
-
-
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
deleted file mode 100644 (file)
index ec157c3..0000000
+++ /dev/null
@@ -1,1329 +0,0 @@
-/*********************************************************************
- *
- * Filename:      ircomm_tty.c
- * Version:       1.0
- * Description:   IrCOMM serial TTY driver
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Jun  6 21:00:56 1999
- * Modified at:   Wed Feb 23 00:09:02 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Sources:       serial.c and previous IrCOMM work by Takahide Higuchi
- *
- *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/sched/signal.h>
-#include <linux/seq_file.h>
-#include <linux/termios.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>              /* for MODULE_ALIAS_CHARDEV_MAJOR */
-
-#include <linux/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_param.h>
-#include <net/irda/ircomm_tty_attach.h>
-#include <net/irda/ircomm_tty.h>
-
-static int ircomm_tty_install(struct tty_driver *driver,
-               struct tty_struct *tty);
-static int  ircomm_tty_open(struct tty_struct *tty, struct file *filp);
-static void ircomm_tty_close(struct tty_struct * tty, struct file *filp);
-static int  ircomm_tty_write(struct tty_struct * tty,
-                            const unsigned char *buf, int count);
-static int  ircomm_tty_write_room(struct tty_struct *tty);
-static void ircomm_tty_throttle(struct tty_struct *tty);
-static void ircomm_tty_unthrottle(struct tty_struct *tty);
-static int  ircomm_tty_chars_in_buffer(struct tty_struct *tty);
-static void ircomm_tty_flush_buffer(struct tty_struct *tty);
-static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch);
-static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout);
-static void ircomm_tty_hangup(struct tty_struct *tty);
-static void ircomm_tty_do_softint(struct work_struct *work);
-static void ircomm_tty_shutdown(struct ircomm_tty_cb *self);
-static void ircomm_tty_stop(struct tty_struct *tty);
-
-static int ircomm_tty_data_indication(void *instance, void *sap,
-                                     struct sk_buff *skb);
-static int ircomm_tty_control_indication(void *instance, void *sap,
-                                        struct sk_buff *skb);
-static void ircomm_tty_flow_indication(void *instance, void *sap,
-                                      LOCAL_FLOW cmd);
-#ifdef CONFIG_PROC_FS
-static const struct file_operations ircomm_tty_proc_fops;
-#endif /* CONFIG_PROC_FS */
-static struct tty_driver *driver;
-
-static hashbin_t *ircomm_tty = NULL;
-
-static const struct tty_operations ops = {
-       .install         = ircomm_tty_install,
-       .open            = ircomm_tty_open,
-       .close           = ircomm_tty_close,
-       .write           = ircomm_tty_write,
-       .write_room      = ircomm_tty_write_room,
-       .chars_in_buffer = ircomm_tty_chars_in_buffer,
-       .flush_buffer    = ircomm_tty_flush_buffer,
-       .ioctl           = ircomm_tty_ioctl,    /* ircomm_tty_ioctl.c */
-       .tiocmget        = ircomm_tty_tiocmget, /* ircomm_tty_ioctl.c */
-       .tiocmset        = ircomm_tty_tiocmset, /* ircomm_tty_ioctl.c */
-       .throttle        = ircomm_tty_throttle,
-       .unthrottle      = ircomm_tty_unthrottle,
-       .send_xchar      = ircomm_tty_send_xchar,
-       .set_termios     = ircomm_tty_set_termios,
-       .stop            = ircomm_tty_stop,
-       .start           = ircomm_tty_start,
-       .hangup          = ircomm_tty_hangup,
-       .wait_until_sent = ircomm_tty_wait_until_sent,
-#ifdef CONFIG_PROC_FS
-       .proc_fops       = &ircomm_tty_proc_fops,
-#endif /* CONFIG_PROC_FS */
-};
-
-static void ircomm_port_raise_dtr_rts(struct tty_port *port, int raise)
-{
-       struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb,
-                       port);
-       /*
-        * Here, we use to lock those two guys, but as ircomm_param_request()
-        * does it itself, I don't see the point (and I see the deadlock).
-        * Jean II
-        */
-       if (raise)
-               self->settings.dte |= IRCOMM_RTS | IRCOMM_DTR;
-       else
-               self->settings.dte &= ~(IRCOMM_RTS | IRCOMM_DTR);
-
-       ircomm_param_request(self, IRCOMM_DTE, TRUE);
-}
-
-static int ircomm_port_carrier_raised(struct tty_port *port)
-{
-       struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb,
-                       port);
-       return self->settings.dce & IRCOMM_CD;
-}
-
-static const struct tty_port_operations ircomm_port_ops = {
-       .dtr_rts = ircomm_port_raise_dtr_rts,
-       .carrier_raised = ircomm_port_carrier_raised,
-};
-
-/*
- * Function ircomm_tty_init()
- *
- *    Init IrCOMM TTY layer/driver
- *
- */
-static int __init ircomm_tty_init(void)
-{
-       driver = alloc_tty_driver(IRCOMM_TTY_PORTS);
-       if (!driver)
-               return -ENOMEM;
-       ircomm_tty = hashbin_new(HB_LOCK);
-       if (ircomm_tty == NULL) {
-               net_err_ratelimited("%s(), can't allocate hashbin!\n",
-                                   __func__);
-               put_tty_driver(driver);
-               return -ENOMEM;
-       }
-
-       driver->driver_name     = "ircomm";
-       driver->name            = "ircomm";
-       driver->major           = IRCOMM_TTY_MAJOR;
-       driver->minor_start     = IRCOMM_TTY_MINOR;
-       driver->type            = TTY_DRIVER_TYPE_SERIAL;
-       driver->subtype         = SERIAL_TYPE_NORMAL;
-       driver->init_termios    = tty_std_termios;
-       driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       driver->flags           = TTY_DRIVER_REAL_RAW;
-       tty_set_operations(driver, &ops);
-       if (tty_register_driver(driver)) {
-               net_err_ratelimited("%s(): Couldn't register serial driver\n",
-                                   __func__);
-               put_tty_driver(driver);
-               return -1;
-       }
-       return 0;
-}
-
-static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       ircomm_tty_shutdown(self);
-
-       self->magic = 0;
-       tty_port_destroy(&self->port);
-       kfree(self);
-}
-
-/*
- * Function ircomm_tty_cleanup ()
- *
- *    Remove IrCOMM TTY layer/driver
- *
- */
-static void __exit ircomm_tty_cleanup(void)
-{
-       int ret;
-
-       ret = tty_unregister_driver(driver);
-       if (ret) {
-               net_err_ratelimited("%s(), failed to unregister driver\n",
-                                   __func__);
-               return;
-       }
-
-       hashbin_delete(ircomm_tty, (FREE_FUNC) __ircomm_tty_cleanup);
-       put_tty_driver(driver);
-}
-
-/*
- * Function ircomm_startup (self)
- *
- *
- *
- */
-static int ircomm_tty_startup(struct ircomm_tty_cb *self)
-{
-       notify_t notify;
-       int ret = -ENODEV;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       /* Check if already open */
-       if (tty_port_initialized(&self->port)) {
-               pr_debug("%s(), already open so break out!\n", __func__);
-               return 0;
-       }
-       tty_port_set_initialized(&self->port, 1);
-
-       /* Register with IrCOMM */
-       irda_notify_init(&notify);
-       /* These callbacks we must handle ourselves */
-       notify.data_indication       = ircomm_tty_data_indication;
-       notify.udata_indication      = ircomm_tty_control_indication;
-       notify.flow_indication       = ircomm_tty_flow_indication;
-
-       /* Use the ircomm_tty interface for these ones */
-       notify.disconnect_indication = ircomm_tty_disconnect_indication;
-       notify.connect_confirm       = ircomm_tty_connect_confirm;
-       notify.connect_indication    = ircomm_tty_connect_indication;
-       strlcpy(notify.name, "ircomm_tty", sizeof(notify.name));
-       notify.instance = self;
-
-       if (!self->ircomm) {
-               self->ircomm = ircomm_open(&notify, self->service_type,
-                                          self->line);
-       }
-       if (!self->ircomm)
-               goto err;
-
-       self->slsap_sel = self->ircomm->slsap_sel;
-
-       /* Connect IrCOMM link with remote device */
-       ret = ircomm_tty_attach_cable(self);
-       if (ret < 0) {
-               net_err_ratelimited("%s(), error attaching cable!\n", __func__);
-               goto err;
-       }
-
-       return 0;
-err:
-       tty_port_set_initialized(&self->port, 0);
-       return ret;
-}
-
-/*
- * Function ircomm_block_til_ready (self, filp)
- *
- *
- *
- */
-static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
-               struct tty_struct *tty, struct file *filp)
-{
-       struct tty_port *port = &self->port;
-       DECLARE_WAITQUEUE(wait, current);
-       int             retval;
-       int             do_clocal = 0;
-       unsigned long   flags;
-
-       /*
-        * If non-blocking mode is set, or the port is not enabled,
-        * then make the check up front and then exit.
-        */
-       if (tty_io_error(tty)) {
-               tty_port_set_active(port, 1);
-               return 0;
-       }
-
-       if (filp->f_flags & O_NONBLOCK) {
-               /* nonblock mode is set */
-               if (C_BAUD(tty))
-                       tty_port_raise_dtr_rts(port);
-               tty_port_set_active(port, 1);
-               pr_debug("%s(), O_NONBLOCK requested!\n", __func__);
-               return 0;
-       }
-
-       if (C_CLOCAL(tty)) {
-               pr_debug("%s(), doing CLOCAL!\n", __func__);
-               do_clocal = 1;
-       }
-
-       /* Wait for carrier detect and the line to become
-        * free (i.e., not in use by the callout).  While we are in
-        * this loop, port->count is dropped by one, so that
-        * mgsl_close() knows when to free things.  We restore it upon
-        * exit, either normal or abnormal.
-        */
-
-       retval = 0;
-       add_wait_queue(&port->open_wait, &wait);
-
-       pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n",
-                __FILE__, __LINE__, tty->driver->name, port->count);
-
-       spin_lock_irqsave(&port->lock, flags);
-       port->count--;
-       port->blocked_open++;
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       while (1) {
-               if (C_BAUD(tty) && tty_port_initialized(port))
-                       tty_port_raise_dtr_rts(port);
-
-               set_current_state(TASK_INTERRUPTIBLE);
-
-               if (tty_hung_up_p(filp) || !tty_port_initialized(port)) {
-                       retval = (port->flags & ASYNC_HUP_NOTIFY) ?
-                                       -EAGAIN : -ERESTARTSYS;
-                       break;
-               }
-
-               /*
-                * Check if link is ready now. Even if CLOCAL is
-                * specified, we cannot return before the IrCOMM link is
-                * ready
-                */
-               if ((do_clocal || tty_port_carrier_raised(port)) &&
-                   self->state == IRCOMM_TTY_READY)
-               {
-                       break;
-               }
-
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-
-               pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n",
-                        __FILE__, __LINE__, tty->driver->name, port->count);
-
-               schedule();
-       }
-
-       __set_current_state(TASK_RUNNING);
-       remove_wait_queue(&port->open_wait, &wait);
-
-       spin_lock_irqsave(&port->lock, flags);
-       if (!tty_hung_up_p(filp))
-               port->count++;
-       port->blocked_open--;
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n",
-                __FILE__, __LINE__, tty->driver->name, port->count);
-
-       if (!retval)
-               tty_port_set_active(port, 1);
-
-       return retval;
-}
-
-
-static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self;
-       unsigned int line = tty->index;
-
-       /* Check if instance already exists */
-       self = hashbin_lock_find(ircomm_tty, line, NULL);
-       if (!self) {
-               /* No, so make new instance */
-               self = kzalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL);
-               if (self == NULL)
-                       return -ENOMEM;
-
-               tty_port_init(&self->port);
-               self->port.ops = &ircomm_port_ops;
-               self->magic = IRCOMM_TTY_MAGIC;
-               self->flow = FLOW_STOP;
-
-               self->line = line;
-               INIT_WORK(&self->tqueue, ircomm_tty_do_softint);
-               self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED;
-               self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED;
-
-               /* Init some important stuff */
-               init_timer(&self->watchdog_timer);
-               spin_lock_init(&self->spinlock);
-
-               /*
-                * Force TTY into raw mode by default which is usually what
-                * we want for IrCOMM and IrLPT. This way applications will
-                * not have to twiddle with printcap etc.
-                *
-                * Note this is completely usafe and doesn't work properly
-                */
-               tty->termios.c_iflag = 0;
-               tty->termios.c_oflag = 0;
-
-               /* Insert into hash */
-               hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL);
-       }
-
-       tty->driver_data = self;
-
-       return tty_port_install(&self->port, driver, tty);
-}
-
-/*
- * Function ircomm_tty_open (tty, filp)
- *
- *    This routine is called when a particular tty device is opened. This
- *    routine is mandatory; if this routine is not filled in, the attempted
- *    open will fail with ENODEV.
- */
-static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
-{
-       struct ircomm_tty_cb *self = tty->driver_data;
-       unsigned long   flags;
-       int ret;
-
-       /* ++ is not atomic, so this should be protected - Jean II */
-       spin_lock_irqsave(&self->port.lock, flags);
-       self->port.count++;
-       spin_unlock_irqrestore(&self->port.lock, flags);
-       tty_port_tty_set(&self->port, tty);
-
-       pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name,
-                self->line, self->port.count);
-
-       /* Not really used by us, but lets do it anyway */
-       self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
-       /* Check if this is a "normal" ircomm device, or an irlpt device */
-       if (self->line < 0x10) {
-               self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE;
-               self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */
-               /* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */
-               self->settings.dce = IRCOMM_CTS | IRCOMM_CD | IRCOMM_DSR | IRCOMM_RI; /* Default line settings */
-               pr_debug("%s(), IrCOMM device\n", __func__);
-       } else {
-               pr_debug("%s(), IrLPT device\n", __func__);
-               self->service_type = IRCOMM_3_WIRE_RAW;
-               self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */
-       }
-
-       ret = ircomm_tty_startup(self);
-       if (ret)
-               return ret;
-
-       ret = ircomm_tty_block_til_ready(self, tty, filp);
-       if (ret) {
-               pr_debug("%s(), returning after block_til_ready with %d\n",
-                        __func__, ret);
-
-               return ret;
-       }
-       return 0;
-}
-
-/*
- * Function ircomm_tty_close (tty, filp)
- *
- *    This routine is called when a particular tty device is closed.
- *
- */
-static void ircomm_tty_close(struct tty_struct *tty, struct file *filp)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       struct tty_port *port = &self->port;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       if (tty_port_close_start(port, tty, filp) == 0)
-               return;
-
-       ircomm_tty_shutdown(self);
-
-       tty_driver_flush_buffer(tty);
-
-       tty_port_close_end(port, tty);
-       tty_port_tty_set(port, NULL);
-}
-
-/*
- * Function ircomm_tty_flush_buffer (tty)
- *
- *
- *
- */
-static void ircomm_tty_flush_buffer(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       /*
-        * Let do_softint() do this to avoid race condition with
-        * do_softint() ;-)
-        */
-       schedule_work(&self->tqueue);
-}
-
-/*
- * Function ircomm_tty_do_softint (work)
- *
- *    We use this routine to give the write wakeup to the user at at a
- *    safe time (as fast as possible after write have completed). This
- *    can be compared to the Tx interrupt.
- */
-static void ircomm_tty_do_softint(struct work_struct *work)
-{
-       struct ircomm_tty_cb *self =
-               container_of(work, struct ircomm_tty_cb, tqueue);
-       struct tty_struct *tty;
-       unsigned long flags;
-       struct sk_buff *skb, *ctrl_skb;
-
-       if (!self || self->magic != IRCOMM_TTY_MAGIC)
-               return;
-
-       tty = tty_port_tty_get(&self->port);
-       if (!tty)
-               return;
-
-       /* Unlink control buffer */
-       spin_lock_irqsave(&self->spinlock, flags);
-
-       ctrl_skb = self->ctrl_skb;
-       self->ctrl_skb = NULL;
-
-       spin_unlock_irqrestore(&self->spinlock, flags);
-
-       /* Flush control buffer if any */
-       if(ctrl_skb) {
-               if(self->flow == FLOW_START)
-                       ircomm_control_request(self->ircomm, ctrl_skb);
-               /* Drop reference count - see ircomm_ttp_data_request(). */
-               dev_kfree_skb(ctrl_skb);
-       }
-
-       if (tty->hw_stopped)
-               goto put;
-
-       /* Unlink transmit buffer */
-       spin_lock_irqsave(&self->spinlock, flags);
-
-       skb = self->tx_skb;
-       self->tx_skb = NULL;
-
-       spin_unlock_irqrestore(&self->spinlock, flags);
-
-       /* Flush transmit buffer if any */
-       if (skb) {
-               ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL);
-               /* Drop reference count - see ircomm_ttp_data_request(). */
-               dev_kfree_skb(skb);
-       }
-
-       /* Check if user (still) wants to be waken up */
-       tty_wakeup(tty);
-put:
-       tty_kref_put(tty);
-}
-
-/*
- * Function ircomm_tty_write (tty, buf, count)
- *
- *    This routine is called by the kernel to write a series of characters
- *    to the tty device. The characters may come from user space or kernel
- *    space. This routine will return the number of characters actually
- *    accepted for writing. This routine is mandatory.
- */
-static int ircomm_tty_write(struct tty_struct *tty,
-                           const unsigned char *buf, int count)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       unsigned long flags;
-       struct sk_buff *skb;
-       int tailroom = 0;
-       int len = 0;
-       int size;
-
-       pr_debug("%s(), count=%d, hw_stopped=%d\n", __func__ , count,
-                tty->hw_stopped);
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       /* We may receive packets from the TTY even before we have finished
-        * our setup. Not cool.
-        * The problem is that we don't know the final header and data size
-        * to create the proper skb, so any skb we would create would have
-        * bogus header and data size, so need care.
-        * We use a bogus header size to safely detect this condition.
-        * Another problem is that hw_stopped was set to 0 way before it
-        * should be, so we would drop this skb. It should now be fixed.
-        * One option is to not accept data until we are properly setup.
-        * But, I suspect that when it happens, the ppp line discipline
-        * just "drops" the data, which might screw up connect scripts.
-        * The second option is to create a "safe skb", with large header
-        * and small size (see ircomm_tty_open() for values).
-        * We just need to make sure that when the real values get filled,
-        * we don't mess up the original "safe skb" (see tx_data_size).
-        * Jean II */
-       if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) {
-               pr_debug("%s() : not initialised\n", __func__);
-#ifdef IRCOMM_NO_TX_BEFORE_INIT
-               /* We didn't consume anything, TTY will retry */
-               return 0;
-#endif
-       }
-
-       if (count < 1)
-               return 0;
-
-       /* Protect our manipulation of self->tx_skb and related */
-       spin_lock_irqsave(&self->spinlock, flags);
-
-       /* Fetch current transmit buffer */
-       skb = self->tx_skb;
-
-       /*
-        * Send out all the data we get, possibly as multiple fragmented
-        * frames, but this will only happen if the data is larger than the
-        * max data size. The normal case however is just the opposite, and
-        * this function may be called multiple times, and will then actually
-        * defragment the data and send it out as one packet as soon as
-        * possible, but at a safer point in time
-        */
-       while (count) {
-               size = count;
-
-               /* Adjust data size to the max data size */
-               if (size > self->max_data_size)
-                       size = self->max_data_size;
-
-               /*
-                * Do we already have a buffer ready for transmit, or do
-                * we need to allocate a new frame
-                */
-               if (skb) {
-                       /*
-                        * Any room for more data at the end of the current
-                        * transmit buffer? Cannot use skb_tailroom, since
-                        * dev_alloc_skb gives us a larger skb than we
-                        * requested
-                        * Note : use tx_data_size, because max_data_size
-                        * may have changed and we don't want to overwrite
-                        * the skb. - Jean II
-                        */
-                       if ((tailroom = (self->tx_data_size - skb->len)) > 0) {
-                               /* Adjust data to tailroom */
-                               if (size > tailroom)
-                                       size = tailroom;
-                       } else {
-                               /*
-                                * Current transmit frame is full, so break
-                                * out, so we can send it as soon as possible
-                                */
-                               break;
-                       }
-               } else {
-                       /* Prepare a full sized frame */
-                       skb = alloc_skb(self->max_data_size+
-                                       self->max_header_size,
-                                       GFP_ATOMIC);
-                       if (!skb) {
-                               spin_unlock_irqrestore(&self->spinlock, flags);
-                               return -ENOBUFS;
-                       }
-                       skb_reserve(skb, self->max_header_size);
-                       self->tx_skb = skb;
-                       /* Remember skb size because max_data_size may
-                        * change later on - Jean II */
-                       self->tx_data_size = self->max_data_size;
-               }
-
-               /* Copy data */
-               skb_put_data(skb, buf + len, size);
-
-               count -= size;
-               len += size;
-       }
-
-       spin_unlock_irqrestore(&self->spinlock, flags);
-
-       /*
-        * Schedule a new thread which will transmit the frame as soon
-        * as possible, but at a safe point in time. We do this so the
-        * "user" can give us data multiple times, as PPP does (because of
-        * its 256 byte tx buffer). We will then defragment and send out
-        * all this data as one single packet.
-        */
-       schedule_work(&self->tqueue);
-
-       return len;
-}
-
-/*
- * Function ircomm_tty_write_room (tty)
- *
- *    This routine returns the numbers of characters the tty driver will
- *    accept for queuing to be written. This number is subject to change as
- *    output buffers get emptied, or if the output flow control is acted.
- */
-static int ircomm_tty_write_room(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       unsigned long flags;
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-#ifdef IRCOMM_NO_TX_BEFORE_INIT
-       /* max_header_size tells us if the channel is initialised or not. */
-       if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED)
-               /* Don't bother us yet */
-               return 0;
-#endif
-
-       /* Check if we are allowed to transmit any data.
-        * hw_stopped is the regular flow control.
-        * Jean II */
-       if (tty->hw_stopped)
-               ret = 0;
-       else {
-               spin_lock_irqsave(&self->spinlock, flags);
-               if (self->tx_skb)
-                       ret = self->tx_data_size - self->tx_skb->len;
-               else
-                       ret = self->max_data_size;
-               spin_unlock_irqrestore(&self->spinlock, flags);
-       }
-       pr_debug("%s(), ret=%d\n", __func__ , ret);
-
-       return ret;
-}
-
-/*
- * Function ircomm_tty_wait_until_sent (tty, timeout)
- *
- *    This routine waits until the device has written out all of the
- *    characters in its transmitter FIFO.
- */
-static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       unsigned long orig_jiffies, poll_time;
-       unsigned long flags;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       orig_jiffies = jiffies;
-
-       /* Set poll time to 200 ms */
-       poll_time = msecs_to_jiffies(200);
-       if (timeout)
-               poll_time = min_t(unsigned long, timeout, poll_time);
-
-       spin_lock_irqsave(&self->spinlock, flags);
-       while (self->tx_skb && self->tx_skb->len) {
-               spin_unlock_irqrestore(&self->spinlock, flags);
-               schedule_timeout_interruptible(poll_time);
-               spin_lock_irqsave(&self->spinlock, flags);
-               if (signal_pending(current))
-                       break;
-               if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                       break;
-       }
-       spin_unlock_irqrestore(&self->spinlock, flags);
-       __set_current_state(TASK_RUNNING);
-}
-
-/*
- * Function ircomm_tty_throttle (tty)
- *
- *    This routine notifies the tty driver that input buffers for the line
- *    discipline are close to full, and it should somehow signal that no
- *    more characters should be sent to the tty.
- */
-static void ircomm_tty_throttle(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       /* Software flow control? */
-       if (I_IXOFF(tty))
-               ircomm_tty_send_xchar(tty, STOP_CHAR(tty));
-
-       /* Hardware flow control? */
-       if (C_CRTSCTS(tty)) {
-               self->settings.dte &= ~IRCOMM_RTS;
-               self->settings.dte |= IRCOMM_DELTA_RTS;
-
-               ircomm_param_request(self, IRCOMM_DTE, TRUE);
-       }
-
-       ircomm_flow_request(self->ircomm, FLOW_STOP);
-}
-
-/*
- * Function ircomm_tty_unthrottle (tty)
- *
- *    This routine notifies the tty drivers that it should signals that
- *    characters can now be sent to the tty without fear of overrunning the
- *    input buffers of the line disciplines.
- */
-static void ircomm_tty_unthrottle(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       /* Using software flow control? */
-       if (I_IXOFF(tty))
-               ircomm_tty_send_xchar(tty, START_CHAR(tty));
-
-       /* Using hardware flow control? */
-       if (C_CRTSCTS(tty)) {
-               self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS);
-
-               ircomm_param_request(self, IRCOMM_DTE, TRUE);
-               pr_debug("%s(), FLOW_START\n", __func__);
-       }
-       ircomm_flow_request(self->ircomm, FLOW_START);
-}
-
-/*
- * Function ircomm_tty_chars_in_buffer (tty)
- *
- *    Indicates if there are any data in the buffer
- *
- */
-static int ircomm_tty_chars_in_buffer(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       unsigned long flags;
-       int len = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       spin_lock_irqsave(&self->spinlock, flags);
-
-       if (self->tx_skb)
-               len = self->tx_skb->len;
-
-       spin_unlock_irqrestore(&self->spinlock, flags);
-
-       return len;
-}
-
-static void ircomm_tty_shutdown(struct ircomm_tty_cb *self)
-{
-       unsigned long flags;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       if (!tty_port_initialized(&self->port))
-               return;
-       tty_port_set_initialized(&self->port, 0);
-
-       ircomm_tty_detach_cable(self);
-
-       spin_lock_irqsave(&self->spinlock, flags);
-
-       del_timer(&self->watchdog_timer);
-
-       /* Free parameter buffer */
-       if (self->ctrl_skb) {
-               dev_kfree_skb(self->ctrl_skb);
-               self->ctrl_skb = NULL;
-       }
-
-       /* Free transmit buffer */
-       if (self->tx_skb) {
-               dev_kfree_skb(self->tx_skb);
-               self->tx_skb = NULL;
-       }
-
-       if (self->ircomm) {
-               ircomm_close(self->ircomm);
-               self->ircomm = NULL;
-       }
-
-       spin_unlock_irqrestore(&self->spinlock, flags);
-}
-
-/*
- * Function ircomm_tty_hangup (tty)
- *
- *    This routine notifies the tty driver that it should hangup the tty
- *    device.
- *
- */
-static void ircomm_tty_hangup(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       struct tty_port *port = &self->port;
-       unsigned long   flags;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       /* ircomm_tty_flush_buffer(tty); */
-       ircomm_tty_shutdown(self);
-
-       spin_lock_irqsave(&port->lock, flags);
-       if (port->tty) {
-               set_bit(TTY_IO_ERROR, &port->tty->flags);
-               tty_kref_put(port->tty);
-       }
-       port->tty = NULL;
-       port->count = 0;
-       spin_unlock_irqrestore(&port->lock, flags);
-       tty_port_set_active(port, 0);
-
-       wake_up_interruptible(&port->open_wait);
-}
-
-/*
- * Function ircomm_tty_send_xchar (tty, ch)
- *
- *    This routine is used to send a high-priority XON/XOFF character to
- *    the device.
- */
-static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch)
-{
-       pr_debug("%s(), not impl\n", __func__);
-}
-
-/*
- * Function ircomm_tty_start (tty)
- *
- *    This routine notifies the tty driver that it resume sending
- *    characters to the tty device.
- */
-void ircomm_tty_start(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
-       ircomm_flow_request(self->ircomm, FLOW_START);
-}
-
-/*
- * Function ircomm_tty_stop (tty)
- *
- *     This routine notifies the tty driver that it should stop outputting
- *     characters to the tty device.
- */
-static void ircomm_tty_stop(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       ircomm_flow_request(self->ircomm, FLOW_STOP);
-}
-
-/*
- * Function ircomm_check_modem_status (self)
- *
- *    Check for any changes in the DCE's line settings. This function should
- *    be called whenever the dce parameter settings changes, to update the
- *    flow control settings and other things
- */
-void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
-{
-       struct tty_struct *tty;
-       int status;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       tty = tty_port_tty_get(&self->port);
-
-       status = self->settings.dce;
-
-       if (status & IRCOMM_DCE_DELTA_ANY) {
-               /*wake_up_interruptible(&self->delta_msr_wait);*/
-       }
-       if (tty_port_check_carrier(&self->port) && (status & IRCOMM_DELTA_CD)) {
-               pr_debug("%s(), ircomm%d CD now %s...\n", __func__ , self->line,
-                        (status & IRCOMM_CD) ? "on" : "off");
-
-               if (status & IRCOMM_CD) {
-                       wake_up_interruptible(&self->port.open_wait);
-               } else {
-                       pr_debug("%s(), Doing serial hangup..\n", __func__);
-                       if (tty)
-                               tty_hangup(tty);
-
-                       /* Hangup will remote the tty, so better break out */
-                       goto put;
-               }
-       }
-       if (tty && tty_port_cts_enabled(&self->port)) {
-               if (tty->hw_stopped) {
-                       if (status & IRCOMM_CTS) {
-                               pr_debug("%s(), CTS tx start...\n", __func__);
-                               tty->hw_stopped = 0;
-
-                               /* Wake up processes blocked on open */
-                               wake_up_interruptible(&self->port.open_wait);
-
-                               schedule_work(&self->tqueue);
-                               goto put;
-                       }
-               } else {
-                       if (!(status & IRCOMM_CTS)) {
-                               pr_debug("%s(), CTS tx stop...\n", __func__);
-                               tty->hw_stopped = 1;
-                       }
-               }
-       }
-put:
-       tty_kref_put(tty);
-}
-
-/*
- * Function ircomm_tty_data_indication (instance, sap, skb)
- *
- *    Handle incoming data, and deliver it to the line discipline
- *
- */
-static int ircomm_tty_data_indication(void *instance, void *sap,
-                                     struct sk_buff *skb)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-       struct tty_struct *tty;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       tty = tty_port_tty_get(&self->port);
-       if (!tty) {
-               pr_debug("%s(), no tty!\n", __func__);
-               return 0;
-       }
-
-       /*
-        * If we receive data when hardware is stopped then something is wrong.
-        * We try to poll the peers line settings to check if we are up todate.
-        * Devices like WinCE can do this, and since they don't send any
-        * params, we can just as well declare the hardware for running.
-        */
-       if (tty->hw_stopped && (self->flow == FLOW_START)) {
-               pr_debug("%s(), polling for line settings!\n", __func__);
-               ircomm_param_request(self, IRCOMM_POLL, TRUE);
-
-               /* We can just as well declare the hardware for running */
-               ircomm_tty_send_initial_parameters(self);
-               ircomm_tty_link_established(self);
-       }
-       tty_kref_put(tty);
-
-       /*
-        * Use flip buffer functions since the code may be called from interrupt
-        * context
-        */
-       tty_insert_flip_string(&self->port, skb->data, skb->len);
-       tty_flip_buffer_push(&self->port);
-
-       /* No need to kfree_skb - see ircomm_ttp_data_indication() */
-
-       return 0;
-}
-
-/*
- * Function ircomm_tty_control_indication (instance, sap, skb)
- *
- *    Parse all incoming parameters (easy!)
- *
- */
-static int ircomm_tty_control_indication(void *instance, void *sap,
-                                        struct sk_buff *skb)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-       int clen;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       clen = skb->data[0];
-
-       irda_param_extract_all(self, skb->data+1, IRDA_MIN(skb->len-1, clen),
-                              &ircomm_param_info);
-
-       /* No need to kfree_skb - see ircomm_control_indication() */
-
-       return 0;
-}
-
-/*
- * Function ircomm_tty_flow_indication (instance, sap, cmd)
- *
- *    This function is called by IrTTP when it wants us to slow down the
- *    transmission of data. We just mark the hardware as stopped, and wait
- *    for IrTTP to notify us that things are OK again.
- */
-static void ircomm_tty_flow_indication(void *instance, void *sap,
-                                      LOCAL_FLOW cmd)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-       struct tty_struct *tty;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       tty = tty_port_tty_get(&self->port);
-
-       switch (cmd) {
-       case FLOW_START:
-               pr_debug("%s(), hw start!\n", __func__);
-               if (tty)
-                       tty->hw_stopped = 0;
-
-               /* ircomm_tty_do_softint will take care of the rest */
-               schedule_work(&self->tqueue);
-               break;
-       default:  /* If we get here, something is very wrong, better stop */
-       case FLOW_STOP:
-               pr_debug("%s(), hw stopped!\n", __func__);
-               if (tty)
-                       tty->hw_stopped = 1;
-               break;
-       }
-
-       tty_kref_put(tty);
-       self->flow = cmd;
-}
-
-#ifdef CONFIG_PROC_FS
-static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m)
-{
-       struct tty_struct *tty;
-       char sep;
-
-       seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]);
-
-       seq_puts(m, "Service type: ");
-       if (self->service_type & IRCOMM_9_WIRE)
-               seq_puts(m, "9_WIRE");
-       else if (self->service_type & IRCOMM_3_WIRE)
-               seq_puts(m, "3_WIRE");
-       else if (self->service_type & IRCOMM_3_WIRE_RAW)
-               seq_puts(m, "3_WIRE_RAW");
-       else
-               seq_puts(m, "No common service type!\n");
-       seq_putc(m, '\n');
-
-       seq_printf(m, "Port name: %s\n", self->settings.port_name);
-
-       seq_printf(m, "DTE status:");
-       sep = ' ';
-       if (self->settings.dte & IRCOMM_RTS) {
-               seq_printf(m, "%cRTS", sep);
-               sep = '|';
-       }
-       if (self->settings.dte & IRCOMM_DTR) {
-               seq_printf(m, "%cDTR", sep);
-               sep = '|';
-       }
-       seq_putc(m, '\n');
-
-       seq_puts(m, "DCE status:");
-       sep = ' ';
-       if (self->settings.dce & IRCOMM_CTS) {
-               seq_printf(m, "%cCTS", sep);
-               sep = '|';
-       }
-       if (self->settings.dce & IRCOMM_DSR) {
-               seq_printf(m, "%cDSR", sep);
-               sep = '|';
-       }
-       if (self->settings.dce & IRCOMM_CD) {
-               seq_printf(m, "%cCD", sep);
-               sep = '|';
-       }
-       if (self->settings.dce & IRCOMM_RI) {
-               seq_printf(m, "%cRI", sep);
-               sep = '|';
-       }
-       seq_putc(m, '\n');
-
-       seq_puts(m, "Configuration: ");
-       if (!self->settings.null_modem)
-               seq_puts(m, "DTE <-> DCE\n");
-       else
-               seq_puts(m, "DTE <-> DTE (null modem emulation)\n");
-
-       seq_printf(m, "Data rate: %d\n", self->settings.data_rate);
-
-       seq_puts(m, "Flow control:");
-       sep = ' ';
-       if (self->settings.flow_control & IRCOMM_XON_XOFF_IN) {
-               seq_printf(m, "%cXON_XOFF_IN", sep);
-               sep = '|';
-       }
-       if (self->settings.flow_control & IRCOMM_XON_XOFF_OUT) {
-               seq_printf(m, "%cXON_XOFF_OUT", sep);
-               sep = '|';
-       }
-       if (self->settings.flow_control & IRCOMM_RTS_CTS_IN) {
-               seq_printf(m, "%cRTS_CTS_IN", sep);
-               sep = '|';
-       }
-       if (self->settings.flow_control & IRCOMM_RTS_CTS_OUT) {
-               seq_printf(m, "%cRTS_CTS_OUT", sep);
-               sep = '|';
-       }
-       if (self->settings.flow_control & IRCOMM_DSR_DTR_IN) {
-               seq_printf(m, "%cDSR_DTR_IN", sep);
-               sep = '|';
-       }
-       if (self->settings.flow_control & IRCOMM_DSR_DTR_OUT) {
-               seq_printf(m, "%cDSR_DTR_OUT", sep);
-               sep = '|';
-       }
-       if (self->settings.flow_control & IRCOMM_ENQ_ACK_IN) {
-               seq_printf(m, "%cENQ_ACK_IN", sep);
-               sep = '|';
-       }
-       if (self->settings.flow_control & IRCOMM_ENQ_ACK_OUT) {
-               seq_printf(m, "%cENQ_ACK_OUT", sep);
-               sep = '|';
-       }
-       seq_putc(m, '\n');
-
-       seq_puts(m, "Flags:");
-       sep = ' ';
-       if (tty_port_cts_enabled(&self->port)) {
-               seq_printf(m, "%cASYNC_CTS_FLOW", sep);
-               sep = '|';
-       }
-       if (tty_port_check_carrier(&self->port)) {
-               seq_printf(m, "%cASYNC_CHECK_CD", sep);
-               sep = '|';
-       }
-       if (tty_port_initialized(&self->port)) {
-               seq_printf(m, "%cASYNC_INITIALIZED", sep);
-               sep = '|';
-       }
-       if (self->port.flags & ASYNC_LOW_LATENCY) {
-               seq_printf(m, "%cASYNC_LOW_LATENCY", sep);
-               sep = '|';
-       }
-       if (tty_port_active(&self->port)) {
-               seq_printf(m, "%cASYNC_NORMAL_ACTIVE", sep);
-               sep = '|';
-       }
-       seq_putc(m, '\n');
-
-       seq_printf(m, "Role: %s\n", self->client ? "client" : "server");
-       seq_printf(m, "Open count: %d\n", self->port.count);
-       seq_printf(m, "Max data size: %d\n", self->max_data_size);
-       seq_printf(m, "Max header size: %d\n", self->max_header_size);
-
-       tty = tty_port_tty_get(&self->port);
-       if (tty) {
-               seq_printf(m, "Hardware: %s\n",
-                              tty->hw_stopped ? "Stopped" : "Running");
-               tty_kref_put(tty);
-       }
-}
-
-static int ircomm_tty_proc_show(struct seq_file *m, void *v)
-{
-       struct ircomm_tty_cb *self;
-       unsigned long flags;
-
-       spin_lock_irqsave(&ircomm_tty->hb_spinlock, flags);
-
-       self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty);
-       while (self != NULL) {
-               if (self->magic != IRCOMM_TTY_MAGIC)
-                       break;
-
-               ircomm_tty_line_info(self, m);
-               self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty);
-       }
-       spin_unlock_irqrestore(&ircomm_tty->hb_spinlock, flags);
-       return 0;
-}
-
-static int ircomm_tty_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ircomm_tty_proc_show, NULL);
-}
-
-static const struct file_operations ircomm_tty_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = ircomm_tty_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-#endif /* CONFIG_PROC_FS */
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("IrCOMM serial TTY driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(IRCOMM_TTY_MAJOR);
-
-module_init(ircomm_tty_init);
-module_exit(ircomm_tty_cleanup);
diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c
deleted file mode 100644 (file)
index 0a41101..0000000
+++ /dev/null
@@ -1,987 +0,0 @@
-/*********************************************************************
- *
- * Filename:      ircomm_tty_attach.c
- * Version:
- * Description:   Code for attaching the serial driver to IrCOMM
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sat Jun  5 17:42:00 1999
- * Modified at:   Tue Jan  4 14:20:49 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-#include <linux/sched.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/parameters.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_param.h>
-#include <net/irda/ircomm_event.h>
-
-#include <net/irda/ircomm_tty.h>
-#include <net/irda/ircomm_tty_attach.h>
-
-static void ircomm_tty_ias_register(struct ircomm_tty_cb *self);
-static void ircomm_tty_discovery_indication(discinfo_t *discovery,
-                                           DISCOVERY_MODE mode,
-                                           void *priv);
-static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
-                                       struct ias_value *value, void *priv);
-static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
-                                           int timeout);
-static void ircomm_tty_watchdog_timer_expired(void *data);
-
-static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
-                                IRCOMM_TTY_EVENT event,
-                                struct sk_buff *skb,
-                                struct ircomm_tty_info *info);
-static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
-                                  IRCOMM_TTY_EVENT event,
-                                  struct sk_buff *skb,
-                                  struct ircomm_tty_info *info);
-static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
-                                            IRCOMM_TTY_EVENT event,
-                                            struct sk_buff *skb,
-                                            struct ircomm_tty_info *info);
-static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
-                                          IRCOMM_TTY_EVENT event,
-                                          struct sk_buff *skb,
-                                          struct ircomm_tty_info *info);
-static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
-                                 IRCOMM_TTY_EVENT event,
-                                 struct sk_buff *skb,
-                                 struct ircomm_tty_info *info);
-static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
-                                 IRCOMM_TTY_EVENT event,
-                                 struct sk_buff *skb,
-                                 struct ircomm_tty_info *info);
-
-const char *const ircomm_tty_state[] = {
-       "IRCOMM_TTY_IDLE",
-       "IRCOMM_TTY_SEARCH",
-       "IRCOMM_TTY_QUERY_PARAMETERS",
-       "IRCOMM_TTY_QUERY_LSAP_SEL",
-       "IRCOMM_TTY_SETUP",
-       "IRCOMM_TTY_READY",
-       "*** ERROR *** ",
-};
-
-static const char *const ircomm_tty_event[] __maybe_unused = {
-       "IRCOMM_TTY_ATTACH_CABLE",
-       "IRCOMM_TTY_DETACH_CABLE",
-       "IRCOMM_TTY_DATA_REQUEST",
-       "IRCOMM_TTY_DATA_INDICATION",
-       "IRCOMM_TTY_DISCOVERY_REQUEST",
-       "IRCOMM_TTY_DISCOVERY_INDICATION",
-       "IRCOMM_TTY_CONNECT_CONFIRM",
-       "IRCOMM_TTY_CONNECT_INDICATION",
-       "IRCOMM_TTY_DISCONNECT_REQUEST",
-       "IRCOMM_TTY_DISCONNECT_INDICATION",
-       "IRCOMM_TTY_WD_TIMER_EXPIRED",
-       "IRCOMM_TTY_GOT_PARAMETERS",
-       "IRCOMM_TTY_GOT_LSAPSEL",
-       "*** ERROR ****",
-};
-
-static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
-                     struct sk_buff *skb, struct ircomm_tty_info *info) =
-{
-       ircomm_tty_state_idle,
-       ircomm_tty_state_search,
-       ircomm_tty_state_query_parameters,
-       ircomm_tty_state_query_lsap_sel,
-       ircomm_tty_state_setup,
-       ircomm_tty_state_ready,
-};
-
-/*
- * Function ircomm_tty_attach_cable (driver)
- *
- *    Try to attach cable (IrCOMM link). This function will only return
- *    when the link has been connected, or if an error condition occurs.
- *    If success, the return value is the resulting service type.
- */
-int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
-{
-       struct tty_struct *tty;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       /* Check if somebody has already connected to us */
-       if (ircomm_is_connected(self->ircomm)) {
-               pr_debug("%s(), already connected!\n", __func__);
-               return 0;
-       }
-
-       /* Make sure nobody tries to write before the link is up */
-       tty = tty_port_tty_get(&self->port);
-       if (tty) {
-               tty->hw_stopped = 1;
-               tty_kref_put(tty);
-       }
-
-       ircomm_tty_ias_register(self);
-
-       ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL);
-
-       return 0;
-}
-
-/*
- * Function ircomm_detach_cable (driver)
- *
- *    Detach cable, or cable has been detached by peer
- *
- */
-void ircomm_tty_detach_cable(struct ircomm_tty_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       del_timer(&self->watchdog_timer);
-
-       /* Remove discovery handler */
-       if (self->ckey) {
-               irlmp_unregister_client(self->ckey);
-               self->ckey = NULL;
-       }
-       /* Remove IrCOMM hint bits */
-       if (self->skey) {
-               irlmp_unregister_service(self->skey);
-               self->skey = NULL;
-       }
-
-       if (self->iriap) {
-               iriap_close(self->iriap);
-               self->iriap = NULL;
-       }
-
-       /* Remove LM-IAS object */
-       if (self->obj) {
-               irias_delete_object(self->obj);
-               self->obj = NULL;
-       }
-
-       ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL);
-
-       /* Reset some values */
-       self->daddr = self->saddr = 0;
-       self->dlsap_sel = self->slsap_sel = 0;
-
-       memset(&self->settings, 0, sizeof(struct ircomm_params));
-}
-
-/*
- * Function ircomm_tty_ias_register (self)
- *
- *    Register with LM-IAS depending on which service type we are
- *
- */
-static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
-{
-       __u8 oct_seq[6];
-       __u16 hints;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       /* Compute hint bits based on service */
-       hints = irlmp_service_to_hint(S_COMM);
-       if (self->service_type & IRCOMM_3_WIRE_RAW)
-               hints |= irlmp_service_to_hint(S_PRINTER);
-
-       /* Advertise IrCOMM hint bit in discovery */
-       if (!self->skey)
-               self->skey = irlmp_register_service(hints);
-       /* Set up a discovery handler */
-       if (!self->ckey)
-               self->ckey = irlmp_register_client(hints,
-                                                  ircomm_tty_discovery_indication,
-                                                  NULL, (void *) self);
-
-       /* If already done, no need to do it again */
-       if (self->obj)
-               return;
-
-       if (self->service_type & IRCOMM_3_WIRE_RAW) {
-               /* Register IrLPT with LM-IAS */
-               self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID);
-               irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel",
-                                        self->slsap_sel, IAS_KERNEL_ATTR);
-       } else {
-               /* Register IrCOMM with LM-IAS */
-               self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID);
-               irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel",
-                                        self->slsap_sel, IAS_KERNEL_ATTR);
-
-               /* Code the parameters into the buffer */
-               irda_param_pack(oct_seq, "bbbbbb",
-                               IRCOMM_SERVICE_TYPE, 1, self->service_type,
-                               IRCOMM_PORT_TYPE,    1, IRCOMM_SERIAL);
-
-               /* Register parameters with LM-IAS */
-               irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6,
-                                       IAS_KERNEL_ATTR);
-       }
-       irias_insert_object(self->obj);
-}
-
-/*
- * Function ircomm_tty_ias_unregister (self)
- *
- *    Remove our IAS object and client hook while connected.
- *
- */
-static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self)
-{
-       /* Remove LM-IAS object now so it is not reused.
-        * IrCOMM deals very poorly with multiple incoming connections.
-        * It should looks a lot more like IrNET, and "dup" a server TSAP
-        * to the application TSAP (based on various rules).
-        * This is a cheap workaround allowing multiple clients to
-        * connect to us. It will not always work.
-        * Each IrCOMM socket has an IAS entry. Incoming connection will
-        * pick the first one found. So, when we are fully connected,
-        * we remove our IAS entries so that the next IAS entry is used.
-        * We do that for *both* client and server, because a server
-        * can also create client instances.
-        * Jean II */
-       if (self->obj) {
-               irias_delete_object(self->obj);
-               self->obj = NULL;
-       }
-
-#if 0
-       /* Remove discovery handler.
-        * While we are connected, we no longer need to receive
-        * discovery events. This would be the case if there is
-        * multiple IrLAP interfaces. Jean II */
-       if (self->ckey) {
-               irlmp_unregister_client(self->ckey);
-               self->ckey = NULL;
-       }
-#endif
-}
-
-/*
- * Function ircomm_send_initial_parameters (self)
- *
- *    Send initial parameters to the remote IrCOMM device. These parameters
- *    must be sent before any data.
- */
-int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (self->service_type & IRCOMM_3_WIRE_RAW)
-               return 0;
-
-       /*
-        * Set default values, but only if the application for some reason
-        * haven't set them already
-        */
-       pr_debug("%s(), data-rate = %d\n", __func__ ,
-                self->settings.data_rate);
-       if (!self->settings.data_rate)
-               self->settings.data_rate = 9600;
-       pr_debug("%s(), data-format = %d\n", __func__ ,
-                self->settings.data_format);
-       if (!self->settings.data_format)
-               self->settings.data_format = IRCOMM_WSIZE_8;  /* 8N1 */
-
-       pr_debug("%s(), flow-control = %d\n", __func__ ,
-                self->settings.flow_control);
-       /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
-
-       /* Do not set delta values for the initial parameters */
-       self->settings.dte = IRCOMM_DTR | IRCOMM_RTS;
-
-       /* Only send service type parameter when we are the client */
-       if (self->client)
-               ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE);
-       ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
-       ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
-
-       /* For a 3 wire service, we just flush the last parameter and return */
-       if (self->settings.service_type == IRCOMM_3_WIRE) {
-               ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
-               return 0;
-       }
-
-       /* Only 9-wire service types continue here */
-       ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE);
-#if 0
-       ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE);
-       ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE);
-#endif
-       /* Notify peer that we are ready to receive data */
-       ircomm_param_request(self, IRCOMM_DTE, TRUE);
-
-       return 0;
-}
-
-/*
- * Function ircomm_tty_discovery_indication (discovery)
- *
- *    Remote device is discovered, try query the remote IAS to see which
- *    device it is, and which services it has.
- *
- */
-static void ircomm_tty_discovery_indication(discinfo_t *discovery,
-                                           DISCOVERY_MODE mode,
-                                           void *priv)
-{
-       struct ircomm_tty_cb *self;
-       struct ircomm_tty_info info;
-
-       /* Important note :
-        * We need to drop all passive discoveries.
-        * The LSAP management of IrComm is deficient and doesn't deal
-        * with the case of two instance connecting to each other
-        * simultaneously (it will deadlock in LMP).
-        * The proper fix would be to use the same technique as in IrNET,
-        * to have one server socket and separate instances for the
-        * connecting/connected socket.
-        * The workaround is to drop passive discovery, which drastically
-        * reduce the probability of this happening.
-        * Jean II */
-       if(mode == DISCOVERY_PASSIVE)
-               return;
-
-       info.daddr = discovery->daddr;
-       info.saddr = discovery->saddr;
-
-       self = priv;
-       ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
-                           NULL, &info);
-}
-
-/*
- * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb)
- *
- *    Link disconnected
- *
- */
-void ircomm_tty_disconnect_indication(void *instance, void *sap,
-                                     LM_REASON reason,
-                                     struct sk_buff *skb)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-       struct tty_struct *tty;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       tty = tty_port_tty_get(&self->port);
-       if (!tty)
-               return;
-
-       /* This will stop control data transfers */
-       self->flow = FLOW_STOP;
-
-       /* Stop data transfers */
-       tty->hw_stopped = 1;
-
-       ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
-                           NULL);
-       tty_kref_put(tty);
-}
-
-/*
- * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
- *
- *    Got result from the IAS query we make
- *
- */
-static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
-                                       struct ias_value *value,
-                                       void *priv)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       /* We probably don't need to make any more queries */
-       iriap_close(self->iriap);
-       self->iriap = NULL;
-
-       /* Check if request succeeded */
-       if (result != IAS_SUCCESS) {
-               pr_debug("%s(), got NULL value!\n", __func__);
-               return;
-       }
-
-       switch (value->type) {
-       case IAS_OCT_SEQ:
-               pr_debug("%s(), got octet sequence\n", __func__);
-
-               irda_param_extract_all(self, value->t.oct_seq, value->len,
-                                      &ircomm_param_info);
-
-               ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
-                                   NULL);
-               break;
-       case IAS_INTEGER:
-               /* Got LSAP selector */
-               pr_debug("%s(), got lsapsel = %d\n", __func__ ,
-                        value->t.integer);
-
-               if (value->t.integer == -1) {
-                       pr_debug("%s(), invalid value!\n", __func__);
-               } else
-                       self->dlsap_sel = value->t.integer;
-
-               ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
-               break;
-       case IAS_MISSING:
-               pr_debug("%s(), got IAS_MISSING\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), got unknown type!\n", __func__);
-               break;
-       }
-       irias_delete_value(value);
-}
-
-/*
- * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
- *
- *    Connection confirmed
- *
- */
-void ircomm_tty_connect_confirm(void *instance, void *sap,
-                               struct qos_info *qos,
-                               __u32 max_data_size,
-                               __u8 max_header_size,
-                               struct sk_buff *skb)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       self->client = TRUE;
-       self->max_data_size = max_data_size;
-       self->max_header_size = max_header_size;
-       self->flow = FLOW_START;
-
-       ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
-
-       /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
-}
-
-/*
- * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
- *                                         skb)
- *
- *    we are discovered and being requested to connect by remote device !
- *
- */
-void ircomm_tty_connect_indication(void *instance, void *sap,
-                                  struct qos_info *qos,
-                                  __u32 max_data_size,
-                                  __u8 max_header_size,
-                                  struct sk_buff *skb)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
-       int clen;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       self->client = FALSE;
-       self->max_data_size = max_data_size;
-       self->max_header_size = max_header_size;
-       self->flow = FLOW_START;
-
-       clen = skb->data[0];
-       if (clen)
-               irda_param_extract_all(self, skb->data+1,
-                                      IRDA_MIN(skb->len, clen),
-                                      &ircomm_param_info);
-
-       ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
-
-       /* No need to kfree_skb - see ircomm_ttp_connect_indication() */
-}
-
-/*
- * Function ircomm_tty_link_established (self)
- *
- *    Called when the IrCOMM link is established
- *
- */
-void ircomm_tty_link_established(struct ircomm_tty_cb *self)
-{
-       struct tty_struct *tty;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       tty = tty_port_tty_get(&self->port);
-       if (!tty)
-               return;
-
-       del_timer(&self->watchdog_timer);
-
-       /*
-        * IrCOMM link is now up, and if we are not using hardware
-        * flow-control, then declare the hardware as running. Otherwise we
-        * will have to wait for the peer device (DCE) to raise the CTS
-        * line.
-        */
-       if (tty_port_cts_enabled(&self->port) &&
-                       ((self->settings.dce & IRCOMM_CTS) == 0)) {
-               pr_debug("%s(), waiting for CTS ...\n", __func__);
-               goto put;
-       } else {
-               pr_debug("%s(), starting hardware!\n", __func__);
-
-               tty->hw_stopped = 0;
-
-               /* Wake up processes blocked on open */
-               wake_up_interruptible(&self->port.open_wait);
-       }
-
-       schedule_work(&self->tqueue);
-put:
-       tty_kref_put(tty);
-}
-
-/*
- * Function ircomm_tty_start_watchdog_timer (self, timeout)
- *
- *    Start the watchdog timer. This timer is used to make sure that any
- *    connection attempt is successful, and if not, we will retry after
- *    the timeout
- */
-static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
-                                           int timeout)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
-                        ircomm_tty_watchdog_timer_expired);
-}
-
-/*
- * Function ircomm_tty_watchdog_timer_expired (data)
- *
- *    Called when the connect procedure have taken to much time.
- *
- */
-static void ircomm_tty_watchdog_timer_expired(void *data)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
-}
-
-
-/*
- * Function ircomm_tty_do_event (self, event, skb)
- *
- *    Process event
- *
- */
-int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
-                       struct sk_buff *skb, struct ircomm_tty_info *info)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       pr_debug("%s: state=%s, event=%s\n", __func__ ,
-                ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
-       return (*state[self->state])(self, event, skb, info);
-}
-
-/*
- * Function ircomm_tty_next_state (self, state)
- *
- *    Switch state
- *
- */
-static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
-{
-       /*
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
-
-       pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
-       ircomm_tty_state[self->state], self->service_type);
-       */
-       self->state = state;
-}
-
-/*
- * Function ircomm_tty_state_idle (self, event, skb, info)
- *
- *    Just hanging around
- *
- */
-static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
-                                IRCOMM_TTY_EVENT event,
-                                struct sk_buff *skb,
-                                struct ircomm_tty_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s: state=%s, event=%s\n", __func__ ,
-                ircomm_tty_state[self->state], ircomm_tty_event[event]);
-       switch (event) {
-       case IRCOMM_TTY_ATTACH_CABLE:
-               /* Try to discover any remote devices */
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
-
-               irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
-               break;
-       case IRCOMM_TTY_DISCOVERY_INDICATION:
-               self->daddr = info->daddr;
-               self->saddr = info->saddr;
-
-               if (self->iriap) {
-                       net_warn_ratelimited("%s(), busy with a previous query\n",
-                                            __func__);
-                       return -EBUSY;
-               }
-
-               self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
-                                        ircomm_tty_getvalue_confirm);
-
-               iriap_getvaluebyclass_request(self->iriap,
-                                             self->saddr, self->daddr,
-                                             "IrDA:IrCOMM", "Parameters");
-
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
-               break;
-       case IRCOMM_TTY_CONNECT_INDICATION:
-               del_timer(&self->watchdog_timer);
-
-               /* Accept connection */
-               ircomm_connect_response(self->ircomm, NULL);
-               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
-               break;
-       case IRCOMM_TTY_WD_TIMER_EXPIRED:
-               /* Just stay idle */
-               break;
-       case IRCOMM_TTY_DETACH_CABLE:
-               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
-               break;
-       default:
-               pr_debug("%s(), unknown event: %s\n", __func__ ,
-                        ircomm_tty_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_tty_state_search (self, event, skb, info)
- *
- *    Trying to discover an IrCOMM device
- *
- */
-static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
-                                  IRCOMM_TTY_EVENT event,
-                                  struct sk_buff *skb,
-                                  struct ircomm_tty_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s: state=%s, event=%s\n", __func__ ,
-                ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
-       switch (event) {
-       case IRCOMM_TTY_DISCOVERY_INDICATION:
-               self->daddr = info->daddr;
-               self->saddr = info->saddr;
-
-               if (self->iriap) {
-                       net_warn_ratelimited("%s(), busy with a previous query\n",
-                                            __func__);
-                       return -EBUSY;
-               }
-
-               self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
-                                        ircomm_tty_getvalue_confirm);
-
-               if (self->service_type == IRCOMM_3_WIRE_RAW) {
-                       iriap_getvaluebyclass_request(self->iriap, self->saddr,
-                                                     self->daddr, "IrLPT",
-                                                     "IrDA:IrLMP:LsapSel");
-                       ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
-               } else {
-                       iriap_getvaluebyclass_request(self->iriap, self->saddr,
-                                                     self->daddr,
-                                                     "IrDA:IrCOMM",
-                                                     "Parameters");
-
-                       ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
-               }
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               break;
-       case IRCOMM_TTY_CONNECT_INDICATION:
-               del_timer(&self->watchdog_timer);
-               ircomm_tty_ias_unregister(self);
-
-               /* Accept connection */
-               ircomm_connect_response(self->ircomm, NULL);
-               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
-               break;
-       case IRCOMM_TTY_WD_TIMER_EXPIRED:
-#if 1
-               /* Give up */
-#else
-               /* Try to discover any remote devices */
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
-#endif
-               break;
-       case IRCOMM_TTY_DETACH_CABLE:
-               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
-               break;
-       default:
-               pr_debug("%s(), unknown event: %s\n", __func__ ,
-                        ircomm_tty_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_tty_state_query (self, event, skb, info)
- *
- *    Querying the remote LM-IAS for IrCOMM parameters
- *
- */
-static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
-                                            IRCOMM_TTY_EVENT event,
-                                            struct sk_buff *skb,
-                                            struct ircomm_tty_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s: state=%s, event=%s\n", __func__ ,
-                ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
-       switch (event) {
-       case IRCOMM_TTY_GOT_PARAMETERS:
-               if (self->iriap) {
-                       net_warn_ratelimited("%s(), busy with a previous query\n",
-                                            __func__);
-                       return -EBUSY;
-               }
-
-               self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
-                                        ircomm_tty_getvalue_confirm);
-
-               iriap_getvaluebyclass_request(self->iriap, self->saddr,
-                                             self->daddr, "IrDA:IrCOMM",
-                                             "IrDA:TinyTP:LsapSel");
-
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
-               break;
-       case IRCOMM_TTY_WD_TIMER_EXPIRED:
-               /* Go back to search mode */
-               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               break;
-       case IRCOMM_TTY_CONNECT_INDICATION:
-               del_timer(&self->watchdog_timer);
-               ircomm_tty_ias_unregister(self);
-
-               /* Accept connection */
-               ircomm_connect_response(self->ircomm, NULL);
-               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
-               break;
-       case IRCOMM_TTY_DETACH_CABLE:
-               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
-               break;
-       default:
-               pr_debug("%s(), unknown event: %s\n", __func__ ,
-                        ircomm_tty_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info)
- *
- *    Query remote LM-IAS for the LSAP selector which we can connect to
- *
- */
-static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
-                                          IRCOMM_TTY_EVENT event,
-                                          struct sk_buff *skb,
-                                          struct ircomm_tty_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s: state=%s, event=%s\n", __func__ ,
-                ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
-       switch (event) {
-       case IRCOMM_TTY_GOT_LSAPSEL:
-               /* Connect to remote device */
-               ret = ircomm_connect_request(self->ircomm, self->dlsap_sel,
-                                            self->saddr, self->daddr,
-                                            NULL, self->service_type);
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               ircomm_tty_next_state(self, IRCOMM_TTY_SETUP);
-               break;
-       case IRCOMM_TTY_WD_TIMER_EXPIRED:
-               /* Go back to search mode */
-               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               break;
-       case IRCOMM_TTY_CONNECT_INDICATION:
-               del_timer(&self->watchdog_timer);
-               ircomm_tty_ias_unregister(self);
-
-               /* Accept connection */
-               ircomm_connect_response(self->ircomm, NULL);
-               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
-               break;
-       case IRCOMM_TTY_DETACH_CABLE:
-               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
-               break;
-       default:
-               pr_debug("%s(), unknown event: %s\n", __func__ ,
-                        ircomm_tty_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_tty_state_setup (self, event, skb, info)
- *
- *    Trying to connect
- *
- */
-static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
-                                 IRCOMM_TTY_EVENT event,
-                                 struct sk_buff *skb,
-                                 struct ircomm_tty_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s: state=%s, event=%s\n", __func__ ,
-                ircomm_tty_state[self->state], ircomm_tty_event[event]);
-
-       switch (event) {
-       case IRCOMM_TTY_CONNECT_CONFIRM:
-               del_timer(&self->watchdog_timer);
-               ircomm_tty_ias_unregister(self);
-
-               /*
-                * Send initial parameters. This will also send out queued
-                * parameters waiting for the connection to come up
-                */
-               ircomm_tty_send_initial_parameters(self);
-               ircomm_tty_link_established(self);
-               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
-               break;
-       case IRCOMM_TTY_CONNECT_INDICATION:
-               del_timer(&self->watchdog_timer);
-               ircomm_tty_ias_unregister(self);
-
-               /* Accept connection */
-               ircomm_connect_response(self->ircomm, NULL);
-               ircomm_tty_next_state(self, IRCOMM_TTY_READY);
-               break;
-       case IRCOMM_TTY_WD_TIMER_EXPIRED:
-               /* Go back to search mode */
-               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-               break;
-       case IRCOMM_TTY_DETACH_CABLE:
-               /* ircomm_disconnect_request(self->ircomm, NULL); */
-               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
-               break;
-       default:
-               pr_debug("%s(), unknown event: %s\n", __func__ ,
-                        ircomm_tty_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Function ircomm_tty_state_ready (self, event, skb, info)
- *
- *    IrCOMM is now connected
- *
- */
-static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
-                                 IRCOMM_TTY_EVENT event,
-                                 struct sk_buff *skb,
-                                 struct ircomm_tty_info *info)
-{
-       int ret = 0;
-
-       switch (event) {
-       case IRCOMM_TTY_DATA_REQUEST:
-               ret = ircomm_data_request(self->ircomm, skb);
-               break;
-       case IRCOMM_TTY_DETACH_CABLE:
-               ircomm_disconnect_request(self->ircomm, NULL);
-               ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
-               break;
-       case IRCOMM_TTY_DISCONNECT_INDICATION:
-               ircomm_tty_ias_register(self);
-               ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
-               ircomm_tty_start_watchdog_timer(self, 3*HZ);
-
-               if (tty_port_check_carrier(&self->port)) {
-                       /* Drop carrier */
-                       self->settings.dce = IRCOMM_DELTA_CD;
-                       ircomm_tty_check_modem_status(self);
-               } else {
-                       pr_debug("%s(), hanging up!\n", __func__);
-                       tty_port_tty_hangup(&self->port, false);
-               }
-               break;
-       default:
-               pr_debug("%s(), unknown event: %s\n", __func__ ,
-                        ircomm_tty_event[event]);
-               ret = -EINVAL;
-       }
-       return ret;
-}
-
diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c
deleted file mode 100644 (file)
index 171c3de..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-/*********************************************************************
- *
- * Filename:      ircomm_tty_ioctl.c
- * Version:
- * Description:
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Thu Jun 10 14:39:09 1999
- * Modified at:   Wed Jan  5 14:45:43 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/termios.h>
-#include <linux/tty.h>
-#include <linux/serial.h>
-
-#include <linux/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-
-#include <net/irda/ircomm_core.h>
-#include <net/irda/ircomm_param.h>
-#include <net/irda/ircomm_tty_attach.h>
-#include <net/irda/ircomm_tty.h>
-
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
-/*
- * Function ircomm_tty_change_speed (driver)
- *
- *    Change speed of the driver. If the remote device is a DCE, then this
- *    should make it change the speed of its serial port
- */
-static void ircomm_tty_change_speed(struct ircomm_tty_cb *self,
-               struct tty_struct *tty)
-{
-       unsigned int cflag, cval;
-       int baud;
-
-       if (!self->ircomm)
-               return;
-
-       cflag = tty->termios.c_cflag;
-
-       /*  byte size and parity */
-       switch (cflag & CSIZE) {
-       case CS5: cval = IRCOMM_WSIZE_5; break;
-       case CS6: cval = IRCOMM_WSIZE_6; break;
-       case CS7: cval = IRCOMM_WSIZE_7; break;
-       case CS8: cval = IRCOMM_WSIZE_8; break;
-       default:  cval = IRCOMM_WSIZE_5; break;
-       }
-       if (cflag & CSTOPB)
-               cval |= IRCOMM_2_STOP_BIT;
-
-       if (cflag & PARENB)
-               cval |= IRCOMM_PARITY_ENABLE;
-       if (!(cflag & PARODD))
-               cval |= IRCOMM_PARITY_EVEN;
-
-       /* Determine divisor based on baud rate */
-       baud = tty_get_baud_rate(tty);
-       if (!baud)
-               baud = 9600;    /* B0 transition handled in rs_set_termios */
-
-       self->settings.data_rate = baud;
-       ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
-
-       /* CTS flow control flag and modem status interrupts */
-       tty_port_set_cts_flow(&self->port, cflag & CRTSCTS);
-       if (cflag & CRTSCTS) {
-               self->settings.flow_control |= IRCOMM_RTS_CTS_IN;
-               /* This got me. Bummer. Jean II */
-               if (self->service_type == IRCOMM_3_WIRE_RAW)
-                       net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n",
-                                            __func__);
-       } else {
-               self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
-       }
-       tty_port_set_check_carrier(&self->port, ~cflag & CLOCAL);
-
-       self->settings.data_format = cval;
-
-       ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
-       ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
-}
-
-/*
- * Function ircomm_tty_set_termios (tty, old_termios)
- *
- *    This routine allows the tty driver to be notified when device's
- *    termios settings have changed.  Note that a well-designed tty driver
- *    should be prepared to accept the case where old == NULL, and try to
- *    do something rational.
- */
-void ircomm_tty_set_termios(struct tty_struct *tty,
-                           struct ktermios *old_termios)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       unsigned int cflag = tty->termios.c_cflag;
-
-       if ((cflag == old_termios->c_cflag) &&
-           (RELEVANT_IFLAG(tty->termios.c_iflag) ==
-            RELEVANT_IFLAG(old_termios->c_iflag)))
-       {
-               return;
-       }
-
-       ircomm_tty_change_speed(self, tty);
-
-       /* Handle transition to B0 status */
-       if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) {
-               self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS);
-               ircomm_param_request(self, IRCOMM_DTE, TRUE);
-       }
-
-       /* Handle transition away from B0 status */
-       if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
-               self->settings.dte |= IRCOMM_DTR;
-               if (!C_CRTSCTS(tty) || !tty_throttled(tty))
-                       self->settings.dte |= IRCOMM_RTS;
-               ircomm_param_request(self, IRCOMM_DTE, TRUE);
-       }
-
-       /* Handle turning off CRTSCTS */
-       if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty))
-       {
-               tty->hw_stopped = 0;
-               ircomm_tty_start(tty);
-       }
-}
-
-/*
- * Function ircomm_tty_tiocmget (tty)
- *
- *
- *
- */
-int ircomm_tty_tiocmget(struct tty_struct *tty)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       unsigned int result;
-
-       if (tty_io_error(tty))
-               return -EIO;
-
-       result =  ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
-               | ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
-               | ((self->settings.dce & IRCOMM_CD)  ? TIOCM_CAR : 0)
-               | ((self->settings.dce & IRCOMM_RI)  ? TIOCM_RNG : 0)
-               | ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
-               | ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
-       return result;
-}
-
-/*
- * Function ircomm_tty_tiocmset (tty, set, clear)
- *
- *
- *
- */
-int ircomm_tty_tiocmset(struct tty_struct *tty,
-                       unsigned int set, unsigned int clear)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-
-       if (tty_io_error(tty))
-               return -EIO;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
-
-       if (set & TIOCM_RTS)
-               self->settings.dte |= IRCOMM_RTS;
-       if (set & TIOCM_DTR)
-               self->settings.dte |= IRCOMM_DTR;
-
-       if (clear & TIOCM_RTS)
-               self->settings.dte &= ~IRCOMM_RTS;
-       if (clear & TIOCM_DTR)
-               self->settings.dte &= ~IRCOMM_DTR;
-
-       if ((set|clear) & TIOCM_RTS)
-               self->settings.dte |= IRCOMM_DELTA_RTS;
-       if ((set|clear) & TIOCM_DTR)
-               self->settings.dte |= IRCOMM_DELTA_DTR;
-
-       ircomm_param_request(self, IRCOMM_DTE, TRUE);
-
-       return 0;
-}
-
-/*
- * Function get_serial_info (driver, retinfo)
- *
- *
- *
- */
-static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self,
-                                     struct serial_struct __user *retinfo)
-{
-       struct serial_struct info;
-
-       memset(&info, 0, sizeof(info));
-       info.line = self->line;
-       info.flags = self->port.flags;
-       info.baud_base = self->settings.data_rate;
-       info.close_delay = self->port.close_delay;
-       info.closing_wait = self->port.closing_wait;
-
-       /* For compatibility  */
-       info.type = PORT_16550A;
-
-       if (copy_to_user(retinfo, &info, sizeof(*retinfo)))
-               return -EFAULT;
-
-       return 0;
-}
-
-/*
- * Function set_serial_info (driver, new_info)
- *
- *
- *
- */
-static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self,
-                                     struct serial_struct __user *new_info)
-{
-       return 0;
-}
-
-/*
- * Function ircomm_tty_ioctl (tty, cmd, arg)
- *
- *
- *
- */
-int ircomm_tty_ioctl(struct tty_struct *tty,
-                    unsigned int cmd, unsigned long arg)
-{
-       struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
-       int ret = 0;
-
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
-           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
-               if (tty_io_error(tty))
-                   return -EIO;
-       }
-
-       switch (cmd) {
-       case TIOCGSERIAL:
-               ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg);
-               break;
-       case TIOCSSERIAL:
-               ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg);
-               break;
-       case TIOCMIWAIT:
-               pr_debug("(), TIOCMIWAIT, not impl!\n");
-               break;
-
-       case TIOCGICOUNT:
-               pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__);
-               return 0;
-       default:
-               ret = -ENOIOCTLCMD;  /* ioctls which we must ignore */
-       }
-       return ret;
-}
-
-
-
diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c
deleted file mode 100644 (file)
index 890b90d..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irda_device.c
- * Version:       0.9
- * Description:   Utility functions used by the device drivers
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sat Oct  9 09:22:27 1999
- * Modified at:   Sun Jan 23 17:41:24 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/string.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <linux/capability.h>
-#include <linux/if.h>
-#include <linux/if_ether.h>
-#include <linux/if_arp.h>
-#include <linux/netdevice.h>
-#include <linux/init.h>
-#include <linux/tty.h>
-#include <linux/kmod.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include <asm/ioctls.h>
-#include <linux/uaccess.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-
-#include <net/irda/irda_device.h>
-#include <net/irda/irlap.h>
-#include <net/irda/timer.h>
-#include <net/irda/wrapper.h>
-
-static void __irda_task_delete(struct irda_task *task);
-
-static hashbin_t *dongles = NULL;
-static hashbin_t *tasks = NULL;
-
-static void irda_task_timer_expired(void *data);
-
-int __init irda_device_init( void)
-{
-       dongles = hashbin_new(HB_NOLOCK);
-       if (dongles == NULL) {
-               net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n");
-               return -ENOMEM;
-       }
-       spin_lock_init(&dongles->hb_spinlock);
-
-       tasks = hashbin_new(HB_LOCK);
-       if (tasks == NULL) {
-               net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n");
-               hashbin_delete(dongles, NULL);
-               return -ENOMEM;
-       }
-
-       /* We no longer initialise the driver ourselves here, we let
-        * the system do it for us... - Jean II */
-
-       return 0;
-}
-
-static void leftover_dongle(void *arg)
-{
-       struct dongle_reg *reg = arg;
-       net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n",
-                            reg->type);
-}
-
-void irda_device_cleanup(void)
-{
-       hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete);
-
-       hashbin_delete(dongles, leftover_dongle);
-}
-
-/*
- * Function irda_device_set_media_busy (self, status)
- *
- *    Called when we have detected that another station is transmitting
- *    in contention mode.
- */
-void irda_device_set_media_busy(struct net_device *dev, int status)
-{
-       struct irlap_cb *self;
-
-       pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE");
-
-       self = (struct irlap_cb *) dev->atalk_ptr;
-
-       /* Some drivers may enable the receive interrupt before calling
-        * irlap_open(), or they may disable the receive interrupt
-        * after calling irlap_close().
-        * The IrDA stack is protected from this in irlap_driver_rcv().
-        * However, the driver calls directly the wrapper, that calls
-        * us directly. Make sure we protect ourselves.
-        * Jean II */
-       if (!self || self->magic != LAP_MAGIC)
-               return;
-
-       if (status) {
-               self->media_busy = TRUE;
-               if (status == SMALL)
-                       irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT);
-               else
-                       irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT);
-               pr_debug("Media busy!\n");
-       } else {
-               self->media_busy = FALSE;
-               irlap_stop_mbusy_timer(self);
-       }
-}
-EXPORT_SYMBOL(irda_device_set_media_busy);
-
-
-/*
- * Function irda_device_is_receiving (dev)
- *
- *    Check if the device driver is currently receiving data
- *
- */
-int irda_device_is_receiving(struct net_device *dev)
-{
-       struct if_irda_req req;
-       int ret;
-
-       if (!dev->netdev_ops->ndo_do_ioctl) {
-               net_err_ratelimited("%s: do_ioctl not impl. by device driver\n",
-                                   __func__);
-               return -1;
-       }
-
-       ret = (dev->netdev_ops->ndo_do_ioctl)(dev, (struct ifreq *) &req,
-                                             SIOCGRECEIVING);
-       if (ret < 0)
-               return ret;
-
-       return req.ifr_receiving;
-}
-
-static void __irda_task_delete(struct irda_task *task)
-{
-       del_timer(&task->timer);
-
-       kfree(task);
-}
-
-static void irda_task_delete(struct irda_task *task)
-{
-       /* Unregister task */
-       hashbin_remove(tasks, (long) task, NULL);
-
-       __irda_task_delete(task);
-}
-
-/*
- * Function irda_task_kick (task)
- *
- *    Tries to execute a task possible multiple times until the task is either
- *    finished, or askes for a timeout. When a task is finished, we do post
- *    processing, and notify the parent task, that is waiting for this task
- *    to complete.
- */
-static int irda_task_kick(struct irda_task *task)
-{
-       int finished = TRUE;
-       int count = 0;
-       int timeout;
-
-       IRDA_ASSERT(task != NULL, return -1;);
-       IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;);
-
-       /* Execute task until it's finished, or askes for a timeout */
-       do {
-               timeout = task->function(task);
-               if (count++ > 100) {
-                       net_err_ratelimited("%s: error in task handler!\n",
-                                           __func__);
-                       irda_task_delete(task);
-                       return TRUE;
-               }
-       } while ((timeout == 0) && (task->state != IRDA_TASK_DONE));
-
-       if (timeout < 0) {
-               net_err_ratelimited("%s: Error executing task!\n", __func__);
-               irda_task_delete(task);
-               return TRUE;
-       }
-
-       /* Check if we are finished */
-       if (task->state == IRDA_TASK_DONE) {
-               del_timer(&task->timer);
-
-               /* Do post processing */
-               if (task->finished)
-                       task->finished(task);
-
-               /* Notify parent */
-               if (task->parent) {
-                       /* Check if parent is waiting for us to complete */
-                       if (task->parent->state == IRDA_TASK_CHILD_WAIT) {
-                               task->parent->state = IRDA_TASK_CHILD_DONE;
-
-                               /* Stop timer now that we are here */
-                               del_timer(&task->parent->timer);
-
-                               /* Kick parent task */
-                               irda_task_kick(task->parent);
-                       }
-               }
-               irda_task_delete(task);
-       } else if (timeout > 0) {
-               irda_start_timer(&task->timer, timeout, (void *) task,
-                                irda_task_timer_expired);
-               finished = FALSE;
-       } else {
-               pr_debug("%s(), not finished, and no timeout!\n",
-                        __func__);
-               finished = FALSE;
-       }
-
-       return finished;
-}
-
-/*
- * Function irda_task_timer_expired (data)
- *
- *    Task time has expired. We now try to execute task (again), and restart
- *    the timer if the task has not finished yet
- */
-static void irda_task_timer_expired(void *data)
-{
-       struct irda_task *task;
-
-       task = data;
-
-       irda_task_kick(task);
-}
-
-/*
- * Function irda_device_setup (dev)
- *
- *    This function should be used by low level device drivers in a similar way
- *    as ether_setup() is used by normal network device drivers
- */
-static void irda_device_setup(struct net_device *dev)
-{
-       dev->hard_header_len = 0;
-       dev->addr_len        = LAP_ALEN;
-
-       dev->type            = ARPHRD_IRDA;
-       dev->tx_queue_len    = 8; /* Window size + 1 s-frame */
-
-       memset(dev->broadcast, 0xff, LAP_ALEN);
-
-       dev->mtu = 2048;
-       dev->flags = IFF_NOARP;
-}
-
-/*
- * Funciton  alloc_irdadev
- *     Allocates and sets up an IRDA device in a manner similar to
- *     alloc_etherdev.
- */
-struct net_device *alloc_irdadev(int sizeof_priv)
-{
-       return alloc_netdev(sizeof_priv, "irda%d", NET_NAME_UNKNOWN,
-                           irda_device_setup);
-}
-EXPORT_SYMBOL(alloc_irdadev);
-
-#ifdef CONFIG_ISA_DMA_API
-/*
- * Function setup_dma (idev, buffer, count, mode)
- *
- *    Setup the DMA channel. Commonly used by LPC FIR drivers
- *
- */
-void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode)
-{
-       unsigned long flags;
-
-       flags = claim_dma_lock();
-
-       disable_dma(channel);
-       clear_dma_ff(channel);
-       set_dma_mode(channel, mode);
-       set_dma_addr(channel, buffer);
-       set_dma_count(channel, count);
-       enable_dma(channel);
-
-       release_dma_lock(flags);
-}
-EXPORT_SYMBOL(irda_setup_dma);
-#endif
diff --git a/net/irda/iriap.c b/net/irda/iriap.c
deleted file mode 100644 (file)
index 1138eaf..0000000
+++ /dev/null
@@ -1,1085 +0,0 @@
-/*********************************************************************
- *
- * Filename:      iriap.c
- * Version:       0.8
- * Description:   Information Access Protocol (IAP)
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Thu Aug 21 00:02:07 1997
- * Modified at:   Sat Dec 25 16:42:42 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/iriap_event.h>
-#include <net/irda/iriap.h>
-
-/* FIXME: This one should go in irlmp.c */
-static const char *const ias_charset_types[] __maybe_unused = {
-       "CS_ASCII",
-       "CS_ISO_8859_1",
-       "CS_ISO_8859_2",
-       "CS_ISO_8859_3",
-       "CS_ISO_8859_4",
-       "CS_ISO_8859_5",
-       "CS_ISO_8859_6",
-       "CS_ISO_8859_7",
-       "CS_ISO_8859_8",
-       "CS_ISO_8859_9",
-       "CS_UNICODE"
-};
-
-static hashbin_t *iriap = NULL;
-static void *service_handle;
-
-static void __iriap_close(struct iriap_cb *self);
-static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode);
-static void iriap_disconnect_indication(void *instance, void *sap,
-                                       LM_REASON reason, struct sk_buff *skb);
-static void iriap_connect_indication(void *instance, void *sap,
-                                    struct qos_info *qos, __u32 max_sdu_size,
-                                    __u8 max_header_size,
-                                    struct sk_buff *skb);
-static void iriap_connect_confirm(void *instance, void *sap,
-                                 struct qos_info *qos,
-                                 __u32 max_sdu_size, __u8 max_header_size,
-                                 struct sk_buff *skb);
-static int iriap_data_indication(void *instance, void *sap,
-                                struct sk_buff *skb);
-
-static void iriap_watchdog_timer_expired(void *data);
-
-static inline void iriap_start_watchdog_timer(struct iriap_cb *self,
-                                             int timeout)
-{
-       irda_start_timer(&self->watchdog_timer, timeout, self,
-                        iriap_watchdog_timer_expired);
-}
-
-static struct lock_class_key irias_objects_key;
-
-/*
- * Function iriap_init (void)
- *
- *    Initializes the IrIAP layer, called by the module initialization code
- *    in irmod.c
- */
-int __init iriap_init(void)
-{
-       struct ias_object *obj;
-       struct iriap_cb *server;
-       __u8 oct_seq[6];
-       __u16 hints;
-
-       /* Allocate master array */
-       iriap = hashbin_new(HB_LOCK);
-       if (!iriap)
-               return -ENOMEM;
-
-       /* Object repository - defined in irias_object.c */
-       irias_objects = hashbin_new(HB_LOCK);
-       if (!irias_objects) {
-               net_warn_ratelimited("%s: Can't allocate irias_objects hashbin!\n",
-                                    __func__);
-               hashbin_delete(iriap, NULL);
-               return -ENOMEM;
-       }
-
-       lockdep_set_class_and_name(&irias_objects->hb_spinlock, &irias_objects_key,
-                                  "irias_objects");
-
-       /*
-        *  Register some default services for IrLMP
-        */
-       hints  = irlmp_service_to_hint(S_COMPUTER);
-       service_handle = irlmp_register_service(hints);
-
-       /* Register the Device object with LM-IAS */
-       obj = irias_new_object("Device", IAS_DEVICE_ID);
-       irias_add_string_attrib(obj, "DeviceName", "Linux", IAS_KERNEL_ATTR);
-
-       oct_seq[0] = 0x01;  /* Version 1 */
-       oct_seq[1] = 0x00;  /* IAS support bits */
-       oct_seq[2] = 0x00;  /* LM-MUX support bits */
-#ifdef CONFIG_IRDA_ULTRA
-       oct_seq[2] |= 0x04; /* Connectionless Data support */
-#endif
-       irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3,
-                               IAS_KERNEL_ATTR);
-       irias_insert_object(obj);
-
-       /*
-        *  Register server support with IrLMP so we can accept incoming
-        *  connections
-        */
-       server = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
-       if (!server) {
-               pr_debug("%s(), unable to open server\n", __func__);
-               return -1;
-       }
-       iriap_register_lsap(server, LSAP_IAS, IAS_SERVER);
-
-       return 0;
-}
-
-/*
- * Function iriap_cleanup (void)
- *
- *    Initializes the IrIAP layer, called by the module cleanup code in
- *    irmod.c
- */
-void iriap_cleanup(void)
-{
-       irlmp_unregister_service(service_handle);
-
-       hashbin_delete(iriap, (FREE_FUNC) __iriap_close);
-       hashbin_delete(irias_objects, (FREE_FUNC) __irias_delete_object);
-}
-
-/*
- * Function iriap_open (void)
- *
- *    Opens an instance of the IrIAP layer, and registers with IrLMP
- */
-struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv,
-                           CONFIRM_CALLBACK callback)
-{
-       struct iriap_cb *self;
-
-       self = kzalloc(sizeof(*self), GFP_ATOMIC);
-       if (!self)
-               return NULL;
-
-       /*
-        *  Initialize instance
-        */
-
-       self->magic = IAS_MAGIC;
-       self->mode = mode;
-       if (mode == IAS_CLIENT) {
-               if (iriap_register_lsap(self, slsap_sel, mode)) {
-                       kfree(self);
-                       return NULL;
-               }
-       }
-
-       self->confirm = callback;
-       self->priv = priv;
-
-       /* iriap_getvaluebyclass_request() will construct packets before
-        * we connect, so this must have a sane value... Jean II */
-       self->max_header_size = LMP_MAX_HEADER;
-
-       init_timer(&self->watchdog_timer);
-
-       hashbin_insert(iriap, (irda_queue_t *) self, (long) self, NULL);
-
-       /* Initialize state machines */
-       iriap_next_client_state(self, S_DISCONNECT);
-       iriap_next_call_state(self, S_MAKE_CALL);
-       iriap_next_server_state(self, R_DISCONNECT);
-       iriap_next_r_connect_state(self, R_WAITING);
-
-       return self;
-}
-EXPORT_SYMBOL(iriap_open);
-
-/*
- * Function __iriap_close (self)
- *
- *    Removes (deallocates) the IrIAP instance
- *
- */
-static void __iriap_close(struct iriap_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       del_timer(&self->watchdog_timer);
-
-       if (self->request_skb)
-               dev_kfree_skb(self->request_skb);
-
-       self->magic = 0;
-
-       kfree(self);
-}
-
-/*
- * Function iriap_close (void)
- *
- *    Closes IrIAP and deregisters with IrLMP
- */
-void iriap_close(struct iriap_cb *self)
-{
-       struct iriap_cb *entry;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       if (self->lsap) {
-               irlmp_close_lsap(self->lsap);
-               self->lsap = NULL;
-       }
-
-       entry = (struct iriap_cb *) hashbin_remove(iriap, (long) self, NULL);
-       IRDA_ASSERT(entry == self, return;);
-
-       __iriap_close(self);
-}
-EXPORT_SYMBOL(iriap_close);
-
-static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode)
-{
-       notify_t notify;
-
-       irda_notify_init(&notify);
-       notify.connect_confirm       = iriap_connect_confirm;
-       notify.connect_indication    = iriap_connect_indication;
-       notify.disconnect_indication = iriap_disconnect_indication;
-       notify.data_indication       = iriap_data_indication;
-       notify.instance = self;
-       if (mode == IAS_CLIENT)
-               strcpy(notify.name, "IrIAS cli");
-       else
-               strcpy(notify.name, "IrIAS srv");
-
-       self->lsap = irlmp_open_lsap(slsap_sel, &notify, 0);
-       if (self->lsap == NULL) {
-               net_err_ratelimited("%s: Unable to allocated LSAP!\n",
-                                   __func__);
-               return -1;
-       }
-       self->slsap_sel = self->lsap->slsap_sel;
-
-       return 0;
-}
-
-/*
- * Function iriap_disconnect_indication (handle, reason)
- *
- *    Got disconnect, so clean up everything associated with this connection
- *
- */
-static void iriap_disconnect_indication(void *instance, void *sap,
-                                       LM_REASON reason,
-                                       struct sk_buff *skb)
-{
-       struct iriap_cb *self;
-
-       pr_debug("%s(), reason=%s [%d]\n", __func__,
-                irlmp_reason_str(reason), reason);
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       IRDA_ASSERT(iriap != NULL, return;);
-
-       del_timer(&self->watchdog_timer);
-
-       /* Not needed */
-       if (skb)
-               dev_kfree_skb(skb);
-
-       if (self->mode == IAS_CLIENT) {
-               pr_debug("%s(), disconnect as client\n", __func__);
-
-
-               iriap_do_client_event(self, IAP_LM_DISCONNECT_INDICATION,
-                                     NULL);
-               /*
-                * Inform service user that the request failed by sending
-                * it a NULL value. Warning, the client might close us, so
-                * remember no to use self anymore after calling confirm
-                */
-               if (self->confirm)
-                       self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
-       } else {
-               pr_debug("%s(), disconnect as server\n", __func__);
-               iriap_do_server_event(self, IAP_LM_DISCONNECT_INDICATION,
-                                     NULL);
-               iriap_close(self);
-       }
-}
-
-/*
- * Function iriap_disconnect_request (handle)
- */
-static void iriap_disconnect_request(struct iriap_cb *self)
-{
-       struct sk_buff *tx_skb;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
-       if (tx_skb == NULL) {
-               pr_debug("%s(), Could not allocate an sk_buff of length %d\n",
-                        __func__, LMP_MAX_HEADER);
-               return;
-       }
-
-       /*
-        *  Reserve space for MUX control and LAP header
-        */
-       skb_reserve(tx_skb, LMP_MAX_HEADER);
-
-       irlmp_disconnect_request(self->lsap, tx_skb);
-}
-
-/*
- * Function iriap_getvaluebyclass (addr, name, attr)
- *
- *    Retrieve all values from attribute in all objects with given class
- *    name
- */
-int iriap_getvaluebyclass_request(struct iriap_cb *self,
-                                 __u32 saddr, __u32 daddr,
-                                 char *name, char *attr)
-{
-       struct sk_buff *tx_skb;
-       int name_len, attr_len, skb_len;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return -1;);
-
-       /* Client must supply the destination device address */
-       if (!daddr)
-               return -1;
-
-       self->daddr = daddr;
-       self->saddr = saddr;
-
-       /*
-        *  Save operation, so we know what the later indication is about
-        */
-       self->operation = GET_VALUE_BY_CLASS;
-
-       /* Give ourselves 10 secs to finish this operation */
-       iriap_start_watchdog_timer(self, 10*HZ);
-
-       name_len = strlen(name);        /* Up to IAS_MAX_CLASSNAME = 60 */
-       attr_len = strlen(attr);        /* Up to IAS_MAX_ATTRIBNAME = 60 */
-
-       skb_len = self->max_header_size+2+name_len+1+attr_len+4;
-       tx_skb = alloc_skb(skb_len, GFP_ATOMIC);
-       if (!tx_skb)
-               return -ENOMEM;
-
-       /* Reserve space for MUX and LAP header */
-       skb_reserve(tx_skb, self->max_header_size);
-       skb_put(tx_skb, 3+name_len+attr_len);
-       frame = tx_skb->data;
-
-       /* Build frame */
-       frame[0] = IAP_LST | GET_VALUE_BY_CLASS;
-       frame[1] = name_len;                       /* Insert length of name */
-       memcpy(frame+2, name, name_len);           /* Insert name */
-       frame[2+name_len] = attr_len;              /* Insert length of attr */
-       memcpy(frame+3+name_len, attr, attr_len);  /* Insert attr */
-
-       iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, tx_skb);
-
-       /* Drop reference count - see state_s_disconnect(). */
-       dev_kfree_skb(tx_skb);
-
-       return 0;
-}
-EXPORT_SYMBOL(iriap_getvaluebyclass_request);
-
-/*
- * Function iriap_getvaluebyclass_confirm (self, skb)
- *
- *    Got result from GetValueByClass command. Parse it and return result
- *    to service user.
- *
- */
-static void iriap_getvaluebyclass_confirm(struct iriap_cb *self,
-                                         struct sk_buff *skb)
-{
-       struct ias_value *value;
-       int charset;
-       __u32 value_len;
-       __u32 tmp_cpu32;
-       __u16 obj_id;
-       __u16 len;
-       __u8  type;
-       __u8 *fp;
-       int n;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       /* Initialize variables */
-       fp = skb->data;
-       n = 2;
-
-       /* Get length, MSB first */
-       len = get_unaligned_be16(fp + n);
-       n += 2;
-
-       pr_debug("%s(), len=%d\n", __func__, len);
-
-       /* Get object ID, MSB first */
-       obj_id = get_unaligned_be16(fp + n);
-       n += 2;
-
-       type = fp[n++];
-       pr_debug("%s(), Value type = %d\n", __func__, type);
-
-       switch (type) {
-       case IAS_INTEGER:
-               memcpy(&tmp_cpu32, fp+n, 4); n += 4;
-               be32_to_cpus(&tmp_cpu32);
-               value = irias_new_integer_value(tmp_cpu32);
-
-               /*  Legal values restricted to 0x01-0x6f, page 15 irttp */
-               pr_debug("%s(), lsap=%d\n", __func__, value->t.integer);
-               break;
-       case IAS_STRING:
-               charset = fp[n++];
-
-               switch (charset) {
-               case CS_ASCII:
-                       break;
-/*             case CS_ISO_8859_1: */
-/*             case CS_ISO_8859_2: */
-/*             case CS_ISO_8859_3: */
-/*             case CS_ISO_8859_4: */
-/*             case CS_ISO_8859_5: */
-/*             case CS_ISO_8859_6: */
-/*             case CS_ISO_8859_7: */
-/*             case CS_ISO_8859_8: */
-/*             case CS_ISO_8859_9: */
-/*             case CS_UNICODE: */
-               default:
-                       pr_debug("%s(), charset [%d] %s, not supported\n",
-                                __func__, charset,
-                                charset < ARRAY_SIZE(ias_charset_types) ?
-                                ias_charset_types[charset] :
-                                "(unknown)");
-
-                       /* Aborting, close connection! */
-                       iriap_disconnect_request(self);
-                       return;
-                       /* break; */
-               }
-               value_len = fp[n++];
-               pr_debug("%s(), strlen=%d\n", __func__, value_len);
-
-               /* Make sure the string is null-terminated */
-               if (n + value_len < skb->len)
-                       fp[n + value_len] = 0x00;
-               pr_debug("Got string %s\n", fp+n);
-
-               /* Will truncate to IAS_MAX_STRING bytes */
-               value = irias_new_string_value(fp+n);
-               break;
-       case IAS_OCT_SEQ:
-               value_len = get_unaligned_be16(fp + n);
-               n += 2;
-
-               /* Will truncate to IAS_MAX_OCTET_STRING bytes */
-               value = irias_new_octseq_value(fp+n, value_len);
-               break;
-       default:
-               value = irias_new_missing_value();
-               break;
-       }
-
-       /* Finished, close connection! */
-       iriap_disconnect_request(self);
-
-       /* Warning, the client might close us, so remember no to use self
-        * anymore after calling confirm
-        */
-       if (self->confirm)
-               self->confirm(IAS_SUCCESS, obj_id, value, self->priv);
-       else {
-               pr_debug("%s(), missing handler!\n", __func__);
-               irias_delete_value(value);
-       }
-}
-
-/*
- * Function iriap_getvaluebyclass_response ()
- *
- *    Send answer back to remote LM-IAS
- *
- */
-static void iriap_getvaluebyclass_response(struct iriap_cb *self,
-                                          __u16 obj_id,
-                                          __u8 ret_code,
-                                          struct ias_value *value)
-{
-       struct sk_buff *tx_skb;
-       int n;
-       __be32 tmp_be32;
-       __be16 tmp_be16;
-       __u8 *fp;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-       IRDA_ASSERT(value != NULL, return;);
-       IRDA_ASSERT(value->len <= 1024, return;);
-
-       /* Initialize variables */
-       n = 0;
-
-       /*
-        *  We must adjust the size of the response after the length of the
-        *  value. We add 32 bytes because of the 6 bytes for the frame and
-        *  max 5 bytes for the value coding.
-        */
-       tx_skb = alloc_skb(value->len + self->max_header_size + 32,
-                          GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       /* Reserve space for MUX and LAP header */
-       skb_reserve(tx_skb, self->max_header_size);
-       skb_put(tx_skb, 6);
-
-       fp = tx_skb->data;
-
-       /* Build frame */
-       fp[n++] = GET_VALUE_BY_CLASS | IAP_LST;
-       fp[n++] = ret_code;
-
-       /* Insert list length (MSB first) */
-       tmp_be16 = htons(0x0001);
-       memcpy(fp+n, &tmp_be16, 2);  n += 2;
-
-       /* Insert object identifier ( MSB first) */
-       tmp_be16 = cpu_to_be16(obj_id);
-       memcpy(fp+n, &tmp_be16, 2); n += 2;
-
-       switch (value->type) {
-       case IAS_STRING:
-               skb_put(tx_skb, 3 + value->len);
-               fp[n++] = value->type;
-               fp[n++] = 0; /* ASCII */
-               fp[n++] = (__u8) value->len;
-               memcpy(fp+n, value->t.string, value->len); n+=value->len;
-               break;
-       case IAS_INTEGER:
-               skb_put(tx_skb, 5);
-               fp[n++] = value->type;
-
-               tmp_be32 = cpu_to_be32(value->t.integer);
-               memcpy(fp+n, &tmp_be32, 4); n += 4;
-               break;
-       case IAS_OCT_SEQ:
-               skb_put(tx_skb, 3 + value->len);
-               fp[n++] = value->type;
-
-               tmp_be16 = cpu_to_be16(value->len);
-               memcpy(fp+n, &tmp_be16, 2); n += 2;
-               memcpy(fp+n, value->t.oct_seq, value->len); n+=value->len;
-               break;
-       case IAS_MISSING:
-               pr_debug("%s: sending IAS_MISSING\n", __func__);
-               skb_put(tx_skb, 1);
-               fp[n++] = value->type;
-               break;
-       default:
-               pr_debug("%s(), type not implemented!\n", __func__);
-               break;
-       }
-       iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb);
-
-       /* Drop reference count - see state_r_execute(). */
-       dev_kfree_skb(tx_skb);
-}
-
-/*
- * Function iriap_getvaluebyclass_indication (self, skb)
- *
- *    getvaluebyclass is requested from peer LM-IAS
- *
- */
-static void iriap_getvaluebyclass_indication(struct iriap_cb *self,
-                                            struct sk_buff *skb)
-{
-       struct ias_object *obj;
-       struct ias_attrib *attrib;
-       int name_len;
-       int attr_len;
-       char name[IAS_MAX_CLASSNAME + 1];       /* 60 bytes */
-       char attr[IAS_MAX_ATTRIBNAME + 1];      /* 60 bytes */
-       __u8 *fp;
-       int n;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       fp = skb->data;
-       n = 1;
-
-       name_len = fp[n++];
-
-       IRDA_ASSERT(name_len < IAS_MAX_CLASSNAME + 1, return;);
-
-       memcpy(name, fp+n, name_len); n+=name_len;
-       name[name_len] = '\0';
-
-       attr_len = fp[n++];
-
-       IRDA_ASSERT(attr_len < IAS_MAX_ATTRIBNAME + 1, return;);
-
-       memcpy(attr, fp+n, attr_len); n+=attr_len;
-       attr[attr_len] = '\0';
-
-       pr_debug("LM-IAS: Looking up %s: %s\n", name, attr);
-       obj = irias_find_object(name);
-
-       if (obj == NULL) {
-               pr_debug("LM-IAS: Object %s not found\n", name);
-               iriap_getvaluebyclass_response(self, 0x1235, IAS_CLASS_UNKNOWN,
-                                              &irias_missing);
-               return;
-       }
-       pr_debug("LM-IAS: found %s, id=%d\n", obj->name, obj->id);
-
-       attrib = irias_find_attrib(obj, attr);
-       if (attrib == NULL) {
-               pr_debug("LM-IAS: Attribute %s not found\n", attr);
-               iriap_getvaluebyclass_response(self, obj->id,
-                                              IAS_ATTRIB_UNKNOWN,
-                                              &irias_missing);
-               return;
-       }
-
-       /* We have a match; send the value.  */
-       iriap_getvaluebyclass_response(self, obj->id, IAS_SUCCESS,
-                                      attrib->value);
-}
-
-/*
- * Function iriap_send_ack (void)
- *
- *    Currently not used
- *
- */
-void iriap_send_ack(struct iriap_cb *self)
-{
-       struct sk_buff *tx_skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       tx_skb = alloc_skb(LMP_MAX_HEADER + 1, GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       /* Reserve space for MUX and LAP header */
-       skb_reserve(tx_skb, self->max_header_size);
-       skb_put(tx_skb, 1);
-       frame = tx_skb->data;
-
-       /* Build frame */
-       frame[0] = IAP_LST | IAP_ACK | self->operation;
-
-       irlmp_data_request(self->lsap, tx_skb);
-}
-
-void iriap_connect_request(struct iriap_cb *self)
-{
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       ret = irlmp_connect_request(self->lsap, LSAP_IAS,
-                                   self->saddr, self->daddr,
-                                   NULL, NULL);
-       if (ret < 0) {
-               pr_debug("%s(), connect failed!\n", __func__);
-               self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
-       }
-}
-
-/*
- * Function iriap_connect_confirm (handle, skb)
- *
- *    LSAP connection confirmed!
- *
- */
-static void iriap_connect_confirm(void *instance, void *sap,
-                                 struct qos_info *qos, __u32 max_seg_size,
-                                 __u8 max_header_size,
-                                 struct sk_buff *skb)
-{
-       struct iriap_cb *self;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       self->max_data_size = max_seg_size;
-       self->max_header_size = max_header_size;
-
-       del_timer(&self->watchdog_timer);
-
-       iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, skb);
-
-       /* Drop reference count - see state_s_make_call(). */
-       dev_kfree_skb(skb);
-}
-
-/*
- * Function iriap_connect_indication ( handle, skb)
- *
- *    Remote LM-IAS is requesting connection
- *
- */
-static void iriap_connect_indication(void *instance, void *sap,
-                                    struct qos_info *qos, __u32 max_seg_size,
-                                    __u8 max_header_size,
-                                    struct sk_buff *skb)
-{
-       struct iriap_cb *self, *new;
-
-       self = instance;
-
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(self != NULL, goto out;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;);
-
-       /* Start new server */
-       new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
-       if (!new) {
-               pr_debug("%s(), open failed\n", __func__);
-               goto out;
-       }
-
-       /* Now attach up the new "socket" */
-       new->lsap = irlmp_dup(self->lsap, new);
-       if (!new->lsap) {
-               pr_debug("%s(), dup failed!\n", __func__);
-               goto out;
-       }
-
-       new->max_data_size = max_seg_size;
-       new->max_header_size = max_header_size;
-
-       /* Clean up the original one to keep it in listen state */
-       irlmp_listen(self->lsap);
-
-       iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, skb);
-
-out:
-       /* Drop reference count - see state_r_disconnect(). */
-       dev_kfree_skb(skb);
-}
-
-/*
- * Function iriap_data_indication (handle, skb)
- *
- *    Receives data from connection identified by handle from IrLMP
- *
- */
-static int iriap_data_indication(void *instance, void *sap,
-                                struct sk_buff *skb)
-{
-       struct iriap_cb *self;
-       __u8  *frame;
-       __u8  opcode;
-
-       self = instance;
-
-       IRDA_ASSERT(skb != NULL, return 0;);
-       IRDA_ASSERT(self != NULL, goto out;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;);
-
-       frame = skb->data;
-
-       if (self->mode == IAS_SERVER) {
-               /* Call server */
-               pr_debug("%s(), Calling server!\n", __func__);
-               iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb);
-               goto out;
-       }
-       opcode = frame[0];
-       if (~opcode & IAP_LST) {
-               net_warn_ratelimited("%s:, IrIAS multiframe commands or results is not implemented yet!\n",
-                                    __func__);
-               goto out;
-       }
-
-       /* Check for ack frames since they don't contain any data */
-       if (opcode & IAP_ACK) {
-               pr_debug("%s() Got ack frame!\n", __func__);
-               goto out;
-       }
-
-       opcode &= ~IAP_LST; /* Mask away LST bit */
-
-       switch (opcode) {
-       case GET_INFO_BASE:
-               pr_debug("IrLMP GetInfoBaseDetails not implemented!\n");
-               break;
-       case GET_VALUE_BY_CLASS:
-               iriap_do_call_event(self, IAP_RECV_F_LST, NULL);
-
-               switch (frame[1]) {
-               case IAS_SUCCESS:
-                       iriap_getvaluebyclass_confirm(self, skb);
-                       break;
-               case IAS_CLASS_UNKNOWN:
-                       pr_debug("%s(), No such class!\n", __func__);
-                       /* Finished, close connection! */
-                       iriap_disconnect_request(self);
-
-                       /*
-                        * Warning, the client might close us, so remember
-                        * no to use self anymore after calling confirm
-                        */
-                       if (self->confirm)
-                               self->confirm(IAS_CLASS_UNKNOWN, 0, NULL,
-                                             self->priv);
-                       break;
-               case IAS_ATTRIB_UNKNOWN:
-                       pr_debug("%s(), No such attribute!\n", __func__);
-                       /* Finished, close connection! */
-                       iriap_disconnect_request(self);
-
-                       /*
-                        * Warning, the client might close us, so remember
-                        * no to use self anymore after calling confirm
-                        */
-                       if (self->confirm)
-                               self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL,
-                                             self->priv);
-                       break;
-               }
-               break;
-       default:
-               pr_debug("%s(), Unknown op-code: %02x\n", __func__,
-                        opcode);
-               break;
-       }
-
-out:
-       /* Cleanup - sub-calls will have done skb_get() as needed. */
-       dev_kfree_skb(skb);
-       return 0;
-}
-
-/*
- * Function iriap_call_indication (self, skb)
- *
- *    Received call to server from peer LM-IAS
- *
- */
-void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb)
-{
-       __u8 *fp;
-       __u8 opcode;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       fp = skb->data;
-
-       opcode = fp[0];
-       if (~opcode & 0x80) {
-               net_warn_ratelimited("%s: IrIAS multiframe commands or results is not implemented yet!\n",
-                                    __func__);
-               return;
-       }
-       opcode &= 0x7f; /* Mask away LST bit */
-
-       switch (opcode) {
-       case GET_INFO_BASE:
-               net_warn_ratelimited("%s: GetInfoBaseDetails not implemented yet!\n",
-                                    __func__);
-               break;
-       case GET_VALUE_BY_CLASS:
-               iriap_getvaluebyclass_indication(self, skb);
-               break;
-       }
-       /* skb will be cleaned up in iriap_data_indication */
-}
-
-/*
- * Function iriap_watchdog_timer_expired (data)
- *
- *    Query has taken too long time, so abort
- *
- */
-static void iriap_watchdog_timer_expired(void *data)
-{
-       struct iriap_cb *self = (struct iriap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       /* iriap_close(self); */
-}
-
-#ifdef CONFIG_PROC_FS
-
-static const char *const ias_value_types[] = {
-       "IAS_MISSING",
-       "IAS_INTEGER",
-       "IAS_OCT_SEQ",
-       "IAS_STRING"
-};
-
-static inline struct ias_object *irias_seq_idx(loff_t pos)
-{
-       struct ias_object *obj;
-
-       for (obj = (struct ias_object *) hashbin_get_first(irias_objects);
-            obj; obj = (struct ias_object *) hashbin_get_next(irias_objects)) {
-               if (pos-- == 0)
-                       break;
-       }
-
-       return obj;
-}
-
-static void *irias_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       spin_lock_irq(&irias_objects->hb_spinlock);
-
-       return *pos ? irias_seq_idx(*pos - 1) : SEQ_START_TOKEN;
-}
-
-static void *irias_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-       ++*pos;
-
-       return (v == SEQ_START_TOKEN)
-               ? (void *) hashbin_get_first(irias_objects)
-               : (void *) hashbin_get_next(irias_objects);
-}
-
-static void irias_seq_stop(struct seq_file *seq, void *v)
-{
-       spin_unlock_irq(&irias_objects->hb_spinlock);
-}
-
-static int irias_seq_show(struct seq_file *seq, void *v)
-{
-       if (v == SEQ_START_TOKEN)
-               seq_puts(seq, "LM-IAS Objects:\n");
-       else {
-               struct ias_object *obj = v;
-               struct ias_attrib *attrib;
-
-               IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -EINVAL;);
-
-               seq_printf(seq, "name: %s, id=%d\n",
-                          obj->name, obj->id);
-
-               /* Careful for priority inversions here !
-                * All other uses of attrib spinlock are independent of
-                * the object spinlock, so we are safe. Jean II */
-               spin_lock(&obj->attribs->hb_spinlock);
-
-               /* List all attributes for this object */
-               for (attrib = (struct ias_attrib *) hashbin_get_first(obj->attribs);
-                    attrib != NULL;
-                    attrib = (struct ias_attrib *) hashbin_get_next(obj->attribs)) {
-
-                       IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC,
-                                   goto outloop; );
-
-                       seq_printf(seq, " - Attribute name: \"%s\", ",
-                                  attrib->name);
-                       seq_printf(seq, "value[%s]: ",
-                                  ias_value_types[attrib->value->type]);
-
-                       switch (attrib->value->type) {
-                       case IAS_INTEGER:
-                               seq_printf(seq, "%d\n",
-                                          attrib->value->t.integer);
-                               break;
-                       case IAS_STRING:
-                               seq_printf(seq, "\"%s\"\n",
-                                          attrib->value->t.string);
-                               break;
-                       case IAS_OCT_SEQ:
-                               seq_printf(seq, "octet sequence (%d bytes)\n",
-                                          attrib->value->len);
-                               break;
-                       case IAS_MISSING:
-                               seq_puts(seq, "missing\n");
-                               break;
-                       default:
-                               seq_printf(seq, "type %d?\n",
-                                          attrib->value->type);
-                       }
-                       seq_putc(seq, '\n');
-
-               }
-       IRDA_ASSERT_LABEL(outloop:)
-               spin_unlock(&obj->attribs->hb_spinlock);
-       }
-
-       return 0;
-}
-
-static const struct seq_operations irias_seq_ops = {
-       .start  = irias_seq_start,
-       .next   = irias_seq_next,
-       .stop   = irias_seq_stop,
-       .show   = irias_seq_show,
-};
-
-static int irias_seq_open(struct inode *inode, struct file *file)
-{
-       IRDA_ASSERT( irias_objects != NULL, return -EINVAL;);
-
-       return seq_open(file, &irias_seq_ops);
-}
-
-const struct file_operations irias_seq_fops = {
-       .owner          = THIS_MODULE,
-       .open           = irias_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release,
-};
-
-#endif /* PROC_FS */
diff --git a/net/irda/iriap_event.c b/net/irda/iriap_event.c
deleted file mode 100644 (file)
index e6098b2..0000000
+++ /dev/null
@@ -1,496 +0,0 @@
-/*********************************************************************
- *
- * Filename:      iriap_event.c
- * Version:       0.1
- * Description:   IAP Finite State Machine
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Thu Aug 21 00:02:07 1997
- * Modified at:   Wed Mar  1 11:28:34 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1997, 1999-2000 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/iriap_event.h>
-
-static void state_s_disconnect   (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_s_connecting   (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_s_call         (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-
-static void state_s_make_call    (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_s_calling      (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_s_outstanding  (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_s_replying     (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_s_wait_active  (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-
-static void state_r_disconnect   (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_r_call         (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_r_waiting      (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_r_wait_active  (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_r_receiving    (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_r_execute      (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-static void state_r_returning    (struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb);
-
-static void (*iriap_state[])(struct iriap_cb *self, IRIAP_EVENT event,
-                            struct sk_buff *skb) = {
-       /* Client FSM */
-       state_s_disconnect,
-       state_s_connecting,
-       state_s_call,
-
-       /* S-Call FSM */
-       state_s_make_call,
-       state_s_calling,
-       state_s_outstanding,
-       state_s_replying,
-       state_s_wait_for_call,
-       state_s_wait_active,
-
-       /* Server FSM */
-       state_r_disconnect,
-       state_r_call,
-
-       /* R-Connect FSM */
-       state_r_waiting,
-       state_r_wait_active,
-       state_r_receiving,
-       state_r_execute,
-       state_r_returning,
-};
-
-void iriap_next_client_state(struct iriap_cb *self, IRIAP_STATE state)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       self->client_state = state;
-}
-
-void iriap_next_call_state(struct iriap_cb *self, IRIAP_STATE state)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       self->call_state = state;
-}
-
-void iriap_next_server_state(struct iriap_cb *self, IRIAP_STATE state)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       self->server_state = state;
-}
-
-void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       self->r_connect_state = state;
-}
-
-void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event,
-                          struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       (*iriap_state[ self->client_state]) (self, event, skb);
-}
-
-void iriap_do_call_event(struct iriap_cb *self, IRIAP_EVENT event,
-                        struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       (*iriap_state[ self->call_state]) (self, event, skb);
-}
-
-void iriap_do_server_event(struct iriap_cb *self, IRIAP_EVENT event,
-                          struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       (*iriap_state[ self->server_state]) (self, event, skb);
-}
-
-void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event,
-                             struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       (*iriap_state[ self->r_connect_state]) (self, event, skb);
-}
-
-
-/*
- * Function state_s_disconnect (event, skb)
- *
- *    S-Disconnect, The device has no LSAP connection to a particular
- *    remote device.
- */
-static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
-                              struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       switch (event) {
-       case IAP_CALL_REQUEST_GVBC:
-               iriap_next_client_state(self, S_CONNECTING);
-               IRDA_ASSERT(self->request_skb == NULL, return;);
-               /* Don't forget to refcount it -
-                * see iriap_getvaluebyclass_request(). */
-               skb_get(skb);
-               self->request_skb = skb;
-               iriap_connect_request(self);
-               break;
-       case IAP_LM_DISCONNECT_INDICATION:
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__, event);
-               break;
-       }
-}
-
-/*
- * Function state_s_connecting (self, event, skb)
- *
- *    S-Connecting
- *
- */
-static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event,
-                              struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       switch (event) {
-       case IAP_LM_CONNECT_CONFIRM:
-               /*
-                *  Jump to S-Call FSM
-                */
-               iriap_do_call_event(self, IAP_CALL_REQUEST, skb);
-               /* iriap_call_request(self, 0,0,0); */
-               iriap_next_client_state(self, S_CALL);
-               break;
-       case IAP_LM_DISCONNECT_INDICATION:
-               /* Abort calls */
-               iriap_next_call_state(self, S_MAKE_CALL);
-               iriap_next_client_state(self, S_DISCONNECT);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__, event);
-               break;
-       }
-}
-
-/*
- * Function state_s_call (self, event, skb)
- *
- *    S-Call, The device can process calls to a specific remote
- *    device. Whenever the LSAP connection is disconnected, this state
- *    catches that event and clears up
- */
-static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event,
-                        struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-
-       switch (event) {
-       case IAP_LM_DISCONNECT_INDICATION:
-               /* Abort calls */
-               iriap_next_call_state(self, S_MAKE_CALL);
-               iriap_next_client_state(self, S_DISCONNECT);
-               break;
-       default:
-               pr_debug("state_s_call: Unknown event %d\n", event);
-               break;
-       }
-}
-
-/*
- * Function state_s_make_call (event, skb)
- *
- *    S-Make-Call
- *
- */
-static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event,
-                             struct sk_buff *skb)
-{
-       struct sk_buff *tx_skb;
-
-       IRDA_ASSERT(self != NULL, return;);
-
-       switch (event) {
-       case IAP_CALL_REQUEST:
-               /* Already refcounted - see state_s_disconnect() */
-               tx_skb = self->request_skb;
-               self->request_skb = NULL;
-
-               irlmp_data_request(self->lsap, tx_skb);
-               iriap_next_call_state(self, S_OUTSTANDING);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__, event);
-               break;
-       }
-}
-
-/*
- * Function state_s_calling (event, skb)
- *
- *    S-Calling
- *
- */
-static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event,
-                           struct sk_buff *skb)
-{
-       pr_debug("%s(), Not implemented\n", __func__);
-}
-
-/*
- * Function state_s_outstanding (event, skb)
- *
- *    S-Outstanding, The device is waiting for a response to a command
- *
- */
-static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event,
-                               struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-
-       switch (event) {
-       case IAP_RECV_F_LST:
-               /*iriap_send_ack(self);*/
-               /*LM_Idle_request(idle); */
-
-               iriap_next_call_state(self, S_WAIT_FOR_CALL);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__, event);
-               break;
-       }
-}
-
-/*
- * Function state_s_replying (event, skb)
- *
- *    S-Replying, The device is collecting a multiple part response
- */
-static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event,
-                            struct sk_buff *skb)
-{
-       pr_debug("%s(), Not implemented\n", __func__);
-}
-
-/*
- * Function state_s_wait_for_call (event, skb)
- *
- *    S-Wait-for-Call
- *
- */
-static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
-                                 struct sk_buff *skb)
-{
-       pr_debug("%s(), Not implemented\n", __func__);
-}
-
-
-/*
- * Function state_s_wait_active (event, skb)
- *
- *    S-Wait-Active
- *
- */
-static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
-                               struct sk_buff *skb)
-{
-       pr_debug("%s(), Not implemented\n", __func__);
-}
-
-/**************************************************************************
- *
- *  Server FSM
- *
- **************************************************************************/
-
-/*
- * Function state_r_disconnect (self, event, skb)
- *
- *    LM-IAS server is disconnected (not processing any requests!)
- *
- */
-static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
-                              struct sk_buff *skb)
-{
-       struct sk_buff *tx_skb;
-
-       switch (event) {
-       case IAP_LM_CONNECT_INDICATION:
-               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
-               if (tx_skb == NULL)
-                       return;
-
-               /* Reserve space for MUX_CONTROL and LAP header */
-               skb_reserve(tx_skb, LMP_MAX_HEADER);
-
-               irlmp_connect_response(self->lsap, tx_skb);
-               /*LM_Idle_request(idle); */
-
-               iriap_next_server_state(self, R_CALL);
-
-               /*
-                *  Jump to R-Connect FSM, we skip R-Waiting since we do not
-                *  care about LM_Idle_request()!
-                */
-               iriap_next_r_connect_state(self, R_RECEIVING);
-               break;
-       default:
-               pr_debug("%s(), unknown event %d\n", __func__, event);
-               break;
-       }
-}
-
-/*
- * Function state_r_call (self, event, skb)
- */
-static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event,
-                        struct sk_buff *skb)
-{
-       switch (event) {
-       case IAP_LM_DISCONNECT_INDICATION:
-               /* Abort call */
-               iriap_next_server_state(self, R_DISCONNECT);
-               iriap_next_r_connect_state(self, R_WAITING);
-               break;
-       default:
-               pr_debug("%s(), unknown event!\n", __func__);
-               break;
-       }
-}
-
-/*
- *  R-Connect FSM
- */
-
-/*
- * Function state_r_waiting (self, event, skb)
- */
-static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event,
-                           struct sk_buff *skb)
-{
-       pr_debug("%s(), Not implemented\n", __func__);
-}
-
-static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
-                               struct sk_buff *skb)
-{
-       pr_debug("%s(), Not implemented\n", __func__);
-}
-
-/*
- * Function state_r_receiving (self, event, skb)
- *
- *    We are receiving a command
- *
- */
-static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event,
-                             struct sk_buff *skb)
-{
-       switch (event) {
-       case IAP_RECV_F_LST:
-               iriap_next_r_connect_state(self, R_EXECUTE);
-
-               iriap_call_indication(self, skb);
-               break;
-       default:
-               pr_debug("%s(), unknown event!\n", __func__);
-               break;
-       }
-}
-
-/*
- * Function state_r_execute (self, event, skb)
- *
- *    The server is processing the request
- *
- */
-static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
-                           struct sk_buff *skb)
-{
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
-
-       switch (event) {
-       case IAP_CALL_RESPONSE:
-               /*
-                *  Since we don't implement the Waiting state, we return
-                *  to state Receiving instead, DB.
-                */
-               iriap_next_r_connect_state(self, R_RECEIVING);
-
-               /* Don't forget to refcount it - see
-                * iriap_getvaluebyclass_response(). */
-               skb_get(skb);
-
-               irlmp_data_request(self->lsap, skb);
-               break;
-       default:
-               pr_debug("%s(), unknown event!\n", __func__);
-               break;
-       }
-}
-
-static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event,
-                             struct sk_buff *skb)
-{
-       pr_debug("%s(), event=%d\n", __func__, event);
-
-       switch (event) {
-       case IAP_RECV_F_LST:
-               break;
-       default:
-               break;
-       }
-}
diff --git a/net/irda/irias_object.c b/net/irda/irias_object.c
deleted file mode 100644 (file)
index 53b86d0..0000000
+++ /dev/null
@@ -1,555 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irias_object.c
- * Version:       0.3
- * Description:   IAS object database and functions
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Thu Oct  1 22:50:04 1998
- * Modified at:   Wed Dec 15 11:23:16 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/socket.h>
-#include <linux/module.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irias_object.h>
-
-hashbin_t *irias_objects;
-
-/*
- *  Used when a missing value needs to be returned
- */
-struct ias_value irias_missing = { IAS_MISSING, 0, 0, 0, {0}};
-
-
-/*
- * Function ias_new_object (name, id)
- *
- *    Create a new IAS object
- *
- */
-struct ias_object *irias_new_object( char *name, int id)
-{
-       struct ias_object *obj;
-
-       obj = kzalloc(sizeof(struct ias_object), GFP_ATOMIC);
-       if (obj == NULL) {
-               net_warn_ratelimited("%s(), Unable to allocate object!\n",
-                                    __func__);
-               return NULL;
-       }
-
-       obj->magic = IAS_OBJECT_MAGIC;
-       obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC);
-       if (!obj->name) {
-               net_warn_ratelimited("%s(), Unable to allocate name!\n",
-                                    __func__);
-               kfree(obj);
-               return NULL;
-       }
-       obj->id = id;
-
-       /* Locking notes : the attrib spinlock has lower precendence
-        * than the objects spinlock. Never grap the objects spinlock
-        * while holding any attrib spinlock (risk of deadlock). Jean II */
-       obj->attribs = hashbin_new(HB_LOCK);
-
-       if (obj->attribs == NULL) {
-               net_warn_ratelimited("%s(), Unable to allocate attribs!\n",
-                                    __func__);
-               kfree(obj->name);
-               kfree(obj);
-               return NULL;
-       }
-
-       return obj;
-}
-EXPORT_SYMBOL(irias_new_object);
-
-/*
- * Function irias_delete_attrib (attrib)
- *
- *    Delete given attribute and deallocate all its memory
- *
- */
-static void __irias_delete_attrib(struct ias_attrib *attrib)
-{
-       IRDA_ASSERT(attrib != NULL, return;);
-       IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
-
-       kfree(attrib->name);
-
-       irias_delete_value(attrib->value);
-       attrib->magic = ~IAS_ATTRIB_MAGIC;
-
-       kfree(attrib);
-}
-
-void __irias_delete_object(struct ias_object *obj)
-{
-       IRDA_ASSERT(obj != NULL, return;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
-       kfree(obj->name);
-
-       hashbin_delete(obj->attribs, (FREE_FUNC) __irias_delete_attrib);
-
-       obj->magic = ~IAS_OBJECT_MAGIC;
-
-       kfree(obj);
-}
-
-/*
- * Function irias_delete_object (obj)
- *
- *    Remove object from hashbin and deallocate all attributes associated with
- *    with this object and the object itself
- *
- */
-int irias_delete_object(struct ias_object *obj)
-{
-       struct ias_object *node;
-
-       IRDA_ASSERT(obj != NULL, return -1;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
-
-       /* Remove from list */
-       node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj);
-       if (!node)
-               pr_debug("%s(), object already removed!\n",
-                        __func__);
-
-       /* Destroy */
-       __irias_delete_object(obj);
-
-       return 0;
-}
-EXPORT_SYMBOL(irias_delete_object);
-
-/*
- * Function irias_delete_attrib (obj)
- *
- *    Remove attribute from hashbin and, if it was the last attribute of
- *    the object, remove the object as well.
- *
- */
-int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib,
-                       int cleanobject)
-{
-       struct ias_attrib *node;
-
-       IRDA_ASSERT(obj != NULL, return -1;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
-       IRDA_ASSERT(attrib != NULL, return -1;);
-
-       /* Remove attribute from object */
-       node = hashbin_remove_this(obj->attribs, (irda_queue_t *) attrib);
-       if (!node)
-               return 0; /* Already removed or non-existent */
-
-       /* Deallocate attribute */
-       __irias_delete_attrib(node);
-
-       /* Check if object has still some attributes, destroy it if none.
-        * At first glance, this look dangerous, as the kernel reference
-        * various IAS objects. However, we only use this function on
-        * user attributes, not kernel attributes, so there is no risk
-        * of deleting a kernel object this way. Jean II */
-       node = (struct ias_attrib *) hashbin_get_first(obj->attribs);
-       if (cleanobject && !node)
-               irias_delete_object(obj);
-
-       return 0;
-}
-
-/*
- * Function irias_insert_object (obj)
- *
- *    Insert an object into the LM-IAS database
- *
- */
-void irias_insert_object(struct ias_object *obj)
-{
-       IRDA_ASSERT(obj != NULL, return;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
-       hashbin_insert(irias_objects, (irda_queue_t *) obj, 0, obj->name);
-}
-EXPORT_SYMBOL(irias_insert_object);
-
-/*
- * Function irias_find_object (name)
- *
- *    Find object with given name
- *
- */
-struct ias_object *irias_find_object(char *name)
-{
-       IRDA_ASSERT(name != NULL, return NULL;);
-
-       /* Unsafe (locking), object might change */
-       return hashbin_lock_find(irias_objects, 0, name);
-}
-EXPORT_SYMBOL(irias_find_object);
-
-/*
- * Function irias_find_attrib (obj, name)
- *
- *    Find named attribute in object
- *
- */
-struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name)
-{
-       struct ias_attrib *attrib;
-
-       IRDA_ASSERT(obj != NULL, return NULL;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;);
-       IRDA_ASSERT(name != NULL, return NULL;);
-
-       attrib = hashbin_lock_find(obj->attribs, 0, name);
-       if (attrib == NULL)
-               return NULL;
-
-       /* Unsafe (locking), attrib might change */
-       return attrib;
-}
-
-/*
- * Function irias_add_attribute (obj, attrib)
- *
- *    Add attribute to object
- *
- */
-static void irias_add_attrib(struct ias_object *obj, struct ias_attrib *attrib,
-                            int owner)
-{
-       IRDA_ASSERT(obj != NULL, return;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
-       IRDA_ASSERT(attrib != NULL, return;);
-       IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
-
-       /* Set if attrib is owned by kernel or user space */
-       attrib->value->owner = owner;
-
-       hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name);
-}
-
-/*
- * Function irias_object_change_attribute (obj_name, attrib_name, new_value)
- *
- *    Change the value of an objects attribute.
- *
- */
-int irias_object_change_attribute(char *obj_name, char *attrib_name,
-                                 struct ias_value *new_value)
-{
-       struct ias_object *obj;
-       struct ias_attrib *attrib;
-       unsigned long flags;
-
-       /* Find object */
-       obj = hashbin_lock_find(irias_objects, 0, obj_name);
-       if (obj == NULL) {
-               net_warn_ratelimited("%s: Unable to find object: %s\n",
-                                    __func__, obj_name);
-               return -1;
-       }
-
-       /* Slightly unsafe (obj might get removed under us) */
-       spin_lock_irqsave(&obj->attribs->hb_spinlock, flags);
-
-       /* Find attribute */
-       attrib = hashbin_find(obj->attribs, 0, attrib_name);
-       if (attrib == NULL) {
-               net_warn_ratelimited("%s: Unable to find attribute: %s\n",
-                                    __func__, attrib_name);
-               spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
-               return -1;
-       }
-
-       if ( attrib->value->type != new_value->type) {
-               pr_debug("%s(), changing value type not allowed!\n",
-                        __func__);
-               spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
-               return -1;
-       }
-
-       /* Delete old value */
-       irias_delete_value(attrib->value);
-
-       /* Insert new value */
-       attrib->value = new_value;
-
-       /* Success */
-       spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
-       return 0;
-}
-EXPORT_SYMBOL(irias_object_change_attribute);
-
-/*
- * Function irias_object_add_integer_attrib (obj, name, value)
- *
- *    Add an integer attribute to an LM-IAS object
- *
- */
-void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
-                             int owner)
-{
-       struct ias_attrib *attrib;
-
-       IRDA_ASSERT(obj != NULL, return;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-       IRDA_ASSERT(name != NULL, return;);
-
-       attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
-       if (attrib == NULL) {
-               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
-                                    __func__);
-               return;
-       }
-
-       attrib->magic = IAS_ATTRIB_MAGIC;
-       attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
-
-       /* Insert value */
-       attrib->value = irias_new_integer_value(value);
-       if (!attrib->name || !attrib->value) {
-               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
-                                    __func__);
-               if (attrib->value)
-                       irias_delete_value(attrib->value);
-               kfree(attrib->name);
-               kfree(attrib);
-               return;
-       }
-
-       irias_add_attrib(obj, attrib, owner);
-}
-EXPORT_SYMBOL(irias_add_integer_attrib);
-
- /*
- * Function irias_add_octseq_attrib (obj, name, octet_seq, len)
- *
- *    Add a octet sequence attribute to an LM-IAS object
- *
- */
-
-void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
-                            int len, int owner)
-{
-       struct ias_attrib *attrib;
-
-       IRDA_ASSERT(obj != NULL, return;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
-       IRDA_ASSERT(name != NULL, return;);
-       IRDA_ASSERT(octets != NULL, return;);
-
-       attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
-       if (attrib == NULL) {
-               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
-                                    __func__);
-               return;
-       }
-
-       attrib->magic = IAS_ATTRIB_MAGIC;
-       attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
-
-       attrib->value = irias_new_octseq_value( octets, len);
-       if (!attrib->name || !attrib->value) {
-               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
-                                    __func__);
-               if (attrib->value)
-                       irias_delete_value(attrib->value);
-               kfree(attrib->name);
-               kfree(attrib);
-               return;
-       }
-
-       irias_add_attrib(obj, attrib, owner);
-}
-EXPORT_SYMBOL(irias_add_octseq_attrib);
-
-/*
- * Function irias_object_add_string_attrib (obj, string)
- *
- *    Add a string attribute to an LM-IAS object
- *
- */
-void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
-                            int owner)
-{
-       struct ias_attrib *attrib;
-
-       IRDA_ASSERT(obj != NULL, return;);
-       IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
-
-       IRDA_ASSERT(name != NULL, return;);
-       IRDA_ASSERT(value != NULL, return;);
-
-       attrib = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC);
-       if (attrib == NULL) {
-               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
-                                    __func__);
-               return;
-       }
-
-       attrib->magic = IAS_ATTRIB_MAGIC;
-       attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC);
-
-       attrib->value = irias_new_string_value(value);
-       if (!attrib->name || !attrib->value) {
-               net_warn_ratelimited("%s: Unable to allocate attribute!\n",
-                                    __func__);
-               if (attrib->value)
-                       irias_delete_value(attrib->value);
-               kfree(attrib->name);
-               kfree(attrib);
-               return;
-       }
-
-       irias_add_attrib(obj, attrib, owner);
-}
-EXPORT_SYMBOL(irias_add_string_attrib);
-
-/*
- * Function irias_new_integer_value (integer)
- *
- *    Create new IAS integer value
- *
- */
-struct ias_value *irias_new_integer_value(int integer)
-{
-       struct ias_value *value;
-
-       value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
-       if (value == NULL)
-               return NULL;
-
-       value->type = IAS_INTEGER;
-       value->len = 4;
-       value->t.integer = integer;
-
-       return value;
-}
-EXPORT_SYMBOL(irias_new_integer_value);
-
-/*
- * Function irias_new_string_value (string)
- *
- *    Create new IAS string value
- *
- * Per IrLMP 1.1, 4.3.3.2, strings are up to 256 chars - Jean II
- */
-struct ias_value *irias_new_string_value(char *string)
-{
-       struct ias_value *value;
-
-       value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
-       if (value == NULL)
-               return NULL;
-
-       value->type = IAS_STRING;
-       value->charset = CS_ASCII;
-       value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC);
-       if (!value->t.string) {
-               net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__);
-               kfree(value);
-               return NULL;
-       }
-
-       value->len = strlen(value->t.string);
-
-       return value;
-}
-
-/*
- * Function irias_new_octseq_value (octets, len)
- *
- *    Create new IAS octet-sequence value
- *
- * Per IrLMP 1.1, 4.3.3.2, octet-sequence are up to 1024 bytes - Jean II
- */
-struct ias_value *irias_new_octseq_value(__u8 *octseq , int len)
-{
-       struct ias_value *value;
-
-       value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
-       if (value == NULL)
-               return NULL;
-
-       value->type = IAS_OCT_SEQ;
-       /* Check length */
-       if(len > IAS_MAX_OCTET_STRING)
-               len = IAS_MAX_OCTET_STRING;
-       value->len = len;
-
-       value->t.oct_seq = kmemdup(octseq, len, GFP_ATOMIC);
-       if (value->t.oct_seq == NULL){
-               net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__);
-               kfree(value);
-               return NULL;
-       }
-       return value;
-}
-
-struct ias_value *irias_new_missing_value(void)
-{
-       struct ias_value *value;
-
-       value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
-       if (value == NULL)
-               return NULL;
-
-       value->type = IAS_MISSING;
-
-       return value;
-}
-
-/*
- * Function irias_delete_value (value)
- *
- *    Delete IAS value
- *
- */
-void irias_delete_value(struct ias_value *value)
-{
-       IRDA_ASSERT(value != NULL, return;);
-
-       switch (value->type) {
-       case IAS_INTEGER: /* Fallthrough */
-       case IAS_MISSING:
-               /* No need to deallocate */
-               break;
-       case IAS_STRING:
-               /* Deallocate string */
-               kfree(value->t.string);
-               break;
-       case IAS_OCT_SEQ:
-               /* Deallocate byte stream */
-                kfree(value->t.oct_seq);
-                break;
-       default:
-               pr_debug("%s(), Unknown value type!\n", __func__);
-               break;
-       }
-       kfree(value);
-}
-EXPORT_SYMBOL(irias_delete_value);
diff --git a/net/irda/irlan/Kconfig b/net/irda/irlan/Kconfig
deleted file mode 100644 (file)
index 951abc2..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-config IRLAN
-       tristate "IrLAN protocol"
-       depends on IRDA
-       help
-         Say Y here if you want to build support for the IrLAN protocol.
-         To compile it as a module, choose M here: the module will be called
-         irlan.  IrLAN emulates an Ethernet and makes it possible to put up
-         a wireless LAN using infrared beams.
-
-         The IrLAN protocol can be used to talk with infrared access points
-         like the HP NetbeamIR, or the ESI JetEye NET.  You can also connect
-         to another Linux machine running the IrLAN protocol for ad-hoc
-         networking!
-
diff --git a/net/irda/irlan/Makefile b/net/irda/irlan/Makefile
deleted file mode 100644 (file)
index 94eefbc..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Makefile for the Linux IrDA IrLAN protocol layer.
-#
-
-obj-$(CONFIG_IRLAN) += irlan.o
-
-irlan-y := irlan_common.o irlan_eth.o irlan_event.o irlan_client.o irlan_provider.o irlan_filter.o irlan_provider_event.o irlan_client_event.o
diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c
deleted file mode 100644 (file)
index c5837a4..0000000
+++ /dev/null
@@ -1,559 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlan_client.c
- * Version:       0.9
- * Description:   IrDA LAN Access Protocol (IrLAN) Client
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Tue Dec 14 15:47:02 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
- *                slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
- *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- *
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-#include <linux/bitops.h>
-#include <net/arp.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/iriap.h>
-#include <net/irda/timer.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_event.h>
-#include <net/irda/irlan_eth.h>
-#include <net/irda/irlan_provider.h>
-#include <net/irda/irlan_client.h>
-
-#undef CONFIG_IRLAN_GRATUITOUS_ARP
-
-static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
-                                                   LM_REASON reason,
-                                                   struct sk_buff *);
-static int irlan_client_ctrl_data_indication(void *instance, void *sap,
-                                            struct sk_buff *skb);
-static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
-                                             struct qos_info *qos,
-                                             __u32 max_sdu_size,
-                                             __u8 max_header_size,
-                                             struct sk_buff *);
-static void irlan_check_response_param(struct irlan_cb *self, char *param,
-                                      char *value, int val_len);
-static void irlan_client_open_ctrl_tsap(struct irlan_cb *self);
-
-static void irlan_client_kick_timer_expired(void *data)
-{
-       struct irlan_cb *self = (struct irlan_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /*
-        * If we are in peer mode, the client may not have got the discovery
-        * indication it needs to make progress. If the client is still in
-        * IDLE state, we must kick it to, but only if the provider is not IDLE
-        */
-       if ((self->provider.access_type == ACCESS_PEER) &&
-           (self->client.state == IRLAN_IDLE) &&
-           (self->provider.state != IRLAN_IDLE)) {
-               irlan_client_wakeup(self, self->saddr, self->daddr);
-       }
-}
-
-static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout)
-{
-       irda_start_timer(&self->client.kick_timer, timeout, (void *) self,
-                        irlan_client_kick_timer_expired);
-}
-
-/*
- * Function irlan_client_wakeup (self, saddr, daddr)
- *
- *    Wake up client
- *
- */
-void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /*
-        * Check if we are already awake, or if we are a provider in direct
-        * mode (in that case we must leave the client idle
-        */
-       if ((self->client.state != IRLAN_IDLE) ||
-           (self->provider.access_type == ACCESS_DIRECT))
-       {
-               pr_debug("%s(), already awake!\n", __func__);
-                       return;
-       }
-
-       /* Addresses may have changed! */
-       self->saddr = saddr;
-       self->daddr = daddr;
-
-       if (self->disconnect_reason == LM_USER_REQUEST) {
-               pr_debug("%s(), still stopped by user\n", __func__);
-                       return;
-       }
-
-       /* Open TSAPs */
-       irlan_client_open_ctrl_tsap(self);
-       irlan_open_data_tsap(self);
-
-       irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL);
-
-       /* Start kick timer */
-       irlan_client_start_kick_timer(self, 2*HZ);
-}
-
-/*
- * Function irlan_discovery_indication (daddr)
- *
- *    Remote device with IrLAN server support discovered
- *
- */
-void irlan_client_discovery_indication(discinfo_t *discovery,
-                                      DISCOVERY_MODE mode,
-                                      void *priv)
-{
-       struct irlan_cb *self;
-       __u32 saddr, daddr;
-
-       IRDA_ASSERT(discovery != NULL, return;);
-
-       /*
-        * I didn't check it, but I bet that IrLAN suffer from the same
-        * deficiency as IrComm and doesn't handle two instances
-        * simultaneously connecting to each other.
-        * Same workaround, drop passive discoveries.
-        * Jean II */
-       if(mode == DISCOVERY_PASSIVE)
-               return;
-
-       saddr = discovery->saddr;
-       daddr = discovery->daddr;
-
-       /* Find instance */
-       rcu_read_lock();
-       self = irlan_get_any();
-       if (self) {
-               IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;);
-
-               pr_debug("%s(), Found instance (%08x)!\n", __func__ ,
-                        daddr);
-
-               irlan_client_wakeup(self, saddr, daddr);
-       }
-IRDA_ASSERT_LABEL(out:)
-       rcu_read_unlock();
-}
-
-/*
- * Function irlan_client_data_indication (handle, skb)
- *
- *    This function gets the data that is received on the control channel
- *
- */
-static int irlan_client_ctrl_data_indication(void *instance, void *sap,
-                                            struct sk_buff *skb)
-{
-       struct irlan_cb *self;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb);
-
-       /* Ready for a new command */
-       pr_debug("%s(), clearing tx_busy\n", __func__);
-       self->client.tx_busy = FALSE;
-
-       /* Check if we have some queued commands waiting to be sent */
-       irlan_run_ctrl_tx_queue(self);
-
-       return 0;
-}
-
-static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
-                                                   LM_REASON reason,
-                                                   struct sk_buff *userdata)
-{
-       struct irlan_cb *self;
-       struct tsap_cb *tsap;
-       struct sk_buff *skb;
-
-       pr_debug("%s(), reason=%d\n", __func__ , reason);
-
-       self = instance;
-       tsap = sap;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-       IRDA_ASSERT(tsap != NULL, return;);
-       IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
-
-       IRDA_ASSERT(tsap == self->client.tsap_ctrl, return;);
-
-       /* Remove frames queued on the control channel */
-       while ((skb = skb_dequeue(&self->client.txq)) != NULL) {
-               dev_kfree_skb(skb);
-       }
-       self->client.tx_busy = FALSE;
-
-       irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
-}
-
-/*
- * Function irlan_client_open_tsaps (self)
- *
- *    Initialize callbacks and open IrTTP TSAPs
- *
- */
-static void irlan_client_open_ctrl_tsap(struct irlan_cb *self)
-{
-       struct tsap_cb *tsap;
-       notify_t notify;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /* Check if already open */
-       if (self->client.tsap_ctrl)
-               return;
-
-       irda_notify_init(&notify);
-
-       /* Set up callbacks */
-       notify.data_indication       = irlan_client_ctrl_data_indication;
-       notify.connect_confirm       = irlan_client_ctrl_connect_confirm;
-       notify.disconnect_indication = irlan_client_ctrl_disconnect_indication;
-       notify.instance = self;
-       strlcpy(notify.name, "IrLAN ctrl (c)", sizeof(notify.name));
-
-       tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
-       if (!tsap) {
-               pr_debug("%s(), Got no tsap!\n", __func__);
-               return;
-       }
-       self->client.tsap_ctrl = tsap;
-}
-
-/*
- * Function irlan_client_connect_confirm (handle, skb)
- *
- *    Connection to peer IrLAN laye confirmed
- *
- */
-static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
-                                             struct qos_info *qos,
-                                             __u32 max_sdu_size,
-                                             __u8 max_header_size,
-                                             struct sk_buff *skb)
-{
-       struct irlan_cb *self;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       self->client.max_sdu_size = max_sdu_size;
-       self->client.max_header_size = max_header_size;
-
-       /* TODO: we could set the MTU depending on the max_sdu_size */
-
-       irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL);
-}
-
-/*
- * Function print_ret_code (code)
- *
- *    Print return code of request to peer IrLAN layer.
- *
- */
-static void print_ret_code(__u8 code)
-{
-       switch(code) {
-       case 0:
-               printk(KERN_INFO "Success\n");
-               break;
-       case 1:
-               net_warn_ratelimited("IrLAN: Insufficient resources\n");
-               break;
-       case 2:
-               net_warn_ratelimited("IrLAN: Invalid command format\n");
-               break;
-       case 3:
-               net_warn_ratelimited("IrLAN: Command not supported\n");
-               break;
-       case 4:
-               net_warn_ratelimited("IrLAN: Parameter not supported\n");
-               break;
-       case 5:
-               net_warn_ratelimited("IrLAN: Value not supported\n");
-               break;
-       case 6:
-               net_warn_ratelimited("IrLAN: Not open\n");
-               break;
-       case 7:
-               net_warn_ratelimited("IrLAN: Authentication required\n");
-               break;
-       case 8:
-               net_warn_ratelimited("IrLAN: Invalid password\n");
-               break;
-       case 9:
-               net_warn_ratelimited("IrLAN: Protocol error\n");
-               break;
-       case 255:
-               net_warn_ratelimited("IrLAN: Asynchronous status\n");
-               break;
-       }
-}
-
-/*
- * Function irlan_client_parse_response (self, skb)
- *
- *    Extract all parameters from received buffer, then feed them to
- *    check_params for parsing
- */
-void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb)
-{
-       __u8 *frame;
-       __u8 *ptr;
-       int count;
-       int ret;
-       __u16 val_len;
-       int i;
-       char *name;
-       char *value;
-
-       IRDA_ASSERT(skb != NULL, return;);
-
-       pr_debug("%s() skb->len=%d\n", __func__ , (int)skb->len);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       if (!skb) {
-               net_err_ratelimited("%s(), Got NULL skb!\n", __func__);
-               return;
-       }
-       frame = skb->data;
-
-       /*
-        *  Check return code and print it if not success
-        */
-       if (frame[0]) {
-               print_ret_code(frame[0]);
-               return;
-       }
-
-       name = kmalloc(255, GFP_ATOMIC);
-       if (!name)
-               return;
-       value = kmalloc(1016, GFP_ATOMIC);
-       if (!value) {
-               kfree(name);
-               return;
-       }
-
-       /* How many parameters? */
-       count = frame[1];
-
-       pr_debug("%s(), got %d parameters\n", __func__ , count);
-
-       ptr = frame+2;
-
-       /* For all parameters */
-       for (i=0; i<count;i++) {
-               ret = irlan_extract_param(ptr, name, value, &val_len);
-               if (ret < 0) {
-                       pr_debug("%s(), IrLAN, Error!\n", __func__);
-                       break;
-               }
-               ptr += ret;
-               irlan_check_response_param(self, name, value, val_len);
-       }
-       /* Cleanup */
-       kfree(name);
-       kfree(value);
-}
-
-/*
- * Function irlan_check_response_param (self, param, value, val_len)
- *
- *     Check which parameter is received and update local variables
- *
- */
-static void irlan_check_response_param(struct irlan_cb *self, char *param,
-                                      char *value, int val_len)
-{
-       __u16 tmp_cpu; /* Temporary value in host order */
-       __u8 *bytes;
-       int i;
-
-       pr_debug("%s(), parm=%s\n", __func__ , param);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /* Media type */
-       if (strcmp(param, "MEDIA") == 0) {
-               if (strcmp(value, "802.3") == 0)
-                       self->media = MEDIA_802_3;
-               else
-                       self->media = MEDIA_802_5;
-               return;
-       }
-       if (strcmp(param, "FILTER_TYPE") == 0) {
-               if (strcmp(value, "DIRECTED") == 0)
-                       self->client.filter_type |= IRLAN_DIRECTED;
-               else if (strcmp(value, "FUNCTIONAL") == 0)
-                       self->client.filter_type |= IRLAN_FUNCTIONAL;
-               else if (strcmp(value, "GROUP") == 0)
-                       self->client.filter_type |= IRLAN_GROUP;
-               else if (strcmp(value, "MAC_FRAME") == 0)
-                       self->client.filter_type |= IRLAN_MAC_FRAME;
-               else if (strcmp(value, "MULTICAST") == 0)
-                       self->client.filter_type |= IRLAN_MULTICAST;
-               else if (strcmp(value, "BROADCAST") == 0)
-                       self->client.filter_type |= IRLAN_BROADCAST;
-               else if (strcmp(value, "IPX_SOCKET") == 0)
-                       self->client.filter_type |= IRLAN_IPX_SOCKET;
-
-       }
-       if (strcmp(param, "ACCESS_TYPE") == 0) {
-               if (strcmp(value, "DIRECT") == 0)
-                       self->client.access_type = ACCESS_DIRECT;
-               else if (strcmp(value, "PEER") == 0)
-                       self->client.access_type = ACCESS_PEER;
-               else if (strcmp(value, "HOSTED") == 0)
-                       self->client.access_type = ACCESS_HOSTED;
-               else {
-                       pr_debug("%s(), unknown access type!\n", __func__);
-               }
-       }
-       /* IRLAN version */
-       if (strcmp(param, "IRLAN_VER") == 0) {
-               pr_debug("IrLAN version %d.%d\n", (__u8)value[0],
-                        (__u8)value[1]);
-
-               self->version[0] = value[0];
-               self->version[1] = value[1];
-               return;
-       }
-       /* Which remote TSAP to use for data channel */
-       if (strcmp(param, "DATA_CHAN") == 0) {
-               self->dtsap_sel_data = value[0];
-               pr_debug("Data TSAP = %02x\n", self->dtsap_sel_data);
-               return;
-       }
-       if (strcmp(param, "CON_ARB") == 0) {
-               memcpy(&tmp_cpu, value, 2); /* Align value */
-               le16_to_cpus(&tmp_cpu);     /* Convert to host order */
-               self->client.recv_arb_val = tmp_cpu;
-               pr_debug("%s(), receive arb val=%d\n", __func__ ,
-                        self->client.recv_arb_val);
-       }
-       if (strcmp(param, "MAX_FRAME") == 0) {
-               memcpy(&tmp_cpu, value, 2); /* Align value */
-               le16_to_cpus(&tmp_cpu);     /* Convert to host order */
-               self->client.max_frame = tmp_cpu;
-               pr_debug("%s(), max frame=%d\n", __func__ ,
-                        self->client.max_frame);
-       }
-
-       /* RECONNECT_KEY, in case the link goes down! */
-       if (strcmp(param, "RECONNECT_KEY") == 0) {
-               pr_debug("Got reconnect key: ");
-               /* for (i = 0; i < val_len; i++) */
-/*                     printk("%02x", value[i]); */
-               memcpy(self->client.reconnect_key, value, val_len);
-               self->client.key_len = val_len;
-               pr_debug("\n");
-       }
-       /* FILTER_ENTRY, have we got an ethernet address? */
-       if (strcmp(param, "FILTER_ENTRY") == 0) {
-               bytes = value;
-               pr_debug("Ethernet address = %pM\n", bytes);
-               for (i = 0; i < 6; i++)
-                       self->dev->dev_addr[i] = bytes[i];
-       }
-}
-
-/*
- * Function irlan_client_get_value_confirm (obj_id, value)
- *
- *    Got results from remote LM-IAS
- *
- */
-void irlan_client_get_value_confirm(int result, __u16 obj_id,
-                                   struct ias_value *value, void *priv)
-{
-       struct irlan_cb *self;
-
-       IRDA_ASSERT(priv != NULL, return;);
-
-       self = priv;
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /* We probably don't need to make any more queries */
-       iriap_close(self->client.iriap);
-       self->client.iriap = NULL;
-
-       /* Check if request succeeded */
-       if (result != IAS_SUCCESS) {
-               pr_debug("%s(), got NULL value!\n", __func__);
-               irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL,
-                                     NULL);
-               return;
-       }
-
-       switch (value->type) {
-       case IAS_INTEGER:
-               self->dtsap_sel_ctrl = value->t.integer;
-
-               if (value->t.integer != -1) {
-                       irlan_do_client_event(self, IRLAN_IAS_PROVIDER_AVAIL,
-                                             NULL);
-                       return;
-               }
-               irias_delete_value(value);
-               break;
-       default:
-               pr_debug("%s(), unknown type!\n", __func__);
-               break;
-       }
-       irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL);
-}
diff --git a/net/irda/irlan/irlan_client_event.c b/net/irda/irlan/irlan_client_event.c
deleted file mode 100644 (file)
index cc93fab..0000000
+++ /dev/null
@@ -1,511 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlan_client_event.c
- * Version:       0.9
- * Description:   IrLAN client state machine
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Sun Dec 26 21:52:24 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/timer.h>
-#include <net/irda/irmod.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irttp.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_client.h>
-#include <net/irda/irlan_event.h>
-
-static int irlan_client_state_idle (struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_conn (struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_info (struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_open (struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_wait (struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_arb  (struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_data (struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-static int irlan_client_state_sync (struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb);
-
-static int (*state[])(struct irlan_cb *, IRLAN_EVENT event, struct sk_buff *) =
-{
-       irlan_client_state_idle,
-       irlan_client_state_query,
-       irlan_client_state_conn,
-       irlan_client_state_info,
-       irlan_client_state_media,
-       irlan_client_state_open,
-       irlan_client_state_wait,
-       irlan_client_state_arb,
-       irlan_client_state_data,
-       irlan_client_state_close,
-       irlan_client_state_sync
-};
-
-void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event,
-                          struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       (*state[ self->client.state]) (self, event, skb);
-}
-
-/*
- * Function irlan_client_state_idle (event, skb, info)
- *
- *    IDLE, We are waiting for an indication that there is a provider
- *    available.
- */
-static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
-                                  struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
-       switch (event) {
-       case IRLAN_DISCOVERY_INDICATION:
-               if (self->client.iriap) {
-                       net_warn_ratelimited("%s(), busy with a previous query\n",
-                                            __func__);
-                       return -EBUSY;
-               }
-
-               self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
-                                               irlan_client_get_value_confirm);
-               /* Get some values from peer IAS */
-               irlan_next_client_state(self, IRLAN_QUERY);
-               iriap_getvaluebyclass_request(self->client.iriap,
-                                             self->saddr, self->daddr,
-                                             "IrLAN", "IrDA:TinyTP:LsapSel");
-               break;
-       case IRLAN_WATCHDOG_TIMEOUT:
-               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_query (event, skb, info)
- *
- *    QUERY, We have queryed the remote IAS and is ready to connect
- *    to provider, just waiting for the confirm.
- *
- */
-static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
-       switch(event) {
-       case IRLAN_IAS_PROVIDER_AVAIL:
-               IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;);
-
-               self->client.open_retries = 0;
-
-               irttp_connect_request(self->client.tsap_ctrl,
-                                     self->dtsap_sel_ctrl,
-                                     self->saddr, self->daddr, NULL,
-                                     IRLAN_MTU, NULL);
-               irlan_next_client_state(self, IRLAN_CONN);
-               break;
-       case IRLAN_IAS_PROVIDER_NOT_AVAIL:
-               pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__);
-               irlan_next_client_state(self, IRLAN_IDLE);
-
-               /* Give the client a kick! */
-               if ((self->provider.access_type == ACCESS_PEER) &&
-                   (self->provider.state != IRLAN_IDLE))
-                       irlan_client_wakeup(self, self->saddr, self->daddr);
-               break;
-       case IRLAN_LMP_DISCONNECT:
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_client_state(self, IRLAN_IDLE);
-               break;
-       case IRLAN_WATCHDOG_TIMEOUT:
-               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_conn (event, skb, info)
- *
- *    CONN, We have connected to a provider but has not issued any
- *    commands yet.
- *
- */
-static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
-                                  struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch (event) {
-       case IRLAN_CONNECT_COMPLETE:
-               /* Send getinfo cmd */
-               irlan_get_provider_info(self);
-               irlan_next_client_state(self, IRLAN_INFO);
-               break;
-       case IRLAN_LMP_DISCONNECT:
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_client_state(self, IRLAN_IDLE);
-               break;
-       case IRLAN_WATCHDOG_TIMEOUT:
-               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_info (self, event, skb, info)
- *
- *    INFO, We have issued a GetInfo command and is awaiting a reply.
- */
-static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
-                                  struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch (event) {
-       case IRLAN_DATA_INDICATION:
-               IRDA_ASSERT(skb != NULL, return -1;);
-
-               irlan_client_parse_response(self, skb);
-
-               irlan_next_client_state(self, IRLAN_MEDIA);
-
-               irlan_get_media_char(self);
-               break;
-
-       case IRLAN_LMP_DISCONNECT:
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_client_state(self, IRLAN_IDLE);
-               break;
-       case IRLAN_WATCHDOG_TIMEOUT:
-               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_media (self, event, skb, info)
- *
- *    MEDIA, The irlan_client has issued a GetMedia command and is awaiting a
- *    reply.
- *
- */
-static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch(event) {
-       case IRLAN_DATA_INDICATION:
-               irlan_client_parse_response(self, skb);
-               irlan_open_data_channel(self);
-               irlan_next_client_state(self, IRLAN_OPEN);
-               break;
-       case IRLAN_LMP_DISCONNECT:
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_client_state(self, IRLAN_IDLE);
-               break;
-       case IRLAN_WATCHDOG_TIMEOUT:
-               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_open (self, event, skb, info)
- *
- *    OPEN, The irlan_client has issued a OpenData command and is awaiting a
- *    reply
- *
- */
-static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
-                                  struct sk_buff *skb)
-{
-       struct qos_info qos;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch(event) {
-       case IRLAN_DATA_INDICATION:
-               irlan_client_parse_response(self, skb);
-
-               /*
-                *  Check if we have got the remote TSAP for data
-                *  communications
-                */
-               IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;);
-
-               /* Check which access type we are dealing with */
-               switch (self->client.access_type) {
-               case ACCESS_PEER:
-                   if (self->provider.state == IRLAN_OPEN) {
-
-                           irlan_next_client_state(self, IRLAN_ARB);
-                           irlan_do_client_event(self, IRLAN_CHECK_CON_ARB,
-                                                 NULL);
-                   } else {
-
-                           irlan_next_client_state(self, IRLAN_WAIT);
-                   }
-                   break;
-               case ACCESS_DIRECT:
-               case ACCESS_HOSTED:
-                       qos.link_disc_time.bits = 0x01; /* 3 secs */
-
-                       irttp_connect_request(self->tsap_data,
-                                             self->dtsap_sel_data,
-                                             self->saddr, self->daddr, &qos,
-                                             IRLAN_MTU, NULL);
-
-                       irlan_next_client_state(self, IRLAN_DATA);
-                       break;
-               default:
-                       pr_debug("%s(), unknown access type!\n", __func__);
-                       break;
-               }
-               break;
-       case IRLAN_LMP_DISCONNECT:
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_client_state(self, IRLAN_IDLE);
-               break;
-       case IRLAN_WATCHDOG_TIMEOUT:
-               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_wait (self, event, skb, info)
- *
- *    WAIT, The irlan_client is waiting for the local provider to enter the
- *    provider OPEN state.
- *
- */
-static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event,
-                                  struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch(event) {
-       case IRLAN_PROVIDER_SIGNAL:
-               irlan_next_client_state(self, IRLAN_ARB);
-               irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL);
-               break;
-       case IRLAN_LMP_DISCONNECT:
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_client_state(self, IRLAN_IDLE);
-               break;
-       case IRLAN_WATCHDOG_TIMEOUT:
-               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
-                                 struct sk_buff *skb)
-{
-       struct qos_info qos;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch(event) {
-       case IRLAN_CHECK_CON_ARB:
-               if (self->client.recv_arb_val == self->provider.send_arb_val) {
-                       irlan_next_client_state(self, IRLAN_CLOSE);
-                       irlan_close_data_channel(self);
-               } else if (self->client.recv_arb_val <
-                          self->provider.send_arb_val)
-               {
-                       qos.link_disc_time.bits = 0x01; /* 3 secs */
-
-                       irlan_next_client_state(self, IRLAN_DATA);
-                       irttp_connect_request(self->tsap_data,
-                                             self->dtsap_sel_data,
-                                             self->saddr, self->daddr, &qos,
-                                             IRLAN_MTU, NULL);
-               } else if (self->client.recv_arb_val >
-                          self->provider.send_arb_val)
-               {
-                       pr_debug("%s(), lost the battle :-(\n", __func__);
-               }
-               break;
-       case IRLAN_DATA_CONNECT_INDICATION:
-               irlan_next_client_state(self, IRLAN_DATA);
-               break;
-       case IRLAN_LMP_DISCONNECT:
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_client_state(self, IRLAN_IDLE);
-               break;
-       case IRLAN_WATCHDOG_TIMEOUT:
-               pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_data (self, event, skb, info)
- *
- *    DATA, The data channel is connected, allowing data transfers between
- *    the local and remote machines.
- *
- */
-static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
-                                  struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
-       switch(event) {
-       case IRLAN_DATA_INDICATION:
-               irlan_client_parse_response(self, skb);
-               break;
-       case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_client_state(self, IRLAN_IDLE);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_close (self, event, skb, info)
- *
- *
- *
- */
-static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
-                                   struct sk_buff *skb)
-{
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_client_state_sync (self, event, skb, info)
- *
- *
- *
- */
-static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event,
-                                  struct sk_buff *skb)
-{
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c
deleted file mode 100644 (file)
index 481bbc2..0000000
+++ /dev/null
@@ -1,1176 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlan_common.c
- * Version:       0.9
- * Description:   IrDA LAN Access Protocol Implementation
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Sun Dec 26 21:53:10 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/gfp.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/proc_fs.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <linux/random.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
-#include <linux/moduleparam.h>
-#include <linux/bitops.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/timer.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_client.h>
-#include <net/irda/irlan_provider.h>
-#include <net/irda/irlan_eth.h>
-#include <net/irda/irlan_filter.h>
-
-
-/* extern char sysctl_devname[]; */
-
-/*
- *  Master structure
- */
-static LIST_HEAD(irlans);
-
-static void *ckey;
-static void *skey;
-
-/* Module parameters */
-static bool eth;   /* Use "eth" or "irlan" name for devices */
-static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */
-
-#ifdef CONFIG_PROC_FS
-static const char *const irlan_access[] = {
-       "UNKNOWN",
-       "DIRECT",
-       "PEER",
-       "HOSTED"
-};
-
-static const char *const irlan_media[] = {
-       "UNKNOWN",
-       "802.3",
-       "802.5"
-};
-
-extern struct proc_dir_entry *proc_irda;
-
-static int irlan_seq_open(struct inode *inode, struct file *file);
-
-static const struct file_operations irlan_fops = {
-       .owner   = THIS_MODULE,
-       .open    = irlan_seq_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = seq_release,
-};
-
-extern struct proc_dir_entry *proc_irda;
-#endif /* CONFIG_PROC_FS */
-
-static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr);
-static void __irlan_close(struct irlan_cb *self);
-static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
-                               __u8 value_byte, __u16 value_short,
-                               __u8 *value_array, __u16 value_len);
-static void irlan_open_unicast_addr(struct irlan_cb *self);
-static void irlan_get_unicast_addr(struct irlan_cb *self);
-void irlan_close_tsaps(struct irlan_cb *self);
-
-/*
- * Function irlan_init (void)
- *
- *    Initialize IrLAN layer
- *
- */
-static int __init irlan_init(void)
-{
-       struct irlan_cb *new;
-       __u16 hints;
-
-#ifdef CONFIG_PROC_FS
-       { struct proc_dir_entry *proc;
-       proc = proc_create("irlan", 0, proc_irda, &irlan_fops);
-       if (!proc) {
-               printk(KERN_ERR "irlan_init: can't create /proc entry!\n");
-               return -ENODEV;
-       }
-       }
-#endif /* CONFIG_PROC_FS */
-
-       hints = irlmp_service_to_hint(S_LAN);
-
-       /* Register with IrLMP as a client */
-       ckey = irlmp_register_client(hints, &irlan_client_discovery_indication,
-                                    NULL, NULL);
-       if (!ckey)
-               goto err_ckey;
-
-       /* Register with IrLMP as a service */
-       skey = irlmp_register_service(hints);
-       if (!skey)
-               goto err_skey;
-
-       /* Start the master IrLAN instance (the only one for now) */
-       new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY);
-       if (!new)
-               goto err_open;
-
-       /* The master will only open its (listen) control TSAP */
-       irlan_provider_open_ctrl_tsap(new);
-
-       /* Do some fast discovery! */
-       irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
-
-       return 0;
-
-err_open:
-       irlmp_unregister_service(skey);
-err_skey:
-       irlmp_unregister_client(ckey);
-err_ckey:
-#ifdef CONFIG_PROC_FS
-       remove_proc_entry("irlan", proc_irda);
-#endif /* CONFIG_PROC_FS */
-
-       return -ENOMEM;
-}
-
-static void __exit irlan_cleanup(void)
-{
-       struct irlan_cb *self, *next;
-
-       irlmp_unregister_client(ckey);
-       irlmp_unregister_service(skey);
-
-#ifdef CONFIG_PROC_FS
-       remove_proc_entry("irlan", proc_irda);
-#endif /* CONFIG_PROC_FS */
-
-       /* Cleanup any leftover network devices */
-       rtnl_lock();
-       list_for_each_entry_safe(self, next, &irlans, dev_list) {
-               __irlan_close(self);
-       }
-       rtnl_unlock();
-}
-
-/*
- * Function irlan_open (void)
- *
- *    Open new instance of a client/provider, we should only register the
- *    network device if this instance is ment for a particular client/provider
- */
-static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr)
-{
-       struct net_device *dev;
-       struct irlan_cb *self;
-
-       /* Create network device with irlan */
-       dev = alloc_irlandev(eth ? "eth%d" : "irlan%d");
-       if (!dev)
-               return NULL;
-
-       self = netdev_priv(dev);
-       self->dev = dev;
-
-       /*
-        *  Initialize local device structure
-        */
-       self->magic = IRLAN_MAGIC;
-       self->saddr = saddr;
-       self->daddr = daddr;
-
-       /* Provider access can only be PEER, DIRECT, or HOSTED */
-       self->provider.access_type = access;
-       if (access == ACCESS_DIRECT) {
-               /*
-                * Since we are emulating an IrLAN sever we will have to
-                * give ourself an ethernet address!
-                */
-               dev->dev_addr[0] = 0x40;
-               dev->dev_addr[1] = 0x00;
-               dev->dev_addr[2] = 0x00;
-               dev->dev_addr[3] = 0x00;
-               get_random_bytes(dev->dev_addr+4, 1);
-               get_random_bytes(dev->dev_addr+5, 1);
-       }
-
-       self->media = MEDIA_802_3;
-       self->disconnect_reason = LM_USER_REQUEST;
-       init_timer(&self->watchdog_timer);
-       init_timer(&self->client.kick_timer);
-       init_waitqueue_head(&self->open_wait);
-
-       skb_queue_head_init(&self->client.txq);
-
-       irlan_next_client_state(self, IRLAN_IDLE);
-       irlan_next_provider_state(self, IRLAN_IDLE);
-
-       if (register_netdev(dev)) {
-               pr_debug("%s(), register_netdev() failed!\n",
-                        __func__);
-               self = NULL;
-               free_netdev(dev);
-       } else {
-               rtnl_lock();
-               list_add_rcu(&self->dev_list, &irlans);
-               rtnl_unlock();
-       }
-
-       return self;
-}
-/*
- * Function __irlan_close (self)
- *
- *    This function closes and deallocates the IrLAN client instances. Be
- *    aware that other functions which calls client_close() must
- *    remove self from irlans list first.
- */
-static void __irlan_close(struct irlan_cb *self)
-{
-       ASSERT_RTNL();
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       del_timer_sync(&self->watchdog_timer);
-       del_timer_sync(&self->client.kick_timer);
-
-       /* Close all open connections and remove TSAPs */
-       irlan_close_tsaps(self);
-
-       if (self->client.iriap)
-               iriap_close(self->client.iriap);
-
-       /* Remove frames queued on the control channel */
-       skb_queue_purge(&self->client.txq);
-
-       /* Unregister and free self via destructor */
-       unregister_netdevice(self->dev);
-}
-
-/* Find any instance of irlan, used for client discovery wakeup */
-struct irlan_cb *irlan_get_any(void)
-{
-       struct irlan_cb *self;
-
-       list_for_each_entry_rcu(self, &irlans, dev_list) {
-               return self;
-       }
-       return NULL;
-}
-
-/*
- * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb)
- *
- *    Here we receive the connect indication for the data channel
- *
- */
-static void irlan_connect_indication(void *instance, void *sap,
-                                    struct qos_info *qos,
-                                    __u32 max_sdu_size,
-                                    __u8 max_header_size,
-                                    struct sk_buff *skb)
-{
-       struct irlan_cb *self;
-       struct tsap_cb *tsap;
-
-       self = instance;
-       tsap = sap;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-       IRDA_ASSERT(tsap == self->tsap_data,return;);
-
-       self->max_sdu_size = max_sdu_size;
-       self->max_header_size = max_header_size;
-
-       pr_debug("%s: We are now connected!\n", __func__);
-
-       del_timer(&self->watchdog_timer);
-
-       /* If you want to pass the skb to *both* state machines, you will
-        * need to skb_clone() it, so that you don't free it twice.
-        * As the state machines don't need it, git rid of it here...
-        * Jean II */
-       if (skb)
-               dev_kfree_skb(skb);
-
-       irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
-       irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
-
-       if (self->provider.access_type == ACCESS_PEER) {
-               /*
-                * Data channel is open, so we are now allowed to
-                * configure the remote filter
-                */
-               irlan_get_unicast_addr(self);
-               irlan_open_unicast_addr(self);
-       }
-       /* Ready to transfer Ethernet frames (at last) */
-       netif_start_queue(self->dev); /* Clear reason */
-}
-
-static void irlan_connect_confirm(void *instance, void *sap,
-                                 struct qos_info *qos,
-                                 __u32 max_sdu_size,
-                                 __u8 max_header_size,
-                                 struct sk_buff *skb)
-{
-       struct irlan_cb *self;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       self->max_sdu_size = max_sdu_size;
-       self->max_header_size = max_header_size;
-
-       /* TODO: we could set the MTU depending on the max_sdu_size */
-
-       pr_debug("%s: We are now connected!\n", __func__);
-       del_timer(&self->watchdog_timer);
-
-       /*
-        * Data channel is open, so we are now allowed to configure the remote
-        * filter
-        */
-       irlan_get_unicast_addr(self);
-       irlan_open_unicast_addr(self);
-
-       /* Open broadcast and multicast filter by default */
-       irlan_set_broadcast_filter(self, TRUE);
-       irlan_set_multicast_filter(self, TRUE);
-
-       /* Ready to transfer Ethernet frames */
-       netif_start_queue(self->dev);
-       self->disconnect_reason = 0; /* Clear reason */
-       wake_up_interruptible(&self->open_wait);
-}
-
-/*
- * Function irlan_client_disconnect_indication (handle)
- *
- *    Callback function for the IrTTP layer. Indicates a disconnection of
- *    the specified connection (handle)
- */
-static void irlan_disconnect_indication(void *instance,
-                                       void *sap, LM_REASON reason,
-                                       struct sk_buff *userdata)
-{
-       struct irlan_cb *self;
-       struct tsap_cb *tsap;
-
-       pr_debug("%s(), reason=%d\n", __func__ , reason);
-
-       self = instance;
-       tsap = sap;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-       IRDA_ASSERT(tsap != NULL, return;);
-       IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
-
-       IRDA_ASSERT(tsap == self->tsap_data, return;);
-
-       pr_debug("IrLAN, data channel disconnected by peer!\n");
-
-       /* Save reason so we know if we should try to reconnect or not */
-       self->disconnect_reason = reason;
-
-       switch (reason) {
-       case LM_USER_REQUEST: /* User request */
-               pr_debug("%s(), User requested\n", __func__);
-               break;
-       case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */
-               pr_debug("%s(), Unexpected IrLAP disconnect\n", __func__);
-               break;
-       case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */
-               pr_debug("%s(), IrLAP connect failed\n", __func__);
-               break;
-       case LM_LAP_RESET:  /* IrLAP reset */
-               pr_debug("%s(), IrLAP reset\n", __func__);
-               break;
-       case LM_INIT_DISCONNECT:
-               pr_debug("%s(), IrLMP connect failed\n", __func__);
-               break;
-       default:
-               net_err_ratelimited("%s(), Unknown disconnect reason\n",
-                                   __func__);
-               break;
-       }
-
-       /* If you want to pass the skb to *both* state machines, you will
-        * need to skb_clone() it, so that you don't free it twice.
-        * As the state machines don't need it, git rid of it here...
-        * Jean II */
-       if (userdata)
-               dev_kfree_skb(userdata);
-
-       irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
-       irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
-
-       wake_up_interruptible(&self->open_wait);
-}
-
-void irlan_open_data_tsap(struct irlan_cb *self)
-{
-       struct tsap_cb *tsap;
-       notify_t notify;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /* Check if already open */
-       if (self->tsap_data)
-               return;
-
-       irda_notify_init(&notify);
-
-       notify.data_indication       = irlan_eth_receive;
-       notify.udata_indication      = irlan_eth_receive;
-       notify.connect_indication    = irlan_connect_indication;
-       notify.connect_confirm       = irlan_connect_confirm;
-       notify.flow_indication       = irlan_eth_flow_indication;
-       notify.disconnect_indication = irlan_disconnect_indication;
-       notify.instance              = self;
-       strlcpy(notify.name, "IrLAN data", sizeof(notify.name));
-
-       tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
-       if (!tsap) {
-               pr_debug("%s(), Got no tsap!\n", __func__);
-               return;
-       }
-       self->tsap_data = tsap;
-
-       /*
-        *  This is the data TSAP selector which we will pass to the client
-        *  when the client ask for it.
-        */
-       self->stsap_sel_data = self->tsap_data->stsap_sel;
-}
-
-void irlan_close_tsaps(struct irlan_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /* Disconnect and close all open TSAP connections */
-       if (self->tsap_data) {
-               irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL);
-               irttp_close_tsap(self->tsap_data);
-               self->tsap_data = NULL;
-       }
-       if (self->client.tsap_ctrl) {
-               irttp_disconnect_request(self->client.tsap_ctrl, NULL,
-                                        P_NORMAL);
-               irttp_close_tsap(self->client.tsap_ctrl);
-               self->client.tsap_ctrl = NULL;
-       }
-       if (self->provider.tsap_ctrl) {
-               irttp_disconnect_request(self->provider.tsap_ctrl, NULL,
-                                        P_NORMAL);
-               irttp_close_tsap(self->provider.tsap_ctrl);
-               self->provider.tsap_ctrl = NULL;
-       }
-       self->disconnect_reason = LM_USER_REQUEST;
-}
-
-/*
- * Function irlan_ias_register (self, tsap_sel)
- *
- *    Register with LM-IAS
- *
- */
-void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel)
-{
-       struct ias_object *obj;
-       struct ias_value *new_value;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /*
-        * Check if object has already been registered by a previous provider.
-        * If that is the case, we just change the value of the attribute
-        */
-       if (!irias_find_object("IrLAN")) {
-               obj = irias_new_object("IrLAN", IAS_IRLAN_ID);
-               irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel,
-                                        IAS_KERNEL_ATTR);
-               irias_insert_object(obj);
-       } else {
-               new_value = irias_new_integer_value(tsap_sel);
-               irias_object_change_attribute("IrLAN", "IrDA:TinyTP:LsapSel",
-                                             new_value);
-       }
-
-       /* Register PnP object only if not registered before */
-       if (!irias_find_object("PnP")) {
-               obj = irias_new_object("PnP", IAS_PNP_ID);
-#if 0
-               irias_add_string_attrib(obj, "Name", sysctl_devname,
-                                       IAS_KERNEL_ATTR);
-#else
-               irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR);
-#endif
-               irias_add_string_attrib(obj, "DeviceID", "HWP19F0",
-                                       IAS_KERNEL_ATTR);
-               irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR);
-               if (self->provider.access_type == ACCESS_PEER)
-                       irias_add_string_attrib(obj, "Comp#01", "PNP8389",
-                                               IAS_KERNEL_ATTR);
-               else
-                       irias_add_string_attrib(obj, "Comp#01", "PNP8294",
-                                               IAS_KERNEL_ATTR);
-
-               irias_add_string_attrib(obj, "Manufacturer",
-                                       "Linux-IrDA Project", IAS_KERNEL_ATTR);
-               irias_insert_object(obj);
-       }
-}
-
-/*
- * Function irlan_run_ctrl_tx_queue (self)
- *
- *    Try to send the next command in the control transmit queue
- *
- */
-int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
-{
-       struct sk_buff *skb;
-
-       if (irda_lock(&self->client.tx_busy) == FALSE)
-               return -EBUSY;
-
-       skb = skb_dequeue(&self->client.txq);
-       if (!skb) {
-               self->client.tx_busy = FALSE;
-               return 0;
-       }
-
-       /* Check that it's really possible to send commands */
-       if ((self->client.tsap_ctrl == NULL) ||
-           (self->client.state == IRLAN_IDLE))
-       {
-               self->client.tx_busy = FALSE;
-               dev_kfree_skb(skb);
-               return -1;
-       }
-       pr_debug("%s(), sending ...\n", __func__);
-
-       return irttp_data_request(self->client.tsap_ctrl, skb);
-}
-
-/*
- * Function irlan_ctrl_data_request (self, skb)
- *
- *    This function makes sure that commands on the control channel is being
- *    sent in a command/response fashion
- */
-static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb)
-{
-       /* Queue command */
-       skb_queue_tail(&self->client.txq, skb);
-
-       /* Try to send command */
-       irlan_run_ctrl_tx_queue(self);
-}
-
-/*
- * Function irlan_get_provider_info (self)
- *
- *    Send Get Provider Information command to peer IrLAN layer
- *
- */
-void irlan_get_provider_info(struct irlan_cb *self)
-{
-       struct sk_buff *skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER,
-                       GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       /* Reserve space for TTP, LMP, and LAP header */
-       skb_reserve(skb, self->client.max_header_size);
-       skb_put(skb, 2);
-
-       frame = skb->data;
-
-       frame[0] = CMD_GET_PROVIDER_INFO;
-       frame[1] = 0x00;                 /* Zero parameters */
-
-       irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_open_data_channel (self)
- *
- *    Send an Open Data Command to provider
- *
- */
-void irlan_open_data_channel(struct irlan_cb *self)
-{
-       struct sk_buff *skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
-                       IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3") +
-                       IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "DIRECT"),
-                       GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       skb_reserve(skb, self->client.max_header_size);
-       skb_put(skb, 2);
-
-       frame = skb->data;
-
-       /* Build frame */
-       frame[0] = CMD_OPEN_DATA_CHANNEL;
-       frame[1] = 0x02; /* Two parameters */
-
-       irlan_insert_string_param(skb, "MEDIA", "802.3");
-       irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT");
-       /* irlan_insert_string_param(skb, "MODE", "UNRELIABLE"); */
-
-/*     self->use_udata = TRUE; */
-
-       irlan_ctrl_data_request(self, skb);
-}
-
-void irlan_close_data_channel(struct irlan_cb *self)
-{
-       struct sk_buff *skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /* Check if the TSAP is still there */
-       if (self->client.tsap_ctrl == NULL)
-               return;
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
-                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN"),
-                       GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       skb_reserve(skb, self->client.max_header_size);
-       skb_put(skb, 2);
-
-       frame = skb->data;
-
-       /* Build frame */
-       frame[0] = CMD_CLOSE_DATA_CHAN;
-       frame[1] = 0x01; /* One parameter */
-
-       irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
-
-       irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_open_unicast_addr (self)
- *
- *    Make IrLAN provider accept ethernet frames addressed to the unicast
- *    address.
- *
- */
-static void irlan_open_unicast_addr(struct irlan_cb *self)
-{
-       struct sk_buff *skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
-                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"),
-                       GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       /* Reserve space for TTP, LMP, and LAP header */
-       skb_reserve(skb, self->max_header_size);
-       skb_put(skb, 2);
-
-       frame = skb->data;
-
-       frame[0] = CMD_FILTER_OPERATION;
-       frame[1] = 0x03;                 /* Three parameters */
-       irlan_insert_byte_param(skb, "DATA_CHAN" , self->dtsap_sel_data);
-       irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
-       irlan_insert_string_param(skb, "FILTER_MODE", "FILTER");
-
-       irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_set_broadcast_filter (self, status)
- *
- *    Make IrLAN provider accept ethernet frames addressed to the broadcast
- *    address. Be careful with the use of this one, since there may be a lot
- *    of broadcast traffic out there. We can still function without this
- *    one but then _we_ have to initiate all communication with other
- *    hosts, since ARP request for this host will not be answered.
- */
-void irlan_set_broadcast_filter(struct irlan_cb *self, int status)
-{
-       struct sk_buff *skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
-                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") +
-                       /* We may waste one byte here...*/
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"),
-                       GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       /* Reserve space for TTP, LMP, and LAP header */
-       skb_reserve(skb, self->client.max_header_size);
-       skb_put(skb, 2);
-
-       frame = skb->data;
-
-       frame[0] = CMD_FILTER_OPERATION;
-       frame[1] = 0x03;                 /* Three parameters */
-       irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
-       irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST");
-       if (status)
-               irlan_insert_string_param(skb, "FILTER_MODE", "FILTER");
-       else
-               irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
-
-       irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_set_multicast_filter (self, status)
- *
- *    Make IrLAN provider accept ethernet frames addressed to the multicast
- *    address.
- *
- */
-void irlan_set_multicast_filter(struct irlan_cb *self, int status)
-{
-       struct sk_buff *skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
-                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") +
-                       /* We may waste one byte here...*/
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "NONE"),
-                       GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       /* Reserve space for TTP, LMP, and LAP header */
-       skb_reserve(skb, self->client.max_header_size);
-       skb_put(skb, 2);
-
-       frame = skb->data;
-
-       frame[0] = CMD_FILTER_OPERATION;
-       frame[1] = 0x03;                 /* Three parameters */
-       irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
-       irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST");
-       if (status)
-               irlan_insert_string_param(skb, "FILTER_MODE", "ALL");
-       else
-               irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
-
-       irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_get_unicast_addr (self)
- *
- *    Retrieves the unicast address from the IrLAN provider. This address
- *    will be inserted into the devices structure, so the ethernet layer
- *    can construct its packets.
- *
- */
-static void irlan_get_unicast_addr(struct irlan_cb *self)
-{
-       struct sk_buff *skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
-                       IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") +
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_OPERATION",
-                                                  "DYNAMIC"),
-                       GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       /* Reserve space for TTP, LMP, and LAP header */
-       skb_reserve(skb, self->client.max_header_size);
-       skb_put(skb, 2);
-
-       frame = skb->data;
-
-       frame[0] = CMD_FILTER_OPERATION;
-       frame[1] = 0x03;                 /* Three parameters */
-       irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
-       irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
-       irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC");
-
-       irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function irlan_get_media_char (self)
- *
- *
- *
- */
-void irlan_get_media_char(struct irlan_cb *self)
-{
-       struct sk_buff *skb;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
-                       IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3"),
-                       GFP_ATOMIC);
-
-       if (!skb)
-               return;
-
-       /* Reserve space for TTP, LMP, and LAP header */
-       skb_reserve(skb, self->client.max_header_size);
-       skb_put(skb, 2);
-
-       frame = skb->data;
-
-       /* Build frame */
-       frame[0] = CMD_GET_MEDIA_CHAR;
-       frame[1] = 0x01; /* One parameter */
-
-       irlan_insert_string_param(skb, "MEDIA", "802.3");
-       irlan_ctrl_data_request(self, skb);
-}
-
-/*
- * Function insert_byte_param (skb, param, value)
- *
- *    Insert byte parameter into frame
- *
- */
-int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value)
-{
-       return __irlan_insert_param(skb, param, IRLAN_BYTE, value, 0, NULL, 0);
-}
-
-int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value)
-{
-       return __irlan_insert_param(skb, param, IRLAN_SHORT, 0, value, NULL, 0);
-}
-
-/*
- * Function insert_string (skb, param, value)
- *
- *    Insert string parameter into frame
- *
- */
-int irlan_insert_string_param(struct sk_buff *skb, char *param, char *string)
-{
-       int string_len = strlen(string);
-
-       return __irlan_insert_param(skb, param, IRLAN_ARRAY, 0, 0, string,
-                                   string_len);
-}
-
-/*
- * Function insert_array_param(skb, param, value, len_value)
- *
- *    Insert array parameter into frame
- *
- */
-int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *array,
-                            __u16 array_len)
-{
-       return __irlan_insert_param(skb, name, IRLAN_ARRAY, 0, 0, array,
-                                   array_len);
-}
-
-/*
- * Function insert_param (skb, param, value, byte)
- *
- *    Insert parameter at end of buffer, structure of a parameter is:
- *
- *    -----------------------------------------------------------------------
- *    | Name Length[1] | Param Name[1..255] | Val Length[2] | Value[0..1016]|
- *    -----------------------------------------------------------------------
- */
-static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
-                               __u8 value_byte, __u16 value_short,
-                               __u8 *value_array, __u16 value_len)
-{
-       __u8 *frame;
-       __u8 param_len;
-       __le16 tmp_le; /* Temporary value in little endian format */
-       int n=0;
-
-       if (skb == NULL) {
-               pr_debug("%s(), Got NULL skb\n", __func__);
-               return 0;
-       }
-
-       param_len = strlen(param);
-       switch (type) {
-       case IRLAN_BYTE:
-               value_len = 1;
-               break;
-       case IRLAN_SHORT:
-               value_len = 2;
-               break;
-       case IRLAN_ARRAY:
-               IRDA_ASSERT(value_array != NULL, return 0;);
-               IRDA_ASSERT(value_len > 0, return 0;);
-               break;
-       default:
-               pr_debug("%s(), Unknown parameter type!\n", __func__);
-               return 0;
-       }
-
-       /* Insert at end of sk-buffer */
-       frame = skb_tail_pointer(skb);
-
-       /* Make space for data */
-       if (skb_tailroom(skb) < (param_len+value_len+3)) {
-               pr_debug("%s(), No more space at end of skb\n", __func__);
-               return 0;
-       }
-       skb_put(skb, param_len+value_len+3);
-
-       /* Insert parameter length */
-       frame[n++] = param_len;
-
-       /* Insert parameter */
-       memcpy(frame+n, param, param_len); n += param_len;
-
-       /* Insert value length (2 byte little endian format, LSB first) */
-       tmp_le = cpu_to_le16(value_len);
-       memcpy(frame+n, &tmp_le, 2); n += 2; /* To avoid alignment problems */
-
-       /* Insert value */
-       switch (type) {
-       case IRLAN_BYTE:
-               frame[n++] = value_byte;
-               break;
-       case IRLAN_SHORT:
-               tmp_le = cpu_to_le16(value_short);
-               memcpy(frame+n, &tmp_le, 2); n += 2;
-               break;
-       case IRLAN_ARRAY:
-               memcpy(frame+n, value_array, value_len); n+=value_len;
-               break;
-       default:
-               break;
-       }
-       IRDA_ASSERT(n == (param_len+value_len+3), return 0;);
-
-       return param_len+value_len+3;
-}
-
-/*
- * Function irlan_extract_param (buf, name, value, len)
- *
- *    Extracts a single parameter name/value pair from buffer and updates
- *    the buffer pointer to point to the next name/value pair.
- */
-int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len)
-{
-       __u8 name_len;
-       __u16 val_len;
-       int n=0;
-
-       /* get length of parameter name (1 byte) */
-       name_len = buf[n++];
-
-       if (name_len > 254) {
-               pr_debug("%s(), name_len > 254\n", __func__);
-               return -RSP_INVALID_COMMAND_FORMAT;
-       }
-
-       /* get parameter name */
-       memcpy(name, buf+n, name_len);
-       name[name_len] = '\0';
-       n+=name_len;
-
-       /*
-        *  Get length of parameter value (2 bytes in little endian
-        *  format)
-        */
-       memcpy(&val_len, buf+n, 2); /* To avoid alignment problems */
-       le16_to_cpus(&val_len); n+=2;
-
-       if (val_len >= 1016) {
-               pr_debug("%s(), parameter length to long\n", __func__);
-               return -RSP_INVALID_COMMAND_FORMAT;
-       }
-       *len = val_len;
-
-       /* get parameter value */
-       memcpy(value, buf+n, val_len);
-       value[val_len] = '\0';
-       n+=val_len;
-
-       pr_debug("Parameter: %s ", name);
-       pr_debug("Value: %s\n", value);
-
-       return n;
-}
-
-#ifdef CONFIG_PROC_FS
-
-/*
- * Start of reading /proc entries.
- * Return entry at pos,
- *     or start_token to indicate print header line
- *     or NULL if end of file
- */
-static void *irlan_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       rcu_read_lock();
-       return seq_list_start_head(&irlans, *pos);
-}
-
-/* Return entry after v, and increment pos */
-static void *irlan_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-       return seq_list_next(v, &irlans, pos);
-}
-
-/* End of reading /proc file */
-static void irlan_seq_stop(struct seq_file *seq, void *v)
-{
-       rcu_read_unlock();
-}
-
-
-/*
- * Show one entry in /proc file.
- */
-static int irlan_seq_show(struct seq_file *seq, void *v)
-{
-       if (v == &irlans)
-               seq_puts(seq, "IrLAN instances:\n");
-       else {
-               struct irlan_cb *self = list_entry(v, struct irlan_cb, dev_list);
-
-               IRDA_ASSERT(self != NULL, return -1;);
-               IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
-               seq_printf(seq,"ifname: %s,\n",
-                              self->dev->name);
-               seq_printf(seq,"client state: %s, ",
-                              irlan_state[ self->client.state]);
-               seq_printf(seq,"provider state: %s,\n",
-                              irlan_state[ self->provider.state]);
-               seq_printf(seq,"saddr: %#08x, ",
-                              self->saddr);
-               seq_printf(seq,"daddr: %#08x\n",
-                              self->daddr);
-               seq_printf(seq,"version: %d.%d,\n",
-                              self->version[1], self->version[0]);
-               seq_printf(seq,"access type: %s\n",
-                              irlan_access[self->client.access_type]);
-               seq_printf(seq,"media: %s\n",
-                              irlan_media[self->media]);
-
-               seq_printf(seq,"local filter:\n");
-               seq_printf(seq,"remote filter: ");
-               irlan_print_filter(seq, self->client.filter_type);
-               seq_printf(seq,"tx busy: %s\n",
-                              netif_queue_stopped(self->dev) ? "TRUE" : "FALSE");
-
-               seq_putc(seq,'\n');
-       }
-       return 0;
-}
-
-static const struct seq_operations irlan_seq_ops = {
-       .start = irlan_seq_start,
-       .next  = irlan_seq_next,
-       .stop  = irlan_seq_stop,
-       .show  = irlan_seq_show,
-};
-
-static int irlan_seq_open(struct inode *inode, struct file *file)
-{
-       return seq_open(file, &irlan_seq_ops);
-}
-#endif
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("The Linux IrDA LAN protocol");
-MODULE_LICENSE("GPL");
-
-module_param(eth, bool, 0);
-MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)");
-module_param(access, int, 0);
-MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3");
-
-module_init(irlan_init);
-module_exit(irlan_cleanup);
-
diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c
deleted file mode 100644 (file)
index 3be8528..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlan_eth.c
- * Version:
- * Description:
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Thu Oct 15 08:37:58 1998
- * Modified at:   Tue Mar 21 09:06:41 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
- *                slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
- *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- *
- *     Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/if_arp.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <net/arp.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_client.h>
-#include <net/irda/irlan_event.h>
-#include <net/irda/irlan_eth.h>
-
-static int  irlan_eth_open(struct net_device *dev);
-static int  irlan_eth_close(struct net_device *dev);
-static netdev_tx_t  irlan_eth_xmit(struct sk_buff *skb,
-                                        struct net_device *dev);
-static void irlan_eth_set_multicast_list(struct net_device *dev);
-
-static const struct net_device_ops irlan_eth_netdev_ops = {
-       .ndo_open               = irlan_eth_open,
-       .ndo_stop               = irlan_eth_close,
-       .ndo_start_xmit         = irlan_eth_xmit,
-       .ndo_set_rx_mode        = irlan_eth_set_multicast_list,
-       .ndo_validate_addr      = eth_validate_addr,
-};
-
-/*
- * Function irlan_eth_setup (dev)
- *
- *    The network device initialization function.
- *
- */
-static void irlan_eth_setup(struct net_device *dev)
-{
-       ether_setup(dev);
-
-       dev->netdev_ops         = &irlan_eth_netdev_ops;
-       dev->needs_free_netdev  = true;
-       dev->min_mtu            = 0;
-       dev->max_mtu            = ETH_MAX_MTU;
-
-       /*
-        * Lets do all queueing in IrTTP instead of this device driver.
-        * Queueing here as well can introduce some strange latency
-        * problems, which we will avoid by setting the queue size to 0.
-        */
-       /*
-        * The bugs in IrTTP and IrLAN that created this latency issue
-        * have now been fixed, and we can propagate flow control properly
-        * to the network layer. However, this requires a minimal queue of
-        * packets for the device.
-        * Without flow control, the Tx Queue is 14 (ttp) + 0 (dev) = 14
-        * With flow control, the Tx Queue is 7 (ttp) + 4 (dev) = 11
-        * See irlan_eth_flow_indication()...
-        * Note : this number was randomly selected and would need to
-        * be adjusted.
-        * Jean II */
-       dev->tx_queue_len = 4;
-}
-
-/*
- * Function alloc_irlandev
- *
- *    Allocate network device and control block
- *
- */
-struct net_device *alloc_irlandev(const char *name)
-{
-       return alloc_netdev(sizeof(struct irlan_cb), name, NET_NAME_UNKNOWN,
-                           irlan_eth_setup);
-}
-
-/*
- * Function irlan_eth_open (dev)
- *
- *    Network device has been opened by user
- *
- */
-static int irlan_eth_open(struct net_device *dev)
-{
-       struct irlan_cb *self = netdev_priv(dev);
-
-       /* Ready to play! */
-       netif_stop_queue(dev); /* Wait until data link is ready */
-
-       /* We are now open, so time to do some work */
-       self->disconnect_reason = 0;
-       irlan_client_wakeup(self, self->saddr, self->daddr);
-
-       /* Make sure we have a hardware address before we return,
-          so DHCP clients gets happy */
-       return wait_event_interruptible(self->open_wait,
-                                       !self->tsap_data->connected);
-}
-
-/*
- * Function irlan_eth_close (dev)
- *
- *    Stop the ether network device, his function will usually be called by
- *    ifconfig down. We should now disconnect the link, We start the
- *    close timer, so that the instance will be removed if we are unable
- *    to discover the remote device after the disconnect.
- */
-static int irlan_eth_close(struct net_device *dev)
-{
-       struct irlan_cb *self = netdev_priv(dev);
-
-       /* Stop device */
-       netif_stop_queue(dev);
-
-       irlan_close_data_channel(self);
-       irlan_close_tsaps(self);
-
-       irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
-       irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
-
-       /* Remove frames queued on the control channel */
-       skb_queue_purge(&self->client.txq);
-
-       self->client.tx_busy = 0;
-
-       return 0;
-}
-
-/*
- * Function irlan_eth_tx (skb)
- *
- *    Transmits ethernet frames over IrDA link.
- *
- */
-static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb,
-                                       struct net_device *dev)
-{
-       struct irlan_cb *self = netdev_priv(dev);
-       int ret;
-       unsigned int len;
-
-       /* skb headroom large enough to contain all IrDA-headers? */
-       if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) {
-               struct sk_buff *new_skb =
-                       skb_realloc_headroom(skb, self->max_header_size);
-
-               /*  We have to free the original skb anyway */
-               dev_kfree_skb(skb);
-
-               /* Did the realloc succeed? */
-               if (new_skb == NULL)
-                       return NETDEV_TX_OK;
-
-               /* Use the new skb instead */
-               skb = new_skb;
-       }
-
-       netif_trans_update(dev);
-
-       len = skb->len;
-       /* Now queue the packet in the transport layer */
-       if (self->use_udata)
-               ret = irttp_udata_request(self->tsap_data, skb);
-       else
-               ret = irttp_data_request(self->tsap_data, skb);
-
-       if (ret < 0) {
-               /*
-                * IrTTPs tx queue is full, so we just have to
-                * drop the frame! You might think that we should
-                * just return -1 and don't deallocate the frame,
-                * but that is dangerous since it's possible that
-                * we have replaced the original skb with a new
-                * one with larger headroom, and that would really
-                * confuse do_dev_queue_xmit() in dev.c! I have
-                * tried :-) DB
-                */
-               /* irttp_data_request already free the packet */
-               dev->stats.tx_dropped++;
-       } else {
-               dev->stats.tx_packets++;
-               dev->stats.tx_bytes += len;
-       }
-
-       return NETDEV_TX_OK;
-}
-
-/*
- * Function irlan_eth_receive (handle, skb)
- *
- *    This function gets the data that is received on the data channel
- *
- */
-int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb)
-{
-       struct irlan_cb *self = instance;
-       struct net_device *dev = self->dev;
-
-       if (skb == NULL) {
-               dev->stats.rx_dropped++;
-               return 0;
-       }
-       if (skb->len < ETH_HLEN) {
-               pr_debug("%s() : IrLAN frame too short (%d)\n",
-                        __func__, skb->len);
-               dev->stats.rx_dropped++;
-               dev_kfree_skb(skb);
-               return 0;
-       }
-
-       /*
-        * Adopt this frame! Important to set all these fields since they
-        * might have been previously set by the low level IrDA network
-        * device driver
-        */
-       skb->protocol = eth_type_trans(skb, dev); /* Remove eth header */
-
-       dev->stats.rx_packets++;
-       dev->stats.rx_bytes += skb->len;
-
-       netif_rx(skb);   /* Eat it! */
-
-       return 0;
-}
-
-/*
- * Function irlan_eth_flow (status)
- *
- *    Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by
- *    controlling the queue stop/start.
- *
- * The IrDA link layer has the advantage to have flow control, and
- * IrTTP now properly handles that. Flow controlling the higher layers
- * prevent us to drop Tx packets in here (up to 15% for a TCP socket,
- * more for UDP socket).
- * Also, this allow us to reduce the overall transmit queue, which means
- * less latency in case of mixed traffic.
- * Jean II
- */
-void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
-{
-       struct irlan_cb *self;
-       struct net_device *dev;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       dev = self->dev;
-
-       IRDA_ASSERT(dev != NULL, return;);
-
-       pr_debug("%s() : flow %s ; running %d\n", __func__,
-                flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START",
-                netif_running(dev));
-
-       switch (flow) {
-       case FLOW_STOP:
-               /* IrTTP is full, stop higher layers */
-               netif_stop_queue(dev);
-               break;
-       case FLOW_START:
-       default:
-               /* Tell upper layers that its time to transmit frames again */
-               /* Schedule network layer */
-               netif_wake_queue(dev);
-               break;
-       }
-}
-
-/*
- * Function set_multicast_list (dev)
- *
- *    Configure the filtering of the device
- *
- */
-#define HW_MAX_ADDRS 4 /* Must query to get it! */
-static void irlan_eth_set_multicast_list(struct net_device *dev)
-{
-       struct irlan_cb *self = netdev_priv(dev);
-
-       /* Check if data channel has been connected yet */
-       if (self->client.state != IRLAN_DATA) {
-               pr_debug("%s(), delaying!\n", __func__);
-               return;
-       }
-
-       if (dev->flags & IFF_PROMISC) {
-               /* Enable promiscuous mode */
-               net_warn_ratelimited("Promiscuous mode not implemented by IrLAN!\n");
-       } else if ((dev->flags & IFF_ALLMULTI) ||
-                netdev_mc_count(dev) > HW_MAX_ADDRS) {
-               /* Disable promiscuous mode, use normal mode. */
-               pr_debug("%s(), Setting multicast filter\n", __func__);
-               /* hardware_set_filter(NULL); */
-
-               irlan_set_multicast_filter(self, TRUE);
-       } else if (!netdev_mc_empty(dev)) {
-               pr_debug("%s(), Setting multicast filter\n", __func__);
-               /* Walk the address list, and load the filter */
-               /* hardware_set_filter(dev->mc_list); */
-
-               irlan_set_multicast_filter(self, TRUE);
-       } else {
-               pr_debug("%s(), Clearing multicast filter\n", __func__);
-               irlan_set_multicast_filter(self, FALSE);
-       }
-
-       if (dev->flags & IFF_BROADCAST)
-               irlan_set_broadcast_filter(self, TRUE);
-       else
-               irlan_set_broadcast_filter(self, FALSE);
-}
diff --git a/net/irda/irlan/irlan_event.c b/net/irda/irlan/irlan_event.c
deleted file mode 100644 (file)
index 9a1cc11..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlan_event.c
- * Version:
- * Description:
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Tue Oct 20 09:10:16 1998
- * Modified at:   Sat Oct 30 12:59:01 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <net/irda/irlan_event.h>
-
-const char * const irlan_state[] = {
-       "IRLAN_IDLE",
-       "IRLAN_QUERY",
-       "IRLAN_CONN",
-       "IRLAN_INFO",
-       "IRLAN_MEDIA",
-       "IRLAN_OPEN",
-       "IRLAN_WAIT",
-       "IRLAN_ARB",
-       "IRLAN_DATA",
-       "IRLAN_CLOSE",
-       "IRLAN_SYNC",
-};
-
-void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state)
-{
-       pr_debug("%s(), %s\n", __func__ , irlan_state[state]);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       self->client.state = state;
-}
-
-void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state)
-{
-       pr_debug("%s(), %s\n", __func__ , irlan_state[state]);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       self->provider.state = state;
-}
-
diff --git a/net/irda/irlan/irlan_filter.c b/net/irda/irlan/irlan_filter.c
deleted file mode 100644 (file)
index e755e90..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlan_filter.c
- * Version:
- * Description:
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Fri Jan 29 11:16:38 1999
- * Modified at:   Sat Oct 30 12:58:45 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/random.h>
-#include <linux/seq_file.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_filter.h>
-
-/*
- * Function irlan_filter_request (self, skb)
- *
- *    Handle filter request from client peer device
- *
- */
-void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       if ((self->provider.filter_type == IRLAN_DIRECTED) &&
-           (self->provider.filter_operation == DYNAMIC))
-       {
-               pr_debug("Giving peer a dynamic Ethernet address\n");
-               self->provider.mac_address[0] = 0x40;
-               self->provider.mac_address[1] = 0x00;
-               self->provider.mac_address[2] = 0x00;
-               self->provider.mac_address[3] = 0x00;
-
-               /* Use arbitration value to generate MAC address */
-               if (self->provider.access_type == ACCESS_PEER) {
-                       self->provider.mac_address[4] =
-                               self->provider.send_arb_val & 0xff;
-                       self->provider.mac_address[5] =
-                               (self->provider.send_arb_val >> 8) & 0xff;
-               } else {
-                       /* Just generate something for now */
-                       get_random_bytes(self->provider.mac_address+4, 1);
-                       get_random_bytes(self->provider.mac_address+5, 1);
-               }
-
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x03;
-               irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
-               irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001);
-               irlan_insert_array_param(skb, "FILTER_ENTRY",
-                                        self->provider.mac_address, 6);
-               return;
-       }
-
-       if ((self->provider.filter_type == IRLAN_DIRECTED) &&
-           (self->provider.filter_mode == FILTER))
-       {
-               pr_debug("Directed filter on\n");
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x00;
-               return;
-       }
-       if ((self->provider.filter_type == IRLAN_DIRECTED) &&
-           (self->provider.filter_mode == NONE))
-       {
-               pr_debug("Directed filter off\n");
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x00;
-               return;
-       }
-
-       if ((self->provider.filter_type == IRLAN_BROADCAST) &&
-           (self->provider.filter_mode == FILTER))
-       {
-               pr_debug("Broadcast filter on\n");
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x00;
-               return;
-       }
-       if ((self->provider.filter_type == IRLAN_BROADCAST) &&
-           (self->provider.filter_mode == NONE))
-       {
-               pr_debug("Broadcast filter off\n");
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x00;
-               return;
-       }
-       if ((self->provider.filter_type == IRLAN_MULTICAST) &&
-           (self->provider.filter_mode == FILTER))
-       {
-               pr_debug("Multicast filter on\n");
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x00;
-               return;
-       }
-       if ((self->provider.filter_type == IRLAN_MULTICAST) &&
-           (self->provider.filter_mode == NONE))
-       {
-               pr_debug("Multicast filter off\n");
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x00;
-               return;
-       }
-       if ((self->provider.filter_type == IRLAN_MULTICAST) &&
-           (self->provider.filter_operation == GET))
-       {
-               pr_debug("Multicast filter get\n");
-               skb->data[0] = 0x00; /* Success? */
-               skb->data[1] = 0x02;
-               irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
-               irlan_insert_short_param(skb, "MAX_ENTRY", 16);
-               return;
-       }
-       skb->data[0] = 0x00; /* Command not supported */
-       skb->data[1] = 0x00;
-
-       pr_debug("Not implemented!\n");
-}
-
-/*
- * Function check_request_param (self, param, value)
- *
- *    Check parameters in request from peer device
- *
- */
-void irlan_check_command_param(struct irlan_cb *self, char *param, char *value)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       pr_debug("%s, %s\n", param, value);
-
-       /*
-        *  This is experimental!! DB.
-        */
-        if (strcmp(param, "MODE") == 0) {
-               self->use_udata = TRUE;
-               return;
-       }
-
-       /*
-        *  FILTER_TYPE
-        */
-       if (strcmp(param, "FILTER_TYPE") == 0) {
-               if (strcmp(value, "DIRECTED") == 0) {
-                       self->provider.filter_type = IRLAN_DIRECTED;
-                       return;
-               }
-               if (strcmp(value, "MULTICAST") == 0) {
-                       self->provider.filter_type = IRLAN_MULTICAST;
-                       return;
-               }
-               if (strcmp(value, "BROADCAST") == 0) {
-                       self->provider.filter_type = IRLAN_BROADCAST;
-                       return;
-               }
-       }
-       /*
-        *  FILTER_MODE
-        */
-       if (strcmp(param, "FILTER_MODE") == 0) {
-               if (strcmp(value, "ALL") == 0) {
-                       self->provider.filter_mode = ALL;
-                       return;
-               }
-               if (strcmp(value, "FILTER") == 0) {
-                       self->provider.filter_mode = FILTER;
-                       return;
-               }
-               if (strcmp(value, "NONE") == 0) {
-                       self->provider.filter_mode = FILTER;
-                       return;
-               }
-       }
-       /*
-        *  FILTER_OPERATION
-        */
-       if (strcmp(param, "FILTER_OPERATION") == 0) {
-               if (strcmp(value, "DYNAMIC") == 0) {
-                       self->provider.filter_operation = DYNAMIC;
-                       return;
-               }
-               if (strcmp(value, "GET") == 0) {
-                       self->provider.filter_operation = GET;
-                       return;
-               }
-       }
-}
-
-/*
- * Function irlan_print_filter (filter_type, buf)
- *
- *    Print status of filter. Used by /proc file system
- *
- */
-#ifdef CONFIG_PROC_FS
-#define MASK2STR(m,s)  { .mask = m, .str = s }
-
-void irlan_print_filter(struct seq_file *seq, int filter_type)
-{
-       static struct {
-               int mask;
-               const char *str;
-       } filter_mask2str[] = {
-               MASK2STR(IRLAN_DIRECTED,        "DIRECTED"),
-               MASK2STR(IRLAN_FUNCTIONAL,      "FUNCTIONAL"),
-               MASK2STR(IRLAN_GROUP,           "GROUP"),
-               MASK2STR(IRLAN_MAC_FRAME,       "MAC_FRAME"),
-               MASK2STR(IRLAN_MULTICAST,       "MULTICAST"),
-               MASK2STR(IRLAN_BROADCAST,       "BROADCAST"),
-               MASK2STR(IRLAN_IPX_SOCKET,      "IPX_SOCKET"),
-               MASK2STR(0,                     NULL)
-       }, *p;
-
-       for (p = filter_mask2str; p->str; p++) {
-               if (filter_type & p->mask)
-                       seq_printf(seq, "%s ", p->str);
-       }
-       seq_putc(seq, '\n');
-}
-#undef MASK2STR
-#endif
diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c
deleted file mode 100644 (file)
index 15c292c..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlan_provider.c
- * Version:       0.9
- * Description:   IrDA LAN Access Protocol Implementation
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Sat Oct 30 12:52:10 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
- *                slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
- *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- *
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/init.h>
-#include <linux/random.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/iriap.h>
-#include <net/irda/timer.h>
-
-#include <net/irda/irlan_common.h>
-#include <net/irda/irlan_eth.h>
-#include <net/irda/irlan_event.h>
-#include <net/irda/irlan_provider.h>
-#include <net/irda/irlan_filter.h>
-#include <net/irda/irlan_client.h>
-
-static void irlan_provider_connect_indication(void *instance, void *sap,
-                                             struct qos_info *qos,
-                                             __u32 max_sdu_size,
-                                             __u8 max_header_size,
-                                             struct sk_buff *skb);
-
-/*
- * Function irlan_provider_control_data_indication (handle, skb)
- *
- *    This function gets the data that is received on the control channel
- *
- */
-static int irlan_provider_data_indication(void *instance, void *sap,
-                                         struct sk_buff *skb)
-{
-       struct irlan_cb *self;
-       __u8 code;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       code = skb->data[0];
-       switch(code) {
-       case CMD_GET_PROVIDER_INFO:
-               pr_debug("Got GET_PROVIDER_INFO command!\n");
-               irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb);
-               break;
-
-       case CMD_GET_MEDIA_CHAR:
-               pr_debug("Got GET_MEDIA_CHAR command!\n");
-               irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb);
-               break;
-       case CMD_OPEN_DATA_CHANNEL:
-               pr_debug("Got OPEN_DATA_CHANNEL command!\n");
-               irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb);
-               break;
-       case CMD_FILTER_OPERATION:
-               pr_debug("Got FILTER_OPERATION command!\n");
-               irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb);
-               break;
-       case CMD_RECONNECT_DATA_CHAN:
-               pr_debug("%s(), Got RECONNECT_DATA_CHAN command\n", __func__);
-               pr_debug("%s(), NOT IMPLEMENTED\n", __func__);
-               break;
-       case CMD_CLOSE_DATA_CHAN:
-               pr_debug("Got CLOSE_DATA_CHAN command!\n");
-               pr_debug("%s(), NOT IMPLEMENTED\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown command!\n", __func__);
-               break;
-       }
-       return 0;
-}
-
-/*
- * Function irlan_provider_connect_indication (handle, skb, priv)
- *
- *    Got connection from peer IrLAN client
- *
- */
-static void irlan_provider_connect_indication(void *instance, void *sap,
-                                             struct qos_info *qos,
-                                             __u32 max_sdu_size,
-                                             __u8 max_header_size,
-                                             struct sk_buff *skb)
-{
-       struct irlan_cb *self;
-       struct tsap_cb *tsap;
-
-       self = instance;
-       tsap = sap;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;);
-       IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;);
-
-       self->provider.max_sdu_size = max_sdu_size;
-       self->provider.max_header_size = max_header_size;
-
-       irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL);
-
-       /*
-        * If we are in peer mode, the client may not have got the discovery
-        * indication it needs to make progress. If the client is still in
-        * IDLE state, we must kick it.
-        */
-       if ((self->provider.access_type == ACCESS_PEER) &&
-           (self->client.state == IRLAN_IDLE))
-       {
-               irlan_client_wakeup(self, self->saddr, self->daddr);
-       }
-}
-
-/*
- * Function irlan_provider_connect_response (handle)
- *
- *    Accept incoming connection
- *
- */
-void irlan_provider_connect_response(struct irlan_cb *self,
-                                    struct tsap_cb *tsap)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       /* Just accept */
-       irttp_connect_response(tsap, IRLAN_MTU, NULL);
-}
-
-static void irlan_provider_disconnect_indication(void *instance, void *sap,
-                                                LM_REASON reason,
-                                                struct sk_buff *userdata)
-{
-       struct irlan_cb *self;
-       struct tsap_cb *tsap;
-
-       pr_debug("%s(), reason=%d\n", __func__ , reason);
-
-       self = instance;
-       tsap = sap;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-       IRDA_ASSERT(tsap != NULL, return;);
-       IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
-
-       IRDA_ASSERT(tsap == self->provider.tsap_ctrl, return;);
-
-       irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
-}
-
-/*
- * Function irlan_parse_open_data_cmd (self, skb)
- *
- *
- *
- */
-int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb)
-{
-       int ret;
-
-       ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb);
-
-       /* Open data channel */
-       irlan_open_data_tsap(self);
-
-       return ret;
-}
-
-/*
- * Function parse_command (skb)
- *
- *    Extract all parameters from received buffer, then feed them to
- *    check_params for parsing
- *
- */
-int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
-                                struct sk_buff *skb)
-{
-       __u8 *frame;
-       __u8 *ptr;
-       int count;
-       __u16 val_len;
-       int i;
-       char *name;
-       char *value;
-       int ret = RSP_SUCCESS;
-
-       IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;);
-
-       pr_debug("%s(), skb->len=%d\n", __func__ , (int)skb->len);
-
-       IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;);
-
-       if (!skb)
-               return -RSP_PROTOCOL_ERROR;
-
-       frame = skb->data;
-
-       name = kmalloc(255, GFP_ATOMIC);
-       if (!name)
-               return -RSP_INSUFFICIENT_RESOURCES;
-       value = kmalloc(1016, GFP_ATOMIC);
-       if (!value) {
-               kfree(name);
-               return -RSP_INSUFFICIENT_RESOURCES;
-       }
-
-       /* How many parameters? */
-       count = frame[1];
-
-       pr_debug("Got %d parameters\n", count);
-
-       ptr = frame+2;
-
-       /* For all parameters */
-       for (i=0; i<count;i++) {
-               ret = irlan_extract_param(ptr, name, value, &val_len);
-               if (ret < 0) {
-                       pr_debug("%s(), IrLAN, Error!\n", __func__);
-                       break;
-               }
-               ptr+=ret;
-               ret = RSP_SUCCESS;
-               irlan_check_command_param(self, name, value);
-       }
-       /* Cleanup */
-       kfree(name);
-       kfree(value);
-
-       return ret;
-}
-
-/*
- * Function irlan_provider_send_reply (self, info)
- *
- *    Send reply to query to peer IrLAN layer
- *
- */
-void irlan_provider_send_reply(struct irlan_cb *self, int command,
-                              int ret_code)
-{
-       struct sk_buff *skb;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
-
-       skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
-                       /* Bigger param length comes from CMD_GET_MEDIA_CHAR */
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") +
-                       IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") +
-                       IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "HOSTED"),
-                       GFP_ATOMIC);
-
-       if (!skb)
-               return;
-
-       /* Reserve space for TTP, LMP, and LAP header */
-       skb_reserve(skb, self->provider.max_header_size);
-       skb_put(skb, 2);
-
-       switch (command) {
-       case CMD_GET_PROVIDER_INFO:
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x02; /* 2 parameters */
-               switch (self->media) {
-               case MEDIA_802_3:
-                       irlan_insert_string_param(skb, "MEDIA", "802.3");
-                       break;
-               case MEDIA_802_5:
-                       irlan_insert_string_param(skb, "MEDIA", "802.5");
-                       break;
-               default:
-                       pr_debug("%s(), unknown media type!\n", __func__);
-                       break;
-               }
-               irlan_insert_short_param(skb, "IRLAN_VER", 0x0101);
-               break;
-
-       case CMD_GET_MEDIA_CHAR:
-               skb->data[0] = 0x00; /* Success */
-               skb->data[1] = 0x05; /* 5 parameters */
-               irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
-               irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST");
-               irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST");
-
-               switch (self->provider.access_type) {
-               case ACCESS_DIRECT:
-                       irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT");
-                       break;
-               case ACCESS_PEER:
-                       irlan_insert_string_param(skb, "ACCESS_TYPE", "PEER");
-                       break;
-               case ACCESS_HOSTED:
-                       irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED");
-                       break;
-               default:
-                       pr_debug("%s(), Unknown access type\n", __func__);
-                       break;
-               }
-               irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee);
-               break;
-       case CMD_OPEN_DATA_CHANNEL:
-               skb->data[0] = 0x00; /* Success */
-               if (self->provider.send_arb_val) {
-                       skb->data[1] = 0x03; /* 3 parameters */
-                       irlan_insert_short_param(skb, "CON_ARB",
-                                                self->provider.send_arb_val);
-               } else
-                       skb->data[1] = 0x02; /* 2 parameters */
-               irlan_insert_byte_param(skb, "DATA_CHAN", self->stsap_sel_data);
-               irlan_insert_string_param(skb, "RECONNECT_KEY", "LINUX RULES!");
-               break;
-       case CMD_FILTER_OPERATION:
-               irlan_filter_request(self, skb);
-               break;
-       default:
-               pr_debug("%s(), Unknown command!\n", __func__);
-               break;
-       }
-
-       irttp_data_request(self->provider.tsap_ctrl, skb);
-}
-
-/*
- * Function irlan_provider_register(void)
- *
- *    Register provider support so we can accept incoming connections.
- *
- */
-int irlan_provider_open_ctrl_tsap(struct irlan_cb *self)
-{
-       struct tsap_cb *tsap;
-       notify_t notify;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
-       /* Check if already open */
-       if (self->provider.tsap_ctrl)
-               return -1;
-
-       /*
-        *  First register well known control TSAP
-        */
-       irda_notify_init(&notify);
-       notify.data_indication       = irlan_provider_data_indication;
-       notify.connect_indication    = irlan_provider_connect_indication;
-       notify.disconnect_indication = irlan_provider_disconnect_indication;
-       notify.instance = self;
-       strlcpy(notify.name, "IrLAN ctrl (p)", sizeof(notify.name));
-
-       tsap = irttp_open_tsap(LSAP_ANY, 1, &notify);
-       if (!tsap) {
-               pr_debug("%s(), Got no tsap!\n", __func__);
-               return -1;
-       }
-       self->provider.tsap_ctrl = tsap;
-
-       /* Register with LM-IAS */
-       irlan_ias_register(self, tsap->stsap_sel);
-
-       return 0;
-}
-
diff --git a/net/irda/irlan/irlan_provider_event.c b/net/irda/irlan/irlan_provider_event.c
deleted file mode 100644 (file)
index 9c4f7f5..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlan_provider_event.c
- * Version:       0.9
- * Description:   IrLAN provider state machine)
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Sat Oct 30 12:52:41 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <net/irda/irda.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irttp.h>
-
-#include <net/irda/irlan_provider.h>
-#include <net/irda/irlan_event.h>
-
-static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
-                                    struct sk_buff *skb);
-static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
-                                    struct sk_buff *skb);
-static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
-                                    struct sk_buff *skb);
-static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
-                                    struct sk_buff *skb);
-
-static int (*state[])(struct irlan_cb *self, IRLAN_EVENT event,
-                     struct sk_buff *skb) =
-{
-       irlan_provider_state_idle,
-       NULL, /* Query */
-       NULL, /* Info */
-       irlan_provider_state_info,
-       NULL, /* Media */
-       irlan_provider_state_open,
-       NULL, /* Wait */
-       NULL, /* Arb */
-       irlan_provider_state_data,
-       NULL, /* Close */
-       NULL, /* Sync */
-};
-
-void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event,
-                            struct sk_buff *skb)
-{
-       IRDA_ASSERT(*state[ self->provider.state] != NULL, return;);
-
-       (*state[self->provider.state]) (self, event, skb);
-}
-
-/*
- * Function irlan_provider_state_idle (event, skb, info)
- *
- *    IDLE, We are waiting for an indication that there is a provider
- *    available.
- */
-static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
-                                    struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch(event) {
-       case IRLAN_CONNECT_INDICATION:
-            irlan_provider_connect_response( self, self->provider.tsap_ctrl);
-            irlan_next_provider_state( self, IRLAN_INFO);
-            break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_provider_state_info (self, event, skb, info)
- *
- *    INFO, We have issued a GetInfo command and is awaiting a reply.
- */
-static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
-                                    struct sk_buff *skb)
-{
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch(event) {
-       case IRLAN_GET_INFO_CMD:
-               /* Be sure to use 802.3 in case of peer mode */
-               if (self->provider.access_type == ACCESS_PEER) {
-                       self->media = MEDIA_802_3;
-
-                       /* Check if client has started yet */
-                       if (self->client.state == IRLAN_IDLE) {
-                               /* This should get the client going */
-                               irlmp_discovery_request(8);
-                       }
-               }
-
-               irlan_provider_send_reply(self, CMD_GET_PROVIDER_INFO,
-                                         RSP_SUCCESS);
-               /* Keep state */
-               break;
-       case IRLAN_GET_MEDIA_CMD:
-               irlan_provider_send_reply(self, CMD_GET_MEDIA_CHAR,
-                                         RSP_SUCCESS);
-               /* Keep state */
-               break;
-       case IRLAN_OPEN_DATA_CMD:
-               ret = irlan_parse_open_data_cmd(self, skb);
-               if (self->provider.access_type == ACCESS_PEER) {
-                       /* FIXME: make use of random functions! */
-                       self->provider.send_arb_val = (jiffies & 0xffff);
-               }
-               irlan_provider_send_reply(self, CMD_OPEN_DATA_CHANNEL, ret);
-
-               if (ret == RSP_SUCCESS) {
-                       irlan_next_provider_state(self, IRLAN_OPEN);
-
-                       /* Signal client that we are now open */
-                       irlan_do_client_event(self, IRLAN_PROVIDER_SIGNAL, NULL);
-               }
-               break;
-       case IRLAN_LMP_DISCONNECT:  /* FALLTHROUGH */
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_provider_state(self, IRLAN_IDLE);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_provider_state_open (self, event, skb, info)
- *
- *    OPEN, The client has issued a OpenData command and is awaiting a
- *    reply
- *
- */
-static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
-                                    struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-
-       switch(event) {
-       case IRLAN_FILTER_CONFIG_CMD:
-               irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
-               irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
-                                         RSP_SUCCESS);
-               /* Keep state */
-               break;
-       case IRLAN_DATA_CONNECT_INDICATION:
-               irlan_next_provider_state(self, IRLAN_DATA);
-               irlan_provider_connect_response(self, self->tsap_data);
-               break;
-       case IRLAN_LMP_DISCONNECT:  /* FALLTHROUGH */
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_provider_state(self, IRLAN_IDLE);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irlan_provider_state_data (self, event, skb, info)
- *
- *    DATA, The data channel is connected, allowing data transfers between
- *    the local and remote machines.
- *
- */
-static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
-                                    struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
-
-       switch(event) {
-       case IRLAN_FILTER_CONFIG_CMD:
-               irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
-               irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
-                                         RSP_SUCCESS);
-               break;
-       case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
-       case IRLAN_LAP_DISCONNECT:
-               irlan_next_provider_state(self, IRLAN_IDLE);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__ , event);
-               break;
-       }
-       if (skb)
-               dev_kfree_skb(skb);
-
-       return 0;
-}
-
-
-
-
-
-
-
-
-
-
diff --git a/net/irda/irlap.c b/net/irda/irlap.c
deleted file mode 100644 (file)
index 1cde711..0000000
+++ /dev/null
@@ -1,1207 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlap.c
- * Version:       1.0
- * Description:   IrLAP implementation for Linux
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Tue Dec 14 09:26:44 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/random.h>
-#include <linux/module.h>
-#include <linux/seq_file.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/irqueue.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irlmp_frame.h>
-#include <net/irda/irlap_frame.h>
-#include <net/irda/irlap.h>
-#include <net/irda/timer.h>
-#include <net/irda/qos.h>
-
-static hashbin_t *irlap = NULL;
-int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ;
-
-/* This is the delay of missed pf period before generating an event
- * to the application. The spec mandate 3 seconds, but in some cases
- * it's way too long. - Jean II */
-int sysctl_warn_noreply_time = 3;
-
-extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
-static void __irlap_close(struct irlap_cb *self);
-static void irlap_init_qos_capabilities(struct irlap_cb *self,
-                                       struct qos_info *qos_user);
-
-static const char *const lap_reasons[] __maybe_unused = {
-       "ERROR, NOT USED",
-       "LAP_DISC_INDICATION",
-       "LAP_NO_RESPONSE",
-       "LAP_RESET_INDICATION",
-       "LAP_FOUND_NONE",
-       "LAP_MEDIA_BUSY",
-       "LAP_PRIMARY_CONFLICT",
-       "ERROR, NOT USED",
-};
-
-int __init irlap_init(void)
-{
-       /* Check if the compiler did its job properly.
-        * May happen on some ARM configuration, check with Russell King. */
-       IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;);
-       IRDA_ASSERT(sizeof(struct test_frame) == 10, ;);
-       IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;);
-       IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;);
-
-       /* Allocate master array */
-       irlap = hashbin_new(HB_LOCK);
-       if (irlap == NULL) {
-               net_err_ratelimited("%s: can't allocate irlap hashbin!\n",
-                                   __func__);
-               return -ENOMEM;
-       }
-
-       return 0;
-}
-
-void irlap_cleanup(void)
-{
-       IRDA_ASSERT(irlap != NULL, return;);
-
-       hashbin_delete(irlap, (FREE_FUNC) __irlap_close);
-}
-
-/*
- * Function irlap_open (driver)
- *
- *    Initialize IrLAP layer
- *
- */
-struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
-                           const char *hw_name)
-{
-       struct irlap_cb *self;
-
-       /* Initialize the irlap structure. */
-       self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL);
-       if (self == NULL)
-               return NULL;
-
-       self->magic = LAP_MAGIC;
-
-       /* Make a binding between the layers */
-       self->netdev = dev;
-       self->qos_dev = qos;
-       /* Copy hardware name */
-       if(hw_name != NULL) {
-               strlcpy(self->hw_name, hw_name, sizeof(self->hw_name));
-       } else {
-               self->hw_name[0] = '\0';
-       }
-
-       /* FIXME: should we get our own field? */
-       dev->atalk_ptr = self;
-
-       self->state = LAP_OFFLINE;
-
-       /* Initialize transmit queue */
-       skb_queue_head_init(&self->txq);
-       skb_queue_head_init(&self->txq_ultra);
-       skb_queue_head_init(&self->wx_list);
-
-       /* My unique IrLAP device address! */
-       /* We don't want the broadcast address, neither the NULL address
-        * (most often used to signify "invalid"), and we don't want an
-        * address already in use (otherwise connect won't be able
-        * to select the proper link). - Jean II */
-       do {
-               get_random_bytes(&self->saddr, sizeof(self->saddr));
-       } while ((self->saddr == 0x0) || (self->saddr == BROADCAST) ||
-                (hashbin_lock_find(irlap, self->saddr, NULL)) );
-       /* Copy to the driver */
-       memcpy(dev->dev_addr, &self->saddr, 4);
-
-       init_timer(&self->slot_timer);
-       init_timer(&self->query_timer);
-       init_timer(&self->discovery_timer);
-       init_timer(&self->final_timer);
-       init_timer(&self->poll_timer);
-       init_timer(&self->wd_timer);
-       init_timer(&self->backoff_timer);
-       init_timer(&self->media_busy_timer);
-
-       irlap_apply_default_connection_parameters(self);
-
-       self->N3 = 3; /* # connections attempts to try before giving up */
-
-       self->state = LAP_NDM;
-
-       hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL);
-
-       irlmp_register_link(self, self->saddr, &self->notify);
-
-       return self;
-}
-EXPORT_SYMBOL(irlap_open);
-
-/*
- * Function __irlap_close (self)
- *
- *    Remove IrLAP and all allocated memory. Stop any pending timers.
- *
- */
-static void __irlap_close(struct irlap_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* Stop timers */
-       del_timer(&self->slot_timer);
-       del_timer(&self->query_timer);
-       del_timer(&self->discovery_timer);
-       del_timer(&self->final_timer);
-       del_timer(&self->poll_timer);
-       del_timer(&self->wd_timer);
-       del_timer(&self->backoff_timer);
-       del_timer(&self->media_busy_timer);
-
-       irlap_flush_all_queues(self);
-
-       self->magic = 0;
-
-       kfree(self);
-}
-
-/*
- * Function irlap_close (self)
- *
- *    Remove IrLAP instance
- *
- */
-void irlap_close(struct irlap_cb *self)
-{
-       struct irlap_cb *lap;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* We used to send a LAP_DISC_INDICATION here, but this was
-        * racy. This has been move within irlmp_unregister_link()
-        * itself. Jean II */
-
-       /* Kill the LAP and all LSAPs on top of it */
-       irlmp_unregister_link(self->saddr);
-       self->notify.instance = NULL;
-
-       /* Be sure that we manage to remove ourself from the hash */
-       lap = hashbin_remove(irlap, self->saddr, NULL);
-       if (!lap) {
-               pr_debug("%s(), Didn't find myself!\n", __func__);
-               return;
-       }
-       __irlap_close(lap);
-}
-EXPORT_SYMBOL(irlap_close);
-
-/*
- * Function irlap_connect_indication (self, skb)
- *
- *    Another device is attempting to make a connection
- *
- */
-void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       irlap_init_qos_capabilities(self, NULL); /* No user QoS! */
-
-       irlmp_link_connect_indication(self->notify.instance, self->saddr,
-                                     self->daddr, &self->qos_tx, skb);
-}
-
-/*
- * Function irlap_connect_response (self, skb)
- *
- *    Service user has accepted incoming connection
- *
- */
-void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata)
-{
-       irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL);
-}
-
-/*
- * Function irlap_connect_request (self, daddr, qos_user, sniff)
- *
- *    Request connection with another device, sniffing is not implemented
- *    yet.
- *
- */
-void irlap_connect_request(struct irlap_cb *self, __u32 daddr,
-                          struct qos_info *qos_user, int sniff)
-{
-       pr_debug("%s(), daddr=0x%08x\n", __func__, daddr);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       self->daddr = daddr;
-
-       /*
-        *  If the service user specifies QoS values for this connection,
-        *  then use them
-        */
-       irlap_init_qos_capabilities(self, qos_user);
-
-       if ((self->state == LAP_NDM) && !self->media_busy)
-               irlap_do_event(self, CONNECT_REQUEST, NULL, NULL);
-       else
-               self->connect_pending = TRUE;
-}
-
-/*
- * Function irlap_connect_confirm (self, skb)
- *
- *    Connection request has been accepted
- *
- */
-void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb);
-}
-
-/*
- * Function irlap_data_indication (self, skb)
- *
- *    Received data frames from IR-port, so we just pass them up to
- *    IrLMP for further processing
- *
- */
-void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb,
-                          int unreliable)
-{
-       /* Hide LAP header from IrLMP layer */
-       skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
-
-       irlmp_link_data_indication(self->notify.instance, skb, unreliable);
-}
-
-
-/*
- * Function irlap_data_request (self, skb)
- *
- *    Queue data for transmission, must wait until XMIT state
- *
- */
-void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb,
-                       int unreliable)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER),
-                   return;);
-       skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
-
-       /*
-        *  Must set frame format now so that the rest of the code knows
-        *  if its dealing with an I or an UI frame
-        */
-       if (unreliable)
-               skb->data[1] = UI_FRAME;
-       else
-               skb->data[1] = I_FRAME;
-
-       /* Don't forget to refcount it - see irlmp_connect_request(). */
-       skb_get(skb);
-
-       /* Add at the end of the queue (keep ordering) - Jean II */
-       skb_queue_tail(&self->txq, skb);
-
-       /*
-        *  Send event if this frame only if we are in the right state
-        *  FIXME: udata should be sent first! (skb_queue_head?)
-        */
-       if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) {
-               /* If we are not already processing the Tx queue, trigger
-                * transmission immediately - Jean II */
-               if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy))
-                       irlap_do_event(self, DATA_REQUEST, skb, NULL);
-               /* Otherwise, the packets will be sent normally at the
-                * next pf-poll - Jean II */
-       }
-}
-
-/*
- * Function irlap_unitdata_request (self, skb)
- *
- *    Send Ultra data. This is data that must be sent outside any connection
- *
- */
-#ifdef CONFIG_IRDA_ULTRA
-void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER),
-              return;);
-       skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
-
-       skb->data[0] = CBROADCAST;
-       skb->data[1] = UI_FRAME;
-
-       /* Don't need to refcount, see irlmp_connless_data_request() */
-
-       skb_queue_tail(&self->txq_ultra, skb);
-
-       irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);
-}
-#endif /*CONFIG_IRDA_ULTRA */
-
-/*
- * Function irlap_udata_indication (self, skb)
- *
- *    Receive Ultra data. This is data that is received outside any connection
- *
- */
-#ifdef CONFIG_IRDA_ULTRA
-void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       /* Hide LAP header from IrLMP layer */
-       skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
-
-       irlmp_link_unitdata_indication(self->notify.instance, skb);
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irlap_disconnect_request (void)
- *
- *    Request to disconnect connection by service user
- */
-void irlap_disconnect_request(struct irlap_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* Don't disconnect until all data frames are successfully sent */
-       if (!skb_queue_empty(&self->txq)) {
-               self->disconnect_pending = TRUE;
-               return;
-       }
-
-       /* Check if we are in the right state for disconnecting */
-       switch (self->state) {
-       case LAP_XMIT_P:        /* FALLTHROUGH */
-       case LAP_XMIT_S:        /* FALLTHROUGH */
-       case LAP_CONN:          /* FALLTHROUGH */
-       case LAP_RESET_WAIT:    /* FALLTHROUGH */
-       case LAP_RESET_CHECK:
-               irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL);
-               break;
-       default:
-               pr_debug("%s(), disconnect pending!\n", __func__);
-               self->disconnect_pending = TRUE;
-               break;
-       }
-}
-
-/*
- * Function irlap_disconnect_indication (void)
- *
- *    Disconnect request from other device
- *
- */
-void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason)
-{
-       pr_debug("%s(), reason=%s\n", __func__, lap_reasons[reason]);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* Flush queues */
-       irlap_flush_all_queues(self);
-
-       switch (reason) {
-       case LAP_RESET_INDICATION:
-               pr_debug("%s(), Sending reset request!\n", __func__);
-               irlap_do_event(self, RESET_REQUEST, NULL, NULL);
-               break;
-       case LAP_NO_RESPONSE:      /* FALLTHROUGH */
-       case LAP_DISC_INDICATION:  /* FALLTHROUGH */
-       case LAP_FOUND_NONE:       /* FALLTHROUGH */
-       case LAP_MEDIA_BUSY:
-               irlmp_link_disconnect_indication(self->notify.instance, self,
-                                                reason, NULL);
-               break;
-       default:
-               net_err_ratelimited("%s: Unknown reason %d\n",
-                                   __func__, reason);
-       }
-}
-
-/*
- * Function irlap_discovery_request (gen_addr_bit)
- *
- *    Start one single discovery operation.
- *
- */
-void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
-{
-       struct irlap_info info;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-       IRDA_ASSERT(discovery != NULL, return;);
-
-       pr_debug("%s(), nslots = %d\n", __func__, discovery->nslots);
-
-       IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) ||
-                   (discovery->nslots == 8) || (discovery->nslots == 16),
-                   return;);
-
-       /* Discovery is only possible in NDM mode */
-       if (self->state != LAP_NDM) {
-               pr_debug("%s(), discovery only possible in NDM mode\n",
-                        __func__);
-               irlap_discovery_confirm(self, NULL);
-               /* Note : in theory, if we are not in NDM, we could postpone
-                * the discovery like we do for connection request.
-                * In practice, it's not worth it. If the media was busy,
-                * it's likely next time around it won't be busy. If we are
-                * in REPLY state, we will get passive discovery info & event.
-                * Jean II */
-               return;
-       }
-
-       /* Check if last discovery request finished in time, or if
-        * it was aborted due to the media busy flag. */
-       if (self->discovery_log != NULL) {
-               hashbin_delete(self->discovery_log, (FREE_FUNC) kfree);
-               self->discovery_log = NULL;
-       }
-
-       /* All operations will occur at predictable time, no need to lock */
-       self->discovery_log = hashbin_new(HB_NOLOCK);
-
-       if (self->discovery_log == NULL) {
-               net_warn_ratelimited("%s(), Unable to allocate discovery log!\n",
-                                    __func__);
-               return;
-       }
-
-       info.S = discovery->nslots; /* Number of slots */
-       info.s = 0; /* Current slot */
-
-       self->discovery_cmd = discovery;
-       info.discovery = discovery;
-
-       /* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */
-       self->slot_timeout = msecs_to_jiffies(sysctl_slot_timeout);
-
-       irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info);
-}
-
-/*
- * Function irlap_discovery_confirm (log)
- *
- *    A device has been discovered in front of this station, we
- *    report directly to LMP.
- */
-void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       IRDA_ASSERT(self->notify.instance != NULL, return;);
-
-       /*
-        * Check for successful discovery, since we are then allowed to clear
-        * the media busy condition (IrLAP 6.13.4 - p.94). This should allow
-        * us to make connection attempts much faster and easier (i.e. no
-        * collisions).
-        * Setting media busy to false will also generate an event allowing
-        * to process pending events in NDM state machine.
-        * Note : the spec doesn't define what's a successful discovery is.
-        * If we want Ultra to work, it's successful even if there is
-        * nobody discovered - Jean II
-        */
-       if (discovery_log)
-               irda_device_set_media_busy(self->netdev, FALSE);
-
-       /* Inform IrLMP */
-       irlmp_link_discovery_confirm(self->notify.instance, discovery_log);
-}
-
-/*
- * Function irlap_discovery_indication (log)
- *
- *    Somebody is trying to discover us!
- *
- */
-void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-       IRDA_ASSERT(discovery != NULL, return;);
-
-       IRDA_ASSERT(self->notify.instance != NULL, return;);
-
-       /* A device is very likely to connect immediately after it performs
-        * a successful discovery. This means that in our case, we are much
-        * more likely to receive a connection request over the medium.
-        * So, we backoff to avoid collisions.
-        * IrLAP spec 6.13.4 suggest 100ms...
-        * Note : this little trick actually make a *BIG* difference. If I set
-        * my Linux box with discovery enabled and one Ultra frame sent every
-        * second, my Palm has no trouble connecting to it every time !
-        * Jean II */
-       irda_device_set_media_busy(self->netdev, SMALL);
-
-       irlmp_link_discovery_indication(self->notify.instance, discovery);
-}
-
-/*
- * Function irlap_status_indication (quality_of_link)
- */
-void irlap_status_indication(struct irlap_cb *self, int quality_of_link)
-{
-       switch (quality_of_link) {
-       case STATUS_NO_ACTIVITY:
-               net_info_ratelimited("IrLAP, no activity on link!\n");
-               break;
-       case STATUS_NOISY:
-               net_info_ratelimited("IrLAP, noisy link!\n");
-               break;
-       default:
-               break;
-       }
-       irlmp_status_indication(self->notify.instance,
-                               quality_of_link, LOCK_NO_CHANGE);
-}
-
-/*
- * Function irlap_reset_indication (void)
- */
-void irlap_reset_indication(struct irlap_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       if (self->state == LAP_RESET_WAIT)
-               irlap_do_event(self, RESET_REQUEST, NULL, NULL);
-       else
-               irlap_do_event(self, RESET_RESPONSE, NULL, NULL);
-}
-
-/*
- * Function irlap_reset_confirm (void)
- */
-void irlap_reset_confirm(void)
-{
-}
-
-/*
- * Function irlap_generate_rand_time_slot (S, s)
- *
- *    Generate a random time slot between s and S-1 where
- *    S = Number of slots (0 -> S-1)
- *    s = Current slot
- */
-int irlap_generate_rand_time_slot(int S, int s)
-{
-       static int rand;
-       int slot;
-
-       IRDA_ASSERT((S - s) > 0, return 0;);
-
-       rand += jiffies;
-       rand ^= (rand << 12);
-       rand ^= (rand >> 20);
-
-       slot = s + rand % (S-s);
-
-       IRDA_ASSERT((slot >= s) || (slot < S), return 0;);
-
-       return slot;
-}
-
-/*
- * Function irlap_update_nr_received (nr)
- *
- *    Remove all acknowledged frames in current window queue. This code is
- *    not intuitive and you should not try to change it. If you think it
- *    contains bugs, please mail a patch to the author instead.
- */
-void irlap_update_nr_received(struct irlap_cb *self, int nr)
-{
-       struct sk_buff *skb = NULL;
-       int count = 0;
-
-       /*
-        * Remove all the ack-ed frames from the window queue.
-        */
-
-       /*
-        *  Optimize for the common case. It is most likely that the receiver
-        *  will acknowledge all the frames we have sent! So in that case we
-        *  delete all frames stored in window.
-        */
-       if (nr == self->vs) {
-               while ((skb = skb_dequeue(&self->wx_list)) != NULL) {
-                       dev_kfree_skb(skb);
-               }
-               /* The last acked frame is the next to send minus one */
-               self->va = nr - 1;
-       } else {
-               /* Remove all acknowledged frames in current window */
-               while ((skb_peek(&self->wx_list) != NULL) &&
-                      (((self->va+1) % 8) != nr))
-               {
-                       skb = skb_dequeue(&self->wx_list);
-                       dev_kfree_skb(skb);
-
-                       self->va = (self->va + 1) % 8;
-                       count++;
-               }
-       }
-
-       /* Advance window */
-       self->window = self->window_size - skb_queue_len(&self->wx_list);
-}
-
-/*
- * Function irlap_validate_ns_received (ns)
- *
- *    Validate the next to send (ns) field from received frame.
- */
-int irlap_validate_ns_received(struct irlap_cb *self, int ns)
-{
-       /*  ns as expected?  */
-       if (ns == self->vr)
-               return NS_EXPECTED;
-       /*
-        *  Stations are allowed to treat invalid NS as unexpected NS
-        *  IrLAP, Recv ... with-invalid-Ns. p. 84
-        */
-       return NS_UNEXPECTED;
-
-       /* return NR_INVALID; */
-}
-/*
- * Function irlap_validate_nr_received (nr)
- *
- *    Validate the next to receive (nr) field from received frame.
- *
- */
-int irlap_validate_nr_received(struct irlap_cb *self, int nr)
-{
-       /*  nr as expected?  */
-       if (nr == self->vs) {
-               pr_debug("%s(), expected!\n", __func__);
-               return NR_EXPECTED;
-       }
-
-       /*
-        *  unexpected nr? (but within current window), first we check if the
-        *  ns numbers of the frames in the current window wrap.
-        */
-       if (self->va < self->vs) {
-               if ((nr >= self->va) && (nr <= self->vs))
-                       return NR_UNEXPECTED;
-       } else {
-               if ((nr >= self->va) || (nr <= self->vs))
-                       return NR_UNEXPECTED;
-       }
-
-       /* Invalid nr!  */
-       return NR_INVALID;
-}
-
-/*
- * Function irlap_initiate_connection_state ()
- *
- *    Initialize the connection state parameters
- *
- */
-void irlap_initiate_connection_state(struct irlap_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* Next to send and next to receive */
-       self->vs = self->vr = 0;
-
-       /* Last frame which got acked (0 - 1) % 8 */
-       self->va = 7;
-
-       self->window = 1;
-
-       self->remote_busy = FALSE;
-       self->retry_count = 0;
-}
-
-/*
- * Function irlap_wait_min_turn_around (self, qos)
- *
- *    Wait negotiated minimum turn around time, this function actually sets
- *    the number of BOS's that must be sent before the next transmitted
- *    frame in order to delay for the specified amount of time. This is
- *    done to avoid using timers, and the forbidden udelay!
- */
-void irlap_wait_min_turn_around(struct irlap_cb *self, struct qos_info *qos)
-{
-       __u32 min_turn_time;
-       __u32 speed;
-
-       /* Get QoS values.  */
-       speed = qos->baud_rate.value;
-       min_turn_time = qos->min_turn_time.value;
-
-       /* No need to calculate XBOFs for speeds over 115200 bps */
-       if (speed > 115200) {
-               self->mtt_required = min_turn_time;
-               return;
-       }
-
-       /*
-        *  Send additional BOF's for the next frame for the requested
-        *  min turn time, so now we must calculate how many chars (XBOF's) we
-        *  must send for the requested time period (min turn time)
-        */
-       self->xbofs_delay = irlap_min_turn_time_in_bytes(speed, min_turn_time);
-}
-
-/*
- * Function irlap_flush_all_queues (void)
- *
- *    Flush all queues
- *
- */
-void irlap_flush_all_queues(struct irlap_cb *self)
-{
-       struct sk_buff* skb;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* Free transmission queue */
-       while ((skb = skb_dequeue(&self->txq)) != NULL)
-               dev_kfree_skb(skb);
-
-       while ((skb = skb_dequeue(&self->txq_ultra)) != NULL)
-               dev_kfree_skb(skb);
-
-       /* Free sliding window buffered packets */
-       while ((skb = skb_dequeue(&self->wx_list)) != NULL)
-               dev_kfree_skb(skb);
-}
-
-/*
- * Function irlap_setspeed (self, speed)
- *
- *    Change the speed of the IrDA port
- *
- */
-static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now)
-{
-       struct sk_buff *skb;
-
-       pr_debug("%s(), setting speed to %d\n", __func__, speed);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       self->speed = speed;
-
-       /* Change speed now, or just piggyback speed on frames */
-       if (now) {
-               /* Send down empty frame to trigger speed change */
-               skb = alloc_skb(0, GFP_ATOMIC);
-               if (skb)
-                       irlap_queue_xmit(self, skb);
-       }
-}
-
-/*
- * Function irlap_init_qos_capabilities (self, qos)
- *
- *    Initialize QoS for this IrLAP session, What we do is to compute the
- *    intersection of the QoS capabilities for the user, driver and for
- *    IrLAP itself. Normally, IrLAP will not specify any values, but it can
- *    be used to restrict certain values.
- */
-static void irlap_init_qos_capabilities(struct irlap_cb *self,
-                                       struct qos_info *qos_user)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-       IRDA_ASSERT(self->netdev != NULL, return;);
-
-       /* Start out with the maximum QoS support possible */
-       irda_init_max_qos_capabilies(&self->qos_rx);
-
-       /* Apply drivers QoS capabilities */
-       irda_qos_compute_intersection(&self->qos_rx, self->qos_dev);
-
-       /*
-        *  Check for user supplied QoS parameters. The service user is only
-        *  allowed to supply these values. We check each parameter since the
-        *  user may not have set all of them.
-        */
-       if (qos_user) {
-               pr_debug("%s(), Found user specified QoS!\n", __func__);
-
-               if (qos_user->baud_rate.bits)
-                       self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits;
-
-               if (qos_user->max_turn_time.bits)
-                       self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits;
-               if (qos_user->data_size.bits)
-                       self->qos_rx.data_size.bits &= qos_user->data_size.bits;
-
-               if (qos_user->link_disc_time.bits)
-                       self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits;
-       }
-
-       /* Use 500ms in IrLAP for now */
-       self->qos_rx.max_turn_time.bits &= 0x01;
-
-       /* Set data size */
-       /*self->qos_rx.data_size.bits &= 0x03;*/
-
-       irda_qos_bits_to_value(&self->qos_rx);
-}
-
-/*
- * Function irlap_apply_default_connection_parameters (void, now)
- *
- *    Use the default connection and transmission parameters
- */
-void irlap_apply_default_connection_parameters(struct irlap_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* xbofs : Default value in NDM */
-       self->next_bofs   = 12;
-       self->bofs_count  = 12;
-
-       /* NDM Speed is 9600 */
-       irlap_change_speed(self, 9600, TRUE);
-
-       /* Set mbusy when going to NDM state */
-       irda_device_set_media_busy(self->netdev, TRUE);
-
-       /*
-        * Generate random connection address for this session, which must
-        * be 7 bits wide and different from 0x00 and 0xfe
-        */
-       while ((self->caddr == 0x00) || (self->caddr == 0xfe)) {
-               get_random_bytes(&self->caddr, sizeof(self->caddr));
-               self->caddr &= 0xfe;
-       }
-
-       /* Use default values until connection has been negitiated */
-       self->slot_timeout = sysctl_slot_timeout;
-       self->final_timeout = FINAL_TIMEOUT;
-       self->poll_timeout = POLL_TIMEOUT;
-       self->wd_timeout = WD_TIMEOUT;
-
-       /* Set some default values */
-       self->qos_tx.baud_rate.value = 9600;
-       self->qos_rx.baud_rate.value = 9600;
-       self->qos_tx.max_turn_time.value = 0;
-       self->qos_rx.max_turn_time.value = 0;
-       self->qos_tx.min_turn_time.value = 0;
-       self->qos_rx.min_turn_time.value = 0;
-       self->qos_tx.data_size.value = 64;
-       self->qos_rx.data_size.value = 64;
-       self->qos_tx.window_size.value = 1;
-       self->qos_rx.window_size.value = 1;
-       self->qos_tx.additional_bofs.value = 12;
-       self->qos_rx.additional_bofs.value = 12;
-       self->qos_tx.link_disc_time.value = 0;
-       self->qos_rx.link_disc_time.value = 0;
-
-       irlap_flush_all_queues(self);
-
-       self->disconnect_pending = FALSE;
-       self->connect_pending = FALSE;
-}
-
-/*
- * Function irlap_apply_connection_parameters (qos, now)
- *
- *    Initialize IrLAP with the negotiated QoS values
- *
- * If 'now' is false, the speed and xbofs will be changed after the next
- * frame is sent.
- * If 'now' is true, the speed and xbofs is changed immediately
- */
-void irlap_apply_connection_parameters(struct irlap_cb *self, int now)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* Set the negotiated xbofs value */
-       self->next_bofs   = self->qos_tx.additional_bofs.value;
-       if (now)
-               self->bofs_count = self->next_bofs;
-
-       /* Set the negotiated link speed (may need the new xbofs value) */
-       irlap_change_speed(self, self->qos_tx.baud_rate.value, now);
-
-       self->window_size = self->qos_tx.window_size.value;
-       self->window      = self->qos_tx.window_size.value;
-
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
-       /*
-        *  Calculate how many bytes it is possible to transmit before the
-        *  link must be turned around
-        */
-       self->line_capacity =
-               irlap_max_line_capacity(self->qos_tx.baud_rate.value,
-                                       self->qos_tx.max_turn_time.value);
-       self->bytes_left = self->line_capacity;
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-
-
-       /*
-        *  Initialize timeout values, some of the rules are listed on
-        *  page 92 in IrLAP.
-        */
-       IRDA_ASSERT(self->qos_tx.max_turn_time.value != 0, return;);
-       IRDA_ASSERT(self->qos_rx.max_turn_time.value != 0, return;);
-       /* The poll timeout applies only to the primary station.
-        * It defines the maximum time the primary stay in XMIT mode
-        * before timeout and turning the link around (sending a RR).
-        * Or, this is how much we can keep the pf bit in primary mode.
-        * Therefore, it must be lower or equal than our *OWN* max turn around.
-        * Jean II */
-       self->poll_timeout = msecs_to_jiffies(
-                               self->qos_tx.max_turn_time.value);
-       /* The Final timeout applies only to the primary station.
-        * It defines the maximum time the primary wait (mostly in RECV mode)
-        * for an answer from the secondary station before polling it again.
-        * Therefore, it must be greater or equal than our *PARTNER*
-        * max turn around time - Jean II */
-       self->final_timeout = msecs_to_jiffies(
-                               self->qos_rx.max_turn_time.value);
-       /* The Watchdog Bit timeout applies only to the secondary station.
-        * It defines the maximum time the secondary wait (mostly in RECV mode)
-        * for poll from the primary station before getting annoyed.
-        * Therefore, it must be greater or equal than our *PARTNER*
-        * max turn around time - Jean II */
-       self->wd_timeout = self->final_timeout * 2;
-
-       /*
-        * N1 and N2 are maximum retry count for *both* the final timer
-        * and the wd timer (with a factor 2) as defined above.
-        * After N1 retry of a timer, we give a warning to the user.
-        * After N2 retry, we consider the link dead and disconnect it.
-        * Jean II
-        */
-
-       /*
-        *  Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to
-        *  3 seconds otherwise. See page 71 in IrLAP for more details.
-        *  Actually, it's not always 3 seconds, as we allow to set
-        *  it via sysctl... Max maxtt is 500ms, and N1 need to be multiple
-        *  of 2, so 1 second is minimum we can allow. - Jean II
-        */
-       if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time)
-               /*
-                * If we set N1 to 0, it will trigger immediately, which is
-                * not what we want. What we really want is to disable it,
-                * Jean II
-                */
-               self->N1 = -2; /* Disable - Need to be multiple of 2*/
-       else
-               self->N1 = sysctl_warn_noreply_time * 1000 /
-                 self->qos_rx.max_turn_time.value;
-
-       pr_debug("Setting N1 = %d\n", self->N1);
-
-       /* Set N2 to match our own disconnect time */
-       self->N2 = self->qos_tx.link_disc_time.value * 1000 /
-               self->qos_rx.max_turn_time.value;
-       pr_debug("Setting N2 = %d\n", self->N2);
-}
-
-#ifdef CONFIG_PROC_FS
-struct irlap_iter_state {
-       int id;
-};
-
-static void *irlap_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       struct irlap_iter_state *iter = seq->private;
-       struct irlap_cb *self;
-
-       /* Protect our access to the tsap list */
-       spin_lock_irq(&irlap->hb_spinlock);
-       iter->id = 0;
-
-       for (self = (struct irlap_cb *) hashbin_get_first(irlap);
-            self; self = (struct irlap_cb *) hashbin_get_next(irlap)) {
-               if (iter->id == *pos)
-                       break;
-               ++iter->id;
-       }
-
-       return self;
-}
-
-static void *irlap_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-       struct irlap_iter_state *iter = seq->private;
-
-       ++*pos;
-       ++iter->id;
-       return (void *) hashbin_get_next(irlap);
-}
-
-static void irlap_seq_stop(struct seq_file *seq, void *v)
-{
-       spin_unlock_irq(&irlap->hb_spinlock);
-}
-
-static int irlap_seq_show(struct seq_file *seq, void *v)
-{
-       const struct irlap_iter_state *iter = seq->private;
-       const struct irlap_cb *self = v;
-
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -EINVAL;);
-
-       seq_printf(seq, "irlap%d ", iter->id);
-       seq_printf(seq, "state: %s\n",
-                  irlap_state[self->state]);
-
-       seq_printf(seq, "  device name: %s, ",
-                  (self->netdev) ? self->netdev->name : "bug");
-       seq_printf(seq, "hardware name: %s\n", self->hw_name);
-
-       seq_printf(seq, "  caddr: %#02x, ", self->caddr);
-       seq_printf(seq, "saddr: %#08x, ", self->saddr);
-       seq_printf(seq, "daddr: %#08x\n", self->daddr);
-
-       seq_printf(seq, "  win size: %d, ",
-                  self->window_size);
-       seq_printf(seq, "win: %d, ", self->window);
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
-       seq_printf(seq, "line capacity: %d, ",
-                  self->line_capacity);
-       seq_printf(seq, "bytes left: %d\n", self->bytes_left);
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-       seq_printf(seq, "  tx queue len: %d ",
-                  skb_queue_len(&self->txq));
-       seq_printf(seq, "win queue len: %d ",
-                  skb_queue_len(&self->wx_list));
-       seq_printf(seq, "rbusy: %s", self->remote_busy ?
-                  "TRUE" : "FALSE");
-       seq_printf(seq, " mbusy: %s\n", self->media_busy ?
-                  "TRUE" : "FALSE");
-
-       seq_printf(seq, "  retrans: %d ", self->retry_count);
-       seq_printf(seq, "vs: %d ", self->vs);
-       seq_printf(seq, "vr: %d ", self->vr);
-       seq_printf(seq, "va: %d\n", self->va);
-
-       seq_printf(seq, "  qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n");
-
-       seq_printf(seq, "  tx\t%d\t",
-                  self->qos_tx.baud_rate.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_tx.max_turn_time.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_tx.data_size.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_tx.window_size.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_tx.additional_bofs.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_tx.min_turn_time.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_tx.link_disc_time.value);
-       seq_printf(seq, "\n");
-
-       seq_printf(seq, "  rx\t%d\t",
-                  self->qos_rx.baud_rate.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_rx.max_turn_time.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_rx.data_size.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_rx.window_size.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_rx.additional_bofs.value);
-       seq_printf(seq, "%d\t",
-                  self->qos_rx.min_turn_time.value);
-       seq_printf(seq, "%d\n",
-                  self->qos_rx.link_disc_time.value);
-
-       return 0;
-}
-
-static const struct seq_operations irlap_seq_ops = {
-       .start  = irlap_seq_start,
-       .next   = irlap_seq_next,
-       .stop   = irlap_seq_stop,
-       .show   = irlap_seq_show,
-};
-
-static int irlap_seq_open(struct inode *inode, struct file *file)
-{
-       if (irlap == NULL)
-               return -EINVAL;
-
-       return seq_open_private(file, &irlap_seq_ops,
-                       sizeof(struct irlap_iter_state));
-}
-
-const struct file_operations irlap_seq_fops = {
-       .owner          = THIS_MODULE,
-       .open           = irlap_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release_private,
-};
-
-#endif /* CONFIG_PROC_FS */
diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c
deleted file mode 100644 (file)
index 0e1b4d7..0000000
+++ /dev/null
@@ -1,2316 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlap_event.c
- * Version:       0.9
- * Description:   IrLAP state machine implementation
- * Status:        Experimental.
- * Author:        Dag Brattli <dag@brattli.net>
- * Created at:    Sat Aug 16 00:59:29 1997
- * Modified at:   Sat Dec 25 21:07:57 1999
- * Modified by:   Dag Brattli <dag@brattli.net>
- *
- *     Copyright (c) 1998-2000 Dag Brattli <dag@brattli.net>,
- *     Copyright (c) 1998      Thomas Davis <ratbert@radiks.net>
- *     All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlap_event.h>
-
-#include <net/irda/timer.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlap_frame.h>
-#include <net/irda/qos.h>
-#include <net/irda/parameters.h>
-#include <net/irda/irlmp.h>            /* irlmp_flow_indication(), ... */
-
-#include <net/irda/irda_device.h>
-
-#ifdef CONFIG_IRDA_FAST_RR
-int sysctl_fast_poll_increase = 50;
-#endif
-
-static int irlap_state_ndm    (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_query  (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reply  (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_conn   (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_setup  (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_xmit_p (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_pclose (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_nrm_p  (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
-                                 struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reset  (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_nrm_s  (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_xmit_s (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info);
-static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event,
-                                  struct sk_buff *, struct irlap_info *);
-
-static const char *const irlap_event[] __maybe_unused = {
-       "DISCOVERY_REQUEST",
-       "CONNECT_REQUEST",
-       "CONNECT_RESPONSE",
-       "DISCONNECT_REQUEST",
-       "DATA_REQUEST",
-       "RESET_REQUEST",
-       "RESET_RESPONSE",
-       "SEND_I_CMD",
-       "SEND_UI_FRAME",
-       "RECV_DISCOVERY_XID_CMD",
-       "RECV_DISCOVERY_XID_RSP",
-       "RECV_SNRM_CMD",
-       "RECV_TEST_CMD",
-       "RECV_TEST_RSP",
-       "RECV_UA_RSP",
-       "RECV_DM_RSP",
-       "RECV_RD_RSP",
-       "RECV_I_CMD",
-       "RECV_I_RSP",
-       "RECV_UI_FRAME",
-       "RECV_FRMR_RSP",
-       "RECV_RR_CMD",
-       "RECV_RR_RSP",
-       "RECV_RNR_CMD",
-       "RECV_RNR_RSP",
-       "RECV_REJ_CMD",
-       "RECV_REJ_RSP",
-       "RECV_SREJ_CMD",
-       "RECV_SREJ_RSP",
-       "RECV_DISC_CMD",
-       "SLOT_TIMER_EXPIRED",
-       "QUERY_TIMER_EXPIRED",
-       "FINAL_TIMER_EXPIRED",
-       "POLL_TIMER_EXPIRED",
-       "DISCOVERY_TIMER_EXPIRED",
-       "WD_TIMER_EXPIRED",
-       "BACKOFF_TIMER_EXPIRED",
-       "MEDIA_BUSY_TIMER_EXPIRED",
-};
-
-const char *const irlap_state[] = {
-       "LAP_NDM",
-       "LAP_QUERY",
-       "LAP_REPLY",
-       "LAP_CONN",
-       "LAP_SETUP",
-       "LAP_OFFLINE",
-       "LAP_XMIT_P",
-       "LAP_PCLOSE",
-       "LAP_NRM_P",
-       "LAP_RESET_WAIT",
-       "LAP_RESET",
-       "LAP_NRM_S",
-       "LAP_XMIT_S",
-       "LAP_SCLOSE",
-       "LAP_RESET_CHECK",
-};
-
-static int (*state[])(struct irlap_cb *self, IRLAP_EVENT event,
-                     struct sk_buff *skb, struct irlap_info *info) =
-{
-       irlap_state_ndm,
-       irlap_state_query,
-       irlap_state_reply,
-       irlap_state_conn,
-       irlap_state_setup,
-       irlap_state_offline,
-       irlap_state_xmit_p,
-       irlap_state_pclose,
-       irlap_state_nrm_p,
-       irlap_state_reset_wait,
-       irlap_state_reset,
-       irlap_state_nrm_s,
-       irlap_state_xmit_s,
-       irlap_state_sclose,
-       irlap_state_reset_check,
-};
-
-/*
- * Function irda_poll_timer_expired (data)
- *
- *    Poll timer has expired. Normally we must now send a RR frame to the
- *    remote device
- */
-static void irlap_poll_timer_expired(void *data)
-{
-       struct irlap_cb *self = (struct irlap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Calculate and set time before we will have to send back the pf bit
- * to the peer. Use in primary.
- * Make sure that state is XMIT_P/XMIT_S when calling this function
- * (and that nobody messed up with the state). - Jean II
- */
-static void irlap_start_poll_timer(struct irlap_cb *self, int timeout)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-#ifdef CONFIG_IRDA_FAST_RR
-       /*
-        * Send out the RR frames faster if our own transmit queue is empty, or
-        * if the peer is busy. The effect is a much faster conversation
-        */
-       if (skb_queue_empty(&self->txq) || self->remote_busy) {
-               if (self->fast_RR == TRUE) {
-                       /*
-                        *  Assert that the fast poll timer has not reached the
-                        *  normal poll timer yet
-                        */
-                       if (self->fast_RR_timeout < timeout) {
-                               /*
-                                *  FIXME: this should be a more configurable
-                                *         function
-                                */
-                               self->fast_RR_timeout +=
-                                       (sysctl_fast_poll_increase * HZ/1000);
-
-                               /* Use this fast(er) timeout instead */
-                               timeout = self->fast_RR_timeout;
-                       }
-               } else {
-                       self->fast_RR = TRUE;
-
-                       /* Start with just 0 ms */
-                       self->fast_RR_timeout = 0;
-                       timeout = 0;
-               }
-       } else
-               self->fast_RR = FALSE;
-
-       pr_debug("%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies);
-#endif /* CONFIG_IRDA_FAST_RR */
-
-       if (timeout == 0)
-               irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);
-       else
-               irda_start_timer(&self->poll_timer, timeout, self,
-                                irlap_poll_timer_expired);
-}
-
-/*
- * Function irlap_do_event (event, skb, info)
- *
- *    Rushes through the state machine without any delay. If state == XMIT
- *    then send queued data frames.
- */
-void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
-                   struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret;
-
-       if (!self || self->magic != LAP_MAGIC)
-               return;
-
-       pr_debug("%s(), event = %s, state = %s\n", __func__,
-                irlap_event[event], irlap_state[self->state]);
-
-       ret = (*state[self->state])(self, event, skb, info);
-
-       /*
-        *  Check if there are any pending events that needs to be executed
-        */
-       switch (self->state) {
-       case LAP_XMIT_P: /* FALLTHROUGH */
-       case LAP_XMIT_S:
-               /*
-                * We just received the pf bit and are at the beginning
-                * of a new LAP transmit window.
-                * Check if there are any queued data frames, and do not
-                * try to disconnect link if we send any data frames, since
-                * that will change the state away form XMIT
-                */
-               pr_debug("%s() : queue len = %d\n", __func__,
-                        skb_queue_len(&self->txq));
-
-               if (!skb_queue_empty(&self->txq)) {
-                       /* Prevent race conditions with irlap_data_request() */
-                       self->local_busy = TRUE;
-
-                       /* Theory of operation.
-                        * We send frames up to when we fill the window or
-                        * reach line capacity. Those frames will queue up
-                        * in the device queue, and the driver will slowly
-                        * send them.
-                        * After each frame that we send, we poll the higher
-                        * layer for more data. It's the right time to do
-                        * that because the link layer need to perform the mtt
-                        * and then send the first frame, so we can afford
-                        * to send a bit of time in kernel space.
-                        * The explicit flow indication allow to minimise
-                        * buffers (== lower latency), to avoid higher layer
-                        * polling via timers (== less context switches) and
-                        * to implement a crude scheduler - Jean II */
-
-                       /* Try to send away all queued data frames */
-                       while ((skb = skb_dequeue(&self->txq)) != NULL) {
-                               /* Send one frame */
-                               ret = (*state[self->state])(self, SEND_I_CMD,
-                                                           skb, NULL);
-                               /* Drop reference count.
-                                * It will be increase as needed in
-                                * irlap_send_data_xxx() */
-                               kfree_skb(skb);
-
-                               /* Poll the higher layers for one more frame */
-                               irlmp_flow_indication(self->notify.instance,
-                                                     FLOW_START);
-
-                               if (ret == -EPROTO)
-                                       break; /* Try again later! */
-                       }
-                       /* Finished transmitting */
-                       self->local_busy = FALSE;
-               } else if (self->disconnect_pending) {
-                       self->disconnect_pending = FALSE;
-
-                       ret = (*state[self->state])(self, DISCONNECT_REQUEST,
-                                                   NULL, NULL);
-               }
-               break;
-/*     case LAP_NDM: */
-/*     case LAP_CONN: */
-/*     case LAP_RESET_WAIT: */
-/*     case LAP_RESET_CHECK: */
-       default:
-               break;
-       }
-}
-
-/*
- * Function irlap_state_ndm (event, skb, frame)
- *
- *    NDM (Normal Disconnected Mode) state
- *
- */
-static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
-                          struct sk_buff *skb, struct irlap_info *info)
-{
-       discovery_t *discovery_rsp;
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case CONNECT_REQUEST:
-               IRDA_ASSERT(self->netdev != NULL, return -1;);
-
-               if (self->media_busy) {
-                       /* Note : this will never happen, because we test
-                        * media busy in irlap_connect_request() and
-                        * postpone the event... - Jean II */
-                       pr_debug("%s(), CONNECT_REQUEST: media busy!\n",
-                                __func__);
-
-                       /* Always switch state before calling upper layers */
-                       irlap_next_state(self, LAP_NDM);
-
-                       irlap_disconnect_indication(self, LAP_MEDIA_BUSY);
-               } else {
-                       irlap_send_snrm_frame(self, &self->qos_rx);
-
-                       /* Start Final-bit timer */
-                       irlap_start_final_timer(self, self->final_timeout);
-
-                       self->retry_count = 0;
-                       irlap_next_state(self, LAP_SETUP);
-               }
-               break;
-       case RECV_SNRM_CMD:
-               /* Check if the frame contains and I field */
-               if (info) {
-                       self->daddr = info->daddr;
-                       self->caddr = info->caddr;
-
-                       irlap_next_state(self, LAP_CONN);
-
-                       irlap_connect_indication(self, skb);
-               } else {
-                       pr_debug("%s(), SNRM frame does not contain an I field!\n",
-                                __func__);
-               }
-               break;
-       case DISCOVERY_REQUEST:
-               IRDA_ASSERT(info != NULL, return -1;);
-
-               if (self->media_busy) {
-                       pr_debug("%s(), DISCOVERY_REQUEST: media busy!\n",
-                                __func__);
-                       /* irlap->log.condition = MEDIA_BUSY; */
-
-                       /* This will make IrLMP try again */
-                       irlap_discovery_confirm(self, NULL);
-                       /* Note : the discovery log is not cleaned up here,
-                        * it will be done in irlap_discovery_request()
-                        * Jean II */
-                       return 0;
-               }
-
-               self->S = info->S;
-               self->s = info->s;
-               irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE,
-                                              info->discovery);
-               self->frame_sent = FALSE;
-               self->s++;
-
-               irlap_start_slot_timer(self, self->slot_timeout);
-               irlap_next_state(self, LAP_QUERY);
-               break;
-       case RECV_DISCOVERY_XID_CMD:
-               IRDA_ASSERT(info != NULL, return -1;);
-
-               /* Assert that this is not the final slot */
-               if (info->s <= info->S) {
-                       self->slot = irlap_generate_rand_time_slot(info->S,
-                                                                  info->s);
-                       if (self->slot == info->s) {
-                               discovery_rsp = irlmp_get_discovery_response();
-                               discovery_rsp->data.daddr = info->daddr;
-
-                               irlap_send_discovery_xid_frame(self, info->S,
-                                                              self->slot,
-                                                              FALSE,
-                                                              discovery_rsp);
-                               self->frame_sent = TRUE;
-                       } else
-                               self->frame_sent = FALSE;
-
-                       /*
-                        * Go to reply state until end of discovery to
-                        * inhibit our own transmissions. Set the timer
-                        * to not stay forever there... Jean II
-                        */
-                       irlap_start_query_timer(self, info->S, info->s);
-                       irlap_next_state(self, LAP_REPLY);
-               } else {
-               /* This is the final slot. How is it possible ?
-                * This would happen is both discoveries are just slightly
-                * offset (if they are in sync, all packets are lost).
-                * Most often, all the discovery requests will be received
-                * in QUERY state (see my comment there), except for the
-                * last frame that will come here.
-                * The big trouble when it happen is that active discovery
-                * doesn't happen, because nobody answer the discoveries
-                * frame of the other guy, so the log shows up empty.
-                * What should we do ?
-                * Not much. It's too late to answer those discovery frames,
-                * so we just pass the info to IrLMP who will put it in the
-                * log (and post an event).
-                * Another cause would be devices that do discovery much
-                * slower than us, however the latest fixes should minimise
-                * those cases...
-                * Jean II
-                */
-                       pr_debug("%s(), Receiving final discovery request, missed the discovery slots :-(\n",
-                                __func__);
-
-                       /* Last discovery request -> in the log */
-                       irlap_discovery_indication(self, info->discovery);
-               }
-               break;
-       case MEDIA_BUSY_TIMER_EXPIRED:
-               /* A bunch of events may be postponed because the media is
-                * busy (usually immediately after we close a connection),
-                * or while we are doing discovery (state query/reply).
-                * In all those cases, the media busy flag will be cleared
-                * when it's OK for us to process those postponed events.
-                * This event is not mentioned in the state machines in the
-                * IrLAP spec. It's because they didn't consider Ultra and
-                * postponing connection request is optional.
-                * Jean II */
-#ifdef CONFIG_IRDA_ULTRA
-               /* Send any pending Ultra frames if any */
-               if (!skb_queue_empty(&self->txq_ultra)) {
-                       /* We don't send the frame, just post an event.
-                        * Also, previously this code was in timer.c...
-                        * Jean II */
-                       ret = (*state[self->state])(self, SEND_UI_FRAME,
-                                                   NULL, NULL);
-               }
-#endif /* CONFIG_IRDA_ULTRA */
-               /* Check if we should try to connect.
-                * This code was previously in irlap_do_event() */
-               if (self->connect_pending) {
-                       self->connect_pending = FALSE;
-
-                       /* This one *should* not pend in this state, except
-                        * if a socket try to connect and immediately
-                        * disconnect. - clear - Jean II */
-                       if (self->disconnect_pending)
-                               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
-                       else
-                               ret = (*state[self->state])(self,
-                                                           CONNECT_REQUEST,
-                                                           NULL, NULL);
-                       self->disconnect_pending = FALSE;
-               }
-               /* Note : one way to test if this code works well (including
-                * media busy and small busy) is to create a user space
-                * application generating an Ultra packet every 3.05 sec (or
-                * 2.95 sec) and to see how it interact with discovery.
-                * It's fairly easy to check that no packet is lost, that the
-                * packets are postponed during discovery and that after
-                * discovery indication you have a 100ms "gap".
-                * As connection request and Ultra are now processed the same
-                * way, this avoid the tedious job of trying IrLAP connection
-                * in all those cases...
-                * Jean II */
-               break;
-#ifdef CONFIG_IRDA_ULTRA
-       case SEND_UI_FRAME:
-       {
-               int i;
-               /* Only allowed to repeat an operation twice */
-               for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) {
-                       skb = skb_dequeue(&self->txq_ultra);
-                       if (skb)
-                               irlap_send_ui_frame(self, skb, CBROADCAST,
-                                                   CMD_FRAME);
-                       else
-                               break;
-                       /* irlap_send_ui_frame() won't increase skb reference
-                        * count, so no dev_kfree_skb() - Jean II */
-               }
-               if (i == 2) {
-                       /* Force us to listen 500 ms again */
-                       irda_device_set_media_busy(self->netdev, TRUE);
-               }
-               break;
-       }
-       case RECV_UI_FRAME:
-               /* Only accept broadcast frames in NDM mode */
-               if (info->caddr != CBROADCAST) {
-                       pr_debug("%s(), not a broadcast frame!\n",
-                                __func__);
-               } else
-                       irlap_unitdata_indication(self, skb);
-               break;
-#endif /* CONFIG_IRDA_ULTRA */
-       case RECV_TEST_CMD:
-               /* Remove test frame header */
-               skb_pull(skb, sizeof(struct test_frame));
-
-               /*
-                * Send response. This skb will not be sent out again, and
-                * will only be used to send out the same info as the cmd
-                */
-               irlap_send_test_frame(self, CBROADCAST, info->daddr, skb);
-               break;
-       case RECV_TEST_RSP:
-               pr_debug("%s() not implemented!\n", __func__);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n", __func__,
-                        irlap_event[event]);
-
-               ret = -1;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_query (event, skb, info)
- *
- *    QUERY state
- *
- */
-static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
-                            struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case RECV_DISCOVERY_XID_RSP:
-               IRDA_ASSERT(info != NULL, return -1;);
-               IRDA_ASSERT(info->discovery != NULL, return -1;);
-
-               pr_debug("%s(), daddr=%08x\n", __func__,
-                        info->discovery->data.daddr);
-
-               if (!self->discovery_log) {
-                       net_warn_ratelimited("%s: discovery log is gone! maybe the discovery timeout has been set too short?\n",
-                                            __func__);
-                       break;
-               }
-               hashbin_insert(self->discovery_log,
-                              (irda_queue_t *) info->discovery,
-                              info->discovery->data.daddr, NULL);
-
-               /* Keep state */
-               /* irlap_next_state(self, LAP_QUERY);  */
-
-               break;
-       case RECV_DISCOVERY_XID_CMD:
-               /* Yes, it is possible to receive those frames in this mode.
-                * Note that most often the last discovery request won't
-                * occur here but in NDM state (see my comment there).
-                * What should we do ?
-                * Not much. We are currently performing our own discovery,
-                * therefore we can't answer those frames. We don't want
-                * to change state either. We just pass the info to
-                * IrLMP who will put it in the log (and post an event).
-                * Jean II
-                */
-
-               IRDA_ASSERT(info != NULL, return -1;);
-
-               pr_debug("%s(), Receiving discovery request (s = %d) while performing discovery :-(\n",
-                        __func__, info->s);
-
-               /* Last discovery request ? */
-               if (info->s == 0xff)
-                       irlap_discovery_indication(self, info->discovery);
-               break;
-       case SLOT_TIMER_EXPIRED:
-               /*
-                * Wait a little longer if we detect an incoming frame. This
-                * is not mentioned in the spec, but is a good thing to do,
-                * since we want to work even with devices that violate the
-                * timing requirements.
-                */
-               if (irda_device_is_receiving(self->netdev) && !self->add_wait) {
-                       pr_debug("%s(), device is slow to answer, waiting some more!\n",
-                                __func__);
-                       irlap_start_slot_timer(self, msecs_to_jiffies(10));
-                       self->add_wait = TRUE;
-                       return ret;
-               }
-               self->add_wait = FALSE;
-
-               if (self->s < self->S) {
-                       irlap_send_discovery_xid_frame(self, self->S,
-                                                      self->s, TRUE,
-                                                      self->discovery_cmd);
-                       self->s++;
-                       irlap_start_slot_timer(self, self->slot_timeout);
-
-                       /* Keep state */
-                       irlap_next_state(self, LAP_QUERY);
-               } else {
-                       /* This is the final slot! */
-                       irlap_send_discovery_xid_frame(self, self->S, 0xff,
-                                                      TRUE,
-                                                      self->discovery_cmd);
-
-                       /* Always switch state before calling upper layers */
-                       irlap_next_state(self, LAP_NDM);
-
-                       /*
-                        *  We are now finished with the discovery procedure,
-                        *  so now we must return the results
-                        */
-                       irlap_discovery_confirm(self, self->discovery_log);
-
-                       /* IrLMP should now have taken care of the log */
-                       self->discovery_log = NULL;
-               }
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n", __func__,
-                        irlap_event[event]);
-
-               ret = -1;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_reply (self, event, skb, info)
- *
- *    REPLY, we have received a XID discovery frame from a device and we
- *    are waiting for the right time slot to send a response XID frame
- *
- */
-static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
-                            struct sk_buff *skb, struct irlap_info *info)
-{
-       discovery_t *discovery_rsp;
-       int ret=0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case QUERY_TIMER_EXPIRED:
-               pr_debug("%s(), QUERY_TIMER_EXPIRED <%ld>\n",
-                        __func__, jiffies);
-               irlap_next_state(self, LAP_NDM);
-               break;
-       case RECV_DISCOVERY_XID_CMD:
-               IRDA_ASSERT(info != NULL, return -1;);
-               /* Last frame? */
-               if (info->s == 0xff) {
-                       del_timer(&self->query_timer);
-
-                       /* info->log.condition = REMOTE; */
-
-                       /* Always switch state before calling upper layers */
-                       irlap_next_state(self, LAP_NDM);
-
-                       irlap_discovery_indication(self, info->discovery);
-               } else {
-                       /* If it's our slot, send our reply */
-                       if ((info->s >= self->slot) && (!self->frame_sent)) {
-                               discovery_rsp = irlmp_get_discovery_response();
-                               discovery_rsp->data.daddr = info->daddr;
-
-                               irlap_send_discovery_xid_frame(self, info->S,
-                                                              self->slot,
-                                                              FALSE,
-                                                              discovery_rsp);
-
-                               self->frame_sent = TRUE;
-                       }
-                       /* Readjust our timer to accommodate devices
-                        * doing faster or slower discovery than us...
-                        * Jean II */
-                       irlap_start_query_timer(self, info->S, info->s);
-
-                       /* Keep state */
-                       //irlap_next_state(self, LAP_REPLY);
-               }
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d, %s\n", __func__,
-                        event, irlap_event[event]);
-
-               ret = -1;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_conn (event, skb, info)
- *
- *    CONN, we have received a SNRM command and is waiting for the upper
- *    layer to accept or refuse connection
- *
- */
-static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event,
-                           struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case CONNECT_RESPONSE:
-               skb_pull(skb, sizeof(struct snrm_frame));
-
-               IRDA_ASSERT(self->netdev != NULL, return -1;);
-
-               irlap_qos_negotiate(self, skb);
-
-               irlap_initiate_connection_state(self);
-
-               /*
-                * Applying the parameters now will make sure we change speed
-                * *after* we have sent the next frame
-                */
-               irlap_apply_connection_parameters(self, FALSE);
-
-               /*
-                * Sending this frame will force a speed change after it has
-                * been sent (i.e. the frame will be sent at 9600).
-                */
-               irlap_send_ua_response_frame(self, &self->qos_rx);
-
-#if 0
-               /*
-                * We are allowed to send two frames, but this may increase
-                * the connect latency, so lets not do it for now.
-                */
-               /* This is full of good intentions, but doesn't work in
-                * practice.
-                * After sending the first UA response, we switch the
-                * dongle to the negotiated speed, which is usually
-                * different than 9600 kb/s.
-                * From there, there is two solutions :
-                * 1) The other end has received the first UA response :
-                * it will set up the connection, move to state LAP_NRM_P,
-                * and will ignore and drop the second UA response.
-                * Actually, it's even worse : the other side will almost
-                * immediately send a RR that will likely collide with the
-                * UA response (depending on negotiated turnaround).
-                * 2) The other end has not received the first UA response,
-                * will stay at 9600 and will never see the second UA response.
-                * Jean II */
-               irlap_send_ua_response_frame(self, &self->qos_rx);
-#endif
-
-               /*
-                *  The WD-timer could be set to the duration of the P-timer
-                *  for this case, but it is recommended to use twice the
-                *  value (note 3 IrLAP p. 60).
-                */
-               irlap_start_wd_timer(self, self->wd_timeout);
-               irlap_next_state(self, LAP_NRM_S);
-
-               break;
-       case RECV_DISCOVERY_XID_CMD:
-               pr_debug("%s(), event RECV_DISCOVER_XID_CMD!\n",
-                        __func__);
-               irlap_next_state(self, LAP_NDM);
-
-               break;
-       case DISCONNECT_REQUEST:
-               pr_debug("%s(), Disconnect request!\n", __func__);
-               irlap_send_dm_frame(self);
-               irlap_next_state( self, LAP_NDM);
-               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d, %s\n", __func__,
-                        event, irlap_event[event]);
-
-               ret = -1;
-               break;
-       }
-
-       return ret;
-}
-
-/*
- * Function irlap_state_setup (event, skb, frame)
- *
- *    SETUP state, The local layer has transmitted a SNRM command frame to
- *    a remote peer layer and is awaiting a reply .
- *
- */
-static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
-                            struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case FINAL_TIMER_EXPIRED:
-               if (self->retry_count < self->N3) {
-/*
- *  Perform random backoff, Wait a random number of time units, minimum
- *  duration half the time taken to transmitt a SNRM frame, maximum duration
- *  1.5 times the time taken to transmit a SNRM frame. So this time should
- *  between 15 msecs and 45 msecs.
- */
-                       irlap_start_backoff_timer(self, msecs_to_jiffies(20 +
-                                                       (jiffies % 30)));
-               } else {
-                       /* Always switch state before calling upper layers */
-                       irlap_next_state(self, LAP_NDM);
-
-                       irlap_disconnect_indication(self, LAP_FOUND_NONE);
-               }
-               break;
-       case BACKOFF_TIMER_EXPIRED:
-               irlap_send_snrm_frame(self, &self->qos_rx);
-               irlap_start_final_timer(self, self->final_timeout);
-               self->retry_count++;
-               break;
-       case RECV_SNRM_CMD:
-               pr_debug("%s(), SNRM battle!\n", __func__);
-
-               IRDA_ASSERT(skb != NULL, return 0;);
-               IRDA_ASSERT(info != NULL, return 0;);
-
-               /*
-                *  The device with the largest device address wins the battle
-                *  (both have sent a SNRM command!)
-                */
-               if (info &&(info->daddr > self->saddr)) {
-                       del_timer(&self->final_timer);
-                       irlap_initiate_connection_state(self);
-
-                       IRDA_ASSERT(self->netdev != NULL, return -1;);
-
-                       skb_pull(skb, sizeof(struct snrm_frame));
-
-                       irlap_qos_negotiate(self, skb);
-
-                       /* Send UA frame and then change link settings */
-                       irlap_apply_connection_parameters(self, FALSE);
-                       irlap_send_ua_response_frame(self, &self->qos_rx);
-
-                       irlap_next_state(self, LAP_NRM_S);
-                       irlap_connect_confirm(self, skb);
-
-                       /*
-                        *  The WD-timer could be set to the duration of the
-                        *  P-timer for this case, but it is recommended
-                        *  to use twice the value (note 3 IrLAP p. 60).
-                        */
-                       irlap_start_wd_timer(self, self->wd_timeout);
-               } else {
-                       /* We just ignore the other device! */
-                       irlap_next_state(self, LAP_SETUP);
-               }
-               break;
-       case RECV_UA_RSP:
-               /* Stop F-timer */
-               del_timer(&self->final_timer);
-
-               /* Initiate connection state */
-               irlap_initiate_connection_state(self);
-
-               /* Negotiate connection parameters */
-               IRDA_ASSERT(skb->len > 10, return -1;);
-
-               skb_pull(skb, sizeof(struct ua_frame));
-
-               IRDA_ASSERT(self->netdev != NULL, return -1;);
-
-               irlap_qos_negotiate(self, skb);
-
-               /* Set the new link setting *now* (before the rr frame) */
-               irlap_apply_connection_parameters(self, TRUE);
-               self->retry_count = 0;
-
-               /* Wait for turnaround time to give a chance to the other
-                * device to be ready to receive us.
-                * Note : the time to switch speed is typically larger
-                * than the turnaround time, but as we don't have the other
-                * side speed switch time, that's our best guess...
-                * Jean II */
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-
-               /* This frame will actually be sent at the new speed */
-               irlap_send_rr_frame(self, CMD_FRAME);
-
-               /* The timer is set to half the normal timer to quickly
-                * detect a failure to negotiate the new connection
-                * parameters. IrLAP 6.11.3.2, note 3.
-                * Note that currently we don't process this failure
-                * properly, as we should do a quick disconnect.
-                * Jean II */
-               irlap_start_final_timer(self, self->final_timeout/2);
-               irlap_next_state(self, LAP_NRM_P);
-
-               irlap_connect_confirm(self, skb);
-               break;
-       case RECV_DM_RSP:     /* FALLTHROUGH */
-       case RECV_DISC_CMD:
-               del_timer(&self->final_timer);
-               irlap_next_state(self, LAP_NDM);
-
-               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d, %s\n", __func__,
-                        event, irlap_event[event]);
-
-               ret = -1;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_offline (self, event, skb, info)
- *
- *    OFFLINE state, not used for now!
- *
- */
-static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event,
-                              struct sk_buff *skb, struct irlap_info *info)
-{
-       pr_debug("%s(), Unknown event\n", __func__);
-
-       return -1;
-}
-
-/*
- * Function irlap_state_xmit_p (self, event, skb, info)
- *
- *    XMIT, Only the primary station has right to transmit, and we
- *    therefore do not expect to receive any transmissions from other
- *    stations.
- *
- */
-static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
-                             struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-
-       switch (event) {
-       case SEND_I_CMD:
-               /*
-                *  Only send frame if send-window > 0.
-                */
-               if ((self->window > 0) && (!self->remote_busy)) {
-                       int nextfit;
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
-                       struct sk_buff *skb_next;
-
-                       /* With DYNAMIC_WINDOW, we keep the window size
-                        * maximum, and adapt on the packets we are sending.
-                        * At 115k, we can send only 2 packets of 2048 bytes
-                        * in a 500 ms turnaround. Without this option, we
-                        * would always limit the window to 2. With this
-                        * option, if we send smaller packets, we can send
-                        * up to 7 of them (always depending on QoS).
-                        * Jean II */
-
-                       /* Look at the next skb. This is safe, as we are
-                        * the only consumer of the Tx queue (if we are not,
-                        * we have other problems) - Jean II */
-                       skb_next = skb_peek(&self->txq);
-
-                       /* Check if a subsequent skb exist and would fit in
-                        * the current window (with respect to turnaround
-                        * time).
-                        * This allow us to properly mark the current packet
-                        * with the pf bit, to avoid falling back on the
-                        * second test below, and avoid waiting the
-                        * end of the window and sending a extra RR.
-                        * Note : (skb_next != NULL) <=> (skb_queue_len() > 0)
-                        * Jean II */
-                       nextfit = ((skb_next != NULL) &&
-                                  ((skb_next->len + skb->len) <=
-                                   self->bytes_left));
-
-                       /*
-                        * The current packet may not fit ! Because of test
-                        * above, this should not happen any more !!!
-                        *  Test if we have transmitted more bytes over the
-                        *  link than its possible to do with the current
-                        *  speed and turn-around-time.
-                        */
-                       if((!nextfit) && (skb->len > self->bytes_left)) {
-                               pr_debug("%s(), Not allowed to transmit more bytes!\n",
-                                        __func__);
-                               /* Requeue the skb */
-                               skb_queue_head(&self->txq, skb_get(skb));
-                               /*
-                                *  We should switch state to LAP_NRM_P, but
-                                *  that is not possible since we must be sure
-                                *  that we poll the other side. Since we have
-                                *  used up our time, the poll timer should
-                                *  trigger anyway now, so we just wait for it
-                                *  DB
-                                */
-                               /*
-                                * Sorry, but that's not totally true. If
-                                * we send 2000B packets, we may wait another
-                                * 1000B until our turnaround expire. That's
-                                * why we need to be proactive in avoiding
-                                * coming here. - Jean II
-                                */
-                               return -EPROTO;
-                       }
-
-                       /* Subtract space used by this skb */
-                       self->bytes_left -= skb->len;
-#else  /* CONFIG_IRDA_DYNAMIC_WINDOW */
-                       /* Window has been adjusted for the max packet
-                        * size, so much simpler... - Jean II */
-                       nextfit = !skb_queue_empty(&self->txq);
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-                       /*
-                        *  Send data with poll bit cleared only if window > 1
-                        *  and there is more frames after this one to be sent
-                        */
-                       if ((self->window > 1) && (nextfit)) {
-                               /* More packet to send in current window */
-                               irlap_send_data_primary(self, skb);
-                               irlap_next_state(self, LAP_XMIT_P);
-                       } else {
-                               /* Final packet of window */
-                               irlap_send_data_primary_poll(self, skb);
-
-                               /*
-                                * Make sure state machine does not try to send
-                                * any more frames
-                                */
-                               ret = -EPROTO;
-                       }
-#ifdef CONFIG_IRDA_FAST_RR
-                       /* Peer may want to reply immediately */
-                       self->fast_RR = FALSE;
-#endif /* CONFIG_IRDA_FAST_RR */
-               } else {
-                       pr_debug("%s(), Unable to send! remote busy?\n",
-                                __func__);
-                       skb_queue_head(&self->txq, skb_get(skb));
-
-                       /*
-                        *  The next ret is important, because it tells
-                        *  irlap_next_state _not_ to deliver more frames
-                        */
-                       ret = -EPROTO;
-               }
-               break;
-       case POLL_TIMER_EXPIRED:
-               pr_debug("%s(), POLL_TIMER_EXPIRED <%ld>\n",
-                        __func__, jiffies);
-               irlap_send_rr_frame(self, CMD_FRAME);
-               /* Return to NRM properly - Jean II  */
-               self->window = self->window_size;
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
-               /* Allowed to transmit a maximum number of bytes again. */
-               self->bytes_left = self->line_capacity;
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-               irlap_start_final_timer(self, self->final_timeout);
-               irlap_next_state(self, LAP_NRM_P);
-               break;
-       case DISCONNECT_REQUEST:
-               del_timer(&self->poll_timer);
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-               irlap_send_disc_frame(self);
-               irlap_flush_all_queues(self);
-               irlap_start_final_timer(self, self->final_timeout);
-               self->retry_count = 0;
-               irlap_next_state(self, LAP_PCLOSE);
-               break;
-       case DATA_REQUEST:
-               /* Nothing to do, irlap_do_event() will send the packet
-                * when we return... - Jean II */
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n",
-                        __func__, irlap_event[event]);
-
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_pclose (event, skb, info)
- *
- *    PCLOSE state
- */
-static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event,
-                             struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case RECV_UA_RSP: /* FALLTHROUGH */
-       case RECV_DM_RSP:
-               del_timer(&self->final_timer);
-
-               /* Set new link parameters */
-               irlap_apply_default_connection_parameters(self);
-
-               /* Always switch state before calling upper layers */
-               irlap_next_state(self, LAP_NDM);
-
-               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
-               break;
-       case FINAL_TIMER_EXPIRED:
-               if (self->retry_count < self->N3) {
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_disc_frame(self);
-                       irlap_start_final_timer(self, self->final_timeout);
-                       self->retry_count++;
-                       /* Keep state */
-               } else {
-                       irlap_apply_default_connection_parameters(self);
-
-                       /*  Always switch state before calling upper layers */
-                       irlap_next_state(self, LAP_NDM);
-
-                       irlap_disconnect_indication(self, LAP_NO_RESPONSE);
-               }
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d\n", __func__, event);
-
-               ret = -1;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_nrm_p (self, event, skb, info)
- *
- *   NRM_P (Normal Response Mode as Primary), The primary station has given
- *   permissions to a secondary station to transmit IrLAP resonse frames
- *   (by sending a frame with the P bit set). The primary station will not
- *   transmit any frames and is expecting to receive frames only from the
- *   secondary to which transmission permissions has been given.
- */
-static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
-                            struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-       int ns_status;
-       int nr_status;
-
-       switch (event) {
-       case RECV_I_RSP: /* Optimize for the common case */
-               if (unlikely(skb->len <= LAP_ADDR_HEADER + LAP_CTRL_HEADER)) {
-                       /*
-                        * Input validation check: a stir4200/mcp2150
-                        * combination sometimes results in an empty i:rsp.
-                        * This makes no sense; we can just ignore the frame
-                        * and send an rr:cmd immediately. This happens before
-                        * changing nr or ns so triggers a retransmit
-                        */
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_rr_frame(self, CMD_FRAME);
-                       /* Keep state */
-                       break;
-               }
-               /* FIXME: must check for remote_busy below */
-#ifdef CONFIG_IRDA_FAST_RR
-               /*
-                *  Reset the fast_RR so we can use the fast RR code with
-                *  full speed the next time since peer may have more frames
-                *  to transmitt
-                */
-               self->fast_RR = FALSE;
-#endif /* CONFIG_IRDA_FAST_RR */
-               IRDA_ASSERT( info != NULL, return -1;);
-
-               ns_status = irlap_validate_ns_received(self, info->ns);
-               nr_status = irlap_validate_nr_received(self, info->nr);
-
-               /*
-                *  Check for expected I(nformation) frame
-                */
-               if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) {
-
-                       /* Update Vr (next frame for us to receive) */
-                       self->vr = (self->vr + 1) % 8;
-
-                       /* Update Nr received, cleanup our retry queue */
-                       irlap_update_nr_received(self, info->nr);
-
-                       /*
-                        *  Got expected NR, so reset the
-                        *  retry_count. This is not done by IrLAP spec,
-                        *  which is strange!
-                        */
-                       self->retry_count = 0;
-                       self->ack_required = TRUE;
-
-                       /*  poll bit cleared?  */
-                       if (!info->pf) {
-                               /* Keep state, do not move this line */
-                               irlap_next_state(self, LAP_NRM_P);
-
-                               irlap_data_indication(self, skb, FALSE);
-                       } else {
-                               /* No longer waiting for pf */
-                               del_timer(&self->final_timer);
-
-                               irlap_wait_min_turn_around(self, &self->qos_tx);
-
-                               /* Call higher layer *before* changing state
-                                * to give them a chance to send data in the
-                                * next LAP frame.
-                                * Jean II */
-                               irlap_data_indication(self, skb, FALSE);
-
-                               /* XMIT states are the most dangerous state
-                                * to be in, because user requests are
-                                * processed directly and may change state.
-                                * On the other hand, in NDM_P, those
-                                * requests are queued and we will process
-                                * them when we return to irlap_do_event().
-                                * Jean II
-                                */
-                               irlap_next_state(self, LAP_XMIT_P);
-
-                               /* This is the last frame.
-                                * Make sure it's always called in XMIT state.
-                                * - Jean II */
-                               irlap_start_poll_timer(self, self->poll_timeout);
-                       }
-                       break;
-
-               }
-               /* Unexpected next to send (Ns) */
-               if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED))
-               {
-                       if (!info->pf) {
-                               irlap_update_nr_received(self, info->nr);
-
-                               /*
-                                *  Wait until the last frame before doing
-                                *  anything
-                                */
-
-                               /* Keep state */
-                               irlap_next_state(self, LAP_NRM_P);
-                       } else {
-                               pr_debug("%s(), missing or duplicate frame!\n",
-                                        __func__);
-
-                               /* Update Nr received */
-                               irlap_update_nr_received(self, info->nr);
-
-                               irlap_wait_min_turn_around(self, &self->qos_tx);
-                               irlap_send_rr_frame(self, CMD_FRAME);
-
-                               self->ack_required = FALSE;
-
-                               irlap_start_final_timer(self, self->final_timeout);
-                               irlap_next_state(self, LAP_NRM_P);
-                       }
-                       break;
-               }
-               /*
-                *  Unexpected next to receive (Nr)
-                */
-               if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
-               {
-                       if (info->pf) {
-                               self->vr = (self->vr + 1) % 8;
-
-                               /* Update Nr received */
-                               irlap_update_nr_received(self, info->nr);
-
-                               /* Resend rejected frames */
-                               irlap_resend_rejected_frames(self, CMD_FRAME);
-
-                               self->ack_required = FALSE;
-
-                               /* Make sure we account for the time
-                                * to transmit our frames. See comemnts
-                                * in irlap_send_data_primary_poll().
-                                * Jean II */
-                               irlap_start_final_timer(self, 2 * self->final_timeout);
-
-                               /* Keep state, do not move this line */
-                               irlap_next_state(self, LAP_NRM_P);
-
-                               irlap_data_indication(self, skb, FALSE);
-                       } else {
-                               /*
-                                *  Do not resend frames until the last
-                                *  frame has arrived from the other
-                                *  device. This is not documented in
-                                *  IrLAP!!
-                                */
-                               self->vr = (self->vr + 1) % 8;
-
-                               /* Update Nr received */
-                               irlap_update_nr_received(self, info->nr);
-
-                               self->ack_required = FALSE;
-
-                               /* Keep state, do not move this line!*/
-                               irlap_next_state(self, LAP_NRM_P);
-
-                               irlap_data_indication(self, skb, FALSE);
-                       }
-                       break;
-               }
-               /*
-                *  Unexpected next to send (Ns) and next to receive (Nr)
-                *  Not documented by IrLAP!
-                */
-               if ((ns_status == NS_UNEXPECTED) &&
-                   (nr_status == NR_UNEXPECTED))
-               {
-                       pr_debug("%s(), unexpected nr and ns!\n",
-                                __func__);
-                       if (info->pf) {
-                               /* Resend rejected frames */
-                               irlap_resend_rejected_frames(self, CMD_FRAME);
-
-                               /* Give peer some time to retransmit!
-                                * But account for our own Tx. */
-                               irlap_start_final_timer(self, 2 * self->final_timeout);
-
-                               /* Keep state, do not move this line */
-                               irlap_next_state(self, LAP_NRM_P);
-                       } else {
-                               /* Update Nr received */
-                               /* irlap_update_nr_received( info->nr); */
-
-                               self->ack_required = FALSE;
-                       }
-                       break;
-               }
-
-               /*
-                *  Invalid NR or NS
-                */
-               if ((nr_status == NR_INVALID) || (ns_status == NS_INVALID)) {
-                       if (info->pf) {
-                               del_timer(&self->final_timer);
-
-                               irlap_next_state(self, LAP_RESET_WAIT);
-
-                               irlap_disconnect_indication(self, LAP_RESET_INDICATION);
-                               self->xmitflag = TRUE;
-                       } else {
-                               del_timer(&self->final_timer);
-
-                               irlap_disconnect_indication(self, LAP_RESET_INDICATION);
-
-                               self->xmitflag = FALSE;
-                       }
-                       break;
-               }
-               pr_debug("%s(), Not implemented!\n", __func__);
-               pr_debug("%s(), event=%s, ns_status=%d, nr_status=%d\n",
-                        __func__, irlap_event[event], ns_status, nr_status);
-               break;
-       case RECV_UI_FRAME:
-               /* Poll bit cleared? */
-               if (!info->pf) {
-                       irlap_data_indication(self, skb, TRUE);
-                       irlap_next_state(self, LAP_NRM_P);
-               } else {
-                       del_timer(&self->final_timer);
-                       irlap_data_indication(self, skb, TRUE);
-                       irlap_next_state(self, LAP_XMIT_P);
-                       pr_debug("%s: RECV_UI_FRAME: next state %s\n",
-                                __func__, irlap_state[self->state]);
-                       irlap_start_poll_timer(self, self->poll_timeout);
-               }
-               break;
-       case RECV_RR_RSP:
-               /*
-                *  If you get a RR, the remote isn't busy anymore,
-                *  no matter what the NR
-                */
-               self->remote_busy = FALSE;
-
-               /* Stop final timer */
-               del_timer(&self->final_timer);
-
-               /*
-                *  Nr as expected?
-                */
-               ret = irlap_validate_nr_received(self, info->nr);
-               if (ret == NR_EXPECTED) {
-                       /* Update Nr received */
-                       irlap_update_nr_received(self, info->nr);
-
-                       /*
-                        *  Got expected NR, so reset the retry_count. This
-                        *  is not done by the IrLAP standard , which is
-                        *  strange! DB.
-                        */
-                       self->retry_count = 0;
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-
-                       irlap_next_state(self, LAP_XMIT_P);
-
-                       /* Start poll timer */
-                       irlap_start_poll_timer(self, self->poll_timeout);
-               } else if (ret == NR_UNEXPECTED) {
-                       IRDA_ASSERT(info != NULL, return -1;);
-                       /*
-                        *  Unexpected nr!
-                        */
-
-                       /* Update Nr received */
-                       irlap_update_nr_received(self, info->nr);
-
-                       pr_debug("RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n",
-                                self->retry_count, info->nr, self->va,
-                                self->vs, self->vr);
-
-                       /* Resend rejected frames */
-                       irlap_resend_rejected_frames(self, CMD_FRAME);
-                       irlap_start_final_timer(self, self->final_timeout * 2);
-
-                       irlap_next_state(self, LAP_NRM_P);
-               } else if (ret == NR_INVALID) {
-                       pr_debug("%s(), Received RR with invalid nr !\n",
-                                __func__);
-
-                       irlap_next_state(self, LAP_RESET_WAIT);
-
-                       irlap_disconnect_indication(self, LAP_RESET_INDICATION);
-                       self->xmitflag = TRUE;
-               }
-               break;
-       case RECV_RNR_RSP:
-               IRDA_ASSERT(info != NULL, return -1;);
-
-               /* Stop final timer */
-               del_timer(&self->final_timer);
-               self->remote_busy = TRUE;
-
-               /* Update Nr received */
-               irlap_update_nr_received(self, info->nr);
-               irlap_next_state(self, LAP_XMIT_P);
-
-               /* Start poll timer */
-               irlap_start_poll_timer(self, self->poll_timeout);
-               break;
-       case RECV_FRMR_RSP:
-               del_timer(&self->final_timer);
-               self->xmitflag = TRUE;
-               irlap_next_state(self, LAP_RESET_WAIT);
-               irlap_reset_indication(self);
-               break;
-       case FINAL_TIMER_EXPIRED:
-               /*
-                *  We are allowed to wait for additional 300 ms if
-                *  final timer expires when we are in the middle
-                *  of receiving a frame (page 45, IrLAP). Check that
-                *  we only do this once for each frame.
-                */
-               if (irda_device_is_receiving(self->netdev) && !self->add_wait) {
-                       pr_debug("FINAL_TIMER_EXPIRED when receiving a frame! Waiting a little bit more!\n");
-                       irlap_start_final_timer(self, msecs_to_jiffies(300));
-
-                       /*
-                        *  Don't allow this to happen one more time in a row,
-                        *  or else we can get a pretty tight loop here if
-                        *  if we only receive half a frame. DB.
-                        */
-                       self->add_wait = TRUE;
-                       break;
-               }
-               self->add_wait = FALSE;
-
-               /* N2 is the disconnect timer. Until we reach it, we retry */
-               if (self->retry_count < self->N2) {
-                       if (skb_peek(&self->wx_list) == NULL) {
-                               /* Retry sending the pf bit to the secondary */
-                               pr_debug("nrm_p: resending rr");
-                               irlap_wait_min_turn_around(self, &self->qos_tx);
-                               irlap_send_rr_frame(self, CMD_FRAME);
-                       } else {
-                               pr_debug("nrm_p: resend frames");
-                               irlap_resend_rejected_frames(self, CMD_FRAME);
-                       }
-
-                       irlap_start_final_timer(self, self->final_timeout);
-                       self->retry_count++;
-                       pr_debug("irlap_state_nrm_p: FINAL_TIMER_EXPIRED: retry_count=%d\n",
-                                self->retry_count);
-
-                       /* Early warning event. I'm using a pretty liberal
-                        * interpretation of the spec and generate an event
-                        * every time the timer is multiple of N1 (and not
-                        * only the first time). This allow application
-                        * to know precisely if connectivity restart...
-                        * Jean II */
-                       if((self->retry_count % self->N1) == 0)
-                               irlap_status_indication(self,
-                                                       STATUS_NO_ACTIVITY);
-
-                       /* Keep state */
-               } else {
-                       irlap_apply_default_connection_parameters(self);
-
-                       /* Always switch state before calling upper layers */
-                       irlap_next_state(self, LAP_NDM);
-                       irlap_disconnect_indication(self, LAP_NO_RESPONSE);
-               }
-               break;
-       case RECV_REJ_RSP:
-               irlap_update_nr_received(self, info->nr);
-               if (self->remote_busy) {
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_rr_frame(self, CMD_FRAME);
-               } else
-                       irlap_resend_rejected_frames(self, CMD_FRAME);
-               irlap_start_final_timer(self, 2 * self->final_timeout);
-               break;
-       case RECV_SREJ_RSP:
-               irlap_update_nr_received(self, info->nr);
-               if (self->remote_busy) {
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_rr_frame(self, CMD_FRAME);
-               } else
-                       irlap_resend_rejected_frame(self, CMD_FRAME);
-               irlap_start_final_timer(self, 2 * self->final_timeout);
-               break;
-       case RECV_RD_RSP:
-               pr_debug("%s(), RECV_RD_RSP\n", __func__);
-
-               irlap_flush_all_queues(self);
-               irlap_next_state(self, LAP_XMIT_P);
-               /* Call back the LAP state machine to do a proper disconnect */
-               irlap_disconnect_request(self);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n",
-                        __func__, irlap_event[event]);
-
-               ret = -1;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_reset_wait (event, skb, info)
- *
- *    We have informed the service user of a reset condition, and is
- *    awaiting reset of disconnect request.
- *
- */
-static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
-                                 struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s(), event = %s\n", __func__, irlap_event[event]);
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case RESET_REQUEST:
-               if (self->xmitflag) {
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_snrm_frame(self, NULL);
-                       irlap_start_final_timer(self, self->final_timeout);
-                       irlap_next_state(self, LAP_RESET);
-               } else {
-                       irlap_start_final_timer(self, self->final_timeout);
-                       irlap_next_state(self, LAP_RESET);
-               }
-               break;
-       case DISCONNECT_REQUEST:
-               irlap_wait_min_turn_around( self, &self->qos_tx);
-               irlap_send_disc_frame( self);
-               irlap_flush_all_queues( self);
-               irlap_start_final_timer( self, self->final_timeout);
-               self->retry_count = 0;
-               irlap_next_state( self, LAP_PCLOSE);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n", __func__,
-                        irlap_event[event]);
-
-               ret = -1;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_reset (self, event, skb, info)
- *
- *    We have sent a SNRM reset command to the peer layer, and is awaiting
- *    reply.
- *
- */
-static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
-                            struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s(), event = %s\n", __func__, irlap_event[event]);
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case RECV_DISC_CMD:
-               del_timer(&self->final_timer);
-
-               irlap_apply_default_connection_parameters(self);
-
-               /* Always switch state before calling upper layers */
-               irlap_next_state(self, LAP_NDM);
-
-               irlap_disconnect_indication(self, LAP_NO_RESPONSE);
-
-               break;
-       case RECV_UA_RSP:
-               del_timer(&self->final_timer);
-
-               /* Initiate connection state */
-               irlap_initiate_connection_state(self);
-
-               irlap_reset_confirm();
-
-               self->remote_busy = FALSE;
-
-               irlap_next_state(self, LAP_XMIT_P);
-
-               irlap_start_poll_timer(self, self->poll_timeout);
-
-               break;
-       case FINAL_TIMER_EXPIRED:
-               if (self->retry_count < 3) {
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-
-                       IRDA_ASSERT(self->netdev != NULL, return -1;);
-                       irlap_send_snrm_frame(self, self->qos_dev);
-
-                       self->retry_count++; /* Experimental!! */
-
-                       irlap_start_final_timer(self, self->final_timeout);
-                       irlap_next_state(self, LAP_RESET);
-               } else if (self->retry_count >= self->N3) {
-                       irlap_apply_default_connection_parameters(self);
-
-                       /* Always switch state before calling upper layers */
-                       irlap_next_state(self, LAP_NDM);
-
-                       irlap_disconnect_indication(self, LAP_NO_RESPONSE);
-               }
-               break;
-       case RECV_SNRM_CMD:
-               /*
-                * SNRM frame is not allowed to contain an I-field in this
-                * state
-                */
-               if (!info) {
-                       pr_debug("%s(), RECV_SNRM_CMD\n", __func__);
-                       irlap_initiate_connection_state(self);
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_ua_response_frame(self, &self->qos_rx);
-                       irlap_reset_confirm();
-                       irlap_start_wd_timer(self, self->wd_timeout);
-                       irlap_next_state(self, LAP_NDM);
-               } else {
-                       pr_debug("%s(), SNRM frame contained an I field!\n",
-                                __func__);
-               }
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n",
-                        __func__, irlap_event[event]);
-
-               ret = -1;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_xmit_s (event, skb, info)
- *
- *   XMIT_S, The secondary station has been given the right to transmit,
- *   and we therefore do not expect to receive any transmissions from other
- *   stations.
- */
-static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
-                             struct sk_buff *skb, struct irlap_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
-
-       IRDA_ASSERT(self != NULL, return -ENODEV;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
-
-       switch (event) {
-       case SEND_I_CMD:
-               /*
-                *  Send frame only if send window > 0
-                */
-               if ((self->window > 0) && (!self->remote_busy)) {
-                       int nextfit;
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
-                       struct sk_buff *skb_next;
-
-                       /*
-                        * Same deal as in irlap_state_xmit_p(), so see
-                        * the comments at that point.
-                        * We are the secondary, so there are only subtle
-                        * differences. - Jean II
-                        */
-
-                       /* Check if a subsequent skb exist and would fit in
-                        * the current window (with respect to turnaround
-                        * time). - Jean II */
-                       skb_next = skb_peek(&self->txq);
-                       nextfit = ((skb_next != NULL) &&
-                                  ((skb_next->len + skb->len) <=
-                                   self->bytes_left));
-
-                       /*
-                        *  Test if we have transmitted more bytes over the
-                        *  link than its possible to do with the current
-                        *  speed and turn-around-time.
-                        */
-                       if((!nextfit) && (skb->len > self->bytes_left)) {
-                               pr_debug("%s(), Not allowed to transmit more bytes!\n",
-                                        __func__);
-                               /* Requeue the skb */
-                               skb_queue_head(&self->txq, skb_get(skb));
-
-                               /*
-                                *  Switch to NRM_S, this is only possible
-                                *  when we are in secondary mode, since we
-                                *  must be sure that we don't miss any RR
-                                *  frames
-                                */
-                               self->window = self->window_size;
-                               self->bytes_left = self->line_capacity;
-                               irlap_start_wd_timer(self, self->wd_timeout);
-
-                               irlap_next_state(self, LAP_NRM_S);
-                               /* Slight difference with primary :
-                                * here we would wait for the other side to
-                                * expire the turnaround. - Jean II */
-
-                               return -EPROTO; /* Try again later */
-                       }
-                       /* Subtract space used by this skb */
-                       self->bytes_left -= skb->len;
-#else  /* CONFIG_IRDA_DYNAMIC_WINDOW */
-                       /* Window has been adjusted for the max packet
-                        * size, so much simpler... - Jean II */
-                       nextfit = !skb_queue_empty(&self->txq);
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-                       /*
-                        *  Send data with final bit cleared only if window > 1
-                        *  and there is more frames to be sent
-                        */
-                       if ((self->window > 1) && (nextfit)) {
-                               irlap_send_data_secondary(self, skb);
-                               irlap_next_state(self, LAP_XMIT_S);
-                       } else {
-                               irlap_send_data_secondary_final(self, skb);
-                               irlap_next_state(self, LAP_NRM_S);
-
-                               /*
-                                * Make sure state machine does not try to send
-                                * any more frames
-                                */
-                               ret = -EPROTO;
-                       }
-               } else {
-                       pr_debug("%s(), Unable to send!\n", __func__);
-                       skb_queue_head(&self->txq, skb_get(skb));
-                       ret = -EPROTO;
-               }
-               break;
-       case DISCONNECT_REQUEST:
-               irlap_send_rd_frame(self);
-               irlap_flush_all_queues(self);
-               irlap_start_wd_timer(self, self->wd_timeout);
-               irlap_next_state(self, LAP_SCLOSE);
-               break;
-       case DATA_REQUEST:
-               /* Nothing to do, irlap_do_event() will send the packet
-                * when we return... - Jean II */
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n", __func__,
-                        irlap_event[event]);
-
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_nrm_s (event, skb, info)
- *
- *    NRM_S (Normal Response Mode as Secondary) state, in this state we are
- *    expecting to receive frames from the primary station
- *
- */
-static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
-                            struct sk_buff *skb, struct irlap_info *info)
-{
-       int ns_status;
-       int nr_status;
-       int ret = 0;
-
-       pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       switch (event) {
-       case RECV_I_CMD: /* Optimize for the common case */
-               /* FIXME: must check for remote_busy below */
-               pr_debug("%s(), event=%s nr=%d, vs=%d, ns=%d, vr=%d, pf=%d\n",
-                        __func__, irlap_event[event], info->nr,
-                        self->vs, info->ns, self->vr, info->pf);
-
-               self->retry_count = 0;
-
-               ns_status = irlap_validate_ns_received(self, info->ns);
-               nr_status = irlap_validate_nr_received(self, info->nr);
-               /*
-                *  Check for expected I(nformation) frame
-                */
-               if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) {
-
-                       /* Update Vr (next frame for us to receive) */
-                       self->vr = (self->vr + 1) % 8;
-
-                       /* Update Nr received */
-                       irlap_update_nr_received(self, info->nr);
-
-                       /*
-                        *  poll bit cleared?
-                        */
-                       if (!info->pf) {
-
-                               self->ack_required = TRUE;
-
-                               /*
-                                *  Starting WD-timer here is optional, but
-                                *  not recommended. Note 6 IrLAP p. 83
-                                */
-#if 0
-                               irda_start_timer(WD_TIMER, self->wd_timeout);
-#endif
-                               /* Keep state, do not move this line */
-                               irlap_next_state(self, LAP_NRM_S);
-
-                               irlap_data_indication(self, skb, FALSE);
-                               break;
-                       } else {
-                               /*
-                                *  We should wait before sending RR, and
-                                *  also before changing to XMIT_S
-                                *  state. (note 1, IrLAP p. 82)
-                                */
-                               irlap_wait_min_turn_around(self, &self->qos_tx);
-
-                               /*
-                                * Give higher layers a chance to
-                                * immediately reply with some data before
-                                * we decide if we should send a RR frame
-                                * or not
-                                */
-                               irlap_data_indication(self, skb, FALSE);
-
-                               /* Any pending data requests?  */
-                               if (!skb_queue_empty(&self->txq) &&
-                                   (self->window > 0))
-                               {
-                                       self->ack_required = TRUE;
-
-                                       del_timer(&self->wd_timer);
-
-                                       irlap_next_state(self, LAP_XMIT_S);
-                               } else {
-                                       irlap_send_rr_frame(self, RSP_FRAME);
-                                       irlap_start_wd_timer(self,
-                                                            self->wd_timeout);
-
-                                       /* Keep the state */
-                                       irlap_next_state(self, LAP_NRM_S);
-                               }
-                               break;
-                       }
-               }
-               /*
-                *  Check for Unexpected next to send (Ns)
-                */
-               if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED))
-               {
-                       /* Unexpected next to send, with final bit cleared */
-                       if (!info->pf) {
-                               irlap_update_nr_received(self, info->nr);
-
-                               irlap_start_wd_timer(self, self->wd_timeout);
-                       } else {
-                               /* Update Nr received */
-                               irlap_update_nr_received(self, info->nr);
-
-                               irlap_wait_min_turn_around(self, &self->qos_tx);
-                               irlap_send_rr_frame(self, RSP_FRAME);
-
-                               irlap_start_wd_timer(self, self->wd_timeout);
-                       }
-                       break;
-               }
-
-               /*
-                *  Unexpected Next to Receive(NR) ?
-                */
-               if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
-               {
-                       if (info->pf) {
-                               pr_debug("RECV_I_RSP: frame(s) lost\n");
-
-                               self->vr = (self->vr + 1) % 8;
-
-                               /* Update Nr received */
-                               irlap_update_nr_received(self, info->nr);
-
-                               /* Resend rejected frames */
-                               irlap_resend_rejected_frames(self, RSP_FRAME);
-
-                               /* Keep state, do not move this line */
-                               irlap_next_state(self, LAP_NRM_S);
-
-                               irlap_data_indication(self, skb, FALSE);
-                               irlap_start_wd_timer(self, self->wd_timeout);
-                               break;
-                       }
-                       /*
-                        *  This is not documented in IrLAP!! Unexpected NR
-                        *  with poll bit cleared
-                        */
-                       if (!info->pf) {
-                               self->vr = (self->vr + 1) % 8;
-
-                               /* Update Nr received */
-                               irlap_update_nr_received(self, info->nr);
-
-                               /* Keep state, do not move this line */
-                               irlap_next_state(self, LAP_NRM_S);
-
-                               irlap_data_indication(self, skb, FALSE);
-                               irlap_start_wd_timer(self, self->wd_timeout);
-                       }
-                       break;
-               }
-
-               if (ret == NR_INVALID) {
-                       pr_debug("NRM_S, NR_INVALID not implemented!\n");
-               }
-               if (ret == NS_INVALID) {
-                       pr_debug("NRM_S, NS_INVALID not implemented!\n");
-               }
-               break;
-       case RECV_UI_FRAME:
-               /*
-                *  poll bit cleared?
-                */
-               if (!info->pf) {
-                       irlap_data_indication(self, skb, TRUE);
-                       irlap_next_state(self, LAP_NRM_S); /* Keep state */
-               } else {
-                       /*
-                        *  Any pending data requests?
-                        */
-                       if (!skb_queue_empty(&self->txq) &&
-                           (self->window > 0) && !self->remote_busy)
-                       {
-                               irlap_data_indication(self, skb, TRUE);
-
-                               del_timer(&self->wd_timer);
-
-                               irlap_next_state(self, LAP_XMIT_S);
-                       } else {
-                               irlap_data_indication(self, skb, TRUE);
-
-                               irlap_wait_min_turn_around(self, &self->qos_tx);
-
-                               irlap_send_rr_frame(self, RSP_FRAME);
-                               self->ack_required = FALSE;
-
-                               irlap_start_wd_timer(self, self->wd_timeout);
-
-                               /* Keep the state */
-                               irlap_next_state(self, LAP_NRM_S);
-                       }
-               }
-               break;
-       case RECV_RR_CMD:
-               self->retry_count = 0;
-
-               /*
-                *  Nr as expected?
-                */
-               nr_status = irlap_validate_nr_received(self, info->nr);
-               if (nr_status == NR_EXPECTED) {
-                       if (!skb_queue_empty(&self->txq) &&
-                           (self->window > 0)) {
-                               self->remote_busy = FALSE;
-
-                               /* Update Nr received */
-                               irlap_update_nr_received(self, info->nr);
-                               del_timer(&self->wd_timer);
-
-                               irlap_wait_min_turn_around(self, &self->qos_tx);
-                               irlap_next_state(self, LAP_XMIT_S);
-                       } else {
-                               self->remote_busy = FALSE;
-                               /* Update Nr received */
-                               irlap_update_nr_received(self, info->nr);
-                               irlap_wait_min_turn_around(self, &self->qos_tx);
-                               irlap_start_wd_timer(self, self->wd_timeout);
-
-                               /* Note : if the link is idle (this case),
-                                * we never go in XMIT_S, so we never get a
-                                * chance to process any DISCONNECT_REQUEST.
-                                * Do it now ! - Jean II */
-                               if (self->disconnect_pending) {
-                                       /* Disconnect */
-                                       irlap_send_rd_frame(self);
-                                       irlap_flush_all_queues(self);
-
-                                       irlap_next_state(self, LAP_SCLOSE);
-                               } else {
-                                       /* Just send back pf bit */
-                                       irlap_send_rr_frame(self, RSP_FRAME);
-
-                                       irlap_next_state(self, LAP_NRM_S);
-                               }
-                       }
-               } else if (nr_status == NR_UNEXPECTED) {
-                       self->remote_busy = FALSE;
-                       irlap_update_nr_received(self, info->nr);
-                       irlap_resend_rejected_frames(self, RSP_FRAME);
-
-                       irlap_start_wd_timer(self, self->wd_timeout);
-
-                       /* Keep state */
-                       irlap_next_state(self, LAP_NRM_S);
-               } else {
-                       pr_debug("%s(), invalid nr not implemented!\n",
-                                __func__);
-               }
-               break;
-       case RECV_SNRM_CMD:
-               /* SNRM frame is not allowed to contain an I-field */
-               if (!info) {
-                       del_timer(&self->wd_timer);
-                       pr_debug("%s(), received SNRM cmd\n", __func__);
-                       irlap_next_state(self, LAP_RESET_CHECK);
-
-                       irlap_reset_indication(self);
-               } else {
-                       pr_debug("%s(), SNRM frame contained an I-field!\n",
-                                __func__);
-
-               }
-               break;
-       case RECV_REJ_CMD:
-               irlap_update_nr_received(self, info->nr);
-               if (self->remote_busy) {
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_rr_frame(self, RSP_FRAME);
-               } else
-                       irlap_resend_rejected_frames(self, RSP_FRAME);
-               irlap_start_wd_timer(self, self->wd_timeout);
-               break;
-       case RECV_SREJ_CMD:
-               irlap_update_nr_received(self, info->nr);
-               if (self->remote_busy) {
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_rr_frame(self, RSP_FRAME);
-               } else
-                       irlap_resend_rejected_frame(self, RSP_FRAME);
-               irlap_start_wd_timer(self, self->wd_timeout);
-               break;
-       case WD_TIMER_EXPIRED:
-               /*
-                *  Wait until retry_count * n matches negotiated threshold/
-                *  disconnect time (note 2 in IrLAP p. 82)
-                *
-                * Similar to irlap_state_nrm_p() -> FINAL_TIMER_EXPIRED
-                * Note : self->wd_timeout = (self->final_timeout * 2),
-                *   which explain why we use (self->N2 / 2) here !!!
-                * Jean II
-                */
-               pr_debug("%s(), retry_count = %d\n", __func__,
-                        self->retry_count);
-
-               if (self->retry_count < (self->N2 / 2)) {
-                       /* No retry, just wait for primary */
-                       irlap_start_wd_timer(self, self->wd_timeout);
-                       self->retry_count++;
-
-                       if((self->retry_count % (self->N1 / 2)) == 0)
-                               irlap_status_indication(self,
-                                                       STATUS_NO_ACTIVITY);
-               } else {
-                       irlap_apply_default_connection_parameters(self);
-
-                       /* Always switch state before calling upper layers */
-                       irlap_next_state(self, LAP_NDM);
-                       irlap_disconnect_indication(self, LAP_NO_RESPONSE);
-               }
-               break;
-       case RECV_DISC_CMD:
-               /* Always switch state before calling upper layers */
-               irlap_next_state(self, LAP_NDM);
-
-               /* Send disconnect response */
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-               irlap_send_ua_response_frame(self, NULL);
-
-               del_timer(&self->wd_timer);
-               irlap_flush_all_queues(self);
-               /* Set default link parameters */
-               irlap_apply_default_connection_parameters(self);
-
-               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
-               break;
-       case RECV_DISCOVERY_XID_CMD:
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-               irlap_send_rr_frame(self, RSP_FRAME);
-               self->ack_required = TRUE;
-               irlap_start_wd_timer(self, self->wd_timeout);
-               irlap_next_state(self, LAP_NRM_S);
-
-               break;
-       case RECV_TEST_CMD:
-               /* Remove test frame header (only LAP header in NRM) */
-               skb_pull(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
-
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-               irlap_start_wd_timer(self, self->wd_timeout);
-
-               /* Send response (info will be copied) */
-               irlap_send_test_frame(self, self->caddr, info->daddr, skb);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
-                        event, irlap_event[event]);
-
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlap_state_sclose (self, event, skb, info)
- */
-static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event,
-                             struct sk_buff *skb, struct irlap_info *info)
-{
-       IRDA_ASSERT(self != NULL, return -ENODEV;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
-
-       switch (event) {
-       case RECV_DISC_CMD:
-               /* Always switch state before calling upper layers */
-               irlap_next_state(self, LAP_NDM);
-
-               /* Send disconnect response */
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-               irlap_send_ua_response_frame(self, NULL);
-
-               del_timer(&self->wd_timer);
-               /* Set default link parameters */
-               irlap_apply_default_connection_parameters(self);
-
-               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
-               break;
-       case RECV_DM_RSP:
-               /* IrLAP-1.1 p.82: in SCLOSE, S and I type RSP frames
-                * shall take us down into default NDM state, like DM_RSP
-                */
-       case RECV_RR_RSP:
-       case RECV_RNR_RSP:
-       case RECV_REJ_RSP:
-       case RECV_SREJ_RSP:
-       case RECV_I_RSP:
-               /* Always switch state before calling upper layers */
-               irlap_next_state(self, LAP_NDM);
-
-               del_timer(&self->wd_timer);
-               irlap_apply_default_connection_parameters(self);
-
-               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
-               break;
-       case WD_TIMER_EXPIRED:
-               /* Always switch state before calling upper layers */
-               irlap_next_state(self, LAP_NDM);
-
-               irlap_apply_default_connection_parameters(self);
-
-               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
-               break;
-       default:
-               /* IrLAP-1.1 p.82: in SCLOSE, basically any received frame
-                * with pf=1 shall restart the wd-timer and resend the rd:rsp
-                */
-               if (info != NULL  &&  info->pf) {
-                       del_timer(&self->wd_timer);
-                       irlap_wait_min_turn_around(self, &self->qos_tx);
-                       irlap_send_rd_frame(self);
-                       irlap_start_wd_timer(self, self->wd_timeout);
-                       break;          /* stay in SCLOSE */
-               }
-
-               pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
-                        event, irlap_event[event]);
-
-               break;
-       }
-
-       return -1;
-}
-
-static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event,
-                                  struct sk_buff *skb,
-                                  struct irlap_info *info)
-{
-       int ret = 0;
-
-       pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
-
-       IRDA_ASSERT(self != NULL, return -ENODEV;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
-
-       switch (event) {
-       case RESET_RESPONSE:
-               irlap_send_ua_response_frame(self, &self->qos_rx);
-               irlap_initiate_connection_state(self);
-               irlap_start_wd_timer(self, WD_TIMEOUT);
-               irlap_flush_all_queues(self);
-
-               irlap_next_state(self, LAP_NRM_S);
-               break;
-       case DISCONNECT_REQUEST:
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-               irlap_send_rd_frame(self);
-               irlap_start_wd_timer(self, WD_TIMEOUT);
-               irlap_next_state(self, LAP_SCLOSE);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
-                        event, irlap_event[event]);
-
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c
deleted file mode 100644 (file)
index debda3d..0000000
+++ /dev/null
@@ -1,1407 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlap_frame.c
- * Version:       1.0
- * Description:   Build and transmit IrLAP frames
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Tue Aug 19 10:27:26 1997
- * Modified at:   Wed Jan  5 08:59:04 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/if.h>
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/irda.h>
-#include <linux/slab.h>
-
-#include <net/pkt_sched.h>
-#include <net/sock.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/irlap.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/timer.h>
-#include <net/irda/irlap_frame.h>
-#include <net/irda/qos.h>
-
-static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb,
-                              int command);
-
-/*
- * Function irlap_insert_info (self, skb)
- *
- *    Insert minimum turnaround time and speed information into the skb. We
- *    need to do this since it's per packet relevant information. Safe to
- *    have this function inlined since it's only called from one place
- */
-static inline void irlap_insert_info(struct irlap_cb *self,
-                                    struct sk_buff *skb)
-{
-       struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
-
-       /*
-        * Insert MTT (min. turn time) and speed into skb, so that the
-        * device driver knows which settings to use
-        */
-       cb->magic = LAP_MAGIC;
-       cb->mtt = self->mtt_required;
-       cb->next_speed = self->speed;
-
-       /* Reset */
-       self->mtt_required = 0;
-
-       /*
-        * Delay equals negotiated BOFs count, plus the number of BOFs to
-        * force the negotiated minimum turnaround time
-        */
-       cb->xbofs = self->bofs_count;
-       cb->next_xbofs = self->next_bofs;
-       cb->xbofs_delay = self->xbofs_delay;
-
-       /* Reset XBOF's delay (used only for getting min turn time) */
-       self->xbofs_delay = 0;
-       /* Put the correct xbofs value for the next packet */
-       self->bofs_count = self->next_bofs;
-}
-
-/*
- * Function irlap_queue_xmit (self, skb)
- *
- *    A little wrapper for dev_queue_xmit, so we can insert some common
- *    code into it.
- */
-void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb)
-{
-       /* Some common init stuff */
-       skb->dev = self->netdev;
-       skb_reset_mac_header(skb);
-       skb_reset_network_header(skb);
-       skb_reset_transport_header(skb);
-       skb->protocol = htons(ETH_P_IRDA);
-       skb->priority = TC_PRIO_BESTEFFORT;
-
-       irlap_insert_info(self, skb);
-
-       if (unlikely(self->mode & IRDA_MODE_MONITOR)) {
-               pr_debug("%s(): %s is in monitor mode\n", __func__,
-                        self->netdev->name);
-               dev_kfree_skb(skb);
-               return;
-       }
-
-       dev_queue_xmit(skb);
-}
-
-/*
- * Function irlap_send_snrm_cmd (void)
- *
- *    Transmits a connect SNRM command frame
- */
-void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos)
-{
-       struct sk_buff *tx_skb;
-       struct snrm_frame *frame;
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* Allocate frame */
-       tx_skb = alloc_skb(sizeof(struct snrm_frame) +
-                          IRLAP_NEGOCIATION_PARAMS_LEN,
-                          GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       frame = skb_put(tx_skb, 2);
-
-       /* Insert connection address field */
-       if (qos)
-               frame->caddr = CMD_FRAME | CBROADCAST;
-       else
-               frame->caddr = CMD_FRAME | self->caddr;
-
-       /* Insert control field */
-       frame->control = SNRM_CMD | PF_BIT;
-
-       /*
-        *  If we are establishing a connection then insert QoS parameters
-        */
-       if (qos) {
-               skb_put(tx_skb, 9); /* 25 left */
-               frame->saddr = cpu_to_le32(self->saddr);
-               frame->daddr = cpu_to_le32(self->daddr);
-
-               frame->ncaddr = self->caddr;
-
-               ret = irlap_insert_qos_negotiation_params(self, tx_skb);
-               if (ret < 0) {
-                       dev_kfree_skb(tx_skb);
-                       return;
-               }
-       }
-       irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_recv_snrm_cmd (skb, info)
- *
- *    Received SNRM (Set Normal Response Mode) command frame
- *
- */
-static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
-                               struct irlap_info *info)
-{
-       struct snrm_frame *frame;
-
-       if (pskb_may_pull(skb,sizeof(struct snrm_frame))) {
-               frame = (struct snrm_frame *) skb->data;
-
-               /* Copy the new connection address ignoring the C/R bit */
-               info->caddr = frame->ncaddr & 0xFE;
-
-               /* Check if the new connection address is valid */
-               if ((info->caddr == 0x00) || (info->caddr == 0xfe)) {
-                       pr_debug("%s(), invalid connection address!\n",
-                                __func__);
-                       return;
-               }
-
-               /* Copy peer device address */
-               info->daddr = le32_to_cpu(frame->saddr);
-               info->saddr = le32_to_cpu(frame->daddr);
-
-               /* Only accept if addressed directly to us */
-               if (info->saddr != self->saddr) {
-                       pr_debug("%s(), not addressed to us!\n",
-                                __func__);
-                       return;
-               }
-               irlap_do_event(self, RECV_SNRM_CMD, skb, info);
-       } else {
-               /* Signal that this SNRM frame does not contain and I-field */
-               irlap_do_event(self, RECV_SNRM_CMD, skb, NULL);
-       }
-}
-
-/*
- * Function irlap_send_ua_response_frame (qos)
- *
- *    Send UA (Unnumbered Acknowledgement) frame
- *
- */
-void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos)
-{
-       struct sk_buff *tx_skb;
-       struct ua_frame *frame;
-       int ret;
-
-       pr_debug("%s() <%ld>\n", __func__, jiffies);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /* Allocate frame */
-       tx_skb = alloc_skb(sizeof(struct ua_frame) +
-                          IRLAP_NEGOCIATION_PARAMS_LEN,
-                          GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       frame = skb_put(tx_skb, 10);
-
-       /* Build UA response */
-       frame->caddr = self->caddr;
-       frame->control = UA_RSP | PF_BIT;
-
-       frame->saddr = cpu_to_le32(self->saddr);
-       frame->daddr = cpu_to_le32(self->daddr);
-
-       /* Should we send QoS negotiation parameters? */
-       if (qos) {
-               ret = irlap_insert_qos_negotiation_params(self, tx_skb);
-               if (ret < 0) {
-                       dev_kfree_skb(tx_skb);
-                       return;
-               }
-       }
-
-       irlap_queue_xmit(self, tx_skb);
-}
-
-
-/*
- * Function irlap_send_dm_frame (void)
- *
- *    Send disconnected mode (DM) frame
- *
- */
-void irlap_send_dm_frame( struct irlap_cb *self)
-{
-       struct sk_buff *tx_skb = NULL;
-       struct dm_frame *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       tx_skb = alloc_skb(sizeof(struct dm_frame), GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       frame = skb_put(tx_skb, 2);
-
-       if (self->state == LAP_NDM)
-               frame->caddr = CBROADCAST;
-       else
-               frame->caddr = self->caddr;
-
-       frame->control = DM_RSP | PF_BIT;
-
-       irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_send_disc_frame (void)
- *
- *    Send disconnect (DISC) frame
- *
- */
-void irlap_send_disc_frame(struct irlap_cb *self)
-{
-       struct sk_buff *tx_skb = NULL;
-       struct disc_frame *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       tx_skb = alloc_skb(sizeof(struct disc_frame), GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       frame = skb_put(tx_skb, 2);
-
-       frame->caddr = self->caddr | CMD_FRAME;
-       frame->control = DISC_CMD | PF_BIT;
-
-       irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_send_discovery_xid_frame (S, s, command)
- *
- *    Build and transmit a XID (eXchange station IDentifier) discovery
- *    frame.
- */
-void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
-                                   __u8 command, discovery_t *discovery)
-{
-       struct sk_buff *tx_skb = NULL;
-       struct xid_frame *frame;
-       __u32 bcast = BROADCAST;
-       __u8 *info;
-
-       pr_debug("%s(), s=%d, S=%d, command=%d\n", __func__,
-                s, S, command);
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-       IRDA_ASSERT(discovery != NULL, return;);
-
-       tx_skb = alloc_skb(sizeof(struct xid_frame) + IRLAP_DISCOVERY_INFO_LEN,
-                          GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       skb_put(tx_skb, 14);
-       frame = (struct xid_frame *) tx_skb->data;
-
-       if (command) {
-               frame->caddr = CBROADCAST | CMD_FRAME;
-               frame->control =  XID_CMD | PF_BIT;
-       } else {
-               frame->caddr = CBROADCAST;
-               frame->control =  XID_RSP | PF_BIT;
-       }
-       frame->ident = XID_FORMAT;
-
-       frame->saddr = cpu_to_le32(self->saddr);
-
-       if (command)
-               frame->daddr = cpu_to_le32(bcast);
-       else
-               frame->daddr = cpu_to_le32(discovery->data.daddr);
-
-       switch (S) {
-       case 1:
-               frame->flags = 0x00;
-               break;
-       case 6:
-               frame->flags = 0x01;
-               break;
-       case 8:
-               frame->flags = 0x02;
-               break;
-       case 16:
-               frame->flags = 0x03;
-               break;
-       default:
-               frame->flags = 0x02;
-               break;
-       }
-
-       frame->slotnr = s;
-       frame->version = 0x00;
-
-       /*
-        *  Provide info for final slot only in commands, and for all
-        *  responses. Send the second byte of the hint only if the
-        *  EXTENSION bit is set in the first byte.
-        */
-       if (!command || (frame->slotnr == 0xff)) {
-               int len;
-
-               if (discovery->data.hints[0] & HINT_EXTENSION) {
-                       info = skb_put(tx_skb, 2);
-                       info[0] = discovery->data.hints[0];
-                       info[1] = discovery->data.hints[1];
-               } else {
-                       info = skb_put(tx_skb, 1);
-                       info[0] = discovery->data.hints[0];
-               }
-               info = skb_put(tx_skb, 1);
-               info[0] = discovery->data.charset;
-
-               len = IRDA_MIN(discovery->name_len, skb_tailroom(tx_skb));
-               skb_put_data(tx_skb, discovery->data.info, len);
-       }
-       irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_recv_discovery_xid_rsp (skb, info)
- *
- *    Received a XID discovery response
- *
- */
-static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
-                                        struct sk_buff *skb,
-                                        struct irlap_info *info)
-{
-       struct xid_frame *xid;
-       discovery_t *discovery = NULL;
-       __u8 *discovery_info;
-       char *text;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
-               net_err_ratelimited("%s: frame too short!\n", __func__);
-               return;
-       }
-
-       xid = (struct xid_frame *) skb->data;
-
-       info->daddr = le32_to_cpu(xid->saddr);
-       info->saddr = le32_to_cpu(xid->daddr);
-
-       /* Make sure frame is addressed to us */
-       if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
-               pr_debug("%s(), frame is not addressed to us!\n",
-                        __func__);
-               return;
-       }
-
-       if ((discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) {
-               net_warn_ratelimited("%s: kmalloc failed!\n", __func__);
-               return;
-       }
-
-       discovery->data.daddr = info->daddr;
-       discovery->data.saddr = self->saddr;
-       discovery->timestamp = jiffies;
-
-       pr_debug("%s(), daddr=%08x\n", __func__,
-                discovery->data.daddr);
-
-       discovery_info = skb_pull(skb, sizeof(struct xid_frame));
-
-       /* Get info returned from peer */
-       discovery->data.hints[0] = discovery_info[0];
-       if (discovery_info[0] & HINT_EXTENSION) {
-               pr_debug("EXTENSION\n");
-               discovery->data.hints[1] = discovery_info[1];
-               discovery->data.charset = discovery_info[2];
-               text = (char *) &discovery_info[3];
-       } else {
-               discovery->data.hints[1] = 0;
-               discovery->data.charset = discovery_info[1];
-               text = (char *) &discovery_info[2];
-       }
-       /*
-        *  Terminate info string, should be safe since this is where the
-        *  FCS bytes resides.
-        */
-       skb->data[skb->len] = '\0';
-       strncpy(discovery->data.info, text, NICKNAME_MAX_LEN);
-       discovery->name_len = strlen(discovery->data.info);
-
-       info->discovery = discovery;
-
-       irlap_do_event(self, RECV_DISCOVERY_XID_RSP, skb, info);
-}
-
-/*
- * Function irlap_recv_discovery_xid_cmd (skb, info)
- *
- *    Received a XID discovery command
- *
- */
-static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
-                                        struct sk_buff *skb,
-                                        struct irlap_info *info)
-{
-       struct xid_frame *xid;
-       discovery_t *discovery = NULL;
-       __u8 *discovery_info;
-       char *text;
-
-       if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
-               net_err_ratelimited("%s: frame too short!\n", __func__);
-               return;
-       }
-
-       xid = (struct xid_frame *) skb->data;
-
-       info->daddr = le32_to_cpu(xid->saddr);
-       info->saddr = le32_to_cpu(xid->daddr);
-
-       /* Make sure frame is addressed to us */
-       if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
-               pr_debug("%s(), frame is not addressed to us!\n",
-                        __func__);
-               return;
-       }
-
-       switch (xid->flags & 0x03) {
-       case 0x00:
-               info->S = 1;
-               break;
-       case 0x01:
-               info->S = 6;
-               break;
-       case 0x02:
-               info->S = 8;
-               break;
-       case 0x03:
-               info->S = 16;
-               break;
-       default:
-               /* Error!! */
-               return;
-       }
-       info->s = xid->slotnr;
-
-       discovery_info = skb_pull(skb, sizeof(struct xid_frame));
-
-       /*
-        *  Check if last frame
-        */
-       if (info->s == 0xff) {
-               /* Check if things are sane at this point... */
-               if((discovery_info == NULL) ||
-                  !pskb_may_pull(skb, 3)) {
-                       net_err_ratelimited("%s: discovery frame too short!\n",
-                                           __func__);
-                       return;
-               }
-
-               /*
-                *  We now have some discovery info to deliver!
-                */
-               discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC);
-               if (!discovery)
-                       return;
-
-               discovery->data.daddr = info->daddr;
-               discovery->data.saddr = self->saddr;
-               discovery->timestamp = jiffies;
-
-               discovery->data.hints[0] = discovery_info[0];
-               if (discovery_info[0] & HINT_EXTENSION) {
-                       discovery->data.hints[1] = discovery_info[1];
-                       discovery->data.charset = discovery_info[2];
-                       text = (char *) &discovery_info[3];
-               } else {
-                       discovery->data.hints[1] = 0;
-                       discovery->data.charset = discovery_info[1];
-                       text = (char *) &discovery_info[2];
-               }
-               /*
-                *  Terminate string, should be safe since this is where the
-                *  FCS bytes resides.
-                */
-               skb->data[skb->len] = '\0';
-               strncpy(discovery->data.info, text, NICKNAME_MAX_LEN);
-               discovery->name_len = strlen(discovery->data.info);
-
-               info->discovery = discovery;
-       } else
-               info->discovery = NULL;
-
-       irlap_do_event(self, RECV_DISCOVERY_XID_CMD, skb, info);
-}
-
-/*
- * Function irlap_send_rr_frame (self, command)
- *
- *    Build and transmit RR (Receive Ready) frame. Notice that it is currently
- *    only possible to send RR frames with the poll bit set.
- */
-void irlap_send_rr_frame(struct irlap_cb *self, int command)
-{
-       struct sk_buff *tx_skb;
-       struct rr_frame *frame;
-
-       tx_skb = alloc_skb(sizeof(struct rr_frame), GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       frame = skb_put(tx_skb, 2);
-
-       frame->caddr = self->caddr;
-       frame->caddr |= (command) ? CMD_FRAME : 0;
-
-       frame->control = RR | PF_BIT | (self->vr << 5);
-
-       irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_send_rd_frame (self)
- *
- *    Request disconnect. Used by a secondary station to request the
- *    disconnection of the link.
- */
-void irlap_send_rd_frame(struct irlap_cb *self)
-{
-       struct sk_buff *tx_skb;
-       struct rd_frame *frame;
-
-       tx_skb = alloc_skb(sizeof(struct rd_frame), GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       frame = skb_put(tx_skb, 2);
-
-       frame->caddr = self->caddr;
-       frame->control = RD_RSP | PF_BIT;
-
-       irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_recv_rr_frame (skb, info)
- *
- *    Received RR (Receive Ready) frame from peer station, no harm in
- *    making it inline since its called only from one single place
- *    (irlap_driver_rcv).
- */
-static inline void irlap_recv_rr_frame(struct irlap_cb *self,
-                                      struct sk_buff *skb,
-                                      struct irlap_info *info, int command)
-{
-       info->nr = skb->data[1] >> 5;
-
-       /* Check if this is a command or a response frame */
-       if (command)
-               irlap_do_event(self, RECV_RR_CMD, skb, info);
-       else
-               irlap_do_event(self, RECV_RR_RSP, skb, info);
-}
-
-/*
- * Function irlap_recv_rnr_frame (self, skb, info)
- *
- *    Received RNR (Receive Not Ready) frame from peer station
- *
- */
-static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb,
-                                struct irlap_info *info, int command)
-{
-       info->nr = skb->data[1] >> 5;
-
-       pr_debug("%s(), nr=%d, %ld\n", __func__, info->nr, jiffies);
-
-       if (command)
-               irlap_do_event(self, RECV_RNR_CMD, skb, info);
-       else
-               irlap_do_event(self, RECV_RNR_RSP, skb, info);
-}
-
-static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb,
-                                struct irlap_info *info, int command)
-{
-       info->nr = skb->data[1] >> 5;
-
-       /* Check if this is a command or a response frame */
-       if (command)
-               irlap_do_event(self, RECV_REJ_CMD, skb, info);
-       else
-               irlap_do_event(self, RECV_REJ_RSP, skb, info);
-}
-
-static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb,
-                                 struct irlap_info *info, int command)
-{
-       info->nr = skb->data[1] >> 5;
-
-       /* Check if this is a command or a response frame */
-       if (command)
-               irlap_do_event(self, RECV_SREJ_CMD, skb, info);
-       else
-               irlap_do_event(self, RECV_SREJ_RSP, skb, info);
-}
-
-static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb,
-                                 struct irlap_info *info, int command)
-{
-       /* Check if this is a command or a response frame */
-       if (command)
-               irlap_do_event(self, RECV_DISC_CMD, skb, info);
-       else
-               irlap_do_event(self, RECV_RD_RSP, skb, info);
-}
-
-/*
- * Function irlap_recv_ua_frame (skb, frame)
- *
- *    Received UA (Unnumbered Acknowledgement) frame
- *
- */
-static inline void irlap_recv_ua_frame(struct irlap_cb *self,
-                                      struct sk_buff *skb,
-                                      struct irlap_info *info)
-{
-       irlap_do_event(self, RECV_UA_RSP, skb, info);
-}
-
-/*
- * Function irlap_send_data_primary(self, skb)
- *
- *    Send I-frames as the primary station but without the poll bit set
- *
- */
-void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb)
-{
-       struct sk_buff *tx_skb;
-
-       if (skb->data[1] == I_FRAME) {
-
-               /*
-                *  Insert frame sequence number (Vs) in control field before
-                *  inserting into transmit window queue.
-                */
-               skb->data[1] = I_FRAME | (self->vs << 1);
-
-               /*
-                *  Insert frame in store, in case of retransmissions
-                *  Increase skb reference count, see irlap_do_event()
-                */
-               skb_get(skb);
-               skb_queue_tail(&self->wx_list, skb);
-
-               /* Copy buffer */
-               tx_skb = skb_clone(skb, GFP_ATOMIC);
-               if (tx_skb == NULL) {
-                       return;
-               }
-
-               self->vs = (self->vs + 1) % 8;
-               self->ack_required = FALSE;
-               self->window -= 1;
-
-               irlap_send_i_frame( self, tx_skb, CMD_FRAME);
-       } else {
-               pr_debug("%s(), sending unreliable frame\n", __func__);
-               irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
-               self->window -= 1;
-       }
-}
-/*
- * Function irlap_send_data_primary_poll (self, skb)
- *
- *    Send I(nformation) frame as primary with poll bit set
- */
-void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
-{
-       struct sk_buff *tx_skb;
-       int transmission_time;
-
-       /* Stop P timer */
-       del_timer(&self->poll_timer);
-
-       /* Is this reliable or unreliable data? */
-       if (skb->data[1] == I_FRAME) {
-
-               /*
-                *  Insert frame sequence number (Vs) in control field before
-                *  inserting into transmit window queue.
-                */
-               skb->data[1] = I_FRAME | (self->vs << 1);
-
-               /*
-                *  Insert frame in store, in case of retransmissions
-                *  Increase skb reference count, see irlap_do_event()
-                */
-               skb_get(skb);
-               skb_queue_tail(&self->wx_list, skb);
-
-               /* Copy buffer */
-               tx_skb = skb_clone(skb, GFP_ATOMIC);
-               if (tx_skb == NULL) {
-                       return;
-               }
-
-               /*
-                *  Set poll bit if necessary. We do this to the copied
-                *  skb, since retransmitted need to set or clear the poll
-                *  bit depending on when they are sent.
-                */
-               tx_skb->data[1] |= PF_BIT;
-
-               self->vs = (self->vs + 1) % 8;
-               self->ack_required = FALSE;
-
-               irlap_next_state(self, LAP_NRM_P);
-               irlap_send_i_frame(self, tx_skb, CMD_FRAME);
-       } else {
-               pr_debug("%s(), sending unreliable frame\n", __func__);
-
-               if (self->ack_required) {
-                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
-                       irlap_next_state(self, LAP_NRM_P);
-                       irlap_send_rr_frame(self, CMD_FRAME);
-                       self->ack_required = FALSE;
-               } else {
-                       skb->data[1] |= PF_BIT;
-                       irlap_next_state(self, LAP_NRM_P);
-                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
-               }
-       }
-
-       /* How much time we took for transmission of all frames.
-        * We don't know, so let assume we used the full window. Jean II */
-       transmission_time = self->final_timeout;
-
-       /* Reset parameter so that we can fill next window */
-       self->window = self->window_size;
-
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
-       /* Remove what we have not used. Just do a prorata of the
-        * bytes left in window to window capacity.
-        * See max_line_capacities[][] in qos.c for details. Jean II */
-       transmission_time -= (self->final_timeout * self->bytes_left
-                             / self->line_capacity);
-       pr_debug("%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n",
-                __func__, self->final_timeout, self->bytes_left,
-                self->line_capacity, transmission_time);
-
-       /* We are allowed to transmit a maximum number of bytes again. */
-       self->bytes_left = self->line_capacity;
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-
-       /*
-        * The network layer has a intermediate buffer between IrLAP
-        * and the IrDA driver which can contain 8 frames. So, even
-        * though IrLAP is currently sending the *last* frame of the
-        * tx-window, the driver most likely has only just started
-        * sending the *first* frame of the same tx-window.
-        * I.e. we are always at the very beginning of or Tx window.
-        * Now, we are supposed to set the final timer from the end
-        * of our tx-window to let the other peer reply. So, we need
-        * to add extra time to compensate for the fact that we
-        * are really at the start of tx-window, otherwise the final timer
-        * might expire before he can answer...
-        * Jean II
-        */
-       irlap_start_final_timer(self, self->final_timeout + transmission_time);
-
-       /*
-        * The clever amongst you might ask why we do this adjustement
-        * only here, and not in all the other cases in irlap_event.c.
-        * In all those other case, we only send a very short management
-        * frame (few bytes), so the adjustement would be lost in the
-        * noise...
-        * The exception of course is irlap_resend_rejected_frame().
-        * Jean II */
-}
-
-/*
- * Function irlap_send_data_secondary_final (self, skb)
- *
- *    Send I(nformation) frame as secondary with final bit set
- *
- */
-void irlap_send_data_secondary_final(struct irlap_cb *self,
-                                    struct sk_buff *skb)
-{
-       struct sk_buff *tx_skb = NULL;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       /* Is this reliable or unreliable data? */
-       if (skb->data[1] == I_FRAME) {
-
-               /*
-                *  Insert frame sequence number (Vs) in control field before
-                *  inserting into transmit window queue.
-                */
-               skb->data[1] = I_FRAME | (self->vs << 1);
-
-               /*
-                *  Insert frame in store, in case of retransmissions
-                *  Increase skb reference count, see irlap_do_event()
-                */
-               skb_get(skb);
-               skb_queue_tail(&self->wx_list, skb);
-
-               tx_skb = skb_clone(skb, GFP_ATOMIC);
-               if (tx_skb == NULL) {
-                       return;
-               }
-
-               tx_skb->data[1] |= PF_BIT;
-
-               self->vs = (self->vs + 1) % 8;
-               self->ack_required = FALSE;
-
-               irlap_send_i_frame(self, tx_skb, RSP_FRAME);
-       } else {
-               if (self->ack_required) {
-                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
-                       irlap_send_rr_frame(self, RSP_FRAME);
-                       self->ack_required = FALSE;
-               } else {
-                       skb->data[1] |= PF_BIT;
-                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
-               }
-       }
-
-       self->window = self->window_size;
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
-       /* We are allowed to transmit a maximum number of bytes again. */
-       self->bytes_left = self->line_capacity;
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-
-       irlap_start_wd_timer(self, self->wd_timeout);
-}
-
-/*
- * Function irlap_send_data_secondary (self, skb)
- *
- *    Send I(nformation) frame as secondary without final bit set
- *
- */
-void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb)
-{
-       struct sk_buff *tx_skb = NULL;
-
-       /* Is this reliable or unreliable data? */
-       if (skb->data[1] == I_FRAME) {
-
-               /*
-                *  Insert frame sequence number (Vs) in control field before
-                *  inserting into transmit window queue.
-                */
-               skb->data[1] = I_FRAME | (self->vs << 1);
-
-               /*
-                *  Insert frame in store, in case of retransmissions
-                *  Increase skb reference count, see irlap_do_event()
-                */
-               skb_get(skb);
-               skb_queue_tail(&self->wx_list, skb);
-
-               tx_skb = skb_clone(skb, GFP_ATOMIC);
-               if (tx_skb == NULL) {
-                       return;
-               }
-
-               self->vs = (self->vs + 1) % 8;
-               self->ack_required = FALSE;
-               self->window -= 1;
-
-               irlap_send_i_frame(self, tx_skb, RSP_FRAME);
-       } else {
-               irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
-               self->window -= 1;
-       }
-}
-
-/*
- * Function irlap_resend_rejected_frames (nr)
- *
- *    Resend frames which has not been acknowledged. Should be safe to
- *    traverse the list without locking it since this function will only be
- *    called from interrupt context (BH)
- */
-void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
-{
-       struct sk_buff *tx_skb;
-       struct sk_buff *skb;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /*  Resend unacknowledged frame(s) */
-       skb_queue_walk(&self->wx_list, skb) {
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-
-               /* We copy the skb to be retransmitted since we will have to
-                * modify it. Cloning will confuse packet sniffers
-                */
-               /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
-               tx_skb = skb_copy(skb, GFP_ATOMIC);
-               if (!tx_skb) {
-                       pr_debug("%s(), unable to copy\n", __func__);
-                       return;
-               }
-
-               /* Clear old Nr field + poll bit */
-               tx_skb->data[1] &= 0x0f;
-
-               /*
-                *  Set poll bit on the last frame retransmitted
-                */
-               if (skb_queue_is_last(&self->wx_list, skb))
-                       tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
-               else
-                       tx_skb->data[1] &= ~PF_BIT; /* Clear p/f bit */
-
-               irlap_send_i_frame(self, tx_skb, command);
-       }
-#if 0 /* Not yet */
-       /*
-        *  We can now fill the window with additional data frames
-        */
-       while (!skb_queue_empty(&self->txq)) {
-
-               pr_debug("%s(), sending additional frames!\n", __func__);
-               if (self->window > 0) {
-                       skb = skb_dequeue( &self->txq);
-                       IRDA_ASSERT(skb != NULL, return;);
-
-                       /*
-                        *  If send window > 1 then send frame with pf
-                        *  bit cleared
-                        */
-                       if ((self->window > 1) &&
-                           !skb_queue_empty(&self->txq)) {
-                               irlap_send_data_primary(self, skb);
-                       } else {
-                               irlap_send_data_primary_poll(self, skb);
-                       }
-                       kfree_skb(skb);
-               }
-       }
-#endif
-}
-
-void irlap_resend_rejected_frame(struct irlap_cb *self, int command)
-{
-       struct sk_buff *tx_skb;
-       struct sk_buff *skb;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       /*  Resend unacknowledged frame(s) */
-       skb = skb_peek(&self->wx_list);
-       if (skb != NULL) {
-               irlap_wait_min_turn_around(self, &self->qos_tx);
-
-               /* We copy the skb to be retransmitted since we will have to
-                * modify it. Cloning will confuse packet sniffers
-                */
-               /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
-               tx_skb = skb_copy(skb, GFP_ATOMIC);
-               if (!tx_skb) {
-                       pr_debug("%s(), unable to copy\n", __func__);
-                       return;
-               }
-
-               /* Clear old Nr field + poll bit */
-               tx_skb->data[1] &= 0x0f;
-
-               /*  Set poll/final bit */
-               tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
-
-               irlap_send_i_frame(self, tx_skb, command);
-       }
-}
-
-/*
- * Function irlap_send_ui_frame (self, skb, command)
- *
- *    Contruct and transmit an Unnumbered Information (UI) frame
- *
- */
-void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
-                        __u8 caddr, int command)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       /* Insert connection address */
-       skb->data[0] = caddr | ((command) ? CMD_FRAME : 0);
-
-       irlap_queue_xmit(self, skb);
-}
-
-/*
- * Function irlap_send_i_frame (skb)
- *
- *    Contruct and transmit Information (I) frame
- */
-static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb,
-                              int command)
-{
-       /* Insert connection address */
-       skb->data[0] = self->caddr;
-       skb->data[0] |= (command) ? CMD_FRAME : 0;
-
-       /* Insert next to receive (Vr) */
-       skb->data[1] |= (self->vr << 5);  /* insert nr */
-
-       irlap_queue_xmit(self, skb);
-}
-
-/*
- * Function irlap_recv_i_frame (skb, frame)
- *
- *    Receive and parse an I (Information) frame, no harm in making it inline
- *    since it's called only from one single place (irlap_driver_rcv).
- */
-static inline void irlap_recv_i_frame(struct irlap_cb *self,
-                                     struct sk_buff *skb,
-                                     struct irlap_info *info, int command)
-{
-       info->nr = skb->data[1] >> 5;          /* Next to receive */
-       info->pf = skb->data[1] & PF_BIT;      /* Final bit */
-       info->ns = (skb->data[1] >> 1) & 0x07; /* Next to send */
-
-       /* Check if this is a command or a response frame */
-       if (command)
-               irlap_do_event(self, RECV_I_CMD, skb, info);
-       else
-               irlap_do_event(self, RECV_I_RSP, skb, info);
-}
-
-/*
- * Function irlap_recv_ui_frame (self, skb, info)
- *
- *    Receive and parse an Unnumbered Information (UI) frame
- *
- */
-static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
-                               struct irlap_info *info)
-{
-       info->pf = skb->data[1] & PF_BIT;      /* Final bit */
-
-       irlap_do_event(self, RECV_UI_FRAME, skb, info);
-}
-
-/*
- * Function irlap_recv_frmr_frame (skb, frame)
- *
- *    Received Frame Reject response.
- *
- */
-static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb,
-                                 struct irlap_info *info)
-{
-       __u8 *frame;
-       int w, x, y, z;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(info != NULL, return;);
-
-       if (!pskb_may_pull(skb, 4)) {
-               net_err_ratelimited("%s: frame too short!\n", __func__);
-               return;
-       }
-
-       frame = skb->data;
-
-       info->nr = frame[2] >> 5;          /* Next to receive */
-       info->pf = frame[2] & PF_BIT;      /* Final bit */
-       info->ns = (frame[2] >> 1) & 0x07; /* Next to send */
-
-       w = frame[3] & 0x01;
-       x = frame[3] & 0x02;
-       y = frame[3] & 0x04;
-       z = frame[3] & 0x08;
-
-       if (w) {
-               pr_debug("Rejected control field is undefined or not implemented\n");
-       }
-       if (x) {
-               pr_debug("Rejected control field was invalid because it contained a non permitted I field\n");
-       }
-       if (y) {
-               pr_debug("Received I field exceeded the maximum negotiated for the existing connection or exceeded the maximum this station supports if no connection exists\n");
-       }
-       if (z) {
-               pr_debug("Rejected control field control field contained an invalid Nr count\n");
-       }
-       irlap_do_event(self, RECV_FRMR_RSP, skb, info);
-}
-
-/*
- * Function irlap_send_test_frame (self, daddr)
- *
- *    Send a test frame response
- *
- */
-void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
-                          struct sk_buff *cmd)
-{
-       struct sk_buff *tx_skb;
-       struct test_frame *frame;
-
-       tx_skb = alloc_skb(cmd->len + sizeof(struct test_frame), GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       /* Broadcast frames must include saddr and daddr fields */
-       if (caddr == CBROADCAST) {
-               frame = skb_put(tx_skb, sizeof(struct test_frame));
-
-               /* Insert the swapped addresses */
-               frame->saddr = cpu_to_le32(self->saddr);
-               frame->daddr = cpu_to_le32(daddr);
-       } else
-               frame = skb_put(tx_skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
-
-       frame->caddr = caddr;
-       frame->control = TEST_RSP | PF_BIT;
-
-       /* Copy info */
-       skb_put_data(tx_skb, cmd->data, cmd->len);
-
-       /* Return to sender */
-       irlap_wait_min_turn_around(self, &self->qos_tx);
-       irlap_queue_xmit(self, tx_skb);
-}
-
-/*
- * Function irlap_recv_test_frame (self, skb)
- *
- *    Receive a test frame
- *
- */
-static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
-                                 struct irlap_info *info, int command)
-{
-       struct test_frame *frame;
-
-       if (!pskb_may_pull(skb, sizeof(*frame))) {
-               net_err_ratelimited("%s: frame too short!\n", __func__);
-               return;
-       }
-       frame = (struct test_frame *) skb->data;
-
-       /* Broadcast frames must carry saddr and daddr fields */
-       if (info->caddr == CBROADCAST) {
-               if (skb->len < sizeof(struct test_frame)) {
-                       pr_debug("%s() test frame too short!\n",
-                                __func__);
-                       return;
-               }
-
-               /* Read and swap addresses */
-               info->daddr = le32_to_cpu(frame->saddr);
-               info->saddr = le32_to_cpu(frame->daddr);
-
-               /* Make sure frame is addressed to us */
-               if ((info->saddr != self->saddr) &&
-                   (info->saddr != BROADCAST)) {
-                       return;
-               }
-       }
-
-       if (command)
-               irlap_do_event(self, RECV_TEST_CMD, skb, info);
-       else
-               irlap_do_event(self, RECV_TEST_RSP, skb, info);
-}
-
-/*
- * Function irlap_driver_rcv (skb, netdev, ptype)
- *
- *    Called when a frame is received. Dispatches the right receive function
- *    for processing of the frame.
- *
- * Note on skb management :
- * After calling the higher layers of the IrDA stack, we always
- * kfree() the skb, which drop the reference count (and potentially
- * destroy it).
- * If a higher layer of the stack want to keep the skb around (to put
- * in a queue or pass it to the higher layer), it will need to use
- * skb_get() to keep a reference on it. This is usually done at the
- * LMP level in irlmp.c.
- * Jean II
- */
-int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
-                    struct packet_type *ptype, struct net_device *orig_dev)
-{
-       struct irlap_info info;
-       struct irlap_cb *self;
-       int command;
-       __u8 control;
-       int ret = -1;
-
-       if (!net_eq(dev_net(dev), &init_net))
-               goto out;
-
-       /* FIXME: should we get our own field? */
-       self = (struct irlap_cb *) dev->atalk_ptr;
-
-       /* If the net device is down, then IrLAP is gone! */
-       if (!self || self->magic != LAP_MAGIC)
-               goto err;
-
-       /* We are no longer an "old" protocol, so we need to handle
-        * share and non linear skbs. This should never happen, so
-        * we don't need to be clever about it. Jean II */
-       if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
-               net_err_ratelimited("%s: can't clone shared skb!\n", __func__);
-               goto err;
-       }
-
-       /* Check if frame is large enough for parsing */
-       if (!pskb_may_pull(skb, 2)) {
-               net_err_ratelimited("%s: frame too short!\n", __func__);
-               goto err;
-       }
-
-       command    = skb->data[0] & CMD_FRAME;
-       info.caddr = skb->data[0] & CBROADCAST;
-
-       info.pf      = skb->data[1] &  PF_BIT;
-       info.control = skb->data[1] & ~PF_BIT; /* Mask away poll/final bit */
-
-       control = info.control;
-
-       /*  First we check if this frame has a valid connection address */
-       if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) {
-               pr_debug("%s(), wrong connection address!\n",
-                        __func__);
-               goto out;
-       }
-       /*
-        *  Optimize for the common case and check if the frame is an
-        *  I(nformation) frame. Only I-frames have bit 0 set to 0
-        */
-       if (~control & 0x01) {
-               irlap_recv_i_frame(self, skb, &info, command);
-               goto out;
-       }
-       /*
-        *  We now check is the frame is an S(upervisory) frame. Only
-        *  S-frames have bit 0 set to 1 and bit 1 set to 0
-        */
-       if (~control & 0x02) {
-               /*
-                *  Received S(upervisory) frame, check which frame type it is
-                *  only the first nibble is of interest
-                */
-               switch (control & 0x0f) {
-               case RR:
-                       irlap_recv_rr_frame(self, skb, &info, command);
-                       break;
-               case RNR:
-                       irlap_recv_rnr_frame(self, skb, &info, command);
-                       break;
-               case REJ:
-                       irlap_recv_rej_frame(self, skb, &info, command);
-                       break;
-               case SREJ:
-                       irlap_recv_srej_frame(self, skb, &info, command);
-                       break;
-               default:
-                       net_warn_ratelimited("%s: Unknown S-frame %02x received!\n",
-                                            __func__, info.control);
-                       break;
-               }
-               goto out;
-       }
-       /*
-        *  This must be a C(ontrol) frame
-        */
-       switch (control) {
-       case XID_RSP:
-               irlap_recv_discovery_xid_rsp(self, skb, &info);
-               break;
-       case XID_CMD:
-               irlap_recv_discovery_xid_cmd(self, skb, &info);
-               break;
-       case SNRM_CMD:
-               irlap_recv_snrm_cmd(self, skb, &info);
-               break;
-       case DM_RSP:
-               irlap_do_event(self, RECV_DM_RSP, skb, &info);
-               break;
-       case DISC_CMD: /* And RD_RSP since they have the same value */
-               irlap_recv_disc_frame(self, skb, &info, command);
-               break;
-       case TEST_CMD:
-               irlap_recv_test_frame(self, skb, &info, command);
-               break;
-       case UA_RSP:
-               irlap_recv_ua_frame(self, skb, &info);
-               break;
-       case FRMR_RSP:
-               irlap_recv_frmr_frame(self, skb, &info);
-               break;
-       case UI_FRAME:
-               irlap_recv_ui_frame(self, skb, &info);
-               break;
-       default:
-               net_warn_ratelimited("%s: Unknown frame %02x received!\n",
-                                    __func__, info.control);
-               break;
-       }
-out:
-       ret = 0;
-err:
-       /* Always drop our reference on the skb */
-       dev_kfree_skb(skb);
-       return ret;
-}
diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c
deleted file mode 100644 (file)
index 4396459..0000000
+++ /dev/null
@@ -1,1996 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlmp.c
- * Version:       1.0
- * Description:   IrDA Link Management Protocol (LMP) layer
- * Status:        Stable.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Aug 17 20:54:32 1997
- * Modified at:   Wed Jan  5 11:26:03 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/skbuff.h>
-#include <linux/types.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/kmod.h>
-#include <linux/random.h>
-#include <linux/seq_file.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/timer.h>
-#include <net/irda/qos.h>
-#include <net/irda/irlap.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irlmp_frame.h>
-
-#include <asm/unaligned.h>
-
-static __u8 irlmp_find_free_slsap(void);
-static int irlmp_slsap_inuse(__u8 slsap_sel);
-
-/* Master structure */
-struct irlmp_cb *irlmp = NULL;
-
-/* These can be altered by the sysctl interface */
-int  sysctl_discovery         = 0;
-int  sysctl_discovery_timeout = 3; /* 3 seconds by default */
-int  sysctl_discovery_slots   = 6; /* 6 slots by default */
-int  sysctl_lap_keepalive_time = LM_IDLE_TIMEOUT * 1000 / HZ;
-char sysctl_devname[65];
-
-static const char *irlmp_reasons[] = {
-       "ERROR, NOT USED",
-       "LM_USER_REQUEST",
-       "LM_LAP_DISCONNECT",
-       "LM_CONNECT_FAILURE",
-       "LM_LAP_RESET",
-       "LM_INIT_DISCONNECT",
-       "ERROR, NOT USED",
-       "UNKNOWN",
-};
-
-const char *irlmp_reason_str(LM_REASON reason)
-{
-       reason = min_t(size_t, reason, ARRAY_SIZE(irlmp_reasons) - 1);
-       return irlmp_reasons[reason];
-}
-
-/*
- * Function irlmp_init (void)
- *
- *    Create (allocate) the main IrLMP structure
- *
- */
-int __init irlmp_init(void)
-{
-       /* Initialize the irlmp structure. */
-       irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL);
-       if (irlmp == NULL)
-               return -ENOMEM;
-
-       irlmp->magic = LMP_MAGIC;
-
-       irlmp->clients = hashbin_new(HB_LOCK);
-       irlmp->services = hashbin_new(HB_LOCK);
-       irlmp->links = hashbin_new(HB_LOCK);
-       irlmp->unconnected_lsaps = hashbin_new(HB_LOCK);
-       irlmp->cachelog = hashbin_new(HB_NOLOCK);
-
-       if ((irlmp->clients == NULL) ||
-           (irlmp->services == NULL) ||
-           (irlmp->links == NULL) ||
-           (irlmp->unconnected_lsaps == NULL) ||
-           (irlmp->cachelog == NULL)) {
-               return -ENOMEM;
-       }
-
-       spin_lock_init(&irlmp->cachelog->hb_spinlock);
-
-       irlmp->last_lsap_sel = 0x0f; /* Reserved 0x00-0x0f */
-       strcpy(sysctl_devname, "Linux");
-
-       init_timer(&irlmp->discovery_timer);
-
-       /* Do discovery every 3 seconds, conditionally */
-       if (sysctl_discovery)
-               irlmp_start_discovery_timer(irlmp,
-                                           sysctl_discovery_timeout*HZ);
-
-       return 0;
-}
-
-/*
- * Function irlmp_cleanup (void)
- *
- *    Remove IrLMP layer
- *
- */
-void irlmp_cleanup(void)
-{
-       /* Check for main structure */
-       IRDA_ASSERT(irlmp != NULL, return;);
-       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;);
-
-       del_timer(&irlmp->discovery_timer);
-
-       hashbin_delete(irlmp->links, (FREE_FUNC) kfree);
-       hashbin_delete(irlmp->unconnected_lsaps, (FREE_FUNC) kfree);
-       hashbin_delete(irlmp->clients, (FREE_FUNC) kfree);
-       hashbin_delete(irlmp->services, (FREE_FUNC) kfree);
-       hashbin_delete(irlmp->cachelog, (FREE_FUNC) kfree);
-
-       /* De-allocate main structure */
-       kfree(irlmp);
-       irlmp = NULL;
-}
-
-/*
- * Function irlmp_open_lsap (slsap, notify)
- *
- *   Register with IrLMP and create a local LSAP,
- *   returns handle to LSAP.
- */
-struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid)
-{
-       struct lsap_cb *self;
-
-       IRDA_ASSERT(notify != NULL, return NULL;);
-       IRDA_ASSERT(irlmp != NULL, return NULL;);
-       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return NULL;);
-       IRDA_ASSERT(notify->instance != NULL, return NULL;);
-
-       /*  Does the client care which Source LSAP selector it gets?  */
-       if (slsap_sel == LSAP_ANY) {
-               slsap_sel = irlmp_find_free_slsap();
-               if (!slsap_sel)
-                       return NULL;
-       } else if (irlmp_slsap_inuse(slsap_sel))
-               return NULL;
-
-       /* Allocate new instance of a LSAP connection */
-       self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC);
-       if (self == NULL)
-               return NULL;
-
-       self->magic = LMP_LSAP_MAGIC;
-       self->slsap_sel = slsap_sel;
-
-       /* Fix connectionless LSAP's */
-       if (slsap_sel == LSAP_CONNLESS) {
-#ifdef CONFIG_IRDA_ULTRA
-               self->dlsap_sel = LSAP_CONNLESS;
-               self->pid = pid;
-#endif /* CONFIG_IRDA_ULTRA */
-       } else
-               self->dlsap_sel = LSAP_ANY;
-       /* self->connected = FALSE; -> already NULL via memset() */
-
-       init_timer(&self->watchdog_timer);
-
-       self->notify = *notify;
-
-       self->lsap_state = LSAP_DISCONNECTED;
-
-       /* Insert into queue of unconnected LSAPs */
-       hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self,
-                      (long) self, NULL);
-
-       return self;
-}
-EXPORT_SYMBOL(irlmp_open_lsap);
-
-/*
- * Function __irlmp_close_lsap (self)
- *
- *    Remove an instance of LSAP
- */
-static void __irlmp_close_lsap(struct lsap_cb *self)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-
-       /*
-        *  Set some of the variables to preset values
-        */
-       self->magic = 0;
-       del_timer(&self->watchdog_timer); /* Important! */
-
-       if (self->conn_skb)
-               dev_kfree_skb(self->conn_skb);
-
-       kfree(self);
-}
-
-/*
- * Function irlmp_close_lsap (self)
- *
- *    Close and remove LSAP
- *
- */
-void irlmp_close_lsap(struct lsap_cb *self)
-{
-       struct lap_cb *lap;
-       struct lsap_cb *lsap = NULL;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-
-       /*
-        *  Find out if we should remove this LSAP from a link or from the
-        *  list of unconnected lsaps (not associated with a link)
-        */
-       lap = self->lap;
-       if (lap) {
-               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
-               /* We might close a LSAP before it has completed the
-                * connection setup. In those case, higher layers won't
-                * send a proper disconnect request. Harmless, except
-                * that we will forget to close LAP... - Jean II */
-               if(self->lsap_state != LSAP_DISCONNECTED) {
-                       self->lsap_state = LSAP_DISCONNECTED;
-                       irlmp_do_lap_event(self->lap,
-                                          LM_LAP_DISCONNECT_REQUEST, NULL);
-               }
-               /* Now, remove from the link */
-               lsap = hashbin_remove(lap->lsaps, (long) self, NULL);
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-               lap->cache.valid = FALSE;
-#endif
-       }
-       self->lap = NULL;
-       /* Check if we found the LSAP! If not then try the unconnected lsaps */
-       if (!lsap) {
-               lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self,
-                                     NULL);
-       }
-       if (!lsap) {
-               pr_debug("%s(), Looks like somebody has removed me already!\n",
-                        __func__);
-               return;
-       }
-       __irlmp_close_lsap(self);
-}
-EXPORT_SYMBOL(irlmp_close_lsap);
-
-/*
- * Function irlmp_register_irlap (saddr, notify)
- *
- *    Register IrLAP layer with IrLMP. There is possible to have multiple
- *    instances of the IrLAP layer, each connected to different IrDA ports
- *
- */
-void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify)
-{
-       struct lap_cb *lap;
-
-       IRDA_ASSERT(irlmp != NULL, return;);
-       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;);
-       IRDA_ASSERT(notify != NULL, return;);
-
-       /*
-        *  Allocate new instance of a LSAP connection
-        */
-       lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL);
-       if (lap == NULL)
-               return;
-
-       lap->irlap = irlap;
-       lap->magic = LMP_LAP_MAGIC;
-       lap->saddr = saddr;
-       lap->daddr = DEV_ADDR_ANY;
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-       lap->cache.valid = FALSE;
-#endif
-       lap->lsaps = hashbin_new(HB_LOCK);
-       if (lap->lsaps == NULL) {
-               net_warn_ratelimited("%s(), unable to kmalloc lsaps\n",
-                                    __func__);
-               kfree(lap);
-               return;
-       }
-
-       lap->lap_state = LAP_STANDBY;
-
-       init_timer(&lap->idle_timer);
-
-       /*
-        *  Insert into queue of LMP links
-        */
-       hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL);
-
-       /*
-        *  We set only this variable so IrLAP can tell us on which link the
-        *  different events happened on
-        */
-       irda_notify_init(notify);
-       notify->instance = lap;
-}
-
-/*
- * Function irlmp_unregister_irlap (saddr)
- *
- *    IrLAP layer has been removed!
- *
- */
-void irlmp_unregister_link(__u32 saddr)
-{
-       struct lap_cb *link;
-
-       /* We must remove ourselves from the hashbin *first*. This ensure
-        * that no more LSAPs will be open on this link and no discovery
-        * will be triggered anymore. Jean II */
-       link = hashbin_remove(irlmp->links, saddr, NULL);
-       if (link) {
-               IRDA_ASSERT(link->magic == LMP_LAP_MAGIC, return;);
-
-               /* Kill all the LSAPs on this link. Jean II */
-               link->reason = LAP_DISC_INDICATION;
-               link->daddr = DEV_ADDR_ANY;
-               irlmp_do_lap_event(link, LM_LAP_DISCONNECT_INDICATION, NULL);
-
-               /* Remove all discoveries discovered at this link */
-               irlmp_expire_discoveries(irlmp->cachelog, link->saddr, TRUE);
-
-               /* Final cleanup */
-               del_timer(&link->idle_timer);
-               link->magic = 0;
-               hashbin_delete(link->lsaps, (FREE_FUNC) __irlmp_close_lsap);
-               kfree(link);
-       }
-}
-
-/*
- * Function irlmp_connect_request (handle, dlsap, userdata)
- *
- *    Connect with a peer LSAP
- *
- */
-int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
-                         __u32 saddr, __u32 daddr,
-                         struct qos_info *qos, struct sk_buff *userdata)
-{
-       struct sk_buff *tx_skb = userdata;
-       struct lap_cb *lap;
-       struct lsap_cb *lsap;
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -EBADR;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;);
-
-       pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n",
-                __func__, self->slsap_sel, dlsap_sel, saddr, daddr);
-
-       if (test_bit(0, &self->connected)) {
-               ret = -EISCONN;
-               goto err;
-       }
-
-       /* Client must supply destination device address */
-       if (!daddr) {
-               ret = -EINVAL;
-               goto err;
-       }
-
-       /* Any userdata? */
-       if (tx_skb == NULL) {
-               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
-               if (!tx_skb)
-                       return -ENOMEM;
-
-               skb_reserve(tx_skb, LMP_MAX_HEADER);
-       }
-
-       /* Make room for MUX control header (3 bytes) */
-       IRDA_ASSERT(skb_headroom(tx_skb) >= LMP_CONTROL_HEADER, return -1;);
-       skb_push(tx_skb, LMP_CONTROL_HEADER);
-
-       self->dlsap_sel = dlsap_sel;
-
-       /*
-        * Find the link to where we should try to connect since there may
-        * be more than one IrDA port on this machine. If the client has
-        * passed us the saddr (and already knows which link to use), then
-        * we use that to find the link, if not then we have to look in the
-        * discovery log and check if any of the links has discovered a
-        * device with the given daddr
-        */
-       if ((!saddr) || (saddr == DEV_ADDR_ANY)) {
-               discovery_t *discovery;
-               unsigned long flags;
-
-               spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags);
-               if (daddr != DEV_ADDR_ANY)
-                       discovery = hashbin_find(irlmp->cachelog, daddr, NULL);
-               else {
-                       pr_debug("%s(), no daddr\n", __func__);
-                       discovery = (discovery_t *)
-                               hashbin_get_first(irlmp->cachelog);
-               }
-
-               if (discovery) {
-                       saddr = discovery->data.saddr;
-                       daddr = discovery->data.daddr;
-               }
-               spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags);
-       }
-       lap = hashbin_lock_find(irlmp->links, saddr, NULL);
-       if (lap == NULL) {
-               pr_debug("%s(), Unable to find a usable link!\n", __func__);
-               ret = -EHOSTUNREACH;
-               goto err;
-       }
-
-       /* Check if LAP is disconnected or already connected */
-       if (lap->daddr == DEV_ADDR_ANY)
-               lap->daddr = daddr;
-       else if (lap->daddr != daddr) {
-               /* Check if some LSAPs are active on this LAP */
-               if (HASHBIN_GET_SIZE(lap->lsaps) == 0) {
-                       /* No active connection, but LAP hasn't been
-                        * disconnected yet (waiting for timeout in LAP).
-                        * Maybe we could give LAP a bit of help in this case.
-                        */
-                       pr_debug("%s(), sorry, but I'm waiting for LAP to timeout!\n",
-                                __func__);
-                       ret = -EAGAIN;
-                       goto err;
-               }
-
-               /* LAP is already connected to a different node, and LAP
-                * can only talk to one node at a time */
-               pr_debug("%s(), sorry, but link is busy!\n", __func__);
-               ret = -EBUSY;
-               goto err;
-       }
-
-       self->lap = lap;
-
-       /*
-        *  Remove LSAP from list of unconnected LSAPs and insert it into the
-        *  list of connected LSAPs for the particular link
-        */
-       lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, NULL);
-
-       IRDA_ASSERT(lsap != NULL, return -1;);
-       IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;);
-       IRDA_ASSERT(lsap->lap != NULL, return -1;);
-       IRDA_ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;);
-
-       hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (long) self,
-                      NULL);
-
-       set_bit(0, &self->connected);   /* TRUE */
-
-       /*
-        *  User supplied qos specifications?
-        */
-       if (qos)
-               self->qos = *qos;
-
-       irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, tx_skb);
-
-       /* Drop reference count - see irlap_data_request(). */
-       dev_kfree_skb(tx_skb);
-
-       return 0;
-
-err:
-       /* Cleanup */
-       if(tx_skb)
-               dev_kfree_skb(tx_skb);
-       return ret;
-}
-EXPORT_SYMBOL(irlmp_connect_request);
-
-/*
- * Function irlmp_connect_indication (self)
- *
- *    Incoming connection
- *
- */
-void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb)
-{
-       int max_seg_size;
-       int lap_header_size;
-       int max_header_size;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(self->lap != NULL, return;);
-
-       pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
-                __func__, self->slsap_sel, self->dlsap_sel);
-
-       /* Note : self->lap is set in irlmp_link_data_indication(),
-        * (case CONNECT_CMD:) because we have no way to set it here.
-        * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap().
-        * Jean II */
-
-       self->qos = *self->lap->qos;
-
-       max_seg_size = self->lap->qos->data_size.value-LMP_HEADER;
-       lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap);
-       max_header_size = LMP_HEADER + lap_header_size;
-
-       /* Hide LMP_CONTROL_HEADER header from layer above */
-       skb_pull(skb, LMP_CONTROL_HEADER);
-
-       if (self->notify.connect_indication) {
-               /* Don't forget to refcount it - see irlap_driver_rcv(). */
-               skb_get(skb);
-               self->notify.connect_indication(self->notify.instance, self,
-                                               &self->qos, max_seg_size,
-                                               max_header_size, skb);
-       }
-}
-
-/*
- * Function irlmp_connect_response (handle, userdata)
- *
- *    Service user is accepting connection
- *
- */
-int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-       IRDA_ASSERT(userdata != NULL, return -1;);
-
-       /* We set the connected bit and move the lsap to the connected list
-        * in the state machine itself. Jean II */
-
-       pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
-                __func__, self->slsap_sel, self->dlsap_sel);
-
-       /* Make room for MUX control header (3 bytes) */
-       IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;);
-       skb_push(userdata, LMP_CONTROL_HEADER);
-
-       irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata);
-
-       /* Drop reference count - see irlap_data_request(). */
-       dev_kfree_skb(userdata);
-
-       return 0;
-}
-EXPORT_SYMBOL(irlmp_connect_response);
-
-/*
- * Function irlmp_connect_confirm (handle, skb)
- *
- *    LSAP connection confirmed peer device!
- */
-void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb)
-{
-       int max_header_size;
-       int lap_header_size;
-       int max_seg_size;
-
-       IRDA_ASSERT(skb != NULL, return;);
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-       IRDA_ASSERT(self->lap != NULL, return;);
-
-       self->qos = *self->lap->qos;
-
-       max_seg_size    = self->lap->qos->data_size.value-LMP_HEADER;
-       lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap);
-       max_header_size = LMP_HEADER + lap_header_size;
-
-       pr_debug("%s(), max_header_size=%d\n",
-                __func__, max_header_size);
-
-       /* Hide LMP_CONTROL_HEADER header from layer above */
-       skb_pull(skb, LMP_CONTROL_HEADER);
-
-       if (self->notify.connect_confirm) {
-               /* Don't forget to refcount it - see irlap_driver_rcv() */
-               skb_get(skb);
-               self->notify.connect_confirm(self->notify.instance, self,
-                                            &self->qos, max_seg_size,
-                                            max_header_size, skb);
-       }
-}
-
-/*
- * Function irlmp_dup (orig, instance)
- *
- *    Duplicate LSAP, can be used by servers to confirm a connection on a
- *    new LSAP so it can keep listening on the old one.
- *
- */
-struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
-{
-       struct lsap_cb *new;
-       unsigned long flags;
-
-       spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
-       /* Only allowed to duplicate unconnected LSAP's, and only LSAPs
-        * that have received a connect indication. Jean II */
-       if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) ||
-           (orig->lap == NULL)) {
-               pr_debug("%s(), invalid LSAP (wrong state)\n",
-                        __func__);
-               spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
-                                      flags);
-               return NULL;
-       }
-
-       /* Allocate a new instance */
-       new = kmemdup(orig, sizeof(*new), GFP_ATOMIC);
-       if (!new)  {
-               pr_debug("%s(), unable to kmalloc\n", __func__);
-               spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
-                                      flags);
-               return NULL;
-       }
-       /* new->lap = orig->lap; => done in the memcpy() */
-       /* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */
-       new->conn_skb = NULL;
-
-       spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
-       /* Not everything is the same */
-       new->notify.instance = instance;
-
-       init_timer(&new->watchdog_timer);
-
-       hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new,
-                      (long) new, NULL);
-
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-       /* Make sure that we invalidate the LSAP cache */
-       new->lap->cache.valid = FALSE;
-#endif /* CONFIG_IRDA_CACHE_LAST_LSAP */
-
-       return new;
-}
-
-/*
- * Function irlmp_disconnect_request (handle, userdata)
- *
- *    The service user is requesting disconnection, this will not remove the
- *    LSAP, but only mark it as disconnected
- */
-int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata)
-{
-       struct lsap_cb *lsap;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-       IRDA_ASSERT(userdata != NULL, return -1;);
-
-       /* Already disconnected ?
-        * There is a race condition between irlmp_disconnect_indication()
-        * and us that might mess up the hashbins below. This fixes it.
-        * Jean II */
-       if (! test_and_clear_bit(0, &self->connected)) {
-               pr_debug("%s(), already disconnected!\n", __func__);
-               dev_kfree_skb(userdata);
-               return -1;
-       }
-
-       skb_push(userdata, LMP_CONTROL_HEADER);
-
-       /*
-        *  Do the event before the other stuff since we must know
-        *  which lap layer that the frame should be transmitted on
-        */
-       irlmp_do_lsap_event(self, LM_DISCONNECT_REQUEST, userdata);
-
-       /* Drop reference count - see irlap_data_request(). */
-       dev_kfree_skb(userdata);
-
-       /*
-        *  Remove LSAP from list of connected LSAPs for the particular link
-        *  and insert it into the list of unconnected LSAPs
-        */
-       IRDA_ASSERT(self->lap != NULL, return -1;);
-       IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
-       IRDA_ASSERT(self->lap->lsaps != NULL, return -1;);
-
-       lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL);
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-       self->lap->cache.valid = FALSE;
-#endif
-
-       IRDA_ASSERT(lsap != NULL, return -1;);
-       IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;);
-       IRDA_ASSERT(lsap == self, return -1;);
-
-       hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self,
-                      (long) self, NULL);
-
-       /* Reset some values */
-       self->dlsap_sel = LSAP_ANY;
-       self->lap = NULL;
-
-       return 0;
-}
-EXPORT_SYMBOL(irlmp_disconnect_request);
-
-/*
- * Function irlmp_disconnect_indication (reason, userdata)
- *
- *    LSAP is being closed!
- */
-void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
-                                struct sk_buff *skb)
-{
-       struct lsap_cb *lsap;
-
-       pr_debug("%s(), reason=%s [%d]\n", __func__,
-                irlmp_reason_str(reason), reason);
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-
-       pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
-                __func__, self->slsap_sel, self->dlsap_sel);
-
-       /* Already disconnected ?
-        * There is a race condition between irlmp_disconnect_request()
-        * and us that might mess up the hashbins below. This fixes it.
-        * Jean II */
-       if (! test_and_clear_bit(0, &self->connected)) {
-               pr_debug("%s(), already disconnected!\n", __func__);
-               return;
-       }
-
-       /*
-        *  Remove association between this LSAP and the link it used
-        */
-       IRDA_ASSERT(self->lap != NULL, return;);
-       IRDA_ASSERT(self->lap->lsaps != NULL, return;);
-
-       lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL);
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-       self->lap->cache.valid = FALSE;
-#endif
-
-       IRDA_ASSERT(lsap != NULL, return;);
-       IRDA_ASSERT(lsap == self, return;);
-       hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap,
-                      (long) lsap, NULL);
-
-       self->dlsap_sel = LSAP_ANY;
-       self->lap = NULL;
-
-       /*
-        *  Inform service user
-        */
-       if (self->notify.disconnect_indication) {
-               /* Don't forget to refcount it - see irlap_driver_rcv(). */
-               if(skb)
-                       skb_get(skb);
-               self->notify.disconnect_indication(self->notify.instance,
-                                                  self, reason, skb);
-       } else {
-               pr_debug("%s(), no handler\n", __func__);
-       }
-}
-
-/*
- * Function irlmp_do_expiry (void)
- *
- *    Do a cleanup of the discovery log (remove old entries)
- *
- * Note : separate from irlmp_do_discovery() so that we can handle
- * passive discovery properly.
- */
-void irlmp_do_expiry(void)
-{
-       struct lap_cb *lap;
-
-       /*
-        * Expire discovery on all links which are *not* connected.
-        * On links which are connected, we can't do discovery
-        * anymore and can't refresh the log, so we freeze the
-        * discovery log to keep info about the device we are
-        * connected to.
-        * This info is mandatory if we want irlmp_connect_request()
-        * to work properly. - Jean II
-        */
-       lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
-       while (lap != NULL) {
-               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
-
-               if (lap->lap_state == LAP_STANDBY) {
-                       /* Expire discoveries discovered on this link */
-                       irlmp_expire_discoveries(irlmp->cachelog, lap->saddr,
-                                                FALSE);
-               }
-               lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
-       }
-}
-
-/*
- * Function irlmp_do_discovery (nslots)
- *
- *    Do some discovery on all links
- *
- * Note : log expiry is done above.
- */
-void irlmp_do_discovery(int nslots)
-{
-       struct lap_cb *lap;
-       __u16 *data_hintsp;
-
-       /* Make sure the value is sane */
-       if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){
-               net_warn_ratelimited("%s: invalid value for number of slots!\n",
-                                    __func__);
-               nslots = sysctl_discovery_slots = 8;
-       }
-
-       /* Construct new discovery info to be used by IrLAP, */
-       data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints;
-       put_unaligned(irlmp->hints.word, data_hintsp);
-
-       /*
-        *  Set character set for device name (we use ASCII), and
-        *  copy device name. Remember to make room for a \0 at the
-        *  end
-        */
-       irlmp->discovery_cmd.data.charset = CS_ASCII;
-       strncpy(irlmp->discovery_cmd.data.info, sysctl_devname,
-               NICKNAME_MAX_LEN);
-       irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.data.info);
-       irlmp->discovery_cmd.nslots = nslots;
-
-       /*
-        * Try to send discovery packets on all links
-        */
-       lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
-       while (lap != NULL) {
-               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
-
-               if (lap->lap_state == LAP_STANDBY) {
-                       /* Try to discover */
-                       irlmp_do_lap_event(lap, LM_LAP_DISCOVERY_REQUEST,
-                                          NULL);
-               }
-               lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
-       }
-}
-
-/*
- * Function irlmp_discovery_request (nslots)
- *
- *    Do a discovery of devices in front of the computer
- *
- * If the caller has registered a client discovery callback, this
- * allow him to receive the full content of the discovery log through
- * this callback (as normally he will receive only new discoveries).
- */
-void irlmp_discovery_request(int nslots)
-{
-       /* Return current cached discovery log (in full) */
-       irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_LOG);
-
-       /*
-        * Start a single discovery operation if discovery is not already
-        * running
-        */
-       if (!sysctl_discovery) {
-               /* Check if user wants to override the default */
-               if (nslots == DISCOVERY_DEFAULT_SLOTS)
-                       nslots = sysctl_discovery_slots;
-
-               irlmp_do_discovery(nslots);
-               /* Note : we never do expiry here. Expiry will run on the
-                * discovery timer regardless of the state of sysctl_discovery
-                * Jean II */
-       }
-}
-EXPORT_SYMBOL(irlmp_discovery_request);
-
-/*
- * Function irlmp_get_discoveries (pn, mask, slots)
- *
- *    Return the current discovery log
- *
- * If discovery is not enabled, you should call this function again
- * after 1 or 2 seconds (i.e. after discovery has been done).
- */
-struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots)
-{
-       /* If discovery is not enabled, it's likely that the discovery log
-        * will be empty. So, we trigger a single discovery, so that next
-        * time the user call us there might be some results in the log.
-        * Jean II
-        */
-       if (!sysctl_discovery) {
-               /* Check if user wants to override the default */
-               if (nslots == DISCOVERY_DEFAULT_SLOTS)
-                       nslots = sysctl_discovery_slots;
-
-               /* Start discovery - will complete sometime later */
-               irlmp_do_discovery(nslots);
-               /* Note : we never do expiry here. Expiry will run on the
-                * discovery timer regardless of the state of sysctl_discovery
-                * Jean II */
-       }
-
-       /* Return current cached discovery log */
-       return irlmp_copy_discoveries(irlmp->cachelog, pn, mask, TRUE);
-}
-EXPORT_SYMBOL(irlmp_get_discoveries);
-
-/*
- * Function irlmp_notify_client (log)
- *
- *    Notify all about discovered devices
- *
- * Clients registered with IrLMP are :
- *     o IrComm
- *     o IrLAN
- *     o Any socket (in any state - ouch, that may be a lot !)
- * The client may have defined a callback to be notified in case of
- * partial/selective discovery based on the hints that it passed to IrLMP.
- */
-static inline void
-irlmp_notify_client(irlmp_client_t *client,
-                   hashbin_t *log, DISCOVERY_MODE mode)
-{
-       discinfo_t *discoveries;        /* Copy of the discovery log */
-       int     number;                 /* Number of nodes in the log */
-       int     i;
-
-       /* Check if client wants or not partial/selective log (optimisation) */
-       if (!client->disco_callback)
-               return;
-
-       /*
-        * Locking notes :
-        * the old code was manipulating the log directly, which was
-        * very racy. Now, we use copy_discoveries, that protects
-        * itself while dumping the log for us.
-        * The overhead of the copy is compensated by the fact that
-        * we only pass new discoveries in normal mode and don't
-        * pass the same old entry every 3s to the caller as we used
-        * to do (virtual function calling is expensive).
-        * Jean II
-        */
-
-       /*
-        * Now, check all discovered devices (if any), and notify client
-        * only about the services that the client is interested in
-        * We also notify only about the new devices unless the caller
-        * explicitly request a dump of the log. Jean II
-        */
-       discoveries = irlmp_copy_discoveries(log, &number,
-                                            client->hint_mask.word,
-                                            (mode == DISCOVERY_LOG));
-       /* Check if the we got some results */
-       if (discoveries == NULL)
-               return; /* No nodes discovered */
-
-       /* Pass all entries to the listener */
-       for(i = 0; i < number; i++)
-               client->disco_callback(&(discoveries[i]), mode, client->priv);
-
-       /* Free up our buffer */
-       kfree(discoveries);
-}
-
-/*
- * Function irlmp_discovery_confirm ( self, log)
- *
- *    Some device(s) answered to our discovery request! Check to see which
- *    device it is, and give indication to the client(s)
- *
- */
-void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
-{
-       irlmp_client_t *client;
-       irlmp_client_t *client_next;
-
-       IRDA_ASSERT(log != NULL, return;);
-
-       if (!(HASHBIN_GET_SIZE(log)))
-               return;
-
-       /* For each client - notify callback may touch client list */
-       client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
-       while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
-                                        (void *) &client_next) ) {
-               /* Check if we should notify client */
-               irlmp_notify_client(client, log, mode);
-
-               client = client_next;
-       }
-}
-
-/*
- * Function irlmp_discovery_expiry (expiry)
- *
- *     This device is no longer been discovered, and therefore it is being
- *     purged from the discovery log. Inform all clients who have
- *     registered for this event...
- *
- *     Note : called exclusively from discovery.c
- *     Note : this is no longer called under discovery spinlock, so the
- *             client can do whatever he wants in the callback.
- */
-void irlmp_discovery_expiry(discinfo_t *expiries, int number)
-{
-       irlmp_client_t *client;
-       irlmp_client_t *client_next;
-       int             i;
-
-       IRDA_ASSERT(expiries != NULL, return;);
-
-       /* For each client - notify callback may touch client list */
-       client = (irlmp_client_t *) hashbin_get_first(irlmp->clients);
-       while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL,
-                                        (void *) &client_next) ) {
-
-               /* Pass all entries to the listener */
-               for(i = 0; i < number; i++) {
-                       /* Check if we should notify client */
-                       if ((client->expir_callback) &&
-                           (client->hint_mask.word &
-                            get_unaligned((__u16 *)expiries[i].hints)
-                            & 0x7f7f) )
-                               client->expir_callback(&(expiries[i]),
-                                                      EXPIRY_TIMEOUT,
-                                                      client->priv);
-               }
-
-               /* Next client */
-               client = client_next;
-       }
-}
-
-/*
- * Function irlmp_get_discovery_response ()
- *
- *    Used by IrLAP to get the discovery info it needs when answering
- *    discovery requests by other devices.
- */
-discovery_t *irlmp_get_discovery_response(void)
-{
-       IRDA_ASSERT(irlmp != NULL, return NULL;);
-
-       put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints);
-
-       /*
-        *  Set character set for device name (we use ASCII), and
-        *  copy device name. Remember to make room for a \0 at the
-        *  end
-        */
-       irlmp->discovery_rsp.data.charset = CS_ASCII;
-
-       strncpy(irlmp->discovery_rsp.data.info, sysctl_devname,
-               NICKNAME_MAX_LEN);
-       irlmp->discovery_rsp.name_len = strlen(irlmp->discovery_rsp.data.info);
-
-       return &irlmp->discovery_rsp;
-}
-
-/*
- * Function irlmp_data_request (self, skb)
- *
- *    Send some data to peer device
- *
- * Note on skb management :
- * After calling the lower layers of the IrDA stack, we always
- * kfree() the skb, which drop the reference count (and potentially
- * destroy it).
- * IrLMP and IrLAP may queue the packet, and in those cases will need
- * to use skb_get() to keep it around.
- * Jean II
- */
-int irlmp_data_request(struct lsap_cb *self, struct sk_buff *userdata)
-{
-       int     ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
-       /* Make room for MUX header */
-       IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;);
-       skb_push(userdata, LMP_HEADER);
-
-       ret = irlmp_do_lsap_event(self, LM_DATA_REQUEST, userdata);
-
-       /* Drop reference count - see irlap_data_request(). */
-       dev_kfree_skb(userdata);
-
-       return ret;
-}
-EXPORT_SYMBOL(irlmp_data_request);
-
-/*
- * Function irlmp_data_indication (handle, skb)
- *
- *    Got data from LAP layer so pass it up to upper layer
- *
- */
-void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb)
-{
-       /* Hide LMP header from layer above */
-       skb_pull(skb, LMP_HEADER);
-
-       if (self->notify.data_indication) {
-               /* Don't forget to refcount it - see irlap_driver_rcv(). */
-               skb_get(skb);
-               self->notify.data_indication(self->notify.instance, self, skb);
-       }
-}
-
-/*
- * Function irlmp_udata_request (self, skb)
- */
-int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata)
-{
-       int     ret;
-
-       IRDA_ASSERT(userdata != NULL, return -1;);
-
-       /* Make room for MUX header */
-       IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;);
-       skb_push(userdata, LMP_HEADER);
-
-       ret = irlmp_do_lsap_event(self, LM_UDATA_REQUEST, userdata);
-
-       /* Drop reference count - see irlap_data_request(). */
-       dev_kfree_skb(userdata);
-
-       return ret;
-}
-
-/*
- * Function irlmp_udata_indication (self, skb)
- *
- *    Send unreliable data (but still within the connection)
- *
- */
-void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       /* Hide LMP header from layer above */
-       skb_pull(skb, LMP_HEADER);
-
-       if (self->notify.udata_indication) {
-               /* Don't forget to refcount it - see irlap_driver_rcv(). */
-               skb_get(skb);
-               self->notify.udata_indication(self->notify.instance, self,
-                                             skb);
-       }
-}
-
-/*
- * Function irlmp_connless_data_request (self, skb)
- */
-#ifdef CONFIG_IRDA_ULTRA
-int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata,
-                               __u8 pid)
-{
-       struct sk_buff *clone_skb;
-       struct lap_cb *lap;
-
-       IRDA_ASSERT(userdata != NULL, return -1;);
-
-       /* Make room for MUX and PID header */
-       IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER+LMP_PID_HEADER,
-                   return -1;);
-
-       /* Insert protocol identifier */
-       skb_push(userdata, LMP_PID_HEADER);
-       if(self != NULL)
-         userdata->data[0] = self->pid;
-       else
-         userdata->data[0] = pid;
-
-       /* Connectionless sockets must use 0x70 */
-       skb_push(userdata, LMP_HEADER);
-       userdata->data[0] = userdata->data[1] = LSAP_CONNLESS;
-
-       /* Try to send Connectionless  packets out on all links */
-       lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
-       while (lap != NULL) {
-               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return -1;);
-
-               clone_skb = skb_clone(userdata, GFP_ATOMIC);
-               if (!clone_skb) {
-                       dev_kfree_skb(userdata);
-                       return -ENOMEM;
-               }
-
-               irlap_unitdata_request(lap->irlap, clone_skb);
-               /* irlap_unitdata_request() don't increase refcount,
-                * so no dev_kfree_skb() - Jean II */
-
-               lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
-       }
-       dev_kfree_skb(userdata);
-
-       return 0;
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irlmp_connless_data_indication (self, skb)
- *
- *    Receive unreliable data outside any connection. Mostly used by Ultra
- *
- */
-#ifdef CONFIG_IRDA_ULTRA
-void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       /* Hide LMP and PID header from layer above */
-       skb_pull(skb, LMP_HEADER+LMP_PID_HEADER);
-
-       if (self->notify.udata_indication) {
-               /* Don't forget to refcount it - see irlap_driver_rcv(). */
-               skb_get(skb);
-               self->notify.udata_indication(self->notify.instance, self,
-                                             skb);
-       }
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Propagate status indication from LAP to LSAPs (via LMP)
- * This don't trigger any change of state in lap_cb, lmp_cb or lsap_cb,
- * and the event is stateless, therefore we can bypass both state machines
- * and send the event direct to the LSAP user.
- * Jean II
- */
-void irlmp_status_indication(struct lap_cb *self,
-                            LINK_STATUS link, LOCK_STATUS lock)
-{
-       struct lsap_cb *next;
-       struct lsap_cb *curr;
-
-       /* Send status_indication to all LSAPs using this link */
-       curr = (struct lsap_cb *) hashbin_get_first( self->lsaps);
-       while (NULL != hashbin_find_next(self->lsaps, (long) curr, NULL,
-                                        (void *) &next) ) {
-               IRDA_ASSERT(curr->magic == LMP_LSAP_MAGIC, return;);
-               /*
-                *  Inform service user if he has requested it
-                */
-               if (curr->notify.status_indication != NULL)
-                       curr->notify.status_indication(curr->notify.instance,
-                                                      link, lock);
-               else
-                       pr_debug("%s(), no handler\n", __func__);
-
-               curr = next;
-       }
-}
-
-/*
- * Receive flow control indication from LAP.
- * LAP want us to send it one more frame. We implement a simple round
- * robin scheduler between the active sockets so that we get a bit of
- * fairness. Note that the round robin is far from perfect, but it's
- * better than nothing.
- * We then poll the selected socket so that we can do synchronous
- * refilling of IrLAP (which allow to minimise the number of buffers).
- * Jean II
- */
-void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow)
-{
-       struct lsap_cb *next;
-       struct lsap_cb *curr;
-       int     lsap_todo;
-
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-       IRDA_ASSERT(flow == FLOW_START, return;);
-
-       /* Get the number of lsap. That's the only safe way to know
-        * that we have looped around... - Jean II */
-       lsap_todo = HASHBIN_GET_SIZE(self->lsaps);
-       pr_debug("%s() : %d lsaps to scan\n", __func__, lsap_todo);
-
-       /* Poll lsap in order until the queue is full or until we
-        * tried them all.
-        * Most often, the current LSAP will have something to send,
-        * so we will go through this loop only once. - Jean II */
-       while((lsap_todo--) &&
-             (IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) {
-               /* Try to find the next lsap we should poll. */
-               next = self->flow_next;
-               /* If we have no lsap, restart from first one */
-               if(next == NULL)
-                       next = (struct lsap_cb *) hashbin_get_first(self->lsaps);
-               /* Verify current one and find the next one */
-               curr = hashbin_find_next(self->lsaps, (long) next, NULL,
-                                        (void *) &self->flow_next);
-               /* Uh-oh... Paranoia */
-               if(curr == NULL)
-                       break;
-               pr_debug("%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n",
-                        __func__, curr, next, self->flow_next, lsap_todo,
-                        IRLAP_GET_TX_QUEUE_LEN(self->irlap));
-
-               /* Inform lsap user that it can send one more packet. */
-               if (curr->notify.flow_indication != NULL)
-                       curr->notify.flow_indication(curr->notify.instance,
-                                                    curr, flow);
-               else
-                       pr_debug("%s(), no handler\n", __func__);
-       }
-}
-
-#if 0
-/*
- * Function irlmp_hint_to_service (hint)
- *
- *    Returns a list of all servics contained in the given hint bits. This
- *    function assumes that the hint bits have the size of two bytes only
- */
-__u8 *irlmp_hint_to_service(__u8 *hint)
-{
-       __u8 *service;
-       int i = 0;
-
-       /*
-        * Allocate array to store services in. 16 entries should be safe
-        * since we currently only support 2 hint bytes
-        */
-       service = kmalloc(16, GFP_ATOMIC);
-       if (!service)
-               return NULL;
-
-       if (!hint[0]) {
-               pr_debug("<None>\n");
-               kfree(service);
-               return NULL;
-       }
-       if (hint[0] & HINT_PNP)
-               pr_debug("PnP Compatible ");
-       if (hint[0] & HINT_PDA)
-               pr_debug("PDA/Palmtop ");
-       if (hint[0] & HINT_COMPUTER)
-               pr_debug("Computer ");
-       if (hint[0] & HINT_PRINTER) {
-               pr_debug("Printer ");
-               service[i++] = S_PRINTER;
-       }
-       if (hint[0] & HINT_MODEM)
-               pr_debug("Modem ");
-       if (hint[0] & HINT_FAX)
-               pr_debug("Fax ");
-       if (hint[0] & HINT_LAN) {
-               pr_debug("LAN Access ");
-               service[i++] = S_LAN;
-       }
-       /*
-        *  Test if extension byte exists. This byte will usually be
-        *  there, but this is not really required by the standard.
-        *  (IrLMP p. 29)
-        */
-       if (hint[0] & HINT_EXTENSION) {
-               if (hint[1] & HINT_TELEPHONY) {
-                       pr_debug("Telephony ");
-                       service[i++] = S_TELEPHONY;
-               }
-               if (hint[1] & HINT_FILE_SERVER)
-                       pr_debug("File Server ");
-
-               if (hint[1] & HINT_COMM) {
-                       pr_debug("IrCOMM ");
-                       service[i++] = S_COMM;
-               }
-               if (hint[1] & HINT_OBEX) {
-                       pr_debug("IrOBEX ");
-                       service[i++] = S_OBEX;
-               }
-       }
-       pr_debug("\n");
-
-       /* So that client can be notified about any discovery */
-       service[i++] = S_ANY;
-
-       service[i] = S_END;
-
-       return service;
-}
-#endif
-
-static const __u16 service_hint_mapping[S_END][2] = {
-       { HINT_PNP,             0 },                    /* S_PNP */
-       { HINT_PDA,             0 },                    /* S_PDA */
-       { HINT_COMPUTER,        0 },                    /* S_COMPUTER */
-       { HINT_PRINTER,         0 },                    /* S_PRINTER */
-       { HINT_MODEM,           0 },                    /* S_MODEM */
-       { HINT_FAX,             0 },                    /* S_FAX */
-       { HINT_LAN,             0 },                    /* S_LAN */
-       { HINT_EXTENSION,       HINT_TELEPHONY },       /* S_TELEPHONY */
-       { HINT_EXTENSION,       HINT_COMM },            /* S_COMM */
-       { HINT_EXTENSION,       HINT_OBEX },            /* S_OBEX */
-       { 0xFF,                 0xFF },                 /* S_ANY */
-};
-
-/*
- * Function irlmp_service_to_hint (service)
- *
- *    Converts a service type, to a hint bit
- *
- *    Returns: a 16 bit hint value, with the service bit set
- */
-__u16 irlmp_service_to_hint(int service)
-{
-       __u16_host_order hint;
-
-       hint.byte[0] = service_hint_mapping[service][0];
-       hint.byte[1] = service_hint_mapping[service][1];
-
-       return hint.word;
-}
-EXPORT_SYMBOL(irlmp_service_to_hint);
-
-/*
- * Function irlmp_register_service (service)
- *
- *    Register local service with IrLMP
- *
- */
-void *irlmp_register_service(__u16 hints)
-{
-       irlmp_service_t *service;
-
-       pr_debug("%s(), hints = %04x\n", __func__, hints);
-
-       /* Make a new registration */
-       service = kmalloc(sizeof(irlmp_service_t), GFP_ATOMIC);
-       if (!service)
-               return NULL;
-
-       service->hints.word = hints;
-       hashbin_insert(irlmp->services, (irda_queue_t *) service,
-                      (long) service, NULL);
-
-       irlmp->hints.word |= hints;
-
-       return (void *)service;
-}
-EXPORT_SYMBOL(irlmp_register_service);
-
-/*
- * Function irlmp_unregister_service (handle)
- *
- *    Unregister service with IrLMP.
- *
- *    Returns: 0 on success, -1 on error
- */
-int irlmp_unregister_service(void *handle)
-{
-       irlmp_service_t *service;
-       unsigned long flags;
-
-       if (!handle)
-               return -1;
-
-       /* Caller may call with invalid handle (it's legal) - Jean II */
-       service = hashbin_lock_find(irlmp->services, (long) handle, NULL);
-       if (!service) {
-               pr_debug("%s(), Unknown service!\n", __func__);
-               return -1;
-       }
-
-       hashbin_remove_this(irlmp->services, (irda_queue_t *) service);
-       kfree(service);
-
-       /* Remove old hint bits */
-       irlmp->hints.word = 0;
-
-       /* Refresh current hint bits */
-       spin_lock_irqsave(&irlmp->services->hb_spinlock, flags);
-       service = (irlmp_service_t *) hashbin_get_first(irlmp->services);
-       while (service) {
-               irlmp->hints.word |= service->hints.word;
-
-               service = (irlmp_service_t *)hashbin_get_next(irlmp->services);
-       }
-       spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags);
-       return 0;
-}
-EXPORT_SYMBOL(irlmp_unregister_service);
-
-/*
- * Function irlmp_register_client (hint_mask, callback1, callback2)
- *
- *    Register a local client with IrLMP
- *     First callback is selective discovery (based on hints)
- *     Second callback is for selective discovery expiries
- *
- *    Returns: handle > 0 on success, 0 on error
- */
-void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
-                           DISCOVERY_CALLBACK2 expir_clb, void *priv)
-{
-       irlmp_client_t *client;
-
-       IRDA_ASSERT(irlmp != NULL, return NULL;);
-
-       /* Make a new registration */
-       client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC);
-       if (!client)
-               return NULL;
-
-       /* Register the details */
-       client->hint_mask.word = hint_mask;
-       client->disco_callback = disco_clb;
-       client->expir_callback = expir_clb;
-       client->priv = priv;
-
-       hashbin_insert(irlmp->clients, (irda_queue_t *) client,
-                      (long) client, NULL);
-
-       return (void *) client;
-}
-EXPORT_SYMBOL(irlmp_register_client);
-
-/*
- * Function irlmp_update_client (handle, hint_mask, callback1, callback2)
- *
- *    Updates specified client (handle) with possibly new hint_mask and
- *    callback
- *
- *    Returns: 0 on success, -1 on error
- */
-int irlmp_update_client(void *handle, __u16 hint_mask,
-                       DISCOVERY_CALLBACK1 disco_clb,
-                       DISCOVERY_CALLBACK2 expir_clb, void *priv)
-{
-       irlmp_client_t *client;
-
-       if (!handle)
-               return -1;
-
-       client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
-       if (!client) {
-               pr_debug("%s(), Unknown client!\n", __func__);
-               return -1;
-       }
-
-       client->hint_mask.word = hint_mask;
-       client->disco_callback = disco_clb;
-       client->expir_callback = expir_clb;
-       client->priv = priv;
-
-       return 0;
-}
-EXPORT_SYMBOL(irlmp_update_client);
-
-/*
- * Function irlmp_unregister_client (handle)
- *
- *    Returns: 0 on success, -1 on error
- *
- */
-int irlmp_unregister_client(void *handle)
-{
-       struct irlmp_client *client;
-
-       if (!handle)
-               return -1;
-
-       /* Caller may call with invalid handle (it's legal) - Jean II */
-       client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
-       if (!client) {
-               pr_debug("%s(), Unknown client!\n", __func__);
-               return -1;
-       }
-
-       pr_debug("%s(), removing client!\n", __func__);
-       hashbin_remove_this(irlmp->clients, (irda_queue_t *) client);
-       kfree(client);
-
-       return 0;
-}
-EXPORT_SYMBOL(irlmp_unregister_client);
-
-/*
- * Function irlmp_slsap_inuse (slsap)
- *
- *    Check if the given source LSAP selector is in use
- *
- * This function is clearly not very efficient. On the mitigating side, the
- * stack make sure that in 99% of the cases, we are called only once
- * for each socket allocation. We could probably keep a bitmap
- * of the allocated LSAP, but I'm not sure the complexity is worth it.
- * Jean II
- */
-static int irlmp_slsap_inuse(__u8 slsap_sel)
-{
-       struct lsap_cb *self;
-       struct lap_cb *lap;
-       unsigned long flags;
-
-       IRDA_ASSERT(irlmp != NULL, return TRUE;);
-       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;);
-       IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;);
-
-#ifdef CONFIG_IRDA_ULTRA
-       /* Accept all bindings to the connectionless LSAP */
-       if (slsap_sel == LSAP_CONNLESS)
-               return FALSE;
-#endif /* CONFIG_IRDA_ULTRA */
-
-       /* Valid values are between 0 and 127 (0x0-0x6F) */
-       if (slsap_sel > LSAP_MAX)
-               return TRUE;
-
-       /*
-        *  Check if slsap is already in use. To do this we have to loop over
-        *  every IrLAP connection and check every LSAP associated with each
-        *  the connection.
-        */
-       spin_lock_irqsave_nested(&irlmp->links->hb_spinlock, flags,
-                       SINGLE_DEPTH_NESTING);
-       lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
-       while (lap != NULL) {
-               IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, goto errlap;);
-
-               /* Careful for priority inversions here !
-                * irlmp->links is never taken while another IrDA
-                * spinlock is held, so we are safe. Jean II */
-               spin_lock(&lap->lsaps->hb_spinlock);
-
-               /* For this IrLAP, check all the LSAPs */
-               self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
-               while (self != NULL) {
-                       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC,
-                                   goto errlsap;);
-
-                       if ((self->slsap_sel == slsap_sel)) {
-                               pr_debug("Source LSAP selector=%02x in use\n",
-                                        self->slsap_sel);
-                               goto errlsap;
-                       }
-                       self = (struct lsap_cb*) hashbin_get_next(lap->lsaps);
-               }
-               spin_unlock(&lap->lsaps->hb_spinlock);
-
-               /* Next LAP */
-               lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
-       }
-       spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
-
-       /*
-        * Server sockets are typically waiting for connections and
-        * therefore reside in the unconnected list. We don't want
-        * to give out their LSAPs for obvious reasons...
-        * Jean II
-        */
-       spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
-       self = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
-       while (self != NULL) {
-               IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;);
-               if ((self->slsap_sel == slsap_sel)) {
-                       pr_debug("Source LSAP selector=%02x in use (unconnected)\n",
-                                self->slsap_sel);
-                       goto erruncon;
-               }
-               self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps);
-       }
-       spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
-       return FALSE;
-
-       /* Error exit from within one of the two nested loops.
-        * Make sure we release the right spinlock in the righ order.
-        * Jean II */
-errlsap:
-       spin_unlock(&lap->lsaps->hb_spinlock);
-IRDA_ASSERT_LABEL(errlap:)
-       spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags);
-       return TRUE;
-
-       /* Error exit from within the unconnected loop.
-        * Just one spinlock to release... Jean II */
-erruncon:
-       spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-       return TRUE;
-}
-
-/*
- * Function irlmp_find_free_slsap ()
- *
- *    Find a free source LSAP to use. This function is called if the service
- *    user has requested a source LSAP equal to LM_ANY
- */
-static __u8 irlmp_find_free_slsap(void)
-{
-       __u8 lsap_sel;
-       int wrapped = 0;
-
-       IRDA_ASSERT(irlmp != NULL, return -1;);
-       IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return -1;);
-
-       /* Most users don't really care which LSAPs they are given,
-        * and therefore we automatically give them a free LSAP.
-        * This function try to find a suitable LSAP, i.e. which is
-        * not in use and is within the acceptable range. Jean II */
-
-       do {
-               /* Always increment to LSAP number before using it.
-                * In theory, we could reuse the last LSAP number, as long
-                * as it is no longer in use. Some IrDA stack do that.
-                * However, the previous socket may be half closed, i.e.
-                * we closed it, we think it's no longer in use, but the
-                * other side did not receive our close and think it's
-                * active and still send data on it.
-                * This is similar to what is done with PIDs and TCP ports.
-                * Also, this reduce the number of calls to irlmp_slsap_inuse()
-                * which is an expensive function to call.
-                * Jean II */
-               irlmp->last_lsap_sel++;
-
-               /* Check if we need to wraparound (0x70-0x7f are reserved) */
-               if (irlmp->last_lsap_sel > LSAP_MAX) {
-                       /* 0x00-0x10 are also reserved for well know ports */
-                       irlmp->last_lsap_sel = 0x10;
-
-                       /* Make sure we terminate the loop */
-                       if (wrapped++) {
-                               net_err_ratelimited("%s: no more free LSAPs !\n",
-                                                   __func__);
-                               return 0;
-                       }
-               }
-
-               /* If the LSAP is in use, try the next one.
-                * Despite the autoincrement, we need to check if the lsap
-                * is really in use or not, first because LSAP may be
-                * directly allocated in irlmp_open_lsap(), and also because
-                * we may wraparound on old sockets. Jean II */
-       } while (irlmp_slsap_inuse(irlmp->last_lsap_sel));
-
-       /* Got it ! */
-       lsap_sel = irlmp->last_lsap_sel;
-       pr_debug("%s(), found free lsap_sel=%02x\n",
-                __func__, lsap_sel);
-
-       return lsap_sel;
-}
-
-/*
- * Function irlmp_convert_lap_reason (lap_reason)
- *
- *    Converts IrLAP disconnect reason codes to IrLMP disconnect reason
- *    codes
- *
- */
-LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason)
-{
-       int reason = LM_LAP_DISCONNECT;
-
-       switch (lap_reason) {
-       case LAP_DISC_INDICATION: /* Received a disconnect request from peer */
-               pr_debug("%s(), LAP_DISC_INDICATION\n", __func__);
-               reason = LM_USER_REQUEST;
-               break;
-       case LAP_NO_RESPONSE:    /* To many retransmits without response */
-               pr_debug("%s(), LAP_NO_RESPONSE\n", __func__);
-               reason = LM_LAP_DISCONNECT;
-               break;
-       case LAP_RESET_INDICATION:
-               pr_debug("%s(), LAP_RESET_INDICATION\n", __func__);
-               reason = LM_LAP_RESET;
-               break;
-       case LAP_FOUND_NONE:
-       case LAP_MEDIA_BUSY:
-       case LAP_PRIMARY_CONFLICT:
-               pr_debug("%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n",
-                        __func__);
-               reason = LM_CONNECT_FAILURE;
-               break;
-       default:
-               pr_debug("%s(), Unknown IrLAP disconnect reason %d!\n",
-                        __func__, lap_reason);
-               reason = LM_LAP_DISCONNECT;
-               break;
-       }
-
-       return reason;
-}
-
-#ifdef CONFIG_PROC_FS
-
-struct irlmp_iter_state {
-       hashbin_t *hashbin;
-};
-
-#define LSAP_START_TOKEN       ((void *)1)
-#define LINK_START_TOKEN       ((void *)2)
-
-static void *irlmp_seq_hb_idx(struct irlmp_iter_state *iter, loff_t *off)
-{
-       void *element;
-
-       spin_lock_irq(&iter->hashbin->hb_spinlock);
-       for (element = hashbin_get_first(iter->hashbin);
-            element != NULL;
-            element = hashbin_get_next(iter->hashbin)) {
-               if (!off || (*off)-- == 0) {
-                       /* NB: hashbin left locked */
-                       return element;
-               }
-       }
-       spin_unlock_irq(&iter->hashbin->hb_spinlock);
-       iter->hashbin = NULL;
-       return NULL;
-}
-
-
-static void *irlmp_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       struct irlmp_iter_state *iter = seq->private;
-       void *v;
-       loff_t off = *pos;
-
-       iter->hashbin = NULL;
-       if (off-- == 0)
-               return LSAP_START_TOKEN;
-
-       iter->hashbin = irlmp->unconnected_lsaps;
-       v = irlmp_seq_hb_idx(iter, &off);
-       if (v)
-               return v;
-
-       if (off-- == 0)
-               return LINK_START_TOKEN;
-
-       iter->hashbin = irlmp->links;
-       return irlmp_seq_hb_idx(iter, &off);
-}
-
-static void *irlmp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-       struct irlmp_iter_state *iter = seq->private;
-
-       ++*pos;
-
-       if (v == LSAP_START_TOKEN) {            /* start of list of lsaps */
-               iter->hashbin = irlmp->unconnected_lsaps;
-               v = irlmp_seq_hb_idx(iter, NULL);
-               return v ? v : LINK_START_TOKEN;
-       }
-
-       if (v == LINK_START_TOKEN) {            /* start of list of links */
-               iter->hashbin = irlmp->links;
-               return irlmp_seq_hb_idx(iter, NULL);
-       }
-
-       v = hashbin_get_next(iter->hashbin);
-
-       if (v == NULL) {                        /* no more in this hash bin */
-               spin_unlock_irq(&iter->hashbin->hb_spinlock);
-
-               if (iter->hashbin == irlmp->unconnected_lsaps)
-                       v =  LINK_START_TOKEN;
-
-               iter->hashbin = NULL;
-       }
-       return v;
-}
-
-static void irlmp_seq_stop(struct seq_file *seq, void *v)
-{
-       struct irlmp_iter_state *iter = seq->private;
-
-       if (iter->hashbin)
-               spin_unlock_irq(&iter->hashbin->hb_spinlock);
-}
-
-static int irlmp_seq_show(struct seq_file *seq, void *v)
-{
-       const struct irlmp_iter_state *iter = seq->private;
-       struct lsap_cb *self = v;
-
-       if (v == LSAP_START_TOKEN)
-               seq_puts(seq, "Unconnected LSAPs:\n");
-       else if (v == LINK_START_TOKEN)
-               seq_puts(seq, "\nRegistered Link Layers:\n");
-       else if (iter->hashbin == irlmp->unconnected_lsaps) {
-               self = v;
-               IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EINVAL; );
-               seq_printf(seq, "lsap state: %s, ",
-                          irlsap_state[ self->lsap_state]);
-               seq_printf(seq,
-                          "slsap_sel: %#02x, dlsap_sel: %#02x, ",
-                          self->slsap_sel, self->dlsap_sel);
-               seq_printf(seq, "(%s)", self->notify.name);
-               seq_printf(seq, "\n");
-       } else if (iter->hashbin == irlmp->links) {
-               struct lap_cb *lap = v;
-
-               seq_printf(seq, "lap state: %s, ",
-                          irlmp_state[lap->lap_state]);
-
-               seq_printf(seq, "saddr: %#08x, daddr: %#08x, ",
-                          lap->saddr, lap->daddr);
-               seq_printf(seq, "num lsaps: %d",
-                          HASHBIN_GET_SIZE(lap->lsaps));
-               seq_printf(seq, "\n");
-
-               /* Careful for priority inversions here !
-                * All other uses of attrib spinlock are independent of
-                * the object spinlock, so we are safe. Jean II */
-               spin_lock(&lap->lsaps->hb_spinlock);
-
-               seq_printf(seq, "\n  Connected LSAPs:\n");
-               for (self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
-                    self != NULL;
-                    self = (struct lsap_cb *)hashbin_get_next(lap->lsaps)) {
-                       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC,
-                                   goto outloop;);
-                       seq_printf(seq, "  lsap state: %s, ",
-                                  irlsap_state[ self->lsap_state]);
-                       seq_printf(seq,
-                                  "slsap_sel: %#02x, dlsap_sel: %#02x, ",
-                                  self->slsap_sel, self->dlsap_sel);
-                       seq_printf(seq, "(%s)", self->notify.name);
-                       seq_putc(seq, '\n');
-
-               }
-       IRDA_ASSERT_LABEL(outloop:)
-               spin_unlock(&lap->lsaps->hb_spinlock);
-               seq_putc(seq, '\n');
-       } else
-               return -EINVAL;
-
-       return 0;
-}
-
-static const struct seq_operations irlmp_seq_ops = {
-       .start  = irlmp_seq_start,
-       .next   = irlmp_seq_next,
-       .stop   = irlmp_seq_stop,
-       .show   = irlmp_seq_show,
-};
-
-static int irlmp_seq_open(struct inode *inode, struct file *file)
-{
-       IRDA_ASSERT(irlmp != NULL, return -EINVAL;);
-
-       return seq_open_private(file, &irlmp_seq_ops,
-                       sizeof(struct irlmp_iter_state));
-}
-
-const struct file_operations irlmp_seq_fops = {
-       .owner          = THIS_MODULE,
-       .open           = irlmp_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release_private,
-};
-
-#endif /* PROC_FS */
diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c
deleted file mode 100644 (file)
index e306cf2..0000000
+++ /dev/null
@@ -1,886 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlmp_event.c
- * Version:       0.8
- * Description:   An IrDA LMP event driver for Linux
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Tue Dec 14 23:04:16 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/kernel.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/timer.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irlmp_frame.h>
-#include <net/irda/irlmp_event.h>
-
-const char *const irlmp_state[] = {
-       "LAP_STANDBY",
-       "LAP_U_CONNECT",
-       "LAP_ACTIVE",
-};
-
-const char *const irlsap_state[] = {
-       "LSAP_DISCONNECTED",
-       "LSAP_CONNECT",
-       "LSAP_CONNECT_PEND",
-       "LSAP_DATA_TRANSFER_READY",
-       "LSAP_SETUP",
-       "LSAP_SETUP_PEND",
-};
-
-static const char *const irlmp_event[] __maybe_unused = {
-       "LM_CONNECT_REQUEST",
-       "LM_CONNECT_CONFIRM",
-       "LM_CONNECT_RESPONSE",
-       "LM_CONNECT_INDICATION",
-
-       "LM_DISCONNECT_INDICATION",
-       "LM_DISCONNECT_REQUEST",
-
-       "LM_DATA_REQUEST",
-       "LM_UDATA_REQUEST",
-       "LM_DATA_INDICATION",
-       "LM_UDATA_INDICATION",
-
-       "LM_WATCHDOG_TIMEOUT",
-
-       /* IrLAP events */
-       "LM_LAP_CONNECT_REQUEST",
-       "LM_LAP_CONNECT_INDICATION",
-       "LM_LAP_CONNECT_CONFIRM",
-       "LM_LAP_DISCONNECT_INDICATION",
-       "LM_LAP_DISCONNECT_REQUEST",
-       "LM_LAP_DISCOVERY_REQUEST",
-       "LM_LAP_DISCOVERY_CONFIRM",
-       "LM_LAP_IDLE_TIMEOUT",
-};
-
-/* LAP Connection control proto declarations */
-static void irlmp_state_standby  (struct lap_cb *, IRLMP_EVENT,
-                                 struct sk_buff *);
-static void irlmp_state_u_connect(struct lap_cb *, IRLMP_EVENT,
-                                 struct sk_buff *);
-static void irlmp_state_active   (struct lap_cb *, IRLMP_EVENT,
-                                 struct sk_buff *);
-
-/* LSAP Connection control proto declarations */
-static int irlmp_state_disconnected(struct lsap_cb *, IRLMP_EVENT,
-                                   struct sk_buff *);
-static int irlmp_state_connect     (struct lsap_cb *, IRLMP_EVENT,
-                                   struct sk_buff *);
-static int irlmp_state_connect_pend(struct lsap_cb *, IRLMP_EVENT,
-                                   struct sk_buff *);
-static int irlmp_state_dtr         (struct lsap_cb *, IRLMP_EVENT,
-                                   struct sk_buff *);
-static int irlmp_state_setup       (struct lsap_cb *, IRLMP_EVENT,
-                                   struct sk_buff *);
-static int irlmp_state_setup_pend  (struct lsap_cb *, IRLMP_EVENT,
-                                   struct sk_buff *);
-
-static void (*lap_state[]) (struct lap_cb *, IRLMP_EVENT, struct sk_buff *) =
-{
-       irlmp_state_standby,
-       irlmp_state_u_connect,
-       irlmp_state_active,
-};
-
-static int (*lsap_state[])( struct lsap_cb *, IRLMP_EVENT, struct sk_buff *) =
-{
-       irlmp_state_disconnected,
-       irlmp_state_connect,
-       irlmp_state_connect_pend,
-       irlmp_state_dtr,
-       irlmp_state_setup,
-       irlmp_state_setup_pend
-};
-
-static inline void irlmp_next_lap_state(struct lap_cb *self,
-                                       IRLMP_STATE state)
-{
-       /*
-         pr_debug("%s(), LMP LAP = %s\n", __func__, irlmp_state[state]);
-       */
-       self->lap_state = state;
-}
-
-static inline void irlmp_next_lsap_state(struct lsap_cb *self,
-                                        LSAP_STATE state)
-{
-       /*
-       IRDA_ASSERT(self != NULL, return;);
-       pr_debug("%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]);
-       */
-       self->lsap_state = state;
-}
-
-/* Do connection control events */
-int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
-                       struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
-       pr_debug("%s(), EVENT = %s, STATE = %s\n",
-                __func__, irlmp_event[event], irlsap_state[self->lsap_state]);
-
-       return (*lsap_state[self->lsap_state]) (self, event, skb);
-}
-
-/*
- * Function do_lap_event (event, skb, info)
- *
- *    Do IrLAP control events
- *
- */
-void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
-                       struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-
-       pr_debug("%s(), EVENT = %s, STATE = %s\n", __func__,
-                irlmp_event[event],
-                irlmp_state[self->lap_state]);
-
-       (*lap_state[self->lap_state]) (self, event, skb);
-}
-
-void irlmp_discovery_timer_expired(void *data)
-{
-       /* We always cleanup the log (active & passive discovery) */
-       irlmp_do_expiry();
-
-       irlmp_do_discovery(sysctl_discovery_slots);
-
-       /* Restart timer */
-       irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout * HZ);
-}
-
-void irlmp_watchdog_timer_expired(void *data)
-{
-       struct lsap_cb *self = (struct lsap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-
-       irlmp_do_lsap_event(self, LM_WATCHDOG_TIMEOUT, NULL);
-}
-
-void irlmp_idle_timer_expired(void *data)
-{
-       struct lap_cb *self = (struct lap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-
-       irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL);
-}
-
-/*
- * Send an event on all LSAPs attached to this LAP.
- */
-static inline void
-irlmp_do_all_lsap_event(hashbin_t *    lsap_hashbin,
-                       IRLMP_EVENT     event)
-{
-       struct lsap_cb *lsap;
-       struct lsap_cb *lsap_next;
-
-       /* Note : this function use the new hashbin_find_next()
-        * function, instead of the old hashbin_get_next().
-        * This make sure that we are always pointing one lsap
-        * ahead, so that if the current lsap is removed as the
-        * result of sending the event, we don't care.
-        * Also, as we store the context ourselves, if an enumeration
-        * of the same lsap hashbin happens as the result of sending the
-        * event, we don't care.
-        * The only problem is if the next lsap is removed. In that case,
-        * hashbin_find_next() will return NULL and we will abort the
-        * enumeration. - Jean II */
-
-       /* Also : we don't accept any skb in input. We can *NOT* pass
-        * the same skb to multiple clients safely, we would need to
-        * skb_clone() it. - Jean II */
-
-       lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin);
-
-       while (NULL != hashbin_find_next(lsap_hashbin,
-                                        (long) lsap,
-                                        NULL,
-                                        (void *) &lsap_next) ) {
-               irlmp_do_lsap_event(lsap, event, NULL);
-               lsap = lsap_next;
-       }
-}
-
-/*********************************************************************
- *
- *    LAP connection control states
- *
- ********************************************************************/
-
-/*
- * Function irlmp_state_standby (event, skb, info)
- *
- *    STANDBY, The IrLAP connection does not exist.
- *
- */
-static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
-                               struct sk_buff *skb)
-{
-       IRDA_ASSERT(self->irlap != NULL, return;);
-
-       switch (event) {
-       case LM_LAP_DISCOVERY_REQUEST:
-               /* irlmp_next_station_state( LMP_DISCOVER); */
-
-               irlap_discovery_request(self->irlap, &irlmp->discovery_cmd);
-               break;
-       case LM_LAP_CONNECT_INDICATION:
-               /*  It's important to switch state first, to avoid IrLMP to
-                *  think that the link is free since IrLMP may then start
-                *  discovery before the connection is properly set up. DB.
-                */
-               irlmp_next_lap_state(self, LAP_ACTIVE);
-
-               /* Just accept connection TODO, this should be fixed */
-               irlap_connect_response(self->irlap, skb);
-               break;
-       case LM_LAP_CONNECT_REQUEST:
-               pr_debug("%s() LS_CONNECT_REQUEST\n", __func__);
-
-               irlmp_next_lap_state(self, LAP_U_CONNECT);
-
-               /* FIXME: need to set users requested QoS */
-               irlap_connect_request(self->irlap, self->daddr, NULL, 0);
-               break;
-       case LM_LAP_DISCONNECT_INDICATION:
-               pr_debug("%s(), Error LM_LAP_DISCONNECT_INDICATION\n",
-                        __func__);
-
-               irlmp_next_lap_state(self, LAP_STANDBY);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n",
-                        __func__, irlmp_event[event]);
-               break;
-       }
-}
-
-/*
- * Function irlmp_state_u_connect (event, skb, info)
- *
- *    U_CONNECT, The layer above has tried to open an LSAP connection but
- *    since the IrLAP connection does not exist, we must first start an
- *    IrLAP connection. We are now waiting response from IrLAP.
- * */
-static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
-                                 struct sk_buff *skb)
-{
-       pr_debug("%s(), event=%s\n", __func__, irlmp_event[event]);
-
-       switch (event) {
-       case LM_LAP_CONNECT_INDICATION:
-               /*  It's important to switch state first, to avoid IrLMP to
-                *  think that the link is free since IrLMP may then start
-                *  discovery before the connection is properly set up. DB.
-                */
-               irlmp_next_lap_state(self, LAP_ACTIVE);
-
-               /* Just accept connection TODO, this should be fixed */
-               irlap_connect_response(self->irlap, skb);
-
-               /* Tell LSAPs that they can start sending data */
-               irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
-
-               /* Note : by the time we get there (LAP retries and co),
-                * the lsaps may already have gone. This avoid getting stuck
-                * forever in LAP_ACTIVE state - Jean II */
-               if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
-                       pr_debug("%s() NO LSAPs !\n",  __func__);
-                       irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
-               }
-               break;
-       case LM_LAP_CONNECT_REQUEST:
-               /* Already trying to connect */
-               break;
-       case LM_LAP_CONNECT_CONFIRM:
-               /* For all lsap_ce E Associated do LS_Connect_confirm */
-               irlmp_next_lap_state(self, LAP_ACTIVE);
-
-               /* Tell LSAPs that they can start sending data */
-               irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
-
-               /* Note : by the time we get there (LAP retries and co),
-                * the lsaps may already have gone. This avoid getting stuck
-                * forever in LAP_ACTIVE state - Jean II */
-               if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
-                       pr_debug("%s() NO LSAPs !\n",  __func__);
-                       irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
-               }
-               break;
-       case LM_LAP_DISCONNECT_INDICATION:
-               pr_debug("%s(), LM_LAP_DISCONNECT_INDICATION\n",  __func__);
-               irlmp_next_lap_state(self, LAP_STANDBY);
-
-               /* Send disconnect event to all LSAPs using this link */
-               irlmp_do_all_lsap_event(self->lsaps,
-                                       LM_LAP_DISCONNECT_INDICATION);
-               break;
-       case LM_LAP_DISCONNECT_REQUEST:
-               pr_debug("%s(), LM_LAP_DISCONNECT_REQUEST\n",  __func__);
-
-               /* One of the LSAP did timeout or was closed, if it was
-                * the last one, try to get out of here - Jean II */
-               if (HASHBIN_GET_SIZE(self->lsaps) <= 1) {
-                       irlap_disconnect_request(self->irlap);
-               }
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n",
-                        __func__, irlmp_event[event]);
-               break;
-       }
-}
-
-/*
- * Function irlmp_state_active (event, skb, info)
- *
- *    ACTIVE, IrLAP connection is active
- *
- */
-static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
-                              struct sk_buff *skb)
-{
-       switch (event) {
-       case LM_LAP_CONNECT_REQUEST:
-               pr_debug("%s(), LS_CONNECT_REQUEST\n", __func__);
-
-               /*
-                * IrLAP may have a pending disconnect. We tried to close
-                * IrLAP, but it was postponed because the link was
-                * busy or we were still sending packets. As we now
-                * need it, make sure it stays on. Jean II
-                */
-               irlap_clear_disconnect(self->irlap);
-
-               /*
-                *  LAP connection already active, just bounce back! Since we
-                *  don't know which LSAP that tried to do this, we have to
-                *  notify all LSAPs using this LAP, but that should be safe to
-                *  do anyway.
-                */
-               irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
-
-               /* Needed by connect indication */
-               irlmp_do_all_lsap_event(irlmp->unconnected_lsaps,
-                                       LM_LAP_CONNECT_CONFIRM);
-               /* Keep state */
-               break;
-       case LM_LAP_DISCONNECT_REQUEST:
-               /*
-                *  Need to find out if we should close IrLAP or not. If there
-                *  is only one LSAP connection left on this link, that LSAP
-                *  must be the one that tries to close IrLAP. It will be
-                *  removed later and moved to the list of unconnected LSAPs
-                */
-               if (HASHBIN_GET_SIZE(self->lsaps) > 0) {
-                       /* Timer value is checked in irsysctl - Jean II */
-                       irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000);
-               } else {
-                       /* No more connections, so close IrLAP */
-
-                       /* We don't want to change state just yet, because
-                        * we want to reflect accurately the real state of
-                        * the LAP, not the state we wish it was in,
-                        * so that we don't lose LM_LAP_CONNECT_REQUEST.
-                        * In some cases, IrLAP won't close the LAP
-                        * immediately. For example, it might still be
-                        * retrying packets or waiting for the pf bit.
-                        * As the LAP always send a DISCONNECT_INDICATION
-                        * in PCLOSE or SCLOSE, just change state on that.
-                        * Jean II */
-                       irlap_disconnect_request(self->irlap);
-               }
-               break;
-       case LM_LAP_IDLE_TIMEOUT:
-               if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
-                       /* Same reasoning as above - keep state */
-                       irlap_disconnect_request(self->irlap);
-               }
-               break;
-       case LM_LAP_DISCONNECT_INDICATION:
-               irlmp_next_lap_state(self, LAP_STANDBY);
-
-               /* In some case, at this point our side has already closed
-                * all lsaps, and we are waiting for the idle_timer to
-                * expire. If another device reconnect immediately, the
-                * idle timer will expire in the midle of the connection
-                * initialisation, screwing up things a lot...
-                * Therefore, we must stop the timer... */
-               irlmp_stop_idle_timer(self);
-
-               /*
-                *  Inform all connected LSAP's using this link
-                */
-               irlmp_do_all_lsap_event(self->lsaps,
-                                       LM_LAP_DISCONNECT_INDICATION);
-
-               /* Force an expiry of the discovery log.
-                * Now that the LAP is free, the system may attempt to
-                * connect to another device. Unfortunately, our entries
-                * are stale. There is a small window (<3s) before the
-                * normal discovery will run and where irlmp_connect_request()
-                * can get the wrong info, so make sure things get
-                * cleaned *NOW* ;-) - Jean II */
-               irlmp_do_expiry();
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s\n",
-                        __func__, irlmp_event[event]);
-               break;
-       }
-}
-
-/*********************************************************************
- *
- *    LSAP connection control states
- *
- ********************************************************************/
-
-/*
- * Function irlmp_state_disconnected (event, skb, info)
- *
- *    DISCONNECTED
- *
- */
-static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
-                                   struct sk_buff *skb)
-{
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
-       switch (event) {
-#ifdef CONFIG_IRDA_ULTRA
-       case LM_UDATA_INDICATION:
-               /* This is most bizarre. Those packets are  aka unreliable
-                * connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA.
-                * Why do we pass them as Ultra ??? Jean II */
-               irlmp_connless_data_indication(self, skb);
-               break;
-#endif /* CONFIG_IRDA_ULTRA */
-       case LM_CONNECT_REQUEST:
-               pr_debug("%s(), LM_CONNECT_REQUEST\n", __func__);
-
-               if (self->conn_skb) {
-                       net_warn_ratelimited("%s: busy with another request!\n",
-                                            __func__);
-                       return -EBUSY;
-               }
-               /* Don't forget to refcount it (see irlmp_connect_request()) */
-               skb_get(skb);
-               self->conn_skb = skb;
-
-               irlmp_next_lsap_state(self, LSAP_SETUP_PEND);
-
-               /* Start watchdog timer (5 secs for now) */
-               irlmp_start_watchdog_timer(self, 5*HZ);
-
-               irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
-               break;
-       case LM_CONNECT_INDICATION:
-               if (self->conn_skb) {
-                       net_warn_ratelimited("%s: busy with another request!\n",
-                                            __func__);
-                       return -EBUSY;
-               }
-               /* Don't forget to refcount it (see irlap_driver_rcv()) */
-               skb_get(skb);
-               self->conn_skb = skb;
-
-               irlmp_next_lsap_state(self, LSAP_CONNECT_PEND);
-
-               /* Start watchdog timer
-                * This is not mentionned in the spec, but there is a rare
-                * race condition that can get the socket stuck.
-                * If we receive this event while our LAP is closing down,
-                * the LM_LAP_CONNECT_REQUEST get lost and we get stuck in
-                * CONNECT_PEND state forever.
-                * The other cause of getting stuck down there is if the
-                * higher layer never reply to the CONNECT_INDICATION.
-                * Anyway, it make sense to make sure that we always have
-                * a backup plan. 1 second is plenty (should be immediate).
-                * Jean II */
-               irlmp_start_watchdog_timer(self, 1*HZ);
-
-               irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
-                        __func__, irlmp_event[event], self->slsap_sel);
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlmp_state_connect (self, event, skb)
- *
- *    CONNECT
- *
- */
-static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
-                               struct sk_buff *skb)
-{
-       struct lsap_cb *lsap;
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
-       switch (event) {
-       case LM_CONNECT_RESPONSE:
-               /*
-                *  Bind this LSAP to the IrLAP link where the connect was
-                *  received
-                */
-               lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self,
-                                     NULL);
-
-               IRDA_ASSERT(lsap == self, return -1;);
-               IRDA_ASSERT(self->lap != NULL, return -1;);
-               IRDA_ASSERT(self->lap->lsaps != NULL, return -1;);
-
-               hashbin_insert(self->lap->lsaps, (irda_queue_t *) self,
-                              (long) self, NULL);
-
-               set_bit(0, &self->connected);   /* TRUE */
-
-               irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
-                                  self->slsap_sel, CONNECT_CNF, skb);
-
-               del_timer(&self->watchdog_timer);
-
-               irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
-               break;
-       case LM_WATCHDOG_TIMEOUT:
-               /* May happen, who knows...
-                * Jean II */
-               pr_debug("%s() WATCHDOG_TIMEOUT!\n",  __func__);
-
-               /* Disconnect, get out... - Jean II */
-               self->lap = NULL;
-               self->dlsap_sel = LSAP_ANY;
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-               break;
-       default:
-               /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
-                * are *not* yet bound to the IrLAP link. Jean II */
-               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
-                        __func__, irlmp_event[event], self->slsap_sel);
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlmp_state_connect_pend (event, skb, info)
- *
- *    CONNECT_PEND
- *
- */
-static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
-                                   struct sk_buff *skb)
-{
-       struct sk_buff *tx_skb;
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
-       switch (event) {
-       case LM_CONNECT_REQUEST:
-               /* Keep state */
-               break;
-       case LM_CONNECT_RESPONSE:
-               pr_debug("%s(), LM_CONNECT_RESPONSE, no indication issued yet\n",
-                        __func__);
-               /* Keep state */
-               break;
-       case LM_DISCONNECT_REQUEST:
-               pr_debug("%s(), LM_DISCONNECT_REQUEST, not yet bound to IrLAP connection\n",
-                        __func__);
-               /* Keep state */
-               break;
-       case LM_LAP_CONNECT_CONFIRM:
-               pr_debug("%s(), LS_CONNECT_CONFIRM\n",  __func__);
-               irlmp_next_lsap_state(self, LSAP_CONNECT);
-
-               tx_skb = self->conn_skb;
-               self->conn_skb = NULL;
-
-               irlmp_connect_indication(self, tx_skb);
-               /* Drop reference count - see irlmp_connect_indication(). */
-               dev_kfree_skb(tx_skb);
-               break;
-       case LM_WATCHDOG_TIMEOUT:
-               /* Will happen in some rare cases because of a race condition.
-                * Just make sure we don't stay there forever...
-                * Jean II */
-               pr_debug("%s() WATCHDOG_TIMEOUT!\n",  __func__);
-
-               /* Go back to disconnected mode, keep the socket waiting */
-               self->lap = NULL;
-               self->dlsap_sel = LSAP_ANY;
-               if(self->conn_skb)
-                       dev_kfree_skb(self->conn_skb);
-               self->conn_skb = NULL;
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-               break;
-       default:
-               /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
-                * are *not* yet bound to the IrLAP link. Jean II */
-               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
-                        __func__, irlmp_event[event], self->slsap_sel);
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlmp_state_dtr (self, event, skb)
- *
- *    DATA_TRANSFER_READY
- *
- */
-static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
-                          struct sk_buff *skb)
-{
-       LM_REASON reason;
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-       IRDA_ASSERT(self->lap != NULL, return -1;);
-
-       switch (event) {
-       case LM_DATA_REQUEST: /* Optimize for the common case */
-               irlmp_send_data_pdu(self->lap, self->dlsap_sel,
-                                   self->slsap_sel, FALSE, skb);
-               break;
-       case LM_DATA_INDICATION: /* Optimize for the common case */
-               irlmp_data_indication(self, skb);
-               break;
-       case LM_UDATA_REQUEST:
-               IRDA_ASSERT(skb != NULL, return -1;);
-               irlmp_send_data_pdu(self->lap, self->dlsap_sel,
-                                   self->slsap_sel, TRUE, skb);
-               break;
-       case LM_UDATA_INDICATION:
-               irlmp_udata_indication(self, skb);
-               break;
-       case LM_CONNECT_REQUEST:
-               pr_debug("%s(), LM_CONNECT_REQUEST, error, LSAP already connected\n",
-                        __func__);
-               /* Keep state */
-               break;
-       case LM_CONNECT_RESPONSE:
-               pr_debug("%s(), LM_CONNECT_RESPONSE, error, LSAP already connected\n",
-                        __func__);
-               /* Keep state */
-               break;
-       case LM_DISCONNECT_REQUEST:
-               irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, self->slsap_sel,
-                                  DISCONNECT, skb);
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-               /* Called only from irlmp_disconnect_request(), will
-                * unbind from LAP over there. Jean II */
-
-               /* Try to close the LAP connection if its still there */
-               if (self->lap) {
-                       pr_debug("%s(), trying to close IrLAP\n",
-                                __func__);
-                       irlmp_do_lap_event(self->lap,
-                                          LM_LAP_DISCONNECT_REQUEST,
-                                          NULL);
-               }
-               break;
-       case LM_LAP_DISCONNECT_INDICATION:
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
-               reason = irlmp_convert_lap_reason(self->lap->reason);
-
-               irlmp_disconnect_indication(self, reason, NULL);
-               break;
-       case LM_DISCONNECT_INDICATION:
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
-               IRDA_ASSERT(self->lap != NULL, return -1;);
-               IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
-
-               IRDA_ASSERT(skb != NULL, return -1;);
-               IRDA_ASSERT(skb->len > 3, return -1;);
-               reason = skb->data[3];
-
-                /* Try to close the LAP connection */
-               pr_debug("%s(), trying to close IrLAP\n", __func__);
-               irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
-
-               irlmp_disconnect_indication(self, reason, skb);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
-                        __func__, irlmp_event[event], self->slsap_sel);
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlmp_state_setup (event, skb, info)
- *
- *    SETUP, Station Control has set up the underlying IrLAP connection.
- *    An LSAP connection request has been transmitted to the peer
- *    LSAP-Connection Control FSM and we are awaiting reply.
- */
-static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
-                            struct sk_buff *skb)
-{
-       LM_REASON reason;
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
-
-       switch (event) {
-       case LM_CONNECT_CONFIRM:
-               irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
-
-               del_timer(&self->watchdog_timer);
-
-               irlmp_connect_confirm(self, skb);
-               break;
-       case LM_DISCONNECT_INDICATION:
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
-               IRDA_ASSERT(self->lap != NULL, return -1;);
-               IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
-
-               IRDA_ASSERT(skb != NULL, return -1;);
-               IRDA_ASSERT(skb->len > 3, return -1;);
-               reason = skb->data[3];
-
-                /* Try to close the LAP connection */
-               pr_debug("%s(), trying to close IrLAP\n",  __func__);
-               irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
-
-               irlmp_disconnect_indication(self, reason, skb);
-               break;
-       case LM_LAP_DISCONNECT_INDICATION:
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
-               del_timer(&self->watchdog_timer);
-
-               IRDA_ASSERT(self->lap != NULL, return -1;);
-               IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
-
-               reason = irlmp_convert_lap_reason(self->lap->reason);
-
-               irlmp_disconnect_indication(self, reason, skb);
-               break;
-       case LM_WATCHDOG_TIMEOUT:
-               pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__);
-
-               IRDA_ASSERT(self->lap != NULL, return -1;);
-               irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
-               irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
-                        __func__, irlmp_event[event], self->slsap_sel);
-               break;
-       }
-       return ret;
-}
-
-/*
- * Function irlmp_state_setup_pend (event, skb, info)
- *
- *    SETUP_PEND, An LM_CONNECT_REQUEST has been received from the service
- *    user to set up an LSAP connection. A request has been sent to the
- *    LAP FSM to set up the underlying IrLAP connection, and we
- *    are awaiting confirm.
- */
-static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
-                                 struct sk_buff *skb)
-{
-       struct sk_buff *tx_skb;
-       LM_REASON reason;
-       int ret = 0;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(irlmp != NULL, return -1;);
-
-       switch (event) {
-       case LM_LAP_CONNECT_CONFIRM:
-               IRDA_ASSERT(self->conn_skb != NULL, return -1;);
-
-               tx_skb = self->conn_skb;
-               self->conn_skb = NULL;
-
-               irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
-                                  self->slsap_sel, CONNECT_CMD, tx_skb);
-               /* Drop reference count - see irlap_data_request(). */
-               dev_kfree_skb(tx_skb);
-
-               irlmp_next_lsap_state(self, LSAP_SETUP);
-               break;
-       case LM_WATCHDOG_TIMEOUT:
-               pr_debug("%s() : WATCHDOG_TIMEOUT !\n",  __func__);
-
-               IRDA_ASSERT(self->lap != NULL, return -1;);
-               irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
-               irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
-               break;
-       case LM_LAP_DISCONNECT_INDICATION: /* LS_Disconnect.indication */
-               del_timer( &self->watchdog_timer);
-
-               irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
-
-               reason = irlmp_convert_lap_reason(self->lap->reason);
-
-               irlmp_disconnect_indication(self, reason, NULL);
-               break;
-       default:
-               pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
-                        __func__, irlmp_event[event], self->slsap_sel);
-               break;
-       }
-       return ret;
-}
diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c
deleted file mode 100644 (file)
index 38b0f99..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irlmp_frame.c
- * Version:       0.9
- * Description:   IrLMP frame implementation
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Tue Aug 19 02:09:59 1997
- * Modified at:   Mon Dec 13 13:41:12 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
- *     All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/kernel.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
-#include <net/irda/timer.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irlmp_frame.h>
-#include <net/irda/discovery.h>
-
-static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap,
-                                      __u8 slsap, int status, hashbin_t *);
-
-inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
-                               int expedited, struct sk_buff *skb)
-{
-       skb->data[0] = dlsap;
-       skb->data[1] = slsap;
-
-       if (expedited) {
-               pr_debug("%s(), sending expedited data\n", __func__);
-               irlap_data_request(self->irlap, skb, TRUE);
-       } else
-               irlap_data_request(self->irlap, skb, FALSE);
-}
-
-/*
- * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb)
- *
- *    Send Link Control Frame to IrLAP
- */
-void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
-                       __u8 opcode, struct sk_buff *skb)
-{
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       frame = skb->data;
-
-       frame[0] = dlsap | CONTROL_BIT;
-       frame[1] = slsap;
-
-       frame[2] = opcode;
-
-       if (opcode == DISCONNECT)
-               frame[3] = 0x01; /* Service user request */
-       else
-               frame[3] = 0x00; /* rsvd */
-
-       irlap_data_request(self->irlap, skb, FALSE);
-}
-
-/*
- * Function irlmp_input (skb)
- *
- *    Used by IrLAP to pass received data frames to IrLMP layer
- *
- */
-void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
-                               int unreliable)
-{
-       struct lsap_cb *lsap;
-       __u8   slsap_sel;   /* Source (this) LSAP address */
-       __u8   dlsap_sel;   /* Destination LSAP address */
-       __u8   *fp;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-       IRDA_ASSERT(skb->len > 2, return;);
-
-       fp = skb->data;
-
-       /*
-        *  The next statements may be confusing, but we do this so that
-        *  destination LSAP of received frame is source LSAP in our view
-        */
-       slsap_sel = fp[0] & LSAP_MASK;
-       dlsap_sel = fp[1];
-
-       /*
-        *  Check if this is an incoming connection, since we must deal with
-        *  it in a different way than other established connections.
-        */
-       if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) {
-               pr_debug("%s(), incoming connection, source LSAP=%d, dest LSAP=%d\n",
-                        __func__, slsap_sel, dlsap_sel);
-
-               /* Try to find LSAP among the unconnected LSAPs */
-               lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD,
-                                      irlmp->unconnected_lsaps);
-
-               /* Maybe LSAP was already connected, so try one more time */
-               if (!lsap) {
-                       pr_debug("%s(), incoming connection for LSAP already connected\n",
-                                __func__);
-                       lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
-                                              self->lsaps);
-               }
-       } else
-               lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
-                                      self->lsaps);
-
-       if (lsap == NULL) {
-               pr_debug("IrLMP, Sorry, no LSAP for received frame!\n");
-               pr_debug("%s(), slsap_sel = %02x, dlsap_sel = %02x\n",
-                        __func__, slsap_sel, dlsap_sel);
-               if (fp[0] & CONTROL_BIT) {
-                       pr_debug("%s(), received control frame %02x\n",
-                                __func__, fp[2]);
-               } else {
-                       pr_debug("%s(), received data frame\n", __func__);
-               }
-               return;
-       }
-
-       /*
-        *  Check if we received a control frame?
-        */
-       if (fp[0] & CONTROL_BIT) {
-               switch (fp[2]) {
-               case CONNECT_CMD:
-                       lsap->lap = self;
-                       irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb);
-                       break;
-               case CONNECT_CNF:
-                       irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb);
-                       break;
-               case DISCONNECT:
-                       pr_debug("%s(), Disconnect indication!\n",
-                                __func__);
-                       irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION,
-                                           skb);
-                       break;
-               case ACCESSMODE_CMD:
-                       pr_debug("Access mode cmd not implemented!\n");
-                       break;
-               case ACCESSMODE_CNF:
-                       pr_debug("Access mode cnf not implemented!\n");
-                       break;
-               default:
-                       pr_debug("%s(), Unknown control frame %02x\n",
-                                __func__, fp[2]);
-                       break;
-               }
-       } else if (unreliable) {
-               /* Optimize and bypass the state machine if possible */
-               if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
-                       irlmp_udata_indication(lsap, skb);
-               else
-                       irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb);
-       } else {
-               /* Optimize and bypass the state machine if possible */
-               if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
-                       irlmp_data_indication(lsap, skb);
-               else
-                       irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb);
-       }
-}
-
-/*
- * Function irlmp_link_unitdata_indication (self, skb)
- *
- *
- *
- */
-#ifdef CONFIG_IRDA_ULTRA
-void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
-{
-       struct lsap_cb *lsap;
-       __u8   slsap_sel;   /* Source (this) LSAP address */
-       __u8   dlsap_sel;   /* Destination LSAP address */
-       __u8   pid;         /* Protocol identifier */
-       __u8   *fp;
-       unsigned long flags;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-       IRDA_ASSERT(skb->len > 2, return;);
-
-       fp = skb->data;
-
-       /*
-        *  The next statements may be confusing, but we do this so that
-        *  destination LSAP of received frame is source LSAP in our view
-        */
-       slsap_sel = fp[0] & LSAP_MASK;
-       dlsap_sel = fp[1];
-       pid       = fp[2];
-
-       if (pid & 0x80) {
-               pr_debug("%s(), extension in PID not supp!\n",
-                        __func__);
-               return;
-       }
-
-       /* Check if frame is addressed to the connectionless LSAP */
-       if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
-               pr_debug("%s(), dropping frame!\n", __func__);
-               return;
-       }
-
-       /* Search the connectionless LSAP */
-       spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-       lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
-       while (lsap != NULL) {
-               /*
-                *  Check if source LSAP and dest LSAP selectors and PID match.
-                */
-               if ((lsap->slsap_sel == slsap_sel) &&
-                   (lsap->dlsap_sel == dlsap_sel) &&
-                   (lsap->pid == pid))
-               {
-                       break;
-               }
-               lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
-       }
-       spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
-
-       if (lsap)
-               irlmp_connless_data_indication(lsap, skb);
-       else {
-               pr_debug("%s(), found no matching LSAP!\n", __func__);
-       }
-}
-#endif /* CONFIG_IRDA_ULTRA */
-
-/*
- * Function irlmp_link_disconnect_indication (reason, userdata)
- *
- *    IrLAP has disconnected
- *
- */
-void irlmp_link_disconnect_indication(struct lap_cb *lap,
-                                     struct irlap_cb *irlap,
-                                     LAP_REASON reason,
-                                     struct sk_buff *skb)
-{
-       IRDA_ASSERT(lap != NULL, return;);
-       IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
-
-       lap->reason = reason;
-       lap->daddr = DEV_ADDR_ANY;
-
-       /* FIXME: must do something with the skb if any */
-
-       /*
-        *  Inform station state machine
-        */
-       irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL);
-}
-
-/*
- * Function irlmp_link_connect_indication (qos)
- *
- *    Incoming LAP connection!
- *
- */
-void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr,
-                                  __u32 daddr, struct qos_info *qos,
-                                  struct sk_buff *skb)
-{
-       /* Copy QoS settings for this session */
-       self->qos = qos;
-
-       /* Update destination device address */
-       self->daddr = daddr;
-       IRDA_ASSERT(self->saddr == saddr, return;);
-
-       irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb);
-}
-
-/*
- * Function irlmp_link_connect_confirm (qos)
- *
- *    LAP connection confirmed!
- *
- */
-void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
-                               struct sk_buff *skb)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-       IRDA_ASSERT(qos != NULL, return;);
-
-       /* Don't need use the skb for now */
-
-       /* Copy QoS settings for this session */
-       self->qos = qos;
-
-       irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL);
-}
-
-/*
- * Function irlmp_link_discovery_indication (self, log)
- *
- *    Device is discovering us
- *
- * It's not an answer to our own discoveries, just another device trying
- * to perform discovery, but we don't want to miss the opportunity
- * to exploit this information, because :
- *     o We may not actively perform discovery (just passive discovery)
- *     o This type of discovery is much more reliable. In some cases, it
- *       seem that less than 50% of our discoveries get an answer, while
- *       we always get ~100% of these.
- *     o Make faster discovery, statistically divide time of discovery
- *       events by 2 (important for the latency aspect and user feel)
- *     o Even is we do active discovery, the other node might not
- *       answer our discoveries (ex: Palm). The Palm will just perform
- *       one active discovery and connect directly to us.
- *
- * However, when both devices discover each other, they might attempt to
- * connect to each other following the discovery event, and it would create
- * collisions on the medium (SNRM battle).
- * The "fix" for that is to disable all connection requests in IrLAP
- * for 100ms after a discovery indication by setting the media_busy flag.
- * Previously, we used to postpone the event which was quite ugly. Now
- * that IrLAP takes care of this problem, just pass the event up...
- *
- * Jean II
- */
-void irlmp_link_discovery_indication(struct lap_cb *self,
-                                    discovery_t *discovery)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-
-       /* Add to main log, cleanup */
-       irlmp_add_discovery(irlmp->cachelog, discovery);
-
-       /* Just handle it the same way as a discovery confirm,
-        * bypass the LM_LAP state machine (see below) */
-       irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE);
-}
-
-/*
- * Function irlmp_link_discovery_confirm (self, log)
- *
- *    Called by IrLAP with a list of discoveries after the discovery
- *    request has been carried out. A NULL log is received if IrLAP
- *    was unable to carry out the discovery request
- *
- */
-void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
-
-       /* Add to main log, cleanup */
-       irlmp_add_discovery_log(irlmp->cachelog, log);
-
-       /* Propagate event to various LSAPs registered for it.
-        * We bypass the LM_LAP state machine because
-        *      1) We do it regardless of the LM_LAP state
-        *      2) It doesn't affect the LM_LAP state
-        *      3) Faster, slimer, simpler, ...
-        * Jean II */
-       irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE);
-}
-
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-static inline void irlmp_update_cache(struct lap_cb *lap,
-                                     struct lsap_cb *lsap)
-{
-       /* Prevent concurrent read to get garbage */
-       lap->cache.valid = FALSE;
-       /* Update cache entry */
-       lap->cache.dlsap_sel = lsap->dlsap_sel;
-       lap->cache.slsap_sel = lsap->slsap_sel;
-       lap->cache.lsap = lsap;
-       lap->cache.valid = TRUE;
-}
-#endif
-
-/*
- * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
- *
- *    Find handle associated with destination and source LSAP
- *
- * Any IrDA connection (LSAP/TSAP) is uniquely identified by
- * 3 parameters, the local lsap, the remote lsap and the remote address.
- * We may initiate multiple connections to the same remote service
- * (they will have different local lsap), a remote device may initiate
- * multiple connections to the same local service (they will have
- * different remote lsap), or multiple devices may connect to the same
- * service and may use the same remote lsap (and they will have
- * different remote address).
- * So, where is the remote address ? Each LAP connection is made with
- * a single remote device, so imply a specific remote address.
- * Jean II
- */
-static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
-                                      __u8 slsap_sel, int status,
-                                      hashbin_t *queue)
-{
-       struct lsap_cb *lsap;
-       unsigned long flags;
-
-       /*
-        *  Optimize for the common case. We assume that the last frame
-        *  received is in the same connection as the last one, so check in
-        *  cache first to avoid the linear search
-        */
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-       if ((self->cache.valid) &&
-           (self->cache.slsap_sel == slsap_sel) &&
-           (self->cache.dlsap_sel == dlsap_sel))
-       {
-               return self->cache.lsap;
-       }
-#endif
-
-       spin_lock_irqsave(&queue->hb_spinlock, flags);
-
-       lsap = (struct lsap_cb *) hashbin_get_first(queue);
-       while (lsap != NULL) {
-               /*
-                *  If this is an incoming connection, then the destination
-                *  LSAP selector may have been specified as LM_ANY so that
-                *  any client can connect. In that case we only need to check
-                *  if the source LSAP (in our view!) match!
-                */
-               if ((status == CONNECT_CMD) &&
-                   (lsap->slsap_sel == slsap_sel) &&
-                   (lsap->dlsap_sel == LSAP_ANY)) {
-                       /* This is where the dest lsap sel is set on incoming
-                        * lsaps */
-                       lsap->dlsap_sel = dlsap_sel;
-                       break;
-               }
-               /*
-                *  Check if source LSAP and dest LSAP selectors match.
-                */
-               if ((lsap->slsap_sel == slsap_sel) &&
-                   (lsap->dlsap_sel == dlsap_sel))
-                       break;
-
-               lsap = (struct lsap_cb *) hashbin_get_next(queue);
-       }
-#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
-       if(lsap)
-               irlmp_update_cache(self, lsap);
-#endif
-       spin_unlock_irqrestore(&queue->hb_spinlock, flags);
-
-       /* Return what we've found or NULL */
-       return lsap;
-}
diff --git a/net/irda/irmod.c b/net/irda/irmod.c
deleted file mode 100644 (file)
index c5e35b8..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irmod.c
- * Version:       0.9
- * Description:   IrDA stack main entry points
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Mon Dec 15 13:55:39 1997
- * Modified at:   Wed Jan  5 15:12:41 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1997, 1999-2000 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2004 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-/*
- * This file contains the main entry points of the IrDA stack.
- * They are in this file and not af_irda.c because some developpers
- * are using the IrDA stack without the socket API (compiling out
- * af_irda.c).
- * Jean II
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irmod.h>            /* notify_t */
-#include <net/irda/irlap.h>            /* irlap_init */
-#include <net/irda/irlmp.h>            /* irlmp_init */
-#include <net/irda/iriap.h>            /* iriap_init */
-#include <net/irda/irttp.h>            /* irttp_init */
-#include <net/irda/irda_device.h>      /* irda_device_init */
-
-/* Packet type handler.
- * Tell the kernel how IrDA packets should be handled.
- */
-static struct packet_type irda_packet_type __read_mostly = {
-       .type   = cpu_to_be16(ETH_P_IRDA),
-       .func   = irlap_driver_rcv,     /* Packet type handler irlap_frame.c */
-};
-
-/*
- * Function irda_notify_init (notify)
- *
- *    Used for initializing the notify structure
- *
- */
-void irda_notify_init(notify_t *notify)
-{
-       notify->data_indication = NULL;
-       notify->udata_indication = NULL;
-       notify->connect_confirm = NULL;
-       notify->connect_indication = NULL;
-       notify->disconnect_indication = NULL;
-       notify->flow_indication = NULL;
-       notify->status_indication = NULL;
-       notify->instance = NULL;
-       strlcpy(notify->name, "Unknown", sizeof(notify->name));
-}
-EXPORT_SYMBOL(irda_notify_init);
-
-/*
- * Function irda_init (void)
- *
- *  Protocol stack initialisation entry point.
- *  Initialise the various components of the IrDA stack
- */
-static int __init irda_init(void)
-{
-       int ret = 0;
-
-       /* Lower layer of the stack */
-       irlmp_init();
-       irlap_init();
-
-       /* Driver/dongle support */
-       irda_device_init();
-
-       /* Higher layers of the stack */
-       iriap_init();
-       irttp_init();
-       ret = irsock_init();
-       if (ret < 0)
-               goto out_err_1;
-
-       /* Add IrDA packet type (Start receiving packets) */
-       dev_add_pack(&irda_packet_type);
-
-       /* External APIs */
-#ifdef CONFIG_PROC_FS
-       irda_proc_register();
-#endif
-#ifdef CONFIG_SYSCTL
-       ret = irda_sysctl_register();
-       if (ret < 0)
-               goto out_err_2;
-#endif
-
-       ret = irda_nl_register();
-       if (ret < 0)
-               goto out_err_3;
-
-       return 0;
-
- out_err_3:
-#ifdef CONFIG_SYSCTL
-       irda_sysctl_unregister();
- out_err_2:
-#endif
-#ifdef CONFIG_PROC_FS
-       irda_proc_unregister();
-#endif
-
-       /* Remove IrDA packet type (stop receiving packets) */
-       dev_remove_pack(&irda_packet_type);
-
-       /* Remove higher layers */
-       irsock_cleanup();
- out_err_1:
-       irttp_cleanup();
-       iriap_cleanup();
-
-       /* Remove lower layers */
-       irda_device_cleanup();
-       irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */
-
-       /* Remove middle layer */
-       irlmp_cleanup();
-
-
-       return ret;
-}
-
-/*
- * Function irda_cleanup (void)
- *
- *  Protocol stack cleanup/removal entry point.
- *  Cleanup the various components of the IrDA stack
- */
-static void __exit irda_cleanup(void)
-{
-       /* Remove External APIs */
-       irda_nl_unregister();
-
-#ifdef CONFIG_SYSCTL
-       irda_sysctl_unregister();
-#endif
-#ifdef CONFIG_PROC_FS
-       irda_proc_unregister();
-#endif
-
-       /* Remove IrDA packet type (stop receiving packets) */
-       dev_remove_pack(&irda_packet_type);
-
-       /* Remove higher layers */
-       irsock_cleanup();
-       irttp_cleanup();
-       iriap_cleanup();
-
-       /* Remove lower layers */
-       irda_device_cleanup();
-       irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */
-
-       /* Remove middle layer */
-       irlmp_cleanup();
-}
-
-/*
- * The IrDA stack must be initialised *before* drivers get initialised,
- * and *before* higher protocols (IrLAN/IrCOMM/IrNET) get initialised,
- * otherwise bad things will happen (hashbins will be NULL for example).
- * Those modules are at module_init()/device_initcall() level.
- *
- * On the other hand, it needs to be initialised *after* the basic
- * networking, the /proc/net filesystem and sysctl module. Those are
- * currently initialised in .../init/main.c (before initcalls).
- * Also, IrDA drivers needs to be initialised *after* the random number
- * generator (main stack and higher layer init don't need it anymore).
- *
- * Jean II
- */
-subsys_initcall(irda_init);
-module_exit(irda_cleanup);
-
-MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> & Jean Tourrilhes <jt@hpl.hp.com>");
-MODULE_DESCRIPTION("The Linux IrDA Protocol Stack");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_NETPROTO(PF_IRDA);
diff --git a/net/irda/irnet/Kconfig b/net/irda/irnet/Kconfig
deleted file mode 100644 (file)
index 28c557f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-config IRNET
-       tristate "IrNET protocol"
-       depends on IRDA && PPP
-       help
-         Say Y here if you want to build support for the IrNET protocol.
-         To compile it as a module, choose M here: the module will be
-         called irnet.  IrNET is a PPP driver, so you will also need a
-         working PPP subsystem (driver, daemon and config)...
-
-         IrNET is an alternate way to transfer TCP/IP traffic over IrDA.  It
-         uses synchronous PPP over a set of point to point IrDA sockets.  You
-         can use it between Linux machine or with W2k.
-
diff --git a/net/irda/irnet/Makefile b/net/irda/irnet/Makefile
deleted file mode 100644 (file)
index 61c365c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Makefile for the Linux IrDA IrNET protocol layer.
-#
-
-obj-$(CONFIG_IRNET) += irnet.o
-
-irnet-y := irnet_ppp.o irnet_irda.o
diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h
deleted file mode 100644 (file)
index 9d451f8..0000000
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- *     IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- *             Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file contains definitions and declarations global to the IrNET module,
- * all grouped in one place...
- * This file is a *private* header, so other modules don't want to know
- * what's in there...
- *
- * Note : as most part of the Linux kernel, this module is available
- * under the GNU General Public License (GPL).
- */
-
-#ifndef IRNET_H
-#define IRNET_H
-
-/************************** DOCUMENTATION ***************************/
-/*
- * What is IrNET
- * -------------
- * IrNET is a protocol allowing to carry TCP/IP traffic between two
- * IrDA peers in an efficient fashion. It is a thin layer, passing PPP
- * packets to IrTTP and vice versa. It uses PPP in synchronous mode,
- * because IrTTP offer a reliable sequenced packet service (as opposed
- * to a byte stream). In fact, you could see IrNET as carrying TCP/IP
- * in a IrDA socket, using PPP to provide the glue.
- *
- * The main difference with traditional PPP over IrCOMM is that we
- * avoid the framing and serial emulation which are a performance
- * bottleneck. It also allows multipoint communications in a sensible
- * fashion.
- *
- * The main difference with IrLAN is that we use PPP for the link
- * management, which is more standard, interoperable and flexible than
- * the IrLAN protocol. For example, PPP adds authentication,
- * encryption, compression, header compression and automated routing
- * setup. And, as IrNET let PPP do the hard work, the implementation
- * is much simpler than IrLAN.
- *
- * The Linux implementation
- * ------------------------
- * IrNET is written on top of the Linux-IrDA stack, and interface with
- * the generic Linux PPP driver. Because IrNET depend on recent
- * changes of the PPP driver interface, IrNET will work only with very
- * recent kernel (2.3.99-pre6 and up).
- *
- * The present implementation offer the following features :
- *     o simple user interface using pppd
- *     o efficient implementation (interface directly to PPP and IrTTP)
- *     o addressing (you can specify the name of the IrNET recipient)
- *     o multipoint operation (limited by IrLAP specification)
- *     o information in /proc/net/irda/irnet
- *     o IrNET events on /dev/irnet (for user space daemon)
- *     o IrNET daemon (irnetd) to automatically handle incoming requests
- *     o Windows 2000 compatibility (tested, but need more work)
- * Currently missing :
- *     o Lot's of testing (that's your job)
- *     o Connection retries (may be too hard to do)
- *     o Check pppd persist mode
- *     o User space daemon (to automatically handle incoming requests)
- *
- * The setup is not currently the most easy, but this should get much
- * better when everything will get integrated...
- *
- * Acknowledgements
- * ----------------
- * This module is based on :
- *     o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras
- *     o The IrLAN protocol (irlan_common/XXX) by Dag Brattli
- *     o The IrSock interface (af_irda) by Dag Brattli
- *     o Some other bits from the kernel and my drivers...
- * Infinite thanks to those brave souls for providing the infrastructure
- * upon which IrNET is built.
- *
- * Thanks to all my colleagues in HP for helping me. In particular,
- * thanks to Salil Pradhan and Bill Serra for W2k testing...
- * Thanks to Luiz Magalhaes for irnetd and much testing...
- *
- * Thanks to Alan Cox for answering lot's of my stupid questions, and
- * to Paul Mackerras answering my questions on how to best integrate
- * IrNET and pppd.
- *
- * Jean II
- *
- * Note on some implementations choices...
- * ------------------------------------
- *     1) Direct interface vs tty/socket
- * I could have used a tty interface to hook to ppp and use the full
- * socket API to connect to IrDA. The code would have been easier to
- * maintain, and maybe the code would have been smaller...
- * Instead, we hook directly to ppp_generic and to IrTTP, which make
- * things more complicated...
- *
- * The first reason is flexibility : this allow us to create IrNET
- * instances on demand (no /dev/ircommX crap) and to allow linkname
- * specification on pppd command line...
- *
- * Second reason is speed optimisation. If you look closely at the
- * transmit and receive paths, you will notice that they are "super lean"
- * (that's why they look ugly), with no function calls and as little data
- * copy and modification as I could...
- *
- *     2) irnetd in user space
- * irnetd is implemented in user space, which is necessary to call pppd.
- * This also give maximum benefits in term of flexibility and customability,
- * and allow to offer the event channel, useful for other stuff like debug.
- *
- * On the other hand, this require a loose coordination between the
- * present module and irnetd. One critical area is how incoming request
- * are handled.
- * When irnet receive an incoming request, it send an event to irnetd and
- * drop the incoming IrNET socket.
- * irnetd start a pppd instance, which create a new IrNET socket. This new
- * socket is then connected in the originating node to the pppd instance.
- * At this point, in the originating node, the first socket is closed.
- *
- * I admit, this is a bit messy and waste some resources. The alternative
- * is caching incoming socket, and that's also quite messy and waste
- * resources.
- * We also make connection time slower. For example, on a 115 kb/s link it
- * adds 60ms to the connection time (770 ms). However, this is slower than
- * the time it takes to fire up pppd on my P133...
- *
- *
- * History :
- * -------
- *
- * v1 - 15.5.00 - Jean II
- *     o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint)
- *     o control channel on /dev/irnet (set name/address)
- *     o event channel on /dev/irnet (for user space daemon)
- *
- * v2 - 5.6.00 - Jean II
- *     o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness...
- *     o Add DISCONNECT_TO event and rename DISCONNECT_FROM.
- *     o Set official device number alloaction on /dev/irnet
- *
- * v3 - 30.8.00 - Jean II
- *     o Update to latest Linux-IrDA changes :
- *             - queue_t => irda_queue_t
- *     o Update to ppp-2.4.0 :
- *             - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD
- *     o Add EXPIRE event (depend on new IrDA-Linux patch)
- *     o Switch from `hashbin_remove' to `hashbin_remove_this' to fix
- *       a multilink bug... (depend on new IrDA-Linux patch)
- *     o fix a self->daddr to self->raddr in irda_irnet_connect to fix
- *       another multilink bug (darn !)
- *     o Remove LINKNAME_IOCTL cruft
- *
- * v3b - 31.8.00 - Jean II
- *     o Dump discovery log at event channel startup
- *
- * v4 - 28.9.00 - Jean II
- *     o Fix interaction between poll/select and dump discovery log
- *     o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch)
- *     o Add IRNET_NOANSWER_FROM event (mostly to help support)
- *     o Release flow control in disconnect_indication
- *     o Block packets while connecting (speed up connections)
- *
- * v5 - 11.01.01 - Jean II
- *     o Init self->max_header_size, just in case...
- *     o Set up ap->chan.hdrlen, to get zero copy on tx side working.
- *     o avoid tx->ttp->flow->ppp->tx->... loop, by checking flow state
- *             Thanks to Christian Gennerat for finding this bug !
- *     ---
- *     o Declare the proper MTU/MRU that we can support
- *             (but PPP doesn't read the MTU value :-()
- *     o Declare hashbin HB_NOLOCK instead of HB_LOCAL to avoid
- *             disabling and enabling irq twice
- *
- * v6 - 31.05.01 - Jean II
- *     o Print source address in Found, Discovery, Expiry & Request events
- *     o Print requested source address in /proc/net/irnet
- *     o Change control channel input. Allow multiple commands in one line.
- *     o Add saddr command to change ap->rsaddr (and use that in IrDA)
- *     ---
- *     o Make the IrDA connection procedure totally asynchronous.
- *       Heavy rewrite of the IAS query code and the whole connection
- *       procedure. Now, irnet_connect() no longer need to be called from
- *       a process context...
- *     o Enable IrDA connect retries in ppp_irnet_send(). The good thing
- *       is that IrDA connect retries are directly driven by PPP LCP
- *       retries (we retry for each LCP packet), so that everything
- *       is transparently controlled from pppd lcp-max-configure.
- *     o Add ttp_connect flag to prevent rentry on the connect procedure
- *     o Test and fixups to eliminate side effects of retries
- *
- * v7 - 22.08.01 - Jean II
- *     o Cleanup : Change "saddr = 0x0" to "saddr = DEV_ADDR_ANY"
- *     o Fix bug in BLOCK_WHEN_CONNECT introduced in v6 : due to the
- *       asynchronous IAS query, self->tsap is NULL when PPP send the
- *       first packet.  This was preventing "connect-delay 0" to work.
- *       Change the test in ppp_irnet_send() to self->ttp_connect.
- *
- * v8 - 1.11.01 - Jean II
- *     o Tighten the use of self->ttp_connect and self->ttp_open to
- *       prevent various race conditions.
- *     o Avoid leaking discovery log and skb
- *     o Replace "self" with "server" in irnet_connect_indication() to
- *       better detect cut'n'paste error ;-)
- *
- * v9 - 29.11.01 - Jean II
- *     o Fix event generation in disconnect indication that I broke in v8
- *       It was always generation "No-Answer" because I was testing ttp_open
- *       just after clearing it. *blush*.
- *     o Use newly created irttp_listen() to fix potential crash when LAP
- *       destroyed before irnet module removed.
- *
- * v10 - 4.3.2 - Jean II
- *     o When receiving a disconnect indication, don't reenable the
- *       PPP Tx queue, this will trigger a reconnect. Instead, close
- *       the channel, which will kill pppd...
- *
- * v11 - 20.3.02 - Jean II
- *     o Oops ! v10 fix disabled IrNET retries and passive behaviour.
- *       Better fix in irnet_disconnect_indication() :
- *       - if connected, kill pppd via hangup.
- *       - if not connected, reenable ppp Tx, which trigger IrNET retry.
- *
- * v12 - 10.4.02 - Jean II
- *     o Fix race condition in irnet_connect_indication().
- *       If the socket was already trying to connect, drop old connection
- *       and use new one only if acting as primary. See comments.
- *
- * v13 - 30.5.02 - Jean II
- *     o Update module init code
- *
- * v14 - 20.2.03 - Jean II
- *     o Add discovery hint bits in the control channel.
- *     o Remove obsolete MOD_INC/DEC_USE_COUNT in favor of .owner
- *
- * v15 - 7.4.03 - Jean II
- *     o Replace spin_lock_irqsave() with spin_lock_bh() so that we can
- *       use ppp_unit_number(). It's probably also better overall...
- *     o Disable call to ppp_unregister_channel(), because we can't do it.
- */
-
-/***************************** INCLUDES *****************************/
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/skbuff.h>
-#include <linux/tty.h>
-#include <linux/proc_fs.h>
-#include <linux/netdevice.h>
-#include <linux/poll.h>
-#include <linux/capability.h>
-#include <linux/ctype.h>       /* isspace() */
-#include <linux/string.h>      /* skip_spaces() */
-#include <linux/uaccess.h>
-#include <linux/init.h>
-
-#include <linux/ppp_defs.h>
-#include <linux/ppp-ioctl.h>
-#include <linux/ppp_channel.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irias_object.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/irttp.h>
-#include <net/irda/discovery.h>
-
-/***************************** OPTIONS *****************************/
-/*
- * Define or undefine to compile or not some optional part of the
- * IrNET driver...
- * Note : the present defaults make sense, play with that at your
- * own risk...
- */
-/* IrDA side of the business... */
-#define DISCOVERY_NOMASK       /* To enable W2k compatibility... */
-#define ADVERTISE_HINT         /* Advertise IrLAN hint bit */
-#define ALLOW_SIMULT_CONNECT   /* This seem to work, cross fingers... */
-#define DISCOVERY_EVENTS       /* Query the discovery log to post events */
-#define INITIAL_DISCOVERY      /* Dump current discovery log as events */
-#undef STREAM_COMPAT           /* Not needed - potentially messy */
-#undef CONNECT_INDIC_KICK      /* Might mess IrDA, not needed */
-#undef FAIL_SEND_DISCONNECT    /* Might mess IrDA, not needed */
-#undef PASS_CONNECT_PACKETS    /* Not needed ? Safe */
-#undef MISSING_PPP_API         /* Stuff I wish I could do */
-
-/* PPP side of the business */
-#define BLOCK_WHEN_CONNECT     /* Block packets when connecting */
-#define CONNECT_IN_SEND                /* Retry IrDA connection procedure */
-#undef FLUSH_TO_PPP            /* Not sure about this one, let's play safe */
-#undef SECURE_DEVIRNET         /* Bah... */
-
-/****************************** DEBUG ******************************/
-
-/*
- * This set of flags enable and disable all the various warning,
- * error and debug message of this driver.
- * Each section can be enabled and disabled independently
- */
-/* In the PPP part */
-#define DEBUG_CTRL_TRACE       0       /* Control channel */
-#define DEBUG_CTRL_INFO                0       /* various info */
-#define DEBUG_CTRL_ERROR       1       /* problems */
-#define DEBUG_FS_TRACE         0       /* filesystem callbacks */
-#define DEBUG_FS_INFO          0       /* various info */
-#define DEBUG_FS_ERROR         1       /* problems */
-#define DEBUG_PPP_TRACE                0       /* PPP related functions */
-#define DEBUG_PPP_INFO         0       /* various info */
-#define DEBUG_PPP_ERROR                1       /* problems */
-#define DEBUG_MODULE_TRACE     0       /* module insertion/removal */
-#define DEBUG_MODULE_ERROR     1       /* problems */
-
-/* In the IrDA part */
-#define DEBUG_IRDA_SR_TRACE    0       /* IRDA subroutines */
-#define DEBUG_IRDA_SR_INFO     0       /* various info */
-#define DEBUG_IRDA_SR_ERROR    1       /* problems */
-#define DEBUG_IRDA_SOCK_TRACE  0       /* IRDA main socket functions */
-#define DEBUG_IRDA_SOCK_INFO   0       /* various info */
-#define DEBUG_IRDA_SOCK_ERROR  1       /* problems */
-#define DEBUG_IRDA_SERV_TRACE  0       /* The IrNET server */
-#define DEBUG_IRDA_SERV_INFO   0       /* various info */
-#define DEBUG_IRDA_SERV_ERROR  1       /* problems */
-#define DEBUG_IRDA_TCB_TRACE   0       /* IRDA IrTTP callbacks */
-#define DEBUG_IRDA_CB_INFO     0       /* various info */
-#define DEBUG_IRDA_CB_ERROR    1       /* problems */
-#define DEBUG_IRDA_OCB_TRACE   0       /* IRDA other callbacks */
-#define DEBUG_IRDA_OCB_INFO    0       /* various info */
-#define DEBUG_IRDA_OCB_ERROR   1       /* problems */
-
-#define DEBUG_ASSERT           0       /* Verify all assertions */
-
-/*
- * These are the macros we are using to actually print the debug
- * statements. Don't look at it, it's ugly...
- *
- * One of the trick is that, as the DEBUG_XXX are constant, the
- * compiler will optimise away the if() in all cases.
- */
-/* All error messages (will show up in the normal logs) */
-#define DERROR(dbg, format, args...) \
-       {if(DEBUG_##dbg) \
-               printk(KERN_INFO "irnet: %s(): " format, __func__ , ##args);}
-
-/* Normal debug message (will show up in /var/log/debug) */
-#define DEBUG(dbg, format, args...) \
-       {if(DEBUG_##dbg) \
-               printk(KERN_DEBUG "irnet: %s(): " format, __func__ , ##args);}
-
-/* Entering a function (trace) */
-#define DENTER(dbg, format, args...) \
-       {if(DEBUG_##dbg) \
-               printk(KERN_DEBUG "irnet: -> %s" format, __func__ , ##args);}
-
-/* Entering and exiting a function in one go (trace) */
-#define DPASS(dbg, format, args...) \
-       {if(DEBUG_##dbg) \
-               printk(KERN_DEBUG "irnet: <>%s" format, __func__ , ##args);}
-
-/* Exiting a function (trace) */
-#define DEXIT(dbg, format, args...) \
-       {if(DEBUG_##dbg) \
-               printk(KERN_DEBUG "irnet: <-%s()" format, __func__ , ##args);}
-
-/* Exit a function with debug */
-#define DRETURN(ret, dbg, args...) \
-       {DEXIT(dbg, ": " args);\
-       return ret; }
-
-/* Exit a function on failed condition */
-#define DABORT(cond, ret, dbg, args...) \
-       {if(cond) {\
-               DERROR(dbg, args);\
-               return ret; }}
-
-/* Invalid assertion, print out an error and exit... */
-#define DASSERT(cond, ret, dbg, args...) \
-       {if((DEBUG_ASSERT) && !(cond)) {\
-               DERROR(dbg, "Invalid assertion: " args);\
-               return ret; }}
-
-/************************ CONSTANTS & MACROS ************************/
-
-/* Paranoia */
-#define IRNET_MAGIC    0xB00754
-
-/* Number of control events in the control channel buffer... */
-#define IRNET_MAX_EVENTS       8       /* Should be more than enough... */
-
-/****************************** TYPES ******************************/
-
-/*
- * This is the main structure where we store all the data pertaining to
- * one instance of irnet.
- * Note : in irnet functions, a pointer this structure is usually called
- * "ap" or "self". If the code is borrowed from the IrDA stack, it tend
- * to be called "self", and if it is borrowed from the PPP driver it is
- * "ap". Apart from that, it's exactly the same structure ;-)
- */
-typedef struct irnet_socket
-{
-  /* ------------------- Instance management ------------------- */
-  /* We manage a linked list of IrNET socket instances */
-  irda_queue_t         q;              /* Must be first - for hasbin */
-  int                  magic;          /* Paranoia */
-
-  /* --------------------- FileSystem part --------------------- */
-  /* "pppd" interact directly with us on a /dev/ file */
-  struct file *                file;           /* File descriptor of this instance */
-  /* TTY stuff - to keep "pppd" happy */
-  struct ktermios      termios;        /* Various tty flags */
-  /* Stuff for the control channel */
-  int                  event_index;    /* Last read in the event log */
-
-  /* ------------------------- PPP part ------------------------- */
-  /* We interface directly to the ppp_generic driver in the kernel */
-  int                  ppp_open;       /* registered with ppp_generic */
-  struct ppp_channel   chan;           /* Interface to generic ppp layer */
-
-  int                  mru;            /* Max size of PPP payload */
-  u32                  xaccm[8];       /* Asynchronous character map (just */
-  u32                  raccm;          /* to please pppd - dummy) */
-  unsigned int         flags;          /* PPP flags (compression, ...) */
-  unsigned int         rbits;          /* Unused receive flags ??? */
-  struct work_struct disconnect_work;   /* Process context disconnection */
-  /* ------------------------ IrTTP part ------------------------ */
-  /* We create a pseudo "socket" over the IrDA tranport */
-  unsigned long                ttp_open;       /* Set when IrTTP is ready */
-  unsigned long                ttp_connect;    /* Set when IrTTP is connecting */
-  struct tsap_cb *     tsap;           /* IrTTP instance (the connection) */
-
-  char                 rname[NICKNAME_MAX_LEN + 1];
-                                       /* IrDA nickname of destination */
-  __u32                        rdaddr;         /* Requested peer IrDA address */
-  __u32                        rsaddr;         /* Requested local IrDA address */
-  __u32                        daddr;          /* actual peer IrDA address */
-  __u32                        saddr;          /* my local IrDA address */
-  __u8                 dtsap_sel;      /* Remote TSAP selector */
-  __u8                 stsap_sel;      /* Local TSAP selector */
-
-  __u32                        max_sdu_size_rx;/* Socket parameters used for IrTTP */
-  __u32                        max_sdu_size_tx;
-  __u32                        max_data_size;
-  __u8                 max_header_size;
-  LOCAL_FLOW           tx_flow;        /* State of the Tx path in IrTTP */
-
-  /* ------------------- IrLMP and IrIAS part ------------------- */
-  /* Used for IrDA Discovery and socket name resolution */
-  void *               ckey;           /* IrLMP client handle */
-  __u16                        mask;           /* Hint bits mask (filter discov.)*/
-  int                  nslots;         /* Number of slots for discovery */
-
-  struct iriap_cb *    iriap;          /* Used to query remote IAS */
-  int                  errno;          /* status of the IAS query */
-
-  /* -------------------- Discovery log part -------------------- */
-  /* Used by initial discovery on the control channel
-   * and by irnet_discover_daddr_and_lsap_sel() */
-  struct irda_device_info *discoveries;        /* Copy of the discovery log */
-  int                  disco_index;    /* Last read in the discovery log */
-  int                  disco_number;   /* Size of the discovery log */
-
-  struct mutex         lock;
-
-} irnet_socket;
-
-/*
- * This is the various event that we will generate on the control channel
- */
-typedef enum irnet_event
-{
-  IRNET_DISCOVER,              /* New IrNET node discovered */
-  IRNET_EXPIRE,                        /* IrNET node expired */
-  IRNET_CONNECT_TO,            /* IrNET socket has connected to other node */
-  IRNET_CONNECT_FROM,          /* Other node has connected to IrNET socket */
-  IRNET_REQUEST_FROM,          /* Non satisfied connection request */
-  IRNET_NOANSWER_FROM,         /* Failed connection request */
-  IRNET_BLOCKED_LINK,          /* Link (IrLAP) is blocked for > 3s */
-  IRNET_DISCONNECT_FROM,       /* IrNET socket has disconnected */
-  IRNET_DISCONNECT_TO          /* Closing IrNET socket */
-} irnet_event;
-
-/*
- * This is the storage for an event and its arguments
- */
-typedef struct irnet_log
-{
-  irnet_event  event;
-  int          unit;
-  __u32                saddr;
-  __u32                daddr;
-  char         name[NICKNAME_MAX_LEN + 1];     /* 21 + 1 */
-  __u16_host_order hints;                      /* Discovery hint bits */
-} irnet_log;
-
-/*
- * This is the storage for all events and related stuff...
- */
-typedef struct irnet_ctrl_channel
-{
-  irnet_log    log[IRNET_MAX_EVENTS];  /* Event log */
-  int          index;          /* Current index in log */
-  spinlock_t   spinlock;       /* Serialize access to the event log */
-  wait_queue_head_t    rwait;  /* processes blocked on read (or poll) */
-} irnet_ctrl_channel;
-
-/**************************** PROTOTYPES ****************************/
-/*
- * Global functions of the IrNET module
- * Note : we list here also functions called from one file to the other.
- */
-
-/* -------------------------- IRDA PART -------------------------- */
-int irda_irnet_create(irnet_socket *); /* Initialise an IrNET socket */
-int irda_irnet_connect(irnet_socket *);        /* Try to connect over IrDA */
-void irda_irnet_destroy(irnet_socket *);       /* Teardown an IrNET socket */
-int irda_irnet_init(void);             /* Initialise IrDA part of IrNET */
-void irda_irnet_cleanup(void);         /* Teardown IrDA part of IrNET */
-
-/**************************** VARIABLES ****************************/
-
-/* Control channel stuff - allocated in irnet_irda.h */
-extern struct irnet_ctrl_channel       irnet_events;
-
-#endif /* IRNET_H */
diff --git a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c
deleted file mode 100644 (file)
index e390bce..0000000
+++ /dev/null
@@ -1,1885 +0,0 @@
-/*
- *     IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- *             Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file implement the IRDA interface of IrNET.
- * Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly,
- * and exchange frames with IrTTP.
- */
-
-#include "irnet_irda.h"                /* Private header */
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <asm/unaligned.h>
-
-/*
- * PPP disconnect work: we need to make sure we're in
- * process context when calling ppp_unregister_channel().
- */
-static void irnet_ppp_disconnect(struct work_struct *work)
-{
-       irnet_socket * self =
-               container_of(work, irnet_socket, disconnect_work);
-
-       if (self == NULL)
-               return;
-       /*
-        * If we were connected, cleanup & close the PPP
-        * channel, which will kill pppd (hangup) and the rest.
-        */
-       if (self->ppp_open && !self->ttp_open && !self->ttp_connect) {
-               ppp_unregister_channel(&self->chan);
-               self->ppp_open = 0;
-       }
-}
-
-/************************* CONTROL CHANNEL *************************/
-/*
- * When ppp is not active, /dev/irnet act as a control channel.
- * Writing allow to set up the IrDA destination of the IrNET channel,
- * and any application may be read events happening on IrNET...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Post an event to the control channel...
- * Put the event in the log, and then wait all process blocked on read
- * so they can read the log...
- */
-static void
-irnet_post_event(irnet_socket *        ap,
-                irnet_event    event,
-                __u32          saddr,
-                __u32          daddr,
-                char *         name,
-                __u16          hints)
-{
-  int                  index;          /* In the log */
-
-  DENTER(CTRL_TRACE, "(ap=0x%p, event=%d, daddr=%08x, name=``%s'')\n",
-        ap, event, daddr, name);
-
-  /* Protect this section via spinlock.
-   * Note : as we are the only event producer, we only need to exclude
-   * ourself when touching the log, which is nice and easy.
-   */
-  spin_lock_bh(&irnet_events.spinlock);
-
-  /* Copy the event in the log */
-  index = irnet_events.index;
-  irnet_events.log[index].event = event;
-  irnet_events.log[index].daddr = daddr;
-  irnet_events.log[index].saddr = saddr;
-  /* Try to copy IrDA nickname */
-  if(name)
-    strcpy(irnet_events.log[index].name, name);
-  else
-    irnet_events.log[index].name[0] = '\0';
-  /* Copy hints */
-  irnet_events.log[index].hints.word = hints;
-  /* Try to get ppp unit number */
-  if((ap != (irnet_socket *) NULL) && (ap->ppp_open))
-    irnet_events.log[index].unit = ppp_unit_number(&ap->chan);
-  else
-    irnet_events.log[index].unit = -1;
-
-  /* Increment the index
-   * Note that we increment the index only after the event is written,
-   * to make sure that the readers don't get garbage... */
-  irnet_events.index = (index + 1) % IRNET_MAX_EVENTS;
-
-  DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index);
-
-  /* Spin lock end */
-  spin_unlock_bh(&irnet_events.spinlock);
-
-  /* Now : wake up everybody waiting for events... */
-  wake_up_interruptible_all(&irnet_events.rwait);
-
-  DEXIT(CTRL_TRACE, "\n");
-}
-
-/************************* IRDA SUBROUTINES *************************/
-/*
- * These are a bunch of subroutines called from other functions
- * down there, mostly common code or to improve readability...
- *
- * Note : we duplicate quite heavily some routines of af_irda.c,
- * because our input structure (self) is quite different
- * (struct irnet instead of struct irda_sock), which make sharing
- * the same code impossible (at least, without templates).
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_open_tsap (self)
- *
- *    Open local Transport Service Access Point (TSAP)
- *
- * Create a IrTTP instance for us and set all the IrTTP callbacks.
- */
-static inline int
-irnet_open_tsap(irnet_socket * self)
-{
-  notify_t     notify;         /* Callback structure */
-
-  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
-  DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n");
-
-  /* Initialize IrTTP callbacks to be used by the IrDA stack */
-  irda_notify_init(&notify);
-  notify.connect_confirm       = irnet_connect_confirm;
-  notify.connect_indication    = irnet_connect_indication;
-  notify.disconnect_indication = irnet_disconnect_indication;
-  notify.data_indication       = irnet_data_indication;
-  /*notify.udata_indication    = NULL;*/
-  notify.flow_indication       = irnet_flow_indication;
-  notify.status_indication     = irnet_status_indication;
-  notify.instance              = self;
-  strlcpy(notify.name, IRNET_NOTIFY_NAME, sizeof(notify.name));
-
-  /* Open an IrTTP instance */
-  self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
-                              &notify);
-  DABORT(self->tsap == NULL, -ENOMEM,
-        IRDA_SR_ERROR, "Unable to allocate TSAP !\n");
-
-  /* Remember which TSAP selector we actually got */
-  self->stsap_sel = self->tsap->stsap_sel;
-
-  DEXIT(IRDA_SR_TRACE, " - tsap=0x%p, sel=0x%X\n",
-       self->tsap, self->stsap_sel);
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_ias_to_tsap (self, result, value)
- *
- *    Examine an IAS object and extract TSAP
- *
- * We do an IAP query to find the TSAP associated with the IrNET service.
- * When IrIAP pass us the result of the query, this function look at
- * the return values to check for failures and extract the TSAP if
- * possible.
- * Also deallocate value
- * The failure is in self->errno
- * Return TSAP or -1
- */
-static inline __u8
-irnet_ias_to_tsap(irnet_socket *       self,
-                 int                   result,
-                 struct ias_value *    value)
-{
-  __u8 dtsap_sel = 0;          /* TSAP we are looking for */
-
-  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
-  /* By default, no error */
-  self->errno = 0;
-
-  /* Check if request succeeded */
-  switch(result)
-    {
-      /* Standard errors : service not available */
-    case IAS_CLASS_UNKNOWN:
-    case IAS_ATTRIB_UNKNOWN:
-      DEBUG(IRDA_SR_INFO, "IAS object doesn't exist ! (%d)\n", result);
-      self->errno = -EADDRNOTAVAIL;
-      break;
-
-      /* Other errors, most likely IrDA stack failure */
-    default :
-      DEBUG(IRDA_SR_INFO, "IAS query failed ! (%d)\n", result);
-      self->errno = -EHOSTUNREACH;
-      break;
-
-      /* Success : we got what we wanted */
-    case IAS_SUCCESS:
-      break;
-    }
-
-  /* Check what was returned to us */
-  if(value != NULL)
-    {
-      /* What type of argument have we got ? */
-      switch(value->type)
-       {
-       case IAS_INTEGER:
-         DEBUG(IRDA_SR_INFO, "result=%d\n", value->t.integer);
-         if(value->t.integer != -1)
-           /* Get the remote TSAP selector */
-           dtsap_sel = value->t.integer;
-         else
-           self->errno = -EADDRNOTAVAIL;
-         break;
-       default:
-         self->errno = -EADDRNOTAVAIL;
-         DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", value->type);
-         break;
-       }
-
-      /* Cleanup */
-      irias_delete_value(value);
-    }
-  else /* value == NULL */
-    {
-      /* Nothing returned to us - usually result != SUCCESS */
-      if(!(self->errno))
-       {
-         DERROR(IRDA_SR_ERROR,
-                "IrDA bug : result == SUCCESS && value == NULL\n");
-         self->errno = -EHOSTUNREACH;
-       }
-    }
-  DEXIT(IRDA_SR_TRACE, "\n");
-
-  /* Return the TSAP */
-  return dtsap_sel;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_find_lsap_sel (self)
- *
- *    Try to lookup LSAP selector in remote LM-IAS
- *
- * Basically, we start a IAP query, and then go to sleep. When the query
- * return, irnet_getvalue_confirm will wake us up, and we can examine the
- * result of the query...
- * Note that in some case, the query fail even before we go to sleep,
- * creating some races...
- */
-static inline int
-irnet_find_lsap_sel(irnet_socket *     self)
-{
-  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
-  /* This should not happen */
-  DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n");
-
-  /* Create an IAP instance, will be closed in irnet_getvalue_confirm() */
-  self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
-                          irnet_getvalue_confirm);
-
-  /* Treat unexpected signals as disconnect */
-  self->errno = -EHOSTUNREACH;
-
-  /* Query remote LM-IAS */
-  iriap_getvaluebyclass_request(self->iriap, self->rsaddr, self->daddr,
-                               IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
-
-  /* The above request is non-blocking.
-   * After a while, IrDA will call us back in irnet_getvalue_confirm()
-   * We will then call irnet_ias_to_tsap() and finish the
-   * connection procedure */
-
-  DEXIT(IRDA_SR_TRACE, "\n");
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_connect_tsap (self)
- *
- *    Initialise the TTP socket and initiate TTP connection
- *
- */
-static inline int
-irnet_connect_tsap(irnet_socket *      self)
-{
-  int          err;
-
-  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
-  /* Open a local TSAP (an IrTTP instance) */
-  err = irnet_open_tsap(self);
-  if(err != 0)
-    {
-      clear_bit(0, &self->ttp_connect);
-      DERROR(IRDA_SR_ERROR, "connect aborted!\n");
-      return err;
-    }
-
-  /* Connect to remote device */
-  err = irttp_connect_request(self->tsap, self->dtsap_sel,
-                             self->rsaddr, self->daddr, NULL,
-                             self->max_sdu_size_rx, NULL);
-  if(err != 0)
-    {
-      clear_bit(0, &self->ttp_connect);
-      DERROR(IRDA_SR_ERROR, "connect aborted!\n");
-      return err;
-    }
-
-  /* The above call is non-blocking.
-   * After a while, the IrDA stack will either call us back in
-   * irnet_connect_confirm() or irnet_disconnect_indication()
-   * See you there ;-) */
-
-  DEXIT(IRDA_SR_TRACE, "\n");
-  return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_discover_next_daddr (self)
- *
- *    Query the IrNET TSAP of the next device in the log.
- *
- * Used in the TSAP discovery procedure.
- */
-static inline int
-irnet_discover_next_daddr(irnet_socket *       self)
-{
-  /* Close the last instance of IrIAP, and open a new one.
-   * We can't reuse the IrIAP instance in the IrIAP callback */
-  if(self->iriap)
-    {
-      iriap_close(self->iriap);
-      self->iriap = NULL;
-    }
-  /* Create a new IAP instance */
-  self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
-                          irnet_discovervalue_confirm);
-  if(self->iriap == NULL)
-    return -ENOMEM;
-
-  /* Next discovery - before the call to avoid races */
-  self->disco_index++;
-
-  /* Check if we have one more address to try */
-  if(self->disco_index < self->disco_number)
-    {
-      /* Query remote LM-IAS */
-      iriap_getvaluebyclass_request(self->iriap,
-                                   self->discoveries[self->disco_index].saddr,
-                                   self->discoveries[self->disco_index].daddr,
-                                   IRNET_SERVICE_NAME, IRNET_IAS_VALUE);
-      /* The above request is non-blocking.
-       * After a while, IrDA will call us back in irnet_discovervalue_confirm()
-       * We will then call irnet_ias_to_tsap() and come back here again... */
-      return 0;
-    }
-  else
-    return 1;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_discover_daddr_and_lsap_sel (self)
- *
- *    This try to find a device with the requested service.
- *
- * Initiate a TSAP discovery procedure.
- * It basically look into the discovery log. For each address in the list,
- * it queries the LM-IAS of the device to find if this device offer
- * the requested service.
- * If there is more than one node supporting the service, we complain
- * to the user (it should move devices around).
- * If we find one node which have the requested TSAP, we connect to it.
- *
- * This function just start the whole procedure. It request the discovery
- * log and submit the first IAS query.
- * The bulk of the job is handled in irnet_discovervalue_confirm()
- *
- * Note : this procedure fails if there is more than one device in range
- * on the same dongle, because IrLMP doesn't disconnect the LAP when the
- * last LSAP is closed. Moreover, we would need to wait the LAP
- * disconnection...
- */
-static inline int
-irnet_discover_daddr_and_lsap_sel(irnet_socket *       self)
-{
-  int  ret;
-
-  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
-  /* Ask lmp for the current discovery log */
-  self->discoveries = irlmp_get_discoveries(&self->disco_number, self->mask,
-                                           DISCOVERY_DEFAULT_SLOTS);
-
-  /* Check if the we got some results */
-  if(self->discoveries == NULL)
-    {
-      self->disco_number = -1;
-      clear_bit(0, &self->ttp_connect);
-      DRETURN(-ENETUNREACH, IRDA_SR_INFO, "No Cachelog...\n");
-    }
-  DEBUG(IRDA_SR_INFO, "Got the log (0x%p), size is %d\n",
-       self->discoveries, self->disco_number);
-
-  /* Start with the first discovery */
-  self->disco_index = -1;
-  self->daddr = DEV_ADDR_ANY;
-
-  /* This will fail if the log is empty - this is non-blocking */
-  ret = irnet_discover_next_daddr(self);
-  if(ret)
-    {
-      /* Close IAP */
-      if(self->iriap)
-       iriap_close(self->iriap);
-      self->iriap = NULL;
-
-      /* Cleanup our copy of the discovery log */
-      kfree(self->discoveries);
-      self->discoveries = NULL;
-
-      clear_bit(0, &self->ttp_connect);
-      DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
-    }
-
-  /* Follow me in irnet_discovervalue_confirm() */
-
-  DEXIT(IRDA_SR_TRACE, "\n");
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_dname_to_daddr (self)
- *
- *    Convert an IrDA nickname to a valid IrDA address
- *
- * It basically look into the discovery log until there is a match.
- */
-static inline int
-irnet_dname_to_daddr(irnet_socket *    self)
-{
-  struct irda_device_info *discoveries;        /* Copy of the discovery log */
-  int  number;                 /* Number of nodes in the log */
-  int  i;
-
-  DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self);
-
-  /* Ask lmp for the current discovery log */
-  discoveries = irlmp_get_discoveries(&number, 0xffff,
-                                     DISCOVERY_DEFAULT_SLOTS);
-  /* Check if the we got some results */
-  if(discoveries == NULL)
-    DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n");
-
-  /*
-   * Now, check all discovered devices (if any), and connect
-   * client only about the services that the client is
-   * interested in...
-   */
-  for(i = 0; i < number; i++)
-    {
-      /* Does the name match ? */
-      if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN))
-       {
-         /* Yes !!! Get it.. */
-         self->daddr = discoveries[i].daddr;
-         DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n",
-               self->rname, self->daddr);
-         kfree(discoveries);
-         DEXIT(IRDA_SR_TRACE, "\n");
-         return 0;
-       }
-    }
-  /* No luck ! */
-  DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname);
-  kfree(discoveries);
-  return -EADDRNOTAVAIL;
-}
-
-
-/************************* SOCKET ROUTINES *************************/
-/*
- * This are the main operations on IrNET sockets, basically to create
- * and destroy IrNET sockets. These are called from the PPP part...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Create a IrNET instance : just initialise some parameters...
- */
-int
-irda_irnet_create(irnet_socket *       self)
-{
-  DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
-
-  self->magic = IRNET_MAGIC;   /* Paranoia */
-
-  self->ttp_open = 0;          /* Prevent higher layer from accessing IrTTP */
-  self->ttp_connect = 0;       /* Not connecting yet */
-  self->rname[0] = '\0';       /* May be set via control channel */
-  self->rdaddr = DEV_ADDR_ANY; /* May be set via control channel */
-  self->rsaddr = DEV_ADDR_ANY; /* May be set via control channel */
-  self->daddr = DEV_ADDR_ANY;  /* Until we get connected */
-  self->saddr = DEV_ADDR_ANY;  /* Until we get connected */
-  self->max_sdu_size_rx = TTP_SAR_UNBOUND;
-
-  /* Register as a client with IrLMP */
-  self->ckey = irlmp_register_client(0, NULL, NULL, NULL);
-#ifdef DISCOVERY_NOMASK
-  self->mask = 0xffff;         /* For W2k compatibility */
-#else /* DISCOVERY_NOMASK */
-  self->mask = irlmp_service_to_hint(S_LAN);
-#endif /* DISCOVERY_NOMASK */
-  self->tx_flow = FLOW_START;  /* Flow control from IrTTP */
-
-  INIT_WORK(&self->disconnect_work, irnet_ppp_disconnect);
-
-  DEXIT(IRDA_SOCK_TRACE, "\n");
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Connect to the other side :
- *     o convert device name to an address
- *     o find the socket number (dlsap)
- *     o Establish the connection
- *
- * Note : We no longer mimic af_irda. The IAS query for finding the TSAP
- * is done asynchronously, like the TTP connection. This allow us to
- * call this function from any context (not only process).
- * The downside is that following what's happening in there is tricky
- * because it involve various functions all over the place...
- */
-int
-irda_irnet_connect(irnet_socket *      self)
-{
-  int          err;
-
-  DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
-
-  /* Check if we are already trying to connect.
-   * Because irda_irnet_connect() can be called directly by pppd plus
-   * packet retries in ppp_generic and connect may take time, plus we may
-   * race with irnet_connect_indication(), we need to be careful there... */
-  if(test_and_set_bit(0, &self->ttp_connect))
-    DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n");
-  if((self->iriap != NULL) || (self->tsap != NULL))
-    DERROR(IRDA_SOCK_ERROR, "Socket not cleaned up...\n");
-
-  /* Insert ourselves in the hashbin so that the IrNET server can find us.
-   * Notes : 4th arg is string of 32 char max and must be null terminated
-   *        When 4th arg is used (string), 3rd arg isn't (int)
-   *        Can't re-insert (MUST remove first) so check for that... */
-  if((irnet_server.running) && (self->q.q_next == NULL))
-    {
-      spin_lock_bh(&irnet_server.spinlock);
-      hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname);
-      spin_unlock_bh(&irnet_server.spinlock);
-      DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname);
-    }
-
-  /* If we don't have anything (no address, no name) */
-  if((self->rdaddr == DEV_ADDR_ANY) && (self->rname[0] == '\0'))
-    {
-      /* Try to find a suitable address */
-      if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0)
-       DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n");
-      /* In most cases, the call above is non-blocking */
-    }
-  else
-    {
-      /* If we have only the name (no address), try to get an address */
-      if(self->rdaddr == DEV_ADDR_ANY)
-       {
-         if((err = irnet_dname_to_daddr(self)) != 0)
-           DRETURN(err, IRDA_SOCK_INFO, "name connect failed!\n");
-       }
-      else
-       /* Use the requested destination address */
-       self->daddr = self->rdaddr;
-
-      /* Query remote LM-IAS to find LSAP selector */
-      irnet_find_lsap_sel(self);
-      /* The above call is non blocking */
-    }
-
-  /* At this point, we are waiting for the IrDA stack to call us back,
-   * or we have already failed.
-   * We will finish the connection procedure in irnet_connect_tsap().
-   */
-  DEXIT(IRDA_SOCK_TRACE, "\n");
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_irnet_destroy(self)
- *
- *    Destroy irnet instance
- *
- * Note : this need to be called from a process context.
- */
-void
-irda_irnet_destroy(irnet_socket *      self)
-{
-  DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self);
-  if(self == NULL)
-    return;
-
-  /* Remove ourselves from hashbin (if we are queued in hashbin)
-   * Note : `irnet_server.running' protect us from calls in hashbin_delete() */
-  if((irnet_server.running) && (self->q.q_next != NULL))
-    {
-      struct irnet_socket *    entry;
-      DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n");
-      spin_lock_bh(&irnet_server.spinlock);
-      entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self);
-      self->q.q_next = NULL;
-      spin_unlock_bh(&irnet_server.spinlock);
-      DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n");
-    }
-
-  /* If we were connected, post a message */
-  if(test_bit(0, &self->ttp_open))
-    {
-      /* Note : as the disconnect comes from ppp_generic, the unit number
-       * doesn't exist anymore when we post the event, so we need to pass
-       * NULL as the first arg... */
-      irnet_post_event(NULL, IRNET_DISCONNECT_TO,
-                      self->saddr, self->daddr, self->rname, 0);
-    }
-
-  /* Prevent various IrDA callbacks from messing up things
-   * Need to be first */
-  clear_bit(0, &self->ttp_connect);
-
-  /* Prevent higher layer from accessing IrTTP */
-  clear_bit(0, &self->ttp_open);
-
-  /* Unregister with IrLMP */
-  irlmp_unregister_client(self->ckey);
-
-  /* Unregister with LM-IAS */
-  if(self->iriap)
-    {
-      iriap_close(self->iriap);
-      self->iriap = NULL;
-    }
-
-  /* Cleanup eventual discoveries from connection attempt or control channel */
-  if(self->discoveries != NULL)
-    {
-      /* Cleanup our copy of the discovery log */
-      kfree(self->discoveries);
-      self->discoveries = NULL;
-    }
-
-  /* Close our IrTTP connection */
-  if(self->tsap)
-    {
-      DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n");
-      irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
-      irttp_close_tsap(self->tsap);
-      self->tsap = NULL;
-    }
-  self->stsap_sel = 0;
-
-  DEXIT(IRDA_SOCK_TRACE, "\n");
-}
-
-
-/************************** SERVER SOCKET **************************/
-/*
- * The IrNET service is composed of one server socket and a variable
- * number of regular IrNET sockets. The server socket is supposed to
- * handle incoming connections and redirect them to one IrNET sockets.
- * It's a superset of the regular IrNET socket, but has a very distinct
- * behaviour...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_daddr_to_dname (self)
- *
- *    Convert an IrDA address to a IrDA nickname
- *
- * It basically look into the discovery log until there is a match.
- */
-static inline int
-irnet_daddr_to_dname(irnet_socket *    self)
-{
-  struct irda_device_info *discoveries;        /* Copy of the discovery log */
-  int  number;                 /* Number of nodes in the log */
-  int  i;
-
-  DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
-
-  /* Ask lmp for the current discovery log */
-  discoveries = irlmp_get_discoveries(&number, 0xffff,
-                                     DISCOVERY_DEFAULT_SLOTS);
-  /* Check if the we got some results */
-  if (discoveries == NULL)
-    DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n");
-
-  /* Now, check all discovered devices (if any) */
-  for(i = 0; i < number; i++)
-    {
-      /* Does the name match ? */
-      if(discoveries[i].daddr == self->daddr)
-       {
-         /* Yes !!! Get it.. */
-         strlcpy(self->rname, discoveries[i].info, sizeof(self->rname));
-         self->rname[sizeof(self->rname) - 1] = '\0';
-         DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n",
-               self->daddr, self->rname);
-         kfree(discoveries);
-         DEXIT(IRDA_SERV_TRACE, "\n");
-         return 0;
-       }
-    }
-  /* No luck ! */
-  DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr);
-  kfree(discoveries);
-  return -EADDRNOTAVAIL;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_find_socket (self)
- *
- *    Find the correct IrNET socket
- *
- * Look into the list of IrNET sockets and finds one with the right
- * properties...
- */
-static inline irnet_socket *
-irnet_find_socket(irnet_socket *       self)
-{
-  irnet_socket *       new = (irnet_socket *) NULL;
-  int                  err;
-
-  DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
-
-  /* Get the addresses of the requester */
-  self->daddr = irttp_get_daddr(self->tsap);
-  self->saddr = irttp_get_saddr(self->tsap);
-
-  /* Try to get the IrDA nickname of the requester */
-  err = irnet_daddr_to_dname(self);
-
-  /* Protect access to the instance list */
-  spin_lock_bh(&irnet_server.spinlock);
-
-  /* So now, try to get an socket having specifically
-   * requested that nickname */
-  if(err == 0)
-    {
-      new = (irnet_socket *) hashbin_find(irnet_server.list,
-                                         0, self->rname);
-      if(new)
-       DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches rname ``%s''.\n",
-             new, new->rname);
-    }
-
-  /* If no name matches, try to find an socket by the destination address */
-  /* It can be either the requested destination address (set via the
-   * control channel), or the current destination address if the
-   * socket is in the middle of a connection request */
-  if(new == (irnet_socket *) NULL)
-    {
-      new = (irnet_socket *) hashbin_get_first(irnet_server.list);
-      while(new !=(irnet_socket *) NULL)
-       {
-         /* Does it have the same address ? */
-         if((new->rdaddr == self->daddr) || (new->daddr == self->daddr))
-           {
-             /* Yes !!! Get it.. */
-             DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches daddr %#08x.\n",
-                   new, self->daddr);
-             break;
-           }
-         new = (irnet_socket *) hashbin_get_next(irnet_server.list);
-       }
-    }
-
-  /* If we don't have any socket, get the first unconnected socket */
-  if(new == (irnet_socket *) NULL)
-    {
-      new = (irnet_socket *) hashbin_get_first(irnet_server.list);
-      while(new !=(irnet_socket *) NULL)
-       {
-         /* Is it available ? */
-         if(!(test_bit(0, &new->ttp_open)) && (new->rdaddr == DEV_ADDR_ANY) &&
-            (new->rname[0] == '\0') && (new->ppp_open))
-           {
-             /* Yes !!! Get it.. */
-             DEBUG(IRDA_SERV_INFO, "Socket 0x%p is free.\n",
-                   new);
-             break;
-           }
-         new = (irnet_socket *) hashbin_get_next(irnet_server.list);
-       }
-    }
-
-  /* Spin lock end */
-  spin_unlock_bh(&irnet_server.spinlock);
-
-  DEXIT(IRDA_SERV_TRACE, " - new = 0x%p\n", new);
-  return new;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_connect_socket (self)
- *
- *    Connect an incoming connection to the socket
- *
- */
-static inline int
-irnet_connect_socket(irnet_socket *    server,
-                    irnet_socket *     new,
-                    struct qos_info *  qos,
-                    __u32              max_sdu_size,
-                    __u8               max_header_size)
-{
-  DENTER(IRDA_SERV_TRACE, "(server=0x%p, new=0x%p)\n",
-        server, new);
-
-  /* Now attach up the new socket */
-  new->tsap = irttp_dup(server->tsap, new);
-  DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n");
-
-  /* Set up all the relevant parameters on the new socket */
-  new->stsap_sel = new->tsap->stsap_sel;
-  new->dtsap_sel = new->tsap->dtsap_sel;
-  new->saddr = irttp_get_saddr(new->tsap);
-  new->daddr = irttp_get_daddr(new->tsap);
-
-  new->max_header_size = max_header_size;
-  new->max_sdu_size_tx = max_sdu_size;
-  new->max_data_size   = max_sdu_size;
-#ifdef STREAM_COMPAT
-  /* If we want to receive "stream sockets" */
-  if(max_sdu_size == 0)
-    new->max_data_size = irttp_get_max_seg_size(new->tsap);
-#endif /* STREAM_COMPAT */
-
-  /* Clean up the original one to keep it in listen state */
-  irttp_listen(server->tsap);
-
-  /* Send a connection response on the new socket */
-  irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL);
-
-  /* Allow PPP to send its junk over the new socket... */
-  set_bit(0, &new->ttp_open);
-
-  /* Not connecting anymore, and clean up last possible remains
-   * of connection attempts on the socket */
-  clear_bit(0, &new->ttp_connect);
-  if(new->iriap)
-    {
-      iriap_close(new->iriap);
-      new->iriap = NULL;
-    }
-  if(new->discoveries != NULL)
-    {
-      kfree(new->discoveries);
-      new->discoveries = NULL;
-    }
-
-#ifdef CONNECT_INDIC_KICK
-  /* As currently we don't block packets in ppp_irnet_send() while passive,
-   * this is not really needed...
-   * Also, not doing it give IrDA a chance to finish the setup properly
-   * before being swamped with packets... */
-  ppp_output_wakeup(&new->chan);
-#endif /* CONNECT_INDIC_KICK */
-
-  /* Notify the control channel */
-  irnet_post_event(new, IRNET_CONNECT_FROM,
-                  new->saddr, new->daddr, server->rname, 0);
-
-  DEXIT(IRDA_SERV_TRACE, "\n");
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_disconnect_server (self)
- *
- *    Cleanup the server socket when the incoming connection abort
- *
- */
-static inline void
-irnet_disconnect_server(irnet_socket * self,
-                       struct sk_buff *skb)
-{
-  DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self);
-
-  /* Put the received packet in the black hole */
-  kfree_skb(skb);
-
-#ifdef FAIL_SEND_DISCONNECT
-  /* Tell the other party we don't want to be connected */
-  /* Hum... Is it the right thing to do ? And do we need to send
-   * a connect response before ? It looks ok without this... */
-  irttp_disconnect_request(self->tsap, NULL, P_NORMAL);
-#endif /* FAIL_SEND_DISCONNECT */
-
-  /* Notify the control channel (see irnet_find_socket()) */
-  irnet_post_event(NULL, IRNET_REQUEST_FROM,
-                  self->saddr, self->daddr, self->rname, 0);
-
-  /* Clean up the server to keep it in listen state */
-  irttp_listen(self->tsap);
-
-  DEXIT(IRDA_SERV_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_setup_server (self)
- *
- *    Create a IrTTP server and set it up...
- *
- * Register the IrLAN hint bit, create a IrTTP instance for us,
- * set all the IrTTP callbacks and create an IrIAS entry...
- */
-static inline int
-irnet_setup_server(void)
-{
-  __u16                hints;
-
-  DENTER(IRDA_SERV_TRACE, "()\n");
-
-  /* Initialise the regular socket part of the server */
-  irda_irnet_create(&irnet_server.s);
-
-  /* Open a local TSAP (an IrTTP instance) for the server */
-  irnet_open_tsap(&irnet_server.s);
-
-  /* PPP part setup */
-  irnet_server.s.ppp_open = 0;
-  irnet_server.s.chan.private = NULL;
-  irnet_server.s.file = NULL;
-
-  /* Get the hint bit corresponding to IrLAN */
-  /* Note : we overload the IrLAN hint bit. As it is only a "hint", and as
-   * we provide roughly the same functionality as IrLAN, this is ok.
-   * In fact, the situation is similar as JetSend overloading the Obex hint
-   */
-  hints = irlmp_service_to_hint(S_LAN);
-
-#ifdef ADVERTISE_HINT
-  /* Register with IrLMP as a service (advertise our hint bit) */
-  irnet_server.skey = irlmp_register_service(hints);
-#endif /* ADVERTISE_HINT */
-
-  /* Register with LM-IAS (so that people can connect to us) */
-  irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies);
-  irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE,
-                          irnet_server.s.stsap_sel, IAS_KERNEL_ATTR);
-  irias_insert_object(irnet_server.ias_obj);
-
-#ifdef DISCOVERY_EVENTS
-  /* Tell IrLMP we want to be notified of newly discovered nodes */
-  irlmp_update_client(irnet_server.s.ckey, hints,
-                     irnet_discovery_indication, irnet_expiry_indication,
-                     (void *) &irnet_server.s);
-#endif
-
-  DEXIT(IRDA_SERV_TRACE, " - self=0x%p\n", &irnet_server.s);
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irda_destroy_server (self)
- *
- *    Destroy the IrTTP server...
- *
- * Reverse of the previous function...
- */
-static inline void
-irnet_destroy_server(void)
-{
-  DENTER(IRDA_SERV_TRACE, "()\n");
-
-#ifdef ADVERTISE_HINT
-  /* Unregister with IrLMP */
-  irlmp_unregister_service(irnet_server.skey);
-#endif /* ADVERTISE_HINT */
-
-  /* Unregister with LM-IAS */
-  if(irnet_server.ias_obj)
-    irias_delete_object(irnet_server.ias_obj);
-
-  /* Cleanup the socket part */
-  irda_irnet_destroy(&irnet_server.s);
-
-  DEXIT(IRDA_SERV_TRACE, "\n");
-}
-
-
-/************************ IRDA-TTP CALLBACKS ************************/
-/*
- * When we create a IrTTP instance, we pass to it a set of callbacks
- * that IrTTP will call in case of various events.
- * We take care of those events here.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_data_indication (instance, sap, skb)
- *
- *    Received some data from TinyTP. Just queue it on the receive queue
- *
- */
-static int
-irnet_data_indication(void *   instance,
-                     void *    sap,
-                     struct sk_buff *skb)
-{
-  irnet_socket *       ap = (irnet_socket *) instance;
-  unsigned char *      p;
-  int                  code = 0;
-
-  DENTER(IRDA_TCB_TRACE, "(self/ap=0x%p, skb=0x%p)\n",
-        ap, skb);
-  DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n");
-
-  /* Check is ppp is ready to receive our packet */
-  if(!ap->ppp_open)
-    {
-      DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n");
-      /* When we return error, TTP will need to requeue the skb and
-       * will stop the sender. IrTTP will stall until we send it a
-       * flow control request... */
-      return -ENOMEM;
-    }
-
-  /* strip address/control field if present */
-  p = skb->data;
-  if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI))
-    {
-      /* chop off address/control */
-      if(skb->len < 3)
-       goto err_exit;
-      p = skb_pull(skb, 2);
-    }
-
-  /* decompress protocol field if compressed */
-  if(p[0] & 1)
-    {
-      /* protocol is compressed */
-      *(u8 *)skb_push(skb, 1) = 0;
-    }
-  else
-    if(skb->len < 2)
-      goto err_exit;
-
-  /* pass to generic ppp layer */
-  /* Note : how do I know if ppp can accept or not the packet ? This is
-   * essential if I want to manage flow control smoothly... */
-  ppp_input(&ap->chan, skb);
-
-  DEXIT(IRDA_TCB_TRACE, "\n");
-  return 0;
-
- err_exit:
-  DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n");
-  kfree_skb(skb);
-  ppp_input_error(&ap->chan, code);
-  return 0;    /* Don't return an error code, only for flow control... */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_disconnect_indication (instance, sap, reason, skb)
- *
- *    Connection has been closed. Chech reason to find out why
- *
- * Note : there are many cases where we come here :
- *     o attempted to connect, timeout
- *     o connected, link is broken, LAP has timeout
- *     o connected, other side close the link
- *     o connection request on the server not handled
- */
-static void
-irnet_disconnect_indication(void *     instance,
-                           void *      sap,
-                           LM_REASON   reason,
-                           struct sk_buff *skb)
-{
-  irnet_socket *       self = (irnet_socket *) instance;
-  int                  test_open;
-  int                  test_connect;
-
-  DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
-  DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
-
-  /* Don't care about it, but let's not leak it */
-  if(skb)
-    dev_kfree_skb(skb);
-
-  /* Prevent higher layer from accessing IrTTP */
-  test_open = test_and_clear_bit(0, &self->ttp_open);
-  /* Not connecting anymore...
-   * (note : TSAP is open, so IAP callbacks are no longer pending...) */
-  test_connect = test_and_clear_bit(0, &self->ttp_connect);
-
-  /* If both self->ttp_open and self->ttp_connect are NULL, it mean that we
-   * have a race condition with irda_irnet_destroy() or
-   * irnet_connect_indication(), so don't mess up tsap...
-   */
-  if(!(test_open || test_connect))
-    {
-      DERROR(IRDA_CB_ERROR, "Race condition detected...\n");
-      return;
-    }
-
-  /* If we were active, notify the control channel */
-  if(test_open)
-    irnet_post_event(self, IRNET_DISCONNECT_FROM,
-                    self->saddr, self->daddr, self->rname, 0);
-  else
-    /* If we were trying to connect, notify the control channel */
-    if((self->tsap) && (self != &irnet_server.s))
-      irnet_post_event(self, IRNET_NOANSWER_FROM,
-                      self->saddr, self->daddr, self->rname, 0);
-
-  /* Close our IrTTP connection, cleanup tsap */
-  if((self->tsap) && (self != &irnet_server.s))
-    {
-      DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n");
-      irttp_close_tsap(self->tsap);
-      self->tsap = NULL;
-    }
-  /* Cleanup the socket in case we want to reconnect in ppp_output_wakeup() */
-  self->stsap_sel = 0;
-  self->daddr = DEV_ADDR_ANY;
-  self->tx_flow = FLOW_START;
-
-  /* Deal with the ppp instance if it's still alive */
-  if(self->ppp_open)
-    {
-      if(test_open)
-       {
-         /* ppp_unregister_channel() wants a user context. */
-         schedule_work(&self->disconnect_work);
-       }
-      else
-       {
-         /* If we were trying to connect, flush (drain) ppp_generic
-          * Tx queue (most often we have blocked it), which will
-          * trigger an other attempt to connect. If we are passive,
-          * this will empty the Tx queue after last try. */
-         ppp_output_wakeup(&self->chan);
-       }
-    }
-
-  DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb)
- *
- *    Connections has been confirmed by the remote device
- *
- */
-static void
-irnet_connect_confirm(void *   instance,
-                     void *    sap,
-                     struct qos_info *qos,
-                     __u32     max_sdu_size,
-                     __u8      max_header_size,
-                     struct sk_buff *skb)
-{
-  irnet_socket *       self = (irnet_socket *) instance;
-
-  DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
-
-  /* Check if socket is closing down (via irda_irnet_destroy()) */
-  if(! test_bit(0, &self->ttp_connect))
-    {
-      DERROR(IRDA_CB_ERROR, "Socket no longer connecting. Ouch !\n");
-      return;
-    }
-
-  /* How much header space do we need to reserve */
-  self->max_header_size = max_header_size;
-
-  /* IrTTP max SDU size in transmit direction */
-  self->max_sdu_size_tx = max_sdu_size;
-  self->max_data_size = max_sdu_size;
-#ifdef STREAM_COMPAT
-  if(max_sdu_size == 0)
-    self->max_data_size = irttp_get_max_seg_size(self->tsap);
-#endif /* STREAM_COMPAT */
-
-  /* At this point, IrLMP has assigned our source address */
-  self->saddr = irttp_get_saddr(self->tsap);
-
-  /* Allow higher layer to access IrTTP */
-  set_bit(0, &self->ttp_open);
-  clear_bit(0, &self->ttp_connect);    /* Not racy, IrDA traffic is serial */
-  /* Give a kick in the ass of ppp_generic so that he sends us some data */
-  ppp_output_wakeup(&self->chan);
-
-  /* Check size of received packet */
-  if(skb->len > 0)
-    {
-#ifdef PASS_CONNECT_PACKETS
-      DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
-      /* Try to pass it to PPP */
-      irnet_data_indication(instance, sap, skb);
-#else /* PASS_CONNECT_PACKETS */
-      DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
-      kfree_skb(skb);  /* Note : will be optimised with other kfree... */
-#endif /* PASS_CONNECT_PACKETS */
-    }
-  else
-    kfree_skb(skb);
-
-  /* Notify the control channel */
-  irnet_post_event(self, IRNET_CONNECT_TO,
-                  self->saddr, self->daddr, self->rname, 0);
-
-  DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_flow_indication (instance, sap, flow)
- *
- *    Used by TinyTP to tell us if it can accept more data or not
- *
- */
-static void
-irnet_flow_indication(void *   instance,
-                     void *    sap,
-                     LOCAL_FLOW flow)
-{
-  irnet_socket *       self = (irnet_socket *) instance;
-  LOCAL_FLOW           oldflow = self->tx_flow;
-
-  DENTER(IRDA_TCB_TRACE, "(self=0x%p, flow=%d)\n", self, flow);
-
-  /* Update our state */
-  self->tx_flow = flow;
-
-  /* Check what IrTTP want us to do... */
-  switch(flow)
-    {
-    case FLOW_START:
-      DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n");
-      /* Check if we really need to wake up PPP */
-      if(oldflow == FLOW_STOP)
-       ppp_output_wakeup(&self->chan);
-      else
-       DEBUG(IRDA_CB_INFO, "But we were already transmitting !!!\n");
-      break;
-    case FLOW_STOP:
-      DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n");
-      break;
-    default:
-      DEBUG(IRDA_CB_INFO, "Unknown flow command!\n");
-      break;
-    }
-
-  DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_status_indication (instance, sap, reason, skb)
- *
- *    Link (IrLAP) status report.
- *
- */
-static void
-irnet_status_indication(void * instance,
-                       LINK_STATUS link,
-                       LOCK_STATUS lock)
-{
-  irnet_socket *       self = (irnet_socket *) instance;
-
-  DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self);
-  DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n");
-
-  /* We can only get this event if we are connected */
-  switch(link)
-    {
-    case STATUS_NO_ACTIVITY:
-      irnet_post_event(self, IRNET_BLOCKED_LINK,
-                      self->saddr, self->daddr, self->rname, 0);
-      break;
-    default:
-      DEBUG(IRDA_CB_INFO, "Unknown status...\n");
-    }
-
-  DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata)
- *
- *    Incoming connection
- *
- * In theory, this function is called only on the server socket.
- * Some other node is attempting to connect to the IrNET service, and has
- * sent a connection request on our server socket.
- * We just redirect the connection to the relevant IrNET socket.
- *
- * Note : we also make sure that between 2 irnet nodes, there can
- * exist only one irnet connection.
- */
-static void
-irnet_connect_indication(void *                instance,
-                        void *         sap,
-                        struct qos_info *qos,
-                        __u32          max_sdu_size,
-                        __u8           max_header_size,
-                        struct sk_buff *skb)
-{
-  irnet_socket *       server = &irnet_server.s;
-  irnet_socket *       new = (irnet_socket *) NULL;
-
-  DENTER(IRDA_TCB_TRACE, "(server=0x%p)\n", server);
-  DASSERT(instance == &irnet_server, , IRDA_CB_ERROR,
-         "Invalid instance (0x%p) !!!\n", instance);
-  DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n");
-
-  /* Try to find the most appropriate IrNET socket */
-  new = irnet_find_socket(server);
-
-  /* After all this hard work, do we have an socket ? */
-  if(new == (irnet_socket *) NULL)
-    {
-      DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n");
-      irnet_disconnect_server(server, skb);
-      return;
-    }
-
-  /* Is the socket already busy ? */
-  if(test_bit(0, &new->ttp_open))
-    {
-      DEXIT(IRDA_CB_INFO, ": Socket already connected.\n");
-      irnet_disconnect_server(server, skb);
-      return;
-    }
-
-  /* The following code is a bit tricky, so need comments ;-)
-   */
-  /* If ttp_connect is set, the socket is trying to connect to the other
-   * end and may have sent a IrTTP connection request and is waiting for
-   * a connection response (that may never come).
-   * Now, the pain is that the socket may have opened a tsap and is
-   * waiting on it, while the other end is trying to connect to it on
-   * another tsap.
-   * Because IrNET can be peer to peer, we need to workaround this.
-   * Furthermore, the way the irnetd script is implemented, the
-   * target will create a second IrNET connection back to the
-   * originator and expect the originator to bind this new connection
-   * to the original PPPD instance.
-   * And of course, if we don't use irnetd, we can have a race when
-   * both side try to connect simultaneously, which could leave both
-   * connections half closed (yuck).
-   * Conclusions :
-   *   1) The "originator" must accept the new connection and get rid
-   *      of the old one so that irnetd works
-   *   2) One side must deny the new connection to avoid races,
-   *      but both side must agree on which side it is...
-   * Most often, the originator is primary at the LAP layer.
-   * Jean II
-   */
-  /* Now, let's look at the way I wrote the test...
-   * We need to clear up the ttp_connect flag atomically to prevent
-   * irnet_disconnect_indication() to mess up the tsap we are going to close.
-   * We want to clear the ttp_connect flag only if we close the tsap,
-   * otherwise we will never close it, so we need to check for primary
-   * *before* doing the test on the flag.
-   * And of course, ALLOW_SIMULT_CONNECT can disable this entirely...
-   * Jean II
-   */
-
-  /* Socket already connecting ? On primary ? */
-  if(0
-#ifdef ALLOW_SIMULT_CONNECT
-     || ((irttp_is_primary(server->tsap) == 1) &&      /* primary */
-        (test_and_clear_bit(0, &new->ttp_connect)))
-#endif /* ALLOW_SIMULT_CONNECT */
-     )
-    {
-      DERROR(IRDA_CB_ERROR, "Socket already connecting, but going to reuse it !\n");
-
-      /* Cleanup the old TSAP if necessary - IrIAP will be cleaned up later */
-      if(new->tsap != NULL)
-       {
-         /* Close the old connection the new socket was attempting,
-          * so that we can hook it up to the new connection.
-          * It's now safe to do it... */
-         irttp_close_tsap(new->tsap);
-         new->tsap = NULL;
-       }
-    }
-  else
-    {
-      /* Three options :
-       * 1) socket was not connecting or connected : ttp_connect should be 0.
-       * 2) we don't want to connect the socket because we are secondary or
-       * ALLOW_SIMULT_CONNECT is undefined. ttp_connect should be 1.
-       * 3) we are half way in irnet_disconnect_indication(), and it's a
-       * nice race condition... Fortunately, we can detect that by checking
-       * if tsap is still alive. On the other hand, we can't be in
-       * irda_irnet_destroy() otherwise we would not have found this
-       * socket in the hashbin.
-       * Jean II */
-      if((test_bit(0, &new->ttp_connect)) || (new->tsap != NULL))
-       {
-         /* Don't mess this socket, somebody else in in charge... */
-         DERROR(IRDA_CB_ERROR, "Race condition detected, socket in use, abort connect...\n");
-         irnet_disconnect_server(server, skb);
-         return;
-       }
-    }
-
-  /* So : at this point, we have a socket, and it is idle. Good ! */
-  irnet_connect_socket(server, new, qos, max_sdu_size, max_header_size);
-
-  /* Check size of received packet */
-  if(skb->len > 0)
-    {
-#ifdef PASS_CONNECT_PACKETS
-      DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n");
-      /* Try to pass it to PPP */
-      irnet_data_indication(new, new->tsap, skb);
-#else /* PASS_CONNECT_PACKETS */
-      DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n");
-      kfree_skb(skb);  /* Note : will be optimised with other kfree... */
-#endif /* PASS_CONNECT_PACKETS */
-    }
-  else
-    kfree_skb(skb);
-
-  DEXIT(IRDA_TCB_TRACE, "\n");
-}
-
-
-/********************** IRDA-IAS/LMP CALLBACKS **********************/
-/*
- * These are the callbacks called by other layers of the IrDA stack,
- * mainly LMP for discovery and IAS for name queries.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_getvalue_confirm (result, obj_id, value, priv)
- *
- *    Got answer from remote LM-IAS, just connect
- *
- * This is the reply to a IAS query we were doing to find the TSAP of
- * the device we want to connect to.
- * If we have found a valid TSAP, just initiate the TTP connection
- * on this TSAP.
- */
-static void
-irnet_getvalue_confirm(int     result,
-                      __u16    obj_id,
-                      struct ias_value *value,
-                      void *   priv)
-{
-  irnet_socket *       self = (irnet_socket *) priv;
-
-  DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
-  DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n");
-
-  /* Check if already connected (via irnet_connect_socket())
-   * or socket is closing down (via irda_irnet_destroy()) */
-  if(! test_bit(0, &self->ttp_connect))
-    {
-      DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n");
-      return;
-    }
-
-  /* We probably don't need to make any more queries */
-  iriap_close(self->iriap);
-  self->iriap = NULL;
-
-  /* Post process the IAS reply */
-  self->dtsap_sel = irnet_ias_to_tsap(self, result, value);
-
-  /* If error, just go out */
-  if(self->errno)
-    {
-      clear_bit(0, &self->ttp_connect);
-      DERROR(IRDA_OCB_ERROR, "IAS connect failed ! (0x%X)\n", self->errno);
-      return;
-    }
-
-  DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
-       self->daddr, self->dtsap_sel);
-
-  /* Start up TTP - non blocking */
-  irnet_connect_tsap(self);
-
-  DEXIT(IRDA_OCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_discovervalue_confirm (result, obj_id, value, priv)
- *
- *    Handle the TSAP discovery procedure state machine.
- *    Got answer from remote LM-IAS, try next device
- *
- * We are doing a  TSAP discovery procedure, and we got an answer to
- * a IAS query we were doing to find the TSAP on one of the address
- * in the discovery log.
- *
- * If we have found a valid TSAP for the first time, save it. If it's
- * not the first time we found one, complain.
- *
- * If we have more addresses in the log, just initiate a new query.
- * Note that those query may fail (see irnet_discover_daddr_and_lsap_sel())
- *
- * Otherwise, wrap up the procedure (cleanup), check if we have found
- * any device and connect to it.
- */
-static void
-irnet_discovervalue_confirm(int                result,
-                           __u16       obj_id,
-                           struct ias_value *value,
-                           void *      priv)
-{
-  irnet_socket *       self = (irnet_socket *) priv;
-  __u8                 dtsap_sel;              /* TSAP we are looking for */
-
-  DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
-  DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n");
-
-  /* Check if already connected (via irnet_connect_socket())
-   * or socket is closing down (via irda_irnet_destroy()) */
-  if(! test_bit(0, &self->ttp_connect))
-    {
-      DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n");
-      return;
-    }
-
-  /* Post process the IAS reply */
-  dtsap_sel = irnet_ias_to_tsap(self, result, value);
-
-  /* Have we got something ? */
-  if(self->errno == 0)
-    {
-      /* We found the requested service */
-      if(self->daddr != DEV_ADDR_ANY)
-       {
-         DERROR(IRDA_OCB_ERROR, "More than one device in range supports IrNET...\n");
-       }
-      else
-       {
-         /* First time we found that one, save it ! */
-         self->daddr = self->discoveries[self->disco_index].daddr;
-         self->dtsap_sel = dtsap_sel;
-       }
-    }
-
-  /* If no failure */
-  if((self->errno == -EADDRNOTAVAIL) || (self->errno == 0))
-    {
-      int      ret;
-
-      /* Search the next node */
-      ret = irnet_discover_next_daddr(self);
-      if(!ret)
-       {
-         /* In this case, the above request was non-blocking.
-          * We will return here after a while... */
-         return;
-       }
-      /* In this case, we have processed the last discovery item */
-    }
-
-  /* No more queries to be done (failure or last one) */
-
-  /* We probably don't need to make any more queries */
-  iriap_close(self->iriap);
-  self->iriap = NULL;
-
-  /* No more items : remove the log and signal termination */
-  DEBUG(IRDA_OCB_INFO, "Cleaning up log (0x%p)\n",
-       self->discoveries);
-  if(self->discoveries != NULL)
-    {
-      /* Cleanup our copy of the discovery log */
-      kfree(self->discoveries);
-      self->discoveries = NULL;
-    }
-  self->disco_number = -1;
-
-  /* Check out what we found */
-  if(self->daddr == DEV_ADDR_ANY)
-    {
-      self->daddr = DEV_ADDR_ANY;
-      clear_bit(0, &self->ttp_connect);
-      DEXIT(IRDA_OCB_TRACE, ": cannot discover IrNET in any device !!!\n");
-      return;
-    }
-
-  /* We have a valid address - just connect */
-
-  DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n",
-       self->daddr, self->dtsap_sel);
-
-  /* Start up TTP - non blocking */
-  irnet_connect_tsap(self);
-
-  DEXIT(IRDA_OCB_TRACE, "\n");
-}
-
-#ifdef DISCOVERY_EVENTS
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_discovery_indication (discovery)
- *
- *    Got a discovery indication from IrLMP, post an event
- *
- * Note : IrLMP take care of matching the hint mask for us, and also
- * check if it is a "new" node for us...
- *
- * As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET
- * nodes, so it's only at connection time that we will know if the
- * node support IrNET, IrLAN or both. The other solution is to check
- * in IAS the PNP ids and service name.
- * Note : even if a node support IrNET (or IrLAN), it's no guarantee
- * that we will be able to connect to it, the node might already be
- * busy...
- *
- * One last thing : in some case, this function will trigger duplicate
- * discovery events. On the other hand, we should catch all
- * discoveries properly (i.e. not miss one). Filtering duplicate here
- * is to messy, so we leave that to user space...
- */
-static void
-irnet_discovery_indication(discinfo_t *                discovery,
-                          DISCOVERY_MODE       mode,
-                          void *               priv)
-{
-  irnet_socket *       self = &irnet_server.s;
-
-  DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
-  DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR,
-         "Invalid instance (0x%p) !!!\n", priv);
-
-  DEBUG(IRDA_OCB_INFO, "Discovered new IrNET/IrLAN node %s...\n",
-       discovery->info);
-
-  /* Notify the control channel */
-  irnet_post_event(NULL, IRNET_DISCOVER,
-                  discovery->saddr, discovery->daddr, discovery->info,
-                  get_unaligned((__u16 *)discovery->hints));
-
-  DEXIT(IRDA_OCB_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_expiry_indication (expiry)
- *
- *    Got a expiry indication from IrLMP, post an event
- *
- * Note : IrLMP take care of matching the hint mask for us, we only
- * check if it is a "new" node...
- */
-static void
-irnet_expiry_indication(discinfo_t *   expiry,
-                       DISCOVERY_MODE  mode,
-                       void *          priv)
-{
-  irnet_socket *       self = &irnet_server.s;
-
-  DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self);
-  DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR,
-         "Invalid instance (0x%p) !!!\n", priv);
-
-  DEBUG(IRDA_OCB_INFO, "IrNET/IrLAN node %s expired...\n",
-       expiry->info);
-
-  /* Notify the control channel */
-  irnet_post_event(NULL, IRNET_EXPIRE,
-                  expiry->saddr, expiry->daddr, expiry->info,
-                  get_unaligned((__u16 *)expiry->hints));
-
-  DEXIT(IRDA_OCB_TRACE, "\n");
-}
-#endif /* DISCOVERY_EVENTS */
-
-
-/*********************** PROC ENTRY CALLBACKS ***********************/
-/*
- * We create a instance in the /proc filesystem, and here we take care
- * of that...
- */
-
-#ifdef CONFIG_PROC_FS
-static int
-irnet_proc_show(struct seq_file *m, void *v)
-{
-  irnet_socket *       self;
-  char *               state;
-  int                  i = 0;
-
-  /* Get the IrNET server information... */
-  seq_printf(m, "IrNET server - ");
-  seq_printf(m, "IrDA state: %s, ",
-                (irnet_server.running ? "running" : "dead"));
-  seq_printf(m, "stsap_sel: %02x, ", irnet_server.s.stsap_sel);
-  seq_printf(m, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel);
-
-  /* Do we need to continue ? */
-  if(!irnet_server.running)
-    return 0;
-
-  /* Protect access to the instance list */
-  spin_lock_bh(&irnet_server.spinlock);
-
-  /* Get the sockets one by one... */
-  self = (irnet_socket *) hashbin_get_first(irnet_server.list);
-  while(self != NULL)
-    {
-      /* Start printing info about the socket. */
-      seq_printf(m, "\nIrNET socket %d - ", i++);
-
-      /* First, get the requested configuration */
-      seq_printf(m, "Requested IrDA name: \"%s\", ", self->rname);
-      seq_printf(m, "daddr: %08x, ", self->rdaddr);
-      seq_printf(m, "saddr: %08x\n", self->rsaddr);
-
-      /* Second, get all the PPP info */
-      seq_printf(m, "  PPP state: %s",
-                (self->ppp_open ? "registered" : "unregistered"));
-      if(self->ppp_open)
-       {
-         seq_printf(m, ", unit: ppp%d",
-                        ppp_unit_number(&self->chan));
-         seq_printf(m, ", channel: %d",
-                        ppp_channel_index(&self->chan));
-         seq_printf(m, ", mru: %d",
-                        self->mru);
-         /* Maybe add self->flags ? Later... */
-       }
-
-      /* Then, get all the IrDA specific info... */
-      if(self->ttp_open)
-       state = "connected";
-      else
-       if(self->tsap != NULL)
-         state = "connecting";
-       else
-         if(self->iriap != NULL)
-           state = "searching";
-         else
-           if(self->ttp_connect)
-             state = "weird";
-           else
-             state = "idle";
-      seq_printf(m, "\n        IrDA state: %s, ", state);
-      seq_printf(m, "daddr: %08x, ", self->daddr);
-      seq_printf(m, "stsap_sel: %02x, ", self->stsap_sel);
-      seq_printf(m, "dtsap_sel: %02x\n", self->dtsap_sel);
-
-      /* Next socket, please... */
-      self = (irnet_socket *) hashbin_get_next(irnet_server.list);
-    }
-
-  /* Spin lock end */
-  spin_unlock_bh(&irnet_server.spinlock);
-
-  return 0;
-}
-
-static int irnet_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, irnet_proc_show, NULL);
-}
-
-static const struct file_operations irnet_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = irnet_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-#endif /* PROC_FS */
-
-
-/********************** CONFIGURATION/CLEANUP **********************/
-/*
- * Initialisation and teardown of the IrDA part, called at module
- * insertion and removal...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare the IrNET layer for operation...
- */
-int __init
-irda_irnet_init(void)
-{
-  int          err = 0;
-
-  DENTER(MODULE_TRACE, "()\n");
-
-  /* Pure paranoia - should be redundant */
-  memset(&irnet_server, 0, sizeof(struct irnet_root));
-
-  /* Setup start of irnet instance list */
-  irnet_server.list = hashbin_new(HB_NOLOCK);
-  DABORT(irnet_server.list == NULL, -ENOMEM,
-        MODULE_ERROR, "Can't allocate hashbin!\n");
-  /* Init spinlock for instance list */
-  spin_lock_init(&irnet_server.spinlock);
-
-  /* Initialise control channel */
-  init_waitqueue_head(&irnet_events.rwait);
-  irnet_events.index = 0;
-  /* Init spinlock for event logging */
-  spin_lock_init(&irnet_events.spinlock);
-
-#ifdef CONFIG_PROC_FS
-  /* Add a /proc file for irnet infos */
-  proc_create("irnet", 0, proc_irda, &irnet_proc_fops);
-#endif /* CONFIG_PROC_FS */
-
-  /* Setup the IrNET server */
-  err = irnet_setup_server();
-
-  if(!err)
-    /* We are no longer functional... */
-    irnet_server.running = 1;
-
-  DEXIT(MODULE_TRACE, "\n");
-  return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Cleanup at exit...
- */
-void __exit
-irda_irnet_cleanup(void)
-{
-  DENTER(MODULE_TRACE, "()\n");
-
-  /* We are no longer there... */
-  irnet_server.running = 0;
-
-#ifdef CONFIG_PROC_FS
-  /* Remove our /proc file */
-  remove_proc_entry("irnet", proc_irda);
-#endif /* CONFIG_PROC_FS */
-
-  /* Remove our IrNET server from existence */
-  irnet_destroy_server();
-
-  /* Remove all instances of IrNET socket still present */
-  hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy);
-
-  DEXIT(MODULE_TRACE, "\n");
-}
diff --git a/net/irda/irnet/irnet_irda.h b/net/irda/irnet/irnet_irda.h
deleted file mode 100644 (file)
index 3e40895..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- *     IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- *             Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file contains all definitions and declarations necessary for the
- * IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co).
- * This file is a private header, so other modules don't want to know
- * what's in there...
- */
-
-#ifndef IRNET_IRDA_H
-#define IRNET_IRDA_H
-
-/***************************** INCLUDES *****************************/
-/* Please add other headers in irnet.h */
-
-#include "irnet.h"             /* Module global include */
-
-/************************ CONSTANTS & MACROS ************************/
-
-/*
- * Name of the service (socket name) used by IrNET
- */
-/* IAS object name (or part of it) */
-#define IRNET_SERVICE_NAME     "IrNetv1"
-/* IAS attribute */
-#define IRNET_IAS_VALUE                "IrDA:TinyTP:LsapSel"
-/* LMP notify name for client (only for /proc/net/irda/irlmp) */
-#define IRNET_NOTIFY_NAME      "IrNET socket"
-/* LMP notify name for server (only for /proc/net/irda/irlmp) */
-#define IRNET_NOTIFY_NAME_SERV "IrNET server"
-
-/****************************** TYPES ******************************/
-
-/*
- * This is the main structure where we store all the data pertaining to
- * the IrNET server (listen for connection requests) and the root
- * of the IrNET socket list
- */
-typedef struct irnet_root
-{
-  irnet_socket         s;              /* To pretend we are a client... */
-
-  /* Generic stuff */
-  int                  magic;          /* Paranoia */
-  int                  running;        /* Are we operational ? */
-
-  /* Link list of all IrNET instances opened */
-  hashbin_t *          list;
-  spinlock_t           spinlock;       /* Serialize access to the list */
-  /* Note : the way hashbin has been designed is absolutely not
-   * reentrant, beware... So, we blindly protect all with spinlock */
-
-  /* Handle for the hint bit advertised in IrLMP */
-  void *               skey;
-
-  /* Server socket part */
-  struct ias_object *  ias_obj;        /* Our service name + lsap in IAS */
-
-} irnet_root;
-
-
-/**************************** PROTOTYPES ****************************/
-
-/* ----------------------- CONTROL CHANNEL ----------------------- */
-static void
-       irnet_post_event(irnet_socket *,
-                        irnet_event,
-                        __u32,
-                        __u32,
-                        char *,
-                        __u16);
-/* ----------------------- IRDA SUBROUTINES ----------------------- */
-static inline int
-       irnet_open_tsap(irnet_socket *);
-static inline __u8
-       irnet_ias_to_tsap(irnet_socket *,
-                         int,
-                         struct ias_value *);
-static inline int
-       irnet_find_lsap_sel(irnet_socket *);
-static inline int
-       irnet_connect_tsap(irnet_socket *);
-static inline int
-       irnet_discover_next_daddr(irnet_socket *);
-static inline int
-       irnet_discover_daddr_and_lsap_sel(irnet_socket *);
-static inline int
-       irnet_dname_to_daddr(irnet_socket *);
-/* ------------------------ SERVER SOCKET ------------------------ */
-static inline int
-       irnet_daddr_to_dname(irnet_socket *);
-static inline irnet_socket *
-       irnet_find_socket(irnet_socket *);
-static inline int
-       irnet_connect_socket(irnet_socket *,
-                            irnet_socket *,
-                            struct qos_info *,
-                            __u32,
-                            __u8);
-static inline void
-       irnet_disconnect_server(irnet_socket *,
-                               struct sk_buff *);
-static inline int
-       irnet_setup_server(void);
-static inline void
-       irnet_destroy_server(void);
-/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */
-static int
-       irnet_data_indication(void *,           /* instance */
-                             void *,           /* sap */
-                             struct sk_buff *);
-static void
-       irnet_disconnect_indication(void *,
-                                   void *,
-                                   LM_REASON,
-                                   struct sk_buff *);
-static void
-       irnet_connect_confirm(void *,
-                             void *,
-                             struct qos_info *,
-                             __u32,
-                             __u8,
-                             struct sk_buff *);
-static void
-       irnet_flow_indication(void *,
-                             void *,
-                             LOCAL_FLOW);
-static void
-       irnet_status_indication(void *,
-                               LINK_STATUS,
-                               LOCK_STATUS);
-static void
-       irnet_connect_indication(void *,
-                                void *,
-                                struct qos_info *,
-                                __u32,
-                                __u8,
-                                struct sk_buff *);
-/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */
-static void
-       irnet_getvalue_confirm(int,
-                              __u16,
-                              struct ias_value *,
-                              void *);
-static void
-       irnet_discovervalue_confirm(int,
-                                   __u16,
-                                   struct ias_value *,
-                                   void *);
-#ifdef DISCOVERY_EVENTS
-static void
-       irnet_discovery_indication(discinfo_t *,
-                                  DISCOVERY_MODE,
-                                  void *);
-static void
-       irnet_expiry_indication(discinfo_t *,
-                               DISCOVERY_MODE,
-                               void *);
-#endif
-
-/**************************** VARIABLES ****************************/
-
-/*
- * The IrNET server. Listen to connection requests and co...
- */
-static struct irnet_root       irnet_server;
-
-/* Control channel stuff (note : extern) */
-struct irnet_ctrl_channel      irnet_events;
-
-/* The /proc/net/irda directory, defined elsewhere... */
-#ifdef CONFIG_PROC_FS
-extern struct proc_dir_entry *proc_irda;
-#endif /* CONFIG_PROC_FS */
-
-#endif /* IRNET_IRDA_H */
diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c
deleted file mode 100644 (file)
index 7025dcb..0000000
+++ /dev/null
@@ -1,1189 +0,0 @@
-/*
- *     IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- *             Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file implement the PPP interface and /dev/irnet character device.
- * The PPP interface hook to the ppp_generic module, handle all our
- *     relationship to the PPP code in the kernel (and by extension to pppd),
- *     and exchange PPP frames with this module (send/receive).
- * The /dev/irnet device is used primarily for 2 functions :
- *     1) as a stub for pppd (the ppp daemon), so that we can appropriately
- *     generate PPP sessions (we pretend we are a tty).
- *     2) as a control channel (write commands, read events)
- */
-
-#include <linux/sched/signal.h>
-#include <linux/slab.h>
-
-#include "irnet_ppp.h"         /* Private header */
-/* Please put other headers in irnet.h - Thanks */
-
-/* Generic PPP callbacks (to call us) */
-static const struct ppp_channel_ops irnet_ppp_ops = {
-       .start_xmit = ppp_irnet_send,
-       .ioctl = ppp_irnet_ioctl
-};
-
-/************************* CONTROL CHANNEL *************************/
-/*
- * When a pppd instance is not active on /dev/irnet, it acts as a control
- * channel.
- * Writing allow to set up the IrDA destination of the IrNET channel,
- * and any application may be read events happening in IrNET...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Write is used to send a command to configure a IrNET channel
- * before it is open by pppd. The syntax is : "command argument"
- * Currently there is only two defined commands :
- *     o name : set the requested IrDA nickname of the IrNET peer.
- *     o addr : set the requested IrDA address of the IrNET peer.
- * Note : the code is crude, but effective...
- */
-static inline ssize_t
-irnet_ctrl_write(irnet_socket *        ap,
-                const char __user *buf,
-                size_t         count)
-{
-  char         command[IRNET_MAX_COMMAND];
-  char *       start;          /* Current command being processed */
-  char *       next;           /* Next command to process */
-  int          length;         /* Length of current command */
-
-  DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count);
-
-  /* Check for overflow... */
-  DABORT(count >= IRNET_MAX_COMMAND, -ENOMEM,
-        CTRL_ERROR, "Too much data !!!\n");
-
-  /* Get the data in the driver */
-  if(copy_from_user(command, buf, count))
-    {
-      DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
-      return -EFAULT;
-    }
-
-  /* Safe terminate the string */
-  command[count] = '\0';
-  DEBUG(CTRL_INFO, "Command line received is ``%s'' (%zd).\n",
-       command, count);
-
-  /* Check every commands in the command line */
-  next = command;
-  while(next != NULL)
-    {
-      /* Look at the next command */
-      start = next;
-
-       /* Scrap whitespaces before the command */
-       start = skip_spaces(start);
-
-      /* ',' is our command separator */
-      next = strchr(start, ',');
-      if(next)
-       {
-         *next = '\0';                 /* Terminate command */
-         length = next - start;        /* Length */
-         next++;                       /* Skip the '\0' */
-       }
-      else
-       length = strlen(start);
-
-      DEBUG(CTRL_INFO, "Found command ``%s'' (%d).\n", start, length);
-
-      /* Check if we recognised one of the known command
-       * We can't use "switch" with strings, so hack with "continue" */
-
-      /* First command : name -> Requested IrDA nickname */
-      if(!strncmp(start, "name", 4))
-       {
-         /* Copy the name only if is included and not "any" */
-         if((length > 5) && (strcmp(start + 5, "any")))
-           {
-             /* Strip out trailing whitespaces */
-             while(isspace(start[length - 1]))
-               length--;
-
-             DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5,
-                    -EINVAL, CTRL_ERROR, "Invalid nickname.\n");
-
-             /* Copy the name for later reuse */
-             memcpy(ap->rname, start + 5, length - 5);
-             ap->rname[length - 5] = '\0';
-           }
-         else
-           ap->rname[0] = '\0';
-         DEBUG(CTRL_INFO, "Got rname = ``%s''\n", ap->rname);
-
-         /* Restart the loop */
-         continue;
-       }
-
-      /* Second command : addr, daddr -> Requested IrDA destination address
-       * Also process : saddr -> Requested IrDA source address */
-      if((!strncmp(start, "addr", 4)) ||
-        (!strncmp(start, "daddr", 5)) ||
-        (!strncmp(start, "saddr", 5)))
-       {
-         __u32         addr = DEV_ADDR_ANY;
-
-         /* Copy the address only if is included and not "any" */
-         if((length > 5) && (strcmp(start + 5, "any")))
-           {
-             char *    begp = start + 5;
-             char *    endp;
-
-             /* Scrap whitespaces before the command */
-             begp = skip_spaces(begp);
-
-             /* Convert argument to a number (last arg is the base) */
-             addr = simple_strtoul(begp, &endp, 16);
-             /* Has it worked  ? (endp should be start + length) */
-             DABORT(endp <= (start + 5), -EINVAL,
-                    CTRL_ERROR, "Invalid address.\n");
-           }
-         /* Which type of address ? */
-         if(start[0] == 's')
-           {
-             /* Save it */
-             ap->rsaddr = addr;
-             DEBUG(CTRL_INFO, "Got rsaddr = %08x\n", ap->rsaddr);
-           }
-         else
-           {
-             /* Save it */
-             ap->rdaddr = addr;
-             DEBUG(CTRL_INFO, "Got rdaddr = %08x\n", ap->rdaddr);
-           }
-
-         /* Restart the loop */
-         continue;
-       }
-
-      /* Other possible command : connect N (number of retries) */
-
-      /* No command matched -> Failed... */
-      DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n");
-    }
-
-  /* Success : we have parsed all commands successfully */
-  return count;
-}
-
-#ifdef INITIAL_DISCOVERY
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_get_discovery_log (self)
- *
- *    Query the content on the discovery log if not done
- *
- * This function query the current content of the discovery log
- * at the startup of the event channel and save it in the internal struct.
- */
-static void
-irnet_get_discovery_log(irnet_socket * ap)
-{
-  __u16                mask = irlmp_service_to_hint(S_LAN);
-
-  /* Ask IrLMP for the current discovery log */
-  ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask,
-                                         DISCOVERY_DEFAULT_SLOTS);
-
-  /* Check if the we got some results */
-  if(ap->discoveries == NULL)
-    ap->disco_number = -1;
-
-  DEBUG(CTRL_INFO, "Got the log (0x%p), size is %d\n",
-       ap->discoveries, ap->disco_number);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Function irnet_read_discovery_log (self, event)
- *
- *    Read the content on the discovery log
- *
- * This function dump the current content of the discovery log
- * at the startup of the event channel.
- * Return 1 if wrote an event on the control channel...
- *
- * State of the ap->disco_XXX variables :
- * Socket creation :  discoveries = NULL ; disco_index = 0 ; disco_number = 0
- * While reading :    discoveries = ptr  ; disco_index = X ; disco_number = Y
- * After reading :    discoveries = NULL ; disco_index = Y ; disco_number = -1
- */
-static inline int
-irnet_read_discovery_log(irnet_socket *ap, char *event, int buf_size)
-{
-  int          done_event = 0;
-
-  DENTER(CTRL_TRACE, "(ap=0x%p, event=0x%p)\n",
-        ap, event);
-
-  /* Test if we have some work to do or we have already finished */
-  if(ap->disco_number == -1)
-    {
-      DEBUG(CTRL_INFO, "Already done\n");
-      return 0;
-    }
-
-  /* Test if it's the first time and therefore we need to get the log */
-  if(ap->discoveries == NULL)
-    irnet_get_discovery_log(ap);
-
-  /* Check if we have more item to dump */
-  if(ap->disco_index < ap->disco_number)
-    {
-      /* Write an event */
-      snprintf(event, buf_size,
-              "Found %08x (%s) behind %08x {hints %02X-%02X}\n",
-              ap->discoveries[ap->disco_index].daddr,
-              ap->discoveries[ap->disco_index].info,
-              ap->discoveries[ap->disco_index].saddr,
-              ap->discoveries[ap->disco_index].hints[0],
-              ap->discoveries[ap->disco_index].hints[1]);
-      DEBUG(CTRL_INFO, "Writing discovery %d : %s\n",
-           ap->disco_index, ap->discoveries[ap->disco_index].info);
-
-      /* We have an event */
-      done_event = 1;
-      /* Next discovery */
-      ap->disco_index++;
-    }
-
-  /* Check if we have done the last item */
-  if(ap->disco_index >= ap->disco_number)
-    {
-      /* No more items : remove the log and signal termination */
-      DEBUG(CTRL_INFO, "Cleaning up log (0x%p)\n",
-           ap->discoveries);
-      if(ap->discoveries != NULL)
-       {
-         /* Cleanup our copy of the discovery log */
-         kfree(ap->discoveries);
-         ap->discoveries = NULL;
-       }
-      ap->disco_number = -1;
-    }
-
-  return done_event;
-}
-#endif /* INITIAL_DISCOVERY */
-
-/*------------------------------------------------------------------*/
-/*
- * Read is used to get IrNET events
- */
-static inline ssize_t
-irnet_ctrl_read(irnet_socket * ap,
-               struct file *   file,
-               char __user *   buf,
-               size_t          count)
-{
-  DECLARE_WAITQUEUE(wait, current);
-  char         event[75];
-  ssize_t      ret = 0;
-
-  DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count);
-
-#ifdef INITIAL_DISCOVERY
-  /* Check if we have read the log */
-  if (irnet_read_discovery_log(ap, event, sizeof(event)))
-    {
-      count = min(strlen(event), count);
-      if (copy_to_user(buf, event, count))
-       {
-         DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
-         return -EFAULT;
-       }
-
-      DEXIT(CTRL_TRACE, "\n");
-      return count;
-    }
-#endif /* INITIAL_DISCOVERY */
-
-  /* Put ourselves on the wait queue to be woken up */
-  add_wait_queue(&irnet_events.rwait, &wait);
-  set_current_state(TASK_INTERRUPTIBLE);
-  for(;;)
-    {
-      /* If there is unread events */
-      ret = 0;
-      if(ap->event_index != irnet_events.index)
-       break;
-      ret = -EAGAIN;
-      if(file->f_flags & O_NONBLOCK)
-       break;
-      ret = -ERESTARTSYS;
-      if(signal_pending(current))
-       break;
-      /* Yield and wait to be woken up */
-      schedule();
-    }
-  __set_current_state(TASK_RUNNING);
-  remove_wait_queue(&irnet_events.rwait, &wait);
-
-  /* Did we got it ? */
-  if(ret != 0)
-    {
-      /* No, return the error code */
-      DEXIT(CTRL_TRACE, " - ret %zd\n", ret);
-      return ret;
-    }
-
-  /* Which event is it ? */
-  switch(irnet_events.log[ap->event_index].event)
-    {
-    case IRNET_DISCOVER:
-      snprintf(event, sizeof(event),
-              "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name,
-              irnet_events.log[ap->event_index].saddr,
-              irnet_events.log[ap->event_index].hints.byte[0],
-              irnet_events.log[ap->event_index].hints.byte[1]);
-      break;
-    case IRNET_EXPIRE:
-      snprintf(event, sizeof(event),
-              "Expired %08x (%s) behind %08x {hints %02X-%02X}\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name,
-              irnet_events.log[ap->event_index].saddr,
-              irnet_events.log[ap->event_index].hints.byte[0],
-              irnet_events.log[ap->event_index].hints.byte[1]);
-      break;
-    case IRNET_CONNECT_TO:
-      snprintf(event, sizeof(event), "Connected to %08x (%s) on ppp%d\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name,
-              irnet_events.log[ap->event_index].unit);
-      break;
-    case IRNET_CONNECT_FROM:
-      snprintf(event, sizeof(event), "Connection from %08x (%s) on ppp%d\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name,
-              irnet_events.log[ap->event_index].unit);
-      break;
-    case IRNET_REQUEST_FROM:
-      snprintf(event, sizeof(event), "Request from %08x (%s) behind %08x\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name,
-              irnet_events.log[ap->event_index].saddr);
-      break;
-    case IRNET_NOANSWER_FROM:
-      snprintf(event, sizeof(event), "No-answer from %08x (%s) on ppp%d\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name,
-              irnet_events.log[ap->event_index].unit);
-      break;
-    case IRNET_BLOCKED_LINK:
-      snprintf(event, sizeof(event), "Blocked link with %08x (%s) on ppp%d\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name,
-              irnet_events.log[ap->event_index].unit);
-      break;
-    case IRNET_DISCONNECT_FROM:
-      snprintf(event, sizeof(event), "Disconnection from %08x (%s) on ppp%d\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name,
-              irnet_events.log[ap->event_index].unit);
-      break;
-    case IRNET_DISCONNECT_TO:
-      snprintf(event, sizeof(event), "Disconnected to %08x (%s)\n",
-              irnet_events.log[ap->event_index].daddr,
-              irnet_events.log[ap->event_index].name);
-      break;
-    default:
-      snprintf(event, sizeof(event), "Bug\n");
-    }
-  /* Increment our event index */
-  ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS;
-
-  DEBUG(CTRL_INFO, "Event is :%s", event);
-
-  count = min(strlen(event), count);
-  if (copy_to_user(buf, event, count))
-    {
-      DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
-      return -EFAULT;
-    }
-
-  DEXIT(CTRL_TRACE, "\n");
-  return count;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Poll : called when someone do a select on /dev/irnet.
- * Just check if there are new events...
- */
-static inline unsigned int
-irnet_ctrl_poll(irnet_socket * ap,
-               struct file *   file,
-               poll_table *    wait)
-{
-  unsigned int mask;
-
-  DENTER(CTRL_TRACE, "(ap=0x%p)\n", ap);
-
-  poll_wait(file, &irnet_events.rwait, wait);
-  mask = POLLOUT | POLLWRNORM;
-  /* If there is unread events */
-  if(ap->event_index != irnet_events.index)
-    mask |= POLLIN | POLLRDNORM;
-#ifdef INITIAL_DISCOVERY
-  if(ap->disco_number != -1)
-    {
-      /* Test if it's the first time and therefore we need to get the log */
-      if(ap->discoveries == NULL)
-       irnet_get_discovery_log(ap);
-      /* Recheck */
-      if(ap->disco_number != -1)
-       mask |= POLLIN | POLLRDNORM;
-    }
-#endif /* INITIAL_DISCOVERY */
-
-  DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask);
-  return mask;
-}
-
-
-/*********************** FILESYSTEM CALLBACKS ***********************/
-/*
- * Implement the usual open, read, write functions that will be called
- * by the file system when some action is performed on /dev/irnet.
- * Most of those actions will in fact be performed by "pppd" or
- * the control channel, we just act as a redirector...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Open : when somebody open /dev/irnet
- * We basically create a new instance of irnet and initialise it.
- */
-static int
-dev_irnet_open(struct inode *  inode,
-              struct file *    file)
-{
-  struct irnet_socket *        ap;
-  int                  err;
-
-  DENTER(FS_TRACE, "(file=0x%p)\n", file);
-
-#ifdef SECURE_DEVIRNET
-  /* This could (should?) be enforced by the permissions on /dev/irnet. */
-  if(!capable(CAP_NET_ADMIN))
-    return -EPERM;
-#endif /* SECURE_DEVIRNET */
-
-  /* Allocate a private structure for this IrNET instance */
-  ap = kzalloc(sizeof(*ap), GFP_KERNEL);
-  DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n");
-
-  /* initialize the irnet structure */
-  ap->file = file;
-
-  /* PPP channel setup */
-  ap->ppp_open = 0;
-  ap->chan.private = ap;
-  ap->chan.ops = &irnet_ppp_ops;
-  ap->chan.mtu = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN);
-  ap->chan.hdrlen = 2 + TTP_MAX_HEADER;                /* for A/C + Max IrDA hdr */
-  /* PPP parameters */
-  ap->mru = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN);
-  ap->xaccm[0] = ~0U;
-  ap->xaccm[3] = 0x60000000U;
-  ap->raccm = ~0U;
-
-  /* Setup the IrDA part... */
-  err = irda_irnet_create(ap);
-  if(err)
-    {
-      DERROR(FS_ERROR, "Can't setup IrDA link...\n");
-      kfree(ap);
-
-      return err;
-    }
-
-  /* For the control channel */
-  ap->event_index = irnet_events.index;        /* Cancel all past events */
-
-  mutex_init(&ap->lock);
-
-  /* Put our stuff where we will be able to find it later */
-  file->private_data = ap;
-
-  DEXIT(FS_TRACE, " - ap=0x%p\n", ap);
-
-  return 0;
-}
-
-
-/*------------------------------------------------------------------*/
-/*
- * Close : when somebody close /dev/irnet
- * Destroy the instance of /dev/irnet
- */
-static int
-dev_irnet_close(struct inode * inode,
-               struct file *   file)
-{
-  irnet_socket *       ap = file->private_data;
-
-  DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n",
-        file, ap);
-  DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n");
-
-  /* Detach ourselves */
-  file->private_data = NULL;
-
-  /* Close IrDA stuff */
-  irda_irnet_destroy(ap);
-
-  /* Disconnect from the generic PPP layer if not already done */
-  if(ap->ppp_open)
-    {
-      DERROR(FS_ERROR, "Channel still registered - deregistering !\n");
-      ap->ppp_open = 0;
-      ppp_unregister_channel(&ap->chan);
-    }
-
-  kfree(ap);
-
-  DEXIT(FS_TRACE, "\n");
-  return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Write does nothing.
- * (we receive packet from ppp_generic through ppp_irnet_send())
- */
-static ssize_t
-dev_irnet_write(struct file *  file,
-               const char __user *buf,
-               size_t          count,
-               loff_t *        ppos)
-{
-  irnet_socket *       ap = file->private_data;
-
-  DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n",
-       file, ap, count);
-  DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n");
-
-  /* If we are connected to ppp_generic, let it handle the job */
-  if(ap->ppp_open)
-    return -EAGAIN;
-  else
-    return irnet_ctrl_write(ap, buf, count);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read doesn't do much either.
- * (pppd poll us, but ultimately reads through /dev/ppp)
- */
-static ssize_t
-dev_irnet_read(struct file *   file,
-              char __user *    buf,
-              size_t           count,
-              loff_t *         ppos)
-{
-  irnet_socket *       ap = file->private_data;
-
-  DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n",
-       file, ap, count);
-  DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n");
-
-  /* If we are connected to ppp_generic, let it handle the job */
-  if(ap->ppp_open)
-    return -EAGAIN;
-  else
-    return irnet_ctrl_read(ap, file, buf, count);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Poll : called when someone do a select on /dev/irnet
- */
-static unsigned int
-dev_irnet_poll(struct file *   file,
-              poll_table *     wait)
-{
-  irnet_socket *       ap = file->private_data;
-  unsigned int         mask;
-
-  DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n",
-        file, ap);
-
-  mask = POLLOUT | POLLWRNORM;
-  DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n");
-
-  /* If we are connected to ppp_generic, let it handle the job */
-  if(!ap->ppp_open)
-    mask |= irnet_ctrl_poll(ap, file, wait);
-
-  DEXIT(FS_TRACE, " - mask=0x%X\n", mask);
-  return mask;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * IOCtl : Called when someone does some ioctls on /dev/irnet
- * This is the way pppd configure us and control us while the PPP
- * instance is active.
- */
-static long
-dev_irnet_ioctl(
-               struct file *   file,
-               unsigned int    cmd,
-               unsigned long   arg)
-{
-  irnet_socket *       ap = file->private_data;
-  int                  err;
-  int                  val;
-  void __user *argp = (void __user *)arg;
-
-  DENTER(FS_TRACE, "(file=0x%p, ap=0x%p, cmd=0x%X)\n",
-        file, ap, cmd);
-
-  /* Basic checks... */
-  DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");
-#ifdef SECURE_DEVIRNET
-  if(!capable(CAP_NET_ADMIN))
-    return -EPERM;
-#endif /* SECURE_DEVIRNET */
-
-  err = -EFAULT;
-  switch(cmd)
-    {
-      /* Set discipline (should be N_SYNC_PPP or N_TTY) */
-    case TIOCSETD:
-      if(get_user(val, (int __user *)argp))
-       break;
-      if((val == N_SYNC_PPP) || (val == N_PPP))
-       {
-         DEBUG(FS_INFO, "Entering PPP discipline.\n");
-         /* PPP channel setup (ap->chan in configured in dev_irnet_open())*/
-         if (mutex_lock_interruptible(&ap->lock))
-                 return -EINTR;
-
-         err = ppp_register_channel(&ap->chan);
-         if(err == 0)
-           {
-             /* Our ppp side is active */
-             ap->ppp_open = 1;
-
-             DEBUG(FS_INFO, "Trying to establish a connection.\n");
-             /* Setup the IrDA link now - may fail... */
-             irda_irnet_connect(ap);
-           }
-         else
-           DERROR(FS_ERROR, "Can't setup PPP channel...\n");
-
-          mutex_unlock(&ap->lock);
-       }
-      else
-       {
-         /* In theory, should be N_TTY */
-         DEBUG(FS_INFO, "Exiting PPP discipline.\n");
-         /* Disconnect from the generic PPP layer */
-         if (mutex_lock_interruptible(&ap->lock))
-                 return -EINTR;
-
-         if(ap->ppp_open)
-           {
-             ap->ppp_open = 0;
-             ppp_unregister_channel(&ap->chan);
-           }
-         else
-           DERROR(FS_ERROR, "Channel not registered !\n");
-         err = 0;
-
-         mutex_unlock(&ap->lock);
-       }
-      break;
-
-      /* Query PPP channel and unit number */
-    case PPPIOCGCHAN:
-      if (mutex_lock_interruptible(&ap->lock))
-             return -EINTR;
-
-      if(ap->ppp_open && !put_user(ppp_channel_index(&ap->chan),
-                                               (int __user *)argp))
-       err = 0;
-
-      mutex_unlock(&ap->lock);
-      break;
-    case PPPIOCGUNIT:
-      if (mutex_lock_interruptible(&ap->lock))
-             return -EINTR;
-
-      if(ap->ppp_open && !put_user(ppp_unit_number(&ap->chan),
-                                               (int __user *)argp))
-        err = 0;
-
-      mutex_unlock(&ap->lock);
-      break;
-
-      /* All these ioctls can be passed both directly and from ppp_generic,
-       * so we just deal with them in one place...
-       */
-    case PPPIOCGFLAGS:
-    case PPPIOCSFLAGS:
-    case PPPIOCGASYNCMAP:
-    case PPPIOCSASYNCMAP:
-    case PPPIOCGRASYNCMAP:
-    case PPPIOCSRASYNCMAP:
-    case PPPIOCGXASYNCMAP:
-    case PPPIOCSXASYNCMAP:
-    case PPPIOCGMRU:
-    case PPPIOCSMRU:
-      DEBUG(FS_INFO, "Standard PPP ioctl.\n");
-      if(!capable(CAP_NET_ADMIN))
-       err = -EPERM;
-      else {
-       if (mutex_lock_interruptible(&ap->lock))
-             return -EINTR;
-
-       err = ppp_irnet_ioctl(&ap->chan, cmd, arg);
-
-       mutex_unlock(&ap->lock);
-      }
-      break;
-
-      /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */
-      /* Get termios */
-    case TCGETS:
-      DEBUG(FS_INFO, "Get termios.\n");
-      if (mutex_lock_interruptible(&ap->lock))
-             return -EINTR;
-
-#ifndef TCGETS2
-      if(!kernel_termios_to_user_termios((struct termios __user *)argp, &ap->termios))
-       err = 0;
-#else
-      if(kernel_termios_to_user_termios_1((struct termios __user *)argp, &ap->termios))
-       err = 0;
-#endif
-
-      mutex_unlock(&ap->lock);
-      break;
-      /* Set termios */
-    case TCSETSF:
-      DEBUG(FS_INFO, "Set termios.\n");
-      if (mutex_lock_interruptible(&ap->lock))
-             return -EINTR;
-
-#ifndef TCGETS2
-      if(!user_termios_to_kernel_termios(&ap->termios, (struct termios __user *)argp))
-       err = 0;
-#else
-      if(!user_termios_to_kernel_termios_1(&ap->termios, (struct termios __user *)argp))
-       err = 0;
-#endif
-
-      mutex_unlock(&ap->lock);
-      break;
-
-      /* Set DTR/RTS */
-    case TIOCMBIS:
-    case TIOCMBIC:
-      /* Set exclusive/non-exclusive mode */
-    case TIOCEXCL:
-    case TIOCNXCL:
-      DEBUG(FS_INFO, "TTY compatibility.\n");
-      err = 0;
-      break;
-
-    case TCGETA:
-      DEBUG(FS_INFO, "TCGETA\n");
-      break;
-
-    case TCFLSH:
-      DEBUG(FS_INFO, "TCFLSH\n");
-      /* Note : this will flush buffers in PPP, so it *must* be done
-       * We should also worry that we don't accept junk here and that
-       * we get rid of our own buffers */
-#ifdef FLUSH_TO_PPP
-      if (mutex_lock_interruptible(&ap->lock))
-             return -EINTR;
-      ppp_output_wakeup(&ap->chan);
-      mutex_unlock(&ap->lock);
-#endif /* FLUSH_TO_PPP */
-      err = 0;
-      break;
-
-    case FIONREAD:
-      DEBUG(FS_INFO, "FIONREAD\n");
-      val = 0;
-      if(put_user(val, (int __user *)argp))
-       break;
-      err = 0;
-      break;
-
-    default:
-      DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd);
-      err = -ENOTTY;
-    }
-
-  DEXIT(FS_TRACE, " - err = 0x%X\n", err);
-  return err;
-}
-
-/************************** PPP CALLBACKS **************************/
-/*
- * This are the functions that the generic PPP driver in the kernel
- * will call to communicate to us.
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare the ppp frame for transmission over the IrDA socket.
- * We make sure that the header space is enough, and we change ppp header
- * according to flags passed by pppd.
- * This is not a callback, but just a helper function used in ppp_irnet_send()
- */
-static inline struct sk_buff *
-irnet_prepare_skb(irnet_socket *       ap,
-                 struct sk_buff *      skb)
-{
-  unsigned char *      data;
-  int                  proto;          /* PPP protocol */
-  int                  islcp;          /* Protocol == LCP */
-  int                  needaddr;       /* Need PPP address */
-
-  DENTER(PPP_TRACE, "(ap=0x%p, skb=0x%p)\n",
-        ap, skb);
-
-  /* Extract PPP protocol from the frame */
-  data  = skb->data;
-  proto = (data[0] << 8) + data[1];
-
-  /* LCP packets with codes between 1 (configure-request)
-   * and 7 (code-reject) must be sent as though no options
-   * have been negotiated. */
-  islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7);
-
-  /* compress protocol field if option enabled */
-  if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp))
-    skb_pull(skb,1);
-
-  /* Check if we need address/control fields */
-  needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp);
-
-  /* Is the skb headroom large enough to contain all IrDA-headers? */
-  if((skb_headroom(skb) < (ap->max_header_size + needaddr)) ||
-      (skb_shared(skb)))
-    {
-      struct sk_buff * new_skb;
-
-      DEBUG(PPP_INFO, "Reallocating skb\n");
-
-      /* Create a new skb */
-      new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr);
-
-      /* We have to free the original skb anyway */
-      dev_kfree_skb(skb);
-
-      /* Did the realloc succeed ? */
-      DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n");
-
-      /* Use the new skb instead */
-      skb = new_skb;
-    }
-
-  /* prepend address/control fields if necessary */
-  if(needaddr)
-    {
-      skb_push(skb, 2);
-      skb->data[0] = PPP_ALLSTATIONS;
-      skb->data[1] = PPP_UI;
-    }
-
-  DEXIT(PPP_TRACE, "\n");
-
-  return skb;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Send a packet to the peer over the IrTTP connection.
- * Returns 1 iff the packet was accepted.
- * Returns 0 iff packet was not consumed.
- * If the packet was not accepted, we will call ppp_output_wakeup
- * at some later time to reactivate flow control in ppp_generic.
- */
-static int
-ppp_irnet_send(struct ppp_channel *    chan,
-              struct sk_buff *         skb)
-{
-  irnet_socket *       self = (struct irnet_socket *) chan->private;
-  int                  ret;
-
-  DENTER(PPP_TRACE, "(channel=0x%p, ap/self=0x%p)\n",
-        chan, self);
-
-  /* Check if things are somewhat valid... */
-  DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n");
-
-  /* Check if we are connected */
-  if(!(test_bit(0, &self->ttp_open)))
-    {
-#ifdef CONNECT_IN_SEND
-      /* Let's try to connect one more time... */
-      /* Note : we won't be connected after this call, but we should be
-       * ready for next packet... */
-      /* If we are already connecting, this will fail */
-      irda_irnet_connect(self);
-#endif /* CONNECT_IN_SEND */
-
-      DEBUG(PPP_INFO, "IrTTP not ready ! (%ld-%ld)\n",
-           self->ttp_open, self->ttp_connect);
-
-      /* Note : we can either drop the packet or block the packet.
-       *
-       * Blocking the packet allow us a better connection time,
-       * because by calling ppp_output_wakeup() we can have
-       * ppp_generic resending the LCP request immediately to us,
-       * rather than waiting for one of pppd periodic transmission of
-       * LCP request.
-       *
-       * On the other hand, if we block all packet, all those periodic
-       * transmissions of pppd accumulate in ppp_generic, creating a
-       * backlog of LCP request. When we eventually connect later on,
-       * we have to transmit all this backlog before we can connect
-       * proper (if we don't timeout before).
-       *
-       * The current strategy is as follow :
-       * While we are attempting to connect, we block packets to get
-       * a better connection time.
-       * If we fail to connect, we drain the queue and start dropping packets
-       */
-#ifdef BLOCK_WHEN_CONNECT
-      /* If we are attempting to connect */
-      if(test_bit(0, &self->ttp_connect))
-       {
-         /* Blocking packet, ppp_generic will retry later */
-         return 0;
-       }
-#endif /* BLOCK_WHEN_CONNECT */
-
-      /* Dropping packet, pppd will retry later */
-      dev_kfree_skb(skb);
-      return 1;
-    }
-
-  /* Check if the queue can accept any packet, otherwise block */
-  if(self->tx_flow != FLOW_START)
-    DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n",
-           skb_queue_len(&self->tsap->tx_queue));
-
-  /* Prepare ppp frame for transmission */
-  skb = irnet_prepare_skb(self, skb);
-  DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n");
-
-  /* Send the packet to IrTTP */
-  ret = irttp_data_request(self->tsap, skb);
-  if(ret < 0)
-    {
-      /*
-       * > IrTTPs tx queue is full, so we just have to
-       * > drop the frame! You might think that we should
-       * > just return -1 and don't deallocate the frame,
-       * > but that is dangerous since it's possible that
-       * > we have replaced the original skb with a new
-       * > one with larger headroom, and that would really
-       * > confuse do_dev_queue_xmit() in dev.c! I have
-       * > tried :-) DB
-       * Correction : we verify the flow control above (self->tx_flow),
-       * so we come here only if IrTTP doesn't like the packet (empty,
-       * too large, IrTTP not connected). In those rare cases, it's ok
-       * to drop it, we don't want to see it here again...
-       * Jean II
-       */
-      DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret);
-      /* irttp_data_request already free the packet */
-    }
-
-  DEXIT(PPP_TRACE, "\n");
-  return 1;    /* Packet has been consumed */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Take care of the ioctls that ppp_generic doesn't want to deal with...
- * Note : we are also called from dev_irnet_ioctl().
- */
-static int
-ppp_irnet_ioctl(struct ppp_channel *   chan,
-               unsigned int            cmd,
-               unsigned long           arg)
-{
-  irnet_socket *       ap = (struct irnet_socket *) chan->private;
-  int                  err;
-  int                  val;
-  u32                  accm[8];
-  void __user *argp = (void __user *)arg;
-
-  DENTER(PPP_TRACE, "(channel=0x%p, ap=0x%p, cmd=0x%X)\n",
-        chan, ap, cmd);
-
-  /* Basic checks... */
-  DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n");
-
-  err = -EFAULT;
-  switch(cmd)
-    {
-      /* PPP flags */
-    case PPPIOCGFLAGS:
-      val = ap->flags | ap->rbits;
-      if(put_user(val, (int __user *) argp))
-       break;
-      err = 0;
-      break;
-    case PPPIOCSFLAGS:
-      if(get_user(val, (int __user *) argp))
-       break;
-      ap->flags = val & ~SC_RCV_BITS;
-      ap->rbits = val & SC_RCV_BITS;
-      err = 0;
-      break;
-
-      /* Async map stuff - all dummy to please pppd */
-    case PPPIOCGASYNCMAP:
-      if(put_user(ap->xaccm[0], (u32 __user *) argp))
-       break;
-      err = 0;
-      break;
-    case PPPIOCSASYNCMAP:
-      if(get_user(ap->xaccm[0], (u32 __user *) argp))
-       break;
-      err = 0;
-      break;
-    case PPPIOCGRASYNCMAP:
-      if(put_user(ap->raccm, (u32 __user *) argp))
-       break;
-      err = 0;
-      break;
-    case PPPIOCSRASYNCMAP:
-      if(get_user(ap->raccm, (u32 __user *) argp))
-       break;
-      err = 0;
-      break;
-    case PPPIOCGXASYNCMAP:
-      if(copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm)))
-       break;
-      err = 0;
-      break;
-    case PPPIOCSXASYNCMAP:
-      if(copy_from_user(accm, argp, sizeof(accm)))
-       break;
-      accm[2] &= ~0x40000000U;         /* can't escape 0x5e */
-      accm[3] |= 0x60000000U;          /* must escape 0x7d, 0x7e */
-      memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
-      err = 0;
-      break;
-
-      /* Max PPP frame size */
-    case PPPIOCGMRU:
-      if(put_user(ap->mru, (int __user *) argp))
-       break;
-      err = 0;
-      break;
-    case PPPIOCSMRU:
-      if(get_user(val, (int __user *) argp))
-       break;
-      if(val < PPP_MRU)
-       val = PPP_MRU;
-      ap->mru = val;
-      err = 0;
-      break;
-
-    default:
-      DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd);
-      err = -ENOIOCTLCMD;
-    }
-
-  DEXIT(PPP_TRACE, " - err = 0x%X\n", err);
-  return err;
-}
-
-/************************** INITIALISATION **************************/
-/*
- * Module initialisation and all that jazz...
- */
-
-/*------------------------------------------------------------------*/
-/*
- * Hook our device callbacks in the filesystem, to connect our code
- * to /dev/irnet
- */
-static inline int __init
-ppp_irnet_init(void)
-{
-  int err = 0;
-
-  DENTER(MODULE_TRACE, "()\n");
-
-  /* Allocate ourselves as a minor in the misc range */
-  err = misc_register(&irnet_misc_device);
-
-  DEXIT(MODULE_TRACE, "\n");
-  return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Cleanup at exit...
- */
-static inline void __exit
-ppp_irnet_cleanup(void)
-{
-  DENTER(MODULE_TRACE, "()\n");
-
-  /* De-allocate /dev/irnet minor in misc range */
-  misc_deregister(&irnet_misc_device);
-
-  DEXIT(MODULE_TRACE, "\n");
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Module main entry point
- */
-static int __init
-irnet_init(void)
-{
-  int err;
-
-  /* Initialise both parts... */
-  err = irda_irnet_init();
-  if(!err)
-    err = ppp_irnet_init();
-  return err;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Module exit
- */
-static void __exit
-irnet_cleanup(void)
-{
-  irda_irnet_cleanup();
-  ppp_irnet_cleanup();
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Module magic
- */
-module_init(irnet_init);
-module_exit(irnet_cleanup);
-MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
-MODULE_DESCRIPTION("IrNET : Synchronous PPP over IrDA");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV(10, 187);
diff --git a/net/irda/irnet/irnet_ppp.h b/net/irda/irnet/irnet_ppp.h
deleted file mode 100644 (file)
index 3206144..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- *     IrNET protocol module : Synchronous PPP over an IrDA socket.
- *
- *             Jean II - HPL `00 - <jt@hpl.hp.com>
- *
- * This file contains all definitions and declarations necessary for the
- * PPP part of the IrNET module.
- * This file is a private header, so other modules don't want to know
- * what's in there...
- */
-
-#ifndef IRNET_PPP_H
-#define IRNET_PPP_H
-
-/***************************** INCLUDES *****************************/
-
-#include "irnet.h"             /* Module global include */
-#include <linux/miscdevice.h>
-
-/************************ CONSTANTS & MACROS ************************/
-
-/* IrNET control channel stuff */
-#define IRNET_MAX_COMMAND      256     /* Max length of a command line */
-
-/* PPP hardcore stuff */
-
-/* Bits in rbits (PPP flags in irnet struct) */
-#define SC_RCV_BITS    (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
-
-/* Bit numbers in busy */
-#define XMIT_BUSY      0
-#define RECV_BUSY      1
-#define XMIT_WAKEUP    2
-#define XMIT_FULL      3
-
-/* Queue management */
-#define PPPSYNC_MAX_RQLEN      32      /* arbitrary */
-
-/****************************** TYPES ******************************/
-
-
-/**************************** PROTOTYPES ****************************/
-
-/* ----------------------- CONTROL CHANNEL ----------------------- */
-static inline ssize_t
-       irnet_ctrl_write(irnet_socket *,
-                        const char *,
-                        size_t);
-static inline ssize_t
-       irnet_ctrl_read(irnet_socket *,
-                       struct file *,
-                       char *,
-                       size_t);
-static inline unsigned int
-       irnet_ctrl_poll(irnet_socket *,
-                       struct file *,
-                       poll_table *);
-/* ----------------------- CHARACTER DEVICE ----------------------- */
-static int
-       dev_irnet_open(struct inode *,  /* fs callback : open */
-                      struct file *),
-       dev_irnet_close(struct inode *,
-                       struct file *);
-static ssize_t
-       dev_irnet_write(struct file *,
-                       const char __user *,
-                       size_t,
-                       loff_t *),
-       dev_irnet_read(struct file *,
-                      char __user *,
-                      size_t,
-                      loff_t *);
-static unsigned int
-       dev_irnet_poll(struct file *,
-                      poll_table *);
-static long
-       dev_irnet_ioctl(struct file *,
-                       unsigned int,
-                       unsigned long);
-/* ------------------------ PPP INTERFACE ------------------------ */
-static inline struct sk_buff *
-       irnet_prepare_skb(irnet_socket *,
-                         struct sk_buff *);
-static int
-       ppp_irnet_send(struct ppp_channel *,
-                     struct sk_buff *);
-static int
-       ppp_irnet_ioctl(struct ppp_channel *,
-                       unsigned int,
-                       unsigned long);
-
-/**************************** VARIABLES ****************************/
-
-/* Filesystem callbacks (to call us) */
-static const struct file_operations irnet_device_fops =
-{
-       .owner          = THIS_MODULE,
-       .read           = dev_irnet_read,
-       .write          = dev_irnet_write,
-       .poll           = dev_irnet_poll,
-       .unlocked_ioctl = dev_irnet_ioctl,
-       .open           = dev_irnet_open,
-       .release        = dev_irnet_close,
-       .llseek         = noop_llseek,
-  /* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */
-};
-
-/* Structure so that the misc major (drivers/char/misc.c) take care of us... */
-static struct miscdevice irnet_misc_device =
-{
-       .minor = IRNET_MINOR,
-       .name = "irnet",
-       .fops = &irnet_device_fops
-};
-
-#endif /* IRNET_PPP_H */
diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c
deleted file mode 100644 (file)
index 7fc340e..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * IrDA netlink layer, for stack configuration.
- *
- * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org>
- *
- * Partly based on the 802.11 nelink implementation
- * (see net/wireless/nl80211.c) which is:
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/socket.h>
-#include <linux/irda.h>
-#include <linux/gfp.h>
-#include <net/net_namespace.h>
-#include <net/sock.h>
-#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
-#include <net/genetlink.h>
-
-
-
-static struct genl_family irda_nl_family;
-
-static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *info)
-{
-       char * ifname;
-
-       if (!info->attrs[IRDA_NL_ATTR_IFNAME])
-               return NULL;
-
-       ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]);
-
-       pr_debug("%s(): Looking for %s\n", __func__, ifname);
-
-       return dev_get_by_name(net, ifname);
-}
-
-static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info)
-{
-       struct net_device * dev;
-       struct irlap_cb * irlap;
-       u32 mode;
-
-       if (!info->attrs[IRDA_NL_ATTR_MODE])
-               return -EINVAL;
-
-       mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]);
-
-       pr_debug("%s(): Switching to mode: %d\n", __func__, mode);
-
-       dev = ifname_to_netdev(&init_net, info);
-       if (!dev)
-               return -ENODEV;
-
-       irlap = (struct irlap_cb *)dev->atalk_ptr;
-       if (!irlap) {
-               dev_put(dev);
-               return -ENODEV;
-       }
-
-       irlap->mode = mode;
-
-       dev_put(dev);
-
-       return 0;
-}
-
-static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info)
-{
-       struct net_device * dev;
-       struct irlap_cb * irlap;
-       struct sk_buff *msg;
-       void *hdr;
-       int ret = -ENOBUFS;
-
-       dev = ifname_to_netdev(&init_net, info);
-       if (!dev)
-               return -ENODEV;
-
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg) {
-               dev_put(dev);
-               return -ENOMEM;
-       }
-
-       irlap = (struct irlap_cb *)dev->atalk_ptr;
-       if (!irlap) {
-               ret = -ENODEV;
-               goto err_out;
-       }
-
-       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
-                         &irda_nl_family, 0,  IRDA_NL_CMD_GET_MODE);
-       if (hdr == NULL) {
-               ret = -EMSGSIZE;
-               goto err_out;
-       }
-
-       if(nla_put_string(msg, IRDA_NL_ATTR_IFNAME,
-                         dev->name))
-               goto err_out;
-
-       if(nla_put_u32(msg, IRDA_NL_ATTR_MODE, irlap->mode))
-               goto err_out;
-
-       genlmsg_end(msg, hdr);
-
-       return genlmsg_reply(msg, info);
-
- err_out:
-       nlmsg_free(msg);
-       dev_put(dev);
-
-       return ret;
-}
-
-static const struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = {
-       [IRDA_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING,
-                                 .len = IFNAMSIZ-1 },
-       [IRDA_NL_ATTR_MODE] = { .type = NLA_U32 },
-};
-
-static const struct genl_ops irda_nl_ops[] = {
-       {
-               .cmd = IRDA_NL_CMD_SET_MODE,
-               .doit = irda_nl_set_mode,
-               .policy = irda_nl_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = IRDA_NL_CMD_GET_MODE,
-               .doit = irda_nl_get_mode,
-               .policy = irda_nl_policy,
-               /* can be retrieved by unprivileged users */
-       },
-
-};
-
-static struct genl_family irda_nl_family __ro_after_init = {
-       .name = IRDA_NL_NAME,
-       .hdrsize = 0,
-       .version = IRDA_NL_VERSION,
-       .maxattr = IRDA_NL_CMD_MAX,
-       .module = THIS_MODULE,
-       .ops = irda_nl_ops,
-       .n_ops = ARRAY_SIZE(irda_nl_ops),
-};
-
-int __init irda_nl_register(void)
-{
-       return genl_register_family(&irda_nl_family);
-}
-
-void irda_nl_unregister(void)
-{
-       genl_unregister_family(&irda_nl_family);
-}
diff --git a/net/irda/irproc.c b/net/irda/irproc.c
deleted file mode 100644 (file)
index 77cfdde..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irproc.c
- * Version:       1.0
- * Description:   Various entries in the /proc file system
- * Status:        Experimental.
- * Author:        Thomas Davis, <ratbert@radiks.net>
- * Created at:    Sat Feb 21 21:33:24 1998
- * Modified at:   Sun Nov 14 08:54:54 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-1999, Dag Brattli <dagb@cs.uit.no>
- *     Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>,
- *     All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     I, Thomas Davis, provide no warranty for any of this software.
- *     This material is provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <net/net_namespace.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlmp.h>
-
-extern const struct file_operations discovery_seq_fops;
-extern const struct file_operations irlap_seq_fops;
-extern const struct file_operations irlmp_seq_fops;
-extern const struct file_operations irttp_seq_fops;
-extern const struct file_operations irias_seq_fops;
-
-struct irda_entry {
-       const char *name;
-       const struct file_operations *fops;
-};
-
-struct proc_dir_entry *proc_irda;
-EXPORT_SYMBOL(proc_irda);
-
-static const struct irda_entry irda_dirs[] = {
-       {"discovery",   &discovery_seq_fops},
-       {"irttp",       &irttp_seq_fops},
-       {"irlmp",       &irlmp_seq_fops},
-       {"irlap",       &irlap_seq_fops},
-       {"irias",       &irias_seq_fops},
-};
-
-/*
- * Function irda_proc_register (void)
- *
- *    Register irda entry in /proc file system
- *
- */
-void __init irda_proc_register(void)
-{
-       int i;
-
-       proc_irda = proc_mkdir("irda", init_net.proc_net);
-       if (proc_irda == NULL)
-               return;
-
-       for (i = 0; i < ARRAY_SIZE(irda_dirs); i++)
-               (void) proc_create(irda_dirs[i].name, 0, proc_irda,
-                                  irda_dirs[i].fops);
-}
-
-/*
- * Function irda_proc_unregister (void)
- *
- *    Unregister irda entry in /proc file system
- *
- */
-void irda_proc_unregister(void)
-{
-       int i;
-
-       if (proc_irda) {
-               for (i=0; i<ARRAY_SIZE(irda_dirs); i++)
-                       remove_proc_entry(irda_dirs[i].name, proc_irda);
-
-               remove_proc_entry("irda", init_net.proc_net);
-               proc_irda = NULL;
-       }
-}
-
-
diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c
deleted file mode 100644 (file)
index 160dc89..0000000
+++ /dev/null
@@ -1,911 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irqueue.c
- * Version:       0.3
- * Description:   General queue implementation
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Tue Jun  9 13:29:31 1998
- * Modified at:   Sun Dec 12 13:48:22 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Modified at:   Thu Jan  4 14:29:10 CET 2001
- * Modified by:   Marc Zyngier <mzyngier@freesurf.fr>
- *
- *     Copyright (C) 1998-1999, Aage Kvalnes <aage@cs.uit.no>
- *     Copyright (C) 1998, Dag Brattli,
- *     All Rights Reserved.
- *
- *     This code is taken from the Vortex Operating System written by Aage
- *     Kvalnes. Aage has agreed that this code can use the GPL licence,
- *     although he does not use that licence in his own code.
- *
- *     This copyright does however _not_ include the ELF hash() function
- *     which I currently don't know which licence or copyright it
- *     has. Please inform me if you know.
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-/*
- * NOTE :
- * There are various problems with this package :
- *     o the hash function for ints is pathetic (but could be changed)
- *     o locking is sometime suspicious (especially during enumeration)
- *     o most users have only a few elements (== overhead)
- *     o most users never use search, so don't benefit from hashing
- * Problem already fixed :
- *     o not 64 bit compliant (most users do hashv = (int) self)
- *     o hashbin_remove() is broken => use hashbin_remove_this()
- * I think most users would be better served by a simple linked list
- * (like include/linux/list.h) with a global spinlock per list.
- * Jean II
- */
-
-/*
- * Notes on the concurrent access to hashbin and other SMP issues
- * -------------------------------------------------------------
- *     Hashbins are very often in the IrDA stack a global repository of
- * information, and therefore used in a very asynchronous manner following
- * various events (driver calls, timers, user calls...).
- *     Therefore, very often it is highly important to consider the
- * management of concurrent access to the hashbin and how to guarantee the
- * consistency of the operations on it.
- *
- *     First, we need to define the objective of locking :
- *             1) Protect user data (content pointed by the hashbin)
- *             2) Protect hashbin structure itself (linked list in each bin)
- *
- *                          OLD LOCKING
- *                          -----------
- *
- *     The previous locking strategy, either HB_LOCAL or HB_GLOBAL were
- * both inadequate in *both* aspect.
- *             o HB_GLOBAL was using a spinlock for each bin (local locking).
- *             o HB_LOCAL was disabling irq on *all* CPUs, so use a single
- *               global semaphore.
- *     The problems were :
- *             A) Global irq disabling is no longer supported by the kernel
- *             B) No protection for the hashbin struct global data
- *                     o hashbin_delete()
- *                     o hb_current
- *             C) No protection for user data in some cases
- *
- *     A) HB_LOCAL use global irq disabling, so doesn't work on kernel
- * 2.5.X. Even when it is supported (kernel 2.4.X and earlier), its
- * performance is not satisfactory on SMP setups. Most hashbins were
- * HB_LOCAL, so (A) definitely need fixing.
- *     B) HB_LOCAL could be modified to fix (B). However, because HB_GLOBAL
- * lock only the individual bins, it will never be able to lock the
- * global data, so can't do (B).
- *     C) Some functions return pointer to data that is still in the
- * hashbin :
- *             o hashbin_find()
- *             o hashbin_get_first()
- *             o hashbin_get_next()
- *     As the data is still in the hashbin, it may be changed or free'd
- * while the caller is examinimg the data. In those case, locking can't
- * be done within the hashbin, but must include use of the data within
- * the caller.
- *     The caller can easily do this with HB_LOCAL (just disable irqs).
- * However, this is impossible with HB_GLOBAL because the caller has no
- * way to know the proper bin, so don't know which spinlock to use.
- *
- *     Quick summary : can no longer use HB_LOCAL, and HB_GLOBAL is
- * fundamentally broken and will never work.
- *
- *                          NEW LOCKING
- *                          -----------
- *
- *     To fix those problems, I've introduce a few changes in the
- * hashbin locking :
- *             1) New HB_LOCK scheme
- *             2) hashbin->hb_spinlock
- *             3) New hashbin usage policy
- *
- * HB_LOCK :
- * -------
- *     HB_LOCK is a locking scheme intermediate between the old HB_LOCAL
- * and HB_GLOBAL. It uses a single spinlock to protect the whole content
- * of the hashbin. As it is a single spinlock, it can protect the global
- * data of the hashbin and not only the bins themselves.
- *     HB_LOCK can only protect some of the hashbin calls, so it only lock
- * call that can be made 100% safe and leave other call unprotected.
- *     HB_LOCK in theory is slower than HB_GLOBAL, but as the hashbin
- * content is always small contention is not high, so it doesn't matter
- * much. HB_LOCK is probably faster than HB_LOCAL.
- *
- * hashbin->hb_spinlock :
- * --------------------
- *     The spinlock that HB_LOCK uses is available for caller, so that
- * the caller can protect unprotected calls (see below).
- *     If the caller want to do entirely its own locking (HB_NOLOCK), he
- * can do so and may use safely this spinlock.
- *     Locking is done like this :
- *             spin_lock_irqsave(&hashbin->hb_spinlock, flags);
- *     Releasing the lock :
- *             spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
- *
- * Safe & Protected calls :
- * ----------------------
- *     The following calls are safe or protected via HB_LOCK :
- *             o hashbin_new()         -> safe
- *             o hashbin_delete()
- *             o hashbin_insert()
- *             o hashbin_remove_first()
- *             o hashbin_remove()
- *             o hashbin_remove_this()
- *             o HASHBIN_GET_SIZE()    -> atomic
- *
- *     The following calls only protect the hashbin itself :
- *             o hashbin_lock_find()
- *             o hashbin_find_next()
- *
- * Unprotected calls :
- * -----------------
- *     The following calls need to be protected by the caller :
- *             o hashbin_find()
- *             o hashbin_get_first()
- *             o hashbin_get_next()
- *
- * Locking Policy :
- * --------------
- *     If the hashbin is used only in a single thread of execution
- * (explicitly or implicitely), you can use HB_NOLOCK
- *     If the calling module already provide concurrent access protection,
- * you may use HB_NOLOCK.
- *
- *     In all other cases, you need to use HB_LOCK and lock the hashbin
- * every time before calling one of the unprotected calls. You also must
- * use the pointer returned by the unprotected call within the locked
- * region.
- *
- * Extra care for enumeration :
- * --------------------------
- *     hashbin_get_first() and hashbin_get_next() use the hashbin to
- * store the current position, in hb_current.
- *     As long as the hashbin remains locked, this is safe. If you unlock
- * the hashbin, the current position may change if anybody else modify
- * or enumerate the hashbin.
- *     Summary : do the full enumeration while locked.
- *
- *     Alternatively, you may use hashbin_find_next(). But, this will
- * be slower, is more complex to use and doesn't protect the hashbin
- * content. So, care is needed here as well.
- *
- * Other issues :
- * ------------
- *     I believe that we are overdoing it by using spin_lock_irqsave()
- * and we should use only spin_lock_bh() or similar. But, I don't have
- * the balls to try it out.
- *     Don't believe that because hashbin are now (somewhat) SMP safe
- * that the rest of the code is. Higher layers tend to be safest,
- * but LAP and LMP would need some serious dedicated love.
- *
- * Jean II
- */
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irqueue.h>
-
-/************************ QUEUE SUBROUTINES ************************/
-
-/*
- * Hashbin
- */
-#define GET_HASHBIN(x) ( x & HASHBIN_MASK )
-
-/*
- * Function hash (name)
- *
- *    This function hash the input string 'name' using the ELF hash
- *    function for strings.
- */
-static __u32 hash( const char* name)
-{
-       __u32 h = 0;
-       __u32 g;
-
-       while(*name) {
-               h = (h<<4) + *name++;
-               if ((g = (h & 0xf0000000)))
-                       h ^=g>>24;
-               h &=~g;
-       }
-       return h;
-}
-
-/*
- * Function enqueue_first (queue, proc)
- *
- *    Insert item first in queue.
- *
- */
-static void enqueue_first(irda_queue_t **queue, irda_queue_t* element)
-{
-
-       /*
-        * Check if queue is empty.
-        */
-       if ( *queue == NULL ) {
-               /*
-                * Queue is empty.  Insert one element into the queue.
-                */
-               element->q_next = element->q_prev = *queue = element;
-
-       } else {
-               /*
-                * Queue is not empty.  Insert element into front of queue.
-                */
-               element->q_next          = (*queue);
-               (*queue)->q_prev->q_next = element;
-               element->q_prev          = (*queue)->q_prev;
-               (*queue)->q_prev         = element;
-               (*queue)                 = element;
-       }
-}
-
-
-/*
- * Function dequeue (queue)
- *
- *    Remove first entry in queue
- *
- */
-static irda_queue_t *dequeue_first(irda_queue_t **queue)
-{
-       irda_queue_t *ret;
-
-       pr_debug("dequeue_first()\n");
-
-       /*
-        * Set return value
-        */
-       ret =  *queue;
-
-       if ( *queue == NULL ) {
-               /*
-                * Queue was empty.
-                */
-       } else if ( (*queue)->q_next == *queue ) {
-               /*
-                *  Queue only contained a single element. It will now be
-                *  empty.
-                */
-               *queue = NULL;
-       } else {
-               /*
-                * Queue contained several element.  Remove the first one.
-                */
-               (*queue)->q_prev->q_next = (*queue)->q_next;
-               (*queue)->q_next->q_prev = (*queue)->q_prev;
-               *queue = (*queue)->q_next;
-       }
-
-       /*
-        * Return the removed entry (or NULL of queue was empty).
-        */
-       return ret;
-}
-
-/*
- * Function dequeue_general (queue, element)
- *
- *
- */
-static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element)
-{
-       irda_queue_t *ret;
-
-       pr_debug("dequeue_general()\n");
-
-       /*
-        * Set return value
-        */
-       ret =  *queue;
-
-       if ( *queue == NULL ) {
-               /*
-                * Queue was empty.
-                */
-       } else if ( (*queue)->q_next == *queue ) {
-               /*
-                *  Queue only contained a single element. It will now be
-                *  empty.
-                */
-               *queue = NULL;
-
-       } else {
-               /*
-                *  Remove specific element.
-                */
-               element->q_prev->q_next = element->q_next;
-               element->q_next->q_prev = element->q_prev;
-               if ( (*queue) == element)
-                       (*queue) = element->q_next;
-       }
-
-       /*
-        * Return the removed entry (or NULL of queue was empty).
-        */
-       return ret;
-}
-
-/************************ HASHBIN MANAGEMENT ************************/
-
-/*
- * Function hashbin_create ( type, name )
- *
- *    Create hashbin!
- *
- */
-hashbin_t *hashbin_new(int type)
-{
-       hashbin_t* hashbin;
-
-       /*
-        * Allocate new hashbin
-        */
-       hashbin = kzalloc(sizeof(*hashbin), GFP_ATOMIC);
-       if (!hashbin)
-               return NULL;
-
-       /*
-        * Initialize structure
-        */
-       hashbin->hb_type = type;
-       hashbin->magic = HB_MAGIC;
-       //hashbin->hb_current = NULL;
-
-       /* Make sure all spinlock's are unlocked */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_lock_init(&hashbin->hb_spinlock);
-       }
-
-       return hashbin;
-}
-EXPORT_SYMBOL(hashbin_new);
-
-
-/*
- * Function hashbin_delete (hashbin, free_func)
- *
- *    Destroy hashbin, the free_func can be a user supplied special routine
- *    for deallocating this structure if it's complex. If not the user can
- *    just supply kfree, which should take care of the job.
- */
-int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
-{
-       irda_queue_t* queue;
-       unsigned long flags = 0;
-       int i;
-
-       IRDA_ASSERT(hashbin != NULL, return -1;);
-       IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;);
-
-       /* Synchronize */
-       if (hashbin->hb_type & HB_LOCK)
-               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-
-       /*
-        *  Free the entries in the hashbin, TODO: use hashbin_clear when
-        *  it has been shown to work
-        */
-       for (i = 0; i < HASHBIN_SIZE; i ++ ) {
-               while (1) {
-                       queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
-
-                       if (!queue)
-                               break;
-
-                       if (free_func) {
-                               if (hashbin->hb_type & HB_LOCK)
-                                       spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-                               free_func(queue);
-                               if (hashbin->hb_type & HB_LOCK)
-                                       spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-                       }
-               }
-       }
-
-       /* Cleanup local data */
-       hashbin->hb_current = NULL;
-       hashbin->magic = ~HB_MAGIC;
-
-       /* Release lock */
-       if (hashbin->hb_type & HB_LOCK)
-               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-
-       /*
-        *  Free the hashbin structure
-        */
-       kfree(hashbin);
-
-       return 0;
-}
-EXPORT_SYMBOL(hashbin_delete);
-
-/********************* HASHBIN LIST OPERATIONS *********************/
-
-/*
- * Function hashbin_insert (hashbin, entry, name)
- *
- *    Insert an entry into the hashbin
- *
- */
-void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
-                   const char* name)
-{
-       unsigned long flags = 0;
-       int bin;
-
-       IRDA_ASSERT( hashbin != NULL, return;);
-       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;);
-
-       /*
-        * Locate hashbin
-        */
-       if ( name )
-               hashv = hash( name );
-       bin = GET_HASHBIN( hashv );
-
-       /* Synchronize */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-       } /* Default is no-lock  */
-
-       /*
-        * Store name and key
-        */
-       entry->q_hash = hashv;
-       if ( name )
-               strlcpy( entry->q_name, name, sizeof(entry->q_name));
-
-       /*
-        * Insert new entry first
-        */
-       enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ],
-                      entry);
-       hashbin->hb_size++;
-
-       /* Release lock */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-       } /* Default is no-lock  */
-}
-EXPORT_SYMBOL(hashbin_insert);
-
-/*
- *  Function hashbin_remove_first (hashbin)
- *
- *    Remove first entry of the hashbin
- *
- * Note : this function no longer use hashbin_remove(), but does things
- * similar to hashbin_remove_this(), so can be considered safe.
- * Jean II
- */
-void *hashbin_remove_first( hashbin_t *hashbin)
-{
-       unsigned long flags = 0;
-       irda_queue_t *entry = NULL;
-
-       /* Synchronize */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-       } /* Default is no-lock  */
-
-       entry = hashbin_get_first( hashbin);
-       if ( entry != NULL) {
-               int     bin;
-               long    hashv;
-               /*
-                * Locate hashbin
-                */
-               hashv = entry->q_hash;
-               bin = GET_HASHBIN( hashv );
-
-               /*
-                * Dequeue the entry...
-                */
-               dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
-                                entry);
-               hashbin->hb_size--;
-               entry->q_next = NULL;
-               entry->q_prev = NULL;
-
-               /*
-                *  Check if this item is the currently selected item, and in
-                *  that case we must reset hb_current
-                */
-               if ( entry == hashbin->hb_current)
-                       hashbin->hb_current = NULL;
-       }
-
-       /* Release lock */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-       } /* Default is no-lock  */
-
-       return entry;
-}
-
-
-/*
- *  Function hashbin_remove (hashbin, hashv, name)
- *
- *    Remove entry with the given name
- *
- *  The use of this function is highly discouraged, because the whole
- *  concept behind hashbin_remove() is broken. In many cases, it's not
- *  possible to guarantee the unicity of the index (either hashv or name),
- *  leading to removing the WRONG entry.
- *  The only simple safe use is :
- *             hashbin_remove(hasbin, (int) self, NULL);
- *  In other case, you must think hard to guarantee unicity of the index.
- *  Jean II
- */
-void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name)
-{
-       int bin, found = FALSE;
-       unsigned long flags = 0;
-       irda_queue_t* entry;
-
-       IRDA_ASSERT( hashbin != NULL, return NULL;);
-       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-
-       /*
-        * Locate hashbin
-        */
-       if ( name )
-               hashv = hash( name );
-       bin = GET_HASHBIN( hashv );
-
-       /* Synchronize */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-       } /* Default is no-lock  */
-
-       /*
-        * Search for entry
-        */
-       entry = hashbin->hb_queue[ bin ];
-       if ( entry ) {
-               do {
-                       /*
-                        * Check for key
-                        */
-                       if ( entry->q_hash == hashv ) {
-                               /*
-                                * Name compare too?
-                                */
-                               if ( name ) {
-                                       if ( strcmp( entry->q_name, name) == 0)
-                                       {
-                                               found = TRUE;
-                                               break;
-                                       }
-                               } else {
-                                       found = TRUE;
-                                       break;
-                               }
-                       }
-                       entry = entry->q_next;
-               } while ( entry != hashbin->hb_queue[ bin ] );
-       }
-
-       /*
-        * If entry was found, dequeue it
-        */
-       if ( found ) {
-               dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
-                                entry);
-               hashbin->hb_size--;
-
-               /*
-                *  Check if this item is the currently selected item, and in
-                *  that case we must reset hb_current
-                */
-               if ( entry == hashbin->hb_current)
-                       hashbin->hb_current = NULL;
-       }
-
-       /* Release lock */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-       } /* Default is no-lock  */
-
-
-       /* Return */
-       if ( found )
-               return entry;
-       else
-               return NULL;
-
-}
-EXPORT_SYMBOL(hashbin_remove);
-
-/*
- *  Function hashbin_remove_this (hashbin, entry)
- *
- *    Remove entry with the given name
- *
- * In some cases, the user of hashbin can't guarantee the unicity
- * of either the hashv or name.
- * In those cases, using the above function is guaranteed to cause troubles,
- * so we use this one instead...
- * And by the way, it's also faster, because we skip the search phase ;-)
- */
-void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
-{
-       unsigned long flags = 0;
-       int     bin;
-       long    hashv;
-
-       IRDA_ASSERT( hashbin != NULL, return NULL;);
-       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-       IRDA_ASSERT( entry != NULL, return NULL;);
-
-       /* Synchronize */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-       } /* Default is no-lock  */
-
-       /* Check if valid and not already removed... */
-       if((entry->q_next == NULL) || (entry->q_prev == NULL)) {
-               entry = NULL;
-               goto out;
-       }
-
-       /*
-        * Locate hashbin
-        */
-       hashv = entry->q_hash;
-       bin = GET_HASHBIN( hashv );
-
-       /*
-        * Dequeue the entry...
-        */
-       dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
-                        entry);
-       hashbin->hb_size--;
-       entry->q_next = NULL;
-       entry->q_prev = NULL;
-
-       /*
-        *  Check if this item is the currently selected item, and in
-        *  that case we must reset hb_current
-        */
-       if ( entry == hashbin->hb_current)
-               hashbin->hb_current = NULL;
-out:
-       /* Release lock */
-       if ( hashbin->hb_type & HB_LOCK ) {
-               spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-       } /* Default is no-lock  */
-
-       return entry;
-}
-EXPORT_SYMBOL(hashbin_remove_this);
-
-/*********************** HASHBIN ENUMERATION ***********************/
-
-/*
- * Function hashbin_common_find (hashbin, hashv, name)
- *
- *    Find item with the given hashv or name
- *
- */
-void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name )
-{
-       int bin;
-       irda_queue_t* entry;
-
-       pr_debug("hashbin_find()\n");
-
-       IRDA_ASSERT( hashbin != NULL, return NULL;);
-       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-
-       /*
-        * Locate hashbin
-        */
-       if ( name )
-               hashv = hash( name );
-       bin = GET_HASHBIN( hashv );
-
-       /*
-        * Search for entry
-        */
-       entry = hashbin->hb_queue[ bin];
-       if ( entry ) {
-               do {
-                       /*
-                        * Check for key
-                        */
-                       if ( entry->q_hash == hashv ) {
-                               /*
-                                * Name compare too?
-                                */
-                               if ( name ) {
-                                       if ( strcmp( entry->q_name, name ) == 0 ) {
-                                               return entry;
-                                       }
-                               } else {
-                                       return entry;
-                               }
-                       }
-                       entry = entry->q_next;
-               } while ( entry != hashbin->hb_queue[ bin ] );
-       }
-
-       return NULL;
-}
-EXPORT_SYMBOL(hashbin_find);
-
-/*
- * Function hashbin_lock_find (hashbin, hashv, name)
- *
- *    Find item with the given hashv or name
- *
- * Same, but with spinlock protection...
- * I call it safe, but it's only safe with respect to the hashbin, not its
- * content. - Jean II
- */
-void* hashbin_lock_find( hashbin_t* hashbin, long hashv, const char* name )
-{
-       unsigned long flags = 0;
-       irda_queue_t* entry;
-
-       /* Synchronize */
-       spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-
-       /*
-        * Search for entry
-        */
-       entry = hashbin_find(hashbin, hashv, name);
-
-       /* Release lock */
-       spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-
-       return entry;
-}
-EXPORT_SYMBOL(hashbin_lock_find);
-
-/*
- * Function hashbin_find (hashbin, hashv, name, pnext)
- *
- *    Find an item with the given hashv or name, and its successor
- *
- * This function allow to do concurrent enumerations without the
- * need to lock over the whole session, because the caller keep the
- * context of the search. On the other hand, it might fail and return
- * NULL if the entry is removed. - Jean II
- */
-void* hashbin_find_next( hashbin_t* hashbin, long hashv, const char* name,
-                        void ** pnext)
-{
-       unsigned long flags = 0;
-       irda_queue_t* entry;
-
-       /* Synchronize */
-       spin_lock_irqsave(&hashbin->hb_spinlock, flags);
-
-       /*
-        * Search for current entry
-        * This allow to check if the current item is still in the
-        * hashbin or has been removed.
-        */
-       entry = hashbin_find(hashbin, hashv, name);
-
-       /*
-        * Trick hashbin_get_next() to return what we want
-        */
-       if(entry) {
-               hashbin->hb_current = entry;
-               *pnext = hashbin_get_next( hashbin );
-       } else
-               *pnext = NULL;
-
-       /* Release lock */
-       spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-
-       return entry;
-}
-
-/*
- * Function hashbin_get_first (hashbin)
- *
- *    Get a pointer to first element in hashbin, this function must be
- *    called before any calls to hashbin_get_next()!
- *
- */
-irda_queue_t *hashbin_get_first( hashbin_t* hashbin)
-{
-       irda_queue_t *entry;
-       int i;
-
-       IRDA_ASSERT( hashbin != NULL, return NULL;);
-       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-
-       if ( hashbin == NULL)
-               return NULL;
-
-       for ( i = 0; i < HASHBIN_SIZE; i ++ ) {
-               entry = hashbin->hb_queue[ i];
-               if ( entry) {
-                       hashbin->hb_current = entry;
-                       return entry;
-               }
-       }
-       /*
-        *  Did not find any item in hashbin
-        */
-       return NULL;
-}
-EXPORT_SYMBOL(hashbin_get_first);
-
-/*
- * Function hashbin_get_next (hashbin)
- *
- *    Get next item in hashbin. A series of hashbin_get_next() calls must
- *    be started by a call to hashbin_get_first(). The function returns
- *    NULL when all items have been traversed
- *
- * The context of the search is stored within the hashbin, so you must
- * protect yourself from concurrent enumerations. - Jean II
- */
-irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
-{
-       irda_queue_t* entry;
-       int bin;
-       int i;
-
-       IRDA_ASSERT( hashbin != NULL, return NULL;);
-       IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
-
-       if ( hashbin->hb_current == NULL) {
-               IRDA_ASSERT( hashbin->hb_current != NULL, return NULL;);
-               return NULL;
-       }
-       entry = hashbin->hb_current->q_next;
-       bin = GET_HASHBIN( entry->q_hash);
-
-       /*
-        *  Make sure that we are not back at the beginning of the queue
-        *  again
-        */
-       if ( entry != hashbin->hb_queue[ bin ]) {
-               hashbin->hb_current = entry;
-
-               return entry;
-       }
-
-       /*
-        *  Check that this is not the last queue in hashbin
-        */
-       if ( bin >= HASHBIN_SIZE)
-               return NULL;
-
-       /*
-        *  Move to next queue in hashbin
-        */
-       bin++;
-       for ( i = bin; i < HASHBIN_SIZE; i++ ) {
-               entry = hashbin->hb_queue[ i];
-               if ( entry) {
-                       hashbin->hb_current = entry;
-
-                       return entry;
-               }
-       }
-       return NULL;
-}
-EXPORT_SYMBOL(hashbin_get_next);
diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c
deleted file mode 100644 (file)
index 873da5e..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irsysctl.c
- * Version:       1.0
- * Description:   Sysctl interface for IrDA
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun May 24 22:12:06 1998
- * Modified at:   Fri Jun  4 02:50:15 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
- *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/mm.h>
-#include <linux/ctype.h>
-#include <linux/sysctl.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>             /* irda_debug */
-#include <net/irda/irlmp.h>
-#include <net/irda/timer.h>
-#include <net/irda/irias_object.h>
-
-extern int  sysctl_discovery;
-extern int  sysctl_discovery_slots;
-extern int  sysctl_discovery_timeout;
-extern int  sysctl_slot_timeout;
-extern int  sysctl_fast_poll_increase;
-extern char sysctl_devname[];
-extern int  sysctl_max_baud_rate;
-extern unsigned int sysctl_min_tx_turn_time;
-extern unsigned int sysctl_max_tx_data_size;
-extern unsigned int sysctl_max_tx_window;
-extern int  sysctl_max_noreply_time;
-extern int  sysctl_warn_noreply_time;
-extern int  sysctl_lap_keepalive_time;
-
-extern struct irlmp_cb *irlmp;
-
-/* this is needed for the proc_dointvec_minmax - Jean II */
-static int max_discovery_slots = 16;           /* ??? */
-static int min_discovery_slots = 1;
-/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices
- * seems to require it. (from Dag's comment) */
-static int max_slot_timeout = 160;
-static int min_slot_timeout = 20;
-static int max_max_baud_rate = 16000000;       /* See qos.c - IrLAP spec */
-static int min_max_baud_rate = 2400;
-static int max_min_tx_turn_time = 10000;       /* See qos.c - IrLAP spec */
-static int min_min_tx_turn_time;
-static int max_max_tx_data_size = 2048;                /* See qos.c - IrLAP spec */
-static int min_max_tx_data_size = 64;
-static int max_max_tx_window = 7;              /* See qos.c - IrLAP spec */
-static int min_max_tx_window = 1;
-static int max_max_noreply_time = 40;          /* See qos.c - IrLAP spec */
-static int min_max_noreply_time = 3;
-static int max_warn_noreply_time = 3;          /* 3s == standard */
-static int min_warn_noreply_time = 1;          /* 1s == min WD_TIMER */
-static int max_lap_keepalive_time = 10000;     /* 10s */
-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(struct ctl_table *table, int write,
-                     void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-       int ret;
-
-       ret = proc_dostring(table, write, buffer, lenp, ppos);
-       if (ret == 0 && write) {
-               struct ias_value *val;
-
-               val = irias_new_string_value(sysctl_devname);
-               if (val)
-                       irias_object_change_attribute("Device", "DeviceName", val);
-       }
-       return ret;
-}
-
-
-static int do_discovery(struct ctl_table *table, int write,
-                    void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-       int ret;
-
-       ret = proc_dointvec(table, write, buffer, lenp, ppos);
-       if (ret)
-              return ret;
-
-       if (irlmp == NULL)
-              return -ENODEV;
-
-       if (sysctl_discovery)
-              irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout*HZ);
-       else
-              del_timer_sync(&irlmp->discovery_timer);
-
-       return ret;
-}
-
-/* One file */
-static struct ctl_table irda_table[] = {
-       {
-               .procname       = "discovery",
-               .data           = &sysctl_discovery,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = do_discovery,
-       },
-       {
-               .procname       = "devname",
-               .data           = sysctl_devname,
-               .maxlen         = 65,
-               .mode           = 0644,
-               .proc_handler   = do_devname,
-       },
-#ifdef CONFIG_IRDA_FAST_RR
-       {
-               .procname       = "fast_poll_increase",
-               .data           = &sysctl_fast_poll_increase,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec
-       },
-#endif
-       {
-               .procname       = "discovery_slots",
-               .data           = &sysctl_discovery_slots,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_discovery_slots,
-               .extra2         = &max_discovery_slots
-       },
-       {
-               .procname       = "discovery_timeout",
-               .data           = &sysctl_discovery_timeout,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec
-       },
-       {
-               .procname       = "slot_timeout",
-               .data           = &sysctl_slot_timeout,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_slot_timeout,
-               .extra2         = &max_slot_timeout
-       },
-       {
-               .procname       = "max_baud_rate",
-               .data           = &sysctl_max_baud_rate,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_max_baud_rate,
-               .extra2         = &max_max_baud_rate
-       },
-       {
-               .procname       = "min_tx_turn_time",
-               .data           = &sysctl_min_tx_turn_time,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_min_tx_turn_time,
-               .extra2         = &max_min_tx_turn_time
-       },
-       {
-               .procname       = "max_tx_data_size",
-               .data           = &sysctl_max_tx_data_size,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_max_tx_data_size,
-               .extra2         = &max_max_tx_data_size
-       },
-       {
-               .procname       = "max_tx_window",
-               .data           = &sysctl_max_tx_window,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_max_tx_window,
-               .extra2         = &max_max_tx_window
-       },
-       {
-               .procname       = "max_noreply_time",
-               .data           = &sysctl_max_noreply_time,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_max_noreply_time,
-               .extra2         = &max_max_noreply_time
-       },
-       {
-               .procname       = "warn_noreply_time",
-               .data           = &sysctl_warn_noreply_time,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_warn_noreply_time,
-               .extra2         = &max_warn_noreply_time
-       },
-       {
-               .procname       = "lap_keepalive_time",
-               .data           = &sysctl_lap_keepalive_time,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_lap_keepalive_time,
-               .extra2         = &max_lap_keepalive_time
-       },
-       { }
-};
-
-static struct ctl_table_header *irda_table_header;
-
-/*
- * Function irda_sysctl_register (void)
- *
- *    Register our sysctl interface
- *
- */
-int __init irda_sysctl_register(void)
-{
-       irda_table_header = register_net_sysctl(&init_net, "net/irda", irda_table);
-       if (!irda_table_header)
-               return -ENOMEM;
-
-       return 0;
-}
-
-/*
- * Function irda_sysctl_unregister (void)
- *
- *    Unregister our sysctl interface
- *
- */
-void irda_sysctl_unregister(void)
-{
-       unregister_net_sysctl_table(irda_table_header);
-}
-
-
-
diff --git a/net/irda/irttp.c b/net/irda/irttp.c
deleted file mode 100644 (file)
index b6ab41d..0000000
+++ /dev/null
@@ -1,1891 +0,0 @@
-/*********************************************************************
- *
- * Filename:      irttp.c
- * Version:       1.2
- * Description:   Tiny Transport Protocol (TTP) implementation
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sun Aug 31 20:14:31 1997
- * Modified at:   Wed Jan  5 11:31:27 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/parameters.h>
-#include <net/irda/irttp.h>
-
-static struct irttp_cb *irttp;
-
-static void __irttp_close_tsap(struct tsap_cb *self);
-
-static int irttp_data_indication(void *instance, void *sap,
-                                struct sk_buff *skb);
-static int irttp_udata_indication(void *instance, void *sap,
-                                 struct sk_buff *skb);
-static void irttp_disconnect_indication(void *instance, void *sap,
-                                       LM_REASON reason, struct sk_buff *);
-static void irttp_connect_indication(void *instance, void *sap,
-                                    struct qos_info *qos, __u32 max_sdu_size,
-                                    __u8 header_size, struct sk_buff *skb);
-static void irttp_connect_confirm(void *instance, void *sap,
-                                 struct qos_info *qos, __u32 max_sdu_size,
-                                 __u8 header_size, struct sk_buff *skb);
-static void irttp_run_tx_queue(struct tsap_cb *self);
-static void irttp_run_rx_queue(struct tsap_cb *self);
-
-static void irttp_flush_queues(struct tsap_cb *self);
-static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb);
-static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
-static void irttp_todo_expired(unsigned long data);
-static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
-                                   int get);
-
-static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow);
-static void irttp_status_indication(void *instance,
-                                   LINK_STATUS link, LOCK_STATUS lock);
-
-/* Information for parsing parameters in IrTTP */
-static const pi_minor_info_t pi_minor_call_table[] = {
-       { NULL, 0 },                                             /* 0x00 */
-       { irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */
-};
-static const pi_major_info_t pi_major_call_table[] = {
-       { pi_minor_call_table, 2 }
-};
-static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 };
-
-/************************ GLOBAL PROCEDURES ************************/
-
-/*
- * Function irttp_init (void)
- *
- *    Initialize the IrTTP layer. Called by module initialization code
- *
- */
-int __init irttp_init(void)
-{
-       irttp = kzalloc(sizeof(struct irttp_cb), GFP_KERNEL);
-       if (irttp == NULL)
-               return -ENOMEM;
-
-       irttp->magic = TTP_MAGIC;
-
-       irttp->tsaps = hashbin_new(HB_LOCK);
-       if (!irttp->tsaps) {
-               net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n",
-                                   __func__);
-               kfree(irttp);
-               return -ENOMEM;
-       }
-
-       return 0;
-}
-
-/*
- * Function irttp_cleanup (void)
- *
- *    Called by module destruction/cleanup code
- *
- */
-void irttp_cleanup(void)
-{
-       /* Check for main structure */
-       IRDA_ASSERT(irttp->magic == TTP_MAGIC, return;);
-
-       /*
-        *  Delete hashbin and close all TSAP instances in it
-        */
-       hashbin_delete(irttp->tsaps, (FREE_FUNC) __irttp_close_tsap);
-
-       irttp->magic = 0;
-
-       /* De-allocate main structure */
-       kfree(irttp);
-
-       irttp = NULL;
-}
-
-/*************************** SUBROUTINES ***************************/
-
-/*
- * Function irttp_start_todo_timer (self, timeout)
- *
- *    Start todo timer.
- *
- * Made it more effient and unsensitive to race conditions - Jean II
- */
-static inline void irttp_start_todo_timer(struct tsap_cb *self, int timeout)
-{
-       /* Set new value for timer */
-       mod_timer(&self->todo_timer, jiffies + timeout);
-}
-
-/*
- * Function irttp_todo_expired (data)
- *
- *    Todo timer has expired!
- *
- * One of the restriction of the timer is that it is run only on the timer
- * interrupt which run every 10ms. This mean that even if you set the timer
- * with a delay of 0, it may take up to 10ms before it's run.
- * So, to minimise latency and keep cache fresh, we try to avoid using
- * it as much as possible.
- * Note : we can't use tasklets, because they can't be asynchronously
- * killed (need user context), and we can't guarantee that here...
- * Jean II
- */
-static void irttp_todo_expired(unsigned long data)
-{
-       struct tsap_cb *self = (struct tsap_cb *) data;
-
-       /* Check that we still exist */
-       if (!self || self->magic != TTP_TSAP_MAGIC)
-               return;
-
-       pr_debug("%s(instance=%p)\n", __func__, self);
-
-       /* Try to make some progress, especially on Tx side - Jean II */
-       irttp_run_rx_queue(self);
-       irttp_run_tx_queue(self);
-
-       /* Check if time for disconnect */
-       if (test_bit(0, &self->disconnect_pend)) {
-               /* Check if it's possible to disconnect yet */
-               if (skb_queue_empty(&self->tx_queue)) {
-                       /* Make sure disconnect is not pending anymore */
-                       clear_bit(0, &self->disconnect_pend);   /* FALSE */
-
-                       /* Note : self->disconnect_skb may be NULL */
-                       irttp_disconnect_request(self, self->disconnect_skb,
-                                                P_NORMAL);
-                       self->disconnect_skb = NULL;
-               } else {
-                       /* Try again later */
-                       irttp_start_todo_timer(self, HZ/10);
-
-                       /* No reason to try and close now */
-                       return;
-               }
-       }
-
-       /* Check if it's closing time */
-       if (self->close_pend)
-               /* Finish cleanup */
-               irttp_close_tsap(self);
-}
-
-/*
- * Function irttp_flush_queues (self)
- *
- *     Flushes (removes all frames) in transitt-buffer (tx_list)
- */
-static void irttp_flush_queues(struct tsap_cb *self)
-{
-       struct sk_buff *skb;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
-       /* Deallocate frames waiting to be sent */
-       while ((skb = skb_dequeue(&self->tx_queue)) != NULL)
-               dev_kfree_skb(skb);
-
-       /* Deallocate received frames */
-       while ((skb = skb_dequeue(&self->rx_queue)) != NULL)
-               dev_kfree_skb(skb);
-
-       /* Deallocate received fragments */
-       while ((skb = skb_dequeue(&self->rx_fragments)) != NULL)
-               dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_reassemble (self)
- *
- *    Makes a new (continuous) skb of all the fragments in the fragment
- *    queue
- *
- */
-static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
-{
-       struct sk_buff *skb, *frag;
-       int n = 0;  /* Fragment index */
-
-       IRDA_ASSERT(self != NULL, return NULL;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;);
-
-       pr_debug("%s(), self->rx_sdu_size=%d\n", __func__,
-                self->rx_sdu_size);
-
-       skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size);
-       if (!skb)
-               return NULL;
-
-       /*
-        * Need to reserve space for TTP header in case this skb needs to
-        * be requeued in case delivery failes
-        */
-       skb_reserve(skb, TTP_HEADER);
-       skb_put(skb, self->rx_sdu_size);
-
-       /*
-        *  Copy all fragments to a new buffer
-        */
-       while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) {
-               skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len);
-               n += frag->len;
-
-               dev_kfree_skb(frag);
-       }
-
-       pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n",
-                __func__, n, self->rx_sdu_size, self->rx_max_sdu_size);
-       /* Note : irttp_run_rx_queue() calculate self->rx_sdu_size
-        * by summing the size of all fragments, so we should always
-        * have n == self->rx_sdu_size, except in cases where we
-        * droped the last fragment (when self->rx_sdu_size exceed
-        * self->rx_max_sdu_size), where n < self->rx_sdu_size.
-        * Jean II */
-       IRDA_ASSERT(n <= self->rx_sdu_size, n = self->rx_sdu_size;);
-
-       /* Set the new length */
-       skb_trim(skb, n);
-
-       self->rx_sdu_size = 0;
-
-       return skb;
-}
-
-/*
- * Function irttp_fragment_skb (skb)
- *
- *    Fragments a frame and queues all the fragments for transmission
- *
- */
-static inline void irttp_fragment_skb(struct tsap_cb *self,
-                                     struct sk_buff *skb)
-{
-       struct sk_buff *frag;
-       __u8 *frame;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       /*
-        *  Split frame into a number of segments
-        */
-       while (skb->len > self->max_seg_size) {
-               pr_debug("%s(), fragmenting ...\n", __func__);
-
-               /* Make new segment */
-               frag = alloc_skb(self->max_seg_size+self->max_header_size,
-                                GFP_ATOMIC);
-               if (!frag)
-                       return;
-
-               skb_reserve(frag, self->max_header_size);
-
-               /* Copy data from the original skb into this fragment. */
-               skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size),
-                             self->max_seg_size);
-
-               /* Insert TTP header, with the more bit set */
-               frame = skb_push(frag, TTP_HEADER);
-               frame[0] = TTP_MORE;
-
-               /* Hide the copied data from the original skb */
-               skb_pull(skb, self->max_seg_size);
-
-               /* Queue fragment */
-               skb_queue_tail(&self->tx_queue, frag);
-       }
-       /* Queue what is left of the original skb */
-       pr_debug("%s(), queuing last segment\n", __func__);
-
-       frame = skb_push(skb, TTP_HEADER);
-       frame[0] = 0x00; /* Clear more bit */
-
-       /* Queue fragment */
-       skb_queue_tail(&self->tx_queue, skb);
-}
-
-/*
- * Function irttp_param_max_sdu_size (self, param)
- *
- *    Handle the MaxSduSize parameter in the connect frames, this function
- *    will be called both when this parameter needs to be inserted into, and
- *    extracted from the connect frames
- */
-static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
-                                   int get)
-{
-       struct tsap_cb *self;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->tx_max_sdu_size;
-       else
-               self->tx_max_sdu_size = param->pv.i;
-
-       pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i);
-
-       return 0;
-}
-
-/*************************** CLIENT CALLS ***************************/
-/************************** LMP CALLBACKS **************************/
-/* Everything is happily mixed up. Waiting for next clean up - Jean II */
-
-/*
- * Initialization, that has to be done on new tsap
- * instance allocation and on duplication
- */
-static void irttp_init_tsap(struct tsap_cb *tsap)
-{
-       spin_lock_init(&tsap->lock);
-       init_timer(&tsap->todo_timer);
-
-       skb_queue_head_init(&tsap->rx_queue);
-       skb_queue_head_init(&tsap->tx_queue);
-       skb_queue_head_init(&tsap->rx_fragments);
-}
-
-/*
- * Function irttp_open_tsap (stsap, notify)
- *
- *    Create TSAP connection endpoint,
- */
-struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
-{
-       struct tsap_cb *self;
-       struct lsap_cb *lsap;
-       notify_t ttp_notify;
-
-       IRDA_ASSERT(irttp->magic == TTP_MAGIC, return NULL;);
-
-       /* The IrLMP spec (IrLMP 1.1 p10) says that we have the right to
-        * use only 0x01-0x6F. Of course, we can use LSAP_ANY as well.
-        * JeanII */
-       if ((stsap_sel != LSAP_ANY) &&
-          ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) {
-               pr_debug("%s(), invalid tsap!\n", __func__);
-               return NULL;
-       }
-
-       self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC);
-       if (self == NULL)
-               return NULL;
-
-       /* Initialize internal objects */
-       irttp_init_tsap(self);
-
-       /* Initialise todo timer */
-       self->todo_timer.data     = (unsigned long) self;
-       self->todo_timer.function = &irttp_todo_expired;
-
-       /* Initialize callbacks for IrLMP to use */
-       irda_notify_init(&ttp_notify);
-       ttp_notify.connect_confirm = irttp_connect_confirm;
-       ttp_notify.connect_indication = irttp_connect_indication;
-       ttp_notify.disconnect_indication = irttp_disconnect_indication;
-       ttp_notify.data_indication = irttp_data_indication;
-       ttp_notify.udata_indication = irttp_udata_indication;
-       ttp_notify.flow_indication = irttp_flow_indication;
-       if (notify->status_indication != NULL)
-               ttp_notify.status_indication = irttp_status_indication;
-       ttp_notify.instance = self;
-       strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME);
-
-       self->magic = TTP_TSAP_MAGIC;
-       self->connected = FALSE;
-
-       /*
-        *  Create LSAP at IrLMP layer
-        */
-       lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0);
-       if (lsap == NULL) {
-               pr_debug("%s: unable to allocate LSAP!!\n", __func__);
-               __irttp_close_tsap(self);
-               return NULL;
-       }
-
-       /*
-        *  If user specified LSAP_ANY as source TSAP selector, then IrLMP
-        *  will replace it with whatever source selector which is free, so
-        *  the stsap_sel we have might not be valid anymore
-        */
-       self->stsap_sel = lsap->slsap_sel;
-       pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel);
-
-       self->notify = *notify;
-       self->lsap = lsap;
-
-       hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (long) self, NULL);
-
-       if (credit > TTP_RX_MAX_CREDIT)
-               self->initial_credit = TTP_RX_MAX_CREDIT;
-       else
-               self->initial_credit = credit;
-
-       return self;
-}
-EXPORT_SYMBOL(irttp_open_tsap);
-
-/*
- * Function irttp_close (handle)
- *
- *    Remove an instance of a TSAP. This function should only deal with the
- *    deallocation of the TSAP, and resetting of the TSAPs values;
- *
- */
-static void __irttp_close_tsap(struct tsap_cb *self)
-{
-       /* First make sure we're connected. */
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
-       irttp_flush_queues(self);
-
-       del_timer(&self->todo_timer);
-
-       /* This one won't be cleaned up if we are disconnect_pend + close_pend
-        * and we receive a disconnect_indication */
-       if (self->disconnect_skb)
-               dev_kfree_skb(self->disconnect_skb);
-
-       self->connected = FALSE;
-       self->magic = ~TTP_TSAP_MAGIC;
-
-       kfree(self);
-}
-
-/*
- * Function irttp_close (self)
- *
- *    Remove TSAP from list of all TSAPs and then deallocate all resources
- *    associated with this TSAP
- *
- * Note : because we *free* the tsap structure, it is the responsibility
- * of the caller to make sure we are called only once and to deal with
- * possible race conditions. - Jean II
- */
-int irttp_close_tsap(struct tsap_cb *self)
-{
-       struct tsap_cb *tsap;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
-       /* Make sure tsap has been disconnected */
-       if (self->connected) {
-               /* Check if disconnect is not pending */
-               if (!test_bit(0, &self->disconnect_pend)) {
-                       net_warn_ratelimited("%s: TSAP still connected!\n",
-                                            __func__);
-                       irttp_disconnect_request(self, NULL, P_NORMAL);
-               }
-               self->close_pend = TRUE;
-               irttp_start_todo_timer(self, HZ/10);
-
-               return 0; /* Will be back! */
-       }
-
-       tsap = hashbin_remove(irttp->tsaps, (long) self, NULL);
-
-       IRDA_ASSERT(tsap == self, return -1;);
-
-       /* Close corresponding LSAP */
-       if (self->lsap) {
-               irlmp_close_lsap(self->lsap);
-               self->lsap = NULL;
-       }
-
-       __irttp_close_tsap(self);
-
-       return 0;
-}
-EXPORT_SYMBOL(irttp_close_tsap);
-
-/*
- * Function irttp_udata_request (self, skb)
- *
- *    Send unreliable data on this TSAP
- *
- */
-int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb)
-{
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       /* Take shortcut on zero byte packets */
-       if (skb->len == 0) {
-               ret = 0;
-               goto err;
-       }
-
-       /* Check that nothing bad happens */
-       if (!self->connected) {
-               net_warn_ratelimited("%s(), Not connected\n", __func__);
-               ret = -ENOTCONN;
-               goto err;
-       }
-
-       if (skb->len > self->max_seg_size) {
-               net_err_ratelimited("%s(), UData is too large for IrLAP!\n",
-                                   __func__);
-               ret = -EMSGSIZE;
-               goto err;
-       }
-
-       irlmp_udata_request(self->lsap, skb);
-       self->stats.tx_packets++;
-
-       return 0;
-
-err:
-       dev_kfree_skb(skb);
-       return ret;
-}
-EXPORT_SYMBOL(irttp_udata_request);
-
-
-/*
- * Function irttp_data_request (handle, skb)
- *
- *    Queue frame for transmission. If SAR is enabled, fragement the frame
- *    and queue the fragments for transmission
- */
-int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
-{
-       __u8 *frame;
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       pr_debug("%s() : queue len = %d\n", __func__,
-                skb_queue_len(&self->tx_queue));
-
-       /* Take shortcut on zero byte packets */
-       if (skb->len == 0) {
-               ret = 0;
-               goto err;
-       }
-
-       /* Check that nothing bad happens */
-       if (!self->connected) {
-               net_warn_ratelimited("%s: Not connected\n", __func__);
-               ret = -ENOTCONN;
-               goto err;
-       }
-
-       /*
-        *  Check if SAR is disabled, and the frame is larger than what fits
-        *  inside an IrLAP frame
-        */
-       if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) {
-               net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n",
-                                   __func__);
-               ret = -EMSGSIZE;
-               goto err;
-       }
-
-       /*
-        *  Check if SAR is enabled, and the frame is larger than the
-        *  TxMaxSduSize
-        */
-       if ((self->tx_max_sdu_size != 0) &&
-           (self->tx_max_sdu_size != TTP_SAR_UNBOUND) &&
-           (skb->len > self->tx_max_sdu_size)) {
-               net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n",
-                                   __func__);
-               ret = -EMSGSIZE;
-               goto err;
-       }
-       /*
-        *  Check if transmit queue is full
-        */
-       if (skb_queue_len(&self->tx_queue) >= TTP_TX_MAX_QUEUE) {
-               /*
-                *  Give it a chance to empty itself
-                */
-               irttp_run_tx_queue(self);
-
-               /* Drop packet. This error code should trigger the caller
-                * to resend the data in the client code - Jean II */
-               ret = -ENOBUFS;
-               goto err;
-       }
-
-       /* Queue frame, or queue frame segments */
-       if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) {
-               /* Queue frame */
-               IRDA_ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;);
-               frame = skb_push(skb, TTP_HEADER);
-               frame[0] = 0x00; /* Clear more bit */
-
-               skb_queue_tail(&self->tx_queue, skb);
-       } else {
-               /*
-                *  Fragment the frame, this function will also queue the
-                *  fragments, we don't care about the fact the transmit
-                *  queue may be overfilled by all the segments for a little
-                *  while
-                */
-               irttp_fragment_skb(self, skb);
-       }
-
-       /* Check if we can accept more data from client */
-       if ((!self->tx_sdu_busy) &&
-           (skb_queue_len(&self->tx_queue) > TTP_TX_HIGH_THRESHOLD)) {
-               /* Tx queue filling up, so stop client. */
-               if (self->notify.flow_indication) {
-                       self->notify.flow_indication(self->notify.instance,
-                                                    self, FLOW_STOP);
-               }
-               /* self->tx_sdu_busy is the state of the client.
-                * Update state after notifying client to avoid
-                * race condition with irttp_flow_indication().
-                * If the queue empty itself after our test but before
-                * we set the flag, we will fix ourselves below in
-                * irttp_run_tx_queue().
-                * Jean II */
-               self->tx_sdu_busy = TRUE;
-       }
-
-       /* Try to make some progress */
-       irttp_run_tx_queue(self);
-
-       return 0;
-
-err:
-       dev_kfree_skb(skb);
-       return ret;
-}
-EXPORT_SYMBOL(irttp_data_request);
-
-/*
- * Function irttp_run_tx_queue (self)
- *
- *    Transmit packets queued for transmission (if possible)
- *
- */
-static void irttp_run_tx_queue(struct tsap_cb *self)
-{
-       struct sk_buff *skb;
-       unsigned long flags;
-       int n;
-
-       pr_debug("%s() : send_credit = %d, queue_len = %d\n",
-                __func__,
-                self->send_credit, skb_queue_len(&self->tx_queue));
-
-       /* Get exclusive access to the tx queue, otherwise don't touch it */
-       if (irda_lock(&self->tx_queue_lock) == FALSE)
-               return;
-
-       /* Try to send out frames as long as we have credits
-        * and as long as LAP is not full. If LAP is full, it will
-        * poll us through irttp_flow_indication() - Jean II */
-       while ((self->send_credit > 0) &&
-              (!irlmp_lap_tx_queue_full(self->lsap)) &&
-              (skb = skb_dequeue(&self->tx_queue))) {
-               /*
-                *  Since we can transmit and receive frames concurrently,
-                *  the code below is a critical region and we must assure that
-                *  nobody messes with the credits while we update them.
-                */
-               spin_lock_irqsave(&self->lock, flags);
-
-               n = self->avail_credit;
-               self->avail_credit = 0;
-
-               /* Only room for 127 credits in frame */
-               if (n > 127) {
-                       self->avail_credit = n-127;
-                       n = 127;
-               }
-               self->remote_credit += n;
-               self->send_credit--;
-
-               spin_unlock_irqrestore(&self->lock, flags);
-
-               /*
-                *  More bit must be set by the data_request() or fragment()
-                *  functions
-                */
-               skb->data[0] |= (n & 0x7f);
-
-               /* Detach from socket.
-                * The current skb has a reference to the socket that sent
-                * it (skb->sk). When we pass it to IrLMP, the skb will be
-                * stored in in IrLAP (self->wx_list). When we are within
-                * IrLAP, we lose the notion of socket, so we should not
-                * have a reference to a socket. So, we drop it here.
-                *
-                * Why does it matter ?
-                * When the skb is freed (kfree_skb), if it is associated
-                * with a socket, it release buffer space on the socket
-                * (through sock_wfree() and sock_def_write_space()).
-                * If the socket no longer exist, we may crash. Hard.
-                * When we close a socket, we make sure that associated packets
-                * in IrTTP are freed. However, we have no way to cancel
-                * the packet that we have passed to IrLAP. So, if a packet
-                * remains in IrLAP (retry on the link or else) after we
-                * close the socket, we are dead !
-                * Jean II */
-               if (skb->sk != NULL) {
-                       /* IrSOCK application, IrOBEX, ... */
-                       skb_orphan(skb);
-               }
-                       /* IrCOMM over IrTTP, IrLAN, ... */
-
-               /* Pass the skb to IrLMP - done */
-               irlmp_data_request(self->lsap, skb);
-               self->stats.tx_packets++;
-       }
-
-       /* Check if we can accept more frames from client.
-        * We don't want to wait until the todo timer to do that, and we
-        * can't use tasklets (grr...), so we are obliged to give control
-        * to client. That's ok, this test will be true not too often
-        * (max once per LAP window) and we are called from places
-        * where we can spend a bit of time doing stuff. - Jean II */
-       if ((self->tx_sdu_busy) &&
-           (skb_queue_len(&self->tx_queue) < TTP_TX_LOW_THRESHOLD) &&
-           (!self->close_pend)) {
-               if (self->notify.flow_indication)
-                       self->notify.flow_indication(self->notify.instance,
-                                                    self, FLOW_START);
-
-               /* self->tx_sdu_busy is the state of the client.
-                * We don't really have a race here, but it's always safer
-                * to update our state after the client - Jean II */
-               self->tx_sdu_busy = FALSE;
-       }
-
-       /* Reset lock */
-       self->tx_queue_lock = 0;
-}
-
-/*
- * Function irttp_give_credit (self)
- *
- *    Send a dataless flowdata TTP-PDU and give available credit to peer
- *    TSAP
- */
-static inline void irttp_give_credit(struct tsap_cb *self)
-{
-       struct sk_buff *tx_skb = NULL;
-       unsigned long flags;
-       int n;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
-       pr_debug("%s() send=%d,avail=%d,remote=%d\n",
-                __func__,
-                self->send_credit, self->avail_credit, self->remote_credit);
-
-       /* Give credit to peer */
-       tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC);
-       if (!tx_skb)
-               return;
-
-       /* Reserve space for LMP, and LAP header */
-       skb_reserve(tx_skb, LMP_MAX_HEADER);
-
-       /*
-        *  Since we can transmit and receive frames concurrently,
-        *  the code below is a critical region and we must assure that
-        *  nobody messes with the credits while we update them.
-        */
-       spin_lock_irqsave(&self->lock, flags);
-
-       n = self->avail_credit;
-       self->avail_credit = 0;
-
-       /* Only space for 127 credits in frame */
-       if (n > 127) {
-               self->avail_credit = n - 127;
-               n = 127;
-       }
-       self->remote_credit += n;
-
-       spin_unlock_irqrestore(&self->lock, flags);
-
-       skb_put(tx_skb, 1);
-       tx_skb->data[0] = (__u8) (n & 0x7f);
-
-       irlmp_data_request(self->lsap, tx_skb);
-       self->stats.tx_packets++;
-}
-
-/*
- * Function irttp_udata_indication (instance, sap, skb)
- *
- *    Received some unit-data (unreliable)
- *
- */
-static int irttp_udata_indication(void *instance, void *sap,
-                                 struct sk_buff *skb)
-{
-       struct tsap_cb *self;
-       int err;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-       IRDA_ASSERT(skb != NULL, return -1;);
-
-       self->stats.rx_packets++;
-
-       /* Just pass data to layer above */
-       if (self->notify.udata_indication) {
-               err = self->notify.udata_indication(self->notify.instance,
-                                                   self, skb);
-               /* Same comment as in irttp_do_data_indication() */
-               if (!err)
-                       return 0;
-       }
-       /* Either no handler, or handler returns an error */
-       dev_kfree_skb(skb);
-
-       return 0;
-}
-
-/*
- * Function irttp_data_indication (instance, sap, skb)
- *
- *    Receive segment from IrLMP.
- *
- */
-static int irttp_data_indication(void *instance, void *sap,
-                                struct sk_buff *skb)
-{
-       struct tsap_cb *self;
-       unsigned long flags;
-       int n;
-
-       self = instance;
-
-       n = skb->data[0] & 0x7f;     /* Extract the credits */
-
-       self->stats.rx_packets++;
-
-       /*  Deal with inbound credit
-        *  Since we can transmit and receive frames concurrently,
-        *  the code below is a critical region and we must assure that
-        *  nobody messes with the credits while we update them.
-        */
-       spin_lock_irqsave(&self->lock, flags);
-       self->send_credit += n;
-       if (skb->len > 1)
-               self->remote_credit--;
-       spin_unlock_irqrestore(&self->lock, flags);
-
-       /*
-        *  Data or dataless packet? Dataless frames contains only the
-        *  TTP_HEADER.
-        */
-       if (skb->len > 1) {
-               /*
-                *  We don't remove the TTP header, since we must preserve the
-                *  more bit, so the defragment routing knows what to do
-                */
-               skb_queue_tail(&self->rx_queue, skb);
-       } else {
-               /* Dataless flowdata TTP-PDU */
-               dev_kfree_skb(skb);
-       }
-
-
-       /* Push data to the higher layer.
-        * We do it synchronously because running the todo timer for each
-        * receive packet would be too much overhead and latency.
-        * By passing control to the higher layer, we run the risk that
-        * it may take time or grab a lock. Most often, the higher layer
-        * will only put packet in a queue.
-        * Anyway, packets are only dripping through the IrDA, so we can
-        * have time before the next packet.
-        * Further, we are run from NET_BH, so the worse that can happen is
-        * us missing the optimal time to send back the PF bit in LAP.
-        * Jean II */
-       irttp_run_rx_queue(self);
-
-       /* We now give credits to peer in irttp_run_rx_queue().
-        * We need to send credit *NOW*, otherwise we are going
-        * to miss the next Tx window. The todo timer may take
-        * a while before it's run... - Jean II */
-
-       /*
-        * If the peer device has given us some credits and we didn't have
-        * anyone from before, then we need to shedule the tx queue.
-        * We need to do that because our Tx have stopped (so we may not
-        * get any LAP flow indication) and the user may be stopped as
-        * well. - Jean II
-        */
-       if (self->send_credit == n) {
-               /* Restart pushing stuff to LAP */
-               irttp_run_tx_queue(self);
-               /* Note : we don't want to schedule the todo timer
-                * because it has horrible latency. No tasklets
-                * because the tasklet API is broken. - Jean II */
-       }
-
-       return 0;
-}
-
-/*
- * Function irttp_status_indication (self, reason)
- *
- *    Status_indication, just pass to the higher layer...
- *
- */
-static void irttp_status_indication(void *instance,
-                                   LINK_STATUS link, LOCK_STATUS lock)
-{
-       struct tsap_cb *self;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
-       /* Check if client has already closed the TSAP and gone away */
-       if (self->close_pend)
-               return;
-
-       /*
-        *  Inform service user if he has requested it
-        */
-       if (self->notify.status_indication != NULL)
-               self->notify.status_indication(self->notify.instance,
-                                              link, lock);
-       else
-               pr_debug("%s(), no handler\n", __func__);
-}
-
-/*
- * Function irttp_flow_indication (self, reason)
- *
- *    Flow_indication : IrLAP tells us to send more data.
- *
- */
-static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
-{
-       struct tsap_cb *self;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
-       pr_debug("%s(instance=%p)\n", __func__, self);
-
-       /* We are "polled" directly from LAP, and the LAP want to fill
-        * its Tx window. We want to do our best to send it data, so that
-        * we maximise the window. On the other hand, we want to limit the
-        * amount of work here so that LAP doesn't hang forever waiting
-        * for packets. - Jean II */
-
-       /* Try to send some packets. Currently, LAP calls us every time
-        * there is one free slot, so we will send only one packet.
-        * This allow the scheduler to do its round robin - Jean II */
-       irttp_run_tx_queue(self);
-
-       /* Note regarding the interraction with higher layer.
-        * irttp_run_tx_queue() may call the client when its queue
-        * start to empty, via notify.flow_indication(). Initially.
-        * I wanted this to happen in a tasklet, to avoid client
-        * grabbing the CPU, but we can't use tasklets safely. And timer
-        * is definitely too slow.
-        * This will happen only once per LAP window, and usually at
-        * the third packet (unless window is smaller). LAP is still
-        * doing mtt and sending first packet so it's sort of OK
-        * to do that. Jean II */
-
-       /* If we need to send disconnect. try to do it now */
-       if (self->disconnect_pend)
-               irttp_start_todo_timer(self, 0);
-}
-
-/*
- * Function irttp_flow_request (self, command)
- *
- *    This function could be used by the upper layers to tell IrTTP to stop
- *    delivering frames if the receive queues are starting to get full, or
- *    to tell IrTTP to start delivering frames again.
- */
-void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow)
-{
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
-       switch (flow) {
-       case FLOW_STOP:
-               pr_debug("%s(), flow stop\n", __func__);
-               self->rx_sdu_busy = TRUE;
-               break;
-       case FLOW_START:
-               pr_debug("%s(), flow start\n", __func__);
-               self->rx_sdu_busy = FALSE;
-
-               /* Client say he can accept more data, try to free our
-                * queues ASAP - Jean II */
-               irttp_run_rx_queue(self);
-
-               break;
-       default:
-               pr_debug("%s(), Unknown flow command!\n", __func__);
-       }
-}
-EXPORT_SYMBOL(irttp_flow_request);
-
-/*
- * Function irttp_connect_request (self, dtsap_sel, daddr, qos)
- *
- *    Try to connect to remote destination TSAP selector
- *
- */
-int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
-                         __u32 saddr, __u32 daddr,
-                         struct qos_info *qos, __u32 max_sdu_size,
-                         struct sk_buff *userdata)
-{
-       struct sk_buff *tx_skb;
-       __u8 *frame;
-       __u8 n;
-
-       pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size);
-
-       IRDA_ASSERT(self != NULL, return -EBADR;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;);
-
-       if (self->connected) {
-               if (userdata)
-                       dev_kfree_skb(userdata);
-               return -EISCONN;
-       }
-
-       /* Any userdata supplied? */
-       if (userdata == NULL) {
-               tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER,
-                                  GFP_ATOMIC);
-               if (!tx_skb)
-                       return -ENOMEM;
-
-               /* Reserve space for MUX_CONTROL and LAP header */
-               skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER);
-       } else {
-               tx_skb = userdata;
-               /*
-                *  Check that the client has reserved enough space for
-                *  headers
-                */
-               IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER,
-                       { dev_kfree_skb(userdata); return -1; });
-       }
-
-       /* Initialize connection parameters */
-       self->connected = FALSE;
-       self->avail_credit = 0;
-       self->rx_max_sdu_size = max_sdu_size;
-       self->rx_sdu_size = 0;
-       self->rx_sdu_busy = FALSE;
-       self->dtsap_sel = dtsap_sel;
-
-       n = self->initial_credit;
-
-       self->remote_credit = 0;
-       self->send_credit = 0;
-
-       /*
-        *  Give away max 127 credits for now
-        */
-       if (n > 127) {
-               self->avail_credit = n - 127;
-               n = 127;
-       }
-
-       self->remote_credit = n;
-
-       /* SAR enabled? */
-       if (max_sdu_size > 0) {
-               IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER),
-                       { dev_kfree_skb(tx_skb); return -1; });
-
-               /* Insert SAR parameters */
-               frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER);
-
-               frame[0] = TTP_PARAMETERS | n;
-               frame[1] = 0x04; /* Length */
-               frame[2] = 0x01; /* MaxSduSize */
-               frame[3] = 0x02; /* Value length */
-
-               put_unaligned(cpu_to_be16((__u16) max_sdu_size),
-                             (__be16 *)(frame+4));
-       } else {
-               /* Insert plain TTP header */
-               frame = skb_push(tx_skb, TTP_HEADER);
-
-               /* Insert initial credit in frame */
-               frame[0] = n & 0x7f;
-       }
-
-       /* Connect with IrLMP. No QoS parameters for now */
-       return irlmp_connect_request(self->lsap, dtsap_sel, saddr, daddr, qos,
-                                    tx_skb);
-}
-EXPORT_SYMBOL(irttp_connect_request);
-
-/*
- * Function irttp_connect_confirm (handle, qos, skb)
- *
- *    Service user confirms TSAP connection with peer.
- *
- */
-static void irttp_connect_confirm(void *instance, void *sap,
-                                 struct qos_info *qos, __u32 max_seg_size,
-                                 __u8 max_header_size, struct sk_buff *skb)
-{
-       struct tsap_cb *self;
-       int parameters;
-       int ret;
-       __u8 plen;
-       __u8 n;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       self->max_seg_size = max_seg_size - TTP_HEADER;
-       self->max_header_size = max_header_size + TTP_HEADER;
-
-       /*
-        *  Check if we have got some QoS parameters back! This should be the
-        *  negotiated QoS for the link.
-        */
-       if (qos) {
-               pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n",
-                        qos->baud_rate.bits);
-               pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n",
-                        qos->baud_rate.value);
-       }
-
-       n = skb->data[0] & 0x7f;
-
-       pr_debug("%s(), Initial send_credit=%d\n", __func__, n);
-
-       self->send_credit = n;
-       self->tx_max_sdu_size = 0;
-       self->connected = TRUE;
-
-       parameters = skb->data[0] & 0x80;
-
-       IRDA_ASSERT(skb->len >= TTP_HEADER, return;);
-       skb_pull(skb, TTP_HEADER);
-
-       if (parameters) {
-               plen = skb->data[0];
-
-               ret = irda_param_extract_all(self, skb->data+1,
-                                            IRDA_MIN(skb->len-1, plen),
-                                            &param_info);
-
-               /* Any errors in the parameter list? */
-               if (ret < 0) {
-                       net_warn_ratelimited("%s: error extracting parameters\n",
-                                            __func__);
-                       dev_kfree_skb(skb);
-
-                       /* Do not accept this connection attempt */
-                       return;
-               }
-               /* Remove parameters */
-               skb_pull(skb, IRDA_MIN(skb->len, plen+1));
-       }
-
-       pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__,
-                self->send_credit, self->avail_credit, self->remote_credit);
-
-       pr_debug("%s(), MaxSduSize=%d\n", __func__,
-                self->tx_max_sdu_size);
-
-       if (self->notify.connect_confirm) {
-               self->notify.connect_confirm(self->notify.instance, self, qos,
-                                            self->tx_max_sdu_size,
-                                            self->max_header_size, skb);
-       } else
-               dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_connect_indication (handle, skb)
- *
- *    Some other device is connecting to this TSAP
- *
- */
-static void irttp_connect_indication(void *instance, void *sap,
-               struct qos_info *qos, __u32 max_seg_size, __u8 max_header_size,
-               struct sk_buff *skb)
-{
-       struct tsap_cb *self;
-       struct lsap_cb *lsap;
-       int parameters;
-       int ret;
-       __u8 plen;
-       __u8 n;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-       IRDA_ASSERT(skb != NULL, return;);
-
-       lsap = sap;
-
-       self->max_seg_size = max_seg_size - TTP_HEADER;
-       self->max_header_size = max_header_size+TTP_HEADER;
-
-       pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel);
-
-       /* Need to update dtsap_sel if its equal to LSAP_ANY */
-       self->dtsap_sel = lsap->dlsap_sel;
-
-       n = skb->data[0] & 0x7f;
-
-       self->send_credit = n;
-       self->tx_max_sdu_size = 0;
-
-       parameters = skb->data[0] & 0x80;
-
-       IRDA_ASSERT(skb->len >= TTP_HEADER, return;);
-       skb_pull(skb, TTP_HEADER);
-
-       if (parameters) {
-               plen = skb->data[0];
-
-               ret = irda_param_extract_all(self, skb->data+1,
-                                            IRDA_MIN(skb->len-1, plen),
-                                            &param_info);
-
-               /* Any errors in the parameter list? */
-               if (ret < 0) {
-                       net_warn_ratelimited("%s: error extracting parameters\n",
-                                            __func__);
-                       dev_kfree_skb(skb);
-
-                       /* Do not accept this connection attempt */
-                       return;
-               }
-
-               /* Remove parameters */
-               skb_pull(skb, IRDA_MIN(skb->len, plen+1));
-       }
-
-       if (self->notify.connect_indication) {
-               self->notify.connect_indication(self->notify.instance, self,
-                                               qos, self->tx_max_sdu_size,
-                                               self->max_header_size, skb);
-       } else
-               dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_connect_response (handle, userdata)
- *
- *    Service user is accepting the connection, just pass it down to
- *    IrLMP!
- *
- */
-int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
-                          struct sk_buff *userdata)
-{
-       struct sk_buff *tx_skb;
-       __u8 *frame;
-       int ret;
-       __u8 n;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
-       pr_debug("%s(), Source TSAP selector=%02x\n", __func__,
-                self->stsap_sel);
-
-       /* Any userdata supplied? */
-       if (userdata == NULL) {
-               tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER,
-                                  GFP_ATOMIC);
-               if (!tx_skb)
-                       return -ENOMEM;
-
-               /* Reserve space for MUX_CONTROL and LAP header */
-               skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER);
-       } else {
-               tx_skb = userdata;
-               /*
-                *  Check that the client has reserved enough space for
-                *  headers
-                */
-               IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER,
-                       { dev_kfree_skb(userdata); return -1; });
-       }
-
-       self->avail_credit = 0;
-       self->remote_credit = 0;
-       self->rx_max_sdu_size = max_sdu_size;
-       self->rx_sdu_size = 0;
-       self->rx_sdu_busy = FALSE;
-
-       n = self->initial_credit;
-
-       /* Frame has only space for max 127 credits (7 bits) */
-       if (n > 127) {
-               self->avail_credit = n - 127;
-               n = 127;
-       }
-
-       self->remote_credit = n;
-       self->connected = TRUE;
-
-       /* SAR enabled? */
-       if (max_sdu_size > 0) {
-               IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER),
-                       { dev_kfree_skb(tx_skb); return -1; });
-
-               /* Insert TTP header with SAR parameters */
-               frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER);
-
-               frame[0] = TTP_PARAMETERS | n;
-               frame[1] = 0x04; /* Length */
-
-               /* irda_param_insert(self, IRTTP_MAX_SDU_SIZE, frame+1,  */
-/*                               TTP_SAR_HEADER, &param_info) */
-
-               frame[2] = 0x01; /* MaxSduSize */
-               frame[3] = 0x02; /* Value length */
-
-               put_unaligned(cpu_to_be16((__u16) max_sdu_size),
-                             (__be16 *)(frame+4));
-       } else {
-               /* Insert TTP header */
-               frame = skb_push(tx_skb, TTP_HEADER);
-
-               frame[0] = n & 0x7f;
-       }
-
-       ret = irlmp_connect_response(self->lsap, tx_skb);
-
-       return ret;
-}
-EXPORT_SYMBOL(irttp_connect_response);
-
-/*
- * Function irttp_dup (self, instance)
- *
- *    Duplicate TSAP, can be used by servers to confirm a connection on a
- *    new TSAP so it can keep listening on the old one.
- */
-struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
-{
-       struct tsap_cb *new;
-       unsigned long flags;
-
-       /* Protect our access to the old tsap instance */
-       spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags);
-
-       /* Find the old instance */
-       if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) {
-               pr_debug("%s(), unable to find TSAP\n", __func__);
-               spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
-               return NULL;
-       }
-
-       /* Allocate a new instance */
-       new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC);
-       if (!new) {
-               pr_debug("%s(), unable to kmalloc\n", __func__);
-               spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
-               return NULL;
-       }
-       spin_lock_init(&new->lock);
-
-       /* We don't need the old instance any more */
-       spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
-
-       /* Try to dup the LSAP (may fail if we were too slow) */
-       new->lsap = irlmp_dup(orig->lsap, new);
-       if (!new->lsap) {
-               pr_debug("%s(), dup failed!\n", __func__);
-               kfree(new);
-               return NULL;
-       }
-
-       /* Not everything should be copied */
-       new->notify.instance = instance;
-
-       /* Initialize internal objects */
-       irttp_init_tsap(new);
-
-       /* This is locked */
-       hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL);
-
-       return new;
-}
-EXPORT_SYMBOL(irttp_dup);
-
-/*
- * Function irttp_disconnect_request (self)
- *
- *    Close this connection please! If priority is high, the queued data
- *    segments, if any, will be deallocated first
- *
- */
-int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
-                            int priority)
-{
-       int ret;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
-       /* Already disconnected? */
-       if (!self->connected) {
-               pr_debug("%s(), already disconnected!\n", __func__);
-               if (userdata)
-                       dev_kfree_skb(userdata);
-               return -1;
-       }
-
-       /* Disconnect already pending ?
-        * We need to use an atomic operation to prevent reentry. This
-        * function may be called from various context, like user, timer
-        * for following a disconnect_indication() (i.e. net_bh).
-        * Jean II */
-       if (test_and_set_bit(0, &self->disconnect_pend)) {
-               pr_debug("%s(), disconnect already pending\n",
-                        __func__);
-               if (userdata)
-                       dev_kfree_skb(userdata);
-
-               /* Try to make some progress */
-               irttp_run_tx_queue(self);
-               return -1;
-       }
-
-       /*
-        *  Check if there is still data segments in the transmit queue
-        */
-       if (!skb_queue_empty(&self->tx_queue)) {
-               if (priority == P_HIGH) {
-                       /*
-                        *  No need to send the queued data, if we are
-                        *  disconnecting right now since the data will
-                        *  not have any usable connection to be sent on
-                        */
-                       pr_debug("%s(): High priority!!()\n", __func__);
-                       irttp_flush_queues(self);
-               } else if (priority == P_NORMAL) {
-                       /*
-                        *  Must delay disconnect until after all data segments
-                        *  have been sent and the tx_queue is empty
-                        */
-                       /* We'll reuse this one later for the disconnect */
-                       self->disconnect_skb = userdata;  /* May be NULL */
-
-                       irttp_run_tx_queue(self);
-
-                       irttp_start_todo_timer(self, HZ/10);
-                       return -1;
-               }
-       }
-       /* Note : we don't need to check if self->rx_queue is full and the
-        * state of self->rx_sdu_busy because the disconnect response will
-        * be sent at the LMP level (so even if the peer has its Tx queue
-        * full of data). - Jean II */
-
-       pr_debug("%s(), Disconnecting ...\n", __func__);
-       self->connected = FALSE;
-
-       if (!userdata) {
-               struct sk_buff *tx_skb;
-               tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
-               if (!tx_skb)
-                       return -ENOMEM;
-
-               /*
-                *  Reserve space for MUX and LAP header
-                */
-               skb_reserve(tx_skb, LMP_MAX_HEADER);
-
-               userdata = tx_skb;
-       }
-       ret = irlmp_disconnect_request(self->lsap, userdata);
-
-       /* The disconnect is no longer pending */
-       clear_bit(0, &self->disconnect_pend);   /* FALSE */
-
-       return ret;
-}
-EXPORT_SYMBOL(irttp_disconnect_request);
-
-/*
- * Function irttp_disconnect_indication (self, reason)
- *
- *    Disconnect indication, TSAP disconnected by peer?
- *
- */
-static void irttp_disconnect_indication(void *instance, void *sap,
-               LM_REASON reason, struct sk_buff *skb)
-{
-       struct tsap_cb *self;
-
-       self = instance;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
-       /* Prevent higher layer to send more data */
-       self->connected = FALSE;
-
-       /* Check if client has already tried to close the TSAP */
-       if (self->close_pend) {
-               /* In this case, the higher layer is probably gone. Don't
-                * bother it and clean up the remains - Jean II */
-               if (skb)
-                       dev_kfree_skb(skb);
-               irttp_close_tsap(self);
-               return;
-       }
-
-       /* If we are here, we assume that is the higher layer is still
-        * waiting for the disconnect notification and able to process it,
-        * even if he tried to disconnect. Otherwise, it would have already
-        * attempted to close the tsap and self->close_pend would be TRUE.
-        * Jean II */
-
-       /* No need to notify the client if has already tried to disconnect */
-       if (self->notify.disconnect_indication)
-               self->notify.disconnect_indication(self->notify.instance, self,
-                                                  reason, skb);
-       else
-               if (skb)
-                       dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_do_data_indication (self, skb)
- *
- *    Try to deliver reassembled skb to layer above, and requeue it if that
- *    for some reason should fail. We mark rx sdu as busy to apply back
- *    pressure is necessary.
- */
-static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb)
-{
-       int err;
-
-       /* Check if client has already closed the TSAP and gone away */
-       if (self->close_pend) {
-               dev_kfree_skb(skb);
-               return;
-       }
-
-       err = self->notify.data_indication(self->notify.instance, self, skb);
-
-       /* Usually the layer above will notify that it's input queue is
-        * starting to get filled by using the flow request, but this may
-        * be difficult, so it can instead just refuse to eat it and just
-        * give an error back
-        */
-       if (err) {
-               pr_debug("%s() requeueing skb!\n", __func__);
-
-               /* Make sure we take a break */
-               self->rx_sdu_busy = TRUE;
-
-               /* Need to push the header in again */
-               skb_push(skb, TTP_HEADER);
-               skb->data[0] = 0x00; /* Make sure MORE bit is cleared */
-
-               /* Put skb back on queue */
-               skb_queue_head(&self->rx_queue, skb);
-       }
-}
-
-/*
- * Function irttp_run_rx_queue (self)
- *
- *     Check if we have any frames to be transmitted, or if we have any
- *     available credit to give away.
- */
-static void irttp_run_rx_queue(struct tsap_cb *self)
-{
-       struct sk_buff *skb;
-       int more = 0;
-
-       pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__,
-                self->send_credit, self->avail_credit, self->remote_credit);
-
-       /* Get exclusive access to the rx queue, otherwise don't touch it */
-       if (irda_lock(&self->rx_queue_lock) == FALSE)
-               return;
-
-       /*
-        *  Reassemble all frames in receive queue and deliver them
-        */
-       while (!self->rx_sdu_busy && (skb = skb_dequeue(&self->rx_queue))) {
-               /* This bit will tell us if it's the last fragment or not */
-               more = skb->data[0] & 0x80;
-
-               /* Remove TTP header */
-               skb_pull(skb, TTP_HEADER);
-
-               /* Add the length of the remaining data */
-               self->rx_sdu_size += skb->len;
-
-               /*
-                * If SAR is disabled, or user has requested no reassembly
-                * of received fragments then we just deliver them
-                * immediately. This can be requested by clients that
-                * implements byte streams without any message boundaries
-                */
-               if (self->rx_max_sdu_size == TTP_SAR_DISABLE) {
-                       irttp_do_data_indication(self, skb);
-                       self->rx_sdu_size = 0;
-
-                       continue;
-               }
-
-               /* Check if this is a fragment, and not the last fragment */
-               if (more) {
-                       /*
-                        *  Queue the fragment if we still are within the
-                        *  limits of the maximum size of the rx_sdu
-                        */
-                       if (self->rx_sdu_size <= self->rx_max_sdu_size) {
-                               pr_debug("%s(), queueing frag\n",
-                                        __func__);
-                               skb_queue_tail(&self->rx_fragments, skb);
-                       } else {
-                               /* Free the part of the SDU that is too big */
-                               dev_kfree_skb(skb);
-                       }
-                       continue;
-               }
-               /*
-                *  This is the last fragment, so time to reassemble!
-                */
-               if ((self->rx_sdu_size <= self->rx_max_sdu_size) ||
-                   (self->rx_max_sdu_size == TTP_SAR_UNBOUND)) {
-                       /*
-                        * A little optimizing. Only queue the fragment if
-                        * there are other fragments. Since if this is the
-                        * last and only fragment, there is no need to
-                        * reassemble :-)
-                        */
-                       if (!skb_queue_empty(&self->rx_fragments)) {
-                               skb_queue_tail(&self->rx_fragments,
-                                              skb);
-
-                               skb = irttp_reassemble_skb(self);
-                       }
-
-                       /* Now we can deliver the reassembled skb */
-                       irttp_do_data_indication(self, skb);
-               } else {
-                       pr_debug("%s(), Truncated frame\n", __func__);
-
-                       /* Free the part of the SDU that is too big */
-                       dev_kfree_skb(skb);
-
-                       /* Deliver only the valid but truncated part of SDU */
-                       skb = irttp_reassemble_skb(self);
-
-                       irttp_do_data_indication(self, skb);
-               }
-               self->rx_sdu_size = 0;
-       }
-
-       /*
-        * It's not trivial to keep track of how many credits are available
-        * by incrementing at each packet, because delivery may fail
-        * (irttp_do_data_indication() may requeue the frame) and because
-        * we need to take care of fragmentation.
-        * We want the other side to send up to initial_credit packets.
-        * We have some frames in our queues, and we have already allowed it
-        * to send remote_credit.
-        * No need to spinlock, write is atomic and self correcting...
-        * Jean II
-        */
-       self->avail_credit = (self->initial_credit -
-                             (self->remote_credit +
-                              skb_queue_len(&self->rx_queue) +
-                              skb_queue_len(&self->rx_fragments)));
-
-       /* Do we have too much credits to send to peer ? */
-       if ((self->remote_credit <= TTP_RX_MIN_CREDIT) &&
-           (self->avail_credit > 0)) {
-               /* Send explicit credit frame */
-               irttp_give_credit(self);
-               /* Note : do *NOT* check if tx_queue is non-empty, that
-                * will produce deadlocks. I repeat : send a credit frame
-                * even if we have something to send in our Tx queue.
-                * If we have credits, it means that our Tx queue is blocked.
-                *
-                * Let's suppose the peer can't keep up with our Tx. He will
-                * flow control us by not sending us any credits, and we
-                * will stop Tx and start accumulating credits here.
-                * Up to the point where the peer will stop its Tx queue,
-                * for lack of credits.
-                * Let's assume the peer application is single threaded.
-                * It will block on Tx and never consume any Rx buffer.
-                * Deadlock. Guaranteed. - Jean II
-                */
-       }
-
-       /* Reset lock */
-       self->rx_queue_lock = 0;
-}
-
-#ifdef CONFIG_PROC_FS
-struct irttp_iter_state {
-       int id;
-};
-
-static void *irttp_seq_start(struct seq_file *seq, loff_t *pos)
-{
-       struct irttp_iter_state *iter = seq->private;
-       struct tsap_cb *self;
-
-       /* Protect our access to the tsap list */
-       spin_lock_irq(&irttp->tsaps->hb_spinlock);
-       iter->id = 0;
-
-       for (self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps);
-            self != NULL;
-            self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps)) {
-               if (iter->id == *pos)
-                       break;
-               ++iter->id;
-       }
-
-       return self;
-}
-
-static void *irttp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
-       struct irttp_iter_state *iter = seq->private;
-
-       ++*pos;
-       ++iter->id;
-       return (void *) hashbin_get_next(irttp->tsaps);
-}
-
-static void irttp_seq_stop(struct seq_file *seq, void *v)
-{
-       spin_unlock_irq(&irttp->tsaps->hb_spinlock);
-}
-
-static int irttp_seq_show(struct seq_file *seq, void *v)
-{
-       const struct irttp_iter_state *iter = seq->private;
-       const struct tsap_cb *self = v;
-
-       seq_printf(seq, "TSAP %d, ", iter->id);
-       seq_printf(seq, "stsap_sel: %02x, ",
-                  self->stsap_sel);
-       seq_printf(seq, "dtsap_sel: %02x\n",
-                  self->dtsap_sel);
-       seq_printf(seq, "  connected: %s, ",
-                  self->connected ? "TRUE" : "FALSE");
-       seq_printf(seq, "avail credit: %d, ",
-                  self->avail_credit);
-       seq_printf(seq, "remote credit: %d, ",
-                  self->remote_credit);
-       seq_printf(seq, "send credit: %d\n",
-                  self->send_credit);
-       seq_printf(seq, "  tx packets: %lu, ",
-                  self->stats.tx_packets);
-       seq_printf(seq, "rx packets: %lu, ",
-                  self->stats.rx_packets);
-       seq_printf(seq, "tx_queue len: %u ",
-                  skb_queue_len(&self->tx_queue));
-       seq_printf(seq, "rx_queue len: %u\n",
-                  skb_queue_len(&self->rx_queue));
-       seq_printf(seq, "  tx_sdu_busy: %s, ",
-                  self->tx_sdu_busy ? "TRUE" : "FALSE");
-       seq_printf(seq, "rx_sdu_busy: %s\n",
-                  self->rx_sdu_busy ? "TRUE" : "FALSE");
-       seq_printf(seq, "  max_seg_size: %u, ",
-                  self->max_seg_size);
-       seq_printf(seq, "tx_max_sdu_size: %u, ",
-                  self->tx_max_sdu_size);
-       seq_printf(seq, "rx_max_sdu_size: %u\n",
-                  self->rx_max_sdu_size);
-
-       seq_printf(seq, "  Used by (%s)\n\n",
-                  self->notify.name);
-       return 0;
-}
-
-static const struct seq_operations irttp_seq_ops = {
-       .start  = irttp_seq_start,
-       .next   = irttp_seq_next,
-       .stop   = irttp_seq_stop,
-       .show   = irttp_seq_show,
-};
-
-static int irttp_seq_open(struct inode *inode, struct file *file)
-{
-       return seq_open_private(file, &irttp_seq_ops,
-                       sizeof(struct irttp_iter_state));
-}
-
-const struct file_operations irttp_seq_fops = {
-       .owner          = THIS_MODULE,
-       .open           = irttp_seq_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release_private,
-};
-
-#endif /* PROC_FS */
diff --git a/net/irda/parameters.c b/net/irda/parameters.c
deleted file mode 100644 (file)
index 16ce32f..0000000
+++ /dev/null
@@ -1,584 +0,0 @@
-/*********************************************************************
- *
- * Filename:      parameters.c
- * Version:       1.0
- * Description:   A more general way to handle (pi,pl,pv) parameters
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Mon Jun  7 10:25:11 1999
- * Modified at:   Sun Jan 30 14:08:39 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/types.h>
-#include <linux/module.h>
-
-#include <asm/unaligned.h>
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/parameters.h>
-
-static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
-                               PV_TYPE type, PI_HANDLER func);
-static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
-                              PV_TYPE type, PI_HANDLER func);
-static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
-                              PV_TYPE type, PI_HANDLER func);
-static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi,
-                                PV_TYPE type, PI_HANDLER func);
-
-static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
-                              PV_TYPE type, PI_HANDLER func);
-static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
-                               PV_TYPE type, PI_HANDLER func);
-
-static int irda_param_unpack(__u8 *buf, char *fmt, ...);
-
-/* Parameter value call table. Must match PV_TYPE */
-static const PV_HANDLER pv_extract_table[] = {
-       irda_extract_integer, /* Handler for any length integers */
-       irda_extract_integer, /* Handler for 8  bits integers */
-       irda_extract_integer, /* Handler for 16 bits integers */
-       irda_extract_string,  /* Handler for strings */
-       irda_extract_integer, /* Handler for 32 bits integers */
-       irda_extract_octseq,  /* Handler for octet sequences */
-       irda_extract_no_value /* Handler for no value parameters */
-};
-
-static const PV_HANDLER pv_insert_table[] = {
-       irda_insert_integer, /* Handler for any length integers */
-       irda_insert_integer, /* Handler for 8  bits integers */
-       irda_insert_integer, /* Handler for 16 bits integers */
-       NULL,                /* Handler for strings */
-       irda_insert_integer, /* Handler for 32 bits integers */
-       NULL,                /* Handler for octet sequences */
-       irda_insert_no_value /* Handler for no value parameters */
-};
-
-/*
- * Function irda_insert_no_value (self, buf, len, pi, type, func)
- */
-static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
-                               PV_TYPE type, PI_HANDLER func)
-{
-       irda_param_t p;
-       int ret;
-
-       p.pi = pi;
-       p.pl = 0;
-
-       /* Call handler for this parameter */
-       ret = (*func)(self, &p, PV_GET);
-
-       /* Extract values anyway, since handler may need them */
-       irda_param_pack(buf, "bb", p.pi, p.pl);
-
-       if (ret < 0)
-               return ret;
-
-       return 2; /* Inserted pl+2 bytes */
-}
-
-/*
- * Function irda_extract_no_value (self, buf, len, type, func)
- *
- *    Extracts a parameter without a pv field (pl=0)
- *
- */
-static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi,
-                                PV_TYPE type, PI_HANDLER func)
-{
-       irda_param_t p;
-       int ret;
-
-       /* Extract values anyway, since handler may need them */
-       irda_param_unpack(buf, "bb", &p.pi, &p.pl);
-
-       /* Call handler for this parameter */
-       ret = (*func)(self, &p, PV_PUT);
-
-       if (ret < 0)
-               return ret;
-
-       return 2; /* Extracted pl+2 bytes */
-}
-
-/*
- * Function irda_insert_integer (self, buf, len, pi, type, func)
- */
-static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
-                              PV_TYPE type, PI_HANDLER func)
-{
-       irda_param_t p;
-       int n = 0;
-       int err;
-
-       p.pi = pi;             /* In case handler needs to know */
-       p.pl = type & PV_MASK; /* The integer type codes the length as well */
-       p.pv.i = 0;            /* Clear value */
-
-       /* Call handler for this parameter */
-       err = (*func)(self, &p, PV_GET);
-       if (err < 0)
-               return err;
-
-       /*
-        * If parameter length is still 0, then (1) this is an any length
-        * integer, and (2) the handler function does not care which length
-        * we choose to use, so we pick the one the gives the fewest bytes.
-        */
-       if (p.pl == 0) {
-               if (p.pv.i < 0xff) {
-                       pr_debug("%s(), using 1 byte\n", __func__);
-                       p.pl = 1;
-               } else if (p.pv.i < 0xffff) {
-                       pr_debug("%s(), using 2 bytes\n", __func__);
-                       p.pl = 2;
-               } else {
-                       pr_debug("%s(), using 4 bytes\n", __func__);
-                       p.pl = 4; /* Default length */
-               }
-       }
-       /* Check if buffer is long enough for insertion */
-       if (len < (2+p.pl)) {
-               net_warn_ratelimited("%s: buffer too short for insertion!\n",
-                                    __func__);
-               return -1;
-       }
-       pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
-                p.pi, p.pl, p.pv.i);
-       switch (p.pl) {
-       case 1:
-               n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i);
-               break;
-       case 2:
-               if (type & PV_BIG_ENDIAN)
-                       p.pv.i = cpu_to_be16((__u16) p.pv.i);
-               else
-                       p.pv.i = cpu_to_le16((__u16) p.pv.i);
-               n += irda_param_pack(buf, "bbs", p.pi, p.pl, (__u16) p.pv.i);
-               break;
-       case 4:
-               if (type & PV_BIG_ENDIAN)
-                       cpu_to_be32s(&p.pv.i);
-               else
-                       cpu_to_le32s(&p.pv.i);
-               n += irda_param_pack(buf, "bbi", p.pi, p.pl, p.pv.i);
-
-               break;
-       default:
-               net_warn_ratelimited("%s: length %d not supported\n",
-                                    __func__, p.pl);
-               /* Skip parameter */
-               return -1;
-       }
-
-       return p.pl+2; /* Inserted pl+2 bytes */
-}
-
-/*
- * Function irda_extract integer (self, buf, len, pi, type, func)
- *
- *    Extract a possibly variable length integer from buffer, and call
- *    handler for processing of the parameter
- */
-static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
-                               PV_TYPE type, PI_HANDLER func)
-{
-       irda_param_t p;
-       int n = 0;
-       int extract_len;        /* Real length we extract */
-       int err;
-
-       p.pi = pi;     /* In case handler needs to know */
-       p.pl = buf[1]; /* Extract length of value */
-       p.pv.i = 0;    /* Clear value */
-       extract_len = p.pl;     /* Default : extract all */
-
-       /* Check if buffer is long enough for parsing */
-       if (len < (2+p.pl)) {
-               net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
-                                    __func__, p.pl, len);
-               return -1;
-       }
-
-       /*
-        * Check that the integer length is what we expect it to be. If the
-        * handler want a 16 bits integer then a 32 bits is not good enough
-        * PV_INTEGER means that the handler is flexible.
-        */
-       if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) {
-               net_err_ratelimited("%s: invalid parameter length! Expected %d bytes, but value had %d bytes!\n",
-                                   __func__, type & PV_MASK, p.pl);
-
-               /* Most parameters are bit/byte fields or little endian,
-                * so it's ok to only extract a subset of it (the subset
-                * that the handler expect). This is necessary, as some
-                * broken implementations seems to add extra undefined bits.
-                * If the parameter is shorter than we expect or is big
-                * endian, we can't play those tricks. Jean II */
-               if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) {
-                       /* Skip parameter */
-                       return p.pl+2;
-               } else {
-                       /* Extract subset of it, fallthrough */
-                       extract_len = type & PV_MASK;
-               }
-       }
-
-
-       switch (extract_len) {
-       case 1:
-               n += irda_param_unpack(buf+2, "b", &p.pv.i);
-               break;
-       case 2:
-               n += irda_param_unpack(buf+2, "s", &p.pv.i);
-               if (type & PV_BIG_ENDIAN)
-                       p.pv.i = be16_to_cpu((__u16) p.pv.i);
-               else
-                       p.pv.i = le16_to_cpu((__u16) p.pv.i);
-               break;
-       case 4:
-               n += irda_param_unpack(buf+2, "i", &p.pv.i);
-               if (type & PV_BIG_ENDIAN)
-                       be32_to_cpus(&p.pv.i);
-               else
-                       le32_to_cpus(&p.pv.i);
-               break;
-       default:
-               net_warn_ratelimited("%s: length %d not supported\n",
-                                    __func__, p.pl);
-
-               /* Skip parameter */
-               return p.pl+2;
-       }
-
-       pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
-                p.pi, p.pl, p.pv.i);
-       /* Call handler for this parameter */
-       err = (*func)(self, &p, PV_PUT);
-       if (err < 0)
-               return err;
-
-       return p.pl+2; /* Extracted pl+2 bytes */
-}
-
-/*
- * Function irda_extract_string (self, buf, len, type, func)
- */
-static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
-                              PV_TYPE type, PI_HANDLER func)
-{
-       char str[33];
-       irda_param_t p;
-       int err;
-
-       p.pi = pi;     /* In case handler needs to know */
-       p.pl = buf[1]; /* Extract length of value */
-       if (p.pl > 32)
-               p.pl = 32;
-
-       pr_debug("%s(), pi=%#x, pl=%d\n", __func__,
-                p.pi, p.pl);
-
-       /* Check if buffer is long enough for parsing */
-       if (len < (2+p.pl)) {
-               net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
-                                    __func__, p.pl, len);
-               return -1;
-       }
-
-       /* Should be safe to copy string like this since we have already
-        * checked that the buffer is long enough */
-       strncpy(str, buf+2, p.pl);
-
-       pr_debug("%s(), str=0x%02x 0x%02x\n",
-                __func__, (__u8)str[0], (__u8)str[1]);
-
-       /* Null terminate string */
-       str[p.pl] = '\0';
-
-       p.pv.c = str; /* Handler will need to take a copy */
-
-       /* Call handler for this parameter */
-       err = (*func)(self, &p, PV_PUT);
-       if (err < 0)
-               return err;
-
-       return p.pl+2; /* Extracted pl+2 bytes */
-}
-
-/*
- * Function irda_extract_octseq (self, buf, len, type, func)
- */
-static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
-                              PV_TYPE type, PI_HANDLER func)
-{
-       irda_param_t p;
-
-       p.pi = pi;     /* In case handler needs to know */
-       p.pl = buf[1]; /* Extract length of value */
-
-       /* Check if buffer is long enough for parsing */
-       if (len < (2+p.pl)) {
-               net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
-                                    __func__, p.pl, len);
-               return -1;
-       }
-
-       pr_debug("%s(), not impl\n", __func__);
-
-       return p.pl+2; /* Extracted pl+2 bytes */
-}
-
-/*
- * Function irda_param_pack (skb, fmt, ...)
- *
- *    Format:
- *        'i' = 32 bits integer
- *        's' = string
- *
- */
-int irda_param_pack(__u8 *buf, char *fmt, ...)
-{
-       irda_pv_t arg;
-       va_list args;
-       char *p;
-       int n = 0;
-
-       va_start(args, fmt);
-
-       for (p = fmt; *p != '\0'; p++) {
-               switch (*p) {
-               case 'b':  /* 8 bits unsigned byte */
-                       buf[n++] = (__u8)va_arg(args, int);
-                       break;
-               case 's':  /* 16 bits unsigned short */
-                       arg.i = (__u16)va_arg(args, int);
-                       put_unaligned((__u16)arg.i, (__u16 *)(buf+n)); n+=2;
-                       break;
-               case 'i':  /* 32 bits unsigned integer */
-                       arg.i = va_arg(args, __u32);
-                       put_unaligned(arg.i, (__u32 *)(buf+n)); n+=4;
-                       break;
-#if 0
-               case 'c': /* \0 terminated string */
-                       arg.c = va_arg(args, char *);
-                       strcpy(buf+n, arg.c);
-                       n += strlen(arg.c) + 1;
-                       break;
-#endif
-               default:
-                       va_end(args);
-                       return -1;
-               }
-       }
-       va_end(args);
-
-       return 0;
-}
-EXPORT_SYMBOL(irda_param_pack);
-
-/*
- * Function irda_param_unpack (skb, fmt, ...)
- */
-static int irda_param_unpack(__u8 *buf, char *fmt, ...)
-{
-       irda_pv_t arg;
-       va_list args;
-       char *p;
-       int n = 0;
-
-       va_start(args, fmt);
-
-       for (p = fmt; *p != '\0'; p++) {
-               switch (*p) {
-               case 'b':  /* 8 bits byte */
-                       arg.ip = va_arg(args, __u32 *);
-                       *arg.ip = buf[n++];
-                       break;
-               case 's':  /* 16 bits short */
-                       arg.ip = va_arg(args, __u32 *);
-                       *arg.ip = get_unaligned((__u16 *)(buf+n)); n+=2;
-                       break;
-               case 'i':  /* 32 bits unsigned integer */
-                       arg.ip = va_arg(args, __u32 *);
-                       *arg.ip = get_unaligned((__u32 *)(buf+n)); n+=4;
-                       break;
-#if 0
-               case 'c':   /* \0 terminated string */
-                       arg.c = va_arg(args, char *);
-                       strcpy(arg.c, buf+n);
-                       n += strlen(arg.c) + 1;
-                       break;
-#endif
-               default:
-                       va_end(args);
-                       return -1;
-               }
-
-       }
-       va_end(args);
-
-       return 0;
-}
-
-/*
- * Function irda_param_insert (self, pi, buf, len, info)
- *
- *    Insert the specified parameter (pi) into buffer. Returns number of
- *    bytes inserted
- */
-int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
-                     pi_param_info_t *info)
-{
-       const pi_minor_info_t *pi_minor_info;
-       __u8 pi_minor;
-       __u8 pi_major;
-       int type;
-       int ret = -1;
-       int n = 0;
-
-       IRDA_ASSERT(buf != NULL, return ret;);
-       IRDA_ASSERT(info != NULL, return ret;);
-
-       pi_minor = pi & info->pi_mask;
-       pi_major = pi >> info->pi_major_offset;
-
-       /* Check if the identifier value (pi) is valid */
-       if ((pi_major > info->len-1) ||
-           (pi_minor > info->tables[pi_major].len-1))
-       {
-               pr_debug("%s(), no handler for parameter=0x%02x\n",
-                        __func__, pi);
-
-               /* Skip this parameter */
-               return -1;
-       }
-
-       /* Lookup the info on how to parse this parameter */
-       pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor];
-
-       /* Find expected data type for this parameter identifier (pi)*/
-       type = pi_minor_info->type;
-
-       /*  Check if handler has been implemented */
-       if (!pi_minor_info->func) {
-               net_info_ratelimited("%s: no handler for pi=%#x\n",
-                                    __func__, pi);
-               /* Skip this parameter */
-               return -1;
-       }
-
-       /* Insert parameter value */
-       ret = (*pv_insert_table[type & PV_MASK])(self, buf+n, len, pi, type,
-                                                pi_minor_info->func);
-       return ret;
-}
-EXPORT_SYMBOL(irda_param_insert);
-
-/*
- * Function irda_param_extract (self, buf, len, info)
- *
- *    Parse all parameters. If len is correct, then everything should be
- *    safe. Returns the number of bytes that was parsed
- *
- */
-static int irda_param_extract(void *self, __u8 *buf, int len,
-                             pi_param_info_t *info)
-{
-       const pi_minor_info_t *pi_minor_info;
-       __u8 pi_minor;
-       __u8 pi_major;
-       int type;
-       int ret = -1;
-       int n = 0;
-
-       IRDA_ASSERT(buf != NULL, return ret;);
-       IRDA_ASSERT(info != NULL, return ret;);
-
-       pi_minor = buf[n] & info->pi_mask;
-       pi_major = buf[n] >> info->pi_major_offset;
-
-       /* Check if the identifier value (pi) is valid */
-       if ((pi_major > info->len-1) ||
-           (pi_minor > info->tables[pi_major].len-1))
-       {
-               pr_debug("%s(), no handler for parameter=0x%02x\n",
-                        __func__, buf[0]);
-
-               /* Skip this parameter */
-               return 2 + buf[n + 1];  /* Continue */
-       }
-
-       /* Lookup the info on how to parse this parameter */
-       pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor];
-
-       /* Find expected data type for this parameter identifier (pi)*/
-       type = pi_minor_info->type;
-
-       pr_debug("%s(), pi=[%d,%d], type=%d\n", __func__,
-                pi_major, pi_minor, type);
-
-       /*  Check if handler has been implemented */
-       if (!pi_minor_info->func) {
-               net_info_ratelimited("%s: no handler for pi=%#x\n",
-                                    __func__, buf[n]);
-               /* Skip this parameter */
-               return 2 + buf[n + 1]; /* Continue */
-       }
-
-       /* Parse parameter value */
-       ret = (*pv_extract_table[type & PV_MASK])(self, buf+n, len, buf[n],
-                                                 type, pi_minor_info->func);
-       return ret;
-}
-
-/*
- * Function irda_param_extract_all (self, buf, len, info)
- *
- *    Parse all parameters. If len is correct, then everything should be
- *    safe. Returns the number of bytes that was parsed
- *
- */
-int irda_param_extract_all(void *self, __u8 *buf, int len,
-                          pi_param_info_t *info)
-{
-       int ret = -1;
-       int n = 0;
-
-       IRDA_ASSERT(buf != NULL, return ret;);
-       IRDA_ASSERT(info != NULL, return ret;);
-
-       /*
-        * Parse all parameters. Each parameter must be at least two bytes
-        * long or else there is no point in trying to parse it
-        */
-       while (len > 2) {
-               ret = irda_param_extract(self, buf+n, len, info);
-               if (ret < 0)
-                       return ret;
-
-               n += ret;
-               len -= ret;
-       }
-       return n;
-}
-EXPORT_SYMBOL(irda_param_extract_all);
diff --git a/net/irda/qos.c b/net/irda/qos.c
deleted file mode 100644 (file)
index 25ba850..0000000
+++ /dev/null
@@ -1,771 +0,0 @@
-/*********************************************************************
- *
- * Filename:      qos.c
- * Version:       1.0
- * Description:   IrLAP QoS parameter negotiation
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Tue Sep  9 00:00:26 1997
- * Modified at:   Sun Jan 30 14:29:16 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of
- *     the License, or (at your option) any later version.
- *
- *     This program is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- ********************************************************************/
-
-#include <linux/export.h>
-
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/parameters.h>
-#include <net/irda/qos.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlap_frame.h>
-
-/*
- * Maximum values of the baud rate we negotiate with the other end.
- * Most often, you don't have to change that, because Linux-IrDA will
- * use the maximum offered by the link layer, which usually works fine.
- * In some very rare cases, you may want to limit it to lower speeds...
- */
-int sysctl_max_baud_rate = 16000000;
-/*
- * Maximum value of the lap disconnect timer we negotiate with the other end.
- * Most often, the value below represent the best compromise, but some user
- * may want to keep the LAP alive longer or shorter in case of link failure.
- * Remember that the threshold time (early warning) is fixed to 3s...
- */
-int sysctl_max_noreply_time = 12;
-/*
- * Minimum turn time to be applied before transmitting to the peer.
- * Nonzero values (usec) are used as lower limit to the per-connection
- * mtt value which was announced by the other end during negotiation.
- * Might be helpful if the peer device provides too short mtt.
- * Default is 10us which means using the unmodified value given by the
- * peer except if it's 0 (0 is likely a bug in the other stack).
- */
-unsigned int sysctl_min_tx_turn_time = 10;
-/*
- * Maximum data size to be used in transmission in payload of LAP frame.
- * There is a bit of confusion in the IrDA spec :
- * The LAP spec defines the payload of a LAP frame (I field) to be
- * 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40).
- * On the other hand, the PHY mention frames of 2048 bytes max (IrPHY
- * 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header
- * (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP
- * payload), that's only 2042 bytes. Oups !
- * My nsc-ircc hardware has troubles receiving 2048 bytes frames at 4 Mb/s,
- * so adjust to 2042... I don't know if this bug applies only for 2048
- * bytes frames or all negotiated frame sizes, but you can use the sysctl
- * to play with this value anyway.
- * Jean II */
-unsigned int sysctl_max_tx_data_size = 2042;
-/*
- * Maximum transmit window, i.e. number of LAP frames between turn-around.
- * This allow to override what the peer told us. Some peers are buggy and
- * don't always support what they tell us.
- * Jean II */
-unsigned int sysctl_max_tx_window = 7;
-
-static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
-static int irlap_param_link_disconnect(void *instance, irda_param_t *parm,
-                                      int get);
-static int irlap_param_max_turn_time(void *instance, irda_param_t *param,
-                                    int get);
-static int irlap_param_data_size(void *instance, irda_param_t *param, int get);
-static int irlap_param_window_size(void *instance, irda_param_t *param,
-                                  int get);
-static int irlap_param_additional_bofs(void *instance, irda_param_t *parm,
-                                      int get);
-static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
-                                    int get);
-
-#ifndef CONFIG_IRDA_DYNAMIC_WINDOW
-static __u32 irlap_requested_line_capacity(struct qos_info *qos);
-#endif
-
-static __u32 min_turn_times[]  = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */
-static __u32 baud_rates[]      = { 2400, 9600, 19200, 38400, 57600, 115200, 576000,
-                                  1152000, 4000000, 16000000 };           /* bps */
-static __u32 data_sizes[]      = { 64, 128, 256, 512, 1024, 2048 };        /* bytes */
-static __u32 add_bofs[]        = { 48, 24, 12, 5, 3, 2, 1, 0 };            /* bytes */
-static __u32 max_turn_times[]  = { 500, 250, 100, 50 };                    /* ms */
-static __u32 link_disc_times[] = { 3, 8, 12, 16, 20, 25, 30, 40 };         /* secs */
-
-static __u32 max_line_capacities[10][4] = {
-       /* 500 ms     250 ms  100 ms  50 ms (max turn time) */
-       {    100,      0,      0,     0 }, /*     2400 bps */
-       {    400,      0,      0,     0 }, /*     9600 bps */
-       {    800,      0,      0,     0 }, /*    19200 bps */
-       {   1600,      0,      0,     0 }, /*    38400 bps */
-       {   2360,      0,      0,     0 }, /*    57600 bps */
-       {   4800,   2400,    960,   480 }, /*   115200 bps */
-       {  28800,  11520,   5760,  2880 }, /*   576000 bps */
-       {  57600,  28800,  11520,  5760 }, /*  1152000 bps */
-       { 200000, 100000,  40000, 20000 }, /*  4000000 bps */
-       { 800000, 400000, 160000, 80000 }, /* 16000000 bps */
-};
-
-static const pi_minor_info_t pi_minor_call_table_type_0[] = {
-       { NULL, 0 },
-/* 01 */{ irlap_param_baud_rate,       PV_INTEGER | PV_LITTLE_ENDIAN },
-       { NULL, 0 },
-       { NULL, 0 },
-       { NULL, 0 },
-       { NULL, 0 },
-       { NULL, 0 },
-       { NULL, 0 },
-/* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS }
-};
-
-static const pi_minor_info_t pi_minor_call_table_type_1[] = {
-       { NULL, 0 },
-       { NULL, 0 },
-/* 82 */{ irlap_param_max_turn_time,   PV_INT_8_BITS },
-/* 83 */{ irlap_param_data_size,       PV_INT_8_BITS },
-/* 84 */{ irlap_param_window_size,     PV_INT_8_BITS },
-/* 85 */{ irlap_param_additional_bofs, PV_INT_8_BITS },
-/* 86 */{ irlap_param_min_turn_time,   PV_INT_8_BITS },
-};
-
-static const pi_major_info_t pi_major_call_table[] = {
-       { pi_minor_call_table_type_0, 9 },
-       { pi_minor_call_table_type_1, 7 },
-};
-
-static pi_param_info_t irlap_param_info = { pi_major_call_table, 2, 0x7f, 7 };
-
-/* ---------------------- LOCAL SUBROUTINES ---------------------- */
-/* Note : we start with a bunch of local subroutines.
- * As the compiler is "one pass", this is the only way to get them to
- * inline properly...
- * Jean II
- */
-/*
- * Function value_index (value, array, size)
- *
- *    Returns the index to the value in the specified array
- */
-static inline int value_index(__u32 value, __u32 *array, int size)
-{
-       int i;
-
-       for (i=0; i < size; i++)
-               if (array[i] == value)
-                       break;
-       return i;
-}
-
-/*
- * Function index_value (index, array)
- *
- *    Returns value to index in array, easy!
- *
- */
-static inline __u32 index_value(int index, __u32 *array)
-{
-       return array[index];
-}
-
-/*
- * Function msb_index (word)
- *
- *    Returns index to most significant bit (MSB) in word
- *
- */
-static int msb_index (__u16 word)
-{
-       __u16 msb = 0x8000;
-       int index = 15;   /* Current MSB */
-
-       /* Check for buggy peers.
-        * Note : there is a small probability that it could be us, but I
-        * would expect driver authors to catch that pretty early and be
-        * able to check precisely what's going on. If a end user sees this,
-        * it's very likely the peer. - Jean II */
-       if (word == 0) {
-               net_warn_ratelimited("%s(), Detected buggy peer, adjust null PV to 0x1!\n",
-                                    __func__);
-               /* The only safe choice (we don't know the array size) */
-               word = 0x1;
-       }
-
-       while (msb) {
-               if (word & msb)
-                       break;   /* Found it! */
-               msb >>=1;
-               index--;
-       }
-       return index;
-}
-
-/*
- * Function value_lower_bits (value, array)
- *
- *    Returns a bit field marking all possibility lower than value.
- */
-static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field)
-{
-       int     i;
-       __u16   mask = 0x1;
-       __u16   result = 0x0;
-
-       for (i=0; i < size; i++) {
-               /* Add the current value to the bit field, shift mask */
-               result |= mask;
-               mask <<= 1;
-               /* Finished ? */
-               if (array[i] >= value)
-                       break;
-       }
-       /* Send back a valid index */
-       if(i >= size)
-         i = size - 1; /* Last item */
-       *field = result;
-       return i;
-}
-
-/*
- * Function value_highest_bit (value, array)
- *
- *    Returns a bit field marking the highest possibility lower than value.
- */
-static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field)
-{
-       int     i;
-       __u16   mask = 0x1;
-       __u16   result = 0x0;
-
-       for (i=0; i < size; i++) {
-               /* Finished ? */
-               if (array[i] <= value)
-                       break;
-               /* Shift mask */
-               mask <<= 1;
-       }
-       /* Set the current value to the bit field */
-       result |= mask;
-       /* Send back a valid index */
-       if(i >= size)
-         i = size - 1; /* Last item */
-       *field = result;
-       return i;
-}
-
-/* -------------------------- MAIN CALLS -------------------------- */
-
-/*
- * Function irda_qos_compute_intersection (qos, new)
- *
- *    Compute the intersection of the old QoS capabilities with new ones
- *
- */
-void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new)
-{
-       IRDA_ASSERT(qos != NULL, return;);
-       IRDA_ASSERT(new != NULL, return;);
-
-       /* Apply */
-       qos->baud_rate.bits       &= new->baud_rate.bits;
-       qos->window_size.bits     &= new->window_size.bits;
-       qos->min_turn_time.bits   &= new->min_turn_time.bits;
-       qos->max_turn_time.bits   &= new->max_turn_time.bits;
-       qos->data_size.bits       &= new->data_size.bits;
-       qos->link_disc_time.bits  &= new->link_disc_time.bits;
-       qos->additional_bofs.bits &= new->additional_bofs.bits;
-
-       irda_qos_bits_to_value(qos);
-}
-
-/*
- * Function irda_init_max_qos_capabilies (qos)
- *
- *    The purpose of this function is for layers and drivers to be able to
- *    set the maximum QoS possible and then "and in" their own limitations
- *
- */
-void irda_init_max_qos_capabilies(struct qos_info *qos)
-{
-       int i;
-       /*
-        *  These are the maximum supported values as specified on pages
-        *  39-43 in IrLAP
-        */
-
-       /* Use sysctl to set some configurable values... */
-       /* Set configured max speed */
-       i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10,
-                            &qos->baud_rate.bits);
-       sysctl_max_baud_rate = index_value(i, baud_rates);
-
-       /* Set configured max disc time */
-       i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8,
-                            &qos->link_disc_time.bits);
-       sysctl_max_noreply_time = index_value(i, link_disc_times);
-
-       /* LSB is first byte, MSB is second byte */
-       qos->baud_rate.bits    &= 0x03ff;
-
-       qos->window_size.bits     = 0x7f;
-       qos->min_turn_time.bits   = 0xff;
-       qos->max_turn_time.bits   = 0x0f;
-       qos->data_size.bits       = 0x3f;
-       qos->link_disc_time.bits &= 0xff;
-       qos->additional_bofs.bits = 0xff;
-}
-EXPORT_SYMBOL(irda_init_max_qos_capabilies);
-
-/*
- * Function irlap_adjust_qos_settings (qos)
- *
- *     Adjust QoS settings in case some values are not possible to use because
- *     of other settings
- */
-static void irlap_adjust_qos_settings(struct qos_info *qos)
-{
-       __u32 line_capacity;
-       int index;
-
-       /*
-        * Make sure the mintt is sensible.
-        * Main culprit : Ericsson T39. - Jean II
-        */
-       if (sysctl_min_tx_turn_time > qos->min_turn_time.value) {
-               int i;
-
-               net_warn_ratelimited("%s(), Detected buggy peer, adjust mtt to %dus!\n",
-                                    __func__, sysctl_min_tx_turn_time);
-
-               /* We don't really need bits, but easier this way */
-               i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times,
-                                     8, &qos->min_turn_time.bits);
-               sysctl_min_tx_turn_time = index_value(i, min_turn_times);
-               qos->min_turn_time.value = sysctl_min_tx_turn_time;
-       }
-
-       /*
-        * Not allowed to use a max turn time less than 500 ms if the baudrate
-        * is less than 115200
-        */
-       if ((qos->baud_rate.value < 115200) &&
-           (qos->max_turn_time.value < 500))
-       {
-               pr_debug("%s(), adjusting max turn time from %d to 500 ms\n",
-                        __func__, qos->max_turn_time.value);
-               qos->max_turn_time.value = 500;
-       }
-
-       /*
-        * The data size must be adjusted according to the baud rate and max
-        * turn time
-        */
-       index = value_index(qos->data_size.value, data_sizes, 6);
-       line_capacity = irlap_max_line_capacity(qos->baud_rate.value,
-                                               qos->max_turn_time.value);
-
-#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
-       while ((qos->data_size.value > line_capacity) && (index > 0)) {
-               qos->data_size.value = data_sizes[index--];
-               pr_debug("%s(), reducing data size to %d\n",
-                        __func__, qos->data_size.value);
-       }
-#else /* Use method described in section 6.6.11 of IrLAP */
-       while (irlap_requested_line_capacity(qos) > line_capacity) {
-               IRDA_ASSERT(index != 0, return;);
-
-               /* Must be able to send at least one frame */
-               if (qos->window_size.value > 1) {
-                       qos->window_size.value--;
-                       pr_debug("%s(), reducing window size to %d\n",
-                                __func__, qos->window_size.value);
-               } else if (index > 1) {
-                       qos->data_size.value = data_sizes[index--];
-                       pr_debug("%s(), reducing data size to %d\n",
-                                __func__, qos->data_size.value);
-               } else {
-                       net_warn_ratelimited("%s(), nothing more we can do!\n",
-                                            __func__);
-               }
-       }
-#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-       /*
-        * Fix tx data size according to user limits - Jean II
-        */
-       if (qos->data_size.value > sysctl_max_tx_data_size)
-               /* Allow non discrete adjustement to avoid losing capacity */
-               qos->data_size.value = sysctl_max_tx_data_size;
-       /*
-        * Override Tx window if user request it. - Jean II
-        */
-       if (qos->window_size.value > sysctl_max_tx_window)
-               qos->window_size.value = sysctl_max_tx_window;
-}
-
-/*
- * Function irlap_negotiate (qos_device, qos_session, skb)
- *
- *    Negotiate QoS values, not really that much negotiation :-)
- *    We just set the QoS capabilities for the peer station
- *
- */
-int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb)
-{
-       int ret;
-
-       ret = irda_param_extract_all(self, skb->data, skb->len,
-                                    &irlap_param_info);
-
-       /* Convert the negotiated bits to values */
-       irda_qos_bits_to_value(&self->qos_tx);
-       irda_qos_bits_to_value(&self->qos_rx);
-
-       irlap_adjust_qos_settings(&self->qos_tx);
-
-       pr_debug("Setting BAUD_RATE to %d bps.\n",
-                self->qos_tx.baud_rate.value);
-       pr_debug("Setting DATA_SIZE to %d bytes\n",
-                self->qos_tx.data_size.value);
-       pr_debug("Setting WINDOW_SIZE to %d\n",
-                self->qos_tx.window_size.value);
-       pr_debug("Setting XBOFS to %d\n",
-                self->qos_tx.additional_bofs.value);
-       pr_debug("Setting MAX_TURN_TIME to %d ms.\n",
-                self->qos_tx.max_turn_time.value);
-       pr_debug("Setting MIN_TURN_TIME to %d usecs.\n",
-                self->qos_tx.min_turn_time.value);
-       pr_debug("Setting LINK_DISC to %d secs.\n",
-                self->qos_tx.link_disc_time.value);
-       return ret;
-}
-
-/*
- * Function irlap_insert_negotiation_params (qos, fp)
- *
- *    Insert QoS negotiaion pararameters into frame
- *
- */
-int irlap_insert_qos_negotiation_params(struct irlap_cb *self,
-                                       struct sk_buff *skb)
-{
-       int ret;
-
-       /* Insert data rate */
-       ret = irda_param_insert(self, PI_BAUD_RATE, skb_tail_pointer(skb),
-                               skb_tailroom(skb), &irlap_param_info);
-       if (ret < 0)
-               return ret;
-       skb_put(skb, ret);
-
-       /* Insert max turnaround time */
-       ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb_tail_pointer(skb),
-                               skb_tailroom(skb), &irlap_param_info);
-       if (ret < 0)
-               return ret;
-       skb_put(skb, ret);
-
-       /* Insert data size */
-       ret = irda_param_insert(self, PI_DATA_SIZE, skb_tail_pointer(skb),
-                               skb_tailroom(skb), &irlap_param_info);
-       if (ret < 0)
-               return ret;
-       skb_put(skb, ret);
-
-       /* Insert window size */
-       ret = irda_param_insert(self, PI_WINDOW_SIZE, skb_tail_pointer(skb),
-                               skb_tailroom(skb), &irlap_param_info);
-       if (ret < 0)
-               return ret;
-       skb_put(skb, ret);
-
-       /* Insert additional BOFs */
-       ret = irda_param_insert(self, PI_ADD_BOFS, skb_tail_pointer(skb),
-                               skb_tailroom(skb), &irlap_param_info);
-       if (ret < 0)
-               return ret;
-       skb_put(skb, ret);
-
-       /* Insert minimum turnaround time */
-       ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb_tail_pointer(skb),
-                               skb_tailroom(skb), &irlap_param_info);
-       if (ret < 0)
-               return ret;
-       skb_put(skb, ret);
-
-       /* Insert link disconnect/threshold time */
-       ret = irda_param_insert(self, PI_LINK_DISC, skb_tail_pointer(skb),
-                               skb_tailroom(skb), &irlap_param_info);
-       if (ret < 0)
-               return ret;
-       skb_put(skb, ret);
-
-       return 0;
-}
-
-/*
- * Function irlap_param_baud_rate (instance, param, get)
- *
- *    Negotiate data-rate
- *
- */
-static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get)
-{
-       __u16 final;
-
-       struct irlap_cb *self = (struct irlap_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       if (get) {
-               param->pv.i = self->qos_rx.baud_rate.bits;
-               pr_debug("%s(), baud rate = 0x%02x\n",
-                        __func__, param->pv.i);
-       } else {
-               /*
-                *  Stations must agree on baud rate, so calculate
-                *  intersection
-                */
-               pr_debug("Requested BAUD_RATE: 0x%04x\n", (__u16)param->pv.i);
-               final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits;
-
-               pr_debug("Final BAUD_RATE: 0x%04x\n", final);
-               self->qos_tx.baud_rate.bits = final;
-               self->qos_rx.baud_rate.bits = final;
-       }
-
-       return 0;
-}
-
-/*
- * Function irlap_param_link_disconnect (instance, param, get)
- *
- *    Negotiate link disconnect/threshold time.
- *
- */
-static int irlap_param_link_disconnect(void *instance, irda_param_t *param,
-                                      int get)
-{
-       __u16 final;
-
-       struct irlap_cb *self = (struct irlap_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->qos_rx.link_disc_time.bits;
-       else {
-               /*
-                *  Stations must agree on link disconnect/threshold
-                *  time.
-                */
-               pr_debug("LINK_DISC: %02x\n", (__u8)param->pv.i);
-               final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits;
-
-               pr_debug("Final LINK_DISC: %02x\n", final);
-               self->qos_tx.link_disc_time.bits = final;
-               self->qos_rx.link_disc_time.bits = final;
-       }
-       return 0;
-}
-
-/*
- * Function irlap_param_max_turn_time (instance, param, get)
- *
- *    Negotiate the maximum turnaround time. This is a type 1 parameter and
- *    will be negotiated independently for each station
- *
- */
-static int irlap_param_max_turn_time(void *instance, irda_param_t *param,
-                                    int get)
-{
-       struct irlap_cb *self = (struct irlap_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->qos_rx.max_turn_time.bits;
-       else
-               self->qos_tx.max_turn_time.bits = (__u8) param->pv.i;
-
-       return 0;
-}
-
-/*
- * Function irlap_param_data_size (instance, param, get)
- *
- *    Negotiate the data size. This is a type 1 parameter and
- *    will be negotiated independently for each station
- *
- */
-static int irlap_param_data_size(void *instance, irda_param_t *param, int get)
-{
-       struct irlap_cb *self = (struct irlap_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->qos_rx.data_size.bits;
-       else
-               self->qos_tx.data_size.bits = (__u8) param->pv.i;
-
-       return 0;
-}
-
-/*
- * Function irlap_param_window_size (instance, param, get)
- *
- *    Negotiate the window size. This is a type 1 parameter and
- *    will be negotiated independently for each station
- *
- */
-static int irlap_param_window_size(void *instance, irda_param_t *param,
-                                  int get)
-{
-       struct irlap_cb *self = (struct irlap_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->qos_rx.window_size.bits;
-       else
-               self->qos_tx.window_size.bits = (__u8) param->pv.i;
-
-       return 0;
-}
-
-/*
- * Function irlap_param_additional_bofs (instance, param, get)
- *
- *    Negotiate additional BOF characters. This is a type 1 parameter and
- *    will be negotiated independently for each station.
- */
-static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get)
-{
-       struct irlap_cb *self = (struct irlap_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->qos_rx.additional_bofs.bits;
-       else
-               self->qos_tx.additional_bofs.bits = (__u8) param->pv.i;
-
-       return 0;
-}
-
-/*
- * Function irlap_param_min_turn_time (instance, param, get)
- *
- *    Negotiate the minimum turn around time. This is a type 1 parameter and
- *    will be negotiated independently for each station
- */
-static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
-                                    int get)
-{
-       struct irlap_cb *self = (struct irlap_cb *) instance;
-
-       IRDA_ASSERT(self != NULL, return -1;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
-
-       if (get)
-               param->pv.i = self->qos_rx.min_turn_time.bits;
-       else
-               self->qos_tx.min_turn_time.bits = (__u8) param->pv.i;
-
-       return 0;
-}
-
-/*
- * Function irlap_max_line_capacity (speed, max_turn_time, min_turn_time)
- *
- *    Calculate the maximum line capacity
- *
- */
-__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time)
-{
-       __u32 line_capacity;
-       int i,j;
-
-       pr_debug("%s(), speed=%d, max_turn_time=%d\n",
-                __func__, speed, max_turn_time);
-
-       i = value_index(speed, baud_rates, 10);
-       j = value_index(max_turn_time, max_turn_times, 4);
-
-       IRDA_ASSERT(((i >=0) && (i <10)), return 0;);
-       IRDA_ASSERT(((j >=0) && (j <4)), return 0;);
-
-       line_capacity = max_line_capacities[i][j];
-
-       pr_debug("%s(), line capacity=%d bytes\n",
-                __func__, line_capacity);
-
-       return line_capacity;
-}
-
-#ifndef CONFIG_IRDA_DYNAMIC_WINDOW
-static __u32 irlap_requested_line_capacity(struct qos_info *qos)
-{
-       __u32 line_capacity;
-
-       line_capacity = qos->window_size.value *
-               (qos->data_size.value + 6 + qos->additional_bofs.value) +
-               irlap_min_turn_time_in_bytes(qos->baud_rate.value,
-                                            qos->min_turn_time.value);
-
-       pr_debug("%s(), requested line capacity=%d\n",
-                __func__, line_capacity);
-
-       return line_capacity;
-}
-#endif
-
-void irda_qos_bits_to_value(struct qos_info *qos)
-{
-       int index;
-
-       IRDA_ASSERT(qos != NULL, return;);
-
-       index = msb_index(qos->baud_rate.bits);
-       qos->baud_rate.value = baud_rates[index];
-
-       index = msb_index(qos->data_size.bits);
-       qos->data_size.value = data_sizes[index];
-
-       index = msb_index(qos->window_size.bits);
-       qos->window_size.value = index+1;
-
-       index = msb_index(qos->min_turn_time.bits);
-       qos->min_turn_time.value = min_turn_times[index];
-
-       index = msb_index(qos->max_turn_time.bits);
-       qos->max_turn_time.value = max_turn_times[index];
-
-       index = msb_index(qos->link_disc_time.bits);
-       qos->link_disc_time.value = link_disc_times[index];
-
-       index = msb_index(qos->additional_bofs.bits);
-       qos->additional_bofs.value = add_bofs[index];
-}
-EXPORT_SYMBOL(irda_qos_bits_to_value);
diff --git a/net/irda/timer.c b/net/irda/timer.c
deleted file mode 100644 (file)
index f2280f7..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*********************************************************************
- *
- * Filename:      timer.c
- * Version:
- * Description:
- * Status:        Experimental.
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Sat Aug 16 00:59:29 1997
- * Modified at:   Wed Dec  8 12:50:34 1999
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- *
- *     Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/delay.h>
-
-#include <net/irda/timer.h>
-#include <net/irda/irda.h>
-#include <net/irda/irda_device.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlmp.h>
-
-extern int  sysctl_slot_timeout;
-
-static void irlap_slot_timer_expired(void* data);
-static void irlap_query_timer_expired(void* data);
-static void irlap_final_timer_expired(void* data);
-static void irlap_wd_timer_expired(void* data);
-static void irlap_backoff_timer_expired(void* data);
-static void irlap_media_busy_expired(void* data);
-
-void irlap_start_slot_timer(struct irlap_cb *self, int timeout)
-{
-       irda_start_timer(&self->slot_timer, timeout, (void *) self,
-                        irlap_slot_timer_expired);
-}
-
-void irlap_start_query_timer(struct irlap_cb *self, int S, int s)
-{
-       int timeout;
-
-       /* Calculate when the peer discovery should end. Normally, we
-        * get the end-of-discovery frame, so this is just in case
-        * we miss it.
-        * Basically, we multiply the number of remaining slots by our
-        * slot time, plus add some extra time to properly receive the last
-        * discovery packet (which is longer due to extra discovery info),
-        * to avoid messing with for incoming connections requests and
-        * to accommodate devices that perform discovery slower than us.
-        * Jean II */
-       timeout = msecs_to_jiffies(sysctl_slot_timeout) * (S - s)
-                  + XIDEXTRA_TIMEOUT + SMALLBUSY_TIMEOUT;
-
-       /* Set or re-set the timer. We reset the timer for each received
-        * discovery query, which allow us to automatically adjust to
-        * the speed of the peer discovery (faster or slower). Jean II */
-       irda_start_timer( &self->query_timer, timeout, (void *) self,
-                         irlap_query_timer_expired);
-}
-
-void irlap_start_final_timer(struct irlap_cb *self, int timeout)
-{
-       irda_start_timer(&self->final_timer, timeout, (void *) self,
-                        irlap_final_timer_expired);
-}
-
-void irlap_start_wd_timer(struct irlap_cb *self, int timeout)
-{
-       irda_start_timer(&self->wd_timer, timeout, (void *) self,
-                        irlap_wd_timer_expired);
-}
-
-void irlap_start_backoff_timer(struct irlap_cb *self, int timeout)
-{
-       irda_start_timer(&self->backoff_timer, timeout, (void *) self,
-                        irlap_backoff_timer_expired);
-}
-
-void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout)
-{
-       irda_start_timer(&self->media_busy_timer, timeout,
-                        (void *) self, irlap_media_busy_expired);
-}
-
-void irlap_stop_mbusy_timer(struct irlap_cb *self)
-{
-       /* If timer is activated, kill it! */
-       del_timer(&self->media_busy_timer);
-
-       /* If we are in NDM, there is a bunch of events in LAP that
-        * that be pending due to the media_busy condition, such as
-        * CONNECT_REQUEST and SEND_UI_FRAME. If we don't generate
-        * an event, they will wait forever...
-        * Jean II */
-       if (self->state == LAP_NDM)
-               irlap_do_event(self, MEDIA_BUSY_TIMER_EXPIRED, NULL, NULL);
-}
-
-void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout)
-{
-       irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
-                        irlmp_watchdog_timer_expired);
-}
-
-void irlmp_start_discovery_timer(struct irlmp_cb *self, int timeout)
-{
-       irda_start_timer(&self->discovery_timer, timeout, (void *) self,
-                        irlmp_discovery_timer_expired);
-}
-
-void irlmp_start_idle_timer(struct lap_cb *self, int timeout)
-{
-       irda_start_timer(&self->idle_timer, timeout, (void *) self,
-                        irlmp_idle_timer_expired);
-}
-
-void irlmp_stop_idle_timer(struct lap_cb *self)
-{
-       /* If timer is activated, kill it! */
-       del_timer(&self->idle_timer);
-}
-
-/*
- * Function irlap_slot_timer_expired (data)
- *
- *    IrLAP slot timer has expired
- *
- */
-static void irlap_slot_timer_expired(void *data)
-{
-       struct irlap_cb *self = (struct irlap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       irlap_do_event(self, SLOT_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Function irlap_query_timer_expired (data)
- *
- *    IrLAP query timer has expired
- *
- */
-static void irlap_query_timer_expired(void *data)
-{
-       struct irlap_cb *self = (struct irlap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       irlap_do_event(self, QUERY_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Function irda_final_timer_expired (data)
- *
- *
- *
- */
-static void irlap_final_timer_expired(void *data)
-{
-       struct irlap_cb *self = (struct irlap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       irlap_do_event(self, FINAL_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Function irda_wd_timer_expired (data)
- *
- *
- *
- */
-static void irlap_wd_timer_expired(void *data)
-{
-       struct irlap_cb *self = (struct irlap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       irlap_do_event(self, WD_TIMER_EXPIRED, NULL, NULL);
-}
-
-/*
- * Function irda_backoff_timer_expired (data)
- *
- *
- *
- */
-static void irlap_backoff_timer_expired(void *data)
-{
-       struct irlap_cb *self = (struct irlap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-       IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
-
-       irlap_do_event(self, BACKOFF_TIMER_EXPIRED, NULL, NULL);
-}
-
-
-/*
- * Function irtty_media_busy_expired (data)
- *
- *
- */
-static void irlap_media_busy_expired(void *data)
-{
-       struct irlap_cb *self = (struct irlap_cb *) data;
-
-       IRDA_ASSERT(self != NULL, return;);
-
-       irda_device_set_media_busy(self->netdev, FALSE);
-       /* Note : the LAP event will be send in irlap_stop_mbusy_timer(),
-       * to catch other cases where the flag is cleared (for example
-       * after a discovery) - Jean II */
-}
diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c
deleted file mode 100644 (file)
index 40a0f99..0000000
+++ /dev/null
@@ -1,492 +0,0 @@
-/*********************************************************************
- *
- * Filename:      wrapper.c
- * Version:       1.2
- * Description:   IrDA SIR async wrapper layer
- * Status:        Stable
- * Author:        Dag Brattli <dagb@cs.uit.no>
- * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Fri Jan 28 13:21:09 2000
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * Modified at:   Fri May 28  3:11 CST 1999
- * Modified by:   Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
- *
- *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
- *     All Rights Reserved.
- *     Copyright (c) 2000-2002 Jean Tourrilhes <jt@hpl.hp.com>
- *
- *     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.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is
- *     provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/skbuff.h>
-#include <linux/string.h>
-#include <linux/module.h>
-#include <asm/byteorder.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/wrapper.h>
-#include <net/irda/crc.h>
-#include <net/irda/irlap.h>
-#include <net/irda/irlap_frame.h>
-#include <net/irda/irda_device.h>
-
-/************************** FRAME WRAPPING **************************/
-/*
- * Unwrap and unstuff SIR frames
- *
- * Note : at FIR and MIR, HDLC framing is used and usually handled
- * by the controller, so we come here only for SIR... Jean II
- */
-
-/*
- * Function stuff_byte (byte, buf)
- *
- *    Byte stuff one single byte and put the result in buffer pointed to by
- *    buf. The buffer must at all times be able to have two bytes inserted.
- *
- * This is in a tight loop, better inline it, so need to be prior to callers.
- * (2000 bytes on P6 200MHz, non-inlined ~370us, inline ~170us) - Jean II
- */
-static inline int stuff_byte(__u8 byte, __u8 *buf)
-{
-       switch (byte) {
-       case BOF: /* FALLTHROUGH */
-       case EOF: /* FALLTHROUGH */
-       case CE:
-               /* Insert transparently coded */
-               buf[0] = CE;               /* Send link escape */
-               buf[1] = byte^IRDA_TRANS;    /* Complement bit 5 */
-               return 2;
-               /* break; */
-       default:
-                /* Non-special value, no transparency required */
-               buf[0] = byte;
-               return 1;
-               /* break; */
-       }
-}
-
-/*
- * Function async_wrap (skb, *tx_buff, buffsize)
- *
- *    Makes a new buffer with wrapping and stuffing, should check that
- *    we don't get tx buffer overflow.
- */
-int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
-{
-       struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
-       int xbofs;
-       int i;
-       int n;
-       union {
-               __u16 value;
-               __u8 bytes[2];
-       } fcs;
-
-       /* Initialize variables */
-       fcs.value = INIT_FCS;
-       n = 0;
-
-       /*
-        *  Send  XBOF's for required min. turn time and for the negotiated
-        *  additional XBOFS
-        */
-
-       if (cb->magic != LAP_MAGIC) {
-               /*
-                * This will happen for all frames sent from user-space.
-                * Nothing to worry about, but we set the default number of
-                * BOF's
-                */
-               pr_debug("%s(), wrong magic in skb!\n", __func__);
-               xbofs = 10;
-       } else
-               xbofs = cb->xbofs + cb->xbofs_delay;
-
-       pr_debug("%s(), xbofs=%d\n", __func__, xbofs);
-
-       /* Check that we never use more than 115 + 48 xbofs */
-       if (xbofs > 163) {
-               pr_debug("%s(), too many xbofs (%d)\n", __func__,
-                        xbofs);
-               xbofs = 163;
-       }
-
-       memset(tx_buff + n, XBOF, xbofs);
-       n += xbofs;
-
-       /* Start of packet character BOF */
-       tx_buff[n++] = BOF;
-
-       /* Insert frame and calc CRC */
-       for (i=0; i < skb->len; i++) {
-               /*
-                *  Check for the possibility of tx buffer overflow. We use
-                *  bufsize-5 since the maximum number of bytes that can be
-                *  transmitted after this point is 5.
-                */
-               if(n >= (buffsize-5)) {
-                       net_err_ratelimited("%s(), tx buffer overflow (n=%d)\n",
-                                           __func__, n);
-                       return n;
-               }
-
-               n += stuff_byte(skb->data[i], tx_buff+n);
-               fcs.value = irda_fcs(fcs.value, skb->data[i]);
-       }
-
-       /* Insert CRC in little endian format (LSB first) */
-       fcs.value = ~fcs.value;
-#ifdef __LITTLE_ENDIAN
-       n += stuff_byte(fcs.bytes[0], tx_buff+n);
-       n += stuff_byte(fcs.bytes[1], tx_buff+n);
-#else /* ifdef __BIG_ENDIAN */
-       n += stuff_byte(fcs.bytes[1], tx_buff+n);
-       n += stuff_byte(fcs.bytes[0], tx_buff+n);
-#endif
-       tx_buff[n++] = EOF;
-
-       return n;
-}
-EXPORT_SYMBOL(async_wrap_skb);
-
-/************************* FRAME UNWRAPPING *************************/
-/*
- * Unwrap and unstuff SIR frames
- *
- * Complete rewrite by Jean II :
- * More inline, faster, more compact, more logical. Jean II
- * (16 bytes on P6 200MHz, old 5 to 7 us, new 4 to 6 us)
- * (24 bytes on P6 200MHz, old 9 to 10 us, new 7 to 8 us)
- * (for reference, 115200 b/s is 1 byte every 69 us)
- * And reduce wrapper.o by ~900B in the process ;-)
- *
- * Then, we have the addition of ZeroCopy, which is optional
- * (i.e. the driver must initiate it) and improve final processing.
- * (2005 B frame + EOF on P6 200MHz, without 30 to 50 us, with 10 to 25 us)
- *
- * Note : at FIR and MIR, HDLC framing is used and usually handled
- * by the controller, so we come here only for SIR... Jean II
- */
-
-/*
- * We can also choose where we want to do the CRC calculation. We can
- * do it "inline", as we receive the bytes, or "postponed", when
- * receiving the End-Of-Frame.
- * (16 bytes on P6 200MHz, inlined 4 to 6 us, postponed 4 to 5 us)
- * (24 bytes on P6 200MHz, inlined 7 to 8 us, postponed 5 to 7 us)
- * With ZeroCopy :
- * (2005 B frame on P6 200MHz, inlined 10 to 25 us, postponed 140 to 180 us)
- * Without ZeroCopy :
- * (2005 B frame on P6 200MHz, inlined 30 to 50 us, postponed 150 to 180 us)
- * (Note : numbers taken with irq disabled)
- *
- * From those numbers, it's not clear which is the best strategy, because
- * we end up running through a lot of data one way or another (i.e. cache
- * misses). I personally prefer to avoid the huge latency spike of the
- * "postponed" solution, because it come just at the time when we have
- * lot's of protocol processing to do and it will hurt our ability to
- * reach low link turnaround times... Jean II
- */
-//#define POSTPONE_RX_CRC
-
-/*
- * Function async_bump (buf, len, stats)
- *
- *    Got a frame, make a copy of it, and pass it up the stack! We can try
- *    to inline it since it's only called from state_inside_frame
- */
-static inline void
-async_bump(struct net_device *dev,
-          struct net_device_stats *stats,
-          iobuff_t *rx_buff)
-{
-       struct sk_buff *newskb;
-       struct sk_buff *dataskb;
-       int             docopy;
-
-       /* Check if we need to copy the data to a new skb or not.
-        * If the driver doesn't use ZeroCopy Rx, we have to do it.
-        * With ZeroCopy Rx, the rx_buff already point to a valid
-        * skb. But, if the frame is small, it is more efficient to
-        * copy it to save memory (copy will be fast anyway - that's
-        * called Rx-copy-break). Jean II */
-       docopy = ((rx_buff->skb == NULL) ||
-                 (rx_buff->len < IRDA_RX_COPY_THRESHOLD));
-
-       /* Allocate a new skb */
-       newskb = dev_alloc_skb(docopy ? rx_buff->len + 1 : rx_buff->truesize);
-       if (!newskb)  {
-               stats->rx_dropped++;
-               /* We could deliver the current skb if doing ZeroCopy Rx,
-                * but this would stall the Rx path. Better drop the
-                * packet... Jean II */
-               return;
-       }
-
-       /* Align IP header to 20 bytes (i.e. increase skb->data)
-        * Note this is only useful with IrLAN, as PPP has a variable
-        * header size (2 or 1 bytes) - Jean II */
-       skb_reserve(newskb, 1);
-
-       if(docopy) {
-               /* Copy data without CRC (length already checked) */
-               skb_copy_to_linear_data(newskb, rx_buff->data,
-                                       rx_buff->len - 2);
-               /* Deliver this skb */
-               dataskb = newskb;
-       } else {
-               /* We are using ZeroCopy. Deliver old skb */
-               dataskb = rx_buff->skb;
-               /* And hook the new skb to the rx_buff */
-               rx_buff->skb = newskb;
-               rx_buff->head = newskb->data;   /* NOT newskb->head */
-               //printk(KERN_DEBUG "ZeroCopy : len = %d, dataskb = %p, newskb = %p\n", rx_buff->len, dataskb, newskb);
-       }
-
-       /* Set proper length on skb (without CRC) */
-       skb_put(dataskb, rx_buff->len - 2);
-
-       /* Feed it to IrLAP layer */
-       dataskb->dev = dev;
-       skb_reset_mac_header(dataskb);
-       dataskb->protocol = htons(ETH_P_IRDA);
-
-       netif_rx(dataskb);
-
-       stats->rx_packets++;
-       stats->rx_bytes += rx_buff->len;
-
-       /* Clean up rx_buff (redundant with async_unwrap_bof() ???) */
-       rx_buff->data = rx_buff->head;
-       rx_buff->len = 0;
-}
-
-/*
- * Function async_unwrap_bof(dev, byte)
- *
- *    Handle Beginning Of Frame character received within a frame
- *
- */
-static inline void
-async_unwrap_bof(struct net_device *dev,
-                struct net_device_stats *stats,
-                iobuff_t *rx_buff, __u8 byte)
-{
-       switch(rx_buff->state) {
-       case LINK_ESCAPE:
-       case INSIDE_FRAME:
-               /* Not supposed to happen, the previous frame is not
-                * finished - Jean II */
-               pr_debug("%s(), Discarding incomplete frame\n",
-                        __func__);
-               stats->rx_errors++;
-               stats->rx_missed_errors++;
-               irda_device_set_media_busy(dev, TRUE);
-               break;
-
-       case OUTSIDE_FRAME:
-       case BEGIN_FRAME:
-       default:
-               /* We may receive multiple BOF at the start of frame */
-               break;
-       }
-
-       /* Now receiving frame */
-       rx_buff->state = BEGIN_FRAME;
-       rx_buff->in_frame = TRUE;
-
-       /* Time to initialize receive buffer */
-       rx_buff->data = rx_buff->head;
-       rx_buff->len = 0;
-       rx_buff->fcs = INIT_FCS;
-}
-
-/*
- * Function async_unwrap_eof(dev, byte)
- *
- *    Handle End Of Frame character received within a frame
- *
- */
-static inline void
-async_unwrap_eof(struct net_device *dev,
-                struct net_device_stats *stats,
-                iobuff_t *rx_buff, __u8 byte)
-{
-#ifdef POSTPONE_RX_CRC
-       int     i;
-#endif
-
-       switch(rx_buff->state) {
-       case OUTSIDE_FRAME:
-               /* Probably missed the BOF */
-               stats->rx_errors++;
-               stats->rx_missed_errors++;
-               irda_device_set_media_busy(dev, TRUE);
-               break;
-
-       case BEGIN_FRAME:
-       case LINK_ESCAPE:
-       case INSIDE_FRAME:
-       default:
-               /* Note : in the case of BEGIN_FRAME and LINK_ESCAPE,
-                * the fcs will most likely not match and generate an
-                * error, as expected - Jean II */
-               rx_buff->state = OUTSIDE_FRAME;
-               rx_buff->in_frame = FALSE;
-
-#ifdef POSTPONE_RX_CRC
-               /* If we haven't done the CRC as we receive bytes, we
-                * must do it now... Jean II */
-               for(i = 0; i < rx_buff->len; i++)
-                       rx_buff->fcs = irda_fcs(rx_buff->fcs,
-                                               rx_buff->data[i]);
-#endif
-
-               /* Test FCS and signal success if the frame is good */
-               if (rx_buff->fcs == GOOD_FCS) {
-                       /* Deliver frame */
-                       async_bump(dev, stats, rx_buff);
-                       break;
-               } else {
-                       /* Wrong CRC, discard frame!  */
-                       irda_device_set_media_busy(dev, TRUE);
-
-                       pr_debug("%s(), crc error\n", __func__);
-                       stats->rx_errors++;
-                       stats->rx_crc_errors++;
-               }
-               break;
-       }
-}
-
-/*
- * Function async_unwrap_ce(dev, byte)
- *
- *    Handle Character Escape character received within a frame
- *
- */
-static inline void
-async_unwrap_ce(struct net_device *dev,
-                struct net_device_stats *stats,
-                iobuff_t *rx_buff, __u8 byte)
-{
-       switch(rx_buff->state) {
-       case OUTSIDE_FRAME:
-               /* Activate carrier sense */
-               irda_device_set_media_busy(dev, TRUE);
-               break;
-
-       case LINK_ESCAPE:
-               net_warn_ratelimited("%s: state not defined\n", __func__);
-               break;
-
-       case BEGIN_FRAME:
-       case INSIDE_FRAME:
-       default:
-               /* Stuffed byte coming */
-               rx_buff->state = LINK_ESCAPE;
-               break;
-       }
-}
-
-/*
- * Function async_unwrap_other(dev, byte)
- *
- *    Handle other characters received within a frame
- *
- */
-static inline void
-async_unwrap_other(struct net_device *dev,
-                  struct net_device_stats *stats,
-                  iobuff_t *rx_buff, __u8 byte)
-{
-       switch(rx_buff->state) {
-               /* This is on the critical path, case are ordered by
-                * probability (most frequent first) - Jean II */
-       case INSIDE_FRAME:
-               /* Must be the next byte of the frame */
-               if (rx_buff->len < rx_buff->truesize)  {
-                       rx_buff->data[rx_buff->len++] = byte;
-#ifndef POSTPONE_RX_CRC
-                       rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
-#endif
-               } else {
-                       pr_debug("%s(), Rx buffer overflow, aborting\n",
-                                __func__);
-                       rx_buff->state = OUTSIDE_FRAME;
-               }
-               break;
-
-       case LINK_ESCAPE:
-               /*
-                *  Stuffed char, complement bit 5 of byte
-                *  following CE, IrLAP p.114
-                */
-               byte ^= IRDA_TRANS;
-               if (rx_buff->len < rx_buff->truesize)  {
-                       rx_buff->data[rx_buff->len++] = byte;
-#ifndef POSTPONE_RX_CRC
-                       rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
-#endif
-                       rx_buff->state = INSIDE_FRAME;
-               } else {
-                       pr_debug("%s(), Rx buffer overflow, aborting\n",
-                                __func__);
-                       rx_buff->state = OUTSIDE_FRAME;
-               }
-               break;
-
-       case OUTSIDE_FRAME:
-               /* Activate carrier sense */
-               if(byte != XBOF)
-                       irda_device_set_media_busy(dev, TRUE);
-               break;
-
-       case BEGIN_FRAME:
-       default:
-               rx_buff->data[rx_buff->len++] = byte;
-#ifndef POSTPONE_RX_CRC
-               rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
-#endif
-               rx_buff->state = INSIDE_FRAME;
-               break;
-       }
-}
-
-/*
- * Function async_unwrap_char (dev, rx_buff, byte)
- *
- *    Parse and de-stuff frame received from the IrDA-port
- *
- * This is the main entry point for SIR drivers.
- */
-void async_unwrap_char(struct net_device *dev,
-                      struct net_device_stats *stats,
-                      iobuff_t *rx_buff, __u8 byte)
-{
-       switch(byte) {
-       case CE:
-               async_unwrap_ce(dev, stats, rx_buff, byte);
-               break;
-       case BOF:
-               async_unwrap_bof(dev, stats, rx_buff, byte);
-               break;
-       case EOF:
-               async_unwrap_eof(dev, stats, rx_buff, byte);
-               break;
-       default:
-               async_unwrap_other(dev, stats, rx_buff, byte);
-               break;
-       }
-}
-EXPORT_SYMBOL(async_unwrap_char);
-