[zaptel-commits] sruffell: branch sruffell/zaptel-1.4-transcoder r4200 - in /team/sruffell/zap...

SVN commits to the Zaptel project zaptel-commits at lists.digium.com
Mon Apr 28 03:31:21 CDT 2008


Author: sruffell
Date: Mon Apr 28 03:31:19 2008
New Revision: 4200

URL: http://svn.digium.com/view/zaptel?view=rev&rev=4200
Log:
Saving some initial work at some experiments on a read / write interface for
the zaptel transcoder.

Added:
    team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/codec_test.h
      - copied, made public unchanged from r4199, team/sruffell/private/transcoder/kernel/wctc4xxp/codec_test.h
    team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/codec_test_threaded.c
      - copied, made public, changed from r4199, team/sruffell/private/transcoder/kernel/wctc4xxp/codec_test_threaded.c
Modified:
    team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/Makefile
    team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/base.c
    team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/codec_test.c
    team/sruffell/zaptel-1.4-transcoder/kernel/zaptel-base.c
    team/sruffell/zaptel-1.4-transcoder/kernel/zaptel.h
    team/sruffell/zaptel-1.4-transcoder/kernel/zttranscode.c

Modified: team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/Makefile
URL: http://svn.digium.com/view/zaptel/team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/Makefile?view=diff&rev=4200&r1=4199&r2=4200
==============================================================================
--- team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/Makefile (original)
+++ team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/Makefile Mon Apr 28 03:31:19 2008
@@ -8,8 +8,14 @@
 
 tests: codec_test
 
+CFLAGS += -I../ -DSTANDALONE_ZAPATA
+
 codec_test: codec_test.c ../zaptel.h
-	$(CC) -o $@ $< $(CFLAGS)
+	$(CC) -g -o $@ $< $(CFLAGS)
+
+codec_test_threaded: codec_test_threaded.c ../zaptel.h
+	$(CC) -o $@ $< $(CFLAGS) -lpthread
+	
 
 clean:
 	rm -rf codec_test

Modified: team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/base.c
URL: http://svn.digium.com/view/zaptel/team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/base.c?view=diff&rev=4200&r1=4199&r2=4200
==============================================================================
--- team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/base.c (original)
+++ team/sruffell/zaptel-1.4-transcoder/kernel/wctc4xxp/base.c Mon Apr 28 03:31:19 2008
@@ -1,9 +1,10 @@
 /*
  * Wildcard TC400B Driver
  *
- * Written by John Sloan <jsloan at digium.com>
+ * Written by John Sloan <jsloan at digium.com> and
+ *            Shaun Ruffell <sruffell at digium.com>
  *
- * Copyright (C) 2006, Digium, Inc.
+ * Copyright (C) 2006-2008, Digium, Inc.
  *
  * All rights reserved.
  *
@@ -21,8 +22,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
+ * Glossary:
+ *
+ * DTE -  Digium Transcoding Engine
+ *
  */
-
 
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -34,8 +38,6 @@
 #include <linux/sched.h>
 #include <linux/pci.h>
 #include <linux/interrupt.h>
-#include <linux/vmalloc.h>
-#include <linux/mman.h>
 #include <linux/delay.h>
 #include <asm/io.h>
 #include <asm/semaphore.h>
@@ -43,10 +45,37 @@
 #include <linux/workqueue.h>
 #include <linux/moduleparam.h>
 #include <linux/firmware.h>
-#ifdef CONFIG_DEVFS_FS
-#include <linux/devfs_fs_kernel.h>
+#include <linux/if_ether.h>
+#include <linux/timer.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
 #endif
+
 #include "zaptel.h"
+
+#define INTERRUPT 0 
+#define WORKQUEUE 1
+#define TASKLET   2
+#ifndef DEFERRED_PROCESSING
+#	define DEFERRED_PROCESSING INTERRUPT
+#endif
+
+#define WARN() WARN_ON(1)
+#define DTE_PRINTK(_wc, _lvl, _fmt, _args...) \
+	   printk(KERN_##_lvl "%s: " _fmt, (_wc)->board_name, ## _args)
+
+#define DTE_DEBUG_GENERAL          (1 << 0)
+#define DTE_DEBUG_CHANNEL_SETUP    (1 << 1)
+#define DTE_DEBUG_RTP_TX           (1 << 2)
+#define DTE_DEBUG_RTP_RX           (1 << 3)
+
+#define DTE_DEBUG(_wc, _dbgmask, _fmt, _args...)                            \
+	if ((debug & _dbgmask) == (_dbgmask)) {                             \
+		printk(KERN_DEBUG "%s: " _fmt, (_wc)->board_name, ## _args);\
+	}                                                                   \
+
+#define INVALID 999 /* Used to mark invalid channels, commands, etc.. */
+#define MAX_CHANNEL_PACKETS  5 /* Never let more than 5 outstanding packets exist for any channel. */
 
 /* #define USE_TEST_HW */
 #define USE_TDM_CONFIG
@@ -57,8 +86,15 @@
 #define NUM_CARDS 24
 #define NUM_EC	  4
 
-/* NUM_CHANNELS must be checked if new firmware (dte_firm.h) is used */
-#define NUM_CHANNELS 120
+#if 0
+#define HERE() printk(KERN_DEBUG "HERE: %s:%d\n", __FILE__, __LINE__);
+#define ENTERING() printk(KERN_DEBUG "Entering %s \n", __FUNCTION__);
+#define LEAVING() printk(KERN_DEBUG "Leaving %s (%d)\n", __FUNCTION__, __LINE__);
+#else
+#define HERE() do {;} while (0)
+#define ENTERING() do {;} while (0)
+#define LEAVING() do {;} while (0)
+#endif
 
 #define DTE_FORMAT_ULAW   0x00
 #define DTE_FORMAT_G723_1 0x04
@@ -77,25 +113,9 @@
 #define G723_5K_BYTES 20	/* G.723.1 at 5.3kb/s */
 #define G723_SID_BYTES 4	/* G.723.1 SID frame */
 
-#define ACK_SPACE 20
-
-#define MAX_COMMANDS (NUM_CHANNELS + ACK_SPACE)
-#define MAX_RCV_COMMANDS 16
-
-/* 1432 for boot, 274 for 30msec ulaw, 194 for 20mec ulaw */
-#define BOOT_CMD_LEN 1500
-#define OTHER_CMD_LEN 300
-
-#define MAX_COMMAND_LEN BOOT_CMD_LEN	/* Must be the larger of BOOT_CMD_LEN or OTHER_CMD_LEN */
-
-#define ERING_SIZE (NUM_CHANNELS / 2)		/* Maximum ring size */
-
-#define SFRAME_SIZE MAX_COMMAND_LEN
-
-#define PCI_WINDOW_SIZE ((2*  2 * ERING_SIZE * SFRAME_SIZE) + (2 * ERING_SIZE * 4))
+#define SFRAME_SIZE 1518
 
 #define MDIO_SHIFT_CLK		0x10000
-#define MDIO_DATA_WRITE0 	0x00000
 #define MDIO_DATA_WRITE1 	0x20000
 #define MDIO_ENB		0x00000
 #define MDIO_ENB_IN		0x40000
@@ -106,6 +126,8 @@
 #define RCV_CSMENCAPS_ACK 3
 #define RCV_OTHER         99
 
+/* \todo It might be clearer if these commands were mapped to packed stuctures
+ * instead of used as byte arrays. */
 
 /* TDM Commands */
 #define CMD_MSG_TDM_SELECT_BUS_MODE_LEN 30
@@ -189,6 +211,7 @@
 	0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
 	0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0C, 0x00, 0x00,0x06,0x10,0x00, 0x00,0x00, \
 	0x02,0x00, (t&0x00FF), ((t&0xFF00) >> 8) }
+
 #define CMD_MSG_QUERY_CHANNEL_LEN 30
 #define CMD_MSG_QUERY_CHANNEL(s,t) { \
 	0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
@@ -271,70 +294,235 @@
 	0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x09,0x04, 0x00,0x00, \
 	0x24,0x00, 0x00,0x00 }
 
