diff -u -p linux/include/net/irda/irlmp.d2.h linux/include/net/irda/irlmp.h
--- linux/include/net/irda/irlmp.d2.h   Fri Nov  2 14:11:19 2001
+++ linux/include/net/irda/irlmp.h      Mon Nov  5 18:46:49 2001
@@ -11,6 +11,7 @@
  * 
  *     Copyright (c) 1998-1999 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 
@@ -131,7 +132,6 @@ struct lap_cb {
 
        struct irlap_cb *irlap;   /* Instance of IrLAP layer */
        hashbin_t *lsaps;         /* LSAP associated with this link */
-       int refcount;
 
        __u8  caddr;  /* Connection address */
        __u32 saddr;  /* Source device address */
diff -u -p linux/include/net/irda/irlmp_event.d2.h linux/include/net/irda/irlmp_event.h
--- linux/include/net/irda/irlmp_event.d2.h     Fri Nov  2 18:47:47 2001
+++ linux/include/net/irda/irlmp_event.h        Mon Nov  5 18:47:05 2001
@@ -11,6 +11,7 @@
  * 
  *     Copyright (c) 1997, 1999 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 
@@ -102,10 +103,6 @@ extern const char *irlsap_state[];
 void irlmp_watchdog_timer_expired(void *data);
 void irlmp_discovery_timer_expired(void *data);
 void irlmp_idle_timer_expired(void *data);
-
-void irlmp_next_station_state(IRLMP_STATE state);
-void irlmp_next_lsap_state(struct lsap_cb *self, LSAP_STATE state);
-void irlmp_next_lap_state(struct lap_cb *self, IRLMP_STATE state);
 
 void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event, 
                        struct sk_buff *skb);
diff -u -p linux/include/net/irda/irlap_event.d2.h linux/include/net/irda/irlap_event.h
--- linux/include/net/irda/irlap_event.d2.h     Fri Nov  2 18:47:12 2001
+++ linux/include/net/irda/irlap_event.h        Mon Nov  5 18:46:30 2001
@@ -12,6 +12,7 @@
  * 
  *     Copyright (c) 1998-1999 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 
@@ -103,6 +104,7 @@ typedef enum {
        DISCOVERY_TIMER_EXPIRED,
        WD_TIMER_EXPIRED,
        BACKOFF_TIMER_EXPIRED,
+       MEDIA_BUSY_TIMER_EXPIRED,
 } IRLAP_EVENT;
 
 /*
diff -u -p linux/include/net/irda/timer.d2.h linux/include/net/irda/timer.h
--- linux/include/net/irda/timer.d2.h   Mon Nov  5 14:12:40 2001
+++ linux/include/net/irda/timer.h      Mon Nov  5 18:48:00 2001
@@ -11,6 +11,7 @@
  * 
  *     Copyright (c) 1997, 1998-1999 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 
@@ -47,7 +48,9 @@
  *  duration of the P-timer.
  */
 #define WD_TIMEOUT          (POLL_TIMEOUT*2)
+
 #define MEDIABUSY_TIMEOUT   (500*HZ/1000)    /* 500 msec */
+#define SMALLBUSY_TIMEOUT   (100*HZ/1000)    /* 100 msec - IrLAP 6.13.4 */
 
 /*
  *  Slot timer must never exceed 85 ms, and must always be at least 25 ms, 
@@ -75,7 +78,7 @@ inline void irlap_start_final_timer(stru
 inline void irlap_start_wd_timer(struct irlap_cb *self, int timeout);
 inline void irlap_start_backoff_timer(struct irlap_cb *self, int timeout);
 
-void irlap_start_mbusy_timer(struct irlap_cb *);
+void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout);
 void irlap_stop_mbusy_timer(struct irlap_cb *);
 
 struct lsap_cb;
diff -u -p linux/include/net/irda/irda.d2.h linux/include/net/irda/irda.h
--- linux/include/net/irda/irda.d2.h    Mon Nov  5 14:07:34 2001
+++ linux/include/net/irda/irda.h       Mon Nov  5 18:46:05 2001
@@ -10,6 +10,7 @@
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-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 
@@ -42,6 +43,11 @@ typedef __u32 magic_t;
 
 #ifndef FALSE 
 #define FALSE 0
+#endif
+
+/* Hack to do small backoff when setting media busy in IrLAP */
+#ifndef SMALL
+#define SMALL 5
 #endif
 
 #ifndef IRDA_MIN /* Lets not mix this MIN with other header files */
