/*
- * $Id: ctcmain.c,v 1.74 2005/03/24 09:04:17 mschwide Exp $
+ * $Id: ctcmain.c,v 1.78 2005/09/07 12:18:02 pavlic Exp $
  *
  * CTC / ESCON network driver
  *
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.74 $
+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.78 $
  *
  */
-\f
 #undef DEBUG
 #include <linux/module.h>
 #include <linux/init.h>
        "TX down",
        "Restart",
 };
-\f
+
 /**
  * Events of the channel statemachine
  */
 print_banner(void)
 {
        static int printed = 0;
-       char vbuf[] = "$Revision: 1.74 $";
+       char vbuf[] = "$Revision: 1.78 $";
        char *version = vbuf;
 
        if (printed)
        "Restarting",
        "Not operational",
 };
-\f
+
 #ifdef DEBUG
 /**
  * Dump header and first 16 bytes of an sk_buff for debugging purposes.
 fsm_action_nop(fsm_instance * fi, int event, void *arg)
 {
 }
-\f
+
 /**
  * Actions for channel - statemachines.
  *****************************************************************************/
        fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_RESTART, dev);
 }
 
-\f
 /**
  * The statemachine for a channel.
  */
 };
 
 static const int CH_FSM_LEN = sizeof (ch_fsm) / sizeof (fsm_node);
-\f
+
 /**
  * Functions related to setup and device detection.
  *****************************************************************************/
                fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
 
 }
-\f
+
 /**
  * Actions for interface - statemachine.
  *****************************************************************************/
        int rc = 0;
 
        DBF_TEXT(trace, 5, __FUNCTION__);
+       /* we need to acquire the lock for testing the state
+        * otherwise we can have an IRQ changing the state to 
+        * TXIDLE after the test but before acquiring the lock.
+        */
+       spin_lock_irqsave(&ch->collect_lock, saveflags);
        if (fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) {
                int l = skb->len + LL_HEADER_LENGTH;
 
-               spin_lock_irqsave(&ch->collect_lock, saveflags);
-               if (ch->collect_len + l > ch->max_bufsize - 2)
-                       rc = -EBUSY;
-               else {
+               if (ch->collect_len + l > ch->max_bufsize - 2) {
+                       spin_unlock_irqrestore(&ch->collect_lock, saveflags);
+                       return -EBUSY;
+               } else {
                        atomic_inc(&skb->users);
                        header.length = l;
                        header.type = skb->protocol;
                int ccw_idx;
                struct sk_buff *nskb;
                unsigned long hi;
-
+               spin_unlock_irqrestore(&ch->collect_lock, saveflags);
                /**
                 * Protect skb against beeing free'd by upper
                 * layers.
                        if (!nskb) {
                                atomic_dec(&skb->users);
                                skb_pull(skb, LL_HEADER_LENGTH + 2);
+                               ctc_clear_busy(ch->netdev);
                                return -ENOMEM;
                        } else {
                                memcpy(skb_put(nskb, skb->len),
                                 */
                                atomic_dec(&skb->users);
                                skb_pull(skb, LL_HEADER_LENGTH + 2);
+                               ctc_clear_busy(ch->netdev);
                                return -EBUSY;
                        }
 
                }
        }
 
+       ctc_clear_busy(ch->netdev);
        return rc;
 }
-\f
+
 /**
  * Interface API for upper network layers
  *****************************************************************************/
        dev->trans_start = jiffies;
        if (transmit_skb(privptr->channel[WRITE], skb) != 0)
                rc = 1;
-       ctc_clear_busy(dev);
        return rc;
 }
 
        return count;
 }
 
-\f
 static void
 ctc_netdev_unregister(struct net_device * dev)
 {
        return count;
 }
 
-
 static ssize_t
 ctc_type_show(struct device *dev, struct device_attribute *attr, char *buf)
 {