-#define zt_send_cmd(wc, command, length, hex) \
-	({ \
-		int ret = 0; \
-		do { \
-	 		if (ret == 2) \
-	 		{ \
-				wc->ztsnd_rtx++; \
-	 			if (hex == 0x0010) \
-					wc->ztsnd_0010_rtx++; \
-			} \
-			down(&wc->cmdqsem); \
-	 		wc->last_command_sent = hex; \
-			if ( (((wc->cmdq_wndx + 1) % MAX_COMMANDS) == wc->cmdq_rndx) && debug ) \
-				printk("wcdte error: cmdq is full.\n"); \
-			else { \
-				unsigned char fifo[OTHER_CMD_LEN] = command; \
-				int i; \
-				wc->cmdq[wc->cmdq_wndx].cmdlen = length; \
-				for (i = 0; i < length; i++) \
-					wc->cmdq[wc->cmdq_wndx].cmd[i] = fifo[i]; \
-				wc->last_seqno = fifo[16]; \
-				wc->cmdq_wndx = (wc->cmdq_wndx + 1) % MAX_COMMANDS; \
-			} \
-			__transmit_demand(wc); \
-			up(&wc->cmdqsem); \
-			if (hex == 0x0000) \
-				ret = wcdte_waitfor_csmencaps(wc, RCV_CSMENCAPS_ACK, 2); \
-			else { \
-				ret = wcdte_waitfor_csmencaps(wc, RCV_CSMENCAPS, 0); \
-				if (wc->dsp_crashed) \
-					return 1; \
-			} \
-			if (ret == 1) \
-				return(1); \
-		} while (ret == 2); \
-	})
-
-
-struct cmdq {
+#define ETH_P_CSM_ENCAPS 0x889B
+/* Ehernet packet type for communication control information to the DTE. */
+struct csm_encaps_hdr {
+	/* CSM_ENCAPS HEADER */
+	__be16 op_code;
+	__u8   seq_num;
+	__u8   control;
+	__be16 channel;
+	/* COMMON PART OF PAYLOAD HEADER */
+	__u8   length;
+	__u8   index;
+	__u8   class;
+	__u8   type;
+	__be16 function;
+	__be16 reserved;
+	__le16 params[0]; /* Must be last (AND SWITCHES TO LITTLE ENDIAN) */
+} __attribute__((packed));
+
+
+struct dte_cmd {
+	u8 cmd[SFRAME_SIZE];
+	unsigned int debug;
 	unsigned int cmdlen;
-	unsigned char cmd[MAX_COMMAND_LEN];
+	struct list_head node;
+/*! If set, this command will be freed automatically after completing
+ * transmission.
+ */
+#define NO_AUTO_FREE_CMD 	(1 << 0)
+#define TX_COMPLETE	        (1 << 1)	
+	unsigned long flags;
+	struct completion complete;
 };
 
+/*! In-memory structure shared by the host and the adapter. */
+struct dte_descriptor {
+	u32 des0;
+	u32 des1;
+	u32 buffer1;
+	u32 container; /* Unused */
+} __attribute__((packed));
+
+#define DRING_SIZE (1 << 3) /* Must be a power of two */
+#define DRING_MASK (DRING_SIZE-1)
+#define MIN_PACKET_LEN  64
+
+struct dte_descriptor_ring {
+	/* Pointer to an array of descriptors to give to hardware. */
+	struct dte_descriptor* desc;
+	/* Read completed buffers from the head. */
+	unsigned int 	head;
+	/* Write ready buffers to the tail. */
+	unsigned int 	tail;
+	/* Array to save the kernel virtual address of pending commands. */
+	struct dte_cmd *pending[DRING_SIZE];
+	/* PCI Bus address of the descriptor list. */
+	dma_addr_t	desc_dma;
+	/*! either DMA_FROM_DEVICE or DMA_TO_DEVICE */
+	unsigned int 	direction;
+	/*! The number of buffers currently submitted to the hardware. */
+	unsigned int    count;
+	/*! The number of bytes to pad each descriptor for cache alignment. */
+	unsigned int	padding;
+	/*! Protects this structure from concurrent access. */
+	spinlock_t      lock;
+	/*! PCI device for the card associated with this ring. */
+	struct pci_dev  *pdev;
+};
+
+/* Returns the desriptor at index.
+ * 
+ * We need this function because we do not know what the padding on the
+ * descriptors will be.
+ */
+static inline struct dte_descriptor *
+dte_descriptor(struct dte_descriptor_ring *dr, int index)
+{
+	struct dte_descriptor *d;
+	d = (struct dte_descriptor *)((u8*)dr->desc + 
+		((sizeof(*d) + dr->padding) * index));
+	return d;
+}
+
+static int
+dte_initialize_descriptor_ring(struct pci_dev *pdev, struct dte_descriptor_ring *dr, 
+	u32 des1, unsigned int direction)
+{
+	int i; 
+	const u32 END_OF_RING = 0x02000000;
+	u8 cache_line_size = 0;
+	struct dte_descriptor *d;
+
+	BUG_ON(!pdev);
+	BUG_ON(!dr);
+
+	if (pci_read_config_byte(pdev, 0x0c, &cache_line_size)) {
+		/* \todo Print an error message... */
+		return -EIO;
+	}
+
+	memset(dr, 0, sizeof(*dr));
+
+	/*
+	 * Add some padding to each descriptor to ensure that they are
+	 * aligned on host system cache-line boundaries, but only for the 
+	 * cache-line sizes that we support.
+	 *
+	 */
+	if ((0x08 == cache_line_size) || (0x10 == cache_line_size) ||
+	    (0x20 == cache_line_size)) 
+	{
+		dr->padding = (cache_line_size*sizeof(u32)) - sizeof(*d);
+	} 
+
+	dr->desc = pci_alloc_consistent(pdev, 
+			(sizeof(*d)+dr->padding)*DRING_SIZE, &dr->desc_dma);
+
+	if (!dr->desc) {
+		return -ENOMEM;
+	}
+
+	memset(dr->desc, 0, (sizeof(*d) + dr->padding) * DRING_SIZE);
+	for (i = 0; i < DRING_SIZE; ++i) {
+		d = dte_descriptor(dr, i);
+		d->des1 = cpu_to_le32(des1);
+	}
+
+	d->des1 |= cpu_to_le32(END_OF_RING);
+	dr->direction = direction;
+	spin_lock_init(&dr->lock);
+	return 0;
+}
+
+#define OWN_BIT cpu_to_le32(0x80000000)
+#define OWNED(_d_) (((_d_)->des0)&OWN_BIT)
+#define SET_OWNED(_d_) do { wmb(); (_d_)->des0 |= OWN_BIT; wmb();} while (0)
+
+/*! Submit a command to the descriptor list for processing by the DTE. */
+static int 
+dte_submit(struct dte_descriptor_ring* dr, struct dte_cmd *c)
+{
+	volatile struct dte_descriptor *d;
+	unsigned int tail = dr->tail;
+	unsigned long flags;
+	unsigned int len;
+	const unsigned int BUFFER1_SIZE_MASK = 0x7ff;
+
+	WARN_ON(!c);
+	WARN_ON(c->cmdlen > SFRAME_SIZE);
+	len = (c->cmdlen < MIN_PACKET_LEN) ? MIN_PACKET_LEN : c->cmdlen;
+	if (c->cmdlen > SFRAME_SIZE) {
+		printk(KERN_ERR "Invalid frame size: %d.\n", c->cmdlen);
+		return -EBUSY;
+	}
+
+	spin_lock_irqsave(&dr->lock, flags);
+
+	d = dte_descriptor(dr, tail); 
+	if (d->buffer1) {
+		spin_unlock_irqrestore(&dr->lock, flags);
+		/* Do not overwrite a buffer that is still in progress. */
+		return -EBUSY;
+	}
+	d->des1 &= cpu_to_le32(~(BUFFER1_SIZE_MASK));
+	d->des1 |= cpu_to_le32(len & BUFFER1_SIZE_MASK);
+	d->buffer1 = dma_map_single(&dr->pdev->dev, c->cmd, 
+	                            SFRAME_SIZE, dr->direction);
+
+	SET_OWNED(d); /* That's it until the hardware is done with it. */ 
+
+	dr->pending[tail] = c;
+	dr->tail = (++tail) & DRING_MASK; 
+	++dr->count;
+
+	spin_unlock_irqrestore(&dr->lock, flags);
+
+	return 0;
+}
+
+static inline struct dte_cmd* 
+dte_retrieve(struct dte_descriptor_ring *dr)
+{
+	volatile struct dte_descriptor *d;
+	struct dte_cmd *c;
+	unsigned long flags;
+	unsigned int head = dr->head;
+	spin_lock_irqsave(&dr->lock, flags);
+	d = dte_descriptor(dr, head);
+	if (d->buffer1 && !OWNED(d)) {
+		dma_unmap_single(&dr->pdev->dev, d->buffer1, 
+			SFRAME_SIZE, dr->direction);
+		c = dr->pending[head];
+		WARN_ON(!c);
+		dr->head = (++head) & DRING_MASK; 
+		d->buffer1 = 0;
+		--dr->count;
+		WARN_ON(!c);
+	} else {
+		c = NULL;
+	}
+	spin_unlock_irqrestore(&dr->lock, flags);
+	return c;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+/*! Used to allocate commands to submit to the dte. */
+kmem_cache_t *cmd_cache;
+#else
+/*! Used to allocate commands to submit to the dte. */
+struct kmem_cache *cmd_cache;
+#endif
+
 struct wcdte {
-	struct pci_dev *dev;
-	char *variety;
-	unsigned int intcount;
-	unsigned int rxints;
-	unsigned int txints;
+	char board_name[40];
+	struct pci_dev *pdev;
+	const char *variety;
 	unsigned int intmask;
 	int pos;
-	int freeregion;
-	int rdbl;
-	int tdbl;
 	int cards;
 	spinlock_t reglock;
 	wait_queue_head_t regq;
 	int rcvflags;
 	
 	struct semaphore chansem;
-	struct semaphore cmdqsem;
-	struct cmdq cmdq[MAX_COMMANDS];
-	unsigned int cmdq_wndx;
-	unsigned int cmdq_rndx;
+	spinlock_t cmd_list_lock;
+	struct list_head cmd_list;
+	/* This is a list to hold those commands which were flagged to not
+	 * auto delete, but which we stopped waiting for because of a signal.
+	 * Use the cmd_list_lock to protect this list. */
+	struct list_head lost_cmd_list;
 
 	unsigned int last_seqno;
 	unsigned int last_rseqno;
@@ -342,56 +530,236 @@
 	unsigned int last_rcommand;
 	unsigned int last_rparm1;
 	unsigned int seq_num;
-	long timeout;
-
-	unsigned int dsp_crashed;
-	unsigned int dumping;
-
-	unsigned int ztsnd_rtx;
-	unsigned int ztsnd_0010_rtx;
 
 	unsigned char numchannels;
 	unsigned char complexname[40];
 
 	unsigned long iobase;
-	dma_addr_t 	readdma;
-	dma_addr_t	writedma;
-	dma_addr_t	descripdma;
-	volatile unsigned int *writechunk;					/* Double-word aligned write memory */
-	volatile unsigned int *readchunk;					/* Double-word aligned read memory */
-	volatile unsigned int *descripchunk;					/* Descriptors */
 	
-	int wqueints;
+#if DEFERRED_PROCESSING == WORKQUEUE
 	struct workqueue_struct *dte_wq;
 	struct work_struct dte_work;
+#endif
+
+	struct dte_descriptor_ring txd;
+	struct dte_descriptor_ring rxd;
 
 	struct zt_transcoder *uencode;
 	struct zt_transcoder *udecode;
+	struct channel_pvt *encoders;
+	struct channel_pvt *decoders;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_file;
+#endif
+	/* In order to add this to the list of cards checked by the watchdog
+	 * timer. */
+	struct list_head watchdog_node;
 };
 
+static inline void __wcdte_setctl(struct wcdte *wc, unsigned int addr, unsigned int val)
+{
+	outl(val, wc->iobase + addr);
+}
+
+static inline unsigned int __wcdte_getctl(struct wcdte *wc, unsigned int addr)
+{
+	return inl(wc->iobase + addr);
+}
+
+static inline void wcdte_setctl(struct wcdte *wc, unsigned int addr, unsigned int val)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&wc->reglock, flags);
+	__wcdte_setctl(wc, addr, val);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+/* Instruct the DTE to read in the next rx descriptor */
+static inline void dte_receive_demand_poll(struct wcdte *wc) 
+{
+	wcdte_setctl(wc, 0x0010, 0x00000000);
+}
+
+/* Instruct the DTE to read in the next tx descriptor */
+static inline void dte_transmit_demand_poll(struct wcdte *wc)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&wc->reglock, flags);
+	__wcdte_setctl(wc, 0x0008, 0x00000000);
+	/* \todo Investigate why this register needs to be written twice in
+	 * order to get it to poll reliably.  So far, most of the problems
+	 * I've seen with timeouts had more to do with an untransmitted
+	 * packet sitting in the outbound descriptor list as opposed to any
+	 * problem with the dte firmware.
+	 */
+	__wcdte_setctl(wc, 0x0008, 0x00000000);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static inline struct dte_cmd *
+__alloc_cmd(gfp_t alloc_flags, unsigned long cmd_flags)
+{
+	struct dte_cmd *cmd;
+
+	cmd = kmem_cache_alloc(cmd_cache, alloc_flags);
+	if (likely(cmd)) {
+		memset(cmd, 0, sizeof(*cmd));
+		INIT_LIST_HEAD(&cmd->node);
+		cmd->debug = 0xdeadbeef;
+		init_completion(&cmd->complete);
+		cmd->flags = cmd_flags;
+	}
+	return cmd;
+}
+static inline struct dte_cmd *
+alloc_cmd(void)
+{
+	return __alloc_cmd(GFP_KERNEL, 0);
+}
+
+static inline void 
+free_cmd(struct dte_cmd *cmd)
+{
+	WARN_ON(0xdeadbeef != cmd->debug);
+	kmem_cache_free(cmd_cache, cmd);
+	return;
+}
+
+static void
+wcdte_cleanup_descriptor_ring(struct dte_descriptor_ring *dr) 
+{
+	int i; 
+	struct dte_descriptor *d;
+	unsigned long flags;
+	
+	/* NOTE: The DTE must be in the stopped state. */
+
+	spin_lock_irqsave(&dr->lock, flags);
+	for (i = 0; i < DRING_SIZE; ++i) {
+		d = dte_descriptor(dr, i);
+		if (d->buffer1) {
+			dma_unmap_single(&dr->pdev->dev, d->buffer1, 
+				SFRAME_SIZE, dr->direction);
+			d->buffer1 = 0;
+			free_cmd(dr->pending[i]);
+			dr->pending[i] = NULL;
+		}
+	}
+	dr->head = 0;
+	dr->tail = 0;
+	dr->count = 0;
+	spin_unlock_irqrestore(&dr->lock, flags);
+	pci_free_consistent(dr->pdev, (sizeof(*d)+dr->padding) * DRING_SIZE, dr->desc, dr->desc_dma); 
+}
+
+static void
+wcdte_cleanup_command_list(struct wcdte *wc)
+{
+	unsigned long flags;
+	struct dte_cmd *cmd;
+	struct dte_cmd *temp;
+
+	spin_lock_irqsave(&wc->cmd_list_lock, flags);
+	list_for_each_entry_safe(cmd, temp, &wc->cmd_list, node) {
+		list_del(&cmd->node);
+		free_cmd(cmd);
+	}
+	list_for_each_entry_safe(cmd, temp, &wc->lost_cmd_list, node) {
+		list_del(&cmd->node);
+		free_cmd(cmd);
+	}
+	WARN_ON(!list_empty(&wc->cmd_list));
+	spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
+}
+
+static void wcdte_cleanup_lost_command_list(struct wcdte *wc)
+{
+	unsigned long flags;
+	struct dte_cmd *cmd;
+	struct dte_cmd *temp;
+	spin_lock_irqsave(&wc->cmd_list_lock, flags);
+	list_for_each_entry_safe(cmd, temp, &wc->lost_cmd_list, node) {
+		if (cmd->flags & TX_COMPLETE)  {
+			list_del(&cmd->node);
+			free_cmd(cmd);
+		}
+	}
+	WARN_ON(!list_empty(&wc->cmd_list));
+	spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
+}
+
+/**
+ * The command list is used to store commands that couldn't fit in the tx
+ * descriptor list when they were requested. 
+ */
+static void 
+dte_add_to_command_list(struct wcdte *wc, struct dte_cmd *cmd) 
+{
+	unsigned long flags;
+	spin_lock_irqsave(&wc->cmd_list_lock, flags);
+	list_add_tail(&cmd->node, &wc->cmd_list);
+	spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
+}
+
+static void
+dte_transmit_cmd(struct wcdte *wc, struct dte_cmd *cmd)
+{
+	int res;
+
+	if (cmd->cmdlen < MIN_PACKET_LEN) {
+		memset(&cmd->cmd[cmd->cmdlen],0,MIN_PACKET_LEN-cmd->cmdlen);
+	}
+	if (unlikely((res=dte_submit(&wc->txd, cmd)))) {
+		if (-EBUSY == res) {
+			dte_add_to_command_list(wc, cmd);
+		} else {
+			/* Unknown return value... */
+			WARN_ON(1);
+		}
+	} else {
+		dte_transmit_demand_poll(wc);
+	}
+}
+
 struct wcdte_desc {
-	char *name;
+	const char *short_name;
+	const char *long_name;
 	int flags;
 };
 