diff -u -p linux/net/irda/irlmp.d2.c linux/net/irda/irlmp.c
--- linux/net/irda/irlmp.d2.c   Wed Oct 31 15:44:59 2001
+++ linux/net/irda/irlmp.c      Mon Nov  5 18:43:34 2001
@@ -11,6 +11,7 @@
  * 
  *     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 
@@ -75,7 +76,7 @@ int irlmp_proc_read(char *buf, char **st
  */
 int __init irlmp_init(void)
 {
-       IRDA_DEBUG(0, __FUNCTION__ "()\n");
+       IRDA_DEBUG(1, __FUNCTION__ "()\n");
        /* Initialize the irlmp structure. */
        irlmp = kmalloc( sizeof(struct irlmp_cb), GFP_KERNEL);
        if (irlmp == NULL)
@@ -170,14 +171,14 @@ struct lsap_cb *irlmp_open_lsap(__u8 sls
 #endif /* CONFIG_IRDA_ULTRA */
        } else
                self->dlsap_sel = LSAP_ANY;
-       self->connected = FALSE;
+       /* self->connected = FALSE; -> already NULL via memset() */
 
        init_timer(&self->watchdog_timer);
 
        ASSERT(notify->instance != NULL, return NULL;);
        self->notify = *notify;
 
-       irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+       self->lsap_state = LSAP_DISCONNECTED;
        
        /* Insert into queue of unconnected LSAPs */
        hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, (int) self, 
@@ -237,6 +238,7 @@ void irlmp_close_lsap(struct lsap_cb *se
                ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
                lsap = hashbin_remove(lap->lsaps, (int) self, NULL);
        }
+       self->lap = NULL;
        /* Check if we found the LSAP! If not then try the unconnected lsaps */
        if (!lsap) {
                lsap = hashbin_remove(irlmp->unconnected_lsaps, (int) self, 
@@ -281,7 +283,7 @@ void irlmp_register_link(struct irlap_cb
        lap->daddr = DEV_ADDR_ANY;
        lap->lsaps = hashbin_new(HB_GLOBAL);
 
-       irlmp_next_lap_state(lap, LAP_STANDBY);
+       lap->lap_state = LAP_STANDBY;
        
        init_timer(&lap->idle_timer);
 
@@ -346,7 +348,7 @@ int irlmp_connect_request(struct lsap_cb
              "(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n", 
              self->slsap_sel, dlsap_sel, saddr, daddr);
        
-       if (self->connected) 
+       if (test_bit(0, &self->connected))
                return -EISCONN;
        
        /* Client must supply destination device address */
@@ -435,7 +437,7 @@ int irlmp_connect_request(struct lsap_cb
 
        hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (int) self, NULL);
 
-       self->connected = TRUE;
+       set_bit(0, &self->connected);   /* TRUE */
        
        /*
         *  User supplied qos specifications?
@@ -481,6 +483,8 @@ void irlmp_connect_indication(struct lsa
                self->notify.connect_indication(self->notify.instance, self, 
                                                &self->qos, max_seg_size, 
                                                max_header_size, skb);
+       else
+               dev_kfree_skb(skb);
 }
 
 /*
@@ -495,7 +499,7 @@ int irlmp_connect_response(struct lsap_c
        ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
        ASSERT(userdata != NULL, return -1;);
 
-       self->connected = TRUE;
+       set_bit(0, &self->connected);   /* TRUE */
 
        IRDA_DEBUG(2, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", 
                   self->slsap_sel, self->dlsap_sel);
@@ -543,7 +547,8 @@ void irlmp_connect_confirm(struct lsap_c
                self->notify.connect_confirm(self->notify.instance, self,
                                             &self->qos, max_seg_size,
                                             max_header_size, skb);
-       }
+       } else
+               dev_kfree_skb(skb);
 }
 
 /*
@@ -598,16 +603,18 @@ int irlmp_disconnect_request(struct lsap
 
        ASSERT(self != NULL, return -1;);
        ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+       ASSERT(userdata != NULL, return -1;);
 
-       /* Already disconnected? */
-       if (!self->connected) {
-               WARNING(__FUNCTION__ "(), already disconnected!\n");
+       /* 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)) {
+               IRDA_DEBUG(0, __FUNCTION__ "(), already disconnected!\n");
+               dev_kfree_skb(userdata);
                return -1;
        }
 
-       ASSERT(userdata != NULL, return -1;);
-       ASSERT(self->connected == TRUE, return -1;);
-       
        skb_push(userdata, LMP_CONTROL_HEADER);
 
        /* 
@@ -634,7 +641,6 @@ int irlmp_disconnect_request(struct lsap
                       NULL);
        
        /* Reset some values */
-       self->connected = FALSE;
        self->dlsap_sel = LSAP_ANY;
        self->lap = NULL;
        
@@ -651,16 +657,23 @@ void irlmp_disconnect_indication(struct 
 {
        struct lsap_cb *lsap;
 
-       IRDA_DEBUG(1, __FUNCTION__ "(), reason=%s\n", lmp_reasons[reason]);     
+       IRDA_DEBUG(1, __FUNCTION__ "(), reason=%s\n", lmp_reasons[reason]);
        ASSERT(self != NULL, return;);
        ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
-       ASSERT(self->connected == TRUE, return;); 
 
        IRDA_DEBUG(3, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", 
                   self->slsap_sel, self->dlsap_sel);
 
-       self->connected = FALSE;
-       self->dlsap_sel = LSAP_ANY;
+       /* 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)) {
+               IRDA_DEBUG(0, __FUNCTION__ "(), already disconnected!\n");
+               if (userdata)
+                       dev_kfree_skb(userdata);
+               return;
+       }
 
 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
        irlmp->cache.valid = FALSE;
@@ -679,6 +692,7 @@ void irlmp_disconnect_indication(struct 
        hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap, (int) lsap, 
                       NULL);
 
+       self->dlsap_sel = LSAP_ANY;
        self->lap = NULL;
        
        /*
@@ -689,7 +703,8 @@ void irlmp_disconnect_indication(struct 
                                                   self, reason, userdata);
        else {
                IRDA_DEBUG(0, __FUNCTION__ "(), no handler\n");
-               dev_kfree_skb(userdata);
+               if (userdata)
+                       dev_kfree_skb(userdata);
        }
 }
 
@@ -1401,7 +1416,7 @@ __u32 irlmp_register_client(__u16 hint_m
        irlmp_client_t *client;
        __u32 handle;
 
-       IRDA_DEBUG(0, __FUNCTION__ "()\n");
+       IRDA_DEBUG(1, __FUNCTION__ "()\n");
        ASSERT(irlmp != NULL, return 0;);
        
        /* Get a unique handle for this client */
@@ -1673,14 +1688,15 @@ int irlmp_proc_read(char *buf, char **st
 
                len += sprintf(buf+len, "saddr: %#08x, daddr: %#08x, ",
                               lap->saddr, lap->daddr); 
-               len += sprintf(buf+len, "refcount: %d", lap->refcount);
+               len += sprintf(buf+len, "num lsaps: %d",
+                              HASHBIN_GET_SIZE(lap->lsaps));
                len += sprintf(buf+len, "\n");
 
-               len += sprintf(buf+len, "\nConnected LSAPs:\n");
+               len += sprintf(buf+len, "\n  Connected LSAPs:\n");
                self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
                while (self != NULL) {
                        ASSERT(self->magic == LMP_LSAP_MAGIC, return 0;);
-                       len += sprintf(buf+len, "lsap state: %s, ", 
+                       len += sprintf(buf+len, "  lsap state: %s, ", 
                                       irlsap_state[ self->lsap_state]);
                        len += sprintf(buf+len, 
                                       "slsap_sel: %#02x, dlsap_sel: %#02x, ",
diff -u -p linux/net/irda/irlmp_event.d2.c linux/net/irda/irlmp_event.c
--- linux/net/irda/irlmp_event.d2.c     Fri Nov  2 13:55:19 2001
+++ linux/net/irda/irlmp_event.c        Mon Nov  5 18:43:56 2001
@@ -11,6 +11,7 @@
  * 
  *     Copyright (c) 1998-1999 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 
@@ -114,6 +115,25 @@ static int (*lsap_state[])( struct lsap_
        irlmp_state_setup_pend
 };
 
+static inline void irlmp_next_lap_state(struct lap_cb *self,
+                                       IRLMP_STATE state) 
+{
+       /*
+       IRDA_DEBUG(4, __FUNCTION__ "(), LMP LAP = %s\n", irlmp_state[state]);
+       */
+       self->lap_state = state;
+}
+
+static inline void irlmp_next_lsap_state(struct lsap_cb *self,
+                                        LSAP_STATE state) 
+{
+       /*
+       ASSERT(self != NULL, return;);
+       IRDA_DEBUG(4, __FUNCTION__ "(), LMP LSAP = %s\n", 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)
@@ -223,7 +243,6 @@ static void irlmp_state_standby(struct l
                IRDA_DEBUG(4, __FUNCTION__ "() LS_CONNECT_REQUEST\n");
 
                irlmp_next_lap_state(self, LAP_U_CONNECT);
-               self->refcount++;
 
                /* FIXME: need to set users requested QoS */
                irlap_connect_request(self->irlap, self->daddr, NULL, 0);
@@ -278,12 +297,12 @@ static void irlmp_state_u_connect(struct
                 * the lsaps may already have gone. This avoid getting stuck
                 * forever in LAP_ACTIVE state - Jean II */
                if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
+                       IRDA_DEBUG(0, __FUNCTION__ "() NO LSAPs !\n");
                        irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
                }
                break;
        case LM_LAP_CONNECT_REQUEST:
                /* Already trying to connect */
-               self->refcount++;
                break;
        case LM_LAP_CONNECT_CONFIRM:
                /* For all lsap_ce E Associated do LS_Connect_confirm */
@@ -298,12 +317,13 @@ static void irlmp_state_u_connect(struct
                 * the lsaps may already have gone. This avoid getting stuck
                 * forever in LAP_ACTIVE state - Jean II */
                if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
+                       IRDA_DEBUG(0, __FUNCTION__ "() NO LSAPs !\n");
                        irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
                }
                break;
        case LM_LAP_DISCONNECT_INDICATION:
+               IRDA_DEBUG(4, __FUNCTION__ "(), LM_LAP_DISCONNECT_INDICATION\n");
                irlmp_next_lap_state(self, LAP_STANDBY);
-               self->refcount = 0;
 
                /* Send disconnect event to all LSAPs using this link */
                lsap = (struct lsap_cb *) hashbin_get_first( self->lsaps);
@@ -322,9 +342,11 @@ static void irlmp_state_u_connect(struct
        case LM_LAP_DISCONNECT_REQUEST:
                IRDA_DEBUG(4, __FUNCTION__ "(), LM_LAP_DISCONNECT_REQUEST\n");
 
-               self->refcount--;
-               if (self->refcount == 0)
-                       irlmp_next_lap_state(self, LAP_STANDBY);
+               /* 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:
                IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %s\n",
@@ -352,7 +374,6 @@ static void irlmp_state_active(struct la
        switch (event) {
        case LM_LAP_CONNECT_REQUEST:
                IRDA_DEBUG(4, __FUNCTION__ "(), LS_CONNECT_REQUEST\n");
-               self->refcount++;
 
                /*
                 *  LAP connection allready active, just bounce back! Since we 
@@ -379,8 +400,6 @@ static void irlmp_state_active(struct la
                /* Keep state */
                break;
        case LM_LAP_DISCONNECT_REQUEST:
-               self->refcount--;
-
                /*
                 *  Need to find out if we should close IrLAP or not. If there
                 *  is only one LSAP connection left on this link, that LSAP 
@@ -419,7 +438,6 @@ static void irlmp_state_active(struct la
                break;
        case LM_LAP_DISCONNECT_INDICATION:
                irlmp_next_lap_state(self, LAP_STANDBY);                
-               self->refcount = 0;
                
                /* In some case, at this point our side has already closed
                 * all lsaps, and we are waiting for the idle_timer to
@@ -517,6 +535,8 @@ static int irlmp_state_disconnected(stru
                 * 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 */
@@ -577,9 +597,8 @@ static int irlmp_state_connect(struct ls
                 * Jean II */
                IRDA_DEBUG(0, __FUNCTION__ "() WATCHDOG_TIMEOUT!\n");
 
-               /* Here, we should probably disconnect proper */
+               /* Disconnect, get out... - Jean II */
                self->dlsap_sel = LSAP_ANY;
-               self->conn_skb = NULL;
                irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
                break;
        default:
@@ -612,15 +631,6 @@ static int irlmp_state_connect_pend(stru
        case LM_CONNECT_REQUEST:
                /* Keep state */
                break;
-       case LM_CONNECT_INDICATION:
-               /* Will happen in some rare cases when the socket get stuck,
-                * the other side retries the connect request.
-                * We just unstuck the socket - Jean II */
-               IRDA_DEBUG(0, __FUNCTION__ "(), LM_CONNECT_INDICATION, "
-                          "LSAP stuck in CONNECT_PEND state...\n");
-               /* Keep state */
-               irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
-               break;
        case LM_CONNECT_RESPONSE:
                IRDA_DEBUG(0, __FUNCTION__ "(), LM_CONNECT_RESPONSE, "
                           "no indication issued yet\n");
@@ -648,6 +658,8 @@ static int irlmp_state_connect_pend(stru
 
                /* Go back to disconnected mode, keep the socket waiting */
                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;
@@ -856,7 +868,7 @@ static int irlmp_state_setup_pend(struct
                irlmp_next_lsap_state(self, LSAP_SETUP);
                break;
        case LM_WATCHDOG_TIMEOUT:
-               IRDA_DEBUG(0, __FUNCTION__ "() WATCHDOG_TIMEOUT!\n");
+               IRDA_DEBUG(0, __FUNCTION__ "() : WATCHDOG_TIMEOUT !\n");
 
                ASSERT(self->lap != NULL, return -1;);
                irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
@@ -881,18 +893,4 @@ static int irlmp_state_setup_pend(struct
                break;  
        }
        return ret;
-}
-
-void irlmp_next_lap_state(struct lap_cb *self, IRLMP_STATE state) 
-{
-       IRDA_DEBUG(4, __FUNCTION__ "(), LMP LAP = %s\n", irlmp_state[state]);
-       self->lap_state = state;
-}
-
-void irlmp_next_lsap_state(struct lsap_cb *self, LSAP_STATE state) 
-{
-       ASSERT(self != NULL, return;);
-
-       IRDA_DEBUG(4, __FUNCTION__ "(), LMP LSAP = %s\n", irlsap_state[state]);
-       self->lsap_state = state;
 }
diff -u -p linux/net/irda/irlmp_frame.d2.c linux/net/irda/irlmp_frame.c
--- linux/net/irda/irlmp_frame.d2.c     Thu Nov  1 15:07:57 2001
+++ linux/net/irda/irlmp_frame.c        Mon Nov  5 18:44:14 2001
@@ -11,6 +11,7 @@
  * 
  *     Copyright (c) 1998-1999 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 
@@ -34,9 +35,6 @@
 #include <net/irda/irlmp_frame.h>
 #include <net/irda/discovery.h>
 
-#define        DISCO_SMALL_DELAY       250     /* Delay for some discoveries in ms */
-struct timer_list disco_delay;         /* The timer associated */
-
 static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap, 
                                       __u8 slsap, int status, hashbin_t *);
 
@@ -343,28 +341,6 @@ void irlmp_link_connect_confirm(struct l
 }
 
 /*
- * Function irlmp_discovery_timeout (priv)
- *
- *    Create a discovery event to the state machine (called after a delay)
- *
- * Note : irlmp_do_lap_event will handle the very rare case where the LAP
- * is destroyed while we were sleeping.
- */
-static void irlmp_discovery_timeout(u_long     priv)
-{
-       struct lap_cb *self;
-
-       IRDA_DEBUG(2, __FUNCTION__ "()\n");
-
-       self = (struct lap_cb *) priv;
-       ASSERT(self != NULL, return;);
-
-       /* Just handle it the same way as a discovery confirm,
-        * bypass the LM_LAP state machine (see below) */
-       irlmp_discovery_confirm(irlmp->cachelog);
-}
-
-/*
  * Function irlmp_link_discovery_indication (self, log)
  *
  *    Device is discovering us
@@ -379,25 +355,16 @@ static void irlmp_discovery_timeout(u_lo
  *     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).
+ *       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 trick here is to defer the event by a little delay to avoid both
- * devices to jump in exactly at the same time...
- *
- * The delay is currently set to 0.25s, which leave enough time to perform
- * a connection and don't interfer with next discovery (the lowest discovery
- * period/timeout that may be set is 1s). The message triggering this
- * event was the last of the discovery, so the medium is now free...
- * Maybe more testing is needed to get the value right...
-
- * One more problem : the other node might do only a single discovery
- * and connect immediately to us, and we would receive only a single
- * discovery indication event, and because of the delay, it will arrive
- * while the LAP is connected. That's another good reason to
- * bypass the LM_LAP state machine ;-)
+ * 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
  */
@@ -409,14 +376,9 @@ void irlmp_link_discovery_indication(str
 
        irlmp_add_discovery(irlmp->cachelog, discovery);
        
-       /* If delay was activated, kill it! */
-       if(timer_pending(&disco_delay))
-               del_timer(&disco_delay);
-       /* Set delay timer to expire in 0.25s. */
-       disco_delay.expires = jiffies + (DISCO_SMALL_DELAY * HZ/1000);
-       disco_delay.function = irlmp_discovery_timeout;
-       disco_delay.data = (unsigned long) self;
-       add_timer(&disco_delay);
+       /* Just handle it the same way as a discovery confirm,
+        * bypass the LM_LAP state machine (see below) */
+       irlmp_discovery_confirm(irlmp->cachelog);
 }
 
 /*
@@ -435,10 +397,6 @@ void irlmp_link_discovery_confirm(struct
        ASSERT(self->magic == LMP_LAP_MAGIC, return;);
        
        irlmp_add_discovery_log(irlmp->cachelog, log);
-
-       /* If discovery delay was activated, kill it! */
-       if(timer_pending(&disco_delay))
-               del_timer(&disco_delay);
 
        /* Propagate event to various LSAPs registered for it.
         * We bypass the LM_LAP state machine because
diff -u -p linux/net/irda/irlap.d2.c linux/net/irda/irlap.c
--- linux/net/irda/irlap.d2.c   Fri Nov  2 17:23:04 2001
+++ linux/net/irda/irlap.c      Mon Nov  5 18:41:34 2001
@@ -10,6 +10,7 @@
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-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 
@@ -502,10 +503,17 @@ void irlap_discovery_request(struct irla
                IRDA_DEBUG(4, __FUNCTION__ 
                           "(), discovery only possible in NDM mode\n");
                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 */
+       /* 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;
@@ -555,10 +563,16 @@ void irlap_discovery_confirm(struct irla
        
        /* 
         * Check for successful discovery, since we are then allowed to clear 
-        * the media busy condition (irlap p.94). This should allow us to make 
-        * connection attempts much easier.
+        * 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 && HASHBIN_GET_SIZE(discovery_log) > 0)
+       if (discovery_log)
                irda_device_set_media_busy(self->netdev, FALSE);
        
        /* Inform IrLMP */
@@ -580,7 +594,18 @@ void irlap_discovery_indication(struct i
        ASSERT(discovery != NULL, return;);
 
        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);
 }
 
diff -u -p linux/net/irda/irlap_event.d2.c linux/net/irda/irlap_event.c
--- linux/net/irda/irlap_event.d2.c     Fri Nov  2 16:28:13 2001
+++ linux/net/irda/irlap_event.c        Mon Nov  5 18:42:55 2001
@@ -12,6 +12,7 @@
  *     Copyright (c) 1998-2000 Dag Brattli <dag@brattli.net>,
  *     Copyright (c) 1998      Thomas Davis <ratbert@radiks.net>
  *     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 
@@ -114,6 +115,7 @@ static const char *irlap_event[] = {
        "DISCOVERY_TIMER_EXPIRED",
        "WD_TIMER_EXPIRED",
        "BACKOFF_TIMER_EXPIRED",
+       "MEDIA_BUSY_TIMER_EXPIRED",
 };
 
 const char *irlap_state[] = {
@@ -263,15 +265,7 @@ void irlap_do_event(struct irlap_cb *sel
                                                    NULL, NULL);
                }
                break;
-       case LAP_NDM:
-               /* Check if we should try to connect */
-               if ((self->connect_pending) && !self->media_busy) {
-                       self->connect_pending = FALSE;
-                       
-                       ret = (*state[self->state])(self, CONNECT_REQUEST, 
-                                                   NULL, NULL);
-               }
-               break;
+/*     case LAP_NDM: */
 /*     case LAP_CONN: */
 /*     case LAP_RESET_WAIT: */
 /*     case LAP_RESET_CHECK: */
@@ -305,17 +299,6 @@ void irlap_next_state(struct irlap_cb *s
        if ((state != LAP_XMIT_P) && (state != LAP_XMIT_S))
                self->bytes_left = self->line_capacity;
 #endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
-#ifdef CONFIG_IRDA_ULTRA
-       /* Send any pending Ultra frames if any */
-       /* The higher layers may have sent a few Ultra frames while we
-        * were doing discovery (either query or reply). Those frames
-        * have been queued, but were never sent. It is now time to
-        * send them...
-        * Jean II */
-       if ((state == LAP_NDM) && (!skb_queue_empty(&self->txq_ultra)))
-               /* Force us to listen 500 ms before sending Ultra */
-               irda_device_set_media_busy(self->netdev, TRUE);
-#endif /* CONFIG_IRDA_ULTRA */
 }
 
 /*
@@ -339,6 +322,9 @@ static int irlap_state_ndm(struct irlap_
                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 */
                        IRDA_DEBUG(0, __FUNCTION__
                                   "(), CONNECT_REQUEST: media busy!\n");
                        
@@ -379,6 +365,9 @@ static int irlap_state_ndm(struct irlap_
                                                
                        /* 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;
                } 
                
@@ -417,8 +406,7 @@ static int irlap_state_ndm(struct irlap_
                         */
                        irlap_start_query_timer(self, QUERY_TIMEOUT*info->S);
                        irlap_next_state(self, LAP_REPLY);
-               }
-               else {
+               } 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).
@@ -440,6 +428,54 @@ static int irlap_state_ndm(struct irlap_
                        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:
                /* Only allowed to repeat an operation twice */
@@ -735,8 +771,10 @@ static int irlap_state_conn(struct irlap
 
                break;          
        case DISCONNECT_REQUEST:
+               IRDA_DEBUG(0, __FUNCTION__ "(), Disconnect request!\n");
                irlap_send_dm_frame(self);
-               irlap_next_state( self, LAP_CONN);
+               irlap_next_state( self, LAP_NDM);
+               irlap_disconnect_indication(self, LAP_DISC_INDICATION);
                break;
        default:
                IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, %s\n", event,
@@ -1417,13 +1455,12 @@ static int irlap_state_nrm_p(struct irla
                irlap_start_final_timer(self, self->final_timeout);
                break;
        case RECV_RD_RSP:
-               IRDA_DEBUG(0, __FUNCTION__ "(), RECV_RD_RSP\n");
+               IRDA_DEBUG(1, __FUNCTION__ "(), RECV_RD_RSP\n");
 
-               irlap_next_state(self, LAP_PCLOSE);
-               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_XMIT_P);
+               /* Call back the LAP state machine to do a proper disconnect */
+               irlap_disconnect_request(self);
                break;
        default:
                IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %s\n", 
diff -u -p linux/net/irda/irlap_frame.d2.c linux/net/irda/irlap_frame.c
--- linux/net/irda/irlap_frame.d2.c     Fri Nov  2 18:15:13 2001
+++ linux/net/irda/irlap_frame.c        Mon Nov  5 18:43:15 2001
@@ -11,6 +11,7 @@
  * 
  *     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 
@@ -696,7 +697,7 @@ static void irlap_recv_srej_frame(struct
 static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb, 
                                  struct irlap_info *info, int command)
 {
-       IRDA_DEBUG(0, __FUNCTION__ "()\n");
+       IRDA_DEBUG(2, __FUNCTION__ "()\n");
 
        /* Check if this is a command or a response frame */
        if (command)
diff -u -p linux/net/irda/timer.d2.c linux/net/irda/timer.c
--- linux/net/irda/timer.d2.c   Fri Nov  2 18:04:22 2001
+++ linux/net/irda/timer.c      Mon Nov  5 18:45:23 2001
@@ -11,6 +11,7 @@
  * 
  *     Copyright (c) 1997, 1999 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 
@@ -94,29 +95,24 @@ void irlap_start_backoff_timer(struct ir
                         irlap_backoff_timer_expired);
 }
 
-void irlap_start_mbusy_timer(struct irlap_cb *self)
+void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout)
 {
-       irda_start_timer(&self->media_busy_timer, MEDIABUSY_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! */
-       if(timer_pending(&self->media_busy_timer))
-               del_timer(&self->media_busy_timer);
+       del_timer(&self->media_busy_timer);
 
-#ifdef CONFIG_IRDA_ULTRA
-       /* Send any pending Ultra frames if any */
-       if (!skb_queue_empty(&self->txq_ultra))
-               /* Note : we don't send the frame, just post an event.
-                * Frames will be sent only if we are in NDM mode (see
-                * irlap_event.c).
-                * Also, moved this code from irlap_media_busy_expired()
-                * to here to catch properly all cases...
-                * Jean II */
-               irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);
-#endif /* CONFIG_IRDA_ULTRA */
+       /* 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) 
@@ -237,5 +233,7 @@ void irlap_media_busy_expired(void* data
        ASSERT(self != NULL, return;);
 
        irda_device_set_media_busy(self->netdev, FALSE);
-       /* Note : will deal with Ultra frames */
+       /* 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 -u -p linux/net/irda/irda_device.d2.c linux/net/irda/irda_device.c
--- linux/net/irda/irda_device.d2.c     Fri Nov  2 18:04:40 2001
+++ linux/net/irda/irda_device.c        Mon Nov  5 18:40:43 2001
@@ -10,6 +10,7 @@
  * 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 
@@ -181,7 +182,10 @@ void irda_device_set_media_busy(struct n
 
        if (status) {
                self->media_busy = TRUE;
-               irlap_start_mbusy_timer(self);
+               if (status == SMALL)
+                       irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT);
+               else
+                       irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT);
                IRDA_DEBUG( 4, "Media busy!\n");
        } else {
                self->media_busy = FALSE;
