diff -Naur zaptel-1.4.9.2/build_tools/make_firmware_object zaptel-1.4.9.2.patched/build_tools/make_firmware_object
--- zaptel-1.4.9.2/build_tools/make_firmware_object	2008-05-19 17:27:40.000000000 -0400
+++ zaptel-1.4.9.2.patched/build_tools/make_firmware_object	2010-04-03 22:58:25.000000000 -0400
@@ -5,7 +5,7 @@
 #   1 - firmware file
 #   2 - output file
 
-bfdname=elf64-x86-64
-bfdarch=i386:x86-64
+bfdname=elf32-i386
+bfdarch=i386
 
 objcopy -I binary ${1} -B ${bfdarch} -O ${bfdname} ${2} --rename-section .data=.rodata,alloc,load,data,contents,readonly
diff -Naur zaptel-1.4.9.2/kernel/zaptel.h zaptel-1.4.9.2.patched/kernel/zaptel.h
--- zaptel-1.4.9.2/kernel/zaptel.h	2008-02-04 18:00:48.000000000 -0500
+++ zaptel-1.4.9.2.patched/kernel/zaptel.h	2010-04-04 23:34:45.000000000 -0400
@@ -1686,7 +1686,8 @@
 	/* Flush any pending messages */
 	int (*flush)(void);
 
-	struct zt_dynamic_driver *next;
+	struct list_head list;
+
 };
 
 /* Receive a dynamic span message */
diff -Naur zaptel-1.4.9.2/kernel/ztd-ethmf.c zaptel-1.4.9.2.patched/kernel/ztd-ethmf.c
--- zaptel-1.4.9.2/kernel/ztd-ethmf.c	2008-05-19 17:27:33.000000000 -0400
+++ zaptel-1.4.9.2.patched/kernel/ztd-ethmf.c	2010-04-05 17:16:20.000000000 -0400
@@ -44,8 +44,10 @@
 #endif
 
 #define ETH_P_ZTDETH	0xd00d
-
+#define ETHMF_MAX_PER_SPAN_GROUP        8
+#define ETHMF_MAX_GROUPS                16
 #define ETHMF_FLAG_IGNORE_CHAN0 (1 << 3)