-static struct wcdte_desc wctc400p = { "Wildcard TC400P+TC400M", 0 };
-static struct wcdte_desc wctce400 = { "Wildcard TCE400+TC400M", 0 };
+static struct wcdte_desc wctc400p = { 
+	.short_name = "tc400b",
+	.long_name = "Wildcard TC400P+TC400M", 
+	.flags = 0, 
+};
+
+static struct wcdte_desc wctce400 = { 
+	.short_name = "tce400",
+	.long_name = "Wildcard TCE400+TC400M", 
+	.flags = 0,
+};
 
 static struct wcdte *ifaces[WC_MAX_IFACES];
 
-
+typedef enum { DECODER=0, ENCODER, } encode_t;
+
+struct channel_stats {
+	atomic_t packets_sent;
+	atomic_t packets_received;
+};
 
 /*
  * The following is the definition of the state structure
  * used by the G.721/G.723 encoder and decoder to preserve their internal
  * state between successive calls.  The meanings of the majority
  * of the state structure fields are explained in detail in the
- * CCITT Recommendation G.721.  The field names are essentially indentical
+ * CCITT Recommendation G.721.  The field names are essentially identical
  * to variable names in the bit level description of the coding algorithm
  * included in this Recommendation.
  */
-struct dte_state {
-	int encoder;	/* If we're an encoder */
+struct channel_pvt {
+	spinlock_t lock;		/* Lock for this structure */
+	encode_t encoder;		/* If we're an encoder */
 	struct wcdte *wc;
 
 	unsigned int timestamp;
@@ -399,148 +767,128 @@
 
 	unsigned int cmd_seqno;
 
-	unsigned int timeslot_in_num;		/* DTE chennel on which results we be received from */
-	unsigned int timeslot_out_num;		/* DTE channel to send data to */
-
-	unsigned int chan_in_num;		/* DTE chennel on which results we be received from */
-	unsigned int chan_out_num;		/* DTE channel to send data to */
+	unsigned int timeslot_in_num;	/* DTE channel on which results will be received from */
+	unsigned int timeslot_out_num;	/* DTE channel to send data to */
+
+	unsigned int chan_in_num;	/* DTE channel on which results will be received from */
+	unsigned int chan_out_num;	/* DTE channel to send data to */
 	
-	unsigned int packets_sent;
-	unsigned int packets_received;
+	struct channel_stats stats;
 
 	unsigned int last_dte_seqno;
 	unsigned int dte_seqno_rcv;
 
 	unsigned char ssrc;
+	struct list_head rx_queue;	/* Transcoded packets for this channel. */
+	wait_queue_head_t waitq;
 };
 
 
-static struct zt_transcoder *uencode;
-static struct zt_transcoder *udecode;
-static struct dte_state *encoders;
-static struct dte_state *decoders;
 static int debug = 0;
 static int debug_des = 0;			/* Set the number of descriptor packet bytes to output on errors, 0 disables output */
 static int debug_des_cnt = 0;			/* Set the number of times descriptor packets are displayed before the output is disabled */
-static int force_alert = 0;
-static int debug_notimeout = 0;
 static char *mode;
 static int debug_packets = 0;
 
-static int wcdte_create_channel(struct wcdte *wc, int simple, int complicated, int part1_id, int part2_id, unsigned int *dte_chan1, unsigned int *dte_chan2);
-static int wcdte_destroy_channel(struct wcdte *wc, unsigned int chan1, unsigned int chan2);
+/* Forward Declarations \todo Review: are these are really necessary? */
+static int wcdte_create_channel_pair(struct wcdte *wc, struct channel_pvt *cpvt, u8 simple, u8 complicated);
+static int wcdte_destroy_channel_pair(struct wcdte *wc, struct channel_pvt *cpvt);
 static int __wcdte_setup_channels(struct wcdte *wc);
-
-static int __dump_descriptors(struct wcdte *wc)
-{
-	volatile unsigned char *writechunk, *readchunk;
-	int o2, i, j;
-
-	if (debug_des_cnt == 0)
-		return 1;
-
-	printk("Transmit Descriptors (wc->tdbl = %d)\n", wc->tdbl);
-	for (i = 0; i < ERING_SIZE; i++)
-	{
-		writechunk = (volatile unsigned char *)(wc->writechunk);
-		writechunk += i * SFRAME_SIZE;
-		o2 = i * 4;
-
-		if (i == wc->tdbl)
-			printk("->");
-		else
-			printk("  ");
-		if ((le32_to_cpu(wc->descripchunk[o2]) & 0x80000000))
-			printk("AN983 owns : ");
-		else
-			printk("Driver owns: ");
-
-		for (j = 0; j < debug_des; j++)
-			printk("%02X ", writechunk[j]);
-		printk("\n");
-	}
-
-	printk("Receive Descriptors (wc->rdbl = %d)\n", wc->rdbl);
-	for (i = 0; i < ERING_SIZE; i++)
-	{
-		readchunk = (volatile unsigned char *)wc->readchunk;
-		readchunk += i * SFRAME_SIZE;
-		o2 = i * 4;
-		o2 += ERING_SIZE * 4;
-
-		if (i == wc->rdbl)
-			printk("->");
-		else
-			printk("  ");
-		if ((le32_to_cpu(wc->descripchunk[o2]) & 0x80000000))
-			printk("AN983 owns : ");
-		else
-			printk("Driver owns: ");
-
-		for (j = 0; j < debug_des; j++)
-			printk("%02X ", readchunk[j]);
-		printk("\n");
-	}
-	if (debug_des_cnt > 0)
-		debug_des_cnt--;
-	return 0;
-}
-
-/* Sanity check values */
-static inline int zt_tc_sanitycheck(struct zt_transcode_header *zth, unsigned int outbytes)
-{
-	if (zth->dstoffset >= sizeof(zth->dstdata))
-		return 0;
-	if (zth->dstlen >= sizeof(zth->dstdata))
-		return 0;
-	if (outbytes >= sizeof(zth->dstdata))
-		return 0;
-	if ((zth->dstoffset + zth->dstlen + outbytes) >= sizeof(zth->dstdata))
-		return 0;
-	if (zth->srcoffset >= sizeof(zth->srcdata))
-		return 0;
-	if (zth->srclen >= sizeof(zth->srcdata))
-		return 0;
-	if ((zth->srcoffset + zth->srclen) > sizeof(zth->srcdata))
-		return 0;
-	return 1;
-}
-
-static void dte_init_state(struct dte_state *state_ptr, int encoder, unsigned int channel, struct wcdte *wc)
-{
-	state_ptr->encoder = encoder;
-	state_ptr->wc = wc;
-	state_ptr->timestamp = 0;
-	state_ptr->seqno = 0;
-
-	state_ptr->cmd_seqno = 0;
-
-	state_ptr->packets_sent = 0;
-	state_ptr->packets_received = 0;
-	state_ptr->last_dte_seqno = 0;
-	state_ptr->dte_seqno_rcv = 0;
-
-	state_ptr->chan_in_num = 999;
-	state_ptr->chan_out_num = 999;
-
-	state_ptr->ssrc = 0x78;
+static int wcdte_waitfor_csmencaps(struct wcdte *wc, unsigned int mask, unsigned int wait_mode);
+
+ /* \todo This macro is a candidate for removal.  It's still here because of
+ * how the commands are passed to this zt_send_cmd */
+#define zt_send_cmd(wc, command) ({                                          \
+	int __res;                                                           \
+	u8 _cmd[] = command;                                                 \
+	struct dte_cmd *cmd;                                                 \
+	if (!(cmd=__alloc_cmd(GFP_KERNEL, NO_AUTO_FREE_CMD)))                \
+		return -ENOMEM;                                              \
+	BUG_ON(sizeof(_cmd) > SFRAME_SIZE);                                  \
+	memcpy(cmd->cmd, _cmd, sizeof(_cmd));                                \
+	cmd->cmdlen = sizeof(_cmd);                                          \
+	__res = __zt_send_cmd(wc, cmd);                                      \
+	__res;                                                               \
+})
+
+static int __zt_send_cmd(struct wcdte *wc, struct dte_cmd *cmd)
+{
+	int ret = 0;
+	u16 command;
+	unsigned int retries = 0;
+	const unsigned int MAX_RETRIES = 1;
+
+	command = (cmd->cmd[0x18] << 8) | cmd->cmd[0x19];
+
+	wc->last_command_sent = command;
+	wc->last_seqno = cmd->cmd[16];
+
+	/* \todo I put the retry code in here, before determining that packets
+	 * were sitting in the tx queue.  I haven't yet seen a case where the
+	 * retry actually allows a packet to complete. Might be a candidate
+	 * for removal in order to simplify this function. */
+	while (retries++ < MAX_RETRIES) {
+		dte_transmit_cmd(wc, cmd);
+#if 0 /* This is debug code when investigating the source of the timeouts... */
+		if (wait_for_completion_killable(&cmd->complete)) {
+			DTE_DEBUG(wc, DTE_DEBUG_GENERAL, 
+			 "Failed to complete transmit.\n");
+
+			/* Save this command on the lost list in order to be
+			 * cleaned up later. */
+			spin_lock_irqsave(&wc->cmd_list_lock, flags);
+			list_add_tail(&cmd->node, &wc->lost_cmd_list);
+			spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
+			return -EINTR;
+		}
+#else
+		wait_for_completion(&cmd->complete);
+#endif
+		/* \todo why is this command treated differently? */
+		if (0x0000 == command) {
+			ret = wcdte_waitfor_csmencaps(wc, RCV_CSMENCAPS_ACK, 2);
+		} else {
+			ret = wcdte_waitfor_csmencaps(wc, RCV_CSMENCAPS, 0); 
+		}
+		if (unlikely(-EIO == ret)) {
+			continue;
+		}
+		break;
+	}
+	free_cmd(cmd);
+
+
+	return ret;
+}
+
+static void dte_init_state(struct channel_pvt *cpvt, encode_t encoder, unsigned int channel, struct wcdte *wc)
+{
+	memset(cpvt, 0, sizeof(*cpvt));
+	cpvt->encoder = encoder;
+	cpvt->wc = wc;
+
+	cpvt->chan_in_num = INVALID;
+	cpvt->chan_out_num = INVALID;
+
+	cpvt->ssrc = 0x78;
 	
-	if (encoder == 1)
-	{
-		state_ptr->timeslot_in_num = channel * 2;
-		state_ptr->timeslot_out_num = channel * 2 + 1;
+	if (ENCODER == encoder) {
+		cpvt->timeslot_in_num = channel * 2;
+		cpvt->timeslot_out_num = channel * 2 + 1;
 	} else {
-		state_ptr->timeslot_in_num = channel * 2 + 1;
-		state_ptr->timeslot_out_num = channel * 2;
-	}
-}
-
-static unsigned int wcdte_zapfmt_to_dtefmt(unsigned int fmt)
-{
-	unsigned int pt;
+		cpvt->timeslot_in_num = channel * 2 + 1;
+		cpvt->timeslot_out_num = channel * 2;
+	}
+	spin_lock_init(&cpvt->lock);
+	INIT_LIST_HEAD(&cpvt->rx_queue);
+	init_waitqueue_head(&cpvt->waitq);
+}
+
+static inline u8 wcdte_zapfmt_to_dtefmt(unsigned int fmt)
+{
+	u8 pt;
 	
-	switch(fmt)
-	{
+	switch(fmt) {
 		case ZT_FORMAT_G723_1:
 			pt = DTE_FORMAT_G723_1;
 			break;
@@ -555,39 +903,10 @@
 			break;
 		default:
 			pt = DTE_FORMAT_UNDEF;
-	}
-
-	return(pt);
-}
-
-static inline void __wcdte_setctl(struct wcdte *wc, unsigned int addr, unsigned int val)
-{
-	outl(val, wc->iobase + addr);
-}
-
-static inline unsigned int __wcdte_getctl(struct wcdte *wc, unsigned int addr)
-{
-	return inl(wc->iobase + addr);
-}
-
-static inline void wcdte_setctl(struct wcdte *wc, unsigned int addr, unsigned int val)
-{
-	unsigned long flags;
-	spin_lock_irqsave(&wc->reglock, flags);
-	__wcdte_setctl(wc, addr, val);
-	spin_unlock_irqrestore(&wc->reglock, flags);
-}
-
-static inline void wcdte_reinit_descriptor(struct wcdte *wc, int tx, int dbl, char *s)
-{
-	int o2 = 0;
-	o2 += dbl * 4;
-
-	if (!tx)
-		o2 += ERING_SIZE * 4;
-	wc->descripchunk[o2] = cpu_to_le32(0x80000000);
-
-	wcdte_setctl(wc, 0x0008, 0x00000000);
+			break;
+	}
+
+	return pt;
 }
 
 static inline unsigned int wcdte_getctl(struct wcdte *wc, unsigned int addr)
