vdr on arm (little endian) has problems with epg

Message ID 47BF3EA5.8050307@gmx.net
State New
Headers

Commit Message

Stefan Feb. 22, 2008, 9:29 p.m. UTC
  Hi,

I'm trying to setup vdr with debian unstable on arm (little endian) 
(NSLU2) with a WideView WT-220U PenType Receiver (USB). At first the 
system would freeze when the device got activated by vdr or scan. This 
is because arm has some problems with cache coherence. The appended 
patch fixes that.

The next thing is that although video output is working, there's still 
some trouble with the epg.data not properly handled. epg.data file looks 
like this:
C T-8468-12289-2 arte
c
C T-8468-12289-3 Phoenix
c
...

The same setup on x86 works fine, I double checked that. So I started 
debugging with gdb and had a look at the raw epg data that is copied 
from usb, comparing the output from arm and x86 looks quite similar so I 
guess to that point things are ok. In case you wonder I compared the 
output of  unsigned char buf[4096] in void cSectionHandler::Action(void).
I tried to narrow things further down with lots of debugging and I think 
the problem is related to the following code in libsi/si.h

template <class T> class StructureLoop : public Loop {
public:
   //currently you must use a while-loop testing for hasNext()
   //i must be 0 to get the first descriptor (with the first call)
   bool getNext(T &obj, Iterator &it)
      {
         if (!isValid() || it.i >= getLength())
            return false;
         CharArray d=data;
         d.addOffset(it.i);
         T ret;
         ret.setData(d);
         ret.CheckParse();
         if (!checkSize(ret.getLength()))
            return false;
         it.i+=ret.getLength();
         obj=ret;
         return true;
      }

By putting in some printf statements I found out it.i gets increased 
some bytes, these bytes are processes and that happens till getLength is 
reached. Then the chunk has been processed completely. That works fine 
on x86 but not on arm, I observed it.i increase far beyond getLength 
after a while. At this point things get pretty low-level and I'm stuck.

    Stefan
  

Patch

Index: linux-source-2.6.23/arch/arm/common/dmabounce.c
===================================================================
--- linux-source-2.6.23.orig/arch/arm/common/dmabounce.c	2007-10-09 22:31:38.000000000 +0200
+++ linux-source-2.6.23/arch/arm/common/dmabounce.c	2008-01-11 18:14:16.000000000 +0100
@@ -29,6 +29,7 @@ 
 #include <linux/dma-mapping.h>
 #include <linux/dmapool.h>
 #include <linux/list.h>
+#include <linux/interrupt.h>
 
 #include <asm/cacheflush.h>
 
@@ -45,6 +46,9 @@ 
 struct safe_buffer {
 	struct list_head node;
 
+	/* safe buffer info */
+	struct device *dev;
+
 	/* original request */
 	void		*ptr;
 	size_t		size;
@@ -125,6 +129,7 @@ 
 		return NULL;
 	}
 
+	buf->dev = device_info->dev;
 	buf->ptr = ptr;
 	buf->size = size;
 	buf->direction = dir;
@@ -179,6 +184,38 @@ 
 	read_unlock_irqrestore(&device_info->lock, flags);
 	return rb;
 }
+/* The free safe buffer part. dma_free_coherent cannot be called irq disabled.
+ To cope with that, a tasklet (do_free) does the job upon request */
+static DEFINE_SPINLOCK(buflock);
+static LIST_HEAD(buffers);
+
+static void do_free(unsigned long ignored)
+{
+ spin_lock_irq(&buflock);
+ while (!list_empty(&buffers)) {
+ struct safe_buffer *buf;
+
+ buf = list_entry(buffers.next,
+                struct safe_buffer,
+                node);
+ list_del(&buf->node);
+ spin_unlock_irq(&buflock);
+
+     if (buf->pool)
+         dma_pool_free(buf->pool->pool, buf->safe, buf->safe_dma_addr);
+     else
+         dma_free_coherent(buf->dev, buf->size, buf->safe,
+                 buf->safe_dma_addr);
+
+     kfree(buf);
+
+ spin_lock_irq(&buflock);
+
+ }
+ spin_unlock_irq(&buflock);
+}
+
+static DECLARE_TASKLET(deferred_free, do_free, 0);
 
 static inline void
 free_safe_buffer(struct dmabounce_device_info *device_info, struct safe_buffer *buf)
@@ -193,13 +230,11 @@ 
 
 	write_unlock_irqrestore(&device_info->lock, flags);
 
-	if (buf->pool)
-		dma_pool_free(buf->pool->pool, buf->safe, buf->safe_dma_addr);
-	else
-		dma_free_coherent(device_info->dev, buf->size, buf->safe,
-				    buf->safe_dma_addr);
-
-	kfree(buf);
+	/* pass the safe buffer to the tasklet */
+	spin_lock_irqsave(&buflock, flags);
+	list_add_tail(&buf->node, &buffers);
+	tasklet_schedule(&deferred_free);
+	spin_unlock_irqrestore(&buflock, flags);
 }
 
 /* ************************************************** */