+#define ETHMF_MAX_SPANS                 4
 
 struct ztdeth_header {
   unsigned short subaddr;
@@ -53,20 +55,22 @@
 
 /* Timer for enabling spans - used to combat a Zaptel lock problem */
 static struct timer_list timer;
-/* Variable used by the timer to correctly deduce the amount of time passed */
-static int timer_counter;
+
+/* Whether or not the timer has been deleted */
+static atomic_t timer_deleted = ATOMIC_INIT(0);
+
+/* Global error counter */
+//static atomic_t errcount = ATOMIC_INIT(0);
+
+/* Whether or not we are in shutdown */
+static atomic_t shutdown = ATOMIC_INIT(0);
 
 /* We take the raw message, put it in an ethernet frame, and add a
    two byte addressing header at the top for future use */
-#ifdef DEFINE_SPINLOCK
-static DEFINE_SPINLOCK(zlock);
-#else
-static spinlock_t zlock = SPIN_LOCK_UNLOCKED;
-#endif
 
 static struct sk_buff_head skbs;
 
-static struct ztdeth {
+struct ztdeth {
   unsigned char addr[ETH_ALEN];
   unsigned int addr_hash;
 
@@ -74,38 +78,70 @@
   struct zt_span *span;
   char ethdev[IFNAMSIZ];
   struct net_device *dev;
-  struct ztdeth *next;
   unsigned char *msgbuf;
   int msgbuf_len;
-  int ready;
+
+  atomic_t ready;
   
-  int delay;
+  atomic_t delay;
 
   unsigned char *rcvbuf;
   spinlock_t rblock;
   int real_channels;
-  int no_front_padding;
-} *zdevs = NULL;
+  atomic_t no_front_padding;
+  atomic_t refcnt;
+  struct list_head list;
+};
 
-/*
- * Return the ztdeth structure for a given MultiFrame zt_span
+/**
+ * Lock for adding and removing items in ethmf_list
+ */
+#ifdef DEFINE_SPINLOCK
+static DEFINE_SPINLOCK(ethmf_lock);
+#else
+static spinlock_t ethmf_lock = SPIN_LOCK_UNLOCKED;
+#endif
+/**
+ * The active list of all running spans
  */
-struct ztdeth *ztdethmf_getz(struct zt_span *span)
+static LIST_HEAD(ethmf_list);
+
+static inline void ethmf_errors_inc(void)
 {
-  unsigned long flags;
-  struct ztdeth *z = NULL, *Z = NULL;
+#ifdef USE_PROC_FS
+        atomic_inc(&errcount);
+#endif
+}
 
-  spin_lock_irqsave(&zlock, flags);
-  z = zdevs;
-  while (z) {
-    if (z->span == span)
-      Z = z;
-    z = z->next;
-  }
-  spin_unlock_irqrestore(&zlock, flags);
-  return Z;
+/*
+ * Find the Ztdeth Struct and DAHDI span for a given MAC address and subaddr.
+ *
+ * NOTE: RCU read lock must already be held.
+ */
+static inline void find_ethmf(const unsigned char *addr,
+        const unsigned short subaddr, struct ztdeth **ze,
+        struct zt_span **span)
+{
+        struct ztdeth *z;
+
+        list_for_each_entry_rcu(z, &ethmf_list, list) {
+                if (!atomic_read(&z->delay)) {
+                        if (!memcmp(addr, z->addr, ETH_ALEN)
+                                        && z->subaddr == subaddr) {
+                                *ze = z;
+                                *span = z->span;
+                                return;
+                        }
+                }
+        }
+
+        /* no results */
+        *ze = NULL;
+        *span = NULL;
 }
 
+/*old way of finding ethmf span*/
+/*
 struct zt_span *ztdethmf_getspan(unsigned char *addr, unsigned short subaddr)
 {
   unsigned long flags;
@@ -127,6 +163,41 @@
   spin_unlock_irqrestore(&zlock, flags);
   return span;
 }
+*/
+
+/**
+ * Determines if all spans are ready for transmit. If all spans are ready,
+ * we return the number of spans which indeed are ready and populate the
+ * array of pointers to those spans..
+ *
+ * NOTE: RCU read lock must already be held.
+ */
+static inline int ethmf_trx_spans_ready(unsigned int addr_hash, struct ztdeth *(*ready_spans)[ETHMF_MAX_PER_SPAN_GROUP])
+{
+        struct ztdeth *t;
+        int span_count = 0, spans_ready = 0;
+
+        list_for_each_entry_rcu(t, &ethmf_list, list) {
+                if (!atomic_read(&t->delay) && t->addr_hash == addr_hash) {
+                        ++span_count;
+                        if (atomic_read(&t->ready)) {
+                                short subaddr = ntohs(t->subaddr);
+                                if (subaddr < ETHMF_MAX_PER_SPAN_GROUP) {
+                                        (*ready_spans)[subaddr] = t;
+                                        ++spans_ready;
+                                } else {
+                                        printk(KERN_ERR "More than %d spans per multi-frame group are not currently supported.",
+                                                ETHMF_MAX_PER_SPAN_GROUP);
+                                }
+                        }
+                }
+        }
+
+        if (span_count && spans_ready && span_count == spans_ready) {
+                return spans_ready;
+        }
+        return 0;
+}
 
 #if defined(LINUX26) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
 static int ztdethmf_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
@@ -139,6 +210,8 @@
   struct zt_span *span;
   struct ztdeth *z;
   struct ztdeth_header *zh;
+  unsigned int samples, channels, rbslen, flags;
+  unsigned int skip = 0;
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
   zh = (struct ztdeth_header *)skb_network_header(skb);
@@ -158,72 +231,92 @@
 
     skb_pull(skb, sizeof(struct ztdeth_header));
 
+#ifdef NEW_SKB_LINEARIZE
+                if (skb_is_nonlinear(skb))
+                        skb_linearize(skb);
+#else
+                if (skb_is_nonlinear(skb))
+                        skb_linearize(skb, GFP_KERNEL);
+#endif
+
+
     data = (unsigned char *) skb->data;
+    rcu_read_lock();
     do {
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9)
-      span = ztdethmf_getspan(eth_hdr(skb)->h_source, htons(span_index));
+	
+      find_ethmf(eth_hdr(skb)->h_source,
+                                htons(span_index), &z, &span);
 #else
-      span = ztdethmf_getspan(skb->mac.ethernet->h_source, htons(span_index));
+      find_ethmf(skb->mac.ethernet->h_source,
+                                htons(span_index), &z, &span);
 #endif	
+	if (unlikely(!z || !span)) {
+
+		++span_index;
+		continue;
+	}
 
-      if (span) {
-	unsigned int samples, channels, rbslen, flags;
-	unsigned int skip = 0;
 	samples = data[span_index*6] & 0xFF;
 	flags = data[span_index*6 + 1] & 0xFF;
 	channels = data[span_index*6 + 5] & 0xFF;
 
 	/* Precomputed defaults for most typical values */
-	if (channels == 24)
-	  rbslen = 12;
+      	if (channels == 24)
+	  	rbslen = 12;
 	else if (channels == 31)
-	  rbslen = 16;
+		rbslen = 16;
 	else
-	  rbslen = ((channels + 3) / 4) * 2;
+		rbslen = ((channels + 3) / 4) * 2;
 
-	if (samples == 8 && channels < 32)
-	  {
-	    /* Find the correct ethmf span */
-	    z = ztdethmf_getz(span);
-	    if (z) {
-	      spin_lock(&z->rblock);
 
-	      memcpy(z->rcvbuf, data + 6*span_index, 6); /* TDM Header */
+	if (unlikely(samples != 8 || channels >= 32 || channels == 0)) {
+                                ethmf_errors_inc();
+                                ++span_index;
+                                continue;
+                        }
+	
+	if (atomic_dec_and_test(&z->refcnt) == 0) {
+
+		memcpy(z->rcvbuf, data + 6*span_index, 6); /* TDM Header */
 
 	      /* 
 	       * If we ignore channel zero we must skip the first eight bytes and
 	       * ensure that ztdynamic doesn't get confused by this new flag
 	       */
-	      if (flags & ETHMF_FLAG_IGNORE_CHAN0)
-		{
-		  skip = 8;
-		  /* Remove this flag since ztdynamic may not understand it */
-		  z->rcvbuf[1] = flags & ~(ETHMF_FLAG_IGNORE_CHAN0);
-		  /* Additionally, now we will transmit with front padding */
-		  z->no_front_padding = 0;
+		if (flags & ETHMF_FLAG_IGNORE_CHAN0){
+			skip = 8;
+			/* Remove this flag since ztdynamic may not understand it */
+			z->rcvbuf[1] = flags & ~(ETHMF_FLAG_IGNORE_CHAN0);
+			/* Additionally, now we will transmit with front padding */
+			atomic_set(&z->no_front_padding, 0);
 		} 
-	      else
-		{
-		  /* Disable front padding if we recv'd a packet without it */
-		  z->no_front_padding = 1;
+		else {
+			/* Disable front padding if we recv'd a packet without it */
+			atomic_set(&z->no_front_padding, 1);
 		}
 
-	      memcpy(z->rcvbuf + 6, data + 6*num_spans + 16*span_index, rbslen); /* RBS Header */
+		memcpy(z->rcvbuf + 6, data + 6*num_spans + 16*span_index, rbslen); /* RBS Header */
+
+		/* 256 == 32*8; if padding lengths change, this must be modified */
+		memcpy(z->rcvbuf + 6 + rbslen, 
+			data + 6*num_spans + 16*num_spans + (256)*span_index + skip,
+			channels * 8); /* Payload */
+
+		zt_dynamic_receive(span, z->rcvbuf, 6 + rbslen + channels*8);
+
+	} else {
+	    	ethmf_errors_inc();
+                printk(KERN_INFO "TDMoE span overflow detected. Span %d was dropped.", span_index);
+	}
+
+	atomic_inc(&z->refcnt);
+	++span_index;
+
 
-	      /* 256 == 32*8; if padding lengths change, this must be modified */
-	      memcpy(z->rcvbuf + 6 + rbslen, 
-		     data + 6*num_spans + 16*num_spans + (256)*span_index + skip,
-		     channels * 8); /* Payload */
-
-	      zt_dynamic_receive(span, z->rcvbuf, 6 + rbslen + channels*8);
-	      
-	      spin_unlock(&z->rblock);
-	    }
-	  }
-      }
-      span_index++;
     } while (span_index < num_spans);
+    rcu_read_unlock();
   }
 
 
@@ -235,30 +328,28 @@
 {
   struct net_device *dev = ptr;
   struct ztdeth *z;
-  unsigned long flags;
   switch(event) {
   case NETDEV_GOING_DOWN:
   case NETDEV_DOWN:
-    spin_lock_irqsave(&zlock, flags);
-    z = zdevs;
-    while(z) {
-      /* Note that the device no longer exists */
-      if (z->dev == dev)
-	z->dev = NULL;
-      z = z->next;
+    
+    rcu_read_lock();
+    list_for_each_entry_rcu(z, &ethmf_list, list) {
+    	/* Note that the device no longer exists */
+        if (z->dev == dev) {
+        	z->dev = NULL;
+        }
     }
-    spin_unlock_irqrestore(&zlock, flags);
+    rcu_read_unlock();
     break;
   case NETDEV_UP:
-    spin_lock_irqsave(&zlock, flags);
-    z = zdevs;
-    while(z) {
-      /* Now that the device exists again, use it */
-      if (!strcmp(z->ethdev, dev->name))
-	z->dev = dev;
-      z = z->next;
+    rcu_read_lock();
+    list_for_each_entry_rcu(z, &ethmf_list, list) {
+    /* Now that the device exists again, use it */
+    	if (!strcmp(z->ethdev, dev->name)) {
+    		z->dev = dev;
+        }
     }
-    spin_unlock_irqrestore(&zlock, flags);
+    rcu_read_unlock();
     break;
   }
   return 0;
@@ -266,59 +357,62 @@
 
 static int ztdethmf_transmit(void *pvt, unsigned char *msg, int msglen)
 {
-  struct ztdeth *z, *t, *ready_spans[8];
+  struct ztdeth *z = pvt, *ready_spans[ETHMF_MAX_PER_SPAN_GROUP];
   struct sk_buff *skb;
   struct ztdeth_header *zh;
-  unsigned long flags;
   struct net_device *dev;
   unsigned char addr[ETH_ALEN];
-  int span_count = 0, spans_ready = 0, index = 0;
-  spin_lock_irqsave(&zlock, flags);
+  int spans_ready = 0, index = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
+  static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+  unsigned long flags;
+#endif
+  
+  if (atomic_read(&shutdown))
+	return 0;
 
-  z = pvt;
-  if (z->dev) {
-    /* Store the present data in the buffers */
-    memcpy(z->msgbuf, msg, msglen);
-    z->msgbuf_len = msglen;
-    z->ready = 1;
-
-    /* Are all available spans ready for transmit? */
-    t = zdevs;
-    while(t) {
-      if (!t->delay) {
-	/* 
-	 * A profiling showed that this hash method
-	 * was faster than the previous memcmp
-	 */
-	if (t->addr_hash == z->addr_hash) {
-	  ++span_count;
-	  if (t->ready) {
-	    ready_spans[ntohs(t->subaddr)] = t;
-	    ++spans_ready;
-	  }
-	}
-      }
-      t = t->next;
-    }
+  rcu_read_lock();
+  
+  if (unlikely(!z || !z->dev)) {
+  	rcu_read_unlock();
+     	return 0;
+  }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
+        if (!atomic_read(&z->ready)) {
+                spin_lock_irqsave(&lock, flags);
+                atomic_inc(&z->ready);
+                if (1 == atomic_read(&z->ready)) {
+                        memcpy(z->msgbuf, msg, msglen);
+                        z->msgbuf_len = msglen;
+                }
+                spin_unlock_irqrestore(&lock, flags);
+        }
+#else
+        if (!atomic_read(&z->ready)) {
+                if (atomic_inc_return(&z->ready) == 1) {
+                        memcpy(z->msgbuf, msg, msglen);
+                        z->msgbuf_len = msglen;
+                }
+        }
+#endif
+
+
+  spans_ready = ethmf_trx_spans_ready(z->addr_hash, &ready_spans);
+  if (spans_ready) {
+  	int pad[ETHMF_MAX_SPANS], rbs[ETHMF_MAX_SPANS];
 	
-    /* Copy fields to local variables to remove spinlock ASAP */
-    dev = z->dev;
-    memcpy(addr, z->addr, sizeof(z->addr));
-    spin_unlock_irqrestore(&zlock, flags);
-
-    /* ready_spans[8] forces us to check spans_ready < 8, a minor bug if you will */
-    if (span_count && spans_ready && span_count == spans_ready && spans_ready < 8) {
-      int pad[spans_ready], rbs[spans_ready];
+    	dev = z->dev;
+    	memcpy(addr, z->addr, sizeof(z->addr));
 	
-      for (index = 0; index < spans_ready; index++)
-	{
-	  int chan = ready_spans[index]->real_channels;
+        for (index = 0; index < spans_ready; index++) {
+	  	int chan = ready_spans[index]->real_channels;
 	  /* By default we pad to 32 channels, but if no_front_padding is false then
 	   * we have a pad in the front of 8 bytes, so this implies one less channel */
-	  if (ready_spans[index]->no_front_padding)
-	    pad[index] = (32 - chan)*8;
-	  else
-	    pad[index] = (31 - chan)*8;
+	  	if (atomic_read(&(ready_spans[index]->no_front_padding)))
+	    		pad[index] = (32 - chan)*8;
+	  	else
+	    		pad[index] = (31 - chan)*8;
 
 	  if (chan == 24)
 	    rbs[index] = 12;
@@ -329,25 +423,33 @@
 	}
 
       skb = dev_alloc_skb(1112 + dev->hard_header_len + sizeof(struct ztdeth_header) + 32); /* Standard size for 32-chan frame */
-      if (skb) {
-	unsigned short tmp_subaddr;
+    
+      if (unlikely(!skb)) {
+
+	rcu_read_unlock();
+        ethmf_errors_inc();
+        return 0;
+
+      }	
+      
+//      unsigned short tmp_subaddr;
 	/* Reserve header space */
-	skb_reserve(skb, dev->hard_header_len + sizeof(struct ztdeth_header));
-	for (index = 0; index < spans_ready; index++)
-	  {
-	    if (!ready_spans[index]->no_front_padding)
-	      ready_spans[index]->msgbuf[1] |= ETHMF_FLAG_IGNORE_CHAN0;
+      skb_reserve(skb, dev->hard_header_len + sizeof(struct ztdeth_header));
 
+      for (index = 0; index < spans_ready; index++)
+	  {
+	    if (!atomic_read(&(ready_spans[index]->no_front_padding)))
+            	ready_spans[index]->msgbuf[1]|= ETHMF_FLAG_IGNORE_CHAN0;
 	    memcpy( skb_put(skb, 6), ready_spans[index]->msgbuf, 6);
 	  }
-	for (index = 0; index < spans_ready; index++)
+      for (index = 0; index < spans_ready; index++)
 	  {
 	    memcpy( skb_put(skb, 16), ready_spans[index]->msgbuf + 6 , rbs[index]);
 	  }
-	for (index = 0; index < spans_ready; index++)
+      for (index = 0; index < spans_ready; index++)
 	  {
 	    int chan = ready_spans[index]->real_channels;
-	    if (!ready_spans[index]->no_front_padding) {
+	    if (!atomic_read(&(ready_spans[index]->no_front_padding))) {
 	      /* This adds an additional (padded) channel to our total */
 	      memset(skb_put(skb, 8), 0xA5, 8); /* ETHMF_IGNORE_CHAN0 */
 	    }
@@ -358,14 +460,12 @@
 	      memset( skb_put(skb, pad[index]), 0xDD, pad[index]);
 	    }
 	    /* We're done with this span */
-	    ready_spans[index]->ready = 0;
+	    atomic_set(&(ready_spans[index]->ready), 0);
 	  }
 	    
 	/* Throw on header */
 	zh = (struct ztdeth_header *)skb_push(skb, sizeof(struct ztdeth_header));
-	tmp_subaddr = (0x8000 | (span_count & 0xFF));
-	zh->subaddr = htons(tmp_subaddr);
-		    
+	zh->subaddr = htons((unsigned short)(0x8000 | (unsigned char)(spans_ready & 0xFF)));	    
 	/* Setup protocol and such */
 	skb->protocol = __constant_htons(ETH_P_ZTDETH);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
@@ -381,12 +481,13 @@
 	if (dev->hard_header)
 	  dev->hard_header(skb, dev, ETH_P_ZTDETH, addr, dev->dev_addr, skb->len);
 #endif
-	skb_queue_tail(&skbs, skb);
+	if (dev){
+		skb_queue_tail(&skbs, skb);
+	}
+
+
       }
-    }
-  }
-  else
-    spin_unlock_irqrestore(&zlock, flags);
+  rcu_read_unlock();    
   return 0;
 }
 
@@ -468,30 +569,29 @@
 {
   struct ztdeth *z = pvt;
   unsigned long flags;
-  struct ztdeth *prev=NULL, *cur;
-  spin_lock_irqsave(&zlock, flags);
-  cur = zdevs;
-  while(cur) {
-    if (cur == z) {
-      if (prev)
-	prev->next = cur->next;
-      else
-	zdevs = cur->next;
-      break;
-    }
-    prev = cur;
-    cur = cur->next;
-  }
-  spin_unlock_irqrestore(&zlock, flags);
-  if (cur == z) {	/* Successfully removed */
+
+  atomic_set(&shutdown, 1);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+  synchronize_kernel();
+#else
+  synchronize_rcu();
+#endif
+
+  spin_lock_irqsave(&ethmf_lock, flags);
+  list_del_rcu(&z->list);
+  spin_unlock_irqrestore(&ethmf_lock, flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+  synchronize_kernel();
+#else
+  synchronize_rcu();
+#endif
+  
+  if (z) {	/* Successfully removed */
     printk("TDMoEmf: Removed interface for %s\n", z->span->name);
     kfree(z->msgbuf);
-    kfree(z);
-#ifndef LINUX26
-    MOD_DEC_USE_COUNT;
-#else
+    kfree(z);    
     module_put(THIS_MODULE);
-#endif
+
   } else {
     if (z && z->span && z->span->name) {
       printk("TDMoEmf: Cannot find interface for %s\n", z->span->name);
@@ -513,7 +613,7 @@
     memset(z, 0, sizeof(struct ztdeth));
 
     /* set a delay for xmit/recv to workaround Zaptel problems */
-    z->delay = 8;
+    atomic_set(&z->delay, 4);
 
     /* create a msg buffer */  /* MAX OF 31 CHANNELS!!!! */
     bufsize = 31 * ZT_CHUNKSIZE + 31 / 4 + 48;
@@ -610,10 +710,13 @@
     sprintf(src + strlen(src), "%02x", z->dev->dev_addr[5]);
     printk("TDMoEmf: Added new interface for %s at %s (addr=%s, src=%s, subaddr=%d)\n", span->name, z->dev->name, addr, src, ntohs(z->subaddr));
 
-    spin_lock_irqsave(&zlock, flags);
-    z->next = zdevs;
-    zdevs = z;
-    spin_unlock_irqrestore(&zlock, flags);
+    atomic_set(&z->ready, 0);
+    atomic_set(&z->refcnt, 0);
+
+    spin_lock_irqsave(&ethmf_lock, flags);
+    list_add_rcu(&z->list, &ethmf_list);
+    spin_unlock_irqrestore(&ethmf_lock, flags);
+
 #ifndef LINUX26
     MOD_INC_USE_COUNT;
 #else
@@ -621,14 +724,8 @@
       printk("TDMoEmf: Unable to increment module use count\n");
 #endif
 
-    /* enable the timer for enabling the spans */
-#if 0
-    timer.expires = jiffies + 1;
-    if (!timer_pending(&timer))
-      add_timer(&timer);
-#else
-    mod_timer(&timer, jiffies + 1);
-#endif
+    mod_timer(&timer, jiffies + HZ);
+    atomic_set(&shutdown, 0);
   }
   return z;
 }
@@ -646,57 +743,41 @@
   notifier_call: ztdethmf_notifier,
 };
 
-static void timer_callback(unsigned long param)
+static int ethmf_delay_dec(void)
 {
-  unsigned long flags;
-  struct ztdeth *z;
-  int count_zero = 0, count_nonzero = 0;
-
-  /* loop spans */
-  ++timer_counter;
+        struct ztdeth *z;
+        int count_nonzero = 0;
 
-  if (timer_counter >= HZ) {
-    timer_counter -= HZ;
-    spin_lock_irqsave(&zlock, flags);
-    z = zdevs;
-    while(z) {
-      if (z->delay) {
-	z->delay--;
-	// ++count_nonzero;
-	if (!z->delay) {
-	  printk("TDMoEmf: Span %s is ready for use.\n", z->span->name);
-	} else {
-	  ++count_nonzero;
-	}
-	  } else {
-	    ++count_zero;
-	  }
-	  z = z->next;
-	}
-	spin_unlock_irqrestore(&zlock, flags);
+        rcu_read_lock();
+        list_for_each_entry_rcu(z, &ethmf_list, list) {
+                if (atomic_read(&z->delay)) {
+                        atomic_dec(&z->delay);
+                        ++count_nonzero;
+                } else
+                        atomic_set(&z->delay, 0);
+        }
+        rcu_read_unlock();
+        return count_nonzero;
+}
 
-	if (count_nonzero /* && count_zero && count_nonzero != count_zero */) {
-	  /* re-add the timer event */
-	  timer.expires = jiffies + 1;
-	  add_timer(&timer);
-	} else {
-	  printk("TDMoEmf: All spans are active - removing delay timer.\n");
-	  del_timer(&timer);
-	}
-    
-      } else {
-	timer.expires = jiffies + 1;
-	add_timer(&timer);
-      }
-    }
+static void timer_callback(unsigned long param)
+{
+        if (ethmf_delay_dec()) {
+                if (!atomic_read(&timer_deleted)) {
+                        timer.expires = jiffies + HZ;
+                        add_timer(&timer);
+                }
+        } else {
+                printk(KERN_INFO "All TDMoE multiframe span groups are active.\n");
+                del_timer(&timer);
+        }
+}
 
   static int __init ztdethmf_init(void)
     {
 
-      timer_counter = 0;
-
       init_timer(&timer);
-      timer.expires = jiffies + 1;
+      timer.expires = jiffies + HZ;
       timer.function = &timer_callback;
       if (!timer_pending(&timer))
 	add_timer(&timer);
@@ -712,7 +793,8 @@
 
   static void __exit ztdethmf_exit(void)
     {
-      del_timer(&timer);
+      atomic_set(&timer_deleted, 1);
+      del_timer_sync(&timer);
 
       dev_remove_pack(&ztdethmf_ptype);
       unregister_netdevice_notifier(&ztdethmf_nblock);
diff -Naur zaptel-1.4.9.2/kernel/ztdynamic.c zaptel-1.4.9.2.patched/kernel/ztdynamic.c
--- zaptel-1.4.9.2/kernel/ztdynamic.c	2008-02-04 18:00:48.000000000 -0500
+++ zaptel-1.4.9.2.patched/kernel/ztdynamic.c	2010-04-07 16:34:05.000000000 -0400
@@ -101,7 +101,7 @@
 #endif
 
 
-static struct zt_dynamic {
+struct zt_dynamic {
 	char addr[40];
 	char dname[20];
 	int err;
@@ -112,40 +112,31 @@
 	unsigned short rxcnt;
 	struct zt_span span;
 	struct zt_chan *chans;
-	struct zt_dynamic *next;
 	struct zt_dynamic_driver *driver;
 	void *pvt;
 	int timing;
 	int master;
 	unsigned char *msgbuf;
-} *dspans;
+	struct list_head list;
+};
 
-static struct zt_dynamic_driver *drivers =  NULL;
+static DEFINE_SPINLOCK(dspan_lock);
+static LIST_HEAD(dspan_list);
+
+static DEFINE_SPINLOCK(driver_lock);
+static LIST_HEAD(driver_list);
 
 static int debug = 0;
 
 static int hasmaster = 0;
-#ifdef DEFINE_SPINLOCK
-static DEFINE_SPINLOCK(dlock); 
-#else
-static spinlock_t dlock = SPIN_LOCK_UNLOCKED;
-#endif
-
-#ifdef DEFINE_RWLOCK
-static DEFINE_RWLOCK(drvlock);
-#else
-static rwlock_t drvlock = RW_LOCK_UNLOCKED;
-#endif
 
 static void checkmaster(void)
 {
-	unsigned long flags;
 	int newhasmaster=0;
 	int best = 9999999;
 	struct zt_dynamic *z, *master=NULL;
-	spin_lock_irqsave(&dlock, flags);
-	z = dspans;
-	while(z) {
+	rcu_read_lock();
+	list_for_each_entry_rcu(z, &dspan_list, list) {
 		if (z->timing) {
 			z->master = 0;
 			if (!(z->span.alarms & ZT_ALARM_RED) &&
@@ -157,13 +148,13 @@
 				newhasmaster = 1;
 			}
 		}
-		z = z->next;
 	}
 	hasmaster = newhasmaster;
 	/* Mark the new master if there is one */
 	if (master)
 		master->master = 1;
-	spin_unlock_irqrestore(&dlock, flags);
+	rcu_read_unlock();
+
 	if (master)
 		printk("TDMoX: New master: %s\n", master->span.name);
 	else
@@ -232,13 +223,11 @@
 
 static void __ztdynamic_run(void)
 {
-	unsigned long flags;
 	struct zt_dynamic *z;
 	struct zt_dynamic_driver *drv;
 	int y;
-	spin_lock_irqsave(&dlock, flags);
-	z = dspans;
-	while(z) {
+	rcu_read_lock();
+	list_for_each_entry_rcu(z, &dspan_list, list) {
 		if (!z->dead) {
 			/* Ignore dead spans */
 			for (y=0;y<z->span.channels;y++) {
@@ -250,26 +239,20 @@
 			/* Handle all transmissions now */
 			ztd_sendmessage(z);
 		}
-		z = z->next;
 	}
-	spin_unlock_irqrestore(&dlock, flags);
-
-	read_lock(&drvlock);
-	drv = drivers;
-	while(drv) {
+	list_for_each_entry_rcu(drv, &driver_list, list) {
 		/* Flush any traffic still pending in the driver */
 		if (drv->flush) {
 			drv->flush();
 		}
-		drv = drv->next;
 	}
-	read_unlock(&drvlock);
+	rcu_read_unlock();
 }
 
 #ifdef ENABLE_TASKLETS
 static void ztdynamic_run(void)
 {
-	if (!taskletpending) {
+	if (likely(!taskletpending)) {
 		taskletpending = 1;
 		taskletsched++;
 		tasklet_hi_schedule(&ztd_tlet);
@@ -285,7 +268,6 @@
 {
 	struct zt_dynamic *ztd = span->pvt;
 	int newerr=0;
-	unsigned long flags;
 	int sflags;
 	int xlen;
 	int x, bits, sig;
@@ -293,12 +275,11 @@
 	int newalarm;
 	unsigned short rxpos, rxcnt;
 	
-	
-	spin_lock_irqsave(&dlock, flags);
-	if (msglen < 6) {
-		spin_unlock_irqrestore(&dlock, flags);
+	rcu_read_lock();
+	if (unlikely (msglen < 6)) {
+		rcu_read_unlock();
 		newerr = ERR_LEN;
-		if (newerr != ztd->err) {
+ 		if (newerr != ztd->err) {
 			printk("Span %s: Insufficient samples for header (only %d)\n", span->name, msglen);
 		}
 		ztd->err = newerr;
@@ -306,8 +287,8 @@
 	}
 	
 	/* First, check the chunksize */
-	if (*msg != ZT_CHUNKSIZE) {
-		spin_unlock_irqrestore(&dlock, flags);
+	if (unlikely (*msg != ZT_CHUNKSIZE)) {
+		rcu_read_unlock();
 		newerr = ERR_NSAMP | msg[0];
 		if (newerr != 	ztd->err) {
 			printk("Span %s: Expected %d samples, but receiving %d\n", span->name, ZT_CHUNKSIZE, msg[0]);
@@ -324,8 +305,8 @@
 	msg++;
 	
 	nchans = ntohs(*((unsigned short *)msg));
-	if (nchans != span->channels) {
-		spin_unlock_irqrestore(&dlock, flags);
+	if (unlikely(nchans != span->channels)) {
+		rcu_read_unlock();
 		newerr = ERR_NCHAN | nchans;
 		if (newerr != ztd->err) {
 			printk("Span %s: Expected %d channels, but receiving %d\n", span->name, span->channels, nchans);
@@ -344,13 +325,14 @@
 	/* Add samples of audio */
 	xlen += nchans * ZT_CHUNKSIZE;
 	/* If RBS info is there, add that */
+
 	if (sflags & ZTD_FLAG_SIGBITS_PRESENT) {
 		/* Account for sigbits -- one short per 4 channels*/
 		xlen += ((nchans + 3) / 4) * 2;
 	}
 	
-	if (xlen != msglen) {
-		spin_unlock_irqrestore(&dlock, flags);
+	if (unlikely(xlen != msglen)) {
+		rcu_read_unlock();
 		newerr = ERR_LEN | xlen;
 		if (newerr != ztd->err) {
 			printk("Span %s: Expected message size %d, but was %d instead\n", span->name, xlen, msglen);
@@ -362,6 +344,7 @@
 	bits = 0;
 	
 	/* Record sigbits if present */
+	
 	if (sflags & ZTD_FLAG_SIGBITS_PRESENT) {
 		for (x=0;x<nchans;x++) {
 			if (!(x%4)) {
@@ -392,8 +375,9 @@
 	rxcnt = ztd->rxcnt;
 	ztd->rxcnt = rxpos+1;
 
-	spin_unlock_irqrestore(&dlock, flags);
-	
+	/* Keep track of last received packet */
+	ztd->rxjif = jiffies;
+	rcu_read_unlock();
 	/* Check for Yellow alarm */
 	newalarm = span->alarms & ~(ZT_ALARM_YELLOW | ZT_ALARM_RED);
 	if (sflags & ZTD_FLAG_YELLOW_ALARM)
@@ -405,11 +389,9 @@
 		checkmaster();
 	}
 	
-	/* Keep track of last received packet */
-	ztd->rxjif = jiffies;
 
 	/* note if we had a missing packet */
-	if (rxpos != rxcnt)
+	if (unlikely(rxpos != rxcnt))
 		printk("Span %s: Expected seq no %d, but received %d instead\n", span->name, rxcnt, rxpos);
 
 	/* If this is our master span, then run everything */
@@ -443,60 +425,58 @@
 
 static struct zt_dynamic *find_dynamic(ZT_DYNAMIC_SPAN *zds)
 {
-	struct zt_dynamic *z;
-	z = dspans;
-	while(z) {
+	struct zt_dynamic *z = NULL, *found = NULL;
+	
+	rcu_read_lock();
+	list_for_each_entry_rcu(z, &dspan_list, list) {
 		if (!strcmp(z->dname, zds->driver) &&
-		    !strcmp(z->addr, zds->addr))
+		    !strcmp(z->addr, zds->addr)) {
+			found = z;
 			break;
-		z = z->next;
+		}
 	}
-	return z;
+	rcu_read_unlock();
+	return found;
 }
 
 static struct zt_dynamic_driver *find_driver(char *name)
 {
-	struct zt_dynamic_driver *ztd;
-	ztd = drivers;
-	while(ztd) {
+	struct zt_dynamic_driver *ztd, *found=NULL;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ztd, &driver_list, list) {
 		/* here's our driver */
-		if (!strcmp(name, ztd->name))
+		if (!strcmp(name, ztd->name)) {
+			found = ztd;
 			break;
-		ztd = ztd->next;
+		}
 	}
-	return ztd;
+	rcu_read_unlock();
+	return found;
 }
 
 static int destroy_dynamic(ZT_DYNAMIC_SPAN *zds)
 {
 	unsigned long flags;
-	struct zt_dynamic *z, *cur, *prev=NULL;
-	spin_lock_irqsave(&dlock, flags);
+	struct zt_dynamic *z;
+
 	z = find_dynamic(zds);
-	if (!z) {
-		spin_unlock_irqrestore(&dlock, flags);
+	if (unlikely(!z)) {
 		return -EINVAL;
 	}
 	/* Don't destroy span until it is in use */
 	if (z->usecount) {
-		spin_unlock_irqrestore(&dlock, flags);
 		printk("Attempt to destroy dynamic span while it is in use\n");
 		return -EBUSY;
 	}
-	/* Unlink it */
-	cur = dspans;
-	while(cur) {
-		if (cur == z) {
-			if (prev)
-				prev->next = z->next;
-			else
-				dspans = z->next;
-			break;
-		}
-		prev = cur;
-		cur = cur->next;
-	}
-	spin_unlock_irqrestore(&dlock, flags);
+	spin_lock_irqsave(&dspan_lock, flags);
+	list_del_rcu(&z->list);
+	spin_unlock_irqrestore(&dspan_lock, flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+	synchronize_kernel();
+#else
+	synchronize_rcu();
+#endif
 
 	/* Destroy it */
 	dynamic_destroy(z);
@@ -514,8 +494,8 @@
 {
 	struct zt_dynamic *z;
 	z = chan->span->pvt;
-	if (z) {
-		if (z->dead)
+	if (likely(z)) {
+		if (unlikely(z->dead))
 			return -ENODEV;
 		z->usecount++;
 	}
@@ -566,19 +546,9 @@
 		return -EINVAL;
 	}
 
-	spin_lock_irqsave(&dlock, flags);
 	z = find_dynamic(zds);
-	spin_unlock_irqrestore(&dlock, flags);
 	if (z)
 		return -EEXIST;
-
-	/* XXX There is a silly race here.  We check it doesn't exist, but
-	       someone could create it between now and then and we'd end up
-	       with two of them.  We don't want to hold the spinlock
-	       for *too* long though, especially not if there is a possibility
-	       of kmalloc.  XXX */
-
-
 	/* Allocate memory */
 	z = (struct zt_dynamic *)kmalloc(sizeof(struct zt_dynamic), GFP_KERNEL);
 	if (!z) 
@@ -635,22 +605,16 @@
 		z->chans[x].pvt = z;
 	}
 	
-	spin_lock_irqsave(&dlock, flags);
 	ztd = find_driver(zds->driver);
 	if (!ztd) {
 		/* Try loading the right module */
 		char fn[80];
-		spin_unlock_irqrestore(&dlock, flags);
 		sprintf(fn, "ztd-%s", zds->driver);
 		request_module(fn);
-		spin_lock_irqsave(&dlock, flags);
 		ztd = find_driver(zds->driver);
 	}
-	spin_unlock_irqrestore(&dlock, flags);
 
 
-	/* Another race -- should let the module get unloaded while we
-	   have it here */
 	if (!ztd) {
 		printk("No such driver '%s' for dynamic span\n", zds->driver);
 		dynamic_destroy(z);
@@ -676,11 +640,12 @@
 	}
 
 	/* Okay, created and registered. add it to the list */
-	spin_lock_irqsave(&dlock, flags);
-	z->next = dspans;
-	dspans = z;
-	spin_unlock_irqrestore(&dlock, flags);
 
+	spin_lock_irqsave(&dspan_lock, flags);
+
+	list_add_rcu(&z->list, &dspan_list);
+
+	spin_unlock_irqrestore(&dspan_lock, flags);
 	checkmaster();
 
 	/* All done */
@@ -740,70 +705,63 @@
 {
 	unsigned long flags;
 	int res = 0;
-	write_lock_irqsave(&drvlock, flags);
 	if (find_driver(dri->name))
 		res = -1;
 	else {
-		dri->next = drivers;
-		drivers = dri;
+		spin_lock_irqsave(&driver_lock, flags);
+		list_add_rcu(&dri->list, &driver_list);
+		spin_unlock_irqrestore(&driver_lock, flags);
 	}
-	write_unlock_irqrestore(&drvlock, flags);
+
 	return res;
 }
 
 void zt_dynamic_unregister(struct zt_dynamic_driver *dri)
 {
-	struct zt_dynamic_driver *cur, *prev=NULL;
-	struct zt_dynamic *z, *zp, *zn;
+	struct zt_dynamic *z;
 	unsigned long flags;
-	write_lock_irqsave(&drvlock, flags);
-	cur = drivers;
-	while(cur) {
-		if (cur == dri) {
-			if (prev)
-				prev->next = cur->next;
-			else
-				drivers = cur->next;
-			break;
-		}
-		prev = cur;
-		cur = cur->next;
-	}
-	write_unlock_irqrestore(&drvlock, flags);
-	spin_lock_irqsave(&dlock, flags);
-	z = dspans;
-	zp = NULL;
-	while(z) {
-		zn = z->next;
+	
+	spin_lock_irqsave(&driver_lock, flags);
+	list_del_rcu(&dri->list);
+	spin_unlock_irqrestore(&driver_lock, flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+        synchronize_kernel();
+#else
+        synchronize_rcu();
+#endif
+	
+	list_for_each_entry(z, &dspan_list, list) {
 		if (z->driver == dri) {
 			/* Unlink */
-			if (zp)
-				zp->next = z->next;
-			else
-				dspans = z->next;
+	
+			spin_lock_irqsave(&dspan_lock, flags);
+			list_del_rcu(&z->list);
+			spin_unlock_irqrestore(&dspan_lock, flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+        		synchronize_kernel();
+#else
+        		synchronize_rcu();
+#endif
+
+	
 			if (!z->usecount)
 				dynamic_destroy(z);
 			else
 				z->dead = 1;
-		} else {
-			zp = z;
+
 		}
-		z = zn;
 	}
-	spin_unlock_irqrestore(&dlock, flags);
 }
 
 struct timer_list alarmcheck;
 
 static void check_for_red_alarm(unsigned long ignored)
 {
-	unsigned long flags;
 	int newalarm;
 	int alarmchanged = 0;
 	struct zt_dynamic *z;
-	spin_lock_irqsave(&dlock, flags);
-	z = dspans;
-	while(z) {
+	rcu_read_lock();
+	list_for_each_entry_rcu(z, &dspan_list, list) {
 		newalarm = z->span.alarms & ~ZT_ALARM_RED;
 		/* If nothing received for a second, consider that RED ALARM */
 		if ((jiffies - z->rxjif) > 1 * HZ) {
@@ -814,9 +772,8 @@
 				alarmchanged++;
 			}
 		}
-		z = z->next;
 	}
-	spin_unlock_irqrestore(&dlock, flags);
+	rcu_read_unlock();
 	if (alarmchanged)
 		checkmaster();
 