@@ -600,567 +919,605 @@
 	return val;
 }
 
-static inline int __transmit_demand(struct wcdte *wc)
-{
-	volatile unsigned char *writechunk;
-	int o2,i,j;
-	unsigned int reg, xmt_length;
-
-	reg = wcdte_getctl(wc, 0x0028) & 0x00700000;
+/* \todo This function is a candidate for refactorization, but I want to make
+ * sure the interface is stable before doing so. */
+static void 
+init_CMD_MSG_IP_UDP_RTP(const struct zt_transcoder_channel *ztc, 
+	unsigned int inbytes, const struct channel_pvt *cpvt, struct dte_cmd *cmd)
+{
+	unsigned int ipchksum;
+	u8 msg[] = CMD_MSG_IP_UDP_RTP(
+		 ((inbytes+40) >> 8)                 & 0xFF,
+		  (inbytes+40)                       & 0xFF,
+		   cpvt->seqno                         & 0xFF,
+		   0x00,
+		   0x00,
+		(((cpvt->timeslot_out_num) >> 8)+0x50) & 0xFF,
+		  (cpvt->timeslot_out_num)             & 0xFF,
+		(((cpvt->timeslot_in_num) >> 8)+0x50)  & 0xFF,
+		  (cpvt->timeslot_in_num)              & 0xFF,
+		 ((inbytes+20) >> 8)                 & 0xFF,
+		  (inbytes+20)                       & 0xFF,
+		   0x00,
+		   0x00,
+		   wcdte_zapfmt_to_dtefmt(ztc->srcfmt),					 
+		 ((cpvt->seqno) >> 8)                  & 0xFF,
+		  (cpvt->seqno)                        & 0xFF,
+		 ((cpvt->timestamp) >> 24)             & 0xFF,
+		 ((cpvt->timestamp) >> 16)             & 0xFF,
+		 ((cpvt->timestamp) >> 8)              & 0xFF,
+		  (cpvt->timestamp)                    & 0xFF,
+		  (cpvt->ssrc)                         & 0xFF);
+	BUG_ON(sizeof(msg) > SFRAME_SIZE);
+	memcpy(&cmd->cmd[0], &msg[0], sizeof(msg));
+	cmd->cmdlen = sizeof(msg);
+
+	ipchksum = 0x9869 + (cmd->cmd[16] << 8) + cmd->cmd[17]
+		+ (cmd->cmd[18] << 8) + cmd->cmd[19];
+
+	while (ipchksum >> 16) {
+		ipchksum = (ipchksum & 0xFFFF) + (ipchksum >> 16);
+	}
+
+	ipchksum = (~ipchksum) & 0xFFFF;
+
+	cmd->cmd[24] = ipchksum >> 8;
+	cmd->cmd[25] = ipchksum & 0xFF;
+
+	return;
+}
+
+static void 
+wcdte_cleanup_channel_private(struct wcdte *wc, struct channel_pvt *cpvt)
+{
+	unsigned long flags;
+	struct dte_cmd *cmd, *temp;
+
+	spin_lock_irqsave(&cpvt->lock, flags);
+	list_for_each_entry_safe(cmd, temp, &cpvt->rx_queue, node) {
+		list_del(&cmd->node);
+		free_cmd(cmd);
+	}
+	spin_unlock_irqrestore(&cpvt->lock, flags);
+}
+
+static int
+wcdte_mark_channel_complement_built(struct wcdte *wc, struct zt_transcoder_channel *ztc)
+{
+	int index;
+	struct channel_pvt *cpvt = ztc->pvt;
+	struct zt_transcoder_channel *compl_ztc;
+	struct channel_pvt *compl_cpvt;
+
+	BUG_ON(!cpvt);
+	index = cpvt->timeslot_in_num/2;
+	BUG_ON(index >= wc->numchannels);
+	if (cpvt->encoder == 1) {
+		compl_ztc = &(wc->udecode->channels[index]);
+	} else {
+		compl_ztc = &(wc->uencode->channels[index]);
+	}
+	WARN_ON(compl_ztc->chan_built); /* It shouldn't already have been built... */
+	compl_ztc->chan_built = 1;
+	DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "Marking (%08x)->chan_built = %d\n", (u32)compl_ztc, compl_ztc->chan_built);
+	compl_ztc->built_fmts = ztc->dstfmt | ztc->srcfmt;
+	compl_cpvt = compl_ztc->pvt;
+	DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "ztc: %08x is the complement to %08x\n", (u32)compl_ztc, (u32)ztc);
+	compl_cpvt->chan_in_num = cpvt->chan_out_num;
+	compl_cpvt->chan_out_num = cpvt->chan_in_num;
+	wcdte_cleanup_channel_private(wc, compl_cpvt);
+
+	return 0;
+}
+
+static int
+dte_operation_allocate(struct zt_transcoder_channel *ztc)
+{
+	int res;
+	u8 dte_srcfmt; /* Digium Transcoder Engine Source Format */
+	u8 dte_dstfmt; /* Digium Transcoder Engine Dest Format */
+	struct channel_pvt *cpvt = ztc->pvt;
+	struct wcdte *wc = cpvt->wc;
+
+	ENTERING();
+
+	BUG_ON(!cpvt);
+	BUG_ON(!wc);
+
+
+	DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "Entering %s for channel %08x.\n", __FUNCTION__, (u32)ztc);
+	if (down_interruptible(&wc->chansem)) {
+		HERE();
+		return -EINTR;
+	}
+
+	/* If the channel is already built then we're done. */
+	DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "(%08x)->chan_built = %d\n", (u32)ztc, ztc->chan_built);
+	if (ztc->chan_built) {
+		up(&wc->chansem);
+		DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "Allocating channel %08x which is already built.\n", (u32)ztc);
+		LEAVING();
+		return 0;
+	}
+
+	/* Anything on the rx queue now is old news... */
+	wcdte_cleanup_channel_private(wc, cpvt);
+
+	DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "Allocating a new channel: %08x.\n", (u32)ztc);
+
+	dte_srcfmt = wcdte_zapfmt_to_dtefmt(ztc->srcfmt);
+	dte_dstfmt = wcdte_zapfmt_to_dtefmt(ztc->dstfmt);
+	if ((res = wcdte_create_channel_pair(wc, cpvt, dte_srcfmt, dte_dstfmt))) {
+		/* There was a problem creating the channel.... */
+		up(&wc->chansem);
+		LEAVING();
+		return res;
+	}
+
+	/* Mark this channel as built */
+	ztc->chan_built = 1;
+	ztc->built_fmts = ztc->dstfmt | ztc->srcfmt;
+
+	/* Mark the channel complement (other half of encoder/decoder pair) as built */
+	res = wcdte_mark_channel_complement_built(wc, ztc);
+
+	up(&wc->chansem);
+	LEAVING();
+	return res;
+}
+
+static int
+dte_operation_release(struct zt_transcoder_channel *ztc)
+{
+	int res;
+	int index;
+	/* This is the 'complimentary channel' to ztc.  I.e., if ztc is an
+	 * encoder, compl_ztc is the decoder and vice-versa */
+	struct zt_transcoder_channel *compl_ztc;
+	struct channel_pvt *compl_cpvt;
+	struct channel_pvt *cpvt = ztc->pvt;
+	struct wcdte *wc = cpvt->wc;
+
+	ENTERING();
+	BUG_ON(!cpvt);
+	BUG_ON(!wc);
+
+	wcdte_cleanup_lost_command_list(wc);
+
+	/* The release operation must succeed, we can't abort it in light of a
+	 * signal.
+	 */
+	down(&wc->chansem);
+
+	/* Remove any packets that are waiting on the outbound queue. */
+	wcdte_cleanup_channel_private(wc, cpvt);
 	
-	/* Already transmiting, no need to demand another */
-	if (!((reg == 0) || (reg = 6)))
-		return(1);
-
-	/* Nothing to transmit */
-	if (wc->cmdq_rndx == wc->cmdq_wndx)
-		return(1);
-
-	/* Nothing to transmit */
-	if (wc->cmdq[wc->cmdq_rndx].cmdlen == 0 )
-		return(1);
-
-	writechunk = (volatile unsigned char *)(wc->writechunk);
-
-	writechunk += wc->tdbl * SFRAME_SIZE;
-
-	o2 = wc->tdbl * 4;
-
-	do
-	{
-	} while ((le32_to_cpu(wc->descripchunk[o2]) & 0x80000000));
-
-	xmt_length = wc->cmdq[wc->cmdq_rndx].cmdlen;
-	if (xmt_length < 64)
-		xmt_length = 64;
+	index = cpvt->timeslot_in_num/2;
+	BUG_ON(index >= wc->numchannels);
+
+	if (ENCODER == cpvt->encoder) {
+		compl_ztc = &(wc->udecode->channels[index]);
+	} else {
+		compl_ztc = &(wc->uencode->channels[index]);
+	}
+
+	BUG_ON(!compl_ztc);
+
+	/* If the channel complement (other half of the encoder/decoder pair) is being used... */
+	if ((compl_ztc->flags & ZT_TC_FLAG_BUSY)) {
+		up(&wc->chansem);
+		LEAVING();
+		return -EBUSY;
+	}
+
+	if ((res = wcdte_destroy_channel_pair(wc, cpvt))) {
+		/* There was a problem destorying the channels.*/
+		/* \todo log a message */
+		up(&wc->chansem);
+		HERE();
+		LEAVING();
+		return res;
+	}
+
+	DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "Releasing channel: %08x\n", (u32)ztc);
+	/* Mark this channel as not built */
+	ztc->chan_built = 0;
+	DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "(%08x)->chan_built = %d\n", (u32)ztc, ztc->chan_built);
+	ztc->built_fmts = 0;
+	cpvt->chan_in_num = INVALID;
+	cpvt->chan_out_num = INVALID;
 	
-	wc->descripchunk[o2+1] = cpu_to_le32((le32_to_cpu(wc->descripchunk[o2+1]) & 0xFBFFF800) | xmt_length);
-				
-	for(i = 0; i < wc->cmdq[wc->cmdq_rndx].cmdlen; i++)
-		writechunk[i] = wc->cmdq[wc->cmdq_rndx].cmd[i];
-	for (j = i; j < xmt_length; j++)
-		writechunk[j] = 0;
-
-	if (debug_packets && (writechunk[12] == 0x88) && (writechunk[13] == 0x9B))
-	{
-		printk("wcdte debug: TX: ");
-		for (i=0; i<debug_packets; i++)
-			printk("%02X ", writechunk[i]);
-		printk("\n");
-	}
-
-	wc->cmdq[wc->cmdq_rndx].cmdlen = 0;
-
-	wc->descripchunk[o2] = cpu_to_le32(0x80000000);
-	wcdte_setctl(wc, 0x0008, 0x00000000);			/* Transmit Poll Demand */
+	/* Mark the channel complement as not built */
+	compl_ztc->chan_built = 0;
+	DTE_DEBUG(wc, DTE_DEBUG_CHANNEL_SETUP, "(%08x)->chan_built = %d\n", (u32)compl_ztc, compl_ztc->chan_built);
+	compl_ztc->built_fmts = 0;
+	compl_cpvt = compl_ztc->pvt;
+	compl_cpvt->chan_in_num = INVALID;
+	compl_cpvt->chan_out_num = INVALID;
+
+	cpvt->dte_seqno_rcv = 0;
+
+	up(&wc->chansem);
+	LEAVING();
+	return res;
+}
+
+static inline struct dte_cmd* 
+get_ready_cmd(struct channel_pvt *cpvt)
+{
+	struct dte_cmd *cmd;
+	unsigned long flags;
+	spin_lock_irqsave(&cpvt->lock, flags);
+	if (!list_empty(&cpvt->rx_queue)) {
+		cmd = list_entry(cpvt->rx_queue.next, struct dte_cmd, node); 
+		list_del_init(&cmd->node);
+	} else {
+		cmd = NULL;
+	}
+	spin_unlock_irqrestore(&cpvt->lock, flags);
+	return cmd;
+}
+
+/* Called with a buffer in which to copy a transcoded frame. */
+static ssize_t 
+dte_read(struct file *file, char __user *frame, size_t count, loff_t *ppos)
+{
+	ssize_t ret;
+	struct zt_transcoder_channel *ztc = file->private_data;
+	struct channel_pvt *cpvt = ztc->pvt;
+	struct wcdte *wc = cpvt->wc;
+	struct dte_cmd *cmd;
+	u8  rcodec;
+	u16 rchannel;
+	u16 rlen;
+	u16 rtp_rseq;
+	u16 rtp_eseq;
+	const u8 *packet;
+
+	BUG_ON(!ztc);
+	BUG_ON(!cpvt);
+
+	if (!(cmd = get_ready_cmd(cpvt))) {
+		if (file->f_flags & O_NONBLOCK) {
+			return -EAGAIN;
+		} else {
+			/* Wait here for at most 31ms for a packet to come in
+			 * since it should never take longer than that to
+			 * transcode. 
+			 */
+			ret = wait_event_interruptible_timeout(cpvt->waitq, 
+			       (!list_empty(&cpvt->rx_queue)), HZ/32);
+			if (-ERESTARTSYS == ret) {
+				/* Signal interrupted the wait */
+				return -EINTR;
+			} else if (ret) {
+				/* List went not empty. */
+				cmd = get_ready_cmd(cpvt);
+			} else {
+				/* Timeout... */
+				DTE_PRINTK(wc, INFO, "Timeout while waiting for transcoded packet.\n");
+				WARN_ON(!list_empty(&cpvt->rx_queue));
+				return -ENODATA;
+			}
+		}
+	}
+
+	BUG_ON(!cmd);
+	packet = &cmd->cmd[0];
 	
-	wc->tdbl = (wc->tdbl + 1) % ERING_SIZE;
-
-	wc->cmdq_rndx = (wc->cmdq_rndx + 1) % MAX_COMMANDS;
-

[... 4708 lines stripped ...]



More information about the zaptel-commits mailing list